~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/dsa/tag_edsa.c

Version: ~ [ linux-5.2-rc4 ] ~ [ linux-5.1.9 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.50 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.125 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.181 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.181 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.68 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.39.4 ] ~ [ linux-2.6.38.8 ] ~ [ linux-2.6.37.6 ] ~ [ linux-2.6.36.4 ] ~ [ linux-2.6.35.14 ] ~ [ linux-2.6.34.15 ] ~ [ linux-2.6.33.20 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * net/dsa/tag_edsa.c - Ethertype DSA tagging
  3  * Copyright (c) 2008-2009 Marvell Semiconductor
  4  *
  5  * This program is free software; you can redistribute it and/or modify
  6  * it under the terms of the GNU General Public License as published by
  7  * the Free Software Foundation; either version 2 of the License, or
  8  * (at your option) any later version.
  9  */
 10 
 11 #include <linux/etherdevice.h>
 12 #include <linux/list.h>
 13 #include <linux/netdevice.h>
 14 #include <linux/slab.h>
 15 #include "dsa_priv.h"
 16 
 17 #define DSA_HLEN        4
 18 #define EDSA_HLEN       8
 19 
 20 netdev_tx_t edsa_xmit(struct sk_buff *skb, struct net_device *dev)
 21 {
 22         struct dsa_slave_priv *p = netdev_priv(dev);
 23         u8 *edsa_header;
 24 
 25         dev->stats.tx_packets++;
 26         dev->stats.tx_bytes += skb->len;
 27 
 28         /*
 29          * Convert the outermost 802.1q tag to a DSA tag and prepend
 30          * a DSA ethertype field is the packet is tagged, or insert
 31          * a DSA ethertype plus DSA tag between the addresses and the
 32          * current ethertype field if the packet is untagged.
 33          */
 34         if (skb->protocol == htons(ETH_P_8021Q)) {
 35                 if (skb_cow_head(skb, DSA_HLEN) < 0)
 36                         goto out_free;
 37                 skb_push(skb, DSA_HLEN);
 38 
 39                 memmove(skb->data, skb->data + DSA_HLEN, 2 * ETH_ALEN);
 40 
 41                 /*
 42                  * Construct tagged FROM_CPU DSA tag from 802.1q tag.
 43                  */
 44                 edsa_header = skb->data + 2 * ETH_ALEN;
 45                 edsa_header[0] = (ETH_P_EDSA >> 8) & 0xff;
 46                 edsa_header[1] = ETH_P_EDSA & 0xff;
 47                 edsa_header[2] = 0x00;
 48                 edsa_header[3] = 0x00;
 49                 edsa_header[4] = 0x60 | p->parent->index;
 50                 edsa_header[5] = p->port << 3;
 51 
 52                 /*
 53                  * Move CFI field from byte 6 to byte 5.
 54                  */
 55                 if (edsa_header[6] & 0x10) {
 56                         edsa_header[5] |= 0x01;
 57                         edsa_header[6] &= ~0x10;
 58                 }
 59         } else {
 60                 if (skb_cow_head(skb, EDSA_HLEN) < 0)
 61                         goto out_free;
 62                 skb_push(skb, EDSA_HLEN);
 63 
 64                 memmove(skb->data, skb->data + EDSA_HLEN, 2 * ETH_ALEN);
 65 
 66                 /*
 67                  * Construct untagged FROM_CPU DSA tag.
 68                  */
 69                 edsa_header = skb->data + 2 * ETH_ALEN;
 70                 edsa_header[0] = (ETH_P_EDSA >> 8) & 0xff;
 71                 edsa_header[1] = ETH_P_EDSA & 0xff;
 72                 edsa_header[2] = 0x00;
 73                 edsa_header[3] = 0x00;
 74                 edsa_header[4] = 0x40 | p->parent->index;
 75                 edsa_header[5] = p->port << 3;
 76                 edsa_header[6] = 0x00;
 77                 edsa_header[7] = 0x00;
 78         }
 79 
 80         skb->protocol = htons(ETH_P_EDSA);
 81 
 82         skb->dev = p->parent->dst->master_netdev;
 83         dev_queue_xmit(skb);
 84 
 85         return NETDEV_TX_OK;
 86 
 87 out_free:
 88         kfree_skb(skb);
 89         return NETDEV_TX_OK;
 90 }
 91 
 92 static int edsa_rcv(struct sk_buff *skb, struct net_device *dev,
 93                     struct packet_type *pt, struct net_device *orig_dev)
 94 {
 95         struct dsa_switch_tree *dst = dev->dsa_ptr;
 96         struct dsa_switch *ds;
 97         u8 *edsa_header;
 98         int source_device;
 99         int source_port;
100 
101         if (unlikely(dst == NULL))
102                 goto out_drop;
103 
104         skb = skb_unshare(skb, GFP_ATOMIC);
105         if (skb == NULL)
106                 goto out;
107 
108         if (unlikely(!pskb_may_pull(skb, EDSA_HLEN)))
109                 goto out_drop;
110 
111         /*
112          * Skip the two null bytes after the ethertype.
113          */
114         edsa_header = skb->data + 2;
115 
116         /*
117          * Check that frame type is either TO_CPU or FORWARD.
118          */
119         if ((edsa_header[0] & 0xc0) != 0x00 && (edsa_header[0] & 0xc0) != 0xc0)
120                 goto out_drop;
121 
122         /*
123          * Determine source device and port.
124          */
125         source_device = edsa_header[0] & 0x1f;
126         source_port = (edsa_header[1] >> 3) & 0x1f;
127 
128         /*
129          * Check that the source device exists and that the source
130          * port is a registered DSA port.
131          */
132         if (source_device >= dst->pd->nr_chips)
133                 goto out_drop;
134         ds = dst->ds[source_device];
135         if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL)
136                 goto out_drop;
137 
138         /*
139          * If the 'tagged' bit is set, convert the DSA tag to a 802.1q
140          * tag and delete the ethertype part.  If the 'tagged' bit is
141          * clear, delete the ethertype and the DSA tag parts.
142          */
143         if (edsa_header[0] & 0x20) {
144                 u8 new_header[4];
145 
146                 /*
147                  * Insert 802.1q ethertype and copy the VLAN-related
148                  * fields, but clear the bit that will hold CFI (since
149                  * DSA uses that bit location for another purpose).
150                  */
151                 new_header[0] = (ETH_P_8021Q >> 8) & 0xff;
152                 new_header[1] = ETH_P_8021Q & 0xff;
153                 new_header[2] = edsa_header[2] & ~0x10;
154                 new_header[3] = edsa_header[3];
155 
156                 /*
157                  * Move CFI bit from its place in the DSA header to
158                  * its 802.1q-designated place.
159                  */
160                 if (edsa_header[1] & 0x01)
161                         new_header[2] |= 0x10;
162 
163                 skb_pull_rcsum(skb, DSA_HLEN);
164 
165                 /*
166                  * Update packet checksum if skb is CHECKSUM_COMPLETE.
167                  */
168                 if (skb->ip_summed == CHECKSUM_COMPLETE) {
169                         __wsum c = skb->csum;
170                         c = csum_add(c, csum_partial(new_header + 2, 2, 0));
171                         c = csum_sub(c, csum_partial(edsa_header + 2, 2, 0));
172                         skb->csum = c;
173                 }
174 
175                 memcpy(edsa_header, new_header, DSA_HLEN);
176 
177                 memmove(skb->data - ETH_HLEN,
178                         skb->data - ETH_HLEN - DSA_HLEN,
179                         2 * ETH_ALEN);
180         } else {
181                 /*
182                  * Remove DSA tag and update checksum.
183                  */
184                 skb_pull_rcsum(skb, EDSA_HLEN);
185                 memmove(skb->data - ETH_HLEN,
186                         skb->data - ETH_HLEN - EDSA_HLEN,
187                         2 * ETH_ALEN);
188         }
189 
190         skb->dev = ds->ports[source_port];
191         skb_push(skb, ETH_HLEN);
192         skb->pkt_type = PACKET_HOST;
193         skb->protocol = eth_type_trans(skb, skb->dev);
194 
195         skb->dev->stats.rx_packets++;
196         skb->dev->stats.rx_bytes += skb->len;
197 
198         netif_receive_skb(skb);
199 
200         return 0;
201 
202 out_drop:
203         kfree_skb(skb);
204 out:
205         return 0;
206 }
207 
208 struct packet_type edsa_packet_type __read_mostly = {
209         .type   = cpu_to_be16(ETH_P_EDSA),
210         .func   = edsa_rcv,
211 };
212 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp