Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
efa5356b RP |
2 | /* |
3 | * Bridge per vlan tunnel port dst_metadata handling code | |
4 | * | |
5 | * Authors: | |
6 | * Roopa Prabhu <roopa@cumulusnetworks.com> | |
efa5356b RP |
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 | ||
19 | static 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 | ||
28 | static 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, | |
efa5356b RP |
33 | .obj_cmpfn = br_vlan_tunid_cmp, |
34 | .automatic_shrinking = true, | |
35 | }; | |
36 | ||
11538d03 | 37 | static struct net_bridge_vlan *br_vlan_tunnel_lookup(struct rhashtable *tbl, |
f5fcca89 | 38 | __be64 tunnel_id) |
11538d03 RP |
39 | { |
40 | return rhashtable_lookup_fast(tbl, &tunnel_id, | |
41 | br_vlan_tunnel_rht_params); | |
42 | } | |
43 | ||
58e20717 NA |
44 | static void vlan_tunnel_info_release(struct net_bridge_vlan *vlan) |
45 | { | |
46 | struct metadata_dst *tdst = rtnl_dereference(vlan->tinfo.tunnel_dst); | |
47 | ||
48 | WRITE_ONCE(vlan->tinfo.tunnel_id, 0); | |
49 | RCU_INIT_POINTER(vlan->tinfo.tunnel_dst, NULL); | |
50 | dst_release(&tdst->dst); | |
51 | } | |
52 | ||
efa5356b RP |
53 | void vlan_tunnel_info_del(struct net_bridge_vlan_group *vg, |
54 | struct net_bridge_vlan *vlan) | |
55 | { | |
58e20717 | 56 | if (!rcu_access_pointer(vlan->tinfo.tunnel_dst)) |
efa5356b RP |
57 | return; |
58 | rhashtable_remove_fast(&vg->tunnel_hash, &vlan->tnode, | |
59 | br_vlan_tunnel_rht_params); | |
58e20717 | 60 | vlan_tunnel_info_release(vlan); |
efa5356b RP |
61 | } |
62 | ||
63 | static int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg, | |
64 | struct net_bridge_vlan *vlan, u32 tun_id) | |
65 | { | |
58e20717 | 66 | struct metadata_dst *metadata = rtnl_dereference(vlan->tinfo.tunnel_dst); |
efa5356b RP |
67 | __be64 key = key32_to_tunnel_id(cpu_to_be32(tun_id)); |
68 | int err; | |
69 | ||
58e20717 | 70 | if (metadata) |
efa5356b RP |
71 | return -EEXIST; |
72 | ||
73 | metadata = __ip_tun_set_dst(0, 0, 0, 0, 0, TUNNEL_KEY, | |
74 | key, 0); | |
75 | if (!metadata) | |
76 | return -EINVAL; | |
77 | ||
78 | metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_BRIDGE; | |
58e20717 NA |
79 | rcu_assign_pointer(vlan->tinfo.tunnel_dst, metadata); |
80 | WRITE_ONCE(vlan->tinfo.tunnel_id, key); | |
efa5356b RP |
81 | |
82 | err = rhashtable_lookup_insert_fast(&vg->tunnel_hash, &vlan->tnode, | |
83 | br_vlan_tunnel_rht_params); | |
84 | if (err) | |
85 | goto out; | |
86 | ||
87 | return 0; | |
88 | out: | |
58e20717 | 89 | vlan_tunnel_info_release(vlan); |
efa5356b RP |
90 | |
91 | return err; | |
92 | } | |
93 | ||
94 | /* Must be protected by RTNL. | |
95 | * Must be called with vid in range from 1 to 4094 inclusive. | |
96 | */ | |
53e96632 NA |
97 | int nbp_vlan_tunnel_info_add(const struct net_bridge_port *port, u16 vid, |
98 | u32 tun_id) | |
efa5356b RP |
99 | { |
100 | struct net_bridge_vlan_group *vg; | |
101 | struct net_bridge_vlan *vlan; | |
102 | ||
103 | ASSERT_RTNL(); | |
104 | ||
105 | vg = nbp_vlan_group(port); | |
106 | vlan = br_vlan_find(vg, vid); | |
107 | if (!vlan) | |
108 | return -EINVAL; | |
109 | ||
110 | return __vlan_tunnel_info_add(vg, vlan, tun_id); | |
111 | } | |
112 | ||
113 | /* Must be protected by RTNL. | |
114 | * Must be called with vid in range from 1 to 4094 inclusive. | |
115 | */ | |
53e96632 | 116 | int nbp_vlan_tunnel_info_delete(const struct net_bridge_port *port, u16 vid) |
efa5356b RP |
117 | { |
118 | struct net_bridge_vlan_group *vg; | |
119 | struct net_bridge_vlan *v; | |
120 | ||
121 | ASSERT_RTNL(); | |
122 | ||
123 | vg = nbp_vlan_group(port); | |
124 | v = br_vlan_find(vg, vid); | |
125 | if (!v) | |
126 | return -ENOENT; | |
127 | ||
128 | vlan_tunnel_info_del(vg, v); | |
129 | ||
130 | return 0; | |
131 | } | |
132 | ||
133 | static void __vlan_tunnel_info_flush(struct net_bridge_vlan_group *vg) | |
134 | { | |
135 | struct net_bridge_vlan *vlan, *tmp; | |
136 | ||
137 | list_for_each_entry_safe(vlan, tmp, &vg->vlan_list, vlist) | |
138 | vlan_tunnel_info_del(vg, vlan); | |
139 | } | |
140 | ||
141 | void nbp_vlan_tunnel_info_flush(struct net_bridge_port *port) | |
142 | { | |
143 | struct net_bridge_vlan_group *vg; | |
144 | ||
145 | ASSERT_RTNL(); | |
146 | ||
147 | vg = nbp_vlan_group(port); | |
148 | __vlan_tunnel_info_flush(vg); | |
149 | } | |
150 | ||
151 | int vlan_tunnel_init(struct net_bridge_vlan_group *vg) | |
152 | { | |
153 | return rhashtable_init(&vg->tunnel_hash, &br_vlan_tunnel_rht_params); | |
154 | } | |
155 | ||
156 | void vlan_tunnel_deinit(struct net_bridge_vlan_group *vg) | |
157 | { | |
158 | rhashtable_destroy(&vg->tunnel_hash); | |
159 | } | |
11538d03 | 160 | |
a37c5c26 KP |
161 | void br_handle_ingress_vlan_tunnel(struct sk_buff *skb, |
162 | struct net_bridge_port *p, | |
163 | struct net_bridge_vlan_group *vg) | |
11538d03 RP |
164 | { |
165 | struct ip_tunnel_info *tinfo = skb_tunnel_info(skb); | |
166 | struct net_bridge_vlan *vlan; | |
167 | ||
168 | if (!vg || !tinfo) | |
a37c5c26 | 169 | return; |
11538d03 RP |
170 | |
171 | /* if already tagged, ignore */ | |
172 | if (skb_vlan_tagged(skb)) | |
a37c5c26 | 173 | return; |
11538d03 RP |
174 | |
175 | /* lookup vid, given tunnel id */ | |
176 | vlan = br_vlan_tunnel_lookup(&vg->tunnel_hash, tinfo->key.tun_id); | |
177 | if (!vlan) | |
a37c5c26 | 178 | return; |
11538d03 RP |
179 | |
180 | skb_dst_drop(skb); | |
181 | ||
182 | __vlan_hwaccel_put_tag(skb, p->br->vlan_proto, vlan->vid); | |
11538d03 RP |
183 | } |
184 | ||
185 | int br_handle_egress_vlan_tunnel(struct sk_buff *skb, | |
186 | struct net_bridge_vlan *vlan) | |
187 | { | |
58e20717 NA |
188 | struct metadata_dst *tunnel_dst; |
189 | __be64 tunnel_id; | |
11538d03 RP |
190 | int err; |
191 | ||
58e20717 | 192 | if (!vlan) |
11538d03 RP |
193 | return 0; |
194 | ||
58e20717 NA |
195 | tunnel_id = READ_ONCE(vlan->tinfo.tunnel_id); |
196 | if (!tunnel_id || unlikely(!skb_vlan_tag_present(skb))) | |
11538d03 RP |
197 | return 0; |
198 | ||
199 | skb_dst_drop(skb); | |
200 | err = skb_vlan_pop(skb); | |
201 | if (err) | |
202 | return err; | |
203 | ||
58e20717 | 204 | tunnel_dst = rcu_dereference(vlan->tinfo.tunnel_dst); |
cfc579f9 NA |
205 | if (tunnel_dst && dst_hold_safe(&tunnel_dst->dst)) |
206 | skb_dst_set(skb, &tunnel_dst->dst); | |
11538d03 RP |
207 | |
208 | return 0; | |
209 | } |