Mercurial > hg > toybox
view toys/pending/dhcp.c @ 1572:da1bf31ed322 draft
Tweak the "ignoring return value" fortify workaround for readlinkat.
We zero the buffer and if the link read fails that's left alone, so
it's ok for the symlink not to be there. Unfortunately, typecasting the
return value to (void) doesn't shut up gcc, and having an if(); with the
semicolon on the same line doesn't shut up llvm. (The semicolon on a new
line would, but C does not have significant whitespace and I'm not going
to humor llvm if it plans to start.)
So far, empty curly brackets consistently get the warning to shut up.
author | Rob Landley <rob@landley.net> |
---|---|
date | Mon, 24 Nov 2014 17:23:23 -0600 |
parents | e0c9c5424864 |
children | 856b544f8fce |
line wrap: on
line source
/* dhcp.c - DHCP client for dynamic network configuration. * * Copyright 2012 Madhur Verma <mad.flexi@gmail.com> * Copyright 2013 Kyungwan Han <asura321@gmail.com> * * Not in SUSv4. USE_DHCP(NEWTOY(dhcp, "V:H:F:x*r:O*A#<0T#<0t#<0s:p:i:SBRCaovqnbf", TOYFLAG_SBIN|TOYFLAG_ROOTONLY)) config DHCP bool "dhcp" default n help usage: dhcp [-fbnqvoCRB] [-i IFACE] [-r IP] [-s PROG] [-p PIDFILE] [-H HOSTNAME] [-V VENDOR] [-x OPT:VAL] [-O OPT] Configure network dynamicaly using DHCP. -i Interface to use (default eth0) -p Create pidfile -s Run PROG at DHCP events (default /usr/share/dhcp/default.script) -B Request broadcast replies -t Send up to N discover 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 -a Use arping to validate offered address -O Request option OPT from server (cumulative) -o Don't request any options (unless -O is given) -r Request this IP address -x OPT:VAL Include option OPT in sent packets (cumulative) -F Ask server to update DNS mapping for NAME -H Send NAME as client hostname (default none) -V VENDOR Vendor identifier (default 'toybox VERSION') -C Don't send MAC as client identifier -v Verbose Signals: USR1 Renew current lease USR2 Release current lease */ #define FOR_dhcp #include "toys.h" // TODO: headers not in posix: #include <netinet/ip.h> #include <netinet/udp.h> #include <netpacket/packet.h> #include <linux/filter.h> //FIXME: linux specific. fix for other OS ports #include <linux/if_ether.h> GLOBALS( char *iface; char *pidfile; char *script; long retries; long timeout; long tryagain; struct arg_list *req_opt; char *req_ip; struct arg_list *pkt_opt; char *fdn_name; char *hostname; char *vendor_cls; ) #define flag_get(f,v,d) ((toys.optflags & f) ? v : d) #define flag_chk(f) ((toys.optflags & f) ? 1 : 0) #define STATE_INIT 0 #define STATE_REQUESTING 1 #define STATE_BOUND 2 #define STATE_RENEWING 3 #define STATE_REBINDING 4 #define STATE_RENEW_REQUESTED 5 #define STATE_RELEASED 6 #define BOOTP_BROADCAST 0x8000 #define DHCP_MAGIC 0x63825363 #define DHCP_REQUEST 1 #define DHCP_REPLY 2 #define DHCP_HTYPE_ETHERNET 1 #define DHCPC_SERVER_PORT 67 #define DHCPC_CLIENT_PORT 68 #define DHCPDISCOVER 1 #define DHCPOFFER 2 #define DHCPREQUEST 3 #define DHCPACK 5 #define DHCPNAK 6 #define DHCPRELEASE 7 #define DHCP_OPTION_PADDING 0x00 #define DHCP_OPTION_SUBNET_MASK 0x01 #define DHCP_OPTION_ROUTER 0x03 #define DHCP_OPTION_DNS_SERVER 0x06 #define DHCP_OPTION_HOST_NAME 0x0c #define DHCP_OPTION_BROADCAST 0x1c #define DHCP_OPTION_REQ_IPADDR 0x32 #define DHCP_OPTION_LEASE_TIME 0x33 #define DHCP_OPTION_OVERLOAD 0x34 #define DHCP_OPTION_MSG_TYPE 0x35 #define DHCP_OPTION_SERVER_ID 0x36 #define DHCP_OPTION_REQ_LIST 0x37 #define DHCP_OPTION_MAX_SIZE 0x39 #define DHCP_OPTION_CLIENTID 0x3D #define DHCP_OPTION_VENDOR 0x3C #define DHCP_OPTION_FQDN 0x51 #define DHCP_OPTION_END 0xFF #define DHCP_NUM8 (1<<8) #define DHCP_NUM16 (1<<9) #define DHCP_NUM32 DHCP_NUM16 | DHCP_NUM8 #define DHCP_STRING (1<<10) #define DHCP_STRLST (1<<11) #define DHCP_IP (1<<12) #define DHCP_IPLIST (1<<13) #define DHCP_IPPLST (1<<14) #define DHCP_STCRTS (1<<15) #define LOG_SILENT 0x0 #define LOG_CONSOLE 0x1 #define LOG_SYSTEM 0x2 #define MODE_OFF 0 #define MODE_RAW 1 #define MODE_APP 2 static void (*dbg)(char *format, ...); static void dummy(char *format, ...){ return; } typedef struct dhcpc_result_s { struct in_addr serverid; struct in_addr ipaddr; struct in_addr netmask; struct in_addr dnsaddr; struct in_addr default_router; uint32_t lease_time; } dhcpc_result_t; typedef struct __attribute__((packed)) dhcp_msg_s { uint8_t op; uint8_t htype; uint8_t hlen; uint8_t hops; uint32_t xid; uint16_t secs; uint16_t flags; uint32_t ciaddr; uint32_t yiaddr; uint32_t nsiaddr; uint32_t ngiaddr; uint8_t chaddr[16]; uint8_t sname[64]; uint8_t file[128]; uint32_t cookie; uint8_t options[308]; } dhcp_msg_t; typedef struct __attribute__((packed)) dhcp_raw_s { struct iphdr iph; struct udphdr udph; dhcp_msg_t dhcp; } dhcp_raw_t; typedef struct dhcpc_state_s { uint8_t macaddr[6]; char *iface; int ifindex; int sockfd; int status; int mode; uint32_t mask; struct in_addr ipaddr; struct in_addr serverid; dhcp_msg_t pdhcp; } dhcpc_state_t; typedef struct option_val_s { char *key; uint16_t code; void *val; size_t len; } option_val_t; struct fd_pair { int rd; int wr; }; static uint32_t xid; static dhcpc_state_t *state; static struct fd_pair sigfd; uint8_t bmacaddr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; int set = 1; uint8_t infomode = LOG_CONSOLE; uint8_t raw_opt[29]; int raw_optcount = 0; struct arg_list *x_opt; in_addr_t server = 0; static option_val_t *msgopt_list = NULL; static option_val_t options_list[] = { {"lease" , DHCP_NUM32 | 0x33, NULL, 0}, {"subnet" , DHCP_IP | 0x01, NULL, 0}, {"broadcast" , DHCP_IP | 0x1c, NULL, 0}, {"router" , DHCP_IP | 0x03, NULL, 0}, {"ipttl" , DHCP_NUM8 | 0x17, NULL, 0}, {"mtu" , DHCP_NUM16 | 0x1a, NULL, 0}, {"hostname" , DHCP_STRING | 0x0c, NULL, 0}, {"domain" , DHCP_STRING | 0x0f, NULL, 0}, {"search" , DHCP_STRLST | 0x77, NULL, 0}, {"nisdomain" , DHCP_STRING | 0x28, NULL, 0}, {"timezone" , DHCP_NUM32 | 0x02, NULL, 0}, {"tftp" , DHCP_STRING | 0x42, NULL, 0}, {"bootfile" , DHCP_STRING | 0x43, NULL, 0}, {"bootsize" , DHCP_NUM16 | 0x0d, NULL, 0}, {"rootpath" , DHCP_STRING | 0x11, NULL, 0}, {"wpad" , DHCP_STRING | 0xfc, NULL, 0}, {"serverid" , DHCP_IP | 0x36, NULL, 0}, {"message" , DHCP_STRING | 0x38, NULL, 0}, {"vlanid" , DHCP_NUM32 | 0x84, NULL, 0}, {"vlanpriority" , DHCP_NUM32 | 0x85, NULL, 0}, {"dns" , DHCP_IPLIST | 0x06, NULL, 0}, {"wins" , DHCP_IPLIST | 0x2c, NULL, 0}, {"nissrv" , DHCP_IPLIST | 0x29, NULL, 0}, {"ntpsrv" , DHCP_IPLIST | 0x2a, NULL, 0}, {"lprsrv" , DHCP_IPLIST | 0x09, NULL, 0}, {"swapsrv" , DHCP_IP | 0x10, NULL, 0}, {"routes" , DHCP_STCRTS | 0x21, NULL, 0}, {"staticroutes" , DHCP_STCRTS | 0x79, NULL, 0}, {"msstaticroutes" , DHCP_STCRTS | 0xf9, NULL, 0}, }; static struct sock_filter filter_instr[] = { BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9), BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 0, 6), BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 6), BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x1fff, 4, 0), BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), BPF_STMT(BPF_LD|BPF_H|BPF_IND, 2), BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 68, 0, 1), BPF_STMT(BPF_RET|BPF_K, 0xffffffff), BPF_STMT(BPF_RET|BPF_K, 0), }; static struct sock_fprog filter_prog = { .len = ARRAY_LEN(filter_instr), .filter = (struct sock_filter *) filter_instr, }; // calculate options size. static int dhcp_opt_size(uint8_t *optionptr) { int i = 0; for(;optionptr[i] != 0xff; i++) if(optionptr[i] != 0x00) i += optionptr[i + 1] + 2 -1; return i; } // calculates checksum for dhcp messages. static uint16_t dhcp_checksum(void *addr, int count) { int32_t sum = 0; uint16_t tmp = 0, *source = (uint16_t *)addr; while (count > 1) { sum += *source++; count -= 2; } if (count > 0) { *(uint8_t*)&tmp = *(uint8_t*)source; sum += tmp; } while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16); return ~sum; } // gets information of INTERFACE and updates IFINDEX, MAC and IP static int get_interface( char *interface, int *ifindex, uint32_t *oip, uint8_t *mac) { struct ifreq req; struct sockaddr_in *ip; int fd = xsocket(AF_INET, SOCK_RAW, IPPROTO_RAW); req.ifr_addr.sa_family = AF_INET; strncpy(req.ifr_name, interface, IFNAMSIZ); req.ifr_name[IFNAMSIZ-1] = '\0'; xioctl(fd, SIOCGIFFLAGS, &req); if (!(req.ifr_flags & IFF_UP)) return -1; if (oip) { xioctl(fd, SIOCGIFADDR, &req); ip = (struct sockaddr_in*) &req.ifr_addr; dbg("IP %s\n", inet_ntoa(ip->sin_addr)); *oip = ntohl(ip->sin_addr.s_addr); } if (ifindex) { xioctl(fd, SIOCGIFINDEX, &req); dbg("Adapter index %d\n", req.ifr_ifindex); *ifindex = req.ifr_ifindex; } if (mac) { xioctl(fd, SIOCGIFHWADDR, &req); memcpy(mac, req.ifr_hwaddr.sa_data, 6); dbg("MAC %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } close(fd); return 0; } /* *logs messeges to syslog or console *opening the log is still left with applet. *FIXME: move to more relevent lib. probably libc.c */ static void infomsg(uint8_t infomode, char *s, ...) { int used; char *msg; va_list p, t; if (infomode == LOG_SILENT) return; va_start(p, s); va_copy(t, p); used = vsnprintf(NULL, 0, s, t); used++; va_end(t); msg = xmalloc(used); vsnprintf(msg, used, s, p); va_end(p); if (infomode & LOG_SYSTEM) syslog(LOG_INFO, "%s", msg); if (infomode & LOG_CONSOLE) printf("%s\n", msg); free(msg); } /* * Writes self PID in file PATH * FIXME: libc implementation only writes in /var/run * this is more generic as some implemenation may provide * arguments to write in specific file. as dhcpd does. */ 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); } } // String STR to UINT32 conversion strored in VAR static long strtou32( char *str) { char *endptr = NULL; int base = 10; errno=0; if (str[0]=='0' && (str[1]=='x' || str[1]=='X')) { base = 16; str+=2; } long ret_val = strtol(str, &endptr, base); if (errno) return -1; else if (endptr && (*endptr!='\0'||endptr == str)) return -1; return ret_val; } // IP String STR to binary data. static int striptovar( char *str, void *var) { in_addr_t addr; if(!str) error_exit("NULL address string."); addr = inet_addr(str); if(addr == -1) error_exit("Wrong address %s.",str ); *((uint32_t*)(var)) = (uint32_t)addr; return 0; } // String to dhcp option conversion static int strtoopt( char *str, uint8_t optonly) { char *option, *valstr, *grp, *tp; long optcode = 0, convtmp; uint16_t flag = 0; uint32_t mask, nip, router; int count, size = ARRAY_LEN(options_list); if (!*str) return 0; option = strtok((char*)str, ":"); if (!option) return -1; dbg("-x option : %s ", option); optcode = strtou32(option); if (optcode > 0 && optcode < 256) { // raw option for (count = 0; count < size; count++) { if ((options_list[count].code & 0X00FF) == optcode) { flag = (options_list[count].code & 0XFF00); break; } } if (count == size) error_exit("Obsolete OR Unknown Option : %s", option); } else { // string option for (count = 0; count < size; count++) { if (!strcmp(options_list[count].key, option)) { flag = (options_list[count].code & 0XFF00); optcode = (options_list[count].code & 0X00FF); break; } } if (count == size) error_exit("Obsolete OR Unknown Option : %s", option); } if (!flag || !optcode) return -1; if (optonly) return optcode; valstr = strtok(NULL, "\n"); if (!valstr) error_exit("option %s has no value defined.\n", option); dbg(" value : %-20s \n ", valstr); switch (flag) { case DHCP_NUM32: options_list[count].len = sizeof(uint32_t); options_list[count].val = xmalloc(sizeof(uint32_t)); convtmp = strtou32(valstr); if (convtmp < 0) error_exit("Invalid/wrong formated number %s", valstr); convtmp = htonl(convtmp); memcpy(options_list[count].val, &convtmp, sizeof(uint32_t)); break; case DHCP_NUM16: options_list[count].len = sizeof(uint16_t); options_list[count].val = xmalloc(sizeof(uint16_t)); convtmp = strtou32(valstr); if (convtmp < 0) error_exit("Invalid/malformed number %s", valstr); convtmp = htons(convtmp); memcpy(options_list[count].val, &convtmp, sizeof(uint16_t)); break; case DHCP_NUM8: options_list[count].len = sizeof(uint8_t); options_list[count].val = xmalloc(sizeof(uint8_t)); convtmp = strtou32(valstr); if (convtmp < 0) error_exit("Invalid/malformed number %s", valstr); memcpy(options_list[count].val, &convtmp, sizeof(uint8_t)); break; case DHCP_IP: options_list[count].len = sizeof(uint32_t); options_list[count].val = xmalloc(sizeof(uint32_t)); striptovar(valstr, options_list[count].val); break; case DHCP_STRING: options_list[count].len = strlen(valstr); options_list[count].val = strdup(valstr); break; case DHCP_IPLIST: while(valstr){ options_list[count].val = xrealloc(options_list[count].val, options_list[count].len + sizeof(uint32_t)); striptovar(valstr, ((uint8_t*)options_list[count].val)+options_list[count].len); options_list[count].len += sizeof(uint32_t); valstr = strtok(NULL," \t"); } break; case DHCP_STRLST: case DHCP_IPPLST: break; case DHCP_STCRTS: /* Option binary format: * mask [one byte, 0..32] * ip [0..4 bytes depending on mask] * router [4 bytes] * may be repeated * staticroutes 10.0.0.0/8 10.127.0.1, 10.11.12.0/24 10.11.12.1 */ grp = strtok(valstr, ",");; while(grp){ while(*grp == ' ' || *grp == '\t') grp++; tp = strchr(grp, '/'); if (!tp) error_exit("malformed static route option"); *tp = '\0'; mask = strtol(++tp, &tp, 10); if (striptovar(grp, (uint8_t*)&nip) < 0) error_exit("malformed static route option"); while(*tp == ' ' || *tp == '\t' || *tp == '-') tp++; if (striptovar(tp, (uint8_t*)&router) < 0) error_exit("malformed static route option"); options_list[count].val = xrealloc(options_list[count].val, options_list[count].len + 1 + mask/8 + 4); memcpy(((uint8_t*)options_list[count].val)+options_list[count].len, &mask, 1); options_list[count].len += 1; memcpy(((uint8_t*)options_list[count].val)+options_list[count].len, &nip, mask/8); options_list[count].len += mask/8; memcpy(((uint8_t*)options_list[count].val)+options_list[count].len, &router, 4); options_list[count].len += 4; tp = NULL; grp = strtok(NULL, ","); } break; } return 0; } // Creates environment pointers from RES to use in script static int fill_envp(dhcpc_result_t *res) { struct in_addr temp; int size = ARRAY_LEN(options_list), count, ret = -1; ret = setenv("interface", state->iface, 1); if (!res) return ret; if (res->ipaddr.s_addr) { temp.s_addr = htonl(res->ipaddr.s_addr); ret = setenv("ip", inet_ntoa(temp), 1); if (ret) return ret; } if (msgopt_list) { for (count = 0; count < size; count++) { if ((msgopt_list[count].len == 0) || (msgopt_list[count].val == NULL)) continue; ret = setenv(msgopt_list[count].key, (char*)msgopt_list[count].val, 1); if (ret) return ret; } } return ret; } // Executes Script NAME. static void run_script(dhcpc_result_t *res, char *name) { volatile int error = 0; pid_t pid; char *argv[3]; struct stat sts; char *script = flag_get(FLAG_s, TT.script, "/usr/share/dhcp/default.script"); if (stat(script, &sts) == -1 && errno == ENOENT) return; if (fill_envp(res)) { dbg("Failed to create environment variables."); 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"); } // returns a randome ID static uint32_t getxid(void) { uint32_t randnum; int fd = xopen("/dev/urandom", O_RDONLY); xreadall(fd, &randnum, sizeof(randnum)); xclose(fd); return randnum; } // opens socket in raw mode. static int mode_raw(void) { state->mode = MODE_OFF; struct sockaddr_ll sock; if (state->sockfd > 0) close(state->sockfd); dbg("Opening raw socket on ifindex %d\n", state->ifindex); state->sockfd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); if (state->sockfd < 0) { dbg("MODE RAW : socket fail ERROR : %d\n", state->sockfd); return -1; } dbg("Got raw socket fd %d\n", state->sockfd); memset(&sock, 0, sizeof(sock)); sock.sll_family = AF_PACKET; sock.sll_protocol = htons(ETH_P_IP); sock.sll_ifindex = state->ifindex; if (bind(state->sockfd, (struct sockaddr *) &sock, sizeof(sock))) { dbg("MODE RAW : bind fail.\n"); close(state->sockfd); return -1; } state->mode = MODE_RAW; if (setsockopt(state->sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog)) < 0) dbg("MODE RAW : filter attach fail.\n"); dbg("MODE RAW : success\n"); return 0; } // opens UDP socket static int mode_app(void) { struct sockaddr_in addr; struct ifreq ifr; state->mode = MODE_OFF; if (state->sockfd > 0) close(state->sockfd); dbg("Opening listen socket on *:%d %s\n", DHCPC_CLIENT_PORT, state->iface); state->sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (state->sockfd < 0) { dbg("MODE APP : socket fail ERROR: %d\n", state->sockfd); return -1; } setsockopt(state->sockfd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set)); if (setsockopt(state->sockfd, SOL_SOCKET, SO_BROADCAST, &set, sizeof(set)) == -1) { dbg("MODE APP : brodcast failed.\n"); close(state->sockfd); return -1; } strncpy(ifr.ifr_name, state->iface, IFNAMSIZ); ifr.ifr_name[IFNAMSIZ -1] = '\0'; setsockopt(state->sockfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(DHCPC_CLIENT_PORT); addr.sin_addr.s_addr = INADDR_ANY ; if (bind(state->sockfd, (struct sockaddr *) &addr, sizeof(addr))) { close(state->sockfd); dbg("MODE APP : bind failed.\n"); return -1; } state->mode = MODE_APP; dbg("MODE APP : success\n"); return 0; } static int read_raw(void) { dhcp_raw_t packet; uint16_t check; int bytes = 0; memset(&packet, 0, sizeof(packet)); if ((bytes = read(state->sockfd, &packet, sizeof(packet))) < 0) { dbg("\tPacket read error, ignoring\n"); return bytes; } if (bytes < (int) (sizeof(packet.iph) + sizeof(packet.udph))) { dbg("\tPacket is too short, ignoring\n"); return -2; } if (bytes < ntohs(packet.iph.tot_len)) { dbg("\tOversized packet, ignoring\n"); return -2; } // ignore any extra garbage bytes bytes = ntohs(packet.iph.tot_len); // make sure its the right packet for us, and that it passes sanity checks if (packet.iph.protocol != IPPROTO_UDP || packet.iph.version != IPVERSION || packet.iph.ihl != (sizeof(packet.iph) >> 2) || packet.udph.dest != htons(DHCPC_CLIENT_PORT) || ntohs(packet.udph.len) != (uint16_t)(bytes - sizeof(packet.iph))) { dbg("\tUnrelated/bogus packet, ignoring\n"); return -2; } // verify IP checksum check = packet.iph.check; packet.iph.check = 0; if (check != dhcp_checksum(&packet.iph, sizeof(packet.iph))) { dbg("\tBad IP header checksum, ignoring\n"); return -2; } memset(&packet.iph, 0, ((size_t) &((struct iphdr *)0)->protocol)); packet.iph.tot_len = packet.udph.len; check = packet.udph.check; packet.udph.check = 0; if (check && check != dhcp_checksum(&packet, bytes)) { dbg("\tPacket with bad UDP checksum received, ignoring\n"); return -2; } memcpy(&state->pdhcp, &packet.dhcp, bytes - (sizeof(packet.iph) + sizeof(packet.udph))); if (state->pdhcp.cookie != htonl(DHCP_MAGIC)) { dbg("\tPacket with bad magic, ignoring\n"); return -2; } return bytes - sizeof(packet.iph) - sizeof(packet.udph); } static int read_app(void) { int ret; memset(&state->pdhcp, 0, sizeof(dhcp_msg_t)); if ((ret = read(state->sockfd, &state->pdhcp, sizeof(dhcp_msg_t))) < 0) { dbg("Packet read error, ignoring\n"); return ret; /* returns -1 */ } if (state->pdhcp.cookie != htonl(DHCP_MAGIC)) { dbg("Packet with bad magic, ignoring\n"); return -2; } return ret; } // Sends data through raw socket. static int send_raw(void) { struct sockaddr_ll dest_sll; dhcp_raw_t packet; unsigned padding; int fd, result = -1; memset(&packet, 0, sizeof(dhcp_raw_t)); memcpy(&packet.dhcp, &state->pdhcp, sizeof(dhcp_msg_t)); if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) { dbg("SEND RAW: socket failed\n"); return result; } memset(&dest_sll, 0, sizeof(dest_sll)); dest_sll.sll_family = AF_PACKET; dest_sll.sll_protocol = htons(ETH_P_IP); dest_sll.sll_ifindex = state->ifindex; dest_sll.sll_halen = 6; memcpy(dest_sll.sll_addr, bmacaddr , 6); if (bind(fd, (struct sockaddr *) &dest_sll, sizeof(dest_sll)) < 0) { dbg("SEND RAW: bind failed\n"); close(fd); return result; } padding = 308 - 1 - dhcp_opt_size(state->pdhcp.options); packet.iph.protocol = IPPROTO_UDP; packet.iph.saddr = INADDR_ANY; packet.iph.daddr = INADDR_BROADCAST; packet.udph.source = htons(DHCPC_CLIENT_PORT); packet.udph.dest = htons(DHCPC_SERVER_PORT); packet.udph.len = htons(sizeof(dhcp_raw_t) - sizeof(struct iphdr) - padding); packet.iph.tot_len = packet.udph.len; packet.udph.check = dhcp_checksum(&packet, sizeof(dhcp_raw_t) - padding); packet.iph.tot_len = htons(sizeof(dhcp_raw_t) - padding); packet.iph.ihl = sizeof(packet.iph) >> 2; packet.iph.version = IPVERSION; packet.iph.ttl = IPDEFTTL; packet.iph.check = dhcp_checksum(&packet.iph, sizeof(packet.iph)); result = sendto(fd, &packet, sizeof(dhcp_raw_t) - padding, 0, (struct sockaddr *) &dest_sll, sizeof(dest_sll)); close(fd); if (result < 0) dbg("SEND RAW: PACKET send error\n"); return result; } // Sends data through UDP socket. static int send_app(void) { struct sockaddr_in cli; int fd, ret = -1; if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { dbg("SEND APP: sock failed.\n"); return ret; } setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(set)); memset(&cli, 0, sizeof(cli)); cli.sin_family = AF_INET; cli.sin_port = htons(DHCPC_CLIENT_PORT); cli.sin_addr.s_addr = state->pdhcp.ciaddr; if (bind(fd, (struct sockaddr *)&cli, sizeof(cli)) == -1) { dbg("SEND APP: bind failed.\n"); goto error_fd; } memset(&cli, 0, sizeof(cli)); cli.sin_family = AF_INET; cli.sin_port = htons(DHCPC_SERVER_PORT); cli.sin_addr.s_addr = state->serverid.s_addr; if (connect(fd, (struct sockaddr *)&cli, sizeof(cli)) == -1) { dbg("SEND APP: connect failed.\n"); goto error_fd; } int padding = 308 - 1 - dhcp_opt_size(state->pdhcp.options); if((ret = write(fd, &state->pdhcp, sizeof(dhcp_msg_t) - padding)) < 0) { dbg("SEND APP: write failed error %d\n", ret); goto error_fd; } dbg("SEND APP: write success wrote %d\n", ret); error_fd: close(fd); return ret; } // Generic signal handler real handling is done in main funcrion. static void signal_handler(int sig) { unsigned char ch = sig; if (write(sigfd.wr, &ch, 1) != 1) dbg("can't send signal\n"); } // signal setup for SIGUSR1 SIGUSR2 SIGTERM static int setup_signal() { if (pipe((int *)&sigfd) < 0) { dbg("signal pipe failed\n"); return -1; } fcntl(sigfd.wr , F_SETFD, FD_CLOEXEC); fcntl(sigfd.rd , F_SETFD, FD_CLOEXEC); int flags = fcntl(sigfd.wr, F_GETFL); fcntl(sigfd.wr, F_SETFL, flags | O_NONBLOCK); signal(SIGUSR1, signal_handler); signal(SIGUSR2, signal_handler); signal(SIGTERM, signal_handler); return 0; } // adds client id to dhcp packet static uint8_t *dhcpc_addclientid(uint8_t *optptr) { *optptr++ = DHCP_OPTION_CLIENTID; *optptr++ = 7; *optptr++ = 1; memcpy(optptr, &state->macaddr, 6); return optptr + 6; } // adds messege type to dhcp packet static uint8_t *dhcpc_addmsgtype(uint8_t *optptr, uint8_t type) { *optptr++ = DHCP_OPTION_MSG_TYPE; *optptr++ = 1; *optptr++ = type; return optptr; } // adds max size to dhcp packet static uint8_t *dhcpc_addmaxsize(uint8_t *optptr, uint16_t size) { *optptr++ = DHCP_OPTION_MAX_SIZE; *optptr++ = 2; memcpy(optptr, &size, 2); return optptr + 2; } static uint8_t *dhcpc_addstropt(uint8_t *optptr, uint8_t opcode, char* str, int len) { *optptr++ = opcode; *optptr++ = len; memcpy(optptr, str, len); return optptr + len; } // adds server id to dhcp packet. static uint8_t *dhcpc_addserverid(struct in_addr *serverid, uint8_t *optptr) { *optptr++ = DHCP_OPTION_SERVER_ID; *optptr++ = 4; memcpy(optptr, &serverid->s_addr, 4); return optptr + 4; } // adds requested ip address to dhcp packet. static uint8_t *dhcpc_addreqipaddr(struct in_addr *ipaddr, uint8_t *optptr) { *optptr++ = DHCP_OPTION_REQ_IPADDR; *optptr++ = 4; memcpy(optptr, &ipaddr->s_addr, 4); return optptr + 4; } // adds hostname to dhcp packet. static uint8_t *dhcpc_addfdnname(uint8_t *optptr, char *hname) { int size = strlen(hname); *optptr++ = DHCP_OPTION_FQDN; *optptr++ = size + 3; *optptr++ = 0x1; //flags optptr += 2; // two blank bytes strncpy((char*)optptr, hname, size); // name return optptr + size; } // adds request options using -o,-O flag to dhcp packet static uint8_t *dhcpc_addreqoptions(uint8_t *optptr) { uint8_t *len; *optptr++ = DHCP_OPTION_REQ_LIST; len = optptr; *len = 0; optptr++; if (!flag_chk(FLAG_o)) { *len = 4; *optptr++ = DHCP_OPTION_SUBNET_MASK; *optptr++ = DHCP_OPTION_ROUTER; *optptr++ = DHCP_OPTION_DNS_SERVER; *optptr++ = DHCP_OPTION_BROADCAST; } if (flag_chk(FLAG_O)) { memcpy(optptr++, raw_opt, raw_optcount); *len += raw_optcount; } return optptr; } static uint8_t *dhcpc_addend(uint8_t *optptr) { *optptr++ = DHCP_OPTION_END; return optptr; } // Sets values of -x options in dhcp discover and request packet. static uint8_t* set_xopt(uint8_t *optptr) { int count; int size = ARRAY_LEN(options_list); for (count = 0; count < size; count++) { if ((options_list[count].len == 0) || (options_list[count].val == NULL)) continue; *optptr++ = (uint8_t) (options_list[count].code & 0x00FF); *optptr++ = (uint8_t) options_list[count].len; memcpy(optptr, options_list[count].val, options_list[count].len); optptr += options_list[count].len; } return optptr; } static uint32_t get_option_serverid (uint8_t *opt, dhcpc_result_t *presult) { uint32_t var = 0; while (*opt != DHCP_OPTION_SERVER_ID) { if (*opt == DHCP_OPTION_END) return var; opt += opt[1] + 2; } memcpy(&var, opt+2, sizeof(uint32_t)); state->serverid.s_addr = var; presult->serverid.s_addr = state->serverid.s_addr; presult->serverid.s_addr = ntohl(presult->serverid.s_addr); return var; } static uint8_t get_option_msgtype(uint8_t *opt) { uint32_t var = 0; while (*opt != DHCP_OPTION_MSG_TYPE) { if (*opt == DHCP_OPTION_END) return var; opt += opt[1] + 2; } memcpy(&var, opt+2, sizeof(uint8_t)); return var; } static uint8_t get_option_lease(uint8_t *opt, dhcpc_result_t *presult) { uint32_t var = 0; while (*opt != DHCP_OPTION_LEASE_TIME) { if (*opt == DHCP_OPTION_END) return var; opt += opt[1] + 2; } memcpy(&var, opt+2, sizeof(uint32_t)); var = htonl(var); presult->lease_time = var; return var; } // sends dhcp msg of MSGTYPE static int dhcpc_sendmsg(int msgtype) { uint8_t *pend; struct in_addr rqsd; char *vendor; // Create the common message header settings memset(&state->pdhcp, 0, sizeof(dhcp_msg_t)); state->pdhcp.op = DHCP_REQUEST; state->pdhcp.htype = DHCP_HTYPE_ETHERNET; state->pdhcp.hlen = 6; state->pdhcp.xid = xid; memcpy(state->pdhcp.chaddr, state->macaddr, 6); memset(&state->pdhcp.chaddr[6], 0, 10); state->pdhcp.cookie = htonl(DHCP_MAGIC);; // Add the common header options pend = state->pdhcp.options; pend = dhcpc_addmsgtype(pend, msgtype); if (!flag_chk(FLAG_C)) pend = dhcpc_addclientid(pend); // Handle the message specific settings switch (msgtype) { case DHCPDISCOVER: // Broadcast DISCOVER message to all servers state->pdhcp.flags = htons(BOOTP_BROADCAST); // Broadcast bit. if (flag_chk(FLAG_r)) { inet_aton(TT.req_ip, &rqsd); pend = dhcpc_addreqipaddr(&rqsd, pend); } pend = dhcpc_addmaxsize(pend, htons(sizeof(dhcp_raw_t))); vendor = flag_get(FLAG_V, TT.vendor_cls, "toybox\0"); pend = dhcpc_addstropt(pend, DHCP_OPTION_VENDOR, vendor, strlen(vendor)); if (flag_chk(FLAG_H)) pend = dhcpc_addstropt(pend, DHCP_OPTION_HOST_NAME, TT.hostname, strlen(TT.hostname)); if (flag_chk(FLAG_F)) pend = dhcpc_addfdnname(pend, TT.fdn_name); if ((!flag_chk(FLAG_o)) || flag_chk(FLAG_O)) pend = dhcpc_addreqoptions(pend); if (flag_chk(FLAG_x)) pend = set_xopt(pend); break; case DHCPREQUEST: // Send REQUEST message to the server that sent the *first* OFFER state->pdhcp.flags = htons(BOOTP_BROADCAST); // Broadcast bit. if (state->status == STATE_RENEWING) memcpy(&state->pdhcp.ciaddr, &state->ipaddr.s_addr, 4); pend = dhcpc_addmaxsize(pend, htons(sizeof(dhcp_raw_t))); rqsd.s_addr = htonl(server); pend = dhcpc_addserverid(&rqsd, pend); pend = dhcpc_addreqipaddr(&state->ipaddr, pend); vendor = flag_get(FLAG_V, TT.vendor_cls, "toybox\0"); pend = dhcpc_addstropt(pend, DHCP_OPTION_VENDOR, vendor, strlen(vendor)); if (flag_chk(FLAG_H)) pend = dhcpc_addstropt(pend, DHCP_OPTION_HOST_NAME, TT.hostname, strlen(TT.hostname)); if (flag_chk(FLAG_F)) pend = dhcpc_addfdnname(pend, TT.fdn_name); if ((!flag_chk(FLAG_o)) || flag_chk(FLAG_O)) pend = dhcpc_addreqoptions(pend); if (flag_chk(FLAG_x)) pend = set_xopt(pend); break; case DHCPRELEASE: // Send RELEASE message to the server. memcpy(&state->pdhcp.ciaddr, &state->ipaddr.s_addr, 4); rqsd.s_addr = htonl(server); pend = dhcpc_addserverid(&rqsd, pend); break; default: return -1; } pend = dhcpc_addend(pend); if (state->mode == MODE_APP) return send_app(); return send_raw(); } /* * parses options from received dhcp packet at OPTPTR and * stores result in PRESULT or MSGOPT_LIST */ static uint8_t dhcpc_parseoptions(dhcpc_result_t *presult, uint8_t *optptr) { uint8_t type = 0, *options, overloaded = 0;; uint16_t flag = 0; uint32_t convtmp = 0; char *dest, *pfx; struct in_addr addr; int count, optlen, size = ARRAY_LEN(options_list); if (flag_chk(FLAG_x)) { if(msgopt_list){ for (count = 0; count < size; count++){ if(msgopt_list[count].val) free(msgopt_list[count].val); msgopt_list[count].val = NULL; msgopt_list[count].len = 0; } } else { msgopt_list = xmalloc(sizeof(options_list)); memcpy(msgopt_list, options_list, sizeof(options_list)); for (count = 0; count < size; count++) { msgopt_list[count].len = 0; msgopt_list[count].val = NULL; } } } else { msgopt_list = options_list; for (count = 0; count < size; count++) { msgopt_list[count].len = 0; if(msgopt_list[count].val) free(msgopt_list[count].val); msgopt_list[count].val = NULL; } } while (*optptr != DHCP_OPTION_END) { if (*optptr == DHCP_OPTION_PADDING) { optptr++; continue; } if (*optptr == DHCP_OPTION_OVERLOAD) { overloaded = optptr[2]; optptr += optptr[1] + 2; continue; } for (count = 0, flag = 0; count < size; count++) { if ((msgopt_list[count].code & 0X00FF) == *optptr) { flag = (msgopt_list[count].code & 0XFF00); break; } } switch (flag) { case DHCP_NUM32: memcpy(&convtmp, &optptr[2], sizeof(uint32_t)); convtmp = htonl(convtmp); sprintf(toybuf, "%u", convtmp); msgopt_list[count].val = strdup(toybuf); msgopt_list[count].len = strlen(toybuf); break; case DHCP_NUM16: memcpy(&convtmp, &optptr[2], sizeof(uint16_t)); convtmp = htons(convtmp); sprintf(toybuf, "%u", convtmp); msgopt_list[count].val = strdup(toybuf); msgopt_list[count].len = strlen(toybuf); break; case DHCP_NUM8: memcpy(&convtmp, &optptr[2], sizeof(uint8_t)); sprintf(toybuf, "%u", convtmp); msgopt_list[count].val = strdup(toybuf); msgopt_list[count].len = strlen(toybuf); break; case DHCP_IP: memcpy(&convtmp, &optptr[2], sizeof(uint32_t)); addr.s_addr = convtmp; sprintf(toybuf, "%s", inet_ntoa(addr)); msgopt_list[count].val = strdup(toybuf); msgopt_list[count].len = strlen(toybuf); break; case DHCP_STRING: sprintf(toybuf, "%.*s", optptr[1], &optptr[2]); msgopt_list[count].val = strdup(toybuf); msgopt_list[count].len = strlen(toybuf); break; case DHCP_IPLIST: optlen = optptr[1]; dest = toybuf; while (optlen) { memcpy(&convtmp, &optptr[2], sizeof(uint32_t)); addr.s_addr = convtmp; dest += sprintf(dest, "%s ", inet_ntoa(addr)); optlen -= 4; } *(dest - 1) = '\0'; msgopt_list[count].val = strdup(toybuf); msgopt_list[count].len = strlen(toybuf); break; case DHCP_STRLST: //FIXME: do smthing. case DHCP_IPPLST: break; case DHCP_STCRTS: pfx = ""; dest = toybuf; options = &optptr[2]; optlen = optptr[1]; while (optlen >= 1 + 4) { uint32_t nip = 0; int bytes; uint8_t *p_tmp; unsigned mask = *options; if (mask > 32) break; optlen--; p_tmp = (void*) &nip; bytes = (mask + 7) / 8; while (--bytes >= 0) { *p_tmp++ = *options++; optlen--; } if (optlen < 4) break; dest += sprintf(dest, "%s%u.%u.%u.%u", pfx, ((uint8_t*) &nip)[0], ((uint8_t*) &nip)[1], ((uint8_t*) &nip)[2], ((uint8_t*) &nip)[3]); pfx = " "; dest += sprintf(dest, "/%u ", mask); dest += sprintf(dest, "%u.%u.%u.%u", options[0], options[1], options[2], options[3]); options += 4; optlen -= 4; } msgopt_list[count].val = strdup(toybuf); msgopt_list[count].len = strlen(toybuf); break; default: break; } optptr += optptr[1] + 2; } if ((overloaded == 1) || (overloaded == 3)) dhcpc_parseoptions(presult, optptr); if ((overloaded == 2) || (overloaded == 3)) dhcpc_parseoptions(presult, optptr); return type; } // parses recvd messege to check that it was for us. static uint8_t dhcpc_parsemsg(dhcpc_result_t *presult) { if (state->pdhcp.op == DHCP_REPLY && !memcmp(state->pdhcp.chaddr, state->macaddr, 6) && !memcmp(&state->pdhcp.xid, &xid, sizeof(xid))) { memcpy(&presult->ipaddr.s_addr, &state->pdhcp.yiaddr, 4); presult->ipaddr.s_addr = ntohl(presult->ipaddr.s_addr); return get_option_msgtype(state->pdhcp.options); } return 0; } // Sends a IP renew request. static void renew(void) { infomsg(infomode, "Performing a DHCP renew"); switch (state->status) { case STATE_INIT: break; case STATE_BOUND: mode_raw(); case STATE_RENEWING: // FALLTHROUGH case STATE_REBINDING: // FALLTHROUGH state->status = STATE_RENEW_REQUESTED; break; case STATE_RENEW_REQUESTED: run_script(NULL, "deconfig"); case STATE_REQUESTING: // FALLTHROUGH case STATE_RELEASED: // FALLTHROUGH mode_raw(); state->status = STATE_INIT; break; default: break; } } // Sends a IP release request. static void release(void) { int len = sizeof("255.255.255.255\0"); char buffer[len]; struct in_addr temp_addr; mode_app(); // send release packet if (state->status == STATE_BOUND || state->status == STATE_RENEWING || state->status == STATE_REBINDING) { temp_addr.s_addr = htonl(server); strncpy(buffer, inet_ntoa(temp_addr), sizeof(buffer)); buffer[len - 1] = '\0'; temp_addr.s_addr = state->ipaddr.s_addr; infomsg( infomode, "Unicasting a release of %s to %s", inet_ntoa(temp_addr), buffer); dhcpc_sendmsg(DHCPRELEASE); run_script(NULL, "deconfig"); } infomsg(infomode, "Entering released state"); close(state->sockfd); state->sockfd = -1; state->mode = MODE_OFF; state->status = STATE_RELEASED; } static void free_option_stores(void) { int count, size = ARRAY_LEN(options_list); for (count = 0; count < size; count++) if (options_list[count].val) free(options_list[count].val); if(flag_chk(FLAG_x)){ for (count = 0; count < size; count++) if (msgopt_list[count].val) free(msgopt_list[count].val); free(msgopt_list); } } void dhcp_main(void) { struct timeval tv; int retval, bufflen = 0; dhcpc_result_t result; uint8_t packets = 0, retries = 0; uint32_t timeout = 0, waited = 0; fd_set rfds; xid = 0; setlinebuf(stdout); dbg = dummy; if (flag_chk(FLAG_v)) dbg = xprintf; if (flag_chk(FLAG_p)) write_pid(TT.pidfile); retries = flag_get(FLAG_t, TT.retries, 3); if (flag_chk(FLAG_S)) { openlog("UDHCPC :", LOG_PID, LOG_DAEMON); infomode |= LOG_SYSTEM; } infomsg(infomode, "dhcp started"); if (flag_chk(FLAG_O)) { while (TT.req_opt) { raw_opt[raw_optcount] = (uint8_t) strtoopt(TT.req_opt->arg, 1); raw_optcount++; TT.req_opt = TT.req_opt->next; } } if (flag_chk(FLAG_x)) { while (TT.pkt_opt) { (void) strtoopt(TT.pkt_opt->arg, 0); TT.pkt_opt = TT.pkt_opt->next; } } memset(&result, 0, sizeof(dhcpc_result_t)); state = (dhcpc_state_t*) xmalloc(sizeof(dhcpc_state_t)); memset(state, 0, sizeof(dhcpc_state_t)); state->iface = flag_get(FLAG_i, TT.iface, "eth0"); if (get_interface(state->iface, &state->ifindex, NULL, state->macaddr)) perror_exit("Failed to get interface %s", state->iface); run_script(NULL, "deconfig"); setup_signal(); state->status = STATE_INIT; mode_raw(); fcntl(state->sockfd, F_SETFD, FD_CLOEXEC); for (;;) { FD_ZERO(&rfds); if (state->sockfd >= 0) FD_SET(state->sockfd, &rfds); FD_SET(sigfd.rd, &rfds); tv.tv_sec = timeout - waited; tv.tv_usec = 0; retval = 0; int maxfd = (sigfd.rd > state->sockfd)? sigfd.rd : state->sockfd; dbg("select wait ....\n"); uint32_t timestmp = time(NULL); if((retval = select(maxfd + 1, &rfds, NULL, NULL, &tv)) < 0) { if (errno == EINTR) { waited += (unsigned) time(NULL) - timestmp; continue; } perror_exit("Error in select"); } if (!retval) { // Timed out if (get_interface(state->iface, &state->ifindex, NULL, state->macaddr)) error_exit("Interface lost %s\n", state->iface); switch (state->status) { case STATE_INIT: if (packets < retries) { if (!packets) xid = getxid(); run_script(NULL, "deconfig"); infomsg(infomode, "Sending discover..."); dhcpc_sendmsg(DHCPDISCOVER); server = 0; timeout = flag_get(FLAG_T, TT.timeout, 3); waited = 0; packets++; continue; } lease_fail: run_script(NULL,"leasefail"); if (flag_chk(FLAG_n)) { infomsg(infomode, "Lease failed. Exiting"); goto ret_with_sockfd; } if (flag_chk(FLAG_b)) { infomsg(infomode, "Lease failed. Going Daemon mode"); daemon(0, 0); if (flag_chk(FLAG_p)) write_pid(TT.pidfile); toys.optflags &= ~FLAG_b; toys.optflags |= FLAG_f; } timeout = flag_get(FLAG_A, TT.tryagain, 20); waited = 0; packets = 0; continue; case STATE_REQUESTING: if (packets < retries) { memcpy(&state->ipaddr.s_addr,&state->pdhcp.yiaddr, 4); dhcpc_sendmsg(DHCPREQUEST); infomsg(infomode, "Sending select for %d.%d.%d.%d...", (result.ipaddr.s_addr >> 24) & 0xff, (result.ipaddr.s_addr >> 16) & 0xff, (result.ipaddr.s_addr >> 8) & 0xff, (result.ipaddr.s_addr) & 0xff); timeout = flag_get(FLAG_T, TT.timeout, 3); waited = 0; packets++; continue; } mode_raw(); state->status = STATE_INIT; goto lease_fail; case STATE_BOUND: state->status = STATE_RENEWING; dbg("Entering renew state\n"); // FALLTHROUGH case STATE_RENEW_REQUESTED: // FALLTHROUGH case STATE_RENEWING: renew_requested: if (timeout > 60) { dhcpc_sendmsg(DHCPREQUEST); timeout >>= 1; waited = 0; continue; } dbg("Entering rebinding state\n"); state->status = STATE_REBINDING; // FALLTHROUGH case STATE_REBINDING: mode_raw(); if (timeout > 0) { dhcpc_sendmsg(DHCPREQUEST); timeout >>= 1; waited = 0; continue; } infomsg(infomode, "Lease lost, entering INIT state"); run_script(NULL, "deconfig"); state->status = STATE_INIT; timeout = 0; waited = 0; packets = 0; continue; default: break; } timeout = INT_MAX; waited = 0; continue; } if (FD_ISSET(sigfd.rd, &rfds)) { // Some Activity on RDFDs : is signal unsigned char sig; if (read(sigfd.rd, &sig, 1) != 1) { dbg("signal read failed.\n"); continue; } switch (sig) { case SIGUSR1: infomsg(infomode, "Received SIGUSR1"); renew(); packets = 0; waited = 0; if (state->status == STATE_RENEW_REQUESTED) goto renew_requested; if (state->status == STATE_INIT) timeout = 0; continue; case SIGUSR2: infomsg(infomode, "Received SIGUSR2"); release(); timeout = INT_MAX; waited = 0; packets = 0; continue; case SIGTERM: infomsg(infomode, "Received SIGTERM"); if (flag_chk(FLAG_R)) release(); goto ret_with_sockfd; default: break; } } if (FD_ISSET(state->sockfd, &rfds)) { // Some Activity on RDFDs : is socket dbg("main sock read\n"); uint8_t msgType; if (state->mode == MODE_RAW) bufflen = read_raw(); if (state->mode == MODE_APP) bufflen = read_app(); if (bufflen < 0) { if (state->mode == MODE_RAW) mode_raw(); if (state->mode == MODE_APP) mode_app(); continue; } waited += time(NULL) - timestmp; memset(&result, 0, sizeof(dhcpc_result_t)); msgType = dhcpc_parsemsg(&result); if (msgType != DHCPNAK && result.ipaddr.s_addr == 0 ) continue; // no ip for me ignore if (!msgType || !get_option_serverid(state->pdhcp.options, &result)) continue; //no server id ignore if (msgType == DHCPOFFER && server == 0) server = result.serverid.s_addr; // select the server if (result.serverid.s_addr != server) continue; // not from the server we requested ignore dhcpc_parseoptions(&result, state->pdhcp.options); get_option_lease(state->pdhcp.options, &result); switch (state->status) { case STATE_INIT: if (msgType == DHCPOFFER) { state->status = STATE_REQUESTING; mode_raw(); timeout = 0; waited = 0; packets = 0; } continue; case STATE_REQUESTING: // FALLTHROUGH case STATE_RENEWING: // FALLTHROUGH case STATE_RENEW_REQUESTED: // FALLTHROUGH case STATE_REBINDING: if (msgType == DHCPACK) { timeout = result.lease_time / 2; run_script(&result, state->status == STATE_REQUESTING ? "bound" : "renew"); state->status = STATE_BOUND; infomsg(infomode, "Lease of %d.%d.%d.%d obtained, lease time %d from server %d.%d.%d.%d", (result.ipaddr.s_addr >> 24) & 0xff, (result.ipaddr.s_addr >> 16) & 0xff, (result.ipaddr.s_addr >> 8) & 0xff, (result.ipaddr.s_addr) & 0xff, result.lease_time, (result.serverid.s_addr >> 24) & 0xff, (result.serverid.s_addr >> 16) & 0xff, (result.serverid.s_addr >> 8) & 0xff, (result.serverid.s_addr) & 0xff); if (flag_chk(FLAG_q)) { if (flag_chk(FLAG_R)) release(); goto ret_with_sockfd; } toys.optflags &= ~FLAG_n; if (!flag_chk(FLAG_f)) { daemon(0, 0); toys.optflags |= FLAG_f; if (flag_chk(FLAG_p)) write_pid(TT.pidfile); } waited = 0; continue; } else if (msgType == DHCPNAK) { dbg("NACK received.\n"); run_script(&result, "nak"); if (state->status != STATE_REQUESTING) run_script(NULL, "deconfig"); mode_raw(); sleep(3); state->status = STATE_INIT; state->ipaddr.s_addr = 0; server = 0; timeout = 0; packets = 0; waited = 0; } continue; default: break; } } } ret_with_sockfd: if (CFG_TOYBOX_FREE) { free_option_stores(); if (state->sockfd > 0) close(state->sockfd); free(state); } }