blob: b3fd29d20a3c498b3ee630723a0c55cd529d7eae [file] [log] [blame]
Roopa Prabhuefa53562017-01-31 22:59:54 -08001/*
2 * Bridge per vlan tunnel port dst_metadata handling code
3 *
4 * Authors:
5 * Roopa Prabhu <roopa@cumulusnetworks.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 */
12
13#include <linux/kernel.h>
14#include <linux/netdevice.h>
15#include <linux/rtnetlink.h>
16#include <linux/slab.h>
17#include <net/switchdev.h>
18#include <net/dst_metadata.h>
19
20#include "br_private.h"
21#include "br_private_tunnel.h"
22
23static inline int br_vlan_tunid_cmp(struct rhashtable_compare_arg *arg,
24 const void *ptr)
25{
26 const struct net_bridge_vlan *vle = ptr;
27 __be64 tunid = *(__be64 *)arg->key;
28
29 return vle->tinfo.tunnel_id != tunid;
30}
31
32static const struct rhashtable_params br_vlan_tunnel_rht_params = {
33 .head_offset = offsetof(struct net_bridge_vlan, tnode),
34 .key_offset = offsetof(struct net_bridge_vlan, tinfo.tunnel_id),
35 .key_len = sizeof(__be64),
36 .nelem_hint = 3,
37 .locks_mul = 1,
38 .obj_cmpfn = br_vlan_tunid_cmp,
39 .automatic_shrinking = true,
40};
41
42void vlan_tunnel_info_del(struct net_bridge_vlan_group *vg,
43 struct net_bridge_vlan *vlan)
44{
45 if (!vlan->tinfo.tunnel_dst)
46 return;
47 rhashtable_remove_fast(&vg->tunnel_hash, &vlan->tnode,
48 br_vlan_tunnel_rht_params);
49 vlan->tinfo.tunnel_id = 0;
50 dst_release(&vlan->tinfo.tunnel_dst->dst);
51 vlan->tinfo.tunnel_dst = NULL;
52}
53
54static int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg,
55 struct net_bridge_vlan *vlan, u32 tun_id)
56{
57 struct metadata_dst *metadata = NULL;
58 __be64 key = key32_to_tunnel_id(cpu_to_be32(tun_id));
59 int err;
60
61 if (vlan->tinfo.tunnel_dst)
62 return -EEXIST;
63
64 metadata = __ip_tun_set_dst(0, 0, 0, 0, 0, TUNNEL_KEY,
65 key, 0);
66 if (!metadata)
67 return -EINVAL;
68
69 metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_BRIDGE;
70 vlan->tinfo.tunnel_dst = metadata;
71 vlan->tinfo.tunnel_id = key;
72
73 err = rhashtable_lookup_insert_fast(&vg->tunnel_hash, &vlan->tnode,
74 br_vlan_tunnel_rht_params);
75 if (err)
76 goto out;
77
78 return 0;
79out:
80 dst_release(&vlan->tinfo.tunnel_dst->dst);
81
82 return err;
83}
84
85/* Must be protected by RTNL.
86 * Must be called with vid in range from 1 to 4094 inclusive.
87 */
88int nbp_vlan_tunnel_info_add(struct net_bridge_port *port, u16 vid, u32 tun_id)
89{
90 struct net_bridge_vlan_group *vg;
91 struct net_bridge_vlan *vlan;
92
93 ASSERT_RTNL();
94
95 vg = nbp_vlan_group(port);
96 vlan = br_vlan_find(vg, vid);
97 if (!vlan)
98 return -EINVAL;
99
100 return __vlan_tunnel_info_add(vg, vlan, tun_id);
101}
102
103/* Must be protected by RTNL.
104 * Must be called with vid in range from 1 to 4094 inclusive.
105 */
106int nbp_vlan_tunnel_info_delete(struct net_bridge_port *port, u16 vid)
107{
108 struct net_bridge_vlan_group *vg;
109 struct net_bridge_vlan *v;
110
111 ASSERT_RTNL();
112
113 vg = nbp_vlan_group(port);
114 v = br_vlan_find(vg, vid);
115 if (!v)
116 return -ENOENT;
117
118 vlan_tunnel_info_del(vg, v);
119
120 return 0;
121}
122
123static void __vlan_tunnel_info_flush(struct net_bridge_vlan_group *vg)
124{
125 struct net_bridge_vlan *vlan, *tmp;
126
127 list_for_each_entry_safe(vlan, tmp, &vg->vlan_list, vlist)
128 vlan_tunnel_info_del(vg, vlan);
129}
130
131void nbp_vlan_tunnel_info_flush(struct net_bridge_port *port)
132{
133 struct net_bridge_vlan_group *vg;
134
135 ASSERT_RTNL();
136
137 vg = nbp_vlan_group(port);
138 __vlan_tunnel_info_flush(vg);
139}
140
141int vlan_tunnel_init(struct net_bridge_vlan_group *vg)
142{
143 return rhashtable_init(&vg->tunnel_hash, &br_vlan_tunnel_rht_params);
144}
145
146void vlan_tunnel_deinit(struct net_bridge_vlan_group *vg)
147{
148 rhashtable_destroy(&vg->tunnel_hash);
149}