-/* 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