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