]>
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 RP |
37 | static 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 | ||
efa5356b RP |
44 | void 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 | ||
56 | static 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; | |
81 | out: | |
82 | dst_release(&vlan->tinfo.tunnel_dst->dst); | |
afcb50ba RP |
83 | vlan->tinfo.tunnel_dst = NULL; |
84 | vlan->tinfo.tunnel_id = 0; | |
efa5356b RP |
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 | */ | |
92 | int 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 | */ | |
110 | int 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 | ||
127 | static 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 | ||
135 | void 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 | ||
145 | int vlan_tunnel_init(struct net_bridge_vlan_group *vg) | |
146 | { | |
147 | return rhashtable_init(&vg->tunnel_hash, &br_vlan_tunnel_rht_params); | |
148 | } | |
149 | ||
150 | void vlan_tunnel_deinit(struct net_bridge_vlan_group *vg) | |
151 | { | |
152 | rhashtable_destroy(&vg->tunnel_hash); | |
153 | } | |
11538d03 RP |
154 | |
155 | int 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 | ||
181 | int 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 | } |