2 * swlib.c: Switch configuration API (user space part)
4 * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
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.
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.
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <linux/switch.h>
27 #include <netlink/netlink.h>
28 #include <netlink/genl/genl.h>
29 #include <netlink/genl/family.h>
33 #define DPRINTF(fmt, ...) fprintf(stderr, "%s(%d): " fmt, __func__, __LINE__, ##__VA_ARGS__)
35 #define DPRINTF(fmt, ...) do {} while (0)
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;
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
},
49 static struct nla_policy portmap_policy
[SWITCH_PORTMAP_MAX
] = {
50 [SWITCH_PORTMAP_SEGMENT
] = { .type
= NLA_STRING
},
51 [SWITCH_PORTMAP_VIRT
] = { .type
= NLA_U32
},
55 swlib_alloc(size_t size
)
69 wait_handler(struct nl_msg
*msg
, void *arg
)
77 /* helper function for performing netlink requests */
79 swlib_call(int cmd
, int (*call
)(struct nl_msg
*, void *),
80 int (*data
)(struct nl_msg
*, void *), void *arg
)
83 struct nl_cb
*cb
= NULL
;
90 fprintf(stderr
, "Out of memory!\n");
97 genlmsg_put(msg
, NL_AUTO_PID
, NL_AUTO_SEQ
, genl_family_get_id(family
), 0, flags
, cmd
, 0);
99 if (data(msg
, arg
) < 0)
100 goto nla_put_failure
;
103 cb
= nl_cb_alloc(NL_CB_CUSTOM
);
105 fprintf(stderr
, "nl_cb_alloc failed.\n");
109 err
= nl_send_auto_complete(handle
, msg
);
111 fprintf(stderr
, "nl_send_auto_complete failed: %d\n", err
);
118 nl_cb_set(cb
, NL_CB_VALID
, NL_CB_CUSTOM
, call
, arg
);
121 nl_cb_set(cb
, NL_CB_ACK
, NL_CB_CUSTOM
, wait_handler
, &finished
);
123 nl_cb_set(cb
, NL_CB_FINISH
, NL_CB_CUSTOM
, wait_handler
, &finished
);
125 err
= nl_recvmsgs(handle
, cb
);
131 err
= nl_wait_for_ack(handle
);
142 send_attr(struct nl_msg
*msg
, void *arg
)
144 struct switch_val
*val
= arg
;
145 struct switch_attr
*attr
= val
->attr
;
147 NLA_PUT_U32(msg
, SWITCH_ATTR_ID
, attr
->dev
->id
);
148 NLA_PUT_U32(msg
, SWITCH_ATTR_OP_ID
, attr
->id
);
149 switch(attr
->atype
) {
150 case SWLIB_ATTR_GROUP_PORT
:
151 NLA_PUT_U32(msg
, SWITCH_ATTR_OP_PORT
, val
->port_vlan
);
153 case SWLIB_ATTR_GROUP_VLAN
:
154 NLA_PUT_U32(msg
, SWITCH_ATTR_OP_VLAN
, val
->port_vlan
);
167 store_port_val(struct nl_msg
*msg
, struct nlattr
*nla
, struct switch_val
*val
)
170 int ports
= val
->attr
->dev
->ports
;
174 if (!val
->value
.ports
)
175 val
->value
.ports
= malloc(sizeof(struct switch_port
) * ports
);
177 nla_for_each_nested(p
, nla
, remaining
) {
178 struct nlattr
*tb
[SWITCH_PORT_ATTR_MAX
+1];
179 struct switch_port
*port
;
181 if (val
->len
>= ports
)
184 err
= nla_parse_nested(tb
, SWITCH_PORT_ATTR_MAX
, p
, port_policy
);
188 if (!tb
[SWITCH_PORT_ID
])
191 port
= &val
->value
.ports
[val
->len
];
192 port
->id
= nla_get_u32(tb
[SWITCH_PORT_ID
]);
194 if (tb
[SWITCH_PORT_FLAG_TAGGED
])
195 port
->flags
|= SWLIB_PORT_FLAG_TAGGED
;
205 store_val(struct nl_msg
*msg
, void *arg
)
207 struct genlmsghdr
*gnlh
= nlmsg_data(nlmsg_hdr(msg
));
208 struct switch_val
*val
= arg
;
209 struct switch_attr
*attr
= val
->attr
;
214 if (nla_parse(tb
, SWITCH_ATTR_MAX
- 1, genlmsg_attrdata(gnlh
, 0),
215 genlmsg_attrlen(gnlh
, 0), NULL
) < 0) {
219 if (tb
[SWITCH_ATTR_OP_VALUE_INT
])
220 val
->value
.i
= nla_get_u32(tb
[SWITCH_ATTR_OP_VALUE_INT
]);
221 else if (tb
[SWITCH_ATTR_OP_VALUE_STR
])
222 val
->value
.s
= strdup(nla_get_string(tb
[SWITCH_ATTR_OP_VALUE_STR
]));
223 else if (tb
[SWITCH_ATTR_OP_VALUE_PORTS
])
224 val
->err
= store_port_val(msg
, tb
[SWITCH_ATTR_OP_VALUE_PORTS
], val
);
234 swlib_get_attr(struct switch_dev
*dev
, struct switch_attr
*attr
, struct switch_val
*val
)
239 switch(attr
->atype
) {
240 case SWLIB_ATTR_GROUP_GLOBAL
:
241 cmd
= SWITCH_CMD_GET_GLOBAL
;
243 case SWLIB_ATTR_GROUP_PORT
:
244 cmd
= SWITCH_CMD_GET_PORT
;
246 case SWLIB_ATTR_GROUP_VLAN
:
247 cmd
= SWITCH_CMD_GET_VLAN
;
253 memset(&val
->value
, 0, sizeof(val
->value
));
257 err
= swlib_call(cmd
, store_val
, send_attr
, val
);
265 send_attr_ports(struct nl_msg
*msg
, struct switch_val
*val
)
270 /* TODO implement multipart? */
273 n
= nla_nest_start(msg
, SWITCH_ATTR_OP_VALUE_PORTS
);
275 goto nla_put_failure
;
276 for (i
= 0; i
< val
->len
; i
++) {
277 struct switch_port
*port
= &val
->value
.ports
[i
];
280 np
= nla_nest_start(msg
, SWITCH_ATTR_PORT
);
282 goto nla_put_failure
;
284 NLA_PUT_U32(msg
, SWITCH_PORT_ID
, port
->id
);
285 if (port
->flags
& SWLIB_PORT_FLAG_TAGGED
)
286 NLA_PUT_FLAG(msg
, SWITCH_PORT_FLAG_TAGGED
);
288 nla_nest_end(msg
, np
);
290 nla_nest_end(msg
, n
);
299 send_attr_val(struct nl_msg
*msg
, void *arg
)
301 struct switch_val
*val
= arg
;
302 struct switch_attr
*attr
= val
->attr
;
304 if (send_attr(msg
, arg
))
305 goto nla_put_failure
;
308 case SWITCH_TYPE_NOVAL
:
310 case SWITCH_TYPE_INT
:
311 NLA_PUT_U32(msg
, SWITCH_ATTR_OP_VALUE_INT
, val
->value
.i
);
313 case SWITCH_TYPE_STRING
:
315 goto nla_put_failure
;
316 NLA_PUT_STRING(msg
, SWITCH_ATTR_OP_VALUE_STR
, val
->value
.s
);
318 case SWITCH_TYPE_PORTS
:
319 if (send_attr_ports(msg
, val
) < 0)
320 goto nla_put_failure
;
323 goto nla_put_failure
;
332 swlib_set_attr(struct switch_dev
*dev
, struct switch_attr
*attr
, struct switch_val
*val
)
336 switch(attr
->atype
) {
337 case SWLIB_ATTR_GROUP_GLOBAL
:
338 cmd
= SWITCH_CMD_SET_GLOBAL
;
340 case SWLIB_ATTR_GROUP_PORT
:
341 cmd
= SWITCH_CMD_SET_PORT
;
343 case SWLIB_ATTR_GROUP_VLAN
:
344 cmd
= SWITCH_CMD_SET_VLAN
;
351 return swlib_call(cmd
, NULL
, send_attr_val
, val
);
354 int swlib_set_attr_string(struct switch_dev
*dev
, struct switch_attr
*a
, int port_vlan
, const char *str
)
356 struct switch_port
*ports
;
357 struct switch_val val
;
360 memset(&val
, 0, sizeof(val
));
361 val
.port_vlan
= port_vlan
;
363 case SWITCH_TYPE_INT
:
364 val
.value
.i
= atoi(str
);
366 case SWITCH_TYPE_STRING
:
369 case SWITCH_TYPE_PORTS
:
370 ports
= alloca(sizeof(struct switch_port
) * dev
->ports
);
371 memset(ports
, 0, sizeof(struct switch_port
) * dev
->ports
);
376 while(*ptr
&& isspace(*ptr
))
385 if (val
.len
>= dev
->ports
)
388 ports
[val
.len
].flags
= 0;
389 ports
[val
.len
].id
= strtoul(ptr
, &ptr
, 10);
390 while(*ptr
&& !isspace(*ptr
)) {
392 ports
[val
.len
].flags
|= SWLIB_PORT_FLAG_TAGGED
;
402 val
.value
.ports
= ports
;
404 case SWITCH_TYPE_NOVAL
:
405 if (str
&& !strcmp(str
, "0"))
412 return swlib_set_attr(dev
, a
, &val
);
416 struct attrlist_arg
{
419 struct switch_dev
*dev
;
420 struct switch_attr
*prev
;
421 struct switch_attr
**head
;
425 add_id(struct nl_msg
*msg
, void *arg
)
427 struct attrlist_arg
*l
= arg
;
429 NLA_PUT_U32(msg
, SWITCH_ATTR_ID
, l
->id
);
437 add_attr(struct nl_msg
*msg
, void *ptr
)
439 struct genlmsghdr
*gnlh
= nlmsg_data(nlmsg_hdr(msg
));
440 struct attrlist_arg
*arg
= ptr
;
441 struct switch_attr
*new;
443 if (nla_parse(tb
, SWITCH_ATTR_MAX
- 1, genlmsg_attrdata(gnlh
, 0),
444 genlmsg_attrlen(gnlh
, 0), NULL
) < 0)
447 new = swlib_alloc(sizeof(struct switch_attr
));
452 new->atype
= arg
->atype
;
454 arg
->prev
->next
= new;
456 arg
->prev
= *arg
->head
;
459 arg
->head
= &new->next
;
461 if (tb
[SWITCH_ATTR_OP_ID
])
462 new->id
= nla_get_u32(tb
[SWITCH_ATTR_OP_ID
]);
463 if (tb
[SWITCH_ATTR_OP_TYPE
])
464 new->type
= nla_get_u32(tb
[SWITCH_ATTR_OP_TYPE
]);
465 if (tb
[SWITCH_ATTR_OP_NAME
])
466 new->name
= strdup(nla_get_string(tb
[SWITCH_ATTR_OP_NAME
]));
467 if (tb
[SWITCH_ATTR_OP_DESCRIPTION
])
468 new->description
= strdup(nla_get_string(tb
[SWITCH_ATTR_OP_DESCRIPTION
]));
475 swlib_scan(struct switch_dev
*dev
)
477 struct attrlist_arg arg
;
479 if (dev
->ops
|| dev
->port_ops
|| dev
->vlan_ops
)
482 arg
.atype
= SWLIB_ATTR_GROUP_GLOBAL
;
486 arg
.head
= &dev
->ops
;
487 swlib_call(SWITCH_CMD_LIST_GLOBAL
, add_attr
, add_id
, &arg
);
489 arg
.atype
= SWLIB_ATTR_GROUP_PORT
;
491 arg
.head
= &dev
->port_ops
;
492 swlib_call(SWITCH_CMD_LIST_PORT
, add_attr
, add_id
, &arg
);
494 arg
.atype
= SWLIB_ATTR_GROUP_VLAN
;
496 arg
.head
= &dev
->vlan_ops
;
497 swlib_call(SWITCH_CMD_LIST_VLAN
, add_attr
, add_id
, &arg
);
502 struct switch_attr
*swlib_lookup_attr(struct switch_dev
*dev
,
503 enum swlib_attr_group atype
, const char *name
)
505 struct switch_attr
*head
;
511 case SWLIB_ATTR_GROUP_GLOBAL
:
514 case SWLIB_ATTR_GROUP_PORT
:
515 head
= dev
->port_ops
;
517 case SWLIB_ATTR_GROUP_VLAN
:
518 head
= dev
->vlan_ops
;
522 if (!strcmp(name
, head
->name
))
531 swlib_priv_free(void)
534 nl_cache_free(cache
);
536 nl_socket_free(handle
);
542 swlib_priv_init(void)
546 handle
= nl_socket_alloc();
548 DPRINTF("Failed to create handle\n");
552 if (genl_connect(handle
)) {
553 DPRINTF("Failed to connect to generic netlink\n");
557 ret
= genl_ctrl_alloc_cache(handle
, &cache
);
559 DPRINTF("Failed to allocate netlink cache\n");
563 family
= genl_ctrl_search_by_name(cache
, "switch");
565 DPRINTF("Switch API not present\n");
575 struct swlib_scan_arg
{
577 struct switch_dev
*head
;
578 struct switch_dev
*ptr
;
582 add_port_map(struct switch_dev
*dev
, struct nlattr
*nla
)
585 int err
= 0, idx
= 0;
588 dev
->maps
= malloc(sizeof(struct switch_portmap
) * dev
->ports
);
591 memset(dev
->maps
, 0, sizeof(struct switch_portmap
) * dev
->ports
);
593 nla_for_each_nested(p
, nla
, remaining
) {
594 struct nlattr
*tb
[SWITCH_PORTMAP_MAX
+1];
596 if (idx
>= dev
->ports
)
599 err
= nla_parse_nested(tb
, SWITCH_PORTMAP_MAX
, p
, portmap_policy
);
604 if (tb
[SWITCH_PORTMAP_SEGMENT
] && tb
[SWITCH_PORTMAP_VIRT
]) {
605 dev
->maps
[idx
].segment
= strdup(nla_get_string(tb
[SWITCH_PORTMAP_SEGMENT
]));
606 dev
->maps
[idx
].virt
= nla_get_u32(tb
[SWITCH_PORTMAP_VIRT
]);
617 add_switch(struct nl_msg
*msg
, void *arg
)
619 struct swlib_scan_arg
*sa
= arg
;
620 struct genlmsghdr
*gnlh
= nlmsg_data(nlmsg_hdr(msg
));
621 struct switch_dev
*dev
;
625 if (nla_parse(tb
, SWITCH_ATTR_MAX
, genlmsg_attrdata(gnlh
, 0), genlmsg_attrlen(gnlh
, 0), NULL
) < 0)
628 if (!tb
[SWITCH_ATTR_DEV_NAME
])
631 name
= nla_get_string(tb
[SWITCH_ATTR_DEV_NAME
]);
632 alias
= nla_get_string(tb
[SWITCH_ATTR_ALIAS
]);
634 if (sa
->name
&& (strcmp(name
, sa
->name
) != 0) && (strcmp(alias
, sa
->name
) != 0))
637 dev
= swlib_alloc(sizeof(struct switch_dev
));
641 strncpy(dev
->dev_name
, name
, IFNAMSIZ
- 1);
642 dev
->alias
= strdup(alias
);
643 if (tb
[SWITCH_ATTR_ID
])
644 dev
->id
= nla_get_u32(tb
[SWITCH_ATTR_ID
]);
645 if (tb
[SWITCH_ATTR_NAME
])
646 dev
->name
= strdup(nla_get_string(tb
[SWITCH_ATTR_NAME
]));
647 if (tb
[SWITCH_ATTR_PORTS
])
648 dev
->ports
= nla_get_u32(tb
[SWITCH_ATTR_PORTS
]);
649 if (tb
[SWITCH_ATTR_VLANS
])
650 dev
->vlans
= nla_get_u32(tb
[SWITCH_ATTR_VLANS
]);
651 if (tb
[SWITCH_ATTR_CPU_PORT
])
652 dev
->cpu_port
= nla_get_u32(tb
[SWITCH_ATTR_CPU_PORT
]);
653 if (tb
[SWITCH_ATTR_PORTMAP
])
654 add_port_map(dev
, tb
[SWITCH_ATTR_PORTMAP
]);
670 list_switch(struct nl_msg
*msg
, void *arg
)
672 struct swlib_scan_arg
*sa
= arg
;
673 struct genlmsghdr
*gnlh
= nlmsg_data(nlmsg_hdr(msg
));
674 struct switch_dev
*dev
;
678 if (nla_parse(tb
, SWITCH_ATTR_MAX
, genlmsg_attrdata(gnlh
, 0), genlmsg_attrlen(gnlh
, 0), NULL
) < 0)
681 if (!tb
[SWITCH_ATTR_DEV_NAME
] || !tb
[SWITCH_ATTR_NAME
])
684 printf("Found: %s - %s\n", nla_get_string(tb
[SWITCH_ATTR_DEV_NAME
]),
685 nla_get_string(tb
[SWITCH_ATTR_ALIAS
]));
694 if (swlib_priv_init() < 0)
696 swlib_call(SWITCH_CMD_GET_SWITCH
, list_switch
, NULL
, NULL
);
701 swlib_print_portmap(struct switch_dev
*dev
, char *segment
)
706 if (!strcmp(segment
, "cpu")) {
707 printf("%d ", dev
->cpu_port
);
708 } else if (!strcmp(segment
, "disabled")) {
709 for (i
= 0; i
< dev
->ports
; i
++)
710 if (!dev
->maps
[i
].segment
)
712 } else for (i
= 0; i
< dev
->ports
; i
++) {
713 if (dev
->maps
[i
].segment
&& !strcmp(dev
->maps
[i
].segment
, segment
))
717 printf("%s - %s\n", dev
->dev_name
, dev
->name
);
718 for (i
= 0; i
< dev
->ports
; i
++)
719 if (i
== dev
->cpu_port
)
720 printf("port%d:\tcpu\n", i
);
721 else if (dev
->maps
[i
].segment
)
722 printf("port%d:\t%s.%d\n", i
, dev
->maps
[i
].segment
, dev
->maps
[i
].virt
);
724 printf("port%d:\tdisabled\n", i
);
729 swlib_connect(const char *name
)
731 struct swlib_scan_arg arg
;
735 if (swlib_priv_init() < 0)
742 swlib_call(SWITCH_CMD_GET_SWITCH
, add_switch
, NULL
, &arg
);
751 swlib_free_attributes(struct switch_attr
**head
)
753 struct switch_attr
*a
= *head
;
754 struct switch_attr
*next
;
765 swlib_free(struct switch_dev
*dev
)
767 swlib_free_attributes(&dev
->ops
);
768 swlib_free_attributes(&dev
->port_ops
);
769 swlib_free_attributes(&dev
->vlan_ops
);
777 swlib_free_all(struct switch_dev
*dev
)
779 struct switch_dev
*p
;