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