Commit d4619336 authored by Vladimir Oltean's avatar Vladimir Oltean Committed by David S. Miller

net: dsa: tag_8021q: Create helper function for removing VLAN header

This removes the existing implementation from tag_sja1105, which was
partially incorrect (it was not changing the MAC header offset, thereby
leaving it to point 4 bytes earlier than it should have).

This overwrites the VLAN tag by moving the Ethernet source and
destination MACs 4 bytes to the right. Then skb->data (assumed to be
pointing immediately after the EtherType) is temporarily pushed to the
beginning of the new Ethernet header, the new Ethernet header offset and
length are recorded, then skb->data is moved back to where it was.
Signed-off-by: default avatarVladimir Oltean <olteanv@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5e3f847a
...@@ -20,9 +20,6 @@ int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int index, ...@@ -20,9 +20,6 @@ int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int index,
struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev, struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
u16 tpid, u16 tci); u16 tpid, u16 tci);
struct sk_buff *dsa_8021q_rcv(struct sk_buff *skb, struct net_device *netdev,
struct packet_type *pt, u16 *tpid, u16 *tci);
u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port); u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port);
u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port); u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port);
...@@ -31,6 +28,8 @@ int dsa_8021q_rx_switch_id(u16 vid); ...@@ -31,6 +28,8 @@ int dsa_8021q_rx_switch_id(u16 vid);
int dsa_8021q_rx_source_port(u16 vid); int dsa_8021q_rx_source_port(u16 vid);
struct sk_buff *dsa_8021q_remove_header(struct sk_buff *skb);
#else #else
int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int index, int dsa_port_setup_8021q_tagging(struct dsa_switch *ds, int index,
...@@ -45,12 +44,6 @@ struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev, ...@@ -45,12 +44,6 @@ struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
return NULL; return NULL;
} }
struct sk_buff *dsa_8021q_rcv(struct sk_buff *skb, struct net_device *netdev,
struct packet_type *pt, u16 *tpid, u16 *tci)
{
return NULL;
}
u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port) u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port)
{ {
return 0; return 0;
...@@ -71,6 +64,11 @@ int dsa_8021q_rx_source_port(u16 vid) ...@@ -71,6 +64,11 @@ int dsa_8021q_rx_source_port(u16 vid)
return 0; return 0;
} }
struct sk_buff *dsa_8021q_remove_header(struct sk_buff *skb)
{
return NULL;
}
#endif /* IS_ENABLED(CONFIG_NET_DSA_TAG_8021Q) */ #endif /* IS_ENABLED(CONFIG_NET_DSA_TAG_8021Q) */
#endif /* _NET_DSA_8021Q_H */ #endif /* _NET_DSA_8021Q_H */
...@@ -235,31 +235,48 @@ struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev, ...@@ -235,31 +235,48 @@ struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
} }
EXPORT_SYMBOL_GPL(dsa_8021q_xmit); EXPORT_SYMBOL_GPL(dsa_8021q_xmit);
struct sk_buff *dsa_8021q_rcv(struct sk_buff *skb, struct net_device *netdev, /* In the DSA packet_type handler, skb->data points in the middle of the VLAN
struct packet_type *pt, u16 *tpid, u16 *tci) * tag, after tpid and before tci. This is because so far, ETH_HLEN
* (DMAC, SMAC, EtherType) bytes were pulled.
* There are 2 bytes of VLAN tag left in skb->data, and upper
* layers expect the 'real' EtherType to be consumed as well.
* Coincidentally, a VLAN header is also of the same size as
* the number of bytes that need to be pulled.
*
* skb_mac_header skb->data
* | |
* v v
* | | | | | | | | | | | | | | | | | | |
* +-----------------------+-----------------------+-------+-------+-------+
* | Destination MAC | Source MAC | TPID | TCI | EType |
* +-----------------------+-----------------------+-------+-------+-------+
* ^ | |
* |<--VLAN_HLEN-->to <---VLAN_HLEN--->
* from |
* >>>>>>> v
* >>>>>>> | | | | | | | | | | | | | | |
* >>>>>>> +-----------------------+-----------------------+-------+
* >>>>>>> | Destination MAC | Source MAC | EType |
* +-----------------------+-----------------------+-------+
* ^ ^
* (now part of | |
* skb->head) skb_mac_header skb->data
*/
struct sk_buff *dsa_8021q_remove_header(struct sk_buff *skb)
{ {
struct vlan_ethhdr *tag; u8 *from = skb_mac_header(skb);
u8 *dest = from + VLAN_HLEN;
if (unlikely(!pskb_may_pull(skb, VLAN_HLEN)))
return NULL;
tag = vlan_eth_hdr(skb); memmove(dest, from, ETH_HLEN - VLAN_HLEN);
*tpid = ntohs(tag->h_vlan_proto); skb_pull(skb, VLAN_HLEN);
*tci = ntohs(tag->h_vlan_TCI); skb_push(skb, ETH_HLEN);
skb_reset_mac_header(skb);
/* skb->data points in the middle of the VLAN tag, skb_reset_mac_len(skb);
* after tpid and before tci. This is because so far, skb_pull_rcsum(skb, ETH_HLEN);
* ETH_HLEN (DMAC, SMAC, EtherType) bytes were pulled.
* There are 2 bytes of VLAN tag left in skb->data, and upper
* layers expect the 'real' EtherType to be consumed as well.
* Coincidentally, a VLAN header is also of the same size as
* the number of bytes that need to be pulled.
*/
skb_pull_rcsum(skb, VLAN_HLEN);
return skb; return skb;
} }
EXPORT_SYMBOL_GPL(dsa_8021q_rcv); EXPORT_SYMBOL_GPL(dsa_8021q_remove_header);
static const struct dsa_device_ops dsa_8021q_netdev_ops = { static const struct dsa_device_ops dsa_8021q_netdev_ops = {
.name = "8021q", .name = "8021q",
......
...@@ -66,17 +66,14 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, ...@@ -66,17 +66,14 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
struct net_device *netdev, struct net_device *netdev,
struct packet_type *pt) struct packet_type *pt)
{ {
struct ethhdr *hdr = eth_hdr(skb); int source_port, switch_id;
u64 source_port, switch_id; struct vlan_ethhdr *hdr;
struct sk_buff *nskb;
u16 tpid, vid, tci; u16 tpid, vid, tci;
bool is_tagged; bool is_tagged;
nskb = dsa_8021q_rcv(skb, netdev, pt, &tpid, &tci); hdr = vlan_eth_hdr(skb);
is_tagged = (nskb && tpid == ETH_P_SJA1105); tpid = ntohs(hdr->h_vlan_proto);
is_tagged = (tpid == ETH_P_SJA1105);
skb->priority = (tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
vid = tci & VLAN_VID_MASK;
skb->offload_fwd_mark = 1; skb->offload_fwd_mark = 1;
...@@ -92,8 +89,11 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, ...@@ -92,8 +89,11 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
hdr->h_dest[4] = 0; hdr->h_dest[4] = 0;
} else { } else {
/* Normal traffic path. */ /* Normal traffic path. */
tci = ntohs(hdr->h_vlan_TCI);
vid = tci & VLAN_VID_MASK;
source_port = dsa_8021q_rx_source_port(vid); source_port = dsa_8021q_rx_source_port(vid);
switch_id = dsa_8021q_rx_switch_id(vid); switch_id = dsa_8021q_rx_switch_id(vid);
skb->priority = (tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
} }
skb->dev = dsa_master_find_slave(netdev, switch_id, source_port); skb->dev = dsa_master_find_slave(netdev, switch_id, source_port);
...@@ -106,8 +106,7 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, ...@@ -106,8 +106,7 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
* it there, see dsa_switch_rcv: skb_push(skb, ETH_HLEN). * it there, see dsa_switch_rcv: skb_push(skb, ETH_HLEN).
*/ */
if (is_tagged) if (is_tagged)
memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - VLAN_HLEN, skb = dsa_8021q_remove_header(skb);
ETH_HLEN - VLAN_HLEN);
return skb; return skb;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment