br_ioctl.c 9.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 *	Ioctl handler
 *	Linux ethernet bridge
 *
 *	Authors:
 *	Lennert Buytenhek		<buytenh@gnu.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.
 */

14
#include <linux/capability.h>
L
Linus Torvalds 已提交
15 16 17
#include <linux/kernel.h>
#include <linux/if_bridge.h>
#include <linux/netdevice.h>
18
#include <linux/slab.h>
L
Linus Torvalds 已提交
19
#include <linux/times.h>
20
#include <net/net_namespace.h>
21
#include <linux/uaccess.h>
L
Linus Torvalds 已提交
22 23
#include "br_private.h"

24
static int get_bridge_ifindices(struct net *net, int *indices, int num)
L
Linus Torvalds 已提交
25 26 27 28
{
	struct net_device *dev;
	int i = 0;

29 30
	rcu_read_lock();
	for_each_netdev_rcu(net, dev) {
31 32
		if (i >= num)
			break;
33
		if (dev->priv_flags & IFF_EBRIDGE)
L
Linus Torvalds 已提交
34 35
			indices[i++] = dev->ifindex;
	}
36
	rcu_read_unlock();
L
Linus Torvalds 已提交
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58

	return i;
}

/* called with RTNL */
static void get_port_ifindices(struct net_bridge *br, int *ifindices, int num)
{
	struct net_bridge_port *p;

	list_for_each_entry(p, &br->port_list, list) {
		if (p->port_no < num)
			ifindices[p->port_no] = p->dev->ifindex;
	}
}

/*
 * Format up to a page worth of forwarding table entries
 * userbuf -- where to copy result
 * maxnum  -- maximum number of entries desired
 *            (limited to a page for sanity)
 * offset  -- number of records to skip
 */
59
static int get_fdb_entries(struct net_bridge *br, void __user *userbuf,
L
Linus Torvalds 已提交
60 61 62 63
			   unsigned long maxnum, unsigned long offset)
{
	int num;
	void *buf;
64
	size_t size;
L
Linus Torvalds 已提交
65

66 67
	/* Clamp size to PAGE_SIZE, test maxnum to avoid overflow */
	if (maxnum > PAGE_SIZE/sizeof(struct __fdb_entry))
L
Linus Torvalds 已提交
68
		maxnum = PAGE_SIZE/sizeof(struct __fdb_entry);
69 70

	size = maxnum * sizeof(struct __fdb_entry);
L
Linus Torvalds 已提交
71 72 73 74

	buf = kmalloc(size, GFP_USER);
	if (!buf)
		return -ENOMEM;
75

L
Linus Torvalds 已提交
76 77 78 79 80 81 82 83 84 85
	num = br_fdb_fillbuf(br, buf, maxnum, offset);
	if (num > 0) {
		if (copy_to_user(userbuf, buf, num*sizeof(struct __fdb_entry)))
			num = -EFAULT;
	}
	kfree(buf);

	return num;
}

86
/* called with RTNL */
L
Linus Torvalds 已提交
87 88
static int add_del_if(struct net_bridge *br, int ifindex, int isadd)
{
89
	struct net *net = dev_net(br->dev);
L
Linus Torvalds 已提交
90 91 92
	struct net_device *dev;
	int ret;

93
	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
L
Linus Torvalds 已提交
94 95
		return -EPERM;

96
	dev = __dev_get_by_index(net, ifindex);
L
Linus Torvalds 已提交
97 98
	if (dev == NULL)
		return -EINVAL;
99

L
Linus Torvalds 已提交
100 101
	if (isadd)
		ret = br_add_if(br, dev);
102
	else
L
Linus Torvalds 已提交
103 104
		ret = br_del_if(br, dev);

105 106 107
	if (!ret)
		rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_MASTER, GFP_KERNEL);

L
Linus Torvalds 已提交
108 109 110 111 112 113
	return ret;
}

/*
 * Legacy ioctl's through SIOCDEVPRIVATE
 * This interface is deprecated because it was too difficult to
L
Lucas De Marchi 已提交
114
 * to do the translation for 32/64bit ioctl compatibility.
L
Linus Torvalds 已提交
115 116 117 118
 */
static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	struct net_bridge *br = netdev_priv(dev);
119
	struct net_bridge_port *p = NULL;
L
Linus Torvalds 已提交
120
	unsigned long args[4];
121
	int ret = -EOPNOTSUPP;
122

L
Linus Torvalds 已提交
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
	if (copy_from_user(args, rq->ifr_data, sizeof(args)))
		return -EFAULT;

	switch (args[0]) {
	case BRCTL_ADD_IF:
	case BRCTL_DEL_IF:
		return add_del_if(br, args[1], args[0] == BRCTL_ADD_IF);

	case BRCTL_GET_BRIDGE_INFO:
	{
		struct __bridge_info b;

		memset(&b, 0, sizeof(struct __bridge_info));
		rcu_read_lock();
		memcpy(&b.designated_root, &br->designated_root, 8);
		memcpy(&b.bridge_id, &br->bridge_id, 8);
		b.root_path_cost = br->root_path_cost;
		b.max_age = jiffies_to_clock_t(br->max_age);
		b.hello_time = jiffies_to_clock_t(br->hello_time);
		b.forward_delay = br->forward_delay;
		b.bridge_max_age = br->bridge_max_age;
		b.bridge_hello_time = br->bridge_hello_time;
		b.bridge_forward_delay = jiffies_to_clock_t(br->bridge_forward_delay);
		b.topology_change = br->topology_change;
		b.topology_change_detected = br->topology_change_detected;
		b.root_port = br->root_port;
149 150

		b.stp_enabled = (br->stp_enabled != BR_NO_STP);
L
Linus Torvalds 已提交
151 152 153 154
		b.ageing_time = jiffies_to_clock_t(br->ageing_time);
		b.hello_timer_value = br_timer_value(&br->hello_timer);
		b.tcn_timer_value = br_timer_value(&br->tcn_timer);
		b.topology_change_timer_value = br_timer_value(&br->topology_change_timer);
155
		b.gc_timer_value = br_timer_value(&br->gc_work.timer);
156
		rcu_read_unlock();
L
Linus Torvalds 已提交
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175

		if (copy_to_user((void __user *)args[1], &b, sizeof(b)))
			return -EFAULT;

		return 0;
	}

	case BRCTL_GET_PORT_LIST:
	{
		int num, *indices;

		num = args[2];
		if (num < 0)
			return -EINVAL;
		if (num == 0)
			num = 256;
		if (num > BR_MAX_PORTS)
			num = BR_MAX_PORTS;

176
		indices = kcalloc(num, sizeof(int), GFP_KERNEL);
L
Linus Torvalds 已提交
177 178 179 180 181 182 183 184 185 186 187
		if (indices == NULL)
			return -ENOMEM;

		get_port_ifindices(br, indices, num);
		if (copy_to_user((void __user *)args[1], indices, num*sizeof(int)))
			num =  -EFAULT;
		kfree(indices);
		return num;
	}

	case BRCTL_SET_BRIDGE_FORWARD_DELAY:
188
		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
L
Linus Torvalds 已提交
189 190
			return -EPERM;

191 192
		ret = br_set_forward_delay(br, args[1]);
		break;
L
Linus Torvalds 已提交
193 194

	case BRCTL_SET_BRIDGE_HELLO_TIME:
195
		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
L
Linus Torvalds 已提交
196 197
			return -EPERM;

198 199
		ret = br_set_hello_time(br, args[1]);
		break;
L
Linus Torvalds 已提交
200 201

	case BRCTL_SET_BRIDGE_MAX_AGE:
202
		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
L
Linus Torvalds 已提交
203 204
			return -EPERM;

205 206
		ret = br_set_max_age(br, args[1]);
		break;
L
Linus Torvalds 已提交
207 208

	case BRCTL_SET_AGEING_TIME:
209
		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
L
Linus Torvalds 已提交
210 211
			return -EPERM;

212 213
		ret = br_set_ageing_time(br, args[1]);
		break;
L
Linus Torvalds 已提交
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248

	case BRCTL_GET_PORT_INFO:
	{
		struct __port_info p;
		struct net_bridge_port *pt;

		rcu_read_lock();
		if ((pt = br_get_port(br, args[2])) == NULL) {
			rcu_read_unlock();
			return -EINVAL;
		}

		memset(&p, 0, sizeof(struct __port_info));
		memcpy(&p.designated_root, &pt->designated_root, 8);
		memcpy(&p.designated_bridge, &pt->designated_bridge, 8);
		p.port_id = pt->port_id;
		p.designated_port = pt->designated_port;
		p.path_cost = pt->path_cost;
		p.designated_cost = pt->designated_cost;
		p.state = pt->state;
		p.top_change_ack = pt->topology_change_ack;
		p.config_pending = pt->config_pending;
		p.message_age_timer_value = br_timer_value(&pt->message_age_timer);
		p.forward_delay_timer_value = br_timer_value(&pt->forward_delay_timer);
		p.hold_timer_value = br_timer_value(&pt->hold_timer);

		rcu_read_unlock();

		if (copy_to_user((void __user *)args[1], &p, sizeof(p)))
			return -EFAULT;

		return 0;
	}

	case BRCTL_SET_BRIDGE_STP_STATE:
249
		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
L
Linus Torvalds 已提交
250 251
			return -EPERM;

252
		br_stp_set_enabled(br, args[1]);
253 254
		ret = 0;
		break;
L
Linus Torvalds 已提交
255 256

	case BRCTL_SET_BRIDGE_PRIORITY:
257
		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
L
Linus Torvalds 已提交
258 259 260
			return -EPERM;

		br_stp_set_bridge_priority(br, args[1]);
261 262
		ret = 0;
		break;
L
Linus Torvalds 已提交
263 264 265

	case BRCTL_SET_PORT_PRIORITY:
	{
266
		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
L
Linus Torvalds 已提交
267 268 269
			return -EPERM;

		spin_lock_bh(&br->lock);
270
		if ((p = br_get_port(br, args[1])) == NULL)
L
Linus Torvalds 已提交
271 272
			ret = -EINVAL;
		else
273
			ret = br_stp_set_port_priority(p, args[2]);
L
Linus Torvalds 已提交
274
		spin_unlock_bh(&br->lock);
275
		break;
L
Linus Torvalds 已提交
276 277 278 279
	}

	case BRCTL_SET_PATH_COST:
	{
280
		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
L
Linus Torvalds 已提交
281 282
			return -EPERM;

283
		spin_lock_bh(&br->lock);
L
Linus Torvalds 已提交
284 285 286
		if ((p = br_get_port(br, args[1])) == NULL)
			ret = -EINVAL;
		else
287 288
			ret = br_stp_set_path_cost(p, args[2]);
		spin_unlock_bh(&br->lock);
289
		break;
L
Linus Torvalds 已提交
290 291 292
	}

	case BRCTL_GET_FDB_ENTRIES:
293
		return get_fdb_entries(br, (void __user *)args[1],
L
Linus Torvalds 已提交
294 295 296
				       args[2], args[3]);
	}

297 298 299 300 301 302 303 304
	if (!ret) {
		if (p)
			br_ifinfo_notify(RTM_NEWLINK, p);
		else
			netdev_state_change(br->dev);
	}

	return ret;
L
Linus Torvalds 已提交
305 306
}

307
static int old_deviceless(struct net *net, void __user *uarg)
L
Linus Torvalds 已提交
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
{
	unsigned long args[3];

	if (copy_from_user(args, uarg, sizeof(args)))
		return -EFAULT;

	switch (args[0]) {
	case BRCTL_GET_VERSION:
		return BRCTL_VERSION;

	case BRCTL_GET_BRIDGES:
	{
		int *indices;
		int ret = 0;

		if (args[2] >= 2048)
			return -ENOMEM;
325
		indices = kcalloc(args[2], sizeof(int), GFP_KERNEL);
L
Linus Torvalds 已提交
326 327 328
		if (indices == NULL)
			return -ENOMEM;

329
		args[2] = get_bridge_ifindices(net, indices, args[2]);
L
Linus Torvalds 已提交
330 331 332 333 334 335 336 337 338 339 340 341 342

		ret = copy_to_user((void __user *)args[1], indices, args[2]*sizeof(int))
			? -EFAULT : args[2];

		kfree(indices);
		return ret;
	}

	case BRCTL_ADD_BRIDGE:
	case BRCTL_DEL_BRIDGE:
	{
		char buf[IFNAMSIZ];

343
		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
L
Linus Torvalds 已提交
344 345 346 347 348 349 350 351
			return -EPERM;

		if (copy_from_user(buf, (void __user *)args[1], IFNAMSIZ))
			return -EFAULT;

		buf[IFNAMSIZ-1] = 0;

		if (args[0] == BRCTL_ADD_BRIDGE)
352
			return br_add_bridge(net, buf);
L
Linus Torvalds 已提交
353

354
		return br_del_bridge(net, buf);
L
Linus Torvalds 已提交
355 356 357 358 359 360
	}
	}

	return -EOPNOTSUPP;
}

361
int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *uarg)
L
Linus Torvalds 已提交
362 363 364 365
{
	switch (cmd) {
	case SIOCGIFBR:
	case SIOCSIFBR:
366
		return old_deviceless(net, uarg);
367

L
Linus Torvalds 已提交
368 369 370 371 372
	case SIOCBRADDBR:
	case SIOCBRDELBR:
	{
		char buf[IFNAMSIZ];

373
		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
L
Linus Torvalds 已提交
374 375 376 377 378 379 380
			return -EPERM;

		if (copy_from_user(buf, uarg, IFNAMSIZ))
			return -EFAULT;

		buf[IFNAMSIZ-1] = 0;
		if (cmd == SIOCBRADDBR)
381
			return br_add_bridge(net, buf);
L
Linus Torvalds 已提交
382

383
		return br_del_bridge(net, buf);
L
Linus Torvalds 已提交
384 385 386 387 388 389 390 391 392
	}
	}
	return -EOPNOTSUPP;
}

int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	struct net_bridge *br = netdev_priv(dev);

393
	switch (cmd) {
L
Linus Torvalds 已提交
394 395 396 397 398 399 400 401 402
	case SIOCDEVPRIVATE:
		return old_dev_ioctl(dev, rq, cmd);

	case SIOCBRADDIF:
	case SIOCBRDELIF:
		return add_del_if(br, rq->ifr_ifindex, cmd == SIOCBRADDIF);

	}

403
	br_debug(br, "Bridge does not support ioctl 0x%x\n", cmd);
L
Linus Torvalds 已提交
404 405
	return -EOPNOTSUPP;
}