Merge tag 'for-5.9/dm-fixes-2' of git://git.kernel.org/pub/scm/linux/kernel/git/devic...
[linux-2.6-microblaze.git] / net / dsa / tag_edsa.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * net/dsa/tag_edsa.c - Ethertype DSA tagging
4  * Copyright (c) 2008-2009 Marvell Semiconductor
5  */
6
7 #include <linux/etherdevice.h>
8 #include <linux/list.h>
9 #include <linux/slab.h>
10
11 #include "dsa_priv.h"
12
13 #define DSA_HLEN        4
14 #define EDSA_HLEN       8
15
16 #define FRAME_TYPE_TO_CPU       0x00
17 #define FRAME_TYPE_FORWARD      0x03
18
19 #define TO_CPU_CODE_MGMT_TRAP           0x00
20 #define TO_CPU_CODE_FRAME2REG           0x01
21 #define TO_CPU_CODE_IGMP_MLD_TRAP       0x02
22 #define TO_CPU_CODE_POLICY_TRAP         0x03
23 #define TO_CPU_CODE_ARP_MIRROR          0x04
24 #define TO_CPU_CODE_POLICY_MIRROR       0x05
25
26 static struct sk_buff *edsa_xmit(struct sk_buff *skb, struct net_device *dev)
27 {
28         struct dsa_port *dp = dsa_slave_to_port(dev);
29         u8 *edsa_header;
30
31         /*
32          * Convert the outermost 802.1q tag to a DSA tag and prepend
33          * a DSA ethertype field is the packet is tagged, or insert
34          * a DSA ethertype plus DSA tag between the addresses and the
35          * current ethertype field if the packet is untagged.
36          */
37         if (skb->protocol == htons(ETH_P_8021Q)) {
38                 if (skb_cow_head(skb, DSA_HLEN) < 0)
39                         return NULL;
40                 skb_push(skb, DSA_HLEN);
41
42                 memmove(skb->data, skb->data + DSA_HLEN, 2 * ETH_ALEN);
43
44                 /*
45                  * Construct tagged FROM_CPU DSA tag from 802.1q tag.
46                  */
47                 edsa_header = skb->data + 2 * ETH_ALEN;
48                 edsa_header[0] = (ETH_P_EDSA >> 8) & 0xff;
49                 edsa_header[1] = ETH_P_EDSA & 0xff;
50                 edsa_header[2] = 0x00;
51                 edsa_header[3] = 0x00;
52                 edsa_header[4] = 0x60 | dp->ds->index;
53                 edsa_header[5] = dp->index << 3;
54
55                 /*
56                  * Move CFI field from byte 6 to byte 5.
57                  */
58                 if (edsa_header[6] & 0x10) {
59                         edsa_header[5] |= 0x01;
60                         edsa_header[6] &= ~0x10;
61                 }
62         } else {
63                 if (skb_cow_head(skb, EDSA_HLEN) < 0)
64                         return NULL;
65                 skb_push(skb, EDSA_HLEN);
66
67                 memmove(skb->data, skb->data + EDSA_HLEN, 2 * ETH_ALEN);
68
69                 /*
70                  * Construct untagged FROM_CPU DSA tag.
71                  */
72                 edsa_header = skb->data + 2 * ETH_ALEN;
73                 edsa_header[0] = (ETH_P_EDSA >> 8) & 0xff;
74                 edsa_header[1] = ETH_P_EDSA & 0xff;
75                 edsa_header[2] = 0x00;
76                 edsa_header[3] = 0x00;
77                 edsa_header[4] = 0x40 | dp->ds->index;
78                 edsa_header[5] = dp->index << 3;
79                 edsa_header[6] = 0x00;
80                 edsa_header[7] = 0x00;
81         }
82
83         return skb;
84 }
85
86 static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev,
87                                 struct packet_type *pt)
88 {
89         u8 *edsa_header;
90         int frame_type;
91         int code;
92         int source_device;
93         int source_port;
94
95         if (unlikely(!pskb_may_pull(skb, EDSA_HLEN)))
96                 return NULL;
97
98         /*
99          * Skip the two null bytes after the ethertype.
100          */
101         edsa_header = skb->data + 2;
102
103         /*
104          * Check that frame type is either TO_CPU or FORWARD.
105          */
106         frame_type = edsa_header[0] >> 6;
107
108         switch (frame_type) {
109         case FRAME_TYPE_TO_CPU:
110                 code = (edsa_header[1] & 0x6) | ((edsa_header[2] >> 4) & 1);
111
112                 /*
113                  * Mark the frame to never egress on any port of the same switch
114                  * unless it's a trapped IGMP/MLD packet, in which case the
115                  * bridge might want to forward it.
116                  */
117                 if (code != TO_CPU_CODE_IGMP_MLD_TRAP)
118                         skb->offload_fwd_mark = 1;
119
120                 break;
121
122         case FRAME_TYPE_FORWARD:
123                 skb->offload_fwd_mark = 1;
124                 break;
125
126         default:
127                 return NULL;
128         }
129
130         /*
131          * Determine source device and port.
132          */
133         source_device = edsa_header[0] & 0x1f;
134         source_port = (edsa_header[1] >> 3) & 0x1f;
135
136         skb->dev = dsa_master_find_slave(dev, source_device, source_port);
137         if (!skb->dev)
138                 return NULL;
139
140         /*
141          * If the 'tagged' bit is set, convert the DSA tag to a 802.1q
142          * tag and delete the ethertype part.  If the 'tagged' bit is
143          * clear, delete the ethertype and the DSA tag parts.
144          */
145         if (edsa_header[0] & 0x20) {
146                 u8 new_header[4];
147
148                 /*
149                  * Insert 802.1q ethertype and copy the VLAN-related
150                  * fields, but clear the bit that will hold CFI (since
151                  * DSA uses that bit location for another purpose).
152                  */
153                 new_header[0] = (ETH_P_8021Q >> 8) & 0xff;
154                 new_header[1] = ETH_P_8021Q & 0xff;
155                 new_header[2] = edsa_header[2] & ~0x10;
156                 new_header[3] = edsa_header[3];
157
158                 /*
159                  * Move CFI bit from its place in the DSA header to
160                  * its 802.1q-designated place.
161                  */
162                 if (edsa_header[1] & 0x01)
163                         new_header[2] |= 0x10;
164
165                 skb_pull_rcsum(skb, DSA_HLEN);
166
167                 /*
168                  * Update packet checksum if skb is CHECKSUM_COMPLETE.
169                  */
170                 if (skb->ip_summed == CHECKSUM_COMPLETE) {
171                         __wsum c = skb->csum;
172                         c = csum_add(c, csum_partial(new_header + 2, 2, 0));
173                         c = csum_sub(c, csum_partial(edsa_header + 2, 2, 0));
174                         skb->csum = c;
175                 }
176
177                 memcpy(edsa_header, new_header, DSA_HLEN);
178
179                 memmove(skb->data - ETH_HLEN,
180                         skb->data - ETH_HLEN - DSA_HLEN,
181                         2 * ETH_ALEN);
182         } else {
183                 /*
184                  * Remove DSA tag and update checksum.
185                  */
186                 skb_pull_rcsum(skb, EDSA_HLEN);
187                 memmove(skb->data - ETH_HLEN,
188                         skb->data - ETH_HLEN - EDSA_HLEN,
189                         2 * ETH_ALEN);
190         }
191
192         return skb;
193 }
194
195 static int edsa_tag_flow_dissect(const struct sk_buff *skb, __be16 *proto,
196                                  int *offset)
197 {
198         *offset = 8;
199         *proto = ((__be16 *)skb->data)[3];
200         return 0;
201 }
202
203 static const struct dsa_device_ops edsa_netdev_ops = {
204         .name   = "edsa",
205         .proto  = DSA_TAG_PROTO_EDSA,
206         .xmit   = edsa_xmit,
207         .rcv    = edsa_rcv,
208         .flow_dissect   = edsa_tag_flow_dissect,
209         .overhead = EDSA_HLEN,
210 };
211
212 MODULE_LICENSE("GPL");
213 MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_EDSA);
214
215 module_dsa_tag_driver(edsa_netdev_ops);