Commit | Line | Data |
---|---|---|
f9bbe447 VO |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2019, Vladimir Oltean <olteanv@gmail.com> | |
3 | * | |
4 | * This module is not a complete tagger implementation. It only provides | |
5 | * primitives for taggers that rely on 802.1Q VLAN tags to use. The | |
6 | * dsa_8021q_netdev_ops is registered for API compliance and not used | |
7 | * directly by callers. | |
8 | */ | |
9 | #include <linux/if_bridge.h> | |
10 | #include <linux/if_vlan.h> | |
11 | ||
12 | #include "dsa_priv.h" | |
13 | ||
0471dd42 VO |
14 | /* Binary structure of the fake 12-bit VID field (when the TPID is |
15 | * ETH_P_DSA_8021Q): | |
16 | * | |
17 | * | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | | |
18 | * +-----------+-----+-----------------+-----------+-----------------------+ | |
19 | * | DIR | RSV | SWITCH_ID | RSV | PORT | | |
20 | * +-----------+-----+-----------------+-----------+-----------------------+ | |
21 | * | |
22 | * DIR - VID[11:10]: | |
23 | * Direction flags. | |
24 | * * 1 (0b01) for RX VLAN, | |
25 | * * 2 (0b10) for TX VLAN. | |
26 | * These values make the special VIDs of 0, 1 and 4095 to be left | |
27 | * unused by this coding scheme. | |
28 | * | |
29 | * RSV - VID[9]: | |
30 | * To be used for further expansion of SWITCH_ID or for other purposes. | |
bcccb0a5 | 31 | * Must be transmitted as zero and ignored on receive. |
0471dd42 VO |
32 | * |
33 | * SWITCH_ID - VID[8:6]: | |
34 | * Index of switch within DSA tree. Must be between 0 and | |
35 | * DSA_MAX_SWITCHES - 1. | |
36 | * | |
37 | * RSV - VID[5:4]: | |
38 | * To be used for further expansion of PORT or for other purposes. | |
bcccb0a5 | 39 | * Must be transmitted as zero and ignored on receive. |
0471dd42 VO |
40 | * |
41 | * PORT - VID[3:0]: | |
42 | * Index of switch port. Must be between 0 and DSA_MAX_PORTS - 1. | |
f9bbe447 | 43 | */ |
0471dd42 VO |
44 | |
45 | #define DSA_8021Q_DIR_SHIFT 10 | |
46 | #define DSA_8021Q_DIR_MASK GENMASK(11, 10) | |
47 | #define DSA_8021Q_DIR(x) (((x) << DSA_8021Q_DIR_SHIFT) & \ | |
48 | DSA_8021Q_DIR_MASK) | |
49 | #define DSA_8021Q_DIR_RX DSA_8021Q_DIR(1) | |
50 | #define DSA_8021Q_DIR_TX DSA_8021Q_DIR(2) | |
51 | ||
52 | #define DSA_8021Q_SWITCH_ID_SHIFT 6 | |
53 | #define DSA_8021Q_SWITCH_ID_MASK GENMASK(8, 6) | |
54 | #define DSA_8021Q_SWITCH_ID(x) (((x) << DSA_8021Q_SWITCH_ID_SHIFT) & \ | |
55 | DSA_8021Q_SWITCH_ID_MASK) | |
56 | ||
57 | #define DSA_8021Q_PORT_SHIFT 0 | |
58 | #define DSA_8021Q_PORT_MASK GENMASK(3, 0) | |
59 | #define DSA_8021Q_PORT(x) (((x) << DSA_8021Q_PORT_SHIFT) & \ | |
60 | DSA_8021Q_PORT_MASK) | |
f9bbe447 VO |
61 | |
62 | /* Returns the VID to be inserted into the frame from xmit for switch steering | |
63 | * instructions on egress. Encodes switch ID and port ID. | |
64 | */ | |
65 | u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port) | |
66 | { | |
0471dd42 VO |
67 | return DSA_8021Q_DIR_TX | DSA_8021Q_SWITCH_ID(ds->index) | |
68 | DSA_8021Q_PORT(port); | |
f9bbe447 VO |
69 | } |
70 | EXPORT_SYMBOL_GPL(dsa_8021q_tx_vid); | |
71 | ||
72 | /* Returns the VID that will be installed as pvid for this switch port, sent as | |
73 | * tagged egress towards the CPU port and decoded by the rcv function. | |
74 | */ | |
75 | u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port) | |
76 | { | |
0471dd42 VO |
77 | return DSA_8021Q_DIR_RX | DSA_8021Q_SWITCH_ID(ds->index) | |
78 | DSA_8021Q_PORT(port); | |
f9bbe447 VO |
79 | } |
80 | EXPORT_SYMBOL_GPL(dsa_8021q_rx_vid); | |
81 | ||
82 | /* Returns the decoded switch ID from the RX VID. */ | |
83 | int dsa_8021q_rx_switch_id(u16 vid) | |
84 | { | |
0471dd42 | 85 | return (vid & DSA_8021Q_SWITCH_ID_MASK) >> DSA_8021Q_SWITCH_ID_SHIFT; |
f9bbe447 VO |
86 | } |
87 | EXPORT_SYMBOL_GPL(dsa_8021q_rx_switch_id); | |
88 | ||
89 | /* Returns the decoded port ID from the RX VID. */ | |
90 | int dsa_8021q_rx_source_port(u16 vid) | |
91 | { | |
0471dd42 | 92 | return (vid & DSA_8021Q_PORT_MASK) >> DSA_8021Q_PORT_SHIFT; |
f9bbe447 VO |
93 | } |
94 | EXPORT_SYMBOL_GPL(dsa_8021q_rx_source_port); | |
95 | ||
5f33183b VO |
96 | static int dsa_8021q_restore_pvid(struct dsa_switch *ds, int port) |
97 | { | |
98 | struct bridge_vlan_info vinfo; | |
99 | struct net_device *slave; | |
100 | u16 pvid; | |
101 | int err; | |
102 | ||
103 | if (!dsa_is_user_port(ds, port)) | |
104 | return 0; | |
105 | ||
68bb8ea8 | 106 | slave = dsa_to_port(ds, port)->slave; |
5f33183b VO |
107 | |
108 | err = br_vlan_get_pvid(slave, &pvid); | |
109 | if (err < 0) | |
110 | /* There is no pvid on the bridge for this port, which is | |
111 | * perfectly valid. Nothing to restore, bye-bye! | |
112 | */ | |
113 | return 0; | |
114 | ||
115 | err = br_vlan_get_info(slave, pvid, &vinfo); | |
116 | if (err < 0) { | |
117 | dev_err(ds->dev, "Couldn't determine PVID attributes\n"); | |
118 | return err; | |
119 | } | |
120 | ||
68bb8ea8 | 121 | return dsa_port_vid_add(dsa_to_port(ds, port), pvid, vinfo.flags); |
5f33183b VO |
122 | } |
123 | ||
124 | /* If @enabled is true, installs @vid with @flags into the switch port's HW | |
125 | * filter. | |
126 | * If @enabled is false, deletes @vid (ignores @flags) from the port. Had the | |
127 | * user explicitly configured this @vid through the bridge core, then the @vid | |
128 | * is installed again, but this time with the flags from the bridge layer. | |
129 | */ | |
130 | static int dsa_8021q_vid_apply(struct dsa_switch *ds, int port, u16 vid, | |
131 | u16 flags, bool enabled) | |
132 | { | |
68bb8ea8 | 133 | struct dsa_port *dp = dsa_to_port(ds, port); |
5f33183b VO |
134 | struct bridge_vlan_info vinfo; |
135 | int err; | |
136 | ||
137 | if (enabled) | |
138 | return dsa_port_vid_add(dp, vid, flags); | |
139 | ||
140 | err = dsa_port_vid_del(dp, vid); | |
141 | if (err < 0) | |
142 | return err; | |
143 | ||
144 | /* Nothing to restore from the bridge for a non-user port. | |
145 | * The CPU port VLANs are restored implicitly with the user ports, | |
146 | * similar to how the bridge does in dsa_slave_vlan_add and | |
147 | * dsa_slave_vlan_del. | |
148 | */ | |
149 | if (!dsa_is_user_port(ds, port)) | |
150 | return 0; | |
151 | ||
152 | err = br_vlan_get_info(dp->slave, vid, &vinfo); | |
153 | /* Couldn't determine bridge attributes for this vid, | |
154 | * it means the bridge had not configured it. | |
155 | */ | |
156 | if (err < 0) | |
157 | return 0; | |
158 | ||
159 | /* Restore the VID from the bridge */ | |
160 | err = dsa_port_vid_add(dp, vid, vinfo.flags); | |
161 | if (err < 0) | |
162 | return err; | |
163 | ||
164 | vinfo.flags &= ~BRIDGE_VLAN_INFO_PVID; | |
165 | ||
166 | return dsa_port_vid_add(dp->cpu_dp, vid, vinfo.flags); | |
167 | } | |
168 | ||
f9bbe447 VO |
169 | /* RX VLAN tagging (left) and TX VLAN tagging (right) setup shown for a single |
170 | * front-panel switch port (here swp0). | |
171 | * | |
172 | * Port identification through VLAN (802.1Q) tags has different requirements | |
173 | * for it to work effectively: | |
174 | * - On RX (ingress from network): each front-panel port must have a pvid | |
175 | * that uniquely identifies it, and the egress of this pvid must be tagged | |
176 | * towards the CPU port, so that software can recover the source port based | |
177 | * on the VID in the frame. But this would only work for standalone ports; | |
178 | * if bridged, this VLAN setup would break autonomous forwarding and would | |
179 | * force all switched traffic to pass through the CPU. So we must also make | |
180 | * the other front-panel ports members of this VID we're adding, albeit | |
181 | * we're not making it their PVID (they'll still have their own). | |
182 | * By the way - just because we're installing the same VID in multiple | |
183 | * switch ports doesn't mean that they'll start to talk to one another, even | |
184 | * while not bridged: the final forwarding decision is still an AND between | |
185 | * the L2 forwarding information (which is limiting forwarding in this case) | |
186 | * and the VLAN-based restrictions (of which there are none in this case, | |
187 | * since all ports are members). | |
188 | * - On TX (ingress from CPU and towards network) we are faced with a problem. | |
189 | * If we were to tag traffic (from within DSA) with the port's pvid, all | |
190 | * would be well, assuming the switch ports were standalone. Frames would | |
191 | * have no choice but to be directed towards the correct front-panel port. | |
192 | * But because we also want the RX VLAN to not break bridging, then | |
193 | * inevitably that means that we have to give them a choice (of what | |
194 | * front-panel port to go out on), and therefore we cannot steer traffic | |
195 | * based on the RX VID. So what we do is simply install one more VID on the | |
196 | * front-panel and CPU ports, and profit off of the fact that steering will | |
197 | * work just by virtue of the fact that there is only one other port that's | |
198 | * a member of the VID we're tagging the traffic with - the desired one. | |
199 | * | |
200 | * So at the end, each front-panel port will have one RX VID (also the PVID), | |
201 | * the RX VID of all other front-panel ports, and one TX VID. Whereas the CPU | |
202 | * port will have the RX and TX VIDs of all front-panel ports, and on top of | |
203 | * that, is also tagged-input and tagged-output (VLAN trunk). | |
204 | * | |
205 | * CPU port CPU port | |
206 | * +-------------+-----+-------------+ +-------------+-----+-------------+ | |
207 | * | RX VID | | | | TX VID | | | | |
208 | * | of swp0 | | | | of swp0 | | | | |
209 | * | +-----+ | | +-----+ | | |
210 | * | ^ T | | | Tagged | | |
211 | * | | | | | ingress | | |
212 | * | +-------+---+---+-------+ | | +-----------+ | | |
213 | * | | | | | | | | Untagged | | |
214 | * | | U v U v U v | | v egress | | |
215 | * | +-----+ +-----+ +-----+ +-----+ | | +-----+ +-----+ +-----+ +-----+ | | |
216 | * | | | | | | | | | | | | | | | | | | | | | |
217 | * | |PVID | | | | | | | | | | | | | | | | | | | |
218 | * +-+-----+-+-----+-+-----+-+-----+-+ +-+-----+-+-----+-+-----+-+-----+-+ | |
219 | * swp0 swp1 swp2 swp3 swp0 swp1 swp2 swp3 | |
220 | */ | |
221 | int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int port, bool enabled) | |
222 | { | |
223 | int upstream = dsa_upstream_port(ds, port); | |
f9bbe447 VO |
224 | u16 rx_vid = dsa_8021q_rx_vid(ds, port); |
225 | u16 tx_vid = dsa_8021q_tx_vid(ds, port); | |
226 | int i, err; | |
227 | ||
228 | /* The CPU port is implicitly configured by | |
229 | * configuring the front-panel ports | |
230 | */ | |
231 | if (!dsa_is_user_port(ds, port)) | |
232 | return 0; | |
233 | ||
234 | /* Add this user port's RX VID to the membership list of all others | |
235 | * (including itself). This is so that bridging will not be hindered. | |
236 | * L2 forwarding rules still take precedence when there are no VLAN | |
237 | * restrictions, so there are no concerns about leaking traffic. | |
238 | */ | |
239 | for (i = 0; i < ds->num_ports; i++) { | |
f9bbe447 VO |
240 | u16 flags; |
241 | ||
242 | if (i == upstream) | |
d34d2baa | 243 | continue; |
f9bbe447 VO |
244 | else if (i == port) |
245 | /* The RX VID is pvid on this port */ | |
246 | flags = BRIDGE_VLAN_INFO_UNTAGGED | | |
247 | BRIDGE_VLAN_INFO_PVID; | |
248 | else | |
249 | /* The RX VID is a regular VLAN on all others */ | |
250 | flags = BRIDGE_VLAN_INFO_UNTAGGED; | |
251 | ||
5f33183b | 252 | err = dsa_8021q_vid_apply(ds, i, rx_vid, flags, enabled); |
f9bbe447 VO |
253 | if (err) { |
254 | dev_err(ds->dev, "Failed to apply RX VID %d to port %d: %d\n", | |
255 | rx_vid, port, err); | |
256 | return err; | |
257 | } | |
258 | } | |
d34d2baa IC |
259 | |
260 | /* CPU port needs to see this port's RX VID | |
261 | * as tagged egress. | |
262 | */ | |
5f33183b | 263 | err = dsa_8021q_vid_apply(ds, upstream, rx_vid, 0, enabled); |
d34d2baa IC |
264 | if (err) { |
265 | dev_err(ds->dev, "Failed to apply RX VID %d to port %d: %d\n", | |
266 | rx_vid, port, err); | |
267 | return err; | |
268 | } | |
269 | ||
f9bbe447 | 270 | /* Finally apply the TX VID on this port and on the CPU port */ |
5f33183b VO |
271 | err = dsa_8021q_vid_apply(ds, port, tx_vid, BRIDGE_VLAN_INFO_UNTAGGED, |
272 | enabled); | |
f9bbe447 VO |
273 | if (err) { |
274 | dev_err(ds->dev, "Failed to apply TX VID %d on port %d: %d\n", | |
275 | tx_vid, port, err); | |
276 | return err; | |
277 | } | |
5f33183b | 278 | err = dsa_8021q_vid_apply(ds, upstream, tx_vid, 0, enabled); |
f9bbe447 VO |
279 | if (err) { |
280 | dev_err(ds->dev, "Failed to apply TX VID %d on port %d: %d\n", | |
281 | tx_vid, upstream, err); | |
282 | return err; | |
283 | } | |
284 | ||
5f33183b VO |
285 | if (!enabled) |
286 | err = dsa_8021q_restore_pvid(ds, port); | |
287 | ||
288 | return err; | |
f9bbe447 VO |
289 | } |
290 | EXPORT_SYMBOL_GPL(dsa_port_setup_8021q_tagging); | |
291 | ||
292 | struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev, | |
293 | u16 tpid, u16 tci) | |
294 | { | |
295 | /* skb->data points at skb_mac_header, which | |
296 | * is fine for vlan_insert_tag. | |
297 | */ | |
298 | return vlan_insert_tag(skb, htons(tpid), tci); | |
299 | } | |
300 | EXPORT_SYMBOL_GPL(dsa_8021q_xmit); | |
301 | ||
d4619336 VO |
302 | /* In the DSA packet_type handler, skb->data points in the middle of the VLAN |
303 | * tag, after tpid and before tci. This is because so far, ETH_HLEN | |
304 | * (DMAC, SMAC, EtherType) bytes were pulled. | |
305 | * There are 2 bytes of VLAN tag left in skb->data, and upper | |
306 | * layers expect the 'real' EtherType to be consumed as well. | |
307 | * Coincidentally, a VLAN header is also of the same size as | |
308 | * the number of bytes that need to be pulled. | |
309 | * | |
310 | * skb_mac_header skb->data | |
311 | * | | | |
312 | * v v | |
313 | * | | | | | | | | | | | | | | | | | | | | |
314 | * +-----------------------+-----------------------+-------+-------+-------+ | |
315 | * | Destination MAC | Source MAC | TPID | TCI | EType | | |
316 | * +-----------------------+-----------------------+-------+-------+-------+ | |
317 | * ^ | | | |
318 | * |<--VLAN_HLEN-->to <---VLAN_HLEN---> | |
319 | * from | | |
320 | * >>>>>>> v | |
321 | * >>>>>>> | | | | | | | | | | | | | | | | |
322 | * >>>>>>> +-----------------------+-----------------------+-------+ | |
323 | * >>>>>>> | Destination MAC | Source MAC | EType | | |
324 | * +-----------------------+-----------------------+-------+ | |
325 | * ^ ^ | |
326 | * (now part of | | | |
327 | * skb->head) skb_mac_header skb->data | |
328 | */ | |
329 | struct sk_buff *dsa_8021q_remove_header(struct sk_buff *skb) | |
f9bbe447 | 330 | { |
d4619336 VO |
331 | u8 *from = skb_mac_header(skb); |
332 | u8 *dest = from + VLAN_HLEN; | |
f9bbe447 | 333 | |
d4619336 VO |
334 | memmove(dest, from, ETH_HLEN - VLAN_HLEN); |
335 | skb_pull(skb, VLAN_HLEN); | |
336 | skb_push(skb, ETH_HLEN); | |
337 | skb_reset_mac_header(skb); | |
338 | skb_reset_mac_len(skb); | |
339 | skb_pull_rcsum(skb, ETH_HLEN); | |
f9bbe447 VO |
340 | |
341 | return skb; | |
342 | } | |
d4619336 | 343 | EXPORT_SYMBOL_GPL(dsa_8021q_remove_header); |
f9bbe447 VO |
344 | |
345 | static const struct dsa_device_ops dsa_8021q_netdev_ops = { | |
346 | .name = "8021q", | |
347 | .proto = DSA_TAG_PROTO_8021Q, | |
348 | .overhead = VLAN_HLEN, | |
349 | }; | |
350 | ||
351 | MODULE_LICENSE("GPL v2"); | |
352 | MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_8021Q); | |
353 | ||
354 | module_dsa_tag_driver(dsa_8021q_netdev_ops); |