1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Copyright 2015 Garrett D'Amore <garrett@damore.org> 26 */ 27 28 #include <stdlib.h> 29 #include <string.h> 30 #include <strings.h> 31 #include <sys/types.h> 32 #include <libdladm_impl.h> 33 #include <libdllink.h> 34 #include <libdlstat.h> 35 #include <libdlether.h> 36 37 /* 38 * Ethernet administration library. 39 */ 40 41 /* 42 * kstat names for extracting attributes. 43 */ 44 typedef struct ether_spdx_s { 45 dladm_ether_spdx_t eth_spdx; 46 char *eth_spdx_stat_name; 47 } ether_spdx_t; 48 49 static ether_spdx_t cap_spdx[] = { 50 {{1000, LINK_DUPLEX_FULL}, "cap_1000fdx"}, 51 {{1000, LINK_DUPLEX_HALF}, "cap_1000hdx"}, 52 {{100, LINK_DUPLEX_FULL}, "cap_100fdx"}, 53 {{100, LINK_DUPLEX_HALF}, "cap_100hdx"}, 54 {{10, LINK_DUPLEX_FULL}, "cap_10fdx"}, 55 {{10, LINK_DUPLEX_HALF}, "cap_10hdx"}, 56 {{0, LINK_DUPLEX_UNKNOWN}, NULL} 57 }; 58 59 static ether_spdx_t adv_cap_spdx[] = { 60 {{1000, LINK_DUPLEX_FULL}, "adv_cap_1000fdx"}, 61 {{1000, LINK_DUPLEX_HALF}, "adv_cap_1000hdx"}, 62 {{100, LINK_DUPLEX_FULL}, "adv_cap_100fdx"}, 63 {{100, LINK_DUPLEX_HALF}, "adv_cap_100hdx"}, 64 {{10, LINK_DUPLEX_FULL}, "adv_cap_10fdx"}, 65 {{10, LINK_DUPLEX_HALF}, "adv_cap_10hdx"}, 66 {{0, LINK_DUPLEX_UNKNOWN}, NULL} 67 }; 68 69 static ether_spdx_t lp_cap_spdx[] = { 70 {{1000, LINK_DUPLEX_FULL}, "lp_cap_1000fdx"}, 71 {{1000, LINK_DUPLEX_HALF}, "lp_cap_1000hdx"}, 72 {{100, LINK_DUPLEX_FULL}, "lp_cap_100fdx"}, 73 {{100, LINK_DUPLEX_HALF}, "lp_cap_100hdx"}, 74 {{10, LINK_DUPLEX_FULL}, "lp_cap_10fdx"}, 75 {{10, LINK_DUPLEX_HALF}, "lp_cap_10hdx"}, 76 {{0, LINK_DUPLEX_UNKNOWN}, NULL} 77 }; 78 79 typedef struct attr_kstat_s { 80 char *autoneg_stat; 81 char *pause_stat; 82 char *asmpause_stat; 83 char *fault_stat; 84 ether_spdx_t *spdx_stat; 85 } attr_kstat_t; 86 87 static attr_kstat_t attrstat[] = { 88 {"link_autoneg", /* current */ 89 "link_pause", "link_asmpause", NULL, 90 NULL}, 91 92 {"cap_autoneg", /* capable */ 93 "cap_pause", "cap_asmpause", "cap_rem_fault", 94 cap_spdx}, 95 96 {"adv_cap_autoneg", /* advertised */ 97 "adv_cap_pause", "adv_cap_asmpause", "adv_rem_fault", 98 adv_cap_spdx}, 99 100 {"lp_cap_autoneg", /* peer advertised */ 101 "lp_cap_pause", "lp_cap_asmpause", "lp_rem_fault", 102 lp_cap_spdx} 103 }; 104 105 /* 106 * Get the speed-duplex stats specified in the ether_spdx_t table passed in 107 * by querying the appropriate kstat for each entry in the table. 108 */ 109 static dladm_status_t 110 i_dladm_get_spdx(dladm_handle_t handle, datalink_id_t linkid, 111 dladm_ether_attr_t *eattr, ether_spdx_t *spdx_stat) 112 { 113 int i, nspdx = 0; 114 uint32_t speed; 115 dladm_status_t status; 116 void *ptr; 117 118 eattr->le_spdx = NULL; 119 for (i = 0; spdx_stat[i].eth_spdx_stat_name != NULL; i++) { 120 if ((status = dladm_get_single_mac_stat(handle, linkid, 121 spdx_stat[i].eth_spdx_stat_name, 122 KSTAT_DATA_UINT32, &speed)) != DLADM_STATUS_OK) { 123 124 if (status == DLADM_STATUS_NOTFOUND) { 125 /* 126 * Missing statistic. 127 * Skip this one and try the rest. 128 */ 129 continue; 130 } else { 131 free(eattr->le_spdx); 132 eattr->le_num_spdx = 0; 133 return (status); 134 } 135 } 136 if (speed == 0) 137 continue; 138 nspdx++; 139 ptr = realloc(eattr->le_spdx, 140 nspdx * sizeof (dladm_ether_spdx_t)); 141 if (ptr != NULL) { 142 eattr->le_spdx = ptr; 143 } else { 144 free(eattr->le_spdx); 145 eattr->le_num_spdx = 0; 146 return (DLADM_STATUS_NOMEM); 147 } 148 eattr->le_spdx[nspdx - 1] = spdx_stat[i].eth_spdx; 149 } 150 eattr->le_num_spdx = nspdx; 151 return (DLADM_STATUS_OK); 152 } 153 154 /* 155 * Returns "yes" or "no" based on the autonegotion capabilities 156 * for the parameter type indicated by ptype. The permissible 157 * values for ptype are CURRENT, CAPABLE, ADV, PEERADV. 158 */ 159 char * 160 dladm_ether_autoneg2str(char *buf, size_t buflen, dladm_ether_info_t *eattr, 161 int ptype) 162 { 163 boolean_t autoneg = eattr->lei_attr[ptype].le_autoneg; 164 165 (void) strlcpy(buf, (autoneg ? "yes" : "no"), buflen); 166 return (buf); 167 } 168 169 /* 170 * Returns {"bi", "tx", "none"} based on the flow-control capabilities 171 * for the parameter type indicated by ptype. The permissible 172 * values for ptype are CURRENT, CAPABLE, ADV, PEERADV. 173 */ 174 char * 175 dladm_ether_pause2str(char *buf, size_t buflen, dladm_ether_info_t *eattr, 176 int ptype) 177 { 178 boolean_t pause = eattr->lei_attr[ptype].le_pause; 179 boolean_t asmpause = eattr->lei_attr[ptype].le_asmpause; 180 181 if (pause) 182 (void) strlcpy(buf, "bi", buflen); 183 else if (asmpause) 184 (void) strlcpy(buf, "tx", buflen); 185 else 186 (void) strlcpy(buf, "none", buflen); 187 return (buf); 188 } 189 190 /* 191 * For a given param type, parse the list of speed-duplex pairs in 192 * the dladm_ether_info_t and return a comma-separated string formatted 193 * as <speed><speed-unit-char>-<duplex-chars> where <speed> is the value of 194 * speed, in units specifid by the <speed-unit-char> which is one 195 * of 'M' (Mbits/sec) or 'G' (Gigabits/sec). The permissible values of 196 * <duplex-chars> are 'u' (indicating duplex is "unknown") or one/both of 197 * 'f', 'h' (indicating full-duplex and half-duplex respectively) 198 */ 199 extern char * 200 dladm_ether_spdx2str(char *buf, size_t buflen, dladm_ether_info_t *eattr, 201 int ptype) 202 { 203 int i, j; 204 boolean_t is_full, is_half; 205 int speed; 206 char speed_unit; 207 char tmpbuf[DLADM_STRSIZE]; 208 dladm_ether_spdx_t *spdx; 209 uint32_t nspdx; 210 211 spdx = eattr->lei_attr[ptype].le_spdx; 212 nspdx = eattr->lei_attr[ptype].le_num_spdx; 213 for (i = 0; i < nspdx; i++) { 214 215 speed = spdx[i].lesd_speed; 216 217 /* 218 * if we have already covered this speed for 219 * the <other>-duplex case before this, skip it 220 */ 221 for (j = 0; j < i; j++) { 222 if (speed == spdx[j].lesd_speed) 223 break; 224 } 225 if (j < i) 226 continue; 227 228 if ((speed % 1000) == 0) { 229 speed = speed/1000; 230 speed_unit = 'G'; 231 } else { 232 speed_unit = 'M'; 233 } 234 (void) snprintf(tmpbuf, DLADM_STRSIZE, "%d%c", 235 speed, speed_unit); 236 if (i > 0) 237 (void) strncat(buf, ",", buflen); 238 (void) strncat(buf, tmpbuf, buflen); 239 240 is_full = is_half = B_FALSE; 241 /* 242 * Find all the supported duplex values for this speed. 243 */ 244 for (j = 0; j < nspdx; j++) { 245 if (spdx[j].lesd_speed != spdx[i].lesd_speed) 246 continue; 247 if (spdx[j].lesd_duplex == LINK_DUPLEX_FULL) 248 is_full = B_TRUE; 249 if (spdx[j].lesd_duplex == LINK_DUPLEX_HALF) 250 is_half = B_TRUE; 251 } 252 if (is_full && is_half) 253 (void) strncat(buf, "-fh", buflen); 254 else if (is_full) 255 (void) strncat(buf, "-f", buflen); 256 else if (is_half) 257 (void) strncat(buf, "-h", buflen); 258 } 259 return (buf); 260 } 261 262 /* 263 * Extract Ethernet attributes of the link specified by linkid. 264 * Information for the CURRENT, CAPABLE, ADV and PEERADV parameter 265 * types is extracted into the lei_attr[] entries in the dladm_ether_info_t. 266 * On succesful return, the memory allocated in this function should be 267 * freed by calling dladm_ether_info_done(). 268 */ 269 extern dladm_status_t 270 dladm_ether_info(dladm_handle_t handle, datalink_id_t linkid, 271 dladm_ether_info_t *eattr) 272 { 273 uint32_t autoneg, pause, asmpause, fault; 274 uint64_t sp64; 275 dladm_status_t status; 276 int i; 277 link_duplex_t link_duplex; 278 279 bzero(eattr, sizeof (*eattr)); 280 status = dladm_datalink_id2info(handle, linkid, NULL, NULL, NULL, 281 eattr->lei_linkname, sizeof (eattr->lei_linkname)); 282 if (status != DLADM_STATUS_OK) 283 goto bail; 284 285 /* get current values of speed, duplex, state of link */ 286 eattr->lei_attr[CURRENT].le_num_spdx = 1; 287 eattr->lei_attr[CURRENT].le_spdx = malloc(sizeof (dladm_ether_spdx_t)); 288 if (eattr->lei_attr[CURRENT].le_spdx == NULL) { 289 status = DLADM_STATUS_NOMEM; 290 goto bail; 291 } 292 293 if ((status = dladm_get_single_mac_stat(handle, linkid, "ifspeed", 294 KSTAT_DATA_UINT64, &sp64)) != DLADM_STATUS_OK) 295 goto bail; 296 297 if ((status = dladm_get_single_mac_stat(handle, linkid, "link_duplex", 298 KSTAT_DATA_UINT32, &link_duplex)) != DLADM_STATUS_OK) 299 goto bail; 300 301 eattr->lei_attr[CURRENT].le_spdx->lesd_speed = (int)(sp64/1000000ull); 302 eattr->lei_attr[CURRENT].le_spdx->lesd_duplex = link_duplex; 303 304 status = dladm_get_state(handle, linkid, &eattr->lei_state); 305 if (status != DLADM_STATUS_OK) 306 goto bail; 307 308 /* get the auto, pause, asmpause, fault values */ 309 for (i = CURRENT; i <= PEERADV; i++) { 310 311 status = dladm_get_single_mac_stat(handle, linkid, 312 attrstat[i].autoneg_stat, KSTAT_DATA_UINT32, &autoneg); 313 if (status != DLADM_STATUS_OK) 314 goto bail; 315 316 status = dladm_get_single_mac_stat(handle, linkid, 317 attrstat[i].pause_stat, KSTAT_DATA_UINT32, &pause); 318 if (status != DLADM_STATUS_OK) 319 goto bail; 320 321 status = dladm_get_single_mac_stat(handle, linkid, 322 attrstat[i].asmpause_stat, KSTAT_DATA_UINT32, &asmpause); 323 if (status != DLADM_STATUS_OK) 324 goto bail; 325 326 eattr->lei_attr[i].le_autoneg = (autoneg != 0); 327 eattr->lei_attr[i].le_pause = (pause != 0); 328 eattr->lei_attr[i].le_asmpause = (asmpause != 0); 329 330 if (i == CURRENT) 331 continue; 332 status = dladm_get_single_mac_stat(handle, linkid, 333 attrstat[i].fault_stat, KSTAT_DATA_UINT32, &fault); 334 if (status != DLADM_STATUS_OK) 335 goto bail; 336 eattr->lei_attr[i].le_fault = (pause != 0); 337 338 /* get all the supported speed/duplex values */ 339 status = i_dladm_get_spdx(handle, linkid, &eattr->lei_attr[i], 340 attrstat[i].spdx_stat); 341 if (status != DLADM_STATUS_OK) 342 goto bail; 343 } 344 eattr->lei_attr[CURRENT].le_fault = 345 eattr->lei_attr[ADV].le_fault || eattr->lei_attr[PEERADV].le_fault; 346 bail: 347 if (status != DLADM_STATUS_OK) 348 dladm_ether_info_done(eattr); 349 return (status); 350 } 351 352 extern void 353 dladm_ether_info_done(dladm_ether_info_t *eattr) 354 { 355 int i; 356 357 for (i = CURRENT; i <= PEERADV; i++) 358 free(eattr->lei_attr[i].le_spdx); 359 } 360