Imported Upstream version 1.29
[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         if (dhcp_server->lease_added_cb)
218                 dhcp_server->lease_added_cb(lease->lease_mac, yiaddr);
219
220         return lease;
221 }
222
223 static struct dhcp_lease *find_lease_by_nip(GDHCPServer *dhcp_server,
224                                                                 uint32_t nip)
225 {
226         return g_hash_table_lookup(dhcp_server->nip_lease_hash,
227                                                 GINT_TO_POINTER((int) nip));
228 }
229
230 /* Check if the IP is taken; if it is, add it to the lease table */
231 static bool arp_check(uint32_t nip, const uint8_t *safe_mac)
232 {
233         /* TODO: Add ARP checking */
234         return true;
235 }
236
237 static bool is_expired_lease(struct dhcp_lease *lease)
238 {
239         if (lease->expire < time(NULL))
240                 return true;
241
242         return false;
243 }
244
245 static uint32_t find_free_or_expired_nip(GDHCPServer *dhcp_server,
246                                         const uint8_t *safe_mac)
247 {
248         uint32_t ip_addr;
249         struct dhcp_lease *lease;
250         GList *list;
251         ip_addr = dhcp_server->start_ip;
252         for (; ip_addr <= dhcp_server->end_ip; ip_addr++) {
253                 /* e.g. 192.168.55.0 */
254                 if ((ip_addr & 0xff) == 0)
255                         continue;
256
257                 /* e.g. 192.168.55.255 */
258                 if ((ip_addr & 0xff) == 0xff)
259                         continue;
260
261                 lease = find_lease_by_nip(dhcp_server, ip_addr);
262                 if (lease)
263                         continue;
264
265                 if (arp_check(htonl(ip_addr), safe_mac))
266                         return ip_addr;
267         }
268
269         /* The last lease is the oldest one */
270         list = g_list_last(dhcp_server->lease_list);
271         if (!list)
272                 return 0;
273
274         lease = list->data;
275         if (!lease)
276                 return 0;
277
278          if (!is_expired_lease(lease))
279                 return 0;
280
281          if (!arp_check(lease->lease_nip, safe_mac))
282                 return 0;
283
284         return lease->lease_nip;
285 }
286
287 static void lease_set_expire(GDHCPServer *dhcp_server,
288                         struct dhcp_lease *lease, uint32_t expire)
289 {
290         dhcp_server->lease_list = g_list_remove(dhcp_server->lease_list, lease);
291
292         lease->expire = expire;
293
294         dhcp_server->lease_list = g_list_insert_sorted(dhcp_server->lease_list,
295                                                         lease, compare_expire);
296 }
297
298 static void destroy_lease_table(GDHCPServer *dhcp_server)
299 {
300         GList *list;
301
302         g_hash_table_destroy(dhcp_server->nip_lease_hash);
303
304         dhcp_server->nip_lease_hash = NULL;
305
306         for (list = dhcp_server->lease_list; list; list = list->next) {
307                 struct dhcp_lease *lease = list->data;
308
309                 g_free(lease);
310         }
311
312         g_list_free(dhcp_server->lease_list);
313
314         dhcp_server->lease_list = NULL;
315 }
316 static uint32_t get_interface_address(int index)
317 {
318         struct ifreq ifr;
319         int sk, err;
320         struct sockaddr_in *server_ip;
321         uint32_t ret = 0;
322
323         sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
324         if (sk < 0) {
325                 perror("Open socket error");
326                 return 0;
327         }
328
329         memset(&ifr, 0, sizeof(ifr));
330         ifr.ifr_ifindex = index;
331
332         err = ioctl(sk, SIOCGIFNAME, &ifr);
333         if (err < 0) {
334                 perror("Get interface name error");
335                 goto done;
336         }
337
338         err = ioctl(sk, SIOCGIFADDR, &ifr);
339         if (err < 0) {
340                 perror("Get ip address error");
341                 goto done;
342         }
343
344         server_ip = (struct sockaddr_in *) &ifr.ifr_addr;
345         ret = server_ip->sin_addr.s_addr;
346
347 done:
348         close(sk);
349
350         return ret;
351 }
352
353 GDHCPServer *g_dhcp_server_new(GDHCPType type,
354                 int ifindex, GDHCPServerError *error)
355 {
356         GDHCPServer *dhcp_server = NULL;
357
358         if (ifindex < 0) {
359                 *error = G_DHCP_SERVER_ERROR_INVALID_INDEX;
360                 return NULL;
361         }
362
363         dhcp_server = g_try_new0(GDHCPServer, 1);
364         if (!dhcp_server) {
365                 *error = G_DHCP_SERVER_ERROR_NOMEM;
366                 return NULL;
367         }
368
369         dhcp_server->interface = get_interface_name(ifindex);
370         if (!dhcp_server->interface) {
371                 *error = G_DHCP_SERVER_ERROR_INTERFACE_UNAVAILABLE;
372                 goto error;
373         }
374
375         if (!interface_is_up(ifindex)) {
376                 *error = G_DHCP_SERVER_ERROR_INTERFACE_DOWN;
377                 goto error;
378         }
379
380         dhcp_server->server_nip = get_interface_address(ifindex);
381         if (dhcp_server->server_nip == 0) {
382                 *error = G_DHCP_SERVER_ERROR_IP_ADDRESS_INVALID;
383                 goto error;
384         }
385
386         dhcp_server->nip_lease_hash = g_hash_table_new_full(g_direct_hash,
387                                                 g_direct_equal, NULL, NULL);
388         dhcp_server->option_hash = g_hash_table_new_full(g_direct_hash,
389                                                 g_direct_equal, NULL, NULL);
390
391         dhcp_server->started = FALSE;
392
393         /* All the leases have the same fixed lease time,
394          * do not support DHCP_LEASE_TIME option from client.
395          */
396         dhcp_server->lease_seconds = DEFAULT_DHCP_LEASE_SEC;
397
398         dhcp_server->type = type;
399         dhcp_server->ref_count = 1;
400         dhcp_server->ifindex = ifindex;
401         dhcp_server->listener_sockfd = -1;
402         dhcp_server->listener_watch = -1;
403         dhcp_server->listener_channel = NULL;
404         dhcp_server->save_lease_func = NULL;
405         dhcp_server->debug_func = NULL;
406         dhcp_server->debug_data = NULL;
407
408         *error = G_DHCP_SERVER_ERROR_NONE;
409
410         return dhcp_server;
411
412 error:
413         g_free(dhcp_server->interface);
414         g_free(dhcp_server);
415         return NULL;
416 }
417
418
419 static uint8_t check_packet_type(struct dhcp_packet *packet)
420 {
421         uint8_t *type;
422
423         if (packet->hlen != ETH_ALEN)
424                 return 0;
425
426         if (packet->op != BOOTREQUEST)
427                 return 0;
428
429         type = dhcp_get_option(packet, DHCP_MESSAGE_TYPE);
430
431         if (!type)
432                 return 0;
433
434         if (*type < DHCP_MINTYPE)
435                 return 0;
436
437         if (*type > DHCP_MAXTYPE)
438                 return 0;
439
440         return *type;
441 }
442
443 static void init_packet(GDHCPServer *dhcp_server, struct dhcp_packet *packet,
444                                 struct dhcp_packet *client_packet, char type)
445 {
446         /* Sets op, htype, hlen, cookie fields
447          * and adds DHCP_MESSAGE_TYPE option */
448         dhcp_init_header(packet, type);
449
450         packet->xid = client_packet->xid;
451         memcpy(packet->chaddr, client_packet->chaddr,
452                                 sizeof(client_packet->chaddr));
453         packet->flags = client_packet->flags;
454         packet->gateway_nip = client_packet->gateway_nip;
455         packet->ciaddr = client_packet->ciaddr;
456         dhcp_add_option_uint32(packet, DHCP_SERVER_ID,
457                                         ntohl(dhcp_server->server_nip));
458 }
459
460 static void add_option(gpointer key, gpointer value, gpointer user_data)
461 {
462         const char *option_value = value;
463         uint8_t option_code = GPOINTER_TO_INT(key);
464         struct in_addr nip;
465         struct dhcp_packet *packet = user_data;
466
467         if (!option_value)
468                 return;
469
470         switch (option_code) {
471         case G_DHCP_SUBNET:
472         case G_DHCP_ROUTER:
473         case G_DHCP_DNS_SERVER:
474                 if (inet_aton(option_value, &nip) == 0)
475                         return;
476
477                 dhcp_add_option_uint32(packet, (uint8_t) option_code,
478                                                         ntohl(nip.s_addr));
479                 break;
480         default:
481                 return;
482         }
483 }
484
485 static void add_server_options(GDHCPServer *dhcp_server,
486                                 struct dhcp_packet *packet)
487 {
488         g_hash_table_foreach(dhcp_server->option_hash,
489                                 add_option, packet);
490 }
491
492 static bool check_requested_nip(GDHCPServer *dhcp_server,
493                                         uint32_t requested_nip)
494 {
495         struct dhcp_lease *lease;
496
497         if (requested_nip == 0)
498                 return false;
499
500         if (requested_nip < dhcp_server->start_ip)
501                 return false;
502
503         if (requested_nip > dhcp_server->end_ip)
504                 return false;
505
506         lease = find_lease_by_nip(dhcp_server, requested_nip);
507         if (!lease)
508                 return true;
509
510         if (!is_expired_lease(lease))
511                 return false;
512
513         return true;
514 }
515
516 static void send_packet_to_client(GDHCPServer *dhcp_server,
517                                 struct dhcp_packet *dhcp_pkt)
518 {
519         const uint8_t *chaddr;
520         uint32_t ciaddr;
521
522         if ((dhcp_pkt->flags & htons(BROADCAST_FLAG))
523                                 || dhcp_pkt->ciaddr == 0) {
524                 debug(dhcp_server, "Broadcasting packet to client");
525                 ciaddr = INADDR_BROADCAST;
526                 chaddr = MAC_BCAST_ADDR;
527         } else {
528                 debug(dhcp_server, "Unicasting packet to client ciaddr");
529                 ciaddr = dhcp_pkt->ciaddr;
530                 chaddr = dhcp_pkt->chaddr;
531         }
532
533         dhcp_send_raw_packet(dhcp_pkt,
534                 dhcp_server->server_nip, SERVER_PORT,
535                 ciaddr, CLIENT_PORT, chaddr,
536                 dhcp_server->ifindex, false);
537 }
538
539 static void send_offer(GDHCPServer *dhcp_server,
540                         struct dhcp_packet *client_packet,
541                                 struct dhcp_lease *lease,
542                                         uint32_t requested_nip)
543 {
544         struct dhcp_packet packet;
545         struct in_addr addr;
546
547         init_packet(dhcp_server, &packet, client_packet, DHCPOFFER);
548
549         if (lease)
550                 packet.yiaddr = htonl(lease->lease_nip);
551         else if (check_requested_nip(dhcp_server, requested_nip))
552                 packet.yiaddr = htonl(requested_nip);
553         else
554                 packet.yiaddr = htonl(find_free_or_expired_nip(
555                                         dhcp_server, client_packet->chaddr));
556
557         debug(dhcp_server, "find yiaddr %u", packet.yiaddr);
558
559         if (!packet.yiaddr) {
560                 debug(dhcp_server, "Err: Can not found lease and send offer");
561                 return;
562         }
563
564         lease = add_lease(dhcp_server, OFFER_TIME,
565                                 packet.chaddr, packet.yiaddr);
566         if (!lease) {
567                 debug(dhcp_server,
568                                 "Err: No free IP addresses. OFFER abandoned");
569                 return;
570         }
571
572         dhcp_add_option_uint32(&packet, DHCP_LEASE_TIME,
573                                                 dhcp_server->lease_seconds);
574         add_server_options(dhcp_server, &packet);
575
576         addr.s_addr = packet.yiaddr;
577
578         debug(dhcp_server, "Sending OFFER of %s", inet_ntoa(addr));
579         send_packet_to_client(dhcp_server, &packet);
580 }
581
582 static void save_lease(GDHCPServer *dhcp_server)
583 {
584         GList *list;
585
586         if (!dhcp_server->save_lease_func)
587                 return;
588
589         for (list = dhcp_server->lease_list; list; list = list->next) {
590                 struct dhcp_lease *lease = list->data;
591                 dhcp_server->save_lease_func(lease->lease_mac,
592                                         lease->lease_nip, lease->expire);
593         }
594 }
595
596 static void send_ACK(GDHCPServer *dhcp_server,
597                 struct dhcp_packet *client_packet, uint32_t dest)
598 {
599         struct dhcp_packet packet;
600         uint32_t lease_time_sec;
601         struct in_addr addr;
602
603         init_packet(dhcp_server, &packet, client_packet, DHCPACK);
604         packet.yiaddr = htonl(dest);
605
606         lease_time_sec = dhcp_server->lease_seconds;
607
608         dhcp_add_option_uint32(&packet, DHCP_LEASE_TIME, lease_time_sec);
609
610         add_server_options(dhcp_server, &packet);
611
612         addr.s_addr = htonl(dest);
613
614         debug(dhcp_server, "Sending ACK to %s", inet_ntoa(addr));
615
616         send_packet_to_client(dhcp_server, &packet);
617
618         add_lease(dhcp_server, 0, 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 = 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 }