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