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