📄 swconfig.c
字号:
/* * swconfig.c: Switch configuration API * * 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 General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * 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 <linux/types.h>#include <linux/module.h>#include <linux/init.h>#include <linux/list.h>#include <linux/if.h>#include <linux/if_ether.h>#include <linux/capability.h>#include <linux/skbuff.h>#include <linux/switch.h>//#define DEBUG 1#ifdef DEBUG#define DPRINTF(format, ...) printk("%s: " format, __func__, ##__VA_ARGS__)#else#define DPRINTF(...) do {} while(0)#endifMODULE_AUTHOR("Felix Fietkau <nbd@openwrt.org>");MODULE_LICENSE("GPL");static int swdev_id = 0;static struct list_head swdevs;static spinlock_t swdevs_lock = SPIN_LOCK_UNLOCKED;struct swconfig_callback;struct swconfig_callback{ struct sk_buff *msg; struct genlmsghdr *hdr; struct genl_info *info; int cmd; /* callback for filling in the message data */ int (*fill)(struct swconfig_callback *cb, void *arg); /* callback for closing the message before sending it */ int (*close)(struct swconfig_callback *cb, void *arg); struct nlattr *nest[4]; int args[4];};/* defaults */static intswconfig_get_vlan_ports(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val){ int ret; if (val->port_vlan >= dev->vlans) return -EINVAL; if (!dev->get_vlan_ports) return -EOPNOTSUPP; ret = dev->get_vlan_ports(dev, val); printk("SET PORTS %d\n", val->len); return ret;}static intswconfig_set_vlan_ports(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val){ int i; if (val->port_vlan >= dev->vlans) return -EINVAL; /* validate ports */ if (val->len > dev->ports) return -EINVAL; for (i = 0; i < val->len; i++) { if (val->value.ports[i].id >= dev->ports) return -EINVAL; } if (!dev->set_vlan_ports) return -EOPNOTSUPP; printk("SET PORTS %d\n", val->len); return dev->set_vlan_ports(dev, val);}static intswconfig_apply_config(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val){ /* don't complain if not supported by the switch driver */ if (!dev->apply_config) return 0; return dev->apply_config(dev);}enum global_defaults { GLOBAL_APPLY,};enum vlan_defaults { VLAN_PORTS,};enum port_defaults { PORT_LINK,};static struct switch_attr default_global[] = { [GLOBAL_APPLY] = { .type = SWITCH_TYPE_NOVAL, .name = "apply", .description = "Activate changes in the hardware", .set = swconfig_apply_config, }};static struct switch_attr default_port[] = { [PORT_LINK] = { .type = SWITCH_TYPE_INT, .name = "link", .description = "Current link speed", }};static struct switch_attr default_vlan[] = { [VLAN_PORTS] = { .type = SWITCH_TYPE_PORTS, .name = "ports", .description = "VLAN port mapping", .set = swconfig_set_vlan_ports, .get = swconfig_get_vlan_ports, },};static void swconfig_defaults_init(struct switch_dev *dev){ dev->def_global = 0; dev->def_vlan = 0; dev->def_port = 0; if (dev->get_vlan_ports || dev->set_vlan_ports) set_bit(VLAN_PORTS, &dev->def_vlan); /* always present, can be no-op */ set_bit(GLOBAL_APPLY, &dev->def_global);}static struct genl_family switch_fam = { .id = GENL_ID_GENERATE, .name = "switch", .hdrsize = 0, .version = 1, .maxattr = SWITCH_ATTR_MAX,};static const struct nla_policy switch_policy[SWITCH_ATTR_MAX+1] = { [SWITCH_ATTR_ID] = { .type = NLA_U32 }, [SWITCH_ATTR_OP_ID] = { .type = NLA_U32 }, [SWITCH_ATTR_OP_PORT] = { .type = NLA_U32 }, [SWITCH_ATTR_OP_VLAN] = { .type = NLA_U32 }, [SWITCH_ATTR_OP_VALUE_INT] = { .type = NLA_U32 }, [SWITCH_ATTR_OP_VALUE_STR] = { .type = NLA_NUL_STRING }, [SWITCH_ATTR_OP_VALUE_PORTS] = { .type = NLA_NESTED }, [SWITCH_ATTR_TYPE] = { .type = NLA_U32 },};static const struct nla_policy port_policy[SWITCH_PORT_ATTR_MAX+1] = { [SWITCH_PORT_ID] = { .type = NLA_U32 }, [SWITCH_PORT_FLAG_TAGGED] = { .type = NLA_FLAG },};static inline voidswconfig_lock(void){ spin_lock(&swdevs_lock);}static inline voidswconfig_unlock(void){ spin_unlock(&swdevs_lock);}static struct switch_dev *swconfig_get_dev(struct genl_info *info){ struct switch_dev *dev = NULL; struct switch_dev *p; int id; if (!info->attrs[SWITCH_ATTR_ID]) goto done; id = nla_get_u32(info->attrs[SWITCH_ATTR_ID]); swconfig_lock(); list_for_each_entry(p, &swdevs, dev_list) { if (id != p->id) continue; dev = p; break; } if (dev) spin_lock(&dev->lock); else DPRINTF("device %d not found\n", id); swconfig_unlock();done: return dev;}static inline voidswconfig_put_dev(struct switch_dev *dev){ spin_unlock(&dev->lock);}static intswconfig_dump_attr(struct swconfig_callback *cb, void *arg){ struct switch_attr *op = arg; struct genl_info *info = cb->info; struct sk_buff *msg = cb->msg; int id = cb->args[0]; void *hdr; hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq, &switch_fam, NLM_F_MULTI, SWITCH_CMD_NEW_ATTR); if (IS_ERR(hdr)) return -1; NLA_PUT_U32(msg, SWITCH_ATTR_OP_ID, id); NLA_PUT_U32(msg, SWITCH_ATTR_OP_TYPE, op->type); NLA_PUT_STRING(msg, SWITCH_ATTR_OP_NAME, op->name); if (op->description) NLA_PUT_STRING(msg, SWITCH_ATTR_OP_DESCRIPTION, op->description); return genlmsg_end(msg, hdr);nla_put_failure: genlmsg_cancel(msg, hdr); return -EMSGSIZE;}/* spread multipart messages across multiple message buffers */static intswconfig_send_multipart(struct swconfig_callback *cb, void *arg){ struct genl_info *info = cb->info; int restart = 0; int err; do { if (!cb->msg) { cb->msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); if (cb->msg == NULL) goto error; } if (!(cb->fill(cb, arg) < 0)) break; /* fill failed, check if this was already the second attempt */ if (restart) goto error; /* try again in a new message, send the current one */ restart = 1; if (cb->close) { if (cb->close(cb, arg) < 0) goto error; } err = genlmsg_unicast(cb->msg, info->snd_pid); cb->msg = NULL; if (err < 0) goto error; } while (restart); return 0;error: if (cb->msg) nlmsg_free(cb->msg); return -1;}static intswconfig_list_attrs(struct sk_buff *skb, struct genl_info *info){ struct genlmsghdr *hdr = nlmsg_data(info->nlhdr); const struct switch_attrlist *alist; struct switch_dev *dev; struct swconfig_callback cb; int err = -EINVAL; int i; /* defaults */ struct switch_attr *def_list; unsigned long *def_active; int n_def; dev = swconfig_get_dev(info); if (!dev) return -EINVAL; switch(hdr->cmd) { case SWITCH_CMD_LIST_GLOBAL: alist = &dev->attr_global; def_list = default_global; def_active = &dev->def_global; n_def = ARRAY_SIZE(default_global); break; case SWITCH_CMD_LIST_VLAN: alist = &dev->attr_vlan; def_list = default_vlan; def_active = &dev->def_vlan; n_def = ARRAY_SIZE(default_vlan); break; case SWITCH_CMD_LIST_PORT: alist = &dev->attr_port; def_list = default_port; def_active = &dev->def_port; n_def = ARRAY_SIZE(default_port); break; default: WARN_ON(1); goto out; } memset(&cb, 0, sizeof(cb)); cb.info = info; cb.fill = swconfig_dump_attr; for (i = 0; i < alist->n_attr; i++) { if (alist->attr[i].disabled) continue; cb.args[0] = i; err = swconfig_send_multipart(&cb, &alist->attr[i]); if (err < 0) goto error; } /* defaults */ for (i = 0; i < n_def; i++) { if (!test_bit(i, def_active)) continue; cb.args[0] = SWITCH_ATTR_DEFAULTS_OFFSET + i; err = swconfig_send_multipart(&cb, &def_list[i]); if (err < 0) goto error; } swconfig_put_dev(dev); if (!cb.msg) return 0; return genlmsg_unicast(cb.msg, info->snd_pid);error: if (cb.msg) nlmsg_free(cb.msg);out: swconfig_put_dev(dev); return err;}static struct switch_attr *swconfig_lookup_attr(struct switch_dev *dev, struct genl_info *info, struct switch_val *val){ struct genlmsghdr *hdr = nlmsg_data(info->nlhdr); const struct switch_attrlist *alist; struct switch_attr *attr = NULL; int attr_id; /* defaults */ struct switch_attr *def_list; unsigned long *def_active; int n_def; if (!info->attrs[SWITCH_ATTR_OP_ID]) goto done; switch(hdr->cmd) { case SWITCH_CMD_SET_GLOBAL: case SWITCH_CMD_GET_GLOBAL: alist = &dev->attr_global; def_list = default_global; def_active = &dev->def_global; n_def = ARRAY_SIZE(default_global); break; case SWITCH_CMD_SET_VLAN: case SWITCH_CMD_GET_VLAN: alist = &dev->attr_vlan; def_list = default_vlan; def_active = &dev->def_vlan; n_def = ARRAY_SIZE(default_vlan); if (!info->attrs[SWITCH_ATTR_OP_VLAN]) goto done; val->port_vlan = nla_get_u32(info->attrs[SWITCH_ATTR_OP_VLAN]); break; case SWITCH_CMD_SET_PORT: case SWITCH_CMD_GET_PORT: alist = &dev->attr_port; def_list = default_port; def_active = &dev->def_port; n_def = ARRAY_SIZE(default_port); if (!info->attrs[SWITCH_ATTR_OP_PORT]) goto done; val->port_vlan = nla_get_u32(info->attrs[SWITCH_ATTR_OP_PORT]); break; default: WARN_ON(1); goto done; } if (!alist) goto done; attr_id = nla_get_u32(info->attrs[SWITCH_ATTR_OP_ID]);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -