cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

br_ioctl.c (9613B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *	Ioctl handler
      4 *	Linux ethernet bridge
      5 *
      6 *	Authors:
      7 *	Lennert Buytenhek		<buytenh@gnu.org>
      8 */
      9
     10#include <linux/capability.h>
     11#include <linux/compat.h>
     12#include <linux/kernel.h>
     13#include <linux/if_bridge.h>
     14#include <linux/netdevice.h>
     15#include <linux/slab.h>
     16#include <linux/times.h>
     17#include <net/net_namespace.h>
     18#include <linux/uaccess.h>
     19#include "br_private.h"
     20
     21static int get_bridge_ifindices(struct net *net, int *indices, int num)
     22{
     23	struct net_device *dev;
     24	int i = 0;
     25
     26	rcu_read_lock();
     27	for_each_netdev_rcu(net, dev) {
     28		if (i >= num)
     29			break;
     30		if (netif_is_bridge_master(dev))
     31			indices[i++] = dev->ifindex;
     32	}
     33	rcu_read_unlock();
     34
     35	return i;
     36}
     37
     38/* called with RTNL */
     39static void get_port_ifindices(struct net_bridge *br, int *ifindices, int num)
     40{
     41	struct net_bridge_port *p;
     42
     43	list_for_each_entry(p, &br->port_list, list) {
     44		if (p->port_no < num)
     45			ifindices[p->port_no] = p->dev->ifindex;
     46	}
     47}
     48
     49/*
     50 * Format up to a page worth of forwarding table entries
     51 * userbuf -- where to copy result
     52 * maxnum  -- maximum number of entries desired
     53 *            (limited to a page for sanity)
     54 * offset  -- number of records to skip
     55 */
     56static int get_fdb_entries(struct net_bridge *br, void __user *userbuf,
     57			   unsigned long maxnum, unsigned long offset)
     58{
     59	int num;
     60	void *buf;
     61	size_t size;
     62
     63	/* Clamp size to PAGE_SIZE, test maxnum to avoid overflow */
     64	if (maxnum > PAGE_SIZE/sizeof(struct __fdb_entry))
     65		maxnum = PAGE_SIZE/sizeof(struct __fdb_entry);
     66
     67	size = maxnum * sizeof(struct __fdb_entry);
     68
     69	buf = kmalloc(size, GFP_USER);
     70	if (!buf)
     71		return -ENOMEM;
     72
     73	num = br_fdb_fillbuf(br, buf, maxnum, offset);
     74	if (num > 0) {
     75		if (copy_to_user(userbuf, buf,
     76				 array_size(num, sizeof(struct __fdb_entry))))
     77			num = -EFAULT;
     78	}
     79	kfree(buf);
     80
     81	return num;
     82}
     83
     84/* called with RTNL */
     85static int add_del_if(struct net_bridge *br, int ifindex, int isadd)
     86{
     87	struct net *net = dev_net(br->dev);
     88	struct net_device *dev;
     89	int ret;
     90
     91	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
     92		return -EPERM;
     93
     94	dev = __dev_get_by_index(net, ifindex);
     95	if (dev == NULL)
     96		return -EINVAL;
     97
     98	if (isadd)
     99		ret = br_add_if(br, dev, NULL);
    100	else
    101		ret = br_del_if(br, dev);
    102
    103	return ret;
    104}
    105
    106#define BR_UARGS_MAX 4
    107static int br_dev_read_uargs(unsigned long *args, size_t nr_args,
    108			     void __user **argp, void __user *data)
    109{
    110	int ret;
    111
    112	if (nr_args < 2 || nr_args > BR_UARGS_MAX)
    113		return -EINVAL;
    114
    115	if (in_compat_syscall()) {
    116		unsigned int cargs[BR_UARGS_MAX];
    117		int i;
    118
    119		ret = copy_from_user(cargs, data, nr_args * sizeof(*cargs));
    120		if (ret)
    121			goto fault;
    122
    123		for (i = 0; i < nr_args; ++i)
    124			args[i] = cargs[i];
    125
    126		*argp = compat_ptr(args[1]);
    127	} else {
    128		ret = copy_from_user(args, data, nr_args * sizeof(*args));
    129		if (ret)
    130			goto fault;
    131		*argp = (void __user *)args[1];
    132	}
    133
    134	return 0;
    135fault:
    136	return -EFAULT;
    137}
    138
    139/*
    140 * Legacy ioctl's through SIOCDEVPRIVATE
    141 * This interface is deprecated because it was too difficult
    142 * to do the translation for 32/64bit ioctl compatibility.
    143 */
    144int br_dev_siocdevprivate(struct net_device *dev, struct ifreq *rq,
    145			  void __user *data, int cmd)
    146{
    147	struct net_bridge *br = netdev_priv(dev);
    148	struct net_bridge_port *p = NULL;
    149	unsigned long args[4];
    150	void __user *argp;
    151	int ret;
    152
    153	ret = br_dev_read_uargs(args, ARRAY_SIZE(args), &argp, data);
    154	if (ret)
    155		return ret;
    156
    157	switch (args[0]) {
    158	case BRCTL_ADD_IF:
    159	case BRCTL_DEL_IF:
    160		return add_del_if(br, args[1], args[0] == BRCTL_ADD_IF);
    161
    162	case BRCTL_GET_BRIDGE_INFO:
    163	{
    164		struct __bridge_info b;
    165
    166		memset(&b, 0, sizeof(struct __bridge_info));
    167		rcu_read_lock();
    168		memcpy(&b.designated_root, &br->designated_root, 8);
    169		memcpy(&b.bridge_id, &br->bridge_id, 8);
    170		b.root_path_cost = br->root_path_cost;
    171		b.max_age = jiffies_to_clock_t(br->max_age);
    172		b.hello_time = jiffies_to_clock_t(br->hello_time);
    173		b.forward_delay = br->forward_delay;
    174		b.bridge_max_age = br->bridge_max_age;
    175		b.bridge_hello_time = br->bridge_hello_time;
    176		b.bridge_forward_delay = jiffies_to_clock_t(br->bridge_forward_delay);
    177		b.topology_change = br->topology_change;
    178		b.topology_change_detected = br->topology_change_detected;
    179		b.root_port = br->root_port;
    180
    181		b.stp_enabled = (br->stp_enabled != BR_NO_STP);
    182		b.ageing_time = jiffies_to_clock_t(br->ageing_time);
    183		b.hello_timer_value = br_timer_value(&br->hello_timer);
    184		b.tcn_timer_value = br_timer_value(&br->tcn_timer);
    185		b.topology_change_timer_value = br_timer_value(&br->topology_change_timer);
    186		b.gc_timer_value = br_timer_value(&br->gc_work.timer);
    187		rcu_read_unlock();
    188
    189		if (copy_to_user((void __user *)args[1], &b, sizeof(b)))
    190			return -EFAULT;
    191
    192		return 0;
    193	}
    194
    195	case BRCTL_GET_PORT_LIST:
    196	{
    197		int num, *indices;
    198
    199		num = args[2];
    200		if (num < 0)
    201			return -EINVAL;
    202		if (num == 0)
    203			num = 256;
    204		if (num > BR_MAX_PORTS)
    205			num = BR_MAX_PORTS;
    206
    207		indices = kcalloc(num, sizeof(int), GFP_KERNEL);
    208		if (indices == NULL)
    209			return -ENOMEM;
    210
    211		get_port_ifindices(br, indices, num);
    212		if (copy_to_user(argp, indices, array_size(num, sizeof(int))))
    213			num =  -EFAULT;
    214		kfree(indices);
    215		return num;
    216	}
    217
    218	case BRCTL_SET_BRIDGE_FORWARD_DELAY:
    219		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
    220			return -EPERM;
    221
    222		ret = br_set_forward_delay(br, args[1]);
    223		break;
    224
    225	case BRCTL_SET_BRIDGE_HELLO_TIME:
    226		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
    227			return -EPERM;
    228
    229		ret = br_set_hello_time(br, args[1]);
    230		break;
    231
    232	case BRCTL_SET_BRIDGE_MAX_AGE:
    233		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
    234			return -EPERM;
    235
    236		ret = br_set_max_age(br, args[1]);
    237		break;
    238
    239	case BRCTL_SET_AGEING_TIME:
    240		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
    241			return -EPERM;
    242
    243		ret = br_set_ageing_time(br, args[1]);
    244		break;
    245
    246	case BRCTL_GET_PORT_INFO:
    247	{
    248		struct __port_info p;
    249		struct net_bridge_port *pt;
    250
    251		rcu_read_lock();
    252		if ((pt = br_get_port(br, args[2])) == NULL) {
    253			rcu_read_unlock();
    254			return -EINVAL;
    255		}
    256
    257		memset(&p, 0, sizeof(struct __port_info));
    258		memcpy(&p.designated_root, &pt->designated_root, 8);
    259		memcpy(&p.designated_bridge, &pt->designated_bridge, 8);
    260		p.port_id = pt->port_id;
    261		p.designated_port = pt->designated_port;
    262		p.path_cost = pt->path_cost;
    263		p.designated_cost = pt->designated_cost;
    264		p.state = pt->state;
    265		p.top_change_ack = pt->topology_change_ack;
    266		p.config_pending = pt->config_pending;
    267		p.message_age_timer_value = br_timer_value(&pt->message_age_timer);
    268		p.forward_delay_timer_value = br_timer_value(&pt->forward_delay_timer);
    269		p.hold_timer_value = br_timer_value(&pt->hold_timer);
    270
    271		rcu_read_unlock();
    272
    273		if (copy_to_user(argp, &p, sizeof(p)))
    274			return -EFAULT;
    275
    276		return 0;
    277	}
    278
    279	case BRCTL_SET_BRIDGE_STP_STATE:
    280		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
    281			return -EPERM;
    282
    283		ret = br_stp_set_enabled(br, args[1], NULL);
    284		break;
    285
    286	case BRCTL_SET_BRIDGE_PRIORITY:
    287		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
    288			return -EPERM;
    289
    290		br_stp_set_bridge_priority(br, args[1]);
    291		ret = 0;
    292		break;
    293
    294	case BRCTL_SET_PORT_PRIORITY:
    295	{
    296		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
    297			return -EPERM;
    298
    299		spin_lock_bh(&br->lock);
    300		if ((p = br_get_port(br, args[1])) == NULL)
    301			ret = -EINVAL;
    302		else
    303			ret = br_stp_set_port_priority(p, args[2]);
    304		spin_unlock_bh(&br->lock);
    305		break;
    306	}
    307
    308	case BRCTL_SET_PATH_COST:
    309	{
    310		if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
    311			return -EPERM;
    312
    313		spin_lock_bh(&br->lock);
    314		if ((p = br_get_port(br, args[1])) == NULL)
    315			ret = -EINVAL;
    316		else
    317			ret = br_stp_set_path_cost(p, args[2]);
    318		spin_unlock_bh(&br->lock);
    319		break;
    320	}
    321
    322	case BRCTL_GET_FDB_ENTRIES:
    323		return get_fdb_entries(br, argp, args[2], args[3]);
    324
    325	default:
    326		ret = -EOPNOTSUPP;
    327	}
    328
    329	if (!ret) {
    330		if (p)
    331			br_ifinfo_notify(RTM_NEWLINK, NULL, p);
    332		else
    333			netdev_state_change(br->dev);
    334	}
    335
    336	return ret;
    337}
    338
    339static int old_deviceless(struct net *net, void __user *data)
    340{
    341	unsigned long args[3];
    342	void __user *argp;
    343	int ret;
    344
    345	ret = br_dev_read_uargs(args, ARRAY_SIZE(args), &argp, data);
    346	if (ret)
    347		return ret;
    348
    349	switch (args[0]) {
    350	case BRCTL_GET_VERSION:
    351		return BRCTL_VERSION;
    352
    353	case BRCTL_GET_BRIDGES:
    354	{
    355		int *indices;
    356		int ret = 0;
    357
    358		if (args[2] >= 2048)
    359			return -ENOMEM;
    360		indices = kcalloc(args[2], sizeof(int), GFP_KERNEL);
    361		if (indices == NULL)
    362			return -ENOMEM;
    363
    364		args[2] = get_bridge_ifindices(net, indices, args[2]);
    365
    366		ret = copy_to_user(argp, indices,
    367				   array_size(args[2], sizeof(int)))
    368			? -EFAULT : args[2];
    369
    370		kfree(indices);
    371		return ret;
    372	}
    373
    374	case BRCTL_ADD_BRIDGE:
    375	case BRCTL_DEL_BRIDGE:
    376	{
    377		char buf[IFNAMSIZ];
    378
    379		if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
    380			return -EPERM;
    381
    382		if (copy_from_user(buf, argp, IFNAMSIZ))
    383			return -EFAULT;
    384
    385		buf[IFNAMSIZ-1] = 0;
    386
    387		if (args[0] == BRCTL_ADD_BRIDGE)
    388			return br_add_bridge(net, buf);
    389
    390		return br_del_bridge(net, buf);
    391	}
    392	}
    393
    394	return -EOPNOTSUPP;
    395}
    396
    397int br_ioctl_stub(struct net *net, struct net_bridge *br, unsigned int cmd,
    398		  struct ifreq *ifr, void __user *uarg)
    399{
    400	int ret = -EOPNOTSUPP;
    401
    402	rtnl_lock();
    403
    404	switch (cmd) {
    405	case SIOCGIFBR:
    406	case SIOCSIFBR:
    407		ret = old_deviceless(net, uarg);
    408		break;
    409	case SIOCBRADDBR:
    410	case SIOCBRDELBR:
    411	{
    412		char buf[IFNAMSIZ];
    413
    414		if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) {
    415			ret = -EPERM;
    416			break;
    417		}
    418
    419		if (copy_from_user(buf, uarg, IFNAMSIZ)) {
    420			ret = -EFAULT;
    421			break;
    422		}
    423
    424		buf[IFNAMSIZ-1] = 0;
    425		if (cmd == SIOCBRADDBR)
    426			ret = br_add_bridge(net, buf);
    427		else
    428			ret = br_del_bridge(net, buf);
    429	}
    430		break;
    431	case SIOCBRADDIF:
    432	case SIOCBRDELIF:
    433		ret = add_del_if(br, ifr->ifr_ifindex, cmd == SIOCBRADDIF);
    434		break;
    435	}
    436
    437	rtnl_unlock();
    438
    439	return ret;
    440}