/* 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); } }
/* 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); } } }