1 /* dhcp6.c - DHCP6 client for dynamic network configuration.
2  *
3  * Copyright 2015 Rajni Kant <rajnikant12345@gmail.com>
4  *
5  * Not in SUSv4.
6 USE_DHCP6(NEWTOY(dhcp6, "r:A#<0T#<0t#<0s:p:i:SRvqnbf", TOYFLAG_SBIN|TOYFLAG_ROOTONLY))
7 
8 config DHCP6
9   bool "dhcp6"
10   default n
11   help
12   usage: dhcp6 [-fbnqvR] [-i IFACE] [-r IP] [-s PROG] [-p PIDFILE]
13 
14         Configure network dynamicaly using DHCP.
15 
16       -i Interface to use (default eth0)
17       -p Create pidfile
18       -s Run PROG at DHCP events
19       -t Send up to N Solicit packets
20       -T Pause between packets (default 3 seconds)
21       -A Wait N seconds after failure (default 20)
22       -f Run in foreground
23       -b Background if lease is not obtained
24       -n Exit if lease is not obtained
25       -q Exit after obtaining lease
26       -R Release IP on exit
27       -S Log to syslog too
28       -r Request this IP address
29       -v Verbose
30 
31       Signals:
32       USR1  Renew current lease
33       USR2  Release current lease
34 */
35 #define FOR_dhcp6
36 #include "toys.h"
37 #include <linux/sockios.h>
38 #include <linux/if_ether.h>
39 #include <netinet/ip.h>
40 #include <netinet/ip6.h>
41 #include <netinet/udp.h>
42 #include <linux/if_packet.h>
43 #include <syslog.h>
44 
45 GLOBALS(
46   char *interface_name, *pidfile, *script;
47   long retry, timeout, errortimeout;
48   char *req_ip;
49   int length, state, request_length, sock, sock1, status, retval, retries;
50   struct timeval tv;
51   uint8_t transction_id[3];
52   struct sockaddr_in6 input_socket6;
53 )
54 
55 #define DHCP6SOLICIT        1
56 #define DHCP6ADVERTISE      2   // server -> client
57 #define DHCP6REQUEST        3
58 #define DHCP6CONFIRM        4
59 #define DHCP6RENEW          5
60 #define DHCP6REBIND         6
61 #define DHCP6REPLY          7   // server -> client
62 #define DHCP6RELEASE        8
63 #define DHCP6DECLINE        9
64 #define DHCP6RECONFIGURE    10  // server -> client
65 #define DHCP6INFOREQUEST    11
66 #define DHCP6RELAYFLOW      12  // relay -> relay/server
67 #define DHCP6RELAYREPLY     13  // server/relay -> relay
68 
69 // DHCPv6 option codes (partial). See RFC 3315
70 #define DHCP6_OPT_CLIENTID      1
71 #define DHCP6_OPT_SERVERID      2
72 #define DHCP6_OPT_IA_NA         3
73 #define DHCP6_OPT_IA_ADDR       5
74 #define DHCP6_OPT_ORO           6
75 #define DHCP6_OPT_PREFERENCE    7
76 #define DHCP6_OPT_ELAPSED_TIME  8
77 #define DHCP6_OPT_RELAY_MSG     9
78 #define DHCP6_OPT_STATUS_CODE   13
79 #define DHCP6_OPT_IA_PD         25
80 #define DHCP6_OPT_IA_PREFIX     26
81 
82 #define DHCP6_STATUS_SUCCESS        0
83 #define DHCP6_STATUS_NOADDRSAVAIL   2
84 
85 #define DHCP6_DUID_LLT    1
86 #define DHCP6_DUID_EN     2
87 #define DHCP6_DUID_LL     3
88 #define DHCP6_DUID_UUID   4
89 
90 #define DHCPC_SERVER_PORT     547
91 #define DHCPC_CLIENT_PORT     546
92 
93 #define LOG_SILENT          0x0
94 #define LOG_CONSOLE         0x1
95 #define LOG_SYSTEM          0x2
96 
97 typedef struct __attribute__((packed)) dhcp6_msg_s {
98   uint8_t msgtype, transaction_id[3], options[524];
99 } dhcp6_msg_t;
100 
101 typedef struct __attribute__((packed)) optval_duid_llt {
102   uint16_t type;
103   uint16_t hwtype;
104   uint32_t time;
105   uint8_t lladdr[6];
106 } DUID;
107 
108 typedef struct __attribute__((packed)) optval_ia_na {
109   uint32_t iaid, t1, t2;
110 } IA_NA;
111 
112 typedef struct __attribute__((packed)) dhcp6_raw_s {
113   struct ip6_hdr iph;
114   struct udphdr udph;
115   dhcp6_msg_t dhcp6;
116 } dhcp6_raw_t;
117 
118 typedef struct __attribute__((packed)) dhcp_data_client {
119   uint16_t  status_code;
120   uint32_t iaid , t1,t2, pf_lf, va_lf;
121   uint8_t ipaddr[17] ;
122 } DHCP_DATA;
123 
124 static DHCP_DATA dhcp_data;
125 static dhcp6_raw_t *mymsg;
126 static dhcp6_msg_t mesg;
127 static DUID *duid;
128 
129 static void (*dbg)(char *format, ...);
dummy(char * format,...)130 static void dummy(char *format, ...)
131 {
132   return;
133 }
134 
logit(char * format,...)135 static void logit(char *format, ...)
136 {
137   int used;
138   char *msg;
139   va_list p, t;
140   uint8_t infomode = LOG_SILENT;
141 
142   if (toys.optflags & FLAG_S) infomode |= LOG_SYSTEM;
143   if(toys.optflags & FLAG_v) infomode |= LOG_CONSOLE;
144   va_start(p, format);
145   va_copy(t, p);
146   used = vsnprintf(NULL, 0, format, t);
147   used++;
148   va_end(t);
149 
150   msg = xmalloc(used);
151   vsnprintf(msg, used, format, p);
152   va_end(p);
153 
154   if (infomode & LOG_SYSTEM) syslog(LOG_INFO, "%s", msg);
155   if (infomode & LOG_CONSOLE) printf("%s", msg);
156   free(msg);
157   return;
158 }
159 
get_mac(uint8_t * mac,char * interface)160 static void get_mac(uint8_t *mac, char *interface)
161 {
162   int fd;
163   struct ifreq req;
164 
165   if (!mac) return;
166   fd = xsocket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
167   req.ifr_addr.sa_family = AF_INET6;
168   xstrncpy(req.ifr_name, interface, IFNAMSIZ);
169   xioctl(fd, SIOCGIFHWADDR, &req);
170   memcpy(mac, req.ifr_hwaddr.sa_data, 6);
171   xclose(fd);
172 }
173 
fill_option(uint16_t option_id,uint16_t option_len,uint8_t ** dhmesg)174 static void fill_option(uint16_t option_id, uint16_t option_len, uint8_t **dhmesg)
175 {
176   uint8_t *tmp = *dhmesg;
177 
178   *((uint16_t*)tmp) = htons(option_id);
179   *(uint16_t*)(tmp+2) = htons(option_len);
180   *dhmesg += 4;
181   TT.length += 4;
182 }
183 
fill_clientID()184 static void fill_clientID()
185 {
186   uint8_t *tmp = &mesg.options[TT.length];
187 
188   if(!duid) {
189     uint8_t mac[7] = {0,};
190     duid = (DUID*)malloc(sizeof(DUID));
191     duid->type = htons(1);
192     duid->hwtype = htons(1);
193     duid->time = htonl((uint32_t)(time(NULL) - 946684800) & 0xffffffff);
194     fill_option(DHCP6_OPT_CLIENTID,14,&tmp);
195     get_mac(mac, TT.interface_name);
196     memcpy(duid->lladdr,mac, 6);
197     memcpy(tmp,(uint8_t*)duid,sizeof(DUID));
198   }
199   else {
200     fill_option(DHCP6_OPT_CLIENTID,14,&tmp);
201     memcpy(tmp,(uint8_t*)duid,sizeof(DUID));
202   }
203   TT.length += sizeof(DUID);
204 }
205 
206 // TODO: make it generic for multiple options.
fill_optionRequest()207 static void fill_optionRequest()
208 {
209   uint8_t *tmp = &mesg.options[TT.length];
210 
211   fill_option(DHCP6_OPT_ORO,4,&tmp);
212   *(uint16_t*)(tmp+4) = htons(23);
213   *(uint16_t*)(tmp+6) = htons(24);
214   TT.length += 4;
215 }
216 
fill_elapsedTime()217 static void fill_elapsedTime()
218 {
219   uint8_t *tmp = &mesg.options[TT.length];
220 
221   fill_option(DHCP6_OPT_ELAPSED_TIME, 2, &tmp);
222   *(uint16_t*)(tmp+6) = htons(0);
223   TT.length += 2;
224 }
225 
fill_iaid()226 static void fill_iaid()
227 {
228   IA_NA iana;
229   uint8_t *tmp = &mesg.options[TT.length];
230 
231   fill_option(DHCP6_OPT_IA_NA, 12, &tmp);
232   iana.iaid = rand();
233   iana.t1 = 0xffffffff;
234   iana.t2 = 0xffffffff;
235   memcpy(tmp, (uint8_t*)&iana, sizeof(IA_NA));
236   TT.length += sizeof(IA_NA);
237 }
238 
239 //static void mode_raw(int *sock_t)
mode_raw()240 static void mode_raw()
241 {
242   int constone = 1;
243   struct sockaddr_ll sockll;
244 
245   if (TT.sock > 0) xclose(TT.sock);
246   TT.sock = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6));
247 
248   memset(&sockll, 0, sizeof(sockll));
249   sockll.sll_family = AF_PACKET;
250   sockll.sll_protocol = htons(ETH_P_IPV6);
251   sockll.sll_ifindex = if_nametoindex(TT.interface_name);
252   if (bind(TT.sock, (struct sockaddr *) &sockll, sizeof(sockll))) {
253     xclose(TT.sock);
254     error_exit("MODE RAW : Bind fail.\n");
255   }
256   if (setsockopt(TT.sock, SOL_PACKET, PACKET_HOST,&constone, sizeof(int)) < 0) {
257 		if (errno != ENOPROTOOPT) error_exit("MODE RAW : Bind fail.\n");
258 	}
259 }
260 
generate_transection_id()261 static void generate_transection_id()
262 {
263   int i, r = rand() % 0xffffff;
264 
265   for (i=0; i<3; i++) {
266     TT.transction_id[i] = r%0xff;
267     r = r/10;
268   }
269 }
270 
set_timeout(int seconds)271 static void set_timeout(int seconds)
272 {
273   TT.tv.tv_sec = seconds;
274   TT.tv.tv_usec = 100000;
275 }
276 
send_msg(int type)277 static void  send_msg(int type)
278 {
279   struct sockaddr_in6 addr6;
280   int sendlength = 0;
281 
282   memset(&addr6, 0, sizeof(addr6));
283   addr6.sin6_family = AF_INET6;
284   addr6.sin6_port = htons(DHCPC_SERVER_PORT); //SERVER_PORT
285   inet_pton(AF_INET6, "ff02::1:2", &addr6.sin6_addr);
286   mesg.msgtype = type;
287   generate_transection_id();
288   memcpy(mesg.transaction_id, TT.transction_id, 3);
289 
290   if (type  == DHCP6SOLICIT) {
291     TT.length = 0;
292     fill_clientID();
293     fill_optionRequest();
294     fill_elapsedTime();
295     fill_iaid();
296     sendlength = sizeof(dhcp6_msg_t) - 524 + TT.length;
297   } else if (type == DHCP6REQUEST || type == DHCP6RELEASE || type == DHCP6RENEW)
298     sendlength = TT.request_length;
299   dbg("Sending message type: %d\n", type);
300   sendlength = sendto(TT.sock1, &mesg, sendlength , 0,(struct sockaddr *)&addr6,
301           sizeof(struct sockaddr_in6 ));
302   if (sendlength <= 0) dbg("Error in sending message type: %d\n", type);
303 }
304 
get_msg_ptr(uint8_t * data,int data_length,int msgtype)305 uint8_t *get_msg_ptr(uint8_t *data, int data_length, int msgtype)
306 {
307   uint16_t type =  *((uint16_t*)data), length = *((uint16_t*)(data+2));
308 
309   type = ntohs(type);
310   if (type == msgtype) return data;
311   length = ntohs(length);
312   while (type != msgtype) {
313     data_length -= (4 + length);
314     if (data_length <= 0) break;
315     data = data + 4 + length;
316     type = ntohs(*((uint16_t*)data));
317     length = ntohs(*((uint16_t*)(data+2)));
318     if (type == msgtype) return data;
319   }
320   return NULL;
321 }
322 
check_server_id(uint8_t * data,int data_length)323 static uint8_t *check_server_id(uint8_t *data, int data_length)
324 {
325   return get_msg_ptr(data,  data_length, DHCP6_OPT_SERVERID);
326 }
327 
check_client_id(uint8_t * data,int data_length)328 static int check_client_id(uint8_t *data, int data_length)
329 {
330   if ((data = get_msg_ptr(data,  data_length, DHCP6_OPT_CLIENTID))) {
331     DUID one = *((DUID*)(data+4));
332     DUID two = *((DUID*)&mesg.options[4]);
333 
334     if (!memcmp(&one, &two, sizeof(DUID))) return 1;
335   }
336   return 0;
337 }
338 
validate_ids()339 static int validate_ids()
340 {
341   if (!check_server_id(mymsg->dhcp6.options,
342     TT.status - ((char*)&mymsg->dhcp6.options[0] - (char*)mymsg) )) {
343     dbg("Invalid server id: %d\n");
344     return 0;
345   }
346   if (!check_client_id(mymsg->dhcp6.options,
347     TT.status - ((char*)&mymsg->dhcp6.options[0] - (char*)mymsg) )) {
348     dbg("Invalid client id: %d\n");
349     return 0;
350   }
351   return 1;
352 }
353 
parse_ia_na(uint8_t * data,int data_length)354 static void parse_ia_na(uint8_t *data, int data_length)
355 {
356   uint8_t *t = get_msg_ptr(data, data_length, DHCP6_OPT_IA_NA);
357   uint16_t iana_len, content_len = 0;
358 
359   memset(&dhcp_data,0,sizeof(dhcp_data));
360   if (!t) return;
361 
362   iana_len = ntohs(*((uint16_t*)(t+2)));
363   dhcp_data.iaid = ntohl(*((uint32_t*)(t+4)));
364   dhcp_data.t1 = ntohl(*((uint32_t*)(t+8)));
365   dhcp_data.t2 = ntohl(*((uint32_t*)(t+12)));
366   t += 16;
367   iana_len -= 12;
368 
369   while(iana_len > 0) {
370     uint16_t sub_type = ntohs(*((uint16_t*)(t)));
371 
372     switch (sub_type) {
373       case DHCP6_OPT_IA_ADDR:
374         content_len = ntohs(*((uint16_t*)(t+2)));
375         memcpy(dhcp_data.ipaddr,t+4,16);
376         if (TT.state == DHCP6SOLICIT) {
377           if (TT.req_ip) {
378             struct addrinfo *res = NULL;
379 
380             if(!getaddrinfo(TT.req_ip, NULL, NULL,&res)) {
381               dbg("Requesting IP: %s\n", TT.req_ip);
382               memcpy (&TT.input_socket6, res->ai_addr, res->ai_addrlen);
383               memcpy(t+4, TT.input_socket6.sin6_addr.__in6_u.__u6_addr8, 16);
384             } else xprintf("Invalid IP: %s\n",TT.req_ip);
385             freeaddrinfo(res);
386           }
387         }
388         dhcp_data.pf_lf = ntohl(*((uint32_t*)(t+20)));
389         dhcp_data.va_lf = ntohl(*((uint32_t*)(t+24)));
390         iana_len -= (content_len + 4);
391         t += (content_len + 4);
392         break;
393       case DHCP6_OPT_STATUS_CODE:
394         content_len = ntohs(*((uint16_t*)(t+2)));
395         dhcp_data.status_code = ntohs(*((uint16_t*)(t+4)));
396         iana_len -= (content_len + 4);
397         t += (content_len + 4);
398         break;
399       default:
400         content_len = ntohs(*((uint16_t*)(t+2)));
401         iana_len -= (content_len + 4);
402         t += (content_len + 4);
403         break;
404     }
405   }
406 }
407 
write_pid(char * path)408 static void write_pid(char *path)
409 {
410   int pidfile = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0666);
411 
412   if (pidfile > 0) {
413     char pidbuf[12];
414 
415     sprintf(pidbuf, "%u", (unsigned)getpid());
416     write(pidfile, pidbuf, strlen(pidbuf));
417     close(pidfile);
418   }
419 }
420 
421 // Creates environment pointers from RES to use in script
fill_envp(DHCP_DATA * res)422 static int fill_envp(DHCP_DATA *res)
423 {
424   int ret = setenv("interface", TT.interface_name, 1);
425 
426   if (ret) return ret;
427   inet_ntop(AF_INET6, res->ipaddr, toybuf, INET6_ADDRSTRLEN);
428   ret = setenv("ip",(const char*)toybuf , 1);
429   return ret;
430 }
431 
432 // Executes Script NAME.
run_script(DHCP_DATA * res,char * name)433 static void run_script(DHCP_DATA *res,  char *name)
434 {
435   volatile int error = 0;
436   struct stat sts;
437   pid_t pid;
438   char *argv[3];
439   char *script = (toys.optflags & FLAG_s) ? TT.script
440     : "/usr/share/dhcp/default.script";
441 
442   if (stat(script, &sts) == -1 && errno == ENOENT) return;
443   if (!res || fill_envp(res)) {
444     dbg("Failed to create environment variables.\n");
445     return;
446   }
447   dbg("Executing %s %s\n", script, name);
448   argv[0] = (char*)script;
449   argv[1] = (char*)name;
450   argv[2] = NULL;
451   fflush(NULL);
452 
453   pid = vfork();
454   if (pid < 0) {
455     dbg("Fork failed.\n");
456     return;
457   }
458   if (!pid) {
459     execvp(argv[0], argv);
460     error = errno;
461     _exit(111);
462   }
463   if (error) {
464     waitpid(pid, NULL, 0);
465     errno = error;
466     perror_msg("script exec failed");
467   }
468   dbg("script complete.\n");
469 }
470 
lease_fail()471 static void lease_fail()
472 {
473   dbg("Lease failed.\n");
474   run_script(NULL, "leasefail");
475   if (toys.optflags & FLAG_n) {
476     xclose(TT.sock);
477     xclose(TT.sock1);
478     error_exit("Lease Failed, Exiting.");
479   }
480   if (toys.optflags & FLAG_b) {
481     dbg("Lease failed. Going to daemon mode.\n");
482     if (daemon(0,0)) perror_exit("daemonize");
483     if (toys.optflags & FLAG_p) write_pid(TT.pidfile);
484     toys.optflags &= ~FLAG_b;
485     toys.optflags |= FLAG_f;
486   }
487 }
488 
489 // Generic signal handler real handling is done in main funcrion.
signal_handler(int sig)490 static void signal_handler(int sig)
491 {
492     dbg("Caught signal: %d\n", sig);
493     switch (sig) {
494     case SIGUSR1:
495       dbg("SIGUSR1.\n");
496       if (TT.state == DHCP6RELEASE || TT.state == DHCP6REQUEST ) {
497         TT.state = DHCP6SOLICIT;
498         set_timeout(0);
499         return;
500       }
501       dbg("SIGUSR1 sending renew.\n");
502       send_msg(DHCP6RENEW);
503       TT.state = DHCP6RENEW;
504       TT.retries = 0;
505       set_timeout(0);
506       break;
507     case SIGUSR2:
508       dbg("SIGUSR2.\n");
509       if (TT.state == DHCP6RELEASE) return;
510       if (TT.state != DHCP6CONFIRM ) return;
511       dbg("SIGUSR2 sending release.\n");
512       send_msg(DHCP6RELEASE);
513       TT.state = DHCP6RELEASE;
514       TT.retries = 0;
515       set_timeout(0);
516       break;
517     case SIGTERM:
518     case SIGINT:
519       dbg((sig == SIGTERM)?"SIGTERM.\n":"SIGINT.\n");
520       if ((toys.optflags & FLAG_R) && TT.state == DHCP6CONFIRM)
521         send_msg(DHCP6RELEASE);
522       if(sig == SIGINT) exit(0);
523       break;
524     default: break;
525   }
526 }
527 
528 // signal setup for SIGUSR1 SIGUSR2 SIGTERM
setup_signal()529 static int setup_signal()
530 {
531   signal(SIGUSR1, signal_handler);
532   signal(SIGUSR2, signal_handler);
533   signal(SIGTERM, signal_handler);
534   signal(SIGINT, signal_handler);
535   return 0;
536 }
537 
dhcp6_main(void)538 void dhcp6_main(void)
539 {
540   struct sockaddr_in6  sinaddr6;
541   int constone = 1;
542   fd_set rfds;
543 
544   srand(time(NULL));
545   setlinebuf(stdout);
546   dbg = dummy;
547   TT.state = DHCP6SOLICIT;
548 
549   if (toys.optflags & FLAG_v) dbg = logit;
550   if (!TT.interface_name) TT.interface_name = "eth0";
551   if (toys.optflags & FLAG_p) write_pid(TT.pidfile);
552   if (!TT.retry) TT.retry = 3;
553   if (!TT.timeout) TT.timeout = 3;
554   if (!TT.errortimeout) TT.errortimeout = 20;
555   if (toys.optflags & FLAG_S) {
556     openlog("DHCP6 :", LOG_PID, LOG_DAEMON);
557     dbg = logit;
558   }
559 
560   dbg("Interface: %s\n", TT.interface_name);
561   dbg("pid file: %s\n", TT.pidfile);
562   dbg("Retry count: %d\n", TT.retry);
563   dbg("Timeout : %d\n", TT.timeout);
564   dbg("Error timeout: %d\n", TT.errortimeout);
565 
566 
567 
568   setup_signal();
569   TT.sock1 = xsocket(PF_INET6, SOCK_DGRAM, 0);
570   memset(&sinaddr6, 0, sizeof(sinaddr6));
571   sinaddr6.sin6_family = AF_INET6;
572   sinaddr6.sin6_port = htons(DHCPC_CLIENT_PORT);
573   sinaddr6.sin6_scope_id = if_nametoindex(TT.interface_name);
574   sinaddr6.sin6_addr = in6addr_any ;
575 
576   xsetsockopt(TT.sock1, SOL_SOCKET, SO_REUSEADDR, &constone, sizeof(constone));
577 
578   if (bind(TT.sock1, (struct sockaddr *)&sinaddr6, sizeof(sinaddr6))) {
579     xclose(TT.sock1);
580     error_exit("bind failed");
581   }
582 
583   mode_raw();
584   set_timeout(0);
585   for (;;) {
586     int maxfd = TT.sock;
587 
588     if (TT.sock >= 0) FD_SET(TT.sock, &rfds);
589     TT.retval = 0;
590     if ((TT.retval = select(maxfd + 1, &rfds, NULL, NULL, &TT.tv)) < 0) {
591       if(errno == EINTR) continue;
592       perror_exit("Error in select");
593     }
594     if (!TT.retval) {
595       if (TT.state == DHCP6SOLICIT || TT.state == DHCP6CONFIRM) {
596         dbg("State is solicit, sending solicit packet\n");
597         run_script(NULL, "deconfig");
598         send_msg(DHCP6SOLICIT);
599         TT.state = DHCP6SOLICIT;
600         TT.retries++;
601         if(TT.retries > TT.retry) set_timeout(TT.errortimeout);
602         else if (TT.retries == TT.retry) {
603           dbg("State is solicit, retry count is max.\n");
604           lease_fail();
605           set_timeout(TT.errortimeout);
606         } else set_timeout(TT.timeout);
607         continue;
608       } else if (TT.state == DHCP6REQUEST || TT.state == DHCP6RENEW ||
609               TT.state == DHCP6RELEASE) {
610         dbg("State is %d , sending packet\n", TT.state);
611         send_msg(TT.state);
612         TT.retries++;
613         if (TT.retries > TT.retry) set_timeout(TT.errortimeout);
614         else if (TT.retries == TT.retry) {
615           lease_fail();
616           set_timeout(TT.errortimeout);
617         } else set_timeout(TT.timeout);
618         continue;
619       }
620     } else if (FD_ISSET(TT.sock, &rfds)) {
621       if ((TT.status = read(TT.sock, toybuf, sizeof(toybuf))) <= 0) continue;
622       mymsg = (dhcp6_raw_t*)toybuf;
623       if (ntohs(mymsg->udph.dest) == 546 &&
624               !memcmp(mymsg->dhcp6.transaction_id, TT.transction_id, 3)) {
625         if (TT.state == DHCP6SOLICIT) {
626           if (mymsg->dhcp6.msgtype == DHCP6ADVERTISE ) {
627             if (!validate_ids()) {
628               dbg("Invalid id recieved, solicit.\n");
629               TT.state = DHCP6SOLICIT;
630               continue;
631             }
632             dbg("Got reply to request or solicit.\n");
633             TT.retries = 0;
634             set_timeout(0);
635             TT.request_length = TT.status - ((char*)&mymsg->dhcp6 - (char*)mymsg);
636             memcpy((uint8_t*)&mesg, &mymsg->dhcp6, TT.request_length);
637             parse_ia_na(mesg.options, TT.request_length);
638             dbg("Status code:%d\n", dhcp_data.status_code);
639             inet_ntop(AF_INET6, dhcp_data.ipaddr, toybuf, INET6_ADDRSTRLEN);
640             dbg("Advertiesed IP: %s\n", toybuf);
641             TT.state = DHCP6REQUEST;
642           } else {
643             dbg("Invalid solicit.\n");
644             continue;
645           }
646         } else if (TT.state == DHCP6REQUEST || TT.state == DHCP6RENEW ) {
647           if (mymsg->dhcp6.msgtype == DHCP6REPLY) {
648             if (!validate_ids()) {
649               dbg("Invalid id recieved, %d.\n", TT.state);
650               TT.state = DHCP6REQUEST;
651               continue;
652             }
653             dbg("Got reply to request or renew.\n");
654             TT.request_length = TT.status - ((char*)&mymsg->dhcp6 - (char*)mymsg);
655             memcpy((uint8_t*)&mesg, &mymsg->dhcp6, TT.request_length);
656             parse_ia_na(mymsg->dhcp6.options, TT.request_length);
657             dbg("Status code:%d\n", dhcp_data.status_code);
658             inet_ntop(AF_INET6, dhcp_data.ipaddr, toybuf, INET6_ADDRSTRLEN);
659             dbg("Got IP: %s\n", toybuf);
660             TT.retries = 0;
661             run_script(&dhcp_data, (TT.state == DHCP6REQUEST) ?
662               "request" : "renew");
663             if (toys.optflags & FLAG_q) {
664               if (toys.optflags & FLAG_R) send_msg(DHCP6RELEASE);
665               break;
666             }
667             TT.state = DHCP6CONFIRM;
668             set_timeout((dhcp_data.va_lf)?dhcp_data.va_lf:INT_MAX);
669             dbg("Setting timeout to intmax.");
670             if (TT.state == DHCP6REQUEST || !(toys.optflags & FLAG_f)) {
671               dbg("Making it a daemon\n");
672               if (daemon(0,0)) perror_exit("daemonize");
673               toys.optflags |= FLAG_f;
674               if (toys.optflags & FLAG_p) write_pid(TT.pidfile);
675             }
676             dbg("Making it a foreground.\n");
677             continue;
678           } else {
679             dbg("Invalid reply.\n");
680             continue;
681           }
682         } else if (TT.state == DHCP6RELEASE) {
683           dbg("Got reply to release.\n");
684           run_script(NULL, "release");
685           set_timeout(INT_MAX);
686         }
687       }
688     }
689   }
690   xclose(TT.sock1);
691   xclose(TT.sock);
692 }
693