Blame view

net/bridge/br_ioctl.c 8.8 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
  /*
   *	Ioctl handler
   *	Linux ethernet bridge
   *
   *	Authors:
   *	Lennert Buytenhek		<buytenh@gnu.org>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
   */
4fc268d24   Randy Dunlap   [PATCH] capable/c...
9
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
11
12
  #include <linux/kernel.h>
  #include <linux/if_bridge.h>
  #include <linux/netdevice.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
13
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
14
  #include <linux/times.h>
881d966b4   Eric W. Biederman   [NET]: Make the d...
15
  #include <net/net_namespace.h>
7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
16
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
  #include "br_private.h"
4aa678ba4   Alexey Dobriyan   netns bridge: all...
18
  static int get_bridge_ifindices(struct net *net, int *indices, int num)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
21
  {
  	struct net_device *dev;
  	int i = 0;
31ca0458a   Nikolay Aleksandrov   net: bridge: fix ...
22
23
  	rcu_read_lock();
  	for_each_netdev_rcu(net, dev) {
7562f876c   Pavel Emelianov   [NET]: Rework dev...
24
25
  		if (i >= num)
  			break;
9d6f229fc   YOSHIFUJI Hideaki   [NET] BRIDGE: Fix...
26
  		if (dev->priv_flags & IFF_EBRIDGE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
  			indices[i++] = dev->ifindex;
  	}
31ca0458a   Nikolay Aleksandrov   net: bridge: fix ...
29
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  
  	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
   */
9d6f229fc   YOSHIFUJI Hideaki   [NET] BRIDGE: Fix...
52
  static int get_fdb_entries(struct net_bridge *br, void __user *userbuf,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
54
55
56
  			   unsigned long maxnum, unsigned long offset)
  {
  	int num;
  	void *buf;
ba8379b22   Chris Wright   [PATCH] bridge: f...
57
  	size_t size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58

ba8379b22   Chris Wright   [PATCH] bridge: f...
59
60
  	/* Clamp size to PAGE_SIZE, test maxnum to avoid overflow */
  	if (maxnum > PAGE_SIZE/sizeof(struct __fdb_entry))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
  		maxnum = PAGE_SIZE/sizeof(struct __fdb_entry);
ba8379b22   Chris Wright   [PATCH] bridge: f...
62
63
  
  	size = maxnum * sizeof(struct __fdb_entry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
65
66
67
  
  	buf = kmalloc(size, GFP_USER);
  	if (!buf)
  		return -ENOMEM;
9d6f229fc   YOSHIFUJI Hideaki   [NET] BRIDGE: Fix...
68

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
70
71
72
73
74
75
76
77
  	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;
  }
31ef30c76   Eric Dumazet   bridge: remove de...
78
  /* called with RTNL */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
80
  static int add_del_if(struct net_bridge *br, int ifindex, int isadd)
  {
cb9905030   Eric W. Biederman   net: Allow userns...
81
  	struct net *net = dev_net(br->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
83
  	struct net_device *dev;
  	int ret;
cb9905030   Eric W. Biederman   net: Allow userns...
84
  	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
  		return -EPERM;
cb9905030   Eric W. Biederman   net: Allow userns...
86
  	dev = __dev_get_by_index(net, ifindex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
88
  	if (dev == NULL)
  		return -EINVAL;
9d6f229fc   YOSHIFUJI Hideaki   [NET] BRIDGE: Fix...
89

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
  	if (isadd)
ca752be00   David Ahern   net: bridge: Pass...
91
  		ret = br_add_if(br, dev, NULL);
eccaa9e51   David S. Miller   Revert "bridge: a...
92
  	else
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
  		ret = br_del_if(br, dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
96
97
98
  	return ret;
  }
  
  /*
   * Legacy ioctl's through SIOCDEVPRIVATE
4bbd026cb   Randy Dunlap   net: bridge: dele...
99
   * This interface is deprecated because it was too difficult
25985edce   Lucas De Marchi   Fix common misspe...
100
   * to do the translation for 32/64bit ioctl compatibility.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
103
104
   */
  static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
  {
  	struct net_bridge *br = netdev_priv(dev);
bf871ad79   Xin Long   bridge: a netlink...
105
  	struct net_bridge_port *p = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
  	unsigned long args[4];
bf871ad79   Xin Long   bridge: a netlink...
107
  	int ret = -EOPNOTSUPP;
9d6f229fc   YOSHIFUJI Hideaki   [NET] BRIDGE: Fix...
108

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
  	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;
9cde07087   Stephen Hemminger   bridge: add suppo...
135
136
  
  		b.stp_enabled = (br->stp_enabled != BR_NO_STP);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
138
139
140
  		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);
f7cdee8a7   Nikolay Aleksandrov   bridge: move to w...
141
  		b.gc_timer_value = br_timer_value(&br->gc_work.timer);
9d6f229fc   YOSHIFUJI Hideaki   [NET] BRIDGE: Fix...
142
  		rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
  
  		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;
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
161
  		indices = kcalloc(num, sizeof(int), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
163
  		if (indices == NULL)
  			return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
165
166
167
168
169
170
171
  		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:
cb9905030   Eric W. Biederman   net: Allow userns...
172
  		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
  			return -EPERM;
bf871ad79   Xin Long   bridge: a netlink...
174
175
  		ret = br_set_forward_delay(br, args[1]);
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
177
  
  	case BRCTL_SET_BRIDGE_HELLO_TIME:
cb9905030   Eric W. Biederman   net: Allow userns...
178
  		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
  			return -EPERM;
bf871ad79   Xin Long   bridge: a netlink...
180
181
  		ret = br_set_hello_time(br, args[1]);
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
183
  
  	case BRCTL_SET_BRIDGE_MAX_AGE:
cb9905030   Eric W. Biederman   net: Allow userns...
184
  		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
  			return -EPERM;
bf871ad79   Xin Long   bridge: a netlink...
186
187
  		ret = br_set_max_age(br, args[1]);
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
  
  	case BRCTL_SET_AGEING_TIME:
cb9905030   Eric W. Biederman   net: Allow userns...
190
  		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
  			return -EPERM;
bf871ad79   Xin Long   bridge: a netlink...
192
193
  		ret = br_set_ageing_time(br, args[1]);
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
  
  	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:
cb9905030   Eric W. Biederman   net: Allow userns...
229
  		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230
  			return -EPERM;
419dba8a4   Horatiu Vultur   net: bridge: Add ...
231
  		ret = br_stp_set_enabled(br, args[1], NULL);
bf871ad79   Xin Long   bridge: a netlink...
232
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
234
  
  	case BRCTL_SET_BRIDGE_PRIORITY:
cb9905030   Eric W. Biederman   net: Allow userns...
235
  		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
  			return -EPERM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
  		br_stp_set_bridge_priority(br, args[1]);
bf871ad79   Xin Long   bridge: a netlink...
238
239
  		ret = 0;
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
241
242
  
  	case BRCTL_SET_PORT_PRIORITY:
  	{
cb9905030   Eric W. Biederman   net: Allow userns...
243
  		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
  			return -EPERM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
  		spin_lock_bh(&br->lock);
9d6f229fc   YOSHIFUJI Hideaki   [NET] BRIDGE: Fix...
246
  		if ((p = br_get_port(br, args[1])) == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
248
  			ret = -EINVAL;
  		else
14f98f258   stephen hemminger   bridge: range che...
249
  			ret = br_stp_set_port_priority(p, args[2]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
  		spin_unlock_bh(&br->lock);
bf871ad79   Xin Long   bridge: a netlink...
251
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
255
  	}
  
  	case BRCTL_SET_PATH_COST:
  	{
cb9905030   Eric W. Biederman   net: Allow userns...
256
  		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
  			return -EPERM;
14f98f258   stephen hemminger   bridge: range che...
258
  		spin_lock_bh(&br->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
261
  		if ((p = br_get_port(br, args[1])) == NULL)
  			ret = -EINVAL;
  		else
14f98f258   stephen hemminger   bridge: range che...
262
263
  			ret = br_stp_set_path_cost(p, args[2]);
  		spin_unlock_bh(&br->lock);
bf871ad79   Xin Long   bridge: a netlink...
264
  		break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
  	}
  
  	case BRCTL_GET_FDB_ENTRIES:
9d6f229fc   YOSHIFUJI Hideaki   [NET] BRIDGE: Fix...
268
  		return get_fdb_entries(br, (void __user *)args[1],
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
  				       args[2], args[3]);
  	}
bf871ad79   Xin Long   bridge: a netlink...
271
272
  	if (!ret) {
  		if (p)
928990631   Nikolay Aleksandrov   net: bridge: add ...
273
  			br_ifinfo_notify(RTM_NEWLINK, NULL, p);
bf871ad79   Xin Long   bridge: a netlink...
274
275
276
277
278
  		else
  			netdev_state_change(br->dev);
  	}
  
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
279
  }
4aa678ba4   Alexey Dobriyan   netns bridge: all...
280
  static int old_deviceless(struct net *net, void __user *uarg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
  {
  	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;
0da974f4f   Panagiotis Issaris   [NET]: Conversion...
298
  		indices = kcalloc(args[2], sizeof(int), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
300
  		if (indices == NULL)
  			return -ENOMEM;
4aa678ba4   Alexey Dobriyan   netns bridge: all...
301
  		args[2] = get_bridge_ifindices(net, indices, args[2]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
303
304
305
306
307
308
309
310
311
312
313
  
  		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];
cb9905030   Eric W. Biederman   net: Allow userns...
314
  		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315
316
317
318
319
320
321
322
  			return -EPERM;
  
  		if (copy_from_user(buf, (void __user *)args[1], IFNAMSIZ))
  			return -EFAULT;
  
  		buf[IFNAMSIZ-1] = 0;
  
  		if (args[0] == BRCTL_ADD_BRIDGE)
4aa678ba4   Alexey Dobriyan   netns bridge: all...
323
  			return br_add_bridge(net, buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324

4aa678ba4   Alexey Dobriyan   netns bridge: all...
325
  		return br_del_bridge(net, buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326
327
328
329
330
  	}
  	}
  
  	return -EOPNOTSUPP;
  }
881d966b4   Eric W. Biederman   [NET]: Make the d...
331
  int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *uarg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
333
334
335
  {
  	switch (cmd) {
  	case SIOCGIFBR:
  	case SIOCSIFBR:
4aa678ba4   Alexey Dobriyan   netns bridge: all...
336
  		return old_deviceless(net, uarg);
9d6f229fc   YOSHIFUJI Hideaki   [NET] BRIDGE: Fix...
337

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
339
340
341
  	case SIOCBRADDBR:
  	case SIOCBRDELBR:
  	{
  		char buf[IFNAMSIZ];
cb9905030   Eric W. Biederman   net: Allow userns...
342
  		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
344
345
346
347
348
349
  			return -EPERM;
  
  		if (copy_from_user(buf, uarg, IFNAMSIZ))
  			return -EFAULT;
  
  		buf[IFNAMSIZ-1] = 0;
  		if (cmd == SIOCBRADDBR)
4aa678ba4   Alexey Dobriyan   netns bridge: all...
350
  			return br_add_bridge(net, buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351

4aa678ba4   Alexey Dobriyan   netns bridge: all...
352
  		return br_del_bridge(net, buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
354
355
356
357
358
359
360
  	}
  	}
  	return -EOPNOTSUPP;
  }
  
  int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
  {
  	struct net_bridge *br = netdev_priv(dev);
31a5b837c   tanxiaojun   bridge: add space...
361
  	switch (cmd) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
362
363
364
365
366
367
368
369
  	case SIOCDEVPRIVATE:
  		return old_dev_ioctl(dev, rq, cmd);
  
  	case SIOCBRADDIF:
  	case SIOCBRDELIF:
  		return add_del_if(br, rq->ifr_ifindex, cmd == SIOCBRADDIF);
  
  	}
28a16c979   stephen hemminger   bridge: change co...
370
371
  	br_debug(br, "Bridge does not support ioctl 0x%x
  ", cmd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
373
  	return -EOPNOTSUPP;
  }