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