예제 #1
0
파일: protocol.c 프로젝트: mchf/wicked
static inline unsigned int
ni_dhcp_option_begin(ni_buffer_t *bp, int code)
{
	ni_buffer_putc(bp, code);
	ni_buffer_putc(bp, 0);
	return bp->tail;
}
예제 #2
0
파일: protocol.c 프로젝트: mchf/wicked
/*
 * Inline functions for setting/retrieving options from a buffer
 */
static inline void
ni_dhcp_option_put(ni_buffer_t *bp, int code, const void *data, size_t len)
{
	ni_buffer_putc(bp, code);
	ni_buffer_putc(bp, len);
	ni_buffer_put(bp, data, len);
}
예제 #3
0
파일: protocol.c 프로젝트: mchf/wicked
int
ni_dhcp_build_message(const ni_dhcp_device_t *dev,
			unsigned int msg_code,
			const ni_addrconf_lease_t *lease,
			ni_buffer_t *msgbuf)
{
	const ni_dhcp_config_t *options = dev->config;
	struct in_addr src_addr, dst_addr;
	ni_dhcp_message_t *message = NULL;

	if (!options || !lease)
		return -1;

	if (IN_LINKLOCAL(ntohl(lease->dhcp.address.s_addr))) {
		ni_error("cannot request a link local address");
		goto failed;
	}

	src_addr.s_addr = dst_addr.s_addr = 0;
	switch (msg_code) {
	case DHCP_DISCOVER:
		if (lease->dhcp.serveraddress.s_addr != 0)
			return -1;
		break;

	case DHCP_REQUEST:
	case DHCP_RELEASE:
	case DHCP_INFORM:
		if (lease->dhcp.address.s_addr == 0 || lease->dhcp.serveraddress.s_addr == 0)
			return -1;
		src_addr = lease->dhcp.address;
		dst_addr = lease->dhcp.serveraddress;
		break;
	}

	/* Reserve some room for the IP and UDP header */
	ni_buffer_reserve_head(msgbuf, sizeof(struct ip) + sizeof(struct udphdr));

	/* Build the message */
	message = ni_buffer_push_tail(msgbuf, sizeof(*message));

	message->op = DHCP_BOOTREQUEST;
	message->hwtype = dev->system.arp_type;
	message->xid = dev->dhcp.xid;
	message->cookie = htonl(MAGIC_COOKIE);
	message->secs = htons(ni_dhcp_device_uptime(dev, 0xFFFF));

	if (dev->fsm.state == NI_DHCP_STATE_BOUND
	 || dev->fsm.state == NI_DHCP_STATE_RENEWING
	 || dev->fsm.state == NI_DHCP_STATE_REBINDING)
		message->ciaddr = lease->dhcp.address.s_addr;

	switch (dev->system.arp_type) {
	case ARPHRD_ETHER:
	case ARPHRD_IEEE802:
		if (dev->system.hwaddr.len > sizeof(message->chaddr)) {
			ni_error("dhcp cannot handle hwaddress length %u",
					dev->system.hwaddr.len);
			goto failed;
		}
		message->hwlen = dev->system.hwaddr.len;
		memcpy(&message->chaddr, dev->system.hwaddr.data, dev->system.hwaddr.len);
		break;

	case ARPHRD_IEEE1394:
	case ARPHRD_INFINIBAND:
		message->hwlen = 0;
		if (message->ciaddr == 0)
			message->flags = htons(BROADCAST_FLAG);
		break;

	default:
		ni_error("dhcp: unknown hardware type %d", dev->system.arp_type);
	}

	ni_dhcp_option_put8(msgbuf, DHCP_MESSAGETYPE, msg_code);

	if (msg_code == DHCP_REQUEST)
		ni_dhcp_option_put16(msgbuf, DHCP_MAXMESSAGESIZE, dev->system.mtu);

	ni_dhcp_option_put(msgbuf, DHCP_CLIENTID,
			options->raw_client_id.data,
			options->raw_client_id.len);

	if (msg_code != DHCP_DECLINE && msg_code != DHCP_RELEASE) {
		if (options->userclass.len > 0)
			ni_dhcp_option_put(msgbuf, DHCP_USERCLASS,
					options->userclass.data,
					options->userclass.len);

		if (options->classid && options->classid[0])
			ni_dhcp_option_puts(msgbuf, DHCP_CLASSID, options->classid);
	}

	if (msg_code == DHCP_DISCOVER || msg_code == DHCP_REQUEST) {
		if (lease->dhcp.address.s_addr)
			ni_dhcp_option_put_ipv4(msgbuf, DHCP_ADDRESS, lease->dhcp.address);
		if (lease->dhcp.lease_time != 0)
			ni_dhcp_option_put32(msgbuf, DHCP_LEASETIME, lease->dhcp.lease_time);
	}

	if (msg_code == DHCP_REQUEST) {
		if (lease->dhcp.serveraddress.s_addr)
			ni_dhcp_option_put_ipv4(msgbuf, DHCP_SERVERIDENTIFIER, lease->dhcp.serveraddress);
	}

	if (msg_code == DHCP_DISCOVER || msg_code == DHCP_INFORM || msg_code == DHCP_REQUEST) {
		unsigned int params_begin;

		if (options->hostname && options->hostname[0]) {
			if (options->fqdn == FQDN_DISABLE) {
				ni_dhcp_option_puts(msgbuf, DHCP_HOSTNAME, options->hostname);
			} else {
				/* IETF DHC-FQDN option(81)
				 * http://tools.ietf.org/html/rfc4702#section-2.1
				 *
				 * 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
				 */
				ni_buffer_putc(msgbuf, DHCP_FQDN);
				ni_buffer_putc(msgbuf, strlen(options->hostname) + 3);
				ni_buffer_putc(msgbuf, options->fqdn & 0x9);
				ni_buffer_putc(msgbuf, 0);	/* from server for PTR RR */
				ni_buffer_putc(msgbuf, 0);	/* from server for A RR if S=1 */
				ni_buffer_put(msgbuf, options->hostname, strlen(options->hostname));
			}
		}

		params_begin = ni_dhcp_option_begin(msgbuf, DHCP_PARAMETERREQUESTLIST);

		if (msg_code == DHCP_DISCOVER) {
			/* dhcpcd says we should include just a single option
			 * in discovery packets.
			 * I'm not convinced this is right, but let's do it
			 * this way.
			 */
			ni_buffer_putc(msgbuf, DHCP_DNSSERVER);
		} else {
			if (msg_code != DHCP_INFORM) {
				ni_buffer_putc(msgbuf, DHCP_RENEWALTIME);
				ni_buffer_putc(msgbuf, DHCP_REBINDTIME);
			}
			ni_buffer_putc(msgbuf, DHCP_NETMASK);
			ni_buffer_putc(msgbuf, DHCP_BROADCAST);

			if (options->flags & DHCP_DO_CSR)
				ni_buffer_putc(msgbuf, DHCP_CSR);
			if (options->flags & DHCP_DO_MSCSR)
				ni_buffer_putc(msgbuf, DHCP_MSCSR);

			/* RFC 3442 states classless static routes should be
			 * before routers and static routes as classless static
			 * routes override them both */
			ni_buffer_putc(msgbuf, DHCP_STATICROUTE);
			ni_buffer_putc(msgbuf, DHCP_ROUTERS);
			ni_buffer_putc(msgbuf, DHCP_HOSTNAME);
			ni_buffer_putc(msgbuf, DHCP_DNSSEARCH);
			ni_buffer_putc(msgbuf, DHCP_DNSDOMAIN);
			ni_buffer_putc(msgbuf, DHCP_DNSSERVER);

			if (options->flags & DHCP_DO_NIS) {
				ni_buffer_putc(msgbuf, DHCP_NISDOMAIN);
				ni_buffer_putc(msgbuf, DHCP_NISSERVER);
			}
			if (options->flags & DHCP_DO_NTP)
				ni_buffer_putc(msgbuf, DHCP_NTPSERVER);
			ni_buffer_putc(msgbuf, DHCP_MTU);
			ni_buffer_putc(msgbuf, DHCP_ROOTPATH);
			ni_buffer_putc(msgbuf, DHCP_SIPSERVER);
			ni_buffer_putc(msgbuf, DHCP_LPRSERVER);
			ni_buffer_putc(msgbuf, DHCP_LOGSERVER);
			ni_buffer_putc(msgbuf, DHCP_NETBIOSNAMESERVER);
			ni_buffer_putc(msgbuf, DHCP_NETBIOSDDSERVER);
			ni_buffer_putc(msgbuf, DHCP_NETBIOSNODETYPE);
			ni_buffer_putc(msgbuf, DHCP_NETBIOSSCOPE);
		}

		ni_dhcp_option_end(msgbuf, params_begin);
	}
	ni_buffer_putc(msgbuf, DHCP_END);

#ifdef BOOTP_MESSAGE_LENGTH_MIN
	ni_buffer_pad(msgbuf, BOOTP_MESSAGE_LENGTH_MIN, DHCP_PAD);
#endif

	if (ni_capture_build_udp_header(msgbuf, src_addr, DHCP_CLIENT_PORT, dst_addr, DHCP_SERVER_PORT) < 0) {
		ni_error("unable to build packet header");
		goto failed;
	}

	return 0;

failed:
	return -1;
}
예제 #4
0
int
ni_dhcp4_build_message(const ni_dhcp4_device_t *dev, unsigned int msg_code,
			const ni_addrconf_lease_t *lease, ni_buffer_t *msgbuf)
{
	const ni_dhcp4_config_t *options = dev->config;
	struct in_addr src_addr, dst_addr;
	int renew = dev->fsm.state == NI_DHCP4_STATE_RENEWING && msg_code == DHCP4_REQUEST;

	if (!options || !lease) {
		ni_error("%s: %s: %s: missing %s %s", __func__,
				dev->ifname, ni_dhcp4_message_name(msg_code),
				options? "" : "options", lease ? "" : "lease");
		return -1;
	}

	if (IN_LINKLOCAL(ntohl(lease->dhcp4.address.s_addr))) {
		ni_error("%s: cannot request a link local address", dev->ifname);
		goto failed;
	}

	/* Reserve some room for the IP and UDP header */
	if (!renew)
		ni_buffer_reserve_head(msgbuf, sizeof(struct ip) + sizeof(struct udphdr));

	src_addr.s_addr = dst_addr.s_addr = 0;
	switch (msg_code) {
	case DHCP4_INFORM:
		if (__ni_dhcp4_build_msg_inform(dev, lease, msgbuf) < 0)
			goto failed;
		break;

	case DHCP4_DISCOVER:
		if (__ni_dhcp4_build_msg_discover(dev, lease, msgbuf) < 0)
			goto failed;
		break;

	case DHCP4_DECLINE:
		if (__ni_dhcp4_build_msg_decline(dev, lease, msgbuf) < 0)
			goto failed;
		break;

	case DHCP4_RELEASE:
		if (__ni_dhcp4_build_msg_release(dev, lease, msgbuf) < 0)
			goto failed;
		break;

	case DHCP4_REQUEST:
		switch (dev->fsm.state) {
		case NI_DHCP4_STATE_REQUESTING:
			src_addr.s_addr = 0;
			dst_addr.s_addr = 0;

			if (__ni_dhcp4_build_msg_request_offer(dev, lease, msgbuf) < 0)
				goto failed;
			break;

		case NI_DHCP4_STATE_RENEWING:
			src_addr = lease->dhcp4.address;
			dst_addr = lease->dhcp4.server_id;

			if (__ni_dhcp4_build_msg_request_renew(dev, lease, msgbuf) < 0)
				goto failed;
			break;

		case NI_DHCP4_STATE_REBINDING:
			src_addr = lease->dhcp4.address;
			dst_addr.s_addr = 0;

			if (__ni_dhcp4_build_msg_request_rebind(dev, lease, msgbuf) < 0)
				goto failed;
			break;

		case NI_DHCP4_STATE_REBOOT:
			src_addr.s_addr = 0;
			dst_addr.s_addr = 0;

			if (__ni_dhcp4_build_msg_request_reboot(dev, lease, msgbuf) < 0)
				goto failed;
			break;

		default:
			goto failed;
		}
		break;

	default:
		goto failed;
	}

	ni_buffer_putc(msgbuf, DHCP4_END);

#ifdef BOOTP_MESSAGE_LENGTH_MIN
	ni_buffer_pad(msgbuf, BOOTP_MESSAGE_LENGTH_MIN, DHCP4_PAD);
#endif

	if (!renew && ni_capture_build_udp_header(msgbuf, src_addr,
			DHCP4_CLIENT_PORT, dst_addr, DHCP4_SERVER_PORT) < 0) {
		ni_error("%s: unable to build packet header", dev->ifname);
		goto failed;
	}

	return 0;
failed:
	return -1;
}
예제 #5
0
static int
__ni_dhcp4_build_msg_put_option_request(const ni_dhcp4_device_t *dev,
					unsigned int msg_code, ni_buffer_t *msgbuf)
{
	const ni_dhcp4_config_t *options = dev->config;
	unsigned int params_begin;

	switch (msg_code) {
	case DHCP4_DISCOVER:
	case DHCP4_REQUEST:
	case DHCP4_INFORM:
		break;
	default:
		return 1; /* skipped hint */
	}

	params_begin = ni_dhcp4_option_begin(msgbuf, DHCP4_PARAMETERREQUESTLIST);
	if (msg_code != DHCP4_INFORM) {
		ni_buffer_putc(msgbuf, DHCP4_RENEWALTIME);
		ni_buffer_putc(msgbuf, DHCP4_REBINDTIME);
	}
	ni_buffer_putc(msgbuf, DHCP4_NETMASK);
	ni_buffer_putc(msgbuf, DHCP4_BROADCAST);
	ni_buffer_putc(msgbuf, DHCP4_MTU);

	/*
	 * RFC 3442 states classless static routes override both,
	 * the (default) router and (class) static routes options.
	 * Keep them in front also on request... just in case.
	 */
	if (options->doflags & DHCP4_DO_CSR) {
		ni_buffer_putc(msgbuf, DHCP4_CSR);
	}
	if (options->doflags & DHCP4_DO_MSCSR) {
		ni_buffer_putc(msgbuf, DHCP4_MSCSR);
	}
	if (options->doflags & DHCP4_DO_GATEWAY) {
		ni_buffer_putc(msgbuf, DHCP4_STATICROUTE);
		ni_buffer_putc(msgbuf, DHCP4_ROUTERS);
	}
	if (options->doflags & DHCP4_DO_HOSTNAME) {
		if (options->fqdn == FQDN_DISABLE) {
			ni_buffer_putc(msgbuf, DHCP4_HOSTNAME);
		} else {
			ni_buffer_putc(msgbuf, DHCP4_FQDN);
		}
	}
	if (options->doflags & DHCP4_DO_DNS) {
		ni_buffer_putc(msgbuf, DHCP4_DNSSEARCH);
		ni_buffer_putc(msgbuf, DHCP4_DNSDOMAIN);
		ni_buffer_putc(msgbuf, DHCP4_DNSSERVER);
	}
	if (options->doflags & DHCP4_DO_NIS) {
		ni_buffer_putc(msgbuf, DHCP4_NISDOMAIN);
		ni_buffer_putc(msgbuf, DHCP4_NISSERVER);
	}
	if (options->doflags & DHCP4_DO_NTP) {
		ni_buffer_putc(msgbuf, DHCP4_NTPSERVER);
	}
	if (options->doflags & DHCP4_DO_ROOT) {
		ni_buffer_putc(msgbuf, DHCP4_ROOTPATH);
	}
	if (options->doflags & DHCP4_DO_LPR) {
		ni_buffer_putc(msgbuf, DHCP4_LPRSERVER);
	}
	if (options->doflags & DHCP4_DO_LOG) {
		ni_buffer_putc(msgbuf, DHCP4_LOGSERVER);
	}
	if (options->doflags & DHCP4_DO_NDS) {
		ni_buffer_putc(msgbuf, DHCP4_NDS_SERVER);
		ni_buffer_putc(msgbuf, DHCP4_NDS_TREE);
		ni_buffer_putc(msgbuf, DHCP4_NDS_CTX);
	}
	if (options->doflags & DHCP4_DO_SIP) {
		ni_buffer_putc(msgbuf, DHCP4_SIPSERVER);
	}
	if (options->doflags & DHCP4_DO_SMB) {
		ni_buffer_putc(msgbuf, DHCP4_NETBIOSNAMESERVER);
		ni_buffer_putc(msgbuf, DHCP4_NETBIOSDDSERVER);
		ni_buffer_putc(msgbuf, DHCP4_NETBIOSNODETYPE);
		ni_buffer_putc(msgbuf, DHCP4_NETBIOSSCOPE);
	}
	if (options->doflags & DHCP4_DO_POSIX_TZ) {
		ni_buffer_putc(msgbuf, DHCP4_POSIX_TZ_STRING);
		ni_buffer_putc(msgbuf, DHCP4_POSIX_TZ_DBNAME);
	}
	ni_dhcp4_option_end(msgbuf, params_begin);

	ni_debug_verbose(NI_LOG_DEBUG1, NI_TRACE_DHCP,
			"%s: using an option request", dev->ifname);

	return 0;
}
예제 #6
0
static int
__ni_dhcp4_build_msg_put_our_hostname(const ni_dhcp4_device_t *dev,
					ni_buffer_t *msgbuf)
{
	const ni_dhcp4_config_t *options = dev->config;
	size_t len = ni_string_len(options->hostname);

	if (!len)
		return 1; /* skipped hint */

	if (options->fqdn == FQDN_DISABLE) {
		char hname[64] = {'\0'}, *end;

		/*
		 * Truncate the domain part if fqdn to avoid attempts
		 * to update DNS with foo.bar + update-domain.
		 */
		strncat(hname, options->hostname, sizeof(hname)-1);
		if ((end = strchr(hname, '.')))
			*end = '\0';

		len = ni_string_len(hname);
		if (ni_check_domain_name(hname, len, 0)) {
			ni_dhcp4_option_puts(msgbuf, DHCP4_HOSTNAME, hname);
			ni_debug_verbose(NI_LOG_DEBUG1, NI_TRACE_DHCP,
				"%s: using hostname: %s", dev->ifname, hname);
		} else {
			ni_info("%s: not sending suspect hostname: '%s'",
				dev->ifname, ni_print_suspect(hname, len));
			return 1;
		}
	} else
	if (ni_check_domain_name(options->hostname, len, 0)) {
		/* IETF DHC-FQDN option(81)
		 * http://tools.ietf.org/html/rfc4702#section-2.1
		 *
		 * 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
		 */
		ni_buffer_putc(msgbuf, DHCP4_FQDN);
		ni_buffer_putc(msgbuf, len + 3);
		ni_buffer_putc(msgbuf, options->fqdn & 0x9);
		ni_buffer_putc(msgbuf, 0);	/* from server for PTR RR */
		ni_buffer_putc(msgbuf, 0);	/* from server for A RR if S=1 */
		ni_buffer_put(msgbuf, options->hostname, len);
		ni_debug_verbose(NI_LOG_DEBUG1, NI_TRACE_DHCP,
				"%s: using fqdn: %s", dev->ifname,
				options->hostname);
	} else {
		ni_info("%s: not sending suspect fqdn: '%s'",
			dev->ifname, ni_print_suspect(options->hostname, len));
		return 1;
	}

	return 0;
}