|
@@ -65,179 +65,6 @@ static int vlan_dev_rebuild_header(struct sk_buff *skb)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static inline struct sk_buff *vlan_check_reorder_header(struct sk_buff *skb)
|
|
|
-{
|
|
|
- if (vlan_dev_info(skb->dev)->flags & VLAN_FLAG_REORDER_HDR) {
|
|
|
- if (skb_cow(skb, skb_headroom(skb)) < 0)
|
|
|
- skb = NULL;
|
|
|
- if (skb) {
|
|
|
- /* Lifted from Gleb's VLAN code... */
|
|
|
- memmove(skb->data - ETH_HLEN,
|
|
|
- skb->data - VLAN_ETH_HLEN, 12);
|
|
|
- skb->mac_header += VLAN_HLEN;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return skb;
|
|
|
-}
|
|
|
-
|
|
|
-static inline void vlan_set_encap_proto(struct sk_buff *skb,
|
|
|
- struct vlan_hdr *vhdr)
|
|
|
-{
|
|
|
- __be16 proto;
|
|
|
- unsigned char *rawp;
|
|
|
-
|
|
|
- /*
|
|
|
- * Was a VLAN packet, grab the encapsulated protocol, which the layer
|
|
|
- * three protocols care about.
|
|
|
- */
|
|
|
-
|
|
|
- proto = vhdr->h_vlan_encapsulated_proto;
|
|
|
- if (ntohs(proto) >= 1536) {
|
|
|
- skb->protocol = proto;
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- rawp = skb->data;
|
|
|
- if (*(unsigned short *)rawp == 0xFFFF)
|
|
|
- /*
|
|
|
- * This is a magic hack to spot IPX packets. Older Novell
|
|
|
- * breaks the protocol design and runs IPX over 802.3 without
|
|
|
- * an 802.2 LLC layer. We look for FFFF which isn't a used
|
|
|
- * 802.2 SSAP/DSAP. This won't work for fault tolerant netware
|
|
|
- * but does for the rest.
|
|
|
- */
|
|
|
- skb->protocol = htons(ETH_P_802_3);
|
|
|
- else
|
|
|
- /*
|
|
|
- * Real 802.2 LLC
|
|
|
- */
|
|
|
- skb->protocol = htons(ETH_P_802_2);
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * Determine the packet's protocol ID. The rule here is that we
|
|
|
- * assume 802.3 if the type field is short enough to be a length.
|
|
|
- * This is normal practice and works for any 'now in use' protocol.
|
|
|
- *
|
|
|
- * Also, at this point we assume that we ARE dealing exclusively with
|
|
|
- * VLAN packets, or packets that should be made into VLAN packets based
|
|
|
- * on a default VLAN ID.
|
|
|
- *
|
|
|
- * NOTE: Should be similar to ethernet/eth.c.
|
|
|
- *
|
|
|
- * SANITY NOTE: This method is called when a packet is moving up the stack
|
|
|
- * towards userland. To get here, it would have already passed
|
|
|
- * through the ethernet/eth.c eth_type_trans() method.
|
|
|
- * SANITY NOTE 2: We are referencing to the VLAN_HDR frields, which MAY be
|
|
|
- * stored UNALIGNED in the memory. RISC systems don't like
|
|
|
- * such cases very much...
|
|
|
- * SANITY NOTE 2a: According to Dave Miller & Alexey, it will always be
|
|
|
- * aligned, so there doesn't need to be any of the unaligned
|
|
|
- * stuff. It has been commented out now... --Ben
|
|
|
- *
|
|
|
- */
|
|
|
-int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
|
|
|
- struct packet_type *ptype, struct net_device *orig_dev)
|
|
|
-{
|
|
|
- struct vlan_hdr *vhdr;
|
|
|
- struct vlan_pcpu_stats *rx_stats;
|
|
|
- struct net_device *vlan_dev;
|
|
|
- u16 vlan_id;
|
|
|
- u16 vlan_tci;
|
|
|
-
|
|
|
- skb = skb_share_check(skb, GFP_ATOMIC);
|
|
|
- if (skb == NULL)
|
|
|
- goto err_free;
|
|
|
-
|
|
|
- if (unlikely(!pskb_may_pull(skb, VLAN_HLEN)))
|
|
|
- goto err_free;
|
|
|
-
|
|
|
- vhdr = (struct vlan_hdr *)skb->data;
|
|
|
- vlan_tci = ntohs(vhdr->h_vlan_TCI);
|
|
|
- vlan_id = vlan_tci & VLAN_VID_MASK;
|
|
|
-
|
|
|
- rcu_read_lock();
|
|
|
- vlan_dev = vlan_find_dev(dev, vlan_id);
|
|
|
-
|
|
|
- /* If the VLAN device is defined, we use it.
|
|
|
- * If not, and the VID is 0, it is a 802.1p packet (not
|
|
|
- * really a VLAN), so we will just netif_rx it later to the
|
|
|
- * original interface, but with the skb->proto set to the
|
|
|
- * wrapped proto: we do nothing here.
|
|
|
- */
|
|
|
-
|
|
|
- if (!vlan_dev) {
|
|
|
- if (vlan_id) {
|
|
|
- pr_debug("%s: ERROR: No net_device for VID: %u on dev: %s\n",
|
|
|
- __func__, vlan_id, dev->name);
|
|
|
- goto err_unlock;
|
|
|
- }
|
|
|
- rx_stats = NULL;
|
|
|
- } else {
|
|
|
- skb->dev = vlan_dev;
|
|
|
-
|
|
|
- rx_stats = this_cpu_ptr(vlan_dev_info(skb->dev)->vlan_pcpu_stats);
|
|
|
-
|
|
|
- u64_stats_update_begin(&rx_stats->syncp);
|
|
|
- rx_stats->rx_packets++;
|
|
|
- rx_stats->rx_bytes += skb->len;
|
|
|
-
|
|
|
- skb->priority = vlan_get_ingress_priority(skb->dev, vlan_tci);
|
|
|
-
|
|
|
- pr_debug("%s: priority: %u for TCI: %hu\n",
|
|
|
- __func__, skb->priority, vlan_tci);
|
|
|
-
|
|
|
- switch (skb->pkt_type) {
|
|
|
- case PACKET_BROADCAST:
|
|
|
- /* Yeah, stats collect these together.. */
|
|
|
- /* stats->broadcast ++; // no such counter :-( */
|
|
|
- break;
|
|
|
-
|
|
|
- case PACKET_MULTICAST:
|
|
|
- rx_stats->rx_multicast++;
|
|
|
- break;
|
|
|
-
|
|
|
- case PACKET_OTHERHOST:
|
|
|
- /* Our lower layer thinks this is not local, let's make
|
|
|
- * sure.
|
|
|
- * This allows the VLAN to have a different MAC than the
|
|
|
- * underlying device, and still route correctly.
|
|
|
- */
|
|
|
- if (!compare_ether_addr(eth_hdr(skb)->h_dest,
|
|
|
- skb->dev->dev_addr))
|
|
|
- skb->pkt_type = PACKET_HOST;
|
|
|
- break;
|
|
|
- default:
|
|
|
- break;
|
|
|
- }
|
|
|
- u64_stats_update_end(&rx_stats->syncp);
|
|
|
- }
|
|
|
-
|
|
|
- skb_pull_rcsum(skb, VLAN_HLEN);
|
|
|
- vlan_set_encap_proto(skb, vhdr);
|
|
|
-
|
|
|
- if (vlan_dev) {
|
|
|
- skb = vlan_check_reorder_header(skb);
|
|
|
- if (!skb) {
|
|
|
- rx_stats->rx_errors++;
|
|
|
- goto err_unlock;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- netif_rx(skb);
|
|
|
-
|
|
|
- rcu_read_unlock();
|
|
|
- return NET_RX_SUCCESS;
|
|
|
-
|
|
|
-err_unlock:
|
|
|
- rcu_read_unlock();
|
|
|
-err_free:
|
|
|
- atomic_long_inc(&dev->rx_dropped);
|
|
|
- kfree_skb(skb);
|
|
|
- return NET_RX_DROP;
|
|
|
-}
|
|
|
-
|
|
|
static inline u16
|
|
|
vlan_dev_get_egress_qos_mask(struct net_device *dev, struct sk_buff *skb)
|
|
|
{
|