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_netlink_tunnel.c (8091B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *	Bridge per vlan tunnel port dst_metadata netlink control interface
      4 *
      5 *	Authors:
      6 *	Roopa Prabhu		<roopa@cumulusnetworks.com>
      7 */
      8
      9#include <linux/kernel.h>
     10#include <linux/slab.h>
     11#include <linux/etherdevice.h>
     12#include <net/rtnetlink.h>
     13#include <net/net_namespace.h>
     14#include <net/sock.h>
     15#include <uapi/linux/if_bridge.h>
     16#include <net/dst_metadata.h>
     17
     18#include "br_private.h"
     19#include "br_private_tunnel.h"
     20
     21static size_t __get_vlan_tinfo_size(void)
     22{
     23	return nla_total_size(0) + /* nest IFLA_BRIDGE_VLAN_TUNNEL_INFO */
     24		  nla_total_size(sizeof(u32)) + /* IFLA_BRIDGE_VLAN_TUNNEL_ID */
     25		  nla_total_size(sizeof(u16)) + /* IFLA_BRIDGE_VLAN_TUNNEL_VID */
     26		  nla_total_size(sizeof(u16)); /* IFLA_BRIDGE_VLAN_TUNNEL_FLAGS */
     27}
     28
     29bool vlan_tunid_inrange(const struct net_bridge_vlan *v_curr,
     30			const struct net_bridge_vlan *v_last)
     31{
     32	__be32 tunid_curr = tunnel_id_to_key32(v_curr->tinfo.tunnel_id);
     33	__be32 tunid_last = tunnel_id_to_key32(v_last->tinfo.tunnel_id);
     34
     35	return (be32_to_cpu(tunid_curr) - be32_to_cpu(tunid_last)) == 1;
     36}
     37
     38static int __get_num_vlan_tunnel_infos(struct net_bridge_vlan_group *vg)
     39{
     40	struct net_bridge_vlan *v, *vtbegin = NULL, *vtend = NULL;
     41	int num_tinfos = 0;
     42
     43	/* Count number of vlan infos */
     44	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
     45		/* only a context, bridge vlan not activated */
     46		if (!br_vlan_should_use(v) || !v->tinfo.tunnel_id)
     47			continue;
     48
     49		if (!vtbegin) {
     50			goto initvars;
     51		} else if ((v->vid - vtend->vid) == 1 &&
     52			   vlan_tunid_inrange(v, vtend)) {
     53			vtend = v;
     54			continue;
     55		} else {
     56			if ((vtend->vid - vtbegin->vid) > 0)
     57				num_tinfos += 2;
     58			else
     59				num_tinfos += 1;
     60		}
     61initvars:
     62		vtbegin = v;
     63		vtend = v;
     64	}
     65
     66	if (vtbegin && vtend) {
     67		if ((vtend->vid - vtbegin->vid) > 0)
     68			num_tinfos += 2;
     69		else
     70			num_tinfos += 1;
     71	}
     72
     73	return num_tinfos;
     74}
     75
     76int br_get_vlan_tunnel_info_size(struct net_bridge_vlan_group *vg)
     77{
     78	int num_tinfos;
     79
     80	if (!vg)
     81		return 0;
     82
     83	rcu_read_lock();
     84	num_tinfos = __get_num_vlan_tunnel_infos(vg);
     85	rcu_read_unlock();
     86
     87	return num_tinfos * __get_vlan_tinfo_size();
     88}
     89
     90static int br_fill_vlan_tinfo(struct sk_buff *skb, u16 vid,
     91			      __be64 tunnel_id, u16 flags)
     92{
     93	__be32 tid = tunnel_id_to_key32(tunnel_id);
     94	struct nlattr *tmap;
     95
     96	tmap = nla_nest_start_noflag(skb, IFLA_BRIDGE_VLAN_TUNNEL_INFO);
     97	if (!tmap)
     98		return -EMSGSIZE;
     99	if (nla_put_u32(skb, IFLA_BRIDGE_VLAN_TUNNEL_ID,
    100			be32_to_cpu(tid)))
    101		goto nla_put_failure;
    102	if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_VID,
    103			vid))
    104		goto nla_put_failure;
    105	if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_FLAGS,
    106			flags))
    107		goto nla_put_failure;
    108	nla_nest_end(skb, tmap);
    109
    110	return 0;
    111
    112nla_put_failure:
    113	nla_nest_cancel(skb, tmap);
    114
    115	return -EMSGSIZE;
    116}
    117
    118static int br_fill_vlan_tinfo_range(struct sk_buff *skb,
    119				    struct net_bridge_vlan *vtbegin,
    120				    struct net_bridge_vlan *vtend)
    121{
    122	int err;
    123
    124	if (vtend && (vtend->vid - vtbegin->vid) > 0) {
    125		/* add range to skb */
    126		err = br_fill_vlan_tinfo(skb, vtbegin->vid,
    127					 vtbegin->tinfo.tunnel_id,
    128					 BRIDGE_VLAN_INFO_RANGE_BEGIN);
    129		if (err)
    130			return err;
    131
    132		err = br_fill_vlan_tinfo(skb, vtend->vid,
    133					 vtend->tinfo.tunnel_id,
    134					 BRIDGE_VLAN_INFO_RANGE_END);
    135		if (err)
    136			return err;
    137	} else {
    138		err = br_fill_vlan_tinfo(skb, vtbegin->vid,
    139					 vtbegin->tinfo.tunnel_id,
    140					 0);
    141		if (err)
    142			return err;
    143	}
    144
    145	return 0;
    146}
    147
    148int br_fill_vlan_tunnel_info(struct sk_buff *skb,
    149			     struct net_bridge_vlan_group *vg)
    150{
    151	struct net_bridge_vlan *vtbegin = NULL;
    152	struct net_bridge_vlan *vtend = NULL;
    153	struct net_bridge_vlan *v;
    154	int err;
    155
    156	/* Count number of vlan infos */
    157	list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
    158		/* only a context, bridge vlan not activated */
    159		if (!br_vlan_should_use(v))
    160			continue;
    161
    162		if (!v->tinfo.tunnel_dst)
    163			continue;
    164
    165		if (!vtbegin) {
    166			goto initvars;
    167		} else if ((v->vid - vtend->vid) == 1 &&
    168			    vlan_tunid_inrange(v, vtend)) {
    169			vtend = v;
    170			continue;
    171		} else {
    172			err = br_fill_vlan_tinfo_range(skb, vtbegin, vtend);
    173			if (err)
    174				return err;
    175		}
    176initvars:
    177		vtbegin = v;
    178		vtend = v;
    179	}
    180
    181	if (vtbegin) {
    182		err = br_fill_vlan_tinfo_range(skb, vtbegin, vtend);
    183		if (err)
    184			return err;
    185	}
    186
    187	return 0;
    188}
    189
    190static const struct nla_policy vlan_tunnel_policy[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1] = {
    191	[IFLA_BRIDGE_VLAN_TUNNEL_ID] = { .type = NLA_U32 },
    192	[IFLA_BRIDGE_VLAN_TUNNEL_VID] = { .type = NLA_U16 },
    193	[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS] = { .type = NLA_U16 },
    194};
    195
    196int br_vlan_tunnel_info(const struct net_bridge_port *p, int cmd,
    197			u16 vid, u32 tun_id, bool *changed)
    198{
    199	int err = 0;
    200
    201	if (!p)
    202		return -EINVAL;
    203
    204	switch (cmd) {
    205	case RTM_SETLINK:
    206		err = nbp_vlan_tunnel_info_add(p, vid, tun_id);
    207		if (!err)
    208			*changed = true;
    209		break;
    210	case RTM_DELLINK:
    211		if (!nbp_vlan_tunnel_info_delete(p, vid))
    212			*changed = true;
    213		break;
    214	}
    215
    216	return err;
    217}
    218
    219int br_parse_vlan_tunnel_info(struct nlattr *attr,
    220			      struct vtunnel_info *tinfo)
    221{
    222	struct nlattr *tb[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1];
    223	u32 tun_id;
    224	u16 vid, flags = 0;
    225	int err;
    226
    227	memset(tinfo, 0, sizeof(*tinfo));
    228
    229	err = nla_parse_nested_deprecated(tb, IFLA_BRIDGE_VLAN_TUNNEL_MAX,
    230					  attr, vlan_tunnel_policy, NULL);
    231	if (err < 0)
    232		return err;
    233
    234	if (!tb[IFLA_BRIDGE_VLAN_TUNNEL_ID] ||
    235	    !tb[IFLA_BRIDGE_VLAN_TUNNEL_VID])
    236		return -EINVAL;
    237
    238	tun_id = nla_get_u32(tb[IFLA_BRIDGE_VLAN_TUNNEL_ID]);
    239	vid = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_VID]);
    240	if (vid >= VLAN_VID_MASK)
    241		return -ERANGE;
    242
    243	if (tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS])
    244		flags = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]);
    245
    246	tinfo->tunid = tun_id;
    247	tinfo->vid = vid;
    248	tinfo->flags = flags;
    249
    250	return 0;
    251}
    252
    253/* send a notification if v_curr can't enter the range and start a new one */
    254static void __vlan_tunnel_handle_range(const struct net_bridge_port *p,
    255				       struct net_bridge_vlan **v_start,
    256				       struct net_bridge_vlan **v_end,
    257				       int v_curr, bool curr_change)
    258{
    259	struct net_bridge_vlan_group *vg;
    260	struct net_bridge_vlan *v;
    261
    262	vg = nbp_vlan_group(p);
    263	if (!vg)
    264		return;
    265
    266	v = br_vlan_find(vg, v_curr);
    267
    268	if (!*v_start)
    269		goto out_init;
    270
    271	if (v && curr_change && br_vlan_can_enter_range(v, *v_end)) {
    272		*v_end = v;
    273		return;
    274	}
    275
    276	br_vlan_notify(p->br, p, (*v_start)->vid, (*v_end)->vid, RTM_NEWVLAN);
    277out_init:
    278	/* we start a range only if there are any changes to notify about */
    279	*v_start = curr_change ? v : NULL;
    280	*v_end = *v_start;
    281}
    282
    283int br_process_vlan_tunnel_info(const struct net_bridge *br,
    284				const struct net_bridge_port *p, int cmd,
    285				struct vtunnel_info *tinfo_curr,
    286				struct vtunnel_info *tinfo_last,
    287				bool *changed)
    288{
    289	int err;
    290
    291	if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
    292		if (tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
    293			return -EINVAL;
    294		memcpy(tinfo_last, tinfo_curr, sizeof(struct vtunnel_info));
    295	} else if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_END) {
    296		struct net_bridge_vlan *v_start = NULL, *v_end = NULL;
    297		int t, v;
    298
    299		if (!(tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN))
    300			return -EINVAL;
    301		if ((tinfo_curr->vid - tinfo_last->vid) !=
    302		    (tinfo_curr->tunid - tinfo_last->tunid))
    303			return -EINVAL;
    304		t = tinfo_last->tunid;
    305		for (v = tinfo_last->vid; v <= tinfo_curr->vid; v++) {
    306			bool curr_change = false;
    307
    308			err = br_vlan_tunnel_info(p, cmd, v, t, &curr_change);
    309			if (err)
    310				break;
    311			t++;
    312
    313			if (curr_change)
    314				*changed = curr_change;
    315			 __vlan_tunnel_handle_range(p, &v_start, &v_end, v,
    316						    curr_change);
    317		}
    318		if (v_start && v_end)
    319			br_vlan_notify(br, p, v_start->vid, v_end->vid,
    320				       RTM_NEWVLAN);
    321		if (err)
    322			return err;
    323
    324		memset(tinfo_last, 0, sizeof(struct vtunnel_info));
    325		memset(tinfo_curr, 0, sizeof(struct vtunnel_info));
    326	} else {
    327		if (tinfo_last->flags)
    328			return -EINVAL;
    329		err = br_vlan_tunnel_info(p, cmd, tinfo_curr->vid,
    330					  tinfo_curr->tunid, changed);
    331		if (err)
    332			return err;
    333		br_vlan_notify(br, p, tinfo_curr->vid, 0, RTM_NEWVLAN);
    334		memset(tinfo_last, 0, sizeof(struct vtunnel_info));
    335		memset(tinfo_curr, 0, sizeof(struct vtunnel_info));
    336	}
    337
    338	return 0;
    339}