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(const struct net_bridge_port *port, u16 vid,
  93                             u32 tun_id)
  94{
  95        struct net_bridge_vlan_group *vg;
  96        struct net_bridge_vlan *vlan;
  97
  98        ASSERT_RTNL();
  99
 100        vg = nbp_vlan_group(port);
 101        vlan = br_vlan_find(vg, vid);
 102        if (!vlan)
 103                return -EINVAL;
 104
 105        return __vlan_tunnel_info_add(vg, vlan, tun_id);
 106}
 107
 108/* Must be protected by RTNL.
 109 * Must be called with vid in range from 1 to 4094 inclusive.
 110 */
 111int nbp_vlan_tunnel_info_delete(const struct net_bridge_port *port, u16 vid)
 112{
 113        struct net_bridge_vlan_group *vg;
 114        struct net_bridge_vlan *v;
 115
 116        ASSERT_RTNL();
 117
 118        vg = nbp_vlan_group(port);
 119        v = br_vlan_find(vg, vid);
 120        if (!v)
 121                return -ENOENT;
 122
 123        vlan_tunnel_info_del(vg, v);
 124
 125        return 0;
 126}
 127
 128static void __vlan_tunnel_info_flush(struct net_bridge_vlan_group *vg)
 129{
 130        struct net_bridge_vlan *vlan, *tmp;
 131
 132        list_for_each_entry_safe(vlan, tmp, &vg->vlan_list, vlist)
 133                vlan_tunnel_info_del(vg, vlan);
 134}
 135
 136void nbp_vlan_tunnel_info_flush(struct net_bridge_port *port)
 137{
 138        struct net_bridge_vlan_group *vg;
 139
 140        ASSERT_RTNL();
 141
 142        vg = nbp_vlan_group(port);
 143        __vlan_tunnel_info_flush(vg);
 144}
 145
 146int vlan_tunnel_init(struct net_bridge_vlan_group *vg)
 147{
 148        return rhashtable_init(&vg->tunnel_hash, &br_vlan_tunnel_rht_params);
 149}
 150
 151void vlan_tunnel_deinit(struct net_bridge_vlan_group *vg)
 152{
 153        rhashtable_destroy(&vg->tunnel_hash);
 154}
 155
 156int br_handle_ingress_vlan_tunnel(struct sk_buff *skb,
 157                                  struct net_bridge_port *p,
 158                                  struct net_bridge_vlan_group *vg)
 159{
 160        struct ip_tunnel_info *tinfo = skb_tunnel_info(skb);
 161        struct net_bridge_vlan *vlan;
 162
 163        if (!vg || !tinfo)
 164                return 0;
 165
 166        /* if already tagged, ignore */
 167        if (skb_vlan_tagged(skb))
 168                return 0;
 169
 170        /* lookup vid, given tunnel id */
 171        vlan = br_vlan_tunnel_lookup(&vg->tunnel_hash, tinfo->key.tun_id);
 172        if (!vlan)
 173                return 0;
 174
 175        skb_dst_drop(skb);
 176
 177        __vlan_hwaccel_put_tag(skb, p->br->vlan_proto, vlan->vid);
 178
 179        return 0;
 180}
 181
 182int br_handle_egress_vlan_tunnel(struct sk_buff *skb,
 183                                 struct net_bridge_vlan *vlan)
 184{
 185        int err;
 186
 187        if (!vlan || !vlan->tinfo.tunnel_id)
 188                return 0;
 189
 190        if (unlikely(!skb_vlan_tag_present(skb)))
 191                return 0;
 192
 193        skb_dst_drop(skb);
 194        err = skb_vlan_pop(skb);
 195        if (err)
 196                return err;
 197
 198        skb_dst_set(skb, dst_clone(&vlan->tinfo.tunnel_dst->dst));
 199
 200        return 0;
 201}
 202