Merge "Modified logic to process each VSIE of all vendors." into tizen
[platform/upstream/connman.git] / gdhcp / server.c
1 /*
2  *
3  *  DHCP Server library with GLib integration
4  *
5  *  Copyright (C) 2009-2012  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/ioctl.h>
32 #include <arpa/inet.h>
33
34 #include <netpacket/packet.h>
35 #include <net/ethernet.h>
36 #include <net/if_arp.h>
37
38 #include <linux/if.h>
39 #include <linux/filter.h>
40
41 #include <glib.h>
42
43 #include "common.h"
44
45 /* 8 hours */
46 #define DEFAULT_DHCP_LEASE_SEC (8*60*60)
47
48 /* 5 minutes  */
49 #define OFFER_TIME (5*60)
50
51 struct _GDHCPServer {
52         int ref_count;
53         GDHCPType type;
54         bool started;
55         int ifindex;
56         char *interface;
57         uint32_t start_ip;
58         uint32_t end_ip;
59         uint32_t server_nip;    /* our address in network byte order */
60         uint32_t lease_seconds;
61         int listener_sockfd;
62         guint listener_watch;
63         GIOChannel *listener_channel;
64         GList *lease_list;
65         GHashTable *nip_lease_hash;
66         GHashTable *option_hash; /* Options send to client */
67         GDHCPSaveLeaseFunc save_lease_func;
68         GDHCPLeaseAddedCb lease_added_cb;
69         GDHCPSaveACKLeaseFunc save_ack_lease_func;
70         GDHCPDebugFunc debug_func;
71         gpointer debug_data;
72 };
73
74 struct dhcp_lease {
75         time_t expire;
76         uint32_t lease_nip;
77         uint8_t lease_mac[ETH_ALEN];
78 };
79
80 static inline void debug(GDHCPServer *server, const char *format, ...)
81 {
82         char str[256];
83         va_list ap;
84
85         if (!server->debug_func)
86                 return;
87
88         va_start(ap, format);
89
90         if (vsnprintf(str, sizeof(str), format, ap) > 0)
91                 server->debug_func(str, server->debug_data);
92
93         va_end(ap);
94 }
95
96 static struct dhcp_lease *find_lease_by_mac(GDHCPServer *dhcp_server,
97                                                 const uint8_t *mac)
98 {
99         GList *list;
100
101         for (list = dhcp_server->lease_list; list; list = list->next) {
102                 struct dhcp_lease *lease = list->data;
103
104                 if (memcmp(lease->lease_mac, mac, ETH_ALEN) == 0)
105                         return lease;
106         }
107
108         return NULL;
109 }
110
111 static void remove_lease(GDHCPServer *dhcp_server, struct dhcp_lease *lease)
112 {
113         dhcp_server->lease_list =
114                         g_list_remove(dhcp_server->lease_list, lease);
115
116         g_hash_table_remove(dhcp_server->nip_lease_hash,
117                                 GINT_TO_POINTER((int) lease->lease_nip));
118         g_free(lease);
119 }
120
121 /* Clear the old lease and create the new one */
122 static int get_lease(GDHCPServer *dhcp_server, uint32_t yiaddr,
123                                 const uint8_t *mac, struct dhcp_lease **lease)
124 {
125         struct dhcp_lease *lease_nip, *lease_mac;
126
127         if (yiaddr == 0)
128                 return -ENXIO;
129
130         if (ntohl(yiaddr) < dhcp_server->start_ip)
131                 return -ENXIO;
132
133         if (ntohl(yiaddr) > dhcp_server->end_ip)
134                 return -ENXIO;
135
136         if (memcmp(mac, MAC_BCAST_ADDR, ETH_ALEN) == 0)
137                 return -ENXIO;
138
139         if (memcmp(mac, MAC_ANY_ADDR, ETH_ALEN) == 0)
140                 return -ENXIO;
141
142         lease_mac = find_lease_by_mac(dhcp_server, mac);
143
144         lease_nip = g_hash_table_lookup(dhcp_server->nip_lease_hash,
145                                         GINT_TO_POINTER((int) ntohl(yiaddr)));
146         debug(dhcp_server, "lease_mac %p lease_nip %p", lease_mac, lease_nip);
147
148         if (lease_nip) {
149                 dhcp_server->lease_list =
150                                 g_list_remove(dhcp_server->lease_list,
151                                                                 lease_nip);
152                 g_hash_table_remove(dhcp_server->nip_lease_hash,
153                                 GINT_TO_POINTER((int) ntohl(yiaddr)));
154
155                 if (!lease_mac)
156                         *lease = lease_nip;
157                 else if (lease_nip != lease_mac) {
158                         remove_lease(dhcp_server, lease_mac);
159                         *lease = lease_nip;
160                 } else
161                         *lease = lease_nip;
162
163                 return 0;
164         }
165
166         if (lease_mac) {
167                 dhcp_server->lease_list =
168                                 g_list_remove(dhcp_server->lease_list,
169                                                                 lease_mac);
170                 g_hash_table_remove(dhcp_server->nip_lease_hash,
171                                 GINT_TO_POINTER((int) lease_mac->lease_nip));
172                 *lease = lease_mac;
173
174                 return 0;
175         }
176
177         *lease = g_try_new0(struct dhcp_lease, 1);
178         if (!*lease)
179                 return -ENOMEM;
180
181         return 0;
182 }
183
184 static gint compare_expire(gconstpointer a, gconstpointer b)
185 {
186         const struct dhcp_lease *lease1 = a;
187         const struct dhcp_lease *lease2 = b;
188
189         return lease2->expire - lease1->expire;
190 }
191
192 static struct dhcp_lease *add_lease(GDHCPServer *dhcp_server, uint32_t expire,
193                                         const uint8_t *chaddr, uint32_t yiaddr)
194 {
195         struct dhcp_lease *lease = NULL;
196         int ret;
197
198         ret = get_lease(dhcp_server, yiaddr, chaddr, &lease);
199         if (ret != 0)
200                 return NULL;
201
202         memset(lease, 0, sizeof(*lease));
203
204         memcpy(lease->lease_mac, chaddr, ETH_ALEN);
205         lease->lease_nip = ntohl(yiaddr);
206
207         if (expire == 0)
208                 lease->expire = time(NULL) + dhcp_server->lease_seconds;
209         else
210                 lease->expire = expire;
211
212         dhcp_server->lease_list = g_list_insert_sorted(dhcp_server->lease_list,
213                                                         lease, compare_expire);
214
215         g_hash_table_insert(dhcp_server->nip_lease_hash,
216                                 GINT_TO_POINTER((int) lease->lease_nip), lease);
217
218         if (dhcp_server->lease_added_cb)
219                 dhcp_server->lease_added_cb(lease->lease_mac, yiaddr);
220
221         return lease;
222 }
223
224 static struct dhcp_lease *find_lease_by_nip(GDHCPServer *dhcp_server,
225                                                                 uint32_t nip)
226 {
227         return g_hash_table_lookup(dhcp_server->nip_lease_hash,
228                                                 GINT_TO_POINTER((int) nip));
229 }
230
231 /* Check if the IP is taken; if it is, add it to the lease table */
232 static bool arp_check(uint32_t nip, const uint8_t *safe_mac)
233 {
234         /* TODO: Add ARP checking */
235         return true;
236 }
237
238 static bool is_expired_lease(struct dhcp_lease *lease)
239 {
240         if (lease->expire < time(NULL))
241                 return true;
242
243         return false;
244 }
245
246 static uint32_t find_free_or_expired_nip(GDHCPServer *dhcp_server,
247                                         const uint8_t *safe_mac)
248 {
249         uint32_t ip_addr;
250         struct dhcp_lease *lease;
251         GList *list;
252         ip_addr = dhcp_server->start_ip;
253         for (; ip_addr <= dhcp_server->end_ip; ip_addr++) {
254                 /* e.g. 192.168.55.0 */
255                 if ((ip_addr & 0xff) == 0)
256                         continue;
257
258                 /* e.g. 192.168.55.255 */
259                 if ((ip_addr & 0xff) == 0xff)
260                         continue;
261
262                 lease = find_lease_by_nip(dhcp_server, ip_addr);
263                 if (lease)
264                         continue;
265
266                 if (arp_check(htonl(ip_addr), safe_mac))
267                         return ip_addr;
268         }
269
270         /* The last lease is the oldest one */
271         list = g_list_last(dhcp_server->lease_list);
272         if (!list)
273                 return 0;
274
275         lease = list->data;
276         if (!lease)
277                 return 0;
278
279          if (!is_expired_lease(lease))
280                 return 0;
281
282          if (!arp_check(lease->lease_nip, safe_mac))
283                 return 0;
284
285         return lease->lease_nip;
286 }
287
288 static void lease_set_expire(GDHCPServer *dhcp_server,
289                         struct dhcp_lease *lease, uint32_t expire)
290 {
291         dhcp_server->lease_list = g_list_remove(dhcp_server->lease_list, lease);
292
293         lease->expire = expire;
294
295         dhcp_server->lease_list = g_list_insert_sorted(dhcp_server->lease_list,
296                                                         lease, compare_expire);
297 }
298
299 static void destroy_lease_table(GDHCPServer *dhcp_server)
300 {
301         GList *list;
302
303         g_hash_table_destroy(dhcp_server->nip_lease_hash);
304
305         dhcp_server->nip_lease_hash = NULL;
306
307         for (list = dhcp_server->lease_list; list; list = list->next) {
308                 struct dhcp_lease *lease = list->data;
309
310                 g_free(lease);
311         }
312
313         g_list_free(dhcp_server->lease_list);
314
315         dhcp_server->lease_list = NULL;
316 }
317 static uint32_t get_interface_address(int index)
318 {
319         struct ifreq ifr;
320         int sk, err;
321         struct sockaddr_in *server_ip;
322         uint32_t ret = 0;
323
324         sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
325         if (sk < 0) {
326                 perror("Open socket error");
327                 return 0;
328         }
329
330         memset(&ifr, 0, sizeof(ifr));
331         ifr.ifr_ifindex = index;
332
333         err = ioctl(sk, SIOCGIFNAME, &ifr);
334         if (err < 0) {
335                 perror("Get interface name error");
336                 goto done;
337         }
338
339         err = ioctl(sk, SIOCGIFADDR, &ifr);
340         if (err < 0) {
341                 perror("Get ip address error");
342                 goto done;
343         }
344
345         server_ip = (struct sockaddr_in *) &ifr.ifr_addr;
346         ret = server_ip->sin_addr.s_addr;
347
348 done:
349         close(sk);
350
351         return ret;
352 }
353
354 GDHCPServer *g_dhcp_server_new(GDHCPType type,
355                 int ifindex, GDHCPServerError *error)
356 {
357         GDHCPServer *dhcp_server = NULL;
358
359         if (ifindex < 0) {
360                 *error = G_DHCP_SERVER_ERROR_INVALID_INDEX;
361                 return NULL;
362         }
363
364         dhcp_server = g_try_new0(GDHCPServer, 1);
365         if (!dhcp_server) {
366                 *error = G_DHCP_SERVER_ERROR_NOMEM;
367                 return NULL;
368         }
369
370         dhcp_server->interface = get_interface_name(ifindex);
371         if (!dhcp_server->interface) {
372                 *error = G_DHCP_SERVER_ERROR_INTERFACE_UNAVAILABLE;
373                 goto error;
374         }
375
376         if (!interface_is_up(ifindex)) {
377                 *error = G_DHCP_SERVER_ERROR_INTERFACE_DOWN;
378                 goto error;
379         }
380
381         dhcp_server->server_nip = get_interface_address(ifindex);
382         if (dhcp_server->server_nip == 0) {
383                 *error = G_DHCP_SERVER_ERROR_IP_ADDRESS_INVALID;
384                 goto error;
385         }
386
387         dhcp_server->nip_lease_hash = g_hash_table_new_full(g_direct_hash,
388                                                 g_direct_equal, NULL, NULL);
389         dhcp_server->option_hash = g_hash_table_new_full(g_direct_hash,
390                                                 g_direct_equal, NULL, NULL);
391
392         dhcp_server->started = FALSE;
393
394         /* All the leases have the same fixed lease time,
395          * do not support DHCP_LEASE_TIME option from client.
396          */
397         dhcp_server->lease_seconds = DEFAULT_DHCP_LEASE_SEC;
398
399         dhcp_server->type = type;
400         dhcp_server->ref_count = 1;
401         dhcp_server->ifindex = ifindex;
402         dhcp_server->listener_sockfd = -1;
403         dhcp_server->listener_watch = -1;
404         dhcp_server->listener_channel = NULL;
405         dhcp_server->save_lease_func = NULL;
406         dhcp_server->save_ack_lease_func = NULL;
407         dhcp_server->debug_func = NULL;
408         dhcp_server->debug_data = NULL;
409
410         *error = G_DHCP_SERVER_ERROR_NONE;
411
412         return dhcp_server;
413
414 error:
415         g_free(dhcp_server->interface);
416         g_free(dhcp_server);
417         return NULL;
418 }
419
420
421 static uint8_t check_packet_type(struct dhcp_packet *packet)
422 {
423         uint8_t *type;
424
425         if (packet->hlen != ETH_ALEN)
426                 return 0;
427
428         if (packet->op != BOOTREQUEST)
429                 return 0;
430
431         type = dhcp_get_option(packet, DHCP_MESSAGE_TYPE);
432
433         if (!type)
434                 return 0;
435
436         if (*type < DHCP_MINTYPE)
437                 return 0;
438
439         if (*type > DHCP_MAXTYPE)
440                 return 0;
441
442         return *type;
443 }
444
445 static void init_packet(GDHCPServer *dhcp_server, struct dhcp_packet *packet,
446                                 struct dhcp_packet *client_packet, char type)
447 {
448         /* Sets op, htype, hlen, cookie fields
449          * and adds DHCP_MESSAGE_TYPE option */
450         dhcp_init_header(packet, type);
451
452         packet->xid = client_packet->xid;
453         memcpy(packet->chaddr, client_packet->chaddr,
454                                 sizeof(client_packet->chaddr));
455         packet->flags = client_packet->flags;
456         packet->gateway_nip = client_packet->gateway_nip;
457         packet->ciaddr = client_packet->ciaddr;
458         dhcp_add_option_uint32(packet, DHCP_SERVER_ID,
459                                         ntohl(dhcp_server->server_nip));
460 }
461
462 static void add_option(gpointer key, gpointer value, gpointer user_data)
463 {
464         const char *option_value = value;
465         uint8_t option_code = GPOINTER_TO_INT(key);
466         struct in_addr nip;
467         struct dhcp_packet *packet = user_data;
468
469         if (!option_value)
470                 return;
471
472         switch (option_code) {
473         case G_DHCP_SUBNET:
474         case G_DHCP_ROUTER:
475         case G_DHCP_DNS_SERVER:
476                 if (inet_aton(option_value, &nip) == 0)
477                         return;
478
479                 dhcp_add_option_uint32(packet, (uint8_t) option_code,
480                                                         ntohl(nip.s_addr));
481                 break;
482         default:
483                 return;
484         }
485 }
486
487 static void add_server_options(GDHCPServer *dhcp_server,
488                                 struct dhcp_packet *packet)
489 {
490         g_hash_table_foreach(dhcp_server->option_hash,
491                                 add_option, packet);
492 }
493
494 static bool check_requested_nip(GDHCPServer *dhcp_server,
495                                         uint32_t requested_nip)
496 {
497         struct dhcp_lease *lease;
498
499         if (requested_nip == 0)
500                 return false;
501
502         if (requested_nip < dhcp_server->start_ip)
503                 return false;
504
505         if (requested_nip > dhcp_server->end_ip)
506                 return false;
507
508         lease = find_lease_by_nip(dhcp_server, requested_nip);
509         if (!lease)
510                 return true;
511
512         if (!is_expired_lease(lease))
513                 return false;
514
515         return true;
516 }
517
518 static void send_packet_to_client(GDHCPServer *dhcp_server,
519                                 struct dhcp_packet *dhcp_pkt)
520 {
521         const uint8_t *chaddr;
522         uint32_t ciaddr;
523
524         if ((dhcp_pkt->flags & htons(BROADCAST_FLAG))
525                                 || dhcp_pkt->ciaddr == 0) {
526                 debug(dhcp_server, "Broadcasting packet to client");
527                 ciaddr = INADDR_BROADCAST;
528                 chaddr = MAC_BCAST_ADDR;
529         } else {
530                 debug(dhcp_server, "Unicasting packet to client ciaddr");
531                 ciaddr = dhcp_pkt->ciaddr;
532                 chaddr = dhcp_pkt->chaddr;
533         }
534
535         dhcp_send_raw_packet(dhcp_pkt,
536                 dhcp_server->server_nip, SERVER_PORT,
537                 ciaddr, CLIENT_PORT, chaddr,
538                 dhcp_server->ifindex, false);
539 }
540
541 static void send_offer(GDHCPServer *dhcp_server,
542                         struct dhcp_packet *client_packet,
543                                 struct dhcp_lease *lease,
544                                         uint32_t requested_nip)
545 {
546         struct dhcp_packet packet;
547         struct in_addr addr;
548
549         init_packet(dhcp_server, &packet, client_packet, DHCPOFFER);
550
551         if (lease)
552                 packet.yiaddr = htonl(lease->lease_nip);
553         else if (check_requested_nip(dhcp_server, requested_nip))
554                 packet.yiaddr = htonl(requested_nip);
555         else
556                 packet.yiaddr = htonl(find_free_or_expired_nip(
557                                         dhcp_server, client_packet->chaddr));
558
559         debug(dhcp_server, "find yiaddr %u", packet.yiaddr);
560
561         if (!packet.yiaddr) {
562                 debug(dhcp_server, "Err: Can not found lease and send offer");
563                 return;
564         }
565
566         lease = add_lease(dhcp_server, OFFER_TIME,
567                                 packet.chaddr, packet.yiaddr);
568         if (!lease) {
569                 debug(dhcp_server,
570                                 "Err: No free IP addresses. OFFER abandoned");
571                 return;
572         }
573
574         dhcp_add_option_uint32(&packet, DHCP_LEASE_TIME,
575                                                 dhcp_server->lease_seconds);
576         add_server_options(dhcp_server, &packet);
577
578         addr.s_addr = packet.yiaddr;
579
580         debug(dhcp_server, "Sending OFFER of %s", inet_ntoa(addr));
581         send_packet_to_client(dhcp_server, &packet);
582 }
583
584 static void save_lease(GDHCPServer *dhcp_server)
585 {
586         GList *list;
587
588         if (!dhcp_server->save_lease_func)
589                 return;
590
591         for (list = dhcp_server->lease_list; list; list = list->next) {
592                 struct dhcp_lease *lease = list->data;
593                 dhcp_server->save_lease_func(lease->lease_mac,
594                                         lease->lease_nip, lease->expire);
595         }
596 }
597
598 static void send_ACK(GDHCPServer *dhcp_server,
599                 struct dhcp_packet *client_packet, uint32_t dest)
600 {
601         struct dhcp_packet packet;
602         uint32_t lease_time_sec;
603         struct in_addr addr;
604
605         init_packet(dhcp_server, &packet, client_packet, DHCPACK);
606         packet.yiaddr = htonl(dest);
607
608         lease_time_sec = dhcp_server->lease_seconds;
609
610         dhcp_add_option_uint32(&packet, DHCP_LEASE_TIME, lease_time_sec);
611
612         add_server_options(dhcp_server, &packet);
613
614         addr.s_addr = htonl(dest);
615
616         debug(dhcp_server, "Sending ACK to %s", inet_ntoa(addr));
617
618         send_packet_to_client(dhcp_server, &packet);
619
620         add_lease(dhcp_server, 0, packet.chaddr, packet.yiaddr);
621 }
622
623 static void send_NAK(GDHCPServer *dhcp_server,
624                         struct dhcp_packet *client_packet)
625 {
626         struct dhcp_packet packet;
627
628         init_packet(dhcp_server, &packet, client_packet, DHCPNAK);
629
630         debug(dhcp_server, "Sending NAK");
631
632         dhcp_send_raw_packet(&packet,
633                         dhcp_server->server_nip, SERVER_PORT,
634                         INADDR_BROADCAST, CLIENT_PORT, MAC_BCAST_ADDR,
635                         dhcp_server->ifindex, false);
636 }
637
638 static void send_inform(GDHCPServer *dhcp_server,
639                                 struct dhcp_packet *client_packet)
640 {
641         struct dhcp_packet packet;
642
643         init_packet(dhcp_server, &packet, client_packet, DHCPACK);
644         add_server_options(dhcp_server, &packet);
645         send_packet_to_client(dhcp_server, &packet);
646 }
647
648 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
649                                                         gpointer user_data)
650 {
651         GDHCPServer *dhcp_server = user_data;
652         struct dhcp_packet packet;
653         struct dhcp_lease *lease;
654         uint32_t requested_nip = 0;
655         uint8_t type, *server_id_option, *request_ip_option, *host_name;
656         int re;
657
658         GDHCPOptionType option_type;
659         char *option_value;
660
661         if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
662                 dhcp_server->listener_watch = 0;
663                 return FALSE;
664         }
665
666         re = dhcp_recv_l3_packet(&packet, dhcp_server->listener_sockfd);
667         if (re < 0)
668                 return TRUE;
669
670         type = check_packet_type(&packet);
671         if (type == 0)
672                 return TRUE;
673
674         server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID);
675         if (server_id_option) {
676                 uint32_t server_nid =
677                         get_unaligned((const uint32_t *) server_id_option);
678
679                 if (server_nid != dhcp_server->server_nip)
680                         return TRUE;
681         }
682
683         request_ip_option = dhcp_get_option(&packet, DHCP_REQUESTED_IP);
684         if (request_ip_option)
685                 requested_nip = get_be32(request_ip_option);
686
687         lease = find_lease_by_mac(dhcp_server, packet.chaddr);
688
689         switch (type) {
690         case DHCPDISCOVER:
691                 debug(dhcp_server, "Received DISCOVER");
692
693                 send_offer(dhcp_server, &packet, lease, requested_nip);
694                 break;
695         case DHCPREQUEST:
696                 debug(dhcp_server, "Received REQUEST NIP %d",
697                                                         requested_nip);
698                 if (requested_nip == 0) {
699                         requested_nip = packet.ciaddr;
700                         if (requested_nip == 0)
701                                 break;
702                 }
703
704                 if (lease && requested_nip == lease->lease_nip) {
705                         debug(dhcp_server, "Sending ACK");
706
707                         host_name = dhcp_get_option(&packet, DHCP_HOST_NAME);
708                         option_type = dhcp_get_code_type(DHCP_HOST_NAME);
709                         option_value = malloc_option_value_string(host_name,
710                                                                 option_type);
711                         send_ACK(dhcp_server, &packet,
712                                 lease->lease_nip);
713
714                         if (dhcp_server->save_ack_lease_func)
715                                 dhcp_server->save_ack_lease_func(
716                                                         option_value,
717                                                         lease->lease_mac,
718                                                         lease->lease_nip);
719                         g_free(option_value);
720
721                         break;
722                 }
723
724                 if (server_id_option || !lease) {
725                         debug(dhcp_server, "Sending NAK");
726                         send_NAK(dhcp_server, &packet);
727                 }
728
729                 break;
730         case DHCPDECLINE:
731                 debug(dhcp_server, "Received DECLINE");
732
733                 if (!server_id_option)
734                         break;
735
736                 if (!request_ip_option)
737                         break;
738
739                 if (!lease)
740                         break;
741
742                 if (requested_nip == lease->lease_nip)
743                         remove_lease(dhcp_server, lease);
744
745                 break;
746         case DHCPRELEASE:
747                 debug(dhcp_server, "Received RELEASE");
748
749                 if (!server_id_option)
750                         break;
751
752                 if (!lease)
753                         break;
754
755                 if (packet.ciaddr == lease->lease_nip)
756                         lease_set_expire(dhcp_server, lease,
757                                         time(NULL));
758                 break;
759         case DHCPINFORM:
760                 debug(dhcp_server, "Received INFORM");
761                 send_inform(dhcp_server, &packet);
762                 break;
763         }
764
765         return TRUE;
766 }
767
768 /* Caller need to load leases before call it */
769 int g_dhcp_server_start(GDHCPServer *dhcp_server)
770 {
771         GIOChannel *listener_channel;
772         int listener_sockfd;
773
774         if (dhcp_server->started)
775                 return 0;
776
777         listener_sockfd = dhcp_l3_socket(SERVER_PORT,
778                                         dhcp_server->interface, AF_INET);
779         if (listener_sockfd < 0)
780                 return -EIO;
781
782         listener_channel = g_io_channel_unix_new(listener_sockfd);
783         if (!listener_channel) {
784                 close(listener_sockfd);
785                 return -EIO;
786         }
787
788         dhcp_server->listener_sockfd = listener_sockfd;
789         dhcp_server->listener_channel = listener_channel;
790
791         g_io_channel_set_close_on_unref(listener_channel, TRUE);
792         dhcp_server->listener_watch =
793                         g_io_add_watch_full(listener_channel, G_PRIORITY_HIGH,
794                                 G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
795                                                 listener_event, dhcp_server,
796                                                                 NULL);
797         g_io_channel_unref(dhcp_server->listener_channel);
798
799         dhcp_server->started = TRUE;
800
801         return 0;
802 }
803
804 int g_dhcp_server_set_option(GDHCPServer *dhcp_server,
805                 unsigned char option_code, const char *option_value)
806 {
807         struct in_addr nip;
808
809         if (!option_value)
810                 return -EINVAL;
811
812         debug(dhcp_server, "option_code %d option_value %s",
813                                         option_code, option_value);
814         switch (option_code) {
815         case G_DHCP_SUBNET:
816         case G_DHCP_ROUTER:
817         case G_DHCP_DNS_SERVER:
818                 if (inet_aton(option_value, &nip) == 0)
819                         return -ENXIO;
820                 break;
821         default:
822                 return -EINVAL;
823         }
824
825         g_hash_table_replace(dhcp_server->option_hash,
826                         GINT_TO_POINTER((int) option_code),
827                                         (gpointer) option_value);
828         return 0;
829 }
830
831 void g_dhcp_server_set_save_lease(GDHCPServer *dhcp_server,
832                                 GDHCPSaveLeaseFunc func, gpointer user_data)
833 {
834         if (!dhcp_server)
835                 return;
836
837         dhcp_server->save_lease_func = func;
838 }
839
840 void g_dhcp_server_set_lease_added_cb(GDHCPServer *dhcp_server,
841                                                         GDHCPLeaseAddedCb cb)
842 {
843         if (!dhcp_server)
844                 return;
845
846         dhcp_server->lease_added_cb = cb;
847 }
848
849 void g_dhcp_server_set_save_ack_lease(GDHCPServer *dhcp_server,
850                                 GDHCPSaveACKLeaseFunc func, gpointer user_data)
851 {
852         if (dhcp_server == NULL)
853                 return;
854
855         dhcp_server->save_ack_lease_func = func;
856 }
857
858 GDHCPServer *g_dhcp_server_ref(GDHCPServer *dhcp_server)
859 {
860         if (!dhcp_server)
861                 return NULL;
862
863         __sync_fetch_and_add(&dhcp_server->ref_count, 1);
864
865         return dhcp_server;
866 }
867
868 void g_dhcp_server_stop(GDHCPServer *dhcp_server)
869 {
870         /* Save leases, before stop; load them before start */
871         save_lease(dhcp_server);
872
873         if (dhcp_server->listener_watch > 0) {
874                 g_source_remove(dhcp_server->listener_watch);
875                 dhcp_server->listener_watch = 0;
876         }
877
878         dhcp_server->listener_channel = NULL;
879
880         dhcp_server->started = FALSE;
881 }
882
883 void g_dhcp_server_unref(GDHCPServer *dhcp_server)
884 {
885         if (!dhcp_server)
886                 return;
887
888         if (__sync_fetch_and_sub(&dhcp_server->ref_count, 1) != 1)
889                 return;
890
891         g_dhcp_server_stop(dhcp_server);
892
893         g_hash_table_destroy(dhcp_server->option_hash);
894
895         destroy_lease_table(dhcp_server);
896
897         g_free(dhcp_server->interface);
898
899         g_free(dhcp_server);
900 }
901
902 int g_dhcp_server_set_ip_range(GDHCPServer *dhcp_server,
903                 const char *start_ip, const char *end_ip)
904 {
905         struct in_addr _host_addr;
906
907         if (inet_aton(start_ip, &_host_addr) == 0)
908                 return -ENXIO;
909
910         dhcp_server->start_ip = ntohl(_host_addr.s_addr);
911
912         if (inet_aton(end_ip, &_host_addr) == 0)
913                 return -ENXIO;
914
915         dhcp_server->end_ip = ntohl(_host_addr.s_addr);
916
917         return 0;
918 }
919
920 void g_dhcp_server_set_lease_time(GDHCPServer *dhcp_server,
921                                         unsigned int lease_time)
922 {
923         if (!dhcp_server)
924                 return;
925
926         dhcp_server->lease_seconds = lease_time;
927 }
928
929 void g_dhcp_server_set_debug(GDHCPServer *dhcp_server,
930                                 GDHCPDebugFunc func, gpointer user_data)
931 {
932         if (!dhcp_server)
933                 return;
934
935         dhcp_server->debug_func = func;
936         dhcp_server->debug_data = user_data;
937 }