| /* dhcp6.c - DHCP6 client for dynamic network configuration. |
| * |
| * Copyright 2015 Rajni Kant <rajnikant12345@gmail.com> |
| * |
| * Not in SUSv4. |
| USE_DHCP6(NEWTOY(dhcp6, "r:A#<0T#<0t#<0s:p:i:SRvqnbf", TOYFLAG_SBIN|TOYFLAG_ROOTONLY)) |
| |
| config DHCP6 |
| bool "dhcp6" |
| default n |
| help |
| usage: dhcp6 [-fbnqvR] [-i IFACE] [-r IP] [-s PROG] [-p PIDFILE] |
| |
| Configure network dynamicaly using DHCP. |
| |
| -i Interface to use (default eth0) |
| -p Create pidfile |
| -s Run PROG at DHCP events |
| -t Send up to N Solicit packets |
| -T Pause between packets (default 3 seconds) |
| -A Wait N seconds after failure (default 20) |
| -f Run in foreground |
| -b Background if lease is not obtained |
| -n Exit if lease is not obtained |
| -q Exit after obtaining lease |
| -R Release IP on exit |
| -S Log to syslog too |
| -r Request this IP address |
| -v Verbose |
| |
| Signals: |
| USR1 Renew current lease |
| USR2 Release current lease |
| */ |
| #define FOR_dhcp6 |
| #include "toys.h" |
| #include <linux/sockios.h> |
| #include <linux/if_ether.h> |
| #include <netinet/ip.h> |
| #include <netinet/ip6.h> |
| #include <netinet/udp.h> |
| #include <linux/if_packet.h> |
| #include <syslog.h> |
| |
| GLOBALS( |
| char *interface_name, *pidfile, *script; |
| long retry, timeout, errortimeout; |
| char *req_ip; |
| int length, state, request_length, sock, sock1, status, retval, retries; |
| struct timeval tv; |
| uint8_t transction_id[3]; |
| struct sockaddr_in6 input_socket6; |
| ) |
| |
| #define DHCP6SOLICIT 1 |
| #define DHCP6ADVERTISE 2 // server -> client |
| #define DHCP6REQUEST 3 |
| #define DHCP6CONFIRM 4 |
| #define DHCP6RENEW 5 |
| #define DHCP6REBIND 6 |
| #define DHCP6REPLY 7 // server -> client |
| #define DHCP6RELEASE 8 |
| #define DHCP6DECLINE 9 |
| #define DHCP6RECONFIGURE 10 // server -> client |
| #define DHCP6INFOREQUEST 11 |
| #define DHCP6RELAYFLOW 12 // relay -> relay/server |
| #define DHCP6RELAYREPLY 13 // server/relay -> relay |
| |
| // DHCPv6 option codes (partial). See RFC 3315 |
| #define DHCP6_OPT_CLIENTID 1 |
| #define DHCP6_OPT_SERVERID 2 |
| #define DHCP6_OPT_IA_NA 3 |
| #define DHCP6_OPT_IA_ADDR 5 |
| #define DHCP6_OPT_ORO 6 |
| #define DHCP6_OPT_PREFERENCE 7 |
| #define DHCP6_OPT_ELAPSED_TIME 8 |
| #define DHCP6_OPT_RELAY_MSG 9 |
| #define DHCP6_OPT_STATUS_CODE 13 |
| #define DHCP6_OPT_IA_PD 25 |
| #define DHCP6_OPT_IA_PREFIX 26 |
| |
| #define DHCP6_STATUS_SUCCESS 0 |
| #define DHCP6_STATUS_NOADDRSAVAIL 2 |
| |
| #define DHCP6_DUID_LLT 1 |
| #define DHCP6_DUID_EN 2 |
| #define DHCP6_DUID_LL 3 |
| #define DHCP6_DUID_UUID 4 |
| |
| #define DHCPC_SERVER_PORT 547 |
| #define DHCPC_CLIENT_PORT 546 |
| |
| #define LOG_SILENT 0x0 |
| #define LOG_CONSOLE 0x1 |
| #define LOG_SYSTEM 0x2 |
| |
| typedef struct __attribute__((packed)) dhcp6_msg_s { |
| uint8_t msgtype, transaction_id[3], options[524]; |
| } dhcp6_msg_t; |
| |
| typedef struct __attribute__((packed)) optval_duid_llt { |
| uint16_t type; |
| uint16_t hwtype; |
| uint32_t time; |
| uint8_t lladdr[6]; |
| } DUID; |
| |
| typedef struct __attribute__((packed)) optval_ia_na { |
| uint32_t iaid, t1, t2; |
| } IA_NA; |
| |
| typedef struct __attribute__((packed)) dhcp6_raw_s { |
| struct ip6_hdr iph; |
| struct udphdr udph; |
| dhcp6_msg_t dhcp6; |
| } dhcp6_raw_t; |
| |
| typedef struct __attribute__((packed)) dhcp_data_client { |
| uint16_t status_code; |
| uint32_t iaid , t1,t2, pf_lf, va_lf; |
| uint8_t ipaddr[17] ; |
| } DHCP_DATA; |
| |
| static DHCP_DATA dhcp_data; |
| static dhcp6_raw_t *mymsg; |
| static dhcp6_msg_t mesg; |
| static DUID *duid; |
| |
| static void (*dbg)(char *format, ...); |
| static void dummy(char *format, ...) |
| { |
| return; |
| } |
| |
| static void logit(char *format, ...) |
| { |
| int used; |
| char *msg; |
| va_list p, t; |
| uint8_t infomode = LOG_SILENT; |
| |
| if (toys.optflags & FLAG_S) infomode |= LOG_SYSTEM; |
| if(toys.optflags & FLAG_v) infomode |= LOG_CONSOLE; |
| va_start(p, format); |
| va_copy(t, p); |
| used = vsnprintf(NULL, 0, format, t); |
| used++; |
| va_end(t); |
| |
| msg = xmalloc(used); |
| vsnprintf(msg, used, format, p); |
| va_end(p); |
| |
| if (infomode & LOG_SYSTEM) syslog(LOG_INFO, "%s", msg); |
| if (infomode & LOG_CONSOLE) printf("%s", msg); |
| free(msg); |
| return; |
| } |
| |
| static void get_mac(uint8_t *mac, char *interface) |
| { |
| int fd; |
| struct ifreq req; |
| |
| if (!mac) return; |
| fd = xsocket(AF_INET6, SOCK_RAW, IPPROTO_RAW); |
| req.ifr_addr.sa_family = AF_INET6; |
| xstrncpy(req.ifr_name, interface, IFNAMSIZ); |
| xioctl(fd, SIOCGIFHWADDR, &req); |
| memcpy(mac, req.ifr_hwaddr.sa_data, 6); |
| xclose(fd); |
| } |
| |
| static void fill_option(uint16_t option_id, uint16_t option_len, uint8_t **dhmesg) |
| { |
| uint8_t *tmp = *dhmesg; |
| |
| *((uint16_t*)tmp) = htons(option_id); |
| *(uint16_t*)(tmp+2) = htons(option_len); |
| *dhmesg += 4; |
| TT.length += 4; |
| } |
| |
| static void fill_clientID() |
| { |
| uint8_t *tmp = &mesg.options[TT.length]; |
| |
| if(!duid) { |
| uint8_t mac[7] = {0,}; |
| duid = (DUID*)malloc(sizeof(DUID)); |
| duid->type = htons(1); |
| duid->hwtype = htons(1); |
| duid->time = htonl((uint32_t)(time(NULL) - 946684800) & 0xffffffff); |
| fill_option(DHCP6_OPT_CLIENTID,14,&tmp); |
| get_mac(mac, TT.interface_name); |
| memcpy(duid->lladdr,mac, 6); |
| memcpy(tmp,(uint8_t*)duid,sizeof(DUID)); |
| } |
| else { |
| fill_option(DHCP6_OPT_CLIENTID,14,&tmp); |
| memcpy(tmp,(uint8_t*)duid,sizeof(DUID)); |
| } |
| TT.length += sizeof(DUID); |
| } |
| |
| // TODO: make it generic for multiple options. |
| static void fill_optionRequest() |
| { |
| uint8_t *tmp = &mesg.options[TT.length]; |
| |
| fill_option(DHCP6_OPT_ORO,4,&tmp); |
| *(uint16_t*)(tmp+4) = htons(23); |
| *(uint16_t*)(tmp+6) = htons(24); |
| TT.length += 4; |
| } |
| |
| static void fill_elapsedTime() |
| { |
| uint8_t *tmp = &mesg.options[TT.length]; |
| |
| fill_option(DHCP6_OPT_ELAPSED_TIME, 2, &tmp); |
| *(uint16_t*)(tmp+6) = htons(0); |
| TT.length += 2; |
| } |
| |
| static void fill_iaid() |
| { |
| IA_NA iana; |
| uint8_t *tmp = &mesg.options[TT.length]; |
| |
| fill_option(DHCP6_OPT_IA_NA, 12, &tmp); |
| iana.iaid = rand(); |
| iana.t1 = 0xffffffff; |
| iana.t2 = 0xffffffff; |
| memcpy(tmp, (uint8_t*)&iana, sizeof(IA_NA)); |
| TT.length += sizeof(IA_NA); |
| } |
| |
| //static void mode_raw(int *sock_t) |
| static void mode_raw() |
| { |
| int constone = 1; |
| struct sockaddr_ll sockll; |
| |
| if (TT.sock > 0) xclose(TT.sock); |
| TT.sock = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6)); |
| |
| memset(&sockll, 0, sizeof(sockll)); |
| sockll.sll_family = AF_PACKET; |
| sockll.sll_protocol = htons(ETH_P_IPV6); |
| sockll.sll_ifindex = if_nametoindex(TT.interface_name); |
| if (bind(TT.sock, (struct sockaddr *) &sockll, sizeof(sockll))) { |
| xclose(TT.sock); |
| error_exit("MODE RAW : Bind fail.\n"); |
| } |
| if (setsockopt(TT.sock, SOL_PACKET, PACKET_HOST,&constone, sizeof(int)) < 0) { |
| if (errno != ENOPROTOOPT) error_exit("MODE RAW : Bind fail.\n"); |
| } |
| } |
| |
| static void generate_transection_id() |
| { |
| int i, r = rand() % 0xffffff; |
| |
| for (i=0; i<3; i++) { |
| TT.transction_id[i] = r%0xff; |
| r = r/10; |
| } |
| } |
| |
| static void set_timeout(int seconds) |
| { |
| TT.tv.tv_sec = seconds; |
| TT.tv.tv_usec = 100000; |
| } |
| |
| static void send_msg(int type) |
| { |
| struct sockaddr_in6 addr6; |
| int sendlength = 0; |
| |
| memset(&addr6, 0, sizeof(addr6)); |
| addr6.sin6_family = AF_INET6; |
| addr6.sin6_port = htons(DHCPC_SERVER_PORT); //SERVER_PORT |
| inet_pton(AF_INET6, "ff02::1:2", &addr6.sin6_addr); |
| mesg.msgtype = type; |
| generate_transection_id(); |
| memcpy(mesg.transaction_id, TT.transction_id, 3); |
| |
| if (type == DHCP6SOLICIT) { |
| TT.length = 0; |
| fill_clientID(); |
| fill_optionRequest(); |
| fill_elapsedTime(); |
| fill_iaid(); |
| sendlength = sizeof(dhcp6_msg_t) - 524 + TT.length; |
| } else if (type == DHCP6REQUEST || type == DHCP6RELEASE || type == DHCP6RENEW) |
| sendlength = TT.request_length; |
| dbg("Sending message type: %d\n", type); |
| sendlength = sendto(TT.sock1, &mesg, sendlength , 0,(struct sockaddr *)&addr6, |
| sizeof(struct sockaddr_in6 )); |
| if (sendlength <= 0) dbg("Error in sending message type: %d\n", type); |
| } |
| |
| uint8_t *get_msg_ptr(uint8_t *data, int data_length, int msgtype) |
| { |
| uint16_t type = *((uint16_t*)data), length = *((uint16_t*)(data+2)); |
| |
| type = ntohs(type); |
| if (type == msgtype) return data; |
| length = ntohs(length); |
| while (type != msgtype) { |
| data_length -= (4 + length); |
| if (data_length <= 0) break; |
| data = data + 4 + length; |
| type = ntohs(*((uint16_t*)data)); |
| length = ntohs(*((uint16_t*)(data+2))); |
| if (type == msgtype) return data; |
| } |
| return NULL; |
| } |
| |
| static uint8_t *check_server_id(uint8_t *data, int data_length) |
| { |
| return get_msg_ptr(data, data_length, DHCP6_OPT_SERVERID); |
| } |
| |
| static int check_client_id(uint8_t *data, int data_length) |
| { |
| if ((data = get_msg_ptr(data, data_length, DHCP6_OPT_CLIENTID))) { |
| DUID one = *((DUID*)(data+4)); |
| DUID two = *((DUID*)&mesg.options[4]); |
| |
| if (!memcmp(&one, &two, sizeof(DUID))) return 1; |
| } |
| return 0; |
| } |
| |
| static int validate_ids() |
| { |
| if (!check_server_id(mymsg->dhcp6.options, |
| TT.status - ((char*)&mymsg->dhcp6.options[0] - (char*)mymsg) )) { |
| dbg("Invalid server id: %d\n"); |
| return 0; |
| } |
| if (!check_client_id(mymsg->dhcp6.options, |
| TT.status - ((char*)&mymsg->dhcp6.options[0] - (char*)mymsg) )) { |
| dbg("Invalid client id: %d\n"); |
| return 0; |
| } |
| return 1; |
| } |
| |
| static void parse_ia_na(uint8_t *data, int data_length) |
| { |
| uint8_t *t = get_msg_ptr(data, data_length, DHCP6_OPT_IA_NA); |
| uint16_t iana_len, content_len = 0; |
| |
| memset(&dhcp_data,0,sizeof(dhcp_data)); |
| if (!t) return; |
| |
| iana_len = ntohs(*((uint16_t*)(t+2))); |
| dhcp_data.iaid = ntohl(*((uint32_t*)(t+4))); |
| dhcp_data.t1 = ntohl(*((uint32_t*)(t+8))); |
| dhcp_data.t2 = ntohl(*((uint32_t*)(t+12))); |
| t += 16; |
| iana_len -= 12; |
| |
| while(iana_len > 0) { |
| uint16_t sub_type = ntohs(*((uint16_t*)(t))); |
| |
| switch (sub_type) { |
| case DHCP6_OPT_IA_ADDR: |
| content_len = ntohs(*((uint16_t*)(t+2))); |
| memcpy(dhcp_data.ipaddr,t+4,16); |
| if (TT.state == DHCP6SOLICIT) { |
| if (TT.req_ip) { |
| struct addrinfo *res = NULL; |
| |
| if(!getaddrinfo(TT.req_ip, NULL, NULL,&res)) { |
| dbg("Requesting IP: %s\n", TT.req_ip); |
| memcpy (&TT.input_socket6, res->ai_addr, res->ai_addrlen); |
| memcpy(t+4, TT.input_socket6.sin6_addr.__in6_u.__u6_addr8, 16); |
| } else xprintf("Invalid IP: %s\n",TT.req_ip); |
| freeaddrinfo(res); |
| } |
| } |
| dhcp_data.pf_lf = ntohl(*((uint32_t*)(t+20))); |
| dhcp_data.va_lf = ntohl(*((uint32_t*)(t+24))); |
| iana_len -= (content_len + 4); |
| t += (content_len + 4); |
| break; |
| case DHCP6_OPT_STATUS_CODE: |
| content_len = ntohs(*((uint16_t*)(t+2))); |
| dhcp_data.status_code = ntohs(*((uint16_t*)(t+4))); |
| iana_len -= (content_len + 4); |
| t += (content_len + 4); |
| break; |
| default: |
| content_len = ntohs(*((uint16_t*)(t+2))); |
| iana_len -= (content_len + 4); |
| t += (content_len + 4); |
| break; |
| } |
| } |
| } |
| |
| static void write_pid(char *path) |
| { |
| int pidfile = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0666); |
| |
| if (pidfile > 0) { |
| char pidbuf[12]; |
| |
| sprintf(pidbuf, "%u", (unsigned)getpid()); |
| write(pidfile, pidbuf, strlen(pidbuf)); |
| close(pidfile); |
| } |
| } |
| |
| // Creates environment pointers from RES to use in script |
| static int fill_envp(DHCP_DATA *res) |
| { |
| int ret = setenv("interface", TT.interface_name, 1); |
| |
| if (ret) return ret; |
| inet_ntop(AF_INET6, res->ipaddr, toybuf, INET6_ADDRSTRLEN); |
| ret = setenv("ip",(const char*)toybuf , 1); |
| return ret; |
| } |
| |
| // Executes Script NAME. |
| static void run_script(DHCP_DATA *res, char *name) |
| { |
| volatile int error = 0; |
| struct stat sts; |
| pid_t pid; |
| char *argv[3]; |
| char *script = (toys.optflags & FLAG_s) ? TT.script |
| : "/usr/share/dhcp/default.script"; |
| |
| if (stat(script, &sts) == -1 && errno == ENOENT) return; |
| if (!res || fill_envp(res)) { |
| dbg("Failed to create environment variables.\n"); |
| return; |
| } |
| dbg("Executing %s %s\n", script, name); |
| argv[0] = (char*)script; |
| argv[1] = (char*)name; |
| argv[2] = NULL; |
| fflush(NULL); |
| |
| pid = vfork(); |
| if (pid < 0) { |
| dbg("Fork failed.\n"); |
| return; |
| } |
| if (!pid) { |
| execvp(argv[0], argv); |
| error = errno; |
| _exit(111); |
| } |
| if (error) { |
| waitpid(pid, NULL, 0); |
| errno = error; |
| perror_msg("script exec failed"); |
| } |
| dbg("script complete.\n"); |
| } |
| |
| static void lease_fail() |
| { |
| dbg("Lease failed.\n"); |
| run_script(NULL, "leasefail"); |
| if (toys.optflags & FLAG_n) { |
| xclose(TT.sock); |
| xclose(TT.sock1); |
| error_exit("Lease Failed, Exiting."); |
| } |
| if (toys.optflags & FLAG_b) { |
| dbg("Lease failed. Going to daemon mode.\n"); |
| if (daemon(0,0)) perror_exit("daemonize"); |
| if (toys.optflags & FLAG_p) write_pid(TT.pidfile); |
| toys.optflags &= ~FLAG_b; |
| toys.optflags |= FLAG_f; |
| } |
| } |
| |
| // Generic signal handler real handling is done in main funcrion. |
| static void signal_handler(int sig) |
| { |
| dbg("Caught signal: %d\n", sig); |
| switch (sig) { |
| case SIGUSR1: |
| dbg("SIGUSR1.\n"); |
| if (TT.state == DHCP6RELEASE || TT.state == DHCP6REQUEST ) { |
| TT.state = DHCP6SOLICIT; |
| set_timeout(0); |
| return; |
| } |
| dbg("SIGUSR1 sending renew.\n"); |
| send_msg(DHCP6RENEW); |
| TT.state = DHCP6RENEW; |
| TT.retries = 0; |
| set_timeout(0); |
| break; |
| case SIGUSR2: |
| dbg("SIGUSR2.\n"); |
| if (TT.state == DHCP6RELEASE) return; |
| if (TT.state != DHCP6CONFIRM ) return; |
| dbg("SIGUSR2 sending release.\n"); |
| send_msg(DHCP6RELEASE); |
| TT.state = DHCP6RELEASE; |
| TT.retries = 0; |
| set_timeout(0); |
| break; |
| case SIGTERM: |
| case SIGINT: |
| dbg((sig == SIGTERM)?"SIGTERM.\n":"SIGINT.\n"); |
| if ((toys.optflags & FLAG_R) && TT.state == DHCP6CONFIRM) |
| send_msg(DHCP6RELEASE); |
| if(sig == SIGINT) exit(0); |
| break; |
| default: break; |
| } |
| } |
| |
| // signal setup for SIGUSR1 SIGUSR2 SIGTERM |
| static int setup_signal() |
| { |
| signal(SIGUSR1, signal_handler); |
| signal(SIGUSR2, signal_handler); |
| signal(SIGTERM, signal_handler); |
| signal(SIGINT, signal_handler); |
| return 0; |
| } |
| |
| void dhcp6_main(void) |
| { |
| struct sockaddr_in6 sinaddr6; |
| int constone = 1; |
| fd_set rfds; |
| |
| srand(time(NULL)); |
| setlinebuf(stdout); |
| dbg = dummy; |
| TT.state = DHCP6SOLICIT; |
| |
| if (toys.optflags & FLAG_v) dbg = logit; |
| if (!TT.interface_name) TT.interface_name = "eth0"; |
| if (toys.optflags & FLAG_p) write_pid(TT.pidfile); |
| if (!TT.retry) TT.retry = 3; |
| if (!TT.timeout) TT.timeout = 3; |
| if (!TT.errortimeout) TT.errortimeout = 20; |
| if (toys.optflags & FLAG_S) { |
| openlog("DHCP6 :", LOG_PID, LOG_DAEMON); |
| dbg = logit; |
| } |
| |
| dbg("Interface: %s\n", TT.interface_name); |
| dbg("pid file: %s\n", TT.pidfile); |
| dbg("Retry count: %d\n", TT.retry); |
| dbg("Timeout : %d\n", TT.timeout); |
| dbg("Error timeout: %d\n", TT.errortimeout); |
| |
| |
| |
| setup_signal(); |
| TT.sock1 = xsocket(PF_INET6, SOCK_DGRAM, 0); |
| memset(&sinaddr6, 0, sizeof(sinaddr6)); |
| sinaddr6.sin6_family = AF_INET6; |
| sinaddr6.sin6_port = htons(DHCPC_CLIENT_PORT); |
| sinaddr6.sin6_scope_id = if_nametoindex(TT.interface_name); |
| sinaddr6.sin6_addr = in6addr_any ; |
| |
| xsetsockopt(TT.sock1, SOL_SOCKET, SO_REUSEADDR, &constone, sizeof(constone)); |
| |
| if (bind(TT.sock1, (struct sockaddr *)&sinaddr6, sizeof(sinaddr6))) { |
| xclose(TT.sock1); |
| error_exit("bind failed"); |
| } |
| |
| mode_raw(); |
| set_timeout(0); |
| for (;;) { |
| int maxfd = TT.sock; |
| |
| if (TT.sock >= 0) FD_SET(TT.sock, &rfds); |
| TT.retval = 0; |
| if ((TT.retval = select(maxfd + 1, &rfds, NULL, NULL, &TT.tv)) < 0) { |
| if(errno == EINTR) continue; |
| perror_exit("Error in select"); |
| } |
| if (!TT.retval) { |
| if (TT.state == DHCP6SOLICIT || TT.state == DHCP6CONFIRM) { |
| dbg("State is solicit, sending solicit packet\n"); |
| run_script(NULL, "deconfig"); |
| send_msg(DHCP6SOLICIT); |
| TT.state = DHCP6SOLICIT; |
| TT.retries++; |
| if(TT.retries > TT.retry) set_timeout(TT.errortimeout); |
| else if (TT.retries == TT.retry) { |
| dbg("State is solicit, retry count is max.\n"); |
| lease_fail(); |
| set_timeout(TT.errortimeout); |
| } else set_timeout(TT.timeout); |
| continue; |
| } else if (TT.state == DHCP6REQUEST || TT.state == DHCP6RENEW || |
| TT.state == DHCP6RELEASE) { |
| dbg("State is %d , sending packet\n", TT.state); |
| send_msg(TT.state); |
| TT.retries++; |
| if (TT.retries > TT.retry) set_timeout(TT.errortimeout); |
| else if (TT.retries == TT.retry) { |
| lease_fail(); |
| set_timeout(TT.errortimeout); |
| } else set_timeout(TT.timeout); |
| continue; |
| } |
| } else if (FD_ISSET(TT.sock, &rfds)) { |
| if ((TT.status = read(TT.sock, toybuf, sizeof(toybuf))) <= 0) continue; |
| mymsg = (dhcp6_raw_t*)toybuf; |
| if (ntohs(mymsg->udph.dest) == 546 && |
| !memcmp(mymsg->dhcp6.transaction_id, TT.transction_id, 3)) { |
| if (TT.state == DHCP6SOLICIT) { |
| if (mymsg->dhcp6.msgtype == DHCP6ADVERTISE ) { |
| if (!validate_ids()) { |
| dbg("Invalid id recieved, solicit.\n"); |
| TT.state = DHCP6SOLICIT; |
| continue; |
| } |
| dbg("Got reply to request or solicit.\n"); |
| TT.retries = 0; |
| set_timeout(0); |
| TT.request_length = TT.status - ((char*)&mymsg->dhcp6 - (char*)mymsg); |
| memcpy((uint8_t*)&mesg, &mymsg->dhcp6, TT.request_length); |
| parse_ia_na(mesg.options, TT.request_length); |
| dbg("Status code:%d\n", dhcp_data.status_code); |
| inet_ntop(AF_INET6, dhcp_data.ipaddr, toybuf, INET6_ADDRSTRLEN); |
| dbg("Advertiesed IP: %s\n", toybuf); |
| TT.state = DHCP6REQUEST; |
| } else { |
| dbg("Invalid solicit.\n"); |
| continue; |
| } |
| } else if (TT.state == DHCP6REQUEST || TT.state == DHCP6RENEW ) { |
| if (mymsg->dhcp6.msgtype == DHCP6REPLY) { |
| if (!validate_ids()) { |
| dbg("Invalid id recieved, %d.\n", TT.state); |
| TT.state = DHCP6REQUEST; |
| continue; |
| } |
| dbg("Got reply to request or renew.\n"); |
| TT.request_length = TT.status - ((char*)&mymsg->dhcp6 - (char*)mymsg); |
| memcpy((uint8_t*)&mesg, &mymsg->dhcp6, TT.request_length); |
| parse_ia_na(mymsg->dhcp6.options, TT.request_length); |
| dbg("Status code:%d\n", dhcp_data.status_code); |
| inet_ntop(AF_INET6, dhcp_data.ipaddr, toybuf, INET6_ADDRSTRLEN); |
| dbg("Got IP: %s\n", toybuf); |
| TT.retries = 0; |
| run_script(&dhcp_data, (TT.state == DHCP6REQUEST) ? |
| "request" : "renew"); |
| if (toys.optflags & FLAG_q) { |
| if (toys.optflags & FLAG_R) send_msg(DHCP6RELEASE); |
| break; |
| } |
| TT.state = DHCP6CONFIRM; |
| set_timeout((dhcp_data.va_lf)?dhcp_data.va_lf:INT_MAX); |
| dbg("Setting timeout to intmax."); |
| if (TT.state == DHCP6REQUEST || !(toys.optflags & FLAG_f)) { |
| dbg("Making it a daemon\n"); |
| if (daemon(0,0)) perror_exit("daemonize"); |
| toys.optflags |= FLAG_f; |
| if (toys.optflags & FLAG_p) write_pid(TT.pidfile); |
| } |
| dbg("Making it a foreground.\n"); |
| continue; |
| } else { |
| dbg("Invalid reply.\n"); |
| continue; |
| } |
| } else if (TT.state == DHCP6RELEASE) { |
| dbg("Got reply to release.\n"); |
| run_script(NULL, "release"); |
| set_timeout(INT_MAX); |
| } |
| } |
| } |
| } |
| xclose(TT.sock1); |
| xclose(TT.sock); |
| } |