| /* |
| * |
| * DHCP Server library with GLib integration |
| * |
| * Copyright (C) 2009-2012 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 DEFAULT_DHCP_LEASE_SEC (8*60*60) |
| |
| /* 5 minutes */ |
| #define OFFER_TIME (5*60) |
| |
| struct _GDHCPServer { |
| int ref_count; |
| GDHCPType type; |
| bool started; |
| int ifindex; |
| char *interface; |
| uint32_t start_ip; |
| uint32_t end_ip; |
| uint32_t server_nip; /* our address in network byte order */ |
| 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; |
| GDHCPLeaseAddedCb lease_added_cb; |
| GDHCPDebugFunc debug_func; |
| gpointer debug_data; |
| }; |
| |
| struct dhcp_lease { |
| time_t expire; |
| uint32_t lease_nip; |
| uint8_t lease_mac[ETH_ALEN]; |
| }; |
| |
| static inline void debug(GDHCPServer *server, const char *format, ...) |
| { |
| char str[256]; |
| va_list ap; |
| |
| if (!server->debug_func) |
| return; |
| |
| va_start(ap, format); |
| |
| if (vsnprintf(str, sizeof(str), format, ap) > 0) |
| server->debug_func(str, server->debug_data); |
| |
| va_end(ap); |
| } |
| |
| static struct dhcp_lease *find_lease_by_mac(GDHCPServer *dhcp_server, |
| const uint8_t *mac) |
| { |
| GList *list; |
| |
| for (list = dhcp_server->lease_list; list; list = list->next) { |
| struct dhcp_lease *lease = list->data; |
| |
| if (memcmp(lease->lease_mac, mac, ETH_ALEN) == 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; |
| |
| 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, ETH_ALEN) == 0) |
| return -ENXIO; |
| |
| if (memcmp(mac, MAC_ANY_ADDR, ETH_ALEN) == 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) ntohl(yiaddr))); |
| debug(dhcp_server, "lease_mac %p lease_nip %p", lease_mac, lease_nip); |
| |
| if (lease_nip) { |
| 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) ntohl(yiaddr))); |
| |
| if (!lease_mac) |
| *lease = lease_nip; |
| else if (lease_nip != lease_mac) { |
| remove_lease(dhcp_server, lease_mac); |
| *lease = lease_nip; |
| } else |
| *lease = lease_nip; |
| |
| return 0; |
| } |
| |
| if (lease_mac) { |
| 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) |
| 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; |
| |
| ret = get_lease(dhcp_server, yiaddr, chaddr, &lease); |
| if (ret != 0) |
| return NULL; |
| |
| memset(lease, 0, sizeof(*lease)); |
| |
| memcpy(lease->lease_mac, chaddr, ETH_ALEN); |
| lease->lease_nip = ntohl(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); |
| |
| if (dhcp_server->lease_added_cb) |
| dhcp_server->lease_added_cb(lease->lease_mac, yiaddr); |
| |
| 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 bool arp_check(uint32_t nip, const uint8_t *safe_mac) |
| { |
| /* TODO: Add ARP checking */ |
| return true; |
| } |
| |
| static bool is_expired_lease(struct dhcp_lease *lease) |
| { |
| if (lease->expire < time(NULL)) |
| return true; |
| |
| return false; |
| } |
| |
| static uint32_t find_free_or_expired_nip(GDHCPServer *dhcp_server, |
| const uint8_t *safe_mac) |
| { |
| uint32_t ip_addr; |
| struct dhcp_lease *lease; |
| GList *list; |
| ip_addr = dhcp_server->start_ip; |
| for (; ip_addr <= dhcp_server->end_ip; ip_addr++) { |
| /* e.g. 192.168.55.0 */ |
| if ((ip_addr & 0xff) == 0) |
| continue; |
| |
| /* e.g. 192.168.55.255 */ |
| if ((ip_addr & 0xff) == 0xff) |
| continue; |
| |
| lease = find_lease_by_nip(dhcp_server, ip_addr); |
| if (lease) |
| continue; |
| |
| if (arp_check(htonl(ip_addr), safe_mac)) |
| return ip_addr; |
| } |
| |
| /* The last lease is the oldest one */ |
| list = g_list_last(dhcp_server->lease_list); |
| if (!list) |
| return 0; |
| |
| lease = list->data; |
| if (!lease) |
| return 0; |
| |
| if (!is_expired_lease(lease)) |
| return 0; |
| |
| if (!arp_check(lease->lease_nip, safe_mac)) |
| 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 destroy_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 | SOCK_CLOEXEC, 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; |
| |
| if (ifindex < 0) { |
| *error = G_DHCP_SERVER_ERROR_INVALID_INDEX; |
| return NULL; |
| } |
| |
| dhcp_server = g_try_new0(GDHCPServer, 1); |
| if (!dhcp_server) { |
| *error = G_DHCP_SERVER_ERROR_NOMEM; |
| return NULL; |
| } |
| |
| dhcp_server->interface = get_interface_name(ifindex); |
| if (!dhcp_server->interface) { |
| *error = G_DHCP_SERVER_ERROR_INTERFACE_UNAVAILABLE; |
| goto error; |
| } |
| |
| if (!interface_is_up(ifindex)) { |
| *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, NULL); |
| |
| 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 = DEFAULT_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 != ETH_ALEN) |
| return 0; |
| |
| if (packet->op != BOOTREQUEST) |
| return 0; |
| |
| type = dhcp_get_option(packet, DHCP_MESSAGE_TYPE); |
| |
| if (!type) |
| 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 *client_packet, char type) |
| { |
| /* Sets op, htype, hlen, cookie fields |
| * and adds DHCP_MESSAGE_TYPE option */ |
| dhcp_init_header(packet, type); |
| |
| packet->xid = client_packet->xid; |
| memcpy(packet->chaddr, client_packet->chaddr, |
| sizeof(client_packet->chaddr)); |
| packet->flags = client_packet->flags; |
| packet->gateway_nip = client_packet->gateway_nip; |
| packet->ciaddr = client_packet->ciaddr; |
| dhcp_add_option_uint32(packet, DHCP_SERVER_ID, |
| ntohl(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; |
| |
| if (!option_value) |
| return; |
| |
| switch (option_code) { |
| case G_DHCP_SUBNET: |
| case G_DHCP_ROUTER: |
| case G_DHCP_DNS_SERVER: |
| if (inet_aton(option_value, &nip) == 0) |
| return; |
| |
| dhcp_add_option_uint32(packet, (uint8_t) option_code, |
| ntohl(nip.s_addr)); |
| break; |
| default: |
| return; |
| } |
| } |
| |
| static void add_server_options(GDHCPServer *dhcp_server, |
| struct dhcp_packet *packet) |
| { |
| g_hash_table_foreach(dhcp_server->option_hash, |
| add_option, packet); |
| } |
| |
| static bool check_requested_nip(GDHCPServer *dhcp_server, |
| uint32_t requested_nip) |
| { |
| struct dhcp_lease *lease; |
| |
| if (requested_nip == 0) |
| return false; |
| |
| if (requested_nip < dhcp_server->start_ip) |
| return false; |
| |
| if (requested_nip > dhcp_server->end_ip) |
| return false; |
| |
| lease = find_lease_by_nip(dhcp_server, requested_nip); |
| if (!lease) |
| return true; |
| |
| if (!is_expired_lease(lease)) |
| 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) { |
| debug(dhcp_server, "Broadcasting packet to client"); |
| ciaddr = INADDR_BROADCAST; |
| chaddr = MAC_BCAST_ADDR; |
| } else { |
| debug(dhcp_server, "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, false); |
| } |
| |
| static void send_offer(GDHCPServer *dhcp_server, |
| struct dhcp_packet *client_packet, |
| struct dhcp_lease *lease, |
| uint32_t requested_nip) |
| { |
| struct dhcp_packet packet; |
| struct in_addr addr; |
| |
| init_packet(dhcp_server, &packet, client_packet, DHCPOFFER); |
| |
| if (lease) |
| packet.yiaddr = htonl(lease->lease_nip); |
| else if (check_requested_nip(dhcp_server, requested_nip)) |
| packet.yiaddr = htonl(requested_nip); |
| else |
| packet.yiaddr = htonl(find_free_or_expired_nip( |
| dhcp_server, client_packet->chaddr)); |
| |
| debug(dhcp_server, "find yiaddr %u", packet.yiaddr); |
| |
| if (!packet.yiaddr) { |
| debug(dhcp_server, "Err: Can not found lease and send offer"); |
| return; |
| } |
| |
| lease = add_lease(dhcp_server, OFFER_TIME, |
| packet.chaddr, packet.yiaddr); |
| if (!lease) { |
| debug(dhcp_server, |
| "Err: No free IP addresses. OFFER abandoned"); |
| return; |
| } |
| |
| dhcp_add_option_uint32(&packet, DHCP_LEASE_TIME, |
| dhcp_server->lease_seconds); |
| add_server_options(dhcp_server, &packet); |
| |
| addr.s_addr = packet.yiaddr; |
| |
| debug(dhcp_server, "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) |
| 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 *client_packet, uint32_t dest) |
| { |
| struct dhcp_packet packet; |
| uint32_t lease_time_sec; |
| struct in_addr addr; |
| |
| init_packet(dhcp_server, &packet, client_packet, DHCPACK); |
| packet.yiaddr = htonl(dest); |
| |
| lease_time_sec = dhcp_server->lease_seconds; |
| |
| dhcp_add_option_uint32(&packet, DHCP_LEASE_TIME, lease_time_sec); |
| |
| add_server_options(dhcp_server, &packet); |
| |
| addr.s_addr = htonl(dest); |
| |
| debug(dhcp_server, "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 *client_packet) |
| { |
| struct dhcp_packet packet; |
| |
| init_packet(dhcp_server, &packet, client_packet, DHCPNAK); |
| |
| debug(dhcp_server, "Sending NAK"); |
| |
| dhcp_send_raw_packet(&packet, |
| dhcp_server->server_nip, SERVER_PORT, |
| INADDR_BROADCAST, CLIENT_PORT, MAC_BCAST_ADDR, |
| dhcp_server->ifindex, false); |
| } |
| |
| static void send_inform(GDHCPServer *dhcp_server, |
| struct dhcp_packet *client_packet) |
| { |
| struct dhcp_packet packet; |
| |
| init_packet(dhcp_server, &packet, client_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 = |
| get_unaligned((const 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 = get_be32(request_ip_option); |
| |
| lease = find_lease_by_mac(dhcp_server, packet.chaddr); |
| |
| switch (type) { |
| case DHCPDISCOVER: |
| debug(dhcp_server, "Received DISCOVER"); |
| |
| send_offer(dhcp_server, &packet, lease, requested_nip); |
| break; |
| case DHCPREQUEST: |
| debug(dhcp_server, "Received REQUEST NIP %d", |
| requested_nip); |
| if (requested_nip == 0) { |
| requested_nip = packet.ciaddr; |
| if (requested_nip == 0) |
| break; |
| } |
| |
| if (lease && requested_nip == lease->lease_nip) { |
| debug(dhcp_server, "Sending ACK"); |
| send_ACK(dhcp_server, &packet, |
| lease->lease_nip); |
| break; |
| } |
| |
| if (server_id_option || !lease) { |
| debug(dhcp_server, "Sending NAK"); |
| send_NAK(dhcp_server, &packet); |
| } |
| |
| break; |
| case DHCPDECLINE: |
| debug(dhcp_server, "Received DECLINE"); |
| |
| if (!server_id_option) |
| break; |
| |
| if (!request_ip_option) |
| break; |
| |
| if (!lease) |
| break; |
| |
| if (requested_nip == lease->lease_nip) |
| remove_lease(dhcp_server, lease); |
| |
| break; |
| case DHCPRELEASE: |
| debug(dhcp_server, "Received RELEASE"); |
| |
| if (!server_id_option) |
| break; |
| |
| if (!lease) |
| break; |
| |
| if (packet.ciaddr == lease->lease_nip) |
| lease_set_expire(dhcp_server, lease, |
| time(NULL)); |
| break; |
| case DHCPINFORM: |
| debug(dhcp_server, "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) |
| return 0; |
| |
| listener_sockfd = dhcp_l3_socket(SERVER_PORT, |
| dhcp_server->interface, AF_INET); |
| if (listener_sockfd < 0) |
| return -EIO; |
| |
| listener_channel = g_io_channel_unix_new(listener_sockfd); |
| if (!listener_channel) { |
| 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 | G_IO_NVAL | G_IO_ERR | G_IO_HUP, |
| 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) |
| { |
| struct in_addr nip; |
| |
| if (!option_value) |
| return -EINVAL; |
| |
| debug(dhcp_server, "option_code %d option_value %s", |
| option_code, option_value); |
| switch (option_code) { |
| case G_DHCP_SUBNET: |
| case G_DHCP_ROUTER: |
| case G_DHCP_DNS_SERVER: |
| if (inet_aton(option_value, &nip) == 0) |
| return -ENXIO; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| g_hash_table_replace(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) |
| return; |
| |
| dhcp_server->save_lease_func = func; |
| } |
| |
| void g_dhcp_server_set_lease_added_cb(GDHCPServer *dhcp_server, |
| GDHCPLeaseAddedCb cb) |
| { |
| if (!dhcp_server) |
| return; |
| |
| dhcp_server->lease_added_cb = cb; |
| } |
| |
| GDHCPServer *g_dhcp_server_ref(GDHCPServer *dhcp_server) |
| { |
| if (!dhcp_server) |
| return NULL; |
| |
| __sync_fetch_and_add(&dhcp_server->ref_count, 1); |
| |
| 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) |
| return; |
| |
| if (__sync_fetch_and_sub(&dhcp_server->ref_count, 1) != 1) |
| return; |
| |
| g_dhcp_server_stop(dhcp_server); |
| |
| g_hash_table_destroy(dhcp_server->option_hash); |
| |
| destroy_lease_table(dhcp_server); |
| |
| g_free(dhcp_server->interface); |
| |
| g_free(dhcp_server); |
| } |
| |
| int g_dhcp_server_set_ip_range(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; |
| } |
| |
| void g_dhcp_server_set_lease_time(GDHCPServer *dhcp_server, |
| unsigned int lease_time) |
| { |
| if (!dhcp_server) |
| return; |
| |
| dhcp_server->lease_seconds = lease_time; |
| } |
| |
| void g_dhcp_server_set_debug(GDHCPServer *dhcp_server, |
| GDHCPDebugFunc func, gpointer user_data) |
| { |
| if (!dhcp_server) |
| return; |
| |
| dhcp_server->debug_func = func; |
| dhcp_server->debug_data = user_data; |
| } |