⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 swconfig.c

📁 kernel mode code for configuring switches. can attach to phy abstraction layer. communicates with
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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 + -