]>
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 | */ | |
53e96632 NA |
92 | int nbp_vlan_tunnel_info_add(const struct net_bridge_port *port, u16 vid, |
93 | u32 tun_id) | |
efa5356b RP |
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 | */ | |
53e96632 | 111 | int nbp_vlan_tunnel_info_delete(const struct net_bridge_port *port, u16 vid) |
efa5356b RP |
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 | ||
128 | static 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 | ||
136 | void 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 | ||
146 | int vlan_tunnel_init(struct net_bridge_vlan_group *vg) | |
147 | { | |
148 | return rhashtable_init(&vg->tunnel_hash, &br_vlan_tunnel_rht_params); | |
149 | } | |
150 | ||
151 | void vlan_tunnel_deinit(struct net_bridge_vlan_group *vg) | |
152 | { | |
153 | rhashtable_destroy(&vg->tunnel_hash); | |
154 | } | |
11538d03 RP |
155 | |
156 | int 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 | ||
182 | int 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 | } |