Commit | Line | Data |
---|---|---|
efa5356b RP |
1 | /* |
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 | ||
23 | static 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 | ||
32 | static 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 | ||
11538d03 RP |
42 | static struct net_bridge_vlan *br_vlan_tunnel_lookup(struct rhashtable *tbl, |
43 | u64 tunnel_id) | |
44 | { | |
45 | return rhashtable_lookup_fast(tbl, &tunnel_id, | |
46 | br_vlan_tunnel_rht_params); | |
47 | } | |
48 | ||
efa5356b RP |
49 | void vlan_tunnel_info_del(struct net_bridge_vlan_group *vg, |
50 | struct net_bridge_vlan *vlan) | |
51 | { | |
52 | if (!vlan->tinfo.tunnel_dst) | |
53 | return; | |
54 | rhashtable_remove_fast(&vg->tunnel_hash, &vlan->tnode, | |
55 | br_vlan_tunnel_rht_params); | |
56 | vlan->tinfo.tunnel_id = 0; | |
57 | dst_release(&vlan->tinfo.tunnel_dst->dst); | |
58 | vlan->tinfo.tunnel_dst = NULL; | |
59 | } | |
60 | ||
61 | static int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg, | |
62 | struct net_bridge_vlan *vlan, u32 tun_id) | |
63 | { | |
64 | struct metadata_dst *metadata = NULL; | |
65 | __be64 key = key32_to_tunnel_id(cpu_to_be32(tun_id)); | |
66 | int err; | |
67 | ||
68 | if (vlan->tinfo.tunnel_dst) | |
69 | return -EEXIST; | |
70 | ||
71 | metadata = __ip_tun_set_dst(0, 0, 0, 0, 0, TUNNEL_KEY, | |
72 | key, 0); | |
73 | if (!metadata) | |
74 | return -EINVAL; | |
75 | ||
76 | metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_BRIDGE; | |
77 | vlan->tinfo.tunnel_dst = metadata; | |
78 | vlan->tinfo.tunnel_id = key; | |
79 | ||
80 | err = rhashtable_lookup_insert_fast(&vg->tunnel_hash, &vlan->tnode, | |
81 | br_vlan_tunnel_rht_params); | |
82 | if (err) | |
83 | goto out; | |
84 | ||
85 | return 0; | |
86 | out: | |
87 | dst_release(&vlan->tinfo.tunnel_dst->dst); | |
afcb50ba RP |
88 | vlan->tinfo.tunnel_dst = NULL; |
89 | vlan->tinfo.tunnel_id = 0; | |
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 | */ | |
97 | int nbp_vlan_tunnel_info_add(struct net_bridge_port *port, u16 vid, u32 tun_id) | |
98 | { | |
99 | struct net_bridge_vlan_group *vg; | |
100 | struct net_bridge_vlan *vlan; | |
101 | ||
102 | ASSERT_RTNL(); | |
103 | ||
104 | vg = nbp_vlan_group(port); | |
105 | vlan = br_vlan_find(vg, vid); | |
106 | if (!vlan) | |
107 | return -EINVAL; | |
108 | ||
109 | return __vlan_tunnel_info_add(vg, vlan, tun_id); | |
110 | } | |
111 | ||
112 | /* Must be protected by RTNL. | |
113 | * Must be called with vid in range from 1 to 4094 inclusive. | |
114 | */ | |
115 | int nbp_vlan_tunnel_info_delete(struct net_bridge_port *port, u16 vid) | |
116 | { | |
117 | struct net_bridge_vlan_group *vg; | |
118 | struct net_bridge_vlan *v; | |
119 | ||
120 | ASSERT_RTNL(); | |
121 | ||
122 | vg = nbp_vlan_group(port); | |
123 | v = br_vlan_find(vg, vid); | |
124 | if (!v) | |
125 | return -ENOENT; | |
126 | ||
127 | vlan_tunnel_info_del(vg, v); | |
128 | ||
129 | return 0; | |
130 | } | |
131 | ||
132 | static void __vlan_tunnel_info_flush(struct net_bridge_vlan_group *vg) | |
133 | { | |
134 | struct net_bridge_vlan *vlan, *tmp; | |
135 | ||
136 | list_for_each_entry_safe(vlan, tmp, &vg->vlan_list, vlist) | |
137 | vlan_tunnel_info_del(vg, vlan); | |
138 | } | |
139 | ||
140 | void nbp_vlan_tunnel_info_flush(struct net_bridge_port *port) | |
141 | { | |
142 | struct net_bridge_vlan_group *vg; | |
143 | ||
144 | ASSERT_RTNL(); | |
145 | ||
146 | vg = nbp_vlan_group(port); | |
147 | __vlan_tunnel_info_flush(vg); | |
148 | } | |
149 | ||
150 | int vlan_tunnel_init(struct net_bridge_vlan_group *vg) | |
151 | { | |
152 | return rhashtable_init(&vg->tunnel_hash, &br_vlan_tunnel_rht_params); | |
153 | } | |
154 | ||
155 | void vlan_tunnel_deinit(struct net_bridge_vlan_group *vg) | |
156 | { | |
157 | rhashtable_destroy(&vg->tunnel_hash); | |
158 | } | |
11538d03 RP |
159 | |
160 | int br_handle_ingress_vlan_tunnel(struct sk_buff *skb, | |
161 | struct net_bridge_port *p, | |
162 | struct net_bridge_vlan_group *vg) | |
163 | { | |
164 | struct ip_tunnel_info *tinfo = skb_tunnel_info(skb); | |
165 | struct net_bridge_vlan *vlan; | |
166 | ||
167 | if (!vg || !tinfo) | |
168 | return 0; | |
169 | ||
170 | /* if already tagged, ignore */ | |
171 | if (skb_vlan_tagged(skb)) | |
172 | return 0; | |
173 | ||
174 | /* lookup vid, given tunnel id */ | |
175 | vlan = br_vlan_tunnel_lookup(&vg->tunnel_hash, tinfo->key.tun_id); | |
176 | if (!vlan) | |
177 | return 0; | |
178 | ||
179 | skb_dst_drop(skb); | |
180 | ||
181 | __vlan_hwaccel_put_tag(skb, p->br->vlan_proto, vlan->vid); | |
182 | ||
183 | return 0; | |
184 | } | |
185 | ||
186 | int br_handle_egress_vlan_tunnel(struct sk_buff *skb, | |
187 | struct net_bridge_vlan *vlan) | |
188 | { | |
189 | int err; | |
190 | ||
191 | if (!vlan || !vlan->tinfo.tunnel_id) | |
192 | return 0; | |
193 | ||
194 | if (unlikely(!skb_vlan_tag_present(skb))) | |
195 | return 0; | |
196 | ||
197 | skb_dst_drop(skb); | |
198 | err = skb_vlan_pop(skb); | |
199 | if (err) | |
200 | return err; | |
201 | ||
202 | skb_dst_set(skb, dst_clone(&vlan->tinfo.tunnel_dst->dst)); | |
203 | ||
204 | return 0; | |
205 | } |