85405f193fe3bb0fb418cf3726cb465ed15116b6
[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         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 = 0;
400         dhcp_server->listener_channel = NULL;
401         dhcp_server->save_lease_func = NULL;
402         dhcp_server->debug_func = NULL;
403         dhcp_server->debug_data = NULL;
404
405         *error = G_DHCP_SERVER_ERROR_NONE;
406
407         return dhcp_server;
408
409 error:
410         g_free(dhcp_server->interface);
411         g_free(dhcp_server);
412         return NULL;
413 }
414
415
416 static uint8_t check_packet_type(struct dhcp_packet *packet)
417 {
418         uint8_t *type;
419
420         if (packet->hlen != ETH_ALEN)
421                 return 0;
422
423         if (packet->op != BOOTREQUEST)
424                 return 0;
425
426         type = dhcp_get_option(packet, DHCP_MESSAGE_TYPE);
427
428         if (!type)
429                 return 0;
430
431         if (*type < DHCP_MINTYPE)
432                 return 0;
433
434         if (*type > DHCP_MAXTYPE)
435                 return 0;
436
437         return *type;
438 }
439
440 static void init_packet(GDHCPServer *dhcp_server, struct dhcp_packet *packet,
441                                 struct dhcp_packet *client_packet, char type)
442 {
443         /* Sets op, htype, hlen, cookie fields
444          * and adds DHCP_MESSAGE_TYPE option */
445         dhcp_init_header(packet, type);
446
447         packet->xid = client_packet->xid;
448         memcpy(packet->chaddr, client_packet->chaddr,
449                                 sizeof(client_packet->chaddr));
450         packet->flags = client_packet->flags;
451         packet->gateway_nip = client_packet->gateway_nip;
452         packet->ciaddr = client_packet->ciaddr;
453         dhcp_add_option_uint32(packet, DHCP_SERVER_ID,
454                                         ntohl(dhcp_server->server_nip));
455 }
456
457 static void add_option(gpointer key, gpointer value, gpointer user_data)
458 {
459         const char *option_value = value;
460         uint8_t option_code = GPOINTER_TO_INT(key);
461         struct in_addr nip;
462         struct dhcp_packet *packet = user_data;
463
464         if (!option_value)
465                 return;
466
467         switch (option_code) {
468         case G_DHCP_SUBNET:
469         case G_DHCP_ROUTER:
470         case G_DHCP_DNS_SERVER:
471                 if (inet_aton(option_value, &nip) == 0)
472                         return;
473
474                 dhcp_add_option_uint32(packet, (uint8_t) option_code,
475                                                         ntohl(nip.s_addr));
476                 break;
477         default:
478                 return;
479         }
480 }
481
482 static void add_server_options(GDHCPServer *dhcp_server,
483                                 struct dhcp_packet *packet)
484 {
485         g_hash_table_foreach(dhcp_server->option_hash,
486                                 add_option, packet);
487 }
488
489 static bool check_requested_nip(GDHCPServer *dhcp_server,
490                                         uint32_t requested_nip)
491 {
492         struct dhcp_lease *lease;
493
494         if (requested_nip == 0)
495                 return false;
496
497         if (requested_nip < dhcp_server->start_ip)
498                 return false;
499
500         if (requested_nip > dhcp_server->end_ip)
501                 return false;
502
503         lease = find_lease_by_nip(dhcp_server, requested_nip);
504         if (!lease)
505                 return true;
506
507         if (!is_expired_lease(lease))
508                 return false;
509
510         return true;
511 }
512
513 static void send_packet_to_client(GDHCPServer *dhcp_server,
514                                 struct dhcp_packet *dhcp_pkt)
515 {
516         const uint8_t *chaddr;
517         uint32_t ciaddr;
518
519         if ((dhcp_pkt->flags & htons(BROADCAST_FLAG))
520                                 || dhcp_pkt->ciaddr == 0) {
521                 debug(dhcp_server, "Broadcasting packet to client");
522                 ciaddr = INADDR_BROADCAST;
523                 chaddr = MAC_BCAST_ADDR;
524         } else {
525                 debug(dhcp_server, "Unicasting packet to client ciaddr");
526                 ciaddr = dhcp_pkt->ciaddr;
527                 chaddr = dhcp_pkt->chaddr;
528         }
529
530         dhcp_send_raw_packet(dhcp_pkt,
531                 dhcp_server->server_nip, SERVER_PORT,
532                 ciaddr, CLIENT_PORT, chaddr,
533                 dhcp_server->ifindex, false);
534 }
535
536 static void send_offer(GDHCPServer *dhcp_server,
537                         struct dhcp_packet *client_packet,
538                                 struct dhcp_lease *lease,
539                                         uint32_t requested_nip)
540 {
541         struct dhcp_packet packet;
542         struct in_addr addr;
543
544         init_packet(dhcp_server, &packet, client_packet, DHCPOFFER);
545
546         if (lease)
547                 packet.yiaddr = htonl(lease->lease_nip);
548         else if (check_requested_nip(dhcp_server, requested_nip))
549                 packet.yiaddr = htonl(requested_nip);
550         else
551                 packet.yiaddr = htonl(find_free_or_expired_nip(
552                                         dhcp_server, client_packet->chaddr));
553
554         debug(dhcp_server, "find yiaddr %u", packet.yiaddr);
555
556         if (!packet.yiaddr) {
557                 debug(dhcp_server, "Err: Can not found lease and send offer");
558                 return;
559         }
560
561         lease = add_lease(dhcp_server, OFFER_TIME,
562                                 packet.chaddr, packet.yiaddr);
563         if (!lease) {
564                 debug(dhcp_server,
565                                 "Err: No free IP addresses. OFFER abandoned");
566                 return;
567         }
568
569         dhcp_add_option_uint32(&packet, DHCP_LEASE_TIME,
570                                                 dhcp_server->lease_seconds);
571         add_server_options(dhcp_server, &packet);
572
573         addr.s_addr = packet.yiaddr;
574
575         debug(dhcp_server, "Sending OFFER of %s", inet_ntoa(addr));
576         send_packet_to_client(dhcp_server, &packet);
577 }
578
579 static void save_lease(GDHCPServer *dhcp_server)
580 {
581         GList *list;
582
583         if (!dhcp_server->save_lease_func)
584                 return;
585
586         for (list = dhcp_server->lease_list; list; list = list->next) {
587                 struct dhcp_lease *lease = list->data;
588                 dhcp_server->save_lease_func(lease->lease_mac,
589                                         lease->lease_nip, lease->expire);
590         }
591 }
592
593 static void send_ACK(GDHCPServer *dhcp_server,
594                 struct dhcp_packet *client_packet, uint32_t dest)
595 {
596         struct dhcp_packet packet;
597         uint32_t lease_time_sec;
598         struct in_addr addr;
599
600         init_packet(dhcp_server, &packet, client_packet, DHCPACK);
601         packet.yiaddr = htonl(dest);
602
603         lease_time_sec = dhcp_server->lease_seconds;
604
605         dhcp_add_option_uint32(&packet, DHCP_LEASE_TIME, lease_time_sec);
606
607         add_server_options(dhcp_server, &packet);
608
609         addr.s_addr = htonl(dest);
610
611         debug(dhcp_server, "Sending ACK to %s", inet_ntoa(addr));
612
613         send_packet_to_client(dhcp_server, &packet);
614
615         add_lease(dhcp_server, 0, packet.chaddr, packet.yiaddr);
616
617         if (dhcp_server->lease_added_cb)
618                 dhcp_server->lease_added_cb(packet.chaddr, packet.yiaddr);
619 }
620
621 static void send_NAK(GDHCPServer *dhcp_server,
622                         struct dhcp_packet *client_packet)
623 {
624         struct dhcp_packet packet;
625
626         init_packet(dhcp_server, &packet, client_packet, DHCPNAK);
627
628         debug(dhcp_server, "Sending NAK");
629
630         dhcp_send_raw_packet(&packet,
631                         dhcp_server->server_nip, SERVER_PORT,
632                         INADDR_BROADCAST, CLIENT_PORT, MAC_BCAST_ADDR,
633                         dhcp_server->ifindex, false);
634 }
635
636 static void send_inform(GDHCPServer *dhcp_server,
637                                 struct dhcp_packet *client_packet)
638 {
639         struct dhcp_packet packet;
640
641         init_packet(dhcp_server, &packet, client_packet, DHCPACK);
642         add_server_options(dhcp_server, &packet);
643         send_packet_to_client(dhcp_server, &packet);
644 }
645
646 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
647                                                         gpointer user_data)
648 {
649         GDHCPServer *dhcp_server = user_data;
650         struct dhcp_packet packet;
651         struct dhcp_lease *lease;
652         uint32_t requested_nip = 0;
653         uint8_t type, *server_id_option, *request_ip_option;
654         int re;
655
656         if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
657                 dhcp_server->listener_watch = 0;
658                 return FALSE;
659         }
660
661         re = dhcp_recv_l3_packet(&packet, dhcp_server->listener_sockfd);
662         if (re < 0)
663                 return TRUE;
664
665         type = check_packet_type(&packet);
666         if (type == 0)
667                 return TRUE;
668
669         server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID);
670         if (server_id_option) {
671                 uint32_t server_nid =
672                         get_unaligned((const uint32_t *) 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 = ntohl(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                         send_ACK(dhcp_server, &packet,
702                                 lease->lease_nip);
703                         break;
704                 }
705
706                 if (server_id_option || !lease) {
707                         debug(dhcp_server, "Sending NAK");
708                         send_NAK(dhcp_server, &packet);
709                 }
710
711                 break;
712         case DHCPDECLINE:
713                 debug(dhcp_server, "Received DECLINE");
714
715                 if (!server_id_option)
716                         break;
717
718                 if (!request_ip_option)
719                         break;
720
721                 if (!lease)
722                         break;
723
724                 if (requested_nip == lease->lease_nip)
725                         remove_lease(dhcp_server, lease);
726
727                 break;
728         case DHCPRELEASE:
729                 debug(dhcp_server, "Received RELEASE");
730
731                 if (!server_id_option)
732                         break;
733
734                 if (!lease)
735                         break;
736
737                 if (packet.ciaddr == lease->lease_nip)
738                         lease_set_expire(dhcp_server, lease,
739                                         time(NULL));
740                 break;
741         case DHCPINFORM:
742                 debug(dhcp_server, "Received INFORM");
743                 send_inform(dhcp_server, &packet);
744                 break;
745         }
746
747         return TRUE;
748 }
749
750 /* Caller need to load leases before call it */
751 int g_dhcp_server_start(GDHCPServer *dhcp_server)
752 {
753         GIOChannel *listener_channel;
754         int listener_sockfd;
755
756         if (dhcp_server->started)
757                 return 0;
758
759         listener_sockfd = dhcp_l3_socket(SERVER_PORT,
760                                         dhcp_server->interface, AF_INET);
761         if (listener_sockfd < 0)
762                 return -EIO;
763
764         listener_channel = g_io_channel_unix_new(listener_sockfd);
765         if (!listener_channel) {
766                 close(listener_sockfd);
767                 return -EIO;
768         }
769
770         dhcp_server->listener_sockfd = listener_sockfd;
771         dhcp_server->listener_channel = listener_channel;
772
773         g_io_channel_set_close_on_unref(listener_channel, TRUE);
774         dhcp_server->listener_watch =
775                         g_io_add_watch_full(listener_channel, G_PRIORITY_HIGH,
776                                 G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
777                                                 listener_event, dhcp_server,
778                                                                 NULL);
779         g_io_channel_unref(dhcp_server->listener_channel);
780
781         dhcp_server->started = TRUE;
782
783         return 0;
784 }
785
786 int g_dhcp_server_set_option(GDHCPServer *dhcp_server,
787                 unsigned char option_code, const char *option_value)
788 {
789         struct in_addr nip;
790
791         if (!option_value)
792                 return -EINVAL;
793
794         debug(dhcp_server, "option_code %d option_value %s",
795                                         option_code, option_value);
796         switch (option_code) {
797         case G_DHCP_SUBNET:
798         case G_DHCP_ROUTER:
799         case G_DHCP_DNS_SERVER:
800                 if (inet_aton(option_value, &nip) == 0)
801                         return -ENXIO;
802                 break;
803         default:
804                 return -EINVAL;
805         }
806
807         g_hash_table_replace(dhcp_server->option_hash,
808                         GINT_TO_POINTER((int) option_code),
809                                         (gpointer) option_value);
810         return 0;
811 }
812
813 void g_dhcp_server_set_save_lease(GDHCPServer *dhcp_server,
814                                 GDHCPSaveLeaseFunc func, gpointer user_data)
815 {
816         if (!dhcp_server)
817                 return;
818
819         dhcp_server->save_lease_func = func;
820 }
821
822 void g_dhcp_server_set_lease_added_cb(GDHCPServer *dhcp_server,
823                                                         GDHCPLeaseAddedCb cb)
824 {
825         if (!dhcp_server)
826                 return;
827
828         dhcp_server->lease_added_cb = cb;
829 }
830
831 GDHCPServer *g_dhcp_server_ref(GDHCPServer *dhcp_server)
832 {
833         if (!dhcp_server)
834                 return NULL;
835
836         __sync_fetch_and_add(&dhcp_server->ref_count, 1);
837
838         return dhcp_server;
839 }
840
841 void g_dhcp_server_stop(GDHCPServer *dhcp_server)
842 {
843         /* Save leases, before stop; load them before start */
844         save_lease(dhcp_server);
845
846         if (dhcp_server->listener_watch > 0) {
847                 g_source_remove(dhcp_server->listener_watch);
848                 dhcp_server->listener_watch = 0;
849         }
850
851         dhcp_server->listener_channel = NULL;
852
853         dhcp_server->started = FALSE;
854 }
855
856 void g_dhcp_server_unref(GDHCPServer *dhcp_server)
857 {
858         if (!dhcp_server)
859                 return;
860
861         if (__sync_fetch_and_sub(&dhcp_server->ref_count, 1) != 1)
862                 return;
863
864         g_dhcp_server_stop(dhcp_server);
865
866         g_hash_table_destroy(dhcp_server->option_hash);
867
868         destroy_lease_table(dhcp_server);
869
870         g_free(dhcp_server->interface);
871
872         g_free(dhcp_server);
873 }
874
875 int g_dhcp_server_set_ip_range(GDHCPServer *dhcp_server,
876                 const char *start_ip, const char *end_ip)
877 {
878         struct in_addr _host_addr;
879
880         if (inet_aton(start_ip, &_host_addr) == 0)
881                 return -ENXIO;
882
883         dhcp_server->start_ip = ntohl(_host_addr.s_addr);
884
885         if (inet_aton(end_ip, &_host_addr) == 0)
886                 return -ENXIO;
887
888         dhcp_server->end_ip = ntohl(_host_addr.s_addr);
889
890         return 0;
891 }
892
893 void g_dhcp_server_set_lease_time(GDHCPServer *dhcp_server,
894                                         unsigned int lease_time)
895 {
896         if (!dhcp_server)
897                 return;
898
899         dhcp_server->lease_seconds = lease_time;
900 }
901
902 void g_dhcp_server_set_debug(GDHCPServer *dhcp_server,
903                                 GDHCPDebugFunc func, gpointer user_data)
904 {
905         if (!dhcp_server)
906                 return;
907
908         dhcp_server->debug_func = func;
909         dhcp_server->debug_data = user_data;
910 }