linux/net/bridge/br_vlan_tunnel.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *      Bridge per vlan tunnel port dst_metadata handling code
   4 *
   5 *      Authors:
   6 *      Roopa Prabhu            <roopa@cumulusnetworks.com>
   7 */
   8
   9#include <linux/kernel.h>
  10#include <linux/netdevice.h>
  11#include <linux/rtnetlink.h>
  12#include <linux/slab.h>
  13#include <net/switchdev.h>
  14#include <net/dst_metadata.h>
  15
  16#include "br_private.h"
  17#include "br_private_tunnel.h"
  18
  19static inline int br_vlan_tunid_cmp(struct rhashtable_compare_arg *arg,
  20                                    const void *ptr)
  21{
  22        const struct net_bridge_vlan *vle = ptr;
  23        __be64 tunid = *(__be64 *)arg->key;
  24
  25        return vle->tinfo.tunnel_id != tunid;
  26}
  27
  28static const struct rhashtable_params br_vlan_tunnel_rht_params = {
  29        .head_offset = offsetof(struct net_bridge_vlan, tnode),
  30        .key_offset = offsetof(struct net_bridge_vlan, tinfo.tunnel_id),
  31        .key_len = sizeof(__be64),
  32        .nelem_hint = 3,
  33        .obj_cmpfn = br_vlan_tunid_cmp,
  34        .automatic_shrinking = true,
  35};
  36
  37static struct net_bridge_vlan *br_vlan_tunnel_lookup(struct rhashtable *tbl,
  38                                                     u64 tunnel_id)
  39{
  40        return rhashtable_lookup_fast(tbl, &tunnel_id,
  41                                      br_vlan_tunnel_rht_params);
  42}
  43
  44void vlan_tunnel_info_del(struct net_bridge_vlan_group *vg,
  45                          struct net_bridge_vlan *vlan)
  46{
  47        if (!vlan->tinfo.tunnel_dst)
  48                return;
  49        rhashtable_remove_fast(&vg->tunnel_hash, &vlan->tnode,
  50                               br_vlan_tunnel_rht_params);
  51        vlan->tinfo.tunnel_id = 0;
  52        dst_release(&vlan->tinfo.tunnel_dst->dst);
  53        vlan->tinfo.tunnel_dst = NULL;
  54}
  55
  56static int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg,
  57                                  struct net_bridge_vlan *vlan, u32 tun_id)
  58{
  59        struct metadata_dst *metadata = NULL;
  60        __be64 key = key32_to_tunnel_id(cpu_to_be32(tun_id));
  61        int err;
  62
  63        if (vlan->tinfo.tunnel_dst)
  64                return -EEXIST;
  65
  66        metadata = __ip_tun_set_dst(0, 0, 0, 0, 0, TUNNEL_KEY,
  67                                    key, 0);
  68        if (!metadata)
  69                return -EINVAL;
  70
  71        metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_BRIDGE;
  72        vlan->tinfo.tunnel_dst = metadata;
  73        vlan->tinfo.tunnel_id = key;
  74
  75        err = rhashtable_lookup_insert_fast(&vg->tunnel_hash, &vlan->tnode,
  76                                            br_vlan_tunnel_rht_params);
  77        if (err)
  78                goto out;
  79
  80        return 0;
  81out:
  82        dst_release(&vlan->tinfo.tunnel_dst->dst);
  83        vlan->tinfo.tunnel_dst = NULL;
  84        vlan->tinfo.tunnel_id = 0;
  85
  86        return err;
  87}
  88
  89/* Must be protected by RTNL.
  90 * Must be called with vid in range from 1 to 4094 inclusive.
  91 */
  92int nbp_vlan_tunnel_info_add(struct net_bridge_port *port, u16 vid, u32 tun_id)
  93{
  94        struct net_bridge_vlan_group *vg;
  95        struct net_bridge_vlan *vlan;
  96
  97        ASSERT_RTNL();
  98
  99        vg = nbp_vlan_group(port);
 100        vlan = br_vlan_find(vg, vid);
 101        if (!vlan)
 102                return -EINVAL;
 103
 104        return __vlan_tunnel_info_add(vg, vlan, tun_id);
 105}
 106
 107/* Must be protected by RTNL.
 108 * Must be called with vid in range from 1 to 4094 inclusive.
 109 */
 110int nbp_vlan_tunnel_info_delete(struct net_bridge_port *port, u16 vid)
 111{
 112        struct net_bridge_vlan_group *vg;
 113        struct net_bridge_vlan *v;
 114
 115        ASSERT_RTNL();
 116
 117        vg = nbp_vlan_group(port);
 118        v = br_vlan_find(vg, vid);
 119        if (!v)
 120                return -ENOENT;
 121
 122        vlan_tunnel_info_del(vg, v);
 123
 124        return 0;
 125}
 126
 127static void __vlan_tunnel_info_flush(struct net_bridge_vlan_group *vg)
 128{
 129        struct net_bridge_vlan *vlan, *tmp;
 130
 131        list_for_each_entry_safe(vlan, tmp, &vg->vlan_list, vlist)
 132                vlan_tunnel_info_del(vg, vlan);
 133}
 134
 135void nbp_vlan_tunnel_info_flush(struct net_bridge_port *port)
 136{
 137        struct net_bridge_vlan_group *vg;
 138
 139        ASSERT_RTNL();
 140
 141        vg = nbp_vlan_group(port);
 142        __vlan_tunnel_info_flush(vg);
 143}
 144
 145int vlan_tunnel_init(struct net_bridge_vlan_group *vg)
 146{
 147        return rhashtable_init(&vg->tunnel_hash, &br_vlan_tunnel_rht_params);
 148}
 149
 150void vlan_tunnel_deinit(struct net_bridge_vlan_group *vg)
 151{
 152        rhashtable_destroy(&vg->tunnel_hash);
 153}
 154
 155int br_handle_ingress_vlan_tunnel(struct sk_buff *skb,
 156                                  struct net_bridge_port *p,
 157                                  struct net_bridge_vlan_group *vg)
 158{
 159        struct ip_tunnel_info *tinfo = skb_tunnel_info(skb);
 160        struct net_bridge_vlan *vlan;
 161
 162        if (!vg || !tinfo)
 163                return 0;
 164
 165        /* if already tagged, ignore */
 166        if (skb_vlan_tagged(skb))
 167                return 0;
 168
 169        /* lookup vid, given tunnel id */
 170        vlan = br_vlan_tunnel_lookup(&vg->tunnel_hash, tinfo->key.tun_id);
 171        if (!vlan)
 172                return 0;
 173
 174        skb_dst_drop(skb);
 175
 176        __vlan_hwaccel_put_tag(skb, p->br->vlan_proto, vlan->vid);
 177
 178        return 0;
 179}
 180
 181int br_handle_egress_vlan_tunnel(struct sk_buff *skb,
 182                                 struct net_bridge_vlan *vlan)
 183{
 184        int err;
 185
 186        if (!vlan || !vlan->tinfo.tunnel_id)
 187                return 0;
 188
 189        if (unlikely(!skb_vlan_tag_present(skb)))
 190                return 0;
 191
 192        skb_dst_drop(skb);
 193        err = skb_vlan_pop(skb);
 194        if (err)
 195                return err;
 196
 197        skb_dst_set(skb, dst_clone(&vlan->tinfo.tunnel_dst->dst));
 198
 199        return 0;
 200}
 201