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
i_dladm_get_spdx(dladm_handle_t handle,datalink_id_t linkid,dladm_ether_attr_t * eattr,ether_spdx_t * spdx_stat)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 *
dladm_ether_autoneg2str(char * buf,size_t buflen,dladm_ether_info_t * eattr,int ptype)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 *
dladm_ether_pause2str(char * buf,size_t buflen,dladm_ether_info_t * eattr,int ptype)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 *
dladm_ether_spdx2str(char * buf,size_t buflen,dladm_ether_info_t * eattr,int ptype)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
dladm_ether_info(dladm_handle_t handle,datalink_id_t linkid,dladm_ether_info_t * eattr)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
dladm_ether_info_done(dladm_ether_info_t * eattr)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