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);
  }
}