Exemple #1
0
size_t send_message (const interface_t *iface, const dhcp_t *dhcp,
					 unsigned long xid, char type,
					 const options_t *options)
{
	dhcpmessage_t message;
	struct udp_dhcp_packet packet;
	unsigned char *m = (unsigned char *) &message;
	unsigned char *p = (unsigned char *) &message.options;
	unsigned char *n_params = NULL;
	unsigned long l;
	struct in_addr from;
	struct in_addr to;
	long up = uptime() - iface->start_uptime;
	uint32_t ul;
	uint16_t sz;
	unsigned int message_length;

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

	memset (&from, 0, sizeof (from));
	memset (&to, 0, sizeof (to));

	if (type == DHCP_RELEASE)
		to.s_addr = dhcp->serveraddress.s_addr;

	memset (&message, 0, sizeof (dhcpmessage_t));

	if (type == DHCP_INFORM ||
		type == DHCP_RELEASE ||
		type == DHCP_REQUEST)
	{
		message.ciaddr = iface->previous_address.s_addr;
		from.s_addr = iface->previous_address.s_addr;
	}

	message.op = DHCP_BOOTREQUEST;
	message.hwtype = iface->family;
	switch (iface->family) {
		case ARPHRD_ETHER:
		case ARPHRD_IEEE802:
			message.hwlen = ETHER_ADDR_LEN;
			memcpy (&message.chaddr, &iface->hwaddr, ETHER_ADDR_LEN);
			break;
		case ARPHRD_IEEE1394:
		case ARPHRD_INFINIBAND:
			if (message.ciaddr == 0)
				message.flags = htons (BROADCAST_FLAG);
			message.hwlen = 0;
			break;
		default:
			logger (LOG_ERR, "dhcp: unknown hardware type %d", iface->family);
	}

	if (up < 0 || up > UINT16_MAX)
		message.secs = htons ((short) UINT16_MAX);
	else
		message.secs = htons (up);
	message.xid = xid;
	message.cookie = htonl (MAGIC_COOKIE);

	*p++ = DHCP_MESSAGETYPE; 
	*p++ = 1;
	*p++ = type;

	if (type == DHCP_REQUEST) {
		*p++ = DHCP_MAXMESSAGESIZE;
		*p++ = 2;
		sz = get_mtu (iface->name);
		if (sz < MTU_MIN) {
			if (set_mtu (iface->name, MTU_MIN) == 0)
				sz = MTU_MIN;
		}
		sz = htons (sz);
		memcpy (p, &sz, 2);
		p += 2;
	}

#define PUTADDR(_type, _val) \
	{ \
		*p++ = _type; \
		*p++ = 4; \
		memcpy (p, &_val.s_addr, 4); \
		p += 4; \
	}
	if (dhcp->address.s_addr != 0 && iface->previous_address.s_addr == 0
		&& type != DHCP_RELEASE)
		PUTADDR (DHCP_ADDRESS, dhcp->address);

	if (dhcp->serveraddress.s_addr != 0 && dhcp->address.s_addr !=0 &&
		(iface->previous_address.s_addr == 0 || type == DHCP_RELEASE))
		PUTADDR (DHCP_SERVERIDENTIFIER, dhcp->serveraddress);
#undef PUTADDR

	if (type == DHCP_REQUEST || type == DHCP_DISCOVER) {
		if (options->leasetime != 0) {
			*p++ = DHCP_LEASETIME;
			*p++ = 4;
			ul = htonl (options->leasetime);
			memcpy (p, &ul, 4);
			p += 4;
		}
	}

	if (type == DHCP_DISCOVER || type == DHCP_INFORM || type == DHCP_REQUEST) {
		*p++ = DHCP_PARAMETERREQUESTLIST;
		n_params = p;
		*p++ = 0;

		/* Only request DNSSERVER in discover to keep the packets small.
		   RFC2131 Section 3.5 states that the REQUEST must include the list
		   from the DISCOVER message, so I think we can safely do this. */

		if (type == DHCP_DISCOVER)
			*p++ = DHCP_DNSSERVER;
		else {
			*p++ = DHCP_RENEWALTIME;
			*p++ = DHCP_REBINDTIME;
			*p++ = DHCP_NETMASK;
			*p++ = DHCP_BROADCAST;
			*p++ = DHCP_CSR;
			/* RFC 3442 states classless static routes should be before routers
			 * and static routes as classless static routes override them both */
			*p++ = DHCP_STATICROUTE;
			*p++ = DHCP_ROUTERS;
			*p++ = DHCP_HOSTNAME;
			*p++ = DHCP_DNSSEARCH;
			*p++ = DHCP_DNSDOMAIN;
			*p++ = DHCP_DNSSERVER;
			*p++ = DHCP_NISDOMAIN;
			*p++ = DHCP_NISSERVER;
			*p++ = DHCP_NTPSERVER;
			*p++ = DHCP_MTU;
			*p++ = DHCP_ROOTPATH;
			/* These parameters were requested by dhcpcd-2.0 and earlier
			   but we never did anything with them */
			/*    *p++ = DHCP_DEFAULTIPTTL;
			 *p++ = DHCP_MASKDISCOVERY;
			 *p++ = DHCP_ROUTERDISCOVERY; */
		}

		*n_params = p - n_params - 1;

		if (*options->hostname) {
			if (options->fqdn == FQDN_DISABLE) {
				*p++ = DHCP_HOSTNAME;
				*p++ = l = strlen (options->hostname);
				memcpy (p, options->hostname, l);
				p += l;
			} else {
				/* Draft IETF DHC-FQDN option (81) */
				*p++ = DHCP_FQDN;
				*p++ = (l = strlen (options->hostname)) + 3;
				/* Flags: 0000NEOS
				 * S: 1 => Client requests Server to update A RR in DNS as well as PTR
				 * O: 1 => Server indicates to client that DNS has been updated
				 * E: 1 => Name data is DNS format
				 * N: 1 => Client requests Server to not update DNS
				 */
				*p++ = options->fqdn & 0x9;
				*p++ = 0; /* rcode1, response from DNS server for PTR RR */
				*p++ = 0; /* rcode2, response from DNS server for A RR if S=1 */
				memcpy (p, options->hostname, l);
				p += l;
			}
		}
	}

	if (type != DHCP_DECLINE && type != DHCP_RELEASE) {
		if (options->userclass_len > 0) {
			*p++ = DHCP_USERCLASS;
			*p++ = options->userclass_len;
			memcpy (p, &options->userclass, options->userclass_len);
			p += options->userclass_len;
		}

		*p++ = DHCP_CLASSID;
		*p++ = l = strlen (options->classid);
		memcpy (p, options->classid, l);
		p += l;
	}

	*p++ = DHCP_CLIENTID;
	if (options->clientid[0]) {
		l = strlen (options->clientid);
		*p++ = l + 1;
		*p++ = 0; /* string */
		memcpy (p, options->clientid, l);
		p += l;
	} else {
		*p++ = iface->hwlen + 1;
		*p++ = iface->family;
		memcpy (p, iface->hwaddr, iface->hwlen);
		p += iface->hwlen;
	}

#ifdef BOOTP_MESSAGE_LENTH_MIN
	/* Some crappy DHCP servers think they have to obey the BOOTP minimum
	 * messag length. They are wrong, but we should still cater for them */
	while (p - m < BOOTP_MESSAGE_LENTH_MIN - 1)
		*p++ = DHCP_PAD;
#endif

	*p++ = DHCP_END;
	message_length = p - m;

	memset (&packet, 0, sizeof (struct udp_dhcp_packet));
	make_dhcp_packet (&packet, (unsigned char *) &message, message_length,
					  from, to);

	logger (LOG_DEBUG, "sending %s with xid 0x%lx", dhcp_message[(int) type], xid);
	return send_packet (iface, ETHERTYPE_IP, (unsigned char *) &packet,
						message_length + sizeof (struct ip) +
						sizeof (struct udphdr));
}
ssize_t
make_message(struct dhcp_message **message,
	     const struct interface *iface, const struct dhcp_lease *lease,
	     uint32_t xid, uint8_t type, const struct options *options)
{
	struct dhcp_message *dhcp;
	uint8_t *m, *lp, *p;
	uint8_t *n_params = NULL;
	time_t up = uptime() - iface->start_uptime;
	uint32_t ul;
	uint16_t sz;
	const struct dhcp_opt *opt;
	size_t len;
	const char *hp;

	dhcp = xzalloc(sizeof (*dhcp));
	m = (uint8_t *)dhcp;
	p = dhcp->options;

	if ((type == DHCP_INFORM ||
	     type == DHCP_RELEASE ||
	     type == DHCP_REQUEST) &&
	    !IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
	{
		dhcp->ciaddr = iface->addr.s_addr;
		/* Just incase we haven't actually configured the address yet */
		if (type == DHCP_INFORM && iface->addr.s_addr == 0)
			dhcp->ciaddr = lease->addr.s_addr;
		/* Zero the address if we're currently on a different subnet */
		if (type == DHCP_REQUEST &&
		    iface->net.s_addr != lease->net.s_addr)
			dhcp->ciaddr = 0;
	}

	dhcp->op = DHCP_BOOTREQUEST;
	dhcp->hwtype = iface->family;
	switch (iface->family) {
	case ARPHRD_ETHER:
	case ARPHRD_IEEE802:
		dhcp->hwlen = ETHER_ADDR_LEN;
		memcpy(&dhcp->chaddr, &iface->hwaddr, ETHER_ADDR_LEN);
		break;
	case ARPHRD_IEEE1394:
	case ARPHRD_INFINIBAND:
		dhcp->hwlen = 0;
		if (dhcp->ciaddr == 0 &&
		    type != DHCP_DECLINE && type != DHCP_RELEASE)
			dhcp->flags = htons(BROADCAST_FLAG);
		break;
	}

	if (type != DHCP_DECLINE && type != DHCP_RELEASE) {
		if (up < 0 || up > (time_t)UINT16_MAX)
			dhcp->secs = htons((uint16_t)UINT16_MAX);
		else
			dhcp->secs = htons(up);
	}
	dhcp->xid = xid;
	dhcp->cookie = htonl(MAGIC_COOKIE);

	*p++ = DHO_MESSAGETYPE; 
	*p++ = 1;
	*p++ = type;

	if (iface->clientid) {
		*p++ = DHO_CLIENTID;
		memcpy(p, iface->clientid, iface->clientid[0] + 1);
		p += iface->clientid[0] + 1;
	}

	if (lease->addr.s_addr && !IN_LINKLOCAL(htonl(lease->addr.s_addr))) {
		if (type == DHCP_DECLINE ||
		    type == DHCP_DISCOVER ||
		    (type == DHCP_REQUEST &&
		     lease->addr.s_addr != iface->addr.s_addr))
		{
			PUTADDR(DHO_IPADDRESS, lease->addr);
			if (lease->server.s_addr)
				PUTADDR(DHO_SERVERID, lease->server);
		}
	}

	if (type == DHCP_DECLINE) {
		*p++ = DHO_MESSAGE;
		len = strlen(DAD);
		*p++ = len;
		memcpy(p, DAD, len);
		p += len;
	}

	if (type == DHCP_RELEASE) {
		if (lease->server.s_addr)
			PUTADDR(DHO_SERVERID, lease->server);
	}

	if (type == DHCP_DISCOVER ||
	    type == DHCP_INFORM ||
	    type == DHCP_REQUEST)
	{
		*p++ = DHO_MAXMESSAGESIZE;
		*p++ = 2;
		sz = get_mtu(iface->name);
		if (sz < MTU_MIN) {
			if (set_mtu(iface->name, MTU_MIN) == 0)
				sz = MTU_MIN;
		}
		sz = htons(sz);
		memcpy(p, &sz, 2);
		p += 2;

		if (options->userclass[0]) {
			*p++ = DHO_USERCLASS;
			memcpy(p, options->userclass, options->userclass[0] + 1);
			p += options->userclass[0] + 1;
		}

		if (options->vendorclassid[0]) {
			*p++ = DHO_VENDORCLASSID;
			memcpy(p, options->vendorclassid,
			       options->vendorclassid[0] + 1);
			p += options->vendorclassid[0] + 1;
		}

		if (type != DHCP_INFORM) {
			if (options->leasetime != 0) {
				*p++ = DHO_LEASETIME;
				*p++ = 4;
				ul = htonl(options->leasetime);
				memcpy(p, &ul, 4);
				p += 4;
			}
		}

		/* Regardless of RFC2132, we should always send a hostname
		 * upto the first dot (the short hostname) as otherwise
		 * confuses some DHCP servers when updating DNS.
		 * The FQDN option should be used if a FQDN is required. */
		if (options->hostname[0]) {
			*p++ = DHO_HOSTNAME;
			hp = strchr(options->hostname, '.');
			if (hp)
				len = hp - options->hostname;
			else
				len = strlen(options->hostname);
			*p++ = len;
			memcpy(p, options->hostname, len);
			p += len;
		}
		if (options->fqdn != FQDN_DISABLE) {
			/* IETF DHC-FQDN option (81), RFC4702 */
			*p++ = DHO_FQDN;
			lp = p;
			*p++ = 3;
			/*
			 * Flags: 0000NEOS
			 * S: 1 => Client requests Server to update
			 *         a RR in DNS as well as PTR
			 * O: 1 => Server indicates to client that
			 *         DNS has been updated
			 * E: 1 => Name data is DNS format
			 * N: 1 => Client requests Server to not
			 *         update DNS
			 */
			*p++ = (options->fqdn & 0x09) | 0x04;
			*p++ = 0; /* from server for PTR RR */
			*p++ = 0; /* from server for A RR if S=1 */
			ul = encode_rfc1035(options->hostname, p);
			*lp += ul;
			p += ul;
		}

		/* vendor is already encoded correctly, so just add it */
		if (options->vendor[0]) {
			*p++ = DHO_VENDOR;
			memcpy(p, options->vendor, options->vendor[0] + 1);
			p += options->vendor[0] + 1;
		}

		*p++ = DHO_PARAMETERREQUESTLIST;
		n_params = p;
		*p++ = 0;
		for (opt = dhcp_opts; opt->option; opt++) {
			if (!(opt->type & REQUEST || 
			      has_option_mask(options->requestmask, opt->option)))
				continue;
			switch (opt->option) {
			case DHO_RENEWALTIME:	/* FALLTHROUGH */
			case DHO_REBINDTIME:
				if (type == DHCP_INFORM)
					continue;
				break;
			}
			*p++ = opt->option;
		}
		*n_params = p - n_params - 1;
	}
	*p++ = DHO_END;

#ifdef BOOTP_MESSAGE_LENTH_MIN
	/* Some crappy DHCP servers think they have to obey the BOOTP minimum
	 * message length.
	 * They are wrong, but we should still cater for them. */
	while (p - m < BOOTP_MESSAGE_LENTH_MIN)
		*p++ = DHO_PAD;
#endif

	*message = dhcp;
	return p - m;
}
Exemple #3
0
size_t send_message (interface_t *iface, dhcp_t *dhcp,
		     unsigned long xid, char type, options_t *options)
{
  dhcpmessage_t message;
  unsigned char *p = (unsigned char *) &message.options;
  unsigned char *n_params = NULL;
  unsigned long l;
  struct in_addr from;
  struct in_addr to;

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

  if (type == DHCP_RELEASE || type == DHCP_INFORM ||
      (type == DHCP_REQUEST &&
       iface->previous_address.s_addr == dhcp->address.s_addr))
    from.s_addr = dhcp->address.s_addr;
  else
    from.s_addr = 0;

  if (type == DHCP_RELEASE)
    to.s_addr = dhcp->serveraddress.s_addr;
  else
    to.s_addr = 0;

  memset (&message, 0, sizeof (dhcpmessage_t));

  message.op = DHCP_BOOTREQUEST;
  message.hwtype = ARPHRD_ETHER;
  message.hwlen = ETHER_ADDR_LEN;
  message.secs = htons (10);
  message.xid = xid;
  memcpy (&message.hwaddr, &iface->ethernet_address, ETHER_ADDR_LEN);
  message.cookie = htonl (MAGIC_COOKIE);

  /* This logic should be improved so that we don't need to set the 0
     flag as it's done in the above memset statement */
  if (type == DHCP_REQUEST
      && dhcp->address.s_addr == iface->previous_address.s_addr
      && iface->previous_address.s_addr != 0)
    message.flags = 0;
  else
    message.flags = htons (BROADCAST_FLAG);

  if (iface->previous_address.s_addr != 0
      && (type == DHCP_INFORM || type == DHCP_RELEASE
	  || (type == DHCP_REQUEST
	      && iface->previous_address.s_addr == dhcp->address.s_addr)))
    message.ciaddr = iface->previous_address.s_addr;

  *p++ = DHCP_MESSAGETYPE; 
  *p++ = 1;
  *p++ = type;

  if (type == DHCP_REQUEST)
    {
      *p++ = DHCP_MAXMESSAGESIZE;
      *p++ = 2;
      uint16_t sz = htons (sizeof (struct udp_dhcp_packet));
      memcpy (p, &sz, 2);
      p += 2;
    }

#define PUTADDR(_type, _val) \
    { \
      *p++ = _type; \
      *p++ = 4; \
      memcpy (p, &_val.s_addr, 4); \
      p += 4; \
    }
  if (dhcp->address.s_addr != 0 && iface->previous_address.s_addr == 0
      && type != DHCP_RELEASE)
    PUTADDR (DHCP_ADDRESS, dhcp->address);

  if (dhcp->serveraddress.s_addr != 0 && dhcp->address.s_addr !=0 &&
      (iface->previous_address.s_addr == 0 || type == DHCP_RELEASE))
    PUTADDR (DHCP_SERVERIDENTIFIER, dhcp->serveraddress);
#undef PUTADDR

  if (type == DHCP_REQUEST || type == DHCP_DISCOVER)
    {
      if (options->leasetime != 0)
	{
	  *p++ = DHCP_LEASETIME;
	  *p++ = 4;
	  uint32_t ul = htonl (options->leasetime);
	  memcpy (p, &ul, 4);
	  p += 4;
	}
    }

  if (type == DHCP_DISCOVER || type == DHCP_INFORM || type == DHCP_REQUEST)
    {
      *p++ = DHCP_PARAMETERREQUESTLIST;
      n_params = p;
      *p++ = 0;

      /* If we don't request one item, then we get defaults back which
	 we don't want */
      if (type == DHCP_DISCOVER)
	{
	  *p++ = DHCP_DNSSERVER;
	}
      else
	{
	  *p++ = DHCP_RENEWALTIME;
	  *p++ = DHCP_REBINDTIME;
	  *p++ = DHCP_NETMASK;
	  *p++ = DHCP_BROADCAST;
	  *p++ = DHCP_CSR;
	  /* RFC 3442 states classless static routes should be before routers
	   * and static routes as classless static routes override them both */
	  *p++ = DHCP_ROUTERS;
	  *p++ = DHCP_STATICROUTE;
	  *p++ = DHCP_HOSTNAME;
	  *p++ = DHCP_DNSSEARCH;
	  *p++ = DHCP_DNSDOMAIN;
	  *p++ = DHCP_DNSSERVER;
	  *p++ = DHCP_NISDOMAIN;
	  *p++ = DHCP_NISSERVER;
	  *p++ = DHCP_NTPSERVER;
	  /* These parameters were requested by dhcpcd-2.0 and earlier
	     but we never did anything with them */
	  /*    *p++ = DHCP_DEFAULTIPTTL;
	   *p++ = DHCP_MASKDISCOVERY;
	   *p++ = DHCP_ROUTERDISCOVERY; */
	}

      *n_params = p - n_params - 1;
    }

  if (type == DHCP_REQUEST)
    {
      if (options->hostname) 
	{
	  if (options->fqdn == FQDN_DISABLE)
	    {
	      *p++ = DHCP_HOSTNAME;
	      *p++ = l = strlen (options->hostname);
	      memcpy (p, options->hostname, l);
	      p += l;
	    }
	  else
	    {
	      /* Draft IETF DHC-FQDN option (81) */
	      *p++ = DHCP_FQDN;
	      *p++ = (l = strlen (options->hostname)) + 3;
	      /* Flags: 0000NEOS
	       * S: 1 => Client requests Server to update A RR in DNS as well as PTR
	       * O: 1 => Server indicates to client that DNS has been updated
	       * E: 1 => Name data is DNS format
	       * N: 1 => Client requests Server to not update DNS
	       */
	      *p++ = options->fqdn & 0x9;
	      *p++ = 0; /* rcode1, response from DNS server for PTR RR */
	      *p++ = 0; /* rcode2, response from DNS server for A RR if S=1 */
	      memcpy (p, options->hostname, l);
	      p += l;
	    }
	}
    }

  if (type != DHCP_DECLINE && type != DHCP_RELEASE)
    {
      if (options->userclass)
	{
	  *p++ = DHCP_USERCLASS;
	  *p++ = l = strlen (options->userclass);
	  memcpy (p, options->userclass, l);
	  p += l;
	}
      
      *p++ = DHCP_CLASSID;
      *p++ = l = strlen (options->classid);
      memcpy (p, options->classid, l);
      p += l;
    }

  *p++ = DHCP_CLIENTID;
  if (options->clientid[0])
    {
      l = strlen (options->clientid);
      *p++ = l + 1;
      *p++ = 0; /* string */
      memcpy (p, options, l);
      p += l;
    }
  else
    {
      *p++ = ETHER_ADDR_LEN + 1;
      *p++ = ARPHRD_ETHER;
      memcpy (p, &iface->ethernet_address, ETHER_ADDR_LEN);
      p += ETHER_ADDR_LEN;
    }

  *p = DHCP_END;

  struct udp_dhcp_packet packet;
  memset (&packet, 0, sizeof (struct udp_dhcp_packet));
  make_dhcp_packet (&packet, (unsigned char *) &message, from, to);

  logger (LOG_DEBUG, "Sending %s with xid %d", dhcp_message[(int) type], xid);
  return send_packet (iface, ETHERTYPE_IP, (unsigned char *) &packet,
		      sizeof (struct udp_dhcp_packet));
}
Exemple #4
0
size_t send_message (const interface_t *iface, const dhcp_t *dhcp,
					 unsigned long xid, char type,
					 const options_t *options)
{
	struct udp_dhcp_packet *packet;
	dhcpmessage_t *message;
	unsigned char *m;
	unsigned char *p;
	unsigned char *n_params = NULL;
	unsigned long l;
	struct in_addr from;
	struct in_addr to;
	time_t up = uptime() - iface->start_uptime;
	uint32_t ul;
	uint16_t sz;
	unsigned int message_length;
	size_t retval;

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

	memset (&from, 0, sizeof (from));
	memset (&to, 0, sizeof (to));

	if (type == DHCP_RELEASE)
		to.s_addr = dhcp->serveraddress.s_addr;

	message = xmalloc (sizeof (dhcpmessage_t));
	memset (message, 0, sizeof (dhcpmessage_t));
	m = (unsigned char *) message;
	p = (unsigned char *) &message->options;

	if ((type == DHCP_INFORM ||
		type == DHCP_RELEASE ||
		type == DHCP_REQUEST) &&
		! IN_LINKLOCAL (iface->previous_address.s_addr))
	{
		message->ciaddr = iface->previous_address.s_addr;
		from.s_addr = iface->previous_address.s_addr;

		/* Just incase we haven't actually configured the address yet */
		if (type == DHCP_INFORM && iface->previous_address.s_addr == 0)
			message->ciaddr = dhcp->address.s_addr;
		
		/* Zero the address if we're currently on a different subnet */
		if (type == DHCP_REQUEST &&
			iface->previous_netmask.s_addr != dhcp->netmask.s_addr)
			message->ciaddr = from.s_addr = 0;
	}

	message->op = DHCP_BOOTREQUEST;
	message->hwtype = iface->family;
	switch (iface->family) {
		case ARPHRD_ETHER:
		case ARPHRD_IEEE802:
			message->hwlen = ETHER_ADDR_LEN;
			memcpy (&message->chaddr, &iface->hwaddr, ETHER_ADDR_LEN);
			break;
		case ARPHRD_IEEE1394:
		case ARPHRD_INFINIBAND:
			if (message->ciaddr == 0)
				message->flags = (int16_t) htons (BROADCAST_FLAG);
			message->hwlen = 0;
			break;
		default:
			logger (LOG_ERR, "dhcp: unknown hardware type %d", iface->family);
	}

	if (up < 0 || up > UINT16_MAX)
		message->secs = htons ((short) UINT16_MAX);
	else
		message->secs = htons (up);
	message->xid = xid;
	message->cookie = htonl (MAGIC_COOKIE);

	*p++ = DHCP_MESSAGETYPE; 
	*p++ = 1;
	*p++ = type;

	if (type == DHCP_REQUEST) {
		*p++ = DHCP_MAXMESSAGESIZE;
		*p++ = 2;
		sz = get_mtu (iface->name);
		if (sz < MTU_MIN) {
			if (set_mtu (iface->name, MTU_MIN) == 0)
				sz = MTU_MIN;
		}
		sz = htons (sz);
		memcpy (p, &sz, 2);
		p += 2;
	}

	if (type != DHCP_INFORM) {
#define PUTADDR(_type, _val) \
		{ \
			*p++ = _type; \
			*p++ = 4; \
			memcpy (p, &_val.s_addr, 4); \
			p += 4; \
		}
		if (IN_LINKLOCAL (dhcp->address.s_addr))
			logger (LOG_ERR, "cannot request a link local address");
		else {
			if (dhcp->address.s_addr != iface->previous_address.s_addr &&
				type != DHCP_RELEASE)
				PUTADDR (DHCP_ADDRESS, dhcp->address);

			if (dhcp->serveraddress.s_addr != 0 && dhcp->address.s_addr !=0 &&
				(iface->previous_address.s_addr == 0 || type == DHCP_RELEASE))
				PUTADDR (DHCP_SERVERIDENTIFIER, dhcp->serveraddress);
		}
#undef PUTADDR
	}

	if (type == DHCP_REQUEST || type == DHCP_DISCOVER) {
		if (options->leasetime != 0) {
			*p++ = DHCP_LEASETIME;
			*p++ = 4;
			ul = htonl (options->leasetime);
			memcpy (p, &ul, 4);
			p += 4;
		}
	}

	if (type == DHCP_DISCOVER || type == DHCP_INFORM || type == DHCP_REQUEST) {
		*p++ = DHCP_PARAMETERREQUESTLIST;
		n_params = p;
		*p++ = 0;

		/* Only request DNSSERVER in discover to keep the packets small.
		   RFC2131 Section 3.5 states that the REQUEST must include the list
		   from the DISCOVER message, so I think we can safely do this. */

		if (type == DHCP_DISCOVER && ! options->test)
			*p++ = DHCP_DNSSERVER;
		else {
			if (type != DHCP_INFORM) {
				*p++ = DHCP_RENEWALTIME;
				*p++ = DHCP_REBINDTIME;
			}
			*p++ = DHCP_NETMASK;
			*p++ = DHCP_BROADCAST;
			*p++ = DHCP_CSR;
			/* RFC 3442 states classless static routes should be before routers
			 * and static routes as classless static routes override them both */
			*p++ = DHCP_STATICROUTE;
			*p++ = DHCP_ROUTERS;
			*p++ = DHCP_HOSTNAME;
			*p++ = DHCP_DNSSEARCH;
			*p++ = DHCP_DNSDOMAIN;
			*p++ = DHCP_DNSSERVER;
			*p++ = DHCP_NISDOMAIN;
			*p++ = DHCP_NISSERVER;
			*p++ = DHCP_NTPSERVER;
			*p++ = DHCP_MTU;
			*p++ = DHCP_ROOTPATH;
			*p++ = DHCP_SIPSERVER;
		}

		*n_params = p - n_params - 1;

		if (options->hostname[0]) {
			if (options->fqdn == FQDN_DISABLE) {
				*p++ = DHCP_HOSTNAME;
				*p++ = l = strlen (options->hostname);
				memcpy (p, options->hostname, l);
				p += l;
			} else {
				/* Draft IETF DHC-FQDN option (81) */
				*p++ = DHCP_FQDN;
				*p++ = (l = strlen (options->hostname)) + 3;
				/* Flags: 0000NEOS
				 * S: 1 => Client requests Server to update A RR in DNS as well as PTR
				 * O: 1 => Server indicates to client that DNS has been updated
				 * E: 1 => Name data is DNS format
				 * N: 1 => Client requests Server to not update DNS
				 */
				*p++ = options->fqdn & 0x9;
				*p++ = 0; /* rcode1, response from DNS server for PTR RR */
				*p++ = 0; /* rcode2, response from DNS server for A RR if S=1 */
				memcpy (p, options->hostname, l);
				p += l;
			}
		}
	}

	if (type != DHCP_DECLINE && type != DHCP_RELEASE) {
		if (options->userclass_len > 0) {
			*p++ = DHCP_USERCLASS;
			*p++ = options->userclass_len;
			memcpy (p, &options->userclass, options->userclass_len);
			p += options->userclass_len;
		}

		if (options->classid_len > 0) {
			*p++ = DHCP_CLASSID;
			*p++ = options->classid_len;
			memcpy (p, options->classid, options->classid_len);
			p += options->classid_len;
		}
	}

	*p++ = DHCP_CLIENTID;
	if (options->clientid_len > 0) {
		*p++ = options->clientid_len + 1;
		*p++ = 0; /* string */
		memcpy (p, options->clientid, options->clientid_len);
		p += options->clientid_len;
#ifdef ENABLE_DUID
	} else if (iface->duid && options->clientid_len != -1) {
		*p++ = iface->duid_length + 5;
		*p++ = 255; /* RFC 4361 */

		/* IAID is 4 bytes, so if the interface name is 4 bytes then use it */
		if (strlen (iface->name) == 4) {
			memcpy (p, iface->name, 4);
		} else {
			/* Name isn't 4 bytes, so use the index */
			ul = htonl (if_nametoindex (iface->name));
			memcpy (p, &ul, 4);
		}
		p += 4;

		memcpy (p, iface->duid, iface->duid_length);
		p += iface->duid_length;
#endif
	} else {
		*p++ = iface->hwlen + 1;
		*p++ = iface->family;
		memcpy (p, iface->hwaddr, iface->hwlen);
		p += iface->hwlen;
	}

	*p++ = DHCP_END;

#ifdef BOOTP_MESSAGE_LENTH_MIN
	/* Some crappy DHCP servers think they have to obey the BOOTP minimum
	 * messag length. They are wrong, but we should still cater for them */
	while (p - m < BOOTP_MESSAGE_LENTH_MIN)
		*p++ = DHCP_PAD;
#endif

	message_length = p - m;

	packet = xmalloc (sizeof (struct udp_dhcp_packet));
	memset (packet, 0, sizeof (struct udp_dhcp_packet));
	make_dhcp_packet (packet, (unsigned char *) message, message_length,
					  from, to);
	free (message);

	logger (LOG_DEBUG, "sending %s with xid 0x%lx",
			dhcp_message[(int) type], xid);
	retval = send_packet (iface, ETHERTYPE_IP, (unsigned char *) packet,
						  message_length + sizeof (struct ip) +
						  sizeof (struct udphdr));
	free (packet);
	return (retval);
}