br_ioctl.c 8.8 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-or-later
L
Linus Torvalds 已提交
2 3 4 5 6 7 8 9
/*
 *	Ioctl handler
 *	Linux ethernet bridge
 *
 *	Authors:
 *	Lennert Buytenhek		<buytenh@gnu.org>
 */

10
#include <linux/capability.h>
L
Linus Torvalds 已提交
11 12 13
#include <linux/kernel.h>
#include <linux/if_bridge.h>
#include <linux/netdevice.h>
14
#include <linux/slab.h>
L
Linus Torvalds 已提交
15
#include <linux/times.h>
16
#include <net/net_namespace.h>
17
#include <linux/uaccess.h>
L
Linus Torvalds 已提交
18 19
#include "br_private.h"

20
static int get_bridge_ifindices(struct net *net, int *indices, int num)
L
Linus Torvalds 已提交
21 22 23 24
{
	struct net_device *dev;
	int i = 0;

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

	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
 */
55
static int get_fdb_entries(struct net_bridge *br, void __user *userbuf,
L
Linus Torvalds 已提交
56 57 58 59
			   unsigned long maxnum, unsigned long offset)
{
	int num;
	void *buf;
60
	size_t size;
L
Linus Torvalds 已提交
61

62 63
	/* Clamp size to PAGE_SIZE, test maxnum to avoid overflow */
	if (maxnum > PAGE_SIZE/sizeof(struct __fdb_entry))
L
Linus Torvalds 已提交
64
		maxnum = PAGE_SIZE/sizeof(struct __fdb_entry);
65 66

	size = maxnum * sizeof(struct __fdb_entry);
L
Linus Torvalds 已提交
67 68 69 70

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

L
Linus Torvalds 已提交
72 73 74 75 76 77 78 79 80 81
	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;
}

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

89
	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
L
Linus Torvalds 已提交
90 91
		return -EPERM;

92
	dev = __dev_get_by_index(net, ifindex);
L
Linus Torvalds 已提交
93 94
	if (dev == NULL)
		return -EINVAL;
95

L
Linus Torvalds 已提交
96
	if (isadd)
97
		ret = br_add_if(br, dev, NULL);
98
	else
L
Linus Torvalds 已提交
99 100 101 102 103 104 105 106
		ret = br_del_if(br, dev);

	return ret;
}

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

L
Linus Torvalds 已提交
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
	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;
142 143

		b.stp_enabled = (br->stp_enabled != BR_NO_STP);
L
Linus Torvalds 已提交
144 145 146 147
		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);
148
		b.gc_timer_value = br_timer_value(&br->gc_work.timer);
149
		rcu_read_unlock();
L
Linus Torvalds 已提交
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168

		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;

169
		indices = kcalloc(num, sizeof(int), GFP_KERNEL);
L
Linus Torvalds 已提交
170 171 172 173 174 175 176 177 178 179 180
		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:
181
		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
L
Linus Torvalds 已提交
182 183
			return -EPERM;

184 185
		ret = br_set_forward_delay(br, args[1]);
		break;
L
Linus Torvalds 已提交
186 187

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

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

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

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

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

205 206
		ret = br_set_ageing_time(br, args[1]);
		break;
L
Linus Torvalds 已提交
207 208 209 210 211 212 213 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

	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:
242
		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
L
Linus Torvalds 已提交
243 244
			return -EPERM;

245
		ret = br_stp_set_enabled(br, args[1], NULL);
246
		break;
L
Linus Torvalds 已提交
247 248

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

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

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

		spin_lock_bh(&br->lock);
262
		if ((p = br_get_port(br, args[1])) == NULL)
L
Linus Torvalds 已提交
263 264
			ret = -EINVAL;
		else
265
			ret = br_stp_set_port_priority(p, args[2]);
L
Linus Torvalds 已提交
266
		spin_unlock_bh(&br->lock);
267
		break;
L
Linus Torvalds 已提交
268 269 270 271
	}

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

275
		spin_lock_bh(&br->lock);
L
Linus Torvalds 已提交
276 277 278
		if ((p = br_get_port(br, args[1])) == NULL)
			ret = -EINVAL;
		else
279 280
			ret = br_stp_set_path_cost(p, args[2]);
		spin_unlock_bh(&br->lock);
281
		break;
L
Linus Torvalds 已提交
282 283 284
	}

	case BRCTL_GET_FDB_ENTRIES:
285
		return get_fdb_entries(br, (void __user *)args[1],
L
Linus Torvalds 已提交
286 287 288
				       args[2], args[3]);
	}

289 290
	if (!ret) {
		if (p)
291
			br_ifinfo_notify(RTM_NEWLINK, NULL, p);
292 293 294 295 296
		else
			netdev_state_change(br->dev);
	}

	return ret;
L
Linus Torvalds 已提交
297 298
}

299
static int old_deviceless(struct net *net, void __user *uarg)
L
Linus Torvalds 已提交
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
{
	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;
317
		indices = kcalloc(args[2], sizeof(int), GFP_KERNEL);
L
Linus Torvalds 已提交
318 319 320
		if (indices == NULL)
			return -ENOMEM;

321
		args[2] = get_bridge_ifindices(net, indices, args[2]);
L
Linus Torvalds 已提交
322 323 324 325 326 327 328 329 330 331 332 333 334

		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];

335
		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
L
Linus Torvalds 已提交
336 337 338 339 340 341 342 343
			return -EPERM;

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

		buf[IFNAMSIZ-1] = 0;

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

346
		return br_del_bridge(net, buf);
L
Linus Torvalds 已提交
347 348 349 350 351 352
	}
	}

	return -EOPNOTSUPP;
}

353
int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *uarg)
L
Linus Torvalds 已提交
354 355 356 357
{
	switch (cmd) {
	case SIOCGIFBR:
	case SIOCSIFBR:
358
		return old_deviceless(net, uarg);
359

L
Linus Torvalds 已提交
360 361 362 363 364
	case SIOCBRADDBR:
	case SIOCBRDELBR:
	{
		char buf[IFNAMSIZ];

365
		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
L
Linus Torvalds 已提交
366 367 368 369 370 371 372
			return -EPERM;

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

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

375
		return br_del_bridge(net, buf);
L
Linus Torvalds 已提交
376 377 378 379 380 381 382 383 384
	}
	}
	return -EOPNOTSUPP;
}

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

385
	switch (cmd) {
L
Linus Torvalds 已提交
386 387 388 389 390 391 392 393 394
	case SIOCDEVPRIVATE:
		return old_dev_ioctl(dev, rq, cmd);

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

	}

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