/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * This file contains the core logic of nwamd. * * This functionality is built around state_machine() which consumes an event. * The events which are input to this function are generated by either a change * in interface state or a signal. The events which correspond to signals are * either external requests to shutdown or a timer event. The interface events * indicate if an interface has acquired a new address (via DHCP) or if a new * interface has appeared on the system. The latter event is used to detect new * links. * * state_machine() calls high level routines in llp.c and interface.c to act on * the state of the machine in response to events. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "defines.h" #include "structures.h" #include "functions.h" #include "variables.h" void state_machine(struct np_event *e) { struct interface *evif; llp_t *evllp, *prefllp = NULL; uint64_t flags; boolean_t dhcp_restored = B_FALSE; dprintf("state_machine(event type: %s, name: %s)", npe_type_str(e->npe_type), STRING(e->npe_name)); switch (e->npe_type) { case EV_TIMER: /* Our timer popped; check our dhcp status. */ if ((evif = get_interface(e->npe_name)) == NULL) { dprintf("couldn't find waiting interface; " "ignoring EV_TIMER event"); break; } flags = get_ifflags(evif->if_name, evif->if_family); if ((flags & IFF_DHCPRUNNING) == 0 || (flags & IFF_UP) != 0) { /* * dhcp did come up successfully, or we're no * longer trying to do dhcp in this interface; * so no need to worry about the timer expiring. */ dprintf("timer popped for %s, but dhcp state is okay " "(ifflags 0x%llx)", evif->if_name, flags); break; } /* * dhcp has not yet completed; give up on it for * now and, if it is the currently active llp, * switch to the next best. */ dprintf("giving up on dhcp on %s (ifflags 0x%llx)", evif->if_name, flags); evif->if_lflags |= IF_DHCPFAILED; if (interface_is_active(evif)) { prefllp = llp_best_avail(); llp_swap(prefllp); } break; case EV_ROUTING: if ((evif = get_interface(e->npe_name)) == NULL || (evllp = llp_lookup(e->npe_name)) == NULL) { dprintf("state_machine: either no intf (%p) or no llp " "(%p) for %s; ignoring EV_ROUTING event", (void *)evif, (void *)evllp, STRING(e->npe_name)); break; } prefllp = llp_best_avail(); if (prefllp != link_layer_profile) { dprintf("state_machine: change in state of link %s " "resulted in new preferred llp: %s (was %s)", llp_prnm(evllp), llp_prnm(prefllp), llp_prnm(link_layer_profile)); llp_swap(prefllp); } break; case EV_NEWADDR: if ((evif = get_interface(e->npe_name)) == NULL || (evllp = llp_lookup(e->npe_name)) == NULL) { dprintf("state_machine: either no intf (%p) or no llp " "(%p) for %s; ignoring EV_NEWADDR event", (void *)evif, (void *)evllp, STRING(e->npe_name)); break; } flags = get_ifflags(evif->if_name, evif->if_family); if (!(flags & IFF_DHCPRUNNING) && !(flags & IFF_UP) && evllp->llp_ipv4src == IPV4SRC_DHCP) { evif->if_timer_expire = 0; if ((evif->if_lflags & IF_DHCPFAILED) != 0) { evif->if_lflags &= ~IF_DHCPFAILED; dhcp_restored = B_TRUE; } } if (evllp != link_layer_profile) { if (dhcp_restored && llp_high_pri(evllp, link_layer_profile) == evllp) { dprintf("state_machine: dhcp completed on " "higher priority llp (%s); swapping", llp_prnm(evllp)); llp_swap(evllp); } else { dprintf("state_machine: newaddr event was for " "%s, not for current active link (%s); " "taking down %s", evllp->llp_lname, llp_prnm(link_layer_profile), evllp->llp_lname); takedowninterface(evllp->llp_lname, evllp->llp_ipv4src == IPV4SRC_DHCP, B_FALSE, evllp->llp_ipv6onlink); break; } } /* * An address has been assigned to the current active link. * Notify the user, and activate the upper layer profile. * * Since other changes to the link (netmask change, broadcast * addr change, etc.) can cause a NEWADDR event (XXX would * be good if our event generator could do a better job * filtering!), only do this if there is not currently an * active ulp. */ if (!ulp_is_active()) { show_if_status(evllp->llp_lname); activate_upper_layer_profile(evllp->llp_ipv4src == IPV4SRC_DHCP, evllp->llp_lname); } break; case EV_SHUTDOWN: /* Cleanup not expecting to see any more events after this */ cleanup(); return; /* NOTREACHED */ default: dprintf("unknown event"); break; } } void cleanup(void) { if (link_layer_profile != NULL) { deactivate_upper_layer_profile(); takedowninterface(link_layer_profile->llp_lname, link_layer_profile->llp_ipv4src == IPV4SRC_DHCP, B_FALSE, link_layer_profile->llp_ipv6onlink); } /* * Since actions taken in nwamd result in dhcpagent being * launched, it's under our contract. Thus, it needs to be * stopped when our stop method is executed. But it needs * to stick around long enough for us to release any leases * we might have; thus, we don't want the stop method to * explicitly kill it. We do it here, when we know we've * finished any dhcp cleanup that needed to be done. */ dprintf("killing dhcpagent"); (void) start_child(PKILL, "-z", zonename, "dhcpagent", NULL); }