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 {{400000, LINK_DUPLEX_FULL}, "cap_400gfdx"}, 51 {{200000, LINK_DUPLEX_FULL}, "cap_200gfdx"}, 52 {{100000, LINK_DUPLEX_FULL}, "cap_100gfdx"}, 53 {{50000, LINK_DUPLEX_FULL}, "cap_50gfdx"}, 54 {{40000, LINK_DUPLEX_FULL}, "cap_40gfdx"}, 55 {{25000, LINK_DUPLEX_FULL}, "cap_25gfdx"}, 56 {{10000, LINK_DUPLEX_FULL}, "cap_10gfdx"}, 57 {{5000, LINK_DUPLEX_FULL}, "cap_5000fdx"}, 58 {{2500, LINK_DUPLEX_FULL}, "cap_2500fdx"}, 59 {{1000, LINK_DUPLEX_FULL}, "cap_1000fdx"}, 60 {{1000, LINK_DUPLEX_HALF}, "cap_1000hdx"}, 61 {{100, LINK_DUPLEX_FULL}, "cap_100fdx"}, 62 {{100, LINK_DUPLEX_HALF}, "cap_100hdx"}, 63 {{10, LINK_DUPLEX_FULL}, "cap_10fdx"}, 64 {{10, LINK_DUPLEX_HALF}, "cap_10hdx"}, 65 {{0, LINK_DUPLEX_UNKNOWN}, NULL} 66 }; 67 68 static ether_spdx_t adv_cap_spdx[] = { 69 {{400000, LINK_DUPLEX_FULL}, "adv_cap_400gfdx"}, 70 {{200000, LINK_DUPLEX_FULL}, "adv_cap_200gfdx"}, 71 {{100000, LINK_DUPLEX_FULL}, "adv_cap_100gfdx"}, 72 {{50000, LINK_DUPLEX_FULL}, "adv_cap_50gfdx"}, 73 {{40000, LINK_DUPLEX_FULL}, "adv_cap_40gfdx"}, 74 {{25000, LINK_DUPLEX_FULL}, "adv_cap_25gfdx"}, 75 {{10000, LINK_DUPLEX_FULL}, "adv_cap_10gfdx"}, 76 {{5000, LINK_DUPLEX_FULL}, "adv_cap_5000fdx"}, 77 {{2500, LINK_DUPLEX_FULL}, "adv_cap_2500fdx"}, 78 {{1000, LINK_DUPLEX_FULL}, "adv_cap_1000fdx"}, 79 {{1000, LINK_DUPLEX_HALF}, "adv_cap_1000hdx"}, 80 {{100, LINK_DUPLEX_FULL}, "adv_cap_100fdx"}, 81 {{100, LINK_DUPLEX_HALF}, "adv_cap_100hdx"}, 82 {{10, LINK_DUPLEX_FULL}, "adv_cap_10fdx"}, 83 {{10, LINK_DUPLEX_HALF}, "adv_cap_10hdx"}, 84 {{0, LINK_DUPLEX_UNKNOWN}, NULL} 85 }; 86 87 static ether_spdx_t lp_cap_spdx[] = { 88 {{400000, LINK_DUPLEX_FULL}, "lp_cap_400gfdx"}, 89 {{200000, LINK_DUPLEX_FULL}, "lp_cap_200gfdx"}, 90 {{100000, LINK_DUPLEX_FULL}, "lp_cap_100gfdx"}, 91 {{50000, LINK_DUPLEX_FULL}, "lp_cap_50gfdx"}, 92 {{40000, LINK_DUPLEX_FULL}, "lp_cap_40gfdx"}, 93 {{25000, LINK_DUPLEX_FULL}, "lp_cap_25gfdx"}, 94 {{10000, LINK_DUPLEX_FULL}, "lp_cap_10gfdx"}, 95 {{5000, LINK_DUPLEX_FULL}, "lp_cap_5000fdx"}, 96 {{2500, LINK_DUPLEX_FULL}, "lp_cap_2500fdx"}, 97 {{1000, LINK_DUPLEX_FULL}, "lp_cap_1000fdx"}, 98 {{1000, LINK_DUPLEX_HALF}, "lp_cap_1000hdx"}, 99 {{100, LINK_DUPLEX_FULL}, "lp_cap_100fdx"}, 100 {{100, LINK_DUPLEX_HALF}, "lp_cap_100hdx"}, 101 {{10, LINK_DUPLEX_FULL}, "lp_cap_10fdx"}, 102 {{10, LINK_DUPLEX_HALF}, "lp_cap_10hdx"}, 103 {{0, LINK_DUPLEX_UNKNOWN}, NULL} 104 }; 105 106 typedef struct attr_kstat_s { 107 char *autoneg_stat; 108 char *pause_stat; 109 char *asmpause_stat; 110 char *fault_stat; 111 ether_spdx_t *spdx_stat; 112 } attr_kstat_t; 113 114 static attr_kstat_t attrstat[] = { 115 {"link_autoneg", /* current */ 116 "link_pause", "link_asmpause", NULL, 117 NULL}, 118 119 {"cap_autoneg", /* capable */ 120 "cap_pause", "cap_asmpause", "cap_rem_fault", 121 cap_spdx}, 122 123 {"adv_cap_autoneg", /* advertised */ 124 "adv_cap_pause", "adv_cap_asmpause", "adv_rem_fault", 125 adv_cap_spdx}, 126 127 {"lp_cap_autoneg", /* peer advertised */ 128 "lp_cap_pause", "lp_cap_asmpause", "lp_rem_fault", 129 lp_cap_spdx} 130 }; 131 132 /* 133 * Get the speed-duplex stats specified in the ether_spdx_t table passed in 134 * by querying the appropriate kstat for each entry in the table. 135 */ 136 static dladm_status_t 137 i_dladm_get_spdx(dladm_handle_t handle, datalink_id_t linkid, 138 dladm_ether_attr_t *eattr, ether_spdx_t *spdx_stat) 139 { 140 int i, nspdx = 0; 141 uint32_t speed; 142 dladm_status_t status; 143 void *ptr; 144 145 eattr->le_spdx = NULL; 146 for (i = 0; spdx_stat[i].eth_spdx_stat_name != NULL; i++) { 147 if ((status = dladm_get_single_mac_stat(handle, linkid, 148 spdx_stat[i].eth_spdx_stat_name, 149 KSTAT_DATA_UINT32, &speed)) != DLADM_STATUS_OK) { 150 151 if (status == DLADM_STATUS_NOTFOUND) { 152 /* 153 * Missing statistic. 154 * Skip this one and try the rest. 155 */ 156 continue; 157 } else { 158 free(eattr->le_spdx); 159 eattr->le_num_spdx = 0; 160 return (status); 161 } 162 } 163 if (speed == 0) 164 continue; 165 nspdx++; 166 ptr = realloc(eattr->le_spdx, 167 nspdx * sizeof (dladm_ether_spdx_t)); 168 if (ptr != NULL) { 169 eattr->le_spdx = ptr; 170 } else { 171 free(eattr->le_spdx); 172 eattr->le_num_spdx = 0; 173 return (DLADM_STATUS_NOMEM); 174 } 175 eattr->le_spdx[nspdx - 1] = spdx_stat[i].eth_spdx; 176 } 177 eattr->le_num_spdx = nspdx; 178 return (DLADM_STATUS_OK); 179 } 180 181 /* 182 * Returns "yes" or "no" based on the autonegotion capabilities 183 * for the parameter type indicated by ptype. The permissible 184 * values for ptype are CURRENT, CAPABLE, ADV, PEERADV. 185 */ 186 char * 187 dladm_ether_autoneg2str(char *buf, size_t buflen, dladm_ether_info_t *eattr, 188 int ptype) 189 { 190 boolean_t autoneg = eattr->lei_attr[ptype].le_autoneg; 191 192 (void) strlcpy(buf, (autoneg ? "yes" : "no"), buflen); 193 return (buf); 194 } 195 196 /* 197 * Returns {"bi", "tx", "none"} based on the flow-control capabilities 198 * for the parameter type indicated by ptype. The permissible 199 * values for ptype are CURRENT, CAPABLE, ADV, PEERADV. 200 */ 201 char * 202 dladm_ether_pause2str(char *buf, size_t buflen, dladm_ether_info_t *eattr, 203 int ptype) 204 { 205 boolean_t pause = eattr->lei_attr[ptype].le_pause; 206 boolean_t asmpause = eattr->lei_attr[ptype].le_asmpause; 207 208 if (pause) 209 (void) strlcpy(buf, "bi", buflen); 210 else if (asmpause) 211 (void) strlcpy(buf, "tx", buflen); 212 else 213 (void) strlcpy(buf, "none", buflen); 214 return (buf); 215 } 216 217 /* 218 * For a given param type, parse the list of speed-duplex pairs in 219 * the dladm_ether_info_t and return a comma-separated string formatted 220 * as <speed><speed-unit-char>-<duplex-chars> where <speed> is the value of 221 * speed, in units specifid by the <speed-unit-char> which is one 222 * of 'M' (Mbits/sec) or 'G' (Gigabits/sec). The permissible values of 223 * <duplex-chars> are 'u' (indicating duplex is "unknown") or one/both of 224 * 'f', 'h' (indicating full-duplex and half-duplex respectively) 225 */ 226 extern char * 227 dladm_ether_spdx2str(char *buf, size_t buflen, dladm_ether_info_t *eattr, 228 int ptype) 229 { 230 uint_t i, j; 231 boolean_t is_full, is_half; 232 int speed; 233 char speed_unit; 234 char tmpbuf[DLADM_STRSIZE]; 235 dladm_ether_spdx_t *spdx; 236 uint32_t nspdx; 237 238 spdx = eattr->lei_attr[ptype].le_spdx; 239 nspdx = eattr->lei_attr[ptype].le_num_spdx; 240 for (i = 0; i < nspdx; i++) { 241 242 speed = spdx[i].lesd_speed; 243 244 /* 245 * if we have already covered this speed for 246 * the <other>-duplex case before this, skip it 247 */ 248 for (j = 0; j < i; j++) { 249 if (speed == spdx[j].lesd_speed) 250 break; 251 } 252 if (j < i) 253 continue; 254 255 if ((speed % 1000) == 0) { 256 speed = speed/1000; 257 speed_unit = 'G'; 258 } else { 259 speed_unit = 'M'; 260 } 261 (void) snprintf(tmpbuf, DLADM_STRSIZE, "%d%c", 262 speed, speed_unit); 263 if (i > 0) 264 (void) strncat(buf, ",", buflen); 265 (void) strncat(buf, tmpbuf, buflen); 266 267 is_full = is_half = B_FALSE; 268 /* 269 * Find all the supported duplex values for this speed. 270 */ 271 for (j = 0; j < nspdx; j++) { 272 if (spdx[j].lesd_speed != spdx[i].lesd_speed) 273 continue; 274 if (spdx[j].lesd_duplex == LINK_DUPLEX_FULL) 275 is_full = B_TRUE; 276 if (spdx[j].lesd_duplex == LINK_DUPLEX_HALF) 277 is_half = B_TRUE; 278 } 279 if (is_full && is_half) 280 (void) strncat(buf, "-fh", buflen); 281 else if (is_full) 282 (void) strncat(buf, "-f", buflen); 283 else if (is_half) 284 (void) strncat(buf, "-h", buflen); 285 } 286 return (buf); 287 } 288 289 /* 290 * Extract Ethernet attributes of the link specified by linkid. 291 * Information for the CURRENT, CAPABLE, ADV and PEERADV parameter 292 * types is extracted into the lei_attr[] entries in the dladm_ether_info_t. 293 * On succesful return, the memory allocated in this function should be 294 * freed by calling dladm_ether_info_done(). 295 */ 296 extern dladm_status_t 297 dladm_ether_info(dladm_handle_t handle, datalink_id_t linkid, 298 dladm_ether_info_t *eattr) 299 { 300 uint32_t autoneg, pause, asmpause, fault; 301 uint64_t sp64; 302 dladm_status_t status; 303 int i; 304 link_duplex_t link_duplex; 305 306 bzero(eattr, sizeof (*eattr)); 307 status = dladm_datalink_id2info(handle, linkid, NULL, NULL, NULL, 308 eattr->lei_linkname, sizeof (eattr->lei_linkname)); 309 if (status != DLADM_STATUS_OK) 310 goto bail; 311 312 /* get current values of speed, duplex, state of link */ 313 eattr->lei_attr[CURRENT].le_num_spdx = 1; 314 eattr->lei_attr[CURRENT].le_spdx = malloc(sizeof (dladm_ether_spdx_t)); 315 if (eattr->lei_attr[CURRENT].le_spdx == NULL) { 316 status = DLADM_STATUS_NOMEM; 317 goto bail; 318 } 319 320 if ((status = dladm_get_single_mac_stat(handle, linkid, "ifspeed", 321 KSTAT_DATA_UINT64, &sp64)) != DLADM_STATUS_OK) 322 goto bail; 323 324 if ((status = dladm_get_single_mac_stat(handle, linkid, "link_duplex", 325 KSTAT_DATA_UINT32, &link_duplex)) != DLADM_STATUS_OK) 326 goto bail; 327 328 eattr->lei_attr[CURRENT].le_spdx->lesd_speed = (int)(sp64/1000000ull); 329 eattr->lei_attr[CURRENT].le_spdx->lesd_duplex = link_duplex; 330 331 status = dladm_get_state(handle, linkid, &eattr->lei_state); 332 if (status != DLADM_STATUS_OK) 333 goto bail; 334 335 /* get the auto, pause, asmpause, fault values */ 336 for (i = CURRENT; i <= PEERADV; i++) { 337 338 status = dladm_get_single_mac_stat(handle, linkid, 339 attrstat[i].autoneg_stat, KSTAT_DATA_UINT32, &autoneg); 340 if (status != DLADM_STATUS_OK) 341 goto bail; 342 343 status = dladm_get_single_mac_stat(handle, linkid, 344 attrstat[i].pause_stat, KSTAT_DATA_UINT32, &pause); 345 if (status != DLADM_STATUS_OK) 346 goto bail; 347 348 status = dladm_get_single_mac_stat(handle, linkid, 349 attrstat[i].asmpause_stat, KSTAT_DATA_UINT32, &asmpause); 350 if (status != DLADM_STATUS_OK) 351 goto bail; 352 353 eattr->lei_attr[i].le_autoneg = (autoneg != 0); 354 eattr->lei_attr[i].le_pause = (pause != 0); 355 eattr->lei_attr[i].le_asmpause = (asmpause != 0); 356 357 if (i == CURRENT) 358 continue; 359 status = dladm_get_single_mac_stat(handle, linkid, 360 attrstat[i].fault_stat, KSTAT_DATA_UINT32, &fault); 361 if (status != DLADM_STATUS_OK) 362 goto bail; 363 eattr->lei_attr[i].le_fault = (pause != 0); 364 365 /* get all the supported speed/duplex values */ 366 status = i_dladm_get_spdx(handle, linkid, &eattr->lei_attr[i], 367 attrstat[i].spdx_stat); 368 if (status != DLADM_STATUS_OK) 369 goto bail; 370 } 371 eattr->lei_attr[CURRENT].le_fault = 372 eattr->lei_attr[ADV].le_fault || eattr->lei_attr[PEERADV].le_fault; 373 bail: 374 if (status != DLADM_STATUS_OK) 375 dladm_ether_info_done(eattr); 376 return (status); 377 } 378 379 extern void 380 dladm_ether_info_done(dladm_ether_info_t *eattr) 381 { 382 int i; 383 384 for (i = CURRENT; i <= PEERADV; i++) { 385 free(eattr->lei_attr[i].le_spdx); 386 eattr->lei_attr[i].le_spdx = NULL; 387 } 388 } 389