예제 #1
0
int
locate_network(struct packet *packet)
{
	struct iaddr ia;

	/* If this came through a gateway, find the corresponding subnet... */
	if (packet->raw->giaddr.s_addr) {
		struct subnet *subnet;

		ia.len = 4;
		memcpy(ia.iabuf, &packet->raw->giaddr, 4);
		subnet = find_subnet(ia);
		if (subnet)
			packet->shared_network = subnet->shared_network;
		else
			packet->shared_network = NULL;
	} else {
		packet->shared_network = packet->interface->shared_network;
	}
	if (packet->shared_network)
		return 1;
	return 0;
}
예제 #2
0
파일: udpsock.c 프로젝트: darksoul42/bitrig
void
udpsock_handler(struct protocol *protocol)
{
	int			 sockio;
	char			 cbuf[256], ifname[IF_NAMESIZE];
	ssize_t			 len;
	struct udpsock		*udpsock = protocol->local;
	struct msghdr		 m;
	struct cmsghdr		*cm;
	struct iovec		 iov[1];
	struct sockaddr_storage	 ss;
	struct sockaddr_in	*sin4;
	struct sockaddr_dl	*sdl = NULL;
	struct interface_info	 iface;
	struct iaddr		 from, addr;
	unsigned char		 packetbuf[4095];
	struct dhcp_packet	*packet = (struct dhcp_packet *)packetbuf;
	struct hardware		 hw;
	struct ifreq		 ifr;
	struct subnet		*subnet;

	memset(&hw, 0, sizeof(hw));

	iov[0].iov_base = packetbuf;
	iov[0].iov_len = sizeof(packetbuf);
	memset(&m, 0, sizeof(m));
	m.msg_name = &ss;
	m.msg_namelen = sizeof(ss);
	m.msg_iov = iov;
	m.msg_iovlen = 1;
	m.msg_control = cbuf;
	m.msg_controllen = sizeof(cbuf);

	memset(&iface, 0, sizeof(iface));
	if ((len = recvmsg(udpsock->sock, &m, 0)) < 0) {
		warning("receiving a DHCP message failed: %s", strerror(errno));
		return;
	}
	if (ss.ss_family != AF_INET) {
		warning("received DHCP message is not AF_INET");
		return;
	}
	sin4 = (struct sockaddr_in *)&ss;
	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&m);
	    m.msg_controllen != 0 && cm;
	    cm = (struct cmsghdr *)CMSG_NXTHDR(&m, cm)) {
		if (cm->cmsg_level == IPPROTO_IP &&
		    cm->cmsg_type == IP_RECVIF)
			sdl = (struct sockaddr_dl *)CMSG_DATA(cm);
	}
	if (sdl == NULL) {
		warning("could not get the received interface by IP_RECVIF");
		return;
	}
	if_indextoname(sdl->sdl_index, ifname);

	if ((sockio = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		warning("socket creation failed: %s", strerror(errno));
		return;
	}
	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
	if (ioctl(sockio, SIOCGIFADDR, &ifr, sizeof(ifr)) != 0) {
		warning("Failed to get address for %s: %s", ifname,
		    strerror(errno));
		close(sockio);
		return;
	}
	close(sockio);

	if (ifr.ifr_addr.sa_family != AF_INET)
		return;

	iface.is_udpsock = 1;
	iface.send_packet = udpsock_send_packet;
	iface.wfdesc = udpsock->sock;
	iface.ifp = &ifr;
	iface.index = sdl->sdl_index;
	iface.primary_address = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
	strlcpy(iface.name, ifname, sizeof(iface.name));

	addr.len = 4;
	memcpy(&addr.iabuf, &iface.primary_address, addr.len);

	if ((subnet = find_subnet(addr)) == NULL)
		return;
	iface.shared_network = subnet->shared_network ;
	from.len = 4;
	memcpy(&from.iabuf, &sin4->sin_addr, from.len);
	do_packet(&iface, packet, len, sin4->sin_port, from, &hw);
}
예제 #3
0
void 
dhcpleasequery(struct packet *packet, int ms_nulltp) {
	char msgbuf[256];
	char dbg_info[128];
	struct iaddr cip;
	struct iaddr gip;
	struct data_string uid;
	struct hardware h;
	struct lease *tmp_lease;
	struct lease *lease;
	int want_associated_ip;
	int assoc_ip_cnt;
	u_int32_t assoc_ips[40];  /* XXXSK: arbitrary maximum number of IPs */
	const int nassoc_ips = sizeof(assoc_ips) / sizeof(assoc_ips[0]);

	unsigned char dhcpMsgType;
	const char *dhcp_msg_type_name;
	struct subnet *subnet;
	struct group *relay_group;
	struct option_state *options;
	struct option_cache *oc;
	int allow_leasequery;
	int ignorep;
	u_int32_t lease_duration;
	u_int32_t time_renewal;
	u_int32_t time_rebinding;
	u_int32_t time_expiry;
	u_int32_t client_last_transaction_time;
	struct sockaddr_in to;
	struct in_addr siaddr;
	struct data_string prl;
	struct data_string *prl_ptr;

	int i;
	struct interface_info *interface;

	/* INSIST(packet != NULL); */

	/*
	 * Prepare log information.
	 */
	snprintf(msgbuf, sizeof(msgbuf), 
		"DHCPLEASEQUERY from %s", inet_ntoa(packet->raw->giaddr));

	/* 
	 * We can't reply if there is no giaddr field.
	 */
	if (!packet->raw->giaddr.s_addr) {
		log_info("%s: missing giaddr, ciaddr is %s, no reply sent", 
			 msgbuf, inet_ntoa(packet->raw->ciaddr));
		return;
	}

	/* 
	 * Initially we use the 'giaddr' subnet options scope to determine if
	 * the giaddr-identified relay agent is permitted to perform a
	 * leasequery.  The subnet is not required, and may be omitted, in
	 * which case we are essentially interrogating the root options class
	 * to find a globally permit.
	 */
	gip.len = sizeof(packet->raw->giaddr);
	memcpy(gip.iabuf, &packet->raw->giaddr, sizeof(packet->raw->giaddr));

	subnet = NULL;
	find_subnet(&subnet, gip, MDL);
	if (subnet != NULL)
		relay_group = subnet->group;
	else
		relay_group = root_group;

	subnet_dereference(&subnet, MDL);

	options = NULL;
	if (!option_state_allocate(&options, MDL)) {
		log_error("No memory for option state.");
		log_info("%s: out of memory, no reply sent", msgbuf);
		return;
	}

	execute_statements_in_scope(NULL, packet, NULL, NULL, packet->options,
				    options, &global_scope, relay_group,
				    NULL, NULL);

	for (i=packet->class_count-1; i>=0; i--) {
		execute_statements_in_scope(NULL, packet, NULL, NULL,
					    packet->options, options,
					    &global_scope,
					    packet->classes[i]->group,
					    relay_group, NULL);
	}

	/* 
	 * Because LEASEQUERY has some privacy concerns, default to deny.
	 */
	allow_leasequery = 0;

	/*
	 * See if we are authorized to do LEASEQUERY.
	 */
	oc = lookup_option(&server_universe, options, SV_LEASEQUERY);
	if (oc != NULL) {
		allow_leasequery = evaluate_boolean_option_cache(&ignorep,
					 packet, NULL, NULL, packet->options,
					 options, &global_scope, oc, MDL);
	}

	if (!allow_leasequery) {
		log_info("%s: LEASEQUERY not allowed, query ignored", msgbuf);
		option_state_dereference(&options, MDL);
		return;
	}


	/* 
	 * Copy out the client IP address.
	 */
	cip.len = sizeof(packet->raw->ciaddr);
	memcpy(cip.iabuf, &packet->raw->ciaddr, sizeof(packet->raw->ciaddr));

	/* 
	 * If the client IP address is valid (not all zero), then we 
	 * are looking for information about that IP address.
	 */
	assoc_ip_cnt = 0;
	lease = tmp_lease = NULL;
	if (memcmp(cip.iabuf, "\0\0\0", 4)) {

		want_associated_ip = 0;

		snprintf(dbg_info, sizeof(dbg_info), "IP %s", piaddr(cip));
		find_lease_by_ip_addr(&lease, cip, MDL);


	} else {

		want_associated_ip = 1;

		/*
		 * If the client IP address is all zero, then we will
		 * either look up by the client identifier (if we have
		 * one), or by the MAC address.
		 */

		memset(&uid, 0, sizeof(uid));
		if (get_option(&uid, 
			       &dhcp_universe,
			       packet,
			       NULL,
			       NULL,
			       packet->options,
			       NULL,
			       packet->options, 
			       &global_scope,
			       DHO_DHCP_CLIENT_IDENTIFIER,
			       MDL)) {

			snprintf(dbg_info, 
				 sizeof(dbg_info), 
				 "client-id %s",
				 print_hex_1(uid.len, uid.data, 60));

			find_lease_by_uid(&tmp_lease, uid.data, uid.len, MDL);
			data_string_forget(&uid, MDL);
			get_newest_lease(&lease, tmp_lease, next_uid);
			assoc_ip_cnt = get_associated_ips(tmp_lease,
							  next_uid, 
							  lease,
							  assoc_ips, 
							  nassoc_ips);

		} else {

			if (packet->raw->hlen+1 > sizeof(h.hbuf)) {
				log_info("%s: hardware length too long, "
					 "no reply sent", msgbuf);
				option_state_dereference(&options, MDL);
				return;
			}

			h.hlen = packet->raw->hlen + 1;
			h.hbuf[0] = packet->raw->htype;
			memcpy(&h.hbuf[1], 
			       packet->raw->chaddr, 
			       packet->raw->hlen);

			snprintf(dbg_info, 
				 sizeof(dbg_info), 
				 "MAC address %s",
				 print_hw_addr(h.hbuf[0], 
					       h.hlen - 1, 
					       &h.hbuf[1]));

			find_lease_by_hw_addr(&tmp_lease, h.hbuf, h.hlen, MDL);
			get_newest_lease(&lease, tmp_lease, next_hw);
			assoc_ip_cnt = get_associated_ips(tmp_lease,
							  next_hw, 
							  lease,
							  assoc_ips, 
							  nassoc_ips);

		}

		lease_dereference(&tmp_lease, MDL);

		if (lease != NULL) {
			memcpy(&packet->raw->ciaddr, 
			       lease->ip_addr.iabuf,
			       sizeof(packet->raw->ciaddr));
		}

		/*
		 * Log if we have too many IP addresses associated
		 * with this client.
		 */
		if (want_associated_ip && (assoc_ip_cnt > nassoc_ips)) {
			log_info("%d IP addresses associated with %s, "
				 "only %d sent in reply.",
				 assoc_ip_cnt, dbg_info, nassoc_ips);
		}
	}

	/*
	 * We now know the query target too, so can report this in 
	 * our log message.
	 */
	snprintf(msgbuf, sizeof(msgbuf), 
		"DHCPLEASEQUERY from %s for %s",
		inet_ntoa(packet->raw->giaddr), dbg_info);

	/*
	 * Figure our our return type.
	 */
	if (lease == NULL) {
		dhcpMsgType = DHCPLEASEUNKNOWN;
		dhcp_msg_type_name = "DHCPLEASEUNKNOWN";
	} else {
		if (lease->binding_state == FTS_ACTIVE) {
			dhcpMsgType = DHCPLEASEACTIVE;
			dhcp_msg_type_name = "DHCPLEASEACTIVE";
		} else {
			dhcpMsgType = DHCPLEASEUNASSIGNED;
			dhcp_msg_type_name = "DHCPLEASEUNASSIGNED";
		}
	}

	/* 
	 * Set options that only make sense if we have an active lease.
	 */

	if (dhcpMsgType == DHCPLEASEACTIVE)
	{
		/*
		 * RFC 4388 uses the PRL to request options for the agent to
		 * receive that are "about" the client.  It is confusing
		 * because in some cases it wants to know what was sent to
		 * the client (lease times, adjusted), and in others it wants
		 * to know information the client sent.  You're supposed to
		 * know this on a case-by-case basis.
		 *
		 * "Name servers", "domain name", and the like from the relay
		 * agent's scope seems less than useful.  Our options are to
		 * restart the option cache from the lease's best point of view
		 * (execute statements from the lease pool's group), or to
		 * simply restart the option cache from empty.
		 *
		 * I think restarting the option cache from empty best
		 * approaches RFC 4388's intent; specific options are included.
		 */
		option_state_dereference(&options, MDL);

		if (!option_state_allocate(&options, MDL)) {
			log_error("%s: out of memory, no reply sent", msgbuf);
			lease_dereference(&lease, MDL);
			return;
		}

		/* 
		 * Set the hardware address fields.
		 */

		packet->raw->hlen = lease->hardware_addr.hlen - 1;
		packet->raw->htype = lease->hardware_addr.hbuf[0];
		memcpy(packet->raw->chaddr, 
		       &lease->hardware_addr.hbuf[1], 
		       sizeof(packet->raw->chaddr));

		/*
		 * Set client identifier option.
		 */
		if (lease->uid_len > 0) {
			if (!add_option(options,
					DHO_DHCP_CLIENT_IDENTIFIER,
					lease->uid,
					lease->uid_len)) {
				option_state_dereference(&options, MDL);
				lease_dereference(&lease, MDL);
				log_info("%s: out of memory, no reply sent",
					 msgbuf);
				return;
			}
		}


		/*
		 * Calculate T1 and T2, the times when the client
		 * tries to extend its lease on its networking
		 * address.
		 * These seem to be hard-coded in ISC DHCP, to 0.5 and
		 * 0.875 of the lease time.
		 */

		lease_duration = lease->ends - lease->starts;
		time_renewal = lease->starts + 
			(lease_duration / 2);
		time_rebinding = lease->starts + 
			(lease_duration / 2) +
			(lease_duration / 4) +
			(lease_duration / 8);

		if (time_renewal > cur_time) {
			time_renewal = htonl(time_renewal - cur_time);

			if (!add_option(options, 
					DHO_DHCP_RENEWAL_TIME,
					&time_renewal, 
					sizeof(time_renewal))) {
				option_state_dereference(&options, MDL);
				lease_dereference(&lease, MDL);
				log_info("%s: out of memory, no reply sent",
					 msgbuf);
				return;
			}
		}

		if (time_rebinding > cur_time) {
			time_rebinding = htonl(time_rebinding - cur_time);

			if (!add_option(options, 
					DHO_DHCP_REBINDING_TIME,
					&time_rebinding, 
					sizeof(time_rebinding))) {
				option_state_dereference(&options, MDL);
				lease_dereference(&lease, MDL);
				log_info("%s: out of memory, no reply sent",
					 msgbuf);
				return;
			}
		}

		if (lease->ends > cur_time) {
			time_expiry = htonl(lease->ends - cur_time);

			if (!add_option(options, 
					DHO_DHCP_LEASE_TIME,
					&time_expiry, 
					sizeof(time_expiry))) {
				option_state_dereference(&options, MDL);
				lease_dereference(&lease, MDL);
				log_info("%s: out of memory, no reply sent",
					 msgbuf);
				return;
			}
		}

		/* Supply the Vendor-Class-Identifier. */
		if (lease->scope != NULL) {
			struct data_string vendor_class;

			memset(&vendor_class, 0, sizeof(vendor_class));

			if (find_bound_string(&vendor_class, lease->scope,
					      "vendor-class-identifier")) {
				if (!add_option(options,
						DHO_VENDOR_CLASS_IDENTIFIER,
						(void *)vendor_class.data,
						vendor_class.len)) {
					option_state_dereference(&options,
								 MDL);
					lease_dereference(&lease, MDL);
					log_error("%s: error adding vendor "
						  "class identifier, no reply "
						  "sent", msgbuf);
					data_string_forget(&vendor_class, MDL);
					return;
				}
				data_string_forget(&vendor_class, MDL);
			}
		}

		/*
		 * Set the relay agent info.
		 *
		 * Note that because agent info is appended without regard
		 * to the PRL in cons_options(), this will be sent as the
		 * last option in the packet whether it is listed on PRL or
		 * not.
		 */

		if (lease->agent_options != NULL) {
			int idx = agent_universe.index;
			struct option_chain_head **tmp1 = 
				(struct option_chain_head **)
				&(options->universes[idx]);
				struct option_chain_head *tmp2 = 
				(struct option_chain_head *)
				lease->agent_options;

			option_chain_head_reference(tmp1, tmp2, MDL);
		}

		/* 
	 	 * Set the client last transaction time.
		 * We check to make sure we have a timestamp. For
		 * lease files that were saved before running a 
		 * timestamp-aware version of the server, this may
		 * not be set.
	 	 */

		if (lease->cltt != MIN_TIME) {
			if (cur_time > lease->cltt) {
				client_last_transaction_time = 
					htonl(cur_time - lease->cltt);
			} else {
				client_last_transaction_time = htonl(0);
			}
			if (!add_option(options, 
					DHO_CLIENT_LAST_TRANSACTION_TIME,
					&client_last_transaction_time,
		     			sizeof(client_last_transaction_time))) {
				option_state_dereference(&options, MDL);
				lease_dereference(&lease, MDL);
				log_info("%s: out of memory, no reply sent",
					 msgbuf);
				return;
			}
		}

		/*
	 	 * Set associated IPs, if requested and there are some.
	 	 */
		if (want_associated_ip && (assoc_ip_cnt > 0)) {
			if (!add_option(options, 
					DHO_ASSOCIATED_IP,
					assoc_ips,
					assoc_ip_cnt * sizeof(assoc_ips[0]))) {
				option_state_dereference(&options, MDL);
				lease_dereference(&lease, MDL);
				log_info("%s: out of memory, no reply sent",
					 msgbuf);
				return;
			}
		}
	}

	/* 
	 * Set the message type.
	 */

	packet->raw->op = BOOTREPLY;

	/*
	 * Set DHCP message type.
	 */
	if (!add_option(options, 
		        DHO_DHCP_MESSAGE_TYPE,
		        &dhcpMsgType, 
			sizeof(dhcpMsgType))) {
		option_state_dereference(&options, MDL);
		lease_dereference(&lease, MDL);
		log_info("%s: error adding option, no reply sent", msgbuf);
		return;
	}

	/*
	 * Log the message we've received.
	 */
	log_info("%s", msgbuf);

	/*
	 * Figure out which address to use to send from.
	 */
	get_server_source_address(&siaddr, options, options, packet);

	/* 
	 * Set up the option buffer.
	 */

	memset(&prl, 0, sizeof(prl));
	oc = lookup_option(&dhcp_universe, options, 
			   DHO_DHCP_PARAMETER_REQUEST_LIST);
	if (oc != NULL) {
		evaluate_option_cache(&prl, 
				      packet, 
				      NULL,
				      NULL,
				      packet->options,
				      options,
				      &global_scope,
				      oc,
				      MDL);
	}
	if (prl.len > 0) {
		prl_ptr = &prl;
	} else {
		prl_ptr = NULL;
	}

	packet->packet_length = cons_options(packet, 
					     packet->raw, 
					     lease,
					     NULL,
					     0,
					     packet->options,
					     options,
					     &global_scope,
					     0,
					     0,
					     0, 
					     prl_ptr,
					     NULL);

	data_string_forget(&prl, MDL);	/* SK: safe, even if empty */
	option_state_dereference(&options, MDL);
	lease_dereference(&lease, MDL);

	to.sin_family = AF_INET;
#ifdef HAVE_SA_LEN
	to.sin_len = sizeof(to);
#endif
	memset(to.sin_zero, 0, sizeof(to.sin_zero));

	/* 
	 * Leasequery packets are be sent to the gateway address.
	 */
	to.sin_addr = packet->raw->giaddr;
	if (packet->raw->giaddr.s_addr != htonl(INADDR_LOOPBACK)) {
		to.sin_port = local_port;
	} else {
		to.sin_port = remote_port; /* XXXSK: For debugging. */
	}

	/* 
	 * The fallback_interface lets us send with a real IP
	 * address. The packet interface sends from all-zeros.
	 */
	if (fallback_interface != NULL) {
		interface = fallback_interface;
	} else {
		interface = packet->interface;
	}

	/*
	 * Report what we're sending.
	 */
	log_info("%s to %s for %s (%d associated IPs)",
		dhcp_msg_type_name, 
		inet_ntoa(to.sin_addr), dbg_info, assoc_ip_cnt);

	send_packet(interface,
		    NULL,
		    packet->raw, 
		    packet->packet_length,
		    siaddr,
		    &to,
		    NULL);
}
예제 #4
0
void
discover_interfaces(int *rdomain)
{
	struct interface_info *tmp;
	struct interface_info *last, *next;
	struct subnet *subnet;
	struct shared_network *share;
	struct sockaddr_in foo;
	int ir = 0, ird;
	struct ifreq *tif;
	struct ifaddrs *ifap, *ifa;

	if (getifaddrs(&ifap) != 0)
		error("getifaddrs failed");

	/*
	 * If we already have a list of interfaces, the interfaces were
	 * requested.
	 */
	if (interfaces != NULL)
		ir = 1;
	else
		/* must specify an interface when rdomains are used */
		*rdomain = 0;

	/* Cycle through the list of interfaces looking for IP addresses. */
	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
		/*
		 * See if this is the sort of interface we want to
		 * deal with.  Skip loopback, point-to-point and down
		 * interfaces, except don't skip down interfaces if we're
		 * trying to get a list of configurable interfaces.
		 */
		if ((ifa->ifa_flags & IFF_LOOPBACK) ||
		    (ifa->ifa_flags & IFF_POINTOPOINT) ||
		    (!(ifa->ifa_flags & IFF_UP)) ||
		    (!(ifa->ifa_flags & IFF_BROADCAST)))
			continue;

		/* See if we've seen an interface that matches this one. */
		for (tmp = interfaces; tmp; tmp = tmp->next)
			if (!strcmp(tmp->name, ifa->ifa_name))
				break;

		/* If we are looking for specific interfaces, ignore others. */
		if (tmp == NULL && ir)
			continue;

		ird = get_rdomain(ifa->ifa_name);
		if (*rdomain == -1)
			*rdomain = ird;
		else if (*rdomain != ird && ir)
			error("Interface %s is not in rdomain %d",
			    tmp->name, *rdomain);
		else if (*rdomain != ird && !ir)
			continue;

		/* If there isn't already an interface by this name,
		   allocate one. */
		if (tmp == NULL) {
			tmp = calloc(1, sizeof *tmp);
			if (!tmp)
				error("Insufficient memory to %s %s",
				    "record interface", ifa->ifa_name);
			strlcpy(tmp->name, ifa->ifa_name, sizeof(tmp->name));
			tmp->next = interfaces;
			tmp->noifmedia = tmp->dead = tmp->errors = 0;
			interfaces = tmp;
		}

		/* If we have the capability, extract link information
		   and record it in a linked list. */
		if (ifa->ifa_addr->sa_family == AF_LINK) {
			struct sockaddr_dl *foo =
			    ((struct sockaddr_dl *)(ifa->ifa_addr));
			tmp->index = foo->sdl_index;
			tmp->hw_address.hlen = foo->sdl_alen;
			tmp->hw_address.htype = HTYPE_ETHER; /* XXX */
			memcpy(tmp->hw_address.haddr,
			    LLADDR(foo), foo->sdl_alen);
		} else if (ifa->ifa_addr->sa_family == AF_INET) {
			struct iaddr addr;

			/* Get a pointer to the address... */
			memcpy(&foo, ifa->ifa_addr, sizeof(foo));

			/* We don't want the loopback interface. */
			if (foo.sin_addr.s_addr == htonl (INADDR_LOOPBACK))
				continue;

			/* If this is the first real IP address we've
			   found, keep a pointer to ifreq structure in
			   which we found it. */
			if (!tmp->ifp) {
				int len = (IFNAMSIZ + ifa->ifa_addr->sa_len);
				tif = (struct ifreq *)malloc(len);
				if (!tif)
					error("no space to remember ifp.");
				strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ);
				memcpy(&tif->ifr_addr, ifa->ifa_addr,
				    ifa->ifa_addr->sa_len);
				tmp->ifp = tif;
				tmp->primary_address = foo.sin_addr;
			}

			/* Grab the address... */
			addr.len = 4;
			memcpy(addr.iabuf, &foo.sin_addr.s_addr, addr.len);

			/* If there's a registered subnet for this address,
			   connect it together... */
			if ((subnet = find_subnet(addr))) {
				/* If this interface has multiple aliases
				   on the same subnet, ignore all but the
				   first we encounter. */
				if (!subnet->interface) {
					subnet->interface = tmp;
					subnet->interface_address = addr;
				} else if (subnet->interface != tmp) {
					warning("Multiple %s %s: %s %s",
					    "interfaces match the",
					    "same subnet",
					    subnet->interface->name,
					    tmp->name);
				}
				share = subnet->shared_network;
				if (tmp->shared_network &&
				    tmp->shared_network != share) {
					warning("Interface %s matches %s",
					    tmp->name,
					    "multiple shared networks");
				} else {
					tmp->shared_network = share;
				}

				if (!share->interface) {
					share->interface = tmp;
				} else if (share->interface != tmp) {
					warning("Multiple %s %s: %s %s",
					    "interfaces match the",
					    "same shared network",
					    share->interface->name,
					    tmp->name);
				}
			}
		}
	}

	/* Discard interfaces we can't listen on. */
	last = NULL;
	for (tmp = interfaces; tmp; tmp = next) {
		next = tmp->next;

		if (!tmp->ifp) {
			warning("Can't listen on %s - it has no IP address.",
			    tmp->name);
			/* Remove tmp from the list of interfaces. */
			if (!last)
				interfaces = interfaces->next;
			else
				last->next = tmp->next;
			continue;
		}

		memcpy(&foo, &tmp->ifp->ifr_addr, sizeof tmp->ifp->ifr_addr);

		if (!tmp->shared_network) {
			warning("Can't listen on %s - dhcpd.conf has no subnet "
			    "declaration for %s.", tmp->name,
			    inet_ntoa(foo.sin_addr));
			/* Remove tmp from the list of interfaces. */
			if (!last)
				interfaces = interfaces->next;
			else
				last->next = tmp->next;
			continue;
		}

		last = tmp;

		/* Find subnets that don't have valid interface addresses. */
		for (subnet = (tmp->shared_network ? tmp->shared_network->subnets :
		    NULL); subnet; subnet = subnet->next_sibling) {
			if (!subnet->interface_address.len) {
				/*
				 * Set the interface address for this subnet
				 * to the first address we found.
				 */
				subnet->interface_address.len = 4;
				memcpy(subnet->interface_address.iabuf,
				    &foo.sin_addr.s_addr, 4);
			}
		}

		/* Register the interface... */
		if_register_receive(tmp);
		if_register_send(tmp);
		note("Listening on %s (%s).", tmp->name,
		    inet_ntoa(foo.sin_addr));
	}

	if (interfaces == NULL)
		error("No interfaces to listen on.");

	/* Now register all the remaining interfaces as protocols. */
	for (tmp = interfaces; tmp; tmp = tmp->next)
		add_protocol(tmp->name, tmp->rfdesc, got_one, tmp);

	freeifaddrs(ifap);
}