diff options
Diffstat (limited to 'usr/src/cmd')
44 files changed, 9454 insertions, 1824 deletions
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile index 01c00817c7..6402d952e2 100644 --- a/usr/src/cmd/Makefile +++ b/usr/src/cmd/Makefile @@ -21,7 +21,7 @@ # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -223,6 +223,7 @@ COMMON_SUBDIRS= \ lgrpinfo \ line \ link \ + dlmgmtd \ listen \ loadkeys \ locale \ @@ -800,6 +801,7 @@ MANIFEST_TOPDIRS= \ ipf \ keyserv \ ldapcachemgr \ + dlmgmtd \ nscd \ oplhpd \ power \ diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/functions.h b/usr/src/cmd/cmd-inet/lib/nwamd/functions.h index 96f4c50a63..bb88f4d2d9 100644 --- a/usr/src/cmd/cmd-inet/lib/nwamd/functions.h +++ b/usr/src/cmd/cmd-inet/lib/nwamd/functions.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -62,9 +62,9 @@ extern int lookup_count_property(const char *, const char *, uint64_t *); /* wireless.c: wifi link handling */ extern void init_mutexes(void); -extern boolean_t connect_chosen_lan(struct wireless_lan *, const char *); +extern boolean_t connect_chosen_lan(struct wireless_lan *, struct interface *); extern struct wireless_lan *prompt_for_visited(void); -extern boolean_t handle_wireless_lan(const char *); +extern boolean_t handle_wireless_lan(struct interface *); extern boolean_t scan_wireless_nets(struct interface *); extern void create_known_wifi_nets_file(void); extern void update_known_wifi_nets_file(const char *, const char *); diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/interface.c b/usr/src/cmd/cmd-inet/lib/nwamd/interface.c index b66dee45f5..304cb78467 100644 --- a/usr/src/cmd/cmd-inet/lib/nwamd/interface.c +++ b/usr/src/cmd/cmd-inet/lib/nwamd/interface.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -558,7 +558,7 @@ bringupinterface(const char *ifname, const char *host, const char *ipv6addr, } if (intf->if_type == IF_WIRELESS) { - if (!handle_wireless_lan(ifname)) { + if (!handle_wireless_lan(intf)) { syslog(LOG_INFO, "Could not connect to any WLAN, not " "bringing %s up", ifname); return (B_FALSE); @@ -631,8 +631,8 @@ takedowninterface(const char *ifname, boolean_t popup, boolean_t v6onlink) (void) start_child(IFCONFIG, ifname, "inet6", "unplumb", NULL); } - if (find_if_type(ifname) == IF_WIRELESS) - (void) dladm_wlan_disconnect(ifname); + if (ifp->if_type == IF_WIRELESS) + (void) dladm_wlan_disconnect(ifp->if_linkid); dprintf("takedown interface, free cached ip address"); if (ifp != NULL) { @@ -751,6 +751,7 @@ struct interface * add_interface(sa_family_t family, const char *name, uint64_t flags) { struct interface *i; + datalink_id_t linkid = DATALINK_INVALID_LINKID; enum interface_type iftype; if (name == NULL) @@ -801,6 +802,13 @@ add_interface(sa_family_t family, const char *name, uint64_t flags) i->if_lflags = 0; i->if_timer_expire = 0; + /* + * If linkid is DATALINK_INVALID_LINKID, it is an IP-layer only + * interface. + */ + (void) dladm_name2info(name, &linkid, NULL, NULL, NULL); + i->if_linkid = linkid; + dprintf("added interface %s of type %s af %d; is %savailable", i->if_name, if_type_str(i->if_type), i->if_family, ((i->if_type == IF_WIRELESS) || @@ -1077,7 +1085,7 @@ initialize_interfaces(void) wait_time = NWAM_IF_WAIT_DELTA_MAX; } - (void) dladm_init_linkprop(); + (void) dladm_init_linkprop(DATALINK_ALL_LINKID); (void) icfg_iterate_if(AF_INET, ICFG_PLUMBED, NULL, do_add_interface); @@ -1167,6 +1175,7 @@ check_interface_timer(struct interface *ifp, void *arg) enum interface_type find_if_type(const char *name) { + uint32_t media; enum interface_type type; if (name == NULL) { @@ -1174,20 +1183,19 @@ find_if_type(const char *name) return (IF_UNKNOWN); } - if (strncmp(name, "ip.tun", 6) == 0) { - /* - * We'll need to update our tunnel detection once - * clearview/uv and clearview/tun driver projects - * go back; tunnel names won't necessarily be ip.tunN - */ - type = IF_TUN; - } else { - /* - * We didn't recognize it. Try the libdladm function - * to decide if it is wireless or not; if not, assume - * that it's wired. - */ - type = dladm_wlan_is_valid(name) ? IF_WIRELESS : IF_WIRED; + type = IF_WIRED; + if (dladm_name2info(name, NULL, NULL, NULL, &media) != + DLADM_STATUS_OK) { + if (strncmp(name, "ip.tun", 6) == 0) { + /* + * We'll need to update our tunnel detection once + * clearview/uv and clearview/tun driver projects + * go back; tunnel names won't necessarily be ip.tunN + */ + type = IF_TUN; + } + } else if (media == DL_WIFI) { + type = IF_WIRELESS; } return (type); diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/structures.h b/usr/src/cmd/cmd-inet/lib/nwamd/structures.h index 0f232cd70b..caf0815d32 100644 --- a/usr/src/cmd/cmd-inet/lib/nwamd/structures.h +++ b/usr/src/cmd/cmd-inet/lib/nwamd/structures.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -89,6 +89,7 @@ struct np_event { */ struct interface { char *if_name; + datalink_id_t if_linkid; sa_family_t if_family; uint64_t if_flags; uint32_t if_lflags; diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c b/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c index 78cd0f4c74..f99d103dd3 100644 --- a/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c +++ b/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -152,10 +152,10 @@ static dladm_wlan_key_t *retrieve_key(const char *, const char *, static boolean_t add_wlan_entry(struct interface *, char *, char *, char *, dladm_wlan_secmode_t); static boolean_t already_in_visited_wlan_list(const struct wireless_lan *); -static boolean_t check_wlan(const char *, const char *); -static boolean_t connect_or_autoconf(struct wireless_lan *, const char *); +static boolean_t check_wlan(struct interface *, const char *); +static boolean_t connect_or_autoconf(struct wireless_lan *, struct interface *); static return_vals_t connect_to_new_wlan(const struct wireless_lan *, int, - const char *); + struct interface *); static boolean_t find_wlan_entry(struct interface *, char *, char *); static void free_wireless_lan(struct wireless_lan *); static struct wireless_lan *get_specific_lan(void); @@ -167,7 +167,7 @@ static void free_argv(char **); static char **build_wlanlist_zargv(const struct wireless_lan *, int, const char *, int, const char **, int); static char *get_zenity_response(char *const *); -static boolean_t wlan_autoconf(const char *ifname); +static boolean_t wlan_autoconf(struct interface *); static int zenity_height(int); static boolean_t get_scan_results(void *, dladm_wlan_attr_t *); static boolean_t known_wifi_nets_lookup(const char *, const char *, char *); @@ -438,20 +438,21 @@ clear_lan_entries(void) * to disassociate with it and then return false. */ static boolean_t -check_wlan(const char *intf, const char *exp_essid) +check_wlan(struct interface *intf, const char *exp_essid) { dladm_wlan_linkattr_t attr; dladm_status_t status; char cur_essid[DLADM_STRSIZE]; char errmsg[DLADM_STRSIZE]; - status = dladm_wlan_get_linkattr(intf, &attr); + status = dladm_wlan_get_linkattr(intf->if_linkid, &attr); if (status != DLADM_STATUS_OK) { - dprintf("check_wlan: dladm_wlan_get_linkattr() failed: %s", + dprintf("check_wlan: dladm_wlan_get_linkattr() for %s " + "failed: %s", intf->if_name, dladm_status2str(status, errmsg)); return (B_FALSE); } - if (attr.la_status == DLADM_WLAN_LINKSTATUS_DISCONNECTED) + if (attr.la_status == DLADM_WLAN_LINK_DISCONNECTED) return (B_FALSE); if (exp_essid == NULL) return (B_TRUE); @@ -462,8 +463,10 @@ check_wlan(const char *intf, const char *exp_essid) return (B_TRUE); /* Tell the driver to disassociate with the current AP. */ - if (dladm_wlan_disconnect(intf) != DLADM_STATUS_OK) - dprintf("check_wlan: dladm_wlan_disconnect() fails"); + if (dladm_wlan_disconnect(intf->if_linkid) != DLADM_STATUS_OK) { + dprintf("check_wlan: dladm_wlan_disconnect() for %s fails", + intf->if_name); + } return (B_FALSE); } @@ -505,7 +508,7 @@ scan_wireless_nets(struct interface *intf) * a lock in checking wireless_lan_used. */ num_ap = wireless_lan_used; - status = dladm_wlan_scan(intf->if_name, intf, get_scan_results); + status = dladm_wlan_scan(intf->if_linkid, intf, get_scan_results); if (status != DLADM_STATUS_OK) syslog(LOG_NOTICE, "cannot scan link '%s'", intf->if_name); else @@ -570,6 +573,8 @@ get_scan_results(void *arg, dladm_wlan_attr_t *attrp) void * periodic_wireless_scan(void *arg) { + datalink_id_t linkid; + /* * No periodic scan if the "-i" option is used to change the * interval to 0. @@ -601,13 +606,18 @@ periodic_wireless_scan(void *arg) */ if (ret == 0) { if (cur_llp != NULL) { - if (cur_llp->llp_type != IF_WIRELESS || - dladm_wlan_get_linkattr(cur_llp->llp_lname, - &attr) != DLADM_STATUS_OK) { + if (cur_llp->llp_type != IF_WIRELESS) + continue; + + if (dladm_name2info(cur_llp->llp_lname, &linkid, + NULL, NULL, NULL) != DLADM_STATUS_OK || + dladm_wlan_get_linkattr(linkid, &attr) != + DLADM_STATUS_OK) { continue; } + if (attr.la_status == - DLADM_WLAN_LINKSTATUS_CONNECTED && + DLADM_WLAN_LINK_CONNECTED && attr.la_wlan_attr.wa_strength > wireless_scan_level) { continue; @@ -771,7 +781,7 @@ store_key(struct wireless_lan *wlan) status = dladm_set_secobj(obj_name, class, obj_val, obj_len, - DLADM_OPT_CREATE | DLADM_OPT_PERSIST | DLADM_OPT_TEMP); + DLADM_OPT_CREATE | DLADM_OPT_PERSIST | DLADM_OPT_ACTIVE); if (status != DLADM_STATUS_OK) { syslog(LOG_ERR, "store_key: could not create secure object " "'%s' for key: %s", obj_name, @@ -826,7 +836,7 @@ retrieve_key(const char *essid, const char *bssid, dladm_secobj_class_t req) /* Try the kernel first, then fall back to persistent storage. */ status = dladm_get_secobj(cooked_key->wk_name, &class, cooked_key->wk_val, &cooked_key->wk_len, - DLADM_OPT_TEMP); + DLADM_OPT_ACTIVE); if (status != DLADM_STATUS_OK) { dprintf("retrieve_key: dladm_get_secobj(TEMP) failed: %s", dladm_status2str(status, errmsg)); @@ -1007,7 +1017,7 @@ known_wifi_nets_lookup(const char *new_essid, const char *new_bssid, * reqlan->bssid is optional (i.e., may be NULL) */ boolean_t -connect_chosen_lan(struct wireless_lan *reqlan, const char *ifname) +connect_chosen_lan(struct wireless_lan *reqlan, struct interface *intf) { uint_t keycount; dladm_wlan_key_t *key; @@ -1022,17 +1032,17 @@ connect_chosen_lan(struct wireless_lan *reqlan, const char *ifname) if (reqlan->essid == NULL) return (B_FALSE); dprintf("connect_chosen_lan(%s, %s, %s)", reqlan->essid, - STRING(reqlan->bssid), ifname); + STRING(reqlan->bssid), intf->if_name); /* If it is already connected to the required AP, just return. */ - if (check_wlan(ifname, reqlan->essid)) + if (check_wlan(intf, reqlan->essid)) return (B_TRUE); if (dladm_wlan_str2essid(reqlan->essid, &attr.wa_essid) != DLADM_STATUS_OK) { syslog(LOG_ERR, "connect_chosen_lan: invalid ESSID '%s' for '%s'", - reqlan->essid, ifname); + reqlan->essid, intf->if_name); return (B_FALSE); } attr.wa_valid = DLADM_WLAN_ATTR_ESSID; @@ -1041,7 +1051,7 @@ connect_chosen_lan(struct wireless_lan *reqlan, const char *ifname) DLADM_STATUS_OK) { syslog(LOG_ERR, "connect_chosen_lan: invalid BSSID '%s' for '%s'", - reqlan->bssid, ifname); + reqlan->bssid, intf->if_name); return (B_FALSE); } attr.wa_valid |= DLADM_WLAN_ATTR_BSSID; @@ -1068,8 +1078,8 @@ connect_chosen_lan(struct wireless_lan *reqlan, const char *ifname) * try a second time with just the ESSID. */ - status = dladm_wlan_connect(ifname, &attr, timeout, key, keycount, - flags); + status = dladm_wlan_connect(intf->if_linkid, &attr, timeout, key, + keycount, flags); dprintf("connect_chosen_lan: dladm_wlan_connect returned %s", dladm_status2str(status, errmsg)); if (status == DLADM_STATUS_TIMEDOUT && reqlan->bssid != NULL) { @@ -1078,13 +1088,14 @@ connect_chosen_lan(struct wireless_lan *reqlan, const char *ifname) reqlan->essid, reqlan->bssid, reqlan->essid); attr.wa_valid &= ~DLADM_WLAN_ATTR_BSSID; flags = 0; - status = dladm_wlan_connect(ifname, &attr, timeout, key, - keycount, flags); + status = dladm_wlan_connect(intf->if_linkid, &attr, timeout, + key, keycount, flags); } if (status != DLADM_STATUS_OK) { syslog(LOG_ERR, "connect_chosen_lan: connect to '%s' failed on '%s': %s", - reqlan->essid, ifname, dladm_status2str(status, errmsg)); + reqlan->essid, intf->if_name, + dladm_status2str(status, errmsg)); return (B_FALSE); } return (B_TRUE); @@ -1095,13 +1106,13 @@ connect_chosen_lan(struct wireless_lan *reqlan, const char *ifname) * If that fails, attempt to connect using autoconf. */ static boolean_t -connect_or_autoconf(struct wireless_lan *reqlan, const char *ifname) +connect_or_autoconf(struct wireless_lan *reqlan, struct interface *intf) { - if (!connect_chosen_lan(reqlan, ifname)) { + if (!connect_chosen_lan(reqlan, intf)) { syslog(LOG_WARNING, "Could not connect to chosen WLAN %s, going to auto-conf", reqlan->essid); - return (wlan_autoconf(ifname)); + return (wlan_autoconf(intf)); } return (B_TRUE); } @@ -1198,9 +1209,8 @@ build_wlanlist_zargv(const struct wireless_lan *lanlist, int nlans, static return_vals_t connect_to_new_wlan(const struct wireless_lan *lanlist, int nlans, - const char *ifname) + struct interface *intf) { - struct interface *intf; int i, dlist_cnt; int rtn; char **zargv; @@ -1209,19 +1219,13 @@ connect_to_new_wlan(const struct wireless_lan *lanlist, int nlans, struct wireless_lan *dlist, *reqlan; boolean_t autoconf = B_FALSE; - dprintf("connect_to_new_wlan(..., %d, %s)", nlans, ifname); + dprintf("connect_to_new_wlan(..., %d, %s)", nlans, intf->if_name); if (nlans == 0) { display(gettext("No Wifi networks found; continuing in case " "you know of any which do not broadcast.")); } - if ((intf = get_interface(ifname)) == NULL) { - dprintf("connect_to_new_wlan: cannot find wireless interface: " - "%s", ifname); - return (FAILURE); - } - /* build list of wlans to be displayed */ if ((dlist = calloc(nlans, sizeof (struct wireless_lan))) == NULL) return (FAILURE); @@ -1236,10 +1240,10 @@ connect_to_new_wlan(const struct wireless_lan *lanlist, int nlans, /* * Only use the interface which finds the AP to connect to it. */ - if (strcmp(lanlist[i].wl_if_name, ifname) != 0) { + if (strcmp(lanlist[i].wl_if_name, intf->if_name) != 0) { dprintf("connect_to_new_wlan: wrong interface (%s) for " - "%s (should be %s)", ifname, lanlist[i].essid, - lanlist[i].wl_if_name); + "%s (should be %s)", intf->if_name, + lanlist[i].essid, lanlist[i].wl_if_name); continue; } @@ -1279,7 +1283,7 @@ connect_to_new_wlan(const struct wireless_lan *lanlist, int nlans, if ((reqlan == NULL) || (reqlan->essid == NULL)) { dprintf("did not get user preference; attempting autoconf"); - rtn = wlan_autoconf(ifname) ? SUCCESS : FAILURE; + rtn = wlan_autoconf(intf) ? SUCCESS : FAILURE; goto cleanup; } dprintf("get_user_preference() returned essid %s, bssid %s, encr %s", @@ -1295,7 +1299,7 @@ connect_to_new_wlan(const struct wireless_lan *lanlist, int nlans, * now attempt to connect to selection, backing * off to autoconf if the connect fails */ - if (connect_chosen_lan(reqlan, ifname)) { + if (connect_chosen_lan(reqlan, intf)) { /* * Succeeded, so add entry to known_essid_list_file; * but first make sure the reqlan->bssid isn't empty. @@ -1305,7 +1309,8 @@ connect_to_new_wlan(const struct wireless_lan *lanlist, int nlans, dladm_wlan_linkattr_t attr; char bssid[DLADM_STRSIZE]; - status = dladm_wlan_get_linkattr(ifname, &attr); + status = dladm_wlan_get_linkattr(intf->if_linkid, + &attr); if (status == DLADM_STATUS_OK) { (void) dladm_wlan_bssid2str( @@ -1326,7 +1331,7 @@ connect_to_new_wlan(const struct wireless_lan *lanlist, int nlans, free_wireless_lan(reqlan); if (autoconf) - rtn = wlan_autoconf(ifname) ? SUCCESS : FAILURE; + rtn = wlan_autoconf(intf) ? SUCCESS : FAILURE; else rtn = SUCCESS; @@ -1391,7 +1396,7 @@ prompt_for_visited(void) } static boolean_t -wlan_autoconf(const char *ifname) +wlan_autoconf(struct interface *intf) { dladm_status_t status; boolean_t autoconf; @@ -1402,7 +1407,7 @@ wlan_autoconf(const char *ifname) } /* If the NIC is already associated with something, just return. */ - if (check_wlan(ifname, NULL)) + if (check_wlan(intf, NULL)) return (B_TRUE); /* @@ -1410,14 +1415,14 @@ wlan_autoconf(const char *ifname) * to cycle through WLANs detected in priority order, attempting * to connect. */ - status = dladm_wlan_connect(ifname, NULL, + status = dladm_wlan_connect(intf->if_linkid, NULL, DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT, NULL, 0, 0); if (status != DLADM_STATUS_OK) { char errmsg[DLADM_STRSIZE]; syslog(LOG_ERR, "wlan_autoconf: dladm_wlan_connect failed for '%s': %s", - ifname, dladm_status2str(status, errmsg)); + intf->if_name, dladm_status2str(status, errmsg)); return (B_FALSE); } return (B_TRUE); @@ -1720,7 +1725,7 @@ cleanup: * B_FALSE if we were unable to connect to anything */ boolean_t -handle_wireless_lan(const char *ifname) +handle_wireless_lan(struct interface *intf) { const struct wireless_lan *cur_wlans; int i, num_wlans; @@ -1839,7 +1844,7 @@ start_over: if (strength < strongest) goto connect_any; } - result = connect_or_autoconf(target, ifname); + result = connect_or_autoconf(target, intf); connect_result = result ? SUCCESS : FAILURE; } else if (visited_wlan_list->total > 1) { @@ -1848,21 +1853,21 @@ start_over: * prompt user for which one should we connect to */ if ((req_conf = prompt_for_visited()) != NULL) { - result = connect_or_autoconf(req_conf, ifname); + result = connect_or_autoconf(req_conf, intf); connect_result = result ? SUCCESS : FAILURE; } else { /* * The user didn't make a choice; offer the full list. */ connect_result = connect_to_new_wlan(cur_wlans, - num_wlans, ifname); + num_wlans, intf); result = (connect_result == SUCCESS); } } else { connect_any: /* last case, no previously visited wlan found */ connect_result = connect_to_new_wlan(cur_wlans, num_wlans, - ifname); + intf); result = (connect_result == SUCCESS); } diff --git a/usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c b/usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c index 1a964f0ce2..b9a02b54e7 100644 --- a/usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c +++ b/usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -2632,9 +2632,17 @@ if_report(mib_item_t *item, char *matchname, continue; /* 'for' loop 2a */ new_ifindex = if_nametoindex(logintname); + /* + * First lookup the "link" kstats in + * case the link is renamed. Then + * fallback to the legacy kstats for + * those non-GLDv3 links. + */ if (new_ifindex != ifindex_v4 && - (ksp = kstat_lookup(kc, NULL, -1, - ifname)) != NULL) { + (((ksp = kstat_lookup(kc, "link", 0, + ifname)) != NULL) || + ((ksp = kstat_lookup(kc, NULL, -1, + ifname)) != NULL))) { (void) safe_kstat_read(kc, ksp, NULL); stat.ipackets = @@ -2774,11 +2782,20 @@ if_report(mib_item_t *item, char *matchname, continue; } - ksp = kstat_lookup(kc, NULL, -1, buf); - if (ksp && - ksp->ks_type == KSTAT_TYPE_NAMED) + /* + * First lookup the "link" kstats in + * case the link is renamed. Then + * fallback to the legacy kstats for + * those non-GLDv3 links. + */ + if (((ksp = kstat_lookup(kc, "link", + 0, buf)) != NULL || + (ksp = kstat_lookup(kc, NULL, -1, + buf)) != NULL) && (ksp->ks_type == + KSTAT_TYPE_NAMED)) { (void) safe_kstat_read(kc, ksp, NULL); + } t.ipackets = kstat_named_value(ksp, "ipackets"); @@ -2926,9 +2943,18 @@ if_report(mib_item_t *item, char *matchname, continue; /* 'for' loop 2d */ new_ifindex = if_nametoindex(logintname); + + /* + * First lookup the "link" kstats in + * case the link is renamed. Then + * fallback to the legacy kstats for + * those non-GLDv3 links. + */ if (new_ifindex != ifindex_v6 && + ((ksp = kstat_lookup(kc, "link", 0, + ifname)) != NULL || (ksp = kstat_lookup(kc, NULL, -1, - ifname)) != NULL) { + ifname)) != NULL)) { (void) safe_kstat_read(kc, ksp, NULL); stat.ipackets = @@ -3071,11 +3097,20 @@ if_report(mib_item_t *item, char *matchname, continue; } - ksp = kstat_lookup(kc, NULL, -1, buf); - if (ksp && ksp->ks_type == - KSTAT_TYPE_NAMED) + /* + * First lookup the "link" kstats in + * case the link is renamed. Then + * fallback to the legacy kstats for + * those non-GLDv3 links. + */ + if (((ksp = kstat_lookup(kc, "link", + 0, buf)) != NULL || + (ksp = kstat_lookup(kc, NULL, -1, + buf)) != NULL) && (ksp->ks_type == + KSTAT_TYPE_NAMED)) { (void) safe_kstat_read(kc, ksp, NULL); + } t.ipackets = kstat_named_value(ksp, "ipackets"); diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/driver.h b/usr/src/cmd/cmd-inet/usr.lib/wpad/driver.h index dc13c719ce..1169c82263 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/wpad/driver.h +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/driver.h @@ -1,5 +1,5 @@ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -14,6 +14,7 @@ #pragma ident "%Z%%M% %I% %E% SMI" #include <libdlwlan.h> +#include <libdllink.h> #ifdef __cplusplus extern "C" { @@ -25,15 +26,15 @@ typedef enum { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP, typedef enum { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE } wpa_key_mgmt; struct wpa_driver_ops { - int (*get_bssid)(const char *, char *); - int (*get_ssid)(const char *ifname, char *); - int (*set_wpa)(const char *, boolean_t); - int (*set_key)(const char *, wpa_alg, uint8_t *, + int (*get_bssid)(datalink_id_t, char *); + int (*get_ssid)(datalink_id_t, char *); + int (*set_wpa)(datalink_id_t, boolean_t); + int (*set_key)(datalink_id_t, wpa_alg, uint8_t *, int, boolean_t, uint8_t *, uint32_t, uint8_t *, uint32_t); - int (*scan)(const char *); - int (*get_scan_results)(const char *, dladm_wlan_ess_t *, uint32_t); - int (*disassociate)(const char *, int); - int (*associate)(const char *, const char *, uint8_t *, uint32_t); + int (*scan)(datalink_id_t); + int (*get_scan_results)(datalink_id_t, dladm_wlan_ess_t *, uint32_t); + int (*disassociate)(datalink_id_t, int); + int (*associate)(datalink_id_t, const char *, uint8_t *, uint32_t); }; #ifdef __cplusplus diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/driver_wifi.c b/usr/src/cmd/cmd-inet/usr.lib/wpad/driver_wifi.c index 4ab82a2669..01ca5cea5e 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/wpad/driver_wifi.c +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/driver_wifi.c @@ -1,5 +1,5 @@ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -28,7 +28,7 @@ /* * get_bssid - get the current BSSID - * @ifname: interface name, e.g., wlan0 + * @linkid: linkid of the given interface * @bssid: buffer for BSSID (IEEE80211_ADDR_LEN = 6 bytes) * * Returns: 0 on success, -1 on failure @@ -38,14 +38,14 @@ * associated. */ int -wpa_driver_wifi_get_bssid(const char *ifname, char *bssid) +wpa_driver_wifi_get_bssid(datalink_id_t linkid, char *bssid) { - int ret; + dladm_status_t status; dladm_wlan_linkattr_t attr; dladm_wlan_attr_t *wl_attrp; - ret = dladm_wlan_get_linkattr(ifname, &attr); - if (ret != DLADM_STATUS_OK) + status = dladm_wlan_get_linkattr(linkid, &attr); + if (status != DLADM_STATUS_OK) return (-1); wl_attrp = &attr.la_wlan_attr; @@ -58,12 +58,12 @@ wpa_driver_wifi_get_bssid(const char *ifname, char *bssid) wpa_printf(MSG_DEBUG, "wpa_driver_wifi_get_bssid: " MACSTR, MAC2STR((unsigned char *)bssid)); - return (WPA_STATUS(ret)); + return (WPA_STATUS(status)); } /* * get_ssid - get the current SSID - * @ifname: interface name, e.g., wlan0 + * @linkid: linkid of the given interface * @ssid: buffer for SSID (at least 32 bytes) * * Returns: length of the SSID on success, -1 on failure @@ -72,14 +72,15 @@ wpa_driver_wifi_get_bssid(const char *ifname, char *bssid) * Returning zero is recommended if the STA is not associated. */ int -wpa_driver_wifi_get_ssid(const char *ifname, char *ssid) +wpa_driver_wifi_get_ssid(datalink_id_t linkid, char *ssid) { int ret; + dladm_status_t status; dladm_wlan_linkattr_t attr; dladm_wlan_attr_t *wl_attrp; - ret = dladm_wlan_get_linkattr(ifname, &attr); - if (ret != DLADM_STATUS_OK) + status = dladm_wlan_get_linkattr(linkid, &attr); + if (status != DLADM_STATUS_OK) return (-1); wl_attrp = &attr.la_wlan_attr; @@ -97,20 +98,20 @@ wpa_driver_wifi_get_ssid(const char *ifname, char *ssid) } static int -wpa_driver_wifi_set_wpa_ie(const char *ifname, - uint8_t *wpa_ie, uint32_t wpa_ie_len) +wpa_driver_wifi_set_wpa_ie(datalink_id_t linkid, uint8_t *wpa_ie, + uint32_t wpa_ie_len) { - int ret; + dladm_status_t status; wpa_printf(MSG_DEBUG, "%s", "wpa_driver_wifi_set_wpa_ie"); - ret = dladm_wlan_wpa_set_ie(ifname, wpa_ie, wpa_ie_len); + status = dladm_wlan_wpa_set_ie(linkid, wpa_ie, wpa_ie_len); - return (WPA_STATUS(ret)); + return (WPA_STATUS(status)); } /* * set_wpa - enable/disable WPA support - * @ifname: interface name, e.g., wlan0 + * @linkid: linkid of the given interface * @enabled: 1 = enable, 0 = disable * * Returns: 0 on success, -1 on failure @@ -122,38 +123,38 @@ wpa_driver_wifi_set_wpa_ie(const char *ifname, * allow wpa_supplicant to control roaming). */ static int -wpa_driver_wifi_set_wpa(const char *ifname, boolean_t enabled) +wpa_driver_wifi_set_wpa(datalink_id_t linkid, boolean_t enabled) { - int ret; + dladm_status_t status; wpa_printf(MSG_DEBUG, "wpa_driver_wifi_set_wpa: enable=%d", enabled); - if (!enabled && wpa_driver_wifi_set_wpa_ie(ifname, NULL, 0) < 0) + if (!enabled && wpa_driver_wifi_set_wpa_ie(linkid, NULL, 0) < 0) return (-1); - ret = dladm_wlan_wpa_set_wpa(ifname, enabled); + status = dladm_wlan_wpa_set_wpa(linkid, enabled); - return (WPA_STATUS(ret)); + return (WPA_STATUS(status)); } static int -wpa_driver_wifi_del_key(const char *ifname, int key_idx, unsigned char *addr) +wpa_driver_wifi_del_key(datalink_id_t linkid, int key_idx, unsigned char *addr) { - int ret; + dladm_status_t status; dladm_wlan_bssid_t bss; wpa_printf(MSG_DEBUG, "%s: id=%d", "wpa_driver_wifi_del_key", key_idx); (void) memcpy(bss.wb_bytes, addr, DLADM_WLAN_BSSID_LEN); - ret = dladm_wlan_wpa_del_key(ifname, key_idx, &bss); + status = dladm_wlan_wpa_del_key(linkid, key_idx, &bss); - return (WPA_STATUS(ret)); + return (WPA_STATUS(status)); } /* * set_key - configure encryption key - * @ifname: interface name, e.g., wlan0 + * @linkid: linkid of the given interface * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, * %WPA_ALG_TKIP, %WPA_ALG_CCMP); %WPA_ALG_NONE clears the key. * @addr: address of the peer STA or ff:ff:ff:ff:ff:ff for @@ -185,19 +186,18 @@ wpa_driver_wifi_del_key(const char *ifname, int key_idx, unsigned char *addr) * configuration. */ static int -wpa_driver_wifi_set_key(const char *ifname, wpa_alg alg, - unsigned char *addr, int key_idx, - boolean_t set_tx, uint8_t *seq, uint32_t seq_len, - uint8_t *key, uint32_t key_len) +wpa_driver_wifi_set_key(datalink_id_t linkid, wpa_alg alg, + unsigned char *addr, int key_idx, boolean_t set_tx, uint8_t *seq, + uint32_t seq_len, uint8_t *key, uint32_t key_len) { char *alg_name; dladm_wlan_cipher_t cipher; dladm_wlan_bssid_t bss; - int ret; + dladm_status_t status; wpa_printf(MSG_DEBUG, "%s", "wpa_driver_wifi_set_key"); if (alg == WPA_ALG_NONE) - return (wpa_driver_wifi_del_key(ifname, key_idx, addr)); + return (wpa_driver_wifi_del_key(linkid, key_idx, addr)); switch (alg) { case WPA_ALG_WEP: @@ -230,36 +230,36 @@ wpa_driver_wifi_set_key(const char *ifname, wpa_alg alg, } (void) memcpy(bss.wb_bytes, addr, DLADM_WLAN_BSSID_LEN); - ret = dladm_wlan_wpa_set_key(ifname, cipher, &bss, set_tx, + status = dladm_wlan_wpa_set_key(linkid, cipher, &bss, set_tx, *(uint64_t *)seq, key_idx, key, key_len); - return (WPA_STATUS(ret)); + return (WPA_STATUS(status)); } /* * disassociate - request driver to disassociate - * @ifname: interface name, e.g., wlan0 + * @linkid: linkid of the given interface * @reason_code: 16-bit reason code to be sent in the disassociation * frame * * Return: 0 on success, -1 on failure */ static int -wpa_driver_wifi_disassociate(const char *ifname, int reason_code) +wpa_driver_wifi_disassociate(datalink_id_t linkid, int reason_code) { - int ret; + dladm_status_t status; wpa_printf(MSG_DEBUG, "wpa_driver_wifi_disassociate"); - ret = dladm_wlan_wpa_set_mlme(ifname, DLADM_WLAN_MLME_DISASSOC, + status = dladm_wlan_wpa_set_mlme(linkid, DLADM_WLAN_MLME_DISASSOC, reason_code, NULL); - return (WPA_STATUS(ret)); + return (WPA_STATUS(status)); } /* * associate - request driver to associate - * @ifname: interface name, e.g., wlan0 + * @linkid: linkid of the given interface * @bssid: BSSID of the selected AP * @wpa_ie: WPA information element to be included in (Re)Association * Request (including information element id and length). Use of @@ -277,10 +277,10 @@ wpa_driver_wifi_disassociate(const char *ifname, int reason_code) * Return: 0 on success, -1 on failure */ static int -wpa_driver_wifi_associate(const char *ifname, const char *bssid, +wpa_driver_wifi_associate(datalink_id_t linkid, const char *bssid, uint8_t *wpa_ie, uint32_t wpa_ie_len) { - int ret; + dladm_status_t status; dladm_wlan_bssid_t bss; wpa_printf(MSG_DEBUG, "wpa_driver_wifi_associate : " @@ -291,19 +291,19 @@ wpa_driver_wifi_associate(const char *ifname, const char *bssid, * this is implied by the bssid which is used to locate * the scanned node state which holds it. */ - if (wpa_driver_wifi_set_wpa_ie(ifname, wpa_ie, wpa_ie_len) < 0) + if (wpa_driver_wifi_set_wpa_ie(linkid, wpa_ie, wpa_ie_len) < 0) return (-1); (void) memcpy(bss.wb_bytes, bssid, DLADM_WLAN_BSSID_LEN); - ret = dladm_wlan_wpa_set_mlme(ifname, DLADM_WLAN_MLME_ASSOC, + status = dladm_wlan_wpa_set_mlme(linkid, DLADM_WLAN_MLME_ASSOC, 0, &bss); - return (WPA_STATUS(ret)); + return (WPA_STATUS(status)); } /* * scan - request the driver to initiate scan - * @ifname: interface name, e.g., wlan0 + * @linkid: linkid of the given interface * * Return: 0 on success, -1 on failure * @@ -312,9 +312,9 @@ wpa_driver_wifi_associate(const char *ifname, const char *bssid, * results with wpa_driver_get_scan_results(). */ static int -wpa_driver_wifi_scan(const char *ifname) +wpa_driver_wifi_scan(datalink_id_t linkid) { - int ret; + dladm_status_t status; wpa_printf(MSG_DEBUG, "%s", "wpa_driver_wifi_scan"); /* @@ -322,18 +322,18 @@ wpa_driver_wifi_scan(const char *ifname) * to get ieee80211_begin_scan called. We really want to scan w/o * altering the current state but that's not possible right now. */ - (void) wpa_driver_wifi_disassociate(ifname, + (void) wpa_driver_wifi_disassociate(linkid, DLADM_WLAN_REASON_DISASSOC_LEAVING); - ret = dladm_wlan_scan(ifname, NULL, NULL); + status = dladm_wlan_scan(linkid, NULL, NULL); wpa_printf(MSG_DEBUG, "%s: return", "wpa_driver_wifi_scan"); - return (WPA_STATUS(ret)); + return (WPA_STATUS(status)); } /* * get_scan_results - fetch the latest scan results - * @ifname: interface name, e.g., wlan0 + * @linkid: linkid of the given interface * @results: pointer to buffer for scan results * @max_size: maximum number of entries (buffer size) * @@ -344,15 +344,15 @@ wpa_driver_wifi_scan(const char *ifname) * buffer. */ int -wpa_driver_wifi_get_scan_results(const char *ifname, +wpa_driver_wifi_get_scan_results(datalink_id_t linkid, dladm_wlan_ess_t *results, uint32_t max_size) { uint_t ret; - wpa_printf(MSG_DEBUG, "%s: interface name =%s max size=%d\n", - "wpa_driver_wifi_get_scan_results", ifname, max_size); + wpa_printf(MSG_DEBUG, "%s: max size=%d\n", + "wpa_driver_wifi_get_scan_results", max_size); - if (dladm_wlan_wpa_get_sr(ifname, results, max_size, &ret) + if (dladm_wlan_wpa_get_sr(linkid, results, max_size, &ret) != DLADM_STATUS_OK) { return (-1); } diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa.c b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa.c index 7a7fbed6a8..999e80cfb1 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa.c +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa.c @@ -1,5 +1,5 @@ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -760,13 +760,13 @@ wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s) uint8_t bssid[IEEE80211_ADDR_LEN]; (void) memset(ssid, 0, MAX_ESSID_LENGTH); - ssid_len = wpa_s->driver->get_ssid(wpa_s->ifname, (char *)ssid); + ssid_len = wpa_s->driver->get_ssid(wpa_s->linkid, (char *)ssid); if (ssid_len < 0) { wpa_printf(MSG_WARNING, "Could not read SSID from driver."); return (NULL); } - if (wpa_s->driver->get_bssid(wpa_s->ifname, (char *)bssid) < 0) { + if (wpa_s->driver->get_bssid(wpa_s->linkid, (char *)bssid) < 0) { wpa_printf(MSG_WARNING, "Could not read BSSID from driver."); return (NULL); } @@ -814,7 +814,7 @@ wpa_supplicant_key_request(struct wpa_supplicant *wpa_s, else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; - if (wpa_s->driver->get_bssid(wpa_s->ifname, (char *)bssid) < 0) { + if (wpa_s->driver->get_bssid(wpa_s->linkid, (char *)bssid) < 0) { wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key " "request"); return; @@ -838,7 +838,7 @@ wpa_supplicant_key_request(struct wpa_supplicant *wpa_s, reply = (struct wpa_eapol_key *)(hdr + 1); reply->type = wpa_s->proto == WPA_PROTO_RSN ? - EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info = WPA_KEY_INFO_REQUEST | ver; if (wpa_s->ptk_set) key_info |= WPA_KEY_INFO_MIC; @@ -849,7 +849,7 @@ wpa_supplicant_key_request(struct wpa_supplicant *wpa_s, reply->key_info = BE_16(key_info); reply->key_length = 0; (void) memcpy(reply->replay_counter, wpa_s->request_counter, - WPA_REPLAY_COUNTER_LEN); + WPA_REPLAY_COUNTER_LEN); inc_byte_array(wpa_s->request_counter, WPA_REPLAY_COUNTER_LEN); reply->key_data_length = BE_16(0); @@ -908,10 +908,10 @@ wpa_supplicant_process_1_of_4(struct wpa_supplicant *wpa_s, break; } if (pos[0] == GENERIC_INFO_ELEM && - pos + 1 + RSN_SELECTOR_LEN < end && - pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && - memcmp(pos + 2, RSN_KEY_DATA_PMKID, - RSN_SELECTOR_LEN) == 0) { + pos + 1 + RSN_SELECTOR_LEN < end && + pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && + memcmp(pos + 2, RSN_KEY_DATA_PMKID, + RSN_SELECTOR_LEN) == 0) { pmkid = pos + 2 + RSN_SELECTOR_LEN; wpa_hexdump(MSG_DEBUG, "RSN: PMKID from " "Authenticator", pmkid, PMKID_LEN); @@ -949,12 +949,11 @@ wpa_supplicant_process_1_of_4(struct wpa_supplicant *wpa_s, reply = (struct wpa_eapol_key *)(hdr + 1); reply->type = wpa_s->proto == WPA_PROTO_RSN ? - EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; - reply->key_info = BE_16(ver | WPA_KEY_INFO_KEY_TYPE | - WPA_KEY_INFO_MIC); + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + reply->key_info = BE_16(ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC); reply->key_length = key->key_length; (void) memcpy(reply->replay_counter, key->replay_counter, - WPA_REPLAY_COUNTER_LEN); + WPA_REPLAY_COUNTER_LEN); reply->key_data_length = BE_16(wpa_ie_len); (void) memcpy(reply + 1, wpa_ie, wpa_ie_len); @@ -986,7 +985,7 @@ wpa_supplicant_process_1_of_4(struct wpa_supplicant *wpa_s, (void) memcpy(ptk->u.auth.rx_mic_key, buf, 8); wpa_s->tptk_set = 1; wpa_eapol_key_mic(wpa_s->tptk.mic_key, ver, (uint8_t *)hdr, - rlen - sizeof (*ethhdr), reply->key_mic); + rlen - sizeof (*ethhdr), reply->key_mic); wpa_hexdump(MSG_DEBUG, "WPA: EAPOL-Key MIC", reply->key_mic, 16); wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4"); @@ -1080,23 +1079,21 @@ wpa_supplicant_process_3_of_4_gtk(struct wpa_supplicant *wpa_s, (void) memcpy(gtk + 24, tmpbuf, 8); } if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) { - if (wpa_s->driver->set_key(wpa_s->ifname, alg, + if (wpa_s->driver->set_key(wpa_s->linkid, alg, (uint8_t *)"\xff\xff\xff\xff\xff\xff", keyidx, 1, key->key_rsc, key_rsc_len, gtk, gtk_len) < 0) wpa_printf(MSG_WARNING, "WPA: Failed to set " "GTK to the driver (Group only)."); - } else if (wpa_s->driver->set_key(wpa_s->ifname, alg, - (uint8_t *)"\xff\xff\xff\xff\xff\xff", - keyidx, tx, - key->key_rsc, key_rsc_len, - gtk, gtk_len) < 0) { + } else if (wpa_s->driver->set_key(wpa_s->linkid, alg, + (uint8_t *)"\xff\xff\xff\xff\xff\xff", keyidx, tx, + key->key_rsc, key_rsc_len, gtk, gtk_len) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to " "the driver."); } wpa_printf(MSG_INFO, "WPA: Key negotiation completed with " - MACSTR, MAC2STR(src_addr)); + MACSTR, MAC2STR(src_addr)); eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); wpa_supplicant_cancel_auth_timeout(wpa_s); wpa_s->wpa_state = WPA_COMPLETED; @@ -1136,10 +1133,10 @@ wpa_supplicant_process_3_of_4(struct wpa_supplicant *wpa_s, ie = pos; ie_len = pos[1] + 2; } else if (pos[0] == GENERIC_INFO_ELEM && - pos + 1 + RSN_SELECTOR_LEN < end && - pos[1] > RSN_SELECTOR_LEN + 2 && - memcmp(pos + 2, RSN_KEY_DATA_GROUPKEY, - RSN_SELECTOR_LEN) == 0) { + pos + 1 + RSN_SELECTOR_LEN < end && + pos[1] > RSN_SELECTOR_LEN + 2 && + memcmp(pos + 2, RSN_KEY_DATA_GROUPKEY, + RSN_SELECTOR_LEN) == 0) { if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { wpa_printf(MSG_WARNING, "WPA: GTK IE " "in unencrypted key data"); @@ -1165,7 +1162,7 @@ wpa_supplicant_process_3_of_4(struct wpa_supplicant *wpa_s, if (wpa_s->ap_wpa_ie && (wpa_s->ap_wpa_ie_len != ie_len || - memcmp(wpa_s->ap_wpa_ie, ie, ie_len) != 0)) { + memcmp(wpa_s->ap_wpa_ie, ie, ie_len) != 0)) { wpa_printf(MSG_WARNING, "WPA: WPA IE in 3/4 msg does not match" " with WPA IE in Beacon/ProbeResp (src=" MACSTR ")", MAC2STR(src_addr)); @@ -1222,13 +1219,12 @@ wpa_supplicant_process_3_of_4(struct wpa_supplicant *wpa_s, reply = (struct wpa_eapol_key *)(hdr + 1); reply->type = wpa_s->proto == WPA_PROTO_RSN ? - EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; reply->key_info = BE_16(ver | WPA_KEY_INFO_KEY_TYPE | - WPA_KEY_INFO_MIC | - (key_info & WPA_KEY_INFO_SECURE)); + WPA_KEY_INFO_MIC | (key_info & WPA_KEY_INFO_SECURE)); reply->key_length = key->key_length; (void) memcpy(reply->replay_counter, key->replay_counter, - WPA_REPLAY_COUNTER_LEN); + WPA_REPLAY_COUNTER_LEN); reply->key_data_length = BE_16(0); @@ -1279,7 +1275,7 @@ wpa_supplicant_process_3_of_4(struct wpa_supplicant *wpa_s, wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen); } - if (wpa_s->driver->set_key(wpa_s->ifname, alg, src_addr, + if (wpa_s->driver->set_key(wpa_s->linkid, alg, src_addr, 0, 1, key_rsc, rsclen, (uint8_t *)&wpa_s->ptk.tk1, keylen) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set PTK to the" @@ -1342,9 +1338,9 @@ wpa_supplicant_process_1_of_2(struct wpa_supplicant *wpa_s, gtk_ie = pos + 2 + RSN_SELECTOR_LEN; gtk_ie_len = pos[1] - RSN_SELECTOR_LEN; break; - } else if (pos[0] == GENERIC_INFO_ELEM && - pos[1] == 0) + } else if (pos[0] == GENERIC_INFO_ELEM && pos[1] == 0) { break; + } pos += 2 + pos[1]; } @@ -1423,7 +1419,7 @@ wpa_supplicant_process_1_of_2(struct wpa_supplicant *wpa_s, (void) memcpy(gtk, gtk_ie + 2, gtk_ie_len - 2); } else { keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> - WPA_KEY_INFO_KEY_INDEX_SHIFT; + WPA_KEY_INFO_KEY_INDEX_SHIFT; if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) { (void) memcpy(ek, key->key_iv, 16); (void) memcpy(ek + 16, wpa_s->ptk.encr_key, 16); @@ -1436,7 +1432,7 @@ wpa_supplicant_process_1_of_2(struct wpa_supplicant *wpa_s, return; } if (aes_unwrap(wpa_s->ptk.encr_key, maxkeylen / 8, - (uint8_t *)(key + 1), gtk)) { + (uint8_t *)(key + 1), gtk)) { wpa_printf(MSG_WARNING, "WPA: AES unwrap " "failed - could not decrypt GTK"); return; @@ -1470,13 +1466,13 @@ wpa_supplicant_process_1_of_2(struct wpa_supplicant *wpa_s, (void) memcpy(gtk + 24, tmpbuf, 8); } if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) { - if (wpa_s->driver->set_key(wpa_s->ifname, alg, + if (wpa_s->driver->set_key(wpa_s->linkid, alg, (uint8_t *)"\xff\xff\xff\xff\xff\xff", keyidx, 1, key->key_rsc, key_rsc_len, gtk, keylen) < 0) wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the" " driver (Group only)."); - } else if (wpa_s->driver->set_key(wpa_s->ifname, alg, + } else if (wpa_s->driver->set_key(wpa_s->linkid, alg, (uint8_t *)"\xff\xff\xff\xff\xff\xff", keyidx, tx, key->key_rsc, key_rsc_len, @@ -1730,9 +1726,10 @@ wpa_sm_rx_eapol(struct wpa_supplicant *wpa_s, } if ((key_info & WPA_KEY_INFO_MIC) && - wpa_supplicant_verify_eapol_key_mic(wpa_s, key, ver, buf, - data_len)) + wpa_supplicant_verify_eapol_key_mic(wpa_s, key, ver, buf, + data_len)) { return; + } extra_len = data_len - sizeof (*hdr) - sizeof (*key); diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_impl.h b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_impl.h index e5e6f8f808..7745ec1c7f 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_impl.h +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_impl.h @@ -1,5 +1,5 @@ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -14,6 +14,7 @@ #pragma ident "%Z%%M% %I% %E% SMI" #include <net/wpa.h> +#include <libdladm.h> #ifdef __cplusplus extern "C" { @@ -164,7 +165,7 @@ struct wpa_supplicant { struct l2_packet_data *l2; unsigned char own_addr[IEEE80211_ADDR_LEN]; - char ifname[WPA_STRSIZE]; + datalink_id_t linkid; char kname[WPA_STRSIZE]; uint8_t pmk[PMK_LEN]; diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_supplicant.c b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_supplicant.c index 270b9133ed..06663383f7 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_supplicant.c +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/wpa_supplicant.c @@ -1,5 +1,5 @@ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -141,7 +141,7 @@ wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) wpa_printf(MSG_DEBUG, "Scan SSID: %s", ssid->ssid); } - if (wpa_s->driver->scan(wpa_s->ifname)) { + if (wpa_s->driver->scan(wpa_s->linkid)) { wpa_printf(MSG_WARNING, "Failed to initiate AP scan."); } } @@ -213,16 +213,16 @@ wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) static void wpa_clear_keys(struct wpa_supplicant *wpa_s, uint8_t *addr) { - wpa_s->driver->set_key(wpa_s->ifname, WPA_ALG_NONE, + wpa_s->driver->set_key(wpa_s->linkid, WPA_ALG_NONE, (uint8_t *)"\xff\xff\xff\xff\xff\xff", 0, 0, NULL, 0, NULL, 0); - wpa_s->driver->set_key(wpa_s->ifname, WPA_ALG_NONE, + wpa_s->driver->set_key(wpa_s->linkid, WPA_ALG_NONE, (uint8_t *)"\xff\xff\xff\xff\xff\xff", 1, 0, NULL, 0, NULL, 0); - wpa_s->driver->set_key(wpa_s->ifname, WPA_ALG_NONE, + wpa_s->driver->set_key(wpa_s->linkid, WPA_ALG_NONE, (uint8_t *)"\xff\xff\xff\xff\xff\xff", 2, 0, NULL, 0, NULL, 0); - wpa_s->driver->set_key(wpa_s->ifname, WPA_ALG_NONE, + wpa_s->driver->set_key(wpa_s->linkid, WPA_ALG_NONE, (uint8_t *)"\xff\xff\xff\xff\xff\xff", 3, 0, NULL, 0, NULL, 0); if (addr) { - wpa_s->driver->set_key(wpa_s->ifname, WPA_ALG_NONE, addr, + wpa_s->driver->set_key(wpa_s->linkid, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL, 0); } } @@ -357,7 +357,7 @@ static void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, wpa_clear_keys(wpa_s, bss->we_bssid.wb_bytes); wpa_s->wpa_state = WPA_ASSOCIATING; - wpa_s->driver->associate(wpa_s->ifname, + wpa_s->driver->associate(wpa_s->linkid, (const char *)bss->we_bssid.wb_bytes, wpa_ie, wpa_ie_len); /* Timeout for IEEE 802.11 authentication and association */ @@ -371,7 +371,7 @@ wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s, int reason_code) wpa_s->wpa_state = WPA_DISCONNECTED; if (memcmp(wpa_s->bssid, "\x00\x00\x00\x00\x00\x00", IEEE80211_ADDR_LEN) != 0) { - wpa_s->driver->disassociate(wpa_s->ifname, reason_code); + wpa_s->driver->disassociate(wpa_s->linkid, reason_code); addr = wpa_s->bssid; } wpa_clear_keys(wpa_s, addr); @@ -453,7 +453,7 @@ wpa_supplicant_scan_results(struct wpa_supplicant *wpa_s) struct wpa_ssid *ssid; (void) memset(results, 0, sizeof (dladm_wlan_ess_t) * MAX_SCANRESULTS); - num = wpa_s->driver->get_scan_results(wpa_s->ifname, results, + num = wpa_s->driver->get_scan_results(wpa_s->linkid, results, MAX_SCANRESULTS); wpa_printf(MSG_DEBUG, "Scan results: %d", num); if (num < 0) @@ -506,7 +506,7 @@ wpa_event_handler(void *cookie, wpa_event_type event) WPA_REPLAY_COUNTER_LEN); wpa_s->rx_replay_counter_set = 0; wpa_s->renew_snonce = 1; - if (wpa_s->driver->get_bssid(wpa_s->ifname, + if (wpa_s->driver->get_bssid(wpa_s->linkid, (char *)bssid) >= 0 && memcmp(bssid, wpa_s->bssid, IEEE80211_ADDR_LEN) != 0) { wpa_printf(MSG_DEBUG, "Associated to a new BSS: " @@ -550,9 +550,9 @@ wpa_supplicant_terminate(int sig, void *eloop_ctx, void *signal_ctx) } static int -wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s) +wpa_supplicant_driver_init(const char *link, struct wpa_supplicant *wpa_s) { - wpa_s->l2 = l2_packet_init(wpa_s->ifname, ETHERTYPE_EAPOL, + wpa_s->l2 = l2_packet_init(link, ETHERTYPE_EAPOL, wpa_supplicant_rx_eapol, wpa_s); if (wpa_s->l2 == NULL) return (-1); @@ -562,7 +562,7 @@ wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s) return (-1); } - if (wpa_s->driver->set_wpa(wpa_s->ifname, 1) < 0) { + if (wpa_s->driver->set_wpa(wpa_s->linkid, 1) < 0) { wpa_printf(MSG_ERROR, "Failed to enable WPA in the driver."); return (-1); } @@ -641,6 +641,9 @@ wpa_supplicant_door_destroy(char *doorname) { wpa_printf(MSG_DEBUG, "wpa_supplicant_door_destroy(%s)\n", doorname); + if (door_id == -1) + return; + if (door_revoke(door_id) == -1) { wpa_printf(MSG_ERROR, "failed to door_revoke(%d) %s, exiting.", door_id, strerror(errno)); @@ -701,13 +704,13 @@ wpa_config_read_network(struct wpa_supplicant *wpa_s) ssid->key_mgmt = WPA_KEY_MGMT_PSK; /* | WPA_KEY_MGMT_IEEE8021X; */ (void) memset(buf, 0, MAX_ESSID_LENGTH + 1); - wpa_s->driver->get_ssid(wpa_s->ifname, (char *)buf); + wpa_s->driver->get_ssid(wpa_s->linkid, (char *)buf); (void) wpa_config_parse_ssid(ssid, 0, buf); key_len = sizeof (psk); (void) dladm_get_secobj((const char *)wpa_s->kname, &cl, psk, &key_len, - DLADM_OPT_TEMP); + DLADM_OPT_ACTIVE); psk[key_len] = '\0'; ssid->passphrase = strdup((const char *)psk); @@ -760,9 +763,11 @@ wpa_config_free(struct wpa_config *config) { struct wpa_ssid *ssid = config->ssid; - free(ssid->ssid); - free(ssid->passphrase); - free(ssid); + if (ssid != NULL) { + free(ssid->ssid); + free(ssid->passphrase); + free(ssid); + } free(config); } @@ -798,13 +803,12 @@ static void usage(void) { (void) printf("%s\n\n" - "usage:\n" - " wpa_supplicant [-hv] -i<ifname> -k<keyname>" - "\n" - "options:\n" - " -h = show this help text\n" - " -v = show version\n", - wpa_supplicant_version); + "usage:\n" + " wpa_supplicant [-hv] -i<ifname> -k<keyname>\n" + "options:\n" + " -h = show this help text\n" + " -v = show version\n", + wpa_supplicant_version); } int @@ -813,12 +817,13 @@ main(int argc, char *argv[]) struct wpa_supplicant wpa_s; char *link = NULL; char *key = NULL; + dlpi_handle_t dh = NULL; + datalink_id_t linkid; + dladm_phys_attr_t dpa; int c; int exitcode; char door_file[WPA_STRSIZE]; - (void) memset(&wpa_s, 0, sizeof (wpa_s)); - for (;;) { c = getopt(argc, argv, "Dk:hi:v"); if (c < 0) @@ -845,8 +850,6 @@ main(int argc, char *argv[]) } } - wpa_s.driver = &wpa_driver_wifi_ops; - eloop_init(&wpa_s); /* * key name is required to retrieve PSK value through libwdladm APIs. * key is saved by dladm command by keyname @@ -857,22 +860,48 @@ main(int argc, char *argv[]) return (-1); } - if ((strlen(link) >= sizeof (wpa_s.ifname)) || - (strlen(key) >= sizeof (wpa_s.kname))) { - wpa_printf(MSG_ERROR, "Too long link/key name '%s', '%s'.", - link, key); + if ((strlen(key) >= sizeof (wpa_s.kname))) { + wpa_printf(MSG_ERROR, "Too long key name '%s'.", key); return (-1); } - (void) strlcpy(wpa_s.ifname, link, sizeof (wpa_s.ifname)); - (void) strlcpy(wpa_s.kname, key, sizeof (wpa_s.kname)); + if (daemon(0, 0)) + return (-1); /* - * Setup door file to communicate with driver - * Since this is multiple instance service, different instance - * has different doors. + * Hold this link open to prevent a link renaming operation. */ - (void) snprintf(door_file, WPA_STRSIZE, "%s_%s", WPA_DOOR, link); + if (dlpi_open(link, &dh, 0) != DLPI_SUCCESS) { + wpa_printf(MSG_ERROR, "Failed to open link '%s'.", link); + return (-1); + } + + if (dladm_name2info(link, &linkid, NULL, NULL, NULL) != + DLADM_STATUS_OK) { + wpa_printf(MSG_ERROR, "Invalid link name '%s'.", link); + dlpi_close(dh); + return (-1); + } + + /* + * Get the device name of the link, which will be used as the door + * file name used to communicate with the driver. Note that different + * links use different doors. + */ + if (dladm_phys_info(linkid, &dpa, DLADM_OPT_ACTIVE) != + DLADM_STATUS_OK) { + wpa_printf(MSG_ERROR, + "Failed to get device name of link '%s'.", link); + dlpi_close(dh); + return (-1); + } + (void) snprintf(door_file, WPA_STRSIZE, "%s_%s", WPA_DOOR, dpa.dp_dev); + + (void) memset(&wpa_s, 0, sizeof (wpa_s)); + wpa_s.driver = &wpa_driver_wifi_ops; + wpa_s.linkid = linkid; + (void) strlcpy(wpa_s.kname, key, sizeof (wpa_s.kname)); + eloop_init(&wpa_s); /* * Setup default WPA/WPA2 configuration @@ -881,16 +910,15 @@ main(int argc, char *argv[]) wpa_s.conf = wpa_config_read(&wpa_s); if (wpa_s.conf == NULL || wpa_s.conf->ssid == NULL) { wpa_printf(MSG_ERROR, "\nNo networks (SSID) configured.\n"); - return (-1); - } - - exitcode = 0; - - if (daemon(0, 0)) { exitcode = -1; goto cleanup; } + exitcode = 0; + + /* + * Setup door file to communicate with driver + */ if (wpa_supplicant_door_setup(&wpa_s, door_file) != 0) { wpa_printf(MSG_ERROR, "Failed to setup door(%s)", door_file); exitcode = -1; @@ -898,11 +926,18 @@ main(int argc, char *argv[]) } wpa_s.renew_snonce = 1; - if (wpa_supplicant_driver_init(&wpa_s) < 0) { + if (wpa_supplicant_driver_init(link, &wpa_s) < 0) { exitcode = -1; goto cleanup; } + /* + * This link is hold again in wpa_supplicant_driver_init(), so that + * we release the first reference. + */ + dlpi_close(dh); + dh = NULL; + wpa_printf(MSG_DEBUG, "=> eloop_run"); (void) eloop_register_signal(SIGINT, wpa_supplicant_terminate, NULL); @@ -914,14 +949,17 @@ main(int argc, char *argv[]) wpa_printf(MSG_DEBUG, "<= eloop_run()"); wpa_supplicant_disassociate(&wpa_s, REASON_DEAUTH_LEAVING); -cleanup: - if (wpa_s.driver->set_wpa(wpa_s.ifname, 0) < 0) { + if (wpa_s.driver->set_wpa(wpa_s.linkid, 0) < 0) { wpa_printf(MSG_ERROR, "Failed to disable WPA in the driver.\n"); } +cleanup: wpa_supplicant_door_destroy(door_file); wpa_supplicant_cleanup(&wpa_s); eloop_destroy(); + if (dh != NULL) + dlpi_close(dh); + return (exitcode); } diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/Makefile index 3011207150..4924d2fe4e 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/Makefile +++ b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/Makefile @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -39,7 +39,7 @@ COMMONSRCS= $(CMDINETCOMMONDIR)/$(COMMONOBJS:%.o=%.c) SRCS= $(LOCALSRCS) $(COMMONSRCS) CPPFLAGS += -I$(CMDINETCOMMONDIR) -I$(SRC)/common/net/dhcp -LDLIBS += -ldevinfo -ldhcpagent -linetcfg -ldlpi +LDLIBS += -ldhcpagent -linetcfg -ldlpi LINTFLAGS += -m ROOTUSRSBINLINKS = $(PROG:%=$(ROOTUSRSBIN)/%) diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/defs.h b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/defs.h index c40dac4648..c993baeb02 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/defs.h +++ b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/defs.h @@ -1,5 +1,5 @@ /* - * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* @@ -17,6 +17,7 @@ extern "C" { #endif +#include <errno.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> @@ -24,7 +25,6 @@ extern "C" { #include <ctype.h> #include <string.h> #include <syslog.h> -#include <libdevinfo.h> #include <zone.h> #include <sys/types.h> diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c index ed8ab462f5..877d275947 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c @@ -1,5 +1,5 @@ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* @@ -164,7 +164,7 @@ static void in6_configinfo(int force, uint64_t flags); /* * Misc support functions */ -static int devfs_entry(di_node_t node, di_minor_t minor, void *arg); +static boolean_t ni_entry(const char *, void *); static void foreachinterface(void (*func)(), int argc, char *argv[], int af, int64_t onflags, int64_t offflags, int64_t lifc_flags); @@ -490,7 +490,7 @@ foreachinterface(void (*func)(), int argc, char *argv[], int af, /* * Special case: * ifconfig -a plumb should find all network interfaces - * in the machine by traversing the devinfo tree for global zone. + * in the machine for the global zone. * For non-global zones, only find the assigned interfaces. * Also, there is no need to SIOCGLIF* ioctls, since * those interfaces have already been plumbed @@ -1755,8 +1755,7 @@ updownifs(iface_t *ifs, int up) * static int find_all_global_interfaces(struct lifconf *lifcp, char **buf, * int64_t lifc_flags) * - * It finds all interfaces for the global zone, that is all - * the physical interfaces, using the kernel's devinfo tree. + * It finds all data links for the global zone. * * It takes in input a pointer to struct lifconf to receive interfaces * informations, a **char to hold allocated buffer, and a lifc_flags. @@ -1771,23 +1770,10 @@ find_all_global_interfaces(struct lifconf *lifcp, char **buf, { unsigned bufsize; int n; - di_node_t root; ni_t *nip; struct lifreq *lifrp; - /* - * DINFOCACHE is equivalent to DINFOSUBTREE | DINFOMINOR | - * DINFOPROP | DINFOFORCE. - */ - if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) { - (void) fprintf(stderr, "ifconfig: di_init " - "failed; check the devinfo driver.\n"); - exit(1); - } - - (void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, - NULL, devfs_entry); - di_fini(root); + (void) dlpi_walk(ni_entry, NULL, 0); /* * Now, translate the linked list into @@ -4516,7 +4502,7 @@ strioctl(int s, int cmd, char *buf, int buflen) } static void -add_ni(char *name) +add_ni(const char *name) { ni_t **pp; ni_t *p; @@ -4545,35 +4531,18 @@ add_ni(char *name) } /* ARGSUSED2 */ -static int -devfs_entry(di_node_t node, di_minor_t minor, void *arg) +static boolean_t +ni_entry(const char *linkname, void *arg) { - char *provider; - char linkname[DLPI_LINKNAME_MAX]; dlpi_handle_t dh; - provider = di_minor_name(minor); - if (debug > 2) - (void) fprintf(stderr, "provider = %s\n", provider); - - if (dlpi_makelink(linkname, provider, - di_instance(node)) != DLPI_SUCCESS) - return (DI_WALK_CONTINUE); - if (dlpi_open(linkname, &dh, 0) != DLPI_SUCCESS) - return (DI_WALK_CONTINUE); + return (_B_FALSE); - if (di_minor_type(minor) == DDM_ALIAS) { - if (debug > 2) - (void) fprintf(stderr, "alias node, using instance\n"); - add_ni(linkname); - } else { - if (debug > 2) - (void) fprintf(stderr, "non-alias node, ignoring\n"); - } + add_ni(linkname); dlpi_close(dh); - return (DI_WALK_CONTINUE); + return (_B_FALSE); } /* diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/if.c b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/if.c index 2913f44669..455b4102a4 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/if.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/if.c @@ -1,5 +1,5 @@ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright (c) 1983, 1993 @@ -1902,7 +1902,12 @@ get_if_kstats(struct interface *ifp, struct phyi_data *newdata) if ((kc = kstat_open()) == NULL) return (-1); - if ((ksp = kstat_lookup(kc, NULL, -1, phyi->phyi_name)) == NULL) { + /* + * First we try to query the "link" kstats in case the link is renamed. + * If that fails, fallback to legacy ktats for those non-GLDv3 links. + */ + if (((ksp = kstat_lookup(kc, "link", 0, phyi->phyi_name)) == NULL) && + ((ksp = kstat_lookup(kc, NULL, -1, phyi->phyi_name)) == NULL)) { (void) kstat_close(kc); return (-1); } diff --git a/usr/src/cmd/devfsadm/devfsadm.c b/usr/src/cmd/devfsadm/devfsadm.c index a5bfeb5fbf..e2dfed2871 100644 --- a/usr/src/cmd/devfsadm/devfsadm.c +++ b/usr/src/cmd/devfsadm/devfsadm.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -7822,7 +7822,7 @@ process_rcm_events(void *arg) static int rcm_init(void) { -#define LIBRCM_PATH "/usr/lib/librcm.so" +#define LIBRCM_PATH "/lib/librcm.so" rcm_handle_t *hdl = NULL; int err; diff --git a/usr/src/cmd/dladm/Makefile b/usr/src/cmd/dladm/Makefile index 881a839ebb..94e6842ff3 100644 --- a/usr/src/cmd/dladm/Makefile +++ b/usr/src/cmd/dladm/Makefile @@ -19,14 +19,14 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" # PROG= dladm -CFGFILES= aggregation.conf linkprop.conf secobj.conf +CFGFILES= secobj.conf ROOTFS_PROG= $(PROG) ROOTCFGDIR= $(ROOTETC)/dladm @@ -35,12 +35,11 @@ ROOTCFGFILES= $(CFGFILES:%=$(ROOTCFGDIR)/%) include ../Makefile.cmd XGETFLAGS += -a -x $(PROG).xcl -LDLIBS += -ldladm -ldlpi -lkstat -lsecdb -lbsm -linetutil +LDLIBS += -ldladm -ldlpi -lkstat -lsecdb -lbsm -linetutil -ldevinfo $(ROOTCFGFILES) := OWNER= dladm $(ROOTCFGFILES) := GROUP= sys -$(ROOTCFGDIR)/aggregation.conf $(ROOTCFGDIR)/linkprop.conf := FILEMODE= 644 $(ROOTCFGDIR)/secobj.conf := FILEMODE= 600 .KEEP_STATE: diff --git a/usr/src/cmd/dladm/dladm.c b/usr/src/cmd/dladm/dladm.c index 17a3d53e6a..22cc5c515e 100644 --- a/usr/src/cmd/dladm/dladm.c +++ b/usr/src/cmd/dladm/dladm.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -34,6 +34,7 @@ #include <fcntl.h> #include <string.h> #include <stropts.h> +#include <sys/stat.h> #include <errno.h> #include <kstat.h> #include <strings.h> @@ -45,18 +46,24 @@ #include <auth_attr.h> #include <auth_list.h> #include <libintl.h> +#include <libdevinfo.h> #include <libdlpi.h> #include <libdllink.h> #include <libdlaggr.h> #include <libdlwlan.h> +#include <libdlvlan.h> +#include <libdlvnic.h> #include <libinetutil.h> #include <bsm/adt.h> #include <bsm/adt_event.h> -#define AGGR_DRV "aggr" -#define MAXPORT 256 -#define DUMP_LACP_FORMAT " %-9s %-8s %-7s %-12s " \ - "%-5s %-4s %-4s %-9s %-7s\n" +#define AGGR_DRV "aggr" +#define MAXPORT 256 +#define BUFLEN(lim, ptr) (((lim) > (ptr)) ? ((lim) - (ptr)) : 0) +#define MAXLINELEN 1024 +#define SMF_UPGRADE_FILE "/var/svc/profile/upgrade" +#define SMF_UPGRADEDATALINK_FILE "/var/svc/profile/upgrade_datalink" +#define SMF_DLADM_UPGRADE_MSG " # added by dladm(1M)" typedef struct pktsum_s { uint64_t ipackets; @@ -67,54 +74,56 @@ typedef struct pktsum_s { uint32_t oerrors; } pktsum_t; -typedef struct show_link_state { +typedef struct show_state { boolean_t ls_firstonly; boolean_t ls_donefirst; - boolean_t ls_stats; pktsum_t ls_prevstats; boolean_t ls_parseable; -} show_link_state_t; + uint32_t ls_flags; + dladm_status_t ls_status; +} show_state_t; typedef struct show_grp_state { - uint32_t gs_key; boolean_t gs_lacp; - boolean_t gs_found; + boolean_t gs_extended; boolean_t gs_stats; boolean_t gs_firstonly; + boolean_t gs_donefirst; pktsum_t gs_prevstats[MAXPORT]; boolean_t gs_parseable; + uint32_t gs_flags; + dladm_status_t gs_status; } show_grp_state_t; -typedef struct show_mac_state { - boolean_t ms_firstonly; - boolean_t ms_donefirst; - pktsum_t ms_prevstats; - boolean_t ms_parseable; -} show_mac_state_t; - -typedef void cmdfunc_t(int, char **); +typedef void cmdfunc_t(int, char **); -static cmdfunc_t do_show_link, do_show_dev, do_show_wifi; +static cmdfunc_t do_show_link, do_show_dev, do_show_wifi, do_show_phys; static cmdfunc_t do_create_aggr, do_delete_aggr, do_add_aggr, do_remove_aggr; -static cmdfunc_t do_modify_aggr, do_show_aggr, do_up_aggr, do_down_aggr; +static cmdfunc_t do_modify_aggr, do_show_aggr, do_up_aggr; static cmdfunc_t do_scan_wifi, do_connect_wifi, do_disconnect_wifi; static cmdfunc_t do_show_linkprop, do_set_linkprop, do_reset_linkprop; static cmdfunc_t do_create_secobj, do_delete_secobj, do_show_secobj; static cmdfunc_t do_init_linkprop, do_init_secobj; +static cmdfunc_t do_create_vlan, do_delete_vlan, do_up_vlan, do_show_vlan; +static cmdfunc_t do_rename_link, do_delete_phys, do_init_phys; +static cmdfunc_t do_show_linkmap; -static void show_linkprop_onelink(void *, const char *); +static void altroot_cmd(char *, int, char **); +static int show_linkprop_onelink(datalink_id_t, void *); -static void link_stats(const char *, uint_t); -static void aggr_stats(uint32_t, uint_t); +static void link_stats(datalink_id_t, uint_t); +static void aggr_stats(datalink_id_t, show_grp_state_t *, uint_t); static void dev_stats(const char *dev, uint32_t); +static int get_one_kstat(const char *, const char *, uint8_t, + void *, boolean_t); static void get_mac_stats(const char *, pktsum_t *); static void get_link_stats(const char *, pktsum_t *); -static uint64_t mac_ifspeed(const char *); +static uint64_t get_ifspeed(const char *, boolean_t); static void stats_total(pktsum_t *, pktsum_t *, pktsum_t *); static void stats_diff(pktsum_t *, pktsum_t *, pktsum_t *); -static const char *mac_link_state(const char *, char *); -static const char *mac_link_duplex(const char *, char *); +static const char *get_linkstate(const char *, boolean_t, char *); +static const char *get_linkduplex(const char *, boolean_t, char *); static boolean_t str2int(const char *, int *); static void die(const char *, ...); @@ -139,7 +148,6 @@ static cmd_t cmds[] = { { "modify-aggr", do_modify_aggr }, { "show-aggr", do_show_aggr }, { "up-aggr", do_up_aggr }, - { "down-aggr", do_down_aggr }, { "scan-wifi", do_scan_wifi }, { "connect-wifi", do_connect_wifi }, { "disconnect-wifi", do_disconnect_wifi }, @@ -151,50 +159,67 @@ static cmd_t cmds[] = { { "delete-secobj", do_delete_secobj }, { "show-secobj", do_show_secobj }, { "init-linkprop", do_init_linkprop }, - { "init-secobj", do_init_secobj } + { "init-secobj", do_init_secobj }, + { "create-vlan", do_create_vlan }, + { "delete-vlan", do_delete_vlan }, + { "show-vlan", do_show_vlan }, + { "up-vlan", do_up_vlan }, + { "rename-link", do_rename_link }, + { "delete-phys", do_delete_phys }, + { "show-phys", do_show_phys }, + { "init-phys", do_init_phys }, + { "show-linkmap", do_show_linkmap } +}; + +static const struct option lopts[] = { + {"vlan-id", required_argument, 0, 'v'}, + {"dev", required_argument, 0, 'd'}, + {"policy", required_argument, 0, 'P'}, + {"lacp-mode", required_argument, 0, 'L'}, + {"lacp-timer", required_argument, 0, 'T'}, + {"unicast", required_argument, 0, 'u'}, + {"temporary", no_argument, 0, 't'}, + {"root-dir", required_argument, 0, 'R'}, + {"link", required_argument, 0, 'l'}, + {"forcible", no_argument, 0, 'f'}, + { 0, 0, 0, 0 } }; -static const struct option longopts[] = { - {"vlan-id", required_argument, 0, 'v' }, - {"dev", required_argument, 0, 'd' }, - {"policy", required_argument, 0, 'P' }, - {"lacp-mode", required_argument, 0, 'l' }, - {"lacp-timer", required_argument, 0, 'T' }, - {"unicast", required_argument, 0, 'u' }, - {"statistics", no_argument, 0, 's' }, - {"interval", required_argument, 0, 'i' }, - {"lacp", no_argument, 0, 'L' }, - {"temporary", no_argument, 0, 't' }, - {"root-dir", required_argument, 0, 'r' }, - {"parseable", no_argument, 0, 'p' }, +static const struct option show_lopts[] = { + {"statistics", no_argument, 0, 's'}, + {"interval", required_argument, 0, 'i'}, + {"parseable", no_argument, 0, 'p'}, + {"extended", no_argument, 0, 'x'}, + {"persistent", no_argument, 0, 'P'}, + {"lacp", no_argument, 0, 'L'}, { 0, 0, 0, 0 } }; static const struct option prop_longopts[] = { - {"temporary", no_argument, 0, 't' }, - {"root-dir", required_argument, 0, 'R' }, - {"prop", required_argument, 0, 'p' }, - {"parseable", no_argument, 0, 'c' }, - {"persistent", no_argument, 0, 'P' }, + {"temporary", no_argument, 0, 't' }, + {"root-dir", required_argument, 0, 'R' }, + {"prop", required_argument, 0, 'p' }, + {"parseable", no_argument, 0, 'c' }, + {"persistent", no_argument, 0, 'P' }, { 0, 0, 0, 0 } }; static const struct option wifi_longopts[] = { - {"parseable", no_argument, 0, 'p' }, - {"output", required_argument, 0, 'o' }, - {"essid", required_argument, 0, 'e' }, - {"bsstype", required_argument, 0, 'b' }, - {"mode", required_argument, 0, 'm' }, - {"key", required_argument, 0, 'k' }, - {"sec", required_argument, 0, 's' }, - {"auth", required_argument, 0, 'a' }, - {"create-ibss", required_argument, 0, 'c' }, - {"timeout", required_argument, 0, 'T' }, - {"all-links", no_argument, 0, 'a' }, - {"temporary", no_argument, 0, 't' }, - {"root-dir", required_argument, 0, 'R' }, - {"persistent", no_argument, 0, 'P' }, - {"file", required_argument, 0, 'f' }, + {"parseable", no_argument, 0, 'p' }, + {"output", required_argument, 0, 'o' }, + {"essid", required_argument, 0, 'e' }, + {"bsstype", required_argument, 0, 'b' }, + {"mode", required_argument, 0, 'm' }, + {"key", required_argument, 0, 'k' }, + {"sec", required_argument, 0, 's' }, + {"auth", required_argument, 0, 'a' }, + {"create-ibss", required_argument, 0, 'c' }, + {"timeout", required_argument, 0, 'T' }, + {"all-links", no_argument, 0, 'a' }, + {"temporary", no_argument, 0, 't' }, + {"root-dir", required_argument, 0, 'R' }, + {"persistent", no_argument, 0, 'P' }, + {"file", required_argument, 0, 'f' }, { 0, 0, 0, 0 } }; @@ -205,25 +230,33 @@ static void usage(void) { (void) fprintf(stderr, gettext("usage: dladm <subcommand> <args> ...\n" - "\tshow-link [-p] [-s [-i <interval>]] [<name>]\n" - "\tshow-dev [-p] [-s [-i <interval>]] [<dev>]\n" + "\tshow-link [-pP] [-s [-i <interval>]] [<link>]\n" + "\trename-link [-R <root-dir>] <oldlink> <newlink>\n" + "\n" + "\tdelete-phys <link>\n" + "\tshow-phys [-pP] [<link>]\n" + "\tshow-dev [-p] [-s [-i <interval>]] [<dev>]\n" "\n" - "\tcreate-aggr [-t] [-R <root-dir>] [-P <policy>] [-l <mode>]\n" - "\t [-T <time>] [-u <address>] -d <dev> ... <key>\n" - "\tmodify-aggr [-t] [-R <root-dir>] [-P <policy>] [-l <mode>]\n" - "\t [-T <time>] [-u <address>] <key>\n" - "\tdelete-aggr [-t] [-R <root-dir>] <key>\n" - "\tadd-aggr [-t] [-R <root-dir>] -d <dev> ... <key>\n" - "\tremove-aggr [-t] [-R <root-dir>] -d <dev> ... <key>\n" - "\tshow-aggr [-pL][-s [-i <interval>]] [<key>]\n" + "\tcreate-aggr [-t] [-R <root-dir>] [-P <policy>] [-L <mode>]\n" + "\t [-T <time>] [-u <address>] [-l <link>] ... <link>\n" + "\tmodify-aggr [-t] [-R <root-dir>] [-P <policy>] [-L <mode>]\n" + "\t [-T <time>] [-u <address>] <link>\n" + "\tdelete-aggr [-t] [-R <root-dir>] <link>\n" + "\tadd-aggr [-t] [-R <root-dir>] [-l <link>] ... <link>\n" + "\tremove-aggr [-t] [-R <root-dir>] [-l <link>] ... <link>" + "\n\tshow-aggr [-pPLx][-s [-i <interval>]] [<link>]\n" "\n" - "\tscan-wifi [-p] [-o <field>,...] [<name>]\n" + "\tcreate-vlan [-ft] [-R <root-dir>] -l <link> -v <vid> [link]" + "\n\tdelete-vlan [-t] [-R <root-dir>] <link>\n" + "\tshow-vlan [-pP] [<link>]\n" + "\n" + "\tscan-wifi [-p] [-o <field>,...] [<link>]\n" "\tconnect-wifi [-e <essid>] [-i <bssid>] [-k <key>,...]" " [-s wep|wpa]\n" "\t [-a open|shared] [-b bss|ibss] [-c] [-m a|b|g]\n" - "\t [-T <time>] [<name>]\n" - "\tdisconnect-wifi [-a] [<name>]\n" - "\tshow-wifi [-p] [-o <field>,...] [<name>]\n" + "\t [-T <time>] [<link>]\n" + "\tdisconnect-wifi [-a] [<link>]\n" + "\tshow-wifi [-p] [-o <field>,...] [<link>]\n" "\n" "\tset-linkprop [-t] [-R <root-dir>] -p <prop>=<value>[,...]" " <name>\n" @@ -276,36 +309,35 @@ main(int argc, char *argv[]) static void do_create_aggr(int argc, char *argv[]) { - char option; - int key; - uint32_t policy = AGGR_POLICY_L4; - aggr_lacp_mode_t lacp_mode = AGGR_LACP_OFF; - aggr_lacp_timer_t lacp_timer = AGGR_LACP_TIMER_SHORT; + char option; + int key = 0; + uint32_t policy = AGGR_POLICY_L4; + aggr_lacp_mode_t lacp_mode = AGGR_LACP_OFF; + aggr_lacp_timer_t lacp_timer = AGGR_LACP_TIMER_SHORT; dladm_aggr_port_attr_db_t port[MAXPORT]; - uint_t nport = 0; - uint8_t mac_addr[ETHERADDRL]; - boolean_t mac_addr_fixed = B_FALSE; - boolean_t P_arg = B_FALSE; - boolean_t l_arg = B_FALSE; - boolean_t t_arg = B_FALSE; - boolean_t u_arg = B_FALSE; - boolean_t T_arg = B_FALSE; - char *altroot = NULL; - dladm_status_t status; + uint_t n, ndev, nlink; + uint8_t mac_addr[ETHERADDRL]; + boolean_t mac_addr_fixed = B_FALSE; + boolean_t P_arg = B_FALSE; + boolean_t l_arg = B_FALSE; + boolean_t u_arg = B_FALSE; + boolean_t T_arg = B_FALSE; + uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST; + char *altroot = NULL; + char name[MAXLINKNAMELEN]; + char *devs[MAXPORT]; + char *links[MAXPORT]; + dladm_status_t status; - opterr = 0; - while ((option = getopt_long(argc, argv, ":d:l:P:R:tu:T:", - longopts, NULL)) != -1) { + ndev = nlink = opterr = 0; + while ((option = getopt_long(argc, argv, ":d:l:L:P:R:tfu:T:", + lopts, NULL)) != -1) { switch (option) { case 'd': - if (nport >= MAXPORT) - die("too many <dev> arguments"); - - if (strlcpy(port[nport].lp_devname, optarg, - MAXNAMELEN) >= MAXNAMELEN) - die("device name too long"); + if (ndev + nlink >= MAXPORT) + die("too many ports specified"); - nport++; + devs[ndev++] = optarg; break; case 'P': if (P_arg) @@ -325,6 +357,19 @@ do_create_aggr(int argc, char *argv[]) die("invalid MAC address '%s'", optarg); break; case 'l': + if (isdigit(optarg[strlen(optarg) - 1])) { + + /* + * Ended with digit, possibly a link name. + */ + if (ndev + nlink >= MAXPORT) + die("too many ports specified"); + + links[nlink++] = optarg; + break; + } + /* FALLTHROUGH */ + case 'L': if (l_arg) die_optdup(option); @@ -341,7 +386,10 @@ do_create_aggr(int argc, char *argv[]) die("invalid LACP timer value '%s'", optarg); break; case 't': - t_arg = B_TRUE; + flags &= ~DLADM_OPT_PERSIST; + break; + case 'f': + flags |= DLADM_OPT_FORCE; break; case 'R': altroot = optarg; @@ -352,37 +400,100 @@ do_create_aggr(int argc, char *argv[]) } } - if (nport == 0) + if (ndev + nlink == 0) usage(); - /* get key value (required last argument) */ + /* get key value or the aggregation name (required last argument) */ if (optind != (argc-1)) usage(); - if (!str2int(argv[optind], &key) || key < 1) - die("invalid key value '%s'", argv[optind]); + if (!str2int(argv[optind], &key)) { + if (strlcpy(name, argv[optind], MAXLINKNAMELEN) >= + MAXLINKNAMELEN) { + die("link name too long '%s'", argv[optind]); + } - status = dladm_aggr_create(key, nport, port, policy, mac_addr_fixed, - mac_addr, lacp_mode, lacp_timer, t_arg, altroot); - if (status != DLADM_STATUS_OK) - die_dlerr(status, "create operation failed"); + if (!dladm_valid_linkname(name)) + die("invalid link name '%s'", argv[optind]); + } else { + (void) snprintf(name, MAXLINKNAMELEN, "aggr%d", key); + } + + if (altroot != NULL) + altroot_cmd(altroot, argc, argv); + + for (n = 0; n < ndev; n++) { + if (dladm_dev2linkid(devs[n], &port[n].lp_linkid) != + DLADM_STATUS_OK) { + die("invalid dev name '%s'", devs[n]); + } + } + + for (n = 0; n < nlink; n++) { + if (dladm_name2info(links[n], &port[ndev + n].lp_linkid, + NULL, NULL, NULL) != DLADM_STATUS_OK) { + die("invalid link name '%s'", links[n]); + } + } + + status = dladm_aggr_create(name, key, ndev + nlink, port, policy, + mac_addr_fixed, (const uchar_t *)mac_addr, lacp_mode, + lacp_timer, flags); +done: + if (status != DLADM_STATUS_OK) { + if (status == DLADM_STATUS_NONOTIF) { + die_dlerr(status, "not all links have link up/down " + "detection; must use -f (see dladm(1M))\n"); + } else { + die_dlerr(status, "create operation failed"); + } + } +} + +/* + * arg is either the key or the aggr name. Validate it and convert it to + * the linkid if altroot is NULL. + */ +static dladm_status_t +i_dladm_aggr_get_linkid(const char *altroot, const char *arg, + datalink_id_t *linkidp, uint32_t flags) +{ + int key = 0; + char *aggr = NULL; + dladm_status_t status; + + if (!str2int(arg, &key)) + aggr = (char *)arg; + + if (aggr == NULL && key == 0) + return (DLADM_STATUS_LINKINVAL); + + if (altroot != NULL) + return (DLADM_STATUS_OK); + + if (aggr != NULL) { + status = dladm_name2info(aggr, linkidp, NULL, NULL, NULL); + } else { + status = dladm_key2linkid(key, linkidp, flags); + } + + return (status); } static void do_delete_aggr(int argc, char *argv[]) { - int key; char option; - boolean_t t_arg = B_FALSE; char *altroot = NULL; + uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST; dladm_status_t status; + datalink_id_t linkid; opterr = 0; - while ((option = getopt_long(argc, argv, ":R:t", longopts, - NULL)) != -1) { + while ((option = getopt_long(argc, argv, ":R:t", lopts, NULL)) != -1) { switch (option) { case 't': - t_arg = B_TRUE; + flags &= ~DLADM_OPT_PERSIST; break; case 'R': altroot = optarg; @@ -393,14 +504,19 @@ do_delete_aggr(int argc, char *argv[]) } } - /* get key value (required last argument) */ + /* get key value or the aggregation name (required last argument) */ if (optind != (argc-1)) usage(); - if (!str2int(argv[optind], &key) || key < 1) - die("invalid key value '%s'", argv[optind]); + status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags); + if (status != DLADM_STATUS_OK) + goto done; - status = dladm_aggr_delete(key, t_arg, altroot); + if (altroot != NULL) + altroot_cmd(altroot, argc, argv); + + status = dladm_aggr_delete(linkid, flags); +done: if (status != DLADM_STATUS_OK) die_dlerr(status, "delete operation failed"); } @@ -408,30 +524,37 @@ do_delete_aggr(int argc, char *argv[]) static void do_add_aggr(int argc, char *argv[]) { - char option; - int key; + char option; + uint_t n, ndev, nlink; + char *altroot = NULL; + uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST; + datalink_id_t linkid; + dladm_status_t status; dladm_aggr_port_attr_db_t port[MAXPORT]; - uint_t nport = 0; - boolean_t t_arg = B_FALSE; - char *altroot = NULL; - dladm_status_t status; + char *devs[MAXPORT]; + char *links[MAXPORT]; - opterr = 0; - while ((option = getopt_long(argc, argv, ":d:R:t", longopts, + ndev = nlink = opterr = 0; + while ((option = getopt_long(argc, argv, ":d:l:R:tf", lopts, NULL)) != -1) { switch (option) { case 'd': - if (nport >= MAXPORT) - die("too many <dev> arguments"); + if (ndev + nlink >= MAXPORT) + die("too many ports specified"); - if (strlcpy(port[nport].lp_devname, optarg, - MAXNAMELEN) >= MAXNAMELEN) - die("device name too long"); + devs[ndev++] = optarg; + break; + case 'l': + if (ndev + nlink >= MAXPORT) + die("too many ports specified"); - nport++; + links[nlink++] = optarg; break; case 't': - t_arg = B_TRUE; + flags &= ~DLADM_OPT_PERSIST; + break; + case 'f': + flags |= DLADM_OPT_FORCE; break; case 'R': altroot = optarg; @@ -442,17 +565,38 @@ do_add_aggr(int argc, char *argv[]) } } - if (nport == 0) + if (ndev + nlink == 0) usage(); - /* get key value (required last argument) */ + /* get key value or the aggregation name (required last argument) */ if (optind != (argc-1)) usage(); - if (!str2int(argv[optind], &key) || key < 1) - die("invalid key value '%s'", argv[optind]); + if ((status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, + flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST))) != + DLADM_STATUS_OK) { + goto done; + } + + if (altroot != NULL) + altroot_cmd(altroot, argc, argv); - status = dladm_aggr_add(key, nport, port, t_arg, altroot); + for (n = 0; n < ndev; n++) { + if (dladm_dev2linkid(devs[n], &(port[n].lp_linkid)) != + DLADM_STATUS_OK) { + die("invalid <dev> '%s'", devs[n]); + } + } + + for (n = 0; n < nlink; n++) { + if (dladm_name2info(links[n], &port[n + ndev].lp_linkid, + NULL, NULL, NULL) != DLADM_STATUS_OK) { + die("invalid <link> '%s'", links[n]); + } + } + + status = dladm_aggr_add(linkid, ndev + nlink, port, flags); +done: if (status != DLADM_STATUS_OK) { /* * checking DLADM_STATUS_NOTSUP is a temporary workaround @@ -462,10 +606,14 @@ do_add_aggr(int argc, char *argv[]) (void) fprintf(stderr, gettext("%s: add operation failed: %s\n"), progname, - gettext("device capabilities don't match")); + gettext("link capabilities don't match")); exit(ENOTSUP); + } else if (status == DLADM_STATUS_NONOTIF) { + die_dlerr(status, "not all links have link up/down " + "detection; must use -f (see dladm(1M))\n"); + } else { + die_dlerr(status, "add operation failed"); } - die_dlerr(status, "add operation failed"); } } @@ -473,29 +621,34 @@ static void do_remove_aggr(int argc, char *argv[]) { char option; - int key; dladm_aggr_port_attr_db_t port[MAXPORT]; - uint_t nport = 0; - boolean_t t_arg = B_FALSE; + uint_t n, ndev, nlink; + char *devs[MAXPORT]; + char *links[MAXPORT]; char *altroot = NULL; + uint32_t flags; + datalink_id_t linkid; dladm_status_t status; - opterr = 0; - while ((option = getopt_long(argc, argv, ":d:R:t", - longopts, NULL)) != -1) { + flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST; + ndev = nlink = opterr = 0; + while ((option = getopt_long(argc, argv, ":d:l:R:t", + lopts, NULL)) != -1) { switch (option) { case 'd': - if (nport >= MAXPORT) - die("too many <dev> arguments"); + if (ndev + nlink >= MAXPORT) + die("too many ports specified"); - if (strlcpy(port[nport].lp_devname, optarg, - MAXNAMELEN) >= MAXNAMELEN) - die("device name too long"); + devs[ndev++] = optarg; + break; + case 'l': + if (ndev + nlink >= MAXPORT) + die("too many ports specified"); - nport++; + links[nlink++] = optarg; break; case 't': - t_arg = B_TRUE; + flags &= ~DLADM_OPT_PERSIST; break; case 'R': altroot = optarg; @@ -506,17 +659,36 @@ do_remove_aggr(int argc, char *argv[]) } } - if (nport == 0) + if (ndev + nlink == 0) usage(); - /* get key value (required last argument) */ + /* get key value or the aggregation name (required last argument) */ if (optind != (argc-1)) usage(); - if (!str2int(argv[optind], &key) || key < 1) - die("invalid key value '%s'", argv[optind]); + status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags); + if (status != DLADM_STATUS_OK) + goto done; + + if (altroot != NULL) + altroot_cmd(altroot, argc, argv); + + for (n = 0; n < ndev; n++) { + if (dladm_dev2linkid(devs[n], &(port[n].lp_linkid)) != + DLADM_STATUS_OK) { + die("invalid <dev> '%s'", devs[n]); + } + } + + for (n = 0; n < nlink; n++) { + if (dladm_name2info(links[n], &port[n + ndev].lp_linkid, + NULL, NULL, NULL) != DLADM_STATUS_OK) { + die("invalid <link> '%s'", links[n]); + } + } - status = dladm_aggr_remove(key, nport, port, t_arg, altroot); + status = dladm_aggr_remove(linkid, ndev + nlink, port, flags); +done: if (status != DLADM_STATUS_OK) die_dlerr(status, "remove operation failed"); } @@ -525,19 +697,19 @@ static void do_modify_aggr(int argc, char *argv[]) { char option; - int key; uint32_t policy = AGGR_POLICY_L4; aggr_lacp_mode_t lacp_mode = AGGR_LACP_OFF; aggr_lacp_timer_t lacp_timer = AGGR_LACP_TIMER_SHORT; uint8_t mac_addr[ETHERADDRL]; boolean_t mac_addr_fixed = B_FALSE; uint8_t modify_mask = 0; - boolean_t t_arg = B_FALSE; char *altroot = NULL; + uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST; + datalink_id_t linkid; dladm_status_t status; opterr = 0; - while ((option = getopt_long(argc, argv, ":l:P:R:tu:T:", longopts, + while ((option = getopt_long(argc, argv, ":L:l:P:R:tu:T:", lopts, NULL)) != -1) { switch (option) { case 'P': @@ -560,6 +732,7 @@ do_modify_aggr(int argc, char *argv[]) die("invalid MAC address '%s'", optarg); break; case 'l': + case 'L': if (modify_mask & DLADM_AGGR_MODIFY_LACP_MODE) die_optdup(option); @@ -578,7 +751,7 @@ do_modify_aggr(int argc, char *argv[]) die("invalid LACP timer value '%s'", optarg); break; case 't': - t_arg = B_TRUE; + flags &= ~DLADM_OPT_PERSIST; break; case 'R': altroot = optarg; @@ -592,15 +765,21 @@ do_modify_aggr(int argc, char *argv[]) if (modify_mask == 0) die("at least one of the -PulT options must be specified"); - /* get key value (required last argument) */ + /* get key value or the aggregation name (required last argument) */ if (optind != (argc-1)) usage(); - if (!str2int(argv[optind], &key) || key < 1) - die("invalid key value '%s'", argv[optind]); + status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags); + if (status != DLADM_STATUS_OK) + goto done; + + if (altroot != NULL) + altroot_cmd(altroot, argc, argv); - status = dladm_aggr_modify(key, modify_mask, policy, mac_addr_fixed, - mac_addr, lacp_mode, lacp_timer, t_arg, altroot); + status = dladm_aggr_modify(linkid, modify_mask, policy, mac_addr_fixed, + (const uchar_t *)mac_addr, lacp_mode, lacp_timer, flags); + +done: if (status != DLADM_STATUS_OK) die_dlerr(status, "modify operation failed"); } @@ -608,21 +787,27 @@ do_modify_aggr(int argc, char *argv[]) static void do_up_aggr(int argc, char *argv[]) { - int key = 0; + datalink_id_t linkid = DATALINK_ALL_LINKID; dladm_status_t status; - /* get aggregation key (optional last argument) */ + /* + * get the key or the name of the aggregation (optional last argument) + */ if (argc == 2) { - if (!str2int(argv[1], &key) || key < 1) - die("invalid key value '%s'", argv[1]); + if ((status = i_dladm_aggr_get_linkid(NULL, argv[1], &linkid, + DLADM_OPT_PERSIST)) != DLADM_STATUS_OK) { + goto done; + } } else if (argc > 2) { usage(); } - if ((status = dladm_aggr_up(key, NULL)) != DLADM_STATUS_OK) { - if (key != 0) { - die_dlerr(status, "could not bring up aggregation '%u'", - key); + status = dladm_aggr_up(linkid); +done: + if (status != DLADM_STATUS_OK) { + if (argc == 2) { + die_dlerr(status, + "could not bring up aggregation '%s'", argv[1]); } else { die_dlerr(status, "could not bring aggregations up"); } @@ -630,174 +815,554 @@ do_up_aggr(int argc, char *argv[]) } static void -do_down_aggr(int argc, char *argv[]) +do_create_vlan(int argc, char *argv[]) { + char *link = NULL; + char drv[DLPI_LINKNAME_MAX]; + uint_t ppa; + datalink_id_t linkid; + int vid = 0; + char option; + uint32_t flags = (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST); + char *altroot = NULL; + char vlan[MAXLINKNAMELEN]; dladm_status_t status; - int key = 0; - /* get aggregation key (optional last argument) */ - if (argc == 2) { - if (!str2int(argv[1], &key) || key < 1) - die("invalid key value '%s'", argv[1]); - } else if (argc > 2) { + opterr = 0; + while ((option = getopt_long(argc, argv, ":tfl:v:", + lopts, NULL)) != -1) { + switch (option) { + case 'v': + if (vid != 0) + die_optdup(option); + + if (!str2int(optarg, &vid) || vid < 1 || vid > 4094) + die("invalid VLAN identifier '%s'", optarg); + + break; + case 'l': + if (link != NULL) + die_optdup(option); + + link = optarg; + break; + case 'f': + flags |= DLADM_OPT_FORCE; + break; + case 't': + flags &= ~DLADM_OPT_PERSIST; + break; + case 'R': + altroot = optarg; + break; + default: + die_opterr(optopt, option); + break; + } + } + + /* get vlan name if there is any */ + if ((vid == 0) || (link == NULL) || (argc - optind > 1)) usage(); + + if (optind == (argc - 1)) { + if (strlcpy(vlan, argv[optind], MAXLINKNAMELEN) >= + MAXLINKNAMELEN) { + die("vlan name too long '%s'", argv[optind]); + } + } else { + if ((dlpi_parselink(link, drv, &ppa) != DLPI_SUCCESS) || + (ppa >= 1000) || + (dlpi_makelink(vlan, drv, vid * 1000 + ppa) != + DLPI_SUCCESS)) { + die("invalid link name '%s'", link); + } } - if ((status = dladm_aggr_down(key)) != DLADM_STATUS_OK) { - if (key != 0) { - die_dlerr(status, - "could not bring down aggregation '%u'", key); + if (altroot != NULL) + altroot_cmd(altroot, argc, argv); + + if (dladm_name2info(link, &linkid, NULL, NULL, NULL) != + DLADM_STATUS_OK) { + die("invalid link name '%s'", link); + } + + if ((status = dladm_vlan_create(vlan, linkid, vid, flags)) != + DLADM_STATUS_OK) { + if (status == DLADM_STATUS_NOTSUP) { + die_dlerr(status, "VLAN over '%s' may require lowered " + "MTU; must use -f (see dladm(1M))\n", link); } else { - die_dlerr(status, "could not bring down aggregations"); + die_dlerr(status, "create operation failed"); } } } -#define TYPE_WIDTH 10 +static void +do_delete_vlan(int argc, char *argv[]) +{ + char option; + uint32_t flags = (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST); + char *altroot = NULL; + datalink_id_t linkid; + dladm_status_t status; + + opterr = 0; + while ((option = getopt_long(argc, argv, ":R:t", lopts, NULL)) != -1) { + switch (option) { + case 't': + flags &= ~DLADM_OPT_PERSIST; + break; + case 'R': + altroot = optarg; + break; + default: + die_opterr(optopt, option); + break; + } + } + + /* get VLAN link name (required last argument) */ + if (optind != (argc - 1)) + usage(); + + if (altroot != NULL) + altroot_cmd(altroot, argc, argv); + + status = dladm_name2info(argv[optind], &linkid, NULL, NULL, NULL); + if (status != DLADM_STATUS_OK) + goto done; + + status = dladm_vlan_delete(linkid, flags); +done: + if (status != DLADM_STATUS_OK) + die_dlerr(status, "delete operation failed"); +} static void -print_link_parseable(const char *name, dladm_attr_t *dap, boolean_t legacy) +do_up_vlan(int argc, char *argv[]) { - char type[TYPE_WIDTH]; + datalink_id_t linkid = DATALINK_ALL_LINKID; + dladm_status_t status; - if (!legacy) { - char drv[DLPI_LINKNAME_MAX]; - uint_t instance; + /* + * get the name of the VLAN (optional last argument) + */ + if (argc > 2) + usage(); + + if (argc == 2) { + status = dladm_name2info(argv[1], &linkid, NULL, NULL, NULL); + if (status != DLADM_STATUS_OK) + goto done; + } - if (dap->da_vid != 0) { - (void) snprintf(type, TYPE_WIDTH, "vlan %u", - dap->da_vid); + status = dladm_vlan_up(linkid); +done: + if (status != DLADM_STATUS_OK) { + if (argc == 2) { + die_dlerr(status, + "could not bring up VLAN '%s'", argv[1]); } else { - (void) snprintf(type, TYPE_WIDTH, "non-vlan"); + die_dlerr(status, "could not bring VLANs up"); } + } +} - if (dlpi_parselink(dap->da_dev, drv, &instance) != DLPI_SUCCESS) - return; +static void +do_rename_link(int argc, char *argv[]) +{ + char option; + char *link1, *link2; + char *altroot = NULL; + dladm_status_t status; - if (strncmp(drv, AGGR_DRV, sizeof (AGGR_DRV)) == 0) { - (void) printf("%s type=%s mtu=%d key=%u\n", - name, type, dap->da_max_sdu, instance); - } else { - (void) printf("%s type=%s mtu=%d device=%s\n", - name, type, dap->da_max_sdu, dap->da_dev); + opterr = 0; + while ((option = getopt_long(argc, argv, ":R:", lopts, NULL)) != -1) { + switch (option) { + case 'R': + altroot = optarg; + break; + default: + die_opterr(optopt, option); + break; } - } else { - (void) printf("%s type=legacy mtu=%d device=%s\n", - name, dap->da_max_sdu, name); } + + /* get link1 and link2 name (required the last 2 arguments) */ + if (optind != (argc - 2)) + usage(); + + if (altroot != NULL) + altroot_cmd(altroot, argc, argv); + + link1 = argv[optind++]; + link2 = argv[optind]; + if ((status = dladm_rename_link(link1, link2)) != DLADM_STATUS_OK) + die_dlerr(status, "rename operation failed"); } static void -print_link(const char *name, dladm_attr_t *dap, boolean_t legacy) +do_delete_phys(int argc, char *argv[]) { - char type[TYPE_WIDTH]; + datalink_id_t linkid = DATALINK_ALL_LINKID; + dladm_status_t status; - if (!legacy) { - char drv[DLPI_LINKNAME_MAX]; - uint_t instance; + /* get link name (required the last argument) */ + if (argc > 2) + usage(); - if (dap->da_vid != 0) { - (void) snprintf(type, TYPE_WIDTH, gettext("vlan %u"), - dap->da_vid); - } else { - (void) snprintf(type, TYPE_WIDTH, gettext("non-vlan")); - } + if (argc == 2) { + status = dladm_name2info(argv[1], &linkid, NULL, NULL, NULL); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "cannot delete '%s'", argv[1]); + } - if (dlpi_parselink(dap->da_dev, drv, &instance) != DLPI_SUCCESS) - return; - if (strncmp(drv, AGGR_DRV, sizeof (AGGR_DRV)) == 0) { - (void) printf(gettext("%-9s\ttype: %s\tmtu: %d" - "\taggregation: key %u\n"), name, type, - dap->da_max_sdu, instance); - } else { - (void) printf(gettext("%-9s\ttype: %s\tmtu: " - "%d\tdevice: %s\n"), name, type, dap->da_max_sdu, - dap->da_dev); - } - } else { - (void) printf(gettext("%-9s\ttype: legacy\tmtu: " - "%d\tdevice: %s\n"), name, dap->da_max_sdu, name); + if ((status = dladm_phys_delete(linkid)) != DLADM_STATUS_OK) { + if (argc == 2) + die_dlerr(status, "cannot delete '%s'", argv[1]); + else + die_dlerr(status, "delete operation failed"); } } +/*ARGSUSED*/ static int -get_if_info(const char *name, dladm_attr_t *dlattrp, boolean_t *legacy) +i_dladm_walk_linkmap(datalink_id_t linkid, void *arg) { - int err; + char name[MAXLINKNAMELEN]; + char mediabuf[DLADM_STRSIZE]; + char classbuf[DLADM_STRSIZE]; + datalink_class_t class; + uint32_t media; + uint32_t flags; + + if (dladm_datalink_id2info(linkid, &flags, &class, &media, name, + MAXLINKNAMELEN) == DLADM_STATUS_OK) { + (void) dladm_class2str(class, classbuf); + (void) dladm_media2str(media, mediabuf); + (void) printf("%-12s%8d %-12s%-20s %6d\n", name, + linkid, classbuf, mediabuf, flags); + } + return (DLADM_WALK_CONTINUE); +} - if ((err = dladm_info(name, dlattrp)) == 0) { - *legacy = B_FALSE; - } else if (err < 0 && errno == ENODEV) { - dlpi_handle_t dh; - dlpi_info_t dlinfo; +/*ARGSUSED*/ +static void +do_show_linkmap(int argc, char *argv[]) +{ + if (argc != 1) + die("invalid arguments"); + + (void) printf("%-12s%8s %-12s%-20s %6s\n", "NAME", "LINKID", + "CLASS", "MEDIA", "FLAGS"); + (void) dladm_walk_datalink_id(i_dladm_walk_linkmap, NULL, + DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, + DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST); +} - /* - * A return value of ENODEV means that the specified - * device is not gldv3. - */ - if (dlpi_open(name, &dh, 0) != DLPI_SUCCESS) { - errno = ENOENT; - return (-1); +/* + * Delete inactive physical links. + */ +/*ARGSUSED*/ +static int +purge_phys(datalink_id_t linkid, void *arg) +{ + datalink_class_t class; + uint32_t flags; + + if (dladm_datalink_id2info(linkid, &flags, &class, NULL, + NULL, 0) != DLADM_STATUS_OK) { + return (DLADM_WALK_CONTINUE); + } + + if (class == DATALINK_CLASS_PHYS && !(flags & DLADM_OPT_ACTIVE)) + (void) dladm_phys_delete(linkid); + + return (DLADM_WALK_CONTINUE); +} + +/*ARGSUSED*/ +static void +do_init_phys(int argc, char *argv[]) +{ + di_node_t devtree; + + if (argc > 1) + usage(); + + /* + * Force all the devices to attach, therefore all the network physical + * devices can be known to the dlmgmtd daemon. + */ + if ((devtree = di_init("/", DINFOFORCE | DINFOSUBTREE)) != DI_NODE_NIL) + di_fini(devtree); + + (void) dladm_walk_datalink_id(purge_phys, NULL, + DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); +} + +static void +print_link_head(show_state_t *state) +{ + if (state->ls_donefirst) + return; + state->ls_donefirst = B_TRUE; + + if (state->ls_parseable) + return; + + if (state->ls_flags & DLADM_OPT_ACTIVE) { + (void) printf("%-12s%-8s%6s %-9s%s\n", "LINK", "CLASS", "MTU", + "STATE", "OVER"); + } else { + (void) printf("%-12s%-8s%s\n", "LINK", "CLASS", "OVER"); + } +} + +/* + * Print the active topology information. + */ +static dladm_status_t +print_link_topology(show_state_t *state, datalink_id_t linkid, + datalink_class_t class, char **pptr, char *lim) +{ + char *fmt; + char over[MAXLINKNAMELEN]; + uint32_t flags = state->ls_flags; + dladm_status_t status = DLADM_STATUS_OK; + + if (state->ls_parseable) + fmt = "OVER=\"%s"; + else + fmt = "%s"; + + if (class == DATALINK_CLASS_VLAN) { + dladm_vlan_attr_t vinfo; + + status = dladm_vlan_info(linkid, &vinfo, flags); + if (status != DLADM_STATUS_OK) + goto done; + status = dladm_datalink_id2info(vinfo.dv_linkid, NULL, NULL, + NULL, over, sizeof (over)); + if (status != DLADM_STATUS_OK) + goto done; + + /*LINTED: E_SEC_PRINTF_VAR_FMT*/ + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, over); + } else if (class == DATALINK_CLASS_AGGR) { + dladm_aggr_grp_attr_t ginfo; + int i; + + status = dladm_aggr_info(linkid, &ginfo, flags); + if (status != DLADM_STATUS_OK) + goto done; + + if (ginfo.lg_nports == 0) { + status = DLADM_STATUS_BADVAL; + goto done; } - if (dlpi_info(dh, &dlinfo, 0) != DLPI_SUCCESS) { - dlpi_close(dh); - errno = EINVAL; - return (-1); + for (i = 0; i < ginfo.lg_nports; i++) { + status = dladm_datalink_id2info( + ginfo.lg_ports[i].lp_linkid, NULL, NULL, NULL, over, + sizeof (over)); + if (status != DLADM_STATUS_OK) { + free(ginfo.lg_ports); + goto done; + } + /*LINTED: E_SEC_PRINTF_VAR_FMT*/ + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, over); + fmt = " %s"; + } + free(ginfo.lg_ports); + } else if (class == DATALINK_CLASS_VNIC) { + dladm_vnic_attr_sys_t vinfo; + + if ((status = dladm_vnic_info(linkid, &vinfo, flags)) != + DLADM_STATUS_OK || (status = dladm_datalink_id2info( + vinfo.va_link_id, NULL, NULL, NULL, over, + sizeof (over))) != DLADM_STATUS_OK) { + goto done; } - dlpi_close(dh); - *legacy = B_TRUE; - bzero(dlattrp, sizeof (*dlattrp)); - dlattrp->da_max_sdu = dlinfo.di_max_sdu; + /*LINTED: E_SEC_PRINTF_VAR_FMT*/ + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, over); } else { - /* - * If the return value is not ENODEV, this means that - * user is either passing in a bogus interface name - * or a vlan interface name that doesn't exist yet. - */ - errno = ENOENT; - return (-1); + /*LINTED: E_SEC_PRINTF_VAR_FMT*/ + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, + state->ls_parseable ? "" : "--"); } - return (0); + if (state->ls_parseable) + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), "\"\n"); + else + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), "\n"); + +done: + return (status); } -/* ARGSUSED */ -static void -show_link(void *arg, const char *name) +static dladm_status_t +print_link(show_state_t *state, datalink_id_t linkid, char **pptr, char *lim) { - dladm_attr_t dlattr; - boolean_t legacy = B_TRUE; - show_link_state_t *state = (show_link_state_t *)arg; + char link[MAXLINKNAMELEN]; + char buf[DLADM_STRSIZE]; + datalink_class_t class; + uint_t mtu; + char *fmt; + uint32_t flags; + dladm_status_t status; + + if ((status = dladm_datalink_id2info(linkid, &flags, &class, NULL, + link, sizeof (link))) != DLADM_STATUS_OK) { + goto done; + } + + if (!(state->ls_flags & flags)) { + status = DLADM_STATUS_NOTFOUND; + goto done; + } + + if (state->ls_flags == DLADM_OPT_ACTIVE) { + dladm_attr_t dlattr; + + if (class == DATALINK_CLASS_PHYS) { + dladm_phys_attr_t dpa; + dlpi_handle_t dh; + dlpi_info_t dlinfo; + + if ((status = dladm_phys_info(linkid, &dpa, + DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) { + goto done; + } + + if (!dpa.dp_novanity) + goto link_mtu; + + /* + * This is a physical link that does not have + * vanity naming support. + */ + if (dlpi_open(dpa.dp_dev, &dh, DLPI_DEVONLY) != + DLPI_SUCCESS) { + status = DLADM_STATUS_NOTFOUND; + goto done; + } + + if (dlpi_info(dh, &dlinfo, 0) != DLPI_SUCCESS) { + dlpi_close(dh); + status = DLADM_STATUS_BADARG; + goto done; + } + + dlpi_close(dh); + mtu = dlinfo.di_max_sdu; + } else { +link_mtu: + status = dladm_info(linkid, &dlattr); + if (status != DLADM_STATUS_OK) + goto done; + mtu = dlattr.da_max_sdu; + } + } - if (get_if_info(name, &dlattr, &legacy) < 0) - die("invalid link '%s'", name); + if (state->ls_flags == DLADM_OPT_ACTIVE) { + if (state->ls_parseable) + fmt = "LINK=\"%s\" CLASS=\"%s\" MTU=\"%d\" "; + else + fmt = "%-12s%-8s%6d "; + } else { + if (state->ls_parseable) + fmt = "LINK=\"%s\" CLASS=\"%s\" "; + else + fmt = "%-12s%-8s"; + } - if (state->ls_parseable) { - print_link_parseable(name, &dlattr, legacy); + (void) dladm_class2str(class, buf); + if (state->ls_flags == DLADM_OPT_ACTIVE) { + /*LINTED: E_SEC_PRINTF_VAR_FMT*/ + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, link, + buf, mtu); } else { - print_link(name, &dlattr, legacy); + /*LINTED: E_SEC_PRINTF_VAR_FMT*/ + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, link, buf); } + + (void) get_linkstate(link, B_TRUE, buf); + if (state->ls_flags == DLADM_OPT_ACTIVE) { + if (state->ls_parseable) { + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "STATE=\"%s\" ", buf); + } else { + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "%-9s", buf); + } + } + + status = print_link_topology(state, linkid, class, pptr, lim); + if (status != DLADM_STATUS_OK) + goto done; + +done: + return (status); } -static void -show_link_stats(void *arg, const char *name) +static int +show_link(datalink_id_t linkid, void *arg) +{ + show_state_t *state = arg; + dladm_status_t status; + char buf[MAXLINELEN]; + char *ptr = buf, *lim = buf + MAXLINELEN; + + status = print_link(state, linkid, &ptr, lim); + if (status != DLADM_STATUS_OK) + goto done; + print_link_head(state); + (void) printf("%s", buf); + +done: + state->ls_status = status; + return (DLADM_WALK_CONTINUE); +} + +static int +show_link_stats(datalink_id_t linkid, void *arg) { - show_link_state_t *state = (show_link_state_t *)arg; + char link[MAXLINKNAMELEN]; + datalink_class_t class; + show_state_t *state = arg; pktsum_t stats, diff_stats; + dladm_phys_attr_t dpa; if (state->ls_firstonly) { if (state->ls_donefirst) - return; + return (DLADM_WALK_CONTINUE); state->ls_donefirst = B_TRUE; } else { bzero(&state->ls_prevstats, sizeof (state->ls_prevstats)); } - get_link_stats(name, &stats); + if (dladm_datalink_id2info(linkid, NULL, &class, NULL, link, + sizeof (link)) != DLADM_STATUS_OK) { + return (DLADM_WALK_CONTINUE); + } + + if (class == DATALINK_CLASS_PHYS) { + if (dladm_phys_info(linkid, &dpa, DLADM_OPT_ACTIVE) != + DLADM_STATUS_OK) { + return (DLADM_WALK_CONTINUE); + } + if (dpa.dp_novanity) + get_mac_stats(dpa.dp_dev, &stats); + else + get_link_stats(link, &stats); + } else { + get_link_stats(link, &stats); + } stats_diff(&diff_stats, &stats, &state->ls_prevstats); - (void) printf("%s", name); - (void) printf("\t\t%-10llu", diff_stats.ipackets); + (void) printf("%-12s", link); + (void) printf("%-10llu", diff_stats.ipackets); (void) printf("%-12llu", diff_stats.rbytes); (void) printf("%-8u", diff_stats.ierrors); (void) printf("%-10llu", diff_stats.opackets); @@ -805,218 +1370,358 @@ show_link_stats(void *arg, const char *name) (void) printf("%-8u\n", diff_stats.oerrors); state->ls_prevstats = stats; + return (DLADM_WALK_CONTINUE); } static void -dump_grp(dladm_aggr_grp_attr_t *grp, boolean_t parseable) +print_port_stat(const char *port, pktsum_t *old_stats, pktsum_t *port_stats, + pktsum_t *tot_stats, char **pptr, char *lim) { - char buf[DLADM_STRSIZE]; - char addr_str[ETHERADDRL * 3]; + pktsum_t diff_stats; - if (!parseable) { - (void) printf(gettext("key: %d (0x%04x)"), - grp->lg_key, grp->lg_key); + stats_diff(&diff_stats, port_stats, old_stats); + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "%-12s%-10s%8llu %8llu %8llu %8llu ", "", port, + diff_stats.ipackets, diff_stats.rbytes, diff_stats.opackets, + diff_stats.obytes); - (void) printf(gettext("\tpolicy: %s"), - dladm_aggr_policy2str(grp->lg_policy, buf)); + if (tot_stats->ipackets == 0) { + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), "%8s ", "--"); + } else { + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), "%7.1f%% ", + (double)diff_stats.ipackets/ + (double)tot_stats->ipackets * 100); + } - (void) printf(gettext("\taddress: %s (%s)\n"), - dladm_aggr_macaddr2str(grp->lg_mac, addr_str), - (grp->lg_mac_fixed) ? gettext("fixed") : gettext("auto")); + if (tot_stats->opackets == 0) { + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), "%8s\n", "--"); } else { - (void) printf("aggr key=%d", grp->lg_key); + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), "%7.1f%%\n", + (double)diff_stats.opackets/ + (double)tot_stats->opackets * 100); + } - (void) printf(" policy=%s", - dladm_aggr_policy2str(grp->lg_policy, buf)); + *old_stats = *port_stats; +} - (void) printf(" address=%s", - dladm_aggr_macaddr2str(grp->lg_mac, addr_str)); +static void +print_aggr_head(show_grp_state_t *state) +{ + if (state->gs_donefirst) + return; + state->gs_donefirst = B_TRUE; - (void) printf(" address-type=%s\n", - (grp->lg_mac_fixed) ? "fixed" : "auto"); + if (state->gs_parseable) + return; + + if (state->gs_lacp) { + (void) printf("%-12s%-12s%-13s%-5s%-5s%-5s%-10s%s\n", "LINK", + "PORT", "AGGREGATABLE", "SYNC", "COLL", "DIST", + "DEFAULTED", "EXPIRED"); + } else if (state->gs_extended) { + (void) printf("%-12s%-14s%6s %-9s%-9s%-18s%s\n", "LINK", + "PORT", "SPEED", "DUPLEX", "STATE", "ADDRESS", "PORTSTATE"); + } else if (!state->gs_stats) { + (void) printf("%-12s%-8s%-24s%-13s%-11s%s\n", "LINK", "POLICY", + "ADDRPOLICY", "LACPACTIVITY", "LACPTIMER", "FLAGS"); } } -static void -dump_grp_lacp(dladm_aggr_grp_attr_t *grp, boolean_t parseable) +static dladm_status_t +print_aggr_info(show_grp_state_t *state, const char *link, + dladm_aggr_grp_attr_t *ginfop, char **pptr, char *lim) { - char lacp_mode_str[DLADM_STRSIZE]; - char lacp_timer_str[DLADM_STRSIZE]; + char buf[DLADM_STRSIZE]; + char *fmt; + char addr_str[ETHERADDRL * 3]; + char str[ETHERADDRL * 3 + 2]; - (void) dladm_aggr_lacpmode2str(grp->lg_lacp_mode, lacp_mode_str); - (void) dladm_aggr_lacptimer2str(grp->lg_lacp_timer, lacp_timer_str); + if (state->gs_parseable) + fmt = "LINK=\"%s\" POLICY=\"%s\" ADDRPOLICY=\"%s%s\" "; + else + fmt = "%-12s%-8s%-6s%-18s"; - if (!parseable) { - (void) printf(gettext("\t\tLACP mode: %s"), lacp_mode_str); - (void) printf(gettext("\tLACP timer: %s\n"), lacp_timer_str); + if (ginfop->lg_mac_fixed) { + (void) dladm_aggr_macaddr2str(ginfop->lg_mac, addr_str); + (void) snprintf(str, ETHERADDRL * 3 + 3, " (%s)", addr_str); } else { - (void) printf(" lacp-mode=%s", lacp_mode_str); - (void) printf(" lacp-timer=%s\n", lacp_timer_str); + str[0] = '\0'; } -} -static void -dump_grp_stats(dladm_aggr_grp_attr_t *grp) -{ - (void) printf("key: %d", grp->lg_key); - (void) printf("\tipackets rbytes opackets obytes "); - (void) printf("%%ipkts %%opkts\n"); -} + /*LINTED: E_SEC_PRINTF_VAR_FMT*/ + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, link, + dladm_aggr_policy2str(ginfop->lg_policy, buf), + ginfop->lg_mac_fixed ? "fixed" : "auto", str); -static void -dump_ports_lacp_head(void) -{ - (void) printf(DUMP_LACP_FORMAT, gettext("device"), gettext("activity"), - gettext("timeout"), gettext("aggregatable"), gettext("sync"), - gettext("coll"), gettext("dist"), gettext("defaulted"), - gettext("expired")); -} + (void) dladm_aggr_lacpmode2str(ginfop->lg_lacp_mode, buf); + if (state->gs_parseable) { + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "LACPACTIVITY=\"%s\" ", buf); + } else { + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), "%-13s", buf); + } -static void -dump_ports_head(void) -{ - (void) printf(gettext(" device\taddress\t\t speed\t\tduplex\tlink\t" - "state\n")); + (void) dladm_aggr_lacptimer2str(ginfop->lg_lacp_timer, buf); + if (state->gs_parseable) { + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "LACPTIMER=\"%s\" FLAGS=\"%c----\"\n", buf, + ginfop->lg_force ? 'f' : '-'); + } else { + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "%-11s%c----\n", buf, ginfop->lg_force ? 'f' : '-'); + } + + return (DLADM_STATUS_OK); } -static void -dump_port(dladm_aggr_port_attr_t *port, boolean_t parseable) +static dladm_status_t +print_aggr_extended(show_grp_state_t *state, const char *link, + dladm_aggr_grp_attr_t *ginfop, char **pptr, char *lim) { - char *dev = port->lp_devname; - char mac_addr[ETHERADDRL * 3]; - char buf[DLADM_STRSIZE]; + char addr_str[ETHERADDRL * 3]; + char port[MAXLINKNAMELEN]; + dladm_phys_attr_t dpa; + char buf[DLADM_STRSIZE]; + char *fmt; + int i; + dladm_status_t status; - if (!parseable) { - (void) printf(" %-9s\t%s", dev, dladm_aggr_macaddr2str( - port->lp_mac, mac_addr)); - (void) printf("\t %5uMb", (int)(mac_ifspeed(dev) / - 1000000ull)); - (void) printf("\t%s", mac_link_duplex(dev, buf)); - (void) printf("\t%s", mac_link_state(dev, buf)); - (void) printf("\t%s\n", - dladm_aggr_portstate2str(port->lp_state, buf)); + if (state->gs_parseable) + fmt = "LINK=\"%s\" PORT=\"%s\" SPEED=\"%uMb\" DUPLEX=\"%s\" "; + else + fmt = "%-12s%-14s%4uMb %-9s"; + (void) dladm_aggr_macaddr2str(ginfop->lg_mac, addr_str); + + /*LINTED: E_SEC_PRINTF_VAR_FMT*/ + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, link, + state->gs_parseable ? "" : "--", + (uint_t)((get_ifspeed(link, B_TRUE)) / 1000000ull), + get_linkduplex(link, B_TRUE, buf)); + + (void) get_linkstate(link, B_TRUE, buf); + if (state->gs_parseable) { + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "STATE=\"%s\" ADDRESS=\"%s\" PORTSTATE=\"%s\"\n", buf, + addr_str, ""); } else { - (void) printf(" device=%s address=%s", dev, - dladm_aggr_macaddr2str(port->lp_mac, mac_addr)); - (void) printf(" speed=%u", (int)(mac_ifspeed(dev) / - 1000000ull)); - (void) printf(" duplex=%s", mac_link_duplex(dev, buf)); - (void) printf(" link=%s", mac_link_state(dev, buf)); - (void) printf(" port=%s", - dladm_aggr_portstate2str(port->lp_state, buf)); + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), "%-9s%-18s%s\n", + buf, addr_str, "--"); } -} -static void -dump_port_lacp(dladm_aggr_port_attr_t *port) -{ - aggr_lacp_state_t *state = &port->lp_lacp_state; + for (i = 0; i < ginfop->lg_nports; i++) { + dladm_aggr_port_attr_t *portp = &(ginfop->lg_ports[i]); + const char *tmp; + + if ((status = dladm_datalink_id2info(portp->lp_linkid, NULL, + NULL, NULL, port, sizeof (port))) != DLADM_STATUS_OK) { + goto done; + } + + if ((status = dladm_phys_info(portp->lp_linkid, &dpa, + DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) { + goto done; + } + + (void) dladm_aggr_macaddr2str(portp->lp_mac, addr_str); + + if (state->gs_parseable) + tmp = link; + else + tmp = ""; + + /*LINTED: E_SEC_PRINTF_VAR_FMT*/ + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, tmp, port, + (uint_t)((get_ifspeed(dpa.dp_dev, B_FALSE)) / 1000000ull), + get_linkduplex(dpa.dp_dev, B_FALSE, buf)); + + (void) get_linkstate(dpa.dp_dev, B_FALSE, buf); + if (state->gs_parseable) { + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "STATE=\"%s\" ADDRESS=\"%s\" ", buf, addr_str); + } else { + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "%-9s%-18s", buf, addr_str); + } + + (void) dladm_aggr_portstate2str( + ginfop->lg_ports[i].lp_state, buf); + if (state->gs_parseable) { + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "PORTSTATE=\"%s\"\n", buf); + } else { + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "%s\n", buf); + } + } - (void) printf(DUMP_LACP_FORMAT, - port->lp_devname, state->bit.activity ? "active" : "passive", - state->bit.timeout ? "short" : "long", - state->bit.aggregation ? "yes" : "no", - state->bit.sync ? "yes" : "no", - state->bit.collecting ? "yes" : "no", - state->bit.distributing ? "yes" : "no", - state->bit.defaulted ? "yes" : "no", - state->bit.expired ? "yes" : "no"); + status = DLADM_STATUS_OK; +done: + return (status); } -static void -dump_port_stat(int index, show_grp_state_t *state, pktsum_t *port_stats, - pktsum_t *tot_stats) +static dladm_status_t +print_aggr_lacp(show_grp_state_t *state, const char *link, + dladm_aggr_grp_attr_t *ginfop, char **pptr, char *lim) { - pktsum_t diff_stats; - pktsum_t *old_stats = &state->gs_prevstats[index]; - - stats_diff(&diff_stats, port_stats, old_stats); + char port[MAXLINKNAMELEN]; + char *fmt; + const char *dlink = link; + int i; + dladm_status_t status; - (void) printf("\t%-10llu", diff_stats.ipackets); - (void) printf("%-12llu", diff_stats.rbytes); - (void) printf("%-10llu", diff_stats.opackets); - (void) printf("%-12llu", diff_stats.obytes); + if (state->gs_parseable) { + fmt = "LINK=\"%s\" PORT=\"%s\" AGGREGATABLE=\"%s\" SYNC=\"%s\" " + "COLL=\"%s\" DIST=\"%s\" DEFAULTED=\"%s\" EXPITED=\"%s\"\n"; + } else { + fmt = "%-12s%-12s%-13s%-5s%-5s%-5s%-10s%s\n"; + } - if (tot_stats->ipackets == 0) - (void) printf("\t-"); - else - (void) printf("\t%-6.1f", (double)diff_stats.ipackets/ - (double)tot_stats->ipackets * 100); + for (i = 0; i < ginfop->lg_nports; i++) { + aggr_lacp_state_t *lstate; - if (tot_stats->opackets == 0) - (void) printf("\t-"); - else - (void) printf("\t%-6.1f", (double)diff_stats.opackets/ - (double)tot_stats->opackets * 100); + status = dladm_datalink_id2info(ginfop->lg_ports[i].lp_linkid, + NULL, NULL, NULL, port, sizeof (port)); + if (status != DLADM_STATUS_OK) + goto done; - (void) printf("\n"); + /* + * Only display link for the first port. + */ + if ((i > 0) && !(state->gs_parseable)) + dlink = ""; + lstate = &(ginfop->lg_ports[i].lp_lacp_state); + + /*LINTED: E_SEC_PRINTF_VAR_FMT*/ + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, dlink, port, + lstate->bit.aggregation ? "yes" : "no", + lstate->bit.sync ? "yes" : "no", + lstate->bit.collecting ? "yes" : "no", + lstate->bit.distributing ? "yes" : "no", + lstate->bit.defaulted ? "yes" : "no", + lstate->bit.expired ? "yes" : "no"); + } - *old_stats = *port_stats; + status = DLADM_STATUS_OK; +done: + return (status); } -static int -show_key(void *arg, dladm_aggr_grp_attr_t *grp) +static dladm_status_t +print_aggr_stats(show_grp_state_t *state, const char *link, + dladm_aggr_grp_attr_t *ginfop, char **pptr, char *lim) { - show_grp_state_t *state = (show_grp_state_t *)arg; - int i; + char port[MAXLINKNAMELEN]; + dladm_phys_attr_t dpa; + dladm_aggr_port_attr_t *portp; pktsum_t pktsumtot, port_stat; + dladm_status_t status; + int i; - if (state->gs_key != 0 && state->gs_key != grp->lg_key) - return (0); if (state->gs_firstonly) { - if (state->gs_found) - return (0); + if (state->gs_donefirst) + return (DLADM_WALK_CONTINUE); + state->gs_donefirst = B_TRUE; } else { bzero(&state->gs_prevstats, sizeof (state->gs_prevstats)); } - state->gs_found = B_TRUE; + /* sum the ports statistics */ + bzero(&pktsumtot, sizeof (pktsumtot)); - if (state->gs_stats) { - /* show statistics */ - dump_grp_stats(grp); + for (i = 0; i < ginfop->lg_nports; i++) { - /* sum the ports statistics */ - bzero(&pktsumtot, sizeof (pktsumtot)); - for (i = 0; i < grp->lg_nports; i++) { - get_mac_stats(grp->lg_ports[i].lp_devname, &port_stat); - stats_total(&pktsumtot, &port_stat, - &state->gs_prevstats[i]); + portp = &(ginfop->lg_ports[i]); + if ((status = dladm_phys_info(portp->lp_linkid, &dpa, + DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) { + goto done; } - (void) printf(" Total"); - (void) printf("\t%-10llu", pktsumtot.ipackets); - (void) printf("%-12llu", pktsumtot.rbytes); - (void) printf("%-10llu", pktsumtot.opackets); - (void) printf("%-12llu\n", pktsumtot.obytes); + get_mac_stats(dpa.dp_dev, &port_stat); + stats_total(&pktsumtot, &port_stat, &state->gs_prevstats[i]); + } + + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "%-12s%-10s%8llu %8llu %8llu %8llu %8s %8s\n", link, "--", + pktsumtot.ipackets, pktsumtot.rbytes, pktsumtot.opackets, + pktsumtot.obytes, "--", "--"); + + for (i = 0; i < ginfop->lg_nports; i++) { + portp = &(ginfop->lg_ports[i]); - for (i = 0; i < grp->lg_nports; i++) { - get_mac_stats(grp->lg_ports[i].lp_devname, &port_stat); - (void) printf(" %s", grp->lg_ports[i].lp_devname); - dump_port_stat(i, state, &port_stat, &pktsumtot); + if ((status = dladm_phys_info(portp->lp_linkid, &dpa, + DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) { + goto done; } - } else if (state->gs_lacp) { - /* show LACP info */ - dump_grp(grp, state->gs_parseable); - dump_grp_lacp(grp, state->gs_parseable); - dump_ports_lacp_head(); - for (i = 0; i < grp->lg_nports; i++) - dump_port_lacp(&grp->lg_ports[i]); - } else { - dump_grp(grp, state->gs_parseable); - if (!state->gs_parseable) - dump_ports_head(); - for (i = 0; i < grp->lg_nports; i++) { - if (state->gs_parseable) - (void) printf("dev key=%d", grp->lg_key); - dump_port(&grp->lg_ports[i], state->gs_parseable); - if (state->gs_parseable) - (void) printf("\n"); + + get_mac_stats(dpa.dp_dev, &port_stat); + + if ((status = dladm_datalink_id2info(portp->lp_linkid, NULL, + NULL, NULL, port, sizeof (port))) != DLADM_STATUS_OK) { + goto done; } + + print_port_stat(port, &state->gs_prevstats[i], &port_stat, + &pktsumtot, pptr, lim); } - return (0); + status = DLADM_STATUS_OK; +done: + return (status); +} + +static dladm_status_t +print_aggr(show_grp_state_t *state, datalink_id_t linkid, char **pptr, + char *lim) +{ + char link[MAXLINKNAMELEN]; + dladm_aggr_grp_attr_t ginfo; + uint32_t flags; + dladm_status_t status; + + if ((status = dladm_datalink_id2info(linkid, &flags, NULL, NULL, link, + sizeof (link))) != DLADM_STATUS_OK) { + return (status); + } + + if (!(state->gs_flags & flags)) + return (DLADM_STATUS_NOTFOUND); + + status = dladm_aggr_info(linkid, &ginfo, state->gs_flags); + if (status != DLADM_STATUS_OK) + return (status); + + if (state->gs_lacp) + status = print_aggr_lacp(state, link, &ginfo, pptr, lim); + else if (state->gs_extended) + status = print_aggr_extended(state, link, &ginfo, pptr, lim); + else if (state->gs_stats) + status = print_aggr_stats(state, link, &ginfo, pptr, lim); + else + status = print_aggr_info(state, link, &ginfo, pptr, lim); + +done: + free(ginfo.lg_ports); + return (status); +} + +static int +show_aggr(datalink_id_t linkid, void *arg) +{ + show_grp_state_t *state = arg; + dladm_status_t status; + char buf[MAXLINELEN]; + char *ptr = buf, *lim = buf + MAXLINELEN; + + status = print_aggr(state, linkid, &ptr, lim); + if (status != DLADM_STATUS_OK) + goto done; + print_aggr_head(state); + (void) printf("%s", buf); + +done: + state->gs_status = status; + return (DLADM_WALK_CONTINUE); } static int @@ -1044,77 +1749,90 @@ kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf) return (0); } -static void -show_dev(void *arg, const char *dev) +static int +show_dev(const char *dev, void *arg) { - show_mac_state_t *state = (show_mac_state_t *)arg; - char buf[DLADM_STRSIZE]; + show_state_t *state = arg; + char buf[DLADM_STRSIZE]; + char *fmt; - (void) printf("%s", dev); + if (state->ls_parseable) + fmt = "DEV=\"%s\" STATE=\"%s\" SPEED=\"%u\" "; + else + fmt = "%-12s%-10s%4uMb "; - if (!state->ms_parseable) { - (void) printf(gettext("\t\tlink: %s"), - mac_link_state(dev, buf)); - (void) printf(gettext("\tspeed: %5uMb"), - (unsigned int)(mac_ifspeed(dev) / 1000000ull)); - (void) printf(gettext("\tduplex: %s\n"), - mac_link_duplex(dev, buf)); - } else { - (void) printf(" link=%s", mac_link_state(dev, buf)); - (void) printf(" speed=%u", - (unsigned int)(mac_ifspeed(dev) / 1000000ull)); - (void) printf(" duplex=%s\n", mac_link_duplex(dev, buf)); + if (!state->ls_donefirst) { + if (!state->ls_parseable) { + (void) printf("%-12s%-10s%6s %s\n", "DEV", "STATE", + "SPEED", "DUPLEX"); + } + state->ls_donefirst = B_TRUE; } + + /*LINTED: E_SEC_PRINTF_VAR_FMT*/ + (void) printf(fmt, dev, get_linkstate(dev, B_FALSE, buf), + (uint_t)(get_ifspeed(dev, B_FALSE) / 1000000ull)); + + (void) get_linkduplex(dev, B_FALSE, buf); + if (state->ls_parseable) + (void) printf("DUPLEX=\"%s\"\n", buf); + else + (void) printf("%s\n", buf); + + return (DLADM_WALK_CONTINUE); } -/*ARGSUSED*/ -static void -show_dev_stats(void *arg, const char *dev) +static int +show_dev_stats(const char *dev, void *arg) { - show_mac_state_t *state = (show_mac_state_t *)arg; + show_state_t *state = arg; pktsum_t stats, diff_stats; - if (state->ms_firstonly) { - if (state->ms_donefirst) - return; - state->ms_donefirst = B_TRUE; + if (state->ls_firstonly) { + if (state->ls_donefirst) + return (DLADM_WALK_CONTINUE); + state->ls_donefirst = B_TRUE; } else { - bzero(&state->ms_prevstats, sizeof (state->ms_prevstats)); + bzero(&state->ls_prevstats, sizeof (state->ls_prevstats)); } get_mac_stats(dev, &stats); - stats_diff(&diff_stats, &stats, &state->ms_prevstats); + stats_diff(&diff_stats, &stats, &state->ls_prevstats); - (void) printf("%s", dev); - (void) printf("\t\t%-10llu", diff_stats.ipackets); + (void) printf("%-12s", dev); + (void) printf("%-10llu", diff_stats.ipackets); (void) printf("%-12llu", diff_stats.rbytes); (void) printf("%-8u", diff_stats.ierrors); (void) printf("%-10llu", diff_stats.opackets); (void) printf("%-12llu", diff_stats.obytes); (void) printf("%-8u\n", diff_stats.oerrors); - state->ms_prevstats = stats; + state->ls_prevstats = stats; + return (DLADM_WALK_CONTINUE); } static void do_show_link(int argc, char *argv[]) { - char *name = NULL; int option; boolean_t s_arg = B_FALSE; boolean_t i_arg = B_FALSE; + uint32_t flags = DLADM_OPT_ACTIVE; + boolean_t p_arg = B_FALSE; + datalink_id_t linkid = DATALINK_ALL_LINKID; int interval = 0; - show_link_state_t state; - - state.ls_stats = B_FALSE; - state.ls_parseable = B_FALSE; + show_state_t state; + dladm_status_t status; opterr = 0; - while ((option = getopt_long(argc, argv, ":psi:", - longopts, NULL)) != -1) { + while ((option = getopt_long(argc, argv, ":pPsi:", + show_lopts, NULL)) != -1) { switch (option) { case 'p': - state.ls_parseable = B_TRUE; + if (p_arg) + die_optdup(option); + + p_arg = B_TRUE; break; case 's': if (s_arg) @@ -1122,6 +1840,12 @@ do_show_link(int argc, char *argv[]) s_arg = B_TRUE; break; + case 'P': + if (flags != DLADM_OPT_ACTIVE) + die_optdup(option); + + flags = DLADM_OPT_PERSIST; + break; case 'i': if (i_arg) die_optdup(option); @@ -1139,74 +1863,101 @@ do_show_link(int argc, char *argv[]) if (i_arg && !s_arg) die("the option -i can be used only with -s"); + if (s_arg && (p_arg || flags != DLADM_OPT_ACTIVE)) + die("the option -%c cannot be used with -s", p_arg ? 'p' : 'P'); + /* get link name (optional last argument) */ - if (optind == (argc-1)) - name = argv[optind]; - else if (optind != argc) + if (optind == (argc-1)) { + uint32_t f; + + if ((status = dladm_name2info(argv[optind], &linkid, &f, + NULL, NULL)) != DLADM_STATUS_OK) { + die_dlerr(status, "link %s is not valid", argv[optind]); + } + + if (!(f & flags)) { + die_dlerr(DLADM_STATUS_BADARG, "link %s is %s", + argv[optind], flags == DLADM_OPT_PERSIST ? + "a temporary link" : "temporarily removed"); + } + } else if (optind != argc) { usage(); + } if (s_arg) { - link_stats(name, interval); + link_stats(linkid, interval); return; } - if (name == NULL) { - (void) dladm_walk(show_link, &state); + state.ls_parseable = p_arg; + state.ls_flags = flags; + state.ls_donefirst = B_FALSE; + if (linkid == DATALINK_ALL_LINKID) { + (void) dladm_walk_datalink_id(show_link, &state, + DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, flags); } else { - show_link(&state, name); + (void) show_link(linkid, &state); + if (state.ls_status != DLADM_STATUS_OK) { + die_dlerr(state.ls_status, "failed to show link %s", + argv[optind]); + } } } static void do_show_aggr(int argc, char *argv[]) { - int option; - int key = 0; boolean_t L_arg = B_FALSE; boolean_t s_arg = B_FALSE; boolean_t i_arg = B_FALSE; + boolean_t p_arg = B_FALSE; + boolean_t x_arg = B_FALSE; show_grp_state_t state; + uint32_t flags = DLADM_OPT_ACTIVE; + datalink_id_t linkid = DATALINK_ALL_LINKID; + int option; int interval = 0; - - state.gs_stats = B_FALSE; - state.gs_lacp = B_FALSE; - state.gs_parseable = B_FALSE; + int key; + dladm_status_t status; opterr = 0; - while ((option = getopt_long(argc, argv, ":Lpsi:", - longopts, NULL)) != -1) { + while ((option = getopt_long(argc, argv, ":LpPxsi:", + show_lopts, NULL)) != -1) { switch (option) { case 'L': if (L_arg) die_optdup(option); - if (s_arg || i_arg) { - die("the option -L cannot be used with -i " - "or -s"); - } - L_arg = B_TRUE; - state.gs_lacp = B_TRUE; break; case 'p': - state.gs_parseable = B_TRUE; + if (p_arg) + die_optdup(option); + + p_arg = B_TRUE; + break; + case 'x': + if (x_arg) + die_optdup(option); + + x_arg = B_TRUE; + break; + case 'P': + if (flags != DLADM_OPT_ACTIVE) + die_optdup(option); + + flags = DLADM_OPT_PERSIST; break; case 's': if (s_arg) die_optdup(option); - if (L_arg) - die("the option -s cannot be used with -L"); - s_arg = B_TRUE; break; case 'i': if (i_arg) die_optdup(option); - if (L_arg) - die("the option -i cannot be used with -L"); - i_arg = B_TRUE; if (!str2int(optarg, &interval) || interval == 0) die("invalid interval value '%s'", optarg); @@ -1220,26 +1971,56 @@ do_show_aggr(int argc, char *argv[]) if (i_arg && !s_arg) die("the option -i can be used only with -s"); - /* get aggregation key (optional last argument) */ + if (s_arg && (L_arg || p_arg || x_arg || flags != DLADM_OPT_ACTIVE)) { + die("the option -%c cannot be used with -s", + L_arg ? 'L' : (p_arg ? 'p' : (x_arg ? 'x' : 'P'))); + } + + if (L_arg && flags != DLADM_OPT_ACTIVE) + die("the option -P cannot be used with -L"); + + if (x_arg && (L_arg || flags != DLADM_OPT_ACTIVE)) + die("the option -%c cannot be used with -x", L_arg ? 'L' : 'P'); + + /* get aggregation key or aggrname (optional last argument) */ if (optind == (argc-1)) { - if (!str2int(argv[optind], &key) || key < 1) - die("invalid key value '%s'", argv[optind]); + if (!str2int(argv[optind], &key)) { + status = dladm_name2info(argv[optind], &linkid, NULL, + NULL, NULL); + } else { + status = dladm_key2linkid((uint16_t)key, + &linkid, DLADM_OPT_ACTIVE); + } + + if (status != DLADM_STATUS_OK) + die("non-existent aggregation '%s'", argv[optind]); + } else if (optind != argc) { usage(); } + bzero(&state, sizeof (state)); + state.gs_lacp = L_arg; + state.gs_stats = s_arg; + state.gs_flags = flags; + state.gs_parseable = p_arg; + state.gs_extended = x_arg; + if (s_arg) { - aggr_stats(key, interval); + aggr_stats(linkid, &state, interval); return; } - state.gs_key = key; - state.gs_found = B_FALSE; - - (void) dladm_aggr_walk(show_key, &state); - - if (key != 0 && !state.gs_found) - die("non-existent aggregation key '%u'", key); + if (linkid == DATALINK_ALL_LINKID) { + (void) dladm_walk_datalink_id(show_aggr, &state, + DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, flags); + } else { + (void) show_aggr(linkid, &state); + if (state.gs_status != DLADM_STATUS_OK) { + die_dlerr(state.gs_status, "failed to show aggr %s", + argv[optind]); + } + } } static void @@ -1249,17 +2030,20 @@ do_show_dev(int argc, char *argv[]) char *dev = NULL; boolean_t s_arg = B_FALSE; boolean_t i_arg = B_FALSE; + boolean_t p_arg = B_FALSE; + datalink_id_t linkid; int interval = 0; - show_mac_state_t state; - - state.ms_parseable = B_FALSE; + show_state_t state; opterr = 0; while ((option = getopt_long(argc, argv, ":psi:", - longopts, NULL)) != -1) { + show_lopts, NULL)) != -1) { switch (option) { case 'p': - state.ms_parseable = B_TRUE; + if (p_arg) + die_optdup(option); + + p_arg = B_TRUE; break; case 's': if (s_arg) @@ -1284,26 +2068,25 @@ do_show_dev(int argc, char *argv[]) if (i_arg && !s_arg) die("the option -i can be used only with -s"); + if (s_arg && p_arg) + die("the option -s cannot be used with -p"); + /* get dev name (optional last argument) */ - if (optind == (argc-1)) + if (optind == (argc-1)) { + uint32_t flags; + dev = argv[optind]; - else if (optind != argc) - usage(); - if (dev != NULL) { - uint_t ppa; - char drv[DLPI_LINKNAME_MAX]; - dladm_attr_t dlattr; - boolean_t legacy; + if (dladm_dev2linkid(dev, &linkid) != DLADM_STATUS_OK) + die("invalid device %s", dev); - /* - * Check for invalid devices. - * aggregations and vlans are not considered devices. - */ - if (dlpi_parselink(dev, drv, &ppa) != DLPI_SUCCESS || - strcmp(drv, "aggr") == 0 || ppa >= 1000 || - get_if_info(dev, &dlattr, &legacy) < 0) - die("invalid device '%s'", dev); + if ((dladm_datalink_id2info(linkid, &flags, NULL, NULL, + NULL, 0) != DLADM_STATUS_OK) || + !(flags & DLADM_OPT_ACTIVE)) { + die("device %s has been removed", dev); + } + } else if (optind != argc) { + usage(); } if (s_arg) { @@ -1311,22 +2094,336 @@ do_show_dev(int argc, char *argv[]) return; } - if (dev == NULL) + state.ls_donefirst = B_FALSE; + state.ls_parseable = p_arg; + if (dev == NULL) { (void) dladm_mac_walk(show_dev, &state); + } else { + (void) show_dev(dev, &state); + } +} + +static void +print_phys_head(show_state_t *state) +{ + if (state->ls_donefirst) + return; + state->ls_donefirst = B_TRUE; + + if (state->ls_parseable) + return; + + if (state->ls_flags == DLADM_OPT_ACTIVE) { + (void) printf("%-12s%-20s%-10s%6s %-9s%s\n", "LINK", + "MEDIA", "STATE", "SPEED", "DUPLEX", "DEVICE"); + } else { + (void) printf("%-12s%-12s%-20s%s\n", "LINK", "DEVICE", + "MEDIA", "FLAGS"); + } +} + +static dladm_status_t +print_phys(show_state_t *state, datalink_id_t linkid, char **pptr, char *lim) +{ + char link[MAXLINKNAMELEN]; + dladm_phys_attr_t dpa; + char buf[DLADM_STRSIZE]; + uint32_t flags; + datalink_class_t class; + uint32_t media; + dladm_status_t status; + + if ((status = dladm_datalink_id2info(linkid, &flags, &class, &media, + link, sizeof (link))) != DLADM_STATUS_OK) { + goto done; + } + + if (class != DATALINK_CLASS_PHYS) { + status = DLADM_STATUS_BADARG; + goto done; + } + + if (!(state->ls_flags & flags)) { + status = DLADM_STATUS_NOTFOUND; + goto done; + } + + status = dladm_phys_info(linkid, &dpa, state->ls_flags); + if (status != DLADM_STATUS_OK) + goto done; + + if (state->ls_flags == DLADM_OPT_ACTIVE) { + char name[MAXLINKNAMELEN]; + boolean_t islink; + + if (!dpa.dp_novanity) { + (void) strlcpy(name, link, sizeof (name)); + islink = B_TRUE; + } else { + /* + * This is a physical link that does not have + * vanity naming support. + */ + (void) strlcpy(name, dpa.dp_dev, sizeof (name)); + islink = B_FALSE; + } + + if (state->ls_parseable) { + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "LINK=\"%s\" MEDIA=\"%s\" ", link, + dladm_media2str(media, buf)); + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "STATE=\"%s\" SPEED=\"%uMb\" ", + get_linkstate(name, islink, buf), + (uint_t)((get_ifspeed(name, islink)) / 1000000ull)); + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "DUPLEX=\"%s\" DEVICE=\"%s\"\n", + get_linkduplex(name, islink, buf), dpa.dp_dev); + } else { + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "%-12s%-20s", link, + dladm_media2str(media, buf)); + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "%-10s%4uMb ", + get_linkstate(name, islink, buf), + (uint_t)((get_ifspeed(name, islink)) / 1000000ull)); + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "%-9s%s\n", get_linkduplex(name, islink, buf), + dpa.dp_dev); + } + } else { + if (state->ls_parseable) { + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "LINK=\"%s\" DEVICE=\"%s\" MEDIA=\"%s\" " + "FLAGS=\"%c----\"\n", link, dpa.dp_dev, + dladm_media2str(media, buf), + flags & DLADM_OPT_ACTIVE ? '-' : 'r'); + } else { + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), + "%-12s%-12s%-20s%c----\n", link, + dpa.dp_dev, dladm_media2str(media, buf), + flags & DLADM_OPT_ACTIVE ? '-' : 'r'); + } + } + +done: + return (status); +} + +static int +show_phys(datalink_id_t linkid, void *arg) +{ + show_state_t *state = arg; + dladm_status_t status; + char buf[MAXLINELEN]; + char *ptr = buf, *lim = buf + MAXLINELEN; + + status = print_phys(state, linkid, &ptr, lim); + if (status != DLADM_STATUS_OK) + goto done; + print_phys_head(state); + (void) printf("%s", buf); + +done: + state->ls_status = status; + return (DLADM_WALK_CONTINUE); +} + +static void +print_vlan_head(show_state_t *state) +{ + if (state->ls_donefirst) + return; + state->ls_donefirst = B_TRUE; + + if (state->ls_parseable) + return; + + (void) printf("%-12s%5s %-12s%s\n", "LINK", "VID", "OVER", "FLAGS"); +} + +/* + * Print the active topology information. + */ +static dladm_status_t +print_vlan(show_state_t *state, datalink_id_t linkid, char **pptr, char *lim) +{ + char link[MAXLINKNAMELEN]; + char over[MAXLINKNAMELEN]; + char *fmt; + dladm_vlan_attr_t vinfo; + uint32_t flags; + dladm_status_t status; + + if ((status = dladm_datalink_id2info(linkid, &flags, NULL, NULL, link, + sizeof (link))) != DLADM_STATUS_OK) { + goto done; + } + + if (!(state->ls_flags & flags)) { + status = DLADM_STATUS_NOTFOUND; + goto done; + } + + if ((status = dladm_vlan_info(linkid, &vinfo, state->ls_flags)) != + DLADM_STATUS_OK || (status = dladm_datalink_id2info( + vinfo.dv_linkid, NULL, NULL, NULL, over, sizeof (over))) != + DLADM_STATUS_OK) { + goto done; + } + + if (state->ls_parseable) + fmt = "LINK=\"%s\" VID=\"%d\" OVER=\"%s\" FLAGS=\"%c%c---\"\n"; else - show_dev(&state, dev); + fmt = "%-12s%5d %-12s%c%c---\n"; + /*LINTED: E_SEC_PRINTF_VAR_FMT*/ + *pptr += snprintf(*pptr, BUFLEN(lim, *pptr), fmt, link, + vinfo.dv_vid, over, vinfo.dv_force ? 'f' : '-', + vinfo.dv_implicit ? 'i' : '-'); + +done: + return (status); +} + +static int +show_vlan(datalink_id_t linkid, void *arg) +{ + show_state_t *state = arg; + dladm_status_t status; + char buf[MAXLINELEN]; + char *ptr = buf, *lim = buf + MAXLINELEN; + + status = print_vlan(state, linkid, &ptr, lim); + if (status != DLADM_STATUS_OK) + goto done; + print_vlan_head(state); + (void) printf("%s", buf); + +done: + state->ls_status = status; + return (DLADM_WALK_CONTINUE); +} + +static void +do_show_phys(int argc, char *argv[]) +{ + int option; + uint32_t flags = DLADM_OPT_ACTIVE; + boolean_t p_arg = B_FALSE; + datalink_id_t linkid = DATALINK_ALL_LINKID; + show_state_t state; + dladm_status_t status; + + opterr = 0; + while ((option = getopt_long(argc, argv, ":pP", + show_lopts, NULL)) != -1) { + switch (option) { + case 'p': + if (p_arg) + die_optdup(option); + + p_arg = B_TRUE; + break; + case 'P': + if (flags != DLADM_OPT_ACTIVE) + die_optdup(option); + + flags = DLADM_OPT_PERSIST; + break; + default: + die_opterr(optopt, option); + break; + } + } + + /* get link name (optional last argument) */ + if (optind == (argc-1)) { + if ((status = dladm_name2info(argv[optind], &linkid, NULL, + NULL, NULL)) != DLADM_STATUS_OK) { + die_dlerr(status, "link %s is not valid", argv[optind]); + } + } else if (optind != argc) { + usage(); + } + + state.ls_parseable = p_arg; + state.ls_flags = flags; + state.ls_donefirst = B_FALSE; + + if (linkid == DATALINK_ALL_LINKID) { + (void) dladm_walk_datalink_id(show_phys, &state, + DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, flags); + } else { + (void) show_phys(linkid, &state); + if (state.ls_status != DLADM_STATUS_OK) { + die_dlerr(state.ls_status, + "failed to show physical link %s", argv[optind]); + } + } } -/* ARGSUSED */ static void -link_stats(const char *link, uint_t interval) +do_show_vlan(int argc, char *argv[]) { - dladm_attr_t dlattr; - boolean_t legacy; - show_link_state_t state; + int option; + uint32_t flags = DLADM_OPT_ACTIVE; + boolean_t p_arg = B_FALSE; + datalink_id_t linkid = DATALINK_ALL_LINKID; + show_state_t state; + dladm_status_t status; - if (link != NULL && get_if_info(link, &dlattr, &legacy) < 0) - die("invalid link '%s'", link); + opterr = 0; + while ((option = getopt_long(argc, argv, ":pP", + show_lopts, NULL)) != -1) { + switch (option) { + case 'p': + if (p_arg) + die_optdup(option); + + p_arg = B_TRUE; + break; + case 'P': + if (flags != DLADM_OPT_ACTIVE) + die_optdup(option); + + flags = DLADM_OPT_PERSIST; + break; + default: + die_opterr(optopt, option); + break; + } + } + + /* get link name (optional last argument) */ + if (optind == (argc-1)) { + if ((status = dladm_name2info(argv[optind], &linkid, NULL, + NULL, NULL)) != DLADM_STATUS_OK) { + die_dlerr(status, "link %s is not valid", argv[optind]); + } + } else if (optind != argc) { + usage(); + } + + state.ls_parseable = p_arg; + state.ls_flags = flags; + state.ls_donefirst = B_FALSE; + + if (linkid == DATALINK_ALL_LINKID) { + (void) dladm_walk_datalink_id(show_vlan, &state, + DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, flags); + } else { + (void) show_vlan(linkid, &state); + if (state.ls_status != DLADM_STATUS_OK) { + die_dlerr(state.ls_status, "failed to show vlan %s", + argv[optind]); + } + } +} + +static void +link_stats(datalink_id_t linkid, uint_t interval) +{ + show_state_t state; bzero(&state, sizeof (state)); @@ -1337,14 +2434,18 @@ link_stats(const char *link, uint_t interval) state.ls_firstonly = (interval != 0); for (;;) { - (void) printf("\t\tipackets rbytes ierrors "); - (void) printf("opackets obytes oerrors\n"); + (void) printf("%-12s%-10s%-12s%-8s%-10s%-12s%-8s\n", + "LINK", "IPACKETS", "RBYTES", "IERRORS", "OPACKETS", + "OBYTES", "OERRORS"); state.ls_donefirst = B_FALSE; - if (link == NULL) - (void) dladm_walk(show_link_stats, &state); - else - show_link_stats(&state, link); + if (linkid == DATALINK_ALL_LINKID) { + (void) dladm_walk_datalink_id(show_link_stats, &state, + DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, + DLADM_OPT_ACTIVE); + } else { + (void) show_link_stats(linkid, &state); + } if (interval == 0) break; @@ -1353,27 +2454,28 @@ link_stats(const char *link, uint_t interval) } } -/* ARGSUSED */ static void -aggr_stats(uint32_t key, uint_t interval) +aggr_stats(datalink_id_t linkid, show_grp_state_t *state, uint_t interval) { - show_grp_state_t state; - - bzero(&state, sizeof (state)); - state.gs_stats = B_TRUE; - state.gs_key = key; - /* * If an interval is specified, continuously show the stats * only for the first group. */ - state.gs_firstonly = (interval != 0); + state->gs_firstonly = (interval != 0); for (;;) { - state.gs_found = B_FALSE; - (void) dladm_aggr_walk(show_key, &state); - if (state.gs_key != 0 && !state.gs_found) - die("non-existent aggregation key '%u'", key); + + (void) printf("%-12s%-10s%8s %8s %8s %8s %-9s%s\n", + "LINK", "PORT", "IPACKETS", "RBYTES", "OPACKETS", + "OBYTES", "IPKTDIST", "OPKTDIST"); + + state->gs_donefirst = B_FALSE; + if (linkid == DATALINK_ALL_LINKID) + (void) dladm_walk_datalink_id(show_aggr, state, + DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, + DLADM_OPT_ACTIVE); + else + (void) show_aggr(linkid, state); if (interval == 0) break; @@ -1382,11 +2484,10 @@ aggr_stats(uint32_t key, uint_t interval) } } -/* ARGSUSED */ static void dev_stats(const char *dev, uint32_t interval) { - show_mac_state_t state; + show_state_t state; bzero(&state, sizeof (state)); @@ -1394,24 +2495,28 @@ dev_stats(const char *dev, uint32_t interval) * If an interval is specified, continuously show the stats * only for the first MAC port. */ - state.ms_firstonly = (interval != 0); + state.ls_firstonly = (interval != 0); for (;;) { - (void) printf("\t\tipackets rbytes ierrors "); - (void) printf("opackets obytes oerrors\n"); + (void) printf("%-12s%-10s%-12s%-8s%-10s%-12s%-8s\n", + "DEV", "IPACKETS", "RBYTES", "IERRORS", "OPACKETS", + "OBYTES", "OERRORS"); - state.ms_donefirst = B_FALSE; + state.ls_donefirst = B_FALSE; if (dev == NULL) (void) dladm_mac_walk(show_dev_stats, &state); else - show_dev_stats(&state, dev); + (void) show_dev_stats(dev, &state); if (interval == 0) break; (void) sleep(interval); } + + if (dev != NULL && state.ls_status != DLADM_STATUS_OK) + die_dlerr(state.ls_status, "cannot show device '%s'", dev); } /* accumulate stats (s1 += (s2 - s3)) */ @@ -1438,16 +2543,8 @@ stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3) s1->oerrors = s2->oerrors - s3->oerrors; } -/* - * In the following routines, we do the first kstat_lookup() assuming that - * the device is gldv3-based and that the kstat name is the one passed in - * as the "name" argument. If the lookup fails, we redo the kstat_lookup() - * omitting the kstat name. This second lookup is needed for getting kstats - * from legacy devices. This can fail too if the device is not attached or - * the device is legacy and doesn't export the kstats we need. - */ static void -get_stats(char *module, int instance, char *name, pktsum_t *stats) +get_stats(char *module, int instance, const char *name, pktsum_t *stats) { kstat_ctl_t *kcp; kstat_t *ksp; @@ -1457,8 +2554,7 @@ get_stats(char *module, int instance, char *name, pktsum_t *stats) return; } - if ((ksp = kstat_lookup(kcp, module, instance, name)) == NULL && - (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL) { + if ((ksp = kstat_lookup(kcp, module, instance, (char *)name)) == NULL) { /* * The kstat query could fail if the underlying MAC * driver was already detached. @@ -1494,56 +2590,45 @@ get_stats(char *module, int instance, char *name, pktsum_t *stats) &stats->oerrors) < 0) goto bail; +bail: (void) kstat_close(kcp); return; -bail: - (void) kstat_close(kcp); } static void get_mac_stats(const char *dev, pktsum_t *stats) { - char module[DLPI_LINKNAME_MAX]; - uint_t instance; + char module[DLPI_LINKNAME_MAX]; + uint_t instance; + bzero(stats, sizeof (*stats)); if (dlpi_parselink(dev, module, &instance) != DLPI_SUCCESS) return; - bzero(stats, sizeof (*stats)); + get_stats(module, instance, "mac", stats); } static void get_link_stats(const char *link, pktsum_t *stats) { - char module[DLPI_LINKNAME_MAX]; - uint_t instance; - - if (dlpi_parselink(link, module, &instance) != DLPI_SUCCESS) - return; bzero(stats, sizeof (*stats)); - get_stats(module, instance, (char *)link, stats); + get_stats("link", 0, link, stats); } static int -get_single_mac_stat(const char *dev, const char *name, uint8_t type, - void *val) +query_kstat(char *module, int instance, const char *name, const char *stat, + uint8_t type, void *val) { - char module[DLPI_LINKNAME_MAX]; - uint_t instance; kstat_ctl_t *kcp; kstat_t *ksp; - if (dlpi_parselink(dev, module, &instance) != DLPI_SUCCESS) - return (-1); - if ((kcp = kstat_open()) == NULL) { warn("kstat open operation failed"); return (-1); } - if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL && - (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL) { + if ((ksp = kstat_lookup(kcp, module, instance, (char *)name)) == NULL) { /* * The kstat query could fail if the underlying MAC * driver was already detached. @@ -1556,7 +2641,7 @@ get_single_mac_stat(const char *dev, const char *name, uint8_t type, goto bail; } - if (kstat_value(ksp, name, type, val) < 0) + if (kstat_value(ksp, stat, type, val) < 0) goto bail; (void) kstat_close(kcp); @@ -1567,41 +2652,59 @@ bail: return (-1); } +static int +get_one_kstat(const char *name, const char *stat, uint8_t type, + void *val, boolean_t islink) +{ + char module[DLPI_LINKNAME_MAX]; + uint_t instance; + + if (islink) { + return (query_kstat("link", 0, name, stat, type, val)); + } else { + if (dlpi_parselink(name, module, &instance) != DLPI_SUCCESS) + return (-1); + + return (query_kstat(module, instance, "mac", stat, type, val)); + } +} + static uint64_t -mac_ifspeed(const char *dev) +get_ifspeed(const char *name, boolean_t islink) { uint64_t ifspeed = 0; - (void) get_single_mac_stat(dev, "ifspeed", KSTAT_DATA_UINT64, &ifspeed); + (void) get_one_kstat(name, "ifspeed", KSTAT_DATA_UINT64, + &ifspeed, islink); + return (ifspeed); } static const char * -mac_link_state(const char *dev, char *buf) +get_linkstate(const char *name, boolean_t islink, char *buf) { - link_state_t link_state; + link_state_t linkstate; - if (get_single_mac_stat(dev, "link_state", KSTAT_DATA_UINT32, - &link_state) != 0) { + if (get_one_kstat(name, "link_state", KSTAT_DATA_UINT32, + &linkstate, islink) != 0) { (void) strlcpy(buf, "unknown", DLADM_STRSIZE); return (buf); } - - return (dladm_linkstate2str(link_state, buf)); + return (dladm_linkstate2str(linkstate, buf)); } static const char * -mac_link_duplex(const char *dev, char *buf) +get_linkduplex(const char *name, boolean_t islink, char *buf) { - link_duplex_t link_duplex; + link_duplex_t linkduplex; - if (get_single_mac_stat(dev, "link_duplex", KSTAT_DATA_UINT32, - &link_duplex) != 0) { + if (get_one_kstat(name, "link_duplex", KSTAT_DATA_UINT32, + &linkduplex, islink) != 0) { (void) strlcpy(buf, "unknown", DLADM_STRSIZE); return (buf); } - return (dladm_linkduplex2str(link_duplex, buf)); + return (dladm_linkduplex2str(linkduplex, buf)); } #define WIFI_CMD_SCAN 0x00000001 @@ -1616,17 +2719,17 @@ typedef struct wifi_field { } wifi_field_t; static wifi_field_t wifi_fields[] = { -{ "link", "LINK", 10, 0, WIFI_CMD_ALL}, +{ "link", "LINK", 10, 0, WIFI_CMD_ALL}, { "essid", "ESSID", 19, DLADM_WLAN_ATTR_ESSID, WIFI_CMD_ALL}, { "bssid", "BSSID/IBSSID", 17, DLADM_WLAN_ATTR_BSSID, WIFI_CMD_ALL}, { "ibssid", "BSSID/IBSSID", 17, DLADM_WLAN_ATTR_BSSID, WIFI_CMD_ALL}, { "mode", "MODE", 6, DLADM_WLAN_ATTR_MODE, WIFI_CMD_ALL}, { "speed", "SPEED", 6, DLADM_WLAN_ATTR_SPEED, WIFI_CMD_ALL}, { "auth", "AUTH", 8, DLADM_WLAN_ATTR_AUTH, WIFI_CMD_SHOW}, -{ "bsstype", "BSSTYPE", 8, DLADM_WLAN_ATTR_BSSTYPE, WIFI_CMD_ALL}, -{ "sec", "SEC", 6, DLADM_WLAN_ATTR_SECMODE, WIFI_CMD_ALL}, -{ "status", "STATUS", 17, DLADM_WLAN_LINKATTR_STATUS, WIFI_CMD_SHOW}, -{ "strength", "STRENGTH", 10, DLADM_WLAN_ATTR_STRENGTH, WIFI_CMD_ALL}} +{ "bsstype", "BSSTYPE", 8, DLADM_WLAN_ATTR_BSSTYPE, WIFI_CMD_ALL}, +{ "sec", "SEC", 6, DLADM_WLAN_ATTR_SECMODE, WIFI_CMD_ALL}, +{ "status", "STATUS", 17, DLADM_WLAN_LINKATTR_STATUS, WIFI_CMD_SHOW}, +{ "strength", "STRENGTH", 10, DLADM_WLAN_ATTR_STRENGTH, WIFI_CMD_ALL}} ; static char *all_scan_wifi_fields = @@ -1751,7 +2854,7 @@ fail: } typedef struct print_wifi_state { - const char *ws_link; + char *ws_link; boolean_t ws_parseable; boolean_t ws_header; wifi_field_t **ws_fields; @@ -1877,18 +2980,24 @@ print_scan_results(void *arg, dladm_wlan_attr_t *attrp) return (B_TRUE); } -static boolean_t -scan_wifi(void *arg, const char *link) +static int +scan_wifi(datalink_id_t linkid, void *arg) { print_wifi_state_t *statep = arg; dladm_status_t status; + char link[MAXLINKNAMELEN]; + + if (dladm_datalink_id2info(linkid, NULL, NULL, NULL, link, + sizeof (link)) != DLADM_STATUS_OK) { + return (DLADM_WALK_CONTINUE); + } statep->ws_link = link; - status = dladm_wlan_scan(link, statep, print_scan_results); + status = dladm_wlan_scan(linkid, statep, print_scan_results); if (status != DLADM_STATUS_OK) - die_dlerr(status, "cannot scan link '%s'", link); + die_dlerr(status, "cannot scan link '%s'", statep->ws_link); - return (B_TRUE); + return (DLADM_WALK_CONTINUE); } static void @@ -1907,17 +3016,25 @@ print_link_attr(print_wifi_state_t *statep, wifi_field_t *wfp, print_wlan_attr(statep, wfp, &attrp->la_wlan_attr); } -static boolean_t -show_wifi(void *arg, const char *link) +static int +show_wifi(datalink_id_t linkid, void *arg) { int i; print_wifi_state_t *statep = arg; dladm_wlan_linkattr_t attr; dladm_status_t status; + char link[MAXLINKNAMELEN]; + + if (dladm_datalink_id2info(linkid, NULL, NULL, NULL, link, + sizeof (link)) != DLADM_STATUS_OK) { + return (DLADM_WALK_CONTINUE); + } - status = dladm_wlan_get_linkattr(link, &attr); + status = dladm_wlan_get_linkattr(linkid, &attr); if (status != DLADM_STATUS_OK) - die_dlerr(status, "cannot get link attributes for '%s'", link); + die_dlerr(status, "cannot get link attributes for %s", link); + + statep->ws_link = link; if (statep->ws_header) { statep->ws_header = B_FALSE; @@ -1925,14 +3042,13 @@ show_wifi(void *arg, const char *link) print_wifi_head(statep); } - statep->ws_link = link; statep->ws_overflow = 0; for (i = 0; i < statep->ws_nfields; i++) { statep->ws_lastfield = (i + 1 == statep->ws_nfields); print_link_attr(statep, statep->ws_fields[i], &attr); } (void) putchar('\n'); - return (B_TRUE); + return (DLADM_WALK_CONTINUE); } static void @@ -1941,9 +3057,10 @@ do_display_wifi(int argc, char **argv, int cmd) int option; char *fields_str = NULL; wifi_field_t **fields; - boolean_t (*callback)(void *, const char *); + int (*callback)(datalink_id_t, void *); uint_t nfields; print_wifi_state_t state; + datalink_id_t linkid = DATALINK_ALL_LINKID; dladm_status_t status; if (cmd == WIFI_CMD_SCAN) @@ -1953,7 +3070,6 @@ do_display_wifi(int argc, char **argv, int cmd) else return; - state.ws_link = NULL; state.ws_parseable = B_FALSE; state.ws_header = B_TRUE; opterr = 0; @@ -1974,10 +3090,14 @@ do_display_wifi(int argc, char **argv, int cmd) } } - if (optind == (argc - 1)) - state.ws_link = argv[optind]; - else if (optind != argc) + if (optind == (argc - 1)) { + if ((status = dladm_name2info(argv[optind], &linkid, NULL, + NULL, NULL)) != DLADM_STATUS_OK) { + die_dlerr(status, "link %s is not valid", argv[optind]); + } + } else if (optind != argc) { usage(); + } if (parse_wifi_fields(fields_str, &fields, &nfields, cmd) < 0) die("invalid field(s) specified"); @@ -1985,12 +3105,11 @@ do_display_wifi(int argc, char **argv, int cmd) state.ws_fields = fields; state.ws_nfields = nfields; - if (state.ws_link == NULL) { - status = dladm_wlan_walk(&state, callback); - if (status != DLADM_STATUS_OK) - die_dlerr(status, "cannot walk wifi links"); + if (linkid == DATALINK_ALL_LINKID) { + (void) dladm_walk_datalink_id(callback, &state, + DATALINK_CLASS_PHYS, DL_WIFI, DLADM_OPT_ACTIVE); } else { - (void) (*callback)(&state, state.ws_link); + (void) (*callback)(linkid, &state); } free(fields); } @@ -2009,18 +3128,18 @@ do_show_wifi(int argc, char **argv) typedef struct wlan_count_attr { uint_t wc_count; - const char *wc_link; + datalink_id_t wc_linkid; } wlan_count_attr_t; -static boolean_t -do_count_wlan(void *arg, const char *link) +static int +do_count_wlan(datalink_id_t linkid, void *arg) { wlan_count_attr_t *cp = arg; if (cp->wc_count == 0) - cp->wc_link = strdup(link); + cp->wc_linkid = linkid; cp->wc_count++; - return (B_TRUE); + return (DLADM_WALK_CONTINUE); } static int @@ -2086,7 +3205,7 @@ do_connect_wifi(int argc, char **argv) dladm_wlan_attr_t attr, *attrp; dladm_status_t status = DLADM_STATUS_OK; int timeout = DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT; - const char *link = NULL; + datalink_id_t linkid = DATALINK_ALL_LINKID; dladm_wlan_key_t *keys = NULL; uint_t key_count = 0; uint_t flags = 0; @@ -2186,28 +3305,33 @@ do_connect_wifi(int argc, char **argv) attr.wa_secmode = keysecmode; } - if (optind == (argc - 1)) - link = argv[optind]; - else if (optind != argc) + if (optind == (argc - 1)) { + if ((status = dladm_name2info(argv[optind], &linkid, NULL, + NULL, NULL)) != DLADM_STATUS_OK) { + die_dlerr(status, "link %s is not valid", argv[optind]); + } + } else if (optind != argc) { usage(); + } - if (link == NULL) { + if (linkid == DATALINK_ALL_LINKID) { wlan_count_attr_t wcattr; - wcattr.wc_link = NULL; + wcattr.wc_linkid = DATALINK_INVALID_LINKID; wcattr.wc_count = 0; - (void) dladm_wlan_walk(&wcattr, do_count_wlan); + (void) dladm_walk_datalink_id(do_count_wlan, &wcattr, + DATALINK_CLASS_PHYS, DL_WIFI, DLADM_OPT_ACTIVE); if (wcattr.wc_count == 0) { die("no wifi links are available"); } else if (wcattr.wc_count > 1) { die("link name is required when more than one wifi " "link is available"); } - link = wcattr.wc_link; + linkid = wcattr.wc_linkid; } attrp = (attr.wa_valid == 0) ? NULL : &attr; again: - if ((status = dladm_wlan_connect(link, attrp, timeout, keys, + if ((status = dladm_wlan_connect(linkid, attrp, timeout, keys, key_count, flags)) != DLADM_STATUS_OK) { if ((flags & DLADM_WLAN_CONNECT_NOSCAN) != 0) { /* @@ -2225,29 +3349,29 @@ again: "criteria are available"); } } - die_dlerr(status, "cannot connect link '%s'", link); + die_dlerr(status, "cannot connect"); } free(keys); } /* ARGSUSED */ -static boolean_t -do_all_disconnect_wifi(void *arg, const char *link) +static int +do_all_disconnect_wifi(datalink_id_t linkid, void *arg) { dladm_status_t status; - status = dladm_wlan_disconnect(link); + status = dladm_wlan_disconnect(linkid); if (status != DLADM_STATUS_OK) - warn_dlerr(status, "cannot disconnect link '%s'", link); + warn_dlerr(status, "cannot disconnect link"); - return (B_TRUE); + return (DLADM_WALK_CONTINUE); } static void do_disconnect_wifi(int argc, char **argv) { int option; - const char *link = NULL; + datalink_id_t linkid = DATALINK_ALL_LINKID; boolean_t all_links = B_FALSE; dladm_status_t status; wlan_count_attr_t wcattr; @@ -2265,41 +3389,46 @@ do_disconnect_wifi(int argc, char **argv) } } - if (optind == (argc - 1)) - link = argv[optind]; - else if (optind != argc) + if (optind == (argc - 1)) { + if ((status = dladm_name2info(argv[optind], &linkid, NULL, + NULL, NULL)) != DLADM_STATUS_OK) { + die_dlerr(status, "link %s is not valid", argv[optind]); + } + } else if (optind != argc) { usage(); + } - if (link == NULL) { + if (linkid == DATALINK_ALL_LINKID) { if (!all_links) { - wcattr.wc_link = NULL; + wcattr.wc_linkid = linkid; wcattr.wc_count = 0; - (void) dladm_wlan_walk(&wcattr, do_count_wlan); + (void) dladm_walk_datalink_id(do_count_wlan, &wcattr, + DATALINK_CLASS_PHYS, DL_WIFI, DLADM_OPT_ACTIVE); if (wcattr.wc_count == 0) { die("no wifi links are available"); } else if (wcattr.wc_count > 1) { die("link name is required when more than " "one wifi link is available"); } - link = wcattr.wc_link; + linkid = wcattr.wc_linkid; } else { - (void) dladm_wlan_walk(&all_links, - do_all_disconnect_wifi); + (void) dladm_walk_datalink_id(do_all_disconnect_wifi, + NULL, DATALINK_CLASS_PHYS, DL_WIFI, + DLADM_OPT_ACTIVE); return; } } - status = dladm_wlan_disconnect(link); + status = dladm_wlan_disconnect(linkid); if (status != DLADM_STATUS_OK) - die_dlerr(status, "cannot disconnect link '%s'", link); + die_dlerr(status, "cannot disconnect"); } #define MAX_PROPS 32 -#define MAX_PROP_VALS 32 #define MAX_PROP_LINE 512 typedef struct prop_info { char *pi_name; - char *pi_val[MAX_PROP_VALS]; + char *pi_val[DLADM_MAX_PROP_VALCNT]; uint_t pi_count; } prop_info_t; @@ -2310,7 +3439,7 @@ typedef struct prop_list { } prop_list_t; typedef struct show_linkprop_state { - const char *ls_link; + char ls_link[MAXLINKNAMELEN]; char *ls_line; char **ls_propvals; prop_list_t *ls_proplist; @@ -2364,7 +3493,7 @@ parse_props(char *str, prop_list_t **listp, boolean_t novalues) } if (pip != NULL && c != '=') { - if (pip->pi_count > MAX_PROP_VALS) + if (pip->pi_count > DLADM_MAX_PROP_VALCNT) goto fail; if (novalues) @@ -2401,24 +3530,29 @@ print_linkprop_head(void) } static void -print_linkprop(show_linkprop_state_t *statep, const char *propname, - dladm_prop_type_t type, const char *typename, const char *format, - char **pptr) +print_linkprop(datalink_id_t linkid, show_linkprop_state_t *statep, + const char *propname, dladm_prop_type_t type, const char *typename, + const char *format, char **pptr) { int i; char *ptr, *lim; char buf[DLADM_STRSIZE]; char *unknown = "?", *notsup = ""; char **propvals = statep->ls_propvals; - uint_t valcnt = MAX_PROP_VALS; + uint_t valcnt = DLADM_MAX_PROP_VALCNT; dladm_status_t status; - status = dladm_get_prop(statep->ls_link, type, propname, - propvals, &valcnt); + status = dladm_get_linkprop(linkid, type, propname, propvals, &valcnt); if (status != DLADM_STATUS_OK) { if (status == DLADM_STATUS_TEMPONLY) { - statep->ls_status = status; - return; + if (type == DLADM_PROP_VAL_MODIFIABLE && + statep->ls_persist) { + valcnt = 1; + propvals = &unknown; + } else { + statep->ls_status = status; + return; + } } else if (status == DLADM_STATUS_NOTSUP || statep->ls_persist) { valcnt = 1; @@ -2428,9 +3562,11 @@ print_linkprop(show_linkprop_state_t *statep, const char *propname, propvals = ¬sup; } else { statep->ls_status = status; - warn_dlerr(status, - "cannot get link property '%s' for %s", - propname, statep->ls_link); + if (statep->ls_proplist) { + warn_dlerr(status, + "cannot get link property '%s' for %s", + propname, statep->ls_link); + } return; } } @@ -2457,8 +3593,8 @@ print_linkprop(show_linkprop_state_t *statep, const char *propname, } } -static boolean_t -show_linkprop(void *arg, const char *propname) +static int +show_linkprop(datalink_id_t linkid, const char *propname, void *arg) { show_linkprop_state_t *statep = arg; char *ptr = statep->ls_line; @@ -2475,7 +3611,7 @@ show_linkprop(void *arg, const char *propname) else ptr += snprintf(ptr, lim - ptr, "%-15s ", propname); - print_linkprop(statep, propname, + print_linkprop(linkid, statep, propname, statep->ls_persist ? DLADM_PROP_VAL_PERSISTENT : DLADM_PROP_VAL_CURRENT, "VALUE", "%-14s ", &ptr); @@ -2485,17 +3621,17 @@ show_linkprop(void *arg, const char *propname) * skip the output. */ if (statep->ls_status != DLADM_STATUS_OK) - return (B_TRUE); + return (DLADM_WALK_CONTINUE); - print_linkprop(statep, propname, DLADM_PROP_VAL_DEFAULT, + print_linkprop(linkid, statep, propname, DLADM_PROP_VAL_DEFAULT, "DEFAULT", "%-14s ", &ptr); if (statep->ls_status != DLADM_STATUS_OK) - return (B_TRUE); + return (DLADM_WALK_CONTINUE); - print_linkprop(statep, propname, DLADM_PROP_VAL_MODIFIABLE, + print_linkprop(linkid, statep, propname, DLADM_PROP_VAL_MODIFIABLE, "POSSIBLE", "%-20s ", &ptr); if (statep->ls_status != DLADM_STATUS_OK) - return (B_TRUE); + return (DLADM_WALK_CONTINUE); if (statep->ls_header) { statep->ls_header = B_FALSE; @@ -2503,7 +3639,7 @@ show_linkprop(void *arg, const char *propname) print_linkprop_head(); } (void) printf("%s\n", statep->ls_line); - return (B_TRUE); + return (DLADM_WALK_CONTINUE); } static void @@ -2511,10 +3647,12 @@ do_show_linkprop(int argc, char **argv) { int option; prop_list_t *proplist = NULL; + datalink_id_t linkid = DATALINK_ALL_LINKID; show_linkprop_state_t state; + uint32_t flags = DLADM_OPT_ACTIVE; + dladm_status_t status; opterr = 0; - state.ls_link = NULL; state.ls_propvals = NULL; state.ls_line = NULL; state.ls_parseable = B_FALSE; @@ -2532,6 +3670,7 @@ do_show_linkprop(int argc, char **argv) break; case 'P': state.ls_persist = B_TRUE; + flags = DLADM_OPT_PERSIST; break; default: die_opterr(optopt, option); @@ -2539,40 +3678,59 @@ do_show_linkprop(int argc, char **argv) } } - if (optind == (argc - 1)) - state.ls_link = argv[optind]; - else if (optind != argc) + if (optind == (argc - 1)) { + if ((status = dladm_name2info(argv[optind], &linkid, NULL, + NULL, NULL)) != DLADM_STATUS_OK) { + die_dlerr(status, "link %s is not valid", argv[optind]); + } + } else if (optind != argc) { usage(); + } state.ls_proplist = proplist; state.ls_status = DLADM_STATUS_OK; - if (state.ls_link == NULL) { - (void) dladm_walk(show_linkprop_onelink, &state); + if (linkid == DATALINK_ALL_LINKID) { + (void) dladm_walk_datalink_id(show_linkprop_onelink, &state, + DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, flags); } else { - show_linkprop_onelink(&state, state.ls_link); + (void) show_linkprop_onelink(linkid, &state); } free_props(proplist); - if (state.ls_status != DLADM_STATUS_OK) + if (state.ls_status != DLADM_STATUS_OK) { + if (optind == (argc - 1)) { + warn_dlerr(state.ls_status, + "show-linkprop failed for %s", argv[optind]); + } exit(EXIT_FAILURE); + } } -static void -show_linkprop_onelink(void *arg, const char *link) +static int +show_linkprop_onelink(datalink_id_t linkid, void *arg) { int i; - int retval; char *buf; - dladm_status_t status; + uint32_t flags; prop_list_t *proplist = NULL; - show_linkprop_state_t *statep; - const char *savep; - dlpi_handle_t dh; + show_linkprop_state_t *statep = arg; + dlpi_handle_t dh = NULL; + + statep->ls_status = DLADM_STATUS_OK; + + if (dladm_datalink_id2info(linkid, &flags, NULL, NULL, statep->ls_link, + MAXLINKNAMELEN) != DLADM_STATUS_OK) { + statep->ls_status = DLADM_STATUS_NOTFOUND; + return (DLADM_WALK_CONTINUE); + } + + if ((statep->ls_persist && !(flags & DLADM_OPT_PERSIST)) || + (!statep->ls_persist && !(flags & DLADM_OPT_ACTIVE))) { + statep->ls_status = DLADM_STATUS_BADARG; + return (DLADM_WALK_CONTINUE); + } - statep = (show_linkprop_state_t *)arg; - savep = statep->ls_link; - statep->ls_link = link; proplist = statep->ls_proplist; /* @@ -2580,58 +3738,55 @@ show_linkprop_onelink(void *arg, const char *link) * automatically scans for APs and does other slow operations. Thus, * if there are no open links, the retrieval of link properties * (below) will proceed slowly unless we hold the link open. + * + * Note that failure of dlpi_open() does not necessarily mean invalid + * link properties, because dlpi_open() may fail because of incorrect + * autopush configuration. Therefore, we ingore the return value of + * dlpi_open(). */ - if ((retval = dlpi_open(link, &dh, 0)) != DLPI_SUCCESS) { - warn("cannot open %s: %s", link, dlpi_strerror(retval)); - statep->ls_status = DLADM_STATUS_NOTFOUND; - return; - } + if (!statep->ls_persist) + (void) dlpi_open(statep->ls_link, &dh, 0); - buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) * MAX_PROP_VALS + - MAX_PROP_LINE); + buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) * + DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE); if (buf == NULL) die("insufficient memory"); statep->ls_propvals = (char **)(void *)buf; - for (i = 0; i < MAX_PROP_VALS; i++) { - statep->ls_propvals[i] = buf + sizeof (char *) * MAX_PROP_VALS + + for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) { + statep->ls_propvals[i] = buf + + sizeof (char *) * DLADM_MAX_PROP_VALCNT + i * DLADM_PROP_VAL_MAX; } statep->ls_line = buf + - (sizeof (char *) + DLADM_PROP_VAL_MAX) * MAX_PROP_VALS; + (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT; if (proplist != NULL) { - for (i = 0; i < proplist->pl_count; i++) - (void) show_linkprop(statep, - proplist->pl_info[i].pi_name); + for (i = 0; i < proplist->pl_count; i++) { + (void) show_linkprop(linkid, + proplist->pl_info[i].pi_name, statep); + } } else { - status = dladm_walk_prop(link, statep, show_linkprop); - if (status != DLADM_STATUS_OK) - warn_dlerr(status, "show-linkprop failed for %s", link); + (void) dladm_walk_linkprop(linkid, statep, show_linkprop); } - dlpi_close(dh); + if (dh != NULL) + dlpi_close(dh); free(buf); - statep->ls_link = savep; + return (DLADM_WALK_CONTINUE); } static dladm_status_t -set_linkprop_persist(const char *link, const char *prop_name, char **prop_val, - uint_t val_cnt, boolean_t reset) +set_linkprop_persist(datalink_id_t linkid, const char *prop_name, + char **prop_val, uint_t val_cnt, boolean_t reset) { dladm_status_t status; - char *errprop; - status = dladm_set_prop(link, prop_name, prop_val, val_cnt, - DLADM_OPT_PERSIST, &errprop); + status = dladm_set_linkprop(linkid, prop_name, prop_val, val_cnt, + DLADM_OPT_PERSIST); if (status != DLADM_STATUS_OK) { - if (reset) { - warn_dlerr(status, "cannot persistently reset link " - "property '%s' on '%s'", errprop, link); - } else { - warn_dlerr(status, "cannot persistently set link " - "property '%s' on '%s'", errprop, link); - } + warn_dlerr(status, "cannot persistently %s link property", + reset ? "reset" : "set"); } return (status); } @@ -2641,7 +3796,8 @@ set_linkprop(int argc, char **argv, boolean_t reset) { int i, option; char errmsg[DLADM_STRSIZE]; - const char *link = NULL; + char *altroot = NULL; + datalink_id_t linkid; prop_list_t *proplist = NULL; boolean_t temp = B_FALSE; dladm_status_t status = DLADM_STATUS_OK; @@ -2658,11 +3814,7 @@ set_linkprop(int argc, char **argv, boolean_t reset) temp = B_TRUE; break; case 'R': - status = dladm_set_rootdir(optarg); - if (status != DLADM_STATUS_OK) { - die_dlerr(status, "invalid directory " - "specified"); - } + altroot = optarg; break; default: die_opterr(optopt, option); @@ -2670,30 +3822,32 @@ set_linkprop(int argc, char **argv, boolean_t reset) } } - if (optind == (argc - 1)) - link = argv[optind]; - else if (optind != argc) + /* get link name (required last argument) */ + if (optind != (argc - 1)) usage(); - if (link == NULL) - die("link name must be specified"); + if (proplist == NULL && !reset) + die("link property must be specified"); - if (proplist == NULL) { - char *errprop; + if (altroot != NULL) { + free_props(proplist); + altroot_cmd(altroot, argc, argv); + } - if (!reset) - die("link property must be specified"); + status = dladm_name2info(argv[optind], &linkid, NULL, NULL, NULL); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "link %s is not valid", argv[optind]); - status = dladm_set_prop(link, NULL, NULL, 0, DLADM_OPT_TEMP, - &errprop); - if (status != DLADM_STATUS_OK) { - warn_dlerr(status, "cannot reset link property '%s' " - "on '%s'", errprop, link); + if (proplist == NULL) { + if ((status = dladm_set_linkprop(linkid, NULL, NULL, 0, + DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) { + warn_dlerr(status, "cannot reset link property " + "on '%s'", argv[optind]); } if (!temp) { dladm_status_t s; - s = set_linkprop_persist(link, NULL, NULL, 0, reset); + s = set_linkprop_persist(linkid, NULL, NULL, 0, reset); if (s != DLADM_STATUS_OK) status = s; } @@ -2719,11 +3873,11 @@ set_linkprop(int argc, char **argv, boolean_t reset) continue; } } - s = dladm_set_prop(link, pip->pi_name, val, count, - DLADM_OPT_TEMP, NULL); + s = dladm_set_linkprop(linkid, pip->pi_name, val, count, + DLADM_OPT_ACTIVE); if (s == DLADM_STATUS_OK) { if (!temp) { - s = set_linkprop_persist(link, + s = set_linkprop_persist(linkid, pip->pi_name, val, count, reset); if (s != DLADM_STATUS_OK) status = s; @@ -2739,28 +3893,36 @@ set_linkprop(int argc, char **argv, boolean_t reset) int j; char *ptr, *lim; char **propvals = NULL; - uint_t valcnt = MAX_PROP_VALS; + uint_t valcnt = DLADM_MAX_PROP_VALCNT; ptr = malloc((sizeof (char *) + - DLADM_PROP_VAL_MAX) * MAX_PROP_VALS + + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE); propvals = (char **)(void *)ptr; if (propvals == NULL) die("insufficient memory"); - for (j = 0; j < MAX_PROP_VALS; j++) { + for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) { propvals[j] = ptr + sizeof (char *) * - MAX_PROP_VALS + + DLADM_MAX_PROP_VALCNT + j * DLADM_PROP_VAL_MAX; } - s = dladm_get_prop(link, DLADM_PROP_VAL_MODIFIABLE, - pip->pi_name, propvals, &valcnt); + s = dladm_get_linkprop(linkid, + DLADM_PROP_VAL_MODIFIABLE, pip->pi_name, propvals, + &valcnt); + + if (s != DLADM_STATUS_OK) { + warn_dlerr(status, "cannot set link property " + "'%s' on '%s'", pip->pi_name, argv[optind]); + free(propvals); + break; + } ptr = errmsg; lim = ptr + DLADM_STRSIZE; *ptr = '\0'; - for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) { + for (j = 0; j < valcnt; j++) { ptr += snprintf(ptr, lim - ptr, "%s,", propvals[j]); if (ptr >= lim) @@ -2778,10 +3940,10 @@ set_linkprop(int argc, char **argv, boolean_t reset) default: if (reset) { warn_dlerr(status, "cannot reset link property " - "'%s' on '%s'", pip->pi_name, link); + "'%s' on '%s'", pip->pi_name, argv[optind]); } else { warn_dlerr(status, "cannot set link property " - "'%s' on '%s'", pip->pi_name, link); + "'%s' on '%s'", pip->pi_name, argv[optind]); } break; } @@ -3097,7 +4259,7 @@ do_create_secobj(int argc, char **argv) } status = dladm_set_secobj(obj_name, class, obj_val, obj_len, - DLADM_OPT_CREATE | DLADM_OPT_TEMP); + DLADM_OPT_CREATE | DLADM_OPT_ACTIVE); if (status != DLADM_STATUS_OK) { die_dlerr(status, "could not create secure object '%s'", obj_name); @@ -3161,7 +4323,7 @@ do_delete_secobj(int argc, char **argv) die("authorization '%s' is required", LINK_SEC_AUTH); for (i = 0; i < sp->s_nfields; i++) { - status = dladm_unset_secobj(sp->s_fields[i], DLADM_OPT_TEMP); + status = dladm_unset_secobj(sp->s_fields[i], DLADM_OPT_ACTIVE); if (!temp) { pstatus = dladm_unset_secobj(sp->s_fields[i], DLADM_OPT_PERSIST); @@ -3301,15 +4463,24 @@ do_show_secobj(int argc, char **argv) die_dlerr(status, "show-secobj"); } +/*ARGSUSED*/ +static int +i_dladm_init_linkprop(datalink_id_t linkid, void *arg) +{ + (void) dladm_init_linkprop(linkid); + return (DLADM_WALK_CONTINUE); +} + /* ARGSUSED */ static void do_init_linkprop(int argc, char **argv) { - dladm_status_t status; - - status = dladm_init_linkprop(); - if (status != DLADM_STATUS_OK) - die_dlerr(status, "link property initialization failed"); + /* + * linkprops of links of other classes have been initialized as a + * part of the dladm up-xxx operation. + */ + (void) dladm_walk_datalink_id(i_dladm_init_linkprop, NULL, + DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); } /* ARGSUSED */ @@ -3323,6 +4494,68 @@ do_init_secobj(int argc, char **argv) die_dlerr(status, "secure object initialization failed"); } +/* + * "-R" option support. It is used for live upgrading. Append dladm commands + * to a upgrade script which will be run when the alternative root boots up: + * + * - If the dlmgmtd door file exists on the alternative root, append dladm + * commands to the <altroot>/var/svc/profile/upgrade_datalink script. This + * script will be run as part of the network/physical service. We cannot defer + * this to /var/svc/profile/upgrade because then the configuration will not + * be able to take effect before network/physical plumbs various interfaces. + * + * - If the dlmgmtd door file does not exist on the alternative root, append + * dladm commands to the <altroot>/var/svc/profile/upgrade script, which will + * be run in the manifest-import service. + * + * Note that the SMF team is considering to move the manifest-import service + * to be run at the very begining of boot. Once that is done, the need for + * the /var/svc/profile/upgrade_datalink script will not exist any more. + */ +static void +altroot_cmd(char *altroot, int argc, char *argv[]) +{ + char path[MAXPATHLEN]; + struct stat stbuf; + FILE *fp; + int i; + + /* + * Check for the existence of the dlmgmtd door file, and determine + * the name of script file. + */ + (void) snprintf(path, MAXPATHLEN, "/%s/%s", altroot, DLMGMT_DOOR); + if (stat(path, &stbuf) < 0) { + (void) snprintf(path, MAXPATHLEN, "/%s/%s", altroot, + SMF_UPGRADE_FILE); + } else { + (void) snprintf(path, MAXPATHLEN, "/%s/%s", altroot, + SMF_UPGRADEDATALINK_FILE); + } + + if ((fp = fopen(path, "a+")) == NULL) + die("operation not supported on %s", altroot); + + (void) fprintf(fp, "/sbin/dladm "); + for (i = 0; i < argc; i++) { + /* + * Directly write to the file if it is not the "-R <altroot>" + * option. In which case, skip it. + */ + if (strcmp(argv[i], "-R") != 0) + (void) fprintf(fp, "%s ", argv[i]); + else + i ++; + } + (void) fprintf(fp, "%s\n", SMF_DLADM_UPGRADE_MSG); + (void) fclose(fp); + exit(0); +} + +/* + * Convert the string to an integer. Note that the string must not have any + * trailing non-integer characters. + */ static boolean_t str2int(const char *str, int *valp) { diff --git a/usr/src/cmd/dladm/dladm.xcl b/usr/src/cmd/dladm/dladm.xcl index 1f47d2c1ac..c48e5d4bbb 100644 --- a/usr/src/cmd/dladm/dladm.xcl +++ b/usr/src/cmd/dladm/dladm.xcl @@ -18,7 +18,7 @@ # # CDDL HEADER END # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -131,8 +131,11 @@ msgid "connect-wifi" msgid "create-aggr" msgid "create-ibss" msgid "create-secobj" +msgid "create-vlan" msgid "delete-aggr" +msgid "delete-phys" msgid "delete-secobj" +msgid "delete-vlan" msgid "dev key=%d" msgid "dev" msgid "disconnect-wifi" @@ -180,6 +183,7 @@ msgid "prop" msgid "r" msgid "rbytes64" msgid "remove-aggr" +msgid "rename-link" msgid "reset-linkprop" msgid "root-dir" msgid "scan-wifi" @@ -190,8 +194,10 @@ msgid "show-aggr" msgid "show-dev" msgid "show-link" msgid "show-linkprop" +msgid "show-phys" msgid "show-secobj" msgid "show-wifi" +msgid "show-vlan" msgid "solaris.network.link.security" msgid "speed" msgid "standby" @@ -204,6 +210,7 @@ msgid "unicast" msgid "unknown" msgid "up" msgid "up-aggr" +msgid "up-vlan" msgid "vlan-id" msgid "wep" msgid "yes" diff --git a/usr/src/cmd/dlmgmtd/Makefile b/usr/src/cmd/dlmgmtd/Makefile new file mode 100644 index 0000000000..0c78f21e5e --- /dev/null +++ b/usr/src/cmd/dlmgmtd/Makefile @@ -0,0 +1,77 @@ +# +# 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. +# +# ident "%Z%%M% %I% %E% SMI" +# + +PROG= dlmgmtd +OBJS= dlmgmt_main.o dlmgmt_door.o dlmgmt_util.o dlmgmt_db.o +SRCS= $(OBJS:.o=.c) +SVCMETHOD= svc-dlmgmtd +MANIFEST= dlmgmt.xml +DOOR= .dlmgmt_door +CFGFILES= datalink.conf + +include ../Makefile.cmd + +ROOTMANIFESTDIR= $(ROOTSVCNETWORK) +ROOTETCDOOR= $(DOOR:%=$(ROOTETC)/%) +$(ROOTETCDOOR) := FILEMODE = 444 +ROOTCFGDIR= $(ROOTETC)/dladm +ROOTCFGFILES= $(CFGFILES:%=$(ROOTCFGDIR)/%) + +$(ROOTCFGFILES) := OWNER= dladm +$(ROOTCFGFILES) := GROUP= sys +$(ROOTCFGDIR)/datalink.conf := FILEMODE= 644 + +LDLIBS += -ldladm -ldlpi -lavl + +.KEEP_STATE: + +all: $(PROG) $(DOOR) + +$(DOOR): + $(TOUCH) $(DOOR) + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +install: all $(ROOTSBINPROG) $(ROOTMANIFEST) $(ROOTSVCMETHOD) $(ROOTETCDOOR) \ + $(ROOTCFGDIR) $(ROOTCFGFILES) + +check: $(CHKMANIFEST) + +clean: + $(RM) $(OBJS) + +lint: lint_SRCS + +$(ROOTCFGDIR): + $(INS.dir) + +$(ROOTCFGDIR)/%: $(ROOTCFGDIR) % + $(INS.file) + +include ../Makefile.targ diff --git a/usr/src/cmd/dladm/linkprop.conf b/usr/src/cmd/dlmgmtd/datalink.conf index dd196c1273..91c9931ff3 100644 --- a/usr/src/cmd/dladm/linkprop.conf +++ b/usr/src/cmd/dlmgmtd/datalink.conf @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" diff --git a/usr/src/cmd/dlmgmtd/dlmgmt.xml b/usr/src/cmd/dlmgmtd/dlmgmt.xml new file mode 100644 index 0000000000..e0781a8d8b --- /dev/null +++ b/usr/src/cmd/dlmgmtd/dlmgmt.xml @@ -0,0 +1,90 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- + 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. + + ident "%Z%%M% %I% %E% SMI" + + NOTE: This service manifest is not editable; its contents will + be overwritten by package or patch operations, including + operating system upgrade. Make customizations in a different + file. +--> + +<service_bundle type='manifest' name='SUNWcsr:dlmgmtd'> + +<service + name='network/datalink-management' + type='service' + version='1'> + + <create_default_instance enabled='true' /> + + <single_instance/> + + <dependent name='dlmgmt-install-discovery' + grouping='require_all' + restart_on='none'> + <service_fmri value='svc:/system/install-discovery' /> + </dependent> + + <dependent name='dlmgmt-device-system' + grouping='require_all' + restart_on='none'> + <service_fmri value='svc:/system/device/local' /> + </dependent> + + <dependent name='dlmgmt-network-physical' + grouping='require_all' + restart_on='none'> + <service_fmri value='svc:/network/physical' /> + </dependent> + + <exec_method + type='method' + name='start' + exec='/lib/svc/method/svc-dlmgmtd' + timeout_seconds='60'> + </exec_method> + + <exec_method + type='method' + name='stop' + exec=':kill' + timeout_seconds='3' /> + + <stability value='Unstable' /> + + <template> + <common_name> + <loctext xml:lang='C'> data-link management daemon + </loctext> + </common_name> + <documentation> + <manpage title='dlmgmtd' section='1M' + manpath='/usr/share/man' /> + </documentation> + </template> +</service> + +</service_bundle> diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_db.c b/usr/src/cmd/dlmgmtd/dlmgmt_db.c new file mode 100644 index 0000000000..e65fda35d2 --- /dev/null +++ b/usr/src/cmd/dlmgmtd/dlmgmt_db.c @@ -0,0 +1,949 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <syslog.h> +#include <sys/stat.h> +#include <pthread.h> +#include <unistd.h> +#include "dlmgmt_impl.h" + +typedef enum dlmgmt_db_op { + DLMGMT_DB_OP_WRITE, + DLMGMT_DB_OP_DELETE, + DLMGMT_DB_OP_READ +} dlmgmt_db_op_t; + +typedef struct dlmgmt_db_req_s { + struct dlmgmt_db_req_s *ls_next; + dlmgmt_db_op_t ls_op; + datalink_id_t ls_linkid; + uint32_t ls_flags; /* Either DLMGMT_ACTIVE or */ + /* DLMGMT_PERSIST, not both. */ +} dlmgmt_db_req_t; + +/* + * List of pending db updates (e.g., because of a read-only filesystem). + */ +static dlmgmt_db_req_t *dlmgmt_db_req_head = NULL; +static dlmgmt_db_req_t *dlmgmt_db_req_tail = NULL; + +static int dlmgmt_db_update(dlmgmt_db_op_t, datalink_id_t, + uint32_t); +static int dlmgmt_process_db_req(dlmgmt_db_req_t *); +static int dlmgmt_process_db_onereq(dlmgmt_db_req_t *, boolean_t); +static void *dlmgmt_db_update_thread(void *); +static boolean_t process_link_line(char *, dlmgmt_link_t **); +static int process_db_write(dlmgmt_db_req_t *, FILE *, FILE *); +static int process_db_read(dlmgmt_db_req_t *, FILE *, FILE *); +static void generate_link_line(dlmgmt_link_t *, boolean_t, char *); + +#define BUFLEN(lim, ptr) (((lim) > (ptr)) ? ((lim) - (ptr)) : 0) +#define MAXLINELEN 1024 + +/* + * Translator functions to go from dladm_datatype_t to character strings. + * Each function takes a pointer to a buffer, the size of the buffer, + * the name of the attribute, and the value to be written. The functions + * return the number of bytes written to the buffer. If the buffer is not big + * enough to hold the string representing the value, then nothing is written + * and 0 is returned. + */ +typedef size_t write_func_t(char *, size_t, char *, void *); + +/* + * Translator functions to read from a NULL terminated string buffer into + * something of the given DLADM_TYPE_*. The functions each return the number + * of bytes read from the string buffer. If there is an error reading data + * from the buffer, then 0 is returned. It is the caller's responsibility + * to free the data allocated by these functions. + */ +typedef size_t read_func_t(char *, void **); + +typedef struct translator_s { + const char *type_name; + write_func_t *write_func; + read_func_t *read_func; +} translator_t; + +/* + * Translator functions, defined later but declared here so that + * the translator table can be defined. + */ +static write_func_t write_str, write_boolean, write_uint64; +static read_func_t read_str, read_boolean, read_int64; + +/* + * Translator table, indexed by dladm_datatype_t. + */ +static translator_t translators[] = { + { "string", write_str, read_str }, + { "boolean", write_boolean, read_boolean }, + { "int", write_uint64, read_int64 } +}; + +static size_t ntranslators = sizeof (translators) / sizeof (translator_t); + +#define LINK_PROPERTY_DELIMINATOR ";" +#define LINK_PROPERTY_TYPE_VALUE_SEP "," +#define BASE_PROPERTY_LENGTH(t, n) (strlen(translators[(t)].type_name) +\ + strlen(LINK_PROPERTY_TYPE_VALUE_SEP) +\ + strlen(LINK_PROPERTY_DELIMINATOR) +\ + strlen((n))) +#define GENERATE_PROPERTY_STRING(buf, length, conv, name, type, val) \ + (snprintf((buf), (length), "%s=%s%s" conv "%s", (name), \ + translators[(type)].type_name, \ + LINK_PROPERTY_TYPE_VALUE_SEP, (val), LINK_PROPERTY_DELIMINATOR)) + +#define DLMGMT_DB_OWNER 15 +#define DLMGMT_DB_GROUP 3 + +/* + * Name of the cache file to keep the active <link name, linkid> mapping + */ +static char cachefile[MAXPATHLEN]; + +#define DLMGMT_TEMP_DB_DIR "/etc/svc/volatile" +#define DLMGMT_PERSISTENT_DB_PATH "/etc/dladm/datalink.conf" +#define DLMGMT_MAKE_FILE_DB_PATH(buffer, persistent) \ + (void) snprintf((buffer), MAXPATHLEN, "%s", \ + (persistent) ? DLMGMT_PERSISTENT_DB_PATH : cachefile); + +static size_t +write_str(char *buffer, size_t buffer_length, char *name, void *value) +{ + char *ptr = value; + size_t data_length = strnlen(ptr, buffer_length); + + /* + * Strings are assumed to be NULL terminated. In order to fit in + * the buffer, the string's length must be less then buffer_length. + * If the value is empty, there's no point in writing it, in fact, + * we shouldn't even see that case. + */ + if (data_length + BASE_PROPERTY_LENGTH(DLADM_TYPE_STR, name) == + buffer_length || data_length == 0) + return (0); + + /* + * Since we know the string will fit in the buffer, snprintf will + * always return less than buffer_length, so we can just return + * whatever snprintf returns. + */ + return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%s", + name, DLADM_TYPE_STR, ptr)); +} + +static size_t +write_boolean(char *buffer, size_t buffer_length, char *name, void *value) +{ + boolean_t *ptr = value; + + /* + * Booleans are either zero or one, so we only need room for two + * characters in the buffer. + */ + if (buffer_length <= 1 + BASE_PROPERTY_LENGTH(DLADM_TYPE_BOOLEAN, name)) + return (0); + + return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%d", + name, DLADM_TYPE_BOOLEAN, *ptr)); +} + +static size_t +write_uint64(char *buffer, size_t buffer_length, char *name, void *value) +{ + uint64_t *ptr = value; + + /* + * Limit checking for uint64_t is a little trickier. + */ + if (snprintf(NULL, 0, "%lld", *ptr) + + BASE_PROPERTY_LENGTH(DLADM_TYPE_UINT64, name) >= buffer_length) + return (0); + + return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%lld", + name, DLADM_TYPE_UINT64, *ptr)); +} + +static size_t +read_str(char *buffer, void **value) +{ + char *ptr = calloc(MAXLINKATTRLEN, sizeof (char)); + ssize_t len; + + if (ptr == NULL || (len = snprintf(ptr, MAXLINKATTRLEN, "%s", buffer)) + >= MAXLINKATTRLEN) { + free(ptr); + return (0); + } + + *(char **)value = ptr; + + /* Account for NULL terminator */ + return (len + 1); +} + +static size_t +read_boolean(char *buffer, void **value) +{ + boolean_t *ptr = calloc(1, sizeof (boolean_t)); + + if (ptr == NULL) + return (0); + + *ptr = atoi(buffer); + *(boolean_t **)value = ptr; + + return (sizeof (boolean_t)); +} + +static size_t +read_int64(char *buffer, void **value) +{ + int64_t *ptr = calloc(1, sizeof (int64_t)); + + if (ptr == NULL) + return (0); + + *ptr = (int64_t)atoll(buffer); + *(int64_t **)value = ptr; + + return (sizeof (int64_t)); +} + +static int +dlmgmt_db_update(dlmgmt_db_op_t op, datalink_id_t linkid, uint32_t flags) +{ + dlmgmt_db_req_t *req; + int err; + + /* + * It is either a persistent request or an active request, not both. + */ + assert((flags == DLMGMT_PERSIST) || (flags == DLMGMT_ACTIVE)); + + if ((req = malloc(sizeof (dlmgmt_db_req_t))) == NULL) + return (ENOMEM); + + req->ls_next = NULL; + req->ls_op = op; + req->ls_linkid = linkid; + req->ls_flags = flags; + + /* + * If the return error is EINPROGRESS, this request is handled + * asynchronously; return success. + */ + err = dlmgmt_process_db_req(req); + if (err != EINPROGRESS) + free(req); + else + err = 0; + return (err); +} + +#define DLMGMT_DB_OP_STR(op) \ + (((op) == DLMGMT_DB_OP_READ) ? "read" : \ + (((op) == DLMGMT_DB_OP_WRITE) ? "write" : "delete")) + +#define DLMGMT_DB_CONF_STR(flag) \ + (((flag) == DLMGMT_ACTIVE) ? "active" : \ + (((flag) == DLMGMT_PERSIST) ? "persistent" : "")) + +static int +dlmgmt_process_db_req(dlmgmt_db_req_t *req) +{ + pthread_t tid; + boolean_t writeop; + int err; + + /* + * If there are already pending "write" requests, queue this request in + * the pending list. Note that this function is called while the + * dlmgmt_rw_lock is held, so it is safe to access the global variables. + */ + writeop = (req->ls_op != DLMGMT_DB_OP_READ); + if (writeop && (req->ls_flags == DLMGMT_PERSIST) && + (dlmgmt_db_req_head != NULL)) { + dlmgmt_db_req_tail->ls_next = req; + dlmgmt_db_req_tail = req; + return (EINPROGRESS); + } + + err = dlmgmt_process_db_onereq(req, writeop); + if (err != EINPROGRESS && err != 0 && + (req->ls_flags != DLMGMT_ACTIVE || errno != ENOENT)) { + + /* + * Log the error unless the request processing: + * - is successful; + * - is still in progress; + * - has failed with ENOENT because the active configuration + * file is not created yet; + */ + dlmgmt_log(LOG_WARNING, "dlmgmt_process_db_onereq() %s " + "operation on %s configuration failed: %s", + DLMGMT_DB_OP_STR(req->ls_op), + DLMGMT_DB_CONF_STR(req->ls_flags), strerror(err)); + } + + if (err == EINPROGRESS) { + assert(req->ls_flags == DLMGMT_PERSIST); + assert(writeop && dlmgmt_db_req_head == NULL); + dlmgmt_db_req_tail = dlmgmt_db_req_head = req; + err = pthread_create(&tid, NULL, dlmgmt_db_update_thread, NULL); + if (err == 0) + return (EINPROGRESS); + } + return (err); +} + +static int +dlmgmt_process_db_onereq(dlmgmt_db_req_t *req, boolean_t writeop) +{ + int err = 0; + FILE *fp, *nfp = NULL; + char file[MAXPATHLEN]; + char newfile[MAXPATHLEN]; + int nfd; + + DLMGMT_MAKE_FILE_DB_PATH(file, (req->ls_flags == DLMGMT_PERSIST)); + if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) { + if (writeop && errno == EROFS) { + /* + * This can happen at boot when the file system is + * read-only. So add this request to the pending + * request list and start a retry thread. + */ + return (EINPROGRESS); + } else if (req->ls_flags == DLMGMT_ACTIVE && errno == ENOENT) { + /* + * It is fine if the file keeping active configuration + * does not exist. This happens during a new reboot. + */ + if (!writeop) + return (ENOENT); + /* + * If this is an update request for the active + * configuration, create the file. + */ + if ((fp = fopen(file, "w")) == NULL) + return (errno == EROFS ? EINPROGRESS : errno); + } else { + return (errno); + } + } + + if (writeop) { + (void) snprintf(newfile, MAXPATHLEN, "%s.new", file); + if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) { + (void) fclose(fp); + return (errno); + } + + if ((nfp = fdopen(nfd, "w")) == NULL) { + (void) close(nfd); + (void) fclose(fp); + (void) unlink(newfile); + return (errno); + } + } + if (writeop) + err = process_db_write(req, fp, nfp); + else + err = process_db_read(req, fp, nfp); + if (!writeop || err != 0) + goto done; + + /* + * Configuration files need to be owned by the 'dladm' user. + * If we are invoked by root, the file ownership needs to be fixed. + */ + if (getuid() == 0 || geteuid() == 0) { + if (fchown(nfd, DLMGMT_DB_OWNER, DLMGMT_DB_GROUP) < 0) { + err = errno; + goto done; + } + } + + if (fflush(nfp) == EOF) { + err = errno; + goto done; + } + (void) fclose(fp); + (void) fclose(nfp); + + if (rename(newfile, file) < 0) { + (void) unlink(newfile); + return (errno); + } + + return (0); + +done: + if (nfp != NULL) { + (void) fclose(nfp); + if (err != 0) + (void) unlink(newfile); + } + (void) fclose(fp); + return (err); +} + +/*ARGSUSED*/ +static void * +dlmgmt_db_update_thread(void *arg) +{ + dlmgmt_db_req_t *req; + int err = 0; + + dlmgmt_table_lock(B_TRUE); + + assert(dlmgmt_db_req_head != NULL); + while ((req = dlmgmt_db_req_head) != NULL) { + assert(req->ls_flags == DLMGMT_PERSIST); + err = dlmgmt_process_db_onereq(req, B_TRUE); + if (err == EINPROGRESS) { + /* + * The filesystem is still read only. Go to sleep and + * try again. + */ + dlmgmt_table_unlock(); + (void) sleep(5); + dlmgmt_table_lock(B_TRUE); + continue; + } + + /* + * The filesystem is no longer read only. Continue processing + * and remove the request from the pending list. + */ + dlmgmt_db_req_head = req->ls_next; + if (dlmgmt_db_req_tail == req) { + assert(dlmgmt_db_req_head == NULL); + dlmgmt_db_req_tail = NULL; + } + free(req); + } + + dlmgmt_table_unlock(); + return (NULL); +} + +static int +parse_linkprops(char *buf, dlmgmt_link_t *linkp) +{ + boolean_t found_type = B_FALSE; + dladm_datatype_t type = DLADM_TYPE_STR; + int i, len; + int err = 0; + char *curr; + char attr_name[MAXLINKATTRLEN]; + size_t attr_buf_len = 0; + void *attr_buf = NULL; + + curr = buf; + len = strlen(buf); + attr_name[0] = '\0'; + for (i = 0; i < len && err == 0; i++) { + char c = buf[i]; + boolean_t match = (c == '=' || + (c == ',' && !found_type) || c == ';'); + + /* + * Move to the next character if there is no match and + * if we have not reached the last character. + */ + if (!match && i != len - 1) + continue; + + if (match) { + /* + * NUL-terminate the string pointed to by 'curr'. + */ + buf[i] = '\0'; + if (*curr == '\0') + goto parse_fail; + } + + if (attr_name[0] != '\0' && found_type) { + /* + * We get here after we have processed the "<prop>=" + * pattern. The pattern we are now interested in is + * "<val>;". + */ + if (c == '=') + goto parse_fail; + + if (strcmp(attr_name, "name") == 0) { + (void) read_str(curr, &attr_buf); + (void) snprintf(linkp->ll_link, + MAXLINKNAMELEN, "%s", attr_buf); + } else if (strcmp(attr_name, "class") == 0) { + (void) read_int64(curr, &attr_buf); + linkp->ll_class = + (datalink_class_t)*(int64_t *)attr_buf; + } else if (strcmp(attr_name, "media") == 0) { + (void) read_int64(curr, &attr_buf); + linkp->ll_media = + (uint32_t)*(int64_t *)attr_buf; + } else { + attr_buf_len = translators[type].read_func(curr, + &attr_buf); + err = linkattr_set(&(linkp->ll_head), attr_name, + attr_buf, attr_buf_len, type); + } + + free(attr_buf); + attr_name[0] = '\0'; + found_type = B_FALSE; + } else if (attr_name[0] != '\0') { + /* + * Non-zero length attr_name and found_type of false + * indicates that we have not found the type for this + * attribute. The pattern now is "<type>,<val>;", we + * want the <type> part of the pattern. + */ + for (type = 0; type < ntranslators; type++) { + if (strcmp(curr, + translators[type].type_name) == 0) { + found_type = B_TRUE; + break; + } + } + + if (!found_type) + goto parse_fail; + } else { + /* + * A zero length attr_name indicates we are looking + * at the beginning of a link attribute. + */ + if (c != '=') + goto parse_fail; + + (void) snprintf(attr_name, MAXLINKATTRLEN, "%s", curr); + } + curr = buf + i + 1; + } + + return (err); + +parse_fail: + return (-1); +} + +static boolean_t +process_link_line(char *buf, dlmgmt_link_t **linkpp) +{ + dlmgmt_link_t *linkp; + int i, len, llen; + char *str, *lasts; + char tmpbuf[MAXLINELEN]; + + /* + * Use a copy of buf for parsing so that we can do whatever we want. + */ + (void) strlcpy(tmpbuf, buf, MAXLINELEN); + + /* + * Skip leading spaces, blank lines, and comments. + */ + len = strlen(tmpbuf); + for (i = 0; i < len; i++) { + if (!isspace(tmpbuf[i])) + break; + } + if (i == len || tmpbuf[i] == '#') { + *linkpp = NULL; + return (B_TRUE); + } + + linkp = calloc(1, sizeof (dlmgmt_link_t)); + if (linkp == NULL) + goto fail; + + str = tmpbuf + i; + /* + * Find the link id and assign it to the link structure. + */ + if (strtok_r(str, " \n\t", &lasts) == NULL) + goto fail; + + llen = strlen(str); + linkp->ll_linkid = atoi(str); + + str += llen + 1; + if (str >= tmpbuf + len) + goto fail; + + /* + * Now find the list of link properties. + */ + if ((str = strtok_r(str, " \n\t", &lasts)) == NULL) + goto fail; + + if (parse_linkprops(str, linkp) < 0) + goto fail; + + *linkpp = linkp; + return (B_TRUE); + +fail: + link_destroy(linkp); + + /* + * Delete corrupted line. + */ + buf[0] = '\0'; + return (B_FALSE); +} + +static int +process_db_write(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp) +{ + boolean_t done = B_FALSE; + int err = 0; + dlmgmt_link_t *linkp, *link_in_file, link; + char buf[MAXLINELEN]; + + if (req->ls_op == DLMGMT_DB_OP_WRITE) { + /* + * find the link in the avl tree with the given linkid. + */ + link.ll_linkid = req->ls_linkid; + linkp = avl_find(&dlmgmt_id_avl, &link, NULL); + if (linkp == NULL || (linkp->ll_flags & req->ls_flags) == 0) { + /* + * This link has already been changed. This could + * happen if the request is pending because of + * read-only file-system. If so, we are done. + */ + return (0); + } + } + + while (err == 0 && fgets(buf, sizeof (buf), fp) != NULL && + process_link_line(buf, &link_in_file)) { + if (link_in_file == NULL || done) { + /* + * this is a comment line, write it out. + */ + if (fputs(buf, nfp) == EOF) + err = errno; + continue; + } + + switch (req->ls_op) { + case DLMGMT_DB_OP_WRITE: + /* + * For write operations, if the linkid of the link + * read from the file does not match the id of what + * req->ll_linkid points to, write out the buffer. + * Otherwise, generate a new line. If we get to the + * end and have not seen what req->ll_linkid points + * to, write it out then. + */ + if (linkp == NULL || + linkp->ll_linkid != link_in_file->ll_linkid) { + if (fputs(buf, nfp) == EOF) + err = errno; + } else { + generate_link_line(linkp, + req->ls_flags == DLMGMT_PERSIST, buf); + if (fputs(buf, nfp) == EOF) + err = errno; + done = B_TRUE; + } + break; + case DLMGMT_DB_OP_DELETE: + /* + * Delete is simple. If buf does not represent the + * link we're deleting, write it out. + */ + if (req->ls_linkid != link_in_file->ll_linkid) { + if (fputs(buf, nfp) == EOF) + err = errno; + } else { + done = B_TRUE; + } + break; + case DLMGMT_DB_OP_READ: + default: + err = EINVAL; + break; + } + link_destroy(link_in_file); + } + + /* + * If we get to the end of the file and have not seen what + * req->ll_linkid points to, write it out then. + */ + if (req->ls_op == DLMGMT_DB_OP_WRITE && !done) { + generate_link_line(linkp, req->ls_flags == DLMGMT_PERSIST, buf); + done = B_TRUE; + if (fputs(buf, nfp) == EOF) + err = errno; + } + + if (!done) + err = ENOENT; + + return (err); +} + +/* ARGSUSED1 */ +static int +process_db_read(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp) +{ + avl_index_t name_where, id_where; + dlmgmt_link_t *link_in_file; + dlmgmt_link_t *linkp1, *linkp2; + char buf[MAXLINELEN]; + int err = 0; + + /* + * This loop processes each line of the configuration file. + */ + while (fgets(buf, MAXLINELEN, fp) != NULL) { + if (!process_link_line(buf, &link_in_file)) { + err = EINVAL; + break; + } + + /* + * Skip the comment line. + */ + if (link_in_file == NULL) + continue; + + linkp1 = avl_find(&dlmgmt_name_avl, link_in_file, &name_where); + linkp2 = avl_find(&dlmgmt_id_avl, link_in_file, &id_where); + if ((linkp1 != NULL) || (linkp2 != NULL)) { + /* + * If any of the following conditions are met, this is + * a duplicate entry: + * + * 1. link2 (with the given name) and link2 (with the + * given id) are not the same link; + * 2. This is a persistent req and find the link with + * the given name and id. Note that persistent db + * is read before the active one. + * 3. Found the link with the given name and id but + * the link is already active. + */ + if ((linkp1 != linkp2) || + (req->ls_flags == DLMGMT_PERSIST) || + ((linkp1->ll_flags & DLMGMT_ACTIVE) != 0)) { + dlmgmt_log(LOG_WARNING, "Duplicate link " + "entries in repository: link name %s " + "link id %i", link_in_file->ll_link, + link_in_file->ll_linkid); + } else { + linkp1->ll_flags |= DLMGMT_ACTIVE; + } + link_destroy(link_in_file); + } else { + avl_insert(&dlmgmt_name_avl, link_in_file, name_where); + avl_insert(&dlmgmt_id_avl, link_in_file, id_where); + dlmgmt_advance(link_in_file); + link_in_file->ll_flags |= req->ls_flags; + } + } + + return (err); +} + +/* + * Generate an entry in the link database. + * Each entry has this format: + * <link id> <prop0>=<type>,<val>;...;<propn>=<type>,<val>; + */ +static void +generate_link_line(dlmgmt_link_t *linkp, boolean_t persist, char *buf) +{ + char tmpbuf[MAXLINELEN]; + char *ptr; + char *lim = tmpbuf + MAXLINELEN; + char *name_to_write = NULL; + datalink_id_t id_to_write; + dlmgmt_linkattr_t *cur_p = NULL; + uint64_t u64; + + ptr = tmpbuf; + id_to_write = linkp->ll_linkid; + ptr += snprintf(ptr, BUFLEN(lim, ptr), "%d\t", id_to_write); + name_to_write = linkp->ll_link; + ptr += write_str(ptr, BUFLEN(lim, ptr), "name", name_to_write); + u64 = linkp->ll_class; + ptr += write_uint64(ptr, BUFLEN(lim, ptr), "class", &u64); + u64 = linkp->ll_media; + ptr += write_uint64(ptr, BUFLEN(lim, ptr), "media", &u64); + + /* + * The daemon does not keep any active link attribute. If this request + * is for active configuration, we are done. + */ + if (!persist) + goto done; + + for (cur_p = linkp->ll_head; cur_p != NULL; cur_p = cur_p->lp_next) { + ptr += translators[cur_p->lp_type].write_func(ptr, + BUFLEN(lim, ptr), cur_p->lp_name, cur_p->lp_val); + } +done: + if (ptr > lim) + return; + (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf); +} + +int +dlmgmt_delete_db_entry(datalink_id_t linkid, uint32_t flags) +{ + return (dlmgmt_db_update(DLMGMT_DB_OP_DELETE, linkid, flags)); +} + +int +dlmgmt_write_db_entry(datalink_id_t linkid, uint32_t flags) +{ + int err; + + if (flags & DLMGMT_PERSIST) { + if ((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, + linkid, DLMGMT_PERSIST)) != 0) { + return (err); + } + } + + if (flags & DLMGMT_ACTIVE) { + if (((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, + linkid, DLMGMT_ACTIVE)) != 0) && + (flags & DLMGMT_PERSIST)) { + (void) dlmgmt_db_update(DLMGMT_DB_OP_DELETE, + linkid, DLMGMT_PERSIST); + return (err); + } + } + + return (0); +} + +/* + * Initialize the datalink <link name, linkid> mapping and the link's + * attributes list based on the configuration file /etc/dladm/datalink.conf + * and the active configuration cache file + * /etc/svc/volatile/datalink-management:default.cache. + * + * This function is called when the datalink-management service is started + * during reboot, and when the dlmgmtd daemon is restarted. + */ +int +dlmgmt_db_init() +{ + char filename[MAXPATHLEN]; + dlmgmt_db_req_t req; + int err; + dlmgmt_link_t *linkp; + char *fmri, *c; + + /* + * First derive the name of the cache file from the FMRI name. This + * cache name is used to keep active datalink configuration. + */ + if (debug) { + (void) snprintf(cachefile, MAXPATHLEN, "%s/%s%s", + DLMGMT_TEMP_DB_DIR, progname, ".debug.cache"); + } else { + if ((fmri = getenv("SMF_FMRI")) == NULL) { + dlmgmt_log(LOG_WARNING, "dlmgmtd is an smf(5) managed " + "service and should not be run from the command " + "line."); + return (EINVAL); + } + + /* + * The FMRI name is in the form of + * svc:/service/service:instance. We need to remove the + * prefix "svc:/" and replace '/' with '-'. The cache file + * name is in the form of "service:instance.cache". + */ + if ((c = strchr(fmri, '/')) != NULL) + c++; + else + c = fmri; + (void) snprintf(filename, MAXPATHLEN, "%s.cache", c); + for (c = filename; *c != '\0'; c++) { + if (*c == '/') + *c = '-'; + } + + (void) snprintf(cachefile, MAXPATHLEN, "%s/%s", + DLMGMT_TEMP_DB_DIR, filename); + } + + dlmgmt_table_lock(B_TRUE); + + req.ls_next = NULL; + req.ls_op = DLMGMT_DB_OP_READ; + req.ls_linkid = DATALINK_INVALID_LINKID; + req.ls_flags = DLMGMT_PERSIST; + + if ((err = dlmgmt_process_db_req(&req)) != 0) + goto done; + + req.ls_flags = DLMGMT_ACTIVE; + err = dlmgmt_process_db_req(&req); + if (err == ENOENT) { + /* + * The temporary datalink.conf does not exist. This is + * the first boot. Mark all the physical links active. + */ + for (linkp = avl_first(&dlmgmt_id_avl); linkp != NULL; + linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) { + if (linkp->ll_class == DATALINK_CLASS_PHYS) { + linkp->ll_flags |= DLMGMT_ACTIVE; + (void) dlmgmt_write_db_entry( + linkp->ll_linkid, DLMGMT_ACTIVE); + } + } + err = 0; + } + +done: + dlmgmt_table_unlock(); + return (err); +} diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_door.c b/usr/src/cmd/dlmgmtd/dlmgmt_door.c new file mode 100644 index 0000000000..a4c0de808e --- /dev/null +++ b/usr/src/cmd/dlmgmtd/dlmgmt_door.c @@ -0,0 +1,1075 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Main door handler functions used by dlmgmtd to process the different door + * call requests. Door call requests can come from the user-land applications, + * which will be handled by dlmgmt_call_handler(); or they can come from the + * kernel, which will be handled by dlmgmt_upcall_handler(). + */ + +#include <assert.h> +#include <stdlib.h> +#include <alloca.h> +#include <strings.h> +#include <libdlmgmt.h> +#include "dlmgmt_impl.h" + +static dlmgmt_link_t * +dlmgmt_getlink_by_dev(char *devname) +{ + dlmgmt_link_t *linkp = avl_first(&dlmgmt_id_avl); + + for (; linkp != NULL; linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) { + if ((linkp->ll_class == DATALINK_CLASS_PHYS) && + linkattr_equal(&(linkp->ll_head), FDEVNAME, devname, + strlen(devname) + 1)) { + return (linkp); + } + } + return (NULL); +} + +static void +dlmgmt_upcall_create(dlmgmt_upcall_arg_create_t *create, + dlmgmt_create_retval_t *retvalp) +{ + datalink_class_t class; + uint32_t media; + dlmgmt_link_t *linkp; + char link[MAXLINKNAMELEN]; + uint32_t flags; + int err; + boolean_t created = B_FALSE; + + /* + * Determine whether this link is persistent. Note that this request + * is coming from kernel so this link must be active. + */ + flags = DLMGMT_ACTIVE | (create->ld_persist ? DLMGMT_PERSIST : 0); + + class = create->ld_class; + media = create->ld_media; + + /* + * Hold the writer lock to update the link table. + */ + dlmgmt_table_lock(B_TRUE); + + /* + * Check to see whether this is the reattachment of an existing + * physical link. If so, return its linkid. + */ + if ((class == DATALINK_CLASS_PHYS) && + (linkp = dlmgmt_getlink_by_dev(create->ld_devname)) != NULL) { + err = linkattr_set(&(linkp->ll_head), FPHYMAJ, + &create->ld_phymaj, sizeof (uint64_t), DLADM_TYPE_UINT64); + if (err != 0) + goto done; + + err = linkattr_set(&(linkp->ll_head), FPHYINST, + &create->ld_phyinst, sizeof (uint64_t), DLADM_TYPE_UINT64); + if (err != 0) + goto done; + + linkp->ll_flags |= flags; + linkp->ll_gen++; + goto done; + } + + if ((err = dlmgmt_create_common(create->ld_devname, class, media, + flags, &linkp)) == EEXIST) { + /* + * The link name already exists. Return error if this is a + * non-physical link (in that case, the link name must be + * the same as the given name). + */ + if (class != DATALINK_CLASS_PHYS) + goto done; + + /* + * The physical link's name already exists, request + * a suggested link name: net<nextppa> + */ + err = dlmgmt_generate_name("net", link, MAXLINKNAMELEN); + if (err != 0) + goto done; + + err = dlmgmt_create_common(link, class, media, flags, &linkp); + } + + if (err != 0) + goto done; + + created = B_TRUE; + + /* + * This is a new link. Only need to persist link attributes for + * physical links. + */ + if (class == DATALINK_CLASS_PHYS && + (((err = linkattr_set(&linkp->ll_head, FDEVNAME, create->ld_devname, + strlen(create->ld_devname) + 1, DLADM_TYPE_STR)) != 0) || + ((err = linkattr_set(&linkp->ll_head, FPHYMAJ, &create->ld_phymaj, + sizeof (uint64_t), DLADM_TYPE_UINT64)) != 0) || + ((err = linkattr_set(&linkp->ll_head, FPHYINST, &create->ld_phyinst, + sizeof (uint64_t), DLADM_TYPE_UINT64)) != 0))) { + (void) dlmgmt_destroy_common(linkp, flags); + goto done; + } + +done: + if ((err == 0) && ((err = dlmgmt_write_db_entry(linkp->ll_linkid, + linkp->ll_flags)) != 0) && created) { + (void) dlmgmt_destroy_common(linkp, flags); + } + + if (err == 0) + retvalp->lr_linkid = linkp->ll_linkid; + + retvalp->lr_err = err; + dlmgmt_table_unlock(); +} + +static void +dlmgmt_upcall_update(dlmgmt_upcall_arg_update_t *update, + dlmgmt_update_retval_t *retvalp) +{ + uint32_t media = update->ld_media; + dlmgmt_link_t *linkp; + int err = 0; + + /* + * Hold the writer lock to update the link table. + */ + dlmgmt_table_lock(B_TRUE); + + /* + * Check to see whether this is the reattachment of an existing + * physical link. If so, return its linkid. + */ + if ((linkp = dlmgmt_getlink_by_dev(update->ld_devname)) == NULL) { + err = ENOENT; + goto done; + } + + retvalp->lr_linkid = linkp->ll_linkid; + retvalp->lr_media = media; + if (linkp->ll_media != media && linkp->ll_media != DL_OTHER) { + /* + * Assume a DL_ETHER link ce0, a DL_WIFI link ath0 + * 1. # dladm rename-link ce0 net0 + * 2. DR out ce0. net0 is down. + * 3. use rename-link to have the ath0 device inherit + * the configuration from net0 + * # dladm rename-link ath0 net0 + * 4. DR in ath0. + * As ath0 and ce0 do not have the same media type, ath0 + * cannot inherit the configuration of net0. + */ + err = EEXIST; + + /* + * Return the media type of the existing link to indicate the + * reason for the name conflict. + */ + retvalp->lr_media = linkp->ll_media; + goto done; + } + + if (update->ld_novanity && + (strcmp(update->ld_devname, linkp->ll_link) != 0)) { + /* + * Return an error if this is a physical link that does not + * support vanity naming, but the link name is not the same + * as the given device name. + */ + err = EEXIST; + goto done; + } + + linkp->ll_media = media; + linkp->ll_gen++; + + (void) dlmgmt_write_db_entry(linkp->ll_linkid, linkp->ll_flags); + +done: + dlmgmt_table_unlock(); + retvalp->lr_err = err; +} + +static void +dlmgmt_upcall_destroy(dlmgmt_upcall_arg_destroy_t *destroy, + dlmgmt_destroy_retval_t *retvalp) +{ + datalink_id_t linkid = destroy->ld_linkid; + dlmgmt_link_t *linkp = NULL; + uint32_t flags, dflags = 0; + int err = 0; + + flags = DLMGMT_ACTIVE | (destroy->ld_persist ? DLMGMT_PERSIST : 0); + + /* + * Hold the writer lock to update the link table. + */ + dlmgmt_table_lock(B_TRUE); + + if ((linkp = link_by_id(linkid)) == NULL) { + err = ENOENT; + goto done; + } + + if (((linkp->ll_flags & flags) & DLMGMT_ACTIVE) && + ((err = dlmgmt_delete_db_entry(linkid, DLMGMT_ACTIVE)) != 0)) { + dflags = DLMGMT_ACTIVE; + goto done; + } + + if (((linkp->ll_flags & flags) & DLMGMT_PERSIST) && + ((err = dlmgmt_delete_db_entry(linkid, DLMGMT_PERSIST)) != 0)) { + if (dflags != 0) + (void) dlmgmt_write_db_entry(linkp->ll_linkid, dflags); + dflags |= DLMGMT_PERSIST; + goto done; + } + + if ((err = dlmgmt_destroy_common(linkp, flags)) != 0 && dflags != 0) + (void) dlmgmt_write_db_entry(linkp->ll_linkid, dflags); + +done: + dlmgmt_table_unlock(); + retvalp->lr_err = err; +} + +static void +dlmgmt_getname(dlmgmt_door_getname_t *getname, dlmgmt_getname_retval_t *retvalp) +{ + dlmgmt_link_t *linkp; + int err = 0; + + /* + * Hold the reader lock to access the link + */ + dlmgmt_table_lock(B_FALSE); + if ((linkp = link_by_id(getname->ld_linkid)) == NULL) { + /* + * The link does not exists. + */ + err = ENOENT; + goto done; + } + + if (strlcpy(retvalp->lr_link, linkp->ll_link, MAXLINKNAMELEN) >= + MAXLINKNAMELEN) { + err = ENOSPC; + goto done; + } + retvalp->lr_flags = linkp->ll_flags; + retvalp->lr_class = linkp->ll_class; + retvalp->lr_media = linkp->ll_media; + +done: + dlmgmt_table_unlock(); + retvalp->lr_err = err; +} + +static void +dlmgmt_getlinkid(dlmgmt_door_getlinkid_t *getlinkid, + dlmgmt_getlinkid_retval_t *retvalp) +{ + dlmgmt_link_t *linkp; + int err = 0; + + /* + * Hold the reader lock to access the link + */ + dlmgmt_table_lock(B_FALSE); + if ((linkp = link_by_name(getlinkid->ld_link)) == NULL) { + /* + * The link does not exists. + */ + err = ENOENT; + goto done; + } + + retvalp->lr_linkid = linkp->ll_linkid; + retvalp->lr_flags = linkp->ll_flags; + retvalp->lr_class = linkp->ll_class; + retvalp->lr_media = linkp->ll_media; + +done: + dlmgmt_table_unlock(); + retvalp->lr_err = err; +} + +static void +dlmgmt_getnext(dlmgmt_door_getnext_t *getnext, dlmgmt_getnext_retval_t *retvalp) +{ + dlmgmt_link_t link, *linkp; + datalink_id_t linkid = getnext->ld_linkid; + avl_index_t where; + int err = 0; + + /* + * Hold the reader lock to access the link + */ + dlmgmt_table_lock(B_FALSE); + + link.ll_linkid = (linkid + 1); + linkp = avl_find(&dlmgmt_id_avl, &link, &where); + if (linkp == NULL) + linkp = avl_nearest(&dlmgmt_id_avl, where, AVL_AFTER); + + for (; linkp != NULL; linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) { + if ((linkp->ll_class & getnext->ld_class) && + (linkp->ll_flags & getnext->ld_flags) && + DATALINK_MEDIA_ACCEPTED(getnext->ld_dmedia, + linkp->ll_media)) + break; + } + + if (linkp == NULL) { + err = ENOENT; + } else { + retvalp->lr_linkid = linkp->ll_linkid; + retvalp->lr_class = linkp->ll_class; + retvalp->lr_media = linkp->ll_media; + retvalp->lr_flags = linkp->ll_flags; + } + + dlmgmt_table_unlock(); + retvalp->lr_err = err; +} + +/* + * Note that the caller needs to free the memory of *retvalp, when it returns + * success. + */ +static int +dlmgmt_upcall_getattr(dlmgmt_upcall_arg_getattr_t *getattr, + dlmgmt_getattr_retval_t **retvalpp, size_t *retszp) +{ + dlmgmt_link_t *linkp; + int err = 0; + + /* + * Hold the reader lock to access the link + */ + dlmgmt_table_lock(B_FALSE); + if ((linkp = link_by_id(getattr->ld_linkid)) == NULL) { + /* + * The link does not exist. + */ + err = ENOENT; + goto done; + } + + err = dlmgmt_getattr_common(&linkp->ll_head, getattr->ld_attr, + retvalpp, retszp); + +done: + dlmgmt_table_unlock(); + return (err); +} + +static void +dlmgmt_upcall_handler(void *arg, int cmd) +{ + switch (cmd) { + case DLMGMT_CMD_DLS_CREATE: { + dlmgmt_create_retval_t retval; + + dlmgmt_upcall_create(arg, &retval); + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + break; + } + case DLMGMT_CMD_DLS_UPDATE: { + dlmgmt_update_retval_t retval; + + dlmgmt_upcall_update(arg, &retval); + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + break; + } + case DLMGMT_CMD_DLS_GETATTR: { + dlmgmt_getattr_retval_t retval; + dlmgmt_getattr_retval_t *retvalp = NULL; + dlmgmt_getattr_retval_t *tmp; + size_t retsz = 0; + int err; + + if ((err = dlmgmt_upcall_getattr(arg, &retvalp, &retsz)) != 0) { + retval.lr_err = err; + retvalp = &retval; + retsz = sizeof (retval); + } else { + /* + * For the successful case, retvalp points to + * memory that was allocated with malloc. But, since + * door_return never returns, that memory gets leaked. + * Use alloca and free retvalp. + */ + tmp = alloca(retsz); + bcopy(retvalp, tmp, retsz); + free(retvalp); + retvalp = tmp; + } + (void) door_return((char *)retvalp, retsz, NULL, 0); + break; + } + case DLMGMT_CMD_DLS_DESTROY: { + dlmgmt_destroy_retval_t retval; + + dlmgmt_upcall_destroy(arg, &retval); + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + break; + } + case DLMGMT_CMD_GETNAME: { + dlmgmt_getname_retval_t retval; + + dlmgmt_getname(arg, &retval); + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + break; + } + case DLMGMT_CMD_GETLINKID: { + dlmgmt_getlinkid_retval_t retval; + + dlmgmt_getlinkid(arg, &retval); + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + break; + } + case DLMGMT_CMD_GETNEXT: { + dlmgmt_getnext_retval_t retval; + + dlmgmt_getnext(arg, &retval); + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + break; + } + default: { + struct dlmgmt_null_retval_s retval; + + retval.lr_err = EINVAL; + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + break; + } + } +} + +static void +dlmgmt_createid(dlmgmt_door_createid_t *createid, + dlmgmt_createid_retval_t *retvalp) +{ + dlmgmt_link_t *linkp; + datalink_id_t linkid = DATALINK_INVALID_LINKID; + char link[MAXLINKNAMELEN]; + int err; + + /* + * Hold the writer lock to update the dlconf table. + */ + dlmgmt_table_lock(B_TRUE); + + if (createid->ld_prefix) { + err = dlmgmt_generate_name(createid->ld_link, link, + MAXLINKNAMELEN); + if (err != 0) + goto done; + + err = dlmgmt_create_common(link, createid->ld_class, + createid->ld_media, createid->ld_flags, &linkp); + } else { + err = dlmgmt_create_common(createid->ld_link, + createid->ld_class, createid->ld_media, createid->ld_flags, + &linkp); + } + + if (err == 0) { + /* + * Keep the active mapping. + */ + linkid = linkp->ll_linkid; + if (createid->ld_flags & DLMGMT_ACTIVE) + (void) dlmgmt_write_db_entry(linkid, DLMGMT_ACTIVE); + } + +done: + dlmgmt_table_unlock(); + retvalp->lr_linkid = linkid; + retvalp->lr_err = err; +} + +static void +dlmgmt_destroyid(dlmgmt_door_destroyid_t *destroyid, + dlmgmt_destroyid_retval_t *retvalp) +{ + datalink_id_t linkid = destroyid->ld_linkid; + uint32_t flags = destroyid->ld_flags; + dlmgmt_link_t *linkp = NULL; + int err = 0; + + /* + * Hold the writer lock to update the link table. + */ + dlmgmt_table_lock(B_TRUE); + if ((linkp = link_by_id(linkid)) == NULL) { + err = ENOENT; + goto done; + } + + if ((err = dlmgmt_destroy_common(linkp, flags)) != 0) + goto done; + + /* + * Delete the active mapping. + */ + if (flags & DLMGMT_ACTIVE) + (void) dlmgmt_delete_db_entry(linkid, DLMGMT_ACTIVE); + +done: + dlmgmt_table_unlock(); + retvalp->lr_err = err; +} + +/* + * Remap a linkid to a given link name, i.e., rename an existing link1 + * (ld_linkid) to a non-existent link2 (ld_link): rename link1's name to + * the given link name. + */ +static void +dlmgmt_remapid(dlmgmt_door_remapid_t *remapid, + dlmgmt_remapid_retval_t *retvalp) +{ + datalink_id_t linkid1 = remapid->ld_linkid; + dlmgmt_link_t link, *linkp1, *tmp; + avl_index_t where; + int err = 0; + + if (!dladm_valid_linkname(remapid->ld_link)) { + retvalp->lr_err = EINVAL; + return; + } + + /* + * Hold the writer lock to update the link table. + */ + dlmgmt_table_lock(B_TRUE); + if ((linkp1 = link_by_id(linkid1)) == NULL) { + err = ENOENT; + goto done; + } + + if (link_by_name(remapid->ld_link) != NULL) { + err = EEXIST; + goto done; + } + + avl_remove(&dlmgmt_name_avl, linkp1); + (void) strlcpy(link.ll_link, remapid->ld_link, MAXLINKNAMELEN); + tmp = avl_find(&dlmgmt_name_avl, &link, &where); + assert(tmp == NULL); + (void) strlcpy(linkp1->ll_link, remapid->ld_link, MAXLINKNAMELEN); + avl_insert(&dlmgmt_name_avl, linkp1, where); + dlmgmt_advance(linkp1); + + /* + * If we renamed a temporary link, update the temporary repository. + */ + if (linkp1->ll_flags & DLMGMT_ACTIVE) + (void) dlmgmt_write_db_entry(linkid1, DLMGMT_ACTIVE); +done: + dlmgmt_table_unlock(); + retvalp->lr_err = err; +} + +static void +dlmgmt_upid(dlmgmt_door_upid_t *upid, dlmgmt_upid_retval_t *retvalp) +{ + dlmgmt_link_t *linkp; + int err = 0; + + /* + * Hold the writer lock to update the link table. + */ + dlmgmt_table_lock(B_TRUE); + if ((linkp = link_by_id(upid->ld_linkid)) == NULL) { + err = ENOENT; + goto done; + } + + if (linkp->ll_flags & DLMGMT_ACTIVE) { + err = EINVAL; + goto done; + } + + linkp->ll_flags |= DLMGMT_ACTIVE; + (void) dlmgmt_write_db_entry(linkp->ll_linkid, DLMGMT_ACTIVE); +done: + dlmgmt_table_unlock(); + retvalp->lr_err = err; +} + +static void +dlmgmt_createconf(dlmgmt_door_createconf_t *createconf, + dlmgmt_createconf_retval_t *retvalp) +{ + dlmgmt_dlconf_t dlconf, *dlconfp, *tmp; + avl_index_t where; + int err; + + /* + * Hold the writer lock to update the dlconf table. + */ + dlmgmt_dlconf_table_lock(B_TRUE); + + if ((err = dlconf_create(createconf->ld_link, createconf->ld_linkid, + createconf->ld_class, createconf->ld_media, &dlconfp)) != 0) { + goto done; + } + + dlconf.ld_id = dlconfp->ld_id; + tmp = avl_find(&dlmgmt_dlconf_avl, &dlconf, &where); + assert(tmp == NULL); + avl_insert(&dlmgmt_dlconf_avl, dlconfp, where); + dlmgmt_advance_dlconfid(dlconfp); + + retvalp->lr_conf = (dladm_conf_t)dlconfp->ld_id; +done: + dlmgmt_dlconf_table_unlock(); + retvalp->lr_err = err; +} + +static void +dlmgmt_setattr(dlmgmt_door_setattr_t *setattr, size_t argsz, + dlmgmt_setattr_retval_t *retvalp) +{ + dlmgmt_dlconf_t dlconf, *dlconfp; + int err = 0; + + if (argsz < sizeof (dlmgmt_door_setattr_t) || + argsz != sizeof (dlmgmt_door_setattr_t) + setattr->ld_attrsz - 1) { + retvalp->lr_err = EINVAL; + return; + } + + /* + * Hold the writer lock to update the dlconf table. + */ + dlmgmt_dlconf_table_lock(B_TRUE); + + dlconf.ld_id = (int)setattr->ld_conf; + dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); + if (dlconfp == NULL) { + err = ENOENT; + goto done; + } + + err = linkattr_set(&(dlconfp->ld_head), setattr->ld_attr, + &setattr->ld_attrval, setattr->ld_attrsz, setattr->ld_type); + +done: + dlmgmt_dlconf_table_unlock(); + retvalp->lr_err = err; +} + +static void +dlmgmt_unsetattr(dlmgmt_door_unsetattr_t *unsetattr, + dlmgmt_unsetattr_retval_t *retvalp) +{ + dlmgmt_dlconf_t dlconf, *dlconfp; + int err = 0; + + /* + * Hold the writer lock to update the dlconf table. + */ + dlmgmt_dlconf_table_lock(B_TRUE); + + dlconf.ld_id = (int)unsetattr->ld_conf; + dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); + if (dlconfp == NULL) { + err = ENOENT; + goto done; + } + + err = linkattr_unset(&(dlconfp->ld_head), unsetattr->ld_attr); + +done: + dlmgmt_dlconf_table_unlock(); + retvalp->lr_err = err; +} + +/* + * Note that dlmgmt_readconf() returns a conf ID of a conf AVL tree entry, + * which is managed by dlmgmtd. The ID is used to find the conf entry when + * dlmgmt_write_conf() is called. The conf entry contains an ld_gen value + * (which is the generation number - ll_gen) of the dlmgmt_link_t at the time + * of dlmgmt_readconf(), and ll_gen changes every time the dlmgmt_link_t + * changes its attributes. Therefore, dlmgmt_write_conf() can compare ld_gen + * in the conf entry against the latest dlmgmt_link_t ll_gen value to see if + * anything has changed between the dlmgmt_read_conf() and dlmgmt_writeconf() + * calls. If so, EAGAIN is returned. This mechanism can ensures atomicity + * across the pair of dladm_read_conf() and dladm_write_conf() calls. + */ +static void +dlmgmt_writeconf(dlmgmt_door_writeconf_t *writeconf, + dlmgmt_writeconf_retval_t *retvalp) +{ + dlmgmt_dlconf_t dlconf, *dlconfp; + dlmgmt_link_t *linkp; + dlmgmt_linkattr_t *attrp, *next; + int err = 0; + + /* + * Hold the read lock to access the dlconf table. + */ + dlmgmt_dlconf_table_lock(B_TRUE); + + dlconf.ld_id = (int)writeconf->ld_conf; + dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); + if (dlconfp == NULL) { + err = ENOENT; + goto done; + } + + /* + * Hold the writer lock to update the link table. + */ + dlmgmt_table_lock(B_TRUE); + linkp = link_by_id(dlconfp->ld_linkid); + if ((linkp == NULL) || (linkp->ll_class != dlconfp->ld_class) || + (linkp->ll_media != dlconfp->ld_media) || + (strcmp(linkp->ll_link, dlconfp->ld_link) != 0)) { + /* + * The link does not exist. + */ + dlmgmt_table_unlock(); + err = ENOENT; + goto done; + } + + if (linkp->ll_gen != dlconfp->ld_gen) { + /* + * Something has changed the link configuration; try again. + */ + dlmgmt_table_unlock(); + err = EAGAIN; + goto done; + } + + /* + * Delete the old attribute list. + */ + for (attrp = linkp->ll_head; attrp != NULL; attrp = next) { + next = attrp->lp_next; + free(attrp->lp_val); + free(attrp); + } + linkp->ll_head = NULL; + + /* + * Set the new attribute. + */ + for (attrp = dlconfp->ld_head; attrp != NULL; attrp = attrp->lp_next) { + if ((err = linkattr_set(&(linkp->ll_head), attrp->lp_name, + attrp->lp_val, attrp->lp_sz, attrp->lp_type)) != 0) { + dlmgmt_table_unlock(); + goto done; + } + } + + linkp->ll_gen++; + err = dlmgmt_write_db_entry(linkp->ll_linkid, DLMGMT_PERSIST); + dlmgmt_table_unlock(); +done: + dlmgmt_dlconf_table_unlock(); + retvalp->lr_err = err; +} + +static void +dlmgmt_removeconf(dlmgmt_door_removeconf_t *removeconf, + dlmgmt_removeconf_retval_t *retvalp) +{ + int err; + + dlmgmt_table_lock(B_TRUE); + err = dlmgmt_delete_db_entry(removeconf->ld_linkid, DLMGMT_PERSIST); + dlmgmt_table_unlock(); + retvalp->lr_err = err; +} + +static void +dlmgmt_destroyconf(dlmgmt_door_destroyconf_t *destroyconf, + dlmgmt_destroyconf_retval_t *retvalp) +{ + dlmgmt_dlconf_t dlconf, *dlconfp; + int err = 0; + + /* + * Hold the writer lock to update the dlconf table. + */ + dlmgmt_dlconf_table_lock(B_TRUE); + + dlconf.ld_id = (int)destroyconf->ld_conf; + dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); + if (dlconfp == NULL) { + err = ENOENT; + goto done; + } + + avl_remove(&dlmgmt_dlconf_avl, dlconfp); + dlconf_destroy(dlconfp); + +done: + dlmgmt_dlconf_table_unlock(); + retvalp->lr_err = err; +} + +/* + * See the comments above dladm_write_conf() to see how ld_gen is used to + * ensure atomicity across the {dlmgmt_readconf(), dlmgmt_writeconf()} pair. + */ +static void +dlmgmt_readconf(dlmgmt_door_readconf_t *readconf, + dlmgmt_readconf_retval_t *retvalp) +{ + dlmgmt_link_t *linkp; + datalink_id_t linkid = readconf->ld_linkid; + dlmgmt_dlconf_t *dlconfp, *tmp, dlconf; + dlmgmt_linkattr_t *attrp; + avl_index_t where; + int err = 0; + + /* + * Hold the writer lock to update the dlconf table. + */ + dlmgmt_dlconf_table_lock(B_TRUE); + + /* + * Hold the reader lock to access the link + */ + dlmgmt_table_lock(B_FALSE); + linkp = link_by_id(linkid); + if ((linkp == NULL) || !(linkp->ll_flags & DLMGMT_PERSIST)) { + /* + * The persistent link configuration does not exists. + */ + err = ENOENT; + goto done; + } + + if ((err = dlconf_create(linkp->ll_link, linkp->ll_linkid, + linkp->ll_class, linkp->ll_media, &dlconfp)) != 0) { + goto done; + } + + for (attrp = linkp->ll_head; attrp != NULL; attrp = attrp->lp_next) { + if ((err = linkattr_set(&(dlconfp->ld_head), attrp->lp_name, + attrp->lp_val, attrp->lp_sz, attrp->lp_type)) != 0) { + dlconf_destroy(dlconfp); + goto done; + } + } + dlconfp->ld_gen = linkp->ll_gen; + + dlconf.ld_id = dlconfp->ld_id; + tmp = avl_find(&dlmgmt_dlconf_avl, &dlconf, &where); + assert(tmp == NULL); + avl_insert(&dlmgmt_dlconf_avl, dlconfp, where); + dlmgmt_advance_dlconfid(dlconfp); + + retvalp->lr_conf = (dladm_conf_t)dlconfp->ld_id; +done: + dlmgmt_table_unlock(); + dlmgmt_dlconf_table_unlock(); + retvalp->lr_err = err; +} + +/* + * Note: the caller must free *retvalpp in case of success. + */ +static int +dlmgmt_getattr(dlmgmt_door_getattr_t *getattr, + dlmgmt_getattr_retval_t **retvalpp, size_t *retszp) +{ + dlmgmt_dlconf_t dlconf, *dlconfp; + int err = 0; + + /* + * Hold the writer lock to update the dlconf table. + */ + dlmgmt_dlconf_table_lock(B_FALSE); + + dlconf.ld_id = (int)getattr->ld_conf; + dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL); + if (dlconfp == NULL) { + err = ENOENT; + goto done; + } + + err = dlmgmt_getattr_common(&dlconfp->ld_head, getattr->ld_attr, + retvalpp, retszp); + +done: + dlmgmt_dlconf_table_unlock(); + return (err); +} + +static void +dlmgmt_call_handler(void *arg, size_t argsz, int cmd) +{ + switch (cmd) { + case DLMGMT_CMD_CREATE_LINKID: { + dlmgmt_createid_retval_t retval; + + dlmgmt_createid(arg, &retval); + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + break; + } + case DLMGMT_CMD_DESTROY_LINKID: { + dlmgmt_destroyid_retval_t retval; + + dlmgmt_destroyid(arg, &retval); + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + break; + } + case DLMGMT_CMD_REMAP_LINKID: { + dlmgmt_remapid_retval_t retval; + + dlmgmt_remapid(arg, &retval); + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + break; + } + case DLMGMT_CMD_UP_LINKID: { + dlmgmt_upid_retval_t retval; + + dlmgmt_upid(arg, &retval); + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + break; + } + case DLMGMT_CMD_CREATECONF: { + dlmgmt_createconf_retval_t retval; + + dlmgmt_createconf(arg, &retval); + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + break; + } + case DLMGMT_CMD_SETATTR: { + dlmgmt_setattr_retval_t retval; + + dlmgmt_setattr(arg, argsz, &retval); + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + break; + } + case DLMGMT_CMD_UNSETATTR: { + dlmgmt_unsetattr_retval_t retval; + + dlmgmt_unsetattr(arg, &retval); + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + break; + } + case DLMGMT_CMD_WRITECONF: { + dlmgmt_writeconf_retval_t retval; + + dlmgmt_writeconf(arg, &retval); + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + break; + } + case DLMGMT_CMD_REMOVECONF: { + dlmgmt_removeconf_retval_t retval; + + dlmgmt_removeconf(arg, &retval); + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + break; + } + case DLMGMT_CMD_DESTROYCONF: { + dlmgmt_destroyconf_retval_t retval; + + dlmgmt_destroyconf(arg, &retval); + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + break; + } + case DLMGMT_CMD_READCONF: { + dlmgmt_readconf_retval_t retval; + + dlmgmt_readconf(arg, &retval); + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + break; + } + case DLMGMT_CMD_GETATTR: { + dlmgmt_getattr_retval_t retval; + dlmgmt_getattr_retval_t *retvalp = NULL; + dlmgmt_getattr_retval_t *tmp; + int err; + size_t retsz = 0; + + if ((err = dlmgmt_getattr(arg, &retvalp, &retsz)) != 0) { + retval.lr_err = err; + retvalp = &retval; + retsz = sizeof (retval); + } else { + /* + * For the successful case, retvalp points to memory + * that was allocated in dlmgmt_getattr(). Since + * door_return never returns, that memory would get + * leaked. So we use alloca instead, and free retvalp. + */ + tmp = alloca(retsz); + bcopy(retvalp, tmp, retsz); + free(retvalp); + retvalp = tmp; + } + (void) door_return((char *)retvalp, retsz, NULL, 0); + break; + } + default: { + struct dlmgmt_null_retval_s retval; + + retval.lr_err = EINVAL; + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); + break; + } + } +} + +/* ARGSUSED */ +void +dlmgmt_handler(void *cookie, char *argp, size_t argsz, door_desc_t *dp, + uint_t n_desc) +{ + int cmd = ((dlmgmt_door_arg_t *)(void *)argp)->ld_cmd; + + if (cmd < DLMGMT_CMD_BASE) { + /* + * Upcall request from the dls module. + */ + dlmgmt_upcall_handler(argp, cmd); + } else { + /* + * Door call request from libdladm. + */ + dlmgmt_call_handler(argp, argsz, cmd); + } +} diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_impl.h b/usr/src/cmd/dlmgmtd/dlmgmt_impl.h new file mode 100644 index 0000000000..365dc60d07 --- /dev/null +++ b/usr/src/cmd/dlmgmtd/dlmgmt_impl.h @@ -0,0 +1,134 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Functions to maintain a table of datalink configuration information. + */ + +#ifndef _DLMGMT_IMPL_H +#define _DLMGMT_IMPL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <door.h> +#include <libdllink.h> + +/* + * datalink attribute structure + */ +typedef struct dlmgmt_linkattr_s { + struct dlmgmt_linkattr_s *lp_next; + struct dlmgmt_linkattr_s *lp_prev; + char lp_name[MAXLINKATTRLEN]; + void *lp_val; + dladm_datatype_t lp_type; + uint_t lp_sz; +} dlmgmt_linkattr_t; + +/* + * datalink structure + */ +typedef struct dlmgmt_link_s { + dlmgmt_linkattr_t *ll_head; + char ll_link[MAXLINKNAMELEN]; + datalink_class_t ll_class; + uint32_t ll_media; + datalink_id_t ll_linkid; + avl_node_t ll_node_by_name; + avl_node_t ll_node_by_id; + uint32_t ll_flags; + uint32_t ll_gen; /* generation number */ +} dlmgmt_link_t; + +/* + * datalink configuration request structure + */ +typedef struct dlmgmt_dlconf_s { + dlmgmt_linkattr_t *ld_head; + char ld_link[MAXLINKNAMELEN]; + datalink_id_t ld_linkid; + datalink_class_t ld_class; + uint32_t ld_media; + int ld_id; + uint32_t ld_gen; + avl_node_t ld_node; +} dlmgmt_dlconf_t; + +extern boolean_t debug; +extern const char *progname; + +extern avl_tree_t dlmgmt_name_avl; +extern avl_tree_t dlmgmt_id_avl; +extern avl_tree_t dlmgmt_dlconf_avl; + +boolean_t linkattr_equal(dlmgmt_linkattr_t **, const char *, void *, + size_t); +int linkattr_unset(dlmgmt_linkattr_t **, const char *); +int linkattr_set(dlmgmt_linkattr_t **, const char *, void *, + size_t, dladm_datatype_t); +int linkattr_get(dlmgmt_linkattr_t **, const char *, void **, + size_t *, dladm_datatype_t *); + +void link_destroy(dlmgmt_link_t *); +dlmgmt_link_t *link_by_id(datalink_id_t); +dlmgmt_link_t *link_by_name(const char *); +int dlmgmt_create_common(const char *, datalink_class_t, + uint32_t, uint32_t, dlmgmt_link_t **); +int dlmgmt_destroy_common(dlmgmt_link_t *, uint32_t); +int dlmgmt_getattr_common(dlmgmt_linkattr_t **, const char *, + dlmgmt_getattr_retval_t **, size_t *); + +void dlmgmt_advance(dlmgmt_link_t *); +void dlmgmt_table_lock(boolean_t); +void dlmgmt_table_unlock(); + +int dlconf_create(const char *, datalink_id_t, datalink_class_t, + uint32_t, dlmgmt_dlconf_t **); +void dlconf_destroy(dlmgmt_dlconf_t *); +void dlmgmt_advance_dlconfid(dlmgmt_dlconf_t *); +void dlmgmt_dlconf_table_lock(boolean_t); +void dlmgmt_dlconf_table_unlock(void); + +int dlmgmt_generate_name(const char *, char *, size_t); + +int dlmgmt_linktable_init(void); +void dlmgmt_linktable_fini(void); + +void dlmgmt_handler(void *, char *, size_t, door_desc_t *, uint_t); +void dlmgmt_log(int, const char *, ...); +int dlmgmt_write_db_entry(datalink_id_t, uint32_t); +int dlmgmt_delete_db_entry(datalink_id_t, uint32_t); +int dlmgmt_db_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _DLMGMT_IMPL_H */ diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_main.c b/usr/src/cmd/dlmgmtd/dlmgmt_main.c new file mode 100644 index 0000000000..e67d95d355 --- /dev/null +++ b/usr/src/cmd/dlmgmtd/dlmgmt_main.c @@ -0,0 +1,345 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * The dlmgmtd daemon is started by the datalink-management SMF service. + * This daemon is used to manage <link name, linkid> mapping and the + * persistent datalink configuration. + * + * Today, the <link name, linkid> mapping and the persistent configuration + * of datalinks is kept in /etc/dladm/datalink.conf, and the daemon keeps + * a copy of the datalinks in the memory (see dlmgmt_id_avl and + * dlmgmt_name_avl). The active <link name, linkid> mapping is kept in + * /etc/svc/volatile cache file, so that the mapping can be recovered when + * dlmgmtd exits for some reason (e.g., when dlmgmtd is accidentally killed). + */ + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <priv.h> +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <stropts.h> +#include <strings.h> +#include <syslog.h> +#include <sys/dld.h> +#include <unistd.h> +#include <libdlmgmt.h> +#include "dlmgmt_impl.h" + +const char *progname; +boolean_t debug; +static int pfds[2]; +static char dlmgmt_door_file[] = DLMGMT_DOOR; +static int dlmgmt_door_fd = -1; + +static int +dlmgmt_set_doorfd(boolean_t start) +{ + dld_ioc_door_t did; + struct strioctl iocb; + int fd; + int err = 0; + + if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) + return (EINVAL); + + did.did_start_door = start; + + iocb.ic_cmd = DLDIOC_DOORSERVER; + iocb.ic_timout = 0; + iocb.ic_len = sizeof (did); + iocb.ic_dp = (char *)&did; + + if (ioctl(fd, I_STR, &iocb) == -1) + err = errno; + + (void) close(fd); + return (err); +} + +static int +dlmgmt_door_init() +{ + int err; + + if ((dlmgmt_door_fd = door_create(dlmgmt_handler, NULL, + DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) { + err = errno; + dlmgmt_log(LOG_WARNING, "door_create() failed: %s", + strerror(err)); + return (err); + } + if (fattach(dlmgmt_door_fd, DLMGMT_DOOR) != 0) { + err = errno; + dlmgmt_log(LOG_WARNING, "fattach(%s) failed: %s", + DLMGMT_DOOR, strerror(err)); + goto fail; + } + if ((err = dlmgmt_set_doorfd(B_TRUE)) != 0) { + dlmgmt_log(LOG_WARNING, "cannot set kernel doorfd: %s", + strerror(err)); + goto fail; + } + + return (0); +fail: + if (dlmgmt_door_fd != -1) { + (void) door_revoke(dlmgmt_door_fd); + dlmgmt_door_fd = -1; + } + (void) fdetach(DLMGMT_DOOR); + return (err); +} + +static void +dlmgmt_door_fini() +{ + (void) dlmgmt_set_doorfd(B_FALSE); + if ((dlmgmt_door_fd != -1) && (door_revoke(dlmgmt_door_fd) == -1)) { + dlmgmt_log(LOG_WARNING, "door_revoke(%s) failed: %s", + dlmgmt_door_file, strerror(errno)); + } + (void) fdetach(DLMGMT_DOOR); +} + +static int +dlmgmt_init() +{ + int err; + + if ((err = dlmgmt_linktable_init()) != 0) + return (err); + + if ((err = dlmgmt_db_init()) != 0 || (err = dlmgmt_door_init()) != 0) + dlmgmt_linktable_fini(); + + return (err); +} + +static void +dlmgmt_fini() +{ + dlmgmt_door_fini(); + dlmgmt_linktable_fini(); +} + +/* + * This is called by the child process to inform the parent process to + * exit with the given return value. + */ +static void +dlmgmt_inform_parent_exit(int rv) +{ + if (debug) + return; + + if (write(pfds[1], &rv, sizeof (int)) != sizeof (int)) { + dlmgmt_log(LOG_WARNING, + "dlmgmt_inform_parent_exit() failed: %s", strerror(errno)); + (void) close(pfds[1]); + exit(EXIT_FAILURE); + } + (void) close(pfds[1]); +} + +/*ARGSUSED*/ +static void +dlmgmtd_exit(int signo) +{ + (void) close(pfds[1]); + dlmgmt_fini(); + exit(EXIT_FAILURE); +} + +static void +usage(void) +{ + (void) fprintf(stderr, "Usage: %s [-d]\n", progname); + exit(EXIT_FAILURE); +} + +static int +dlmgmt_setup_privs() +{ + priv_set_t *priv_set = NULL; + char *p; + + priv_set = priv_allocset(); + if (priv_set == NULL || getppriv(PRIV_PERMITTED, priv_set) == -1) { + dlmgmt_log(LOG_WARNING, "failed to get the permitted set of " + "privileges %s", strerror(errno)); + return (-1); + } + + p = priv_set_to_str(priv_set, ',', 0); + dlmgmt_log(LOG_DEBUG, "start with privs %s", p != NULL ? p : "Unknown"); + free(p); + + priv_emptyset(priv_set); + (void) priv_addset(priv_set, "file_dac_write"); + (void) priv_addset(priv_set, "file_chown_self"); + (void) priv_addset(priv_set, "sys_mount"); + (void) priv_addset(priv_set, "sys_net_config"); + + if (setppriv(PRIV_SET, PRIV_INHERITABLE, priv_set) == -1) { + dlmgmt_log(LOG_WARNING, "failed to set the inheritable set of " + "privileges %s", strerror(errno)); + priv_freeset(priv_set); + return (-1); + } + + if (setppriv(PRIV_SET, PRIV_PERMITTED, priv_set) == -1) { + dlmgmt_log(LOG_WARNING, "failed to set the permitted set of " + "privileges %s", strerror(errno)); + priv_freeset(priv_set); + return (-1); + } + + if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) { + dlmgmt_log(LOG_WARNING, "failed to set the effective set of " + "privileges %s", strerror(errno)); + priv_freeset(priv_set); + return (-1); + } + + priv_freeset(priv_set); + return (0); +} + +/* + * Keep the pfds fd open, close other fds. + */ +/*ARGSUSED*/ +static int +closefunc(void *arg, int fd) +{ + if (fd != pfds[1]) + (void) close(fd); + return (0); +} + +static boolean_t +dlmgmt_daemonize(void) +{ + pid_t pid; + int rv; + + if (pipe(pfds) < 0) { + (void) fprintf(stderr, "%s: pipe() failed: %s\n", + progname, strerror(errno)); + exit(EXIT_FAILURE); + } + + if ((pid = fork()) == -1) { + (void) fprintf(stderr, "%s: fork() failed: %s\n", + progname, strerror(errno)); + exit(EXIT_FAILURE); + } else if (pid > 0) { /* Parent */ + (void) close(pfds[1]); + + /* + * Read the child process's return value from the pfds. + * If the child process exits unexpected, read() returns -1. + */ + if (read(pfds[0], &rv, sizeof (int)) != sizeof (int)) { + (void) kill(pid, SIGKILL); + rv = EXIT_FAILURE; + } + + (void) close(pfds[0]); + exit(rv); + } + + /* Child */ + (void) close(pfds[0]); + (void) setsid(); + + /* + * Close all files except pfds[1]. + */ + (void) fdwalk(closefunc, NULL); + (void) chdir("/"); + openlog(progname, LOG_PID, LOG_DAEMON); + return (B_TRUE); +} + +int +main(int argc, char *argv[]) +{ + int opt; + + progname = strrchr(argv[0], '/'); + if (progname != NULL) + progname++; + else + progname = argv[0]; + + /* + * Process options. + */ + while ((opt = getopt(argc, argv, "d")) != EOF) { + switch (opt) { + case 'd': + debug = B_TRUE; + break; + default: + usage(); + } + } + + if (!debug && !dlmgmt_daemonize()) + return (EXIT_FAILURE); + + if (signal(SIGTERM, dlmgmtd_exit) == SIG_ERR) { + dlmgmt_log(LOG_WARNING, "signal() for SIGTERM failed: %s", + strerror(errno)); + goto child_out; + } + + if (dlmgmt_init() != 0) + goto child_out; + + if (dlmgmt_setup_privs() != 0) + goto child_out; + + /* + * Inform the parent process that it can successfully exit. + */ + dlmgmt_inform_parent_exit(EXIT_SUCCESS); + + for (;;) + (void) pause(); + +child_out: + /* return from main() forcibly exits an MT process */ + dlmgmt_inform_parent_exit(EXIT_FAILURE); + return (EXIT_FAILURE); +} diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_util.c b/usr/src/cmd/dlmgmtd/dlmgmt_util.c new file mode 100644 index 0000000000..06416db018 --- /dev/null +++ b/usr/src/cmd/dlmgmtd/dlmgmt_util.c @@ -0,0 +1,748 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Utility functions used by the dlmgmtd daemon. + */ + +#include <assert.h> +#include <pthread.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <strings.h> +#include <syslog.h> +#include <stdarg.h> +#include <libdlpi.h> +#include "dlmgmt_impl.h" + +/* + * There are two datalink AVL tables. One table (dlmgmt_name_avl) is keyed by + * the link name, and the other (dlmgmt_id_avl) is keyed by the link id. + * Each link will be present in both tables. + */ +avl_tree_t dlmgmt_name_avl; +avl_tree_t dlmgmt_id_avl; + +avl_tree_t dlmgmt_dlconf_avl; + +static pthread_rwlock_t dlmgmt_avl_lock = PTHREAD_RWLOCK_INITIALIZER; +static pthread_mutex_t dlmgmt_avl_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t dlmgmt_avl_cv = PTHREAD_COND_INITIALIZER; +static pthread_rwlock_t dlmgmt_dlconf_lock = PTHREAD_RWLOCK_INITIALIZER; + +typedef struct dlmgmt_prefix { + struct dlmgmt_prefix *lp_next; + char lp_prefix[MAXLINKNAMELEN]; + uint_t lp_nextppa; +} dlmgmt_prefix_t; +static dlmgmt_prefix_t *dlmgmt_prefixlist; + +static datalink_id_t dlmgmt_nextlinkid; +static datalink_id_t dlmgmt_nextconfid = 1; + +static int linkattr_add(dlmgmt_linkattr_t **, + dlmgmt_linkattr_t *); +static int linkattr_rm(dlmgmt_linkattr_t **, + dlmgmt_linkattr_t *); +static int link_create(const char *, datalink_class_t, uint32_t, + uint32_t, dlmgmt_link_t **); + +static void dlmgmt_advance_linkid(dlmgmt_link_t *); +static void dlmgmt_advance_ppa(dlmgmt_link_t *); + +void +dlmgmt_log(int pri, const char *fmt, ...) +{ + va_list alist; + + va_start(alist, fmt); + if (debug) { + (void) vfprintf(stderr, fmt, alist); + (void) fputc('\n', stderr); + } else { + vsyslog(pri, fmt, alist); + } + va_end(alist); +} + +static int +cmp_link_by_name(const void *v1, const void *v2) +{ + const dlmgmt_link_t *link1 = v1; + const dlmgmt_link_t *link2 = v2; + int cmp; + + cmp = strcmp(link1->ll_link, link2->ll_link); + return ((cmp == 0) ? 0 : ((cmp < 0) ? -1 : 1)); +} + +static int +cmp_link_by_id(const void *v1, const void *v2) +{ + const dlmgmt_link_t *link1 = v1; + const dlmgmt_link_t *link2 = v2; + + if ((uint64_t)(link1->ll_linkid) == (uint64_t)(link2->ll_linkid)) + return (0); + else if ((uint64_t)(link1->ll_linkid) < (uint64_t)(link2->ll_linkid)) + return (-1); + else + return (1); +} + +static int +cmp_dlconf_by_id(const void *v1, const void *v2) +{ + const dlmgmt_dlconf_t *dlconfp1 = v1; + const dlmgmt_dlconf_t *dlconfp2 = v2; + + if (dlconfp1->ld_id == dlconfp2->ld_id) + return (0); + else if (dlconfp1->ld_id < dlconfp2->ld_id) + return (-1); + else + return (1); +} + +int +dlmgmt_linktable_init() +{ + /* + * Initialize the prefix list. First add the "net" prefix to the list. + */ + dlmgmt_prefixlist = malloc(sizeof (dlmgmt_prefix_t)); + if (dlmgmt_prefixlist == NULL) { + dlmgmt_log(LOG_WARNING, "dlmgmt_linktable_init() failed: %s", + strerror(ENOMEM)); + return (ENOMEM); + } + + dlmgmt_prefixlist->lp_next = NULL; + dlmgmt_prefixlist->lp_nextppa = 0; + (void) strlcpy(dlmgmt_prefixlist->lp_prefix, "net", MAXLINKNAMELEN); + + avl_create(&dlmgmt_name_avl, cmp_link_by_name, sizeof (dlmgmt_link_t), + offsetof(dlmgmt_link_t, ll_node_by_name)); + avl_create(&dlmgmt_id_avl, cmp_link_by_id, sizeof (dlmgmt_link_t), + offsetof(dlmgmt_link_t, ll_node_by_id)); + avl_create(&dlmgmt_dlconf_avl, cmp_dlconf_by_id, + sizeof (dlmgmt_dlconf_t), offsetof(dlmgmt_dlconf_t, ld_node)); + dlmgmt_nextlinkid = 1; + return (0); +} + +void +dlmgmt_linktable_fini() +{ + dlmgmt_prefix_t *lpp, *next; + + for (lpp = dlmgmt_prefixlist; lpp != NULL; lpp = next) { + next = lpp->lp_next; + free(lpp); + } + + avl_destroy(&dlmgmt_dlconf_avl); + avl_destroy(&dlmgmt_name_avl); + avl_destroy(&dlmgmt_id_avl); +} + +static int +linkattr_add(dlmgmt_linkattr_t **headp, dlmgmt_linkattr_t *attrp) +{ + if (*headp == NULL) { + *headp = attrp; + } else { + (*headp)->lp_prev = attrp; + attrp->lp_next = *headp; + *headp = attrp; + } + return (0); +} + +static int +linkattr_rm(dlmgmt_linkattr_t **headp, dlmgmt_linkattr_t *attrp) +{ + dlmgmt_linkattr_t *next, *prev; + + next = attrp->lp_next; + prev = attrp->lp_prev; + if (next != NULL) + next->lp_prev = prev; + if (prev != NULL) + prev->lp_next = next; + else + *headp = next; + + return (0); +} + +int +linkattr_set(dlmgmt_linkattr_t **headp, const char *attr, void *attrval, + size_t attrsz, dladm_datatype_t type) +{ + dlmgmt_linkattr_t *attrp; + int err; + + /* + * See whether the attr is already set. + */ + for (attrp = *headp; attrp != NULL; attrp = attrp->lp_next) { + if (strcmp(attrp->lp_name, attr) == 0) + break; + } + + if (attrp != NULL) { + /* + * It is already set. If the value changed, update it. + */ + if (linkattr_equal(headp, attr, attrval, attrsz)) + return (0); + + free(attrp->lp_val); + } else { + /* + * It is not set yet, allocate the linkattr and prepend to the + * list. + */ + if ((attrp = calloc(1, sizeof (dlmgmt_linkattr_t))) == NULL) + return (ENOMEM); + + if ((err = linkattr_add(headp, attrp)) != 0) { + free(attrp); + return (err); + } + (void) strlcpy(attrp->lp_name, attr, MAXLINKATTRLEN); + } + if ((attrp->lp_val = calloc(1, attrsz)) == NULL) { + (void) linkattr_rm(headp, attrp); + free(attrp); + return (ENOMEM); + } + + bcopy(attrval, attrp->lp_val, attrsz); + attrp->lp_sz = attrsz; + attrp->lp_type = type; + return (0); +} + +int +linkattr_unset(dlmgmt_linkattr_t **headp, const char *attr) +{ + dlmgmt_linkattr_t *attrp, *prev; + + /* + * See whether the attr exists. + */ + for (prev = NULL, attrp = *headp; attrp != NULL; + prev = attrp, attrp = attrp->lp_next) { + if (strcmp(attrp->lp_name, attr) == 0) + break; + } + + /* + * This attribute is not set in the first place. Return success. + */ + if (attrp == NULL) + return (0); + + /* + * Remove this attr from the list. + */ + if (prev == NULL) + *headp = attrp->lp_next; + else + prev->lp_next = attrp->lp_next; + + free(attrp->lp_val); + free(attrp); + return (0); +} + +int +linkattr_get(dlmgmt_linkattr_t **headp, const char *attr, void **attrvalp, + size_t *attrszp, dladm_datatype_t *typep) +{ + dlmgmt_linkattr_t *attrp = *headp; + + /* + * find the specific attr. + */ + for (attrp = *headp; attrp != NULL; attrp = attrp->lp_next) { + if (strcmp(attrp->lp_name, attr) == 0) + break; + } + + if (attrp == NULL) + return (ENOENT); + + *attrvalp = attrp->lp_val; + *attrszp = attrp->lp_sz; + if (typep != NULL) + *typep = attrp->lp_type; + return (0); +} + +boolean_t +linkattr_equal(dlmgmt_linkattr_t **headp, const char *attr, void *attrval, + size_t attrsz) +{ + void *saved_attrval; + size_t saved_attrsz; + + if (linkattr_get(headp, attr, &saved_attrval, &saved_attrsz, NULL) != 0) + return (B_FALSE); + + return ((saved_attrsz == attrsz) && + (memcmp(saved_attrval, attrval, attrsz) == 0)); +} + +static int +dlmgmt_table_readwritelock(boolean_t write) +{ + if (write) + return (pthread_rwlock_trywrlock(&dlmgmt_avl_lock)); + else + return (pthread_rwlock_tryrdlock(&dlmgmt_avl_lock)); +} + +void +dlmgmt_table_lock(boolean_t write) +{ + (void) pthread_mutex_lock(&dlmgmt_avl_mutex); + while (dlmgmt_table_readwritelock(write) == EBUSY) + (void) pthread_cond_wait(&dlmgmt_avl_cv, &dlmgmt_avl_mutex); + + (void) pthread_mutex_unlock(&dlmgmt_avl_mutex); +} + +void +dlmgmt_table_unlock() +{ + (void) pthread_rwlock_unlock(&dlmgmt_avl_lock); + (void) pthread_mutex_lock(&dlmgmt_avl_mutex); + (void) pthread_cond_broadcast(&dlmgmt_avl_cv); + (void) pthread_mutex_unlock(&dlmgmt_avl_mutex); +} + +static int +link_create(const char *name, datalink_class_t class, uint32_t media, + uint32_t flags, dlmgmt_link_t **linkpp) +{ + dlmgmt_link_t *linkp = NULL; + int err = 0; + + if (dlmgmt_nextlinkid == DATALINK_INVALID_LINKID) { + err = ENOSPC; + goto done; + } + + if ((linkp = calloc(1, sizeof (dlmgmt_link_t))) == NULL) { + err = ENOMEM; + goto done; + } + + (void) strlcpy(linkp->ll_link, name, MAXLINKNAMELEN); + linkp->ll_class = class; + linkp->ll_media = media; + linkp->ll_linkid = dlmgmt_nextlinkid; + linkp->ll_flags = flags; + linkp->ll_gen = 0; +done: + *linkpp = linkp; + return (err); +} + +void +link_destroy(dlmgmt_link_t *linkp) +{ + dlmgmt_linkattr_t *next, *attrp; + + for (attrp = linkp->ll_head; attrp != NULL; attrp = next) { + next = attrp->lp_next; + free(attrp->lp_val); + free(attrp); + } + free(linkp); +} + +dlmgmt_link_t * +link_by_id(datalink_id_t linkid) +{ + dlmgmt_link_t link; + + link.ll_linkid = linkid; + return (avl_find(&dlmgmt_id_avl, &link, NULL)); +} + +dlmgmt_link_t * +link_by_name(const char *name) +{ + dlmgmt_link_t link; + + (void) strlcpy(link.ll_link, name, MAXLINKNAMELEN); + return (avl_find(&dlmgmt_name_avl, &link, NULL)); +} + +int +dlmgmt_create_common(const char *name, datalink_class_t class, uint32_t media, + uint32_t flags, dlmgmt_link_t **linkpp) +{ + dlmgmt_link_t link, *linkp, *tmp; + avl_index_t name_where, id_where; + int err; + + /* + * Validate the link. + */ + if (!dladm_valid_linkname(name)) + return (EINVAL); + + /* + * Check to see whether this is an existing link name. + */ + (void) strlcpy(link.ll_link, name, MAXLINKNAMELEN); + if ((linkp = avl_find(&dlmgmt_name_avl, &link, &name_where)) != NULL) + return (EEXIST); + + if ((err = link_create(name, class, media, flags, &linkp)) != 0) + return (err); + + link.ll_linkid = linkp->ll_linkid; + tmp = avl_find(&dlmgmt_id_avl, &link, &id_where); + assert(tmp == NULL); + avl_insert(&dlmgmt_name_avl, linkp, name_where); + avl_insert(&dlmgmt_id_avl, linkp, id_where); + dlmgmt_advance(linkp); + *linkpp = linkp; + return (0); +} + +int +dlmgmt_destroy_common(dlmgmt_link_t *linkp, uint32_t flags) +{ + if ((linkp->ll_flags & flags) == 0) { + /* + * The link does not exist in the specified space. + */ + return (ENOENT); + } + linkp->ll_flags &= ~flags; + if (!(linkp->ll_flags & DLMGMT_PERSIST)) { + dlmgmt_linkattr_t *next, *attrp; + + for (attrp = linkp->ll_head; attrp != NULL; attrp = next) { + next = attrp->lp_next; + free(attrp->lp_val); + free(attrp); + } + linkp->ll_head = NULL; + } + + if (linkp->ll_flags == 0) { + avl_remove(&dlmgmt_id_avl, linkp); + avl_remove(&dlmgmt_name_avl, linkp); + link_destroy(linkp); + } + + return (0); +} + +int +dlmgmt_getattr_common(dlmgmt_linkattr_t **headp, const char *attr, + dlmgmt_getattr_retval_t **retvalpp, size_t *retszp) +{ + int err; + void *attrval; + size_t attrsz; + dladm_datatype_t attrtype; + dlmgmt_getattr_retval_t *retvalp; + + err = linkattr_get(headp, attr, &attrval, &attrsz, &attrtype); + if (err != 0) + return (err); + + assert(attrsz > 0); + *retszp = sizeof (dlmgmt_getattr_retval_t) + attrsz - 1; + if ((retvalp = malloc(*retszp)) == NULL) + return (ENOMEM); + + retvalp->lr_err = 0; + retvalp->lr_type = attrtype; + bcopy(attrval, retvalp->lr_attr, attrsz); + *retvalpp = retvalp; + return (0); +} + +void +dlmgmt_dlconf_table_lock(boolean_t write) +{ + if (write) + (void) pthread_rwlock_wrlock(&dlmgmt_dlconf_lock); + else + (void) pthread_rwlock_rdlock(&dlmgmt_dlconf_lock); +} + +void +dlmgmt_dlconf_table_unlock() +{ + (void) pthread_rwlock_unlock(&dlmgmt_dlconf_lock); +} + +int +dlconf_create(const char *name, datalink_id_t linkid, datalink_class_t class, + uint32_t media, dlmgmt_dlconf_t **dlconfpp) +{ + dlmgmt_dlconf_t *dlconfp = NULL; + int err = 0; + + if (dlmgmt_nextconfid == 0) { + err = ENOSPC; + goto done; + } + + if ((dlconfp = calloc(1, sizeof (dlmgmt_dlconf_t))) == NULL) { + err = ENOMEM; + goto done; + } + + (void) strlcpy(dlconfp->ld_link, name, MAXLINKNAMELEN); + dlconfp->ld_linkid = linkid; + dlconfp->ld_class = class; + dlconfp->ld_media = media; + dlconfp->ld_id = dlmgmt_nextconfid; + +done: + *dlconfpp = dlconfp; + return (err); +} + +void +dlconf_destroy(dlmgmt_dlconf_t *dlconfp) +{ + dlmgmt_linkattr_t *next, *attrp; + + for (attrp = dlconfp->ld_head; attrp != NULL; attrp = next) { + next = attrp->lp_next; + free(attrp->lp_val); + free(attrp); + } + free(dlconfp); +} + +int +dlmgmt_generate_name(const char *prefix, char *name, size_t size) +{ + dlmgmt_prefix_t *lpp, *prev = NULL; + + /* + * See whether the requested prefix is already in the list. + */ + for (lpp = dlmgmt_prefixlist; lpp != NULL; prev = lpp, + lpp = lpp->lp_next) { + if (strcmp(prefix, lpp->lp_prefix) == 0) + break; + } + + /* + * Not found. + */ + if (lpp == NULL) { + dlmgmt_link_t *linkp, link; + + assert(prev != NULL); + + /* + * First add this new prefix into the prefix list. + */ + if ((lpp = malloc(sizeof (dlmgmt_prefix_t))) == NULL) + return (ENOMEM); + + prev->lp_next = lpp; + lpp->lp_next = NULL; + lpp->lp_nextppa = 0; + (void) strlcpy(lpp->lp_prefix, prefix, MAXLINKNAMELEN); + + /* + * Now determine this prefix's nextppa. + */ + (void) snprintf(link.ll_link, MAXLINKNAMELEN, "%s%d", + prefix, lpp->lp_nextppa); + linkp = avl_find(&dlmgmt_name_avl, &link, NULL); + if (linkp != NULL) + dlmgmt_advance_ppa(linkp); + } + + if (lpp->lp_nextppa == (uint_t)-1) + return (ENOSPC); + + (void) snprintf(name, size, "%s%d", prefix, lpp->lp_nextppa); + return (0); +} + +/* + * Advance the next available ppa value if the name prefix of the current + * link is in the prefix list. + */ +static void +dlmgmt_advance_ppa(dlmgmt_link_t *linkp) +{ + dlmgmt_prefix_t *lpp; + char prefix[MAXLINKNAMELEN]; + uint_t start, ppa; + + (void) dlpi_parselink(linkp->ll_link, prefix, &ppa); + + /* + * See whether the requested prefix is already in the list. + */ + for (lpp = dlmgmt_prefixlist; lpp != NULL; lpp = lpp->lp_next) { + if (strcmp(prefix, lpp->lp_prefix) == 0) + break; + } + + /* + * If the link name prefix is in the list, advance the + * next available ppa for the <prefix>N name. + */ + if (lpp == NULL || lpp->lp_nextppa != ppa) + return; + + start = lpp->lp_nextppa++; + linkp = AVL_NEXT(&dlmgmt_name_avl, linkp); + while (lpp->lp_nextppa != start) { + if (lpp->lp_nextppa == (uint_t)-1) { + dlmgmt_link_t link; + + /* + * wrapped around. search from <prefix>1. + */ + lpp->lp_nextppa = 0; + (void) snprintf(link.ll_link, MAXLINKNAMELEN, + "%s%d", lpp->lp_prefix, lpp->lp_nextppa); + linkp = avl_find(&dlmgmt_name_avl, &link, NULL); + if (linkp == NULL) + return; + } else { + if (linkp == NULL) + return; + (void) dlpi_parselink(linkp->ll_link, prefix, &ppa); + if ((strcmp(prefix, lpp->lp_prefix) != 0) || + (ppa != lpp->lp_nextppa)) { + return; + } + } + linkp = AVL_NEXT(&dlmgmt_name_avl, linkp); + lpp->lp_nextppa++; + } + lpp->lp_nextppa = (uint_t)-1; +} + +/* + * Advance to the next available linkid value. + */ +static void +dlmgmt_advance_linkid(dlmgmt_link_t *linkp) +{ + datalink_id_t start; + + if (linkp->ll_linkid != dlmgmt_nextlinkid) + return; + + start = dlmgmt_nextlinkid; + linkp = AVL_NEXT(&dlmgmt_id_avl, linkp); + + do { + if (dlmgmt_nextlinkid == DATALINK_MAX_LINKID) { + dlmgmt_link_t link; + + /* + * wrapped around. search from 1. + */ + dlmgmt_nextlinkid = 1; + link.ll_linkid = 1; + linkp = avl_find(&dlmgmt_id_avl, &link, NULL); + if (linkp == NULL) + return; + } else { + dlmgmt_nextlinkid++; + if (linkp == NULL) + return; + if (linkp->ll_linkid != dlmgmt_nextlinkid) + return; + } + + linkp = AVL_NEXT(&dlmgmt_id_avl, linkp); + } while (dlmgmt_nextlinkid != start); + + dlmgmt_nextlinkid = DATALINK_INVALID_LINKID; +} + +/* + * Advance various global values, for example, next linkid value, next ppa for + * various prefix etc. + */ +void +dlmgmt_advance(dlmgmt_link_t *linkp) +{ + dlmgmt_advance_linkid(linkp); + dlmgmt_advance_ppa(linkp); +} + +/* + * Advance to the next available dlconf id. + */ +void +dlmgmt_advance_dlconfid(dlmgmt_dlconf_t *dlconfp) +{ + uint_t start; + + start = dlmgmt_nextconfid++; + dlconfp = AVL_NEXT(&dlmgmt_dlconf_avl, dlconfp); + while (dlmgmt_nextconfid != start) { + if (dlmgmt_nextconfid == 0) { + dlmgmt_dlconf_t dlconf; + + /* + * wrapped around. search from 1. + */ + dlconf.ld_id = dlmgmt_nextconfid = 1; + dlconfp = avl_find(&dlmgmt_name_avl, &dlconf, NULL); + if (dlconfp == NULL) + return; + } else { + if ((dlconfp == NULL) || + (dlconfp->ld_id != dlmgmt_nextconfid)) { + return; + } + } + dlconfp = AVL_NEXT(&dlmgmt_name_avl, dlconfp); + dlmgmt_nextconfid++; + } + dlmgmt_nextconfid = 0; +} diff --git a/usr/src/cmd/dladm/aggregation.conf b/usr/src/cmd/dlmgmtd/svc-dlmgmtd index 5cb5189578..7559207535 100644 --- a/usr/src/cmd/dladm/aggregation.conf +++ b/usr/src/cmd/dlmgmtd/svc-dlmgmtd @@ -1,10 +1,10 @@ +#!/sbin/sh # # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# 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. @@ -19,13 +19,27 @@ # # CDDL HEADER END # + # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" -# -# DO NOT EDIT OR PARSE THIS FILE! -# -# Use the dladm(1m) command to change the contents of this file. +. /lib/svc/share/smf_include.sh + +# The real daemon is not started in a non-global zone. But we need to +# create a dummy background process to preserve contract lifetime. + +if smf_is_nonglobalzone; then + (while true ; do sleep 3600 ; done) & + exit $SMF_EXIT_OK +fi + +# Start the dlmgmtd daemon. +/sbin/dlmgmtd +if [ $? = 0 ]; then + exit $SMF_EXIT_OK +else + exit $SMF_EXIT_ERR_FATAL +fi diff --git a/usr/src/cmd/rcm_daemon/Makefile.com b/usr/src/cmd/rcm_daemon/Makefile.com index 4b189c4b32..00a36402f8 100644 --- a/usr/src/cmd/rcm_daemon/Makefile.com +++ b/usr/src/cmd/rcm_daemon/Makefile.com @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -50,6 +50,8 @@ COMMON_MOD_SRC = \ $(COMMON)/dump_rcm.c \ $(COMMON)/swap_rcm.c \ $(COMMON)/network_rcm.c \ + $(COMMON)/vlan_rcm.c \ + $(COMMON)/aggr_rcm.c \ $(COMMON)/ip_rcm.c \ $(COMMON)/cluster_rcm.c \ $(COMMON)/pool_rcm.c \ @@ -64,6 +66,8 @@ COMMON_MOD_OBJ = \ dump_rcm.o \ swap_rcm.o \ network_rcm.o \ + vlan_rcm.o \ + aggr_rcm.o \ ip_rcm.o \ cluster_rcm.o \ pool_rcm.o \ @@ -80,6 +84,8 @@ COMMON_RCM_MODS = \ SUNW_dump_rcm.so \ SUNW_swap_rcm.so \ SUNW_network_rcm.so \ + SUNW_vlan_rcm.so \ + SUNW_aggr_rcm.so \ SUNW_ip_rcm.so \ SUNW_cluster_rcm.so \ SUNW_pool_rcm.so \ @@ -110,7 +116,9 @@ LDLIBS_MODULES = SUNW_pool_rcm.so := LDLIBS_MODULES += -L$(ROOT)/usr/lib -lpool SUNW_svm_rcm.so := LDLIBS_MODULES += -L$(ROOT)/usr/lib -lmeta SUNW_network_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -ldladm -SUNW_ip_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -linetutil +SUNW_vlan_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -ldladm +SUNW_aggr_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -ldladm +SUNW_ip_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -linetutil -ldladm SUNW_ip_anon_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -linetutil LDLIBS += -lgen -lelf -lrcm -lnvpair -ldevinfo -lnsl -lsocket diff --git a/usr/src/cmd/rcm_daemon/common/aggr_rcm.c b/usr/src/cmd/rcm_daemon/common/aggr_rcm.c new file mode 100644 index 0000000000..b3d6eeb358 --- /dev/null +++ b/usr/src/cmd/rcm_daemon/common/aggr_rcm.c @@ -0,0 +1,1455 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This RCM module adds support to the RCM framework for AGGR links + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <alloca.h> +#include <sys/types.h> +#include <sys/aggr.h> +#include <synch.h> +#include <assert.h> +#include <strings.h> +#include "rcm_module.h" +#include <libintl.h> +#include <libdllink.h> +#include <libdlaggr.h> + +/* + * Definitions + */ +#ifndef lint +#define _(x) gettext(x) +#else +#define _(x) x +#endif + +/* Some generic well-knowns and defaults used in this module */ +#define RCM_LINK_PREFIX "SUNW_datalink" /* RCM datalink name prefix */ +#define RCM_LINK_RESOURCE_MAX (13 + LINKID_STR_WIDTH) + +/* AGGR link representation */ +typedef struct dl_aggr { + struct dl_aggr *da_next; /* next AGGR on the system */ + struct dl_aggr *da_prev; /* prev AGGR on the system */ + boolean_t da_stale; /* AGGR link is stale? */ + datalink_id_t da_aggrid; + datalink_id_t da_lastport; +} dl_aggr_t; + +/* AGGR Cache state flags */ +typedef enum { + CACHE_NODE_STALE = 0x01, /* stale cached data */ + CACHE_NODE_NEW = 0x02, /* new cached nodes */ + CACHE_NODE_OFFLINED = 0x04, /* node offlined */ + CACHE_AGGR_PORT_OFFLINED = 0x08, /* aggr port offlined */ + CACHE_AGGR_CONSUMER_OFFLINED = 0x10 /* consumers offlined */ +} cache_node_state_t; + +/* Network Cache lookup options */ +#define CACHE_NO_REFRESH 0x1 /* cache refresh not needed */ +#define CACHE_REFRESH 0x2 /* refresh cache */ + +/* + * Cache element. It is used to keep a list of links on the system and + * their associated aggregations. + */ +typedef struct link_cache { + struct link_cache *vc_next; /* next cached resource */ + struct link_cache *vc_prev; /* prev cached resource */ + char *vc_resource; /* resource name */ + datalink_id_t vc_linkid; /* linkid */ + dl_aggr_t *vc_aggr; /* AGGR on this link */ + cache_node_state_t vc_state; /* cache state flags */ +} link_cache_t; + +/* + * Global cache for network AGGRs + */ +static link_cache_t cache_head; +static link_cache_t cache_tail; +static mutex_t cache_lock; +static dl_aggr_t aggr_head; +static dl_aggr_t aggr_tail; +static mutex_t aggr_list_lock; +static int events_registered = 0; + +/* + * RCM module interface prototypes + */ +static int aggr_register(rcm_handle_t *); +static int aggr_unregister(rcm_handle_t *); +static int aggr_get_info(rcm_handle_t *, char *, id_t, uint_t, + char **, char **, nvlist_t *, rcm_info_t **); +static int aggr_suspend(rcm_handle_t *, char *, id_t, + timespec_t *, uint_t, char **, rcm_info_t **); +static int aggr_resume(rcm_handle_t *, char *, id_t, uint_t, + char **, rcm_info_t **); +static int aggr_offline(rcm_handle_t *, char *, id_t, uint_t, + char **, rcm_info_t **); +static int aggr_undo_offline(rcm_handle_t *, char *, id_t, uint_t, + char **, rcm_info_t **); +static int aggr_remove(rcm_handle_t *, char *, id_t, uint_t, + char **, rcm_info_t **); +static int aggr_notify_event(rcm_handle_t *, char *, id_t, uint_t, + char **, nvlist_t *, rcm_info_t **); +static int aggr_configure_all(rcm_handle_t *, datalink_id_t, + boolean_t *); + +/* Module private routines */ +static int cache_update(rcm_handle_t *); +static void cache_remove(link_cache_t *); +static void cache_insert(link_cache_t *); +static void node_free(link_cache_t *); +static void aggr_list_remove(dl_aggr_t *); +static void aggr_list_insert(dl_aggr_t *); +static void aggr_list_free(); +static link_cache_t *cache_lookup(rcm_handle_t *, char *, char); +static int aggr_consumer_offline(rcm_handle_t *, link_cache_t *, + char **, uint_t, rcm_info_t **); +static int aggr_consumer_online(rcm_handle_t *, link_cache_t *, + char **, uint_t, rcm_info_t **); +static int aggr_offline_port(link_cache_t *, cache_node_state_t); +static int aggr_online_port(link_cache_t *, boolean_t *); +static char *aggr_usage(link_cache_t *); +static void aggr_log_err(datalink_id_t, char **, char *); +static int aggr_consumer_notify(rcm_handle_t *, datalink_id_t, + char **, uint_t, rcm_info_t **); + +/* Module-Private data */ +static struct rcm_mod_ops aggr_ops = +{ + RCM_MOD_OPS_VERSION, + aggr_register, + aggr_unregister, + aggr_get_info, + aggr_suspend, + aggr_resume, + aggr_offline, + aggr_undo_offline, + aggr_remove, + NULL, + NULL, + aggr_notify_event +}; + +/* + * rcm_mod_init() - Update registrations, and return the ops structure. + */ +struct rcm_mod_ops * +rcm_mod_init(void) +{ + rcm_log_message(RCM_TRACE1, "AGGR: mod_init\n"); + + cache_head.vc_next = &cache_tail; + cache_head.vc_prev = NULL; + cache_tail.vc_prev = &cache_head; + cache_tail.vc_next = NULL; + (void) mutex_init(&cache_lock, 0, NULL); + aggr_head.da_next = &aggr_tail; + aggr_head.da_prev = NULL; + aggr_tail.da_prev = &aggr_head; + aggr_tail.da_next = NULL; + (void) mutex_init(&aggr_list_lock, NULL, NULL); + + /* Return the ops vectors */ + return (&aggr_ops); +} + +/* + * rcm_mod_info() - Return a string describing this module. + */ +const char * +rcm_mod_info(void) +{ + rcm_log_message(RCM_TRACE1, "AGGR: mod_info\n"); + + return ("AGGR module version %I%"); +} + +/* + * rcm_mod_fini() - Destroy the network AGGR cache. + */ +int +rcm_mod_fini(void) +{ + link_cache_t *node; + + rcm_log_message(RCM_TRACE1, "AGGR: mod_fini\n"); + + /* + * Note that aggr_unregister() does not seem to be called anywhere, + * therefore we free the cache nodes here. In theory we should call + * rcm_register_interest() for each node before we free it, the + * framework does not provide the rcm_handle to allow us to do so. + */ + (void) mutex_lock(&cache_lock); + node = cache_head.vc_next; + while (node != &cache_tail) { + cache_remove(node); + node_free(node); + node = cache_head.vc_next; + } + (void) mutex_unlock(&cache_lock); + (void) mutex_destroy(&cache_lock); + + aggr_list_free(); + (void) mutex_destroy(&aggr_list_lock); + + return (RCM_SUCCESS); +} + +/* + * aggr_list_insert - Insert an aggr in the global aggr list + */ +static void +aggr_list_insert(dl_aggr_t *aggr) +{ + assert(MUTEX_HELD(&aggr_list_lock)); + + /* insert at the head for best performance */ + aggr->da_next = aggr_head.da_next; + aggr->da_prev = &aggr_head; + + aggr->da_next->da_prev = aggr; + aggr->da_prev->da_next = aggr; +} + +/* + * aggr_list_remove - Remove an aggr from the global aggr list + */ +static void +aggr_list_remove(dl_aggr_t *aggr) +{ + assert(MUTEX_HELD(&aggr_list_lock)); + aggr->da_next->da_prev = aggr->da_prev; + aggr->da_prev->da_next = aggr->da_next; + aggr->da_next = NULL; + aggr->da_prev = NULL; +} + +static void +aggr_list_free() +{ + dl_aggr_t *aggr; + + (void) mutex_lock(&aggr_list_lock); + aggr = aggr_head.da_next; + while (aggr != &aggr_tail) { + aggr_list_remove(aggr); + free(aggr); + aggr = aggr_head.da_next; + } + (void) mutex_unlock(&aggr_list_lock); +} + +/* + * aggr_register() - Make sure the cache is properly sync'ed, and its + * registrations are in order. + */ +static int +aggr_register(rcm_handle_t *hd) +{ + rcm_log_message(RCM_TRACE1, "AGGR: register\n"); + + if (cache_update(hd) < 0) + return (RCM_FAILURE); + + /* + * Need to register interest in all new resources + * getting attached, so we get attach event notifications + */ + if (!events_registered) { + if (rcm_register_event(hd, RCM_RESOURCE_LINK_NEW, 0, NULL) + != RCM_SUCCESS) { + rcm_log_message(RCM_ERROR, + _("AGGR: failed to register %s\n"), + RCM_RESOURCE_LINK_NEW); + return (RCM_FAILURE); + } else { + rcm_log_message(RCM_DEBUG, "AGGR: registered %s\n", + RCM_RESOURCE_LINK_NEW); + events_registered++; + } + } + + return (RCM_SUCCESS); +} + +/* + * aggr_unregister() - Walk the cache, unregistering all the networks. + */ +static int +aggr_unregister(rcm_handle_t *hd) +{ + link_cache_t *node; + + rcm_log_message(RCM_TRACE1, "AGGR: unregister\n"); + + /* Walk the cache, unregistering everything */ + (void) mutex_lock(&cache_lock); + node = cache_head.vc_next; + while (node != &cache_tail) { + if (rcm_unregister_interest(hd, node->vc_resource, 0) + != RCM_SUCCESS) { + /* unregister failed for whatever reason */ + rcm_log_message(RCM_ERROR, + _("AGGR: failed to unregister %s\n"), + node->vc_resource); + (void) mutex_unlock(&cache_lock); + return (RCM_FAILURE); + } + cache_remove(node); + node_free(node); + node = cache_head.vc_next; + } + (void) mutex_unlock(&cache_lock); + + aggr_list_free(); + + /* + * Unregister interest in all new resources + */ + if (events_registered) { + if (rcm_unregister_event(hd, RCM_RESOURCE_LINK_NEW, 0) + != RCM_SUCCESS) { + rcm_log_message(RCM_ERROR, + _("AGGR: failed to unregister %s\n"), + RCM_RESOURCE_LINK_NEW); + return (RCM_FAILURE); + } else { + rcm_log_message(RCM_DEBUG, "AGGR: unregistered %s\n", + RCM_RESOURCE_LINK_NEW); + events_registered--; + } + } + + return (RCM_SUCCESS); +} + +/* + * aggr_offline() - Offline AGGRs on a specific link. + */ +static int +aggr_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, + char **errorp, rcm_info_t **depend_info) +{ + link_cache_t *node; + + rcm_log_message(RCM_TRACE1, "AGGR: offline(%s)\n", rsrc); + + /* Lock the cache and lookup the resource */ + (void) mutex_lock(&cache_lock); + node = cache_lookup(hd, rsrc, CACHE_REFRESH); + if (node == NULL) { + /* should not happen because the resource is registered. */ + aggr_log_err(DATALINK_INVALID_LINKID, errorp, + "offline, unrecognized resource"); + (void) mutex_unlock(&cache_lock); + return (RCM_SUCCESS); + } + + /* + * If this given link is the only port in the aggregation, inform + * VLANs and IP interfaces on associated AGGRs to be offlined + */ + if (node->vc_aggr->da_lastport == node->vc_linkid) { + if (aggr_consumer_offline(hd, node, errorp, flags, + depend_info) == RCM_SUCCESS) { + rcm_log_message(RCM_DEBUG, + "AGGR: consumers agreed on offline\n"); + } else { + aggr_log_err(node->vc_linkid, errorp, + "consumers offline failed"); + (void) mutex_unlock(&cache_lock); + return (RCM_FAILURE); + } + } + + /* Check if it's a query */ + if (flags & RCM_QUERY) { + rcm_log_message(RCM_TRACE1, + "AGGR: offline query succeeded(%s)\n", rsrc); + (void) mutex_unlock(&cache_lock); + return (RCM_SUCCESS); + } + + if (aggr_offline_port(node, CACHE_NODE_OFFLINED) != RCM_SUCCESS) { + aggr_log_err(node->vc_linkid, errorp, "offline port failed"); + (void) mutex_unlock(&cache_lock); + return (RCM_FAILURE); + } + + rcm_log_message(RCM_TRACE1, "AGGR: Offline succeeded(%s)\n", rsrc); + (void) mutex_unlock(&cache_lock); + return (RCM_SUCCESS); +} + +/* + * aggr_undo_offline() - Undo offline of a previously offlined link. + */ +/*ARGSUSED*/ +static int +aggr_undo_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, + char **errorp, rcm_info_t **depend_info) +{ + link_cache_t *node; + boolean_t up; + + rcm_log_message(RCM_TRACE1, "AGGR: online(%s)\n", rsrc); + + (void) mutex_lock(&cache_lock); + node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH); + if (node == NULL) { + aggr_log_err(DATALINK_INVALID_LINKID, errorp, + "undo offline, unrecognized resource"); + (void) mutex_unlock(&cache_lock); + errno = ENOENT; + return (RCM_FAILURE); + } + + /* Check if no attempt should be made to online the link here */ + if (!(node->vc_state & CACHE_NODE_OFFLINED)) { + aggr_log_err(node->vc_linkid, errorp, "resource not offlined"); + (void) mutex_unlock(&cache_lock); + errno = ENOTSUP; + return (RCM_SUCCESS); + } + + if (aggr_online_port(node, &up) != RCM_SUCCESS) { + aggr_log_err(node->vc_linkid, errorp, "online failed"); + (void) mutex_unlock(&cache_lock); + return (RCM_FAILURE); + } + + /* + * Inform VLANs and IP interfaces on associated AGGRs to be online + */ + if (!up) + goto done; + + if (aggr_consumer_online(hd, node, errorp, flags, depend_info) == + RCM_SUCCESS) { + rcm_log_message(RCM_DEBUG, "AGGR: Consumers agree on online"); + } else { + rcm_log_message(RCM_WARNING, + _("AGGR: Consumers online failed (%s)\n"), rsrc); + } + +done: + node->vc_state &= ~CACHE_NODE_OFFLINED; + rcm_log_message(RCM_TRACE1, "AGGR: online succeeded(%s)\n", rsrc); + (void) mutex_unlock(&cache_lock); + return (RCM_SUCCESS); +} + +static int +aggr_offline_port(link_cache_t *node, cache_node_state_t state) +{ + dl_aggr_t *aggr; + dladm_status_t status; + char errmsg[DLADM_STRSIZE]; + dladm_aggr_port_attr_db_t port; + + rcm_log_message(RCM_TRACE2, "AGGR: aggr_offline_port %s\n", + node->vc_resource); + + aggr = node->vc_aggr; + + /* + * Try to remove the given port from the AGGR or delete the AGGR + */ + if (aggr->da_lastport == node->vc_linkid) { + rcm_log_message(RCM_TRACE2, "AGGR: delete aggregation %u\n", + aggr->da_aggrid); + status = dladm_aggr_delete(aggr->da_aggrid, DLADM_OPT_ACTIVE); + } else { + rcm_log_message(RCM_TRACE2, + "AGGR: remove port (%s) from aggregation %u\n", + node->vc_resource, aggr->da_aggrid); + port.lp_linkid = node->vc_linkid; + status = dladm_aggr_remove(aggr->da_aggrid, 1, &port, + DLADM_OPT_ACTIVE); + } + if (status != DLADM_STATUS_OK) { + rcm_log_message(RCM_WARNING, + _("AGGR: AGGR offline port failed (%u): %s\n"), + aggr->da_aggrid, dladm_status2str(status, errmsg)); + return (RCM_FAILURE); + } else { + rcm_log_message(RCM_TRACE1, + "AGGR: AGGR offline port succeeded (%u)\n", + aggr->da_aggrid); + node->vc_state |= (CACHE_AGGR_PORT_OFFLINED | state); + return (RCM_SUCCESS); + } +} + +static int +aggr_online_port(link_cache_t *node, boolean_t *up) +{ + dl_aggr_t *aggr; + dladm_status_t status; + char errmsg[DLADM_STRSIZE]; + dladm_aggr_port_attr_db_t port; + + rcm_log_message(RCM_TRACE2, "AGGR: aggr_online_port %s\n", + node->vc_resource); + + *up = B_FALSE; + if (!(node->vc_state & CACHE_AGGR_PORT_OFFLINED)) + return (RCM_SUCCESS); + + /* + * Either add the port into the AGGR or recreate specific AGGR + * depending on whether this link is the only port in the aggregation. + */ + aggr = node->vc_aggr; + if (aggr->da_lastport == node->vc_linkid) { + rcm_log_message(RCM_TRACE2, "AGGR: delete aggregation %u\n", + aggr->da_aggrid); + status = dladm_aggr_up(aggr->da_aggrid); + *up = B_TRUE; + } else { + rcm_log_message(RCM_TRACE2, + "AGGR: add port (%s) to aggregation %u\n", + node->vc_resource, aggr->da_aggrid); + port.lp_linkid = node->vc_linkid; + status = dladm_aggr_add(aggr->da_aggrid, 1, &port, + DLADM_OPT_ACTIVE); + } + if (status != DLADM_STATUS_OK) { + rcm_log_message(RCM_WARNING, + _("AGGR: AGGR online failed (%u): %s\n"), + aggr->da_aggrid, dladm_status2str(status, errmsg)); + *up = B_FALSE; + return (RCM_FAILURE); + } + node->vc_state &= ~CACHE_AGGR_PORT_OFFLINED; + return (RCM_SUCCESS); +} + +/* + * aggr_get_info() - Gather usage information for this resource. + */ +/*ARGSUSED*/ +int +aggr_get_info(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, + char **usagep, char **errorp, nvlist_t *props, rcm_info_t **depend_info) +{ + link_cache_t *node; + + rcm_log_message(RCM_TRACE1, "AGGR: get_info(%s)\n", rsrc); + + (void) mutex_lock(&cache_lock); + node = cache_lookup(hd, rsrc, CACHE_REFRESH); + if (node == NULL) { + rcm_log_message(RCM_INFO, + _("AGGR: get_info(%s) unrecognized resource\n"), rsrc); + (void) mutex_unlock(&cache_lock); + errno = ENOENT; + return (RCM_FAILURE); + } + + /* + * *usagep will be freed by the caller. + */ + *usagep = aggr_usage(node); + (void) mutex_unlock(&cache_lock); + + if (*usagep == NULL) { + /* most likely malloc failure */ + rcm_log_message(RCM_ERROR, + _("AGGR: get_info(%s) malloc failure\n"), rsrc); + (void) mutex_unlock(&cache_lock); + errno = ENOMEM; + return (RCM_FAILURE); + } + + /* Set client/role properties */ + (void) nvlist_add_string(props, RCM_CLIENT_NAME, "AGGR"); + rcm_log_message(RCM_TRACE1, "AGGR: get_info(%s) info = %s\n", + rsrc, *usagep); + return (RCM_SUCCESS); +} + +/* + * aggr_suspend() - Nothing to do, always okay + */ +/*ARGSUSED*/ +static int +aggr_suspend(rcm_handle_t *hd, char *rsrc, id_t id, timespec_t *interval, + uint_t flags, char **errorp, rcm_info_t **depend_info) +{ + rcm_log_message(RCM_TRACE1, "AGGR: suspend(%s)\n", rsrc); + return (RCM_SUCCESS); +} + +/* + * aggr_resume() - Nothing to do, always okay + */ +/*ARGSUSED*/ +static int +aggr_resume(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, + char **errorp, rcm_info_t **depend_info) +{ + rcm_log_message(RCM_TRACE1, "AGGR: resume(%s)\n", rsrc); + return (RCM_SUCCESS); +} + +/* + * aggr_remove() - remove a resource from cache + */ +/*ARGSUSED*/ +static int +aggr_remove(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, + char **errorp, rcm_info_t **depend_info) +{ + link_cache_t *node; + char *exported; + dl_aggr_t *aggr; + int rv = RCM_SUCCESS; + + rcm_log_message(RCM_TRACE1, "AGGR: remove(%s)\n", rsrc); + + (void) mutex_lock(&cache_lock); + node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH); + if (node == NULL) { + rcm_log_message(RCM_INFO, + _("AGGR: remove(%s) unrecognized resource\n"), rsrc); + (void) mutex_unlock(&cache_lock); + errno = ENOENT; + return (RCM_FAILURE); + } + + /* remove the cached entry for the resource */ + cache_remove(node); + (void) mutex_unlock(&cache_lock); + + /* + * If this link is not the only port in the associated aggregation, + * the CACHE_AGGR_CONSUMER_OFFLINED flags won't be set. + */ + if (node->vc_state & CACHE_AGGR_CONSUMER_OFFLINED) { + aggr = node->vc_aggr; + exported = alloca(RCM_LINK_RESOURCE_MAX); + (void) snprintf(exported, RCM_LINK_RESOURCE_MAX, "%s/%u", + RCM_LINK_PREFIX, aggr->da_aggrid); + rv = rcm_notify_remove(hd, exported, flags, depend_info); + if (rv != RCM_SUCCESS) { + rcm_log_message(RCM_WARNING, + _("AGGR: failed to notify remove dependent %s\n"), + exported); + } + } + + node_free(node); + return (rv); +} + +/* + * aggr_notify_event - Project private implementation to receive new resource + * events. It intercepts all new resource events. If the + * new resource is a network resource, pass up a notify + * for it too. The new resource need not be cached, since + * it is done at register again. + */ +/*ARGSUSED*/ +static int +aggr_notify_event(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, + char **errorp, nvlist_t *nvl, rcm_info_t **depend_info) +{ + nvpair_t *nvp = NULL; + datalink_id_t linkid; + uint64_t id64; + boolean_t up; + int rv = RCM_SUCCESS; + + rcm_log_message(RCM_TRACE1, "AGGR: notify_event(%s)\n", rsrc); + + if (strcmp(rsrc, RCM_RESOURCE_LINK_NEW) != 0) { + aggr_log_err(DATALINK_INVALID_LINKID, errorp, + "unrecognized event"); + errno = EINVAL; + return (RCM_FAILURE); + } + + /* Update cache to reflect latest AGGRs */ + if (cache_update(hd) < 0) { + aggr_log_err(DATALINK_INVALID_LINKID, errorp, + "private Cache update failed"); + return (RCM_FAILURE); + } + + /* Process the nvlist for the event */ + rcm_log_message(RCM_TRACE1, "AGGR: process_nvlist\n"); + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + + if (strcmp(nvpair_name(nvp), RCM_NV_LINKID) != 0) + continue; + + if (nvpair_value_uint64(nvp, &id64) != 0) { + aggr_log_err(DATALINK_INVALID_LINKID, errorp, + "cannot get linkid"); + return (RCM_FAILURE); + } + + linkid = (datalink_id_t)id64; + if (aggr_configure_all(hd, linkid, &up) != 0) { + aggr_log_err(linkid, errorp, + "failed configuring AGGR links"); + rv = RCM_FAILURE; + } + + /* Notify all VLAN and IP AGGR consumers */ + if (up && aggr_consumer_notify(hd, linkid, errorp, flags, + depend_info) != 0) { + aggr_log_err(linkid, errorp, "consumer notify failed"); + rv = RCM_FAILURE; + } + } + + rcm_log_message(RCM_TRACE1, + "AGGR: notify_event: link configuration complete\n"); + return (rv); +} + +/* + * aggr_usage - Determine the usage of a link. + * The returned buffer is owned by caller, and the caller + * must free it up when done. + */ +static char * +aggr_usage(link_cache_t *node) +{ + char *buf; + const char *fmt; + char errmsg[DLADM_STRSIZE]; + char name[MAXLINKNAMELEN]; + dladm_status_t status; + size_t bufsz; + + rcm_log_message(RCM_TRACE2, "AGGR: usage(%s)\n", node->vc_resource); + assert(MUTEX_HELD(&cache_lock)); + + if (node->vc_state & CACHE_NODE_OFFLINED) + fmt = _("%s offlined"); + else + fmt = _("%s is part of AGGR "); + + if ((status = dladm_datalink_id2info(node->vc_linkid, NULL, NULL, + NULL, name, sizeof (name))) != DLADM_STATUS_OK) { + rcm_log_message(RCM_ERROR, + _("AGGR: usage(%s) get port name failure(%s)\n"), + node->vc_resource, dladm_status2str(status, errmsg)); + return (NULL); + } + + /* space for resources and message */ + bufsz = MAXLINKNAMELEN + strlen(fmt) + strlen(name) + 1; + if ((buf = malloc(bufsz)) == NULL) { + rcm_log_message(RCM_ERROR, + _("AGGR: usage(%s) malloc failure(%s)\n"), + node->vc_resource, strerror(errno)); + return (NULL); + } + (void) snprintf(buf, bufsz, fmt, name); + + if (node->vc_state & CACHE_NODE_OFFLINED) { + /* Nothing else to do */ + rcm_log_message(RCM_TRACE2, "AGGR: usage (%s) info = %s\n", + node->vc_resource, buf); + return (buf); + } + + if ((status = dladm_datalink_id2info(node->vc_aggr->da_aggrid, NULL, + NULL, NULL, name, sizeof (name))) != DLADM_STATUS_OK) { + rcm_log_message(RCM_ERROR, + _("AGGR: usage(%s) get aggr %u name failure(%s)\n"), + node->vc_resource, node->vc_aggr->da_aggrid, + dladm_status2str(status, errmsg)); + (void) free(buf); + return (NULL); + } + + (void) strlcat(buf, name, bufsz); + + rcm_log_message(RCM_TRACE2, "AGGR: usage (%s) info = %s\n", + node->vc_resource, buf); + return (buf); +} + +/* + * Cache management routines, all cache management functions should be + * be called with cache_lock held. + */ + +/* + * cache_lookup() - Get a cache node for a resource. + * Call with cache lock held. + * + * This ensures that the cache is consistent with the system state and + * returns a pointer to the cache element corresponding to the resource. + */ +static link_cache_t * +cache_lookup(rcm_handle_t *hd, char *rsrc, char options) +{ + link_cache_t *node; + + rcm_log_message(RCM_TRACE2, "AGGR: cache lookup(%s)\n", rsrc); + assert(MUTEX_HELD(&cache_lock)); + + if (options & CACHE_REFRESH) { + /* drop lock since update locks cache again */ + (void) mutex_unlock(&cache_lock); + (void) cache_update(hd); + (void) mutex_lock(&cache_lock); + } + + node = cache_head.vc_next; + for (; node != &cache_tail; node = node->vc_next) { + if (strcmp(rsrc, node->vc_resource) == 0) { + rcm_log_message(RCM_TRACE2, + "AGGR: cache lookup succeeded(%s)\n", rsrc); + return (node); + } + } + return (NULL); +} + +/* + * node_free - Free a node from the cache + */ +static void +node_free(link_cache_t *node) +{ + free(node->vc_resource); + free(node); +} + +/* + * cache_insert - Insert a resource node in cache + */ +static void +cache_insert(link_cache_t *node) +{ + assert(MUTEX_HELD(&cache_lock)); + + /* insert at the head for best performance */ + node->vc_next = cache_head.vc_next; + node->vc_prev = &cache_head; + + node->vc_next->vc_prev = node; + node->vc_prev->vc_next = node; +} + +/* + * cache_remove() - Remove a resource node from cache. + * Call with the cache_lock held. + */ +static void +cache_remove(link_cache_t *node) +{ + assert(MUTEX_HELD(&cache_lock)); + node->vc_next->vc_prev = node->vc_prev; + node->vc_prev->vc_next = node->vc_next; + node->vc_next = NULL; + node->vc_prev = NULL; +} + +static int +aggr_port_update(rcm_handle_t *hd, dl_aggr_t *aggr, datalink_id_t portid) +{ + link_cache_t *node; + char *rsrc; + int ret = -1; + + rcm_log_message(RCM_TRACE1, + "AGGR: aggr_port_update aggr:%u port:%u\n", + aggr->da_aggrid, portid); + assert(MUTEX_HELD(&cache_lock)); + + rsrc = malloc(RCM_LINK_RESOURCE_MAX); + if (rsrc == NULL) { + rcm_log_message(RCM_ERROR, + _("AGGR: resource malloc error(%s)\n"), strerror(errno)); + goto done; + } + + (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u", + RCM_LINK_PREFIX, portid); + + node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH); + if (node != NULL) { + rcm_log_message(RCM_DEBUG, + "AGGR: %s already registered (aggrid:%u)\n", + rsrc, aggr->da_aggrid); + + free(rsrc); + node->vc_state &= ~CACHE_NODE_STALE; + + assert(node->vc_linkid == portid); + /* + * Update vc_aggr directly as only one aggregation can be + * created on one port. + */ + node->vc_aggr = aggr; + } else { + rcm_log_message(RCM_DEBUG, + "AGGR: %s is a new resource (aggrid:%u)\n", + rsrc, aggr->da_aggrid); + + node = calloc(1, sizeof (link_cache_t)); + if (node == NULL) { + free(rsrc); + rcm_log_message(RCM_ERROR, + _("AGGR: calloc: %s\n"), strerror(errno)); + return (ret); + } + + node->vc_resource = rsrc; + node->vc_aggr = aggr; + node->vc_linkid = portid; + node->vc_state |= CACHE_NODE_NEW; + + + cache_insert(node); + } + + ret = 0; +done: + return (ret); +} + +typedef struct aggr_update_arg_s { + rcm_handle_t *hd; + int retval; +} aggr_update_arg_t; + +/* + * aggr_update() - Update physical interface properties + */ +static int +aggr_update(datalink_id_t aggrid, void *arg) +{ + aggr_update_arg_t *aggr_update_argp = arg; + rcm_handle_t *hd = aggr_update_argp->hd; + dladm_aggr_grp_attr_t aggr_attr; + dl_aggr_t *aggr; + dladm_status_t status; + char errmsg[DLADM_STRSIZE]; + boolean_t exist = B_FALSE; + uint32_t i; + int ret = -1; + + rcm_log_message(RCM_TRACE1, "AGGR: aggr_update(%u)\n", aggrid); + + assert(MUTEX_HELD(&aggr_list_lock)); + status = dladm_aggr_info(aggrid, &aggr_attr, DLADM_OPT_ACTIVE); + if (status != DLADM_STATUS_OK) { + rcm_log_message(RCM_TRACE1, + "AGGR: cannot get aggr information for %u error(%s)\n", + aggrid, dladm_status2str(status, errmsg)); + return (DLADM_WALK_CONTINUE); + } + + /* + * Try to find the aggr from the aggr list. + */ + for (aggr = aggr_head.da_next; aggr != &aggr_tail; aggr = aggr->da_next) + if (aggr->da_aggrid == aggr_attr.lg_linkid) + break; + + if (aggr != NULL) { + exist = B_TRUE; + } else { + if ((aggr = calloc(1, sizeof (dl_aggr_t))) == NULL) { + rcm_log_message(RCM_ERROR, _("AGGR: malloc: %s\n"), + strerror(errno)); + goto done; + } + } + + /* Update aggregation information. */ + if (aggr_attr.lg_nports == 1) + aggr->da_lastport = aggr_attr.lg_ports[0].lp_linkid; + else + aggr->da_lastport = DATALINK_INVALID_LINKID; + aggr->da_aggrid = aggr_attr.lg_linkid; + + for (i = 0; i < aggr_attr.lg_nports; i++) { + datalink_id_t portid = (aggr_attr.lg_ports[i]).lp_linkid; + + if (aggr_port_update(hd, aggr, portid) != 0) + goto done; + } + + if (!exist) + aggr_list_insert(aggr); + + aggr->da_stale = B_FALSE; + rcm_log_message(RCM_TRACE3, + "AGGR: aggr_update: succeeded(%u)\n", aggrid); + + ret = 0; +done: + if (!exist && ret != 0) + free(aggr); + free(aggr_attr.lg_ports); + aggr_update_argp->retval = ret; + return (ret == 0 ? DLADM_WALK_CONTINUE : DLADM_WALK_TERMINATE); +} + +/* + * aggr_update_all() - Determine all AGGR links in the system + */ +static int +aggr_update_all(rcm_handle_t *hd) +{ + aggr_update_arg_t arg = {NULL, 0}; + + rcm_log_message(RCM_TRACE2, "AGGR: aggr_update_all\n"); + assert(MUTEX_HELD(&cache_lock)); + + arg.hd = hd; + (void) dladm_walk_datalink_id(aggr_update, &arg, DATALINK_CLASS_AGGR, + DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); + return (arg.retval); +} + +/* + * cache_update() - Update cache with latest interface info + */ +static int +cache_update(rcm_handle_t *hd) +{ + link_cache_t *node, *next; + dl_aggr_t *aggr; + int ret = 0; + + rcm_log_message(RCM_TRACE2, "AGGR: cache_update\n"); + (void) mutex_lock(&aggr_list_lock); + (void) mutex_lock(&cache_lock); + + /* first we walk the entire aggr list, marking each entry stale */ + for (aggr = aggr_head.da_next; aggr != &aggr_tail; aggr = aggr->da_next) + aggr->da_stale = B_TRUE; + + /* then we walk the entire cache, marking each entry stale */ + node = cache_head.vc_next; + for (; node != &cache_tail; node = node->vc_next) + node->vc_state |= CACHE_NODE_STALE; + + ret = aggr_update_all(hd); + + /* + * Even aggr_update_all() fails, continue to delete all the stale + * resources. First, unregister links that are not offlined and + * still in cache. + */ + for (node = cache_head.vc_next; node != &cache_tail; node = next) { + + next = node->vc_next; + if (node->vc_state & CACHE_NODE_STALE) { + (void) rcm_unregister_interest(hd, node->vc_resource, + 0); + rcm_log_message(RCM_DEBUG, + "AGGR: unregistered %s\n", node->vc_resource); + cache_remove(node); + node_free(node); + continue; + } + + if (!(node->vc_state & CACHE_NODE_NEW)) + continue; + + if (rcm_register_interest(hd, node->vc_resource, 0, + + NULL) != RCM_SUCCESS) { + rcm_log_message(RCM_ERROR, + _("AGGR: failed to register %s\n"), + node->vc_resource); + ret = -1; + } else { + rcm_log_message(RCM_DEBUG, "AGGR: registered %s\n", + node->vc_resource); + + node->vc_state &= ~CACHE_NODE_NEW; + } + } + + aggr = aggr_head.da_next; + while (aggr != &aggr_tail) { + dl_aggr_t *next = aggr->da_next; + + /* delete stale AGGRs */ + if (aggr->da_stale) { + aggr_list_remove(aggr); + free(aggr); + } + aggr = next; + } + +done: + (void) mutex_unlock(&cache_lock); + (void) mutex_unlock(&aggr_list_lock); + return (ret); +} + +/* + * aggr_log_err() - RCM error log wrapper + */ +static void +aggr_log_err(datalink_id_t linkid, char **errorp, char *errmsg) +{ + char link[MAXLINKNAMELEN]; + char errstr[DLADM_STRSIZE]; + dladm_status_t status; + int len; + const char *errfmt; + char *error; + + link[0] = '\0'; + if (linkid != DATALINK_INVALID_LINKID) { + char rsrc[RCM_LINK_RESOURCE_MAX]; + + (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u", + RCM_LINK_PREFIX, linkid); + + rcm_log_message(RCM_ERROR, _("AGGR: %s(%s)\n"), errmsg, rsrc); + + if ((status = dladm_datalink_id2info(linkid, NULL, NULL, + NULL, link, sizeof (link))) != DLADM_STATUS_OK) { + rcm_log_message(RCM_WARNING, + _("AGGR: cannot get link name of (%s) %s\n"), + rsrc, dladm_status2str(status, errstr)); + } + } else { + rcm_log_message(RCM_ERROR, _("AGGR: %s\n"), errmsg); + } + + errfmt = strlen(link) > 0 ? _("AGGR: %s(%s)") : _("AGGR: %s"); + len = strlen(errfmt) + strlen(errmsg) + MAXLINKNAMELEN + 1; + if ((error = malloc(len)) != NULL) { + if (strlen(link) > 0) + (void) sprintf(error, errfmt, errmsg, link); + else + (void) sprintf(error, errfmt, errmsg); + } + + if (errorp != NULL) + *errorp = error; +} + +/* + * aggr_consumer_offline() + * + * Offline AGGR consumers. + */ +static int +aggr_consumer_offline(rcm_handle_t *hd, link_cache_t *node, char **errorp, + uint_t flags, rcm_info_t **depend_info) +{ + char rsrc[RCM_LINK_RESOURCE_MAX]; + int ret; + + rcm_log_message(RCM_TRACE2, "AGGR: aggr_consumer_offline %s\n", + node->vc_resource); + + (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u", + RCM_LINK_PREFIX, node->vc_aggr->da_aggrid); + + /* + * Inform associated VLANs and IP interfaces to be offlined + */ + ret = rcm_request_offline(hd, rsrc, flags, depend_info); + if (ret != RCM_SUCCESS) { + rcm_log_message(RCM_DEBUG, + "AGGR: rcm_request_offline failed (%s)\n", rsrc); + return (ret); + } + + node->vc_state |= CACHE_AGGR_CONSUMER_OFFLINED; + rcm_log_message(RCM_TRACE2, "AGGR: aggr_consumer_offline done\n"); + return (ret); +} + +/* + * aggr_consumer_online() + * + * online AGGR consumers. + */ +static int +aggr_consumer_online(rcm_handle_t *hd, link_cache_t *node, char **errorp, + uint_t flags, rcm_info_t **depend_info) +{ + char rsrc[RCM_LINK_RESOURCE_MAX]; + int ret; + + rcm_log_message(RCM_TRACE2, "AGGR: aggr_consumer_online %s\n", + node->vc_resource); + + if (!(node->vc_state & CACHE_AGGR_CONSUMER_OFFLINED)) { + rcm_log_message(RCM_DEBUG, + "AGGR: no consumers offlined (%s)\n", node->vc_resource); + return (RCM_SUCCESS); + } + + (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u", + RCM_LINK_PREFIX, node->vc_aggr->da_aggrid); + + ret = rcm_notify_online(hd, rsrc, flags, depend_info); + if (ret != RCM_SUCCESS) { + rcm_log_message(RCM_DEBUG, + "AGGR: rcm_notify_online failed (%s)\n", rsrc); + return (ret); + } + + node->vc_state &= ~CACHE_AGGR_CONSUMER_OFFLINED; + rcm_log_message(RCM_TRACE2, "AGGR: aggr_consumer_online done\n"); + return (ret); +} + +/* + * Send RCM_RESOURCE_LINK_NEW events to other modules about new aggregations. + * Return 0 on success, -1 on failure. + */ +static int +aggr_notify_new_aggr(rcm_handle_t *hd, char *rsrc) +{ + link_cache_t *node; + dl_aggr_t *aggr; + nvlist_t *nvl = NULL; + uint64_t id; + boolean_t is_only_port; + int ret = -1; + + rcm_log_message(RCM_TRACE2, "AGGR: aggr_notify_new_aggr (%s)\n", rsrc); + + /* Check for the interface in the cache */ + (void) mutex_lock(&cache_lock); + if ((node = cache_lookup(hd, rsrc, CACHE_REFRESH)) == NULL) { + rcm_log_message(RCM_TRACE1, + "AGGR: aggr_notify_new_aggr() unrecognized resource (%s)\n", + rsrc); + (void) mutex_unlock(&cache_lock); + return (0); + } + + if (nvlist_alloc(&nvl, 0, 0) != 0) { + rcm_log_message(RCM_WARNING, + _("AGGR: failed to allocate nvlist\n")); + (void) mutex_unlock(&cache_lock); + goto done; + } + + aggr = node->vc_aggr; + is_only_port = (aggr->da_lastport == node->vc_linkid); + + if (is_only_port) { + rcm_log_message(RCM_TRACE2, + "AGGR: aggr_notify_new_aggr add (%u)\n", + aggr->da_aggrid); + + id = aggr->da_aggrid; + if (nvlist_add_uint64(nvl, RCM_NV_LINKID, id) != 0) { + rcm_log_message(RCM_ERROR, + _("AGGR: failed to construct nvlist\n")); + (void) mutex_unlock(&cache_lock); + goto done; + } + } + + (void) mutex_unlock(&cache_lock); + + /* + * If this link is not the only port in the aggregation, the aggregation + * is not new. No need to inform other consumers in that case. + */ + if (is_only_port && rcm_notify_event(hd, RCM_RESOURCE_LINK_NEW, + 0, nvl, NULL) != RCM_SUCCESS) { + rcm_log_message(RCM_ERROR, + _("AGGR: failed to notify %s event for %s\n"), + RCM_RESOURCE_LINK_NEW, node->vc_resource); + goto done; + } + + ret = 0; +done: + if (nvl != NULL) + nvlist_free(nvl); + return (ret); +} + +/* + * aggr_consumer_notify() - Notify consumers of AGGRs coming back online. + */ +static int +aggr_consumer_notify(rcm_handle_t *hd, datalink_id_t linkid, char **errorp, + uint_t flags, rcm_info_t **depend_info) +{ + char rsrc[RCM_LINK_RESOURCE_MAX]; + link_cache_t *node; + + (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u", + RCM_LINK_PREFIX, linkid); + + rcm_log_message(RCM_TRACE1, "AGGR: aggr_consumer_notify(%s)\n", rsrc); + + /* + * Inform IP and VLAN consumers to be online. + */ + if (aggr_notify_new_aggr(hd, rsrc) != 0) { + (void) mutex_lock(&cache_lock); + if ((node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH)) != NULL) + (void) aggr_offline_port(node, CACHE_NODE_STALE); + (void) mutex_unlock(&cache_lock); + rcm_log_message(RCM_TRACE1, + "AGGR: aggr_notify_new_aggr failed(%s)\n", rsrc); + return (-1); + } + + rcm_log_message(RCM_TRACE2, "AGGR: aggr_consumer_notify succeeded\n"); + return (0); +} + +typedef struct aggr_configure_arg { + datalink_id_t portid; + int retval; + boolean_t up; +} aggr_configure_arg_t; + +static int +aggr_configure(datalink_id_t aggrid, void *arg) +{ + aggr_configure_arg_t *aggr_configure_argp = arg; + datalink_id_t portid; + dladm_aggr_grp_attr_t aggr_attr; + dladm_aggr_port_attr_db_t port_attr; + dladm_status_t status; + uint32_t flags; + char errmsg[DLADM_STRSIZE]; + int i; + + status = dladm_datalink_id2info(aggrid, &flags, NULL, NULL, NULL, 0); + if (status != DLADM_STATUS_OK) + return (DLADM_WALK_CONTINUE); + + status = dladm_aggr_info(aggrid, &aggr_attr, DLADM_OPT_PERSIST); + if (status != DLADM_STATUS_OK) + return (DLADM_WALK_CONTINUE); + + portid = aggr_configure_argp->portid; + for (i = 0; i < aggr_attr.lg_nports; i++) + if (aggr_attr.lg_ports[i].lp_linkid == portid) + break; + + if (i == aggr_attr.lg_nports) { + /* + * The aggregation doesn't contain this port. + */ + free(aggr_attr.lg_ports); + return (DLADM_WALK_CONTINUE); + } + + /* + * If this aggregation already exists, add this port to this + * aggregation, otherwise, bring up this aggregation. + */ + if (flags & DLADM_OPT_ACTIVE) { + rcm_log_message(RCM_TRACE3, + "AGGR: aggr_configure dladm_aggr_add port %u (%u)\n", + portid, aggrid); + port_attr.lp_linkid = portid; + status = dladm_aggr_add(aggrid, 1, &port_attr, + DLADM_OPT_ACTIVE); + } else { + rcm_log_message(RCM_TRACE3, + "AGGR: aggr_configure dladm_aggr_up (%u)\n", aggrid); + status = dladm_aggr_up(aggrid); + } + + if (status != DLADM_STATUS_OK) { + /* + * Print a warning message and continue to UP other AGGRs. + */ + rcm_log_message(RCM_WARNING, + _("AGGR: AGGR online failed (%u): %s\n"), + aggrid, dladm_status2str(status, errmsg)); + aggr_configure_argp->retval = -1; + } else if (!(flags & DLADM_OPT_ACTIVE)) { + aggr_configure_argp->up = B_TRUE; + } + + free(aggr_attr.lg_ports); + return (DLADM_WALK_TERMINATE); +} + +/* + * aggr_configure_all() - Configure AGGRs over a physical link after it attaches + */ +static int +aggr_configure_all(rcm_handle_t *hd, datalink_id_t linkid, boolean_t *up) +{ + char rsrc[RCM_LINK_RESOURCE_MAX]; + link_cache_t *node; + aggr_configure_arg_t arg = {DATALINK_INVALID_LINKID, 0, B_FALSE}; + + *up = B_FALSE; + + /* Check for the AGGRs in the cache */ + (void) snprintf(rsrc, sizeof (rsrc), "%s/%u", RCM_LINK_PREFIX, linkid); + + rcm_log_message(RCM_TRACE1, "AGGR: aggr_configure_all(%s)\n", rsrc); + + /* Check if the link is new or was previously offlined */ + (void) mutex_lock(&cache_lock); + if (((node = cache_lookup(hd, rsrc, CACHE_REFRESH)) != NULL) && + (!(node->vc_state & CACHE_NODE_OFFLINED))) { + rcm_log_message(RCM_TRACE1, + "AGGR: Skipping configured link(%s)\n", rsrc); + (void) mutex_unlock(&cache_lock); + return (0); + } + (void) mutex_unlock(&cache_lock); + + arg.portid = linkid; + (void) dladm_walk_datalink_id(aggr_configure, &arg, DATALINK_CLASS_AGGR, + DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); + + if (arg.retval == 0) { + *up = arg.up; + rcm_log_message(RCM_TRACE1, + "AGGR: aggr_configure_all succeeded(%s)\n", rsrc); + } + return (arg.retval); +} diff --git a/usr/src/cmd/rcm_daemon/common/ip_rcm.c b/usr/src/cmd/rcm_daemon/common/ip_rcm.c index f4a896e41a..5421d7364a 100644 --- a/usr/src/cmd/rcm_daemon/common/ip_rcm.c +++ b/usr/src/cmd/rcm_daemon/common/ip_rcm.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -54,6 +54,7 @@ #include <netdb.h> #include <inet/ip.h> #include <libinetutil.h> +#include <libdllink.h> #include <ipmp_mpathd.h> #include "rcm_module.h" @@ -72,8 +73,8 @@ #define IP_MAX_MODS 9 /* max modules pushed on intr */ #define MAX_RECONFIG_SIZE 1024 /* Max. reconfig string size */ -#define RCM_NET_PREFIX "SUNW_network" /* RCM network name prefix */ -#define RCM_NET_RESOURCE_MAX (13 + LIFNAMSIZ) /* RCM_NET_PREFIX+LIFNAMSIZ */ +#define RCM_LINK_PREFIX "SUNW_datalink" /* RCM datalink name prefix */ +#define RCM_LINK_RESOURCE_MAX (13 + LINKID_STR_WIDTH) #define RCM_STR_SUNW_IP "SUNW_ip/" /* IP address export prefix */ #define RCM_SIZE_SUNW_IP 9 /* strlen("SUNW_ip/") + 1 */ @@ -134,13 +135,6 @@ #define MOD_REMOVE 1 /* Remove a mid-stream module */ #define MOD_CHECK 2 /* Check mid-stream module safety */ -/* VLAN format support */ -#define VLAN_MAX_PPA_ALLOWED 1000 -#define VLAN_GET_PPA(ppa) (ppa % VLAN_MAX_PPA_ALLOWED) - -/* devfsadm attach nvpair values */ -#define PROP_NV_DDI_NETWORK "ddi_network" - /* * in.mpathd(1M) message passing formats */ @@ -217,31 +211,6 @@ static mutex_t cache_lock; static int events_registered = 0; /* - * Global NIC list to be configured after DR-attach - */ -#define NIL_NULL ((struct ni_list *)0) - -struct net_interface { - char *type; /* Name of type of interface (le, ie, etc.) */ - char *name; /* Qualified name of interface (le0, ie0, etc.) */ -}; - -struct ni_list { - struct net_interface *nifp; - struct ni_list *next; -}; - -static mutex_t nil_lock; /* NIC list lock */ -static int num_ni = 0; /* Global new interface count */ -static struct ni_list *nil_head = NIL_NULL; /* Global new if list */ - -struct devfs_minor_data { - int32_t minor_type; - char *minor_name; - char *minor_node_type; -}; - -/* * RCM module interface prototypes */ static int ip_register(rcm_handle_t *); @@ -276,7 +245,7 @@ static int if_cfginfo(ip_cache_t *, uint_t); static int if_unplumb(ip_cache_t *); static int if_replumb(ip_cache_t *); static void ip_log_err(ip_cache_t *, char **, char *); -static char *get_physical_resource(const char *); +static char *get_link_resource(const char *); static void clr_cfg_state(ip_pif_t *); static uint64_t if_get_flags(ip_pif_t *); static int mpathd_send_cmd(mpathd_cmd_t *); @@ -291,12 +260,10 @@ static int ip_offlinelist(rcm_handle_t *, ip_cache_t *, char **, uint_t, rcm_info_t **); static char **ip_get_addrlist(ip_cache_t *); static void ip_free_addrlist(char **); -static void ip_consumer_notify(rcm_handle_t *, char *, char **, uint_t, - rcm_info_t **); +static void ip_consumer_notify(rcm_handle_t *, datalink_id_t, char **, + uint_t, rcm_info_t **); -static int process_nvlist(nvlist_t *); -static void process_minor(char *, char *, int32_t, struct devfs_minor_data *); -static int if_configure(char *); +static int if_configure(datalink_id_t); static int isgrouped(char *); static int if_ipmp_config(char *, int, int); static int if_mpathd_configure(char *, char *, int, int); @@ -335,7 +302,6 @@ rcm_mod_init(void) cache_tail.ip_prev = &cache_head; cache_tail.ip_next = NULL; (void) mutex_init(&cache_lock, NULL, NULL); - (void) mutex_init(&nil_lock, NULL, NULL); /* Return the ops vectors */ return (&ip_ops); @@ -361,7 +327,6 @@ rcm_mod_fini(void) rcm_log_message(RCM_TRACE1, "IP: mod_fini\n"); free_cache(); - (void) mutex_destroy(&nil_lock); (void) mutex_destroy(&cache_lock); return (RCM_SUCCESS); } @@ -386,15 +351,15 @@ ip_register(rcm_handle_t *hd) * getting attached, so we get attach event notifications */ if (!events_registered) { - if (rcm_register_event(hd, RCM_RESOURCE_NETWORK_NEW, 0, NULL) + if (rcm_register_event(hd, RCM_RESOURCE_LINK_NEW, 0, NULL) != RCM_SUCCESS) { rcm_log_message(RCM_ERROR, _("IP: failed to register %s\n"), - RCM_RESOURCE_NETWORK_NEW); + RCM_RESOURCE_LINK_NEW); return (RCM_FAILURE); } else { rcm_log_message(RCM_DEBUG, "IP: registered %s\n", - RCM_RESOURCE_NETWORK_NEW); + RCM_RESOURCE_LINK_NEW); events_registered++; } } @@ -435,15 +400,15 @@ ip_unregister(rcm_handle_t *hd) * Need to unregister interest in all new resources */ if (events_registered) { - if (rcm_unregister_event(hd, RCM_RESOURCE_NETWORK_NEW, 0) + if (rcm_unregister_event(hd, RCM_RESOURCE_LINK_NEW, 0) != RCM_SUCCESS) { rcm_log_message(RCM_ERROR, _("IP: failed to unregister %s\n"), - RCM_RESOURCE_NETWORK_NEW); + RCM_RESOURCE_LINK_NEW); return (RCM_FAILURE); } else { rcm_log_message(RCM_DEBUG, "IP: unregistered %s\n", - RCM_RESOURCE_NETWORK_NEW); + RCM_RESOURCE_LINK_NEW); events_registered--; } } @@ -828,9 +793,9 @@ static int ip_notify_event(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, char **errorp, nvlist_t *nvl, rcm_info_t **depend_info) { - struct ni_list *nilp, *onilp; - struct net_interface *nip; - int n; + datalink_id_t linkid; + nvpair_t *nvp = NULL; + uint64_t id64; assert(hd != NULL); assert(rsrc != NULL); @@ -839,7 +804,7 @@ ip_notify_event(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, rcm_log_message(RCM_TRACE1, "IP: notify_event(%s)\n", rsrc); - if (!STREQ(rsrc, RCM_RESOURCE_NETWORK_NEW)) { + if (!STREQ(rsrc, RCM_RESOURCE_LINK_NEW)) { rcm_log_message(RCM_INFO, _("IP: unrecognized event for %s\n"), rsrc); ip_log_err(NULL, errorp, "unrecognized event"); @@ -847,61 +812,37 @@ ip_notify_event(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, return (RCM_FAILURE); } - /* Update cache to reflect latest interfaces */ + /* Update cache to reflect latest interfaces */ if (update_cache(hd) < 0) { rcm_log_message(RCM_ERROR, _("IP: update_cache failed\n")); ip_log_err(NULL, errorp, "Private Cache update failed"); return (RCM_FAILURE); } - /* Process the nvlist for the event */ - if (process_nvlist(nvl) != 0) { - rcm_log_message(RCM_WARNING, - _("IP: Error processing resource attributes(%s)\n"), rsrc); - rcm_log_message(RCM_WARNING, - _("IP: One or more devices may not be configured.\n")); - ip_log_err(NULL, errorp, "Error processing device properties"); - /* Continue processing interfaces that were valid */ - } - - (void) mutex_lock(&nil_lock); - - /* Configure all new interfaces found */ - for (nilp = nil_head, n = 0; n < num_ni; nilp = nilp->next, n++) { - nip = nilp->nifp; - if (if_configure(nip->name) != 0) { - rcm_log_message(RCM_ERROR, - _("IP: Configuration failed (%s)\n"), nip->name); - ip_log_err(NULL, errorp, - "Failed configuring one or more IP addresses"); - /* continue configuring rest of the interfaces */ - } - } - - /* Notify all IP address consumers and clean up interface list */ - for (nilp = nil_head; nilp; ) { - nip = nilp->nifp; - if (nip != (struct net_interface *)0) { - if (nip->name != 0) { - ip_consumer_notify(hd, nip->name, errorp, flags, - depend_info); - free(nip->name); + rcm_log_message(RCM_TRACE1, "IP: process_nvlist\n"); + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + if (STREQ(nvpair_name(nvp), RCM_NV_LINKID)) { + if (nvpair_value_uint64(nvp, &id64) != 0) { + rcm_log_message(RCM_WARNING, + _("IP: cannot get linkid\n")); + return (RCM_FAILURE); + } + linkid = (datalink_id_t)id64; + if (if_configure(linkid) != 0) { + rcm_log_message(RCM_ERROR, + _("IP: Configuration failed (%u)\n"), + linkid); + ip_log_err(NULL, errorp, + "Failed configuring one or more IP " + "addresses"); } - if (nip->type != 0) - free(nip->type); - free((char *)nip); - } - onilp = nilp; - nilp = nilp->next; - free((char *)onilp); + /* Notify all IP address consumers */ + ip_consumer_notify(hd, linkid, errorp, flags, + depend_info); + } } - num_ni = 0; /* reset new if count */ - nil_head = NIL_NULL; /* reset list head */ - - (void) mutex_unlock(&nil_lock); - rcm_log_message(RCM_TRACE1, "IP: notify_event: device configuration complete\n"); @@ -919,17 +860,42 @@ ip_usage(ip_cache_t *node) ip_lif_t *lif; int numifs; char *buf; - char *nic; + char *linkidstr; + datalink_id_t linkid; const char *fmt; char *sep; + char link[MAXLINKNAMELEN]; char addrstr[INET6_ADDRSTRLEN]; + char errmsg[DLADM_STRSIZE]; + dladm_status_t status; int offline = 0; size_t bufsz; rcm_log_message(RCM_TRACE2, "IP: usage(%s)\n", node->ip_resource); - nic = strchr(node->ip_resource, '/'); - nic = nic ? nic + 1 : node->ip_resource; + /* + * Note that node->ip_resource is in the form of SUNW_datalink/<linkid> + */ + linkidstr = strchr(node->ip_resource, '/'); + assert(linkidstr != NULL); + linkidstr = linkidstr ? linkidstr + 1 : node->ip_resource; + + errno = 0; + linkid = strtol(linkidstr, &buf, 10); + if (errno != 0 || *buf != '\0') { + rcm_log_message(RCM_ERROR, + _("IP: usage(%s) parse linkid failure (%s)\n"), + node->ip_resource, strerror(errno)); + return (NULL); + } + + if ((status = dladm_datalink_id2info(linkid, NULL, NULL, NULL, link, + sizeof (link))) != DLADM_STATUS_OK) { + rcm_log_message(RCM_ERROR, + _("IP: usage(%s) get link name failure(%s)\n"), + node->ip_resource, dladm_status2str(status, errmsg)); + return (NULL); + } /* TRANSLATION_NOTE: separator used between IP addresses */ sep = _(", "); @@ -955,7 +921,7 @@ ip_usage(ip_cache_t *node) /* space for addresses and separators, plus message */ bufsz = ((numifs * (INET6_ADDRSTRLEN + strlen(sep))) + - strlen(fmt) + strlen(nic) + 1); + strlen(fmt) + strlen(link) + 1); if ((buf = malloc(bufsz)) == NULL) { rcm_log_message(RCM_ERROR, _("IP: usage(%s) malloc failure(%s)\n"), @@ -963,7 +929,7 @@ ip_usage(ip_cache_t *node) return (NULL); } bzero(buf, bufsz); - (void) sprintf(buf, fmt, nic); + (void) sprintf(buf, fmt, link); if (offline || (numifs == 0)) { /* Nothing else to do */ rcm_log_message(RCM_TRACE2, "IP: usage (%s) info = %s\n", @@ -1019,7 +985,7 @@ ip_usage(ip_cache_t *node) */ /* - * cache_lookup() - Get a cache node for a resource. Supports VLAN interfaces. + * cache_lookup() - Get a cache node for a resource. * Call with cache lock held. * * This ensures that the cache is consistent with the system state and @@ -1029,7 +995,6 @@ static ip_cache_t * cache_lookup(rcm_handle_t *hd, char *rsrc, char options) { ip_cache_t *probe; - char *resource; /* physical resource */ rcm_log_message(RCM_TRACE2, "IP: cache lookup(%s)\n", rsrc); @@ -1040,23 +1005,16 @@ cache_lookup(rcm_handle_t *hd, char *rsrc, char options) (void) mutex_lock(&cache_lock); } - if ((resource = get_physical_resource(rsrc)) == NULL) { - errno = ENOENT; - return (NULL); - } - probe = cache_head.ip_next; while (probe != &cache_tail) { if (probe->ip_resource && - STREQ(resource, probe->ip_resource)) { + STREQ(rsrc, probe->ip_resource)) { rcm_log_message(RCM_TRACE2, "IP: cache lookup success(%s)\n", rsrc); - free(resource); return (probe); } probe = probe->ip_next; } - free(resource); return (NULL); } @@ -1098,6 +1056,9 @@ free_node(ip_cache_t *node) static void cache_insert(ip_cache_t *node) { + rcm_log_message(RCM_TRACE2, "IP: cache insert(%s)\n", + node->ip_resource); + /* insert at the head for best performance */ node->ip_next = cache_head.ip_next; node->ip_prev = &cache_head; @@ -1113,6 +1074,9 @@ cache_insert(ip_cache_t *node) static void cache_remove(ip_cache_t *node) { + rcm_log_message(RCM_TRACE2, "IP: cache remove(%s)\n", + node->ip_resource); + node->ip_next->ip_prev = node->ip_prev; node->ip_prev->ip_next = node->ip_next; node->ip_next = NULL; @@ -1127,7 +1091,7 @@ cache_remove(ip_cache_t *node) static int update_pif(rcm_handle_t *hd, int af, int sock, struct lifreq *lifr) { - char ifname[RCM_NET_RESOURCE_MAX]; + char *rsrc; ifspec_t ifspec; ushort_t ifnumber = 0; ip_cache_t *probe; @@ -1191,29 +1155,28 @@ update_pif(rcm_handle_t *hd, int af, int sock, struct lifreq *lifr) } (void) memcpy(&ifaddr, &lifreq.lifr_addr, sizeof (ifaddr)); - /* Search for the interface in our cache */ - (void) snprintf(ifname, sizeof (ifname), "%s/%s", RCM_NET_PREFIX, - pif.pi_ifname); + rsrc = get_link_resource(pif.pi_ifname); + if (rsrc == NULL) { + rcm_log_message(RCM_ERROR, + _("IP: get_link_resource(%s) failed\n"), + lifreq.lifr_name); + return (-1); + } - probe = cache_lookup(hd, ifname, CACHE_NO_REFRESH); + probe = cache_lookup(hd, rsrc, CACHE_NO_REFRESH); if (probe != NULL) { + free(rsrc); probe->ip_cachestate &= ~(CACHE_IF_STALE); } else { if ((probe = calloc(1, sizeof (ip_cache_t))) == NULL) { /* malloc errors are bad */ + free(rsrc); rcm_log_message(RCM_ERROR, _("IP: calloc: %s\n"), strerror(errno)); return (-1); } - probe->ip_resource = get_physical_resource(ifname); - if (!probe->ip_resource) { - rcm_log_message(RCM_ERROR, _("IP: strdup: %s\n"), - strerror(errno)); - free(probe); - return (-1); - } - + probe->ip_resource = rsrc; probe->ip_pif = NULL; probe->ip_ifred = RCM_IPMP_MIN_REDUNDANCY; probe->ip_cachestate |= CACHE_IF_NEW; @@ -1503,21 +1466,20 @@ free_cache() static void ip_log_err(ip_cache_t *node, char **errorp, char *errmsg) { - char *nic = NULL; + char *ifname = NULL; int len; const char *errfmt; char *error; if ((node != NULL) && (node->ip_pif != NULL) && (node->ip_pif->pi_ifname != NULL)) { - nic = strrchr(node->ip_pif->pi_ifname, '/'); - nic = nic ? nic + 1 : node->ip_pif->pi_ifname; + ifname = node->ip_pif->pi_ifname; } if (errorp != NULL) *errorp = NULL; - if (nic == NULL) { + if (ifname == NULL) { rcm_log_message(RCM_ERROR, _("IP: %s\n"), errmsg); errfmt = _("IP: %s"); len = strlen(errfmt) + strlen(errmsg) + 1; @@ -1525,11 +1487,11 @@ ip_log_err(ip_cache_t *node, char **errorp, char *errmsg) (void) sprintf(error, errfmt, errmsg); } } else { - rcm_log_message(RCM_ERROR, _("IP: %s(%s)\n"), errmsg, nic); + rcm_log_message(RCM_ERROR, _("IP: %s(%s)\n"), errmsg, ifname); errfmt = _("IP: %s(%s)"); - len = strlen(errfmt) + strlen(errmsg) + strlen(nic) + 1; + len = strlen(errfmt) + strlen(errmsg) + strlen(ifname) + 1; if (error = (char *)calloc(1, len)) { - (void) sprintf(error, errfmt, errmsg, nic); + (void) sprintf(error, errfmt, errmsg, ifname); } } @@ -1696,7 +1658,7 @@ if_unplumb(ip_cache_t *node) } else { /* Unlikely case */ rcm_log_message(RCM_DEBUG, - _("IP: Unplumb ignored (%s:%d)\n"), + "IP: Unplumb ignored (%s:%d)\n", pif->pi_ifname, lif->li_ifnum); lif = lif->li_next; continue; @@ -1781,7 +1743,7 @@ if_replumb(ip_cache_t *node) } else { /* Unlikely case */ rcm_log_message(RCM_DEBUG, - _("IP: Re-plumb ignored (%s:%d)\n"), + "IP: Re-plumb ignored (%s:%d)\n", pif->pi_ifname, lif->li_ifnum); lif = lif->li_next; continue; @@ -1958,37 +1920,46 @@ ip_ipmp_undo_offline(ip_cache_t *node) } /* - * get_physical_resource() - Convert a name (e.g., "SUNW_network/hme0:1" or - * "SUNW_network/hme1000") into a dynamically allocated string containing the - * associated physical device resource name ("SUNW_network/hme0"). Since we - * assume that interface names map directly to device names, this is a - * pass-through operation, with the exception that logical interface numbers - * and VLANs encoded in the PPA are stripped. This logic will need to be - * revisited to support administratively-chosen interface names. + * get_link_resource() - Convert a link name (e.g., net0, hme1000) into a + * dynamically allocated string containing the associated link resource + * name ("SUNW_datalink/<linkid>"). */ static char * -get_physical_resource(const char *rsrc) +get_link_resource(const char *link) { - char *rsrc_ifname, *ifname; - ifspec_t ifspec; + char errmsg[DLADM_STRSIZE]; + datalink_id_t linkid; + uint32_t flags; + char *resource; + dladm_status_t status; - rsrc_ifname = strchr(rsrc, '/'); - if (rsrc_ifname == NULL || !ifparse_ifspec(rsrc_ifname + 1, &ifspec)) { - rcm_log_message(RCM_ERROR, _("IP: bad resource: %s\n"), rsrc); - return (NULL); + if ((status = dladm_name2info(link, &linkid, &flags, NULL, NULL)) + != DLADM_STATUS_OK) { + goto fail; } - ifname = malloc(RCM_NET_RESOURCE_MAX); - if (ifname == NULL) { + if (!(flags & DLADM_OPT_ACTIVE)) { + status = DLADM_STATUS_FAILED; + goto fail; + } + + resource = malloc(RCM_LINK_RESOURCE_MAX); + if (resource == NULL) { rcm_log_message(RCM_ERROR, _("IP: malloc error(%s): %s\n"), - strerror(errno), rsrc); + strerror(errno), link); return (NULL); } - (void) snprintf(ifname, RCM_NET_RESOURCE_MAX, "%s/%s%d", RCM_NET_PREFIX, - ifspec.ifsp_devnm, VLAN_GET_PPA(ifspec.ifsp_ppa)); + (void) snprintf(resource, RCM_LINK_RESOURCE_MAX, "%s/%u", + RCM_LINK_PREFIX, linkid); - return (ifname); + return (resource); + +fail: + rcm_log_message(RCM_ERROR, + _("IP: get_link_resource for %s error(%s)\n"), + link, dladm_status2str(status, errmsg)); + return (NULL); } /* @@ -2107,7 +2078,7 @@ mpathd_send_cmd(mpathd_cmd_t *mpd) if (mpr.resp_sys_errno == EAGAIN) { (void) sleep(1); rcm_log_message(RCM_DEBUG, - _("IP: mpathd retrying\n")); + "IP: mpathd retrying\n"); continue; /* Retry */ } errno = mpr.resp_sys_errno; @@ -2605,36 +2576,24 @@ ip_free_addrlist(char **addrlist) */ static void -ip_consumer_notify(rcm_handle_t *hd, char *ifinst, char **errorp, uint_t flags, - rcm_info_t **depend_info) +ip_consumer_notify(rcm_handle_t *hd, datalink_id_t linkid, char **errorp, + uint_t flags, rcm_info_t **depend_info) { - char ifname[LIFNAMSIZ + 1]; - char cached_name[RCM_NET_RESOURCE_MAX]; + char cached_name[RCM_LINK_RESOURCE_MAX]; ip_cache_t *node; - char *cp; - - rcm_log_message(RCM_TRACE1, "IP: ip_consumer_notify(%s)\n", ifinst); - if (ifinst == NULL) - return; + assert(linkid != DATALINK_INVALID_LINKID); - (void) memcpy(&ifname, ifinst, sizeof (ifname)); - ifname[sizeof (ifname) - 1] = '\0'; - - /* remove LIF component */ - cp = strchr(ifname, ':'); - if (cp) { - *cp = 0; - } + rcm_log_message(RCM_TRACE1, _("IP: ip_consumer_notify(%u)\n"), linkid); /* Check for the interface in the cache */ - (void) snprintf(cached_name, sizeof (cached_name), "%s/%s", - RCM_NET_PREFIX, ifname); + (void) snprintf(cached_name, sizeof (cached_name), "%s/%u", + RCM_LINK_PREFIX, linkid); (void) mutex_lock(&cache_lock); if ((node = cache_lookup(hd, cached_name, CACHE_REFRESH)) == NULL) { - rcm_log_message(RCM_TRACE1, "IP: Skipping interface(%s) \n", - ifname); + rcm_log_message(RCM_TRACE1, _("IP: Skipping interface(%u)\n"), + linkid); (void) mutex_unlock(&cache_lock); return; } @@ -2650,281 +2609,47 @@ ip_consumer_notify(rcm_handle_t *hd, char *ifinst, char **errorp, uint_t flags, return; } -/* - * process_nvlist() - Determine network interfaces on a new attach by - * processing the nvlist - */ -/*ARGSUSED*/ -static int -process_nvlist(nvlist_t *nvl) -{ - nvpair_t *nvp = NULL; - char *driver_name; - char *devfs_path; - int32_t instance; - char *minor_byte_array; /* packed nvlist of minor_data */ - uint_t nminor; /* # of minor nodes */ - struct devfs_minor_data *mdata; - nvlist_t *mnvl; - nvpair_t *mnvp = NULL; - - rcm_log_message(RCM_TRACE1, "IP: process_nvlist\n"); - - while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { - /* Get driver name */ - if (STREQ(nvpair_name(nvp), RCM_NV_DRIVER_NAME)) { - if (nvpair_value_string(nvp, &driver_name) != 0) { - rcm_log_message(RCM_WARNING, - _("IP: cannot get driver name\n")); - return (-1); - } - } - /* Get instance */ - if (STREQ(nvpair_name(nvp), RCM_NV_INSTANCE)) { - if (nvpair_value_int32(nvp, &instance) != 0) { - rcm_log_message(RCM_WARNING, - _("IP: cannot get device instance\n")); - return (-1); - } - } - /* Get devfs_path */ - if (STREQ(nvpair_name(nvp), RCM_NV_DEVFS_PATH)) { - if (nvpair_value_string(nvp, &devfs_path) != 0) { - rcm_log_message(RCM_WARNING, - _("IP: cannot get device path\n")); - return (-1); - } - } - /* Get minor data */ - if (STREQ(nvpair_name(nvp), RCM_NV_MINOR_DATA)) { - if (nvpair_value_byte_array(nvp, - (uchar_t **)&minor_byte_array, &nminor) != 0) { - rcm_log_message(RCM_WARNING, - _("IP: cannot get device minor data\n")); - return (-1); - } - if (nvlist_unpack(minor_byte_array, - nminor, &mnvl, 0) != 0) { - rcm_log_message(RCM_WARNING, - _("IP: cannot get minor node data\n")); - return (-1); - } - mdata = (struct devfs_minor_data *)calloc(1, - sizeof (struct devfs_minor_data)); - if (mdata == NULL) { - rcm_log_message(RCM_WARNING, - _("IP: calloc error(%s)\n"), - strerror(errno)); - nvlist_free(mnvl); - return (-1); - } - /* Enumerate minor node data */ - while ((mnvp = nvlist_next_nvpair(mnvl, mnvp)) != - NULL) { - /* Get minor type */ - if (STREQ(nvpair_name(mnvp), - RCM_NV_MINOR_TYPE)) { - if (nvpair_value_int32(mnvp, - &mdata->minor_type) != 0) { - rcm_log_message(RCM_WARNING, - _("IP: cannot get minor " - "type \n")); - nvlist_free(mnvl); - return (-1); - } - } - /* Get minor name */ - if (STREQ(nvpair_name(mnvp), - RCM_NV_MINOR_NAME)) { - if (nvpair_value_string(mnvp, - &mdata->minor_name) != 0) { - rcm_log_message(RCM_WARNING, - _("IP: cannot get minor " - "name \n")); - nvlist_free(mnvl); - return (-1); - } - } - /* Get minor node type */ - if (STREQ(nvpair_name(mnvp), - RCM_NV_MINOR_NODE_TYPE)) { - if (nvpair_value_string(mnvp, - &mdata->minor_node_type) != 0) { - rcm_log_message(RCM_WARNING, - _("IP: cannot get minor " - "node type \n")); - nvlist_free(mnvl); - return (-1); - } - } - } - (void) process_minor(devfs_path, driver_name, instance, - mdata); - nvlist_free(mnvl); - } - } - - rcm_log_message(RCM_TRACE1, "IP: process_nvlist success\n"); - return (0); -} - -static void -process_minor(char *devfs_path, char *name, int instance, - struct devfs_minor_data *mdata) -{ - struct net_interface *nip; - struct ni_list *nilp; - struct ni_list *p; - struct ni_list **pp; - char *cname; - size_t cnamelen; - - rcm_log_message(RCM_TRACE1, "IP: process_minor\n"); - - if ((mdata->minor_node_type != NULL) && - !STREQ(mdata->minor_node_type, PROP_NV_DDI_NETWORK)) { - /* Process network devices only */ - return; - } - - rcm_log_message(RCM_TRACE1, "IP: Examining %s (%s)\n", - devfs_path, mdata->minor_name); - - /* Sanity check, instances > 999 are illegal */ - if (instance > 999) { - errno = EINVAL; - rcm_log_message(RCM_ERROR, _("IP: invalid instance %d(%s)\n"), - instance, strerror(errno)); - return; - } - - /* Now, let's add the node to the interface list */ - if ((nip = malloc(sizeof (struct net_interface))) == NULL) { - rcm_log_message(RCM_ERROR, _("IP: malloc failure(%s)\n"), - strerror(errno)); - return; - } - (void) memset(nip, 0, sizeof (struct net_interface)); - - cnamelen = strlen(name) + 1; - /* Set NIC type */ - if ((nip->type = (char *)malloc(cnamelen)) == NULL) { - free(nip); - rcm_log_message(RCM_ERROR, _("IP: malloc failure(%s)\n"), - strerror(errno)); - return; - } - (void) memcpy(nip->type, name, cnamelen); - - cnamelen += 3; - if ((cname = (char *)malloc(cnamelen)) == NULL) { - free(nip->type); - free(nip); - rcm_log_message(RCM_ERROR, _("IP: malloc failure(%s)\n"), - strerror(errno)); - return; - } - (void) snprintf(cname, cnamelen, "%s%d", name, instance); - - rcm_log_message(RCM_TRACE1, "IP: Found SUNW_network/%s%d\n", name, - instance); - - /* Set NIC name */ - if ((nip->name = strdup(cname)) == NULL) { - free(nip->type); - free(nip); - free(cname); - rcm_log_message(RCM_ERROR, _("IP: strdup failure(%s)\n"), - strerror(errno)); - return; - } - free(cname); - - /* Add new interface to the list */ - (void) mutex_lock(&nil_lock); - for (pp = &nil_head; (p = *pp) != NULL; pp = &(p->next)) { - cname = p->nifp->name; - if (strcmp(cname, nip->name) == 0) - break; - } - - if (p != NULL) { - (void) mutex_unlock(&nil_lock); - free(nip->name); - free(nip->type); - free(nip); - rcm_log_message(RCM_TRACE1, "IP: secondary node - ignoring\n"); - return; - } - - if ((nilp = malloc(sizeof (struct ni_list))) == NULL) { - (void) mutex_unlock(&nil_lock); - free(nip->name); - free(nip->type); - free(nip); - rcm_log_message(RCM_ERROR, _("IP: malloc failure(%s)\n"), - strerror(errno)); - return; - } - - nilp->nifp = nip; - nilp->next = NULL; - *pp = nilp; - - num_ni++; /* Increment interface count */ - - (void) mutex_unlock(&nil_lock); - rcm_log_message(RCM_TRACE1, "IP: added new node\n"); -} /* * if_configure() - Configure a physical interface after attach */ static int -if_configure(char *ifinst) +if_configure(datalink_id_t linkid) { + char ifinst[MAXLINKNAMELEN]; char cfgfile[MAXPATHLEN]; - char ifname[LIFNAMSIZ + 1]; - char cached_name[RCM_NET_RESOURCE_MAX]; + char cached_name[RCM_LINK_RESOURCE_MAX]; struct stat statbuf; ip_cache_t *node; - char *cp; int af = 0; int ipmp = 0; - if (ifinst == NULL) - return (0); + assert(linkid != DATALINK_INVALID_LINKID); - rcm_log_message(RCM_TRACE1, "IP: if_configure(%s)\n", ifinst); - - /* - * Check if the interface is already configured - */ - - (void) memcpy(&ifname, ifinst, sizeof (ifname)); - ifname[sizeof (ifname) - 1] = '\0'; - - /* remove LIF component */ - cp = strchr(ifname, ':'); - if (cp) { - *cp = 0; - } + rcm_log_message(RCM_TRACE1, _("IP: if_configure(%u)\n"), linkid); /* Check for the interface in the cache */ - (void) snprintf(cached_name, sizeof (cached_name), "%s/%s", - RCM_NET_PREFIX, ifname); + (void) snprintf(cached_name, sizeof (cached_name), "%s/%u", + RCM_LINK_PREFIX, linkid); /* Check if the interface is new or was previously offlined */ (void) mutex_lock(&cache_lock); if (((node = cache_lookup(NULL, cached_name, CACHE_REFRESH)) != NULL) && (!(node->ip_cachestate & CACHE_IF_OFFLINED))) { rcm_log_message(RCM_TRACE1, - "IP: Skipping configured interface(%s) \n", ifname); + _("IP: Skipping configured interface(%u)\n"), linkid); (void) mutex_unlock(&cache_lock); return (0); } (void) mutex_unlock(&cache_lock); + if (dladm_datalink_id2info(linkid, NULL, NULL, NULL, ifinst, + sizeof (ifinst)) != DLADM_STATUS_OK) { + rcm_log_message(RCM_ERROR, + _("IP: get %u link name failed\n"), linkid); + return (-1); + } + /* Scan IPv4 configuration first */ (void) snprintf(cfgfile, MAXPATHLEN, "%s%s", CFGFILE_FMT_IPV4, ifinst); cfgfile[MAXPATHLEN - 1] = '\0'; @@ -3101,7 +2826,7 @@ if_ipmp_config(char *ifinst, int af, int ipmp) if (stat(cfgfile, &statb) != 0) { rcm_log_message(RCM_TRACE1, - _("IP: No config file(%s)\n"), ifinst); + "IP: No config file(%s)\n", ifinst); return (0); } @@ -3142,7 +2867,7 @@ if_ipmp_config(char *ifinst, int af, int ipmp) /* Check if config file is empty, if so, nothing else to do */ if (statb.st_size == 0) { rcm_log_message(RCM_TRACE1, - _("IP: Zero size config file(%s)\n"), ifinst); + "IP: Zero size config file(%s)\n", ifinst); return (0); } @@ -3387,7 +3112,7 @@ if_mpathd_configure(char *syscmd, char *ifinst, int af, int ipmp) if (mpathd_send_cmd(&mpdcmd) < 0) { rcm_log_message(RCM_TRACE1, - _("IP: mpathd set original index unsuccessful: %s\n"), + "IP: mpathd set original index unsuccessful: %s\n", strerror(errno)); return (-1); } @@ -3400,7 +3125,7 @@ if_mpathd_configure(char *syscmd, char *ifinst, int af, int ipmp) } /* - * get_mpathd_addr() - Return current destination for lif; caller is + * get_mpathd_dest() - Return current destination for lif; caller is * responsible to free memory allocated for address */ static char * diff --git a/usr/src/cmd/rcm_daemon/common/network_rcm.c b/usr/src/cmd/rcm_daemon/common/network_rcm.c index 040fe76a6c..5ec088e808 100644 --- a/usr/src/cmd/rcm_daemon/common/network_rcm.c +++ b/usr/src/cmd/rcm_daemon/common/network_rcm.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -41,7 +41,7 @@ #include <libdevinfo.h> #include <sys/types.h> #include <net/if.h> -#include <libdlaggr.h> +#include <libdllink.h> #include "rcm_module.h" /* @@ -56,6 +56,20 @@ #define CACHE_STALE 1 /* flags */ #define CACHE_NEW 2 /* flags */ +/* devfsadm attach nvpair values */ +#define PROP_NV_DDI_NETWORK "ddi_network" + +/* + * Global NIC list to be configured after DR-attach + */ +struct ni_list { + struct ni_list *next; + char dev[MAXNAMELEN]; /* device instance name (le0, ie0, etc.) */ +}; + +static struct ni_list *nil_head = NULL; /* Global new if list */ +static mutex_t nil_lock; /* NIC list lock */ + /* operations */ #define NET_OFFLINE 1 #define NET_ONLINE 2 @@ -66,9 +80,7 @@ typedef struct net_cache { char *resource; - char *exported; - char *driver; - int ppa; + datalink_id_t linkid; int flags; struct net_cache *next; struct net_cache *prev; @@ -77,6 +89,13 @@ typedef struct net_cache static net_cache_t cache_head; static net_cache_t cache_tail; static mutex_t cache_lock; +static int events_registered = 0; + +struct devfs_minor_data { + int32_t minor_type; + char *minor_name; + char *minor_node_type; +}; /* module interface routines */ static int net_register(rcm_handle_t *); @@ -93,6 +112,8 @@ static int net_online(rcm_handle_t *, char *, id_t, uint_t, char **, rcm_info_t **); static int net_remove(rcm_handle_t *, char *, id_t, uint_t, char **, rcm_info_t **); +static int net_notify_event(rcm_handle_t *, char *, id_t, uint_t, + char **, nvlist_t *, rcm_info_t **); /* module private routines */ static void free_cache(void); @@ -102,7 +123,9 @@ static void cache_remove(net_cache_t *node); static net_cache_t *cache_lookup(const char *resource); static void free_node(net_cache_t *); static void cache_insert(net_cache_t *); -static boolean_t is_aggregated(char *driver, int ppa); +static int notify_new_link(rcm_handle_t *, const char *); +static void process_minor(char *, int, struct devfs_minor_data *); +static int process_nvlist(rcm_handle_t *, nvlist_t *); /* * Module-Private data @@ -116,7 +139,10 @@ static struct rcm_mod_ops net_ops = { net_resume, net_offline, net_online, - net_remove + net_remove, + NULL, + NULL, + net_notify_event }; /* @@ -179,6 +205,24 @@ static int net_register(rcm_handle_t *hd) { update_cache(hd); + /* + * Need to register interest in all new resources + * getting attached, so we get attach event notifications + */ + if (!events_registered) { + if (rcm_register_event(hd, RCM_RESOURCE_NETWORK_NEW, 0, NULL) + != RCM_SUCCESS) { + rcm_log_message(RCM_ERROR, + _("NET: failed to register %s\n"), + RCM_RESOURCE_NETWORK_NEW); + return (RCM_FAILURE); + } else { + rcm_log_message(RCM_DEBUG, _("NET: registered %s\n"), + RCM_RESOURCE_NETWORK_NEW); + events_registered++; + } + } + return (RCM_SUCCESS); } @@ -207,6 +251,24 @@ net_unregister(rcm_handle_t *hd) probe = cache_head.next; } (void) mutex_unlock(&cache_lock); + + /* + * Need to unregister interest in all new resources + */ + if (events_registered) { + if (rcm_unregister_event(hd, RCM_RESOURCE_NETWORK_NEW, 0) + != RCM_SUCCESS) { + rcm_log_message(RCM_ERROR, + _("NET: failed to unregister %s\n"), + RCM_RESOURCE_NETWORK_NEW); + return (RCM_FAILURE); + } else { + rcm_log_message(RCM_DEBUG, _("NET: unregistered %s\n"), + RCM_RESOURCE_NETWORK_NEW); + events_registered--; + } + } + return (RCM_SUCCESS); } @@ -221,6 +283,8 @@ net_passthru(rcm_handle_t *hd, int op, const char *rsrc, uint_t flag, { net_cache_t *node; char *exported; + datalink_id_t linkid; + int len; int rv; /* @@ -237,13 +301,15 @@ net_passthru(rcm_handle_t *hd, int op, const char *rsrc, uint_t flag, } /* - * Since node->exported could be freed after we drop cache_lock, - * allocate a stack-local copy. We don't use strdup() because some of - * the operations (such as NET_REMOVE) are not allowed to fail. Note - * that node->exported is never more than MAXPATHLEN bytes. + * Since node could be freed after we drop cache_lock, allocate a + * stack-local copy. We don't use malloc() because some of the + * operations (such as NET_REMOVE) are not allowed to fail. Note + * that exported is never more than MAXPATHLEN bytes. */ - exported = alloca(strlen(node->exported) + 1); - (void) strlcpy(exported, node->exported, strlen(node->exported) + 1); + len = strlen("SUNW_datalink/") + LINKID_STR_WIDTH + 1; + exported = alloca(len); + linkid = node->linkid; + (void) snprintf(exported, len, "SUNW_datalink/%u", linkid); /* * Remove notifications are unconditional in the RCM state model, @@ -263,18 +329,6 @@ net_passthru(rcm_handle_t *hd, int op, const char *rsrc, uint_t flag, (timespec_t *)arg, dependent_reason); break; case NET_OFFLINE: - if (is_aggregated(node->driver, node->ppa)) { - /* device is aggregated */ - *reason = strdup(gettext( - "Resource is in use by aggregation")); - if (*reason == NULL) { - rcm_log_message(RCM_ERROR, - gettext("NET: malloc failure")); - } - errno = EBUSY; - return (RCM_FAILURE); - } - rv = rcm_request_offline(hd, exported, flag, dependent_reason); break; case NET_ONLINE: @@ -282,6 +336,19 @@ net_passthru(rcm_handle_t *hd, int op, const char *rsrc, uint_t flag, break; case NET_REMOVE: rv = rcm_notify_remove(hd, exported, flag, dependent_reason); + if (rv == RCM_SUCCESS) { + rcm_log_message(RCM_DEBUG, + _("NET: mark link %d as removed\n"), linkid); + + /* + * Delete active linkprop before this active link + * is deleted. + */ + (void) dladm_set_linkprop(linkid, NULL, NULL, 0, + DLADM_OPT_ACTIVE); + (void) dladm_destroy_datalink_id(linkid, + DLADM_OPT_ACTIVE); + } break; case NET_RESUME: rv = rcm_notify_resume(hd, exported, flag, dependent_reason); @@ -300,7 +367,6 @@ net_passthru(rcm_handle_t *hd, int op, const char *rsrc, uint_t flag, exported); rcm_log_message(RCM_WARNING, "NET: %s\n", format); } - return (rv); } @@ -321,7 +387,7 @@ net_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, assert(reason != NULL); assert(dependent_reason != NULL); - rcm_log_message(RCM_TRACE1, "NET: offline(%s)\n", rsrc); + rcm_log_message(RCM_TRACE1, _("NET: offline(%s)\n"), rsrc); return (net_passthru(hd, NET_OFFLINE, rsrc, flags, reason, dependent_reason, NULL)); @@ -340,7 +406,7 @@ net_online(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **reason, assert(rsrc != NULL); assert(id == (id_t)0); - rcm_log_message(RCM_TRACE1, "NET: online(%s)\n", rsrc); + rcm_log_message(RCM_TRACE1, _("NET: online(%s)\n"), rsrc); return (net_passthru(hd, NET_ONLINE, rsrc, flag, reason, dependent_reason, NULL)); @@ -362,8 +428,10 @@ net_getinfo(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **info, char **errstr, nvlist_t *proplist, rcm_info_t **depend_info) { int len; + dladm_status_t status; + char link[MAXLINKNAMELEN]; + char errmsg[DLADM_STRSIZE]; char *exported; - char nic[64]; const char *info_fmt; net_cache_t *node; @@ -373,7 +441,7 @@ net_getinfo(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, assert(info != NULL); assert(depend_info != NULL); - rcm_log_message(RCM_TRACE1, "NET: getinfo(%s)\n", rsrc); + rcm_log_message(RCM_TRACE1, _("NET: getinfo(%s)\n"), rsrc); info_fmt = _("Network interface %s"); @@ -386,25 +454,34 @@ net_getinfo(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, errno = ENOENT; return (RCM_FAILURE); } - exported = strdup(node->exported); - if (!exported) { - rcm_log_message(RCM_ERROR, _("NET: strdup failure")); + + len = strlen(info_fmt) + MAXLINKNAMELEN + 1; + if ((status = dladm_datalink_id2info(node->linkid, NULL, NULL, NULL, + link, sizeof (link))) != DLADM_STATUS_OK) { + rcm_log_message(RCM_ERROR, + _("NET: usage(%s) get link name failure(%s)\n"), + node->resource, dladm_status2str(status, errmsg)); (void) mutex_unlock(&cache_lock); return (RCM_FAILURE); - } - - (void) snprintf(nic, sizeof (nic), "%s%d", node->driver, node->ppa); - (void) mutex_unlock(&cache_lock); - - len = strlen(info_fmt) + strlen(nic) + 1; - if ((*info = (char *)malloc(len)) == NULL) { + } else if ((*info = (char *)malloc(len)) == NULL) { rcm_log_message(RCM_ERROR, _("NET: malloc failure")); - free(exported); + (void) mutex_unlock(&cache_lock); return (RCM_FAILURE); } /* Fill in the string */ - (void) snprintf(*info, len, info_fmt, nic); + (void) snprintf(*info, len, info_fmt, link); + + len = strlen("SUNW_datalink/") + LINKID_STR_WIDTH + 1; + exported = malloc(len); + if (!exported) { + rcm_log_message(RCM_ERROR, _("NET: allocation failure")); + free(*info); + (void) mutex_unlock(&cache_lock); + return (RCM_FAILURE); + } + (void) snprintf(exported, len, "SUNW_datalink/%u", node->linkid); + (void) mutex_unlock(&cache_lock); /* Get dependent info if requested */ if ((flag & RCM_INCLUDE_DEPENDENT) || (flag & RCM_INCLUDE_SUBTREE)) { @@ -439,7 +516,7 @@ net_suspend(rcm_handle_t *hd, char *rsrc, id_t id, timespec_t *interval, assert(reason != NULL); assert(dependent_reason != NULL); - rcm_log_message(RCM_TRACE1, "NET: suspend(%s)\n", rsrc); + rcm_log_message(RCM_TRACE1, _("NET: suspend(%s)\n"), rsrc); return (net_passthru(hd, NET_SUSPEND, rsrc, flag, reason, dependent_reason, (void *)interval)); @@ -463,7 +540,7 @@ net_resume(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **info, assert(info != NULL); assert(dependent_info != NULL); - rcm_log_message(RCM_TRACE1, "NET: resume(%s)\n", rsrc); + rcm_log_message(RCM_TRACE1, _("NET: resume(%s)\n"), rsrc); return (net_passthru(hd, NET_RESUME, rsrc, flag, info, dependent_info, NULL)); @@ -488,7 +565,7 @@ net_remove(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flag, char **info, assert(info != NULL); assert(dependent_info != NULL); - rcm_log_message(RCM_TRACE1, "NET: remove(%s)\n", rsrc); + rcm_log_message(RCM_TRACE1, _("NET: remove(%s)\n"), rsrc); return (net_passthru(hd, NET_REMOVE, rsrc, flag, info, dependent_info, NULL)); @@ -532,8 +609,6 @@ free_node(net_cache_t *node) { if (node) { free(node->resource); - free(node->exported); - free(node->driver); free(node); } } @@ -577,12 +652,12 @@ cache_remove(net_cache_t *node) static int devfs_entry(di_node_t node, di_minor_t minor, void *arg) { - char ifname [MAXPATHLEN]; /* should be big enough! */ char *devfspath; char resource[MAXPATHLEN]; - char *name; + char dev[MAXNAMELEN]; + datalink_id_t linkid; + char *drv; char *cp; - int instance; net_cache_t *probe; cp = di_minor_nodetype(minor); @@ -591,28 +666,23 @@ devfs_entry(di_node_t node, di_minor_t minor, void *arg) return (DI_WALK_CONTINUE); } - name = di_driver_name(node); - if (name == NULL) { + drv = di_driver_name(node); + if (drv == NULL) { /* what else can we do? */ return (DI_WALK_CONTINUE); } - instance = di_instance(node); - - (void) snprintf(ifname, sizeof (ifname), "SUNW_network/%s%d", - name, instance); - devfspath = di_devfs_path(node); if (!devfspath) { /* no devfs path?!? */ - rcm_log_message(RCM_DEBUG, "NET: missing devfs path\n"); + rcm_log_message(RCM_DEBUG, _("NET: missing devfs path\n")); return (DI_WALK_CONTINUE); } if (strncmp("/pseudo", devfspath, strlen("/pseudo")) == 0) { /* ignore pseudo devices, probably not really NICs */ - rcm_log_message(RCM_DEBUG, "NET: ignoring pseudo device %s\n", - devfspath); + rcm_log_message(RCM_DEBUG, + _("NET: ignoring pseudo device %s\n"), devfspath); di_devfs_path_free(devfspath); return (DI_WALK_CONTINUE); } @@ -620,14 +690,24 @@ devfs_entry(di_node_t node, di_minor_t minor, void *arg) (void) snprintf(resource, sizeof (resource), "/devices%s", devfspath); di_devfs_path_free(devfspath); + (void) snprintf(dev, sizeof (dev), "%s%d", drv, di_instance(node)); + if (dladm_dev2linkid(dev, &linkid) != DLADM_STATUS_OK) { + rcm_log_message(RCM_DEBUG, + _("NET: failed to find the linkid for %s\n"), dev); + return (DI_WALK_CONTINUE); + } + probe = cache_lookup(resource); if (probe != NULL) { - rcm_log_message(RCM_DEBUG, "NET: %s already registered\n", - resource); + rcm_log_message(RCM_DEBUG, + _("NET: %s already registered (linkid %u)\n"), + resource, linkid); + probe->linkid = linkid; probe->flags &= ~(CACHE_STALE); } else { - rcm_log_message(RCM_DEBUG, "NET: %s is new resource\n", - resource); + rcm_log_message(RCM_DEBUG, + _("NET: %s is new resource (linkid %u)\n"), + resource, linkid); probe = calloc(1, sizeof (net_cache_t)); if (!probe) { rcm_log_message(RCM_ERROR, _("NET: malloc failure")); @@ -635,12 +715,9 @@ devfs_entry(di_node_t node, di_minor_t minor, void *arg) } probe->resource = strdup(resource); - probe->ppa = instance; - probe->driver = strdup(name); - probe->exported = strdup(ifname); + probe->linkid = linkid; - if ((!probe->resource) || (!probe->exported) || - (!probe->driver)) { + if (!probe->resource) { free_node(probe); return (DI_WALK_CONTINUE); } @@ -688,7 +765,7 @@ update_cache(rcm_handle_t *hd) net_cache_t *freeit; if (probe->flags & CACHE_STALE) { (void) rcm_unregister_interest(hd, probe->resource, 0); - rcm_log_message(RCM_DEBUG, "NET: unregistered %s\n", + rcm_log_message(RCM_DEBUG, _("NET: unregistered %s\n"), probe->resource); freeit = probe; probe = probe->next; @@ -702,7 +779,7 @@ update_cache(rcm_handle_t *hd) continue; } - rcm_log_message(RCM_DEBUG, "NET: registering %s\n", + rcm_log_message(RCM_DEBUG, _("NET: registering %s\n"), probe->resource); rv = rcm_register_interest(hd, probe->resource, 0, NULL); if (rv != RCM_SUCCESS) { @@ -711,8 +788,8 @@ update_cache(rcm_handle_t *hd) probe->resource); } else { rcm_log_message(RCM_DEBUG, - "NET: registered %s (as %s)\n", - probe->resource, probe->exported); + _("NET: registered %s as SUNW_datalink/%u\n"), + probe->resource, probe->linkid); probe->flags &= ~(CACHE_NEW); } probe = probe->next; @@ -741,52 +818,282 @@ free_cache(void) } /* - * is_aggregated() checks whether a NIC being removed is part of an - * aggregation. + * net_notify_event - Project private implementation to receive new + * resource events. It intercepts all new resource + * events. If the new resource is a network resource, + * pass up a event for the resource. The new resource + * need not be cached, since it is done at register again. */ +/*ARGSUSED*/ +static int +net_notify_event(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, + char **errorp, nvlist_t *nvl, rcm_info_t **depend_info) +{ + assert(hd != NULL); + assert(rsrc != NULL); + assert(id == (id_t)0); + assert(nvl != NULL); -typedef struct aggr_walker_state_s { - uint_t naggr; - char dev_name[LIFNAMSIZ]; -} aggr_walker_state_t; + rcm_log_message(RCM_TRACE1, _("NET: notify_event(%s)\n"), rsrc); + if (strcmp(rsrc, RCM_RESOURCE_NETWORK_NEW) != 0) { + rcm_log_message(RCM_INFO, + _("NET: unrecognized event for %s\n"), rsrc); + errno = EINVAL; + return (RCM_FAILURE); + } + + /* Update cache to reflect latest physical links */ + update_cache(hd); + + /* Process the nvlist for the event */ + if (process_nvlist(hd, nvl) != 0) { + rcm_log_message(RCM_WARNING, + _("NET: Error processing resource attributes(%s)\n"), rsrc); + rcm_log_message(RCM_WARNING, + _("NET: One or more devices may not be configured.\n")); + } + + rcm_log_message(RCM_TRACE1, + _("NET: notify_event: device configuration complete\n")); + + return (RCM_SUCCESS); +} + +/* + * process_nvlist() - Determine network interfaces on a new attach by + * processing the nvlist + */ static int -aggr_walker(void *arg, dladm_aggr_grp_attr_t *grp) +process_nvlist(rcm_handle_t *hd, nvlist_t *nvl) { - aggr_walker_state_t *state = arg; - dladm_aggr_port_attr_t *port; - int i; - - for (i = 0; i < grp->lg_nports; i++) { - port = &grp->lg_ports[i]; + nvpair_t *nvp = NULL; + char *driver; + char *devfspath; + int32_t instance; + char *minor_byte_array; /* packed nvlist of minor_data */ + uint_t nminor; /* # of minor nodes */ + struct devfs_minor_data *mdata; + nvlist_t *mnvl; + nvpair_t *mnvp = NULL; + struct ni_list *nilp, *next; + + rcm_log_message(RCM_TRACE1, "NET: process_nvlist\n"); + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + /* Get driver name */ + if (strcmp(nvpair_name(nvp), RCM_NV_DRIVER_NAME) == 0) { + if (nvpair_value_string(nvp, &driver) != 0) { + rcm_log_message(RCM_WARNING, + _("NET: cannot get driver name\n")); + return (-1); + } + } + /* Get instance */ + if (strcmp(nvpair_name(nvp), RCM_NV_INSTANCE) == 0) { + if (nvpair_value_int32(nvp, &instance) != 0) { + rcm_log_message(RCM_WARNING, + _("NET: cannot get device instance\n")); + return (-1); + } + } + /* Get devfspath */ + if (strcmp(nvpair_name(nvp), RCM_NV_DEVFS_PATH) == 0) { + if (nvpair_value_string(nvp, &devfspath) != 0) { + rcm_log_message(RCM_WARNING, + _("NET: cannot get device path\n")); + return (-1); + } + if (strncmp("/pseudo", devfspath, + strlen("/pseudo")) == 0) { + /* Ignore pseudo devices, not really NICs */ + rcm_log_message(RCM_DEBUG, + _("NET: ignoring pseudo device %s\n"), + devfspath); + return (0); + } + } - rcm_log_message(RCM_TRACE1, "MAC: aggr (%d) port %s\n", - grp->lg_key, port->lp_devname); + /* Get minor data */ + if (strcmp(nvpair_name(nvp), RCM_NV_MINOR_DATA) == 0) { + if (nvpair_value_byte_array(nvp, + (uchar_t **)&minor_byte_array, &nminor) != 0) { + rcm_log_message(RCM_WARNING, + _("NET: cannot get device minor data\n")); + return (-1); + } + if (nvlist_unpack(minor_byte_array, + nminor, &mnvl, 0) != 0) { + rcm_log_message(RCM_WARNING, + _("NET: cannot get minor node data\n")); + return (-1); + } + mdata = (struct devfs_minor_data *)calloc(1, + sizeof (struct devfs_minor_data)); + if (mdata == NULL) { + rcm_log_message(RCM_WARNING, + _("NET: calloc error(%s)\n"), + strerror(errno)); + nvlist_free(mnvl); + return (-1); + } + /* Enumerate minor node data */ + while ((mnvp = nvlist_next_nvpair(mnvl, mnvp)) != + NULL) { + /* Get minor type */ + if (strcmp(nvpair_name(mnvp), + RCM_NV_MINOR_TYPE) == 0) { + if (nvpair_value_int32(mnvp, + &mdata->minor_type) != 0) { + rcm_log_message(RCM_WARNING, + _("NET: cannot get minor " + "type \n")); + nvlist_free(mnvl); + return (-1); + } + } + /* Get minor name */ + if (strcmp(nvpair_name(mnvp), + RCM_NV_MINOR_NAME) == 0) { + if (nvpair_value_string(mnvp, + &mdata->minor_name) != 0) { + rcm_log_message(RCM_WARNING, + _("NET: cannot get minor " + "name \n")); + nvlist_free(mnvl); + return (-1); + } + } + /* Get minor node type */ + if (strcmp(nvpair_name(mnvp), + RCM_NV_MINOR_NODE_TYPE) == 0) { + if (nvpair_value_string(mnvp, + &mdata->minor_node_type) != 0) { + rcm_log_message(RCM_WARNING, + _("NET: cannot get minor " + "node type \n")); + nvlist_free(mnvl); + return (-1); + } + } + } + (void) process_minor(driver, instance, mdata); + nvlist_free(mnvl); + } + } - if (strcmp(port->lp_devname, state->dev_name) != 0) - continue; + (void) mutex_lock(&nil_lock); - /* found matching MAC port */ - state->naggr++; + /* Notify the event for all new devices found, then clean up the list */ + for (nilp = nil_head; nilp != NULL; nilp = next) { + if (notify_new_link(hd, nilp->dev) != 0) { + rcm_log_message(RCM_ERROR, + _(": Notify %s event failed (%s)\n"), + RCM_RESOURCE_LINK_NEW, nilp->dev); + } + next = nilp->next; + free(nilp); } + nil_head = NULL; + + (void) mutex_unlock(&nil_lock); + rcm_log_message(RCM_TRACE1, _("NET: process_nvlist success\n")); return (0); } -static boolean_t -is_aggregated(char *driver, int ppa) +static void +process_minor(char *name, int instance, struct devfs_minor_data *mdata) { - aggr_walker_state_t state; + char dev[MAXNAMELEN]; + struct ni_list **pp; + struct ni_list *p; + + rcm_log_message(RCM_TRACE1, _("NET: process_minor %s%d\n"), + name, instance); + + if ((mdata->minor_node_type != NULL) && + strcmp(mdata->minor_node_type, PROP_NV_DDI_NETWORK) != 0) { + /* Process network devices only */ + return; + } + + (void) snprintf(dev, sizeof (dev), "%s%d", name, instance); - state.naggr = 0; - (void) snprintf(state.dev_name, sizeof (state.dev_name), "%s%d", - driver, ppa); + /* Add new interface to the list */ + (void) mutex_lock(&nil_lock); + for (pp = &nil_head; (p = *pp) != NULL; pp = &(p->next)) { + if (strcmp(dev, p->dev) == 0) + break; + } + if (p != NULL) { + rcm_log_message(RCM_TRACE1, + _("NET: secondary node - ignoring\n")); + goto done; + } - if (dladm_aggr_walk(aggr_walker, &state) != 0) { - rcm_log_message(RCM_ERROR, gettext("NET: cannot walk " - "aggregations (%s)\n"), strerror(errno)); - return (B_FALSE); + /* Add new device to the list */ + if ((p = malloc(sizeof (struct ni_list))) == NULL) { + rcm_log_message(RCM_ERROR, _("NET: malloc failure(%s)\n"), + strerror(errno)); + goto done; } + (void) strncpy(p->dev, dev, sizeof (p->dev)); + p->next = NULL; + *pp = p; + + rcm_log_message(RCM_TRACE1, _("NET: added new node %s\n"), dev); +done: + (void) mutex_unlock(&nil_lock); +} - return (state.naggr > 0); +/* + * Notify the RCM_RESOURCE_LINK_NEW event to other modules. + * Return 0 on success, -1 on failure. + */ +static int +notify_new_link(rcm_handle_t *hd, const char *dev) +{ + nvlist_t *nvl = NULL; + datalink_id_t linkid; + uint64_t id; + int ret = -1; + + rcm_log_message(RCM_TRACE1, _("NET: notify_new_link %s\n"), dev); + if (dladm_dev2linkid(dev, &linkid) != DLADM_STATUS_OK) { + rcm_log_message(RCM_TRACE1, + _("NET: new link %s has not attached yet\n"), dev); + ret = 0; + goto done; + } + + id = linkid; + if ((nvlist_alloc(&nvl, 0, 0) != 0) || + (nvlist_add_uint64(nvl, RCM_NV_LINKID, id) != 0)) { + rcm_log_message(RCM_ERROR, + _("NET: failed to construct nvlist for %s\n"), dev); + goto done; + } + + /* + * Reset the active linkprop of this specific link. + */ + (void) dladm_init_linkprop(linkid); + + rcm_log_message(RCM_TRACE1, _("NET: notify new link %u (%s)\n"), + linkid, dev); + + if (rcm_notify_event(hd, RCM_RESOURCE_LINK_NEW, 0, nvl, NULL) != + RCM_SUCCESS) { + rcm_log_message(RCM_ERROR, + _("NET: failed to notify %s event for %s\n"), + RCM_RESOURCE_LINK_NEW, dev); + goto done; + } + + ret = 0; +done: + if (nvl != NULL) + nvlist_free(nvl); + return (ret); } diff --git a/usr/src/cmd/rcm_daemon/common/vlan_rcm.c b/usr/src/cmd/rcm_daemon/common/vlan_rcm.c new file mode 100644 index 0000000000..4960040dec --- /dev/null +++ b/usr/src/cmd/rcm_daemon/common/vlan_rcm.c @@ -0,0 +1,1327 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This RCM module adds support to the RCM framework for VLAN links + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <synch.h> +#include <assert.h> +#include <strings.h> +#include "rcm_module.h" +#include <libintl.h> +#include <libdllink.h> +#include <libdlvlan.h> +#include <libdlpi.h> + +/* + * Definitions + */ +#ifndef lint +#define _(x) gettext(x) +#else +#define _(x) x +#endif + +/* Some generic well-knowns and defaults used in this module */ +#define RCM_LINK_PREFIX "SUNW_datalink" /* RCM datalink name prefix */ +#define RCM_LINK_RESOURCE_MAX (13 + LINKID_STR_WIDTH) + +/* VLAN link flags */ +typedef enum { + VLAN_OFFLINED = 0x1, + VLAN_CONSUMER_OFFLINED = 0x2, + VLAN_STALE = 0x4 +} vlan_flag_t; + +/* link representation */ +typedef struct dl_vlan { + struct dl_vlan *dv_next; /* next VLAN on the same link */ + struct dl_vlan *dv_prev; /* prev VLAN on the same link */ + datalink_id_t dv_vlanid; + boolean_t dv_implicit; + vlan_flag_t dv_flags; /* VLAN link flags */ +} dl_vlan_t; + +/* VLAN Cache state flags */ +typedef enum { + CACHE_NODE_STALE = 0x1, /* stale cached data */ + CACHE_NODE_NEW = 0x2, /* new cached nodes */ + CACHE_NODE_OFFLINED = 0x4 /* nodes offlined */ +} cache_node_state_t; + +/* Network Cache lookup options */ +#define CACHE_NO_REFRESH 0x1 /* cache refresh not needed */ +#define CACHE_REFRESH 0x2 /* refresh cache */ + +/* Cache element */ +typedef struct link_cache { + struct link_cache *vc_next; /* next cached resource */ + struct link_cache *vc_prev; /* prev cached resource */ + char *vc_resource; /* resource name */ + datalink_id_t vc_linkid; /* linkid */ + dl_vlan_t *vc_vlan; /* VLAN list on this link */ + cache_node_state_t vc_state; /* cache state flags */ +} link_cache_t; + +/* + * Global cache for network VLANs + */ +static link_cache_t cache_head; +static link_cache_t cache_tail; +static mutex_t cache_lock; +static int events_registered = 0; + +/* + * RCM module interface prototypes + */ +static int vlan_register(rcm_handle_t *); +static int vlan_unregister(rcm_handle_t *); +static int vlan_get_info(rcm_handle_t *, char *, id_t, uint_t, + char **, char **, nvlist_t *, rcm_info_t **); +static int vlan_suspend(rcm_handle_t *, char *, id_t, + timespec_t *, uint_t, char **, rcm_info_t **); +static int vlan_resume(rcm_handle_t *, char *, id_t, uint_t, + char **, rcm_info_t **); +static int vlan_offline(rcm_handle_t *, char *, id_t, uint_t, + char **, rcm_info_t **); +static int vlan_undo_offline(rcm_handle_t *, char *, id_t, uint_t, + char **, rcm_info_t **); +static int vlan_remove(rcm_handle_t *, char *, id_t, uint_t, + char **, rcm_info_t **); +static int vlan_notify_event(rcm_handle_t *, char *, id_t, uint_t, + char **, nvlist_t *, rcm_info_t **); +static int vlan_configure(rcm_handle_t *, datalink_id_t); + +/* Module private routines */ +static void cache_free(); +static int cache_update(rcm_handle_t *); +static void cache_remove(link_cache_t *); +static void node_free(link_cache_t *); +static void cache_insert(link_cache_t *); +static link_cache_t *cache_lookup(rcm_handle_t *, char *, char); +static int vlan_consumer_offline(rcm_handle_t *, link_cache_t *, + char **, uint_t, rcm_info_t **); +static void vlan_consumer_online(rcm_handle_t *, link_cache_t *, + char **, uint_t, rcm_info_t **); +static int vlan_offline_vlan(link_cache_t *, uint32_t, + cache_node_state_t); +static void vlan_online_vlan(link_cache_t *); +static char *vlan_usage(link_cache_t *); +static void vlan_log_err(datalink_id_t, char **, char *); +static int vlan_consumer_notify(rcm_handle_t *, datalink_id_t, + char **, uint_t, rcm_info_t **); + +/* Module-Private data */ +static struct rcm_mod_ops vlan_ops = +{ + RCM_MOD_OPS_VERSION, + vlan_register, + vlan_unregister, + vlan_get_info, + vlan_suspend, + vlan_resume, + vlan_offline, + vlan_undo_offline, + vlan_remove, + NULL, + NULL, + vlan_notify_event +}; + +/* + * rcm_mod_init() - Update registrations, and return the ops structure. + */ +struct rcm_mod_ops * +rcm_mod_init(void) +{ + rcm_log_message(RCM_TRACE1, "VLAN: mod_init\n"); + + cache_head.vc_next = &cache_tail; + cache_head.vc_prev = NULL; + cache_tail.vc_prev = &cache_head; + cache_tail.vc_next = NULL; + (void) mutex_init(&cache_lock, 0, NULL); + + /* Return the ops vectors */ + return (&vlan_ops); +} + +/* + * rcm_mod_info() - Return a string describing this module. + */ +const char * +rcm_mod_info(void) +{ + rcm_log_message(RCM_TRACE1, "VLAN: mod_info\n"); + + return ("VLAN module version %I%"); +} + +/* + * rcm_mod_fini() - Destroy the network VLAN cache. + */ +int +rcm_mod_fini(void) +{ + rcm_log_message(RCM_TRACE1, "VLAN: mod_fini\n"); + + /* + * Note that vlan_unregister() does not seem to be called anywhere, + * therefore we free the cache nodes here. In theory we should call + * rcm_register_interest() for each node before we free it, the + * framework does not provide the rcm_handle to allow us to do so. + */ + cache_free(); + (void) mutex_destroy(&cache_lock); + return (RCM_SUCCESS); +} + +/* + * vlan_register() - Make sure the cache is properly sync'ed, and its + * registrations are in order. + */ +static int +vlan_register(rcm_handle_t *hd) +{ + rcm_log_message(RCM_TRACE1, "VLAN: register\n"); + + if (cache_update(hd) < 0) + return (RCM_FAILURE); + + /* + * Need to register interest in all new resources + * getting attached, so we get attach event notifications + */ + if (!events_registered) { + if (rcm_register_event(hd, RCM_RESOURCE_LINK_NEW, 0, NULL) + != RCM_SUCCESS) { + rcm_log_message(RCM_ERROR, + _("VLAN: failed to register %s\n"), + RCM_RESOURCE_LINK_NEW); + return (RCM_FAILURE); + } else { + rcm_log_message(RCM_DEBUG, "VLAN: registered %s\n", + RCM_RESOURCE_LINK_NEW); + events_registered++; + } + } + + return (RCM_SUCCESS); +} + +/* + * vlan_unregister() - Walk the cache, unregistering all the networks. + */ +static int +vlan_unregister(rcm_handle_t *hd) +{ + link_cache_t *node; + + rcm_log_message(RCM_TRACE1, "VLAN: unregister\n"); + + /* Walk the cache, unregistering everything */ + (void) mutex_lock(&cache_lock); + node = cache_head.vc_next; + while (node != &cache_tail) { + if (rcm_unregister_interest(hd, node->vc_resource, 0) + != RCM_SUCCESS) { + rcm_log_message(RCM_ERROR, + _("VLAN: failed to unregister %s\n"), + node->vc_resource); + (void) mutex_unlock(&cache_lock); + return (RCM_FAILURE); + } + cache_remove(node); + node_free(node); + node = cache_head.vc_next; + } + (void) mutex_unlock(&cache_lock); + + /* + * Unregister interest in all new resources + */ + if (events_registered) { + if (rcm_unregister_event(hd, RCM_RESOURCE_LINK_NEW, 0) + != RCM_SUCCESS) { + rcm_log_message(RCM_ERROR, + _("VLAN: failed to unregister %s\n"), + RCM_RESOURCE_LINK_NEW); + return (RCM_FAILURE); + } else { + rcm_log_message(RCM_DEBUG, "VLAN: unregistered %s\n", + RCM_RESOURCE_LINK_NEW); + events_registered--; + } + } + + return (RCM_SUCCESS); +} + +/* + * vlan_offline() - Offline VLANs on a specific node. + */ +static int +vlan_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, + char **errorp, rcm_info_t **info) +{ + link_cache_t *node; + + rcm_log_message(RCM_TRACE1, "VLAN: offline(%s)\n", rsrc); + + /* Lock the cache and lookup the resource */ + (void) mutex_lock(&cache_lock); + node = cache_lookup(hd, rsrc, CACHE_REFRESH); + if (node == NULL) { + /* should not happen because the resource is registered. */ + vlan_log_err(node->vc_linkid, errorp, "unrecognized resource"); + (void) mutex_unlock(&cache_lock); + return (RCM_SUCCESS); + } + + /* + * Inform consumers (IP interfaces) of associated VLANs to be offlined + */ + if (vlan_consumer_offline(hd, node, errorp, flags, info) == + RCM_SUCCESS) { + rcm_log_message(RCM_DEBUG, + "VLAN: consumers agreed on offline\n"); + } else { + vlan_log_err(node->vc_linkid, errorp, + "consumers failed to offline"); + (void) mutex_unlock(&cache_lock); + return (RCM_FAILURE); + } + + /* Check if it's a query */ + if (flags & RCM_QUERY) { + rcm_log_message(RCM_TRACE1, + "VLAN: offline query succeeded(%s)\n", rsrc); + (void) mutex_unlock(&cache_lock); + return (RCM_SUCCESS); + } + + if (vlan_offline_vlan(node, VLAN_OFFLINED, CACHE_NODE_OFFLINED) != + RCM_SUCCESS) { + vlan_online_vlan(node); + vlan_log_err(node->vc_linkid, errorp, "offline failed"); + (void) mutex_unlock(&cache_lock); + return (RCM_FAILURE); + } + + rcm_log_message(RCM_TRACE1, "VLAN: Offline succeeded(%s)\n", rsrc); + (void) mutex_unlock(&cache_lock); + return (RCM_SUCCESS); +} + +/* + * vlan_undo_offline() - Undo offline of a previously offlined node. + */ +/*ARGSUSED*/ +static int +vlan_undo_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, + char **errorp, rcm_info_t **info) +{ + link_cache_t *node; + + rcm_log_message(RCM_TRACE1, "VLAN: online(%s)\n", rsrc); + + (void) mutex_lock(&cache_lock); + node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH); + if (node == NULL) { + vlan_log_err(DATALINK_INVALID_LINKID, errorp, "no such link"); + (void) mutex_unlock(&cache_lock); + errno = ENOENT; + return (RCM_FAILURE); + } + + /* Check if no attempt should be made to online the link here */ + if (!(node->vc_state & CACHE_NODE_OFFLINED)) { + vlan_log_err(node->vc_linkid, errorp, "link not offlined"); + (void) mutex_unlock(&cache_lock); + errno = ENOTSUP; + return (RCM_SUCCESS); + } + + vlan_online_vlan(node); + + /* + * Inform IP interfaces on associated VLANs to be onlined + */ + vlan_consumer_online(hd, node, errorp, flags, info); + + node->vc_state &= ~CACHE_NODE_OFFLINED; + rcm_log_message(RCM_TRACE1, "VLAN: online succeeded(%s)\n", rsrc); + (void) mutex_unlock(&cache_lock); + return (RCM_SUCCESS); +} + +static void +vlan_online_vlan(link_cache_t *node) +{ + dl_vlan_t *vlan; + dladm_status_t status; + char errmsg[DLADM_STRSIZE]; + + /* + * Try to bring on all offlined VLANs + */ + for (vlan = node->vc_vlan; vlan != NULL; vlan = vlan->dv_next) { + if (!(vlan->dv_flags & VLAN_OFFLINED)) + continue; + + assert(!vlan->dv_implicit); + if ((status = dladm_vlan_up(vlan->dv_vlanid)) != + DLADM_STATUS_OK) { + /* + * Print a warning message and continue to online + * other VLANs. + */ + rcm_log_message(RCM_WARNING, + _("VLAN: VLAN online failed (%u): %s\n"), + vlan->dv_vlanid, dladm_status2str(status, errmsg)); + } else { + vlan->dv_flags &= ~VLAN_OFFLINED; + } + } +} + +static int +vlan_offline_vlan(link_cache_t *node, uint32_t flags, cache_node_state_t state) +{ + dl_vlan_t *vlan; + dladm_status_t status; + char errmsg[DLADM_STRSIZE]; + + rcm_log_message(RCM_TRACE2, "VLAN: vlan_offline_vlan (%s %u %u)\n", + node->vc_resource, flags, state); + + /* + * Try to delete all explicit created VLAN + */ + for (vlan = node->vc_vlan; vlan != NULL; vlan = vlan->dv_next) { + + if (vlan->dv_implicit) + continue; + + if ((status = dladm_vlan_delete(vlan->dv_vlanid, + DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) { + rcm_log_message(RCM_WARNING, + _("VLAN: VLAN offline failed (%u): %s\n"), + vlan->dv_vlanid, dladm_status2str(status, errmsg)); + return (RCM_FAILURE); + } else { + rcm_log_message(RCM_TRACE1, + "VLAN: VLAN offline succeeded(%u)\n", + vlan->dv_vlanid); + vlan->dv_flags |= flags; + } + } + + node->vc_state |= state; + return (RCM_SUCCESS); +} + +/* + * vlan_get_info() - Gather usage information for this resource. + */ +/*ARGSUSED*/ +int +vlan_get_info(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, + char **usagep, char **errorp, nvlist_t *props, rcm_info_t **info) +{ + link_cache_t *node; + + rcm_log_message(RCM_TRACE1, "VLAN: get_info(%s)\n", rsrc); + + (void) mutex_lock(&cache_lock); + node = cache_lookup(hd, rsrc, CACHE_REFRESH); + if (node == NULL) { + rcm_log_message(RCM_INFO, + _("VLAN: get_info(%s) unrecognized resource\n"), rsrc); + (void) mutex_unlock(&cache_lock); + errno = ENOENT; + return (RCM_FAILURE); + } + + *usagep = vlan_usage(node); + (void) mutex_unlock(&cache_lock); + if (*usagep == NULL) { + /* most likely malloc failure */ + rcm_log_message(RCM_ERROR, + _("VLAN: get_info(%s) malloc failure\n"), rsrc); + (void) mutex_unlock(&cache_lock); + errno = ENOMEM; + return (RCM_FAILURE); + } + + /* Set client/role properties */ + (void) nvlist_add_string(props, RCM_CLIENT_NAME, "VLAN"); + + rcm_log_message(RCM_TRACE1, "VLAN: get_info(%s) info = %s\n", + rsrc, *usagep); + return (RCM_SUCCESS); +} + +/* + * vlan_suspend() - Nothing to do, always okay + */ +/*ARGSUSED*/ +static int +vlan_suspend(rcm_handle_t *hd, char *rsrc, id_t id, timespec_t *interval, + uint_t flags, char **errorp, rcm_info_t **info) +{ + rcm_log_message(RCM_TRACE1, "VLAN: suspend(%s)\n", rsrc); + return (RCM_SUCCESS); +} + +/* + * vlan_resume() - Nothing to do, always okay + */ +/*ARGSUSED*/ +static int +vlan_resume(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, + char **errorp, rcm_info_t **info) +{ + rcm_log_message(RCM_TRACE1, "VLAN: resume(%s)\n", rsrc); + return (RCM_SUCCESS); +} + +/* + * vlan_consumer_remove() + * + * Notify VLAN consumers to remove cache. + */ +static int +vlan_consumer_remove(rcm_handle_t *hd, link_cache_t *node, uint_t flags, + rcm_info_t **info) +{ + dl_vlan_t *vlan = NULL; + char rsrc[RCM_LINK_RESOURCE_MAX]; + int ret = RCM_SUCCESS; + + rcm_log_message(RCM_TRACE2, "VLAN: vlan_consumer_remove (%s)\n", + node->vc_resource); + + for (vlan = node->vc_vlan; vlan != NULL; vlan = vlan->dv_next) { + + /* + * This will only be called when the offline operation + * succeeds, so the VLAN consumers must have been offlined + * at this point. + */ + assert(vlan->dv_flags & VLAN_CONSUMER_OFFLINED); + + (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u", + RCM_LINK_PREFIX, vlan->dv_vlanid); + + ret = rcm_notify_remove(hd, rsrc, flags, info); + if (ret != RCM_SUCCESS) { + rcm_log_message(RCM_WARNING, + _("VLAN: notify remove failed (%s)\n"), rsrc); + break; + } + } + + rcm_log_message(RCM_TRACE2, "VLAN: vlan_consumer_remove done\n"); + return (ret); +} + +/* + * vlan_remove() - remove a resource from cache + */ +/*ARGSUSED*/ +static int +vlan_remove(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, + char **errorp, rcm_info_t **info) +{ + link_cache_t *node; + int rv; + + rcm_log_message(RCM_TRACE1, "VLAN: remove(%s)\n", rsrc); + + (void) mutex_lock(&cache_lock); + node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH); + if (node == NULL) { + rcm_log_message(RCM_INFO, + _("VLAN: remove(%s) unrecognized resource\n"), rsrc); + (void) mutex_unlock(&cache_lock); + errno = ENOENT; + return (RCM_FAILURE); + } + + /* remove the cached entry for the resource */ + cache_remove(node); + (void) mutex_unlock(&cache_lock); + + rv = vlan_consumer_remove(hd, node, flags, info); + node_free(node); + return (rv); +} + +/* + * vlan_notify_event - Project private implementation to receive new resource + * events. It intercepts all new resource events. If the + * new resource is a network resource, pass up a notify + * for it too. The new resource need not be cached, since + * it is done at register again. + */ +/*ARGSUSED*/ +static int +vlan_notify_event(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, + char **errorp, nvlist_t *nvl, rcm_info_t **info) +{ + nvpair_t *nvp = NULL; + datalink_id_t linkid; + uint64_t id64; + int rv = RCM_SUCCESS; + + rcm_log_message(RCM_TRACE1, "VLAN: notify_event(%s)\n", rsrc); + + if (strcmp(rsrc, RCM_RESOURCE_LINK_NEW) != 0) { + vlan_log_err(DATALINK_INVALID_LINKID, errorp, + "unrecognized event"); + errno = EINVAL; + return (RCM_FAILURE); + } + + /* Update cache to reflect latest VLANs */ + if (cache_update(hd) < 0) { + vlan_log_err(DATALINK_INVALID_LINKID, errorp, + "private Cache update failed"); + return (RCM_FAILURE); + } + + /* + * Try best to recover all configuration. + */ + rcm_log_message(RCM_DEBUG, "VLAN: process_nvlist\n"); + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + if (strcmp(nvpair_name(nvp), RCM_NV_LINKID) != 0) + continue; + + if (nvpair_value_uint64(nvp, &id64) != 0) { + vlan_log_err(DATALINK_INVALID_LINKID, errorp, + "cannot get linkid"); + rv = RCM_FAILURE; + continue; + } + + linkid = (datalink_id_t)id64; + if (vlan_configure(hd, linkid) != 0) { + vlan_log_err(linkid, errorp, "configuring failed"); + rv = RCM_FAILURE; + continue; + } + + /* Notify all VLAN consumers */ + if (vlan_consumer_notify(hd, linkid, errorp, flags, + info) != 0) { + vlan_log_err(linkid, errorp, "consumer notify failed"); + rv = RCM_FAILURE; + } + } + + rcm_log_message(RCM_TRACE1, + "VLAN: notify_event: link configuration complete\n"); + return (rv); +} + +/* + * vlan_usage - Determine the usage of a link. + * The returned buffer is owned by caller, and the caller + * must free it up when done. + */ +static char * +vlan_usage(link_cache_t *node) +{ + dl_vlan_t *vlan; + int nvlan; + char *buf; + const char *fmt; + char *sep; + char errmsg[DLADM_STRSIZE]; + char name[MAXLINKNAMELEN]; + dladm_status_t status; + size_t bufsz; + + rcm_log_message(RCM_TRACE2, "VLAN: usage(%s)\n", node->vc_resource); + + assert(MUTEX_HELD(&cache_lock)); + if ((status = dladm_datalink_id2info(node->vc_linkid, NULL, NULL, NULL, + name, sizeof (name))) != DLADM_STATUS_OK) { + rcm_log_message(RCM_ERROR, + _("VLAN: usage(%s) get link name failure(%s)\n"), + node->vc_resource, dladm_status2str(status, errmsg)); + return (NULL); + } + + if (node->vc_state & CACHE_NODE_OFFLINED) + fmt = _("%1$s offlined"); + else + fmt = _("%1$s VLANs: "); + + /* TRANSLATION_NOTE: separator used between VLAN linkids */ + sep = _(", "); + + nvlan = 0; + for (vlan = node->vc_vlan; vlan != NULL; vlan = vlan->dv_next) + nvlan++; + + /* space for VLANs and separators, plus message */ + bufsz = nvlan * (MAXLINKNAMELEN + strlen(sep)) + + strlen(fmt) + MAXLINKNAMELEN + 1; + if ((buf = malloc(bufsz)) == NULL) { + rcm_log_message(RCM_ERROR, + _("VLAN: usage(%s) malloc failure(%s)\n"), + node->vc_resource, strerror(errno)); + return (NULL); + } + (void) snprintf(buf, bufsz, fmt, name); + + if (node->vc_state & CACHE_NODE_OFFLINED) { + /* Nothing else to do */ + rcm_log_message(RCM_TRACE2, "VLAN: usage (%s) info = %s\n", + node->vc_resource, buf); + return (buf); + } + + for (vlan = node->vc_vlan; vlan != NULL; vlan = vlan->dv_next) { + rcm_log_message(RCM_DEBUG, "VLAN:= %u\n", vlan->dv_vlanid); + + if ((status = dladm_datalink_id2info(vlan->dv_vlanid, NULL, + NULL, NULL, name, sizeof (name))) != DLADM_STATUS_OK) { + rcm_log_message(RCM_ERROR, + _("VLAN: usage(%s) get vlan %u name failure(%s)\n"), + node->vc_resource, vlan->dv_vlanid, + dladm_status2str(status, errmsg)); + free(buf); + return (NULL); + } + + (void) strlcat(buf, name, bufsz); + if (vlan->dv_next != NULL) + (void) strlcat(buf, sep, bufsz); + } + + rcm_log_message(RCM_TRACE2, "VLAN: usage (%s) info = %s\n", + node->vc_resource, buf); + + return (buf); +} + +/* + * Cache management routines, all cache management functions should be + * be called with cache_lock held. + */ + +/* + * cache_lookup() - Get a cache node for a resource. + * Call with cache lock held. + * + * This ensures that the cache is consistent with the system state and + * returns a pointer to the cache element corresponding to the resource. + */ +static link_cache_t * +cache_lookup(rcm_handle_t *hd, char *rsrc, char options) +{ + link_cache_t *node; + + rcm_log_message(RCM_TRACE2, "VLAN: cache lookup(%s)\n", rsrc); + + assert(MUTEX_HELD(&cache_lock)); + if (options & CACHE_REFRESH) { + /* drop lock since update locks cache again */ + (void) mutex_unlock(&cache_lock); + (void) cache_update(hd); + (void) mutex_lock(&cache_lock); + } + + node = cache_head.vc_next; + for (; node != &cache_tail; node = node->vc_next) { + if (strcmp(rsrc, node->vc_resource) == 0) { + rcm_log_message(RCM_TRACE2, + "VLAN: cache lookup succeeded(%s)\n", rsrc); + return (node); + } + } + return (NULL); +} + +/* + * node_free - Free a node from the cache + */ +static void +node_free(link_cache_t *node) +{ + dl_vlan_t *vlan, *next; + + if (node != NULL) { + free(node->vc_resource); + + /* free the VLAN list */ + for (vlan = node->vc_vlan; vlan != NULL; vlan = next) { + next = vlan->dv_next; + free(vlan); + } + free(node); + } +} + +/* + * cache_insert - Insert a resource node in cache + */ +static void +cache_insert(link_cache_t *node) +{ + assert(MUTEX_HELD(&cache_lock)); + + /* insert at the head for best performance */ + node->vc_next = cache_head.vc_next; + node->vc_prev = &cache_head; + + node->vc_next->vc_prev = node; + node->vc_prev->vc_next = node; +} + +/* + * cache_remove() - Remove a resource node from cache. + */ +static void +cache_remove(link_cache_t *node) +{ + assert(MUTEX_HELD(&cache_lock)); + node->vc_next->vc_prev = node->vc_prev; + node->vc_prev->vc_next = node->vc_next; + node->vc_next = NULL; + node->vc_prev = NULL; +} + +typedef struct vlan_update_arg_s { + rcm_handle_t *hd; + int retval; +} vlan_update_arg_t; + +/* + * vlan_update() - Update physical interface properties + */ +static int +vlan_update(datalink_id_t vlanid, void *arg) +{ + vlan_update_arg_t *vlan_update_argp = arg; + rcm_handle_t *hd = vlan_update_argp->hd; + link_cache_t *node; + dl_vlan_t *vlan; + char *rsrc; + dladm_vlan_attr_t vlan_attr; + dladm_status_t status; + char errmsg[DLADM_STRSIZE]; + int ret = -1; + + rcm_log_message(RCM_TRACE2, "VLAN: vlan_update(%u)\n", vlanid); + + assert(MUTEX_HELD(&cache_lock)); + status = dladm_vlan_info(vlanid, &vlan_attr, DLADM_OPT_ACTIVE); + if (status != DLADM_STATUS_OK) { + rcm_log_message(RCM_TRACE1, + "VLAN: vlan_update() cannot get vlan information for " + "%u(%s)\n", vlanid, dladm_status2str(status, errmsg)); + return (DLADM_WALK_CONTINUE); + } + + rsrc = malloc(RCM_LINK_RESOURCE_MAX); + if (rsrc == NULL) { + rcm_log_message(RCM_ERROR, _("VLAN: malloc error(%s): %u\n"), + strerror(errno), vlanid); + goto done; + } + + (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u", + RCM_LINK_PREFIX, vlan_attr.dv_linkid); + + node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH); + if (node != NULL) { + rcm_log_message(RCM_DEBUG, + "VLAN: %s already registered (vlanid:%d)\n", + rsrc, vlan_attr.dv_vid); + free(rsrc); + } else { + rcm_log_message(RCM_DEBUG, + "VLAN: %s is a new resource (vlanid:%d)\n", + rsrc, vlan_attr.dv_vid); + if ((node = calloc(1, sizeof (link_cache_t))) == NULL) { + free(rsrc); + rcm_log_message(RCM_ERROR, _("VLAN: calloc: %s\n"), + strerror(errno)); + goto done; + } + + node->vc_resource = rsrc; + node->vc_vlan = NULL; + node->vc_linkid = vlan_attr.dv_linkid; + node->vc_state |= CACHE_NODE_NEW; + } + + for (vlan = node->vc_vlan; vlan != NULL; vlan = vlan->dv_next) { + if (vlan->dv_vlanid == vlanid) { + vlan->dv_flags &= ~VLAN_STALE; + break; + } + } + + if (vlan == NULL) { + if ((vlan = calloc(1, sizeof (dl_vlan_t))) == NULL) { + rcm_log_message(RCM_ERROR, _("VLAN: malloc: %s\n"), + strerror(errno)); + if (node->vc_state & CACHE_NODE_NEW) { + free(rsrc); + free(node); + } + goto done; + } + vlan->dv_vlanid = vlanid; + vlan->dv_next = node->vc_vlan; + vlan->dv_prev = NULL; + if (node->vc_vlan != NULL) + node->vc_vlan->dv_prev = vlan; + node->vc_vlan = vlan; + } + + vlan->dv_implicit = vlan_attr.dv_implicit; + node->vc_state &= ~CACHE_NODE_STALE; + + if (node->vc_state & CACHE_NODE_NEW) + cache_insert(node); + + rcm_log_message(RCM_TRACE3, "VLAN: vlan_update: succeeded(%u)\n", + vlanid); + ret = 0; +done: + vlan_update_argp->retval = ret; + return (ret == 0 ? DLADM_WALK_CONTINUE : DLADM_WALK_TERMINATE); +} + +/* + * vlan_update_all() - Determine all VLAN links in the system + */ +static int +vlan_update_all(rcm_handle_t *hd) +{ + vlan_update_arg_t arg = {NULL, 0}; + + rcm_log_message(RCM_TRACE2, "VLAN: vlan_update_all\n"); + + assert(MUTEX_HELD(&cache_lock)); + arg.hd = hd; + (void) dladm_walk_datalink_id(vlan_update, &arg, DATALINK_CLASS_VLAN, + DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); + return (arg.retval); +} + +/* + * cache_update() - Update cache with latest interface info + */ +static int +cache_update(rcm_handle_t *hd) +{ + link_cache_t *node, *nnode; + dl_vlan_t *vlan; + int rv; + + rcm_log_message(RCM_TRACE2, "VLAN: cache_update\n"); + + (void) mutex_lock(&cache_lock); + + /* first we walk the entire cache, marking each entry stale */ + node = cache_head.vc_next; + for (; node != &cache_tail; node = node->vc_next) { + node->vc_state |= CACHE_NODE_STALE; + for (vlan = node->vc_vlan; vlan != NULL; vlan = vlan->dv_next) + vlan->dv_flags |= VLAN_STALE; + } + + rv = vlan_update_all(hd); + + /* + * Continue to delete all stale nodes from the cache even + * vlan_update_all() failed. Unregister link that are not offlined + * and still in cache + */ + for (node = cache_head.vc_next; node != &cache_tail; node = nnode) { + dl_vlan_t *vlan, *next; + + for (vlan = node->vc_vlan; vlan != NULL; vlan = next) { + next = vlan->dv_next; + + /* clear stale VLANs */ + if (vlan->dv_flags & VLAN_STALE) { + if (vlan->dv_prev != NULL) + vlan->dv_prev->dv_next = next; + else + node->vc_vlan = next; + + if (next != NULL) + next->dv_prev = vlan->dv_prev; + free(vlan); + } + } + + nnode = node->vc_next; + if (node->vc_state & CACHE_NODE_STALE) { + (void) rcm_unregister_interest(hd, node->vc_resource, + 0); + rcm_log_message(RCM_DEBUG, "VLAN: unregistered %s\n", + node->vc_resource); + assert(node->vc_vlan == NULL); + cache_remove(node); + node_free(node); + continue; + } + + if (!(node->vc_state & CACHE_NODE_NEW)) + continue; + + if (rcm_register_interest(hd, node->vc_resource, 0, NULL) != + RCM_SUCCESS) { + rcm_log_message(RCM_ERROR, + _("VLAN: failed to register %s\n"), + node->vc_resource); + rv = -1; + } else { + rcm_log_message(RCM_DEBUG, "VLAN: registered %s\n", + node->vc_resource); + node->vc_state &= ~CACHE_NODE_NEW; + } + } + + (void) mutex_unlock(&cache_lock); + return (rv); +} + +/* + * cache_free() - Empty the cache + */ +static void +cache_free() +{ + link_cache_t *node; + + rcm_log_message(RCM_TRACE2, "VLAN: cache_free\n"); + + (void) mutex_lock(&cache_lock); + node = cache_head.vc_next; + while (node != &cache_tail) { + cache_remove(node); + node_free(node); + node = cache_head.vc_next; + } + (void) mutex_unlock(&cache_lock); +} + +/* + * vlan_log_err() - RCM error log wrapper + */ +static void +vlan_log_err(datalink_id_t linkid, char **errorp, char *errmsg) +{ + char link[MAXLINKNAMELEN]; + char errstr[DLADM_STRSIZE]; + dladm_status_t status; + int len; + const char *errfmt; + char *error; + + link[0] = '\0'; + if (linkid != DATALINK_INVALID_LINKID) { + char rsrc[RCM_LINK_RESOURCE_MAX]; + + (void) snprintf(rsrc, sizeof (rsrc), "%s/%u", + RCM_LINK_PREFIX, linkid); + + rcm_log_message(RCM_ERROR, _("VLAN: %s(%s)\n"), errmsg, rsrc); + if ((status = dladm_datalink_id2info(linkid, NULL, NULL, + NULL, link, sizeof (link))) != DLADM_STATUS_OK) { + rcm_log_message(RCM_WARNING, + _("VLAN: cannot get link name for (%s) %s\n"), + rsrc, dladm_status2str(status, errstr)); + } + } else { + rcm_log_message(RCM_ERROR, _("VLAN: %s\n"), errmsg); + } + + errfmt = strlen(link) > 0 ? _("VLAN: %s(%s)") : _("VLAN: %s"); + len = strlen(errfmt) + strlen(errmsg) + MAXLINKNAMELEN + 1; + if ((error = malloc(len)) != NULL) { + if (strlen(link) > 0) + (void) snprintf(error, len, errfmt, errmsg, link); + else + (void) snprintf(error, len, errfmt, errmsg); + } + + if (errorp != NULL) + *errorp = error; +} + +/* + * vlan_consumer_online() + * + * Notify online to VLAN consumers. + */ +/* ARGSUSED */ +static void +vlan_consumer_online(rcm_handle_t *hd, link_cache_t *node, char **errorp, + uint_t flags, rcm_info_t **info) +{ + dl_vlan_t *vlan; + char rsrc[RCM_LINK_RESOURCE_MAX]; + + rcm_log_message(RCM_TRACE2, "VLAN: vlan_consumer_online (%s)\n", + node->vc_resource); + + for (vlan = node->vc_vlan; vlan != NULL; vlan = vlan->dv_next) { + if (!(vlan->dv_flags & VLAN_CONSUMER_OFFLINED)) + continue; + + (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u", + RCM_LINK_PREFIX, vlan->dv_vlanid); + + if (rcm_notify_online(hd, rsrc, flags, info) == RCM_SUCCESS) + vlan->dv_flags &= ~VLAN_CONSUMER_OFFLINED; + } + + rcm_log_message(RCM_TRACE2, "VLAN: vlan_consumer_online done\n"); +} + +/* + * vlan_consumer_offline() + * + * Offline VLAN consumers. + */ +static int +vlan_consumer_offline(rcm_handle_t *hd, link_cache_t *node, char **errorp, + uint_t flags, rcm_info_t **info) +{ + dl_vlan_t *vlan; + char rsrc[RCM_LINK_RESOURCE_MAX]; + int ret = RCM_SUCCESS; + + rcm_log_message(RCM_TRACE2, "VLAN: vlan_consumer_offline (%s)\n", + node->vc_resource); + + for (vlan = node->vc_vlan; vlan != NULL; vlan = vlan->dv_next) { + (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u", + RCM_LINK_PREFIX, vlan->dv_vlanid); + + ret = rcm_request_offline(hd, rsrc, flags, info); + if (ret != RCM_SUCCESS) + break; + + vlan->dv_flags |= VLAN_CONSUMER_OFFLINED; + } + + if (vlan != NULL) + vlan_consumer_online(hd, node, errorp, flags, info); + + rcm_log_message(RCM_TRACE2, "VLAN: vlan_consumer_offline done\n"); + return (ret); +} + +/* + * Send RCM_RESOURCE_LINK_NEW events to other modules about new VLANs. + * Return 0 on success, -1 on failure. + */ +static int +vlan_notify_new_vlan(rcm_handle_t *hd, char *rsrc) +{ + link_cache_t *node; + dl_vlan_t *vlan; + nvlist_t *nvl = NULL; + uint64_t id; + int ret = -1; + + rcm_log_message(RCM_TRACE2, "VLAN: vlan_notify_new_vlan (%s)\n", rsrc); + + (void) mutex_lock(&cache_lock); + if ((node = cache_lookup(hd, rsrc, CACHE_REFRESH)) == NULL) { + (void) mutex_unlock(&cache_lock); + return (0); + } + + if (nvlist_alloc(&nvl, 0, 0) != 0) { + (void) mutex_unlock(&cache_lock); + rcm_log_message(RCM_WARNING, + _("VLAN: failed to allocate nvlist\n")); + goto done; + } + + for (vlan = node->vc_vlan; vlan != NULL; vlan = vlan->dv_next) { + if (!vlan->dv_implicit) { + rcm_log_message(RCM_TRACE2, + "VLAN: vlan_notify_new_vlan add (%u)\n", + vlan->dv_vlanid); + + id = vlan->dv_vlanid; + if (nvlist_add_uint64(nvl, RCM_NV_LINKID, id) != 0) { + rcm_log_message(RCM_ERROR, + _("VLAN: failed to construct nvlist\n")); + (void) mutex_unlock(&cache_lock); + goto done; + } + } + } + (void) mutex_unlock(&cache_lock); + + if (rcm_notify_event(hd, RCM_RESOURCE_LINK_NEW, 0, nvl, NULL) != + RCM_SUCCESS) { + rcm_log_message(RCM_ERROR, + _("VLAN: failed to notify %s event for %s\n"), + RCM_RESOURCE_LINK_NEW, node->vc_resource); + goto done; + } + + ret = 0; +done: + if (nvl != NULL) + nvlist_free(nvl); + return (ret); +} + +/* + * vlan_consumer_notify() - Notify consumers of VLANs coming back online. + */ +static int +vlan_consumer_notify(rcm_handle_t *hd, datalink_id_t linkid, char **errorp, + uint_t flags, rcm_info_t **info) +{ + char rsrc[RCM_LINK_RESOURCE_MAX]; + link_cache_t *node; + + /* Check for the interface in the cache */ + (void) snprintf(rsrc, RCM_LINK_RESOURCE_MAX, "%s/%u", RCM_LINK_PREFIX, + linkid); + + rcm_log_message(RCM_TRACE2, "VLAN: vlan_consumer_notify(%s)\n", rsrc); + + /* + * Inform IP consumers of the new link. + */ + if (vlan_notify_new_vlan(hd, rsrc) != 0) { + (void) mutex_lock(&cache_lock); + if ((node = cache_lookup(hd, rsrc, CACHE_NO_REFRESH)) != NULL) { + (void) vlan_offline_vlan(node, VLAN_STALE, + CACHE_NODE_STALE); + } + (void) mutex_unlock(&cache_lock); + rcm_log_message(RCM_TRACE2, + "VLAN: vlan_notify_new_vlan failed(%s)\n", rsrc); + return (-1); + } + + rcm_log_message(RCM_TRACE2, "VLAN: vlan_consumer_notify succeeded\n"); + return (0); +} + +typedef struct vlan_up_arg_s { + datalink_id_t linkid; + int retval; +} vlan_up_arg_t; + +static int +vlan_up(datalink_id_t vlanid, void *arg) +{ + vlan_up_arg_t *vlan_up_argp = arg; + dladm_status_t status; + dladm_vlan_attr_t vlan_attr; + char errmsg[DLADM_STRSIZE]; + + status = dladm_vlan_info(vlanid, &vlan_attr, DLADM_OPT_PERSIST); + if (status != DLADM_STATUS_OK) { + rcm_log_message(RCM_TRACE1, + "VLAN: vlan_up(): cannot get information for VLAN %u " + "(%s)\n", vlanid, dladm_status2str(status, errmsg)); + return (DLADM_WALK_CONTINUE); + } + + if (vlan_attr.dv_linkid != vlan_up_argp->linkid) + return (DLADM_WALK_CONTINUE); + + rcm_log_message(RCM_TRACE3, "VLAN: vlan_up(%u)\n", vlanid); + if ((status = dladm_vlan_up(vlanid)) == DLADM_STATUS_OK) + return (DLADM_WALK_CONTINUE); + + /* + * Prompt the warning message and continue to UP other VLANs. + */ + rcm_log_message(RCM_WARNING, + _("VLAN: VLAN up failed (%u): %s\n"), + vlanid, dladm_status2str(status, errmsg)); + + vlan_up_argp->retval = -1; + return (DLADM_WALK_CONTINUE); +} + +/* + * vlan_configure() - Configure VLANs over a physical link after it attaches + */ +static int +vlan_configure(rcm_handle_t *hd, datalink_id_t linkid) +{ + char rsrc[RCM_LINK_RESOURCE_MAX]; + link_cache_t *node; + vlan_up_arg_t arg = {DATALINK_INVALID_LINKID, 0}; + + /* Check for the VLANs in the cache */ + (void) snprintf(rsrc, sizeof (rsrc), "%s/%u", RCM_LINK_PREFIX, linkid); + + rcm_log_message(RCM_TRACE2, "VLAN: vlan_configure(%s)\n", rsrc); + + /* Check if the link is new or was previously offlined */ + (void) mutex_lock(&cache_lock); + if (((node = cache_lookup(hd, rsrc, CACHE_REFRESH)) != NULL) && + (!(node->vc_state & CACHE_NODE_OFFLINED))) { + rcm_log_message(RCM_TRACE2, + "VLAN: Skipping configured interface(%s)\n", rsrc); + (void) mutex_unlock(&cache_lock); + return (0); + } + (void) mutex_unlock(&cache_lock); + + arg.linkid = linkid; + (void) dladm_walk_datalink_id(vlan_up, &arg, DATALINK_CLASS_VLAN, + DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); + + if (arg.retval == 0) { + rcm_log_message(RCM_TRACE2, + "VLAN: vlan_configure succeeded(%s)\n", rsrc); + } + return (arg.retval); +} diff --git a/usr/src/cmd/rpcsvc/rstat_proc.c b/usr/src/cmd/rpcsvc/rstat_proc.c index c2a93dd933..6e347cbf07 100644 --- a/usr/src/cmd/rpcsvc/rstat_proc.c +++ b/usr/src/cmd/rpcsvc/rstat_proc.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * 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. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -975,9 +974,15 @@ init_net(void) /* * We found a device of interest. * Now, let's see if there's a kstat for it. + * First we try to query the "link" kstats in case + * the link is renamed. If that fails, fallback + * to legacy ktats for those non-GLDv3 links. */ - if ((ksp = kstat_lookup(kc, NULL, -1, namebuf)) == NULL) + if (((ksp = kstat_lookup(kc, "link", 0, namebuf)) + == NULL) && ((ksp = kstat_lookup(kc, NULL, -1, + namebuf)) == NULL)) { continue; + } if (ksp->ks_type != KSTAT_TYPE_NAMED) continue; if (kstat_read(kc, ksp, NULL) == -1) diff --git a/usr/src/cmd/svc/milestone/manifest-import b/usr/src/cmd/svc/milestone/manifest-import index d5d974fac6..363e410179 100644 --- a/usr/src/cmd/svc/milestone/manifest-import +++ b/usr/src/cmd/svc/milestone/manifest-import @@ -3,9 +3,8 @@ # CDDL HEADER START # # The contents of this file are subject to the terms of the -# Common Development and Distribution License, Version 1.0 only -# (the "License"). You may not use this file except in compliance -# with the License. +# 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. @@ -21,7 +20,7 @@ # CDDL HEADER END # # -# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -508,6 +507,18 @@ svccfg_apply /var/svc/profile/platform.xml /var/svc/profile/upgrade.app.`date +\%Y\%m\%d\%H\%M\%S` activity=true fi + + # + # Rename the datalink upgrade script file. This script is used in the + # network/physical service to upgrade datalink configuration, but + # the file cannot be renamed until now (when the file system becomes + # read-write). + # + datalink_script=/var/svc/profile/upgrade_datalink + if [ -f "${datalink_script}" ]; then + /usr/bin/mv "${datalink_script}" \ + "${datalink_script}".app.`date +\%Y\%m\%d\%H\%M\%S` + fi ) # diff --git a/usr/src/cmd/svc/milestone/net-nwam b/usr/src/cmd/svc/milestone/net-nwam index faee0c3c09..332b965aeb 100644 --- a/usr/src/cmd/svc/milestone/net-nwam +++ b/usr/src/cmd/svc/milestone/net-nwam @@ -20,13 +20,14 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" . /lib/svc/share/smf_include.sh +. /lib/svc/share/net_include.sh # # In a shared-IP zone we need this service to be up, but all of the work @@ -43,6 +44,20 @@ case "$1" in 'start') if smf_is_globalzone; then + net_reconfigure || exit $SMF_EXIT_ERR_CONFIG + + # + # Upgrade handling. The upgrade file consists of a series + # of dladm(1M) commands. Note that after we are done, we + # cannot rename the upgrade script file as the file system + # is still read-only at this point. Defer this to the + # manifest-import service. + # + upgrade_script=/var/svc/profile/upgrade_datalink + if [ -f "${upgrade_script}" ]; then + . "${upgrade_script}" + fi + # Initialize security objects. /sbin/dladm init-secobj fi diff --git a/usr/src/cmd/svc/milestone/net-physical b/usr/src/cmd/svc/milestone/net-physical index 19787a6945..c562d22902 100644 --- a/usr/src/cmd/svc/milestone/net-physical +++ b/usr/src/cmd/svc/milestone/net-physical @@ -20,7 +20,7 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T. @@ -59,6 +59,19 @@ SUNW_NO_MPATHD=; export SUNW_NO_MPATHD smf_netstrategy if smf_is_globalzone; then + net_reconfigure || exit $SMF_EXIT_ERR_CONFIG + + # + # Upgrade handling. The upgrade file consists of a series of dladm(1M) + # commands. Note that after we are done, we cannot rename the upgrade + # script file as the file system is still read-only at this point. + # Defer this to the manifest-import service. + # + upgrade_script=/var/svc/profile/upgrade_datalink + if [ -f "${upgrade_script}" ]; then + . "${upgrade_script}" + fi + # # Bring up link aggregations and initialize security objects. # Note that link property initialization is deferred until after @@ -66,6 +79,7 @@ if smf_is_globalzone; then # be unloaded (and the property settings lost). # /sbin/dladm up-aggr + /sbin/dladm up-vlan /sbin/dladm init-secobj fi diff --git a/usr/src/cmd/svc/seed/Makefile b/usr/src/cmd/svc/seed/Makefile index 37da95d671..807217466b 100644 --- a/usr/src/cmd/svc/seed/Makefile +++ b/usr/src/cmd/svc/seed/Makefile @@ -18,7 +18,7 @@ # # CDDL HEADER END # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -65,6 +65,7 @@ COMMON_DESCRIPTIONS = \ ../milestone/root-fs.xml \ ../milestone/single-user.xml \ ../milestone/usr-fs.xml \ + ../../dlmgmtd/dlmgmt.xml \ ../../rpcbind/bind.xml \ # diff --git a/usr/src/cmd/svc/seed/inc.flg b/usr/src/cmd/svc/seed/inc.flg index 8224abb7f3..37575fd93e 100644 --- a/usr/src/cmd/svc/seed/inc.flg +++ b/usr/src/cmd/svc/seed/inc.flg @@ -20,7 +20,7 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -28,6 +28,7 @@ echo_file usr/src/cmd/cmd-inet/usr.lib/inetd/inetd-upgrade.xml echo_file usr/src/cmd/rpcbind/bind.xml +echo_file usr/src/cmd/dlmgmtd/dlmgmt.xml echo_file usr/src/cmd/utmpd/utmp.xml echo_file usr/src/cmd/lvm/util/metainit.xml echo_file usr/src/cmd/lvm/md_monitord/mdmonitor.xml diff --git a/usr/src/cmd/svc/shell/net_include.sh b/usr/src/cmd/svc/shell/net_include.sh index 4af6b40d30..989a780384 100644 --- a/usr/src/cmd/svc/shell/net_include.sh +++ b/usr/src/cmd/svc/shell/net_include.sh @@ -20,7 +20,7 @@ # CDDL HEADER END # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" @@ -580,3 +580,65 @@ move_addresses() done echo "." } + +# +# net_reconfigure is called from the network/physical service (by the +# net-physical and net-nwam method scripts) to perform tasks that only +# need to be done during a reconfigure boot. This needs to be +# isolated in a function since network/physical has two instances +# (default and nwam) that have distinct method scripts that each need +# to do these things. +# +net_reconfigure () +{ + # + # Is this a reconfigure boot? If not, then there's nothing + # for us to do. + # + svcprop -q -p system/reconfigure system/svc/restarter:default + if [ $? -ne 0 ]; then + return 0 + fi + + # + # Ensure that the datalink-management service is running since + # manifest-import has not yet run for a first boot after + # upgrade. We wouldn't need to do that if manifest-import ran + # earlier in boot, since there is an explicit dependency + # between datalink-management and network/physical. + # + svcadm enable -ts network/datalink-management:default + + # + # There is a bug in SMF which causes the svcadm command above + # to exit prematurely (with an error code of 3) before having + # waited for the service to come online after having enabled + # it. Until that bug is fixed, we need to have the following + # loop to explicitly wait for the service to come online. + # + i=0 + while [ $i -lt 30 ]; do + i=`expr $i + 1` + sleep 1 + state=`svcprop -p restarter/state \ + network/datalink-management:default 2>/dev/null` + if [ $? -ne 0 ]; then + continue + elif [ "$state" = "online" ]; then + break + fi + done + if [ "$state" != "online" ]; then + echo "The network/datalink-management service \c" + echo "did not come online." + return 1 + fi + + # + # Initialize the set of physical links, and validate and + # remove all the physical links which were removed during the + # system shutdown. + # + /sbin/dladm init-phys + return 0 +} diff --git a/usr/src/cmd/truss/codes.c b/usr/src/cmd/truss/codes.c index b3c3d23352..f12895b7ed 100644 --- a/usr/src/cmd/truss/codes.c +++ b/usr/src/cmd/truss/codes.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -839,8 +839,22 @@ const struct ioc { { (uint_t)LAIOC_MODIFY, "LAIOC_MODIFY", "laioc_modify"}, /* dld data-link ioctls */ - { (uint_t)DLDIOCATTR, "DLDIOCATTR", "dld_ioc_attr"}, - { (uint_t)DLDIOCVLAN, "DLDIOCVLAN", "dld_ioc_vlan"}, + { (uint_t)DLDIOC_ATTR, "DLDIOC_ATTR", "dld_ioc_attr"}, + { (uint_t)DLDIOC_PHYS_ATTR, "DLDIOC_PHYS_ATTR", + "dld_ioc_phys_attr"}, + { (uint_t)DLDIOC_VLAN_ATTR, "DLDIOC_VLAN_ATTR", + "dld_ioc_vlan_attr"}, + { (uint_t)DLDIOC_CREATE_VLAN, "DLDIOC_CREATE_VLAN", + "dld_ioc_create_vlan"}, + { (uint_t)DLDIOC_DELETE_VLAN, "DLDIOC_DELETE_VLAN", + "dld_ioc_delete_vlan"}, + { (uint_t)DLDIOC_SETAUTOPUSH, "DLDIOC_SETAUTOPUSH", "dld_ioc_ap"}, + { (uint_t)DLDIOC_GETAUTOPUSH, "DLDIOC_GETAUTOPUSH", "dld_ioc_ap"}, + { (uint_t)DLDIOC_CLRAUTOPUSH, "DLDIOC_CLRAUTOPUSH", "dld_ioc_ap"}, + { (uint_t)DLDIOC_DOORSERVER, "DLDIOC_DOORSERVER", "dld_ioc_door"}, + { (uint_t)DLDIOC_RENAME, "DLDIOC_RENAME", "dld_ioc_rename"}, + { (uint_t)DLDIOC_SETZID, "DLDIOC_SETZID", "dld_ioc_setzid"}, + { (uint_t)DLDIOC_GETZID, "DLDIOC_GETZID", "dld_ioc_getzid"}, /* ZFS ioctls */ { (uint_t)ZFS_IOC_POOL_CREATE, "ZFS_IOC_POOL_CREATE", diff --git a/usr/src/cmd/vna/vna.c b/usr/src/cmd/vna/vna.c index cf13fd1140..c0bb80fd0a 100644 --- a/usr/src/cmd/vna/vna.c +++ b/usr/src/cmd/vna/vna.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -37,125 +37,162 @@ #include <strings.h> #include <sys/types.h> #include <sys/ethernet.h> +#include <libdllink.h> #include <libdlvnic.h> /*ARGSUSED*/ -static dladm_status_t -v_print(void *arg, dladm_vnic_attr_sys_t *attr) +static int +v_print(datalink_id_t vnic_id, void *arg) { - if (attr->va_mac_len != ETHERADDRL) - return (DLADM_STATUS_OK); + dladm_vnic_attr_sys_t attr; + char vnic[MAXLINKNAMELEN]; + char link[MAXLINKNAMELEN]; - (void) printf("%d\t%s\t%s\n", attr->va_vnic_id, attr->va_dev_name, - ether_ntoa((struct ether_addr *)(attr->va_mac_addr))); + if (dladm_vnic_info(vnic_id, &attr, DLADM_OPT_ACTIVE) != + DLADM_STATUS_OK) { + return (DLADM_WALK_CONTINUE); + } - return (DLADM_STATUS_OK); -} + if (attr.va_mac_len != ETHERADDRL) + return (DLADM_WALK_CONTINUE); -static int -v_list(void) -{ - dladm_status_t status; + if (dladm_datalink_id2info(vnic_id, NULL, NULL, NULL, vnic, + sizeof (vnic)) != DLADM_STATUS_OK) { + return (DLADM_WALK_CONTINUE); + } + + if (dladm_datalink_id2info(attr.va_link_id, NULL, NULL, NULL, link, + sizeof (link)) != DLADM_STATUS_OK) { + return (DLADM_WALK_CONTINUE); + } - status = dladm_vnic_walk_sys(v_print, NULL); + (void) printf("%s\t%s\t%s\n", vnic, link, + ether_ntoa((struct ether_addr *)(attr.va_mac_addr))); - if (status != DLADM_STATUS_OK) - return (-1); + return (DLADM_WALK_CONTINUE); +} - return (0); +static void +v_list(void) +{ + (void) dladm_walk_datalink_id(v_print, NULL, DATALINK_CLASS_VNIC, + DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); } -static dladm_status_t -v_find(void *arg, dladm_vnic_attr_sys_t *attr) +static int +v_find(datalink_id_t vnic_id, void *arg) { dladm_vnic_attr_sys_t *specp = arg; + dladm_vnic_attr_sys_t attr; - if (strncmp(attr->va_dev_name, specp->va_dev_name, - strlen(attr->va_dev_name)) != 0) - return (DLADM_STATUS_OK); + if (dladm_vnic_info(vnic_id, &attr, DLADM_OPT_ACTIVE) != + DLADM_STATUS_OK) { + return (DLADM_WALK_CONTINUE); + } - if (attr->va_mac_len != specp->va_mac_len) - return (DLADM_STATUS_OK); + if (attr.va_link_id != specp->va_link_id) + return (DLADM_WALK_CONTINUE); - if (memcmp(attr->va_mac_addr, specp->va_mac_addr, - attr->va_mac_len) != 0) - return (DLADM_STATUS_OK); + if (attr.va_mac_len != specp->va_mac_len) + return (DLADM_WALK_CONTINUE); - specp->va_vnic_id = attr->va_vnic_id; + if (memcmp(attr.va_mac_addr, specp->va_mac_addr, + attr.va_mac_len) != 0) { + return (DLADM_WALK_CONTINUE); + } - return (DLADM_STATUS_EXIST); + specp->va_vnic_id = attr.va_vnic_id; + + return (DLADM_WALK_TERMINATE); } +/* + * Print out the link name of the VNIC. + */ static int -v_add(char *dev, char *addr) +v_add(char *link, char *addr) { struct ether_addr *ea; dladm_vnic_attr_sys_t spec; + datalink_id_t vnic_id, linkid; + char vnic[MAXLINKNAMELEN]; dladm_status_t status; - uint_t vid; char buf[DLADM_STRSIZE]; ea = ether_aton(addr); if (ea == NULL) { - (void) fprintf(stderr, "Invalid ethernet address: %s\n", - addr); + (void) fprintf(stderr, "Invalid ethernet address: %s\n", addr); + return (-1); + } + + if (dladm_name2info(link, &linkid, NULL, NULL, NULL) != + DLADM_STATUS_OK) { + (void) fprintf(stderr, "Invalid link name: %s\n", link); return (-1); } /* - * If a VNIC already exists over the specified device + * If a VNIC already exists over the specified link * with this MAC address, use it. */ - (void) strncpy(spec.va_dev_name, dev, sizeof (spec.va_dev_name) - 1); + spec.va_vnic_id = DATALINK_INVALID_LINKID; + spec.va_link_id = linkid; spec.va_mac_len = ETHERADDRL; (void) memcpy(spec.va_mac_addr, (uchar_t *)ea->ether_addr_octet, spec.va_mac_len); - status = dladm_vnic_walk_sys(v_find, &spec); - switch (status) { - case DLADM_STATUS_EXIST: - vid = spec.va_vnic_id; - break; - - case DLADM_STATUS_OK: + (void) dladm_walk_datalink_id(v_find, &spec, DATALINK_CLASS_VNIC, + DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); + if (spec.va_vnic_id == DATALINK_INVALID_LINKID) { /* * None found, so create. */ - status = dladm_vnic_create(0, dev, VNIC_MAC_ADDR_TYPE_FIXED, - (uchar_t *)ea->ether_addr_octet, ETHERADDRL, - &vid, DLADM_VNIC_OPT_TEMP | DLADM_VNIC_OPT_AUTOID); + status = dladm_vnic_create(NULL, linkid, + VNIC_MAC_ADDR_TYPE_FIXED, (uchar_t *)ea->ether_addr_octet, + ETHERADDRL, &vnic_id, DLADM_OPT_ACTIVE); if (status != DLADM_STATUS_OK) { (void) fprintf(stderr, "dladm_vnic_create: %s\n", dladm_status2str(status, buf)); return (-1); } - break; + } else { + vnic_id = spec.va_vnic_id; + } - default: - (void) fprintf(stderr, "dladm_vnic_walk_sys: %s\n", + if ((status = dladm_datalink_id2info(vnic_id, NULL, NULL, NULL, vnic, + sizeof (vnic))) != DLADM_STATUS_OK) { + (void) fprintf(stderr, "dladm_datalink_id2info: %s\n", dladm_status2str(status, buf)); + if (spec.va_vnic_id == DATALINK_INVALID_LINKID) + (void) dladm_vnic_delete(vnic_id, DLADM_OPT_ACTIVE); return (-1); - /* NOTREACHED */ } - (void) printf("%d\n", vid); + (void) printf("%s\n", vnic); return (0); } +/* + * v_remove() takes VNIC link name as the argument. + */ static int -v_remove(char *vdev) +v_remove(char *vnic) { - uint_t vid; + datalink_id_t vnic_id; dladm_status_t status; + char buf[DLADM_STRSIZE]; - vid = atoi(vdev); + if ((status = dladm_name2info(vnic, &vnic_id, NULL, NULL, NULL)) != + DLADM_STATUS_OK) { + (void) fprintf(stderr, "dladm_name2info: %s\n", + dladm_status2str(status, buf)); + return (-1); + } - status = dladm_vnic_delete(vid, DLADM_VNIC_OPT_TEMP); + status = dladm_vnic_delete(vnic_id, DLADM_OPT_ACTIVE); if (status != DLADM_STATUS_OK) { - char buf[DLADM_STRSIZE]; - (void) fprintf(stderr, "dladm_vnic_delete: %s\n", dladm_status2str(status, buf)); return (-1); @@ -170,7 +207,8 @@ main(int argc, char *argv[]) switch (argc) { case 1: /* List operation. */ - return (v_list()); + v_list(); + return (0); /* NOTREACHED */ case 2: /* Remove operation. */ diff --git a/usr/src/cmd/zoneadmd/Makefile b/usr/src/cmd/zoneadmd/Makefile index 1b83c9b7e7..8f867e06ea 100644 --- a/usr/src/cmd/zoneadmd/Makefile +++ b/usr/src/cmd/zoneadmd/Makefile @@ -22,7 +22,7 @@ # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "%Z%%M% %I% %E% SMI" @@ -43,7 +43,7 @@ CFLAGS += $(CCVERBOSE) LAZYLIBS = $(ZLAZYLOAD) -ltsnet -ltsol $(ZNOLAZYLOAD) lint := LAZYLIBS = -ltsnet -ltsol LDLIBS += -lsocket -lzonecfg -lnsl -ldevinfo -ldevice -lnvpair \ - -lgen -lbsm -lcontract -lzfs -luuid -lbrand -ldladm -ldlpi $(LAZYLIBS) + -lgen -lbsm -lcontract -lzfs -luuid -lbrand -ldladm $(LAZYLIBS) XGETFLAGS += -a -x zoneadmd.xcl .KEEP_STATE: diff --git a/usr/src/cmd/zoneadmd/vplat.c b/usr/src/cmd/zoneadmd/vplat.c index 7e5941e02a..59225164a6 100644 --- a/usr/src/cmd/zoneadmd/vplat.c +++ b/usr/src/cmd/zoneadmd/vplat.c @@ -77,6 +77,7 @@ #include <sys/dlpi.h> #include <libdlpi.h> #include <libdllink.h> +#include <libdlvlan.h> #include <inet/tcp.h> #include <arpa/inet.h> @@ -122,8 +123,6 @@ #include <libtsnet.h> #include <sys/priv.h> -#include <sys/dld.h> /* DLIOCHOLDVLAN and friends */ - #define V4_ADDR_LEN 32 #define V6_ADDR_LEN 128 @@ -148,8 +147,6 @@ static struct mnttab *resolve_lofs_mnts, *resolve_lofs_mnt_max; static tsol_zcent_t *get_zone_label(zlog_t *, priv_set_t *); static int tsol_mounts(zlog_t *, char *, char *); static void tsol_unmounts(zlog_t *, char *); -static int driver_hold_link(const char *name, zoneid_t zoneid); -static int driver_rele_link(const char *name, zoneid_t zoneid); static m_label_t *zlabel = NULL; static m_label_t *zid_label = NULL; @@ -2609,35 +2606,10 @@ add_datalink(zlog_t *zlogp, zoneid_t zoneid, char *dlname) return (-1); } - /* Hold the link for this zone */ - if (dladm_hold_link(dlname, zoneid, B_FALSE) < 0) { - int res, old_errno; - dladm_attr_t da; - - /* - * The following check is similar to 'dladm show-link' - * to determine if this is a legacy interface. - */ - old_errno = errno; - res = dladm_info(dlname, &da); - if (res < 0 && errno == ENODEV) { - /* - * Check if this is a link like 'ce*' which supports - * a direct ioctl. - */ - res = driver_hold_link(dlname, zoneid); - if (res == 0) - return (0); - - zerror(zlogp, B_FALSE, "WARNING: legacy network " - "interface '%s'\nunsupported with an " - "ip-type=exclusive configuration.", dlname); - } else { - errno = old_errno; - zerror(zlogp, B_TRUE, "WARNING: unable to hold network " - "interface '%s'.", dlname); - } - + /* Set zoneid of this link. */ + if (dladm_setzid(dlname, zoneid) != DLADM_STATUS_OK) { + zerror(zlogp, B_TRUE, "WARNING: unable to add network " + "interface '%s'.", dlname); (void) zone_remove_datalink(zoneid, dlname); return (-1); } @@ -2661,13 +2633,10 @@ remove_datalink(zlog_t *zlogp, zoneid_t zoneid, char *dlname) return (-1); } - if (dladm_rele_link(dlname, 0, B_FALSE) < 0) { - /* Fallback to 'ce*' type link */ - if (driver_rele_link(dlname, 0) < 0) { - zerror(zlogp, B_TRUE, "unable to release network " - "interface '%s'", dlname); - return (-1); - } + if (dladm_setzid(dlname, GLOBAL_ZONEID) != DLADM_STATUS_OK) { + zerror(zlogp, B_TRUE, "unable to release network " + "interface '%s'", dlname); + return (-1); } return (0); } @@ -2733,21 +2702,34 @@ configure_exclusive_network_interfaces(zlog_t *zlogp) } /* + * Create the /dev entry for backward compatibility. * Only create the /dev entry if it's not in use. - * Note here the zone still boots when the interfaces - * assigned is inaccessible, used by others, etc. + * Note that the zone still boots when the assigned + * interface is inaccessible, used by others, etc. + * Also, when vanity naming is used, some interface do + * do not have corresponding /dev node names (for example, + * vanity named aggregations). The /dev entry is not + * created in that case. The /dev/net entry is always + * accessible. */ if (add_datalink(zlogp, zoneid, nwiftab.zone_nwif_physical) == 0) { - if (di_prof_add_dev(prof, nwiftab.zone_nwif_physical) - != 0) { - (void) zonecfg_endnwifent(handle); - zonecfg_fini_handle(handle); - zerror(zlogp, B_TRUE, - "failed to add network device"); - return (-1); + char name[DLPI_LINKNAME_MAX]; + datalink_id_t linkid; + + if (dladm_name2info(nwiftab.zone_nwif_physical, + &linkid, NULL, NULL, NULL) == DLADM_STATUS_OK && + dladm_linkid2legacyname(linkid, name, + sizeof (name)) == DLADM_STATUS_OK) { + if (di_prof_add_dev(prof, name) != 0) { + (void) zonecfg_endnwifent(handle); + zonecfg_fini_handle(handle); + zerror(zlogp, B_TRUE, + "failed to add network device"); + return (-1); + } + added = B_TRUE; } - added = B_TRUE; } } (void) zonecfg_endnwifent(handle); @@ -4679,71 +4661,3 @@ error: lofs_discard_mnttab(); return (-1); } - -/* - * Common routine for driver_hold_link and driver_rele_link. - * It invokes ioctl for a link like "ce*", for which the driver has been - * enhanced to support DLDIOC{HOLD,RELE}VLAN. - */ -static int -driver_vlan_ioctl(const char *name, zoneid_t zoneid, int cmd) -{ - int fd; - uint_t ppa; - dld_hold_vlan_t dhv; - struct strioctl istr; - char providername[IFNAMSIZ]; - char path[MAXPATHLEN]; - - if (strlen(name) >= IFNAMSIZ) { - errno = EINVAL; - return (-1); - } - - if (dlpi_parselink(name, providername, &ppa) != DLPI_SUCCESS) { - errno = EINVAL; - return (-1); - } - - (void) snprintf(path, sizeof (path), "/dev/%s", providername); - fd = open(path, O_RDWR); - if (fd < 0) - return (-1); - bzero(&dhv, sizeof (dld_hold_vlan_t)); - (void) strlcpy(dhv.dhv_name, name, IFNAMSIZ); - dhv.dhv_zid = zoneid; - dhv.dhv_docheck = B_FALSE; - - istr.ic_cmd = cmd; - istr.ic_len = sizeof (dhv); - istr.ic_dp = (void *)&dhv; - istr.ic_timout = 0; - - if (ioctl(fd, I_STR, &istr) < 0) { - int olderrno = errno; - (void) close(fd); - errno = olderrno; - return (-1); - } - (void) close(fd); - return (0); -} - -/* - * Hold a data-link where the style-2 datalink driver supports DLDIOCHOLDVLAN. - */ -static int -driver_hold_link(const char *name, zoneid_t zoneid) -{ - return (driver_vlan_ioctl(name, zoneid, DLDIOCHOLDVLAN)); -} - -/* - * Release a data-link where the style-2 datalink driver supports - * DLDIOC{HOLD,RELE}VLAN. - */ -static int -driver_rele_link(const char *name, zoneid_t zoneid) -{ - return (driver_vlan_ioctl(name, zoneid, DLDIOCRELEVLAN)); -} |