/* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * This file contains the routines that manipulate Link Layer Profiles * (aka LLPs) and various support functions. This includes parsing and * updating of the /etc/nwam/llp file. * * The daemon maintains a list of llp_t structures that represent the * provided configuration information for a link. After the llp file * is read, entries are added to the LLP list for any known links * (identified by checking the interface list, which is based on the * v4 interfaces present after 'ifconfig -a plumb') which were not * represented in the llp file. These entries contain the default * "automatic" settings: plumb both IPv4 and IPv6, use DHCP on the * v4 interface, and accept router- and DHCPv6-assigned addresses on * the v6 interface. The default entries created by the daemon are * also added to the llp file. * * LLP priority is assigned based on two factors: the order within * the llp file, with earlier entries having higher priority; and * a preference for wired interfaces before wireless. Entries that * are added to the file by the daemon are added *after* any existing * entries; within the added block, wired entries are added before * wireless. Thus if the llp file is never modified externally, wired * will generally be ordered before wireless. However, if the * administrator creates the file with wireless entries before wired, * that priority order will be respected. * * The llp list is protected by the global llp_lock, which must be * pthread_mutex_lock()'d before reading or writing the list. Only the main * thread can write to the list; this allows the main thread to deal with read * access to structure pointers without holding locks and without the * complexity of reference counts. All other threads must hold llp_lock for * the duration of any read access to the data, and must not deal directly in * structure pointers. (A thread may also hold machine_lock to block the main * thread entirely in order to manipulate the data; such use is isolated to the * door interface.) * * Functions in this file have comments noting where the main thread alone is * the caller. These functions do not need to acquire the lock. * * If you hold both ifs_lock and llp_lock, you must take ifs_lock first. */ #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" /* Lock to protect the llp list. */ static pthread_mutex_t llp_lock = PTHREAD_MUTEX_INITIALIZER; /* Accessed only from main thread or with llp_lock held */ llp_t *link_layer_profile; static struct qelem llp_list; static llp_t *locked_llp; /* * Global variable to hold the highest priority. Need to use the atomic * integer arithmetic functions to update it. */ static uint32_t llp_highest_pri; static void print_llp_list(void); void initialize_llp(void) { llp_list.q_forw = llp_list.q_back = &llp_list; } char * llp_prnm(llp_t *llp) { if (llp == NULL) return ("null_llp"); else if (llp->llp_lname == NULL) return ("null_lname"); else return (llp->llp_lname); } /* * This function removes a given LLP from the global list and discards it. * Called only from the main thread. */ void llp_delete(llp_t *llp) { if (pthread_mutex_lock(&llp_lock) == 0) { if (llp == locked_llp) locked_llp = NULL; assert(llp != link_layer_profile); remque(&llp->llp_links); (void) pthread_mutex_unlock(&llp_lock); free(llp->llp_ipv6addrstr); free(llp->llp_ipv4addrstr); free(llp); } } static void llp_list_free(void) { int retv; llp_t *llp; locked_llp = NULL; if ((retv = pthread_mutex_lock(&llp_lock)) != 0) { /* Something very serious is wrong... */ syslog(LOG_ERR, "llp_list_free: cannot lock mutex: %s", strerror(retv)); return; } while (llp_list.q_forw != &llp_list) { llp = (llp_t *)llp_list.q_forw; remque(&llp->llp_links); free(llp->llp_ipv6addrstr); free(llp->llp_ipv4addrstr); free(llp); } (void) pthread_mutex_unlock(&llp_lock); } /* * Called either from main thread or with llp_lock held. */ llp_t * llp_lookup(const char *link) { llp_t *llp; if (link == NULL) return (NULL); for (llp = (llp_t *)llp_list.q_forw; llp != (llp_t *)&llp_list; llp = (llp_t *)llp->llp_links.q_forw) { if (strcmp(link, llp->llp_lname) == 0) break; } if (llp == (llp_t *)&llp_list) llp = NULL; return (llp); } /* * Choose the higher priority llp of the two passed in. If one is * NULL, the other will be higher priority. If both are NULL, NULL * is returned. * * Assumes that both are available (i.e. doesn't check IFF_RUNNING * or IF_DHCPFAILED flag values). */ llp_t * llp_high_pri(llp_t *a, llp_t *b) { if (a == NULL || a->llp_links.q_forw == NULL) return (b); else if (b == NULL || b->llp_links.q_forw == NULL) return (a); /* Check for locked LLP selection for user interface */ if (a == locked_llp) return (a); else if (b == locked_llp) return (b); if (a->llp_failed && !b->llp_failed) return (b); if (!a->llp_failed && b->llp_failed) return (a); /* * Higher priority is represented by a lower number. This seems a * bit backwards, but for now it makes assigning priorities very easy. */ return ((a->llp_pri <= b->llp_pri) ? a : b); } /* * Chooses the highest priority link that corresponds to an available * interface. Called only in the main thread. */ llp_t * llp_best_avail(void) { llp_t *llp, *rtnllp; if ((rtnllp = locked_llp) == NULL) { for (llp = (llp_t *)llp_list.q_forw; llp != (llp_t *)&llp_list; llp = (llp_t *)llp->llp_links.q_forw) { if (is_interface_ok(llp->llp_lname)) rtnllp = llp_high_pri(llp, rtnllp); } } return (rtnllp); } /* * Called only by the main thread. Note that this leaves link_layer_profile * set to NULL only in the case of abject failure, and then leaves llp_failed * set. */ static void llp_activate(llp_t *llp) { char *host; /* * Choosing "dhcp" as a hostname is unsupported right now. * We use hostname="dhcp" as a keyword telling bringupinterface() * to use dhcp on the interface. */ char *dhcpstr = "dhcp"; assert(link_layer_profile == NULL); host = (llp->llp_ipv4src == IPV4SRC_DHCP) ? dhcpstr : llp->llp_ipv4addrstr; report_llp_selected(llp->llp_lname); switch (bringupinterface(llp->llp_lname, host, llp->llp_ipv6addrstr, llp->llp_ipv6onlink)) { case SUCCESS: llp->llp_failed = B_FALSE; llp->llp_waiting = B_FALSE; link_layer_profile = llp; dprintf("llp_activate: activated llp for %s", llp_prnm(llp)); break; case FAILURE: llp->llp_failed = B_TRUE; llp->llp_waiting = B_FALSE; dprintf("llp_activate: failed to bring up %s", llp_prnm(llp)); report_llp_unselected(llp->llp_lname, dcFailed); link_layer_profile = NULL; break; case WAITING: llp->llp_failed = B_FALSE; llp->llp_waiting = B_TRUE; link_layer_profile = llp; dprintf("llp_activate: waiting for %s", llp_prnm(llp)); } } /* * Replace the currently active link layer profile with the one * specified. And since we're changing the lower layer stuff, * we need to first deactivate the current upper layer profile. * An upper layer profile will be reactivated later, when we get * confirmation that the new llp is fully up (has an address * assigned). * * If the new llp is the same as the currently active one, don't * do anything. * * Called only by the main thread. */ void llp_swap(llp_t *newllp, libnwam_diag_cause_t cause) { int minpri; if (newllp == link_layer_profile) return; deactivate_upper_layer_profile(); if (link_layer_profile != NULL) { dprintf("taking down current link layer profile (%s)", llp_prnm(link_layer_profile)); report_llp_unselected(link_layer_profile->llp_lname, cause); link_layer_profile->llp_waiting = B_FALSE; link_layer_profile = NULL; } /* * Establish the new link layer profile. If we have trouble setting * it, then try to get another. Note that llp_activate sets llp_failed * on failure, so this loop is guaranteed to terminate. */ while (newllp != NULL) { dprintf("bringing up new link layer profile (%s)", llp_prnm(newllp)); llp_activate(newllp); newllp = NULL; if (link_layer_profile == NULL && (newllp = llp_best_avail()) != NULL && newllp->llp_failed) newllp = NULL; } /* * Knock down all interfaces that are at a lower (higher-numbered) * priority than the new one. If there isn't a new one, then leave * everything as it is. */ if (link_layer_profile == NULL) { minpri = -1; if (locked_llp != NULL) dprintf("taking down all but %s", llp_prnm(locked_llp)); } else { minpri = link_layer_profile->llp_pri; dprintf("taking down remaining interfaces below priority %d", minpri); } for (newllp = (llp_t *)llp_list.q_forw; newllp != (llp_t *)&llp_list; newllp = (llp_t *)newllp->llp_links.q_forw) { if (newllp == link_layer_profile) continue; if ((link_layer_profile != NULL && newllp->llp_pri > minpri) || (locked_llp != NULL && newllp != locked_llp)) takedowninterface(newllp->llp_lname, cause); else clear_cached_address(newllp->llp_lname); } } /* * Create the named LLP with default settings. Called only in main thread. */ llp_t * llp_add(const char *name) { int retv; llp_t *llp; if ((llp = calloc(1, sizeof (llp_t))) == NULL) { syslog(LOG_ERR, "cannot allocate LLP: %m"); return (NULL); } if (strlcpy(llp->llp_lname, name, sizeof (llp->llp_lname)) >= sizeof (llp->llp_lname)) { syslog(LOG_ERR, "llp: link name '%s' too long; ignoring entry", name); free(llp); return (NULL); } llp->llp_fileorder = llp->llp_pri = atomic_add_32_nv(&llp_highest_pri, 1); llp->llp_ipv4src = IPV4SRC_DHCP; llp->llp_type = find_if_type(llp->llp_lname); llp->llp_ipv6onlink = B_TRUE; /* * should be a no-op, but for now, make sure we only * create llps for wired and wireless interfaces. */ if (llp->llp_type != IF_WIRED && llp->llp_type != IF_WIRELESS) { syslog(LOG_ERR, "llp: wrong type of interface for %s", name); free(llp); return (NULL); } if ((retv = pthread_mutex_lock(&llp_lock)) != 0) { /* Something very serious is wrong... */ syslog(LOG_ERR, "llp: cannot lock mutex: %s", strerror(retv)); free(llp); return (NULL); } insque(&llp->llp_links, llp_list.q_back); (void) pthread_mutex_unlock(&llp_lock); dprintf("created llp for link %s, priority %d", llp->llp_lname, llp->llp_pri); return (llp); } /* * Walker function to pass to walk_interface() to find out if * an interface description is missing from LLPFILE. If it is, * add it. * * Currently, IF_TUN type interfaces are special-cased: they are * only handled as user-enabled, layered links (which may be created * as part of a higher-layer profile, for example). Thus, they * shouldn't be considered when looking at the llp list, so don't * add them here. * * ifs_lock is held when this function is called. Called only in main thread. */ static void find_and_add_llp(struct interface *ifp, void *arg) { FILE *fp = arg; if (ifp->if_type != IF_TUN && llp_lookup(ifp->if_name) == NULL) { switch (ifp->if_family) { case AF_INET: (void) fprintf(fp, "%s\tdhcp\n", ifp->if_name); break; case AF_INET6: (void) fprintf(fp, "%s\tipv6\n", ifp->if_name); break; default: syslog(LOG_ERR, "interface %s family %d?!", ifp->if_name, ifp->if_family); return; } dprintf("Added %s to %s", ifp->if_name, LLPFILE); /* If we run out of memory, ignore this interface for now. */ (void) llp_add(ifp->if_name); } } static void print_llp_list(void) { llp_t *wp; dprintf("Walking llp list"); for (wp = (llp_t *)llp_list.q_forw; wp != (llp_t *)&llp_list; wp = (llp_t *)wp->llp_links.q_forw) dprintf("==> %s", wp->llp_lname); } /* * This function parses /etc/nwam/llp which describes the phase 0 link layer * profile. The file is line oriented with each line containing tab or space * delimited fields. Each address family (IPv4, IPv6) is described on a * separate line. * The first field is a link name. * The second field can be either static, dhcp, ipv6, noipv6, or priority. * If the second field is static then the next field is an ipv4 address which * can contain a prefix. Previous versions of this file could contain a * hostname in this field which is no longer supported. * If the second field is dhcp then dhcp will be used on the interface. * If the second field is ipv6 then an ipv6 interface is plumbed up. The * outcome of this is that if offered by the network in.ndpd and dhcp * will conspire to put addresses on additional ipv6 logical interfaces. * If the next field is non-null then it is taken to be an IPv6 address * and possible prefix which are applied to the interface. * If the second field is noipv6 then no ipv6 interfaces will be put on that * link. * If the second field is priority, then the next field is an integer * specifying the link priority. * * Called only in main thread. */ void llp_parse_config(void) { static const char STATICSTR[] = "static"; static const char DHCP[] = "dhcp"; static const char IPV6[] = "ipv6"; static const char NOIPV6[] = "noipv6"; static const char PRIORITY[] = "priority"; FILE *fp; char line[LINE_MAX]; char *cp, *lasts, *lstr, *srcstr, *addrstr; int lnum; llp_t *llp; /* Create the NWAM directory in case it does not exist. */ if (mkdir(LLPDIRNAME, LLPDIRMODE) != 0 && errno != EEXIST) { syslog(LOG_ERR, "could not create %s: %m", LLPDIRNAME); return; } fp = fopen(LLPFILE, "r+"); if (fp == NULL) { if (errno != ENOENT) { syslog(LOG_ERR, "open LLP config file: %m"); return; } if ((fp = fopen(LLPFILE, "w+")) == NULL) { syslog(LOG_ERR, "create LLP config file: %m"); return; } } llp_list_free(); for (lnum = 1; fgets(line, sizeof (line), fp) != NULL; lnum++) { if (line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = '\0'; cp = line; while (isspace(*cp)) cp++; if (*cp == '#' || *cp == '\0') continue; dprintf("parsing llp conf file line %d...", lnum); if (((lstr = strtok_r(cp, " \t", &lasts)) == NULL) || ((srcstr = strtok_r(NULL, " \t", &lasts)) == NULL)) { syslog(LOG_ERR, "llp:%d: not enough tokens; " "ignoring entry", lnum); continue; } if ((llp = llp_lookup(lstr)) == NULL && (llp = llp_add(lstr)) == NULL) { syslog(LOG_ERR, "llp:%d: cannot add entry", lnum); continue; } if (strcasecmp(srcstr, STATICSTR) == 0) { if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL || atoi(addrstr) == 0) { /* crude check for number */ syslog(LOG_ERR, "llp:%d: missing ipaddr " "for static config", lnum); } else if ((addrstr = strdup(addrstr)) == NULL) { syslog(LOG_ERR, "llp:%d: cannot save address", lnum); } else { free(llp->llp_ipv4addrstr); llp->llp_ipv4src = IPV4SRC_STATIC; llp->llp_ipv4addrstr = addrstr; } } else if (strcasecmp(srcstr, DHCP) == 0) { llp->llp_ipv4src = IPV4SRC_DHCP; } else if (strcasecmp(srcstr, IPV6) == 0) { llp->llp_ipv6onlink = B_TRUE; if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL) { (void) 0; } else if ((addrstr = strdup(addrstr)) == NULL) { syslog(LOG_ERR, "llp:%d: cannot save address", lnum); } else { free(llp->llp_ipv6addrstr); llp->llp_ipv6addrstr = addrstr; } } else if (strcasecmp(srcstr, NOIPV6) == 0) { llp->llp_ipv6onlink = B_FALSE; } else if (strcasecmp(srcstr, PRIORITY) == 0) { if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL) { syslog(LOG_ERR, "llp:%d: missing priority value", lnum); } else { llp->llp_pri = atoi(addrstr); } } else { syslog(LOG_ERR, "llp:%d: unrecognized field '%s'", lnum, srcstr); } } /* * So we have read in the llp file, is there an interface which * it does not describe? If yes, we'd better add it to the * file for future reference. */ walk_interface(find_and_add_llp, fp); (void) fclose(fp); print_llp_list(); } /* * Called only from the main thread. */ void llp_add_file(const llp_t *llp) { FILE *fp; if ((fp = fopen(LLPFILE, "a")) == NULL) return; (void) fprintf(fp, "%s\tdhcp\n", llp->llp_lname); (void) fclose(fp); } /* * This function rewrites the LLP configuration file entry for a given * interface and keyword. If the keyword is present, then it is updated if * removeonly is B_FALSE, otherwise it's removed. If the keyword is not * present, then it is added immediately after the last entry for that * interface if removeonly is B_FALSE, otherwise no action is taken. User * comments are preserved. * * To preserve file integrity, this is called only from the main thread. */ static void llp_update_config(const char *ifname, const char *keyword, const char *optval, boolean_t removeonly) { FILE *fpin, *fpout; char line[LINE_MAX]; char *cp, *lstr, *keystr, *valstr, *lasts; boolean_t matched_if, copying; long match_pos; if ((fpin = fopen(LLPFILE, "r")) == NULL) return; if ((fpout = fopen(LLPFILETMP, "w")) == NULL) { syslog(LOG_ERR, "create LLP temporary config file: %m"); (void) fclose(fpin); return; } matched_if = copying = B_FALSE; restart: while (fgets(line, sizeof (line), fpin) != NULL) { cp = line + strlen(line) - 1; if (cp >= line && *cp == '\n') *cp = '\0'; cp = line; while (isspace(*cp)) cp++; lstr = NULL; if (copying || *cp == '#' || (lstr = strtok_r(cp, " \t", &lasts)) == NULL || strcmp(lstr, ifname) != 0) { if (!matched_if || copying) { /* * It's ugly to write through the pointer * returned as the third argument of strtok_r, * but doing so saves a data copy. */ if (lstr != NULL && lasts != NULL) lasts[-1] = '\t'; (void) fprintf(fpout, "%s\n", line); } continue; } if (lasts != NULL) lasts[-1] = '\t'; /* * If we've found the keyword, then process removal or update * of the value. */ if ((keystr = strtok_r(NULL, " \t", &lasts)) != NULL && strcmp(keystr, keyword) == 0) { matched_if = copying = B_TRUE; if (removeonly) continue; valstr = strtok_r(NULL, " \t", &lasts); if ((valstr == NULL && optval == NULL) || (valstr != NULL && optval != NULL && strcmp(valstr, optval) == 0)) { /* Value identical; abort update */ goto no_change; } if (optval == NULL) { (void) fprintf(fpout, "%s\t%s\n", ifname, keyword); } else { (void) fprintf(fpout, "%s\t%s %s\n", ifname, keyword, optval); } continue; } /* Otherwise, record the last possible insertion point */ matched_if = B_TRUE; match_pos = ftell(fpin); if (lasts != NULL) lasts[-1] = '\t'; (void) fprintf(fpout, "%s\n", line); } if (!copying) { /* keyword not encountered; we're done if deleting */ if (removeonly) goto no_change; /* need to add keyword and value */ if (optval == NULL) { (void) fprintf(fpout, "%s\t%s\n", ifname, keyword); } else { (void) fprintf(fpout, "%s\t%s %s\n", ifname, keyword, optval); } /* copy the rest of the file */ (void) fseek(fpin, match_pos, SEEK_SET); copying = B_TRUE; goto restart; } (void) fclose(fpin); (void) fclose(fpout); if (rename(LLPFILETMP, LLPFILE) != 0) { syslog(LOG_ERR, "rename LLP temporary config file: %m"); (void) unlink(LLPFILETMP); } return; no_change: (void) fclose(fpin); (void) fclose(fpout); (void) unlink(LLPFILETMP); } /* * This is called back from the main thread by the state machine. */ void llp_write_changed_priority(llp_t *llp) { if (llp->llp_pri == llp->llp_fileorder) { llp_update_config(llp->llp_lname, "priority", NULL, B_TRUE); } else { char prival[32]; (void) snprintf(prival, sizeof (prival), "%d", llp->llp_pri); llp_update_config(llp->llp_lname, "priority", prival, B_FALSE); } } /* * Called by the door interface: set LLP priority and schedule an LLP update if * this interface has changed. */ int set_llp_priority(const char *ifname, int prio) { llp_t *llp; int retv; if (prio < 0) return (EINVAL); if ((retv = pthread_mutex_lock(&llp_lock)) != 0) return (retv); if ((llp = llp_lookup(ifname)) != NULL) { llp->llp_failed = B_FALSE; if (llp->llp_pri != prio) { llp->llp_pri = prio; (void) np_queue_add_event(EV_USER, ifname); } retv = 0; } else { retv = ENXIO; } (void) pthread_mutex_unlock(&llp_lock); return (retv); } /* * Called by the door interface: set a locked LLP and schedule an LLP update if * the locked LLP has changed. */ int set_locked_llp(const char *ifname) { llp_t *llp; int retv; if ((retv = pthread_mutex_lock(&llp_lock)) != 0) return (retv); if (ifname[0] == '\0') { if (locked_llp != NULL) { ifname = locked_llp->llp_lname; locked_llp = NULL; (void) np_queue_add_event(EV_USER, ifname); } } else if ((llp = llp_lookup(ifname)) != NULL) { locked_llp = llp; if (llp != link_layer_profile) (void) np_queue_add_event(EV_USER, ifname); } else { retv = ENXIO; } (void) pthread_mutex_unlock(&llp_lock); return (retv); } /* Copy string to pre-allocated buffer. */ static void strinsert(char **dest, const char *src, char **buf) { if (*dest != NULL) { *dest = strcpy(*buf, src); *buf += strlen(src) + 1; } } /* * Sample the list of LLPs and copy to a single buffer for return through the * door interface. */ llp_t * get_llp_list(size_t *lsize, uint_t *countp, char *selected, char *locked) { llp_t *llplist, *llpl, *llp; char *strptr; uint_t nllp; size_t strspace; int retv; *lsize = 0; if ((retv = pthread_mutex_lock(&llp_lock)) != 0) { errno = retv; return (NULL); } (void) strlcpy(selected, link_layer_profile == NULL ? "" : link_layer_profile->llp_lname, LIFNAMSIZ); (void) strlcpy(locked, locked_llp == NULL ? "" : locked_llp->llp_lname, LIFNAMSIZ); nllp = 0; strspace = 0; for (llp = (llp_t *)llp_list.q_forw; llp != (llp_t *)&llp_list; llp = (llp_t *)llp->llp_links.q_forw) { nllp++; if (llp->llp_ipv4addrstr != NULL) strspace += strlen(llp->llp_ipv4addrstr) + 1; if (llp->llp_ipv6addrstr != NULL) strspace += strlen(llp->llp_ipv6addrstr) + 1; } *countp = nllp; /* Note that malloc doesn't guarantee a NULL return for zero count */ llplist = nllp == 0 ? NULL : malloc(sizeof (*llplist) * nllp + strspace); if (llplist != NULL) { *lsize = sizeof (*llplist) * nllp + strspace; llpl = llplist; strptr = (char *)(llplist + nllp); for (llp = (llp_t *)llp_list.q_forw; llp != (llp_t *)&llp_list; llp = (llp_t *)llp->llp_links.q_forw) { *llpl = *llp; strinsert(&llpl->llp_ipv4addrstr, llp->llp_ipv4addrstr, &strptr); strinsert(&llpl->llp_ipv6addrstr, llp->llp_ipv6addrstr, &strptr); llpl++; } } (void) pthread_mutex_unlock(&llp_lock); /* Add in the special door-only state flags */ llpl = llplist; while (nllp-- > 0) { get_interface_state(llpl->llp_lname, &llpl->llp_dhcp_failed, &llpl->llp_link_up); if (llpl->llp_type == IF_WIRELESS) { get_wireless_state(llpl->llp_lname, &llpl->llp_need_wlan, &llpl->llp_need_key); } llpl++; } return (llplist); } /* * This is called for the special case when there are outstanding requests sent * to the user interface, and the user interface disappears. We handle this * case by re-running bringupinterface() without deselecting. That function * will call the wireless and DHCP-related parts again, and they should proceed * in automatic mode, because the UI is now gone. * * Called only by the main thread or by a thread holding machine_lock. */ void llp_reselect(void) { llp_t *llp; const char *host; /* * If there's no active profile, or if the active profile isn't waiting * on the UI, then just return; nothing to do. */ if ((llp = link_layer_profile) == NULL || !llp->llp_waiting) return; host = (llp->llp_ipv4src == IPV4SRC_DHCP) ? "dhcp" : llp->llp_ipv4addrstr; dprintf("llp_reselect: bringing up %s", llp_prnm(llp)); switch (bringupinterface(llp->llp_lname, host, llp->llp_ipv6addrstr, llp->llp_ipv6onlink)) { case SUCCESS: llp->llp_failed = B_FALSE; llp->llp_waiting = B_FALSE; dprintf("llp_reselect: activated llp for %s", llp_prnm(llp)); break; case FAILURE: llp->llp_failed = B_TRUE; llp->llp_waiting = B_FALSE; dprintf("llp_reselect: failed to bring up %s", llp_prnm(llp)); report_llp_unselected(llp->llp_lname, dcFailed); link_layer_profile = NULL; break; case WAITING: llp->llp_failed = B_FALSE; dprintf("llp_reselect: waiting for %s", llp_prnm(llp)); } } /* * This is used by the wireless module to check on the selected LLP. We don't * do periodic rescans if a wireless interface is current and if its connection * state is good. */ void llp_get_name_and_type(char *ifname, size_t ifnlen, libnwam_interface_type_t *iftype) { *ifname = '\0'; *iftype = IF_UNKNOWN; if (pthread_mutex_lock(&llp_lock) == 0) { if (link_layer_profile != NULL) { (void) strlcpy(ifname, link_layer_profile->llp_lname, ifnlen); *iftype = link_layer_profile->llp_type; } (void) pthread_mutex_unlock(&llp_lock); } } /* * This is called by the interface.c module to check if an interface needs to * run DHCP. It's intentionally called without ifs_lock held. */ libnwam_ipv4src_t llp_get_ipv4src(const char *ifname) { libnwam_ipv4src_t src = IPV4SRC_DHCP; llp_t *llp; if (pthread_mutex_lock(&llp_lock) == 0) { if ((llp = llp_lookup(ifname)) != NULL) src = llp->llp_ipv4src; (void) pthread_mutex_unlock(&llp_lock); } return (src); } /* * Dump out the LLP state via debug messages. */ void print_llp_status(void) { llp_t *llp; if (pthread_mutex_lock(&llp_lock) == 0) { if (link_layer_profile == NULL) dprintf("no LLP selected"); else dprintf("LLP %s selected", link_layer_profile->llp_lname); if (locked_llp == NULL) dprintf("no LLP locked"); else dprintf("LLP %s locked", locked_llp->llp_lname); for (llp = (llp_t *)llp_list.q_forw; llp != (llp_t *)&llp_list; llp = (llp_t *)llp->llp_links.q_forw) { dprintf("LLP %s pri %d file order %d type %d " "%sfailed %swaiting src %d v4addr %s v6addr %s " "v6 %son-link", llp->llp_lname, llp->llp_pri, llp->llp_fileorder, (int)llp->llp_type, llp->llp_failed ? "" : "not ", llp->llp_waiting ? "" : "not ", (int)llp->llp_ipv4src, STRING(llp->llp_ipv4addrstr), STRING(llp->llp_ipv6addrstr), llp->llp_ipv6onlink ? "not " : ""); } (void) pthread_mutex_unlock(&llp_lock); } }