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

l3mdev.c (6743B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * net/l3mdev/l3mdev.c - L3 master device implementation
      4 * Copyright (c) 2015 Cumulus Networks
      5 * Copyright (c) 2015 David Ahern <dsa@cumulusnetworks.com>
      6 */
      7
      8#include <linux/netdevice.h>
      9#include <net/fib_rules.h>
     10#include <net/l3mdev.h>
     11
     12static DEFINE_SPINLOCK(l3mdev_lock);
     13
     14struct l3mdev_handler {
     15	lookup_by_table_id_t dev_lookup;
     16};
     17
     18static struct l3mdev_handler l3mdev_handlers[L3MDEV_TYPE_MAX + 1];
     19
     20static int l3mdev_check_type(enum l3mdev_type l3type)
     21{
     22	if (l3type <= L3MDEV_TYPE_UNSPEC || l3type > L3MDEV_TYPE_MAX)
     23		return -EINVAL;
     24
     25	return 0;
     26}
     27
     28int l3mdev_table_lookup_register(enum l3mdev_type l3type,
     29				 lookup_by_table_id_t fn)
     30{
     31	struct l3mdev_handler *hdlr;
     32	int res;
     33
     34	res = l3mdev_check_type(l3type);
     35	if (res)
     36		return res;
     37
     38	hdlr = &l3mdev_handlers[l3type];
     39
     40	spin_lock(&l3mdev_lock);
     41
     42	if (hdlr->dev_lookup) {
     43		res = -EBUSY;
     44		goto unlock;
     45	}
     46
     47	hdlr->dev_lookup = fn;
     48	res = 0;
     49
     50unlock:
     51	spin_unlock(&l3mdev_lock);
     52
     53	return res;
     54}
     55EXPORT_SYMBOL_GPL(l3mdev_table_lookup_register);
     56
     57void l3mdev_table_lookup_unregister(enum l3mdev_type l3type,
     58				    lookup_by_table_id_t fn)
     59{
     60	struct l3mdev_handler *hdlr;
     61
     62	if (l3mdev_check_type(l3type))
     63		return;
     64
     65	hdlr = &l3mdev_handlers[l3type];
     66
     67	spin_lock(&l3mdev_lock);
     68
     69	if (hdlr->dev_lookup == fn)
     70		hdlr->dev_lookup = NULL;
     71
     72	spin_unlock(&l3mdev_lock);
     73}
     74EXPORT_SYMBOL_GPL(l3mdev_table_lookup_unregister);
     75
     76int l3mdev_ifindex_lookup_by_table_id(enum l3mdev_type l3type,
     77				      struct net *net, u32 table_id)
     78{
     79	lookup_by_table_id_t lookup;
     80	struct l3mdev_handler *hdlr;
     81	int ifindex = -EINVAL;
     82	int res;
     83
     84	res = l3mdev_check_type(l3type);
     85	if (res)
     86		return res;
     87
     88	hdlr = &l3mdev_handlers[l3type];
     89
     90	spin_lock(&l3mdev_lock);
     91
     92	lookup = hdlr->dev_lookup;
     93	if (!lookup)
     94		goto unlock;
     95
     96	ifindex = lookup(net, table_id);
     97
     98unlock:
     99	spin_unlock(&l3mdev_lock);
    100
    101	return ifindex;
    102}
    103EXPORT_SYMBOL_GPL(l3mdev_ifindex_lookup_by_table_id);
    104
    105/**
    106 *	l3mdev_master_ifindex_rcu - get index of L3 master device
    107 *	@dev: targeted interface
    108 */
    109
    110int l3mdev_master_ifindex_rcu(const struct net_device *dev)
    111{
    112	int ifindex = 0;
    113
    114	if (!dev)
    115		return 0;
    116
    117	if (netif_is_l3_master(dev)) {
    118		ifindex = dev->ifindex;
    119	} else if (netif_is_l3_slave(dev)) {
    120		struct net_device *master;
    121		struct net_device *_dev = (struct net_device *)dev;
    122
    123		/* netdev_master_upper_dev_get_rcu calls
    124		 * list_first_or_null_rcu to walk the upper dev list.
    125		 * list_first_or_null_rcu does not handle a const arg. We aren't
    126		 * making changes, just want the master device from that list so
    127		 * typecast to remove the const
    128		 */
    129		master = netdev_master_upper_dev_get_rcu(_dev);
    130		if (master)
    131			ifindex = master->ifindex;
    132	}
    133
    134	return ifindex;
    135}
    136EXPORT_SYMBOL_GPL(l3mdev_master_ifindex_rcu);
    137
    138/**
    139 *	l3mdev_master_upper_ifindex_by_index_rcu - get index of upper l3 master
    140 *					       device
    141 *	@net: network namespace for device index lookup
    142 *	@ifindex: targeted interface
    143 */
    144int l3mdev_master_upper_ifindex_by_index_rcu(struct net *net, int ifindex)
    145{
    146	struct net_device *dev;
    147
    148	dev = dev_get_by_index_rcu(net, ifindex);
    149	while (dev && !netif_is_l3_master(dev))
    150		dev = netdev_master_upper_dev_get_rcu(dev);
    151
    152	return dev ? dev->ifindex : 0;
    153}
    154EXPORT_SYMBOL_GPL(l3mdev_master_upper_ifindex_by_index_rcu);
    155
    156/**
    157 *	l3mdev_fib_table_rcu - get FIB table id associated with an L3
    158 *                             master interface
    159 *	@dev: targeted interface
    160 */
    161
    162u32 l3mdev_fib_table_rcu(const struct net_device *dev)
    163{
    164	u32 tb_id = 0;
    165
    166	if (!dev)
    167		return 0;
    168
    169	if (netif_is_l3_master(dev)) {
    170		if (dev->l3mdev_ops->l3mdev_fib_table)
    171			tb_id = dev->l3mdev_ops->l3mdev_fib_table(dev);
    172	} else if (netif_is_l3_slave(dev)) {
    173		/* Users of netdev_master_upper_dev_get_rcu need non-const,
    174		 * but current inet_*type functions take a const
    175		 */
    176		struct net_device *_dev = (struct net_device *) dev;
    177		const struct net_device *master;
    178
    179		master = netdev_master_upper_dev_get_rcu(_dev);
    180		if (master &&
    181		    master->l3mdev_ops->l3mdev_fib_table)
    182			tb_id = master->l3mdev_ops->l3mdev_fib_table(master);
    183	}
    184
    185	return tb_id;
    186}
    187EXPORT_SYMBOL_GPL(l3mdev_fib_table_rcu);
    188
    189u32 l3mdev_fib_table_by_index(struct net *net, int ifindex)
    190{
    191	struct net_device *dev;
    192	u32 tb_id = 0;
    193
    194	if (!ifindex)
    195		return 0;
    196
    197	rcu_read_lock();
    198
    199	dev = dev_get_by_index_rcu(net, ifindex);
    200	if (dev)
    201		tb_id = l3mdev_fib_table_rcu(dev);
    202
    203	rcu_read_unlock();
    204
    205	return tb_id;
    206}
    207EXPORT_SYMBOL_GPL(l3mdev_fib_table_by_index);
    208
    209/**
    210 *	l3mdev_link_scope_lookup - IPv6 route lookup based on flow for link
    211 *			     local and multicast addresses
    212 *	@net: network namespace for device index lookup
    213 *	@fl6: IPv6 flow struct for lookup
    214 *	This function does not hold refcnt on the returned dst.
    215 *	Caller must hold rcu_read_lock().
    216 */
    217
    218struct dst_entry *l3mdev_link_scope_lookup(struct net *net,
    219					   struct flowi6 *fl6)
    220{
    221	struct dst_entry *dst = NULL;
    222	struct net_device *dev;
    223
    224	WARN_ON_ONCE(!rcu_read_lock_held());
    225	if (fl6->flowi6_oif) {
    226		dev = dev_get_by_index_rcu(net, fl6->flowi6_oif);
    227		if (dev && netif_is_l3_slave(dev))
    228			dev = netdev_master_upper_dev_get_rcu(dev);
    229
    230		if (dev && netif_is_l3_master(dev) &&
    231		    dev->l3mdev_ops->l3mdev_link_scope_lookup)
    232			dst = dev->l3mdev_ops->l3mdev_link_scope_lookup(dev, fl6);
    233	}
    234
    235	return dst;
    236}
    237EXPORT_SYMBOL_GPL(l3mdev_link_scope_lookup);
    238
    239/**
    240 *	l3mdev_fib_rule_match - Determine if flowi references an
    241 *				L3 master device
    242 *	@net: network namespace for device index lookup
    243 *	@fl:  flow struct
    244 *	@arg: store the table the rule matched with here
    245 */
    246
    247int l3mdev_fib_rule_match(struct net *net, struct flowi *fl,
    248			  struct fib_lookup_arg *arg)
    249{
    250	struct net_device *dev;
    251	int rc = 0;
    252
    253	/* update flow ensures flowi_l3mdev is set when relevant */
    254	if (!fl->flowi_l3mdev)
    255		return 0;
    256
    257	rcu_read_lock();
    258
    259	dev = dev_get_by_index_rcu(net, fl->flowi_l3mdev);
    260	if (dev && netif_is_l3_master(dev) &&
    261	    dev->l3mdev_ops->l3mdev_fib_table) {
    262		arg->table = dev->l3mdev_ops->l3mdev_fib_table(dev);
    263		rc = 1;
    264	}
    265
    266	rcu_read_unlock();
    267
    268	return rc;
    269}
    270
    271void l3mdev_update_flow(struct net *net, struct flowi *fl)
    272{
    273	struct net_device *dev;
    274
    275	rcu_read_lock();
    276
    277	if (fl->flowi_oif) {
    278		dev = dev_get_by_index_rcu(net, fl->flowi_oif);
    279		if (dev) {
    280			if (!fl->flowi_l3mdev)
    281				fl->flowi_l3mdev = l3mdev_master_ifindex_rcu(dev);
    282
    283			/* oif set to L3mdev directs lookup to its table;
    284			 * reset to avoid oif match in fib_lookup
    285			 */
    286			if (netif_is_l3_master(dev))
    287				fl->flowi_oif = 0;
    288			goto out;
    289		}
    290	}
    291
    292	if (fl->flowi_iif > LOOPBACK_IFINDEX && !fl->flowi_l3mdev) {
    293		dev = dev_get_by_index_rcu(net, fl->flowi_iif);
    294		if (dev)
    295			fl->flowi_l3mdev = l3mdev_master_ifindex_rcu(dev);
    296	}
    297
    298out:
    299	rcu_read_unlock();
    300}
    301EXPORT_SYMBOL_GPL(l3mdev_update_flow);