*/
#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/in.h>
-#include <linux/init.h>
-#include <asm/uaccess.h> /* for copy_from_user */
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
-#include <net/datalink.h>
-#include <net/p8022.h>
#include <net/arp.h>
#include "vlan.h"
#include "vlanproc.h"
#include <linux/if_vlan.h>
-#include <net/ip.h>
/*
* Rebuild the Ethernet MAC header. This is called after an ARP
unsigned int len)
{
struct vlan_hdr *vhdr;
+ unsigned int vhdrlen = 0;
u16 vlan_tci = 0;
- int rc = 0;
- int build_vlan_header = 0;
-
- pr_debug("%s: skb: %p type: %hx len: %u vlan_id: %hx, daddr: %p\n",
- __func__, skb, type, len, vlan_dev_info(dev)->vlan_id,
- daddr);
+ int rc;
if (WARN_ON(skb_headroom(skb) < dev->hard_header_len))
return -ENOSPC;
- /* build vlan header only if re_order_header flag is NOT set. This
- * fixes some programs that get confused when they see a VLAN device
- * sending a frame that is VLAN encoded (the consensus is that the VLAN
- * device should look completely like an Ethernet device when the
- * REORDER_HEADER flag is set) The drawback to this is some extra
- * header shuffling in the hard_start_xmit. Users can turn off this
- * REORDER behaviour with the vconfig tool.
- */
- if (!(vlan_dev_info(dev)->flags & VLAN_FLAG_REORDER_HDR))
- build_vlan_header = 1;
-
- if (build_vlan_header) {
+ if (!(vlan_dev_info(dev)->flags & VLAN_FLAG_REORDER_HDR)) {
vhdr = (struct vlan_hdr *) skb_push(skb, VLAN_HLEN);
- /* build the four bytes that make this a VLAN header. */
-
- /* Now, construct the second two bytes. This field looks
- * something like:
- * usr_priority: 3 bits (high bits)
- * CFI 1 bit
- * VLAN ID 12 bits (low bits)
- *
- */
vlan_tci = vlan_dev_info(dev)->vlan_id;
vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb);
-
vhdr->h_vlan_TCI = htons(vlan_tci);
/*
* put the length in here instead. It is up to the 802.2
* layer to carry protocol information.
*/
-
if (type != ETH_P_802_3)
vhdr->h_vlan_encapsulated_proto = htons(type);
else
vhdr->h_vlan_encapsulated_proto = htons(len);
skb->protocol = htons(ETH_P_8021Q);
+ type = ETH_P_8021Q;
+ vhdrlen = VLAN_HLEN;
}
/* Before delegating work to the lower layer, enter our MAC-address */
if (saddr == NULL)
saddr = dev->dev_addr;
+ /* Now make the underlying real hard header */
dev = vlan_dev_info(dev)->real_dev;
-
- if (build_vlan_header) {
- /* Now make the underlying real hard header */
- rc = dev_hard_header(skb, dev, ETH_P_8021Q, daddr, saddr,
- len + VLAN_HLEN);
- if (rc > 0)
- rc += VLAN_HLEN;
- else if (rc < 0)
- rc -= VLAN_HLEN;
- } else
- /* If here, then we'll just make a normal looking ethernet
- * frame, but, the hard_start_xmit method will insert the tag
- * (it has to be able to do this for bridged and other skbs
- * that don't come down the protocol stack in an orderly manner.
- */
- rc = dev_hard_header(skb, dev, type, daddr, saddr, len);
-
+ rc = dev_hard_header(skb, dev, type, daddr, saddr, len + vhdrlen);
+ if (rc > 0)
+ rc += vhdrlen;
return rc;
}
* NOTE: THIS ASSUMES DIX ETHERNET, SPECIFICALLY NOT SUPPORTING
* OTHER THINGS LIKE FDDI/TokenRing/802.3 SNAPs...
*/
-
if (veth->h_vlan_proto != htons(ETH_P_8021Q) ||
- vlan_dev_info(dev)->flags & VLAN_FLAG_REORDER_HDR) {
- int orig_headroom = skb_headroom(skb);
+ vlan_dev_info(dev)->flags & VLAN_FLAG_REORDER_HDR) {
+ unsigned int orig_headroom = skb_headroom(skb);
u16 vlan_tci;
- /* This is not a VLAN frame...but we can fix that! */
vlan_dev_info(dev)->cnt_encap_on_xmit++;
- pr_debug("%s: proto to encap: 0x%hx\n",
- __func__, ntohs(veth->h_vlan_proto));
- /* Construct the second two bytes. This field looks something
- * like:
- * usr_priority: 3 bits (high bits)
- * CFI 1 bit
- * VLAN ID 12 bits (low bits)
- */
vlan_tci = vlan_dev_info(dev)->vlan_id;
vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb);
-
skb = __vlan_put_tag(skb, vlan_tci);
if (!skb) {
stats->tx_dropped++;
- return 0;
+ return NETDEV_TX_OK;
}
if (orig_headroom < VLAN_HLEN)
vlan_dev_info(dev)->cnt_inc_headroom_on_tx++;
}
- pr_debug("%s: about to send skb: %p to dev: %s\n",
- __func__, skb, skb->dev->name);
- pr_debug(" " MAC_FMT " " MAC_FMT " %4hx %4hx %4hx\n",
- veth->h_dest[0], veth->h_dest[1], veth->h_dest[2],
- veth->h_dest[3], veth->h_dest[4], veth->h_dest[5],
- veth->h_source[0], veth->h_source[1], veth->h_source[2],
- veth->h_source[3], veth->h_source[4], veth->h_source[5],
- veth->h_vlan_proto, veth->h_vlan_TCI,
- veth->h_vlan_encapsulated_proto);
-
- stats->tx_packets++; /* for statics only */
+ stats->tx_packets++;
stats->tx_bytes += skb->len;
skb->dev = vlan_dev_info(dev)->real_dev;
dev_queue_xmit(skb);
-
- return 0;
+ return NETDEV_TX_OK;
}
static int vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb,
struct net_device_stats *stats = &dev->stats;
u16 vlan_tci;
- /* Construct the second two bytes. This field looks something
- * like:
- * usr_priority: 3 bits (high bits)
- * CFI 1 bit
- * VLAN ID 12 bits (low bits)
- */
vlan_tci = vlan_dev_info(dev)->vlan_id;
vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb);
skb = __vlan_hwaccel_put_tag(skb, vlan_tci);
skb->dev = vlan_dev_info(dev)->real_dev;
dev_queue_xmit(skb);
-
- return 0;
+ return NETDEV_TX_OK;
}
static int vlan_dev_change_mtu(struct net_device *dev, int new_mtu)
if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr)) {
err = dev_unicast_add(real_dev, dev->dev_addr, ETH_ALEN);
if (err < 0)
- return err;
+ goto out;
}
- memcpy(vlan->real_dev_addr, real_dev->dev_addr, ETH_ALEN);
- if (dev->flags & IFF_ALLMULTI)
- dev_set_allmulti(real_dev, 1);
- if (dev->flags & IFF_PROMISC)
- dev_set_promiscuity(real_dev, 1);
+ if (dev->flags & IFF_ALLMULTI) {
+ err = dev_set_allmulti(real_dev, 1);
+ if (err < 0)
+ goto del_unicast;
+ }
+ if (dev->flags & IFF_PROMISC) {
+ err = dev_set_promiscuity(real_dev, 1);
+ if (err < 0)
+ goto clear_allmulti;
+ }
+
+ memcpy(vlan->real_dev_addr, real_dev->dev_addr, ETH_ALEN);
if (vlan->flags & VLAN_FLAG_GVRP)
vlan_gvrp_request_join(dev);
return 0;
+
+clear_allmulti:
+ if (dev->flags & IFF_ALLMULTI)
+ dev_set_allmulti(real_dev, -1);
+del_unicast:
+ if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr))
+ dev_unicast_delete(real_dev, dev->dev_addr, ETH_ALEN);
+out:
+ return err;
}
static int vlan_dev_stop(struct net_device *dev)
* separate class since they always nest.
*/
static struct lock_class_key vlan_netdev_xmit_lock_key;
+static struct lock_class_key vlan_netdev_addr_lock_key;
+
+static void vlan_dev_set_lockdep_one(struct net_device *dev,
+ struct netdev_queue *txq,
+ void *_subclass)
+{
+ lockdep_set_class_and_subclass(&txq->_xmit_lock,
+ &vlan_netdev_xmit_lock_key,
+ *(int *)_subclass);
+}
+
+static void vlan_dev_set_lockdep_class(struct net_device *dev, int subclass)
+{
+ lockdep_set_class_and_subclass(&dev->addr_list_lock,
+ &vlan_netdev_addr_lock_key,
+ subclass);
+ netdev_for_each_tx_queue(dev, vlan_dev_set_lockdep_one, &subclass);
+}
static const struct header_ops vlan_header_ops = {
.create = vlan_dev_hard_header,
if (is_vlan_dev(real_dev))
subclass = 1;
- lockdep_set_class_and_subclass(&dev->_xmit_lock,
- &vlan_netdev_xmit_lock_key, subclass);
+ vlan_dev_set_lockdep_class(dev, subclass);
return 0;
}
return real_dev->ethtool_ops->get_rx_csum(real_dev);
}
+static u32 vlan_ethtool_get_flags(struct net_device *dev)
+{
+ const struct vlan_dev_info *vlan = vlan_dev_info(dev);
+ struct net_device *real_dev = vlan->real_dev;
+
+ if (!(real_dev->features & NETIF_F_HW_VLAN_RX) ||
+ real_dev->ethtool_ops == NULL ||
+ real_dev->ethtool_ops->get_flags == NULL)
+ return 0;
+ return real_dev->ethtool_ops->get_flags(real_dev);
+}
+
static const struct ethtool_ops vlan_ethtool_ops = {
.get_link = ethtool_op_get_link,
.get_rx_csum = vlan_ethtool_get_rx_csum,
+ .get_flags = vlan_ethtool_get_flags,
};
void vlan_setup(struct net_device *dev)