📄 swlib.c
字号:
/* * swlib.c: Switch configuration API (user space part) * * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */#include <stdio.h>#include <string.h>#include <stdlib.h>#include <inttypes.h>#include <errno.h>#include <stdint.h>#include <getopt.h>#include <sys/types.h>#include <sys/socket.h>#include <linux/switch.h>#include "swlib.h"//#define DEBUG 1#ifdef DEBUG#define DPRINTF(fmt, ...) fprintf(stderr, "%s(%d): " fmt, __func__, __LINE__, ##__VA_ARGS__)#else#define DPRINTF(fmt, ...) do {} while (0)#endifstatic struct nl_handle *handle;static struct nl_cache *cache;static struct genl_family *family;static struct nlattr *tb[SWITCH_ATTR_MAX];static int refcount = 0;static struct nla_policy port_policy[] = { [SWITCH_PORT_ID] = { .type = NLA_U32 }, [SWITCH_PORT_FLAG_TAGGED] = { .type = NLA_FLAG },};static inline void *swlib_alloc(size_t size){ void *ptr; ptr = malloc(size); if (!ptr) goto done; memset(ptr, 0, size);done: return ptr;}static intwait_handler(struct nl_msg *msg, void *arg){ int *finished = arg; *finished = 1; return NL_STOP;}/* helper function for performing netlink requests */static intswlib_call(int cmd, int (*call)(struct nl_msg *, void *), int (*data)(struct nl_msg *, void *), void *arg){ struct nl_msg *msg; struct nl_cb *cb = NULL; int finished; int flags = 0; int err; msg = nlmsg_alloc(); if (!msg) { fprintf(stderr, "Out of memory!\n"); exit(1); } if (!data) flags |= NLM_F_DUMP; genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, genl_family_get_id(family), 0, flags, cmd, 0); if (data) { if (data(msg, arg) < 0) goto nla_put_failure; } cb = nl_cb_alloc(NL_CB_CUSTOM); if (!cb) { fprintf(stderr, "nl_cb_alloc failed.\n"); exit(1); } err = nl_send_auto_complete(handle, msg); if (err < 0) { fprintf(stderr, "nl_send_auto_complete failed: %d\n", err); goto out; } finished = 0; if (call) nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, call, arg); if (data) nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, wait_handler, &finished); else nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, wait_handler, &finished); err = nl_recvmsgs(handle, cb); if (err < 0) { goto out; } if (!finished) err = nl_wait_for_ack(handle);out: if (cb) nl_cb_put(cb);nla_put_failure: nlmsg_free(msg); return err;}static intsend_attr(struct nl_msg *msg, void *arg){ struct switch_val *val = arg; struct switch_attr *attr = val->attr; NLA_PUT_U32(msg, SWITCH_ATTR_ID, attr->dev->id); NLA_PUT_U32(msg, SWITCH_ATTR_OP_ID, attr->id); switch(attr->atype) { case SWLIB_ATTR_GROUP_PORT: NLA_PUT_U32(msg, SWITCH_ATTR_OP_PORT, val->port_vlan); break; case SWLIB_ATTR_GROUP_VLAN: NLA_PUT_U32(msg, SWITCH_ATTR_OP_VLAN, val->port_vlan); break; default: break; } return 0;nla_put_failure: return -1;}static intstore_port_val(struct nl_msg *msg, struct nlattr *nla, struct switch_val *val){ struct nlattr *p; int ports = val->attr->dev->ports; int err = 0; int remaining; if (!val->value.ports) val->value.ports = malloc(sizeof(struct switch_port) * ports); nla_for_each_nested(p, nla, remaining) { struct nlattr *tb[SWITCH_PORT_ATTR_MAX+1]; struct switch_port *port; if (val->len >= ports) break; err = nla_parse_nested(tb, SWITCH_PORT_ATTR_MAX, p, port_policy); if (err < 0) goto out; if (!tb[SWITCH_PORT_ID]) continue; port = &val->value.ports[val->len]; port->id = nla_get_u32(tb[SWITCH_PORT_ID]); port->flags = 0; if (tb[SWITCH_PORT_FLAG_TAGGED]) port->flags |= SWLIB_PORT_FLAG_TAGGED; val->len++; }out: return err;}static intstore_val(struct nl_msg *msg, void *arg){ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct switch_val *val = arg; struct switch_attr *attr = val->attr; if (!val) goto error; if (nla_parse(tb, SWITCH_ATTR_MAX - 1, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0) { goto error; } if (tb[SWITCH_ATTR_OP_VALUE_INT]) val->value.i = nla_get_u32(tb[SWITCH_ATTR_OP_VALUE_INT]); else if (tb[SWITCH_ATTR_OP_VALUE_STR]) val->value.s = strdup(nla_get_string(tb[SWITCH_ATTR_OP_VALUE_STR])); else if (tb[SWITCH_ATTR_OP_VALUE_PORTS]) val->err = store_port_val(msg, tb[SWITCH_ATTR_OP_VALUE_PORTS], val); val->err = 0; return 0;error: return NL_SKIP;}intswlib_get_attr(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val){ int cmd; int err; switch(attr->atype) { case SWLIB_ATTR_GROUP_GLOBAL: cmd = SWITCH_CMD_GET_GLOBAL; break; case SWLIB_ATTR_GROUP_PORT: cmd = SWITCH_CMD_GET_PORT; break; case SWLIB_ATTR_GROUP_VLAN: cmd = SWITCH_CMD_GET_VLAN; break; default: return -EINVAL; } memset(&val->value, 0, sizeof(val->value)); val->len = 0; val->attr = attr; val->err = -EINVAL; err = swlib_call(cmd, store_val, send_attr, val); if (!err) err = val->err; return err;}static intsend_attr_ports(struct nl_msg *msg, struct switch_val *val){ struct nlattr *n; int i; /* TODO implement multipart? */ if (val->len == 0) goto done; n = nla_nest_start(msg, SWITCH_ATTR_OP_VALUE_PORTS); if (!n) goto nla_put_failure; for (i = 0; i < val->len; i++) { struct switch_port *port = &val->value.ports[i]; struct nlattr *np; np = nla_nest_start(msg, SWITCH_ATTR_PORT); if (!np) goto nla_put_failure; NLA_PUT_U32(msg, SWITCH_PORT_ID, port->id); if (port->flags & SWLIB_PORT_FLAG_TAGGED) NLA_PUT_FLAG(msg, SWITCH_PORT_FLAG_TAGGED); nla_nest_end(msg, np); } nla_nest_end(msg, n);done: return 0;nla_put_failure: return -1;}static intsend_attr_val(struct nl_msg *msg, void *arg){ struct switch_val *val = arg; struct switch_attr *attr = val->attr; if (send_attr(msg, arg)) goto nla_put_failure; switch(attr->type) { case SWITCH_TYPE_NOVAL: break; case SWITCH_TYPE_INT: NLA_PUT_U32(msg, SWITCH_ATTR_OP_VALUE_INT, val->value.i); break; case SWITCH_TYPE_STRING: if (!val->value.s) goto nla_put_failure; NLA_PUT_STRING(msg, SWITCH_ATTR_OP_VALUE_STR, val->value.s); break; case SWITCH_TYPE_PORTS: if (send_attr_ports(msg, val) < 0) goto nla_put_failure; break; default: goto nla_put_failure; } return 0;nla_put_failure: return -1;}intswlib_set_attr(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val){ int cmd; switch(attr->atype) { case SWLIB_ATTR_GROUP_GLOBAL: cmd = SWITCH_CMD_SET_GLOBAL; break; case SWLIB_ATTR_GROUP_PORT: cmd = SWITCH_CMD_SET_PORT; break; case SWLIB_ATTR_GROUP_VLAN: cmd = SWITCH_CMD_SET_VLAN; break; default: return -EINVAL; } val->attr = attr; return swlib_call(cmd, NULL, send_attr_val, val);}struct attrlist_arg { int id; int atype; struct switch_dev *dev; struct switch_attr *prev; struct switch_attr **head;};static intadd_id(struct nl_msg *msg, void *arg){ struct attrlist_arg *l = arg; NLA_PUT_U32(msg, SWITCH_ATTR_ID, l->id); return 0;nla_put_failure: return -1;}static intadd_attr(struct nl_msg *msg, void *ptr){ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct attrlist_arg *arg = ptr; struct switch_attr *new; if (nla_parse(tb, SWITCH_ATTR_MAX - 1, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0) goto done; new = swlib_alloc(sizeof(struct switch_attr)); if (!new) goto done; new->dev = arg->dev; new->atype = arg->atype; if (arg->prev) { arg->prev->next = new; } else { arg->prev = *arg->head; } *arg->head = new; arg->head = &new->next; if (tb[SWITCH_ATTR_OP_ID]) new->id = nla_get_u32(tb[SWITCH_ATTR_OP_ID]); if (tb[SWITCH_ATTR_OP_TYPE]) new->type = nla_get_u32(tb[SWITCH_ATTR_OP_TYPE]); if (tb[SWITCH_ATTR_OP_NAME]) new->name = strdup(nla_get_string(tb[SWITCH_ATTR_OP_NAME])); if (tb[SWITCH_ATTR_OP_DESCRIPTION]) new->description = strdup(nla_get_string(tb[SWITCH_ATTR_OP_DESCRIPTION]));done: return NL_SKIP;}intswlib_scan(struct switch_dev *dev){ struct attrlist_arg arg; if (dev->ops || dev->port_ops || dev->vlan_ops) return 0; arg.atype = SWLIB_ATTR_GROUP_GLOBAL; arg.dev = dev; arg.id = dev->id; arg.prev = NULL; arg.head = &dev->ops; swlib_call(SWITCH_CMD_LIST_GLOBAL, add_attr, add_id, &arg); arg.atype = SWLIB_ATTR_GROUP_PORT; arg.prev = NULL; arg.head = &dev->port_ops; swlib_call(SWITCH_CMD_LIST_PORT, add_attr, add_id, &arg); arg.atype = SWLIB_ATTR_GROUP_VLAN; arg.prev = NULL; arg.head = &dev->vlan_ops; swlib_call(SWITCH_CMD_LIST_VLAN, add_attr, add_id, &arg); return 0;}struct switch_attr *swlib_lookup_attr(struct switch_dev *dev, enum swlib_attr_group atype, const char *name){ struct switch_attr *head; if (!name || !dev) return NULL; switch(atype) { case SWLIB_ATTR_GROUP_GLOBAL: head = dev->ops; break; case SWLIB_ATTR_GROUP_PORT: head = dev->port_ops; break; case SWLIB_ATTR_GROUP_VLAN: head = dev->vlan_ops; break; } while(head) { if (!strcmp(name, head->name)) return head; head = head->next; } return NULL;}static voidswlib_priv_free(void){ if (cache) nl_cache_free(cache); if (handle) nl_handle_destroy(handle); handle = NULL; cache = NULL;}static intswlib_priv_init(void){ handle = nl_handle_alloc(); if (!handle) { DPRINTF("Failed to create handle\n"); goto err; } if (genl_connect(handle)) { DPRINTF("Failed to connect to generic netlink\n"); goto err; } cache = genl_ctrl_alloc_cache(handle); if (!cache) { DPRINTF("Failed to allocate netlink cache\n"); goto err; } family = genl_ctrl_search_by_name(cache, "switch"); if (!family) { DPRINTF("Switch API not present\n"); goto err; } return 0;err: swlib_priv_free(); return -EINVAL;}struct swlib_scan_arg { const char *name; struct switch_dev *head; struct switch_dev *ptr;};static intadd_switch(struct nl_msg *msg, void *arg){ struct swlib_scan_arg *sa = arg; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct switch_dev *dev; const char *name; if (nla_parse(tb, SWITCH_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0) goto done; if (!tb[SWITCH_ATTR_DEV_NAME]) goto done; name = nla_get_string(tb[SWITCH_ATTR_DEV_NAME]); if (sa->name && (strcmp(name, sa->name) != 0)) goto done; dev = swlib_alloc(sizeof(struct switch_dev)); if (!dev) goto done; dev->dev_name = strdup(name); if (tb[SWITCH_ATTR_ID]) dev->id = nla_get_u32(tb[SWITCH_ATTR_ID]); if (tb[SWITCH_ATTR_NAME]) dev->name = strdup(nla_get_string(tb[SWITCH_ATTR_DEV_NAME])); if (tb[SWITCH_ATTR_PORTS]) dev->ports = nla_get_u32(tb[SWITCH_ATTR_PORTS]); if (tb[SWITCH_ATTR_VLANS]) dev->vlans = nla_get_u32(tb[SWITCH_ATTR_VLANS]); if (!sa->head) { sa->head = dev; sa->ptr = dev; } else { sa->ptr->next = dev; sa->ptr = dev; } refcount++;done: return NL_SKIP;}struct switch_dev *swlib_connect(const char *name){ struct swlib_scan_arg arg; int err; if (!refcount) { if (swlib_priv_init() < 0) return NULL; }; arg.head = NULL; arg.ptr = NULL; arg.name = name; swlib_call(SWITCH_CMD_GET_SWITCH, add_switch, NULL, &arg); if (!refcount) swlib_priv_free(); return arg.head;}static voidswlib_free_attributes(struct switch_attr **head){ struct switch_attr *a = *head; struct switch_attr *next; while (a) { next = a->next; free(a); a = next; } *head = NULL;}voidswlib_free(struct switch_dev *dev){ swlib_free_attributes(&dev->ops); swlib_free_attributes(&dev->port_ops); swlib_free_attributes(&dev->vlan_ops); free(dev); if (--refcount == 0) swlib_priv_free();}voidswlib_free_all(struct switch_dev *dev){ struct switch_dev *p; while (dev) { p = dev->next; swlib_free(dev); dev = p; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -