toybox/toys/pending/dhcp6.c
<<
>>
Prefs
   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