1/*
2 * swlib.c: Switch configuration API (user space part)
3 *
4 * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
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 + 1];
42static int refcount = 0;
43
44static struct nla_policy port_policy[SWITCH_ATTR_MAX] = {
45	[SWITCH_PORT_ID] = { .type = NLA_U32 },
46	[SWITCH_PORT_FLAG_TAGGED] = { .type = NLA_FLAG },
47};
48
49static struct nla_policy portmap_policy[SWITCH_PORTMAP_MAX] = {
50	[SWITCH_PORTMAP_SEGMENT] = { .type = NLA_STRING },
51	[SWITCH_PORTMAP_VIRT] = { .type = NLA_U32 },
52};
53
54static struct nla_policy link_policy[SWITCH_LINK_ATTR_MAX] = {
55	[SWITCH_LINK_FLAG_LINK] = { .type = NLA_FLAG },
56	[SWITCH_LINK_FLAG_DUPLEX] = { .type = NLA_FLAG },
57	[SWITCH_LINK_FLAG_ANEG] = { .type = NLA_FLAG },
58	[SWITCH_LINK_SPEED] = { .type = NLA_U32 },
59	[SWITCH_LINK_FLAG_EEE_100BASET] = { .type = NLA_FLAG },
60	[SWITCH_LINK_FLAG_EEE_1000BASET] = { .type = NLA_FLAG },
61};
62
63static inline void *
64swlib_alloc(size_t size)
65{
66	void *ptr;
67
68	ptr = malloc(size);
69	if (!ptr)
70		goto done;
71	memset(ptr, 0, size);
72
73done:
74	return ptr;
75}
76
77static int
78wait_handler(struct nl_msg *msg, void *arg)
79{
80	int *finished = arg;
81
82	*finished = 1;
83	return NL_STOP;
84}
85
86/* helper function for performing netlink requests */
87static int
88swlib_call(int cmd, int (*call)(struct nl_msg *, void *),
89		int (*data)(struct nl_msg *, void *), void *arg)
90{
91	struct nl_msg *msg;
92	struct nl_cb *cb = NULL;
93	int finished;
94	int flags = 0;
95	int err;
96
97	msg = nlmsg_alloc();
98	if (!msg) {
99		fprintf(stderr, "Out of memory!\n");
100		exit(1);
101	}
102
103	if (!data)
104		flags |= NLM_F_DUMP;
105
106	genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, genl_family_get_id(family), 0, flags, cmd, 0);
107	if (data) {
108		if (data(msg, arg) < 0)
109			goto nla_put_failure;
110	}
111
112	cb = nl_cb_alloc(NL_CB_CUSTOM);
113	if (!cb) {
114		fprintf(stderr, "nl_cb_alloc failed.\n");
115		exit(1);
116	}
117
118	err = nl_send_auto_complete(handle, msg);
119	if (err < 0) {
120		fprintf(stderr, "nl_send_auto_complete failed: %d\n", err);
121		goto out;
122	}
123
124	finished = 0;
125
126	if (call)
127		nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, call, arg);
128
129	if (data)
130		nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, wait_handler, &finished);
131	else
132		nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, wait_handler, &finished);
133
134	err = nl_recvmsgs(handle, cb);
135	if (err < 0) {
136		goto out;
137	}
138
139	if (!finished)
140		err = nl_wait_for_ack(handle);
141
142out:
143	if (cb)
144		nl_cb_put(cb);
145nla_put_failure:
146	nlmsg_free(msg);
147	return err;
148}
149
150static int
151send_attr(struct nl_msg *msg, void *arg)
152{
153	struct switch_val *val = arg;
154	struct switch_attr *attr = val->attr;
155
156	NLA_PUT_U32(msg, SWITCH_ATTR_ID, attr->dev->id);
157	NLA_PUT_U32(msg, SWITCH_ATTR_OP_ID, attr->id);
158	switch(attr->atype) {
159	case SWLIB_ATTR_GROUP_PORT:
160		NLA_PUT_U32(msg, SWITCH_ATTR_OP_PORT, val->port_vlan);
161		break;
162	case SWLIB_ATTR_GROUP_VLAN:
163		NLA_PUT_U32(msg, SWITCH_ATTR_OP_VLAN, val->port_vlan);
164		break;
165	default:
166		break;
167	}
168
169	return 0;
170
171nla_put_failure:
172	return -1;
173}
174
175static int
176store_port_val(struct nl_msg *msg, struct nlattr *nla, struct switch_val *val)
177{
178	struct nlattr *p;
179	int ports = val->attr->dev->ports;
180	int err = 0;
181	int remaining;
182
183	if (!val->value.ports)
184		val->value.ports = malloc(sizeof(struct switch_port) * ports);
185
186	nla_for_each_nested(p, nla, remaining) {
187		struct nlattr *tb[SWITCH_PORT_ATTR_MAX+1];
188		struct switch_port *port;
189
190		if (val->len >= ports)
191			break;
192
193		err = nla_parse_nested(tb, SWITCH_PORT_ATTR_MAX, p, port_policy);
194		if (err < 0)
195			goto out;
196
197		if (!tb[SWITCH_PORT_ID])
198			continue;
199
200		port = &val->value.ports[val->len];
201		port->id = nla_get_u32(tb[SWITCH_PORT_ID]);
202		port->flags = 0;
203		if (tb[SWITCH_PORT_FLAG_TAGGED])
204			port->flags |= SWLIB_PORT_FLAG_TAGGED;
205
206		val->len++;
207	}
208
209out:
210	return err;
211}
212
213static int
214store_link_val(struct nl_msg *msg, struct nlattr *nla, struct switch_val *val)
215{
216	struct nlattr *tb[SWITCH_LINK_ATTR_MAX + 1];
217	struct switch_port_link *link;
218	int err = 0;
219
220	if (!val->value.link)
221		val->value.link = malloc(sizeof(struct switch_port_link));
222
223	err = nla_parse_nested(tb, SWITCH_LINK_ATTR_MAX, nla, link_policy);
224	if (err < 0)
225		goto out;
226
227	link = val->value.link;
228	link->link = !!tb[SWITCH_LINK_FLAG_LINK];
229	link->duplex = !!tb[SWITCH_LINK_FLAG_DUPLEX];
230	link->aneg = !!tb[SWITCH_LINK_FLAG_ANEG];
231	link->tx_flow = !!tb[SWITCH_LINK_FLAG_TX_FLOW];
232	link->rx_flow = !!tb[SWITCH_LINK_FLAG_RX_FLOW];
233	link->speed = nla_get_u32(tb[SWITCH_LINK_SPEED]);
234	link->eee = 0;
235	if (tb[SWITCH_LINK_FLAG_EEE_100BASET])
236		link->eee |= SWLIB_LINK_FLAG_EEE_100BASET;
237	if (tb[SWITCH_LINK_FLAG_EEE_1000BASET])
238		link->eee |= SWLIB_LINK_FLAG_EEE_1000BASET;
239
240out:
241	return err;
242}
243
244static int
245store_val(struct nl_msg *msg, void *arg)
246{
247	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
248	struct switch_val *val = arg;
249
250	if (!val)
251		goto error;
252
253	if (nla_parse(tb, SWITCH_ATTR_MAX - 1, genlmsg_attrdata(gnlh, 0),
254			genlmsg_attrlen(gnlh, 0), NULL) < 0) {
255		goto error;
256	}
257
258	if (tb[SWITCH_ATTR_OP_VALUE_INT])
259		val->value.i = nla_get_u32(tb[SWITCH_ATTR_OP_VALUE_INT]);
260	else if (tb[SWITCH_ATTR_OP_VALUE_STR])
261		val->value.s = strdup(nla_get_string(tb[SWITCH_ATTR_OP_VALUE_STR]));
262	else if (tb[SWITCH_ATTR_OP_VALUE_PORTS])
263		val->err = store_port_val(msg, tb[SWITCH_ATTR_OP_VALUE_PORTS], val);
264	else if (tb[SWITCH_ATTR_OP_VALUE_LINK])
265		val->err = store_link_val(msg, tb[SWITCH_ATTR_OP_VALUE_LINK], val);
266
267	val->err = 0;
268	return 0;
269
270error:
271	return NL_SKIP;
272}
273
274int
275swlib_get_attr(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val)
276{
277	int cmd;
278	int err;
279
280	switch(attr->atype) {
281	case SWLIB_ATTR_GROUP_GLOBAL:
282		cmd = SWITCH_CMD_GET_GLOBAL;
283		break;
284	case SWLIB_ATTR_GROUP_PORT:
285		cmd = SWITCH_CMD_GET_PORT;
286		break;
287	case SWLIB_ATTR_GROUP_VLAN:
288		cmd = SWITCH_CMD_GET_VLAN;
289		break;
290	default:
291		return -EINVAL;
292	}
293
294	memset(&val->value, 0, sizeof(val->value));
295	val->len = 0;
296	val->attr = attr;
297	val->err = -EINVAL;
298	err = swlib_call(cmd, store_val, send_attr, val);
299	if (!err)
300		err = val->err;
301
302	return err;
303}
304
305static int
306send_attr_ports(struct nl_msg *msg, struct switch_val *val)
307{
308	struct nlattr *n;
309	int i;
310
311	/* TODO implement multipart? */
312	if (val->len == 0)
313		goto done;
314	n = nla_nest_start(msg, SWITCH_ATTR_OP_VALUE_PORTS);
315	if (!n)
316		goto nla_put_failure;
317	for (i = 0; i < val->len; i++) {
318		struct switch_port *port = &val->value.ports[i];
319		struct nlattr *np;
320
321		np = nla_nest_start(msg, SWITCH_ATTR_PORT);
322		if (!np)
323			goto nla_put_failure;
324
325		NLA_PUT_U32(msg, SWITCH_PORT_ID, port->id);
326		if (port->flags & SWLIB_PORT_FLAG_TAGGED)
327			NLA_PUT_FLAG(msg, SWITCH_PORT_FLAG_TAGGED);
328
329		nla_nest_end(msg, np);
330	}
331	nla_nest_end(msg, n);
332done:
333	return 0;
334
335nla_put_failure:
336	return -1;
337}
338
339static int
340send_attr_link(struct nl_msg *msg, struct switch_val *val)
341{
342	struct switch_port_link *link = val->value.link;
343	struct nlattr *n;
344
345	n = nla_nest_start(msg, SWITCH_ATTR_OP_VALUE_LINK);
346	if (!n)
347		goto nla_put_failure;
348
349	if (link->duplex)
350		NLA_PUT_FLAG(msg, SWITCH_LINK_FLAG_DUPLEX);
351	if (link->aneg)
352		NLA_PUT_FLAG(msg, SWITCH_LINK_FLAG_ANEG);
353	NLA_PUT_U32(msg, SWITCH_LINK_SPEED, link->speed);
354
355	nla_nest_end(msg, n);
356
357	return 0;
358
359nla_put_failure:
360	return -1;
361}
362
363static int
364send_attr_val(struct nl_msg *msg, void *arg)
365{
366	struct switch_val *val = arg;
367	struct switch_attr *attr = val->attr;
368
369	if (send_attr(msg, arg))
370		goto nla_put_failure;
371
372	switch(attr->type) {
373	case SWITCH_TYPE_NOVAL:
374		break;
375	case SWITCH_TYPE_INT:
376		NLA_PUT_U32(msg, SWITCH_ATTR_OP_VALUE_INT, val->value.i);
377		break;
378	case SWITCH_TYPE_STRING:
379		if (!val->value.s)
380			goto nla_put_failure;
381		NLA_PUT_STRING(msg, SWITCH_ATTR_OP_VALUE_STR, val->value.s);
382		break;
383	case SWITCH_TYPE_PORTS:
384		if (send_attr_ports(msg, val) < 0)
385			goto nla_put_failure;
386		break;
387	case SWITCH_TYPE_LINK:
388		if (send_attr_link(msg, val))
389			goto nla_put_failure;
390		break;
391	default:
392		goto nla_put_failure;
393	}
394	return 0;
395
396nla_put_failure:
397	return -1;
398}
399
400int
401swlib_set_attr(struct switch_dev *dev, struct switch_attr *attr, struct switch_val *val)
402{
403	int cmd;
404
405	switch(attr->atype) {
406	case SWLIB_ATTR_GROUP_GLOBAL:
407		cmd = SWITCH_CMD_SET_GLOBAL;
408		break;
409	case SWLIB_ATTR_GROUP_PORT:
410		cmd = SWITCH_CMD_SET_PORT;
411		break;
412	case SWLIB_ATTR_GROUP_VLAN:
413		cmd = SWITCH_CMD_SET_VLAN;
414		break;
415	default:
416		return -EINVAL;
417	}
418
419	val->attr = attr;
420	return swlib_call(cmd, NULL, send_attr_val, val);
421}
422
423enum {
424	CMD_NONE,
425	CMD_DUPLEX,
426	CMD_ANEG,
427	CMD_SPEED,
428};
429
430int swlib_set_attr_string(struct switch_dev *dev, struct switch_attr *a, int port_vlan, const char *str)
431{
432	struct switch_port *ports;
433	struct switch_port_link *link;
434	struct switch_val val;
435	char *ptr;
436	int cmd = CMD_NONE;
437
438	memset(&val, 0, sizeof(val));
439	val.port_vlan = port_vlan;
440	switch(a->type) {
441	case SWITCH_TYPE_INT:
442		val.value.i = atoi(str);
443		break;
444	case SWITCH_TYPE_STRING:
445		val.value.s = (char *)str;
446		break;
447	case SWITCH_TYPE_PORTS:
448		ports = alloca(sizeof(struct switch_port) * dev->ports);
449		memset(ports, 0, sizeof(struct switch_port) * dev->ports);
450		val.len = 0;
451		ptr = (char *)str;
452		while(ptr && *ptr)
453		{
454			while(*ptr && isspace(*ptr))
455				ptr++;
456
457			if (!*ptr)
458				break;
459
460			if (!isdigit(*ptr))
461				return -1;
462
463			if (val.len >= dev->ports)
464				return -1;
465
466			ports[val.len].flags = 0;
467			ports[val.len].id = strtoul(ptr, &ptr, 10);
468			while(*ptr && !isspace(*ptr)) {
469				if (*ptr == 't')
470					ports[val.len].flags |= SWLIB_PORT_FLAG_TAGGED;
471				else
472					return -1;
473
474				ptr++;
475			}
476			if (*ptr)
477				ptr++;
478			val.len++;
479		}
480		val.value.ports = ports;
481		break;
482	case SWITCH_TYPE_LINK:
483		link = malloc(sizeof(struct switch_port_link));
484		memset(link, 0, sizeof(struct switch_port_link));
485		ptr = (char *)str;
486		for (ptr = strtok(ptr," "); ptr; ptr = strtok(NULL, " ")) {
487			switch (cmd) {
488			case CMD_NONE:
489				if (!strcmp(ptr, "duplex"))
490					cmd = CMD_DUPLEX;
491				else if (!strcmp(ptr, "autoneg"))
492					cmd = CMD_ANEG;
493				else if (!strcmp(ptr, "speed"))
494					cmd = CMD_SPEED;
495				else
496					fprintf(stderr, "Unsupported option %s\n", ptr);
497				break;
498			case CMD_DUPLEX:
499				if (!strcmp(ptr, "half"))
500					link->duplex = 0;
501				else if (!strcmp(ptr, "full"))
502					link->duplex = 1;
503				else
504					fprintf(stderr, "Unsupported value %s\n", ptr);
505				cmd = CMD_NONE;
506				break;
507			case CMD_ANEG:
508				if (!strcmp(ptr, "on"))
509					link->aneg = 1;
510				else if (!strcmp(ptr, "off"))
511					link->aneg = 0;
512				else
513					fprintf(stderr, "Unsupported value %s\n", ptr);
514				cmd = CMD_NONE;
515				break;
516			case CMD_SPEED:
517				link->speed = atoi(ptr);
518				cmd = CMD_NONE;
519				break;
520			}
521		}
522		val.value.link = link;
523		break;
524	case SWITCH_TYPE_NOVAL:
525		if (str && !strcmp(str, "0"))
526			return 0;
527
528		break;
529	default:
530		return -1;
531	}
532	return swlib_set_attr(dev, a, &val);
533}
534
535
536struct attrlist_arg {
537	int id;
538	int atype;
539	struct switch_dev *dev;
540	struct switch_attr *prev;
541	struct switch_attr **head;
542};
543
544static int
545add_id(struct nl_msg *msg, void *arg)
546{
547	struct attrlist_arg *l = arg;
548
549	NLA_PUT_U32(msg, SWITCH_ATTR_ID, l->id);
550
551	return 0;
552nla_put_failure:
553	return -1;
554}
555
556static int
557add_attr(struct nl_msg *msg, void *ptr)
558{
559	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
560	struct attrlist_arg *arg = ptr;
561	struct switch_attr *new;
562
563	if (nla_parse(tb, SWITCH_ATTR_MAX - 1, genlmsg_attrdata(gnlh, 0),
564			genlmsg_attrlen(gnlh, 0), NULL) < 0)
565		goto done;
566
567	new = swlib_alloc(sizeof(struct switch_attr));
568	if (!new)
569		goto done;
570
571	new->dev = arg->dev;
572	new->atype = arg->atype;
573	if (arg->prev) {
574		arg->prev->next = new;
575	} else {
576		arg->prev = *arg->head;
577	}
578	*arg->head = new;
579	arg->head = &new->next;
580
581	if (tb[SWITCH_ATTR_OP_ID])
582		new->id = nla_get_u32(tb[SWITCH_ATTR_OP_ID]);
583	if (tb[SWITCH_ATTR_OP_TYPE])
584		new->type = nla_get_u32(tb[SWITCH_ATTR_OP_TYPE]);
585	if (tb[SWITCH_ATTR_OP_NAME])
586		new->name = strdup(nla_get_string(tb[SWITCH_ATTR_OP_NAME]));
587	if (tb[SWITCH_ATTR_OP_DESCRIPTION])
588		new->description = strdup(nla_get_string(tb[SWITCH_ATTR_OP_DESCRIPTION]));
589
590done:
591	return NL_SKIP;
592}
593
594int
595swlib_scan(struct switch_dev *dev)
596{
597	struct attrlist_arg arg;
598
599	if (dev->ops || dev->port_ops || dev->vlan_ops)
600		return 0;
601
602	arg.atype = SWLIB_ATTR_GROUP_GLOBAL;
603	arg.dev = dev;
604	arg.id = dev->id;
605	arg.prev = NULL;
606	arg.head = &dev->ops;
607	swlib_call(SWITCH_CMD_LIST_GLOBAL, add_attr, add_id, &arg);
608
609	arg.atype = SWLIB_ATTR_GROUP_PORT;
610	arg.prev = NULL;
611	arg.head = &dev->port_ops;
612	swlib_call(SWITCH_CMD_LIST_PORT, add_attr, add_id, &arg);
613
614	arg.atype = SWLIB_ATTR_GROUP_VLAN;
615	arg.prev = NULL;
616	arg.head = &dev->vlan_ops;
617	swlib_call(SWITCH_CMD_LIST_VLAN, add_attr, add_id, &arg);
618
619	return 0;
620}
621
622struct switch_attr *swlib_lookup_attr(struct switch_dev *dev,
623		enum swlib_attr_group atype, const char *name)
624{
625	struct switch_attr *head;
626
627	if (!name || !dev)
628		return NULL;
629
630	switch(atype) {
631	case SWLIB_ATTR_GROUP_GLOBAL:
632		head = dev->ops;
633		break;
634	case SWLIB_ATTR_GROUP_PORT:
635		head = dev->port_ops;
636		break;
637	case SWLIB_ATTR_GROUP_VLAN:
638		head = dev->vlan_ops;
639		break;
640	}
641	while(head) {
642		if (!strcmp(name, head->name))
643			return head;
644		head = head->next;
645	}
646
647	return NULL;
648}
649
650static void
651swlib_priv_free(void)
652{
653	if (family)
654		nl_object_put((struct nl_object*)family);
655	if (cache)
656		nl_cache_free(cache);
657	if (handle)
658		nl_socket_free(handle);
659	family = NULL;
660	handle = NULL;
661	cache = NULL;
662}
663
664static int
665swlib_priv_init(void)
666{
667	int ret;
668
669	handle = nl_socket_alloc();
670	if (!handle) {
671		DPRINTF("Failed to create handle\n");
672		goto err;
673	}
674
675	if (genl_connect(handle)) {
676		DPRINTF("Failed to connect to generic netlink\n");
677		goto err;
678	}
679
680	ret = genl_ctrl_alloc_cache(handle, &cache);
681	if (ret < 0) {
682		DPRINTF("Failed to allocate netlink cache\n");
683		goto err;
684	}
685
686	family = genl_ctrl_search_by_name(cache, "switch");
687	if (!family) {
688		DPRINTF("Switch API not present\n");
689		goto err;
690	}
691	return 0;
692
693err:
694	swlib_priv_free();
695	return -EINVAL;
696}
697
698struct swlib_scan_arg {
699	const char *name;
700	struct switch_dev *head;
701	struct switch_dev *ptr;
702};
703
704static int
705add_port_map(struct switch_dev *dev, struct nlattr *nla)
706{
707	struct nlattr *p;
708	int err = 0, idx = 0;
709	int remaining;
710
711	dev->maps = malloc(sizeof(struct switch_portmap) * dev->ports);
712	if (!dev->maps)
713		return -1;
714	memset(dev->maps, 0, sizeof(struct switch_portmap) * dev->ports);
715
716	nla_for_each_nested(p, nla, remaining) {
717		struct nlattr *tb[SWITCH_PORTMAP_MAX+1];
718
719		if (idx >= dev->ports)
720			continue;
721
722		err = nla_parse_nested(tb, SWITCH_PORTMAP_MAX, p, portmap_policy);
723		if (err < 0)
724			continue;
725
726
727		if (tb[SWITCH_PORTMAP_SEGMENT] && tb[SWITCH_PORTMAP_VIRT]) {
728			dev->maps[idx].segment = strdup(nla_get_string(tb[SWITCH_PORTMAP_SEGMENT]));
729			dev->maps[idx].virt = nla_get_u32(tb[SWITCH_PORTMAP_VIRT]);
730		}
731		idx++;
732	}
733
734out:
735	return err;
736}
737
738
739static int
740add_switch(struct nl_msg *msg, void *arg)
741{
742	struct swlib_scan_arg *sa = arg;
743	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
744	struct switch_dev *dev;
745	const char *name;
746	const char *alias;
747
748	if (nla_parse(tb, SWITCH_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0)
749		goto done;
750
751	if (!tb[SWITCH_ATTR_DEV_NAME])
752		goto done;
753
754	name = nla_get_string(tb[SWITCH_ATTR_DEV_NAME]);
755	alias = nla_get_string(tb[SWITCH_ATTR_ALIAS]);
756
757	if (sa->name && (strcmp(name, sa->name) != 0) && (strcmp(alias, sa->name) != 0))
758		goto done;
759
760	dev = swlib_alloc(sizeof(struct switch_dev));
761	if (!dev)
762		goto done;
763
764	strncpy(dev->dev_name, name, IFNAMSIZ - 1);
765	dev->alias = strdup(alias);
766	if (tb[SWITCH_ATTR_ID])
767		dev->id = nla_get_u32(tb[SWITCH_ATTR_ID]);
768	if (tb[SWITCH_ATTR_NAME])
769		dev->name = strdup(nla_get_string(tb[SWITCH_ATTR_NAME]));
770	if (tb[SWITCH_ATTR_PORTS])
771		dev->ports = nla_get_u32(tb[SWITCH_ATTR_PORTS]);
772	if (tb[SWITCH_ATTR_VLANS])
773		dev->vlans = nla_get_u32(tb[SWITCH_ATTR_VLANS]);
774	if (tb[SWITCH_ATTR_CPU_PORT])
775		dev->cpu_port = nla_get_u32(tb[SWITCH_ATTR_CPU_PORT]);
776	if (tb[SWITCH_ATTR_PORTMAP])
777		add_port_map(dev, tb[SWITCH_ATTR_PORTMAP]);
778
779	if (!sa->head) {
780		sa->head = dev;
781		sa->ptr = dev;
782	} else {
783		sa->ptr->next = dev;
784		sa->ptr = dev;
785	}
786
787	refcount++;
788done:
789	return NL_SKIP;
790}
791
792static int
793list_switch(struct nl_msg *msg, void *arg)
794{
795	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
796
797	if (nla_parse(tb, SWITCH_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL) < 0)
798		goto done;
799
800	if (!tb[SWITCH_ATTR_DEV_NAME] || !tb[SWITCH_ATTR_NAME])
801		goto done;
802
803	printf("Found: %s - %s\n", nla_get_string(tb[SWITCH_ATTR_DEV_NAME]),
804		nla_get_string(tb[SWITCH_ATTR_ALIAS]));
805
806done:
807	return NL_SKIP;
808}
809
810void
811swlib_list(void)
812{
813	if (swlib_priv_init() < 0)
814		return;
815	swlib_call(SWITCH_CMD_GET_SWITCH, list_switch, NULL, NULL);
816	swlib_priv_free();
817}
818
819void
820swlib_print_portmap(struct switch_dev *dev, char *segment)
821{
822	int i;
823
824	if (segment) {
825		if (!strcmp(segment, "cpu")) {
826			printf("%d ", dev->cpu_port);
827		} else if (!strcmp(segment, "disabled")) {
828			for (i = 0; i < dev->ports; i++)
829				if (!dev->maps[i].segment)
830					printf("%d ", i);
831		} else for (i = 0; i < dev->ports; i++) {
832			if (dev->maps[i].segment && !strcmp(dev->maps[i].segment, segment))
833				printf("%d ", i);
834		}
835	} else {
836		printf("%s - %s\n", dev->dev_name, dev->name);
837		for (i = 0; i < dev->ports; i++)
838			if (i == dev->cpu_port)
839				printf("port%d:\tcpu\n", i);
840			else if (dev->maps[i].segment)
841				printf("port%d:\t%s.%d\n", i, dev->maps[i].segment, dev->maps[i].virt);
842			else
843				printf("port%d:\tdisabled\n", i);
844	}
845}
846
847struct switch_dev *
848swlib_connect(const char *name)
849{
850	struct swlib_scan_arg arg;
851
852	if (!refcount) {
853		if (swlib_priv_init() < 0)
854			return NULL;
855	};
856
857	arg.head = NULL;
858	arg.ptr = NULL;
859	arg.name = name;
860	swlib_call(SWITCH_CMD_GET_SWITCH, add_switch, NULL, &arg);
861
862	if (!refcount)
863		swlib_priv_free();
864
865	return arg.head;
866}
867
868static void
869swlib_free_attributes(struct switch_attr **head)
870{
871	struct switch_attr *a = *head;
872	struct switch_attr *next;
873
874	while (a) {
875		next = a->next;
876		free(a->name);
877		free(a->description);
878		free(a);
879		a = next;
880	}
881	*head = NULL;
882}
883
884static void
885swlib_free_port_map(struct switch_dev *dev)
886{
887	int i;
888
889	if (!dev || !dev->maps)
890		return;
891
892	for (i = 0; i < dev->ports; i++)
893		free(dev->maps[i].segment);
894	free(dev->maps);
895}
896
897void
898swlib_free(struct switch_dev *dev)
899{
900	swlib_free_attributes(&dev->ops);
901	swlib_free_attributes(&dev->port_ops);
902	swlib_free_attributes(&dev->vlan_ops);
903	swlib_free_port_map(dev);
904	free(dev->name);
905	free(dev->alias);
906	free(dev);
907
908	if (--refcount == 0)
909		swlib_priv_free();
910}
911
912void
913swlib_free_all(struct switch_dev *dev)
914{
915	struct switch_dev *p;
916
917	while (dev) {
918		p = dev->next;
919		swlib_free(dev);
920		dev = p;
921	}
922}
923