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
