18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	Ioctl handler
48c2ecf20Sopenharmony_ci *	Linux ethernet bridge
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *	Authors:
78c2ecf20Sopenharmony_ci *	Lennert Buytenhek		<buytenh@gnu.org>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/capability.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/if_bridge.h>
138c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/times.h>
168c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
178c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
188c2ecf20Sopenharmony_ci#include "br_private.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic int get_bridge_ifindices(struct net *net, int *indices, int num)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	struct net_device *dev;
238c2ecf20Sopenharmony_ci	int i = 0;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	rcu_read_lock();
268c2ecf20Sopenharmony_ci	for_each_netdev_rcu(net, dev) {
278c2ecf20Sopenharmony_ci		if (i >= num)
288c2ecf20Sopenharmony_ci			break;
298c2ecf20Sopenharmony_ci		if (dev->priv_flags & IFF_EBRIDGE)
308c2ecf20Sopenharmony_ci			indices[i++] = dev->ifindex;
318c2ecf20Sopenharmony_ci	}
328c2ecf20Sopenharmony_ci	rcu_read_unlock();
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	return i;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* called with RTNL */
388c2ecf20Sopenharmony_cistatic void get_port_ifindices(struct net_bridge *br, int *ifindices, int num)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct net_bridge_port *p;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	list_for_each_entry(p, &br->port_list, list) {
438c2ecf20Sopenharmony_ci		if (p->port_no < num)
448c2ecf20Sopenharmony_ci			ifindices[p->port_no] = p->dev->ifindex;
458c2ecf20Sopenharmony_ci	}
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/*
498c2ecf20Sopenharmony_ci * Format up to a page worth of forwarding table entries
508c2ecf20Sopenharmony_ci * userbuf -- where to copy result
518c2ecf20Sopenharmony_ci * maxnum  -- maximum number of entries desired
528c2ecf20Sopenharmony_ci *            (limited to a page for sanity)
538c2ecf20Sopenharmony_ci * offset  -- number of records to skip
548c2ecf20Sopenharmony_ci */
558c2ecf20Sopenharmony_cistatic int get_fdb_entries(struct net_bridge *br, void __user *userbuf,
568c2ecf20Sopenharmony_ci			   unsigned long maxnum, unsigned long offset)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	int num;
598c2ecf20Sopenharmony_ci	void *buf;
608c2ecf20Sopenharmony_ci	size_t size;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	/* Clamp size to PAGE_SIZE, test maxnum to avoid overflow */
638c2ecf20Sopenharmony_ci	if (maxnum > PAGE_SIZE/sizeof(struct __fdb_entry))
648c2ecf20Sopenharmony_ci		maxnum = PAGE_SIZE/sizeof(struct __fdb_entry);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	size = maxnum * sizeof(struct __fdb_entry);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	buf = kmalloc(size, GFP_USER);
698c2ecf20Sopenharmony_ci	if (!buf)
708c2ecf20Sopenharmony_ci		return -ENOMEM;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	num = br_fdb_fillbuf(br, buf, maxnum, offset);
738c2ecf20Sopenharmony_ci	if (num > 0) {
748c2ecf20Sopenharmony_ci		if (copy_to_user(userbuf, buf, num*sizeof(struct __fdb_entry)))
758c2ecf20Sopenharmony_ci			num = -EFAULT;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci	kfree(buf);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	return num;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/* called with RTNL */
838c2ecf20Sopenharmony_cistatic int add_del_if(struct net_bridge *br, int ifindex, int isadd)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	struct net *net = dev_net(br->dev);
868c2ecf20Sopenharmony_ci	struct net_device *dev;
878c2ecf20Sopenharmony_ci	int ret;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
908c2ecf20Sopenharmony_ci		return -EPERM;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	dev = __dev_get_by_index(net, ifindex);
938c2ecf20Sopenharmony_ci	if (dev == NULL)
948c2ecf20Sopenharmony_ci		return -EINVAL;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (isadd)
978c2ecf20Sopenharmony_ci		ret = br_add_if(br, dev, NULL);
988c2ecf20Sopenharmony_ci	else
998c2ecf20Sopenharmony_ci		ret = br_del_if(br, dev);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return ret;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/*
1058c2ecf20Sopenharmony_ci * Legacy ioctl's through SIOCDEVPRIVATE
1068c2ecf20Sopenharmony_ci * This interface is deprecated because it was too difficult
1078c2ecf20Sopenharmony_ci * to do the translation for 32/64bit ioctl compatibility.
1088c2ecf20Sopenharmony_ci */
1098c2ecf20Sopenharmony_cistatic int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	struct net_bridge *br = netdev_priv(dev);
1128c2ecf20Sopenharmony_ci	struct net_bridge_port *p = NULL;
1138c2ecf20Sopenharmony_ci	unsigned long args[4];
1148c2ecf20Sopenharmony_ci	int ret = -EOPNOTSUPP;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (copy_from_user(args, rq->ifr_data, sizeof(args)))
1178c2ecf20Sopenharmony_ci		return -EFAULT;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	switch (args[0]) {
1208c2ecf20Sopenharmony_ci	case BRCTL_ADD_IF:
1218c2ecf20Sopenharmony_ci	case BRCTL_DEL_IF:
1228c2ecf20Sopenharmony_ci		return add_del_if(br, args[1], args[0] == BRCTL_ADD_IF);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	case BRCTL_GET_BRIDGE_INFO:
1258c2ecf20Sopenharmony_ci	{
1268c2ecf20Sopenharmony_ci		struct __bridge_info b;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci		memset(&b, 0, sizeof(struct __bridge_info));
1298c2ecf20Sopenharmony_ci		rcu_read_lock();
1308c2ecf20Sopenharmony_ci		memcpy(&b.designated_root, &br->designated_root, 8);
1318c2ecf20Sopenharmony_ci		memcpy(&b.bridge_id, &br->bridge_id, 8);
1328c2ecf20Sopenharmony_ci		b.root_path_cost = br->root_path_cost;
1338c2ecf20Sopenharmony_ci		b.max_age = jiffies_to_clock_t(br->max_age);
1348c2ecf20Sopenharmony_ci		b.hello_time = jiffies_to_clock_t(br->hello_time);
1358c2ecf20Sopenharmony_ci		b.forward_delay = br->forward_delay;
1368c2ecf20Sopenharmony_ci		b.bridge_max_age = br->bridge_max_age;
1378c2ecf20Sopenharmony_ci		b.bridge_hello_time = br->bridge_hello_time;
1388c2ecf20Sopenharmony_ci		b.bridge_forward_delay = jiffies_to_clock_t(br->bridge_forward_delay);
1398c2ecf20Sopenharmony_ci		b.topology_change = br->topology_change;
1408c2ecf20Sopenharmony_ci		b.topology_change_detected = br->topology_change_detected;
1418c2ecf20Sopenharmony_ci		b.root_port = br->root_port;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		b.stp_enabled = (br->stp_enabled != BR_NO_STP);
1448c2ecf20Sopenharmony_ci		b.ageing_time = jiffies_to_clock_t(br->ageing_time);
1458c2ecf20Sopenharmony_ci		b.hello_timer_value = br_timer_value(&br->hello_timer);
1468c2ecf20Sopenharmony_ci		b.tcn_timer_value = br_timer_value(&br->tcn_timer);
1478c2ecf20Sopenharmony_ci		b.topology_change_timer_value = br_timer_value(&br->topology_change_timer);
1488c2ecf20Sopenharmony_ci		b.gc_timer_value = br_timer_value(&br->gc_work.timer);
1498c2ecf20Sopenharmony_ci		rcu_read_unlock();
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci		if (copy_to_user((void __user *)args[1], &b, sizeof(b)))
1528c2ecf20Sopenharmony_ci			return -EFAULT;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci		return 0;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	case BRCTL_GET_PORT_LIST:
1588c2ecf20Sopenharmony_ci	{
1598c2ecf20Sopenharmony_ci		int num, *indices;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		num = args[2];
1628c2ecf20Sopenharmony_ci		if (num < 0)
1638c2ecf20Sopenharmony_ci			return -EINVAL;
1648c2ecf20Sopenharmony_ci		if (num == 0)
1658c2ecf20Sopenharmony_ci			num = 256;
1668c2ecf20Sopenharmony_ci		if (num > BR_MAX_PORTS)
1678c2ecf20Sopenharmony_ci			num = BR_MAX_PORTS;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci		indices = kcalloc(num, sizeof(int), GFP_KERNEL);
1708c2ecf20Sopenharmony_ci		if (indices == NULL)
1718c2ecf20Sopenharmony_ci			return -ENOMEM;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci		get_port_ifindices(br, indices, num);
1748c2ecf20Sopenharmony_ci		if (copy_to_user((void __user *)args[1], indices, num*sizeof(int)))
1758c2ecf20Sopenharmony_ci			num =  -EFAULT;
1768c2ecf20Sopenharmony_ci		kfree(indices);
1778c2ecf20Sopenharmony_ci		return num;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	case BRCTL_SET_BRIDGE_FORWARD_DELAY:
1818c2ecf20Sopenharmony_ci		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
1828c2ecf20Sopenharmony_ci			return -EPERM;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		ret = br_set_forward_delay(br, args[1]);
1858c2ecf20Sopenharmony_ci		break;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	case BRCTL_SET_BRIDGE_HELLO_TIME:
1888c2ecf20Sopenharmony_ci		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
1898c2ecf20Sopenharmony_ci			return -EPERM;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci		ret = br_set_hello_time(br, args[1]);
1928c2ecf20Sopenharmony_ci		break;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	case BRCTL_SET_BRIDGE_MAX_AGE:
1958c2ecf20Sopenharmony_ci		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
1968c2ecf20Sopenharmony_ci			return -EPERM;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		ret = br_set_max_age(br, args[1]);
1998c2ecf20Sopenharmony_ci		break;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	case BRCTL_SET_AGEING_TIME:
2028c2ecf20Sopenharmony_ci		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
2038c2ecf20Sopenharmony_ci			return -EPERM;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci		ret = br_set_ageing_time(br, args[1]);
2068c2ecf20Sopenharmony_ci		break;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	case BRCTL_GET_PORT_INFO:
2098c2ecf20Sopenharmony_ci	{
2108c2ecf20Sopenharmony_ci		struct __port_info p;
2118c2ecf20Sopenharmony_ci		struct net_bridge_port *pt;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci		rcu_read_lock();
2148c2ecf20Sopenharmony_ci		if ((pt = br_get_port(br, args[2])) == NULL) {
2158c2ecf20Sopenharmony_ci			rcu_read_unlock();
2168c2ecf20Sopenharmony_ci			return -EINVAL;
2178c2ecf20Sopenharmony_ci		}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci		memset(&p, 0, sizeof(struct __port_info));
2208c2ecf20Sopenharmony_ci		memcpy(&p.designated_root, &pt->designated_root, 8);
2218c2ecf20Sopenharmony_ci		memcpy(&p.designated_bridge, &pt->designated_bridge, 8);
2228c2ecf20Sopenharmony_ci		p.port_id = pt->port_id;
2238c2ecf20Sopenharmony_ci		p.designated_port = pt->designated_port;
2248c2ecf20Sopenharmony_ci		p.path_cost = pt->path_cost;
2258c2ecf20Sopenharmony_ci		p.designated_cost = pt->designated_cost;
2268c2ecf20Sopenharmony_ci		p.state = pt->state;
2278c2ecf20Sopenharmony_ci		p.top_change_ack = pt->topology_change_ack;
2288c2ecf20Sopenharmony_ci		p.config_pending = pt->config_pending;
2298c2ecf20Sopenharmony_ci		p.message_age_timer_value = br_timer_value(&pt->message_age_timer);
2308c2ecf20Sopenharmony_ci		p.forward_delay_timer_value = br_timer_value(&pt->forward_delay_timer);
2318c2ecf20Sopenharmony_ci		p.hold_timer_value = br_timer_value(&pt->hold_timer);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci		rcu_read_unlock();
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		if (copy_to_user((void __user *)args[1], &p, sizeof(p)))
2368c2ecf20Sopenharmony_ci			return -EFAULT;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci		return 0;
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	case BRCTL_SET_BRIDGE_STP_STATE:
2428c2ecf20Sopenharmony_ci		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
2438c2ecf20Sopenharmony_ci			return -EPERM;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci		ret = br_stp_set_enabled(br, args[1], NULL);
2468c2ecf20Sopenharmony_ci		break;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	case BRCTL_SET_BRIDGE_PRIORITY:
2498c2ecf20Sopenharmony_ci		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
2508c2ecf20Sopenharmony_ci			return -EPERM;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci		br_stp_set_bridge_priority(br, args[1]);
2538c2ecf20Sopenharmony_ci		ret = 0;
2548c2ecf20Sopenharmony_ci		break;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	case BRCTL_SET_PORT_PRIORITY:
2578c2ecf20Sopenharmony_ci	{
2588c2ecf20Sopenharmony_ci		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
2598c2ecf20Sopenharmony_ci			return -EPERM;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		spin_lock_bh(&br->lock);
2628c2ecf20Sopenharmony_ci		if ((p = br_get_port(br, args[1])) == NULL)
2638c2ecf20Sopenharmony_ci			ret = -EINVAL;
2648c2ecf20Sopenharmony_ci		else
2658c2ecf20Sopenharmony_ci			ret = br_stp_set_port_priority(p, args[2]);
2668c2ecf20Sopenharmony_ci		spin_unlock_bh(&br->lock);
2678c2ecf20Sopenharmony_ci		break;
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	case BRCTL_SET_PATH_COST:
2718c2ecf20Sopenharmony_ci	{
2728c2ecf20Sopenharmony_ci		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
2738c2ecf20Sopenharmony_ci			return -EPERM;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		spin_lock_bh(&br->lock);
2768c2ecf20Sopenharmony_ci		if ((p = br_get_port(br, args[1])) == NULL)
2778c2ecf20Sopenharmony_ci			ret = -EINVAL;
2788c2ecf20Sopenharmony_ci		else
2798c2ecf20Sopenharmony_ci			ret = br_stp_set_path_cost(p, args[2]);
2808c2ecf20Sopenharmony_ci		spin_unlock_bh(&br->lock);
2818c2ecf20Sopenharmony_ci		break;
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	case BRCTL_GET_FDB_ENTRIES:
2858c2ecf20Sopenharmony_ci		return get_fdb_entries(br, (void __user *)args[1],
2868c2ecf20Sopenharmony_ci				       args[2], args[3]);
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (!ret) {
2908c2ecf20Sopenharmony_ci		if (p)
2918c2ecf20Sopenharmony_ci			br_ifinfo_notify(RTM_NEWLINK, NULL, p);
2928c2ecf20Sopenharmony_ci		else
2938c2ecf20Sopenharmony_ci			netdev_state_change(br->dev);
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	return ret;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic int old_deviceless(struct net *net, void __user *uarg)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	unsigned long args[3];
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	if (copy_from_user(args, uarg, sizeof(args)))
3048c2ecf20Sopenharmony_ci		return -EFAULT;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	switch (args[0]) {
3078c2ecf20Sopenharmony_ci	case BRCTL_GET_VERSION:
3088c2ecf20Sopenharmony_ci		return BRCTL_VERSION;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	case BRCTL_GET_BRIDGES:
3118c2ecf20Sopenharmony_ci	{
3128c2ecf20Sopenharmony_ci		int *indices;
3138c2ecf20Sopenharmony_ci		int ret = 0;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci		if (args[2] >= 2048)
3168c2ecf20Sopenharmony_ci			return -ENOMEM;
3178c2ecf20Sopenharmony_ci		indices = kcalloc(args[2], sizeof(int), GFP_KERNEL);
3188c2ecf20Sopenharmony_ci		if (indices == NULL)
3198c2ecf20Sopenharmony_ci			return -ENOMEM;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci		args[2] = get_bridge_ifindices(net, indices, args[2]);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci		ret = copy_to_user((void __user *)args[1], indices, args[2]*sizeof(int))
3248c2ecf20Sopenharmony_ci			? -EFAULT : args[2];
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci		kfree(indices);
3278c2ecf20Sopenharmony_ci		return ret;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	case BRCTL_ADD_BRIDGE:
3318c2ecf20Sopenharmony_ci	case BRCTL_DEL_BRIDGE:
3328c2ecf20Sopenharmony_ci	{
3338c2ecf20Sopenharmony_ci		char buf[IFNAMSIZ];
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
3368c2ecf20Sopenharmony_ci			return -EPERM;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci		if (copy_from_user(buf, (void __user *)args[1], IFNAMSIZ))
3398c2ecf20Sopenharmony_ci			return -EFAULT;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci		buf[IFNAMSIZ-1] = 0;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci		if (args[0] == BRCTL_ADD_BRIDGE)
3448c2ecf20Sopenharmony_ci			return br_add_bridge(net, buf);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		return br_del_bridge(net, buf);
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ciint br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *uarg)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	switch (cmd) {
3568c2ecf20Sopenharmony_ci	case SIOCGIFBR:
3578c2ecf20Sopenharmony_ci	case SIOCSIFBR:
3588c2ecf20Sopenharmony_ci		return old_deviceless(net, uarg);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	case SIOCBRADDBR:
3618c2ecf20Sopenharmony_ci	case SIOCBRDELBR:
3628c2ecf20Sopenharmony_ci	{
3638c2ecf20Sopenharmony_ci		char buf[IFNAMSIZ];
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
3668c2ecf20Sopenharmony_ci			return -EPERM;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci		if (copy_from_user(buf, uarg, IFNAMSIZ))
3698c2ecf20Sopenharmony_ci			return -EFAULT;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci		buf[IFNAMSIZ-1] = 0;
3728c2ecf20Sopenharmony_ci		if (cmd == SIOCBRADDBR)
3738c2ecf20Sopenharmony_ci			return br_add_bridge(net, buf);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci		return br_del_bridge(net, buf);
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ciint br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	struct net_bridge *br = netdev_priv(dev);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	switch (cmd) {
3868c2ecf20Sopenharmony_ci	case SIOCDEVPRIVATE:
3878c2ecf20Sopenharmony_ci		return old_dev_ioctl(dev, rq, cmd);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	case SIOCBRADDIF:
3908c2ecf20Sopenharmony_ci	case SIOCBRDELIF:
3918c2ecf20Sopenharmony_ci		return add_del_if(br, rq->ifr_ifindex, cmd == SIOCBRADDIF);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	br_debug(br, "Bridge does not support ioctl 0x%x\n", cmd);
3968c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
3978c2ecf20Sopenharmony_ci}
398