session: Add session identification method to config
[platform/upstream/connman.git] / gdhcp / client.c
1 /*
2  *
3  *  DHCP client 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 #define _GNU_SOURCE
27 #include <stdio.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/ioctl.h>
33 #include <arpa/inet.h>
34 #include <sys/time.h>
35
36 #include <netpacket/packet.h>
37 #include <netinet/if_ether.h>
38 #include <net/ethernet.h>
39
40 #include <linux/if.h>
41 #include <linux/filter.h>
42
43 #include <glib.h>
44
45 #include "gdhcp.h"
46 #include "common.h"
47 #include "ipv4ll.h"
48
49 #define DISCOVER_TIMEOUT 3
50 #define DISCOVER_RETRIES 10
51
52 #define REQUEST_TIMEOUT 3
53 #define REQUEST_RETRIES 5
54
55 typedef enum _listen_mode {
56         L_NONE,
57         L2,
58         L3,
59         L_ARP,
60 } ListenMode;
61
62 typedef enum _dhcp_client_state {
63         INIT_SELECTING,
64         REQUESTING,
65         BOUND,
66         RENEWING,
67         REBINDING,
68         RELEASED,
69         IPV4LL_PROBE,
70         IPV4LL_ANNOUNCE,
71         IPV4LL_MONITOR,
72         IPV4LL_DEFEND,
73         INFORMATION_REQ,
74         SOLICITATION,
75         REQUEST,
76         CONFIRM,
77         RENEW,
78         REBIND,
79         RELEASE,
80 } ClientState;
81
82 struct _GDHCPClient {
83         int ref_count;
84         GDHCPType type;
85         ClientState state;
86         int ifindex;
87         char *interface;
88         uint8_t mac_address[6];
89         uint32_t xid;
90         uint32_t server_ip;
91         uint32_t requested_ip;
92         char *assigned_ip;
93         time_t start;
94         uint32_t lease_seconds;
95         ListenMode listen_mode;
96         int listener_sockfd;
97         uint8_t retry_times;
98         uint8_t ack_retry_times;
99         uint8_t conflicts;
100         guint timeout;
101         guint listener_watch;
102         GIOChannel *listener_channel;
103         GList *require_list;
104         GList *request_list;
105         GHashTable *code_value_hash;
106         GHashTable *send_value_hash;
107         GDHCPClientEventFunc lease_available_cb;
108         gpointer lease_available_data;
109         GDHCPClientEventFunc ipv4ll_available_cb;
110         gpointer ipv4ll_available_data;
111         GDHCPClientEventFunc no_lease_cb;
112         gpointer no_lease_data;
113         GDHCPClientEventFunc lease_lost_cb;
114         gpointer lease_lost_data;
115         GDHCPClientEventFunc ipv4ll_lost_cb;
116         gpointer ipv4ll_lost_data;
117         GDHCPClientEventFunc address_conflict_cb;
118         gpointer address_conflict_data;
119         GDHCPDebugFunc debug_func;
120         gpointer debug_data;
121         GDHCPClientEventFunc information_req_cb;
122         gpointer information_req_data;
123         GDHCPClientEventFunc solicitation_cb;
124         gpointer solicitation_data;
125         GDHCPClientEventFunc advertise_cb;
126         gpointer advertise_data;
127         GDHCPClientEventFunc request_cb;
128         gpointer request_data;
129         GDHCPClientEventFunc renew_cb;
130         gpointer renew_data;
131         GDHCPClientEventFunc rebind_cb;
132         gpointer rebind_data;
133         GDHCPClientEventFunc release_cb;
134         gpointer release_data;
135         GDHCPClientEventFunc confirm_cb;
136         gpointer confirm_data;
137         char *last_address;
138         unsigned char *duid;
139         int duid_len;
140         unsigned char *server_duid;
141         int server_duid_len;
142         uint16_t status_code;
143         uint32_t iaid;
144         uint32_t T1, T2;
145         struct in6_addr ia_na;
146         struct in6_addr ia_ta;
147         time_t last_renew;
148         time_t last_rebind;
149         time_t expire;
150         gboolean retransmit;
151         struct timeval start_time;
152 };
153
154 static inline void debug(GDHCPClient *client, const char *format, ...)
155 {
156         char str[256];
157         va_list ap;
158
159         if (client->debug_func == NULL)
160                 return;
161
162         va_start(ap, format);
163
164         if (vsnprintf(str, sizeof(str), format, ap) > 0)
165                 client->debug_func(str, client->debug_data);
166
167         va_end(ap);
168 }
169
170 /* Initialize the packet with the proper defaults */
171 static void init_packet(GDHCPClient *dhcp_client, gpointer pkt, char type)
172 {
173         if (dhcp_client->type == G_DHCP_IPV6)
174                 dhcpv6_init_header(pkt, type);
175         else {
176                 struct dhcp_packet *packet = pkt;
177
178                 dhcp_init_header(packet, type);
179                 memcpy(packet->chaddr, dhcp_client->mac_address, 6);
180         }
181 }
182
183 static void add_request_options(GDHCPClient *dhcp_client,
184                                 struct dhcp_packet *packet)
185 {
186         int len = 0;
187         GList *list;
188         uint8_t code;
189         int end = dhcp_end_option(packet->options);
190
191         for (list = dhcp_client->request_list; list; list = list->next) {
192                 code = (uint8_t) GPOINTER_TO_INT(list->data);
193
194                 packet->options[end + OPT_DATA + len] = code;
195                 len++;
196         }
197
198         if (len) {
199                 packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
200                 packet->options[end + OPT_LEN] = len;
201                 packet->options[end + OPT_DATA + len] = DHCP_END;
202         }
203 }
204
205 struct hash_params {
206         unsigned char *buf;
207         int max_buf;
208         unsigned char **ptr_buf;
209 };
210
211 static void add_dhcpv6_binary_option(gpointer key, gpointer value,
212                                         gpointer user_data)
213 {
214         uint8_t *option = value;
215         uint16_t len;
216         struct hash_params *params = user_data;
217
218         /* option[0][1] contains option code */
219         len = option[2] << 8 | option[3];
220
221         if ((*params->ptr_buf + len + 2 + 2) > (params->buf + params->max_buf))
222                 return;
223
224         memcpy(*params->ptr_buf, option, len + 2 + 2);
225         (*params->ptr_buf) += len + 2 + 2;
226 }
227
228 static void add_dhcpv6_send_options(GDHCPClient *dhcp_client,
229                                 unsigned char *buf, int max_buf,
230                                 unsigned char **ptr_buf)
231 {
232         struct hash_params params = {
233                 .buf = buf,
234                 .max_buf = max_buf,
235                 .ptr_buf = ptr_buf
236         };
237
238         if (dhcp_client->type != G_DHCP_IPV6)
239                 return;
240
241         g_hash_table_foreach(dhcp_client->send_value_hash,
242                                 add_dhcpv6_binary_option, &params);
243
244         *ptr_buf = *params.ptr_buf;
245 }
246
247 static void copy_option(uint8_t *buf, uint16_t code, uint16_t len,
248                         uint8_t *msg)
249 {
250         buf[0] = code >> 8;
251         buf[1] = code & 0xff;
252         buf[2] = len >> 8;
253         buf[3] = len & 0xff;
254         if (len > 0 && msg != NULL)
255                 memcpy(&buf[4], msg, len);
256 }
257
258 static int32_t get_time_diff(struct timeval *tv)
259 {
260         struct timeval now;
261         int32_t hsec;
262
263         gettimeofday(&now, NULL);
264
265         hsec = (now.tv_sec - tv->tv_sec) * 100;
266         hsec += (now.tv_usec - tv->tv_usec) / 10000;
267
268         return hsec;
269 }
270
271 static void add_dhcpv6_request_options(GDHCPClient *dhcp_client,
272                                 struct dhcpv6_packet *packet,
273                                 unsigned char *buf, int max_buf,
274                                 unsigned char **ptr_buf)
275 {
276         GList *list;
277         uint16_t code, value;
278         int32_t diff;
279         int len;
280
281         if (dhcp_client->type != G_DHCP_IPV6)
282                 return;
283
284         for (list = dhcp_client->request_list; list; list = list->next) {
285                 code = (uint16_t) GPOINTER_TO_INT(list->data);
286
287                 switch (code) {
288                 case G_DHCPV6_CLIENTID:
289                         if (dhcp_client->duid == NULL)
290                                 return;
291
292                         len = 2 + 2 + dhcp_client->duid_len;
293                         if ((*ptr_buf + len) > (buf + max_buf)) {
294                                 debug(dhcp_client, "Too long dhcpv6 message "
295                                         "when writing client id option");
296                                 return;
297                         }
298
299                         copy_option(*ptr_buf, G_DHCPV6_CLIENTID,
300                                 dhcp_client->duid_len, dhcp_client->duid);
301                         (*ptr_buf) += len;
302                         break;
303
304                 case G_DHCPV6_SERVERID:
305                         if (dhcp_client->server_duid == NULL)
306                                 return;
307
308                         len = 2 + 2 + dhcp_client->server_duid_len;
309                         if ((*ptr_buf + len) > (buf + max_buf)) {
310                                 debug(dhcp_client, "Too long dhcpv6 message "
311                                         "when writing server id option");
312                                 return;
313                         }
314
315                         copy_option(*ptr_buf, G_DHCPV6_SERVERID,
316                                 dhcp_client->server_duid_len,
317                                 dhcp_client->server_duid);
318                         (*ptr_buf) += len;
319                         break;
320
321                 case G_DHCPV6_RAPID_COMMIT:
322                         len = 2 + 2;
323                         if ((*ptr_buf + len) > (buf + max_buf)) {
324                                 debug(dhcp_client, "Too long dhcpv6 message "
325                                         "when writing rapid commit option");
326                                 return;
327                         }
328
329                         copy_option(*ptr_buf, G_DHCPV6_RAPID_COMMIT, 0, 0);
330                         (*ptr_buf) += len;
331                         break;
332
333                 case G_DHCPV6_ORO:
334                         break;
335
336                 case G_DHCPV6_ELAPSED_TIME:
337                         if (dhcp_client->retransmit == FALSE) {
338                                 /*
339                                  * Initial message, elapsed time is 0.
340                                  */
341                                 diff = 0;
342                         } else {
343                                 diff = get_time_diff(&dhcp_client->start_time);
344                                 if (diff < 0 || diff > 0xffff)
345                                         diff = 0xffff;
346                         }
347
348                         len = 2 + 2 + 2;
349                         if ((*ptr_buf + len) > (buf + max_buf)) {
350                                 debug(dhcp_client, "Too long dhcpv6 message "
351                                         "when writing elapsed time option");
352                                 return;
353                         }
354
355                         value = htons((uint16_t)diff);
356                         copy_option(*ptr_buf, G_DHCPV6_ELAPSED_TIME,
357                                 2, (uint8_t *)&value);
358                         (*ptr_buf) += len;
359                         break;
360
361                 case G_DHCPV6_DNS_SERVERS:
362                         break;
363
364                 case G_DHCPV6_SNTP_SERVERS:
365                         break;
366
367                 default:
368                         break;
369                 }
370         }
371 }
372
373 static void add_binary_option(gpointer key, gpointer value, gpointer user_data)
374 {
375         uint8_t *option = value;
376         struct dhcp_packet *packet = user_data;
377
378         dhcp_add_binary_option(packet, option);
379 }
380
381 static void add_send_options(GDHCPClient *dhcp_client,
382                                 struct dhcp_packet *packet)
383 {
384         g_hash_table_foreach(dhcp_client->send_value_hash,
385                                 add_binary_option, packet);
386 }
387
388 /*
389  * Return an RFC 951- and 2131-complaint BOOTP 'secs' value that
390  * represents the number of seconds elapsed from the start of
391  * attempting DHCP to satisfy some DHCP servers that allow for an
392  * "authoritative" reply before responding.
393  */
394 static uint16_t dhcp_attempt_secs(GDHCPClient *dhcp_client)
395 {
396         return htons(MIN(time(NULL) - dhcp_client->start, UINT16_MAX));
397 }
398
399 static int send_discover(GDHCPClient *dhcp_client, uint32_t requested)
400 {
401         struct dhcp_packet packet;
402
403         debug(dhcp_client, "sending DHCP discover request");
404
405         init_packet(dhcp_client, &packet, DHCPDISCOVER);
406
407         packet.xid = dhcp_client->xid;
408         packet.secs = dhcp_attempt_secs(dhcp_client);
409
410         if (requested)
411                 dhcp_add_option_uint32(&packet, DHCP_REQUESTED_IP, requested);
412
413         /* Explicitly saying that we want RFC-compliant packets helps
414          * some buggy DHCP servers to NOT send bigger packets */
415         dhcp_add_option_uint16(&packet, DHCP_MAX_SIZE, 576);
416
417         add_request_options(dhcp_client, &packet);
418
419         add_send_options(dhcp_client, &packet);
420
421         return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
422                                         INADDR_BROADCAST, SERVER_PORT,
423                                         MAC_BCAST_ADDR, dhcp_client->ifindex);
424 }
425
426 static int send_select(GDHCPClient *dhcp_client)
427 {
428         struct dhcp_packet packet;
429
430         debug(dhcp_client, "sending DHCP select request");
431
432         init_packet(dhcp_client, &packet, DHCPREQUEST);
433
434         packet.xid = dhcp_client->xid;
435         packet.secs = dhcp_attempt_secs(dhcp_client);
436
437         dhcp_add_option_uint32(&packet, DHCP_REQUESTED_IP,
438                                                 dhcp_client->requested_ip);
439         dhcp_add_option_uint32(&packet, DHCP_SERVER_ID,
440                                                 dhcp_client->server_ip);
441
442         add_request_options(dhcp_client, &packet);
443
444         add_send_options(dhcp_client, &packet);
445
446         return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
447                                         INADDR_BROADCAST, SERVER_PORT,
448                                         MAC_BCAST_ADDR, dhcp_client->ifindex);
449 }
450
451 static int send_renew(GDHCPClient *dhcp_client)
452 {
453         struct dhcp_packet packet;
454
455         debug(dhcp_client, "sending DHCP renew request");
456
457         init_packet(dhcp_client , &packet, DHCPREQUEST);
458         packet.xid = dhcp_client->xid;
459         packet.ciaddr = htonl(dhcp_client->requested_ip);
460
461         add_request_options(dhcp_client, &packet);
462
463         add_send_options(dhcp_client, &packet);
464
465         return dhcp_send_kernel_packet(&packet,
466                 dhcp_client->requested_ip, CLIENT_PORT,
467                 dhcp_client->server_ip, SERVER_PORT);
468 }
469
470 static int send_rebound(GDHCPClient *dhcp_client)
471 {
472         struct dhcp_packet packet;
473
474         debug(dhcp_client, "sending DHCP rebound request");
475
476         init_packet(dhcp_client , &packet, DHCPREQUEST);
477         packet.xid = dhcp_client->xid;
478         packet.ciaddr = htonl(dhcp_client->requested_ip);
479
480         add_request_options(dhcp_client, &packet);
481
482         add_send_options(dhcp_client, &packet);
483
484         return dhcp_send_raw_packet(&packet, INADDR_ANY, CLIENT_PORT,
485                                         INADDR_BROADCAST, SERVER_PORT,
486                                         MAC_BCAST_ADDR, dhcp_client->ifindex);
487 }
488
489 static int send_release(GDHCPClient *dhcp_client,
490                         uint32_t server, uint32_t ciaddr)
491 {
492         struct dhcp_packet packet;
493
494         debug(dhcp_client, "sending DHCP release request");
495
496         init_packet(dhcp_client, &packet, DHCPRELEASE);
497         packet.xid = rand();
498         packet.ciaddr = htonl(ciaddr);
499
500         dhcp_add_option_uint32(&packet, DHCP_SERVER_ID, server);
501
502         return dhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT,
503                                                 server, SERVER_PORT);
504 }
505
506 static gboolean ipv4ll_probe_timeout(gpointer dhcp_data);
507 static int switch_listening_mode(GDHCPClient *dhcp_client,
508                                         ListenMode listen_mode);
509
510 static gboolean send_probe_packet(gpointer dhcp_data)
511 {
512         GDHCPClient *dhcp_client;
513         guint timeout;
514
515         dhcp_client = dhcp_data;
516         /* if requested_ip is not valid, pick a new address*/
517         if (dhcp_client->requested_ip == 0) {
518                 debug(dhcp_client, "pick a new random address");
519                 dhcp_client->requested_ip = ipv4ll_random_ip(0);
520         }
521
522         debug(dhcp_client, "sending IPV4LL probe request");
523
524         if (dhcp_client->retry_times == 1) {
525                 dhcp_client->state = IPV4LL_PROBE;
526                 switch_listening_mode(dhcp_client, L_ARP);
527         }
528         ipv4ll_send_arp_packet(dhcp_client->mac_address, 0,
529                         dhcp_client->requested_ip, dhcp_client->ifindex);
530
531         if (dhcp_client->retry_times < PROBE_NUM) {
532                 /*add a random timeout in range of PROBE_MIN to PROBE_MAX*/
533                 timeout = ipv4ll_random_delay_ms(PROBE_MAX-PROBE_MIN);
534                 timeout += PROBE_MIN*1000;
535         } else
536                 timeout = (ANNOUNCE_WAIT * 1000);
537
538         dhcp_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
539                                                  timeout,
540                                                  ipv4ll_probe_timeout,
541                                                  dhcp_client,
542                                                  NULL);
543         return FALSE;
544 }
545
546 static gboolean ipv4ll_announce_timeout(gpointer dhcp_data);
547 static gboolean ipv4ll_defend_timeout(gpointer dhcp_data);
548
549 static gboolean send_announce_packet(gpointer dhcp_data)
550 {
551         GDHCPClient *dhcp_client;
552
553         dhcp_client = dhcp_data;
554
555         debug(dhcp_client, "sending IPV4LL announce request");
556
557         ipv4ll_send_arp_packet(dhcp_client->mac_address,
558                                 dhcp_client->requested_ip,
559                                 dhcp_client->requested_ip,
560                                 dhcp_client->ifindex);
561
562         if (dhcp_client->timeout > 0)
563                 g_source_remove(dhcp_client->timeout);
564         dhcp_client->timeout = 0;
565
566         if (dhcp_client->state == IPV4LL_DEFEND) {
567                 dhcp_client->timeout =
568                         g_timeout_add_seconds_full(G_PRIORITY_HIGH,
569                                                 DEFEND_INTERVAL,
570                                                 ipv4ll_defend_timeout,
571                                                 dhcp_client,
572                                                 NULL);
573                 return TRUE;
574         } else
575                 dhcp_client->timeout =
576                         g_timeout_add_seconds_full(G_PRIORITY_HIGH,
577                                                 ANNOUNCE_INTERVAL,
578                                                 ipv4ll_announce_timeout,
579                                                 dhcp_client,
580                                                 NULL);
581         return TRUE;
582 }
583
584 static void get_interface_mac_address(int index, uint8_t *mac_address)
585 {
586         struct ifreq ifr;
587         int sk, err;
588
589         sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
590         if (sk < 0) {
591                 perror("Open socket error");
592                 return;
593         }
594
595         memset(&ifr, 0, sizeof(ifr));
596         ifr.ifr_ifindex = index;
597
598         err = ioctl(sk, SIOCGIFNAME, &ifr);
599         if (err < 0) {
600                 perror("Get interface name error");
601                 goto done;
602         }
603
604         err = ioctl(sk, SIOCGIFHWADDR, &ifr);
605         if (err < 0) {
606                 perror("Get mac address error");
607                 goto done;
608         }
609
610         memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
611
612 done:
613         close(sk);
614 }
615
616 void g_dhcpv6_client_set_retransmit(GDHCPClient *dhcp_client)
617 {
618         if (dhcp_client == NULL)
619                 return;
620
621         dhcp_client->retransmit = TRUE;
622 }
623
624 void g_dhcpv6_client_clear_retransmit(GDHCPClient *dhcp_client)
625 {
626         if (dhcp_client == NULL)
627                 return;
628
629         dhcp_client->retransmit = FALSE;
630 }
631
632 int g_dhcpv6_create_duid(GDHCPDuidType duid_type, int index, int type,
633                         unsigned char **duid, int *duid_len)
634 {
635         time_t duid_time;
636
637         switch (duid_type) {
638         case G_DHCPV6_DUID_LLT:
639                 *duid_len = 2 + 2 + 4 + ETH_ALEN;
640                 *duid = g_try_malloc(*duid_len);
641                 if (*duid == NULL)
642                         return -ENOMEM;
643
644                 (*duid)[0] = 0;
645                 (*duid)[1] = 1;
646                 get_interface_mac_address(index, &(*duid)[2 + 2 + 4]);
647                 (*duid)[2] = 0;
648                 (*duid)[3] = type;
649                 duid_time = time(NULL) - DUID_TIME_EPOCH;
650                 (*duid)[4] = duid_time >> 24;
651                 (*duid)[5] = duid_time >> 16;
652                 (*duid)[6] = duid_time >> 8;
653                 (*duid)[7] = duid_time & 0xff;
654                 break;
655         case G_DHCPV6_DUID_EN:
656                 return -EINVAL;
657         case G_DHCPV6_DUID_LL:
658                 *duid_len = 2 + 2 + ETH_ALEN;
659                 *duid = g_try_malloc(*duid_len);
660                 if (*duid == NULL)
661                         return -ENOMEM;
662
663                 (*duid)[0] = 0;
664                 (*duid)[1] = 3;
665                 get_interface_mac_address(index, &(*duid)[2 + 2]);
666                 (*duid)[2] = 0;
667                 (*duid)[3] = type;
668                 break;
669         }
670
671         return 0;
672 }
673
674 int g_dhcpv6_client_set_duid(GDHCPClient *dhcp_client, unsigned char *duid,
675                         int duid_len)
676 {
677         if (dhcp_client == NULL || dhcp_client->type != G_DHCP_IPV6)
678                 return -EINVAL;
679
680         g_free(dhcp_client->duid);
681
682         dhcp_client->duid = duid;
683         dhcp_client->duid_len = duid_len;
684
685         return 0;
686 }
687
688 uint32_t g_dhcpv6_client_get_iaid(GDHCPClient *dhcp_client)
689 {
690         if (dhcp_client == NULL || dhcp_client->type != G_DHCP_IPV6)
691                 return 0;
692
693         return dhcp_client->iaid;
694 }
695
696 void g_dhcpv6_client_create_iaid(GDHCPClient *dhcp_client, int index,
697                                 unsigned char *iaid)
698 {
699         uint8_t buf[6];
700
701         get_interface_mac_address(index, buf);
702
703         memcpy(iaid, &buf[2], 4);
704         dhcp_client->iaid = iaid[0] << 24 |
705                         iaid[1] << 16 | iaid[2] << 8 | iaid[3];
706 }
707
708 int g_dhcpv6_client_get_timeouts(GDHCPClient *dhcp_client,
709                                 uint32_t *T1, uint32_t *T2,
710                                 time_t *last_renew, time_t *last_rebind,
711                                 time_t *expire)
712 {
713         if (dhcp_client == NULL || dhcp_client->type != G_DHCP_IPV6)
714                 return -EINVAL;
715
716         if (T1 != NULL)
717                 *T1 = dhcp_client->T1;
718
719         if (T2 != NULL)
720                 *T2 = dhcp_client->T2;
721
722         if (last_renew != NULL)
723                 *last_renew = dhcp_client->last_renew;
724
725         if (last_rebind != NULL)
726                 *last_rebind = dhcp_client->last_rebind;
727
728         if (expire != NULL)
729                 *expire = dhcp_client->expire;
730
731         return 0;
732 }
733
734 static uint8_t *create_iaaddr(GDHCPClient *dhcp_client, uint8_t *buf,
735                                 uint16_t len)
736 {
737         buf[0] = 0;
738         buf[1] = G_DHCPV6_IAADDR;
739         buf[2] = 0;
740         buf[3] = len;
741         memcpy(&buf[4], &dhcp_client->ia_na, 16);
742         memset(&buf[20], 0, 4); /* preferred */
743         memset(&buf[24], 0, 4); /* valid */
744         return buf;
745 }
746
747 static void put_iaid(GDHCPClient *dhcp_client, int index, uint8_t *buf)
748 {
749         uint32_t iaid;
750
751         iaid = g_dhcpv6_client_get_iaid(dhcp_client);
752         if (iaid == 0) {
753                 g_dhcpv6_client_create_iaid(dhcp_client, index, buf);
754                 return;
755         }
756
757         buf[0] = iaid >> 24;
758         buf[1] = iaid >> 16;
759         buf[2] = iaid >> 8;
760         buf[3] = iaid;
761 }
762
763 int g_dhcpv6_client_set_ia(GDHCPClient *dhcp_client, int index,
764                         int code, uint32_t *T1, uint32_t *T2,
765                         gboolean add_iaaddr, const char *ia_na)
766 {
767         if (code == G_DHCPV6_IA_TA) {
768                 uint8_t ia_options[4];
769
770                 put_iaid(dhcp_client, index, ia_options);
771
772                 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_IA_TA);
773                 g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_IA_TA,
774                                         ia_options, sizeof(ia_options));
775
776         } else if (code == G_DHCPV6_IA_NA) {
777                 struct in6_addr addr;
778
779                 g_dhcp_client_set_request(dhcp_client, G_DHCPV6_IA_NA);
780
781                 /*
782                  * If caller has specified the IPv6 address it wishes to
783                  * to use (ia_na != NULL and address is valid), then send
784                  * the address to server.
785                  * If caller did not specify the address (ia_na == NULL) and
786                  * if the current address is not set, then we should not send
787                  * the address sub-option.
788                  */
789                 if (add_iaaddr == TRUE && ((ia_na == NULL &&
790                         IN6_IS_ADDR_UNSPECIFIED(&dhcp_client->ia_na) == FALSE)
791                         || (ia_na != NULL &&
792                                 inet_pton(AF_INET6, ia_na, &addr) == 1))) {
793 #define IAADDR_LEN (16+4+4)
794                         uint8_t ia_options[4+4+4+2+2+IAADDR_LEN];
795
796                         if (ia_na != NULL)
797                                 memcpy(&dhcp_client->ia_na, &addr,
798                                                 sizeof(struct in6_addr));
799
800                         put_iaid(dhcp_client, index, ia_options);
801
802                         if (T1 != NULL) {
803                                 ia_options[4] = *T1 >> 24;
804                                 ia_options[5] = *T1 >> 16;
805                                 ia_options[6] = *T1 >> 8;
806                                 ia_options[7] = *T1;
807                         } else
808                                 memset(&ia_options[4], 0x00, 4);
809
810                         if (T2 != NULL) {
811                                 ia_options[8] = *T2 >> 24;
812                                 ia_options[9] = *T2 >> 16;
813                                 ia_options[10] = *T2 >> 8;
814                                 ia_options[11] = *T2;
815                         } else
816                                 memset(&ia_options[8], 0x00, 4);
817
818                         create_iaaddr(dhcp_client, &ia_options[12],
819                                         IAADDR_LEN);
820
821                         g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_IA_NA,
822                                         ia_options, sizeof(ia_options));
823                 } else {
824                         uint8_t ia_options[4+4+4];
825
826                         put_iaid(dhcp_client, index, ia_options);
827
828                         memset(&ia_options[4], 0x00, 4); /* T1 (4 bytes) */
829                         memset(&ia_options[8], 0x00, 4); /* T2 (4 bytes) */
830
831                         g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_IA_NA,
832                                         ia_options, sizeof(ia_options));
833                 }
834
835         } else
836                 return -EINVAL;
837
838         return 0;
839 }
840
841 int g_dhcpv6_client_set_oro(GDHCPClient *dhcp_client, int args, ...)
842 {
843         va_list va;
844         int i, j, len = sizeof(uint16_t) * args;
845         uint8_t *values;
846
847         values = g_try_malloc(len);
848         if (values == NULL)
849                 return -ENOMEM;
850
851         va_start(va, args);
852         for (i = 0, j = 0; i < args; i++) {
853                 uint16_t value = va_arg(va, int);
854                 values[j++] = value >> 8;
855                 values[j++] = value & 0xff;
856         }
857         va_end(va);
858
859         g_dhcpv6_client_set_send(dhcp_client, G_DHCPV6_ORO, values, len);
860
861         g_free(values);
862
863         return 0;
864 }
865
866 static int send_dhcpv6_msg(GDHCPClient *dhcp_client, int type, char *msg)
867 {
868         struct dhcpv6_packet *packet;
869         uint8_t buf[MAX_DHCPV6_PKT_SIZE];
870         unsigned char *ptr;
871         int ret, max_buf;
872
873         memset(buf, 0, sizeof(buf));
874         packet = (struct dhcpv6_packet *)&buf[0];
875         ptr = buf + sizeof(struct dhcpv6_packet);
876
877         init_packet(dhcp_client, packet, type);
878
879         if (dhcp_client->retransmit == FALSE) {
880                 dhcp_client->xid = packet->transaction_id[0] << 16 |
881                                 packet->transaction_id[1] << 8 |
882                                 packet->transaction_id[2];
883                 gettimeofday(&dhcp_client->start_time, NULL);
884         } else {
885                 packet->transaction_id[0] = dhcp_client->xid >> 16;
886                 packet->transaction_id[1] = dhcp_client->xid >> 8 ;
887                 packet->transaction_id[2] = dhcp_client->xid;
888         }
889
890         g_dhcp_client_set_request(dhcp_client, G_DHCPV6_ELAPSED_TIME);
891
892         debug(dhcp_client, "sending DHCPv6 %s message xid 0x%04x", msg,
893                                                         dhcp_client->xid);
894
895         max_buf = MAX_DHCPV6_PKT_SIZE - sizeof(struct dhcpv6_packet);
896
897         add_dhcpv6_request_options(dhcp_client, packet, buf, max_buf, &ptr);
898
899         add_dhcpv6_send_options(dhcp_client, buf, max_buf, &ptr);
900
901         ret = dhcpv6_send_packet(dhcp_client->ifindex, packet, ptr - buf);
902
903         debug(dhcp_client, "sent %d pkt %p len %d", ret, packet, ptr - buf);
904         return ret;
905 }
906
907 static int send_solicitation(GDHCPClient *dhcp_client)
908 {
909         return send_dhcpv6_msg(dhcp_client, DHCPV6_SOLICIT, "solicit");
910 }
911
912 static int send_dhcpv6_request(GDHCPClient *dhcp_client)
913 {
914         return send_dhcpv6_msg(dhcp_client, DHCPV6_REQUEST, "request");
915 }
916
917 static int send_dhcpv6_confirm(GDHCPClient *dhcp_client)
918 {
919         return send_dhcpv6_msg(dhcp_client, DHCPV6_CONFIRM, "confirm");
920 }
921
922 static int send_dhcpv6_renew(GDHCPClient *dhcp_client)
923 {
924         return send_dhcpv6_msg(dhcp_client, DHCPV6_RENEW, "renew");
925 }
926
927 static int send_dhcpv6_rebind(GDHCPClient *dhcp_client)
928 {
929         return send_dhcpv6_msg(dhcp_client, DHCPV6_REBIND, "rebind");
930 }
931
932 static int send_dhcpv6_release(GDHCPClient *dhcp_client)
933 {
934         return send_dhcpv6_msg(dhcp_client, DHCPV6_RELEASE, "release");
935 }
936
937 static int send_information_req(GDHCPClient *dhcp_client)
938 {
939         return send_dhcpv6_msg(dhcp_client, DHCPV6_INFORMATION_REQ,
940                                 "information-req");
941 }
942
943 static void remove_value(gpointer data, gpointer user_data)
944 {
945         char *value = data;
946         g_free(value);
947 }
948
949 static void remove_option_value(gpointer data)
950 {
951         GList *option_value = data;
952
953         g_list_foreach(option_value, remove_value, NULL);
954 }
955
956 GDHCPClient *g_dhcp_client_new(GDHCPType type,
957                         int ifindex, GDHCPClientError *error)
958 {
959         GDHCPClient *dhcp_client;
960
961         if (ifindex < 0) {
962                 *error = G_DHCP_CLIENT_ERROR_INVALID_INDEX;
963                 return NULL;
964         }
965
966         dhcp_client = g_try_new0(GDHCPClient, 1);
967         if (dhcp_client == NULL) {
968                 *error = G_DHCP_CLIENT_ERROR_NOMEM;
969                 return NULL;
970         }
971
972         dhcp_client->interface = get_interface_name(ifindex);
973         if (dhcp_client->interface == NULL) {
974                 *error = G_DHCP_CLIENT_ERROR_INTERFACE_UNAVAILABLE;
975                 goto error;
976         }
977
978         if (interface_is_up(ifindex) == FALSE) {
979                 *error = G_DHCP_CLIENT_ERROR_INTERFACE_DOWN;
980                 goto error;
981         }
982
983         get_interface_mac_address(ifindex, dhcp_client->mac_address);
984
985         dhcp_client->listener_sockfd = -1;
986         dhcp_client->listener_channel = NULL;
987         dhcp_client->listen_mode = L_NONE;
988         dhcp_client->ref_count = 1;
989         dhcp_client->type = type;
990         dhcp_client->ifindex = ifindex;
991         dhcp_client->lease_available_cb = NULL;
992         dhcp_client->ipv4ll_available_cb = NULL;
993         dhcp_client->no_lease_cb = NULL;
994         dhcp_client->lease_lost_cb = NULL;
995         dhcp_client->ipv4ll_lost_cb = NULL;
996         dhcp_client->address_conflict_cb = NULL;
997         dhcp_client->listener_watch = 0;
998         dhcp_client->retry_times = 0;
999         dhcp_client->ack_retry_times = 0;
1000         dhcp_client->code_value_hash = g_hash_table_new_full(g_direct_hash,
1001                                 g_direct_equal, NULL, remove_option_value);
1002         dhcp_client->send_value_hash = g_hash_table_new_full(g_direct_hash,
1003                                 g_direct_equal, NULL, g_free);
1004         dhcp_client->request_list = NULL;
1005         dhcp_client->require_list = NULL;
1006         dhcp_client->duid = NULL;
1007         dhcp_client->duid_len = 0;
1008         dhcp_client->last_renew = dhcp_client->last_rebind = time(NULL);
1009         dhcp_client->expire = 0;
1010
1011         *error = G_DHCP_CLIENT_ERROR_NONE;
1012
1013         return dhcp_client;
1014
1015 error:
1016         g_free(dhcp_client->interface);
1017         g_free(dhcp_client);
1018         return NULL;
1019 }
1020
1021 #define SERVER_AND_CLIENT_PORTS  ((67 << 16) + 68)
1022
1023 static int dhcp_l2_socket(int ifindex)
1024 {
1025         int fd;
1026         struct sockaddr_ll sock;
1027
1028         /*
1029          * Comment:
1030          *
1031          *      I've selected not to see LL header, so BPF doesn't see it, too.
1032          *      The filter may also pass non-IP and non-ARP packets, but we do
1033          *      a more complete check when receiving the message in userspace.
1034          *
1035          * and filter shamelessly stolen from:
1036          *
1037          *      http://www.flamewarmaster.de/software/dhcpclient/
1038          *
1039          * There are a few other interesting ideas on that page (look under
1040          * "Motivation").  Use of netlink events is most interesting.  Think
1041          * of various network servers listening for events and reconfiguring.
1042          * That would obsolete sending HUP signals and/or make use of restarts.
1043          *
1044          * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
1045          * License: GPL v2.
1046          *
1047          * TODO: make conditional?
1048          */
1049         static const struct sock_filter filter_instr[] = {
1050                 /* check for udp */
1051                 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
1052                 /* L5, L1, is UDP? */
1053                 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0),
1054                 /* ugly check for arp on ethernet-like and IPv4 */
1055                 BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), /* L1: */
1056                 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4),/* L3, L4 */
1057                 /* skip IP header */
1058                 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* L5: */
1059                 /* check udp source and destination ports */
1060                 BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0),
1061                 /* L3, L4 */
1062                 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1),
1063                 /* returns */
1064                 BPF_STMT(BPF_RET|BPF_K, 0x0fffffff), /* L3: pass */
1065                 BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */
1066         };
1067
1068         static const struct sock_fprog filter_prog = {
1069                 .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
1070                 /* casting const away: */
1071                 .filter = (struct sock_filter *) filter_instr,
1072         };
1073
1074         fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IP));
1075         if (fd < 0)
1076                 return fd;
1077
1078         if (SERVER_PORT == 67 && CLIENT_PORT == 68)
1079                 /* Use only if standard ports are in use */
1080                 setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
1081                                                         sizeof(filter_prog));
1082
1083         memset(&sock, 0, sizeof(sock));
1084         sock.sll_family = AF_PACKET;
1085         sock.sll_protocol = htons(ETH_P_IP);
1086         sock.sll_ifindex = ifindex;
1087
1088         if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) != 0) {
1089                 close(fd);
1090                 return -errno;
1091         }
1092
1093         return fd;
1094 }
1095
1096 static gboolean sanity_check(struct ip_udp_dhcp_packet *packet, int bytes)
1097 {
1098         if (packet->ip.protocol != IPPROTO_UDP)
1099                 return FALSE;
1100
1101         if (packet->ip.version != IPVERSION)
1102                 return FALSE;
1103
1104         if (packet->ip.ihl != sizeof(packet->ip) >> 2)
1105                 return FALSE;
1106
1107         if (packet->udp.dest != htons(CLIENT_PORT))
1108                 return FALSE;
1109
1110         if (ntohs(packet->udp.len) != (uint16_t)(bytes - sizeof(packet->ip)))
1111                 return FALSE;
1112
1113         return TRUE;
1114 }
1115
1116 static int dhcp_recv_l2_packet(struct dhcp_packet *dhcp_pkt, int fd)
1117 {
1118         int bytes;
1119         struct ip_udp_dhcp_packet packet;
1120         uint16_t check;
1121
1122         memset(&packet, 0, sizeof(packet));
1123
1124         bytes = read(fd, &packet, sizeof(packet));
1125         if (bytes < 0)
1126                 return -1;
1127
1128         if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp)))
1129                 return -1;
1130
1131         if (bytes < ntohs(packet.ip.tot_len))
1132                 /* packet is bigger than sizeof(packet), we did partial read */
1133                 return -1;
1134
1135         /* ignore any extra garbage bytes */
1136         bytes = ntohs(packet.ip.tot_len);
1137
1138         if (sanity_check(&packet, bytes) == FALSE)
1139                 return -1;
1140
1141         check = packet.ip.check;
1142         packet.ip.check = 0;
1143         if (check != dhcp_checksum(&packet.ip, sizeof(packet.ip)))
1144                 return -1;
1145
1146         /* verify UDP checksum. IP header has to be modified for this */
1147         memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
1148         /* ip.xx fields which are not memset: protocol, check, saddr, daddr */
1149         packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
1150         check = packet.udp.check;
1151         packet.udp.check = 0;
1152         if (check && check != dhcp_checksum(&packet, bytes))
1153                 return -1;
1154
1155         memcpy(dhcp_pkt, &packet.data, bytes - (sizeof(packet.ip) +
1156                                                         sizeof(packet.udp)));
1157
1158         if (dhcp_pkt->cookie != htonl(DHCP_MAGIC))
1159                 return -1;
1160
1161         return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
1162 }
1163
1164 static void ipv4ll_start(GDHCPClient *dhcp_client)
1165 {
1166         guint timeout;
1167         int seed;
1168
1169         if (dhcp_client->timeout > 0) {
1170                 g_source_remove(dhcp_client->timeout);
1171                 dhcp_client->timeout = 0;
1172         }
1173
1174         switch_listening_mode(dhcp_client, L_NONE);
1175         dhcp_client->type = G_DHCP_IPV4LL;
1176         dhcp_client->retry_times = 0;
1177         dhcp_client->requested_ip = 0;
1178
1179         /*try to start with a based mac address ip*/
1180         seed = (dhcp_client->mac_address[4] << 8 | dhcp_client->mac_address[4]);
1181         dhcp_client->requested_ip = ipv4ll_random_ip(seed);
1182
1183         /*first wait a random delay to avoid storm of arp request on boot*/
1184         timeout = ipv4ll_random_delay_ms(PROBE_WAIT);
1185
1186         dhcp_client->retry_times++;
1187         dhcp_client->timeout = g_timeout_add_full(G_PRIORITY_HIGH,
1188                                                 timeout,
1189                                                 send_probe_packet,
1190                                                 dhcp_client,
1191                                                 NULL);
1192 }
1193
1194 static void ipv4ll_stop(GDHCPClient *dhcp_client)
1195 {
1196
1197         switch_listening_mode(dhcp_client, L_NONE);
1198
1199         if (dhcp_client->timeout > 0)
1200                 g_source_remove(dhcp_client->timeout);
1201
1202         if (dhcp_client->listener_watch > 0) {
1203                 g_source_remove(dhcp_client->listener_watch);
1204                 dhcp_client->listener_watch = 0;
1205         }
1206
1207         dhcp_client->state = IPV4LL_PROBE;
1208         dhcp_client->retry_times = 0;
1209         dhcp_client->requested_ip = 0;
1210
1211         g_free(dhcp_client->assigned_ip);
1212         dhcp_client->assigned_ip = NULL;
1213 }
1214
1215 static int ipv4ll_recv_arp_packet(GDHCPClient *dhcp_client)
1216 {
1217         int bytes;
1218         struct ether_arp arp;
1219         uint32_t ip_requested;
1220         int source_conflict;
1221         int target_conflict;
1222
1223         memset(&arp, 0, sizeof(arp));
1224         bytes = read(dhcp_client->listener_sockfd, &arp, sizeof(arp));
1225         if (bytes < 0)
1226                 return bytes;
1227
1228         if (arp.arp_op != htons(ARPOP_REPLY) &&
1229                         arp.arp_op != htons(ARPOP_REQUEST))
1230                 return -EINVAL;
1231
1232         ip_requested = htonl(dhcp_client->requested_ip);
1233         source_conflict = !memcmp(arp.arp_spa, &ip_requested,
1234                                                 sizeof(ip_requested));
1235
1236         target_conflict = !memcmp(arp.arp_tpa, &ip_requested,
1237                                 sizeof(ip_requested));
1238
1239         if (!source_conflict && !target_conflict)
1240                 return 0;
1241
1242         dhcp_client->conflicts++;
1243
1244         debug(dhcp_client, "IPV4LL conflict detected");
1245
1246         if (dhcp_client->state == IPV4LL_MONITOR) {
1247                 if (!source_conflict)
1248                         return 0;
1249                 dhcp_client->state = IPV4LL_DEFEND;
1250                 debug(dhcp_client, "DEFEND mode conflicts : %d",
1251                         dhcp_client->conflicts);
1252                 /*Try to defend with a single announce*/
1253                 send_announce_packet(dhcp_client);
1254                 return 0;
1255         }
1256
1257         if (dhcp_client->state == IPV4LL_DEFEND) {
1258                 if (!source_conflict)
1259                         return 0;
1260                 else if (dhcp_client->ipv4ll_lost_cb != NULL)
1261                         dhcp_client->ipv4ll_lost_cb(dhcp_client,
1262                                                 dhcp_client->ipv4ll_lost_data);
1263         }
1264
1265         ipv4ll_stop(dhcp_client);
1266
1267         if (dhcp_client->conflicts < MAX_CONFLICTS) {
1268                 /*restart whole state machine*/
1269                 dhcp_client->retry_times++;
1270                 dhcp_client->timeout =
1271                         g_timeout_add_full(G_PRIORITY_HIGH,
1272                                         ipv4ll_random_delay_ms(PROBE_WAIT),
1273                                         send_probe_packet,
1274                                         dhcp_client,
1275                                         NULL);
1276         }
1277         /* Here we got a lot of conflicts, RFC3927 states that we have
1278          * to wait RATE_LIMIT_INTERVAL before retrying,
1279          * but we just report failure.
1280          */
1281         else if (dhcp_client->no_lease_cb != NULL)
1282                         dhcp_client->no_lease_cb(dhcp_client,
1283                                                 dhcp_client->no_lease_data);
1284
1285         return 0;
1286 }
1287
1288 static gboolean check_package_owner(GDHCPClient *dhcp_client, gpointer pkt)
1289 {
1290         if (dhcp_client->type == G_DHCP_IPV6) {
1291                 struct dhcpv6_packet *packet6 = pkt;
1292                 uint32_t xid;
1293
1294                 if (packet6 == NULL)
1295                         return FALSE;
1296
1297                 xid = packet6->transaction_id[0] << 16 |
1298                         packet6->transaction_id[1] << 8 |
1299                         packet6->transaction_id[2];
1300
1301                 if (xid != dhcp_client->xid)
1302                         return FALSE;
1303         } else {
1304                 struct dhcp_packet *packet = pkt;
1305
1306                 if (packet->xid != dhcp_client->xid)
1307                         return FALSE;
1308
1309                 if (packet->hlen != 6)
1310                         return FALSE;
1311
1312                 if (memcmp(packet->chaddr, dhcp_client->mac_address, 6))
1313                         return FALSE;
1314         }
1315
1316         return TRUE;
1317 }
1318
1319 static void start_request(GDHCPClient *dhcp_client);
1320
1321 static gboolean request_timeout(gpointer user_data)
1322 {
1323         GDHCPClient *dhcp_client = user_data;
1324
1325         debug(dhcp_client, "request timeout (retries %d)",
1326                                         dhcp_client->retry_times);
1327
1328         dhcp_client->retry_times++;
1329
1330         start_request(dhcp_client);
1331
1332         return FALSE;
1333 }
1334
1335 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
1336                                                         gpointer user_data);
1337
1338 static int switch_listening_mode(GDHCPClient *dhcp_client,
1339                                         ListenMode listen_mode)
1340 {
1341         GIOChannel *listener_channel;
1342         int listener_sockfd;
1343
1344         if (dhcp_client->listen_mode == listen_mode)
1345                 return 0;
1346
1347         debug(dhcp_client, "switch listening mode (%d ==> %d)",
1348                                 dhcp_client->listen_mode, listen_mode);
1349
1350         if (dhcp_client->listen_mode != L_NONE) {
1351                 if (dhcp_client->listener_watch > 0)
1352                         g_source_remove(dhcp_client->listener_watch);
1353                 dhcp_client->listener_channel = NULL;
1354                 dhcp_client->listen_mode = L_NONE;
1355                 dhcp_client->listener_sockfd = -1;
1356                 dhcp_client->listener_watch = 0;
1357         }
1358
1359         if (listen_mode == L_NONE)
1360                 return 0;
1361
1362         if (listen_mode == L2)
1363                 listener_sockfd = dhcp_l2_socket(dhcp_client->ifindex);
1364         else if (listen_mode == L3) {
1365                 if (dhcp_client->type == G_DHCP_IPV6)
1366                         listener_sockfd = dhcp_l3_socket(DHCPV6_CLIENT_PORT,
1367                                                         dhcp_client->interface,
1368                                                         AF_INET6);
1369                 else
1370                         listener_sockfd = dhcp_l3_socket(CLIENT_PORT,
1371                                                         dhcp_client->interface,
1372                                                         AF_INET);
1373         } else if (listen_mode == L_ARP)
1374                 listener_sockfd = ipv4ll_arp_socket(dhcp_client->ifindex);
1375         else
1376                 return -EIO;
1377
1378         if (listener_sockfd < 0)
1379                 return -EIO;
1380
1381         listener_channel = g_io_channel_unix_new(listener_sockfd);
1382         if (listener_channel == NULL) {
1383                 /* Failed to create listener channel */
1384                 close(listener_sockfd);
1385                 return -EIO;
1386         }
1387
1388         dhcp_client->listen_mode = listen_mode;
1389         dhcp_client->listener_sockfd = listener_sockfd;
1390         dhcp_client->listener_channel = listener_channel;
1391
1392         g_io_channel_set_close_on_unref(listener_channel, TRUE);
1393         dhcp_client->listener_watch =
1394                         g_io_add_watch_full(listener_channel, G_PRIORITY_HIGH,
1395                                 G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP,
1396                                                 listener_event, dhcp_client,
1397                                                                 NULL);
1398         g_io_channel_unref(dhcp_client->listener_channel);
1399
1400         return 0;
1401 }
1402
1403 static void start_request(GDHCPClient *dhcp_client)
1404 {
1405         debug(dhcp_client, "start request (retries %d)",
1406                                         dhcp_client->retry_times);
1407
1408         if (dhcp_client->retry_times == REQUEST_RETRIES) {
1409                 dhcp_client->state = INIT_SELECTING;
1410                 ipv4ll_start(dhcp_client);
1411
1412                 return;
1413         }
1414
1415         if (dhcp_client->retry_times == 0) {
1416                 dhcp_client->state = REQUESTING;
1417                 switch_listening_mode(dhcp_client, L2);
1418         }
1419
1420         send_select(dhcp_client);
1421
1422         dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1423                                                         REQUEST_TIMEOUT,
1424                                                         request_timeout,
1425                                                         dhcp_client,
1426                                                         NULL);
1427 }
1428
1429 static uint32_t get_lease(struct dhcp_packet *packet)
1430 {
1431         uint8_t *option;
1432         uint32_t lease_seconds;
1433
1434         option = dhcp_get_option(packet, DHCP_LEASE_TIME);
1435         if (option == NULL)
1436                 return 3600;
1437
1438         lease_seconds = get_be32(option);
1439         /* paranoia: must not be prone to overflows */
1440         lease_seconds &= 0x0fffffff;
1441         if (lease_seconds < 10)
1442                 lease_seconds = 10;
1443
1444         return lease_seconds;
1445 }
1446
1447 static void restart_dhcp(GDHCPClient *dhcp_client, int retry_times)
1448 {
1449         debug(dhcp_client, "restart DHCP (retries %d)", retry_times);
1450
1451         if (dhcp_client->timeout > 0) {
1452                 g_source_remove(dhcp_client->timeout);
1453                 dhcp_client->timeout = 0;
1454         }
1455
1456         dhcp_client->retry_times = retry_times;
1457         dhcp_client->requested_ip = 0;
1458         dhcp_client->state = INIT_SELECTING;
1459         switch_listening_mode(dhcp_client, L2);
1460
1461         g_dhcp_client_start(dhcp_client, dhcp_client->last_address);
1462 }
1463
1464 static gboolean start_rebound_timeout(gpointer user_data)
1465 {
1466         GDHCPClient *dhcp_client = user_data;
1467
1468         debug(dhcp_client, "start rebound timeout");
1469
1470         switch_listening_mode(dhcp_client, L2);
1471
1472         dhcp_client->lease_seconds >>= 1;
1473
1474         /* We need to have enough time to receive ACK package*/
1475         if (dhcp_client->lease_seconds <= 6) {
1476
1477                 /* ip need to be cleared */
1478                 if (dhcp_client->lease_lost_cb != NULL)
1479                         dhcp_client->lease_lost_cb(dhcp_client,
1480                                         dhcp_client->lease_lost_data);
1481
1482                 restart_dhcp(dhcp_client, 0);
1483         } else {
1484                 send_rebound(dhcp_client);
1485
1486                 dhcp_client->timeout =
1487                                 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1488                                                 dhcp_client->lease_seconds >> 1,
1489                                                         start_rebound_timeout,
1490                                                                 dhcp_client,
1491                                                                 NULL);
1492         }
1493
1494         return FALSE;
1495 }
1496
1497 static void start_rebound(GDHCPClient *dhcp_client)
1498 {
1499         debug(dhcp_client, "start rebound");
1500
1501         dhcp_client->state = REBINDING;
1502
1503         dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1504                                                 dhcp_client->lease_seconds >> 1,
1505                                                         start_rebound_timeout,
1506                                                                 dhcp_client,
1507                                                                 NULL);
1508 }
1509
1510 static gboolean start_renew_timeout(gpointer user_data)
1511 {
1512         GDHCPClient *dhcp_client = user_data;
1513
1514         debug(dhcp_client, "start renew timeout");
1515
1516         dhcp_client->state = RENEWING;
1517
1518         dhcp_client->lease_seconds >>= 1;
1519
1520         switch_listening_mode(dhcp_client, L3);
1521         if (dhcp_client->lease_seconds <= 60)
1522                 start_rebound(dhcp_client);
1523         else {
1524                 send_renew(dhcp_client);
1525
1526                 if (dhcp_client->timeout > 0)
1527                         g_source_remove(dhcp_client->timeout);
1528
1529                 dhcp_client->timeout =
1530                                 g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1531                                                 dhcp_client->lease_seconds >> 1,
1532                                                         start_renew_timeout,
1533                                                                 dhcp_client,
1534                                                                 NULL);
1535         }
1536
1537         return FALSE;
1538 }
1539
1540 static void start_bound(GDHCPClient *dhcp_client)
1541 {
1542         debug(dhcp_client, "start bound");
1543
1544         dhcp_client->state = BOUND;
1545
1546         if (dhcp_client->timeout > 0)
1547                 g_source_remove(dhcp_client->timeout);
1548
1549         dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
1550                                         dhcp_client->lease_seconds >> 1,
1551                                         start_renew_timeout, dhcp_client,
1552                                                         NULL);
1553 }
1554
1555 static gboolean restart_dhcp_timeout(gpointer user_data)
1556 {
1557         GDHCPClient *dhcp_client = user_data;
1558
1559         debug(dhcp_client, "restart DHCP timeout");
1560
1561         dhcp_client->ack_retry_times++;
1562
1563         restart_dhcp(dhcp_client, dhcp_client->ack_retry_times);
1564
1565         return FALSE;
1566 }
1567
1568 static char *get_ip(uint32_t ip)
1569 {
1570         struct in_addr addr;
1571
1572         addr.s_addr = ip;
1573
1574         return g_strdup(inet_ntoa(addr));
1575 }
1576
1577 /* get a rough idea of how long an option will be */
1578 static const uint8_t len_of_option_as_string[] = {
1579         [OPTION_IP] = sizeof("255.255.255.255 "),
1580         [OPTION_STRING] = 1,
1581         [OPTION_U8] = sizeof("255 "),
1582         [OPTION_U16] = sizeof("65535 "),
1583         [OPTION_U32] = sizeof("4294967295 "),
1584 };
1585
1586 static int sprint_nip(char *dest, const char *pre, const uint8_t *ip)
1587 {
1588         return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]);
1589 }
1590
1591 /* Create "opt_value1 option_value2 ..." string */
1592 static char *malloc_option_value_string(uint8_t *option, GDHCPOptionType type)
1593 {
1594         unsigned upper_length;
1595         int len, optlen;
1596         char *dest, *ret;
1597
1598         len = option[OPT_LEN - OPT_DATA];
1599         type &= OPTION_TYPE_MASK;
1600         optlen = dhcp_option_lengths[type];
1601         if (optlen == 0)
1602                 return NULL;
1603         upper_length = len_of_option_as_string[type] *
1604                         ((unsigned)len / (unsigned)optlen);
1605         dest = ret = g_malloc(upper_length + 1);
1606         if (ret == NULL)
1607                 return NULL;
1608
1609         while (len >= optlen) {
1610                 switch (type) {
1611                 case OPTION_IP:
1612                         dest += sprint_nip(dest, "", option);
1613                         break;
1614                 case OPTION_U16: {
1615                         uint16_t val_u16 = get_be16(option);
1616                         dest += sprintf(dest, "%u", val_u16);
1617                         break;
1618                 }
1619                 case OPTION_U32: {
1620                         uint32_t val_u32 = get_be32(option);
1621                         dest += sprintf(dest, "%u", val_u32);
1622                         break;
1623                 }
1624                 case OPTION_STRING:
1625                         memcpy(dest, option, len);
1626                         dest[len] = '\0';
1627                         return ret;
1628                 default:
1629                         break;
1630                 }
1631                 option += optlen;
1632                 len -= optlen;
1633                 if (len <= 0)
1634                         break;
1635                 *dest++ = ' ';
1636                 *dest = '\0';
1637         }
1638
1639         return ret;
1640 }
1641
1642 static GList *get_option_value_list(char *value, GDHCPOptionType type)
1643 {
1644         char *pos = value;
1645         GList *list = NULL;
1646
1647         if (pos == NULL)
1648                 return NULL;
1649
1650         if (type == OPTION_STRING)
1651                 return g_list_append(list, g_strdup(value));
1652
1653         while ((pos = strchr(pos, ' ')) != NULL) {
1654                 *pos = '\0';
1655
1656                 list = g_list_append(list, g_strdup(value));
1657
1658                 value = ++pos;
1659         }
1660
1661         list = g_list_append(list, g_strdup(value));
1662
1663         return list;
1664 }
1665
1666 static inline uint32_t get_uint32(unsigned char *value)
1667 {
1668         return value[0] << 24 | value[1] << 16 |
1669                 value[2] << 8 | value[3];
1670 }
1671
1672 static inline uint16_t get_uint16(unsigned char *value)
1673 {
1674         return value[0] << 8 | value[1];
1675 }
1676
1677 static GList *get_addresses(GDHCPClient *dhcp_client,
1678                                 int code, int len,
1679                                 unsigned char *value,
1680                                 uint16_t *status)
1681 {
1682         GList *list = NULL;
1683         struct in6_addr addr;
1684         uint32_t iaid, T1 = 0, T2 = 0, preferred = 0, valid = 0;
1685         uint16_t option_len, option_code, st = 0, max_len;
1686         int addr_count = 0, i, pos;
1687         uint8_t *option;
1688         char *str;
1689
1690         if (value == NULL || len < 4)
1691                 return NULL;
1692
1693         iaid = get_uint32(&value[0]);
1694         if (dhcp_client->iaid != iaid)
1695                 return NULL;
1696
1697         if (code == G_DHCPV6_IA_NA) {
1698                 T1 = get_uint32(&value[4]);
1699                 T2 = get_uint32(&value[8]);
1700
1701                 if (T1 > T2)
1702                         /* RFC 3315, 22.4 */
1703                         return NULL;
1704
1705                 pos = 12;
1706         } else
1707                 pos = 4;
1708
1709         if (len <= pos)
1710                 return NULL;
1711
1712         max_len = len - pos;
1713
1714         /* We have more sub-options in this packet. */
1715         do {
1716                 option = dhcpv6_get_sub_option(&value[pos], max_len,
1717                                         &option_code, &option_len);
1718
1719                 debug(dhcp_client, "pos %d option %p code %d len %d",
1720                         pos, option, option_code, option_len);
1721
1722                 if (option == NULL)
1723                         break;
1724
1725                 if (pos >= max_len)
1726                         break;
1727
1728                 switch (option_code) {
1729                 case G_DHCPV6_IAADDR:
1730                         i = 0;
1731                         memcpy(&addr, &option[0], sizeof(addr));
1732                         i += sizeof(addr);
1733                         preferred = get_uint32(&option[i]);
1734                         i += 4;
1735                         valid = get_uint32(&option[i]);
1736
1737                         addr_count++;
1738                         break;
1739
1740                 case G_DHCPV6_STATUS_CODE:
1741                         st = get_uint16(&option[0]);
1742                         debug(dhcp_client, "error code %d", st);
1743                         if (option_len > 2) {
1744                                 str = g_strndup((gchar *)&option[2],
1745                                                 option_len - 2);
1746                                 debug(dhcp_client, "error text: %s", str);
1747                                 g_free(str);
1748                         }
1749
1750                         *status = st;
1751                         break;
1752                 }
1753
1754                 pos += 2 + 2 + option_len;
1755
1756         } while (option != NULL);
1757
1758         if (addr_count > 0 && st == 0) {
1759                 /* We only support one address atm */
1760                 char addr_str[INET6_ADDRSTRLEN + 1];
1761
1762                 if (preferred > valid)
1763                         /* RFC 3315, 22.6 */
1764                         return NULL;
1765
1766                 dhcp_client->T1 = T1;
1767                 dhcp_client->T2 = T2;
1768
1769                 inet_ntop(AF_INET6, &addr, addr_str, INET6_ADDRSTRLEN);
1770                 debug(dhcp_client, "count %d addr %s T1 %u T2 %u",
1771                         addr_count, addr_str, T1, T2);
1772
1773                 list = g_list_append(list, g_strdup(addr_str));
1774
1775                 if (code == G_DHCPV6_IA_NA)
1776                         memcpy(&dhcp_client->ia_na, &addr,
1777                                                 sizeof(struct in6_addr));
1778                 else
1779                         memcpy(&dhcp_client->ia_ta, &addr,
1780                                                 sizeof(struct in6_addr));
1781
1782                 g_dhcpv6_client_set_expire(dhcp_client, valid);
1783         }
1784
1785         return list;
1786 }
1787
1788 static GList *get_dhcpv6_option_value_list(GDHCPClient *dhcp_client,
1789                                         int code, int len,
1790                                         unsigned char *value,
1791                                         uint16_t *status)
1792 {
1793         GList *list = NULL;
1794         char *str;
1795         int i;
1796
1797         if (value == NULL)
1798                 return NULL;
1799
1800         switch (code) {
1801         case G_DHCPV6_DNS_SERVERS:      /* RFC 3646, chapter 3 */
1802         case G_DHCPV6_SNTP_SERVERS:     /* RFC 4075, chapter 4 */
1803                 if (len % 16) {
1804                         debug(dhcp_client,
1805                                 "%s server list length (%d) is invalid",
1806                                 code == G_DHCPV6_DNS_SERVERS ? "DNS" : "SNTP",
1807                                 len);
1808                         return NULL;
1809                 }
1810                 for (i = 0; i < len; i += 16) {
1811
1812                         str = g_try_malloc0(INET6_ADDRSTRLEN+1);
1813                         if (str == NULL)
1814                                 return list;
1815
1816                         if (inet_ntop(AF_INET6, &value[i], str,
1817                                         INET6_ADDRSTRLEN) == NULL)
1818                                 g_free(str);
1819                         else
1820                                 list = g_list_append(list, str);
1821                 }
1822                 break;
1823
1824         case G_DHCPV6_IA_NA:            /* RFC 3315, chapter 22.4 */
1825         case G_DHCPV6_IA_TA:            /* RFC 3315, chapter 22.5 */
1826                 list = get_addresses(dhcp_client, code, len, value, status);
1827                 break;
1828
1829         default:
1830                 break;
1831         }
1832
1833         return list;
1834 }
1835
1836 static void get_dhcpv6_request(GDHCPClient *dhcp_client,
1837                                 struct dhcpv6_packet *packet,
1838                                 uint16_t pkt_len, uint16_t *status)
1839 {
1840         GList *list, *value_list;
1841         uint8_t *option;
1842         uint16_t code;
1843         uint16_t option_len;
1844
1845         for (list = dhcp_client->request_list; list; list = list->next) {
1846                 code = (uint16_t) GPOINTER_TO_INT(list->data);
1847
1848                 option = dhcpv6_get_option(packet, pkt_len, code, &option_len,
1849                                                 NULL);
1850                 if (option == NULL) {
1851                         g_hash_table_remove(dhcp_client->code_value_hash,
1852                                                 GINT_TO_POINTER((int) code));
1853                         continue;
1854                 }
1855
1856                 value_list = get_dhcpv6_option_value_list(dhcp_client, code,
1857                                                 option_len, option, status);
1858
1859                 debug(dhcp_client, "code %d %p len %d list %p", code, option,
1860                         option_len, value_list);
1861
1862                 if (value_list == NULL)
1863                         g_hash_table_remove(dhcp_client->code_value_hash,
1864                                                 GINT_TO_POINTER((int) code));
1865                 else
1866                         g_hash_table_insert(dhcp_client->code_value_hash,
1867                                 GINT_TO_POINTER((int) code), value_list);
1868         }
1869 }
1870
1871 static void get_request(GDHCPClient *dhcp_client, struct dhcp_packet *packet)
1872 {
1873         GDHCPOptionType type;
1874         GList *list, *value_list;
1875         char *option_value;
1876         uint8_t *option;
1877         uint8_t code;
1878
1879         for (list = dhcp_client->request_list; list; list = list->next) {
1880                 code = (uint8_t) GPOINTER_TO_INT(list->data);
1881
1882                 option = dhcp_get_option(packet, code);
1883                 if (option == NULL) {
1884                         g_hash_table_remove(dhcp_client->code_value_hash,
1885                                                 GINT_TO_POINTER((int) code));
1886                         continue;
1887                 }
1888
1889                 type =  dhcp_get_code_type(code);
1890
1891                 option_value = malloc_option_value_string(option, type);
1892                 if (option_value == NULL)
1893                         g_hash_table_remove(dhcp_client->code_value_hash,
1894                                                 GINT_TO_POINTER((int) code));
1895
1896                 value_list = get_option_value_list(option_value, type);
1897
1898                 g_free(option_value);
1899
1900                 if (value_list == NULL)
1901                         g_hash_table_remove(dhcp_client->code_value_hash,
1902                                                 GINT_TO_POINTER((int) code));
1903                 else
1904                         g_hash_table_insert(dhcp_client->code_value_hash,
1905                                 GINT_TO_POINTER((int) code), value_list);
1906         }
1907 }
1908
1909 static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
1910                                                         gpointer user_data)
1911 {
1912         GDHCPClient *dhcp_client = user_data;
1913         struct dhcp_packet packet;
1914         struct dhcpv6_packet *packet6 = NULL;
1915         uint8_t *message_type = NULL, *client_id = NULL, *option,
1916                 *server_id = NULL;
1917         uint16_t option_len = 0, status = 0;
1918         uint32_t xid = 0;
1919         gpointer pkt;
1920         unsigned char buf[MAX_DHCPV6_PKT_SIZE];
1921         uint16_t pkt_len = 0;
1922         int count;
1923         int re;
1924
1925         if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
1926                 dhcp_client->listener_watch = 0;
1927                 return FALSE;
1928         }
1929
1930         if (dhcp_client->listen_mode == L_NONE)
1931                 return FALSE;
1932
1933         pkt = &packet;
1934
1935         dhcp_client->status_code = 0;
1936
1937         if (dhcp_client->listen_mode == L2)
1938                 re = dhcp_recv_l2_packet(&packet,
1939                                         dhcp_client->listener_sockfd);
1940         else if (dhcp_client->listen_mode == L3) {
1941                 if (dhcp_client->type == G_DHCP_IPV6) {
1942                         re = dhcpv6_recv_l3_packet(&packet6, buf, sizeof(buf),
1943                                                 dhcp_client->listener_sockfd);
1944                         pkt_len = re;
1945                         pkt = packet6;
1946                         xid = packet6->transaction_id[0] << 16 |
1947                                 packet6->transaction_id[1] << 8 |
1948                                 packet6->transaction_id[2];
1949                 } else {
1950                         re = dhcp_recv_l3_packet(&packet,
1951                                                 dhcp_client->listener_sockfd);
1952                         xid = packet.xid;
1953                 }
1954         } else if (dhcp_client->listen_mode == L_ARP) {
1955                 ipv4ll_recv_arp_packet(dhcp_client);
1956                 return TRUE;
1957         }
1958         else
1959                 re = -EIO;
1960
1961         if (re < 0)
1962                 return TRUE;
1963
1964         if (check_package_owner(dhcp_client, pkt) == FALSE)
1965                 return TRUE;
1966
1967         if (dhcp_client->type == G_DHCP_IPV6) {
1968                 if (packet6 == NULL)
1969                         return TRUE;
1970
1971                 count = 0;
1972                 client_id = dhcpv6_get_option(packet6, pkt_len,
1973                                 G_DHCPV6_CLIENTID, &option_len, &count);
1974
1975                 if (client_id == NULL || count == 0 || option_len == 0 ||
1976                                 memcmp(dhcp_client->duid, client_id,
1977                                         dhcp_client->duid_len) != 0) {
1978                         debug(dhcp_client,
1979                                 "client duid error, discarding msg %p/%d/%d",
1980                                 client_id, option_len, count);
1981                         return TRUE;
1982                 }
1983
1984                 option = dhcpv6_get_option(packet6, pkt_len,
1985                                 G_DHCPV6_STATUS_CODE, &option_len, NULL);
1986                 if (option != 0 && option_len > 0) {
1987                         status = option[0]<<8 | option[1];
1988                         if (status != 0) {
1989                                 debug(dhcp_client, "error code %d", status);
1990                                 if (option_len > 2) {
1991                                         gchar *txt = g_strndup(
1992                                                 (gchar *)&option[2],
1993                                                 option_len - 2);
1994                                         debug(dhcp_client, "error text: %s",
1995                                                 txt);
1996                                         g_free(txt);
1997                                 }
1998                         }
1999                         dhcp_client->status_code = status;
2000                 }
2001         } else {
2002                 message_type = dhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
2003                 if (message_type == NULL)
2004                         return TRUE;
2005         }
2006
2007         if (message_type == NULL && client_id == NULL)
2008                 /* No message type / client id option, ignore package */
2009                 return TRUE;
2010
2011         debug(dhcp_client, "received DHCP packet xid 0x%04x "
2012                         "(current state %d)", xid, dhcp_client->state);
2013
2014         switch (dhcp_client->state) {
2015         case INIT_SELECTING:
2016                 if (*message_type != DHCPOFFER)
2017                         return TRUE;
2018
2019                 g_source_remove(dhcp_client->timeout);
2020                 dhcp_client->timeout = 0;
2021                 dhcp_client->retry_times = 0;
2022
2023                 option = dhcp_get_option(&packet, DHCP_SERVER_ID);
2024                 dhcp_client->server_ip = get_be32(option);
2025                 dhcp_client->requested_ip = ntohl(packet.yiaddr);
2026
2027                 dhcp_client->state = REQUESTING;
2028
2029                 start_request(dhcp_client);
2030
2031                 return TRUE;
2032         case REQUESTING:
2033         case RENEWING:
2034         case REBINDING:
2035                 if (*message_type == DHCPACK) {
2036                         dhcp_client->retry_times = 0;
2037
2038                         if (dhcp_client->timeout > 0)
2039                                 g_source_remove(dhcp_client->timeout);
2040                         dhcp_client->timeout = 0;
2041
2042                         dhcp_client->lease_seconds = get_lease(&packet);
2043
2044                         get_request(dhcp_client, &packet);
2045
2046                         switch_listening_mode(dhcp_client, L_NONE);
2047
2048                         g_free(dhcp_client->assigned_ip);
2049                         dhcp_client->assigned_ip = get_ip(packet.yiaddr);
2050
2051                         /* Address should be set up here */
2052                         if (dhcp_client->lease_available_cb != NULL)
2053                                 dhcp_client->lease_available_cb(dhcp_client,
2054                                         dhcp_client->lease_available_data);
2055
2056                         start_bound(dhcp_client);
2057                 } else if (*message_type == DHCPNAK) {
2058                         dhcp_client->retry_times = 0;
2059
2060                         if (dhcp_client->timeout > 0)
2061                                 g_source_remove(dhcp_client->timeout);
2062
2063                         dhcp_client->timeout = g_timeout_add_seconds_full(
2064                                                         G_PRIORITY_HIGH, 3,
2065                                                         restart_dhcp_timeout,
2066                                                         dhcp_client,
2067                                                         NULL);
2068                 }
2069
2070                 break;
2071         case SOLICITATION:
2072                 if (dhcp_client->type != G_DHCP_IPV6)
2073                         return TRUE;
2074
2075                 if (packet6->message != DHCPV6_REPLY &&
2076                                 packet6->message != DHCPV6_ADVERTISE)
2077                         return TRUE;
2078
2079                 count = 0;
2080                 server_id = dhcpv6_get_option(packet6, pkt_len,
2081                                 G_DHCPV6_SERVERID, &option_len, &count);
2082                 if (server_id == NULL || count != 1 || option_len == 0) {
2083                         /* RFC 3315, 15.10 */
2084                         debug(dhcp_client,
2085                                 "server duid error, discarding msg %p/%d/%d",
2086                                 server_id, option_len, count);
2087                         return TRUE;
2088                 }
2089                 dhcp_client->server_duid = g_try_malloc(option_len);
2090                 if (dhcp_client->server_duid == NULL)
2091                         return TRUE;
2092                 memcpy(dhcp_client->server_duid, server_id, option_len);
2093                 dhcp_client->server_duid_len = option_len;
2094
2095                 if (packet6->message == DHCPV6_REPLY) {
2096                         uint8_t *rapid_commit;
2097                         count = 0;
2098                         option_len = 0;
2099                         rapid_commit = dhcpv6_get_option(packet6, pkt_len,
2100                                                         G_DHCPV6_RAPID_COMMIT,
2101                                                         &option_len, &count);
2102                         if (rapid_commit == NULL || option_len == 0 ||
2103                                                                 count != 1)
2104                                 /* RFC 3315, 17.1.4 */
2105                                 return TRUE;
2106                 }
2107
2108                 switch_listening_mode(dhcp_client, L_NONE);
2109
2110                 if (dhcp_client->status_code == 0)
2111                         get_dhcpv6_request(dhcp_client, packet6, pkt_len,
2112                                         &dhcp_client->status_code);
2113
2114                 if (packet6->message == DHCPV6_ADVERTISE) {
2115                         if (dhcp_client->advertise_cb != NULL)
2116                                 dhcp_client->advertise_cb(dhcp_client,
2117                                                 dhcp_client->advertise_data);
2118                         return TRUE;
2119                 }
2120
2121                 if (dhcp_client->solicitation_cb != NULL) {
2122                         /*
2123                          * The dhcp_client might not be valid after the
2124                          * callback call so just return immediately.
2125                          */
2126                         dhcp_client->solicitation_cb(dhcp_client,
2127                                         dhcp_client->solicitation_data);
2128                         return TRUE;
2129                 }
2130                 break;
2131         case INFORMATION_REQ:
2132         case REQUEST:
2133         case RENEW:
2134         case REBIND:
2135         case RELEASE:
2136         case CONFIRM:
2137                 if (dhcp_client->type != G_DHCP_IPV6)
2138                         return TRUE;
2139
2140                 if (packet6->message != DHCPV6_REPLY)
2141                         return TRUE;
2142
2143                 count = 0;
2144                 option_len = 0;
2145                 server_id = dhcpv6_get_option(packet6, pkt_len,
2146                                 G_DHCPV6_SERVERID, &option_len, &count);
2147                 if (server_id == NULL || count != 1 || option_len == 0 ||
2148                                 (dhcp_client->server_duid_len > 0 &&
2149                                 memcmp(dhcp_client->server_duid, server_id,
2150                                         dhcp_client->server_duid_len) != 0)) {
2151                         /* RFC 3315, 15.10 */
2152                         debug(dhcp_client,
2153                                 "server duid error, discarding msg %p/%d/%d",
2154                                 server_id, option_len, count);
2155                         return TRUE;
2156                 }
2157
2158                 switch_listening_mode(dhcp_client, L_NONE);
2159
2160                 get_dhcpv6_request(dhcp_client, packet6, pkt_len,
2161                                                 &dhcp_client->status_code);
2162
2163                 if (dhcp_client->information_req_cb != NULL) {
2164                         /*
2165                          * The dhcp_client might not be valid after the
2166                          * callback call so just return immediately.
2167                          */
2168                         dhcp_client->information_req_cb(dhcp_client,
2169                                         dhcp_client->information_req_data);
2170                         return TRUE;
2171                 }
2172                 if (dhcp_client->request_cb != NULL) {
2173                         dhcp_client->request_cb(dhcp_client,
2174                                         dhcp_client->request_data);
2175                         return TRUE;
2176                 }
2177                 if (dhcp_client->renew_cb != NULL) {
2178                         dhcp_client->renew_cb(dhcp_client,
2179                                         dhcp_client->renew_data);
2180                         return TRUE;
2181                 }
2182                 if (dhcp_client->rebind_cb != NULL) {
2183                         dhcp_client->rebind_cb(dhcp_client,
2184                                         dhcp_client->rebind_data);
2185                         return TRUE;
2186                 }
2187                 if (dhcp_client->release_cb != NULL) {
2188                         dhcp_client->release_cb(dhcp_client,
2189                                         dhcp_client->release_data);
2190                         return TRUE;
2191                 }
2192                 if (dhcp_client->confirm_cb != NULL) {
2193                         count = 0;
2194                         server_id = dhcpv6_get_option(packet6, pkt_len,
2195                                                 G_DHCPV6_SERVERID, &option_len,
2196                                                 &count);
2197                         if (server_id == NULL || count != 1 ||
2198                                                         option_len == 0) {
2199                                 /* RFC 3315, 15.10 */
2200                                 debug(dhcp_client,
2201                                         "confirm server duid error, "
2202                                         "discarding msg %p/%d/%d",
2203                                         server_id, option_len, count);
2204                                 return TRUE;
2205                         }
2206                         dhcp_client->server_duid = g_try_malloc(option_len);
2207                         if (dhcp_client->server_duid == NULL)
2208                                 return TRUE;
2209                         memcpy(dhcp_client->server_duid, server_id, option_len);
2210                         dhcp_client->server_duid_len = option_len;
2211
2212                         dhcp_client->confirm_cb(dhcp_client,
2213                                                 dhcp_client->confirm_data);
2214                         return TRUE;
2215                 }
2216                 break;
2217         default:
2218                 break;
2219         }
2220
2221         debug(dhcp_client, "processed DHCP packet (new state %d)",
2222                                                         dhcp_client->state);
2223
2224         return TRUE;
2225 }
2226
2227 static gboolean discover_timeout(gpointer user_data)
2228 {
2229         GDHCPClient *dhcp_client = user_data;
2230
2231         dhcp_client->retry_times++;
2232
2233         /*
2234          * We do not send the REQUESTED IP option if we are retrying because
2235          * if the server is non-authoritative it will ignore the request if the
2236          * option is present.
2237          */
2238         g_dhcp_client_start(dhcp_client, NULL);
2239
2240         return FALSE;
2241 }
2242
2243 static gboolean ipv4ll_defend_timeout(gpointer dhcp_data)
2244 {
2245         GDHCPClient *dhcp_client = dhcp_data;
2246
2247         debug(dhcp_client, "back to MONITOR mode");
2248
2249         dhcp_client->conflicts = 0;
2250         dhcp_client->state = IPV4LL_MONITOR;
2251
2252         return FALSE;
2253 }
2254
2255 static gboolean ipv4ll_announce_timeout(gpointer dhcp_data)
2256 {
2257         GDHCPClient *dhcp_client = dhcp_data;
2258         uint32_t ip;
2259
2260         debug(dhcp_client, "request timeout (retries %d)",
2261                dhcp_client->retry_times);
2262
2263         if (dhcp_client->retry_times != ANNOUNCE_NUM){
2264                 dhcp_client->retry_times++;
2265                 send_announce_packet(dhcp_client);
2266                 return FALSE;
2267         }
2268
2269         ip = htonl(dhcp_client->requested_ip);
2270         debug(dhcp_client, "switching to monitor mode");
2271         dhcp_client->state = IPV4LL_MONITOR;
2272         dhcp_client->assigned_ip = get_ip(ip);
2273
2274         if (dhcp_client->ipv4ll_available_cb != NULL)
2275                 dhcp_client->ipv4ll_available_cb(dhcp_client,
2276                                         dhcp_client->ipv4ll_available_data);
2277         dhcp_client->conflicts = 0;
2278
2279         return FALSE;
2280 }
2281
2282 static gboolean ipv4ll_probe_timeout(gpointer dhcp_data)
2283 {
2284
2285         GDHCPClient *dhcp_client = dhcp_data;
2286
2287         debug(dhcp_client, "IPV4LL probe timeout (retries %d)",
2288                dhcp_client->retry_times);
2289
2290         if (dhcp_client->retry_times == PROBE_NUM) {
2291                 dhcp_client->state = IPV4LL_ANNOUNCE;
2292                 dhcp_client->retry_times = 0;
2293
2294                 dhcp_client->retry_times++;
2295                 send_announce_packet(dhcp_client);
2296                 return FALSE;
2297         }
2298         dhcp_client->retry_times++;
2299         send_probe_packet(dhcp_client);
2300
2301         return FALSE;
2302 }
2303
2304 int g_dhcp_client_start(GDHCPClient *dhcp_client, const char *last_address)
2305 {
2306         int re;
2307         uint32_t addr;
2308
2309         if (dhcp_client->type == G_DHCP_IPV6) {
2310                 if (dhcp_client->information_req_cb) {
2311                         dhcp_client->state = INFORMATION_REQ;
2312                         re = switch_listening_mode(dhcp_client, L3);
2313                         if (re != 0) {
2314                                 switch_listening_mode(dhcp_client, L_NONE);
2315                                 dhcp_client->state = 0;
2316                                 return re;
2317                         }
2318                         send_information_req(dhcp_client);
2319
2320                 } else if (dhcp_client->solicitation_cb) {
2321                         dhcp_client->state = SOLICITATION;
2322                         re = switch_listening_mode(dhcp_client, L3);
2323                         if (re != 0) {
2324                                 switch_listening_mode(dhcp_client, L_NONE);
2325                                 dhcp_client->state = 0;
2326                                 return re;
2327                         }
2328                         send_solicitation(dhcp_client);
2329
2330                 } else if (dhcp_client->request_cb) {
2331                         dhcp_client->state = REQUEST;
2332                         re = switch_listening_mode(dhcp_client, L3);
2333                         if (re != 0) {
2334                                 switch_listening_mode(dhcp_client, L_NONE);
2335                                 dhcp_client->state = 0;
2336                                 return re;
2337                         }
2338                         send_dhcpv6_request(dhcp_client);
2339
2340                 } else if (dhcp_client->confirm_cb) {
2341                         dhcp_client->state = CONFIRM;
2342                         re = switch_listening_mode(dhcp_client, L3);
2343                         if (re != 0) {
2344                                 switch_listening_mode(dhcp_client, L_NONE);
2345                                 dhcp_client->state = 0;
2346                                 return re;
2347                         }
2348                         send_dhcpv6_confirm(dhcp_client);
2349
2350                 } else if (dhcp_client->renew_cb) {
2351                         dhcp_client->state = RENEW;
2352                         re = switch_listening_mode(dhcp_client, L3);
2353                         if (re != 0) {
2354                                 switch_listening_mode(dhcp_client, L_NONE);
2355                                 dhcp_client->state = 0;
2356                                 return re;
2357                         }
2358                         send_dhcpv6_renew(dhcp_client);
2359
2360                 } else if (dhcp_client->rebind_cb) {
2361                         dhcp_client->state = REBIND;
2362                         re = switch_listening_mode(dhcp_client, L3);
2363                         if (re != 0) {
2364                                 switch_listening_mode(dhcp_client, L_NONE);
2365                                 dhcp_client->state = 0;
2366                                 return re;
2367                         }
2368                         send_dhcpv6_rebind(dhcp_client);
2369
2370                 } else if (dhcp_client->release_cb) {
2371                         dhcp_client->state = RENEW;
2372                         re = switch_listening_mode(dhcp_client, L3);
2373                         if (re != 0) {
2374                                 switch_listening_mode(dhcp_client, L_NONE);
2375                                 dhcp_client->state = 0;
2376                                 return re;
2377                         }
2378                         send_dhcpv6_release(dhcp_client);
2379                 }
2380
2381                 return 0;
2382         }
2383
2384         if (dhcp_client->retry_times == DISCOVER_RETRIES) {
2385                 ipv4ll_start(dhcp_client);
2386                 return 0;
2387         }
2388
2389         if (dhcp_client->retry_times == 0) {
2390                 g_free(dhcp_client->assigned_ip);
2391                 dhcp_client->assigned_ip = NULL;
2392
2393                 dhcp_client->state = INIT_SELECTING;
2394                 re = switch_listening_mode(dhcp_client, L2);
2395                 if (re != 0)
2396                         return re;
2397
2398                 dhcp_client->xid = rand();
2399                 dhcp_client->start = time(NULL);
2400         }
2401
2402         if (last_address == NULL) {
2403                 addr = 0;
2404         } else {
2405                 addr = inet_addr(last_address);
2406                 if (addr == 0xFFFFFFFF) {
2407                         addr = 0;
2408                 } else {
2409                         g_free(dhcp_client->last_address);
2410                         dhcp_client->last_address = g_strdup(last_address);
2411                 }
2412         }
2413         send_discover(dhcp_client, addr);
2414
2415         dhcp_client->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
2416                                                         DISCOVER_TIMEOUT,
2417                                                         discover_timeout,
2418                                                         dhcp_client,
2419                                                         NULL);
2420         return 0;
2421 }
2422
2423 void g_dhcp_client_stop(GDHCPClient *dhcp_client)
2424 {
2425         switch_listening_mode(dhcp_client, L_NONE);
2426
2427         if (dhcp_client->state == BOUND ||
2428                         dhcp_client->state == RENEWING ||
2429                                 dhcp_client->state == REBINDING)
2430                 send_release(dhcp_client, dhcp_client->server_ip,
2431                                         dhcp_client->requested_ip);
2432
2433         if (dhcp_client->timeout > 0) {
2434                 g_source_remove(dhcp_client->timeout);
2435                 dhcp_client->timeout = 0;
2436         }
2437
2438         if (dhcp_client->listener_watch > 0) {
2439                 g_source_remove(dhcp_client->listener_watch);
2440                 dhcp_client->listener_watch = 0;
2441         }
2442
2443         dhcp_client->listener_channel = NULL;
2444
2445         dhcp_client->retry_times = 0;
2446         dhcp_client->ack_retry_times = 0;
2447
2448         dhcp_client->requested_ip = 0;
2449         dhcp_client->state = RELEASED;
2450         dhcp_client->lease_seconds = 0;
2451 }
2452
2453 GList *g_dhcp_client_get_option(GDHCPClient *dhcp_client,
2454                                         unsigned char option_code)
2455 {
2456         return g_hash_table_lookup(dhcp_client->code_value_hash,
2457                                         GINT_TO_POINTER((int) option_code));
2458 }
2459
2460 void g_dhcp_client_register_event(GDHCPClient *dhcp_client,
2461                                         GDHCPClientEvent event,
2462                                         GDHCPClientEventFunc func,
2463                                                         gpointer data)
2464 {
2465         switch (event) {
2466         case G_DHCP_CLIENT_EVENT_LEASE_AVAILABLE:
2467                 dhcp_client->lease_available_cb = func;
2468                 dhcp_client->lease_available_data = data;
2469                 return;
2470         case G_DHCP_CLIENT_EVENT_IPV4LL_AVAILABLE:
2471                 if (dhcp_client->type == G_DHCP_IPV6)
2472                         return;
2473                 dhcp_client->ipv4ll_available_cb = func;
2474                 dhcp_client->ipv4ll_available_data = data;
2475                 return;
2476         case G_DHCP_CLIENT_EVENT_NO_LEASE:
2477                 dhcp_client->no_lease_cb = func;
2478                 dhcp_client->no_lease_data = data;
2479                 return;
2480         case G_DHCP_CLIENT_EVENT_LEASE_LOST:
2481                 dhcp_client->lease_lost_cb = func;
2482                 dhcp_client->lease_lost_data = data;
2483                 return;
2484         case G_DHCP_CLIENT_EVENT_IPV4LL_LOST:
2485                 if (dhcp_client->type == G_DHCP_IPV6)
2486                         return;
2487                 dhcp_client->ipv4ll_lost_cb = func;
2488                 dhcp_client->ipv4ll_lost_data = data;
2489                 return;
2490         case G_DHCP_CLIENT_EVENT_ADDRESS_CONFLICT:
2491                 dhcp_client->address_conflict_cb = func;
2492                 dhcp_client->address_conflict_data = data;
2493                 return;
2494         case G_DHCP_CLIENT_EVENT_INFORMATION_REQ:
2495                 if (dhcp_client->type != G_DHCP_IPV6)
2496                         return;
2497                 dhcp_client->information_req_cb = func;
2498                 dhcp_client->information_req_data = data;
2499                 return;
2500         case G_DHCP_CLIENT_EVENT_SOLICITATION:
2501                 if (dhcp_client->type != G_DHCP_IPV6)
2502                         return;
2503                 dhcp_client->solicitation_cb = func;
2504                 dhcp_client->solicitation_data = data;
2505                 return;
2506         case G_DHCP_CLIENT_EVENT_ADVERTISE:
2507                 if (dhcp_client->type != G_DHCP_IPV6)
2508                         return;
2509                 dhcp_client->advertise_cb = func;
2510                 dhcp_client->advertise_data = data;
2511                 return;
2512         case G_DHCP_CLIENT_EVENT_REQUEST:
2513                 if (dhcp_client->type != G_DHCP_IPV6)
2514                         return;
2515                 dhcp_client->request_cb = func;
2516                 dhcp_client->request_data = data;
2517                 return;
2518         case G_DHCP_CLIENT_EVENT_RENEW:
2519                 if (dhcp_client->type != G_DHCP_IPV6)
2520                         return;
2521                 dhcp_client->renew_cb = func;
2522                 dhcp_client->renew_data = data;
2523                 return;
2524         case G_DHCP_CLIENT_EVENT_REBIND:
2525                 if (dhcp_client->type != G_DHCP_IPV6)
2526                         return;
2527                 dhcp_client->rebind_cb = func;
2528                 dhcp_client->rebind_data = data;
2529                 return;
2530         case G_DHCP_CLIENT_EVENT_RELEASE:
2531                 if (dhcp_client->type != G_DHCP_IPV6)
2532                         return;
2533                 dhcp_client->release_cb = func;
2534                 dhcp_client->release_data = data;
2535                 return;
2536         case G_DHCP_CLIENT_EVENT_CONFIRM:
2537                 if (dhcp_client->type != G_DHCP_IPV6)
2538                         return;
2539                 dhcp_client->confirm_cb = func;
2540                 dhcp_client->confirm_data = data;
2541                 return;
2542         }
2543 }
2544
2545 int g_dhcp_client_get_index(GDHCPClient *dhcp_client)
2546 {
2547         return dhcp_client->ifindex;
2548 }
2549
2550 char *g_dhcp_client_get_address(GDHCPClient *dhcp_client)
2551 {
2552         return g_strdup(dhcp_client->assigned_ip);
2553 }
2554
2555 char *g_dhcp_client_get_netmask(GDHCPClient *dhcp_client)
2556 {
2557         GList *option = NULL;
2558
2559         if (dhcp_client->type == G_DHCP_IPV6)
2560                 return NULL;
2561
2562         switch (dhcp_client->state) {
2563         case IPV4LL_DEFEND:
2564         case IPV4LL_MONITOR:
2565                 return g_strdup("255.255.0.0");
2566         case BOUND:
2567         case RENEWING:
2568         case REBINDING:
2569                 option = g_dhcp_client_get_option(dhcp_client, G_DHCP_SUBNET);
2570                 if (option != NULL)
2571                         return g_strdup(option->data);
2572         case INIT_SELECTING:
2573         case REQUESTING:
2574         case RELEASED:
2575         case IPV4LL_PROBE:
2576         case IPV4LL_ANNOUNCE:
2577         case INFORMATION_REQ:
2578         case SOLICITATION:
2579         case REQUEST:
2580         case CONFIRM:
2581         case RENEW:
2582         case REBIND:
2583         case RELEASE:
2584                 break;
2585         }
2586         return NULL;
2587 }
2588
2589 GDHCPClientError g_dhcp_client_set_request(GDHCPClient *dhcp_client,
2590                                                 unsigned int option_code)
2591 {
2592         if (g_list_find(dhcp_client->request_list,
2593                         GINT_TO_POINTER((int) option_code)) == NULL)
2594                 dhcp_client->request_list = g_list_prepend(
2595                                         dhcp_client->request_list,
2596                                         (GINT_TO_POINTER((int) option_code)));
2597
2598         return G_DHCP_CLIENT_ERROR_NONE;
2599 }
2600
2601 void g_dhcp_client_clear_requests(GDHCPClient *dhcp_client)
2602 {
2603         g_list_free(dhcp_client->request_list);
2604         dhcp_client->request_list = NULL;
2605 }
2606
2607 void g_dhcp_client_clear_values(GDHCPClient *dhcp_client)
2608 {
2609         g_hash_table_remove_all(dhcp_client->send_value_hash);
2610 }
2611
2612 static uint8_t *alloc_dhcp_option(int code, const uint8_t *data, unsigned size)
2613 {
2614         uint8_t *storage;
2615
2616         storage = g_try_malloc(size + OPT_DATA);
2617         if (storage == NULL)
2618                 return NULL;
2619
2620         storage[OPT_CODE] = code;
2621         storage[OPT_LEN] = size;
2622         memcpy(&storage[OPT_DATA], data, size);
2623
2624         return storage;
2625 }
2626
2627 static uint8_t *alloc_dhcp_data_option(int code, const uint8_t *data, unsigned size)
2628 {
2629         return alloc_dhcp_option(code, data, MIN(size, 255));
2630 }
2631
2632 static uint8_t *alloc_dhcp_string_option(int code, const char *str)
2633 {
2634         return alloc_dhcp_data_option(code, (const uint8_t *)str, strlen(str));
2635 }
2636
2637 GDHCPClientError g_dhcp_client_set_id(GDHCPClient *dhcp_client)
2638 {
2639         const unsigned maclen = 6;
2640         const unsigned idlen = maclen + 1;
2641         const uint8_t option_code = G_DHCP_CLIENT_ID;
2642         uint8_t idbuf[idlen];
2643         uint8_t *data_option;
2644
2645         idbuf[0] = ARPHRD_ETHER;
2646
2647         memcpy(&idbuf[1], dhcp_client->mac_address, maclen);
2648
2649         data_option = alloc_dhcp_data_option(option_code, idbuf, idlen);
2650         if (data_option == NULL)
2651                 return G_DHCP_CLIENT_ERROR_NOMEM;
2652
2653         g_hash_table_insert(dhcp_client->send_value_hash,
2654                 GINT_TO_POINTER((int) option_code), data_option);
2655
2656         return G_DHCP_CLIENT_ERROR_NONE;
2657 }
2658
2659 /* Now only support send hostname */
2660 GDHCPClientError g_dhcp_client_set_send(GDHCPClient *dhcp_client,
2661                 unsigned char option_code, const char *option_value)
2662 {
2663         uint8_t *binary_option;
2664
2665         if (option_code == G_DHCP_HOST_NAME && option_value != NULL) {
2666                 binary_option = alloc_dhcp_string_option(option_code,
2667                                                         option_value);
2668                 if (binary_option == NULL)
2669                         return G_DHCP_CLIENT_ERROR_NOMEM;
2670
2671                 g_hash_table_insert(dhcp_client->send_value_hash,
2672                         GINT_TO_POINTER((int) option_code), binary_option);
2673         }
2674
2675         return G_DHCP_CLIENT_ERROR_NONE;
2676 }
2677
2678 static uint8_t *alloc_dhcpv6_option(uint16_t code, uint8_t *option,
2679                                 uint16_t len)
2680 {
2681         uint8_t *storage;
2682
2683         storage = g_malloc(2 + 2 + len);
2684         if (storage == NULL)
2685                 return NULL;
2686
2687         storage[0] = code >> 8;
2688         storage[1] = code & 0xff;
2689         storage[2] = len >> 8;
2690         storage[3] = len & 0xff;
2691         memcpy(storage + 2 + 2, option, len);
2692
2693         return storage;
2694 }
2695
2696 void g_dhcpv6_client_set_send(GDHCPClient *dhcp_client,
2697                                         uint16_t option_code,
2698                                         uint8_t *option_value,
2699                                         uint16_t option_len)
2700 {
2701         if (option_value != NULL) {
2702                 uint8_t *binary_option;
2703
2704                 debug(dhcp_client, "setting option %d to %p len %d",
2705                         option_code, option_value, option_len);
2706
2707                 binary_option = alloc_dhcpv6_option(option_code, option_value,
2708                                                 option_len);
2709                 if (binary_option != NULL)
2710                         g_hash_table_insert(dhcp_client->send_value_hash,
2711                                         GINT_TO_POINTER((int) option_code),
2712                                         binary_option);
2713         }
2714 }
2715
2716 void g_dhcpv6_client_reset_renew(GDHCPClient *dhcp_client)
2717 {
2718         if (dhcp_client == NULL || dhcp_client->type != G_DHCP_IPV6)
2719                 return;
2720
2721         dhcp_client->last_renew = time(NULL);
2722 }
2723
2724 void g_dhcpv6_client_reset_rebind(GDHCPClient *dhcp_client)
2725 {
2726         if (dhcp_client == NULL || dhcp_client->type != G_DHCP_IPV6)
2727                 return;
2728
2729         dhcp_client->last_rebind = time(NULL);
2730 }
2731
2732 void g_dhcpv6_client_set_expire(GDHCPClient *dhcp_client, uint32_t timeout)
2733 {
2734         if (dhcp_client == NULL || dhcp_client->type != G_DHCP_IPV6)
2735                 return;
2736
2737         dhcp_client->expire = time(NULL) + timeout;
2738 }
2739
2740 uint16_t g_dhcpv6_client_get_status(GDHCPClient *dhcp_client)
2741 {
2742         if (dhcp_client == NULL || dhcp_client->type != G_DHCP_IPV6)
2743                 return 0;
2744
2745         return dhcp_client->status_code;
2746 }
2747
2748 GDHCPClient *g_dhcp_client_ref(GDHCPClient *dhcp_client)
2749 {
2750         if (dhcp_client == NULL)
2751                 return NULL;
2752
2753         __sync_fetch_and_add(&dhcp_client->ref_count, 1);
2754
2755         return dhcp_client;
2756 }
2757
2758 void g_dhcp_client_unref(GDHCPClient *dhcp_client)
2759 {
2760         if (dhcp_client == NULL)
2761                 return;
2762
2763         if (__sync_fetch_and_sub(&dhcp_client->ref_count, 1) != 1)
2764                 return;
2765
2766         g_dhcp_client_stop(dhcp_client);
2767
2768         g_free(dhcp_client->interface);
2769         g_free(dhcp_client->assigned_ip);
2770         g_free(dhcp_client->last_address);
2771         g_free(dhcp_client->duid);
2772         g_free(dhcp_client->server_duid);
2773
2774         g_list_free(dhcp_client->request_list);
2775         g_list_free(dhcp_client->require_list);
2776
2777         g_hash_table_destroy(dhcp_client->code_value_hash);
2778         g_hash_table_destroy(dhcp_client->send_value_hash);
2779
2780         g_free(dhcp_client);
2781 }
2782
2783 void g_dhcp_client_set_debug(GDHCPClient *dhcp_client,
2784                                 GDHCPDebugFunc func, gpointer user_data)
2785 {
2786         if (dhcp_client == NULL)
2787                 return;
2788
2789         dhcp_client->debug_func = func;
2790         dhcp_client->debug_data = user_data;
2791 }