1/* dhcp6.c - DHCP6 client for dynamic network configuration.
2 *
3 * Copyright 2015 Rajni Kant <rajnikant12345@gmail.com>
4 *
5 * Not in SUSv4.
6USE_DHCP6(NEWTOY(dhcp6, "r:A#<0T#<0t#<0s:p:i:SRvqnbf", TOYFLAG_SBIN|TOYFLAG_ROOTONLY))
7
8config 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
45GLOBALS(
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
97typedef struct __attribute__((packed)) dhcp6_msg_s {
98  uint8_t msgtype, transaction_id[3], options[524];
99} dhcp6_msg_t;
100
101typedef 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
108typedef struct __attribute__((packed)) optval_ia_na {
109  uint32_t iaid, t1, t2;
110} IA_NA;
111
112typedef 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
118typedef 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
124static DHCP_DATA dhcp_data;
125static dhcp6_raw_t *mymsg;
126static dhcp6_msg_t mesg;
127static DUID *duid;
128
129static void (*dbg)(char *format, ...);
130static void dummy(char *format, ...)
131{
132  return;
133}
134
135static 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
160static 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
174static 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
184static 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.
207static 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
217static 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
226static 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)
240static 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
261static 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
271static void set_timeout(int seconds)
272{
273  TT.tv.tv_sec = seconds;
274  TT.tv.tv_usec = 100000;
275}
276
277static 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
305uint8_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
323static 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
328static 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
339static 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
354static 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
408static 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
422static 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.
433static 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
471static 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.
490static 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
529static 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
538void 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