diff options
-rw-r--r-- | include/linux/dsa/8021q.h | 76 | ||||
-rw-r--r-- | include/net/dsa.h | 2 | ||||
-rw-r--r-- | net/dsa/Kconfig | 11 | ||||
-rw-r--r-- | net/dsa/Makefile | 1 | ||||
-rw-r--r-- | net/dsa/tag_8021q.c | 222 |
5 files changed, 312 insertions, 0 deletions
diff --git a/include/linux/dsa/8021q.h b/include/linux/dsa/8021q.h new file mode 100644 index 000000000000..3911e0586478 --- /dev/null +++ b/include/linux/dsa/8021q.h | |||
@@ -0,0 +1,76 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 | ||
2 | * Copyright (c) 2019, Vladimir Oltean <olteanv@gmail.com> | ||
3 | */ | ||
4 | |||
5 | #ifndef _NET_DSA_8021Q_H | ||
6 | #define _NET_DSA_8021Q_H | ||
7 | |||
8 | #include <linux/types.h> | ||
9 | |||
10 | struct dsa_switch; | ||
11 | struct sk_buff; | ||
12 | struct net_device; | ||
13 | struct packet_type; | ||
14 | |||
15 | #if IS_ENABLED(CONFIG_NET_DSA_TAG_8021Q) | ||
16 | |||
17 | int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int index, | ||
18 | bool enabled); | ||
19 | |||
20 | struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev, | ||
21 | u16 tpid, u16 tci); | ||
22 | |||
23 | struct sk_buff *dsa_8021q_rcv(struct sk_buff *skb, struct net_device *netdev, | ||
24 | struct packet_type *pt, u16 *tpid, u16 *tci); | ||
25 | |||
26 | u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port); | ||
27 | |||
28 | u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port); | ||
29 | |||
30 | int dsa_8021q_rx_switch_id(u16 vid); | ||
31 | |||
32 | int dsa_8021q_rx_source_port(u16 vid); | ||
33 | |||
34 | #else | ||
35 | |||
36 | int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int index, | ||
37 | bool enabled) | ||
38 | { | ||
39 | return 0; | ||
40 | } | ||
41 | |||
42 | struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev, | ||
43 | u16 tpid, u16 tci) | ||
44 | { | ||
45 | return NULL; | ||
46 | } | ||
47 | |||
48 | struct sk_buff *dsa_8021q_rcv(struct sk_buff *skb, struct net_device *netdev, | ||
49 | struct packet_type *pt, u16 *tpid, u16 *tci) | ||
50 | { | ||
51 | return NULL; | ||
52 | } | ||
53 | |||
54 | u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port) | ||
55 | { | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port) | ||
60 | { | ||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | int dsa_8021q_rx_switch_id(u16 vid) | ||
65 | { | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | int dsa_8021q_rx_source_port(u16 vid) | ||
70 | { | ||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | #endif /* IS_ENABLED(CONFIG_NET_DSA_TAG_8021Q) */ | ||
75 | |||
76 | #endif /* _NET_DSA_8021Q_H */ | ||
diff --git a/include/net/dsa.h b/include/net/dsa.h index 18db7b8e7a8e..69f3714f42ba 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h | |||
@@ -42,6 +42,7 @@ struct phylink_link_state; | |||
42 | #define DSA_TAG_PROTO_MTK_VALUE 9 | 42 | #define DSA_TAG_PROTO_MTK_VALUE 9 |
43 | #define DSA_TAG_PROTO_QCA_VALUE 10 | 43 | #define DSA_TAG_PROTO_QCA_VALUE 10 |
44 | #define DSA_TAG_PROTO_TRAILER_VALUE 11 | 44 | #define DSA_TAG_PROTO_TRAILER_VALUE 11 |
45 | #define DSA_TAG_PROTO_8021Q_VALUE 12 | ||
45 | 46 | ||
46 | enum dsa_tag_protocol { | 47 | enum dsa_tag_protocol { |
47 | DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, | 48 | DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, |
@@ -56,6 +57,7 @@ enum dsa_tag_protocol { | |||
56 | DSA_TAG_PROTO_MTK = DSA_TAG_PROTO_MTK_VALUE, | 57 | DSA_TAG_PROTO_MTK = DSA_TAG_PROTO_MTK_VALUE, |
57 | DSA_TAG_PROTO_QCA = DSA_TAG_PROTO_QCA_VALUE, | 58 | DSA_TAG_PROTO_QCA = DSA_TAG_PROTO_QCA_VALUE, |
58 | DSA_TAG_PROTO_TRAILER = DSA_TAG_PROTO_TRAILER_VALUE, | 59 | DSA_TAG_PROTO_TRAILER = DSA_TAG_PROTO_TRAILER_VALUE, |
60 | DSA_TAG_PROTO_8021Q = DSA_TAG_PROTO_8021Q_VALUE, | ||
59 | }; | 61 | }; |
60 | 62 | ||
61 | struct packet_type; | 63 | struct packet_type; |
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index c0734028c7dc..fc15a7e1a6df 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig | |||
@@ -17,6 +17,17 @@ menuconfig NET_DSA | |||
17 | 17 | ||
18 | if NET_DSA | 18 | if NET_DSA |
19 | 19 | ||
20 | # tagging formats | ||
21 | config NET_DSA_TAG_8021Q | ||
22 | tristate "Tag driver for switches using custom 802.1Q VLAN headers" | ||
23 | select VLAN_8021Q | ||
24 | help | ||
25 | Unlike the other tagging protocols, the 802.1Q config option simply | ||
26 | provides helpers for other tagging implementations that might rely on | ||
27 | VLAN in one way or another. It is not a complete solution. | ||
28 | |||
29 | Drivers which use these helpers should select this as dependency. | ||
30 | |||
20 | config NET_DSA_TAG_BRCM_COMMON | 31 | config NET_DSA_TAG_BRCM_COMMON |
21 | tristate | 32 | tristate |
22 | default n | 33 | default n |
diff --git a/net/dsa/Makefile b/net/dsa/Makefile index 8a737b6ee94c..e97c794ec57b 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile | |||
@@ -4,6 +4,7 @@ obj-$(CONFIG_NET_DSA) += dsa_core.o | |||
4 | dsa_core-y += dsa.o dsa2.o master.o port.o slave.o switch.o | 4 | dsa_core-y += dsa.o dsa2.o master.o port.o slave.o switch.o |
5 | 5 | ||
6 | # tagging formats | 6 | # tagging formats |
7 | obj-$(CONFIG_NET_DSA_TAG_8021Q) += tag_8021q.o | ||
7 | obj-$(CONFIG_NET_DSA_TAG_BRCM_COMMON) += tag_brcm.o | 8 | obj-$(CONFIG_NET_DSA_TAG_BRCM_COMMON) += tag_brcm.o |
8 | obj-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o | 9 | obj-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o |
9 | obj-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o | 10 | obj-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o |
diff --git a/net/dsa/tag_8021q.c b/net/dsa/tag_8021q.c new file mode 100644 index 000000000000..8ae48c7e1e76 --- /dev/null +++ b/net/dsa/tag_8021q.c | |||
@@ -0,0 +1,222 @@ | |||
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 | |||
14 | /* Allocating two VLAN tags per port - one for the RX VID and | ||
15 | * the other for the TX VID - see below | ||
16 | */ | ||
17 | #define DSA_8021Q_VID_RANGE (DSA_MAX_SWITCHES * DSA_MAX_PORTS) | ||
18 | #define DSA_8021Q_VID_BASE (VLAN_N_VID - 2 * DSA_8021Q_VID_RANGE - 1) | ||
19 | #define DSA_8021Q_RX_VID_BASE (DSA_8021Q_VID_BASE) | ||
20 | #define DSA_8021Q_TX_VID_BASE (DSA_8021Q_VID_BASE + DSA_8021Q_VID_RANGE) | ||
21 | |||
22 | /* Returns the VID to be inserted into the frame from xmit for switch steering | ||
23 | * instructions on egress. Encodes switch ID and port ID. | ||
24 | */ | ||
25 | u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port) | ||
26 | { | ||
27 | return DSA_8021Q_TX_VID_BASE + (DSA_MAX_PORTS * ds->index) + port; | ||
28 | } | ||
29 | EXPORT_SYMBOL_GPL(dsa_8021q_tx_vid); | ||
30 | |||
31 | /* Returns the VID that will be installed as pvid for this switch port, sent as | ||
32 | * tagged egress towards the CPU port and decoded by the rcv function. | ||
33 | */ | ||
34 | u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port) | ||
35 | { | ||
36 | return DSA_8021Q_RX_VID_BASE + (DSA_MAX_PORTS * ds->index) + port; | ||
37 | } | ||
38 | EXPORT_SYMBOL_GPL(dsa_8021q_rx_vid); | ||
39 | |||
40 | /* Returns the decoded switch ID from the RX VID. */ | ||
41 | int dsa_8021q_rx_switch_id(u16 vid) | ||
42 | { | ||
43 | return ((vid - DSA_8021Q_RX_VID_BASE) / DSA_MAX_PORTS); | ||
44 | } | ||
45 | EXPORT_SYMBOL_GPL(dsa_8021q_rx_switch_id); | ||
46 | |||
47 | /* Returns the decoded port ID from the RX VID. */ | ||
48 | int dsa_8021q_rx_source_port(u16 vid) | ||
49 | { | ||
50 | return ((vid - DSA_8021Q_RX_VID_BASE) % DSA_MAX_PORTS); | ||
51 | } | ||
52 | EXPORT_SYMBOL_GPL(dsa_8021q_rx_source_port); | ||
53 | |||
54 | /* RX VLAN tagging (left) and TX VLAN tagging (right) setup shown for a single | ||
55 | * front-panel switch port (here swp0). | ||
56 | * | ||
57 | * Port identification through VLAN (802.1Q) tags has different requirements | ||
58 | * for it to work effectively: | ||
59 | * - On RX (ingress from network): each front-panel port must have a pvid | ||
60 | * that uniquely identifies it, and the egress of this pvid must be tagged | ||
61 | * towards the CPU port, so that software can recover the source port based | ||
62 | * on the VID in the frame. But this would only work for standalone ports; | ||
63 | * if bridged, this VLAN setup would break autonomous forwarding and would | ||
64 | * force all switched traffic to pass through the CPU. So we must also make | ||
65 | * the other front-panel ports members of this VID we're adding, albeit | ||
66 | * we're not making it their PVID (they'll still have their own). | ||
67 | * By the way - just because we're installing the same VID in multiple | ||
68 | * switch ports doesn't mean that they'll start to talk to one another, even | ||
69 | * while not bridged: the final forwarding decision is still an AND between | ||
70 | * the L2 forwarding information (which is limiting forwarding in this case) | ||
71 | * and the VLAN-based restrictions (of which there are none in this case, | ||
72 | * since all ports are members). | ||
73 | * - On TX (ingress from CPU and towards network) we are faced with a problem. | ||
74 | * If we were to tag traffic (from within DSA) with the port's pvid, all | ||
75 | * would be well, assuming the switch ports were standalone. Frames would | ||
76 | * have no choice but to be directed towards the correct front-panel port. | ||
77 | * But because we also want the RX VLAN to not break bridging, then | ||
78 | * inevitably that means that we have to give them a choice (of what | ||
79 | * front-panel port to go out on), and therefore we cannot steer traffic | ||
80 | * based on the RX VID. So what we do is simply install one more VID on the | ||
81 | * front-panel and CPU ports, and profit off of the fact that steering will | ||
82 | * work just by virtue of the fact that there is only one other port that's | ||
83 | * a member of the VID we're tagging the traffic with - the desired one. | ||
84 | * | ||
85 | * So at the end, each front-panel port will have one RX VID (also the PVID), | ||
86 | * the RX VID of all other front-panel ports, and one TX VID. Whereas the CPU | ||
87 | * port will have the RX and TX VIDs of all front-panel ports, and on top of | ||
88 | * that, is also tagged-input and tagged-output (VLAN trunk). | ||
89 | * | ||
90 | * CPU port CPU port | ||
91 | * +-------------+-----+-------------+ +-------------+-----+-------------+ | ||
92 | * | RX VID | | | | TX VID | | | | ||
93 | * | of swp0 | | | | of swp0 | | | | ||
94 | * | +-----+ | | +-----+ | | ||
95 | * | ^ T | | | Tagged | | ||
96 | * | | | | | ingress | | ||
97 | * | +-------+---+---+-------+ | | +-----------+ | | ||
98 | * | | | | | | | | Untagged | | ||
99 | * | | U v U v U v | | v egress | | ||
100 | * | +-----+ +-----+ +-----+ +-----+ | | +-----+ +-----+ +-----+ +-----+ | | ||
101 | * | | | | | | | | | | | | | | | | | | | | | ||
102 | * | |PVID | | | | | | | | | | | | | | | | | | | ||
103 | * +-+-----+-+-----+-+-----+-+-----+-+ +-+-----+-+-----+-+-----+-+-----+-+ | ||
104 | * swp0 swp1 swp2 swp3 swp0 swp1 swp2 swp3 | ||
105 | */ | ||
106 | int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int port, bool enabled) | ||
107 | { | ||
108 | int upstream = dsa_upstream_port(ds, port); | ||
109 | struct dsa_port *dp = &ds->ports[port]; | ||
110 | struct dsa_port *upstream_dp = &ds->ports[upstream]; | ||
111 | u16 rx_vid = dsa_8021q_rx_vid(ds, port); | ||
112 | u16 tx_vid = dsa_8021q_tx_vid(ds, port); | ||
113 | int i, err; | ||
114 | |||
115 | /* The CPU port is implicitly configured by | ||
116 | * configuring the front-panel ports | ||
117 | */ | ||
118 | if (!dsa_is_user_port(ds, port)) | ||
119 | return 0; | ||
120 | |||
121 | /* Add this user port's RX VID to the membership list of all others | ||
122 | * (including itself). This is so that bridging will not be hindered. | ||
123 | * L2 forwarding rules still take precedence when there are no VLAN | ||
124 | * restrictions, so there are no concerns about leaking traffic. | ||
125 | */ | ||
126 | for (i = 0; i < ds->num_ports; i++) { | ||
127 | struct dsa_port *other_dp = &ds->ports[i]; | ||
128 | u16 flags; | ||
129 | |||
130 | if (i == upstream) | ||
131 | /* CPU port needs to see this port's RX VID | ||
132 | * as tagged egress. | ||
133 | */ | ||
134 | flags = 0; | ||
135 | else if (i == port) | ||
136 | /* The RX VID is pvid on this port */ | ||
137 | flags = BRIDGE_VLAN_INFO_UNTAGGED | | ||
138 | BRIDGE_VLAN_INFO_PVID; | ||
139 | else | ||
140 | /* The RX VID is a regular VLAN on all others */ | ||
141 | flags = BRIDGE_VLAN_INFO_UNTAGGED; | ||
142 | |||
143 | if (enabled) | ||
144 | err = dsa_port_vid_add(other_dp, rx_vid, flags); | ||
145 | else | ||
146 | err = dsa_port_vid_del(other_dp, rx_vid); | ||
147 | if (err) { | ||
148 | dev_err(ds->dev, "Failed to apply RX VID %d to port %d: %d\n", | ||
149 | rx_vid, port, err); | ||
150 | return err; | ||
151 | } | ||
152 | } | ||
153 | /* Finally apply the TX VID on this port and on the CPU port */ | ||
154 | if (enabled) | ||
155 | err = dsa_port_vid_add(dp, tx_vid, BRIDGE_VLAN_INFO_UNTAGGED); | ||
156 | else | ||
157 | err = dsa_port_vid_del(dp, tx_vid); | ||
158 | if (err) { | ||
159 | dev_err(ds->dev, "Failed to apply TX VID %d on port %d: %d\n", | ||
160 | tx_vid, port, err); | ||
161 | return err; | ||
162 | } | ||
163 | if (enabled) | ||
164 | err = dsa_port_vid_add(upstream_dp, tx_vid, 0); | ||
165 | else | ||
166 | err = dsa_port_vid_del(upstream_dp, tx_vid); | ||
167 | if (err) { | ||
168 | dev_err(ds->dev, "Failed to apply TX VID %d on port %d: %d\n", | ||
169 | tx_vid, upstream, err); | ||
170 | return err; | ||
171 | } | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | EXPORT_SYMBOL_GPL(dsa_port_setup_8021q_tagging); | ||
176 | |||
177 | struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev, | ||
178 | u16 tpid, u16 tci) | ||
179 | { | ||
180 | /* skb->data points at skb_mac_header, which | ||
181 | * is fine for vlan_insert_tag. | ||
182 | */ | ||
183 | return vlan_insert_tag(skb, htons(tpid), tci); | ||
184 | } | ||
185 | EXPORT_SYMBOL_GPL(dsa_8021q_xmit); | ||
186 | |||
187 | struct sk_buff *dsa_8021q_rcv(struct sk_buff *skb, struct net_device *netdev, | ||
188 | struct packet_type *pt, u16 *tpid, u16 *tci) | ||
189 | { | ||
190 | struct vlan_ethhdr *tag; | ||
191 | |||
192 | if (unlikely(!pskb_may_pull(skb, VLAN_HLEN))) | ||
193 | return NULL; | ||
194 | |||
195 | tag = vlan_eth_hdr(skb); | ||
196 | *tpid = ntohs(tag->h_vlan_proto); | ||
197 | *tci = ntohs(tag->h_vlan_TCI); | ||
198 | |||
199 | /* skb->data points in the middle of the VLAN tag, | ||
200 | * after tpid and before tci. This is because so far, | ||
201 | * ETH_HLEN (DMAC, SMAC, EtherType) bytes were pulled. | ||
202 | * There are 2 bytes of VLAN tag left in skb->data, and upper | ||
203 | * layers expect the 'real' EtherType to be consumed as well. | ||
204 | * Coincidentally, a VLAN header is also of the same size as | ||
205 | * the number of bytes that need to be pulled. | ||
206 | */ | ||
207 | skb_pull_rcsum(skb, VLAN_HLEN); | ||
208 | |||
209 | return skb; | ||
210 | } | ||
211 | EXPORT_SYMBOL_GPL(dsa_8021q_rcv); | ||
212 | |||
213 | static const struct dsa_device_ops dsa_8021q_netdev_ops = { | ||
214 | .name = "8021q", | ||
215 | .proto = DSA_TAG_PROTO_8021Q, | ||
216 | .overhead = VLAN_HLEN, | ||
217 | }; | ||
218 | |||
219 | MODULE_LICENSE("GPL v2"); | ||
220 | MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_8021Q); | ||
221 | |||
222 | module_dsa_tag_driver(dsa_8021q_netdev_ops); | ||