technology: return already enabled when tethering is enabled
[framework/connectivity/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         gboolean 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         GDHCPDebugFunc debug_func;
69         gpointer debug_data;
70 };
71
72 struct dhcp_lease {
73         time_t expire;
74         uint32_t lease_nip;
75         uint8_t lease_mac[ETH_ALEN];
76 };
77
78 static inline void debug(GDHCPServer *server, const char *format, ...)
79 {
80         char str[256];
81         va_list ap;
82
83         if (server->debug_func == NULL)
84                 return;
85
86         va_start(ap, format);
87
88         if (vsnprintf(str, sizeof(str), format, ap) > 0)
89                 server->debug_func(str, server->debug_data);
90
91         va_end(ap);
92 }
93
94 static struct dhcp_lease *find_lease_by_mac(GDHCPServer *dhcp_server,
95                                                 const uint8_t *mac)
96 {
97         GList *list;
98
99         for (list = dhcp_server->lease_list; list; list = list->next) {
100                 struct dhcp_lease *lease = list->data;
101
102                 if (memcmp(lease->lease_mac, mac, ETH_ALEN) == 0)
103                         return lease;
104         }
105
106         return NULL;
107 }
108
109 static void remove_lease(GDHCPServer *dhcp_server, struct dhcp_lease *lease)
110 {
111         dhcp_server->lease_list =
112                         g_list_remove(dhcp_server->lease_list, lease);
113
114         g_hash_table_remove(dhcp_server->nip_lease_hash,
115                                 GINT_TO_POINTER((int) lease->lease_nip));
116         g_free(lease);
117 }
118
119 /* Clear the old lease and create the new one */
120 static int get_lease(GDHCPServer *dhcp_server, uint32_t yiaddr,
121                                 const uint8_t *mac, struct dhcp_lease **lease)
122 {
123         struct dhcp_lease *lease_nip, *lease_mac;
124
125         if (yiaddr == 0)
126                 return -ENXIO;
127
128         if (ntohl(yiaddr) < dhcp_server->start_ip)
129                 return -ENXIO;
130
131         if (ntohl(yiaddr) > dhcp_server->end_ip)
132                 return -ENXIO;
133
134         if (memcmp(mac, MAC_BCAST_ADDR, ETH_ALEN) == 0)
135                 return -ENXIO;
136
137         if (memcmp(mac, MAC_ANY_ADDR, ETH_ALEN) == 0)
138                 return -ENXIO;
139
140         lease_mac = find_lease_by_mac(dhcp_server, mac);
141
142         lease_nip = g_hash_table_lookup(dhcp_server->nip_lease_hash,
143                                                 GINT_TO_POINTER((int) yiaddr));
144         debug(dhcp_server, "lease_mac %p lease_nip %p", lease_mac, lease_nip);
145
146         if (lease_nip != NULL) {
147                 dhcp_server->lease_list =
148                                 g_list_remove(dhcp_server->lease_list,
149                                                                 lease_nip);
150                 g_hash_table_remove(dhcp_server->nip_lease_hash,
151                                         GINT_TO_POINTER((int) yiaddr));
152
153                 if (lease_mac == NULL)
154                         *lease = lease_nip;
155                 else if (lease_nip != lease_mac) {
156                         remove_lease(dhcp_server, lease_mac);
157                         *lease = lease_nip;
158                 } else
159                         *lease = lease_nip;
160
161                 return 0;
162         }
163
164         if (lease_mac != NULL) {
165                 dhcp_server->lease_list =
166                                 g_list_remove(dhcp_server->lease_list,
167                                                                 lease_mac);
168                 g_hash_table_remove(dhcp_server->nip_lease_hash,
169                                 GINT_TO_POINTER((int) lease_mac->lease_nip));
170                 *lease = lease_mac;
171
172                 return 0;
173         }
174
175         *lease = g_try_new0(struct dhcp_lease, 1);
176         if (*lease == NULL)
177                 return -ENOMEM;
178
179         return 0;
180 }
181
182 static gint compare_expire(gconstpointer a, gconstpointer b)
183 {
184         const struct dhcp_lease *lease1 = a;
185         const struct dhcp_lease *lease2 = b;
186
187         return lease2->expire - lease1->expire;
188 }
189
190 static struct dhcp_lease *add_lease(GDHCPServer *dhcp_server, uint32_t expire,
191                                         const uint8_t *chaddr, uint32_t yiaddr)
192 {
193         struct dhcp_lease *lease = NULL;
194         int ret;
195
196         ret = get_lease(dhcp_server, yiaddr, chaddr, &lease);
197         if (ret != 0)
198                 return NULL;
199
200         memset(lease, 0, sizeof(*lease));
201
202         memcpy(lease->lease_mac, chaddr, ETH_ALEN);
203         lease->lease_nip = yiaddr;
204
205         if (expire == 0)
206                 lease->expire = time(NULL) + dhcp_server->lease_seconds;
207         else
208                 lease->expire = expire;
209
210         dhcp_server->lease_list = g_list_insert_sorted(dhcp_server->lease_list,
211                                                         lease, compare_expire);
212
213         g_hash_table_insert(dhcp_server->nip_lease_hash,
214                                 GINT_TO_POINTER((int) lease->lease_nip), lease);
215
216         return lease;
217 }
218
219 static struct dhcp_lease *find_lease_by_nip(GDHCPServer *dhcp_server,
220                                                                 uint32_t nip)
221 {
222         return g_hash_table_lookup(dhcp_server->nip_lease_hash,
223                                                 GINT_TO_POINTER((int) nip));
224 }
225
226 /* Check if the IP is taken; if it is, add it to the lease table */
227 static gboolean arp_check(uint32_t nip, const uint8_t *safe_mac)
228 {
229         /* TODO: Add ARP checking */
230         return TRUE;
231 }
232
233 static gboolean is_expired_lease(struct dhcp_lease *lease)
234 {
235         if (lease->expire < time(NULL))
236                 return TRUE;
237
238         return FALSE;
239 }
240
241 static uint32_t find_free_or_expired_nip(GDHCPServer *dhcp_server,
242                                         const uint8_t *safe_mac)
243 {
244         uint32_t ip_addr;
245         struct dhcp_lease *lease;
246         GList *list;
247         ip_addr = dhcp_server->start_ip;
248         for (; ip_addr <= dhcp_server->end_ip; ip_addr++) {
249                 /* e.g. 192.168.55.0 */
250                 if ((ip_addr & 0xff) == 0)
251                         continue;
252
253                 /* e.g. 192.168.55.255 */
254                 if ((ip_addr & 0xff) == 0xff)
255                         continue;
256
257                 lease = find_lease_by_nip(dhcp_server,
258                                 (uint32_t) htonl(ip_addr));
259                 if (lease != NULL)
260                         continue;
261
262                 if (arp_check(htonl(ip_addr), safe_mac) == TRUE)
263                         return htonl(ip_addr);
264         }
265
266         /* The last lease is the oldest one */
267         list = g_list_last(dhcp_server->lease_list);
268         if (list == NULL)
269                 return 0;
270
271         lease = list->data;
272         if (lease == NULL)
273                 return 0;
274
275          if (is_expired_lease(lease) == FALSE)
276                 return 0;
277
278          if (arp_check(lease->lease_nip, safe_mac) == FALSE)
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 == NULL) {
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 == NULL) {
368                 *error = G_DHCP_SERVER_ERROR_INTERFACE_UNAVAILABLE;
369                 goto error;
370         }
371
372         if (interface_is_up(ifindex) == FALSE) {
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->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 == NULL)
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_simple_option(packet, DHCP_SERVER_ID, dhcp_server->server_nip);
454 }
455
456 static void add_option(gpointer key, gpointer value, gpointer user_data)
457 {
458         const char *option_value = value;
459         uint8_t option_code = GPOINTER_TO_INT(key);
460         struct in_addr nip;
461         struct dhcp_packet *packet = user_data;
462
463         if (option_value == NULL)
464                 return;
465
466         switch (option_code) {
467         case G_DHCP_SUBNET:
468         case G_DHCP_ROUTER:
469         case G_DHCP_DNS_SERVER:
470                 if (inet_aton(option_value, &nip) == 0)
471                         return;
472
473                 dhcp_add_simple_option(packet, (uint8_t) option_code,
474                                                                 nip.s_addr);
475                 break;
476         default:
477                 return;
478         }
479 }
480
481 static void add_server_options(GDHCPServer *dhcp_server,
482                                 struct dhcp_packet *packet)
483 {
484         g_hash_table_foreach(dhcp_server->option_hash,
485                                 add_option, packet);
486 }
487
488 static gboolean check_requested_nip(GDHCPServer *dhcp_server,
489                                         uint32_t requested_nip)
490 {
491         struct dhcp_lease *lease;
492
493         if (requested_nip == 0)
494                 return FALSE;
495
496         if (ntohl(requested_nip) < dhcp_server->start_ip)
497                 return FALSE;
498
499         if (ntohl(requested_nip) > dhcp_server->end_ip)
500                 return FALSE;
501
502         lease = find_lease_by_nip(dhcp_server, requested_nip);
503         if (lease == NULL)
504                 return TRUE;
505
506         if (is_expired_lease(lease) == FALSE)
507                 return FALSE;
508
509         return TRUE;
510 }
511
512 static void send_packet_to_client(GDHCPServer *dhcp_server,
513                                 struct dhcp_packet *dhcp_pkt)
514 {
515         const uint8_t *chaddr;
516         uint32_t ciaddr;
517
518         if ((dhcp_pkt->flags & htons(BROADCAST_FLAG))
519                                 || dhcp_pkt->ciaddr == 0) {
520                 debug(dhcp_server, "Broadcasting packet to client");
521                 ciaddr = INADDR_BROADCAST;
522                 chaddr = MAC_BCAST_ADDR;
523         } else {
524                 debug(dhcp_server, "Unicasting packet to client ciaddr");
525                 ciaddr = dhcp_pkt->ciaddr;
526                 chaddr = dhcp_pkt->chaddr;
527         }
528
529         dhcp_send_raw_packet(dhcp_pkt,
530                 dhcp_server->server_nip, SERVER_PORT,
531                 ciaddr, CLIENT_PORT, chaddr,
532                 dhcp_server->ifindex);
533 }
534
535 static void send_offer(GDHCPServer *dhcp_server,
536                         struct dhcp_packet *client_packet,
537                                 struct dhcp_lease *lease,
538                                         uint32_t requested_nip)
539 {
540         struct dhcp_packet packet;
541         struct in_addr addr;
542
543         init_packet(dhcp_server, &packet, client_packet, DHCPOFFER);
544
545         if (lease)
546                 packet.yiaddr = lease->lease_nip;
547         else if (check_requested_nip(dhcp_server, requested_nip) == TRUE)
548                 packet.yiaddr = requested_nip;
549         else
550                 packet.yiaddr = find_free_or_expired_nip(
551                                 dhcp_server, client_packet->chaddr);
552
553         debug(dhcp_server, "find yiaddr %u", packet.yiaddr);
554
555         if (!packet.yiaddr) {
556                 debug(dhcp_server, "Err: Can not found lease and send offer");
557                 return;
558         }
559
560         lease = add_lease(dhcp_server, OFFER_TIME,
561                                 packet.chaddr, packet.yiaddr);
562         if (lease == NULL) {
563                 debug(dhcp_server,
564                                 "Err: No free IP addresses. OFFER abandoned");
565                 return;
566         }
567
568         dhcp_add_simple_option(&packet, DHCP_LEASE_TIME,
569                                 htonl(dhcp_server->lease_seconds));
570         add_server_options(dhcp_server, &packet);
571
572         addr.s_addr = packet.yiaddr;
573
574         debug(dhcp_server, "Sending OFFER of %s", inet_ntoa(addr));
575         send_packet_to_client(dhcp_server, &packet);
576 }
577
578 static void save_lease(GDHCPServer *dhcp_server)
579 {
580         GList *list;
581
582         if (dhcp_server->save_lease_func == NULL)
583                 return;
584
585         for (list = dhcp_server->lease_list; list; list = list->next) {
586                 struct dhcp_lease *lease = list->data;
587                 dhcp_server->save_lease_func(lease->lease_mac,
588                                         lease->lease_nip, lease->expire);
589         }
590 }
591
592 static void send_ACK(GDHCPServer *dhcp_server,
593                 struct dhcp_packet *client_packet, uint32_t yiaddr)
594 {
595         struct dhcp_packet packet;
596         uint32_t lease_time_sec;
597         struct in_addr addr;
598
599         init_packet(dhcp_server, &packet, client_packet, DHCPACK);
600         packet.yiaddr = yiaddr;
601
602         lease_time_sec = dhcp_server->lease_seconds;
603
604         dhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
605
606         add_server_options(dhcp_server, &packet);
607
608         addr.s_addr = yiaddr;
609
610         debug(dhcp_server, "Sending ACK to %s", inet_ntoa(addr));
611
612         send_packet_to_client(dhcp_server, &packet);
613
614         add_lease(dhcp_server, 0, packet.chaddr, packet.yiaddr);
615 }
616
617 static void send_NAK(GDHCPServer *dhcp_server,
618                         struct dhcp_packet *client_packet)
619 {
620         struct dhcp_packet packet;
621
622         init_packet(dhcp_server, &packet, client_packet, DHCPNAK);
623
624         debug(dhcp_server, "Sending NAK");
625
626         dhcp_send_raw_packet(&packet,
627                         dhcp_server->server_nip, SERVER_PORT,
628                         INADDR_BROADCAST, CLIENT_PORT, MAC_BCAST_ADDR,
629                         dhcp_server->ifindex);
630 }
631
632 static void send_inform(GDHCPServer *dhcp_server,
633                                 struct dhcp_packet *client_packet)
634 {
635         struct dhcp_packet packet;
636
637         init_packet(dhcp_server, &packet, client_packet, DHCPACK);
638         add_server_options(dhcp_server, &packet);
639         send_packet_to_client(dhcp_server, &packet);
640 }
641
642 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
643                                                         gpointer user_data)
644 {
645         GDHCPServer *dhcp_server = user_data;
646         struct dhcp_packet packet;
647         struct dhcp_lease *lease;
648         uint32_t requested_nip = 0;
649         uint8_t type, *server_id_option, *request_ip_option;
650         int re;
651
652         if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
653                 dhcp_server->listener_watch = 0;
654                 return FALSE;
655         }
656
657         re = dhcp_recv_l3_packet(&packet, dhcp_server->listener_sockfd);
658         if (re < 0)
659                 return TRUE;
660
661         type = check_packet_type(&packet);
662         if (type == 0)
663                 return TRUE;
664
665         server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID);
666         if (server_id_option) {
667                 uint32_t server_nid = dhcp_get_unaligned(
668                                         (uint32_t *) server_id_option);
669
670                 if (server_nid != dhcp_server->server_nip)
671                         return TRUE;
672         }
673
674         request_ip_option = dhcp_get_option(&packet, DHCP_REQUESTED_IP);
675         if (request_ip_option)
676                 requested_nip = dhcp_get_unaligned(
677                                         (uint32_t *) request_ip_option);
678
679         lease = find_lease_by_mac(dhcp_server, packet.chaddr);
680
681         switch (type) {
682                 case DHCPDISCOVER:
683                         debug(dhcp_server, "Received DISCOVER");
684
685                         send_offer(dhcp_server, &packet, lease, requested_nip);
686                 break;
687                 case DHCPREQUEST:
688                         debug(dhcp_server, "Received REQUEST NIP %d",
689                                                         requested_nip);
690                         if (requested_nip == 0) {
691                                 requested_nip = packet.ciaddr;
692                                 if (requested_nip == 0)
693                                         break;
694                         }
695
696                         if (lease && requested_nip == lease->lease_nip) {
697                                 debug(dhcp_server, "Sending ACK");
698                                 send_ACK(dhcp_server, &packet,
699                                                 lease->lease_nip);
700                                 break;
701                         }
702
703                         if (server_id_option || lease == NULL) {
704                                 debug(dhcp_server, "Sending NAK");
705                                 send_NAK(dhcp_server, &packet);
706                         }
707
708                 break;
709                 case DHCPDECLINE:
710                         debug(dhcp_server, "Received DECLINE");
711
712                         if (server_id_option == NULL)
713                                 break;
714
715                         if (request_ip_option == NULL)
716                                 break;
717
718                         if (lease == NULL)
719                                 break;
720
721                         if (requested_nip == lease->lease_nip)
722                                 remove_lease(dhcp_server, lease);
723
724                 break;
725                 case DHCPRELEASE:
726                         debug(dhcp_server, "Received RELEASE");
727
728                         if (server_id_option == NULL)
729                                 break;
730
731                         if (lease == NULL)
732                                 break;
733
734                         if (packet.ciaddr == lease->lease_nip)
735                                 lease_set_expire(dhcp_server, lease,
736                                                                 time(NULL));
737                 break;
738                 case DHCPINFORM:
739                         debug(dhcp_server, "Received INFORM");
740                         send_inform(dhcp_server, &packet);
741                 break;
742         }
743
744         return TRUE;
745 }
746
747 /* Caller need to load leases before call it */
748 int g_dhcp_server_start(GDHCPServer *dhcp_server)
749 {
750         GIOChannel *listener_channel;
751         int listener_sockfd;
752
753         if (dhcp_server->started == TRUE)
754                 return 0;
755
756         listener_sockfd = dhcp_l3_socket(SERVER_PORT,
757                                         dhcp_server->interface, AF_INET);
758         if (listener_sockfd < 0)
759                 return -EIO;
760
761         listener_channel = g_io_channel_unix_new(listener_sockfd);
762         if (listener_channel == NULL) {
763                 close(listener_sockfd);
764                 return -EIO;
765         }
766
767         dhcp_server->listener_sockfd = listener_sockfd;
768         dhcp_server->listener_channel = listener_channel;
769
770         g_io_channel_set_close_on_unref(listener_channel, TRUE);
771         dhcp_server->listener_watch =
772                         g_io_add_watch_full(listener_channel, G_PRIORITY_HIGH,
773                                 G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
774                                                 listener_event, dhcp_server,
775                                                                 NULL);
776         g_io_channel_unref(dhcp_server->listener_channel);
777
778         dhcp_server->started = TRUE;
779
780         return 0;
781 }
782
783 int g_dhcp_server_set_option(GDHCPServer *dhcp_server,
784                 unsigned char option_code, const char *option_value)
785 {
786         struct in_addr nip;
787
788         if (option_value == NULL)
789                 return -EINVAL;
790
791         debug(dhcp_server, "option_code %d option_value %s",
792                                         option_code, option_value);
793         switch (option_code) {
794         case G_DHCP_SUBNET:
795         case G_DHCP_ROUTER:
796         case G_DHCP_DNS_SERVER:
797                 if (inet_aton(option_value, &nip) == 0)
798                         return -ENXIO;
799                 break;
800         default:
801                 return -EINVAL;
802         }
803
804         g_hash_table_replace(dhcp_server->option_hash,
805                         GINT_TO_POINTER((int) option_code),
806                                         (gpointer) option_value);
807         return 0;
808 }
809
810 void g_dhcp_server_set_save_lease(GDHCPServer *dhcp_server,
811                                 GDHCPSaveLeaseFunc func, gpointer user_data)
812 {
813         if (dhcp_server == NULL)
814                 return;
815
816         dhcp_server->save_lease_func = func;
817 }
818
819 GDHCPServer *g_dhcp_server_ref(GDHCPServer *dhcp_server)
820 {
821         if (dhcp_server == NULL)
822                 return NULL;
823
824         __sync_fetch_and_add(&dhcp_server->ref_count, 1);
825
826         return dhcp_server;
827 }
828
829 void g_dhcp_server_stop(GDHCPServer *dhcp_server)
830 {
831         /* Save leases, before stop; load them before start */
832         save_lease(dhcp_server);
833
834         if (dhcp_server->listener_watch > 0) {
835                 g_source_remove(dhcp_server->listener_watch);
836                 dhcp_server->listener_watch = 0;
837         }
838
839         dhcp_server->listener_channel = NULL;
840
841         dhcp_server->started = FALSE;
842 }
843
844 void g_dhcp_server_unref(GDHCPServer *dhcp_server)
845 {
846         if (dhcp_server == NULL)
847                 return;
848
849         if (__sync_fetch_and_sub(&dhcp_server->ref_count, 1) != 1)
850                 return;
851
852         g_dhcp_server_stop(dhcp_server);
853
854         g_hash_table_destroy(dhcp_server->option_hash);
855
856         destroy_lease_table(dhcp_server);
857
858         g_free(dhcp_server->interface);
859
860         g_free(dhcp_server);
861 }
862
863 void g_dhcp_server_load_lease(GDHCPServer *dhcp_server, unsigned int expire,
864                                 unsigned char *mac, unsigned int lease_ip)
865 {
866         add_lease(dhcp_server, expire, mac, lease_ip);
867 }
868
869 int g_dhcp_server_set_ip_range(GDHCPServer *dhcp_server,
870                 const char *start_ip, const char *end_ip)
871 {
872         struct in_addr _host_addr;
873
874         if (inet_aton(start_ip, &_host_addr) == 0)
875                 return -ENXIO;
876
877         dhcp_server->start_ip = ntohl(_host_addr.s_addr);
878
879         if (inet_aton(end_ip, &_host_addr) == 0)
880                 return -ENXIO;
881
882         dhcp_server->end_ip = ntohl(_host_addr.s_addr);
883
884         return 0;
885 }
886
887 void g_dhcp_server_set_lease_time(GDHCPServer *dhcp_server, unsigned int lease_time)
888 {
889         if (dhcp_server == NULL)
890                 return;
891
892         dhcp_server->lease_seconds = lease_time;
893 }
894
895 void g_dhcp_server_set_debug(GDHCPServer *dhcp_server,
896                                 GDHCPDebugFunc func, gpointer user_data)
897 {
898         if (dhcp_server == NULL)
899                 return;
900
901         dhcp_server->debug_func = func;
902         dhcp_server->debug_data = user_data;
903 }