OMAPI

Object Management Application Programming Interface

Description:

OMAPI is an programming layer designed for controlling remote applications, and for querying them for their state. It is currently used by the ISC DHCP server and this outline addresses the parts of OMAPI appropriate to the clients of DHCP server. It does this by also describing the use of a thin API layered on top of OMAPI called dhcpctl.

OMAPI uses TCP/IP as the transport for server communication, and security can be imposed by having the client and server cryptographically sign messages using a shared secret.

The dhcpctl*() API works by presenting the client with handles to objects that act as surrogates for the real objects in the server. For example, a client will create a handle for a lease object, and will request the server to fill the lease handle's state. The client application can then pull details such as the lease expiration time from the lease handle.

Modifications can be made to the server state by creating handles to new objects, or by modifying attributes of handles to existing objects, and then instructing the server to update itself according to the changes made.

Usage:

The client application must always call dhcpctl_initialize() before making calls to any other dhcpctl*() functions. This initializes various internal data structures.

To create the connection to the server, the client must use dhcpctl_connect(). As well as making the physical connection, it will also set up the connection data structures to do authentication on each message, if that's required.

All the dhcpctl*() functions return an integer value of type isc_result_t. A successful call will yield a result of ISC_R_SUCCESS, which is defined as 0, but not in a public header; you can define it in your code if you wish. If the call fails for a reason local to the client (e.g., insufficient local memory, or invalid arguments to the call) then the return value of the dhcpctl*() function will show that. If the call succeeds but the server couldn't process the request the error value from the server is returned through another way, shown below.

The easiest way to understand dhcpctl*() is to see it in action. The following program is fully functional, but almost all error checking has been removed to make is shorter and easier to understand. This program will query the server running on the localhost for the details of the lease for IP address 10.0.0.101. It will then print out the time the lease ends.

Note: The code below uses an MDL macro that isn't defined in a public header. You can define it as:
#define MDL __FILE__, __LINE__
#include <stdarg.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>

#include <isc-dhcp/result.h>
#include <dhcp/dhcpctl.h>

int main (int argc, char **argv) {

     /* All modifications of handles and all accesses of
        handle data happen via dhcpctl_data_string objects. */

     dhcpctl_data_string ipaddrstring = NULL;
     dhcpctl_data_string value = NULL;

     dhcpctl_handle connection = NULL;
     dhcpctl_handle lease = NULL;
     isc_result_t waitstatus;
     struct in_addr convaddr;
     time_t thetime;

     /* Required first step. */

     dhcpctl_initialize ();

     /* Set up the connection to the server. The server
        normally listens on port 7911 unless configured to do
        otherwise. */

     dhcpctl_connect (&connection, "127.0.0.1", 7911, 0);

     /* Create a handle to a lease. This call just
        sets up  local  data structure.  The  server  hasn't
        yet  made  any association between the client's data
        structure and any lease it has. */

     dhcpctl_new_object (&lease, connection, "lease");


     /* Create a new data string to store in the handle. */

     memset (&ipaddrstring, 0, sizeof ipaddrstring);

     inet_pton(AF_INET, "10.0.0.101", &convaddr);

     omapi_data_string_new (&ipaddrstring, 4, MDL);

     /* Set the ip-address attribute of the lease handle
        to the given address.  We haven't set any
        other attributes, so when the server makes the
        association, the IP address will be all it uses to
        look up the lease in its tables. */

     memcpy(ipaddrstring->value, &convaddr.s_addr, 4);

     dhcpctl_set_value (lease, ipaddrstring, "ip-address");


     /* Prime the connection with the request to look up
        the lease in the server and fill up the local handle
        with the attributes the  server will send over in
        its answer. */

     dhcpctl_open_object (lease, connection, 0);

     /* Send the message (to look up the lease and send back
        the attribute values  in  the  answer) to the server.
        The value in the variable waitstatus when the function
        returns will be the result from the server. If the
        message couldn't be processed properly by the server,
        then the error will be reflected here. */

     dhcpctl_wait_for_completion (lease, &waitstatus);

     if (waitstatus != ISC_R_SUCCESS) {
        /* server not authoritative */
        exit (0);
     }

     /* Clean up memory we no longer need. */

     dhcpctl_data_string_dereference( &ipaddrstring, MDL);

     /* Get the attribute named "ends" from the lease handle.
        This is a 4-byte integer of the time (in Unix epoch
        seconds) that the lease  will expire. */

     dhcpctl_get_value (&value, lease, "ends");

     memcpy(&thetime, value->value, value->len);
     dhcpctl_data_string_dereference(&value, MDL);

     fprintf (stdout, "ending time is %s",
     ctime(&thetime));
}

Authentication

If the server demands authenticated connections, then before opening the connection the user must call dhcpctl_new_authenticator():

dhcpctl_handle authenticator = NULL;
const char *keyname = "a-key-name";
const char *algorithm = "hmac-md5";
const char *secret = "a-shared-secret";

dhcpctl_new_authenticator (&authenticator, keyname,
                           algorithm, secret,
                           strlen(secret) + 1);

The keyname, algorithm, and secret must all match what's specified in the server's dhcpd.conf file, excepting that the secret should appear in "raw" form, not in base-64 as it would in dhcpd.conf:

key "a-key-name" {
    algorithm hmac-md5;
    secret "a-shared-secret";
};

# Set the omapi-key value to use
# authenticated connections

omapi-key a-key-name;

The authenticator handle that's created by the call to dhcpctl_new_authenticator() must be given as the last (the fourth) argument to dhcpctl_connect(). All messages will then be signed with the given secret string using the specified algorithm.

Contributing author:

OMAPI was created by Ted Lemon of Nominum, Inc. Information about Nominum and support contracts for DHCP and BIND can be found at http://www.nominum.com This documentation was written by James Brister of Nominum, Inc.