diff options
| author | nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2008-10-18 20:44:53 +0000 | 
|---|---|---|
| committer | nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2008-10-18 20:44:53 +0000 | 
| commit | a662d5b130a558956875dbe259c551b632abb0f4 (patch) | |
| tree | fad159c87916c32379b95fedc3dadd6e7c901537 /package/swconfig/src | |
| parent | 9d53aabe66be627b5a90b670933cce149d49612a (diff) | |
add new switch configuration api
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@13009 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'package/swconfig/src')
| -rw-r--r-- | package/swconfig/src/Makefile | 12 | ||||
| -rw-r--r-- | package/swconfig/src/cli.c | 255 | ||||
| -rw-r--r-- | package/swconfig/src/swlib.c | 614 | ||||
| -rw-r--r-- | package/swconfig/src/swlib.h | 213 | 
4 files changed, 1094 insertions, 0 deletions
| diff --git a/package/swconfig/src/Makefile b/package/swconfig/src/Makefile new file mode 100644 index 000000000..64816af54 --- /dev/null +++ b/package/swconfig/src/Makefile @@ -0,0 +1,12 @@ +ifndef CFLAGS +CFLAGS = -O2 -g -I ../src +endif +LIBS=-lnl + +all: swconfig + +%.o: %.c +	$(CC) $(CFLAGS) -c -o $@ $^ + +swconfig: cli.o swlib.o +	$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) diff --git a/package/swconfig/src/cli.c b/package/swconfig/src/cli.c new file mode 100644 index 000000000..c6035e585 --- /dev/null +++ b/package/swconfig/src/cli.c @@ -0,0 +1,255 @@ +/* + * swconfig.c: Switch configuration utility + * + * 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 + * version 2 as published by the Free Software Foundatio. + * + * 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/types.h> +#include <linux/netlink.h> +#include <linux/genetlink.h> +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#include <linux/switch.h> +#include "swlib.h" + +#define GET		1 +#define SET		2 + +void print_attrs(struct switch_attr *attr) +{ +	int i = 0; +	while (attr) { +		const char *type; +		switch(attr->type) { +			case SWITCH_TYPE_INT: +				type = "int"; +				break; +			case SWITCH_TYPE_STRING: +				type = "string"; +				break; +			case SWITCH_TYPE_PORTS: +				type = "ports"; +				break; +			case SWITCH_TYPE_NOVAL: +				type = "none"; +				break; +			default: +				type = "unknown"; +				break; +		} +		printf("\tAttribute %d (%s): %s (%s)\n", ++i, type, attr->name, attr->description); +		attr = attr->next; +	} +} + +void list_attributes(struct switch_dev *dev) +{ +	printf("Switch %d: %s(%s), ports: %d, vlans: %d\n", dev->id, dev->dev_name, dev->name, dev->ports, dev->vlans); +	printf("     --switch\n"); +	print_attrs(dev->ops); +	printf("     --vlan\n"); +	print_attrs(dev->vlan_ops); +	printf("     --port\n"); +	print_attrs(dev->port_ops); +} + +void print_usage(void) +{ +	printf("swconfig dev <dev> [port <port>|vlan <vlan>] (help|set <key> <value>|get <key>)\n"); +	exit(0); +} + +int main(int argc, char **argv) +{ +	int retval = 0; +	struct switch_dev *dev; +	struct switch_attr *a; +	struct switch_val val; +	int err; +	int i; + +	struct switch_port *ports; + +	int cmd = 0; +	char *cdev = NULL; +	int cport = -1; +	int cvlan = -1; +	char *ckey = NULL; +	char *cvalue = NULL; +	int chelp = 0; + +	if(argc < 4) +		print_usage(); + +	if(strcmp(argv[1], "dev")) +		print_usage(); + +	cdev = argv[2]; + +	for(i = 3; i < argc; i++) +	{ +		int p; +		if(!strcmp(argv[i], "help")) +		{ +			chelp = 1; +			continue; +		} +		if(i + 1 >= argc) +			print_usage(); +		p = atoi(argv[i + 1]); +		if(!strcmp(argv[i], "port")) +		{ +			cport = p; +		} else if(!strcmp(argv[i], "vlan")) +		{ +			cvlan = p; +		} else if(!strcmp(argv[i], "set")) +		{ +			if(argc <= i + 1) +				print_usage(); +			cmd = SET; +			ckey = argv[i + 1]; +			if (argc > i + 2) +				cvalue = argv[i + 2]; +			else +				cvalue = NULL; +			i++; +		} else if(!strcmp(argv[i], "get")) +		{ +			cmd = GET; +			ckey = argv[i + 1]; +		} else{ +			print_usage(); +		} +		i++; +	} + +	if(cport > -1 && cvlan > -1) +		print_usage(); + +	dev = swlib_connect(cdev); +	if (!dev) { +		fprintf(stderr, "Failed to connect to the switch\n"); +		return 1; +	} + +	ports = malloc(sizeof(struct switch_port) * dev->ports); +	memset(ports, 0, sizeof(struct switch_port) * dev->ports); +	swlib_scan(dev); + +	if(chelp) +	{ +		list_attributes(dev); +		goto out; +	} + +	if(cport > -1) +		a = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_PORT, ckey); +	else if(cvlan > -1) +		a = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_VLAN, ckey); +	else +		a = swlib_lookup_attr(dev, SWLIB_ATTR_GROUP_GLOBAL, ckey); + +	if(!a) +	{ +		fprintf(stderr, "Unknown attribute \"%s\"\n", ckey); +		goto out; +	} + +	switch(cmd) +	{ +	case SET: +		if ((a->type != SWITCH_TYPE_NOVAL) && +				(cvalue == NULL)) +			print_usage(); + +		switch(a->type) { +		case SWITCH_TYPE_INT: +			val.value.i = atoi(cvalue); +			break; +		case SWITCH_TYPE_STRING: +			val.value.s = cvalue; +			break; +		case SWITCH_TYPE_PORTS: +			val.len = 0; +			while(cvalue && *cvalue) +			{ +				ports[val.len].flags = 0; +				ports[val.len].id = strtol(cvalue, &cvalue, 10); +				while(*cvalue && !isspace(*cvalue)) { +					if (*cvalue == 't') +						ports[val.len].flags |= SWLIB_PORT_FLAG_TAGGED; +					cvalue++; +				} +				if (*cvalue) +					cvalue++; +				val.len++; +			} +			val.value.ports = ports; +			break; +		default: +			break; +		} +		if(cvlan > -1) +			val.port_vlan = cvlan; +		if(cport > -1) +			val.port_vlan = cport; +		if(swlib_set_attr(dev, a, &val) < 0) +		{ +			fprintf(stderr, "failed\n"); +			retval = -1; +			goto out; +		} +		break; +	case GET: +		if(cvlan > -1) +			val.port_vlan = cvlan; +		if(cport > -1) +			val.port_vlan = cport; +		if(swlib_get_attr(dev, a, &val) < 0) +		{ +			fprintf(stderr, "failed\n"); +			retval = -1; +			goto out; +		} +		switch(a->type) { +		case SWITCH_TYPE_INT: +			printf("%d\n", val.value.i); +			break; +		case SWITCH_TYPE_STRING: +			printf("%s\n", val.value.s); +			break; +		case SWITCH_TYPE_PORTS: +			for(i = 0; i < val.len; i++) +				printf("%d ", val.value.ports[i]); +			printf("\n"); +			break; +		} +	} + +out: +	swlib_free_all(dev); +	free(ports); + +	return 0; +} diff --git a/package/swconfig/src/swlib.c b/package/swconfig/src/swlib.c new file mode 100644 index 000000000..3fde81641 --- /dev/null +++ b/package/swconfig/src/swlib.c @@ -0,0 +1,614 @@ +/* + * 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) +#endif + +static 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 int +wait_handler(struct nl_msg *msg, void *arg) +{ +	int *finished = arg; + +	*finished = 1; +	return NL_STOP; +} + +/* helper function for performing netlink requests */ +static int +swlib_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 int +send_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 int +store_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 int +store_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; +} + +int +swlib_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 int +send_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 int +send_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; +} + +int +swlib_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 int +add_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 int +add_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; +} + +int +swlib_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 void +swlib_priv_free(void) +{ +	if (cache) +		nl_cache_free(cache); +	if (handle) +		nl_handle_destroy(handle); +	handle = NULL; +	cache = NULL; +} + +static int +swlib_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 int +add_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 void +swlib_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; +} + +void +swlib_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(); +} + +void +swlib_free_all(struct switch_dev *dev) +{ +	struct switch_dev *p; + +	while (dev) { +		p = dev->next; +		swlib_free(dev); +		dev = p; +	} +} diff --git a/package/swconfig/src/swlib.h b/package/swconfig/src/swlib.h new file mode 100644 index 000000000..e00ff47a5 --- /dev/null +++ b/package/swconfig/src/swlib.h @@ -0,0 +1,213 @@ +/* + * swlib.h: 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. + * + +Usage of the library functions: + +  The main datastructure for a switch is the struct switch_device +  To get started, you first need to use switch_connect() to probe +  for switches and allocate an instance of this struct. + +  There are two possible usage modes: +    dev = switch_connect("eth0"); +      - this call will look for a switch registered for the linux device +  	  "eth0" and only allocate a switch_device for this particular switch. + +    dev = switch_connect(NULL) +      - this will return one switch_device struct for each available +  	  switch. The switch_device structs are chained with by ->next pointer + +  Then to query a switch for all available attributes, use: +    swlib_scan(dev); + +  All allocated datastructures for the switch_device struct can be freed with +    swlib_free(dev); +  or +    swlib_free_all(dev); + +  The latter traverses a whole chain of switch_device structs and frees them all + +  Switch attributes (struct switch_attr) are divided into three groups: +    dev->ops: +      - global settings +    dev->port_ops: +      - per-port settings +    dev->vlan_ops: +      - per-vlan settings + +  switch_lookup_attr() is a small helper function to locate attributes +  by name. + +  switch_set_attr() and switch_get_attr() can alter or request the values +  of attributes. + +Usage of the switch_attr struct: + +  ->atype: attribute group, one of: +    - SWLIB_ATTR_GROUP_GLOBAL +    - SWLIB_ATTR_GROUP_VLAN +    - SWLIB_ATTR_GROUP_PORT + +  ->id: identifier for the attribute + +  ->type: data type, one of: +    - SWITCH_TYPE_INT +    - SWITCH_TYPE_STRING +    - SWITCH_TYPE_PORT + +  ->name: short name of the attribute +  ->description: longer description +  ->next: pointer to the next attribute of the current group + + +Usage of the switch_val struct: + +  When setting attributes, following members of the struct switch_val need +  to be set up: + +    ->len (for attr->type == SWITCH_TYPE_PORT) +    ->port_vlan: +      - port number (for attr->atype == SWLIB_ATTR_GROUP_PORT), or: +      - vlan number (for attr->atype == SWLIB_ATTR_GROUP_VLAN) +    ->value.i (for attr->type == SWITCH_TYPE_INT) +    ->value.s (for attr->type == SWITCH_TYPE_STRING) +      - owned by the caller, not stored in the library internally +    ->value.ports (for attr->type == SWITCH_TYPE_PORT) +      - must point to an array of at lest val->len * sizeof(struct switch_port) + +  When getting string attributes, val->value.s must be freed by the caller +  When getting port list attributes, an internal static buffer is used, +  which changes from call to call. + + */ + +#ifndef __SWLIB_H +#define __SWLIB_H + +enum swlib_attr_group { +	SWLIB_ATTR_GROUP_GLOBAL, +	SWLIB_ATTR_GROUP_VLAN, +	SWLIB_ATTR_GROUP_PORT, +}; + +enum swlib_port_flags { +	SWLIB_PORT_FLAG_TAGGED = (1 << 0), +}; + + +struct switch_dev; +struct switch_attr; +struct switch_port; +struct switch_val; + +struct switch_dev { +	int id; +	const char *name; +	const char *dev_name; +	int ports; +	int vlans; +	struct switch_attr *ops; +	struct switch_attr *port_ops; +	struct switch_attr *vlan_ops; +	struct switch_dev *next; +	void *priv; +}; + +struct switch_val { +	struct switch_attr *attr; +	int len; +	int err; +	int port_vlan; +	union { +		const char *s; +		int i; +		struct switch_port *ports; +	} value; +}; + +struct switch_attr { +	struct switch_dev *dev; +	int atype; +	int id; +	int type; +	const char *name; +	const char *description; +	struct switch_attr *next; +}; + +struct switch_port { +	unsigned int id; +	unsigned int flags; +}; + +/** + * swlib_connect: connect to the switch through netlink + * @name: name of the ethernet interface, + * + * if name is NULL, it connect and builds a chain of all switches + */ +struct switch_dev *swlib_connect(const char *name); + +/** + * swlib_free: free all dynamically allocated data for the switch connection + * @dev: switch device struct + * + * all members of a switch device chain (generated by swlib_connect(NULL)) + * must be freed individually + */ +void swlib_free(struct switch_dev *dev); + +/** + * swlib_free_all: run swlib_free on all devices in the chain + * @dev: switch device struct + */ +void swlib_free_all(struct switch_dev *dev); + +/** + * swlib_scan: probe the switch driver for available commands/attributes + * @dev: switch device struct + */ +int swlib_scan(struct switch_dev *dev); + +/** + * swlib_lookup_attr: look up a switch attribute + * @dev: switch device struct + * @type: global, port or vlan + * @name: name of the attribute + */ +struct switch_attr *swlib_lookup_attr(struct switch_dev *dev, +		enum swlib_attr_group atype, const char *name); + +/** + * swlib_set_attr: set the value for an attribute + * @dev: switch device struct + * @attr: switch attribute struct + * @val: attribute value pointer + * returns 0 on success + */ +int swlib_set_attr(struct switch_dev *dev, struct switch_attr *attr, +		struct switch_val *val); + +/** + * swlib_get_attr: get the value for an attribute + * @dev: switch device struct + * @attr: switch attribute struct + * @val: attribute value pointer + * returns 0 on success + * for string attributes, the result string must be freed by the caller + */ +int swlib_get_attr(struct switch_dev *dev, struct switch_attr *attr, +		struct switch_val *val); + +#endif | 
