/* 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;
}
Example #2
0
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;
}