Root/package/swconfig/src/swlib.c

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            ports[val.len].flags = 0;
372            ports[val.len].id = strtoul(ptr, &ptr, 10);
373            while(*ptr && !isspace(*ptr)) {
374                if (*ptr == 't')
375                    ports[val.len].flags |= SWLIB_PORT_FLAG_TAGGED;
376                ptr++;
377            }
378            if (*ptr)
379                ptr++;
380            val.len++;
381        }
382        val.value.ports = ports;
383        break;
384    case SWITCH_TYPE_NOVAL:
385        break;
386    default:
387        return -1;
388    }
389    return swlib_set_attr(dev, a, &val);
390}
391
392
393struct attrlist_arg {
394    int id;
395    int atype;
396    struct switch_dev *dev;
397    struct switch_attr *prev;
398    struct switch_attr **head;
399};
400
401static int
402add_id(struct nl_msg *msg, void *arg)
403{
404    struct attrlist_arg *l = arg;
405
406    NLA_PUT_U32(msg, SWITCH_ATTR_ID, l->id);
407
408    return 0;
409nla_put_failure:
410    return -1;
411}
412
413static int
414add_attr(struct nl_msg *msg, void *ptr)
415{
416    struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
417    struct attrlist_arg *arg = ptr;
418    struct switch_attr *new;
419
420    if (nla_parse(tb, SWITCH_ATTR_MAX - 1, genlmsg_attrdata(gnlh, 0),
421            genlmsg_attrlen(gnlh, 0), NULL) < 0)
422        goto done;
423
424    new = swlib_alloc(sizeof(struct switch_attr));
425    if (!new)
426        goto done;
427
428    new->dev = arg->dev;
429    new->atype = arg->atype;
430    if (arg->prev) {
431        arg->prev->next = new;
432    } else {
433        arg->prev = *arg->head;
434    }
435    *arg->head = new;
436    arg->head = &new->next;
437
438    if (tb[SWITCH_ATTR_OP_ID])
439        new->id = nla_get_u32(tb[SWITCH_ATTR_OP_ID]);
440    if (tb[SWITCH_ATTR_OP_TYPE])
441        new->type = nla_get_u32(tb[SWITCH_ATTR_OP_TYPE]);
442    if (tb[SWITCH_ATTR_OP_NAME])
443        new->name = strdup(nla_get_string(tb[SWITCH_ATTR_OP_NAME]));
444    if (tb[SWITCH_ATTR_OP_DESCRIPTION])
445        new->description = strdup(nla_get_string(tb[SWITCH_ATTR_OP_DESCRIPTION]));
446
447done:
448    return NL_SKIP;
449}
450
451int
452swlib_scan(struct switch_dev *dev)
453{
454    struct attrlist_arg arg;
455
456    if (dev->ops || dev->port_ops || dev->vlan_ops)
457        return 0;
458
459    arg.atype = SWLIB_ATTR_GROUP_GLOBAL;
460    arg.dev = dev;
461    arg.id = dev->id;
462    arg.prev = NULL;
463    arg.head = &dev->ops;
464    swlib_call(SWITCH_CMD_LIST_GLOBAL, add_attr, add_id, &arg);
465
466    arg.atype = SWLIB_ATTR_GROUP_PORT;
467    arg.prev = NULL;
468    arg.head = &dev->port_ops;
469    swlib_call(SWITCH_CMD_LIST_PORT, add_attr, add_id, &arg);
470
471    arg.atype = SWLIB_ATTR_GROUP_VLAN;
472    arg.prev = NULL;
473    arg.head = &dev->vlan_ops;
474    swlib_call(SWITCH_CMD_LIST_VLAN, add_attr, add_id, &arg);
475
476    return 0;
477}
478
479struct switch_attr *swlib_lookup_attr(struct switch_dev *dev,
480        enum swlib_attr_group atype, const char *name)
481{
482    struct switch_attr *head;
483
484    if (!name || !dev)
485        return NULL;
486
487    switch(atype) {
488    case SWLIB_ATTR_GROUP_GLOBAL:
489        head = dev->ops;
490        break;
491    case SWLIB_ATTR_GROUP_PORT:
492        head = dev->port_ops;
493        break;
494    case SWLIB_ATTR_GROUP_VLAN:
495        head = dev->vlan_ops;
496        break;
497    }
498    while(head) {
499        if (!strcmp(name, head->name))
500            return head;
501        head = head->next;
502    }
503
504    return NULL;
505}
506
507static void
508swlib_priv_free(void)
509{
510    if (cache)
511        nl_cache_free(cache);
512    if (handle)
513        nl_socket_free(handle);
514    handle = NULL;
515    cache = NULL;
516}
517
518static int
519swlib_priv_init(void)
520{
521    int ret;
522
523    handle = nl_socket_alloc();
524    if (!handle) {
525        DPRINTF("Failed to create handle\n");
526        goto err;
527    }
528
529    if (genl_connect(handle)) {
530        DPRINTF("Failed to connect to generic netlink\n");
531        goto err;
532    }
533
534    ret = genl_ctrl_alloc_cache(handle, &cache);
535    if (ret < 0) {
536        DPRINTF("Failed to allocate netlink cache\n");
537        goto err;
538    }
539
540    family = genl_ctrl_search_by_name(cache, "switch");
541    if (!family) {
542        DPRINTF("Switch API not present\n");
543        goto err;
544    }
545    return 0;
546
547err:
548    swlib_priv_free();
549    return -EINVAL;
550}
551
552struct swlib_scan_arg {
553    const char *name;
554    struct switch_dev *head;
555    struct switch_dev *ptr;
556};
557
558static int
559add_switch(struct nl_msg *msg, void *arg)
560{
561    struct swlib_scan_arg *sa = arg;
562    struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
563    struct switch_dev *dev;
564    const char *name;
565
566    if (nla_parse(tb, SWITCH_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0)
567        goto done;
568
569    if (!tb[SWITCH_ATTR_DEV_NAME])
570        goto done;
571
572    name = nla_get_string(tb[SWITCH_ATTR_DEV_NAME]);
573    if (sa->name && (strcmp(name, sa->name) != 0))
574        goto done;
575
576    dev = swlib_alloc(sizeof(struct switch_dev));
577    if (!dev)
578        goto done;
579
580    dev->dev_name = strdup(name);
581    if (tb[SWITCH_ATTR_ID])
582        dev->id = nla_get_u32(tb[SWITCH_ATTR_ID]);
583    if (tb[SWITCH_ATTR_NAME])
584        dev->name = strdup(nla_get_string(tb[SWITCH_ATTR_NAME]));
585    if (tb[SWITCH_ATTR_PORTS])
586        dev->ports = nla_get_u32(tb[SWITCH_ATTR_PORTS]);
587    if (tb[SWITCH_ATTR_VLANS])
588        dev->vlans = nla_get_u32(tb[SWITCH_ATTR_VLANS]);
589    if (tb[SWITCH_ATTR_CPU_PORT])
590        dev->cpu_port = nla_get_u32(tb[SWITCH_ATTR_CPU_PORT]);
591
592    if (!sa->head) {
593        sa->head = dev;
594        sa->ptr = dev;
595    } else {
596        sa->ptr->next = dev;
597        sa->ptr = dev;
598    }
599
600    refcount++;
601done:
602    return NL_SKIP;
603}
604
605
606struct switch_dev *
607swlib_connect(const char *name)
608{
609    struct swlib_scan_arg arg;
610    int err;
611
612    if (!refcount) {
613        if (swlib_priv_init() < 0)
614            return NULL;
615    };
616
617    arg.head = NULL;
618    arg.ptr = NULL;
619    arg.name = name;
620    swlib_call(SWITCH_CMD_GET_SWITCH, add_switch, NULL, &arg);
621
622    if (!refcount)
623        swlib_priv_free();
624
625    return arg.head;
626}
627
628static void
629swlib_free_attributes(struct switch_attr **head)
630{
631    struct switch_attr *a = *head;
632    struct switch_attr *next;
633
634    while (a) {
635        next = a->next;
636        free(a);
637        a = next;
638    }
639    *head = NULL;
640}
641
642void
643swlib_free(struct switch_dev *dev)
644{
645    swlib_free_attributes(&dev->ops);
646    swlib_free_attributes(&dev->port_ops);
647    swlib_free_attributes(&dev->vlan_ops);
648    free(dev);
649
650    if (--refcount == 0)
651        swlib_priv_free();
652}
653
654void
655swlib_free_all(struct switch_dev *dev)
656{
657    struct switch_dev *p;
658
659    while (dev) {
660        p = dev->next;
661        swlib_free(dev);
662        dev = p;
663    }
664}
665

Archive Download this file



interactive