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