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