Beispiel #1
0
/* ARGSUSED */
static void
rtsock_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg)
{
	struct ifslist *ifs;
	union {
		struct ifa_msghdr ifam;
		char buf[1024];
	} msg;
	uint16_t ifindex;
	struct lifreq lifr;
	char *fail;
	int msglen;
	DHCPSTATE oldstate;

	if ((msglen = read(fd, &msg, sizeof (msg))) <= 0)
		return;

	/*
	 * These are the messages that can identify a particular logical
	 * interface by local IP address.
	 */
	if (msg.ifam.ifam_type != RTM_DELADDR &&
	    msg.ifam.ifam_type != RTM_NEWADDR)
		return;

	/* Note that ifam_index is just 16 bits */
	ifindex = msg.ifam.ifam_index;

	for (ifs = lookup_ifs_by_uindex(ifindex, NULL);
	    ifs != NULL;
	    ifs = lookup_ifs_by_uindex(ifindex, ifs)) {

		/*
		 * The if_sock_ip_fd is set to a non-negative integer by
		 * configure_bound().  If it's negative, then DHCP doesn't
		 * think we're bound.
		 *
		 * For pre-bound interfaces, we want to check to see if the
		 * IFF_UP bit has been reported.  This means that DAD is
		 * complete.
		 */
		oldstate = ifs->if_state;
		if (ifs->if_sock_ip_fd == -1 &&
		    (oldstate != PRE_BOUND && oldstate != ADOPTING))
			continue;

		/*
		 * Since we cannot trust the flags reported by the routing
		 * socket (they're just 32 bits -- and thus never include
		 * IFF_DUPLICATE), and we can't trust the ifindex (it's only 16
		 * bits and also doesn't reflect the alias in use), we get
		 * flags on all matching interfaces, and go by that.
		 */
		(void) strlcpy(lifr.lifr_name, ifs->if_name,
		    sizeof (lifr.lifr_name));
		if (ioctl(ifs->if_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
			fail = "unable to retrieve interface flags on %s";
			lifr.lifr_flags = 0;
		} else if (!check_rtm_addr(&msg.ifam, msglen, ifs->if_addr)) {
			/*
			 * If the message is not about this logical interface,
			 * then just ignore it.
			 */
			continue;
		} else if (lifr.lifr_flags & IFF_DUPLICATE) {
			fail = "interface %s has duplicate address";
		} else {
			/*
			 * If we're now up and we were waiting for that, then
			 * kick off this interface.  DAD is done.
			 */
			if (lifr.lifr_flags & IFF_UP) {
				if (oldstate == PRE_BOUND ||
				    oldstate == ADOPTING)
					dhcp_bound_complete(ifs);
				if (oldstate == ADOPTING)
					dhcp_adopt_complete(ifs);
			}
			continue;
		}

		if (ifs->if_sock_ip_fd != -1) {
			(void) close(ifs->if_sock_ip_fd);
			ifs->if_sock_ip_fd = -1;
		}
		dhcpmsg(MSG_ERROR, fail, ifs->if_name);

		/*
		 * The binding has evidently failed, so it's as though it never
		 * happened.  We need to do switch back to PRE_BOUND state so
		 * that send_pkt_internal() uses DLPI instead of sockets.  Our
		 * logical interface has already been torn down by the kernel,
		 * and thus we can't send DHCPDECLINE by way of regular IP.
		 * (Unless we're adopting -- allow the grandparent to be
		 * handled as expected.)
		 */
		if (oldstate != ADOPTING)
			ifs->if_state = PRE_BOUND;

		if (ifs->if_ack->opts[CD_DHCP_TYPE] != NULL &&
		    (lifr.lifr_flags & IFF_DUPLICATE))
			send_decline(ifs, fail, &ifs->if_addr);

		ifs->if_bad_offers++;
		dhcp_restart(ifs);
	}
}
Beispiel #2
0
static void
accept_v6_message(dhcp_smach_t *dsmp, PKT_LIST *plp, const char *pname,
    uchar_t recv_type)
{
	const dhcpv6_option_t *d6o;
	uint_t olen;
	const char *estr, *msg;
	uint_t msglen;
	int status;

	/* Account for received and processed messages */
	dsmp->dsm_received++;

	/* We don't yet support Reconfigure at all. */
	if (recv_type == DHCPV6_MSG_RECONFIGURE) {
		dhcpmsg(MSG_VERBOSE, "accept_v6_message: ignored Reconfigure "
		    "on %s", dsmp->dsm_name);
		free_pkt_entry(plp);
		return;
	}

	/*
	 * All valid DHCPv6 messages must have our Client ID specified.
	 */
	d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_CLIENTID, &olen);
	olen -= sizeof (*d6o);
	if (d6o == NULL || olen != dsmp->dsm_cidlen ||
	    memcmp(d6o + 1, dsmp->dsm_cid, olen) != 0) {
		dhcpmsg(MSG_VERBOSE,
		    "accept_v6_message: discarded %s on %s: %s Client ID",
		    pname, dsmp->dsm_name, d6o == NULL ? "no" : "wrong");
		free_pkt_entry(plp);
		return;
	}

	/*
	 * All valid DHCPv6 messages must have a Server ID specified.
	 *
	 * If this is a Reply and it's not in response to Solicit, Confirm,
	 * Rebind, or Information-Request, then it must also match the Server
	 * ID we're expecting.
	 *
	 * For Reply in the Solicit, Confirm, Rebind, and Information-Request
	 * cases, the Server ID needs to be saved.  This is done inside of
	 * dhcp_bound().
	 */
	d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_SERVERID, &olen);
	if (d6o == NULL) {
		dhcpmsg(MSG_DEBUG,
		    "accept_v6_message: discarded %s on %s: no Server ID",
		    pname, dsmp->dsm_name);
		free_pkt_entry(plp);
		return;
	}
	if (recv_type == DHCPV6_MSG_REPLY && dsmp->dsm_state != SELECTING &&
	    dsmp->dsm_state != INIT_REBOOT && dsmp->dsm_state != REBINDING &&
	    dsmp->dsm_state != INFORM_SENT) {
		olen -= sizeof (*d6o);
		if (olen != dsmp->dsm_serveridlen ||
		    memcmp(d6o + 1, dsmp->dsm_serverid, olen) != 0) {
			dhcpmsg(MSG_DEBUG, "accept_v6_message: discarded %s on "
			    "%s: wrong Server ID", pname, dsmp->dsm_name);
			free_pkt_entry(plp);
			return;
		}
	}

	/*
	 * Break out of the switch if the input message needs to be discarded.
	 * Return from the function if the message has been enqueued or
	 * consumed.
	 */
	switch (dsmp->dsm_state) {
	case SELECTING:
		/* A Reply message signifies a Rapid-Commit. */
		if (recv_type == DHCPV6_MSG_REPLY) {
			if (dhcpv6_pkt_option(plp, NULL,
			    DHCPV6_OPT_RAPID_COMMIT, &olen) == NULL) {
				dhcpmsg(MSG_DEBUG, "accept_v6_message: Reply "
				    "on %s lacks Rapid-Commit; ignoring",
				    dsmp->dsm_name);
				break;
			}
			dhcpmsg(MSG_VERBOSE,
			    "accept_v6_message: rapid-commit Reply on %s",
			    dsmp->dsm_name);
			cancel_offer_timer(dsmp);
			goto rapid_commit;
		}

		/* Otherwise, we're looking for Advertisements. */
		if (recv_type != DHCPV6_MSG_ADVERTISE)
			break;

		/*
		 * Special case: if this advertisement has preference 255, then
		 * we must stop right now and select this server.
		 */
		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_PREFERENCE,
		    &olen);
		if (d6o != NULL && olen == sizeof (*d6o) + 1 &&
		    *(const uchar_t *)(d6o + 1) == 255) {
			pkt_smach_enqueue(dsmp, plp);
			dhcpmsg(MSG_DEBUG, "accept_v6_message: preference 255;"
			    " immediate Request on %s", dsmp->dsm_name);
			dhcp_requesting(NULL, dsmp);
		} else {
			pkt_smach_enqueue(dsmp, plp);
		}
		return;

	case PRE_BOUND:
	case BOUND:
		/*
		 * Not looking for anything in these states.  (If we
		 * implemented reconfigure, that might go here.)
		 */
		break;

	case REQUESTING:
	case INIT_REBOOT:
	case RENEWING:
	case REBINDING:
	case INFORM_SENT:
		/*
		 * We're looking for Reply messages.
		 */
		if (recv_type != DHCPV6_MSG_REPLY)
			break;
		dhcpmsg(MSG_VERBOSE,
		    "accept_v6_message: received Reply message on %s",
		    dsmp->dsm_name);
	rapid_commit:
		/*
		 * Extract the status code option.  If one is present and the
		 * request failed, then try to go to another advertisement in
		 * the list or restart the selection machinery.
		 */
		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
		    &olen);
		status = dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen);
		/*
		 * Check for the UseMulticast status code.  If this is present,
		 * and if we were actually using unicast, then drop back and
		 * try again.  If we weren't using unicast, then just pretend
		 * we never saw this message -- the peer is confused.  (TAHI
		 * does this.)
		 */
		if (status == DHCPV6_STAT_USEMCAST) {
			if (IN6_IS_ADDR_MULTICAST(
			    &dsmp->dsm_send_dest.v6.sin6_addr)) {
				break;
			} else {
				free_pkt_entry(plp);
				dsmp->dsm_send_dest.v6.sin6_addr =
				    ipv6_all_dhcp_relay_and_servers;
				retransmit_now(dsmp);
				return;
			}
		}
		print_server_msg(dsmp, msg, msglen);
		/*
		 * We treat NoBinding at the top level as "success."  Granted,
		 * this doesn't make much sense, but the TAHI test suite does
		 * this.  NoBinding really only makes sense in the context of a
		 * specific IA, as it refers to the GUID:IAID binding, so
		 * ignoring it at the top level is safe.
		 */
		if (status == DHCPV6_STAT_SUCCESS ||
		    status == DHCPV6_STAT_NOBINDING) {
			if (dhcp_bound(dsmp, plp)) {
				/*
				 * dhcp_bound will stop retransmission on
				 * success, if that's called for.
				 */
				server_unicast_option(dsmp, plp);
			} else {
				stop_pkt_retransmission(dsmp);
				dhcpmsg(MSG_WARNING, "accept_v6_message: "
				    "dhcp_bound failed for %s", dsmp->dsm_name);
				(void) remove_hostconf(dsmp->dsm_name,
				    dsmp->dsm_isv6);
				dhcp_restart(dsmp);
			}
		} else {
			dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
			    estr);
			stop_pkt_retransmission(dsmp);
			free_pkt_entry(plp);
			if (dsmp->dsm_state == INFORM_SENT) {
				(void) set_smach_state(dsmp, INIT);
				ipc_action_finish(dsmp, DHCP_IPC_E_SRVFAILED);
			} else {
				(void) remove_hostconf(dsmp->dsm_name,
				    dsmp->dsm_isv6);
				request_failed(dsmp);
			}
		}
		return;

	case DECLINING:
		/*
		 * We're looking for Reply messages.
		 */
		if (recv_type != DHCPV6_MSG_REPLY)
			break;
		stop_pkt_retransmission(dsmp);
		/*
		 * Extract the status code option.  Note that it's not a
		 * failure if the server reports an error.
		 */
		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
		    &olen);
		if (dhcpv6_status_code(d6o, olen, &estr, &msg,
		    &msglen) == DHCPV6_STAT_SUCCESS) {
			print_server_msg(dsmp, msg, msglen);
		} else {
			dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
			    estr);
		}
		free_pkt_entry(plp);
		if (dsmp->dsm_leases == NULL) {
			dhcpmsg(MSG_VERBOSE, "accept_v6_message: %s has no "
			    "leases left", dsmp->dsm_name);
			dhcp_restart(dsmp);
		} else if (dsmp->dsm_lif_wait == 0) {
			(void) set_smach_state(dsmp, BOUND);
		} else {
			(void) set_smach_state(dsmp, PRE_BOUND);
		}
		return;

	case RELEASING:
		/*
		 * We're looking for Reply messages.
		 */
		if (recv_type != DHCPV6_MSG_REPLY)
			break;
		stop_pkt_retransmission(dsmp);
		/*
		 * Extract the status code option.
		 */
		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
		    &olen);
		if (dhcpv6_status_code(d6o, olen, &estr, &msg,
		    &msglen) == DHCPV6_STAT_SUCCESS) {
			print_server_msg(dsmp, msg, msglen);
		} else {
			dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
			    estr);
		}
		free_pkt_entry(plp);
		finished_smach(dsmp, DHCP_IPC_SUCCESS);
		return;
	}

	/*
	 * Break from above switch means that the message must be discarded.
	 */
	dhcpmsg(MSG_VERBOSE,
	    "accept_v6_message: discarded v6 %s on %s; state %s",
	    pname, dsmp->dsm_name, dhcp_state_to_string(dsmp->dsm_state));
	free_pkt_entry(plp);
}
Beispiel #3
0
void
dhcp_requesting(iu_tq_t *tqp, void *arg)
{
	dhcp_smach_t		*dsmp = arg;
	dhcp_pkt_t		*dpkt;
	PKT_LIST		*offer;
	lease_t			lease;
	boolean_t		isv6 = dsmp->dsm_isv6;

	/*
	 * We assume here that if tqp is set, then this means we're being
	 * called back by the offer wait timer.  If so, then drop our hold
	 * on the state machine.  Otherwise, cancel the timer if it's running.
	 */
	if (tqp != NULL) {
		dhcpmsg(MSG_VERBOSE,
		    "dhcp_requesting: offer wait timer on v%d %s",
		    isv6 ? 6 : 4, dsmp->dsm_name);
		dsmp->dsm_offer_timer = -1;
		if (!verify_smach(dsmp))
			return;
	} else {
		cancel_offer_timer(dsmp);
	}

	/*
	 * select the best OFFER; all others pitched.
	 */

	offer = select_best(dsmp);
	if (offer == NULL) {

		dhcpmsg(MSG_VERBOSE,
		    "no OFFERs/Advertisements on %s, waiting...",
		    dsmp->dsm_name);

		/*
		 * no acceptable OFFERs have come in.  reschedule
		 * ourself for callback.
		 */

		if ((dsmp->dsm_offer_timer = iu_schedule_timer(tq,
		    dsmp->dsm_offer_wait, dhcp_requesting, dsmp)) == -1) {

			/*
			 * ugh.  the best we can do at this point is
			 * revert back to INIT and wait for a user to
			 * restart us.
			 */

			dhcpmsg(MSG_WARNING, "dhcp_requesting: cannot "
			    "reschedule callback, reverting to INIT state on "
			    "%s", dsmp->dsm_name);

			stop_pkt_retransmission(dsmp);
			(void) set_smach_state(dsmp, INIT);
			dsmp->dsm_dflags |= DHCP_IF_FAILED;
			ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
		} else {
			hold_smach(dsmp);
		}

		return;
	}

	/*
	 * With IPv4, the DHCPREQUEST packet we're about to transmit implicitly
	 * declines all other offers we've received.  We can no longer use any
	 * cached offers, so we must discard them now.  With DHCPv6, though,
	 * we're permitted to hang onto the advertisements (offers) and try
	 * them if the preferred one doesn't pan out.
	 */
	if (!isv6)
		free_pkt_list(&dsmp->dsm_recv_pkt_list);

	/* stop collecting packets. */

	stop_pkt_retransmission(dsmp);

	/*
	 * For IPv4, check to see whether we got an OFFER or a BOOTP packet.
	 * If we got a BOOTP packet, go to the BOUND state now.
	 */
	if (!isv6 && offer->opts[CD_DHCP_TYPE] == NULL) {
		free_pkt_list(&dsmp->dsm_recv_pkt_list);

		if (!set_smach_state(dsmp, REQUESTING)) {
			dhcp_restart(dsmp);
			return;
		}

		if (!dhcp_bound(dsmp, offer)) {
			dhcpmsg(MSG_WARNING, "dhcp_requesting: dhcp_bound "
			    "failed for %s", dsmp->dsm_name);
			dhcp_restart(dsmp);
			return;
		}

		return;
	}

	if (isv6) {
		const char *estr, *msg;
		const dhcpv6_option_t *d6o;
		uint_t olen, msglen;

		/* If there's a Status Code option, print the message */
		d6o = dhcpv6_pkt_option(offer, NULL, DHCPV6_OPT_STATUS_CODE,
		    &olen);
		(void) dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen);
		print_server_msg(dsmp, msg, msglen);

		/* Copy in the Server ID (guaranteed to be present now) */
		if (!save_server_id(dsmp, offer))
			goto failure;

		/*
		 * Determine how to send this message.  If the Advertisement
		 * (offer) has the unicast option, then use the address
		 * specified in the option.  Otherwise, send via multicast.
		 */
		server_unicast_option(dsmp, offer);

		send_v6_request(dsmp);
	} else {
		/* if we got a message from the server, display it. */
		if (offer->opts[CD_MESSAGE] != NULL) {
			print_server_msg(dsmp,
			    (char *)offer->opts[CD_MESSAGE]->value,
			    offer->opts[CD_MESSAGE]->len);
		}

		/*
		 * assemble a DHCPREQUEST, with the ciaddr field set to 0,
		 * since we got here from the INIT state.
		 */

		dpkt = init_pkt(dsmp, REQUEST);

		/*
		 * Grab the lease out of the OFFER; we know it's valid because
		 * select_best() already checked.  The max dhcp message size
		 * option is set to the interface max, minus the size of the
		 * udp and ip headers.
		 */

		(void) memcpy(&lease, offer->opts[CD_LEASE_TIME]->value,
		    sizeof (lease_t));

		(void) add_pkt_opt32(dpkt, CD_LEASE_TIME, lease);
		(void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE,
		    htons(dsmp->dsm_lif->lif_max - sizeof (struct udpiphdr)));
		(void) add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR,
		    offer->pkt->yiaddr.s_addr);
		(void) add_pkt_opt(dpkt, CD_SERVER_ID,
		    offer->opts[CD_SERVER_ID]->value,
		    offer->opts[CD_SERVER_ID]->len);

		if (class_id_len != 0) {
			(void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id,
			    class_id_len);
		}
		(void) add_pkt_prl(dpkt, dsmp);

		/*
		 * dsm_reqhost was set for this state machine in
		 * dhcp_selecting() if the DF_REQUEST_HOSTNAME option set and a
		 * host name was found
		 */
		if (dsmp->dsm_reqhost != NULL) {
			(void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
			    strlen(dsmp->dsm_reqhost));
		}
		(void) add_pkt_opt(dpkt, CD_END, NULL, 0);

		/*
		 * send out the REQUEST, trying retransmissions.  either a NAK
		 * or too many REQUEST attempts will revert us to SELECTING.
		 */

		if (!set_smach_state(dsmp, REQUESTING)) {
			dhcpmsg(MSG_ERROR, "dhcp_requesting: cannot switch to "
			    "REQUESTING state; reverting to INIT on %s",
			    dsmp->dsm_name);
			goto failure;
		}

		(void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST),
		    stop_requesting);
	}

	/* all done with the offer */
	free_pkt_entry(offer);

	return;

failure:
	dsmp->dsm_dflags |= DHCP_IF_FAILED;
	(void) set_smach_state(dsmp, INIT);
	ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
	free_pkt_list(&dsmp->dsm_recv_pkt_list);
}
Beispiel #4
0
static void
accept_v4_acknak(dhcp_smach_t *dsmp, PKT_LIST *plp)
{
	/* Account for received and processed messages */
	dsmp->dsm_received++;

	if (*plp->opts[CD_DHCP_TYPE]->value == ACK) {
		if (dsmp->dsm_state != INFORM_SENT &&
		    dsmp->dsm_state != INFORMATION &&
		    (plp->opts[CD_LEASE_TIME] == NULL ||
		    plp->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) {
			dhcpmsg(MSG_WARNING, "accept_v4_acknak: ACK packet on "
			    "%s missing mandatory lease option, ignored",
			    dsmp->dsm_name);
			dsmp->dsm_bad_offers++;
			free_pkt_entry(plp);
			return;
		}
		if ((dsmp->dsm_state == RENEWING ||
		    dsmp->dsm_state == REBINDING) &&
		    dsmp->dsm_leases->dl_lifs->lif_addr !=
		    plp->pkt->yiaddr.s_addr) {
			dhcpmsg(MSG_WARNING, "accept_v4_acknak: renewal ACK "
			    "packet has a different IP address (%s), ignored",
			    inet_ntoa(plp->pkt->yiaddr));
			dsmp->dsm_bad_offers++;
			free_pkt_entry(plp);
			return;
		}
	}

	/*
	 * looks good; cancel the retransmission timer and unregister
	 * the acknak handler. ACK to BOUND, NAK back to SELECTING.
	 */

	stop_pkt_retransmission(dsmp);

	if (*plp->opts[CD_DHCP_TYPE]->value == NAK) {
		char saddr[18];

		saddr[0] = '\0';
		if (plp->opts[CD_SERVER_ID] != NULL &&
		    plp->opts[CD_SERVER_ID]->len == sizeof (struct in_addr)) {
			struct in_addr	t_server;

			bcopy(plp->opts[CD_SERVER_ID]->value, &t_server,
			    plp->opts[CD_SERVER_ID]->len);
			(void) strlcpy(saddr, inet_ntoa(t_server),
			    sizeof (saddr));
		}

		dhcpmsg(MSG_WARNING, "accept_v4_acknak: NAK on interface %s "
		    "from %s %s",
		    dsmp->dsm_name,
		    inet_ntoa(plp->pktfrom.v4.sin_addr), saddr);

		dsmp->dsm_bad_offers++;
		free_pkt_entry(plp);
		dhcp_restart(dsmp);

		/*
		 * remove any bogus cached configuration we might have
		 * around (right now would only happen if we got here
		 * from INIT_REBOOT).
		 */

		(void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
		return;
	}

	if (plp->opts[CD_SERVER_ID] == NULL ||
	    plp->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
		dhcpmsg(MSG_ERROR, "accept_v4_acknak: ACK with no valid "
		    "server id on %s", dsmp->dsm_name);
		dsmp->dsm_bad_offers++;
		free_pkt_entry(plp);
		dhcp_restart(dsmp);
		return;
	}

	if (plp->opts[CD_MESSAGE] != NULL) {
		print_server_msg(dsmp, (char *)plp->opts[CD_MESSAGE]->value,
		    plp->opts[CD_MESSAGE]->len);
	}

	dhcpmsg(MSG_VERBOSE, "accept_v4_acknak: ACK on %s", dsmp->dsm_name);
	if (!dhcp_bound(dsmp, plp)) {
		dhcpmsg(MSG_WARNING, "accept_v4_acknak: dhcp_bound failed "
		    "for %s", dsmp->dsm_name);
		dhcp_restart(dsmp);
	}
}
Beispiel #5
0
/* ARGSUSED */
static void
rtsock_event(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg)
{
	dhcp_smach_t *dsmp, *dsmnext;
	union {
		struct ifa_msghdr ifam;
		struct if_msghdr ifm;
		char buf[1024];
	} msg;
	uint16_t ifindex;
	int msglen;
	boolean_t isv6;

	if ((msglen = read(fd, &msg, sizeof (msg))) <= 0)
		return;

	/* Note that the routing socket interface index is just 16 bits */
	if (msg.ifm.ifm_type == RTM_IFINFO) {
		ifindex = msg.ifm.ifm_index;
		isv6 = (msg.ifm.ifm_flags & IFF_IPV6) ? B_TRUE : B_FALSE;
	} else if (msg.ifam.ifam_type == RTM_DELADDR ||
	    msg.ifam.ifam_type == RTM_NEWADDR) {
		ifindex = msg.ifam.ifam_index;
		isv6 = is_rtm_v6(&msg.ifam, msglen);
	} else {
		return;
	}

	for (dsmp = lookup_smach_by_uindex(ifindex, NULL, isv6);
	    dsmp != NULL; dsmp = dsmnext) {
		DHCPSTATE oldstate;
		boolean_t lif_finished;
		boolean_t lease_removed;
		dhcp_lease_t *dlp, *dlnext;

		/*
		 * Note that script_start can call dhcp_drop directly, and
		 * that will do release_smach.
		 */
		dsmnext = lookup_smach_by_uindex(ifindex, dsmp, isv6);
		oldstate = dsmp->dsm_state;

		/*
		 * Ignore state machines that are currently processing drop or
		 * release; there is nothing more we can do for them.
		 */
		if (dsmp->dsm_droprelease)
			continue;

		/*
		 * Look for link up/down notifications.  These occur on a
		 * physical interface basis.
		 */
		if (msg.ifm.ifm_type == RTM_IFINFO) {
			process_link_up_down(dsmp->dsm_lif->lif_pif, &msg.ifm);
			continue;
		}

		/*
		 * Since we cannot trust the flags reported by the routing
		 * socket (they're just 32 bits -- and thus never include
		 * IFF_DUPLICATE), and we can't trust the ifindex (it's only 16
		 * bits and also doesn't reflect the alias in use), we get
		 * flags on all matching interfaces, and go by that.
		 */
		lif_finished = B_FALSE;
		lease_removed = B_FALSE;
		for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlnext) {
			dhcp_lif_t *lif, *lifnext;
			uint_t nlifs = dlp->dl_nlifs;

			dlnext = dlp->dl_next;
			for (lif = dlp->dl_lifs; lif != NULL && nlifs > 0;
			    lif = lifnext, nlifs--) {
				lifnext = lif->lif_next;
				if (check_lif(lif, &msg.ifam, msglen)) {
					dsmp->dsm_lif_wait--;
					lif_finished = B_TRUE;
				}
			}
			if (dlp->dl_nlifs == 0) {
				remove_lease(dlp);
				lease_removed = B_TRUE;
			}
		}

		if ((isv6 && !check_main_lif(dsmp, &msg.ifam, msglen)) ||
		    (!isv6 && !verify_lif(dsmp->dsm_lif))) {
			finished_smach(dsmp, DHCP_IPC_E_INVIF);
			continue;
		}

		/*
		 * Ignore this state machine if nothing interesting has
		 * happened.
		 */
		if (!lif_finished && dsmp->dsm_lif_down == 0 &&
		    (dsmp->dsm_leases != NULL || !lease_removed))
			continue;

		/*
		 * If we're still waiting for DAD to complete on some of the
		 * configured LIFs, then don't send a response.
		 */
		if (dsmp->dsm_lif_wait != 0) {
			dhcpmsg(MSG_VERBOSE, "rtsock_event: %s still has %d "
			    "LIFs waiting on DAD", dsmp->dsm_name,
			    dsmp->dsm_lif_wait);
			continue;
		}

		/*
		 * If we have some failed LIFs, then handle them now.  We'll
		 * remove them from the list.  Any leases that become empty are
		 * also removed as part of the decline-generation process.
		 */
		if (dsmp->dsm_lif_down != 0)
			send_declines(dsmp);

		if (dsmp->dsm_leases == NULL) {
			dsmp->dsm_bad_offers++;
			/*
			 * For DHCPv6, we'll process the restart once we're
			 * done sending Decline messages, because these are
			 * supposed to be acknowledged.  With DHCPv4, there's
			 * no acknowledgment for a DECLINE, so after sending
			 * it, we just restart right away.
			 */
			if (!dsmp->dsm_isv6) {
				dhcpmsg(MSG_VERBOSE, "rtsock_event: %s has no "
				    "LIFs left", dsmp->dsm_name);
				dhcp_restart(dsmp);
			}
		} else {
			/*
			 * If we're now up on at least some of the leases and
			 * we were waiting for that, then kick off the rest of
			 * configuration.  Lease validation and DAD are done.
			 */
			dhcpmsg(MSG_VERBOSE, "rtsock_event: all LIFs verified "
			    "on %s in %s state", dsmp->dsm_name,
			    dhcp_state_to_string(oldstate));
			if (oldstate == PRE_BOUND ||
			    oldstate == ADOPTING)
				dhcp_bound_complete(dsmp);
			if (oldstate == ADOPTING)
				dhcp_adopt_complete(dsmp);
		}
	}
}