DHCP plugin framework, send DHCP Discover upon IP request
authorMartin Willi <martin@revosec.ch>
Tue, 23 Mar 2010 16:18:18 +0000 (17:18 +0100)
committerMartin Willi <martin@revosec.ch>
Thu, 25 Mar 2010 13:28:28 +0000 (14:28 +0100)
src/libcharon/plugins/dhcp/Makefile.am
src/libcharon/plugins/dhcp/dhcp_plugin.c
src/libcharon/plugins/dhcp/dhcp_provider.c [new file with mode: 0644]
src/libcharon/plugins/dhcp/dhcp_provider.h [new file with mode: 0644]
src/libcharon/plugins/dhcp/dhcp_socket.c [new file with mode: 0644]
src/libcharon/plugins/dhcp/dhcp_socket.h [new file with mode: 0644]
src/libcharon/plugins/dhcp/dhcp_transaction.c [new file with mode: 0644]
src/libcharon/plugins/dhcp/dhcp_transaction.h [new file with mode: 0644]

index d082a19..74afcb4 100644 (file)
@@ -9,6 +9,9 @@ else
 plugin_LTLIBRARIES = libstrongswan-dhcp.la
 endif
 
-libstrongswan_dhcp_la_SOURCES = dhcp_plugin.h dhcp_plugin.c
+libstrongswan_dhcp_la_SOURCES = dhcp_plugin.h dhcp_plugin.c \
+       dhcp_provider.h dhcp_provider.c \
+       dhcp_socket.h dhcp_socket.c \
+       dhcp_transaction.h dhcp_transaction.c
 
 libstrongswan_dhcp_la_LDFLAGS = -module -avoid-version
index 0f7ab04..e3dae9b 100644 (file)
@@ -17,6 +17,9 @@
 
 #include <daemon.h>
 
+#include "dhcp_socket.h"
+#include "dhcp_provider.h"
+
 typedef struct private_dhcp_plugin_t private_dhcp_plugin_t;
 
 /**
@@ -28,11 +31,24 @@ struct private_dhcp_plugin_t {
         * implements plugin interface
         */
        dhcp_plugin_t public;
+
+       /**
+        * DHCP communication socket
+        */
+       dhcp_socket_t *socket;
+
+       /**
+        * Attribute provider
+        */
+       dhcp_provider_t *provider;
 };
 
 METHOD(plugin_t, destroy, void,
        private_dhcp_plugin_t *this)
 {
+       lib->attributes->remove_provider(lib->attributes, &this->provider->provider);
+       this->provider->destroy(this->provider);
+       this->socket->destroy(this->socket);
        free(this);
 }
 
@@ -45,8 +61,18 @@ plugin_t *dhcp_plugin_create()
 
        INIT(this,
                .public.plugin.destroy = _destroy,
+               .socket = dhcp_socket_create(),
        );
 
+       if (!this->socket)
+       {
+               free(this);
+               return NULL;
+       }
+
+       this->provider = dhcp_provider_create(this->socket);
+       lib->attributes->add_provider(lib->attributes, &this->provider->provider);
+
        return &this->public.plugin;
 }
 
diff --git a/src/libcharon/plugins/dhcp/dhcp_provider.c b/src/libcharon/plugins/dhcp/dhcp_provider.c
new file mode 100644 (file)
index 0000000..a3e2690
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "dhcp_provider.h"
+
+#include <utils/hashtable.h>
+#include <threading/mutex.h>
+
+typedef struct private_dhcp_provider_t private_dhcp_provider_t;
+
+/**
+ * Private data of an dhcp_provider_t object.
+ */
+struct private_dhcp_provider_t {
+
+       /**
+        * Public dhcp_provider_t interface.
+        */
+       dhcp_provider_t public;
+
+       /**
+        * Completed DHCP transactions
+        */
+       hashtable_t *transactions;
+
+       /**
+        * Lock for transactions
+        */
+       mutex_t *mutex;
+
+       /**
+        * DHCP communication socket
+        */
+       dhcp_socket_t *socket;
+};
+
+/**
+ * Hashtable hash function
+ */
+static u_int hash(void *key)
+{
+       return (uintptr_t)key;
+}
+
+/**
+ * Hashtable equals function
+ */
+static bool equals(void *a, void *b)
+{
+       return a == b;
+}
+
+/**
+ * Hash ID and host to a key
+ */
+static uintptr_t hash_id_host(identification_t *id, host_t *host)
+{
+       return chunk_hash_inc(id->get_encoding(id),
+                                                 chunk_hash(host->get_address(host)));
+}
+
+/**
+ * Hash a DHCP transaction to a key, using address and id
+ */
+static uintptr_t hash_transaction(dhcp_transaction_t *transaction)
+{
+       return hash_id_host(transaction->get_identity(transaction),
+                                               transaction->get_address(transaction));
+}
+
+METHOD(attribute_provider_t, acquire_address, host_t*,
+       private_dhcp_provider_t *this, char *pool,
+       identification_t *id, host_t *requested)
+{
+       if (streq(pool, "dhcp"))
+       {
+               dhcp_transaction_t *transaction, *old;
+               host_t *vip;
+
+               transaction = this->socket->enroll(this->socket, id);
+               if (!transaction)
+               {
+                       return NULL;
+               }
+               vip = transaction->get_address(transaction);
+               vip = vip->clone(vip);
+               this->mutex->lock(this->mutex);
+               old = this->transactions->put(this->transactions,
+                                                       (void*)hash_transaction(transaction), transaction);
+               this->mutex->unlock(this->mutex);
+               DESTROY_IF(old);
+               return vip;
+       }
+       return NULL;
+}
+
+METHOD(attribute_provider_t, release_address, bool,
+       private_dhcp_provider_t *this, char *pool,
+       host_t *address, identification_t *id)
+{
+       if (streq(pool, "dhcp"))
+       {
+               dhcp_transaction_t *transaction;
+
+               this->mutex->lock(this->mutex);
+               transaction = this->transactions->remove(this->transactions,
+                                                                               (void*)hash_id_host(id, address));
+               this->mutex->unlock(this->mutex);
+               if (transaction)
+               {
+                       transaction->destroy(transaction);
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
+METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*,
+       private_dhcp_provider_t *this, identification_t *id, host_t *vip)
+{
+       return enumerator_create_empty();
+}
+
+METHOD(dhcp_provider_t, destroy, void,
+       private_dhcp_provider_t *this)
+{
+       enumerator_t *enumerator;
+       dhcp_transaction_t *value;
+       void *key;
+
+       enumerator = this->transactions->create_enumerator(this->transactions);
+       while (enumerator->enumerate(enumerator, &key, &value))
+       {
+               value->destroy(value);
+       }
+       enumerator->destroy(enumerator);
+       this->transactions->destroy(this->transactions);
+       this->mutex->destroy(this->mutex);
+       free(this);
+}
+
+/**
+ * See header
+ */
+dhcp_provider_t *dhcp_provider_create(dhcp_socket_t *socket)
+{
+       private_dhcp_provider_t *this;
+
+       INIT(this,
+               .public = {
+                       .provider = {
+                               .acquire_address = _acquire_address,
+                               .release_address = _release_address,
+                               .create_attribute_enumerator = _create_attribute_enumerator,
+                       },
+                       .destroy = _destroy,
+               },
+               .socket = socket,
+               .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+               .transactions = hashtable_create(hash, equals, 8),
+       );
+
+       return &this->public;
+}
+
diff --git a/src/libcharon/plugins/dhcp/dhcp_provider.h b/src/libcharon/plugins/dhcp/dhcp_provider.h
new file mode 100644 (file)
index 0000000..e720e87
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup dhcp_provider dhcp_provider
+ * @{ @ingroup dhcp
+ */
+
+#ifndef DHCP_PROVIDER_H_
+#define DHCP_PROVIDER_H_
+
+typedef struct dhcp_provider_t dhcp_provider_t;
+
+#include "dhcp_socket.h"
+
+#include <attributes/attribute_provider.h>
+
+/**
+ * DHCP based attribute provider.
+ */
+struct dhcp_provider_t {
+
+       /**
+        * Implements attribute_provier_t interface.
+        */
+       attribute_provider_t provider;
+
+       /**
+        * Destroy a dhcp_provider_t.
+        */
+       void (*destroy)(dhcp_provider_t *this);
+};
+
+/**
+ * Create a dhcp_provider instance.
+ *
+ * @param socket               socket to use for DHCP communication
+ * @return                             provider instance
+ */
+dhcp_provider_t *dhcp_provider_create(dhcp_socket_t *socket);
+
+#endif /** DHCP_PROVIDER_H_ @}*/
diff --git a/src/libcharon/plugins/dhcp/dhcp_socket.c b/src/libcharon/plugins/dhcp/dhcp_socket.c
new file mode 100644 (file)
index 0000000..6ad600f
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "dhcp_socket.h"
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <linux/if_arp.h>
+
+#include <utils/linked_list.h>
+#include <utils/identification.h>
+#include <threading/mutex.h>
+#include <threading/condvar.h>
+#include <threading/thread.h>
+
+#include <daemon.h>
+#include <processing/jobs/callback_job.h>
+
+#define DHCP_SERVER_PORT 67
+#define DHCP_CLIENT_PORT 68
+
+typedef struct private_dhcp_socket_t private_dhcp_socket_t;
+
+/**
+ * Private data of an dhcp_socket_t object.
+ */
+struct private_dhcp_socket_t {
+
+       /**
+        * Public dhcp_socket_t interface.
+        */
+       dhcp_socket_t public;
+
+       /**
+        * Random number generator
+        */
+       rng_t *rng;
+
+       /**
+        * List of active transactions
+        */
+       linked_list_t *active;
+
+       /**
+        * List of successfully completed transactions
+        */
+       linked_list_t *completed;
+
+       /**
+        * Lock for transactions
+        */
+       mutex_t *mutex;
+
+       /**
+        * Condvar to wait for transaction completion
+        */
+       condvar_t *condvar;
+
+       /**
+        * Threads waiting in condvar
+        */
+       int waiting;
+
+       /**
+        * RAW socket
+        */
+       int skt;
+
+       /**
+        * DHCP server address, or broadcast
+        */
+       host_t *dst;
+
+       /**
+        * Callback job receiving DHCP responses
+        */
+       callback_job_t *job;
+};
+
+typedef enum {
+       BOOTREQUEST = 1,
+       BOOTREPLY = 2,
+} dhcp_opcode_t;
+
+typedef enum {
+       DHCP_HOST_NAME = 12,
+       DHCP_MESSAGE_TYPE = 53,
+       DHCP_PARAM_REQ_LIST = 55,
+} dhcp_option_type_t;
+
+typedef enum {
+       DHCP_DISCOVER = 1,
+} dhcp_message_type_t;
+
+typedef enum {
+       DHCP_ROUTER = 3,
+       DHCP_DNS_SERVER = 6,
+} dhcp_parameter_t;
+
+typedef struct __attribute__((packed)) {
+       u_int8_t type;
+       u_int8_t len;
+       char data[];
+} dhcp_option_t;
+
+typedef struct __attribute__((packed)) {
+       u_int8_t opcode;
+       u_int8_t hw_type;
+       u_int8_t hw_addr_len;
+       u_int8_t hop_count;
+       u_int32_t transaction_id;
+       u_int16_t number_of_seconds;
+       u_int16_t flags;
+       u_int32_t client_address;
+       u_int32_t your_address;
+       u_int32_t server_address;
+       u_int32_t gateway_address;
+       char client_hw_addr[6];
+       char client_hw_padding[10];
+       char server_hostname[64];
+       char boot_filename[128];
+       u_int32_t magic_cookie;
+       char options[252];
+} dhcp_t;
+
+/**
+ * Send DHCP discover using a given transaction
+ */
+static void discover(private_dhcp_socket_t *this,
+                                        dhcp_transaction_t *transaction)
+{
+       chunk_t id_data, broadcast = chunk_from_chars(0xFF,0xFF,0xFF,0xFF);
+       identification_t *identity;
+       dhcp_option_t *option;
+       dhcp_t dhcp;
+       int optlen = 0;
+       u_int hash;
+       host_t *src;
+       ssize_t len;
+
+       memset(&dhcp, 0, sizeof(dhcp));
+       dhcp.opcode = BOOTREQUEST;
+       dhcp.hw_type = ARPHRD_ETHER;
+       dhcp.hw_addr_len = 6;
+       dhcp.transaction_id = transaction->get_id(transaction);
+       if (chunk_equals(broadcast, this->dst->get_address(this->dst)))
+       {
+               /* TODO: send with 0.0.0.0 source address */
+       }
+       else
+       {
+               /* act as relay agent */
+               src = charon->kernel_interface->get_source_addr(
+                                                                       charon->kernel_interface, this->dst, NULL);
+               if (src)
+               {
+                       memcpy(&dhcp.gateway_address, src->get_address(src).ptr,
+                                  sizeof(dhcp.gateway_address));
+                       src->destroy(src);
+               }
+       }
+
+       identity = transaction->get_identity(transaction);
+       id_data = identity->get_encoding(identity);
+
+       /* magic bytes, a locally administered unicast MAC */
+       dhcp.client_hw_addr[0] = 0x7A;
+       dhcp.client_hw_addr[1] = 0xA7;
+       /* with ID specific postfix */
+       hash = htonl(chunk_hash(id_data));
+       memcpy(&dhcp.client_hw_addr[2], &hash, 4);
+
+       dhcp.magic_cookie = htonl(0x63825363);
+
+       option = (dhcp_option_t*)&dhcp.options[optlen];
+       option->type = DHCP_MESSAGE_TYPE;
+       option->len = 1;
+       option->data[0] = DHCP_DISCOVER;
+       optlen += sizeof(dhcp_option_t) + option->len;
+
+       option = (dhcp_option_t*)&dhcp.options[optlen];
+       option->type = DHCP_HOST_NAME;
+       option->len = min(id_data.len, 64);
+       memcpy(option->data, id_data.ptr, option->len);
+       optlen += sizeof(dhcp_option_t) + option->len;
+
+       option = (dhcp_option_t*)&dhcp.options[optlen];
+       option->type = DHCP_PARAM_REQ_LIST;
+       option->len = 2;
+       option->data[0] = DHCP_ROUTER;
+       option->data[1] = DHCP_DNS_SERVER;
+       optlen += sizeof(dhcp_option_t) + option->len;
+
+       dhcp.options[optlen++] = 0xFF;
+
+       len = offsetof(dhcp_t, magic_cookie) + ((optlen + 4) / 64 * 64 + 64);
+       if (sendto(this->skt, &dhcp, len, 0, this->dst->get_sockaddr(this->dst),
+                          *this->dst->get_sockaddr_len(this->dst)) != len)
+       {
+               DBG1(DBG_CFG, "sending DHCP DISCOVER failed: %s", strerror(errno));
+       }
+}
+
+METHOD(dhcp_socket_t, enroll, dhcp_transaction_t*,
+       private_dhcp_socket_t *this, identification_t *identity)
+{
+       dhcp_transaction_t *transaction;
+       u_int32_t id;
+
+       this->rng->get_bytes(this->rng, sizeof(id), (u_int8_t*)&id);
+       transaction = dhcp_transaction_create(id, identity);
+       discover(this, transaction);
+       transaction->destroy(transaction);
+
+       return NULL;
+}
+
+METHOD(dhcp_socket_t, destroy, void,
+       private_dhcp_socket_t *this)
+{
+       if (this->job)
+       {
+               this->job->cancel(this->job);
+       }
+       while (this->waiting)
+       {
+               this->condvar->signal(this->condvar);
+       }
+       if (this->skt > 0)
+       {
+               close(this->skt);
+       }
+       this->mutex->destroy(this->mutex);
+       this->condvar->destroy(this->condvar);
+       this->active->destroy(this->active);
+       this->completed->destroy(this->completed);
+       DESTROY_IF(this->rng);
+       DESTROY_IF(this->dst);
+       free(this);
+}
+
+/**
+ * See header
+ */
+dhcp_socket_t *dhcp_socket_create()
+{
+       private_dhcp_socket_t *this;
+       struct sockaddr_in src;
+       int on = 1;
+
+       INIT(this,
+               .public = {
+                       .enroll = _enroll,
+                       .destroy = _destroy,
+               },
+               .rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK),
+               .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+               .condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
+               .active = linked_list_create(),
+               .completed = linked_list_create(),
+       );
+
+       if (!this->rng)
+       {
+               DBG1(DBG_CFG, "unable to create RNG");
+               destroy(this);
+               return NULL;
+       }
+
+       this->dst = host_create_from_string(lib->settings->get_str(lib->settings,
+                                                       "charon.plugins.dhcp.server", "255.255.255.255"),
+                                                       DHCP_SERVER_PORT);
+       if (!this->dst)
+       {
+               DBG1(DBG_CFG, "configured DHCP server address invalid");
+               destroy(this);
+               return NULL;
+       }
+
+       this->skt = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+       if (this->skt == -1)
+       {
+               DBG1(DBG_CFG, "unable to create DHCP send socket: %s", strerror(errno));
+               destroy(this);
+               return NULL;
+       }
+
+       if (setsockopt(this->skt, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
+       {
+               DBG1(DBG_CFG, "unable to reuse DHCP socket address: %s", strerror(errno));
+               destroy(this);
+               return NULL;
+       }
+       if (setsockopt(this->skt, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) == -1)
+       {
+               DBG1(DBG_CFG, "unable to broadcast on DHCP socket: %s", strerror(errno));
+               destroy(this);
+               return NULL;
+       }
+
+       src.sin_family = AF_INET;
+       src.sin_port = htons(DHCP_CLIENT_PORT);
+       src.sin_addr.s_addr = INADDR_ANY;
+       if (bind(this->skt, (struct sockaddr*)&src, sizeof(src)) == -1)
+       {
+               DBG1(DBG_CFG, "unable to bind DHCP send socket: %s", strerror(errno));
+               destroy(this);
+               return NULL;
+       }
+
+       return &this->public;
+}
+
diff --git a/src/libcharon/plugins/dhcp/dhcp_socket.h b/src/libcharon/plugins/dhcp/dhcp_socket.h
new file mode 100644 (file)
index 0000000..687e95b
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup dhcp_socket dhcp_socket
+ * @{ @ingroup dhcp
+ */
+
+#ifndef DHCP_SOCKET_H_
+#define DHCP_SOCKET_H_
+
+typedef struct dhcp_socket_t dhcp_socket_t;
+
+#include "dhcp_transaction.h"
+
+/**
+ * DHCP socket implementation
+ */
+struct dhcp_socket_t {
+
+       /**
+        * Enroll a client address using DHCP.
+        *
+        * @param identity              peer identity to enroll an address for
+        * @return                              completed DHCP transaction, NULL on failure
+        */
+       dhcp_transaction_t* (*enroll)(dhcp_socket_t *this,
+                                                                 identification_t *identity);
+
+       /**
+        * Destroy a dhcp_socket_t.
+        */
+       void (*destroy)(dhcp_socket_t *this);
+};
+
+/**
+ * Create a dhcp_socket instance.
+ */
+dhcp_socket_t *dhcp_socket_create();
+
+#endif /** DHCP_SOCKET_H_ @}*/
diff --git a/src/libcharon/plugins/dhcp/dhcp_transaction.c b/src/libcharon/plugins/dhcp/dhcp_transaction.c
new file mode 100644 (file)
index 0000000..14e6540
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "dhcp_transaction.h"
+
+typedef struct private_dhcp_transaction_t private_dhcp_transaction_t;
+
+/**
+ * Private data of an dhcp_transaction_t object.
+ */
+struct private_dhcp_transaction_t {
+
+       /**
+        * Public dhcp_transaction_t interface.
+        */
+       dhcp_transaction_t public;
+
+       /**
+        * DHCP transaction ID
+        */
+       u_int32_t id;
+
+       /**
+        * Peer identity
+        */
+       identification_t *identity;
+
+       /**
+        * received DHCP address
+        */
+       host_t *address;
+};
+
+METHOD(dhcp_transaction_t, get_id, u_int32_t,
+       private_dhcp_transaction_t *this)
+{
+       return this->id;
+}
+
+METHOD(dhcp_transaction_t, get_identity, identification_t*,
+       private_dhcp_transaction_t *this)
+{
+       return this->identity;
+}
+
+METHOD(dhcp_transaction_t, set_address, void,
+       private_dhcp_transaction_t *this, host_t *address)
+{
+       DESTROY_IF(this->address);
+       this->address = address;
+}
+
+METHOD(dhcp_transaction_t, get_address, host_t*,
+       private_dhcp_transaction_t *this)
+{
+       return this->address;
+}
+
+METHOD(dhcp_transaction_t, destroy, void,
+       private_dhcp_transaction_t *this)
+{
+       this->identity->destroy(this->identity);
+       DESTROY_IF(this->address);
+       free(this);
+}
+
+/**
+ * See header
+ */
+dhcp_transaction_t *dhcp_transaction_create(u_int32_t id,
+                                                                                       identification_t *identity)
+{
+       private_dhcp_transaction_t *this;
+
+       INIT(this,
+               .public = {
+                       .get_id = _get_id,
+                       .get_identity = _get_identity,
+                       .set_address = _set_address,
+                       .get_address = _get_address,
+                       .destroy = _destroy,
+               },
+               .id = id,
+               .identity = identity->clone(identity),
+       );
+
+       return &this->public;
+}
+
diff --git a/src/libcharon/plugins/dhcp/dhcp_transaction.h b/src/libcharon/plugins/dhcp/dhcp_transaction.h
new file mode 100644 (file)
index 0000000..e8215e9
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup dhcp_transaction dhcp_transaction
+ * @{ @ingroup dhcp
+ */
+
+#ifndef DHCP_TRANSACTION_H_
+#define DHCP_TRANSACTION_H_
+
+#include <utils/host.h>
+#include <utils/identification.h>
+
+typedef struct dhcp_transaction_t dhcp_transaction_t;
+
+/**
+ * DHCP transaction class.
+ */
+struct dhcp_transaction_t {
+
+       /**
+        * Get the DCHP transaction ID.
+        *
+        * @return                      DHCP transaction identifier
+        */
+       u_int32_t (*get_id)(dhcp_transaction_t *this);
+
+       /**
+        * Get the peer identity this transaction is used for.
+        *
+        * @return                      peer Identity
+        */
+       identification_t* (*get_identity)(dhcp_transaction_t *this);
+
+       /**
+        * Set the DHCP address received using this transaction.
+        *
+        * @param host          received DHCP address
+        */
+       void (*set_address)(dhcp_transaction_t *this, host_t *address);
+
+       /**
+        * Get the DHCP address received using this transaction.
+        *
+        * @return                      received DHCP address
+        */
+       host_t* (*get_address)(dhcp_transaction_t *this);
+
+       /**
+        * Destroy a dhcp_transaction_t.
+        */
+       void (*destroy)(dhcp_transaction_t *this);
+};
+
+/**
+ * Create a dhcp_transaction instance.
+ *
+ * @param id           DHCP transaction identifier
+ * @param identity     peer identity this transaction is used for
+ * @return                     transaction instance
+ */
+dhcp_transaction_t *dhcp_transaction_create(u_int32_t id,
+                                                                                       identification_t *identity);
+
+#endif /** DHCP_TRANSACTION_H_ @}*/