1/*
2 * Copyright (C) 2000 Lennert Buytenhek
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <unistd.h>
22#include <errno.h>
23#include <string.h>
24
25#include "libbridge.h"
26#include "libbridge_private.h"
27
28int br_socket_fd = -1;
29struct sysfs_class *br_class_net;
30
31int br_init(void)
32{
33	if ((br_socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
34		return errno;
35
36	br_class_net = sysfs_open_class("net");
37	return 0;
38}
39
40int br_refresh(void)
41{
42	if (br_class_net) {
43		sysfs_close_class(br_class_net);
44		br_class_net = sysfs_open_class("net");
45	}
46
47	return 0;
48}
49
50void br_shutdown(void)
51{
52	sysfs_close_class(br_class_net);
53	br_class_net = NULL;
54	close(br_socket_fd);
55	br_socket_fd = -1;
56}
57
58#ifdef HAVE_LIBSYSFS
59static int isbridge(const struct sysfs_class_device *dev)
60{
61	char path[SYSFS_PATH_MAX];
62
63	snprintf(path, sizeof(path), "%s/bridge", dev->path);
64	return !sysfs_path_is_dir(path);
65}
66
67/*
68 * New interface uses sysfs to find bridges
69 */
70static int new_foreach_bridge(int (*iterator)(const char *name, void *),
71			      void *arg)
72{
73	struct sysfs_class_device *dev;
74	struct dlist *devlist;
75	int count = 0;
76
77	if (!br_class_net) {
78		dprintf("no class /sys/class/net\n");
79		return -EOPNOTSUPP;
80	}
81
82	devlist = sysfs_get_class_devices(br_class_net);
83	if (!devlist) {
84		dprintf("Can't read devices from sysfs\n");
85		return -errno;
86	}
87
88	dlist_for_each_data(devlist, dev, struct sysfs_class_device) {
89		if (isbridge(dev)) {
90			++count;
91			if (iterator(dev->name, arg))
92				break;
93		}
94	}
95
96	return count;
97}
98#endif
99
100/*
101 * Old interface uses ioctl
102 */
103static int old_foreach_bridge(int (*iterator)(const char *, void *),
104			      void *iarg)
105{
106	int i, ret=0, num;
107	char ifname[IFNAMSIZ];
108	int ifindices[MAX_BRIDGES];
109	unsigned long args[3] = { BRCTL_GET_BRIDGES,
110				 (unsigned long)ifindices, MAX_BRIDGES };
111
112	num = ioctl(br_socket_fd, SIOCGIFBR, args);
113	if (num < 0) {
114		dprintf("Get bridge indices failed: %s\n",
115			strerror(errno));
116		return -errno;
117	}
118
119	for (i = 0; i < num; i++) {
120		if (!if_indextoname(ifindices[i], ifname)) {
121			dprintf("get find name for ifindex %d\n",
122				ifindices[i]);
123			return -errno;
124		}
125
126		++ret;
127		if(iterator(ifname, iarg))
128			break;
129
130	}
131
132	return ret;
133
134}
135
136/*
137 * Go over all bridges and call iterator function.
138 * if iterator returns non-zero then stop.
139 */
140int br_foreach_bridge(int (*iterator)(const char *, void *),
141		     void *arg)
142{
143	int ret;
144#ifdef HAVE_LIBSYSFS
145
146	ret = new_foreach_bridge(iterator, arg);
147	if (ret <= 0)
148#endif
149		ret = old_foreach_bridge(iterator, arg);
150
151	return ret;
152}
153
154/*
155 * Only used if sysfs is not available.
156 */
157static int old_foreach_port(const char *brname,
158			    int (*iterator)(const char *br, const char *port,
159					    void *arg),
160			    void *arg)
161{
162	int i, err, count;
163	struct ifreq ifr;
164	char ifname[IFNAMSIZ];
165	int ifindices[MAX_PORTS];
166	unsigned long args[4] = { BRCTL_GET_PORT_LIST,
167				  (unsigned long)ifindices, MAX_PORTS, 0 };
168
169	memset(ifindices, 0, sizeof(ifindices));
170	strncpy(ifr.ifr_name, brname, IFNAMSIZ);
171	ifr.ifr_data = (char *) &args;
172
173	err = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr);
174	if (err < 0) {
175		dprintf("list ports for bridge:'%s' failed: %s\n",
176			brname, strerror(errno));
177		return -errno;
178	}
179
180	count = 0;
181	for (i = 0; i < MAX_PORTS; i++) {
182		if (!ifindices[i])
183			continue;
184
185		if (!if_indextoname(ifindices[i], ifname)) {
186			dprintf("can't find name for ifindex:%d\n",
187				ifindices[i]);
188			continue;
189		}
190
191		++count;
192		if (iterator(brname, ifname, arg))
193			break;
194	}
195
196	return count;
197}
198
199/*
200 * Iterate over all ports in bridge (using sysfs).
201 */
202int br_foreach_port(const char *brname,
203		    int (*iterator)(const char *br, const char *port, void *arg),
204		    void *arg)
205{
206#ifdef HAVE_LIBSYSFS
207	struct sysfs_class_device *dev;
208	struct sysfs_directory *dir;
209	struct sysfs_link *plink;
210	struct dlist *links;
211	int err = 0;
212	char path[SYSFS_PATH_MAX];
213
214	if (!br_class_net ||
215	    !(dev = sysfs_get_class_device(br_class_net, (char *) brname)))
216		goto old;
217
218	snprintf(path, sizeof(path), "%s/%s",
219		 dev->path, SYSFS_BRIDGE_PORT_SUBDIR);
220
221	dprintf("path=%s\n", path);
222	dir = sysfs_open_directory(path);
223	if (!dir) {
224		/* no /sys/class/net/ethX/brif subdirectory
225		 * either: old kernel, or not really a bridge
226		 */
227		goto old;
228	}
229
230	links = sysfs_get_dir_links(dir);
231	if (!links) {
232		err = -ENOSYS;
233		goto out;
234	}
235
236	err = 0;
237	dlist_for_each_data(links, plink, struct sysfs_link) {
238		++err;
239		if (iterator(brname, plink->name, arg))
240			break;
241	}
242 out:
243	sysfs_close_directory(dir);
244	return err;
245
246 old:
247#endif
248	return old_foreach_port(brname, iterator, arg);
249
250}
251