/* We need to obey routing options. * If we have a CSR then we only use that. * Otherwise we add static routes and then routers. */ struct rt * get_option_routes(const struct dhcp_message *dhcp) { const uint8_t *p; const uint8_t *e; struct rt *routes = NULL; struct rt *route = NULL; int len; /* If we have CSR's then we MUST use these only */ p = get_option(dhcp, DHO_CSR, &len, NULL); /* Check for crappy MS option */ if (!p) p = get_option(dhcp, DHO_MSCSR, &len, NULL); if (p) { routes = decode_rfc3442_rt(len, p); if (routes) return routes; } /* OK, get our static routes first. */ p = get_option(dhcp, DHO_STATICROUTE, &len, NULL); if (p) { e = p + len; while (p < e) { if (route) { route->next = xmalloc(sizeof(*route)); route = route->next; } else routes = route = xmalloc(sizeof(*routes)); route->next = NULL; memcpy(&route->dest.s_addr, p, 4); p += 4; memcpy(&route->gate.s_addr, p, 4); p += 4; route->net.s_addr = route_netmask(route->dest.s_addr); } } /* Now grab our routers */ p = get_option(dhcp, DHO_ROUTER, &len, NULL); if (p) { e = p + len; while (p < e) { if (route) { route->next = xzalloc(sizeof(*route)); route = route->next; } else routes = route = xzalloc(sizeof(*route)); memcpy(&route->gate.s_addr, p, 4); p += 4; } } return routes; }
int parse_dhcpmessage (dhcp_t *dhcp, const dhcpmessage_t *message) { const unsigned char *p = message->options; const unsigned char *end = p; /* Add size later for gcc-3 issue */ unsigned char option; unsigned char length; unsigned int len = 0; int i; int retval = -1; struct timeval tv; route_t *routers = NULL; route_t *routersp = NULL; route_t *static_routes = NULL; route_t *static_routesp = NULL; route_t *csr = NULL; bool in_overload = false; bool parse_sname = false; bool parse_file = false; end += sizeof (message->options); if (gettimeofday (&tv, NULL) == -1) { logger (LOG_ERR, "gettimeofday: %s", strerror (errno)); return (-1); } dhcp->address.s_addr = message->yiaddr; dhcp->leasedfrom = tv.tv_sec; dhcp->frominfo = false; dhcp->address.s_addr = message->yiaddr; strlcpy (dhcp->servername, (char *) message->servername, sizeof (dhcp->servername)); #define LEN_ERR \ { \ logger (LOG_ERR, "invalid length %d for option %d", length, option); \ p += length; \ continue; \ } parse_start: while (p < end) { option = *p++; if (! option) continue; if (option == DHCP_END) goto eexit; length = *p++; if (option != DHCP_PAD && length == 0) { logger (LOG_ERR, "option %d has zero length", option); retval = -1; goto eexit; } if (p + length >= end) { logger (LOG_ERR, "dhcp option exceeds message length"); retval = -1; goto eexit; } switch (option) { case DHCP_MESSAGETYPE: retval = (int) *p; p += length; continue; default: if (length == 0) { logger (LOG_DEBUG, "option %d has zero length, skipping", option); continue; } } #define LENGTH(_length) \ if (length != _length) \ LEN_ERR; #define MIN_LENGTH(_length) \ if (length < _length) \ LEN_ERR; #define MULT_LENGTH(_mult) \ if (length % _mult != 0) \ LEN_ERR; #define GET_UINT8(_val) \ LENGTH (sizeof (uint8_t)); \ memcpy (&_val, p, sizeof (uint8_t)); #define GET_UINT16(_val) \ LENGTH (sizeof (uint16_t)); \ memcpy (&_val, p, sizeof (uint16_t)); #define GET_UINT32(_val) \ LENGTH (sizeof (uint32_t)); \ memcpy (&_val, p, sizeof (uint32_t)); #define GET_UINT16_H(_val) \ GET_UINT16 (_val); \ _val = ntohs (_val); #define GET_UINT32_H(_val) \ GET_UINT32 (_val); \ _val = ntohl (_val); switch (option) { case DHCP_ADDRESS: GET_UINT32 (dhcp->address.s_addr); break; case DHCP_NETMASK: GET_UINT32 (dhcp->netmask.s_addr); break; case DHCP_BROADCAST: GET_UINT32 (dhcp->broadcast.s_addr); break; case DHCP_SERVERIDENTIFIER: GET_UINT32 (dhcp->serveraddress.s_addr); break; case DHCP_LEASETIME: GET_UINT32_H (dhcp->leasetime); break; case DHCP_RENEWALTIME: GET_UINT32_H (dhcp->renewaltime); break; case DHCP_REBINDTIME: GET_UINT32_H (dhcp->rebindtime); break; case DHCP_MTU: GET_UINT16_H (dhcp->mtu); /* Minimum legal mtu is 68 accoridng to RFC 2132. In practise it's 576 (minimum maximum message size) */ if (dhcp->mtu < MTU_MIN) { logger (LOG_DEBUG, "MTU %d is too low, minimum is %d; ignoring", dhcp->mtu, MTU_MIN); dhcp->mtu = 0; } break; #undef GET_UINT32_H #undef GET_UINT32 #undef GET_UINT16_H #undef GET_UINT16 #undef GET_UINT8 #define GETSTR(_var) \ MIN_LENGTH (sizeof (char)); \ if (_var) free (_var); \ _var = xmalloc (length + 1); \ memcpy (_var, p, length); \ memset (_var + length, 0, 1); case DHCP_HOSTNAME: GETSTR (dhcp->hostname); break; case DHCP_DNSDOMAIN: GETSTR (dhcp->dnsdomain); break; case DHCP_MESSAGE: GETSTR (dhcp->message); break; case DHCP_ROOTPATH: GETSTR (dhcp->rootpath); break; case DHCP_NISDOMAIN: GETSTR (dhcp->nisdomain); break; #undef GETSTR #define GETADDR(_var) \ MULT_LENGTH (4); \ if (! dhcp_add_address (&_var, p, length)) \ { \ retval = -1; \ goto eexit; \ } case DHCP_DNSSERVER: GETADDR (dhcp->dnsservers); break; case DHCP_NTPSERVER: GETADDR (dhcp->ntpservers); break; case DHCP_NISSERVER: GETADDR (dhcp->nisservers); break; #undef GETADDR case DHCP_DNSSEARCH: MIN_LENGTH (1); free (dhcp->dnssearch); if ((len = decode_search (p, length, NULL)) > 0) { dhcp->dnssearch = xmalloc (len); decode_search (p, length, dhcp->dnssearch); } break; case DHCP_CSR: MIN_LENGTH (5); free_route (csr); csr = decode_CSR (p, length); break; case DHCP_SIPSERVER: free (dhcp->sipservers); dhcp->sipservers = decode_sipservers (p, length); break; case DHCP_STATICROUTE: MULT_LENGTH (8); for (i = 0; i < length; i += 8) { if (static_routesp) { static_routesp->next = xmalloc (sizeof (route_t)); static_routesp = static_routesp->next; } else static_routesp = static_routes = xmalloc (sizeof (route_t)); memset (static_routesp, 0, sizeof (route_t)); memcpy (&static_routesp->destination.s_addr, p + i, 4); memcpy (&static_routesp->gateway.s_addr, p + i + 4, 4); static_routesp->netmask.s_addr = route_netmask (static_routesp->destination.s_addr); } break; case DHCP_ROUTERS: MULT_LENGTH (4); for (i = 0; i < length; i += 4) { if (routersp) { routersp->next = xmalloc (sizeof (route_t)); routersp = routersp->next; } else routersp = routers = xmalloc (sizeof (route_t)); memset (routersp, 0, sizeof (route_t)); memcpy (&routersp->gateway.s_addr, p + i, 4); } break; case DHCP_OPTIONSOVERLOADED: LENGTH (1); /* The overloaded option in an overloaded option * should be ignored, overwise we may get an infinite loop */ if (! in_overload) { if (*p & 1) parse_file = true; if (*p & 2) parse_sname = true; } break; #undef LENGTH #undef MIN_LENGTH #undef MULT_LENGTH default: logger (LOG_DEBUG, "no facility to parse DHCP code %u", option); break; } p += length; } eexit: /* We may have options overloaded, so go back and grab them */ if (parse_file) { parse_file = false; p = message->bootfile; end = p + sizeof (message->bootfile); in_overload = true; goto parse_start; } else if (parse_sname) { parse_sname = false; p = message->servername; end = p + sizeof (message->servername); memset (dhcp->servername, 0, sizeof (dhcp->servername)); in_overload = true; goto parse_start; } /* Fill in any missing fields */ if (! dhcp->netmask.s_addr) dhcp->netmask.s_addr = get_netmask (dhcp->address.s_addr); if (! dhcp->broadcast.s_addr) dhcp->broadcast.s_addr = dhcp->address.s_addr | ~dhcp->netmask.s_addr; /* If we have classess static routes then we discard static routes and routers according to RFC 3442 */ if (csr) { dhcp->routes = csr; free_route (routers); free_route (static_routes); } else { /* Ensure that we apply static routes before routers */ if (static_routes) { dhcp->routes = static_routes; static_routesp->next = routers; } else dhcp->routes = routers; } return retval; }