Example #1
0
int
ni_dhcp4_fsm_recover_lease(ni_dhcp4_device_t *dev, const ni_dhcp4_request_t *req)
{
	ni_addrconf_lease_t *lease;
	time_t now = time(NULL), then;

	/* Don't recover anything if we already have a lease attached. */
	if (dev->lease != NULL)
		return -1;

	lease = ni_addrconf_lease_file_read(dev->ifname, NI_ADDRCONF_DHCP, AF_INET);
	if (!lease)
		return -1;

	if (lease->state != NI_ADDRCONF_STATE_GRANTED)
		goto discard;

	ni_debug_dhcp("trying to recover dhcp4 lease, now inspecting");
	then = lease->time_acquired;
	if (now < then) {
		ni_debug_dhcp("%s: found time-warped lease (hi, grand-grand-pa)", __FUNCTION__);
		goto discard;
	}

	if (now >= then + lease->dhcp4.lease_time) {
		ni_debug_dhcp("%s: found expired lease", __FUNCTION__);
		goto discard;
	}

	if ((req->hostname && !ni_string_eq(req->hostname, dev->lease->hostname))
	 || (req->clientid && !ni_string_eq(req->clientid, dev->lease->dhcp4.client_id))) {
		ni_debug_dhcp("%s: lease doesn't match request", __FUNCTION__);
		goto discard;
	}

	ni_dhcp4_device_set_lease(dev, lease);

	if (now >= then + lease->dhcp4.rebind_time) {
		ni_dhcp4_fsm_rebind(dev);
	} else
	if (now >= then + lease->dhcp4.renewal_time) {
		ni_dhcp4_fsm_renewal(dev);
	} else {
		ni_dhcp4_fsm_set_deadline(dev, then + lease->dhcp4.renewal_time);
		dev->fsm.state = NI_DHCP4_STATE_BOUND;
	}

	ni_debug_dhcp("%s: recovered old lease; now in state=%s",
			dev->ifname, ni_dhcp4_fsm_state_name(dev->fsm.state));
	dev->notify = 1;
	return 0;

discard:
	ni_addrconf_lease_free(lease);
	return -1;
}
Example #2
0
int
ni_dhcp4_fsm_release(ni_dhcp4_device_t *dev)
{
	if (!dev->config) {
		ni_debug_dhcp("%s: not configured, dropping lease", dev->ifname);
		ni_dhcp4_fsm_commit_lease(dev, NULL);
		return 0;
	}

	ni_debug_dhcp("%s: releasing lease", dev->ifname);
	ni_dhcp4_device_send_message(dev, DHCP4_RELEASE, dev->lease);

	ni_dhcp4_fsm_commit_lease(dev, NULL);
	return 0;
}
Example #3
0
/*
 * NAKs in different states need to be treated differently.
 */
static int
ni_dhcp4_process_nak(ni_dhcp4_device_t *dev)
{
	switch (dev->fsm.state) {
	case NI_DHCP4_STATE_BOUND:
		/* RFC says discard NAKs received in state BOUND */
		return 0;

	default:
		/* FIXME: how do we handle a NAK response to an INFORM? */
		ni_dhcp4_device_drop_lease(dev);
		break;
	}

	/* Move back to state INIT */
	ni_dhcp4_fsm_restart(dev);

	if (dev->dhcp4.nak_backoff == 0)
		dev->dhcp4.nak_backoff = 1;

	/* If we constantly get NAKs then we should slowly back off */
	ni_debug_dhcp("Received NAK, backing off for %u seconds", dev->dhcp4.nak_backoff);
	ni_dhcp4_fsm_set_timeout(dev, dev->dhcp4.nak_backoff);

	dev->dhcp4.nak_backoff *= 2;
	if (dev->dhcp4.nak_backoff > NAK_BACKOFF_MAX)
		dev->dhcp4.nak_backoff = NAK_BACKOFF_MAX;
	return 0;
}
Example #4
0
void
ni_dhcp4_device_free(ni_dhcp4_device_t *dev)
{
	ni_dhcp4_device_t **pos;

	ni_assert(dev->users == 0);
	ni_debug_dhcp("%s: Deleting dhcp4 device with index %u",
			dev->ifname, dev->link.ifindex);

	ni_dhcp4_device_drop_buffer(dev);
	ni_dhcp4_device_drop_lease(dev);
	ni_dhcp4_device_drop_best_offer(dev);
	ni_dhcp4_device_close(dev);
	ni_string_free(&dev->system.ifname);
	ni_string_free(&dev->ifname);

	/* Drop existing config and request */
	ni_dhcp4_device_set_config(dev, NULL);
	ni_dhcp4_device_set_request(dev, NULL);

	for (pos = &ni_dhcp4_active; *pos; pos = &(*pos)->next) {
		if (*pos == dev) {
			*pos = dev->next;
			break;
		}
	}
	free(dev);
}
Example #5
0
void
ni_dhcp4_fsm_set_timeout_msec(ni_dhcp4_device_t *dev, unsigned int msec)
{
	if (msec != 0) {
		ni_debug_dhcp("%s: setting timeout to %u msec", dev->ifname, msec);
		if (dev->fsm.timer)
			ni_timer_rearm(dev->fsm.timer, msec);
		else
			dev->fsm.timer = ni_timer_register(msec, __ni_dhcp4_fsm_timeout, dev);
	}
}
Example #6
0
void
ni_dhcp4_fsm_process_arp_packet(ni_arp_socket_t *arph, const ni_arp_packet_t *pkt, void *user_data)
{
	ni_dhcp4_device_t *dev = user_data;
	ni_netconfig_t *nc = ni_global_state_handle(0);
	const ni_netdev_t *ifp;
	ni_bool_t false_alarm = FALSE;
	ni_bool_t found_addr = FALSE;

	if (!pkt || pkt->op != ARPOP_REPLY || !dev || !dev->lease)
		return;

	/* Is it about the address we're validating at all? */
	if (pkt->sip.s_addr != dev->lease->dhcp4.address.s_addr)
		return;

	/* Ignore any ARP replies that seem to come from our own
	 * MAC address. Some helpful switches seem to generate
	 * these. */
	if (ni_link_address_equal(&dev->system.hwaddr, &pkt->sha))
		return;

	/* As well as ARP replies that seem to come from our own
	 * host: dup if same address, not a dup if there are two
	 * interfaces connected to the same broadcast domain.
	 */
	for (ifp = ni_netconfig_devlist(nc); ifp; ifp = ifp->next) {
		if (ifp->link.ifindex == dev->link.ifindex)
			continue;

		if (!ni_netdev_link_is_up(ifp))
			continue;

		if (!ni_link_address_equal(&ifp->link.hwaddr, &pkt->sha))
			continue;

		/* OK, we have an interface matching the hwaddr,
		 * which will answer arp requests when it is on
		 * the same broadcast domain and causes a false
		 * alarm, except it really has the IP assigned.
		 */
		false_alarm = TRUE;
		if (__ni_dhcp4_address_on_device(ifp, pkt->sip))
			found_addr = TRUE;
	}
	if (false_alarm && !found_addr)
		return;

	ni_debug_dhcp("%s: address %s already in use by %s",
			dev->ifname, inet_ntoa(pkt->sip),
			ni_link_address_print(&pkt->sha));
	ni_dhcp4_device_arp_close(dev);
	ni_dhcp4_fsm_decline(dev);
}
Example #7
0
int
ni_dhcp4_fsm_arp_validate(ni_dhcp4_device_t *dev)
{
	struct in_addr null = { 0 };
	struct in_addr claim;

	if (!dev || !dev->lease)
		return -1;

	claim = dev->lease->dhcp4.address;
	if (dev->arp.handle == NULL) {
		dev->arp.handle = ni_arp_socket_open(&dev->system,
				ni_dhcp4_fsm_process_arp_packet, dev);
		if (!dev->arp.handle->user_data) {
			ni_error("%s: unable to create ARP handle", dev->ifname);
			return -1;
		}
	}

	if (dev->arp.nprobes) {
		ni_debug_dhcp("%s: arp validate: probing for %s",
				dev->ifname, inet_ntoa(claim));
		ni_arp_send_request(dev->arp.handle, null, claim);
		dev->arp.nprobes--;
	} else if (dev->arp.nclaims) {
		ni_debug_dhcp("%s: arp validate: claiming %s",
				dev->ifname, inet_ntoa(claim));
		ni_arp_send_grat_request(dev->arp.handle, claim);
		dev->arp.nclaims--;
	} else {
		/* Wow, we're done! */
		ni_info("%s: Successfully validated DHCPv4 address %s",
			dev->ifname, inet_ntoa(claim));
		ni_dhcp4_fsm_commit_lease(dev, dev->lease);
		ni_dhcp4_device_arp_close(dev);
		return 0;
	}

	ni_dhcp4_fsm_set_timeout_msec(dev, NI_DHCP4_ARP_TIMEOUT);
	return 0;
}
Example #8
0
void
ni_dhcp4_fsm_fail_lease(ni_dhcp4_device_t *dev)
{
	ni_debug_dhcp("%s: failing lease", dev->ifname);

	ni_dhcp4_fsm_restart(dev);
	ni_capture_free(dev->capture);
	dev->capture = NULL;

	ni_dhcp4_device_set_lease(dev, NULL);
	dev->notify = 1;
	dev->failed = 1;
}
Example #9
0
/*
 * Remove a lease file
 */
static void
__ni_addrconf_lease_file_remove(const char *dir, const char *ifname,
				int type, int family)
{
	char *filename = NULL;

	if (!__ni_addrconf_lease_file_path(&filename, dir, ifname, type, family))
		return;

	if (ni_file_exists(filename) && unlink(filename) == 0)
		ni_debug_dhcp("removed %s", filename);
	ni_string_free(&filename);
}
Example #10
0
int
ni_dhcp4_fsm_validate_lease(ni_dhcp4_device_t *dev, ni_addrconf_lease_t *lease)
{
	/*
	 * When the address is already set on the link, we
	 * don't need to validate it and just commit it.
	 */
	if (__ni_dhcp4_address_on_link(dev, lease->dhcp4.address)) {
		ni_debug_dhcp("%s: address %s is on link, omit validation",
				dev->ifname, inet_ntoa(lease->dhcp4.address));
		ni_dhcp4_fsm_commit_lease(dev, lease);
		return 0;
	}

	ni_info("%s: Validating DHCPv4 address %s",
		dev->ifname, inet_ntoa(lease->dhcp4.address));

	/* For ARP validations, we will send 3 ARP queries with a timeout
	 * of 200ms each.
	 * The "claims" part is really for IPv4LL
	 */
	dev->arp.nprobes = 3;
	dev->arp.nclaims = 0;

	/* dhcp4cd source code says:
	 * IEEE1394 cannot set ARP target address according to RFC2734
	 */
	if (dev->system.hwaddr.type == ARPHRD_IEEE1394)
		dev->arp.nclaims = 0;

	if (ni_dhcp4_fsm_arp_validate(dev) < 0) {
		ni_debug_dhcp("%s: unable to validate lease", dev->ifname);
		return -1;
	}

	dev->fsm.state = NI_DHCP4_STATE_VALIDATING;
	return 0;
}
Example #11
0
static void
ni_dhcp4_tester_protocol_event(enum ni_dhcp4_event ev, const ni_dhcp4_device_t *dev,
		ni_addrconf_lease_t *lease)
{
	ni_debug_dhcp("%s(ev=%u, dev=%s[%u], config-uuid=%s)", __func__, ev,
			dev->ifname, dev->link.ifindex,
			dev->config ? ni_uuid_print(&dev->config->uuid) : "<none>");

	switch (ev) {
	case NI_DHCP4_EVENT_ACQUIRED:
		if (lease && lease->state == NI_ADDRCONF_STATE_GRANTED) {
			FILE *fp = stdout;

			if (dhcp4_tester_opts.output != NULL) {
				fp = fopen(dhcp4_tester_opts.output, "w");
				if (!fp) {
					ni_error("Cannot open %s for output",
						dhcp4_tester_opts.output);
					dhcp4_tester_status = NI_WICKED_RC_ERROR;
					return;
				}
			}
			if (dhcp4_tester_opts.outfmt == NI_DHCP4_TESTER_OUT_LEASE_XML) {
				xml_node_t *xml = NULL;

				if (ni_addrconf_lease_to_xml(lease, &xml, dev->ifname) != 0) {
					if (dhcp4_tester_opts.output)
						fclose(fp);
					dhcp4_tester_status = NI_WICKED_RC_ERROR;
					return;
				}
				xml_node_print(xml, fp);
				xml_node_free(xml);
			} else {
				ni_leaseinfo_dump(fp, lease, dev->ifname, NULL);
			}
			fflush(fp);
			if (dhcp4_tester_opts.output)
				fclose(fp);
			dhcp4_tester_status = NI_WICKED_RC_SUCCESS;
		}
		break;
	default:
		break;
	}
}
Example #12
0
int
ni_dhcp4_fsm_reboot(ni_dhcp4_device_t *dev)
{
	time_t deadline;
	int rv;

	/* RFC 2131, 3.2 (see also 3.1) */
	ni_debug_dhcp("trying to confirm lease for %s", dev->ifname);

	dev->fsm.state = NI_DHCP4_STATE_REBOOT;
	rv = ni_dhcp4_device_send_message(dev, DHCP4_REQUEST, dev->lease);

	deadline = time(NULL) + 10;
	if (deadline > dev->lease->time_acquired + dev->lease->dhcp4.rebind_time)
		deadline = dev->lease->time_acquired + dev->lease->dhcp4.rebind_time;

	ni_dhcp4_fsm_set_deadline(dev, deadline);
	return rv;
}
Example #13
0
static int
ni_dhcp_option_get_printable(ni_buffer_t *bp, char **var, const char *what)
{
	unsigned int len;
	char *tmp = NULL;

	if (ni_dhcp_option_get_string(bp, &tmp, &len) < 0)
		return -1;

	if (!ni_check_printable(tmp, len)) {
		ni_debug_dhcp("Discarded non-printable %s: %s", what,
			ni_print_suspect(tmp, len));
		free(tmp);
		return -1;
	}

	if (*var)
		free(*var);
	*var = tmp;
	return 0;
}
Example #14
0
int
__ni_dhcp4_fsm_discover(ni_dhcp4_device_t *dev, int scan_offers)
{
	ni_addrconf_lease_t *lease;
	int rv;

	ni_info("%s: Initiating DHCPv4 discovery (ifindex %d)", dev->ifname, dev->link.ifindex);

	/* If we already have a lease, try asking for the same.
	 * If not, create a dummy lease with NULL fields.
	 * Note: if DISCOVER for the old lease times out,
	 * we should fall back to asking for anything.
	 */
	if ((lease = dev->lease) == NULL)
		lease = ni_addrconf_lease_new(NI_ADDRCONF_DHCP, AF_INET);
	lease->uuid = dev->config->uuid;

	dev->fsm.state = NI_DHCP4_STATE_SELECTING;
	rv = ni_dhcp4_device_send_message(dev, DHCP4_DISCOVER, lease);

	dev->dhcp4.accept_any_offer = 1;
	ni_debug_dhcp("valid lease: %d; have prefs: %d",
			ni_addrconf_lease_is_valid(dev->lease),
			ni_dhcp4_config_have_server_preference());
	if (ni_addrconf_lease_is_valid(dev->lease)
	 || (scan_offers && ni_dhcp4_config_have_server_preference())) {
		ni_dhcp4_fsm_set_timeout(dev, dev->config->initial_discovery_timeout);
		dev->dhcp4.accept_any_offer = 0;
	} else {
		ni_dhcp4_fsm_set_timeout(dev, dev->config->request_timeout);
	}

	ni_dhcp4_device_drop_best_offer(dev);

	if (lease != dev->lease)
		ni_addrconf_lease_free(lease);
	return rv;
}
Example #15
0
static int
ni_dhcp4_process_ack(ni_dhcp4_device_t *dev, ni_addrconf_lease_t *lease)
{
	if (lease->dhcp4.lease_time == 0) {
		lease->dhcp4.lease_time = DHCP4_DEFAULT_LEASETIME;
		ni_debug_dhcp("server supplied no lease time, assuming %u seconds",
				lease->dhcp4.lease_time);
	}

	if (lease->dhcp4.rebind_time >= lease->dhcp4.lease_time) {
		ni_debug_dhcp("%s: dhcp4.rebind_time greater than dhcp4.lease_time, using default", dev->ifname);
		lease->dhcp4.rebind_time = lease->dhcp4.lease_time * 7 / 8;
	} else if (lease->dhcp4.rebind_time == 0) {
		ni_debug_dhcp("%s: no dhcp4.rebind_time supplied, using default", dev->ifname);
		lease->dhcp4.rebind_time = lease->dhcp4.lease_time * 7 / 8;
	}

	if (lease->dhcp4.renewal_time >= lease->dhcp4.rebind_time) {
		ni_debug_dhcp("%s: dhcp4.renewal_time greater than dhcp4.rebind_time, using default", dev->ifname);
		lease->dhcp4.renewal_time = lease->dhcp4.lease_time / 2;
	} else if (lease->dhcp4.renewal_time == 0) {
		ni_debug_dhcp("%s: no dhcp4.renewal_time supplied, using default", dev->ifname);
		lease->dhcp4.renewal_time = lease->dhcp4.lease_time / 2;
	}

	if (lease->dhcp4.renewal_time > dev->config->max_lease_time) {
		ni_debug_dhcp("clamping lease time to %u sec", dev->config->max_lease_time);
		lease->dhcp4.renewal_time = dev->config->max_lease_time;
	}

	/* set lease to validate and commit or decline */
	ni_dhcp4_device_set_lease(dev, lease);

	if (dev->config->doflags & DHCP4_DO_ARP) {
		/*
		 * When we cannot init validate [arp], commit it.
		 */
		if (ni_dhcp4_fsm_validate_lease(dev, lease) < 0)
			ni_dhcp4_fsm_commit_lease(dev, lease);
	} else {
		ni_dhcp4_fsm_commit_lease(dev, lease);
	}

	return 0;
}
Example #16
0
static int
ni_dhcp_decode_sipservers(ni_buffer_t *bp, ni_string_array_t *list)
{
	int encoding;

	encoding = ni_buffer_getc(bp);
	switch (encoding) {
	case -1:
		ni_debug_dhcp("%s: missing data", __FUNCTION__);
		return -1;

	case 0:
		return ni_dhcp_decode_dnssearch(bp, list, "sip-server name");

	case 1:
		return ni_dhcp_decode_address_list(bp, list);

	default:
		ni_error("unknown sip encoding %d", encoding);
		return -1;
	}

	return 0;
}
Example #17
0
/*
 * We never received any response. Deal with the traumatic rejection.
 */
static void
ni_dhcp4_fsm_timeout(ni_dhcp4_device_t *dev)
{
	ni_debug_dhcp("%s: timeout in state %s",
			dev->ifname, ni_dhcp4_fsm_state_name(dev->fsm.state));
	dev->fsm.timer = NULL;

	switch (dev->fsm.state) {
	case NI_DHCP4_STATE_INIT:
		/* We get here if we previously received a NAK, and have
		 * started to back off, or if we declined a lease because
		 * the address was already in use. */
		ni_dhcp4_device_drop_lease(dev);
		ni_dhcp4_fsm_discover(dev);
		break;

	case NI_DHCP4_STATE_SELECTING:
		if (!dev->dhcp4.accept_any_offer) {
			ni_dhcp4_config_t *conf = dev->config;

			/* We were scanning all offers to check for a best offer.
			 * There was no perfect match, but we may have a "good enough"
			 * match. Check for it. */
			if (dev->best_offer.lease) {
				ni_addrconf_lease_t *lease = dev->best_offer.lease;

				ni_debug_dhcp("accepting lease offer from %s; server weight=%d",
						inet_ntoa(lease->dhcp4.server_id),
						dev->best_offer.weight);
				ni_dhcp4_process_offer(dev, lease);
				return;
			}

			ni_dhcp4_fsm_fail_lease(dev);
			if (conf->initial_discovery_timeout < conf->request_timeout) {
				__ni_dhcp4_fsm_discover(dev, 0);
				return;
			}
		}
		/* fallthrough */

	case NI_DHCP4_STATE_REQUESTING:
		ni_error("%s: DHCP4 discovery failed", dev->ifname);
		ni_dhcp4_fsm_fail_lease(dev);
		ni_dhcp4_fsm_restart(dev);

		ni_dhcp4_send_event(NI_DHCP4_EVENT_LOST, dev, NULL);

		/* Now decide whether we should keep trying */
		if (dev->config->request_timeout == ~0U)
			ni_dhcp4_fsm_discover(dev);
		break;

	case NI_DHCP4_STATE_VALIDATING:
		/* Send the next ARP probe */
		ni_dhcp4_fsm_arp_validate(dev);
		break;

	case NI_DHCP4_STATE_BOUND:
		ni_dhcp4_fsm_renewal(dev);
		break;

	case NI_DHCP4_STATE_RENEWING:
		ni_error("unable to renew lease within renewal period; trying to rebind");
		ni_dhcp4_fsm_rebind(dev);
		break;

	case NI_DHCP4_STATE_REBINDING:
		ni_error("unable to rebind lease");
		ni_dhcp4_fsm_restart(dev);
		/* FIXME: now decide whether we should try to re-discover */
		break;

	case NI_DHCP4_STATE_REBOOT:
		ni_error("unable to confirm lease");
		ni_dhcp4_fsm_commit_lease(dev, NULL);
		break;

	default:
		;
	}
}
Example #18
0
/*
 * Read a lease from a file
 */
ni_addrconf_lease_t *
ni_addrconf_lease_file_read(const char *ifname, int type, int family)
{
	ni_addrconf_lease_t *lease = NULL;
	xml_node_t *xml = NULL, *lnode;
	char *filename = NULL;
	FILE *fp;

	if (!__ni_addrconf_lease_file_path(&filename,
				ni_config_statedir(),
				ifname, type, family)) {
		ni_error("Unable to construct lease file name: %m");
		return NULL;
	}

	if ((fp = fopen(filename, "re")) == NULL) {
		if (errno == ENOENT) {
			if (__ni_addrconf_lease_file_path(&filename,
						ni_config_storedir(),
						ifname, type, family))
				fp = fopen(filename, "re");
		}
		if (fp == NULL) {
			if (errno != ENOENT) {
				ni_error("Unable to open %s for reading: %m",
						filename);
			}
			ni_string_free(&filename);
			return NULL;
		}
	}

	ni_debug_dhcp("Reading lease from %s", filename);
	xml = xml_node_scan(fp, filename);
	fclose(fp);

	if (xml == NULL) {
		ni_error("Unable to parse %s", filename);
		ni_string_free(&filename);
		return NULL;
	}

	/* find the lease node already here, so we can report it */
	if (!ni_string_eq(xml->name, NI_ADDRCONF_LEASE_XML_NODE))
		lnode = xml_node_get_child(xml, NI_ADDRCONF_LEASE_XML_NODE);
	else
		lnode = xml;
	if (!lnode) {
		ni_error("File '%s' does not contain a valid lease", filename);
		ni_string_free(&filename);
		xml_node_free(xml);
		return NULL;
	}

	if (ni_addrconf_lease_from_xml(&lease, xml) < 0) {
		ni_error("Unable to parse xml lease file '%s'", filename);
		ni_string_free(&filename);
		xml_node_free(xml);
		return NULL;
	}

	ni_string_free(&filename);
	xml_node_free(xml);
	return lease;
}
Example #19
0
/*
 * Write a lease to a file
 */
int
ni_addrconf_lease_file_write(const char *ifname, ni_addrconf_lease_t *lease)
{
	char tempname[PATH_MAX] = {'\0'};
	ni_bool_t fallback = FALSE;
	char *filename = NULL;
	xml_node_t *xml = NULL;
	FILE *fp = NULL;
	int ret = -1;
	int fd;

	if (lease->state == NI_ADDRCONF_STATE_RELEASED) {
		ni_addrconf_lease_file_remove(ifname, lease->type, lease->family);
		return 0;
	}

	if (!__ni_addrconf_lease_file_path(&filename, ni_config_storedir(),
					ifname, lease->type, lease->family)) {
		ni_error("Cannot construct lease file name: %m");
		return -1;
	}

	ni_debug_dhcp("Preparing xml lease data for '%s'", filename);
	if ((ret = ni_addrconf_lease_to_xml(lease, &xml)) != 0) {
		if (ret > 0) {
			ni_debug_dhcp("Skipped, %s:%s leases are disabled",
		                        ni_addrfamily_type_to_name(lease->family),
					ni_addrconf_type_to_name(lease->type));
		} else {
			ni_error("Unable to represent %s:%s lease as XML",
					ni_addrfamily_type_to_name(lease->family),
					ni_addrconf_type_to_name(lease->type));
		}
		goto failed;
	}

	snprintf(tempname, sizeof(tempname), "%s.XXXXXX", filename);
	if ((fd = mkstemp(tempname)) < 0) {
		if (errno == EROFS && __ni_addrconf_lease_file_path(&filename,
						ni_config_statedir(), ifname,
						lease->type, lease->family)) {
			ni_debug_dhcp("Read-only filesystem, try fallback to %s",
					filename);
			snprintf(tempname, sizeof(tempname), "%s.XXXXXX", filename);
			fd = mkstemp(tempname);
			fallback = TRUE;
		}
		if (fd < 0) {
			ni_error("Cannot create temporary lease file '%s': %m",
					tempname);
			tempname[0] = '\0';
			ret = -1;
			goto failed;
		}
	}
	if ((fp = fdopen(fd, "we")) == NULL) {
		ret = -1;
		close(fd);
		ni_error("Cannot reopen temporary lease file '%s': %m", tempname);
		goto failed;
	}

	ni_debug_dhcp("Writing lease to temporary file for '%s'", filename);
	xml_node_print(xml, fp);
	fclose(fp);
	xml_node_free(xml);

	if ((ret = rename(tempname, filename)) != 0) {
		ni_error("Unable to rename temporary lease file '%s' to '%s': %m",
				tempname, filename);
		goto failed;
	} else if (!fallback) {
		__ni_addrconf_lease_file_remove(ni_config_statedir(),
				ifname, lease->type, lease->family);
	}

	ni_debug_dhcp("Lease written to file '%s'", filename);
	ni_string_free(&filename);
	return 0;

failed:
	if (fp)
		fclose(fp);
	if (xml)
		xml_node_free(xml);
	if (tempname[0])
		unlink(tempname);
	ni_string_free(&filename);
	return -1;
}
Example #20
0
/*
 * Parse a DHCP response.
 * FIXME: RFC2131 states that the server is allowed to split a DHCP option into
 * several (partial) options if the total length exceeds 255 octets. We don't
 * handle this yet.
 */
int
ni_dhcp_parse_response(const ni_dhcp_message_t *message, ni_buffer_t *options, ni_addrconf_lease_t **leasep)
{
	ni_buffer_t overload_buf;
	ni_addrconf_lease_t *lease;
	ni_route_array_t default_routes = NI_ROUTE_ARRAY_INIT;
	ni_route_array_t static_routes = NI_ROUTE_ARRAY_INIT;
	ni_route_array_t classless_routes = NI_ROUTE_ARRAY_INIT;
	ni_string_array_t dns_servers = NI_STRING_ARRAY_INIT;
	ni_string_array_t dns_search = NI_STRING_ARRAY_INIT;
	ni_string_array_t nis_servers = NI_STRING_ARRAY_INIT;
	char *nisdomain = NULL;
	char *dnsdomain = NULL;
	int opt_overload = 0;
	int msg_type = -1;
	int use_bootserver = 1;
	int use_bootfile = 1;

	lease = ni_addrconf_lease_new(NI_ADDRCONF_DHCP, AF_INET);

	lease->state = NI_ADDRCONF_STATE_GRANTED;
	lease->type = NI_ADDRCONF_DHCP;
	lease->family = AF_INET;
	lease->time_acquired = time(NULL);

	lease->dhcp.address.s_addr = message->yiaddr;
	lease->dhcp.serveraddress.s_addr = message->siaddr;
	lease->dhcp.address.s_addr = message->yiaddr;

parse_more:
	/* Loop as long as we still have data in the buffer. */
	while (ni_buffer_count(options) && !options->underflow) {
		ni_buffer_t buf;
		int option;

		option = ni_dhcp_option_next(options, &buf);

		//ni_debug_dhcp("handle option %s (%d)", ni_dhcp_option_name(option), option);
		if (option == DHCP_PAD)
			continue;

		if (option == DHCP_END)
			break;

		if (option < 0)
			goto error;

		if (ni_buffer_count(&buf) == 0) {
			ni_error("option %d has zero length", option);
			goto error;
		}

		switch (option) {
		case DHCP_MESSAGETYPE:
			msg_type = ni_buffer_getc(&buf);
			if (msg_type < 0)
				goto error;
			continue;
		case DHCP_ADDRESS:
			ni_dhcp_option_get_ipv4(&buf, &lease->dhcp.address);
			break;
		case DHCP_NETMASK:
			ni_dhcp_option_get_ipv4(&buf, &lease->dhcp.netmask);
			break;
		case DHCP_BROADCAST:
			ni_dhcp_option_get_ipv4(&buf, &lease->dhcp.broadcast);
			break;
		case DHCP_SERVERIDENTIFIER:
			ni_dhcp_option_get_ipv4(&buf, &lease->dhcp.serveraddress);
			break;
		case DHCP_LEASETIME:
			ni_dhcp_option_get32(&buf, &lease->dhcp.lease_time);
			break;
		case DHCP_RENEWALTIME:
			ni_dhcp_option_get32(&buf, &lease->dhcp.renewal_time);
			break;
		case DHCP_REBINDTIME:
			ni_dhcp_option_get32(&buf, &lease->dhcp.rebind_time);
			break;
		case DHCP_MTU:
			ni_dhcp_option_get16(&buf, &lease->dhcp.mtu);
			/* Minimum legal mtu is 68 accoridng to
			 * RFC 2132. In practise it's 576 which is the
			 * minimum maximum message size. */
			if (lease->dhcp.mtu < MTU_MIN) {
				ni_debug_dhcp("MTU %u is too low, minimum is %d; ignoring",
						lease->dhcp.mtu, MTU_MIN);
				lease->dhcp.mtu = 0;
			}
			break;
		case DHCP_HOSTNAME:
			ni_dhcp_option_get_domain(&buf, &lease->hostname,
							"hostname");
			break;
		case DHCP_DNSDOMAIN:
			ni_dhcp_option_get_domain(&buf, &dnsdomain,
							"dns-domain");
			break;
		case DHCP_MESSAGE:
			ni_dhcp_option_get_printable(&buf, &lease->dhcp.message,
							"dhcp-message");
			break;
		case DHCP_ROOTPATH:
			ni_dhcp_option_get_pathname(&buf, &lease->dhcp.rootpath,
							"root-path");
			break;
		case DHCP_NISDOMAIN:
			ni_dhcp_option_get_domain(&buf, &nisdomain,
							"nis-domain");
			break;
		case DHCP_NETBIOSNODETYPE:
			ni_dhcp_option_get_netbios_type(&buf, &lease->netbios_type);
			break;
		case DHCP_NETBIOSSCOPE:
			ni_dhcp_option_get_domain(&buf, &lease->netbios_scope,
							"netbios-scope");
			break;
		case DHCP_DNSSERVER:
			ni_dhcp_decode_address_list(&buf, &dns_servers);
			break;
		case DHCP_NTPSERVER:
			ni_dhcp_decode_address_list(&buf, &lease->ntp_servers);
			break;
		case DHCP_NISSERVER:
			ni_dhcp_decode_address_list(&buf, &nis_servers);
			break;
		case DHCP_LPRSERVER:
			ni_dhcp_decode_address_list(&buf, &lease->lpr_servers);
			break;
		case DHCP_LOGSERVER:
			ni_dhcp_decode_address_list(&buf, &lease->log_servers);
			break;
		case DHCP_NETBIOSNAMESERVER:
			ni_dhcp_decode_address_list(&buf, &lease->netbios_name_servers);
			break;
		case DHCP_NETBIOSDDSERVER:
			ni_dhcp_decode_address_list(&buf, &lease->netbios_dd_servers);
			break;
		case DHCP_DNSSEARCH:
			ni_dhcp_decode_dnssearch(&buf, &dns_search, "dns-search domain");
			break;

		case DHCP_CSR:
		case DHCP_MSCSR:
			ni_route_array_destroy(&classless_routes);
			if (ni_dhcp_decode_csr(&buf, &classless_routes) < 0)
				goto error;
			break;

		case DHCP_SIPSERVER:
			ni_dhcp_decode_sipservers(&buf, &lease->sip_servers);
			break;

		case DHCP_STATICROUTE:
			ni_route_array_destroy(&static_routes);
			if (ni_dhcp_decode_static_routes(&buf, &static_routes) < 0)
				goto error;
			break;

		case DHCP_ROUTERS:
			ni_route_array_destroy(&default_routes);
			if (ni_dhcp_decode_routers(&buf, &default_routes) < 0)
				goto error;
			break;

		case DHCP_OPTIONSOVERLOADED:
			if (options != &overload_buf) {
				opt_overload = ni_buffer_getc(&buf);
			} else {
				ni_debug_dhcp("DHCP: ignoring OVERLOAD option in overloaded data");
				(void) ni_buffer_getc(&buf);
			}
			break;

		case DHCP_FQDN:
			/* We ignore replies about FQDN */
			break;

		default:
			ni_debug_dhcp("ignoring unsupported DHCP code %u", option);
			break;
		}

		if (buf.underflow) {
			ni_debug_dhcp("unable to parse DHCP option %s: too short",
					ni_dhcp_option_name(option));
			goto error;
		} else if (ni_buffer_count(&buf)) {
			ni_debug_dhcp("excess data in DHCP option %s - %u bytes left",
					ni_dhcp_option_name(option),
					ni_buffer_count(&buf));
		}

	}

	if (options->underflow) {
		ni_debug_dhcp("unable to parse DHCP response: truncated packet");
		goto error;
	}

	if (opt_overload) {
		const void *more_data = NULL;
		size_t size = 0;

		if (opt_overload & DHCP_OVERLOAD_BOOTFILE) {
			use_bootfile = 0;
			more_data = message->bootfile;
			size = sizeof(message->bootfile);
			opt_overload &= ~DHCP_OVERLOAD_BOOTFILE;
		} else
		if (opt_overload & DHCP_OVERLOAD_SERVERNAME) {
			use_bootserver = 0;
			more_data = message->servername;
			size = sizeof(message->servername);
			opt_overload &= ~DHCP_OVERLOAD_SERVERNAME;
		} else {
			opt_overload = 0;
		}
		if (more_data) {
			ni_buffer_init_reader(&overload_buf, (void *) more_data, size);
			options = &overload_buf;
			goto parse_more;
		}
	}

	if (use_bootserver && message->servername[0]) {
		char tmp[sizeof(message->servername)];
		size_t len;

		assert(sizeof(lease->dhcp.servername) == sizeof(message->servername));
		memcpy(tmp, message->servername, sizeof(tmp));
		tmp[sizeof(tmp)-1] = '\0';

		len = ni_string_len(tmp);
		if (ni_check_domain_name(tmp, len, 0)) {
			memcpy(lease->dhcp.servername, tmp, sizeof(lease->dhcp.servername));
		} else {
			ni_debug_dhcp("Discarded suspect boot-server name: %s",
					ni_print_suspect(tmp, len));
		}
	}
	if (use_bootfile && message->bootfile[0]) {
		char tmp[sizeof(message->bootfile)];
		size_t len;

		memcpy(tmp, message->bootfile, sizeof(tmp));
		tmp[sizeof(tmp)-1] = '\0';
		len = ni_string_len(tmp);
		if (ni_check_pathname(tmp, len)) {
			ni_string_dup(&lease->dhcp.bootfile, tmp);
		} else {
			ni_debug_dhcp("Discarded suspect boot-file name: %s",
					ni_print_suspect(tmp, len));
		}
	}

	/* Fill in any missing fields */
	if (!lease->dhcp.netmask.s_addr) {
		unsigned int pfxlen = guess_prefix_len(lease->dhcp.address);

		lease->dhcp.netmask.s_addr = htonl(~(0xFFFFFFFF >> pfxlen));
	}
Example #21
0
/*
 * Process a request to reconfigure the device (ie rebind a lease, or discover
 * a new lease).
 */
int
ni_dhcp4_acquire(ni_dhcp4_device_t *dev, const ni_dhcp4_request_t *info)
{
	ni_dhcp4_config_t *config;
	const char *classid;
	size_t len;
	int rv;

	if ((rv = ni_dhcp4_device_refresh(dev)) < 0)
		return rv;

	config = xcalloc(1, sizeof(*config));
	config->dry_run = info->dry_run;
	config->resend_timeout = NI_DHCP4_RESEND_TIMEOUT_INIT;
	config->request_timeout = info->acquire_timeout?: NI_DHCP4_REQUEST_TIMEOUT;
	config->initial_discovery_timeout = NI_DHCP4_DISCOVERY_TIMEOUT;
	config->uuid = info->uuid;
	config->flags = info->flags;
	config->update = info->update;
	config->route_priority = info->route_priority;
	config->start_delay = info->start_delay;
	config->recover_lease = info->recover_lease;
	config->release_lease = info->release_lease;

	config->max_lease_time = ni_dhcp4_config_max_lease_time();
	if (config->max_lease_time == 0)
		config->max_lease_time = ~0U;
	if (info->lease_time && info->lease_time < config->max_lease_time)
		config->max_lease_time = info->lease_time;

	if ((len = ni_string_len(info->hostname)) > 0) {
		if (ni_check_domain_name(info->hostname, len, 0)) {
			strncpy(config->hostname, info->hostname, sizeof(config->hostname) - 1);
		} else {
			ni_debug_dhcp("Discarded request to use suspect hostname: '%s'",
				ni_print_suspect(info->hostname, len));
		}
	}

	if (info->clientid) {
		ni_dhcp4_parse_client_id(&config->client_id, dev->system.hwaddr.type,
					 info->clientid);
	} else {
		/* Set client ID from interface hwaddr */
		ni_dhcp4_set_client_id(&config->client_id, &dev->system.hwaddr);
	}

	if ((classid = info->vendor_class) == NULL)
		classid = ni_dhcp4_config_vendor_class();
	if (classid)
		strncpy(config->classid, classid, sizeof(config->classid) - 1);

	config->doflags = DHCP4_DO_DEFAULT;
	config->doflags |= ni_dhcp4_do_bits(info->update);

	if (ni_debug & NI_TRACE_DHCP) {
		ni_trace("Received request:");
		ni_trace("  acquire-timeout %u", config->request_timeout);
		ni_trace("  lease-time      %u", config->max_lease_time);
		ni_trace("  start-delay     %u", config->start_delay);
		ni_trace("  hostname        %s", config->hostname[0]? config->hostname : "<none>");
		ni_trace("  vendor-class    %s", config->classid[0]? config->classid : "<none>");
		ni_trace("  client-id       %s", ni_print_hex(config->client_id.data, config->client_id.len));
		ni_trace("  uuid            %s", ni_uuid_print(&config->uuid));
		ni_trace("  update-flags    %s", __ni_dhcp4_print_doflags(config->doflags));
		ni_trace("  recover_lease   %s", config->recover_lease ? "true" : "false");
		ni_trace("  release_lease   %s", config->release_lease ? "true" : "false");
	}

	ni_dhcp4_device_set_config(dev, config);

	if (!dev->lease)
		ni_dhcp4_recover_lease(dev);

	if (dev->lease) {
		if (!ni_addrconf_lease_is_valid(dev->lease)
		 || (config->client_id.len && !ni_opaque_eq(&config->client_id, &dev->lease->dhcp4.client_id))) {
			ni_debug_dhcp("%s: lease doesn't match request", dev->ifname);
			ni_dhcp4_device_drop_lease(dev);
			dev->notify = 1;
		}
	}

	ni_note("%s: Request to acquire DHCPv4 lease with UUID %s",
		dev->ifname, ni_uuid_print(&config->uuid));

	if (ni_dhcp4_device_start(dev) < 0)
		return -1;
	return 1;
}
Example #22
0
int
ni_dhcp4_fsm_commit_lease(ni_dhcp4_device_t *dev, ni_addrconf_lease_t *lease)
{
	ni_capture_free(dev->capture);
	dev->capture = NULL;

	if (lease) {
		ni_debug_dhcp("%s: committing lease", dev->ifname);
		if (dev->config->dry_run == NI_DHCP4_RUN_NORMAL) {
			ni_debug_dhcp("%s: schedule renewal of lease in %u seconds",
					dev->ifname, lease->dhcp4.renewal_time);
			ni_dhcp4_fsm_set_timeout(dev, lease->dhcp4.renewal_time);
		}

		/* If the user requested a specific route metric, apply it now */
		if (dev->config) {
			ni_route_table_t *tab;
			ni_route_t *rp;
			unsigned int i;

			for (tab = lease->routes; tab; tab = tab->next) {
				for (i = 0; i < tab->routes.count; ++i) {
					if ((rp = tab->routes.data[i]) == NULL)
						continue;
					rp->protocol = RTPROT_DHCP;
					rp->priority = dev->config->route_priority;
				}
			}
		}

		ni_dhcp4_device_set_lease(dev, lease);
		dev->fsm.state = NI_DHCP4_STATE_BOUND;

		ni_note("%s: Committed DHCPv4 lease with address %s "
			"(lease time %u sec, renew in %u sec, rebind in %u sec)",
			dev->ifname, inet_ntoa(lease->dhcp4.address),
			lease->dhcp4.lease_time, lease->dhcp4.renewal_time,
			lease->dhcp4.rebind_time);

		/* Write the lease to lease cache */
		if (dev->config->dry_run != NI_DHCP4_RUN_OFFER) {
			ni_addrconf_lease_file_write(dev->ifname, lease);
		}

		/* Notify anyone who cares that we've (re-)acquired the lease */
		ni_dhcp4_send_event(NI_DHCP4_EVENT_ACQUIRED, dev, lease);

		if (dev->config->dry_run != NI_DHCP4_RUN_NORMAL) {
			ni_dhcp4_fsm_restart(dev);
			ni_dhcp4_device_stop(dev);
		}
	} else {

		/* Delete old lease file */
		if ((lease = dev->lease) != NULL) {
			ni_note("%s: Dropped DHCPv4 lease with UUID %s",
				dev->ifname, ni_uuid_print(&lease->uuid));

			lease->state = NI_ADDRCONF_STATE_RELEASED;
			ni_dhcp4_send_event(NI_DHCP4_EVENT_RELEASED, dev, lease);

			if (!dev->config || dev->config->dry_run != NI_DHCP4_RUN_OFFER) {
				ni_addrconf_lease_file_remove(dev->ifname, lease->type, lease->family);
			}
			ni_dhcp4_device_drop_lease(dev);
		}

		ni_dhcp4_fsm_restart(dev);
	}

	return 0;
}
Example #23
0
int
ni_dhcp4_tester_run(ni_dhcp4_tester_t *opts)
{
	ni_netconfig_t *nc;
	ni_netdev_t *ifp = NULL;
	ni_dhcp4_device_t *dev = NULL;
	ni_dhcp4_request_t *req = NULL;
	unsigned int link_timeout = 20;
	int rv;

	if (opts->timeout && opts->timeout != -1U) {
		link_timeout = (opts->timeout * 2) / 3;
		opts->timeout -= link_timeout;
	}

	if (!opts || ni_string_empty(opts->ifname))
		ni_fatal("Invalid start parameters!");

	dhcp4_tester_opts   = *opts;
	dhcp4_tester_status = NI_WICKED_RC_ERROR;

	if (!(nc = ni_global_state_handle(1)))
		ni_fatal("Cannot refresh interface list!");

	if (!(ifp = ni_netdev_by_name(nc, opts->ifname)))
		ni_fatal("Cannot find interface with name '%s'", opts->ifname);

	if (!ni_dhcp4_supported(ifp))
		ni_fatal("DHCPv4 not supported on '%s'", opts->ifname);

	if (!(dev = ni_dhcp4_device_new(ifp->name, &ifp->link)))
		ni_fatal("Cannot allocate dhcp4 client for '%s'", opts->ifname);

	ni_dhcp4_set_event_handler(ni_dhcp4_tester_protocol_event);

	if (!(req = ni_dhcp4_request_new())) {
		ni_error("Cannot allocate dhcp4 request");
		goto failure;
	}

	if (!ni_dhcp4_tester_req_init(req, opts->request))
		goto failure;

	if (!ni_netdev_link_is_up(ifp)) {
		ni_netdev_req_t *ifreq;
		ni_debug_dhcp("%s: Link is not up, trying to bring it up",
				ifp->name);

		ifreq = ni_netdev_req_new();
		ifreq->ifflags = NI_IFF_LINK_UP | NI_IFF_NETWORK_UP;
		if ((rv = ni_system_interface_link_change(ifp, ifreq)) < 0) {
			ni_error("%s: Unable to set up link", ifp->name);
			ni_netdev_req_free(ifreq);
			goto failure;
		}
		ni_netdev_req_free(ifreq);

		do {
			sleep(1);

			if (!(nc = ni_global_state_handle(1)))
				goto failure;

			if (!(ifp = ni_netdev_by_index(nc, dev->link.ifindex)))
				break;

			if (ni_netdev_link_is_up(ifp))
				break;

			ni_debug_dhcp("%s: Link is not (yet) up", ifp->name);
		} while (link_timeout-- > 1);

		if (!ifp || !ni_netdev_link_is_up(ifp) || !link_timeout) {
			ni_error("%s: Unable to bring link up",
				ifp && ifp->name ? ifp->name : dev->ifname);
			goto failure;
		}

		/* Do not try to send too early, even link is reported up now */
		sleep(1);
	}

	if (opts->timeout && opts->timeout != -1U)
		req->acquire_timeout = opts->timeout;

	req->broadcast = opts->broadcast;

	if ((rv = ni_dhcp4_acquire(dev, req)) < 0) {
		ni_error("%s: DHCP4v6 acquire request %s failed: %s",
				dev->ifname, ni_uuid_print(&req->uuid),
				ni_strerror(rv));
		goto failure;
	}

	dhcp4_tester_status = NI_WICKED_RC_IN_PROGRESS;
	while (!ni_caught_terminal_signal()) {
		long timeout;

		timeout = ni_timer_next_timeout();

		if (ni_socket_wait(timeout) != 0)
			break;
	}
	ni_server_deactivate_interface_events();
	ni_socket_deactivate_all();

failure:
	if (dev)
		ni_dhcp4_device_put(dev);
	if (req)
		ni_dhcp4_request_free(req);
	return dhcp4_tester_status;
}
Example #24
0
int
ni_dhcp4_fsm_process_dhcp4_packet(ni_dhcp4_device_t *dev, ni_buffer_t *msgbuf)
{
	ni_dhcp4_message_t *message;
	ni_addrconf_lease_t *lease = NULL;
	int msg_code;

	if (dev->fsm.state == NI_DHCP4_STATE_VALIDATING) {
		/* We arrive here, when some dhcp4 packet arrives after
		 * we've got and processed an ACK already. Just ignore.
		 */
		ni_debug_dhcp("%s: ignoring dhcp4 packet arrived in state VALIDATING",
				dev->ifname);
		return -1;
	}

	if (!(message = ni_buffer_pull_head(msgbuf, sizeof(*message)))) {
		ni_debug_dhcp("short DHCP4 packet (%u bytes)", ni_buffer_count(msgbuf));
		return -1;
	}
	if (dev->dhcp4.xid == 0) {
		ni_debug_dhcp("unexpected packet on %s", dev->ifname);
		return -1;
	}
	if (dev->dhcp4.xid != message->xid) {
		ni_debug_dhcp("ignoring packet with wrong xid 0x%x (expected 0x%x)",
				message->xid, dev->dhcp4.xid);
		return -1;
	}

	msg_code = ni_dhcp4_parse_response(message, msgbuf, &lease);
	if (msg_code < 0) {
		/* Ignore this message, time out later */
		ni_error("unable to parse DHCP4 response");
		return -1;
	}

	/* set reqest client-id in the response early to have it in test mode */
	ni_opaque_set(&lease->dhcp4.client_id,	dev->config->client_id.data,
						dev->config->client_id.len);

	ni_debug_dhcp("%s: received %s message in state %s",
			dev->ifname, ni_dhcp4_message_name(msg_code),
			ni_dhcp4_fsm_state_name(dev->fsm.state));

	/* When receiving a DHCP4 OFFER, verify sender address against list of
	 * servers to ignore, and preferred servers. */
	if (msg_code == DHCP4_OFFER && dev->fsm.state == NI_DHCP4_STATE_SELECTING) {
		struct in_addr srv_addr = lease->dhcp4.server_id;
		int weight = 0;

		if (ni_dhcp4_config_ignore_server(srv_addr)) {
			ni_debug_dhcp("%s: ignoring DHCP4 offer from %s",
					dev->ifname, inet_ntoa(srv_addr));
			goto out;
		}

		/* If we're scanning all offers, we need to decide whether
		 * this offer is accepted, or whether we want to wait for
		 * more.
		 */
		if (!dev->dhcp4.accept_any_offer) {

			/* Check if we have any preferred servers. */
			weight = ni_dhcp4_config_server_preference(srv_addr);

			/* If we're refreshing an existing lease (eg after link disconnect
			 * and reconnect), we accept the offer if it comes from the same
			 * server as the original one.
			 */
			if (dev->lease
			 && dev->lease->dhcp4.server_id.s_addr == srv_addr.s_addr)
				weight = 100;

			ni_debug_dhcp("received lease offer from %s; server weight=%d (best offer=%d)",
					inet_ntoa(lease->dhcp4.server_id), weight,
					dev->best_offer.weight);

			/* negative weight means never. */
			if (weight < 0)
				goto out;

			/* weight between 0 and 100 means maybe. */
			if (weight < 100) {
				if (dev->best_offer.weight < weight) {
					ni_dhcp4_device_set_best_offer(dev, lease, weight);
					return 0;
				}
				goto out;
			}
			/* If the weight has maximum value, just accept this offer. */
		}
		ni_dhcp4_device_set_best_offer(dev, lease, weight);
		lease = NULL;
	}

	/* We've received a valid response; if something goes wrong now
	 * it's nothing that could be fixed by retransmitting the message.
	 *
	 * The only exception would be if we ever do some filtering or
	 * matching of OFFERs - then we would certainly want to keep
	 * waiting for additional packets.
	 */
	ni_dhcp4_device_disarm_retransmit(dev);
	dev->dhcp4.xid = 0;

	/* move to next stage of protocol */
	switch (msg_code) {
	case DHCP4_OFFER:
		if (dev->fsm.state != NI_DHCP4_STATE_SELECTING)
			goto ignore;

		/* process best offer set above */
		ni_dhcp4_process_offer(dev, dev->best_offer.lease);
		break;

	case DHCP4_ACK:
		if (dev->fsm.state == NI_DHCP4_STATE_INIT) {
			/*
			 * Received a decline ACK -- wait until
			 * timeout before we restart from begin
			 */
			ni_dhcp4_device_drop_lease(dev);
			break;
		}

		if (dev->fsm.state != NI_DHCP4_STATE_REQUESTING
		 && dev->fsm.state != NI_DHCP4_STATE_RENEWING
		 && dev->fsm.state != NI_DHCP4_STATE_REBOOT
		 && dev->fsm.state != NI_DHCP4_STATE_REBINDING)
			goto ignore;

		ni_dhcp4_process_ack(dev, lease);
		lease = NULL;
		break;

	case DHCP4_NAK:
		/* The RFC 2131 state diagram says, ignore NAKs in state BOUND.
		 * I guess we also have no use for NAK replies to a DHCP4_DISCOVER
		 */
		if (dev->fsm.state == NI_DHCP4_STATE_SELECTING
		 || dev->fsm.state == NI_DHCP4_STATE_BOUND)
			goto ignore;

		ni_dhcp4_process_nak(dev);
		break;

	default:
	ignore:
		ni_debug_dhcp("ignoring %s in state %s",
				ni_dhcp4_message_name(msg_code),
				ni_dhcp4_fsm_state_name(dev->fsm.state));
		break;
	}

out:
	if (lease && dev->lease != lease)
		ni_addrconf_lease_free(lease);

	/* If we received a message other than NAK, reset the NAK
	 * backoff timer. */
	if (msg_code != DHCP4_NAK)
		dev->dhcp4.nak_backoff = 1;

	return 0;
}
Example #25
0
/*
 * Decode an RFC3397 DNS search order option.
 */
static int
ni_dhcp_decode_dnssearch(ni_buffer_t *optbuf, ni_string_array_t *list, const char *what)
{
	ni_stringbuf_t namebuf = NI_STRINGBUF_INIT_DYNAMIC;
	unsigned char *base = ni_buffer_head(optbuf);
	unsigned int base_offset = optbuf->head;
	size_t len;

	ni_string_array_destroy(list);

	while (ni_buffer_count(optbuf) && !optbuf->underflow) {
		ni_buffer_t *bp = optbuf;
		ni_buffer_t jumpbuf;

		while (1) {
			unsigned int pos = bp->head - base_offset;
			unsigned int pointer;
			char label[64];
			int length;

			if ((length = ni_buffer_getc(bp)) < 0)
				goto failure; /* unexpected EOF */

			if (length == 0)
				break;	/* end of this name */

			switch (length & 0xC0) {
			case 0:
				/* Plain name component */
				if (ni_buffer_get(bp, label, length) < 0)
					goto failure;

				label[length] = '\0';
				if (!ni_stringbuf_empty(&namebuf))
					ni_stringbuf_putc(&namebuf, '.');
				ni_stringbuf_puts(&namebuf, label);
				break;

			case 0xC0:
				/* Pointer */
				pointer = (length & 0x3F) << 8;
				if ((length = ni_buffer_getc(bp)) < 0)
					goto failure;

				pointer |= length;
				if (pointer >= pos)
					goto failure;

				ni_buffer_init_reader(&jumpbuf, base, pos);
				jumpbuf.head = pointer;
				bp = &jumpbuf;
				break;

			default:
				goto failure;
			}

		}

		if (!ni_stringbuf_empty(&namebuf)) {

			len = ni_string_len(namebuf.string);
			if (ni_check_domain_name(namebuf.string, len, 0)) {
				ni_string_array_append(list, namebuf.string);
			} else {
				ni_debug_dhcp("Discarded suspect %s: %s", what,
					ni_print_suspect(namebuf.string, len));
			}
		}
		ni_stringbuf_destroy(&namebuf);
	}

	return 0;

failure:
	ni_stringbuf_destroy(&namebuf);
	ni_string_array_destroy(list);
	return -1;
}