The patch is used to implement dhcp-server-lib for connman.
I just did the initial testing on it. Main feature works.
1. assign the IP
2. send the gateway and subnet option
And more test still needs to be done.
Besides, I have not clean up and split the patch.
So the patch is just for your reference.

In the following days, I will continue to debug and clean up the patch.

> -----Original Message-----
> From: Xu, Martin
> Sent: Monday, October 18, 2010 5:05 PM
> To: [email protected]
> Cc: Xu, Martin
> Subject: diff --git a/gdhcp/client.c b/gdhcp/client.c
>
> -/* Initialize the packet with the proper defaults */
>  static void init_packet(GDHCPClient *dhcp_client,
>               struct dhcp_packet *packet, char type)
>  {
> @@ -268,70 +267,7 @@ static int send_release(GDHCPClient *dhcp_client,
>                                               server, SERVER_PORT);
>  }
>
> -static gboolean interface_is_up(int index)
> -{
> -     int sk, err;
> -     struct ifreq ifr;
> -     gboolean ret = FALSE;
> -
> -     sk = socket(PF_INET, SOCK_DGRAM, 0);
> -     if (sk < 0) {
> -             perror("Open socket error");
> -             return FALSE;
> -     }
> -
> -     memset(&ifr, 0, sizeof(ifr));
> -     ifr.ifr_ifindex = index;
> -
> -     err = ioctl(sk, SIOCGIFNAME, &ifr);
> -     if (err < 0) {
> -             perror("Get interface name error");
> -             goto done;
> -     }
> -
> -     err = ioctl(sk, SIOCGIFFLAGS, &ifr);
> -     if (err < 0) {
> -             perror("Get interface flags error");
> -             goto done;
> -     }
> -
> -     if (ifr.ifr_flags & IFF_UP)
> -             ret = TRUE;
> -
> -done:
> -     close(sk);
> -
> -     return ret;
> -}
>
> -static char *get_interface_name(int index)
> -{
> -     struct ifreq ifr;
> -     int sk, err;
> -
> -     if (index < 0)
> -             return NULL;
> -
> -     sk = socket(PF_INET, SOCK_DGRAM, 0);
> -     if (sk < 0) {
> -             perror("Open socket error");
> -             return NULL;
> -     }
> -
> -     memset(&ifr, 0, sizeof(ifr));
> -     ifr.ifr_ifindex = index;
> -
> -     err = ioctl(sk, SIOCGIFNAME, &ifr);
> -     if (err < 0) {
> -             perror("Get interface name error");
> -             close(sk);
> -             return NULL;
> -     }
> -
> -     close(sk);
> -
> -     return g_strdup(ifr.ifr_name);
> -}
>
>  static void get_interface_mac_address(int index, uint8_t *mac_address)
>  {
> @@ -1201,19 +1137,6 @@ GDHCPClientError
> g_dhcp_client_set_request(GDHCPClient *dhcp_client,
>       return G_DHCP_CLIENT_ERROR_NONE;
>  }
>
> -static uint8_t *alloc_dhcp_option(int code, const char *str, int extra)
> -{
> -     uint8_t *storage;
> -     int len = strnlen(str, 255);
> -
> -     storage = malloc(len + extra + OPT_DATA);
> -     storage[OPT_CODE] = code;
> -     storage[OPT_LEN] = len + extra;
> -     memcpy(storage + extra + OPT_DATA, str, len);
> -
> -     return storage;
> -}
> -
>  /* Now only support send hostname */
>  GDHCPClientError g_dhcp_client_set_send(GDHCPClient *dhcp_client,
>               unsigned char option_code, const char *option_value)
> diff --git a/gdhcp/common.c b/gdhcp/common.c
> index fc95881..ede91a4 100644
> --- a/gdhcp/common.c
> +++ b/gdhcp/common.c
> @@ -22,11 +22,22 @@
>  #include <config.h>
>  #endif
>
> +#include <stdio.h>
>  #include <errno.h>
>  #include <unistd.h>
> +#include <sys/ioctl.h>
>  #include <stdint.h>
> +#include <stdlib.h>
>  #include <string.h>
>  #include <endian.h>
> +
> +#include <arpa/inet.h>
> +#include <netpacket/packet.h>
> +#include <net/ethernet.h>
> +#include <net/if_arp.h>
> +
> +
> +#include <linux/if.h>
>  #include <netpacket/packet.h>
>  #include <net/ethernet.h>
>
> @@ -417,3 +428,81 @@ int dhcp_l3_socket(int port, const char *interface)
>
>       return fd;
>  }
> +
> +char *get_interface_name(int index)
> +{
> +     struct ifreq ifr;
> +     int sk, err;
> +
> +     if (index < 0)
> +             return NULL;
> +
> +     sk = socket(PF_INET, SOCK_DGRAM, 0);
> +     if (sk < 0) {
> +             perror("Open socket error");
> +             return NULL;
> +     }
> +
> +     memset(&ifr, 0, sizeof(ifr));
> +     ifr.ifr_ifindex = index;
> +
> +     err = ioctl(sk, SIOCGIFNAME, &ifr);
> +     if (err < 0) {
> +             perror("Get interface name error");
> +             close(sk);
> +             return NULL;
> +     }
> +
> +     close(sk);
> +
> +     return g_strdup(ifr.ifr_name);
> +}
> +
> +gboolean interface_is_up(int index)
> +{
> +     int sk, err;
> +     struct ifreq ifr;
> +     gboolean ret = FALSE;
> +
> +     sk = socket(PF_INET, SOCK_DGRAM, 0);
> +     if (sk < 0) {
> +             perror("Open socket error");
> +             return FALSE;
> +     }
> +
> +     memset(&ifr, 0, sizeof(ifr));
> +     ifr.ifr_ifindex = index;
> +
> +     err = ioctl(sk, SIOCGIFNAME, &ifr);
> +     if (err < 0) {
> +             perror("Get interface name error");
> +             goto done;
> +     }
> +
> +     err = ioctl(sk, SIOCGIFFLAGS, &ifr);
> +     if (err < 0) {
> +             perror("Get interface flags error");
> +             goto done;
> +     }
> +
> +     if (ifr.ifr_flags & IFF_UP)
> +             ret = TRUE;
> +
> +done:
> +     close(sk);
> +
> +     return ret;
> +}
> +
> +uint8_t *alloc_dhcp_option(int code, const char *str, int extra)
> +{
> +     uint8_t *storage;
> +     int len = strnlen(str, 255);
> +
> +     storage = malloc(len + extra + OPT_DATA);
> +     storage[OPT_CODE] = code;
> +     storage[OPT_LEN] = len + extra;
> +     memcpy(storage + extra + OPT_DATA, str, len);
> +
> +     return storage;
> +}
> diff --git a/gdhcp/common.h b/gdhcp/common.h
> index 5b6fe58..b565f98 100644
> --- a/gdhcp/common.h
> +++ b/gdhcp/common.h
> @@ -51,6 +51,10 @@ static const uint8_t MAC_BCAST_ADDR[6]
> __attribute__((aligned(2))) = {
>       0xff, 0xff, 0xff, 0xff, 0xff, 0xff
>  };
>
> +static const uint8_t MAC_ANY_ADDR[6] __attribute__((aligned(2))) = {
> +     0x00, 0x00, 0x00, 0x00, 0x00, 0x00
> +};
> +
>  /* DHCP packet */
>  #define DHCP_MAGIC              0x63825363
>  #define DHCP_OPTIONS_BUFSIZE    308
> @@ -171,3 +175,6 @@ int dhcp_send_kernel_packet(struct dhcp_packet
> *dhcp_pkt,
>                       uint32_t dest_ip, int dest_port);
>  int dhcp_l3_socket(int port, const char *interface);
>  int dhcp_recv_l3_packet(struct dhcp_packet *packet, int fd);
> +char *get_interface_name(int index);
> +gboolean interface_is_up(int index);
> +uint8_t *alloc_dhcp_option(int code, const char *str, int extra);
> diff --git a/gdhcp/gdhcp.h b/gdhcp/gdhcp.h
> index 59b07ea..26e40fe 100644
> --- a/gdhcp/gdhcp.h
> +++ b/gdhcp/gdhcp.h
> @@ -1,6 +1,6 @@
>  /*
>   *
> - *  DHCP client library with GLib integration
> + *  DHCP library with GLib integration
>   *
>   *  Copyright (C) 2009-2010  Intel Corporation. All rights reserved.
>   *
> @@ -28,6 +28,12 @@
>  extern "C" {
>  #endif
>
> +/* For debug temporarily */
> +#define DHCP_DBG(fmt, arg...) do { \
> +printf("%s %s() " fmt "\n",__FILE__, __FUNCTION__ , ## arg); \
> +} while (0)
> +
> +/* DHCP Client part*/
>  struct _GDHCPClient;
>
>  typedef struct _GDHCPClient GDHCPClient;
> @@ -94,6 +100,42 @@ int g_dhcp_client_get_index(GDHCPClient *client);
>  void g_dhcp_client_set_debug(GDHCPClient *client,
>                               GDHCPDebugFunc func, gpointer user_data);
>
> +/* DHCP Server */
> +typedef enum {
> +     G_DHCP_SERVER_ERROR_NONE,
> +     G_DHCP_SERVER_ERROR_INTERFACE_UNAVAILABLE,
> +     G_DHCP_SERVER_ERROR_INTERFACE_IN_USE,
> +     G_DHCP_SERVER_ERROR_INTERFACE_DOWN,
> +     G_DHCP_SERVER_ERROR_NOMEM,
> +     G_DHCP_SERVER_ERROR_INVALID_INDEX,
> +     G_DHCP_SERVER_ERROR_INVALID_OPTION,
> +     G_DHCP_SERVER_ERROR_IP_ADDRESS_INVALID
> +} GDHCPServerError;
> +
> +typedef void (*GDHCPSaveLeaseFunc) (unsigned char *mac,
> +                     unsigned int nip, unsigned int expire);
> +struct _GDHCPServer;
> +
> +typedef struct _GDHCPServer GDHCPServer;
> +
> +GDHCPServer *g_dhcp_server_new(GDHCPType type,
> +             int ifindex, GDHCPServerError *error);
> +int g_dhcp_server_start(GDHCPServer *server);
> +void g_dhcp_server_stop(GDHCPServer *server);
> +
> +GDHCPServer *g_dhcp_server_ref(GDHCPServer *server);
> +void g_dhcp_server_unref(GDHCPServer *server);
> +
> +int g_dhcp_server_set_option(GDHCPServer *server,
> +             unsigned char option_code, const char *option_value);
> +int g_dhcp_server_set_ip_area(GDHCPServer *server,
> +             const char *start_ip, const char *end_ip);
> +void g_dhcp_server_load_lease(GDHCPServer *dhcp_server, unsigned int
> expire,
> +                                     unsigned char *mac, unsigned int 
> lease_ip);
> +void g_dhcp_server_set_debug(GDHCPServer *server,
> +                             GDHCPDebugFunc func, gpointer user_data);
> +void g_dhcp_server_set_save_lease(GDHCPServer *dhcp_server,
> +                             GDHCPSaveLeaseFunc func, gpointer user_data);
>  #ifdef __cplusplus
>  }
>  #endif
> diff --git a/gdhcp/server.c b/gdhcp/server.c
> new file mode 100644
> index 0000000..f7cdbe2
> --- /dev/null
> +++ b/gdhcp/server.c
> @@ -0,0 +1,881 @@
> +/*
> + *
> + *  DHCP Server library with GLib integration
> + *
> + *  Copyright (C) 2009-2010  Intel Corporation. All rights reserved.
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License version 2 as
> + *  published by the Free Software Foundation.
> + *
> + *  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.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with this program; if not, write to the Free Software
> + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
> USA
> + *
> + */
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <stdio.h>
> +#include <errno.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/ioctl.h>
> +#include <arpa/inet.h>
> +
> +#include <netpacket/packet.h>
> +#include <net/ethernet.h>
> +#include <net/if_arp.h>
> +
> +#include <linux/if.h>
> +#include <linux/filter.h>
> +
> +#include <glib.h>
> +
> +#include "common.h"
> +
> +/* 8 hours */
> +#define DHCP_LEASE_SEC 8*60*60
> +
> +struct _GDHCPServer {
> +     gint ref_count;
> +     GDHCPType type;
> +     gboolean started;
> +     int ifindex;
> +     char *interface;
> +//   uint8_t mac_address[6];
> +//   uint32_t xid;
> +     uint32_t start_ip;
> +     uint32_t end_ip;
> +     uint32_t server_nip;
> +//   uint32_t requested_ip;
> +//   char *assigned_ip;
> +     uint32_t lease_seconds;
> +     int listener_sockfd;
> +     guint listener_watch;
> +     GIOChannel *listener_channel;
> +     GList *lease_list;
> +     GHashTable *nip_lease_hash;
> +     GHashTable *option_hash; /* Options send to client */
> +     GDHCPSaveLeaseFunc save_lease_func;
> +     GDHCPDebugFunc debug_func;
> +     gpointer debug_data;
> +};
> +
> +struct dhcp_lease {
> +     uint32_t expire;
> +     uint32_t lease_nip;
> +     uint8_t lease_mac[6];
> +     uint8_t pad[2];
> +     /* total size is a multiply of 4 */
> +}  __attribute__((packed));
> +
> +static struct dhcp_lease *find_lease_by_mac(GDHCPServer *dhcp_server,
> +                                             const uint8_t *mac)
> +{
> +     GList *list;
> +
> +     DHCP_DBG("");
> +     for (list = dhcp_server->lease_list; list; list = list->next) {
> +             struct dhcp_lease *lease = list->data;
> +
> +             if (memcmp(lease->lease_mac, mac, 6) == 0)
> +                     return lease;
> +     }
> +
> +     return NULL;
> +}
> +
> +static void remove_lease(GDHCPServer *dhcp_server, struct dhcp_lease
> *lease)
> +{
> +     dhcp_server->lease_list =
> +                     g_list_remove(dhcp_server->lease_list, lease);
> +
> +     g_hash_table_remove(dhcp_server->nip_lease_hash,
> +                                     GINT_TO_POINTER((int) 
> lease->lease_nip));
> +     g_free(lease);
> +}
> +
> +/* Clear the old lease and create the new one */
> +static int get_lease(GDHCPServer *dhcp_server, uint32_t yiaddr,
> +                             const uint8_t *mac, struct dhcp_lease **lease)
> +{
> +     struct dhcp_lease *lease_nip, *lease_mac;
> +
> +     DHCP_DBG("");
> +
> +     if (yiaddr == 0)
> +             return -ENXIO;
> +
> +     if (ntohl(yiaddr) < dhcp_server->start_ip)
> +             return -ENXIO;
> +
> +     if (ntohl(yiaddr) > dhcp_server->end_ip)
> +             return -ENXIO;
> +
> +     if (memcmp(mac, MAC_BCAST_ADDR, 6) == 0)
> +             return -ENXIO;
> +
> +     if (memcmp(mac, MAC_ANY_ADDR, 6) == 0)
> +             return -ENXIO;
> +
> +     lease_mac = find_lease_by_mac(dhcp_server, mac);
> +
> +     lease_nip = g_hash_table_lookup(dhcp_server->nip_lease_hash,
> +                                             GINT_TO_POINTER((int) yiaddr));
> +
> +     DHCP_DBG("lease_mac %p lease_nip %p", lease_mac, lease_nip);
> +
> +     if (lease_nip != NULL) {
> +             dhcp_server->lease_list =
> +                             g_list_remove(dhcp_server->lease_list,
> +                                                             lease_nip);
> +             g_hash_table_remove(dhcp_server->nip_lease_hash,
> GINT_TO_POINTER((int) yiaddr));
> +
> +             if (lease_nip != lease_mac)
> +                     remove_lease(dhcp_server, lease_mac);
> +
> +             *lease = lease_nip;
> +
> +             return 0;
> +     }
> +
> +     if (lease_mac != NULL) {
> +             dhcp_server->lease_list =
> +                             g_list_remove(dhcp_server->lease_list,
> +                                                             lease_mac);
> +             g_hash_table_remove(dhcp_server->nip_lease_hash,
> +                                             GINT_TO_POINTER((int) 
> lease_mac->lease_nip));
> +             *lease = lease_mac;
> +
> +             return 0;
> +     }
> +
> +     *lease = g_try_new0(struct dhcp_lease, 1);
> +     if (*lease == NULL)
> +             return -ENOMEM;
> +
> +     return 0;
> +}
> +
> +static gint compare_expire(gconstpointer a, gconstpointer b)
> +{
> +     const struct dhcp_lease *lease1 = a;
> +     const struct dhcp_lease *lease2 = b;
> +
> +     return lease2->expire - lease1->expire;
> +}
> +
> +static struct dhcp_lease *add_lease(GDHCPServer *dhcp_server, uint32_t
> expire,
> +                                     const uint8_t *chaddr, uint32_t yiaddr)
> +{
> +     struct dhcp_lease *lease = NULL;
> +     int ret;
> +
> +     DHCP_DBG("");
> +
> +     ret = get_lease(dhcp_server, yiaddr, chaddr, &lease);
> +     if (ret != 0)
> +             return NULL;
> +
> +     memset(lease, 0, sizeof(* lease));
> +
> +     memcpy(lease->lease_mac, chaddr, 6);
> +     lease->lease_nip = yiaddr;
> +
> +     if (expire == 0)
> +             lease->expire = time(NULL) + dhcp_server->lease_seconds;
> +     else
> +             lease->expire = expire;
> +
> +     dhcp_server->lease_list = g_list_insert_sorted(dhcp_server->lease_list,
> +                                                     lease, compare_expire);
> +
> +     g_hash_table_insert(dhcp_server->nip_lease_hash,
> +                             GINT_TO_POINTER((int) lease->lease_nip), lease);
> +
> +     return lease;
> +}
> +
> +static struct dhcp_lease *find_lease_by_nip(GDHCPServer *dhcp_server,
> uint32_t nip)
> +{
> +     return g_hash_table_lookup(dhcp_server->nip_lease_hash,
> GINT_TO_POINTER((int) nip));
> +}
> +
> +/* Check if the IP is taken; if it is, add it to the lease table */
> +static gboolean nobody_responds_to_arp(uint32_t nip, const uint8_t
> *safe_mac)
> +{
> +/* TODO: Add ARP checking */
> +     return TRUE;
> +}
> +
> +static gboolean is_expired_lease(struct dhcp_lease *lease)
> +{
> +     if (lease->expire < (uint32_t) time(NULL))
> +             return TRUE;
> +
> +     return FALSE;
> +}
> +
> +static uint32_t find_free_or_expired_nip(GDHCPServer *dhcp_server,
> +                                     const uint8_t *safe_mac)
> +{
> +     uint32_t addr;
> +     struct dhcp_lease *lease;
> +     GList *list;
> +     addr = dhcp_server->start_ip;
> +     for (; addr <= dhcp_server->end_ip; addr++) {
> +             /* ie, 192.168.55.0 */
> +             if ((addr & 0xff) == 0)
> +                     continue;
> +
> +             /* ie, 192.168.55.255 */
> +             if ((addr & 0xff) == 0xff)
> +                     continue;
> +
> +             lease = find_lease_by_nip(dhcp_server, (uint32_t) htonl(addr));
> +             if (lease != NULL)
> +                     continue;
> +
> +             if (nobody_responds_to_arp(htonl(addr), safe_mac) == TRUE)
> +                     return htonl(addr);
> +     }
> +
> +     /* The last lease is the oldest one */
> +     list = g_list_last(dhcp_server->lease_list);
> +     if (list == NULL)
> +             return 0;
> +
> +     lease = list->data;
> +     if (lease == NULL)
> +             return 0;
> +
> +      if (is_expired_lease(lease) == FALSE)
> +             return 0;
> +
> +      if (nobody_responds_to_arp(lease->lease_nip, safe_mac) == FALSE)
> +             return 0;
> +
> +     return lease->lease_nip;
> +}
> +
> +static void lease_set_expire(GDHCPServer *dhcp_server, struct dhcp_lease
> *lease, uint32_t expire)
> +{
> +     dhcp_server->lease_list = g_list_remove(dhcp_server->lease_list, lease);
> +
> +     lease->expire = expire;
> +
> +     dhcp_server->lease_list = g_list_insert_sorted(dhcp_server->lease_list,
> +                                                     lease, compare_expire);
> +}
> +
> +static void distroy_lease_table(GDHCPServer *dhcp_server)
> +{
> +     GList *list;
> +
> +     g_hash_table_destroy(dhcp_server->nip_lease_hash);
> +
> +     dhcp_server->nip_lease_hash = NULL;
> +
> +     for (list = dhcp_server->lease_list; list; list = list->next) {
> +             struct dhcp_lease *lease = list->data;
> +
> +             g_free(lease);
> +     }
> +
> +     g_list_free(dhcp_server->lease_list);
> +
> +     dhcp_server->lease_list = NULL;
> +}
> +static uint32_t get_interface_address(int index)
> +{
> +     struct ifreq ifr;
> +     int sk, err;
> +     struct sockaddr_in *server_ip;
> +     uint32_t ret = 0;
> +
> +     sk = socket(PF_INET, SOCK_DGRAM, 0);
> +     if (sk < 0) {
> +             perror("Open socket error");
> +             return 0;
> +     }
> +
> +     memset(&ifr, 0, sizeof(ifr));
> +     ifr.ifr_ifindex = index;
> +
> +     err = ioctl(sk, SIOCGIFNAME, &ifr);
> +     if (err < 0) {
> +             perror("Get interface name error");
> +             goto done;
> +     }
> +
> +     err = ioctl(sk, SIOCGIFADDR, &ifr);
> +     if (err < 0) {
> +             perror("Get ip address error");
> +             goto done;
> +     }
> +
> +     server_ip = (struct sockaddr_in *) &ifr.ifr_addr;
> +     ret = server_ip->sin_addr.s_addr;
> +
> +done:
> +     close(sk);
> +
> +     return ret;
> +}
> +
> +GDHCPServer *g_dhcp_server_new(GDHCPType type,
> +             int ifindex, GDHCPServerError *error)
> +{
> +     GDHCPServer *dhcp_server = NULL;
> +
> +     DHCP_DBG("ifindex %d", ifindex);
> +
> +     if (ifindex < 0) {
> +             *error = G_DHCP_SERVER_ERROR_INVALID_INDEX;
> +             return NULL;
> +     }
> +
> +     dhcp_server = g_try_new0(GDHCPServer, 1);
> +     if (dhcp_server == NULL) {
> +             *error = G_DHCP_SERVER_ERROR_NOMEM;
> +             return NULL;
> +     }
> +
> +     dhcp_server->interface = get_interface_name(ifindex);
> +     if (dhcp_server->interface == NULL) {
> +             *error = G_DHCP_SERVER_ERROR_INTERFACE_UNAVAILABLE;
> +             goto error;
> +     }
> +
> +     DHCP_DBG("interface %s", dhcp_server->interface);
> +
> +     if (interface_is_up(ifindex) == FALSE) {
> +             *error = G_DHCP_SERVER_ERROR_INTERFACE_DOWN;
> +             goto error;
> +     }
> +
> +     dhcp_server->server_nip = get_interface_address(ifindex);
> +     if (dhcp_server->server_nip == 0) {
> +             *error = G_DHCP_SERVER_ERROR_IP_ADDRESS_INVALID;
> +             goto error;
> +     }
> +
> +     dhcp_server->nip_lease_hash = g_hash_table_new_full(g_direct_hash,
> +                                             g_direct_equal, NULL, NULL);
> +     dhcp_server->option_hash = g_hash_table_new_full(g_direct_hash,
> +                                             g_direct_equal, NULL, g_free);
> +//   get_interface_mac_address(ifindex, dhcp_server->mac_address);
> +
> +     dhcp_server->started = FALSE;
> +
> +     /* All the leases have the same fixed lease time,
> +      * do not support DHCP_LEASE_TIME option from client.
> +      */
> +     dhcp_server->lease_seconds = DHCP_LEASE_SEC;
> +
> +     dhcp_server->type = type;
> +     dhcp_server->ref_count = 1;
> +     dhcp_server->ifindex = ifindex;
> +     dhcp_server->listener_sockfd = -1;
> +     dhcp_server->listener_watch = -1;
> +     dhcp_server->listener_channel = NULL;
> +     dhcp_server->save_lease_func = NULL;
> +     dhcp_server->debug_func = NULL;
> +     dhcp_server->debug_data = NULL;
> +
> +     *error = G_DHCP_SERVER_ERROR_NONE;
> +
> +     return dhcp_server;
> +
> +error:
> +     g_free(dhcp_server->interface);
> +     g_free(dhcp_server);
> +     return NULL;
> +}
> +
> +
> +static uint8_t check_packet_type(struct dhcp_packet *packet)
> +{
> +     uint8_t *type;
> +
> +     if (packet->hlen != 6)
> +             return 0;
> +
> +     if (packet->op != BOOTREQUEST)
> +             return 0;
> +
> +     type = dhcp_get_option(packet, DHCP_MESSAGE_TYPE);
> +
> +     if (type == NULL)
> +             return 0;
> +
> +     if (*type < DHCP_MINTYPE)
> +             return 0;
> +
> +     if (*type > DHCP_MAXTYPE)
> +             return 0;
> +
> +     return *type;
> +}
> +
> +static void init_packet(GDHCPServer *dhcp_server, struct dhcp_packet
> *packet,
> +                             struct dhcp_packet *old_packet, char type)
> +{
> +     /* Sets op, htype, hlen, cookie fields
> +      * and adds DHCP_MESSAGE_TYPE option */
> +     DHCP_DBG("");
> +     dhcp_init_header(packet, type);
> +
> +     packet->xid = old_packet->xid;
> +     memcpy(packet->chaddr, old_packet->chaddr,
> sizeof(old_packet->chaddr));
> +     packet->flags = old_packet->flags;
> +     packet->gateway_nip = old_packet->gateway_nip;
> +     packet->ciaddr = old_packet->ciaddr;
> +     dhcp_add_simple_option(packet, DHCP_SERVER_ID,
> dhcp_server->server_nip);
> +}
> +
> +static void add_option(gpointer key, gpointer value, gpointer user_data)
> +{
> +     const char *option_value = value;
> +     uint8_t option_code = GPOINTER_TO_INT(key);
> +     struct in_addr nip;
> +     struct dhcp_packet *packet = user_data;
> +
> +     DHCP_DBG("");
> +
> +     if (option_value == NULL)
> +             return;
> +
> +     DHCP_DBG("option_code %d, option_value %s", option_code,
> option_value);
> +
> +     switch (option_code) {
> +     case G_DHCP_SUBNET:
> +     case G_DHCP_ROUTER:
> +             if (inet_aton(option_value, &nip) == 0)
> +                     return;
> +
> +             dhcp_add_simple_option(packet, (uint8_t) option_code,
> +                                                             nip.s_addr);
> +             break;
> +     default:
> +             return;
> +     }
> +}
> +
> +static void add_server_options(GDHCPServer *dhcp_server,
> +                             struct dhcp_packet *packet)
> +{
> +     DHCP_DBG("");
> +     g_hash_table_foreach(dhcp_server->option_hash,
> +                             add_option, packet);
> +}
> +
> +static gboolean check_requested_nip(GDHCPServer *dhcp_server,
> +                                     uint32_t requested_nip)
> +{
> +     struct dhcp_lease *lease;
> +
> +     if (requested_nip == 0)
> +             return FALSE;
> +
> +     if (ntohl(requested_nip) < dhcp_server->start_ip)
> +             return FALSE;
> +
> +     if (ntohl(requested_nip) > dhcp_server->end_ip)
> +             return FALSE;
> +
> +     lease = find_lease_by_nip(dhcp_server, requested_nip);
> +     if (lease == NULL)
> +             return TRUE;
> +
> +     if (is_expired_lease(lease) == FALSE)
> +             return FALSE;
> +
> +     return TRUE;
> +}
> +
> +static void send_packet_to_client(GDHCPServer *dhcp_server,
> +                             struct dhcp_packet *dhcp_pkt)
> +{
> +     const uint8_t *chaddr;
> +     uint32_t ciaddr;
> +
> +     if ((dhcp_pkt->flags & htons(BROADCAST_FLAG))
> +                             || dhcp_pkt->ciaddr == 0) {
> +             DHCP_DBG("Broadcasting packet to client");
> +             ciaddr = INADDR_BROADCAST;
> +             chaddr = MAC_BCAST_ADDR;
> +     } else {
> +             DHCP_DBG("Unicasting packet to client ciaddr");
> +             ciaddr = dhcp_pkt->ciaddr;
> +             chaddr = dhcp_pkt->chaddr;
> +     }
> +
> +     dhcp_send_raw_packet(dhcp_pkt,
> +             dhcp_server->server_nip, SERVER_PORT,
> +             ciaddr, CLIENT_PORT, chaddr,
> +             dhcp_server->ifindex);
> +}
> +
> +static void send_offer(GDHCPServer *dhcp_server,
> +                     struct dhcp_packet *old_packet,
> +                             struct dhcp_lease *lease,
> +                                     uint32_t requested_nip)
> +{
> +     struct dhcp_packet packet;
> +     struct in_addr addr;
> +
> +     DHCP_DBG("");
> +     init_packet(dhcp_server, &packet, old_packet, DHCPOFFER);
> +
> +     if (lease)
> +             packet.yiaddr = lease->lease_nip;
> +     else if (check_requested_nip(dhcp_server, requested_nip) == TRUE)
> +             packet.yiaddr = requested_nip;
> +     else
> +             packet.yiaddr = find_free_or_expired_nip(
> +                             dhcp_server, old_packet->chaddr);
> +
> +     DHCP_DBG("find yiaddr %u", packet.yiaddr);
> +
> +     if (!packet.yiaddr) {
> +             DHCP_DBG("Err: Can not found lease and send offer");
> +             return;
> +     }
> +
> +     lease = add_lease(dhcp_server, 0, packet.chaddr, packet.yiaddr);
> +     if (lease == NULL) {
> +             DHCP_DBG("Err: No free IP addresses. OFFER abandoned");
> +             return;
> +     }
> +
> +     dhcp_add_simple_option(&packet, DHCP_LEASE_TIME,
> +                             htonl(dhcp_server->lease_seconds));
> +     add_server_options(dhcp_server, &packet);
> +
> +     addr.s_addr = packet.yiaddr;
> +
> +     DHCP_DBG("Sending OFFER of %s", inet_ntoa(addr));
> +     send_packet_to_client(dhcp_server, &packet);
> +}
> +
> +static void save_lease(GDHCPServer *dhcp_server)
> +{
> +     GList *list;
> +
> +     if (dhcp_server->save_lease_func == NULL)
> +             return;
> +
> +     for (list = dhcp_server->lease_list; list; list = list->next) {
> +             struct dhcp_lease *lease = list->data;
> +             dhcp_server->save_lease_func(lease->lease_mac,
> +                                     lease->lease_nip, lease->expire);
> +     }
> +}
> +
> +static void send_ACK(GDHCPServer *dhcp_server,
> +             struct dhcp_packet *old_packet, uint32_t yiaddr)
> +{
> +     struct dhcp_packet packet;
> +     uint32_t lease_time_sec;
> +     struct in_addr addr;
> +
> +     DHCP_DBG("");
> +     init_packet(dhcp_server, &packet, old_packet, DHCPACK);
> +     packet.yiaddr = yiaddr;
> +
> +     lease_time_sec = dhcp_server->lease_seconds;
> +
> +     dhcp_add_simple_option(&packet, DHCP_LEASE_TIME,
> htonl(lease_time_sec));
> +
> +     add_server_options(dhcp_server, &packet);
> +
> +     addr.s_addr = yiaddr;
> +
> +     DHCP_DBG("Sending ACK to %s", inet_ntoa(addr));
> +
> +     send_packet_to_client(dhcp_server, &packet);
> +
> +     add_lease(dhcp_server, 0, packet.chaddr, packet.yiaddr);
> +}
> +
> +static void send_NAK(GDHCPServer *dhcp_server, struct dhcp_packet
> *old_packet)
> +{
> +     struct dhcp_packet packet;
> +
> +     init_packet(dhcp_server, &packet, old_packet, DHCPNAK);
> +
> +     DHCP_DBG("Sending NAK");
> +
> +     dhcp_send_raw_packet(&packet,
> +                     dhcp_server->server_nip, SERVER_PORT,
> +                     INADDR_BROADCAST, CLIENT_PORT, MAC_BCAST_ADDR,
> +                     dhcp_server->ifindex);
> +}
> +
> +static void send_inform(GDHCPServer *dhcp_server,
> +                             struct dhcp_packet *old_packet)
> +{
> +     struct dhcp_packet packet;
> +
> +     init_packet(dhcp_server, &packet, old_packet, DHCPACK);
> +     add_server_options(dhcp_server, &packet);
> +     send_packet_to_client(dhcp_server, &packet);
> +}
> +
> +static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
> +                                                     gpointer user_data)
> +{
> +     GDHCPServer *dhcp_server = user_data;
> +     struct dhcp_packet packet;
> +     struct dhcp_lease *lease;
> +     uint32_t requested_nip = 0;
> +     uint8_t type, *server_id_option, *request_ip_option;
> +     int re;
> +
> +     if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
> +             dhcp_server->listener_watch = 0;
> +             return FALSE;
> +     }
> +
> +     re = dhcp_recv_l3_packet(&packet, dhcp_server->listener_sockfd);
> +     if (re < 0)
> +             return TRUE;
> +
> +     type = check_packet_type(&packet);
> +     if (type == 0)
> +             return TRUE;
> +
> +     server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID);
> +     if (server_id_option) {
> +             uint32_t server_nid = dhcp_get_unaligned(
> +                                     (uint32_t *) server_id_option);
> +
> +             if (server_nid != dhcp_server->server_nip)
> +                     return TRUE;
> +     }
> +
> +     request_ip_option = dhcp_get_option(&packet, DHCP_REQUESTED_IP);
> +     if (request_ip_option)
> +             requested_nip = dhcp_get_unaligned(
> +                                     (uint32_t *) request_ip_option);
> +
> +     lease = find_lease_by_mac(dhcp_server, packet.chaddr);
> +
> +     switch (type) {
> +             case DHCPDISCOVER:
> +                     DHCP_DBG("Received DISCOVER");
> +
> +                     send_offer(dhcp_server, &packet, lease, requested_nip);
> +                     break;
> +
> +             case DHCPREQUEST:
> +                     DHCP_DBG("Received REQUEST");
> +                     if (requested_nip == 0) {
> +                             requested_nip = packet.ciaddr;
> +                             if (requested_nip == 0)
> +                                     break;
> +                     }
> +
> +                     if (lease && requested_nip == lease->lease_nip) {
> +                             send_ACK(dhcp_server, &packet, 
> lease->lease_nip);
> +                             break;
> +                     }
> +
> +                     if (server_id_option)
> +                             send_NAK(dhcp_server, &packet);
> +
> +                     break;
> +
> +             case DHCPDECLINE:
> +                     DHCP_DBG("Received DECLINE");
> +
> +                     if (server_id_option == NULL)
> +                             break;
> +
> +                     if (request_ip_option == NULL)
> +                             break;
> +
> +                     if (lease == NULL)
> +                             break;
> +
> +                     if (requested_nip == lease->lease_nip)
> +                             remove_lease(dhcp_server, lease);
> +
> +                     break;
> +             case DHCPRELEASE:
> +                     DHCP_DBG("Received RELEASE");
> +
> +                     if (server_id_option == NULL)
> +                             break;
> +
> +                     if (lease == NULL)
> +                             break;
> +
> +                     if (packet.ciaddr == lease->lease_nip)
> +                             lease_set_expire(dhcp_server, lease,
> +                                                             time(NULL));
> +                     break;
> +             case DHCPINFORM:
> +                     DHCP_DBG("Received INFORM");
> +                     send_inform(dhcp_server, &packet);
> +                     break;
> +     }
> +
> +     return TRUE;
> +}
> +
> +/* Caller need to load leases before call it */
> +int g_dhcp_server_start(GDHCPServer *dhcp_server)
> +{
> +     GIOChannel *listener_channel;
> +     int listener_sockfd;
> +
> +     if (dhcp_server->started == TRUE)
> +             return 0;
> +
> +     listener_sockfd = dhcp_l3_socket(SERVER_PORT,
> +                             dhcp_server->interface);
> +     if (listener_sockfd < 0)
> +             return -EIO;
> +
> +     listener_channel = g_io_channel_unix_new(listener_sockfd);
> +     if (listener_channel == NULL) {
> +             close(listener_sockfd);
> +             return -EIO;
> +     }
> +
> +     dhcp_server->listener_sockfd = listener_sockfd;
> +     dhcp_server->listener_channel = listener_channel;
> +
> +     g_io_channel_set_close_on_unref(listener_channel, TRUE);
> +     dhcp_server->listener_watch =
> +                     g_io_add_watch_full(listener_channel,
> +                                             G_PRIORITY_HIGH, G_IO_IN,
> +                                             listener_event, dhcp_server,
> +                                                             NULL);
> +     g_io_channel_unref(dhcp_server->listener_channel);
> +
> +     dhcp_server->started = TRUE;
> +
> +     return 0;
> +}
> +
> +int g_dhcp_server_set_option(GDHCPServer *dhcp_server,
> +             unsigned char option_code, const char *option_value)
> +{
> +     const char *hash_option_value;
> +     struct in_addr nip;
> +
> +     if (option_value == NULL)
> +             return -EINVAL;
> +
> +     DHCP_DBG("option_code %d option_value %s", option_code,
> option_value);
> +
> +     switch (option_code) {
> +     case G_DHCP_SUBNET:
> +     case G_DHCP_ROUTER:
> +             if (inet_aton(option_value, &nip) == 0)
> +                     return -ENXIO;
> +             break;
> +     default:
> +             return -EINVAL;
> +     }
> +
> +     /* Remove the exist option */
> +     hash_option_value = g_hash_table_lookup(dhcp_server->option_hash,
> +                                     GINT_TO_POINTER((int) option_code));
> +     if (hash_option_value != NULL)
> +             g_hash_table_remove(dhcp_server->option_hash,
> +                                     GINT_TO_POINTER((int) option_code));
> +
> +     g_hash_table_insert(dhcp_server->option_hash,
> +                     GINT_TO_POINTER((int) option_code),
> +                                     (gpointer) option_value);
> +     return 0;
> +}
> +
> +void g_dhcp_server_set_save_lease(GDHCPServer *dhcp_server,
> +                             GDHCPSaveLeaseFunc func, gpointer user_data)
> +{
> +     if (dhcp_server == NULL)
> +             return;
> +
> +     dhcp_server->save_lease_func = func;
> +}
> +
> +GDHCPServer *g_dhcp_server_ref(GDHCPServer *dhcp_server)
> +{
> +     if (dhcp_server == NULL)
> +             return NULL;
> +
> +     g_atomic_int_inc(&dhcp_server->ref_count);
> +
> +     return dhcp_server;
> +}
> +
> +void g_dhcp_server_stop(GDHCPServer *dhcp_server)
> +{
> +     /* Save leases, before stop; load them before start*/
> +     save_lease(dhcp_server);
> +
> +     if (dhcp_server->listener_watch > 0) {
> +             g_source_remove(dhcp_server->listener_watch);
> +             dhcp_server->listener_watch = 0;
> +     }
> +
> +     dhcp_server->listener_channel = NULL;
> +
> +     dhcp_server->started = FALSE;
> +}
> +
> +void g_dhcp_server_unref(GDHCPServer *dhcp_server)
> +{
> +     if (dhcp_server == NULL)
> +             return;
> +
> +     if (g_atomic_int_dec_and_test(&dhcp_server->ref_count) == FALSE)
> +             return;
> +
> +     g_dhcp_server_stop(dhcp_server);
> +
> +     g_hash_table_destroy(dhcp_server->option_hash);
> +
> +     distroy_lease_table(dhcp_server);
> +
> +     g_free(dhcp_server->interface);
> +
> +     g_free(dhcp_server);
> +}
> +
> +void g_dhcp_server_load_lease(GDHCPServer *dhcp_server, unsigned int
> expire,
> +                                     unsigned char *mac, unsigned int 
> lease_ip)
> +{
> +     add_lease(dhcp_server, expire, mac, lease_ip);
> +}
> +
> +int g_dhcp_server_set_ip_area(GDHCPServer *dhcp_server,
> +             const char *start_ip, const char *end_ip)
> +{
> +     struct in_addr _host_addr;
> +
> +     if (inet_aton(start_ip, &_host_addr) == 0)
> +             return -ENXIO;
> +
> +     dhcp_server->start_ip = ntohl(_host_addr.s_addr);
> +
> +     if (inet_aton(end_ip, &_host_addr) == 0)
> +             return -ENXIO;
> +
> +     dhcp_server->end_ip = ntohl(_host_addr.s_addr);
> +
> +     return 0;
> +}
> diff --git a/tools/dhcp-server-test.c b/tools/dhcp-server-test.c
> new file mode 100644
> index 0000000..a50faaf
> --- /dev/null
> +++ b/tools/dhcp-server-test.c
> @@ -0,0 +1,121 @@
> +/*
> + *
> + *  Connection Manager
> + *
> + *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
> + *
> + *  This program is free software; you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License version 2 as
> + *  published by the Free Software Foundation.
> + *
> + *  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.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with this program; if not, write to the Free Software
> + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
> USA
> + *
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <stdio.h>
> +#include <errno.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/ioctl.h>
> +#include <sys/socket.h>
> +#include <arpa/inet.h>
> +#include <net/route.h>
> +#include <net/ethernet.h>
> +#include <linux/if_arp.h>
> +
> +#include <gdhcp/gdhcp.h>
> +
> +static GMainLoop *main_loop;
> +
> +static void sig_term(int sig)
> +{
> +     g_main_loop_quit(main_loop);
> +}
> +
> +static void handle_error(GDHCPServerError error)
> +{
> +     switch (error) {
> +     case G_DHCP_SERVER_ERROR_NONE:
> +             printf("dhcp server ok\n");
> +             break;
> +     case G_DHCP_SERVER_ERROR_INTERFACE_UNAVAILABLE:
> +             printf("Interface unavailable\n");
> +             break;
> +     case G_DHCP_SERVER_ERROR_INTERFACE_IN_USE:
> +             printf("Interface in use\n");
> +             break;
> +     case G_DHCP_SERVER_ERROR_INTERFACE_DOWN:
> +             printf("Interface down\n");
> +             break;
> +     case G_DHCP_SERVER_ERROR_NOMEM:
> +             printf("No memory\n");
> +             break;
> +     case G_DHCP_SERVER_ERROR_INVALID_INDEX:
> +             printf("Invalid index\n");
> +             break;
> +     case G_DHCP_SERVER_ERROR_INVALID_OPTION:
> +             printf("Invalid option\n");
> +             break;
> +     case G_DHCP_SERVER_ERROR_IP_ADDRESS_INVALID:
> +             printf("Invalid address\n");
> +             break;
> +     }
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +     struct sigaction sa;
> +     GDHCPServerError error;
> +     GDHCPServer *dhcp_server;
> +     int index;
> +
> +     if (argc < 2) {
> +             printf("Usage: dhcp-server-test <interface index>\n");
> +             exit(0);
> +     }
> +
> +     index = atoi(argv[1]);
> +
> +     printf("Create DHCP server for interface %d\n", index);
> +
> +     dhcp_server = g_dhcp_server_new(G_DHCP_IPV4, index, &error);
> +     if (dhcp_server == NULL) {
> +             handle_error(error);
> +             exit(0);
> +     }
> +
> +     g_dhcp_server_set_option(dhcp_server, G_DHCP_SUBNET,
> "255.255.0.0");
> +     g_dhcp_server_set_option(dhcp_server, G_DHCP_ROUTER,
> "192.168.0.99");
> +     g_dhcp_server_set_ip_area(dhcp_server,"192.168.0.101",
> "192.168.0.150");
> +
> +     main_loop = g_main_loop_new(NULL, FALSE);
> +
> +     printf("Start DHCP Server operation\n");
> +
> +     g_dhcp_server_start(dhcp_server);
> +
> +     memset(&sa, 0, sizeof(sa));
> +     sa.sa_handler = sig_term;
> +     sigaction(SIGINT, &sa, NULL);
> +     sigaction(SIGTERM, &sa, NULL);
> +
> +     g_main_loop_run(main_loop);
> +
> +     g_dhcp_server_unref(dhcp_server);
> +
> +     g_main_loop_unref(main_loop);
> +
> +     return 0;
> +}
_______________________________________________
connman mailing list
[email protected]
http://lists.connman.net/listinfo/connman

Reply via email to