source: branches/backfire/package/swconfig/src/swlib.c @ 27815

Last change on this file since 27815 was 27815, checked in by jogo, 5 years ago

[backfire] swconfig: Add generic switch identifiers

Backport of r27880.

File size: 13.3 KB
Line 
1/*
2 * swlib.c: Switch configuration API (user space part)
3 *
4 * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
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
38static struct nl_sock *handle;
39static struct nl_cache *cache;
40static struct genl_family *family;
41static struct nlattr *tb[SWITCH_ATTR_MAX];
42static int refcount = 0;
43
44static struct nla_policy port_policy[] = {
45        [SWITCH_PORT_ID] = { .type = NLA_U32 },
46        [SWITCH_PORT_FLAG_TAGGED] = { .type = NLA_FLAG },
47};
48
49static inline void *
50swlib_alloc(size_t size)
51{
52        void *ptr;
53
54        ptr = malloc(size);
55        if (!ptr)
56                goto done;
57        memset(ptr, 0, size);
58
59done:
60        return ptr;
61}
62
63static int
64wait_handler(struct nl_msg *msg, void *arg)
65{
66        int *finished = arg;
67
68        *finished = 1;
69        return NL_STOP;
70}
71
72/* helper function for performing netlink requests */
73static int
74swlib_call(int cmd, int (*call)(struct nl_msg *, void *),
75                int (*data)(struct nl_msg *, void *), void *arg)
76{
77        struct nl_msg *msg;
78        struct nl_cb *cb = NULL;
79        int finished;
80        int flags = 0;
81        int err;
82
83        msg = nlmsg_alloc();
84        if (!msg) {
85                fprintf(stderr, "Out of memory!\n");
86                exit(1);
87        }
88
89        if (!data)
90                flags |= NLM_F_DUMP;
91
92        genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, genl_family_get_id(family), 0, flags, cmd, 0);
93        if (data) {
94                if (data(msg, arg) < 0)
95                        goto nla_put_failure;
96        }
97
98        cb = nl_cb_alloc(NL_CB_CUSTOM);
99        if (!cb) {
100                fprintf(stderr, "nl_cb_alloc failed.\n");
101                exit(1);
102        }
103
104        err = nl_send_auto_complete(handle, msg);
105        if (err < 0) {
106                fprintf(stderr, "nl_send_auto_complete failed: %d\n", err);
107                goto out;
108        }
109
110        finished = 0;
111
112        if (call)
113                nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, call, arg);
114
115        if (data)
116                nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, wait_handler, &finished);
117        else
118                nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, wait_handler, &finished);
119
120        err = nl_recvmsgs(handle, cb);
121        if (err < 0) {
122                goto out;
123        }
124
125        if (!finished)
126                err = nl_wait_for_ack(handle);
127
128out:
129        if (cb)
130                nl_cb_put(cb);
131nla_put_failure:
132        nlmsg_free(msg);
133        return err;
134}
135
136static int
137send_attr(struct nl_msg *msg, void *arg)
138{
139        struct switch_val *val = arg;
140        struct switch_attr *attr = val->attr;
141
142        NLA_PUT_U32(msg, SWITCH_ATTR_ID, attr->dev->id);
143        NLA_PUT_U32(msg, SWITCH_ATTR_OP_ID, attr->id);
144        switch(attr->atype) {
145        case SWLIB_ATTR_GROUP_PORT:
146                NLA_PUT_U32(msg, SWITCH_ATTR_OP_PORT, val->port_vlan);
147                break;
148        case SWLIB_ATTR_GROUP_VLAN:
149                NLA_PUT_U32(msg, SWITCH_ATTR_OP_VLAN, val->port_vlan);
150                break;
151        default:
152                break;
153        }
154
155        return 0;
156
157nla_put_failure:
158        return -1;
159}
160
161static int
162store_port_val(struct nl_msg *msg, struct nlattr *nla, struct switch_val *val)
163{
164        struct nlattr *p;
165        int ports = val->attr->dev->ports;
166        int err = 0;
167        int remaining;
168
169        if (!val->value.ports)
170                val->value.ports = malloc(sizeof(struct switch_port) * ports);
171
172        nla_for_each_nested(p, nla, remaining) {
173                struct nlattr *tb[SWITCH_PORT_ATTR_MAX+1];
174                struct switch_port *port;
175
176                if (val->len >= ports)
177                        break;
178
179                err = nla_parse_nested(tb, SWITCH_PORT_ATTR_MAX, p, port_policy);
180                if (err < 0)
181                        goto out;
182
183                if (!tb[SWITCH_PORT_ID])
184                        continue;
185
186                port = &val->value.ports[val->len];
187                port->id = nla_get_u32(tb[SWITCH_PORT_ID]);
188                port->flags = 0;
189                if (tb[SWITCH_PORT_FLAG_TAGGED])
190                        port->flags |= SWLIB_PORT_FLAG_TAGGED;
191
192                val->len++;
193        }
194
195out:
196        return err;
197}
198
199static int
200store_val(struct nl_msg *msg, void *arg)
201{
202        struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
203        struct switch_val *val = arg;
204        struct switch_attr *attr = val->attr;
205
206        if (!val)
207                goto error;
208
209        if (nla_parse(tb, SWITCH_ATTR_MAX - 1, genlmsg_attrdata(gnlh, 0),
210                        genlmsg_attrlen(gnlh, 0), NULL) < 0) {
211                goto error;
212        }
213
214        if (tb[SWITCH_ATTR_OP_VALUE_INT])
215                val->value.i = nla_get_u32(tb[SWITCH_ATTR_OP_VALUE_INT]);
216        else if (tb[SWITCH_ATTR_OP_VALUE_STR])
217                val->value.s = strdup(nla_get_string(tb[SWITCH_ATTR_OP_VALUE_STR]));
218        else if (tb[SWITCH_ATTR_OP_VALUE_PORTS])
219                val->err = store_port_val(msg, tb[SWITCH_ATTR_OP_VALUE_PORTS], val);
220
221        val->err = 0;
222        return 0;
223
224error:
225        return NL_SKIP;
226}
227
228int
229swlib_get_attr(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val)
230{
231        int cmd;
232        int err;
233
234        switch(attr->atype) {
235        case SWLIB_ATTR_GROUP_GLOBAL:
236                cmd = SWITCH_CMD_GET_GLOBAL;
237                break;
238        case SWLIB_ATTR_GROUP_PORT:
239                cmd = SWITCH_CMD_GET_PORT;
240                break;
241        case SWLIB_ATTR_GROUP_VLAN:
242                cmd = SWITCH_CMD_GET_VLAN;
243                break;
244        default:
245                return -EINVAL;
246        }
247
248        memset(&val->value, 0, sizeof(val->value));
249        val->len = 0;
250        val->attr = attr;
251        val->err = -EINVAL;
252        err = swlib_call(cmd, store_val, send_attr, val);
253        if (!err)
254                err = val->err;
255
256        return err;
257}
258
259static int
260send_attr_ports(struct nl_msg *msg, struct switch_val *val)
261{
262        struct nlattr *n;
263        int i;
264
265        /* TODO implement multipart? */
266        if (val->len == 0)
267                goto done;
268        n = nla_nest_start(msg, SWITCH_ATTR_OP_VALUE_PORTS);
269        if (!n)
270                goto nla_put_failure;
271        for (i = 0; i < val->len; i++) {
272                struct switch_port *port = &val->value.ports[i];
273                struct nlattr *np;
274
275                np = nla_nest_start(msg, SWITCH_ATTR_PORT);
276                if (!np)
277                        goto nla_put_failure;
278
279                NLA_PUT_U32(msg, SWITCH_PORT_ID, port->id);
280                if (port->flags & SWLIB_PORT_FLAG_TAGGED)
281                        NLA_PUT_FLAG(msg, SWITCH_PORT_FLAG_TAGGED);
282
283                nla_nest_end(msg, np);
284        }
285        nla_nest_end(msg, n);
286done:
287        return 0;
288
289nla_put_failure:
290        return -1;
291}
292
293static int
294send_attr_val(struct nl_msg *msg, void *arg)
295{
296        struct switch_val *val = arg;
297        struct switch_attr *attr = val->attr;
298
299        if (send_attr(msg, arg))
300                goto nla_put_failure;
301
302        switch(attr->type) {
303        case SWITCH_TYPE_NOVAL:
304                break;
305        case SWITCH_TYPE_INT:
306                NLA_PUT_U32(msg, SWITCH_ATTR_OP_VALUE_INT, val->value.i);
307                break;
308        case SWITCH_TYPE_STRING:
309                if (!val->value.s)
310                        goto nla_put_failure;
311                NLA_PUT_STRING(msg, SWITCH_ATTR_OP_VALUE_STR, val->value.s);
312                break;
313        case SWITCH_TYPE_PORTS:
314                if (send_attr_ports(msg, val) < 0)
315                        goto nla_put_failure;
316                break;
317        default:
318                goto nla_put_failure;
319        }
320        return 0;
321
322nla_put_failure:
323        return -1;
324}
325
326int
327swlib_set_attr(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val)
328{
329        int cmd;
330
331        switch(attr->atype) {
332        case SWLIB_ATTR_GROUP_GLOBAL:
333                cmd = SWITCH_CMD_SET_GLOBAL;
334                break;
335        case SWLIB_ATTR_GROUP_PORT:
336                cmd = SWITCH_CMD_SET_PORT;
337                break;
338        case SWLIB_ATTR_GROUP_VLAN:
339                cmd = SWITCH_CMD_SET_VLAN;
340                break;
341        default:
342                return -EINVAL;
343        }
344
345        val->attr = attr;
346        return swlib_call(cmd, NULL, send_attr_val, val);
347}
348
349int swlib_set_attr_string(struct switch_dev *dev, struct switch_attr *a, int port_vlan, const char *str)
350{
351        struct switch_port *ports;
352        struct switch_val val;
353        char *ptr;
354
355        memset(&val, 0, sizeof(val));
356        val.port_vlan = port_vlan;
357        switch(a->type) {
358        case SWITCH_TYPE_INT:
359                val.value.i = atoi(str);
360                break;
361        case SWITCH_TYPE_STRING:
362                val.value.s = str;
363                break;
364        case SWITCH_TYPE_PORTS:
365                ports = alloca(sizeof(struct switch_port) * dev->ports);
366                memset(ports, 0, sizeof(struct switch_port) * dev->ports);
367                val.len = 0;
368                ptr = (char *)str;
369                while(ptr && *ptr)
370                {
371                        while(*ptr && isspace(*ptr))
372                                ptr++;
373
374                        if (!*ptr)
375                                break;
376
377                        if (!isdigit(*ptr))
378                                return -1;
379
380                        if (val.len >= dev->ports)
381                                return -1;
382
383                        ports[val.len].flags = 0;
384                        ports[val.len].id = strtoul(ptr, &ptr, 10);
385                        while(*ptr && !isspace(*ptr)) {
386                                if (*ptr == 't')
387                                        ports[val.len].flags |= SWLIB_PORT_FLAG_TAGGED;
388                                else
389                                        return -1;
390
391                                ptr++;
392                        }
393                        if (*ptr)
394                                ptr++;
395                        val.len++;
396                }
397                val.value.ports = ports;
398                break;
399        case SWITCH_TYPE_NOVAL:
400                if (str && !strcmp(str, "0"))
401                        return 0;
402
403                break;
404        default:
405                return -1;
406        }
407        return swlib_set_attr(dev, a, &val);
408}
409
410
411struct attrlist_arg {
412        int id;
413        int atype;
414        struct switch_dev *dev;
415        struct switch_attr *prev;
416        struct switch_attr **head;
417};
418
419static int
420add_id(struct nl_msg *msg, void *arg)
421{
422        struct attrlist_arg *l = arg;
423
424        NLA_PUT_U32(msg, SWITCH_ATTR_ID, l->id);
425
426        return 0;
427nla_put_failure:
428        return -1;
429}
430
431static int
432add_attr(struct nl_msg *msg, void *ptr)
433{
434        struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
435        struct attrlist_arg *arg = ptr;
436        struct switch_attr *new;
437
438        if (nla_parse(tb, SWITCH_ATTR_MAX - 1, genlmsg_attrdata(gnlh, 0),
439                        genlmsg_attrlen(gnlh, 0), NULL) < 0)
440                goto done;
441
442        new = swlib_alloc(sizeof(struct switch_attr));
443        if (!new)
444                goto done;
445
446        new->dev = arg->dev;
447        new->atype = arg->atype;
448        if (arg->prev) {
449                arg->prev->next = new;
450        } else {
451                arg->prev = *arg->head;
452        }
453        *arg->head = new;
454        arg->head = &new->next;
455
456        if (tb[SWITCH_ATTR_OP_ID])
457                new->id = nla_get_u32(tb[SWITCH_ATTR_OP_ID]);
458        if (tb[SWITCH_ATTR_OP_TYPE])
459                new->type = nla_get_u32(tb[SWITCH_ATTR_OP_TYPE]);
460        if (tb[SWITCH_ATTR_OP_NAME])
461                new->name = strdup(nla_get_string(tb[SWITCH_ATTR_OP_NAME]));
462        if (tb[SWITCH_ATTR_OP_DESCRIPTION])
463                new->description = strdup(nla_get_string(tb[SWITCH_ATTR_OP_DESCRIPTION]));
464
465done:
466        return NL_SKIP;
467}
468
469int
470swlib_scan(struct switch_dev *dev)
471{
472        struct attrlist_arg arg;
473
474        if (dev->ops || dev->port_ops || dev->vlan_ops)
475                return 0;
476
477        arg.atype = SWLIB_ATTR_GROUP_GLOBAL;
478        arg.dev = dev;
479        arg.id = dev->id;
480        arg.prev = NULL;
481        arg.head = &dev->ops;
482        swlib_call(SWITCH_CMD_LIST_GLOBAL, add_attr, add_id, &arg);
483
484        arg.atype = SWLIB_ATTR_GROUP_PORT;
485        arg.prev = NULL;
486        arg.head = &dev->port_ops;
487        swlib_call(SWITCH_CMD_LIST_PORT, add_attr, add_id, &arg);
488
489        arg.atype = SWLIB_ATTR_GROUP_VLAN;
490        arg.prev = NULL;
491        arg.head = &dev->vlan_ops;
492        swlib_call(SWITCH_CMD_LIST_VLAN, add_attr, add_id, &arg);
493
494        return 0;
495}
496
497struct switch_attr *swlib_lookup_attr(struct switch_dev *dev,
498                enum swlib_attr_group atype, const char *name)
499{
500        struct switch_attr *head;
501
502        if (!name || !dev)
503                return NULL;
504
505        switch(atype) {
506        case SWLIB_ATTR_GROUP_GLOBAL:
507                head = dev->ops;
508                break;
509        case SWLIB_ATTR_GROUP_PORT:
510                head = dev->port_ops;
511                break;
512        case SWLIB_ATTR_GROUP_VLAN:
513                head = dev->vlan_ops;
514                break;
515        }
516        while(head) {
517                if (!strcmp(name, head->name))
518                        return head;
519                head = head->next;
520        }
521
522        return NULL;
523}
524
525static void
526swlib_priv_free(void)
527{
528        if (cache)
529                nl_cache_free(cache);
530        if (handle)
531                nl_socket_free(handle);
532        handle = NULL;
533        cache = NULL;
534}
535
536static int
537swlib_priv_init(void)
538{
539        int ret;
540
541        handle = nl_socket_alloc();
542        if (!handle) {
543                DPRINTF("Failed to create handle\n");
544                goto err;
545        }
546
547        if (genl_connect(handle)) {
548                DPRINTF("Failed to connect to generic netlink\n");
549                goto err;
550        }
551
552        ret = genl_ctrl_alloc_cache(handle, &cache);
553        if (ret < 0) {
554                DPRINTF("Failed to allocate netlink cache\n");
555                goto err;
556        }
557
558        family = genl_ctrl_search_by_name(cache, "switch");
559        if (!family) {
560                DPRINTF("Switch API not present\n");
561                goto err;
562        }
563        return 0;
564
565err:
566        swlib_priv_free();
567        return -EINVAL;
568}
569
570struct swlib_scan_arg {
571        const char *name;
572        struct switch_dev *head;
573        struct switch_dev *ptr;
574};
575
576static int
577add_switch(struct nl_msg *msg, void *arg)
578{
579        struct swlib_scan_arg *sa = arg;
580        struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
581        struct switch_dev *dev;
582        const char *name;
583        const char *alias;
584
585        if (nla_parse(tb, SWITCH_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0)
586                goto done;
587
588        if (!tb[SWITCH_ATTR_DEV_NAME])
589                goto done;
590
591        name = nla_get_string(tb[SWITCH_ATTR_DEV_NAME]);
592        alias = nla_get_string(tb[SWITCH_ATTR_ALIAS]);
593
594        if (sa->name && (strcmp(name, sa->name) != 0) && (strcmp(alias, sa->name) != 0))
595                goto done;
596
597        dev = swlib_alloc(sizeof(struct switch_dev));
598        if (!dev)
599                goto done;
600
601        strncpy(dev->dev_name, name, IFNAMSIZ - 1);
602        dev->alias = strdup(alias);
603        if (tb[SWITCH_ATTR_ID])
604                dev->id = nla_get_u32(tb[SWITCH_ATTR_ID]);
605        if (tb[SWITCH_ATTR_NAME])
606                dev->name = strdup(nla_get_string(tb[SWITCH_ATTR_NAME]));
607        if (tb[SWITCH_ATTR_PORTS])
608                dev->ports = nla_get_u32(tb[SWITCH_ATTR_PORTS]);
609        if (tb[SWITCH_ATTR_VLANS])
610                dev->vlans = nla_get_u32(tb[SWITCH_ATTR_VLANS]);
611        if (tb[SWITCH_ATTR_CPU_PORT])
612                dev->cpu_port = nla_get_u32(tb[SWITCH_ATTR_CPU_PORT]);
613
614        if (!sa->head) {
615                sa->head = dev;
616                sa->ptr = dev;
617        } else {
618                sa->ptr->next = dev;
619                sa->ptr = dev;
620        }
621
622        refcount++;
623done:
624        return NL_SKIP;
625}
626
627
628struct switch_dev *
629swlib_connect(const char *name)
630{
631        struct swlib_scan_arg arg;
632        int err;
633
634        if (!refcount) {
635                if (swlib_priv_init() < 0)
636                        return NULL;
637        };
638
639        arg.head = NULL;
640        arg.ptr = NULL;
641        arg.name = name;
642        swlib_call(SWITCH_CMD_GET_SWITCH, add_switch, NULL, &arg);
643
644        if (!refcount)
645                swlib_priv_free();
646
647        return arg.head;
648}
649
650static void
651swlib_free_attributes(struct switch_attr **head)
652{
653        struct switch_attr *a = *head;
654        struct switch_attr *next;
655
656        while (a) {
657                next = a->next;
658                free(a);
659                a = next;
660        }
661        *head = NULL;
662}
663
664void
665swlib_free(struct switch_dev *dev)
666{
667        swlib_free_attributes(&dev->ops);
668        swlib_free_attributes(&dev->port_ops);
669        swlib_free_attributes(&dev->vlan_ops);
670        free(dev);
671
672        if (--refcount == 0)
673                swlib_priv_free();
674}
675
676void
677swlib_free_all(struct switch_dev *dev)
678{
679        struct switch_dev *p;
680
681        while (dev) {
682                p = dev->next;
683                swlib_free(dev);
684                dev = p;
685        }
686}
Note: See TracBrowser for help on using the repository browser.