Exemplo n.º 1
0
/* Add our classless static routes to the routes variable
 * and return the last route set */
static route_t *decode_CSR(const unsigned char *p, int len)
{
	const unsigned char *q = p;
	int cidr;
	int ocets;
	route_t *first;
	route_t *route;

	/* Minimum is 5 -first is CIDR and a router length of 4 */
	if (len < 5)
		return NULL;

	first = xmalloc (sizeof (route_t));
	route = first;

	while (q - p < len) {
		memset (route, 0, sizeof (route_t));

		cidr = *q++;
		if (cidr > 32) {
			logger (LOG_ERR, "invalid CIDR of %d in classless static route",
					cidr);
			free_route (first);
			return (NULL);
		}
		ocets = (cidr + 7) / 8;

		if (ocets > 0) {
			memcpy (&route->destination.s_addr, q, ocets);
			q += ocets;
		}

		/* Now enter the netmask */
		if (ocets > 0) {
			memset (&route->netmask.s_addr, 255, ocets - 1);
			memset ((unsigned char *) &route->netmask.s_addr + (ocets - 1),
					(256 - (1 << (32 - cidr) % 8)), 1);
		}

		/* Finally, snag the router */
		memcpy (&route->gateway.s_addr, q, 4);
		q += 4;

		/* We have another route */
		if (q - p < len) {
			route->next = xmalloc (sizeof (route_t));
			route = route->next;
		}
	}

	return first;
}
Exemplo n.º 2
0
void free_dhcp (dhcp_t *dhcp)
{
	if (! dhcp)
		return;

	free_route (dhcp->routes);
	free (dhcp->hostname);
	free_address (dhcp->dnsservers);
	free (dhcp->dnsdomain);
	free (dhcp->dnssearch);
	free_address (dhcp->ntpservers);
	free (dhcp->nisdomain);
	free_address (dhcp->nisservers);
	free (dhcp->rootpath);
	if (dhcp->fqdn) {
		free (dhcp->fqdn->name);
		free (dhcp->fqdn);
	}
}
Exemplo n.º 3
0
int handle_rtm_newroute(const struct nlmsghdr *nl){
	const struct rtmsg *rt = NLMSG_DATA(nl);
	struct rtattr *ra;
	void *as,*ad,*ag;
	int rlen,oif;
	route *r,**prev;
	size_t flen;

	oif = -1;
	if((r = create_route()) == NULL){
		return -1;
	}
	switch( (r->family = rt->rtm_family) ){
	case AF_INET:{
		flen = sizeof(uint32_t);
		as = &((struct sockaddr_in *)&r->sss)->sin_addr;
		ad = &((struct sockaddr_in *)&r->ssd)->sin_addr;
		ag = &((struct sockaddr_in *)&r->ssg)->sin_addr;
	break;}case AF_INET6:{
		flen = sizeof(uint32_t) * 4;
		as = &((struct sockaddr_in6 *)&r->sss)->sin6_addr;
		ad = &((struct sockaddr_in6 *)&r->ssd)->sin6_addr;
		ag = &((struct sockaddr_in6 *)&r->ssg)->sin6_addr;
	break;}case AF_BRIDGE:{
		// FIXME wtf is a bridge route
		diagnostic("got a bridge route hrmmm FIXME");
		return -1; // FIXME
	break;}default:{
		flen = 0;
	break;} }
	r->maskbits = rt->rtm_dst_len;
	if(flen == 0 || flen > sizeof(r->sss.__ss_padding)){
		diagnostic("Unknown route family %u",rt->rtm_family);
		return -1;
	}
	rlen = nl->nlmsg_len - NLMSG_LENGTH(sizeof(*rt));
	ra = (struct rtattr *)((char *)(NLMSG_DATA(nl)) + sizeof(*rt));
	memset(&r->ssg,0,sizeof(r->ssg));
	memset(&r->ssd,0,sizeof(r->ssd));
	memset(&r->sss,0,sizeof(r->sss));
	while(RTA_OK(ra,rlen)){
		switch(ra->rta_type){
		case RTA_DST:{
			if(RTA_PAYLOAD(ra) != flen){
				diagnostic("Expected %zu dst bytes, got %zu",
						flen,RTA_PAYLOAD(ra));
				break;
			}
			if(r->ssd.ss_family){
				diagnostic("Got two destinations for route");
				break;
			}
			memcpy(ad,RTA_DATA(ra),flen);
			r->ssd.ss_family = r->family;
		break;}case RTA_PREFSRC: case RTA_SRC:{
			// FIXME do we not want to prefer PREFSRC?
			if(RTA_PAYLOAD(ra) != flen){
				diagnostic("Expected %zu src bytes, got %zu",
						flen,RTA_PAYLOAD(ra));
				break;
			}
			if(r->sss.ss_family){
				diagnostic("Got two sources for route");
				break;
			}
			memcpy(as,RTA_DATA(ra),flen);
			r->sss.ss_family = r->family;
		break;}case RTA_IIF:{
			if(RTA_PAYLOAD(ra) != sizeof(int)){
				diagnostic("Expected %zu iiface bytes, got %zu",
						sizeof(int),RTA_PAYLOAD(ra));
				break;
			}
			// we don't use RTA_OIF: iif = *(int *)RTA_DATA(ra);
		break;}case RTA_OIF:{
			if(RTA_PAYLOAD(ra) != sizeof(int)){
				diagnostic("Expected %zu oiface bytes, got %zu",
						sizeof(int),RTA_PAYLOAD(ra));
				break;
			}
			oif = *(int *)RTA_DATA(ra);
		break;}case RTA_GATEWAY:{
			if(RTA_PAYLOAD(ra) != flen){
				diagnostic("Expected %zu gw bytes, got %zu",
						flen,RTA_PAYLOAD(ra));
				break;
			}
			if(r->ssg.ss_family){
				diagnostic("Got two gateways for route");
				break;
			}
			// We get 0.0.0.0 as the gateway when there's no 'via'
			if(memcmp(ag,RTA_DATA(ra),flen)){
				memcpy(ag,RTA_DATA(ra),flen);
				r->ssg.ss_family = r->family;
			}
		break;}case RTA_PRIORITY:{
		break;}case RTA_METRICS:{
		break;}case RTA_MULTIPATH:{
		// break;}case RTA_PROTOINFO:{ // unused
		break;}case RTA_FLOW:{
		break;}case RTA_CACHEINFO:{
		// break;}case RTA_SESSION:{ // unused
		// break;}case RTA_MP_ALGO:{ // unused
		break;}case RTA_TABLE:{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
		break;}case RTA_MARK:{
#endif
		break;}case RTA_MFC_STATS:{
		break;}case RTA_VIA:{
		break;}case RTA_NEWDST:{
		break;}case RTA_PREF:{
		break;}case RTA_ENCAP_TYPE:{
		break;}case RTA_ENCAP:{
		break;}case RTA_EXPIRES:{
		break;}case RTA_PAD:{
		break;}default:{
			diagnostic("Unknown rtatype %u",ra->rta_type);
		break;}}
		ra = RTA_NEXT(ra,rlen);
	}
	if(rlen){
		diagnostic("%d excess bytes on newlink message",rlen);
	}
	if((r->iface = iface_by_idx(oif)) == NULL){
		diagnostic("Unknown output interface %d on %s",oif,r->iface->name);
		goto err;
	}
	{
		char str[INET6_ADDRSTRLEN],via[INET6_ADDRSTRLEN];
		inet_ntop(rt->rtm_family,ad,str,sizeof(str));
		inet_ntop(rt->rtm_family,ag,via,sizeof(via));
		diagnostic("[%s] new route to %s/%u %ls%ls%s",
			r->iface->name,str,r->maskbits,
			rt->rtm_type == RTN_LOCAL ? L"(local)" :
			rt->rtm_type == RTN_BROADCAST ? L"(broadcast)" :
			rt->rtm_type == RTN_UNREACHABLE ? L"(unreachable)" :
			rt->rtm_type == RTN_ANYCAST ? L"(anycast)" :
			rt->rtm_type == RTN_UNICAST ? L"(unicast)" :
			rt->rtm_type == RTN_MULTICAST ? L"(multicast)" :
			rt->rtm_type == RTN_BLACKHOLE ? L"(blackhole)" :
			rt->rtm_type == RTN_MULTICAST ? L"(multicast)" : L"",
			r->ssg.ss_family ? L" via " : L"",
			r->ssg.ss_family ? via : "");
	}
	// We're not interest in blackholes, unreachables, prohibits, NATs yet
	if(rt->rtm_type != RTN_UNICAST && rt->rtm_type != RTN_LOCAL
			&& rt->rtm_type != RTN_BROADCAST
			&& rt->rtm_type != RTN_ANYCAST
			&& rt->rtm_type != RTN_MULTICAST){
		free_route(r);
		return 0;
	}
	assert(r->iface);
	if(!r->sss.ss_family){
		struct routepath rp;

		if(get_router(r->sss.ss_family,ad,&rp) == 0){
			if(r->sss.ss_family == AF_INET){
				memcpy(as,rp.src,4);
			}else if(r->sss.ss_family == AF_INET6){
				memcpy(as,rp.src,16);
			}else{
				assert(0);
			}
		}else{ // FIXME vicious hackery!
			if(r->family == AF_INET6){
				memcpy(as,r->iface->ip6defsrc,flen);
				r->sss.ss_family = AF_INET6;
			}
		}
	}
	if(r->family == AF_INET){
		lock_interface(r->iface);
		if(add_route4(r->iface,ad,r->ssg.ss_family ? ag : NULL,
					r->sss.ss_family ? as : NULL,
					r->maskbits)){
			unlock_interface(r->iface);
			diagnostic("Couldn't add route to %s",r->iface->name);
			goto err;
		}
		if(r->ssg.ss_family){
			send_arp_req(r->iface,r->iface->bcast,ag,as);
		}
		unlock_interface(r->iface);
		pthread_mutex_lock(&route_lock);
			prev = &ip_table4;
			// Order most-specific (largest maskbits) to least-specific (0 maskbits)
			while(*prev){
				if(r->maskbits > (*prev)->maskbits){
					break;
				}
				prev = &(*prev)->next;
			}
			r->next = *prev;
			*prev = r;
			if(r->sss.ss_family){
				while( *(prev = &(*prev)->next) ){
					assert((*prev)->maskbits < r->maskbits);
					if(!((*prev)->sss.ss_family)){
						memcpy(&(*prev)->sss,&r->sss,sizeof(r->sss));
					}
				}
			}
		pthread_mutex_unlock(&route_lock);
	}else if(r->family == AF_INET6){
		lock_interface(r->iface);
		if(add_route6(r->iface,ad,r->ssg.ss_family ? ag : NULL,r->sss.ss_family ? as : NULL,r->maskbits)){
			unlock_interface(r->iface);
			diagnostic("Couldn't add route to %s",r->iface->name);
			goto err;
		}
		unlock_interface(r->iface);
		pthread_mutex_lock(&route_lock);
			prev = &ip_table6;
			// Order most-specific (largest maskbits) to least-specific (0 maskbits)
			while(*prev){
				if(r->maskbits > (*prev)->maskbits){
					break;
				}
				prev = &(*prev)->next;
			}
			r->next = *prev;
			*prev = r;
			// FIXME set less-specific sources
		pthread_mutex_unlock(&route_lock);
	}
	return 0;

err:
	free_route(r);
	return -1;
}
Exemplo n.º 4
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;
	route_t *routers = NULL;
	route_t *routersp = NULL;
	route_t *static_routes = NULL;
	route_t *static_routesp = NULL;
	route_t *csr = NULL;

	end += sizeof (message->options);

	dhcp->address.s_addr = message->yiaddr;
	strlcpy (dhcp->servername, message->servername,
			 sizeof (dhcp->servername));

#define LEN_ERR \
	{ \
		logger (LOG_ERR, "invalid length %d for option %d", length, option); \
		p += length; \
		continue; \
	}

	while (p < end) {
		option = *p++;
		if (! option)
			continue;

		length = *p++;

		if (p + length >= end) {
			logger (LOG_ERR, "dhcp option exceeds message length");
			retval = -1;
			goto eexit;
		}

		switch (option) {
			case DHCP_END:
				goto eexit;

			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 = decodeCSR (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 =
						getnetmask (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;

#undef LENGTH
#undef MIN_LENGTH
#undef MULT_LENGTH

			default:
				logger (LOG_DEBUG, "no facility to parse DHCP code %u", option);
				break;
		}

		p += length;
	}

eexit:
	/* Fill in any missing fields */
	if (! dhcp->netmask.s_addr)
		dhcp->netmask.s_addr = getnetmask (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;
}
Exemplo n.º 5
0
int parse_dhcpmessage (dhcp_t *dhcp, dhcpmessage_t *message)
{
  unsigned char *p = message->options;
  unsigned char option;
  unsigned char length;
  unsigned char *end = message->options + sizeof (message->options);
  unsigned int len = 0;
  int i;
  int retval = -1;
  route_t *first_route = xmalloc (sizeof (route_t));
  route_t *route = first_route;
  route_t *last_route = NULL;
  route_t *csr = NULL;
  char classid[CLASS_ID_MAX_LEN];
  char clientid[CLIENT_ID_MAX_LEN];

  memset (first_route, 0, sizeof (route_t));

  /* The message back never has the class or client id's so we save them */
  strcpy (classid, dhcp->classid);
  strcpy (clientid, dhcp->clientid);

  free_dhcp (dhcp);
  memset (dhcp, 0, sizeof (dhcp_t));

  dhcp->address.s_addr = message->yiaddr;
  strcpy (dhcp->servername, message->servername);

  while (p < end)
    {
      option = *p++;
      if (!option)
	continue;

      length = *p++;

      if (p + length >= end)
	{
	  retval = -1;
	  goto eexit;
	}

      switch (option)
	{
	case DHCP_END:
	  goto eexit;

	case DHCP_MESSAGETYPE:
	  retval = (int) *p;
	  break;
#define GET_UINT32(_val) \
	  memcpy (&_val, p, sizeof (uint32_t));
#define GET_UINT32_H(_val) \
	  GET_UINT32 (_val); \
	  _val = ntohl (_val);
	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_UINT32_H (dhcp->mtu);
	  /* Minimum legal mtu is 68 */
	  if (dhcp->mtu > 0 && dhcp->mtu < 68)
	    dhcp->mtu = 68;
	  break;
#undef GET_UINT32_H
#undef GET_UINT32

#define GETSTR(_var) \
	  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) \
	  if (_var) free (_var); \
	  _var = xmalloc (sizeof (address_t)); \
	  dhcp_add_address (_var, p, length);
	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:
	  if (dhcp->dnssearch)
	    free (dhcp->dnssearch);
	  if ((len = decode_search (p, length, NULL)))
	    {
	      dhcp->dnssearch = xmalloc (len);
	      decode_search (p, length, dhcp->dnssearch);
	    }
	  break;

	case DHCP_CSR:
	  csr = decodeCSR (p, length);
	  break;

	case DHCP_STATICROUTE:
	  for (i = 0; i < length; i += 8)
	    {
	      memcpy (&route->destination.s_addr, p + i, 4);
	      memcpy (&route->gateway.s_addr, p + i + 4, 4);
	      route->netmask.s_addr = getnetmask (route->destination.s_addr); 
	      last_route = route;
	      route->next = xmalloc (sizeof (route_t));
	      route = route->next;
	      memset (route, 0, sizeof (route_t));
	    }
	  break;

	case DHCP_ROUTERS:
	  for (i = 0; i < length; i += 4)
	    {
	      memcpy (&route->gateway.s_addr, p + i, 4);
	      last_route = route;
	      route->next = xmalloc (sizeof (route_t));
	      route = route->next;
	      memset (route, 0, sizeof (route_t));
	    }
	  break;

	default:
	  logger (LOG_DEBUG, "no facility to parse DHCP code %u", option);
	  break;
	}

      p += length;
    }

eexit:
  /* Fill in any missing fields */
  if (! dhcp->netmask.s_addr)
    dhcp->netmask.s_addr = getnetmask (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 (first_route); 
    }
  else
    {
      dhcp->routes = first_route;
      if (last_route)
	{
	  free (last_route->next);
	  last_route->next = NULL;
	}
      else
	{
	  free_route (dhcp->routes);
	  dhcp->routes = NULL;
	}
    }

  /* The message back never has the class or client id's so we restore them */
  strcpy (dhcp->classid, classid);
  strcpy (dhcp->clientid, clientid);

  return retval;
}
Exemplo n.º 6
0
int configure (const options_t *options, interface_t *iface,
	       const dhcp_t *dhcp)
{
  route_t *route = NULL;
  route_t *new_route = NULL;
  route_t *old_route = NULL;
  struct hostent *he = NULL;
  char newhostname[HOSTNAME_MAX_LEN] = {0};
  char curhostname[HOSTNAME_MAX_LEN] = {0};
  char *dname = NULL;
  int dnamel = 0;

  if (! options || ! iface || ! dhcp)
    return -1;

  /* Remove old routes
     Always do this as the interface may have >1 address not added by us
     so the routes we added may still exist */
  if (iface->previous_routes)
    {
      for (route = iface->previous_routes; route; route = route->next)
	if (route->destination.s_addr || options->dogateway)
	  {
	    int have = 0;
	    if (dhcp->address.s_addr != 0)
	      for (new_route = dhcp->routes; new_route; new_route = new_route->next)
		if (new_route->destination.s_addr == route->destination.s_addr
		    && new_route->netmask.s_addr == route->netmask.s_addr
		    && new_route->gateway.s_addr == route->gateway.s_addr)
		  {
		    have = 1;
		    break;
		  }
	    if (! have)
	      del_route (iface->name, route->destination, route->netmask,
			 route->gateway, options->metric);
	  }
    }

  /* If we don't have an address, then return */
  if (dhcp->address.s_addr == 0)
    {
      if (iface->previous_routes)
	{
	  free_route (iface->previous_routes);
	  iface->previous_routes = NULL;
	}

      /* Only reset things if we had set them before */
      if (iface->previous_address.s_addr != 0)
	{
	  del_address (iface->name, iface->previous_address,
		       iface->previous_netmask);
	  memset (&iface->previous_address, 0, sizeof (struct in_addr));
	  memset (&iface->previous_netmask, 0, sizeof (struct in_addr));

	  restore_resolv (iface->name);

	  /* we currently don't have a resolvconf style programs for ntp/nis */
	  exec_script (options->script, iface->infofile, "down");
	}
      return 0;
    }

  if (add_address (iface->name, dhcp->address, dhcp->netmask,
		   dhcp->broadcast) < 0 && errno != EEXIST)
    return -1;

  /* Now delete the old address if different */
  if (iface->previous_address.s_addr != dhcp->address.s_addr
      && iface->previous_address.s_addr != 0)
    del_address (iface->name, iface->previous_address, iface->previous_netmask);

#ifdef __linux__
  /* On linux, we need to change the subnet route to have our metric. */
  if (iface->previous_address.s_addr != dhcp->address.s_addr
      && options->metric > 0 && dhcp->netmask.s_addr != INADDR_BROADCAST)
    {
      struct in_addr td;
      struct in_addr tg;
      memset (&td, 0, sizeof (td));
      memset (&tg, 0, sizeof (tg));
      td.s_addr = dhcp->address.s_addr & dhcp->netmask.s_addr;
      add_route (iface->name, td, dhcp->netmask, tg, options->metric);
      del_route (iface->name, td, dhcp->netmask, tg, 0);
    }
#endif

  /* Remember added routes */
  if (dhcp->routes)
    {
      route_t *new_routes = NULL;
      int remember;

      for (route = dhcp->routes; route; route = route->next)
	{
	  /* Don't set default routes if not asked to */
	  if (route->destination.s_addr == 0 && route->netmask.s_addr == 0
	      && ! options->dogateway)
	    continue;

	  remember = add_route (iface->name, route->destination,
				route->netmask,  route->gateway,
				options->metric);
	  /* If we failed to add the route, we may have already added it
	     ourselves. If so, remember it again. */
	  if (remember < 0)
	    for (old_route = iface->previous_routes; old_route;
		 old_route = old_route->next)
	      if (old_route->destination.s_addr == route->destination.s_addr
		  && old_route->netmask.s_addr == route->netmask.s_addr
		  && old_route->gateway.s_addr == route->gateway.s_addr)
		{
		  remember = 1;
		  break;
		}

	  if (remember >= 0)
	    {
	      if (! new_routes)
		{
		  new_routes = xmalloc (sizeof (route_t));
		  memset (new_routes, 0, sizeof (route_t));
		  new_route = new_routes;
		}
	      else
		{
		  new_route->next = xmalloc (sizeof (route_t));
		  new_route = new_route->next;
		}
	      memcpy (new_route, route, sizeof (route_t));
	      new_route -> next = NULL;
	    }
	}

      if (iface->previous_routes)
	free_route (iface->previous_routes);

      iface->previous_routes = new_routes;
    }

  if (options->dodns && dhcp->dnsservers)
    make_resolv(iface->name, dhcp);
  else
    logger (LOG_DEBUG, "no dns information to write");

  if (options->dontp && dhcp->ntpservers)
    make_ntp(iface->name, dhcp);

  if (options->donis && (dhcp->nisservers || dhcp->nisdomain))
    make_nis(iface->name, dhcp);

  /* Now we have made a resolv.conf we can obtain a hostname if we need one */
  if (options->dohostname && ! dhcp->hostname)
    {
      he = gethostbyaddr (inet_ntoa (dhcp->address),
			  sizeof (struct in_addr), AF_INET);
      if (he)
	{
	  dname = he->h_name;
	  while (*dname > 32)
	    dname++;
	  dnamel = dname - he->h_name;
	  memcpy (newhostname, he->h_name, dnamel);
	  newhostname[dnamel] = 0;
	}
    }

  gethostname (curhostname, sizeof (curhostname));

  if (options->dohostname
      || strlen (curhostname) == 0
      || strcmp (curhostname, "(none)") == 0
      || strcmp (curhostname, "localhost") == 0)
    {
      if (dhcp->hostname)
	strcpy (newhostname, dhcp->hostname); 

      if (*newhostname)
	{
	  logger (LOG_INFO, "setting hostname to `%s'", newhostname);
	  sethostname (newhostname, strlen (newhostname));
	}
    }

  write_info (iface, dhcp, options);

  if (iface->previous_address.s_addr != dhcp->address.s_addr ||
      iface->previous_netmask.s_addr != dhcp->netmask.s_addr)
    {
      memcpy (&iface->previous_address,
	      &dhcp->address, sizeof (struct in_addr));
      memcpy (&iface->previous_netmask,
	      &dhcp->netmask, sizeof (struct in_addr));
      exec_script (options->script, iface->infofile, "new");
    }
  else
    exec_script (options->script, iface->infofile, "up");

  return 0;
}