OpenWrt – Blame information for rev 2
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * swlib.c: Switch configuration API (user space part) |
||
3 | * |
||
4 | * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name> |
||
5 | * |
||
6 | * This program is free software; you can redistribute it and/or |
||
7 | * modify it under the terms of the GNU Lesser General Public License |
||
8 | * version 2.1 as published by the Free Software Foundation. |
||
9 | * |
||
10 | * This program is distributed in the hope that it will be useful, |
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
13 | * GNU General Public License for more details. |
||
14 | */ |
||
15 | |||
16 | #include <stdio.h> |
||
17 | #include <string.h> |
||
18 | #include <stdlib.h> |
||
19 | #include <ctype.h> |
||
20 | #include <inttypes.h> |
||
21 | #include <errno.h> |
||
22 | #include <stdint.h> |
||
23 | #include <getopt.h> |
||
24 | #include <sys/types.h> |
||
25 | #include <sys/socket.h> |
||
26 | #include <linux/switch.h> |
||
27 | #include "swlib.h" |
||
28 | #include <netlink/netlink.h> |
||
29 | #include <netlink/genl/genl.h> |
||
30 | #include <netlink/genl/family.h> |
||
31 | |||
32 | //#define DEBUG 1 |
||
33 | #ifdef DEBUG |
||
34 | #define DPRINTF(fmt, ...) fprintf(stderr, "%s(%d): " fmt, __func__, __LINE__, ##__VA_ARGS__) |
||
35 | #else |
||
36 | #define DPRINTF(fmt, ...) do {} while (0) |
||
37 | #endif |
||
38 | |||
39 | static struct nl_sock *handle; |
||
40 | static struct nl_cache *cache; |
||
41 | static struct genl_family *family; |
||
42 | static struct nlattr *tb[SWITCH_ATTR_MAX + 1]; |
||
43 | static int refcount = 0; |
||
44 | |||
45 | static struct nla_policy port_policy[SWITCH_ATTR_MAX] = { |
||
46 | [SWITCH_PORT_ID] = { .type = NLA_U32 }, |
||
47 | [SWITCH_PORT_FLAG_TAGGED] = { .type = NLA_FLAG }, |
||
48 | }; |
||
49 | |||
50 | static struct nla_policy portmap_policy[SWITCH_PORTMAP_MAX] = { |
||
51 | [SWITCH_PORTMAP_SEGMENT] = { .type = NLA_STRING }, |
||
52 | [SWITCH_PORTMAP_VIRT] = { .type = NLA_U32 }, |
||
53 | }; |
||
54 | |||
55 | static struct nla_policy link_policy[SWITCH_LINK_ATTR_MAX] = { |
||
56 | [SWITCH_LINK_FLAG_LINK] = { .type = NLA_FLAG }, |
||
57 | [SWITCH_LINK_FLAG_DUPLEX] = { .type = NLA_FLAG }, |
||
58 | [SWITCH_LINK_FLAG_ANEG] = { .type = NLA_FLAG }, |
||
59 | [SWITCH_LINK_SPEED] = { .type = NLA_U32 }, |
||
60 | [SWITCH_LINK_FLAG_EEE_100BASET] = { .type = NLA_FLAG }, |
||
61 | [SWITCH_LINK_FLAG_EEE_1000BASET] = { .type = NLA_FLAG }, |
||
62 | }; |
||
63 | |||
64 | static inline void * |
||
65 | swlib_alloc(size_t size) |
||
66 | { |
||
67 | void *ptr; |
||
68 | |||
69 | ptr = malloc(size); |
||
70 | if (!ptr) |
||
71 | goto done; |
||
72 | memset(ptr, 0, size); |
||
73 | |||
74 | done: |
||
75 | return ptr; |
||
76 | } |
||
77 | |||
78 | static int |
||
79 | wait_handler(struct nl_msg *msg, void *arg) |
||
80 | { |
||
81 | int *finished = arg; |
||
82 | |||
83 | *finished = 1; |
||
84 | return NL_STOP; |
||
85 | } |
||
86 | |||
87 | /* helper function for performing netlink requests */ |
||
88 | static int |
||
89 | swlib_call(int cmd, int (*call)(struct nl_msg *, void *), |
||
90 | int (*data)(struct nl_msg *, void *), void *arg) |
||
91 | { |
||
92 | struct nl_msg *msg; |
||
93 | struct nl_cb *cb = NULL; |
||
94 | int finished; |
||
95 | int flags = 0; |
||
96 | int err = 0; |
||
97 | |||
98 | msg = nlmsg_alloc(); |
||
99 | if (!msg) { |
||
100 | fprintf(stderr, "Out of memory!\n"); |
||
101 | exit(1); |
||
102 | } |
||
103 | |||
104 | if (!data) |
||
105 | flags |= NLM_F_DUMP; |
||
106 | |||
107 | genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, genl_family_get_id(family), 0, flags, cmd, 0); |
||
108 | if (data) { |
||
109 | err = data(msg, arg); |
||
110 | if (err < 0) |
||
111 | goto nla_put_failure; |
||
112 | } |
||
113 | |||
114 | cb = nl_cb_alloc(NL_CB_CUSTOM); |
||
115 | if (!cb) { |
||
116 | fprintf(stderr, "nl_cb_alloc failed.\n"); |
||
117 | exit(1); |
||
118 | } |
||
119 | |||
120 | err = nl_send_auto_complete(handle, msg); |
||
121 | if (err < 0) { |
||
122 | fprintf(stderr, "nl_send_auto_complete failed: %d\n", err); |
||
123 | goto out; |
||
124 | } |
||
125 | |||
126 | finished = 0; |
||
127 | |||
128 | if (call) |
||
129 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, call, arg); |
||
130 | |||
131 | if (data) |
||
132 | nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, wait_handler, &finished); |
||
133 | else |
||
134 | nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, wait_handler, &finished); |
||
135 | |||
136 | err = nl_recvmsgs(handle, cb); |
||
137 | if (err < 0) { |
||
138 | goto out; |
||
139 | } |
||
140 | |||
141 | if (!finished) |
||
142 | err = nl_wait_for_ack(handle); |
||
143 | |||
144 | out: |
||
145 | if (cb) |
||
146 | nl_cb_put(cb); |
||
147 | nla_put_failure: |
||
148 | nlmsg_free(msg); |
||
149 | return err; |
||
150 | } |
||
151 | |||
152 | static int |
||
153 | send_attr(struct nl_msg *msg, void *arg) |
||
154 | { |
||
155 | struct switch_val *val = arg; |
||
156 | struct switch_attr *attr = val->attr; |
||
157 | |||
158 | NLA_PUT_U32(msg, SWITCH_ATTR_ID, attr->dev->id); |
||
159 | NLA_PUT_U32(msg, SWITCH_ATTR_OP_ID, attr->id); |
||
160 | switch(attr->atype) { |
||
161 | case SWLIB_ATTR_GROUP_PORT: |
||
162 | NLA_PUT_U32(msg, SWITCH_ATTR_OP_PORT, val->port_vlan); |
||
163 | break; |
||
164 | case SWLIB_ATTR_GROUP_VLAN: |
||
165 | NLA_PUT_U32(msg, SWITCH_ATTR_OP_VLAN, val->port_vlan); |
||
166 | break; |
||
167 | default: |
||
168 | break; |
||
169 | } |
||
170 | |||
171 | return 0; |
||
172 | |||
173 | nla_put_failure: |
||
174 | return -1; |
||
175 | } |
||
176 | |||
177 | static int |
||
178 | store_port_val(struct nl_msg *msg, struct nlattr *nla, struct switch_val *val) |
||
179 | { |
||
180 | struct nlattr *p; |
||
181 | int ports = val->attr->dev->ports; |
||
182 | int err = 0; |
||
183 | int remaining; |
||
184 | |||
185 | if (!val->value.ports) |
||
186 | val->value.ports = malloc(sizeof(struct switch_port) * ports); |
||
187 | |||
188 | nla_for_each_nested(p, nla, remaining) { |
||
189 | struct nlattr *tb[SWITCH_PORT_ATTR_MAX+1]; |
||
190 | struct switch_port *port; |
||
191 | |||
192 | if (val->len >= ports) |
||
193 | break; |
||
194 | |||
195 | err = nla_parse_nested(tb, SWITCH_PORT_ATTR_MAX, p, port_policy); |
||
196 | if (err < 0) |
||
197 | goto out; |
||
198 | |||
199 | if (!tb[SWITCH_PORT_ID]) |
||
200 | continue; |
||
201 | |||
202 | port = &val->value.ports[val->len]; |
||
203 | port->id = nla_get_u32(tb[SWITCH_PORT_ID]); |
||
204 | port->flags = 0; |
||
205 | if (tb[SWITCH_PORT_FLAG_TAGGED]) |
||
206 | port->flags |= SWLIB_PORT_FLAG_TAGGED; |
||
207 | |||
208 | val->len++; |
||
209 | } |
||
210 | |||
211 | out: |
||
212 | return err; |
||
213 | } |
||
214 | |||
215 | static int |
||
216 | store_link_val(struct nl_msg *msg, struct nlattr *nla, struct switch_val *val) |
||
217 | { |
||
218 | struct nlattr *tb[SWITCH_LINK_ATTR_MAX + 1]; |
||
219 | struct switch_port_link *link; |
||
220 | int err = 0; |
||
221 | |||
222 | if (!val->value.link) |
||
223 | val->value.link = malloc(sizeof(struct switch_port_link)); |
||
224 | |||
225 | err = nla_parse_nested(tb, SWITCH_LINK_ATTR_MAX, nla, link_policy); |
||
226 | if (err < 0) |
||
227 | goto out; |
||
228 | |||
229 | link = val->value.link; |
||
230 | link->link = !!tb[SWITCH_LINK_FLAG_LINK]; |
||
231 | link->duplex = !!tb[SWITCH_LINK_FLAG_DUPLEX]; |
||
232 | link->aneg = !!tb[SWITCH_LINK_FLAG_ANEG]; |
||
233 | link->tx_flow = !!tb[SWITCH_LINK_FLAG_TX_FLOW]; |
||
234 | link->rx_flow = !!tb[SWITCH_LINK_FLAG_RX_FLOW]; |
||
235 | link->speed = nla_get_u32(tb[SWITCH_LINK_SPEED]); |
||
236 | link->eee = 0; |
||
237 | if (tb[SWITCH_LINK_FLAG_EEE_100BASET]) |
||
238 | link->eee |= SWLIB_LINK_FLAG_EEE_100BASET; |
||
239 | if (tb[SWITCH_LINK_FLAG_EEE_1000BASET]) |
||
240 | link->eee |= SWLIB_LINK_FLAG_EEE_1000BASET; |
||
241 | |||
242 | out: |
||
243 | return err; |
||
244 | } |
||
245 | |||
246 | static int |
||
247 | store_val(struct nl_msg *msg, void *arg) |
||
248 | { |
||
249 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); |
||
250 | struct switch_val *val = arg; |
||
251 | |||
252 | if (!val) |
||
253 | goto error; |
||
254 | |||
255 | if (nla_parse(tb, SWITCH_ATTR_MAX - 1, genlmsg_attrdata(gnlh, 0), |
||
256 | genlmsg_attrlen(gnlh, 0), NULL) < 0) { |
||
257 | goto error; |
||
258 | } |
||
259 | |||
260 | if (tb[SWITCH_ATTR_OP_VALUE_INT]) |
||
261 | val->value.i = nla_get_u32(tb[SWITCH_ATTR_OP_VALUE_INT]); |
||
262 | else if (tb[SWITCH_ATTR_OP_VALUE_STR]) |
||
263 | val->value.s = strdup(nla_get_string(tb[SWITCH_ATTR_OP_VALUE_STR])); |
||
264 | else if (tb[SWITCH_ATTR_OP_VALUE_PORTS]) |
||
265 | val->err = store_port_val(msg, tb[SWITCH_ATTR_OP_VALUE_PORTS], val); |
||
266 | else if (tb[SWITCH_ATTR_OP_VALUE_LINK]) |
||
267 | val->err = store_link_val(msg, tb[SWITCH_ATTR_OP_VALUE_LINK], val); |
||
268 | |||
269 | val->err = 0; |
||
270 | return 0; |
||
271 | |||
272 | error: |
||
273 | return NL_SKIP; |
||
274 | } |
||
275 | |||
276 | int |
||
277 | swlib_get_attr(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) |
||
278 | { |
||
279 | int cmd; |
||
280 | int err; |
||
281 | |||
282 | switch(attr->atype) { |
||
283 | case SWLIB_ATTR_GROUP_GLOBAL: |
||
284 | cmd = SWITCH_CMD_GET_GLOBAL; |
||
285 | break; |
||
286 | case SWLIB_ATTR_GROUP_PORT: |
||
287 | cmd = SWITCH_CMD_GET_PORT; |
||
288 | break; |
||
289 | case SWLIB_ATTR_GROUP_VLAN: |
||
290 | cmd = SWITCH_CMD_GET_VLAN; |
||
291 | break; |
||
292 | default: |
||
293 | return -EINVAL; |
||
294 | } |
||
295 | |||
296 | memset(&val->value, 0, sizeof(val->value)); |
||
297 | val->len = 0; |
||
298 | val->attr = attr; |
||
299 | val->err = -EINVAL; |
||
300 | err = swlib_call(cmd, store_val, send_attr, val); |
||
301 | if (!err) |
||
302 | err = val->err; |
||
303 | |||
304 | return err; |
||
305 | } |
||
306 | |||
307 | static int |
||
308 | send_attr_ports(struct nl_msg *msg, struct switch_val *val) |
||
309 | { |
||
310 | struct nlattr *n; |
||
311 | int i; |
||
312 | |||
313 | /* TODO implement multipart? */ |
||
314 | if (val->len == 0) |
||
315 | goto done; |
||
316 | n = nla_nest_start(msg, SWITCH_ATTR_OP_VALUE_PORTS); |
||
317 | if (!n) |
||
318 | goto nla_put_failure; |
||
319 | for (i = 0; i < val->len; i++) { |
||
320 | struct switch_port *port = &val->value.ports[i]; |
||
321 | struct nlattr *np; |
||
322 | |||
323 | np = nla_nest_start(msg, SWITCH_ATTR_PORT); |
||
324 | if (!np) |
||
325 | goto nla_put_failure; |
||
326 | |||
327 | NLA_PUT_U32(msg, SWITCH_PORT_ID, port->id); |
||
328 | if (port->flags & SWLIB_PORT_FLAG_TAGGED) |
||
329 | NLA_PUT_FLAG(msg, SWITCH_PORT_FLAG_TAGGED); |
||
330 | |||
331 | nla_nest_end(msg, np); |
||
332 | } |
||
333 | nla_nest_end(msg, n); |
||
334 | done: |
||
335 | return 0; |
||
336 | |||
337 | nla_put_failure: |
||
338 | return -1; |
||
339 | } |
||
340 | |||
341 | static int |
||
342 | send_attr_link(struct nl_msg *msg, struct switch_val *val) |
||
343 | { |
||
344 | struct switch_port_link *link = val->value.link; |
||
345 | struct nlattr *n; |
||
346 | |||
347 | n = nla_nest_start(msg, SWITCH_ATTR_OP_VALUE_LINK); |
||
348 | if (!n) |
||
349 | goto nla_put_failure; |
||
350 | |||
351 | if (link->duplex) |
||
352 | NLA_PUT_FLAG(msg, SWITCH_LINK_FLAG_DUPLEX); |
||
353 | if (link->aneg) |
||
354 | NLA_PUT_FLAG(msg, SWITCH_LINK_FLAG_ANEG); |
||
355 | NLA_PUT_U32(msg, SWITCH_LINK_SPEED, link->speed); |
||
356 | |||
357 | nla_nest_end(msg, n); |
||
358 | |||
359 | return 0; |
||
360 | |||
361 | nla_put_failure: |
||
362 | return -1; |
||
363 | } |
||
364 | |||
365 | static int |
||
366 | send_attr_val(struct nl_msg *msg, void *arg) |
||
367 | { |
||
368 | struct switch_val *val = arg; |
||
369 | struct switch_attr *attr = val->attr; |
||
370 | |||
371 | if (send_attr(msg, arg)) |
||
372 | goto nla_put_failure; |
||
373 | |||
374 | switch(attr->type) { |
||
375 | case SWITCH_TYPE_NOVAL: |
||
376 | break; |
||
377 | case SWITCH_TYPE_INT: |
||
378 | NLA_PUT_U32(msg, SWITCH_ATTR_OP_VALUE_INT, val->value.i); |
||
379 | break; |
||
380 | case SWITCH_TYPE_STRING: |
||
381 | if (!val->value.s) |
||
382 | goto nla_put_failure; |
||
383 | NLA_PUT_STRING(msg, SWITCH_ATTR_OP_VALUE_STR, val->value.s); |
||
384 | break; |
||
385 | case SWITCH_TYPE_PORTS: |
||
386 | if (send_attr_ports(msg, val) < 0) |
||
387 | goto nla_put_failure; |
||
388 | break; |
||
389 | case SWITCH_TYPE_LINK: |
||
390 | if (send_attr_link(msg, val)) |
||
391 | goto nla_put_failure; |
||
392 | break; |
||
393 | default: |
||
394 | goto nla_put_failure; |
||
395 | } |
||
396 | return 0; |
||
397 | |||
398 | nla_put_failure: |
||
399 | return -1; |
||
400 | } |
||
401 | |||
402 | int |
||
403 | swlib_set_attr(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val) |
||
404 | { |
||
405 | int cmd; |
||
406 | |||
407 | switch(attr->atype) { |
||
408 | case SWLIB_ATTR_GROUP_GLOBAL: |
||
409 | cmd = SWITCH_CMD_SET_GLOBAL; |
||
410 | break; |
||
411 | case SWLIB_ATTR_GROUP_PORT: |
||
412 | cmd = SWITCH_CMD_SET_PORT; |
||
413 | break; |
||
414 | case SWLIB_ATTR_GROUP_VLAN: |
||
415 | cmd = SWITCH_CMD_SET_VLAN; |
||
416 | break; |
||
417 | default: |
||
418 | return -EINVAL; |
||
419 | } |
||
420 | |||
421 | val->attr = attr; |
||
422 | return swlib_call(cmd, NULL, send_attr_val, val); |
||
423 | } |
||
424 | |||
425 | enum { |
||
426 | CMD_NONE, |
||
427 | CMD_DUPLEX, |
||
428 | CMD_ANEG, |
||
429 | CMD_SPEED, |
||
430 | }; |
||
431 | |||
432 | int swlib_set_attr_string(struct switch_dev *dev, struct switch_attr *a, int port_vlan, const char *str) |
||
433 | { |
||
434 | struct switch_port *ports; |
||
435 | struct switch_port_link *link; |
||
436 | struct switch_val val; |
||
437 | char *ptr; |
||
438 | int cmd = CMD_NONE; |
||
439 | |||
440 | memset(&val, 0, sizeof(val)); |
||
441 | val.port_vlan = port_vlan; |
||
442 | switch(a->type) { |
||
443 | case SWITCH_TYPE_INT: |
||
444 | val.value.i = atoi(str); |
||
445 | break; |
||
446 | case SWITCH_TYPE_STRING: |
||
447 | val.value.s = (char *)str; |
||
448 | break; |
||
449 | case SWITCH_TYPE_PORTS: |
||
450 | ports = alloca(sizeof(struct switch_port) * dev->ports); |
||
451 | memset(ports, 0, sizeof(struct switch_port) * dev->ports); |
||
452 | val.len = 0; |
||
453 | ptr = (char *)str; |
||
454 | while(ptr && *ptr) |
||
455 | { |
||
456 | while(*ptr && isspace(*ptr)) |
||
457 | ptr++; |
||
458 | |||
459 | if (!*ptr) |
||
460 | break; |
||
461 | |||
462 | if (!isdigit(*ptr)) |
||
463 | return -1; |
||
464 | |||
465 | if (val.len >= dev->ports) |
||
466 | return -1; |
||
467 | |||
468 | ports[val.len].flags = 0; |
||
469 | ports[val.len].id = strtoul(ptr, &ptr, 10); |
||
470 | while(*ptr && !isspace(*ptr)) { |
||
471 | if (*ptr == 't') |
||
472 | ports[val.len].flags |= SWLIB_PORT_FLAG_TAGGED; |
||
473 | else |
||
474 | return -1; |
||
475 | |||
476 | ptr++; |
||
477 | } |
||
478 | if (*ptr) |
||
479 | ptr++; |
||
480 | val.len++; |
||
481 | } |
||
482 | val.value.ports = ports; |
||
483 | break; |
||
484 | case SWITCH_TYPE_LINK: |
||
485 | link = malloc(sizeof(struct switch_port_link)); |
||
486 | memset(link, 0, sizeof(struct switch_port_link)); |
||
487 | ptr = (char *)str; |
||
488 | for (ptr = strtok(ptr," "); ptr; ptr = strtok(NULL, " ")) { |
||
489 | switch (cmd) { |
||
490 | case CMD_NONE: |
||
491 | if (!strcmp(ptr, "duplex")) |
||
492 | cmd = CMD_DUPLEX; |
||
493 | else if (!strcmp(ptr, "autoneg")) |
||
494 | cmd = CMD_ANEG; |
||
495 | else if (!strcmp(ptr, "speed")) |
||
496 | cmd = CMD_SPEED; |
||
497 | else |
||
498 | fprintf(stderr, "Unsupported option %s\n", ptr); |
||
499 | break; |
||
500 | case CMD_DUPLEX: |
||
501 | if (!strcmp(ptr, "half")) |
||
502 | link->duplex = 0; |
||
503 | else if (!strcmp(ptr, "full")) |
||
504 | link->duplex = 1; |
||
505 | else |
||
506 | fprintf(stderr, "Unsupported value %s\n", ptr); |
||
507 | cmd = CMD_NONE; |
||
508 | break; |
||
509 | case CMD_ANEG: |
||
510 | if (!strcmp(ptr, "on")) |
||
511 | link->aneg = 1; |
||
512 | else if (!strcmp(ptr, "off")) |
||
513 | link->aneg = 0; |
||
514 | else |
||
515 | fprintf(stderr, "Unsupported value %s\n", ptr); |
||
516 | cmd = CMD_NONE; |
||
517 | break; |
||
518 | case CMD_SPEED: |
||
519 | link->speed = atoi(ptr); |
||
520 | cmd = CMD_NONE; |
||
521 | break; |
||
522 | } |
||
523 | } |
||
524 | val.value.link = link; |
||
525 | break; |
||
526 | case SWITCH_TYPE_NOVAL: |
||
527 | if (str && !strcmp(str, "0")) |
||
528 | return 0; |
||
529 | |||
530 | break; |
||
531 | default: |
||
532 | return -1; |
||
533 | } |
||
534 | return swlib_set_attr(dev, a, &val); |
||
535 | } |
||
536 | |||
537 | |||
538 | struct attrlist_arg { |
||
539 | int id; |
||
540 | int atype; |
||
541 | struct switch_dev *dev; |
||
542 | struct switch_attr *prev; |
||
543 | struct switch_attr **head; |
||
544 | }; |
||
545 | |||
546 | static int |
||
547 | add_id(struct nl_msg *msg, void *arg) |
||
548 | { |
||
549 | struct attrlist_arg *l = arg; |
||
550 | |||
551 | NLA_PUT_U32(msg, SWITCH_ATTR_ID, l->id); |
||
552 | |||
553 | return 0; |
||
554 | nla_put_failure: |
||
555 | return -1; |
||
556 | } |
||
557 | |||
558 | static int |
||
559 | add_attr(struct nl_msg *msg, void *ptr) |
||
560 | { |
||
561 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); |
||
562 | struct attrlist_arg *arg = ptr; |
||
563 | struct switch_attr *new; |
||
564 | |||
565 | if (nla_parse(tb, SWITCH_ATTR_MAX - 1, genlmsg_attrdata(gnlh, 0), |
||
566 | genlmsg_attrlen(gnlh, 0), NULL) < 0) |
||
567 | goto done; |
||
568 | |||
569 | new = swlib_alloc(sizeof(struct switch_attr)); |
||
570 | if (!new) |
||
571 | goto done; |
||
572 | |||
573 | new->dev = arg->dev; |
||
574 | new->atype = arg->atype; |
||
575 | if (arg->prev) { |
||
576 | arg->prev->next = new; |
||
577 | } else { |
||
578 | arg->prev = *arg->head; |
||
579 | } |
||
580 | *arg->head = new; |
||
581 | arg->head = &new->next; |
||
582 | |||
583 | if (tb[SWITCH_ATTR_OP_ID]) |
||
584 | new->id = nla_get_u32(tb[SWITCH_ATTR_OP_ID]); |
||
585 | if (tb[SWITCH_ATTR_OP_TYPE]) |
||
586 | new->type = nla_get_u32(tb[SWITCH_ATTR_OP_TYPE]); |
||
587 | if (tb[SWITCH_ATTR_OP_NAME]) |
||
588 | new->name = strdup(nla_get_string(tb[SWITCH_ATTR_OP_NAME])); |
||
589 | if (tb[SWITCH_ATTR_OP_DESCRIPTION]) |
||
590 | new->description = strdup(nla_get_string(tb[SWITCH_ATTR_OP_DESCRIPTION])); |
||
591 | |||
592 | done: |
||
593 | return NL_SKIP; |
||
594 | } |
||
595 | |||
596 | int |
||
597 | swlib_scan(struct switch_dev *dev) |
||
598 | { |
||
599 | struct attrlist_arg arg; |
||
600 | |||
601 | if (dev->ops || dev->port_ops || dev->vlan_ops) |
||
602 | return 0; |
||
603 | |||
604 | arg.atype = SWLIB_ATTR_GROUP_GLOBAL; |
||
605 | arg.dev = dev; |
||
606 | arg.id = dev->id; |
||
607 | arg.prev = NULL; |
||
608 | arg.head = &dev->ops; |
||
609 | swlib_call(SWITCH_CMD_LIST_GLOBAL, add_attr, add_id, &arg); |
||
610 | |||
611 | arg.atype = SWLIB_ATTR_GROUP_PORT; |
||
612 | arg.prev = NULL; |
||
613 | arg.head = &dev->port_ops; |
||
614 | swlib_call(SWITCH_CMD_LIST_PORT, add_attr, add_id, &arg); |
||
615 | |||
616 | arg.atype = SWLIB_ATTR_GROUP_VLAN; |
||
617 | arg.prev = NULL; |
||
618 | arg.head = &dev->vlan_ops; |
||
619 | swlib_call(SWITCH_CMD_LIST_VLAN, add_attr, add_id, &arg); |
||
620 | |||
621 | return 0; |
||
622 | } |
||
623 | |||
624 | struct switch_attr *swlib_lookup_attr(struct switch_dev *dev, |
||
625 | enum swlib_attr_group atype, const char *name) |
||
626 | { |
||
627 | struct switch_attr *head; |
||
628 | |||
629 | if (!name || !dev) |
||
630 | return NULL; |
||
631 | |||
632 | switch(atype) { |
||
633 | case SWLIB_ATTR_GROUP_GLOBAL: |
||
634 | head = dev->ops; |
||
635 | break; |
||
636 | case SWLIB_ATTR_GROUP_PORT: |
||
637 | head = dev->port_ops; |
||
638 | break; |
||
639 | case SWLIB_ATTR_GROUP_VLAN: |
||
640 | head = dev->vlan_ops; |
||
641 | break; |
||
642 | } |
||
643 | while(head) { |
||
644 | if (!strcmp(name, head->name)) |
||
645 | return head; |
||
646 | head = head->next; |
||
647 | } |
||
648 | |||
649 | return NULL; |
||
650 | } |
||
651 | |||
652 | static void |
||
653 | swlib_priv_free(void) |
||
654 | { |
||
655 | if (family) |
||
656 | nl_object_put((struct nl_object*)family); |
||
657 | if (cache) |
||
658 | nl_cache_free(cache); |
||
659 | if (handle) |
||
660 | nl_socket_free(handle); |
||
661 | family = NULL; |
||
662 | handle = NULL; |
||
663 | cache = NULL; |
||
664 | } |
||
665 | |||
666 | static int |
||
667 | swlib_priv_init(void) |
||
668 | { |
||
669 | int ret; |
||
670 | |||
671 | handle = nl_socket_alloc(); |
||
672 | if (!handle) { |
||
673 | DPRINTF("Failed to create handle\n"); |
||
674 | goto err; |
||
675 | } |
||
676 | |||
677 | if (genl_connect(handle)) { |
||
678 | DPRINTF("Failed to connect to generic netlink\n"); |
||
679 | goto err; |
||
680 | } |
||
681 | |||
682 | ret = genl_ctrl_alloc_cache(handle, &cache); |
||
683 | if (ret < 0) { |
||
684 | DPRINTF("Failed to allocate netlink cache\n"); |
||
685 | goto err; |
||
686 | } |
||
687 | |||
688 | family = genl_ctrl_search_by_name(cache, "switch"); |
||
689 | if (!family) { |
||
690 | DPRINTF("Switch API not present\n"); |
||
691 | goto err; |
||
692 | } |
||
693 | return 0; |
||
694 | |||
695 | err: |
||
696 | swlib_priv_free(); |
||
697 | return -EINVAL; |
||
698 | } |
||
699 | |||
700 | struct swlib_scan_arg { |
||
701 | const char *name; |
||
702 | struct switch_dev *head; |
||
703 | struct switch_dev *ptr; |
||
704 | }; |
||
705 | |||
706 | static int |
||
707 | add_port_map(struct switch_dev *dev, struct nlattr *nla) |
||
708 | { |
||
709 | struct nlattr *p; |
||
710 | int err = 0, idx = 0; |
||
711 | int remaining; |
||
712 | |||
713 | dev->maps = malloc(sizeof(struct switch_portmap) * dev->ports); |
||
714 | if (!dev->maps) |
||
715 | return -1; |
||
716 | memset(dev->maps, 0, sizeof(struct switch_portmap) * dev->ports); |
||
717 | |||
718 | nla_for_each_nested(p, nla, remaining) { |
||
719 | struct nlattr *tb[SWITCH_PORTMAP_MAX+1]; |
||
720 | |||
721 | if (idx >= dev->ports) |
||
722 | continue; |
||
723 | |||
724 | err = nla_parse_nested(tb, SWITCH_PORTMAP_MAX, p, portmap_policy); |
||
725 | if (err < 0) |
||
726 | continue; |
||
727 | |||
728 | |||
729 | if (tb[SWITCH_PORTMAP_SEGMENT] && tb[SWITCH_PORTMAP_VIRT]) { |
||
730 | dev->maps[idx].segment = strdup(nla_get_string(tb[SWITCH_PORTMAP_SEGMENT])); |
||
731 | dev->maps[idx].virt = nla_get_u32(tb[SWITCH_PORTMAP_VIRT]); |
||
732 | } |
||
733 | idx++; |
||
734 | } |
||
735 | |||
736 | out: |
||
737 | return err; |
||
738 | } |
||
739 | |||
740 | |||
741 | static int |
||
742 | add_switch(struct nl_msg *msg, void *arg) |
||
743 | { |
||
744 | struct swlib_scan_arg *sa = arg; |
||
745 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); |
||
746 | struct switch_dev *dev; |
||
747 | const char *name; |
||
748 | const char *alias; |
||
749 | |||
750 | if (nla_parse(tb, SWITCH_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0) |
||
751 | goto done; |
||
752 | |||
753 | if (!tb[SWITCH_ATTR_DEV_NAME]) |
||
754 | goto done; |
||
755 | |||
756 | name = nla_get_string(tb[SWITCH_ATTR_DEV_NAME]); |
||
757 | alias = nla_get_string(tb[SWITCH_ATTR_ALIAS]); |
||
758 | |||
759 | if (sa->name && (strcmp(name, sa->name) != 0) && (strcmp(alias, sa->name) != 0)) |
||
760 | goto done; |
||
761 | |||
762 | dev = swlib_alloc(sizeof(struct switch_dev)); |
||
763 | if (!dev) |
||
764 | goto done; |
||
765 | |||
766 | strncpy(dev->dev_name, name, IFNAMSIZ - 1); |
||
767 | dev->alias = strdup(alias); |
||
768 | if (tb[SWITCH_ATTR_ID]) |
||
769 | dev->id = nla_get_u32(tb[SWITCH_ATTR_ID]); |
||
770 | if (tb[SWITCH_ATTR_NAME]) |
||
771 | dev->name = strdup(nla_get_string(tb[SWITCH_ATTR_NAME])); |
||
772 | if (tb[SWITCH_ATTR_PORTS]) |
||
773 | dev->ports = nla_get_u32(tb[SWITCH_ATTR_PORTS]); |
||
774 | if (tb[SWITCH_ATTR_VLANS]) |
||
775 | dev->vlans = nla_get_u32(tb[SWITCH_ATTR_VLANS]); |
||
776 | if (tb[SWITCH_ATTR_CPU_PORT]) |
||
777 | dev->cpu_port = nla_get_u32(tb[SWITCH_ATTR_CPU_PORT]); |
||
778 | if (tb[SWITCH_ATTR_PORTMAP]) |
||
779 | add_port_map(dev, tb[SWITCH_ATTR_PORTMAP]); |
||
780 | |||
781 | if (!sa->head) { |
||
782 | sa->head = dev; |
||
783 | sa->ptr = dev; |
||
784 | } else { |
||
785 | sa->ptr->next = dev; |
||
786 | sa->ptr = dev; |
||
787 | } |
||
788 | |||
789 | refcount++; |
||
790 | done: |
||
791 | return NL_SKIP; |
||
792 | } |
||
793 | |||
794 | static int |
||
795 | list_switch(struct nl_msg *msg, void *arg) |
||
796 | { |
||
797 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); |
||
798 | |||
799 | if (nla_parse(tb, SWITCH_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0) |
||
800 | goto done; |
||
801 | |||
802 | if (!tb[SWITCH_ATTR_DEV_NAME] || !tb[SWITCH_ATTR_NAME]) |
||
803 | goto done; |
||
804 | |||
805 | printf("Found: %s - %s\n", nla_get_string(tb[SWITCH_ATTR_DEV_NAME]), |
||
806 | nla_get_string(tb[SWITCH_ATTR_ALIAS])); |
||
807 | |||
808 | done: |
||
809 | return NL_SKIP; |
||
810 | } |
||
811 | |||
812 | void |
||
813 | swlib_list(void) |
||
814 | { |
||
815 | if (swlib_priv_init() < 0) |
||
816 | return; |
||
817 | swlib_call(SWITCH_CMD_GET_SWITCH, list_switch, NULL, NULL); |
||
818 | swlib_priv_free(); |
||
819 | } |
||
820 | |||
821 | void |
||
822 | swlib_print_portmap(struct switch_dev *dev, char *segment) |
||
823 | { |
||
824 | int i; |
||
825 | |||
826 | if (segment) { |
||
827 | if (!strcmp(segment, "cpu")) { |
||
828 | printf("%d ", dev->cpu_port); |
||
829 | } else if (!strcmp(segment, "disabled")) { |
||
830 | for (i = 0; i < dev->ports; i++) |
||
831 | if (!dev->maps[i].segment) |
||
832 | printf("%d ", i); |
||
833 | } else for (i = 0; i < dev->ports; i++) { |
||
834 | if (dev->maps[i].segment && !strcmp(dev->maps[i].segment, segment)) |
||
835 | printf("%d ", i); |
||
836 | } |
||
837 | } else { |
||
838 | printf("%s - %s\n", dev->dev_name, dev->name); |
||
839 | for (i = 0; i < dev->ports; i++) |
||
840 | if (i == dev->cpu_port) |
||
841 | printf("port%d:\tcpu\n", i); |
||
842 | else if (dev->maps[i].segment) |
||
843 | printf("port%d:\t%s.%d\n", i, dev->maps[i].segment, dev->maps[i].virt); |
||
844 | else |
||
845 | printf("port%d:\tdisabled\n", i); |
||
846 | } |
||
847 | } |
||
848 | |||
849 | struct switch_dev * |
||
850 | swlib_connect(const char *name) |
||
851 | { |
||
852 | struct swlib_scan_arg arg; |
||
853 | |||
854 | if (!refcount) { |
||
855 | if (swlib_priv_init() < 0) |
||
856 | return NULL; |
||
857 | }; |
||
858 | |||
859 | arg.head = NULL; |
||
860 | arg.ptr = NULL; |
||
861 | arg.name = name; |
||
862 | swlib_call(SWITCH_CMD_GET_SWITCH, add_switch, NULL, &arg); |
||
863 | |||
864 | if (!refcount) |
||
865 | swlib_priv_free(); |
||
866 | |||
867 | return arg.head; |
||
868 | } |
||
869 | |||
870 | static void |
||
871 | swlib_free_attributes(struct switch_attr **head) |
||
872 | { |
||
873 | struct switch_attr *a = *head; |
||
874 | struct switch_attr *next; |
||
875 | |||
876 | while (a) { |
||
877 | next = a->next; |
||
878 | free(a->name); |
||
879 | free(a->description); |
||
880 | free(a); |
||
881 | a = next; |
||
882 | } |
||
883 | *head = NULL; |
||
884 | } |
||
885 | |||
886 | static void |
||
887 | swlib_free_port_map(struct switch_dev *dev) |
||
888 | { |
||
889 | int i; |
||
890 | |||
891 | if (!dev || !dev->maps) |
||
892 | return; |
||
893 | |||
894 | for (i = 0; i < dev->ports; i++) |
||
895 | free(dev->maps[i].segment); |
||
896 | free(dev->maps); |
||
897 | } |
||
898 | |||
899 | void |
||
900 | swlib_free(struct switch_dev *dev) |
||
901 | { |
||
902 | swlib_free_attributes(&dev->ops); |
||
903 | swlib_free_attributes(&dev->port_ops); |
||
904 | swlib_free_attributes(&dev->vlan_ops); |
||
905 | swlib_free_port_map(dev); |
||
906 | free(dev->name); |
||
907 | free(dev->alias); |
||
908 | free(dev); |
||
909 | |||
910 | if (--refcount == 0) |
||
911 | swlib_priv_free(); |
||
912 | } |
||
913 | |||
914 | void |
||
915 | swlib_free_all(struct switch_dev *dev) |
||
916 | { |
||
917 | struct switch_dev *p; |
||
918 | |||
919 | while (dev) { |
||
920 | p = dev->next; |
||
921 | swlib_free(dev); |
||
922 | dev = p; |
||
923 | } |
||
924 | } |