* Ethernet-type device handling.
*
* Authors: Ben Greear <greearb@candelatech.com>
- * Please send support related email to: vlan@scry.wanfear.com
+ * Please send support related email to: netdev@vger.kernel.org
* VLAN Home Page: http://www.candelatech.com/~greear/vlan.html
*
* Fixes: Mar 22 2001: Martin Bokaemper <mbokaemper@unispherenetworks.com>
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.
int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *ptype, struct net_device *orig_dev)
{
- unsigned char *rawp = NULL;
struct vlan_hdr *vhdr;
unsigned short vid;
struct net_device_stats *stats;
unsigned short vlan_TCI;
- __be16 proto;
- if (dev->nd_net != &init_net) {
- kfree_skb(skb);
- return -1;
- }
+ if (dev->nd_net != &init_net)
+ goto err_free;
skb = skb_share_check(skb, GFP_ATOMIC);
if (skb == NULL)
- return -1;
+ goto err_free;
- if (unlikely(!pskb_may_pull(skb, VLAN_HLEN))) {
- kfree_skb(skb);
- return -1;
- }
+ if (unlikely(!pskb_may_pull(skb, VLAN_HLEN)))
+ goto err_free;
- vhdr = (struct vlan_hdr *)(skb->data);
-
- /* vlan_TCI = ntohs(get_unaligned(&vhdr->h_vlan_TCI)); */
+ vhdr = (struct vlan_hdr *)skb->data;
vlan_TCI = ntohs(vhdr->h_vlan_TCI);
-
vid = (vlan_TCI & VLAN_VID_MASK);
- /* Ok, we will find the correct VLAN device, strip the header,
- * and then go on as usual.
- */
-
- /* We have 12 bits of vlan ID.
- *
- * We must not drop allow preempt until we hold a
- * reference to the device (netif_rx does that) or we
- * fail.
- */
-
rcu_read_lock();
skb->dev = __find_vlan_dev(dev, vid);
if (!skb->dev) {
- rcu_read_unlock();
pr_debug("%s: ERROR: No net_device for VID: %u on dev: %s\n",
__FUNCTION__, (unsigned int)vid, dev->name);
- kfree_skb(skb);
- return -1;
+ goto err_unlock;
}
skb->dev->last_rx = jiffies;
- /* Bump the rx counters for the VLAN device. */
stats = &skb->dev->stats;
stats->rx_packets++;
stats->rx_bytes += skb->len;
- /* Take off the VLAN header (4 bytes currently) */
skb_pull_rcsum(skb, VLAN_HLEN);
- /*
- * Deal with ingress priority mapping.
- */
skb->priority = vlan_get_ingress_priority(skb->dev,
ntohs(vhdr->h_vlan_TCI));
pr_debug("%s: priority: %u for TCI: %hu\n",
__FUNCTION__, skb->priority, ntohs(vhdr->h_vlan_TCI));
- /* The ethernet driver already did the pkt_type calculations
- * for us...
- */
switch (skb->pkt_type) {
case PACKET_BROADCAST: /* Yeah, stats collect these together.. */
/* stats->broadcast ++; // no such counter :-( */
*/
if (!compare_ether_addr(eth_hdr(skb)->h_dest,
skb->dev->dev_addr))
- /* It is for our (changed) MAC-address! */
skb->pkt_type = PACKET_HOST;
break;
default:
break;
}
- /* Was a VLAN packet, grab the encapsulated protocol, which the layer
- * three protocols care about.
- */
- /* proto = get_unaligned(&vhdr->h_vlan_encapsulated_proto); */
- proto = vhdr->h_vlan_encapsulated_proto;
-
- skb->protocol = proto;
- if (ntohs(proto) >= 1536) {
- /* place it back on the queue to be handled by
- * true layer 3 protocols.
- */
-
- /* See if we are configured to re-write the VLAN header
- * to make it look like ethernet...
- */
- skb = vlan_check_reorder_header(skb);
-
- /* Can be null if skb-clone fails when re-ordering */
- if (skb) {
- netif_rx(skb);
- } else {
- /* TODO: Add a more specific counter here. */
- stats->rx_errors++;
- }
- rcu_read_unlock();
- return 0;
- }
-
- rawp = skb->data;
-
- /*
- * 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.
- */
- if (*(unsigned short *)rawp == 0xFFFF) {
- skb->protocol = htons(ETH_P_802_3);
- /* place it back on the queue to be handled by true layer 3
- * protocols. */
-
- /* See if we are configured to re-write the VLAN header
- * to make it look like ethernet...
- */
- skb = vlan_check_reorder_header(skb);
-
- /* Can be null if skb-clone fails when re-ordering */
- if (skb) {
- netif_rx(skb);
- } else {
- /* TODO: Add a more specific counter here. */
- stats->rx_errors++;
- }
- rcu_read_unlock();
- return 0;
- }
-
- /*
- * Real 802.2 LLC
- */
- skb->protocol = htons(ETH_P_802_2);
- /* place it back on the queue to be handled by upper layer protocols.
- */
+ vlan_set_encap_proto(skb, vhdr);
- /* See if we are configured to re-write the VLAN header
- * to make it look like ethernet...
- */
skb = vlan_check_reorder_header(skb);
-
- /* Can be null if skb-clone fails when re-ordering */
- if (skb) {
- netif_rx(skb);
- } else {
- /* TODO: Add a more specific counter here. */
+ if (!skb) {
stats->rx_errors++;
+ goto err_unlock;
}
+
+ netif_rx(skb);
rcu_read_unlock();
- return 0;
+ return NET_RX_SUCCESS;
+
+err_unlock:
+ rcu_read_unlock();
+err_free:
+ kfree_skb(skb);
+ return NET_RX_DROP;
}
static inline unsigned short
vlan_dev_info(dev)->cnt_encap_on_xmit++;
pr_debug("%s: proto to encap: 0x%hx\n",
- __FUNCTION__, htons(veth->h_vlan_proto));
+ __FUNCTION__, ntohs(veth->h_vlan_proto));
/* Construct the second two bytes. This field looks something
* like:
* usr_priority: 3 bits (high bits)
struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
dev_mc_unsync(real_dev, dev);
+ dev_unicast_unsync(real_dev, dev);
if (dev->flags & IFF_ALLMULTI)
dev_set_allmulti(real_dev, -1);
if (dev->flags & IFF_PROMISC)
dev_set_promiscuity(real_dev, dev->flags & IFF_PROMISC ? 1 : -1);
}
-static void vlan_dev_set_multicast_list(struct net_device *vlan_dev)
+static void vlan_dev_set_rx_mode(struct net_device *vlan_dev)
{
dev_mc_sync(vlan_dev_info(vlan_dev)->real_dev, vlan_dev);
+ dev_unicast_sync(vlan_dev_info(vlan_dev)->real_dev, vlan_dev);
}
/*
int subclass = 0;
/* IFF_BROADCAST|IFF_MULTICAST; ??? */
- dev->flags = real_dev->flags & ~IFF_UP;
+ dev->flags = real_dev->flags & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI);
dev->iflink = real_dev->ifindex;
dev->state = (real_dev->state & ((1<<__LINK_STATE_NOCARRIER) |
(1<<__LINK_STATE_DORMANT))) |
return 0;
}
+static void vlan_dev_uninit(struct net_device *dev)
+{
+ struct vlan_priority_tci_mapping *pm;
+ struct vlan_dev_info *vlan = vlan_dev_info(dev);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vlan->egress_priority_map); i++) {
+ while ((pm = vlan->egress_priority_map[i]) != NULL) {
+ vlan->egress_priority_map[i] = pm->next;
+ kfree(pm);
+ }
+ }
+}
+
void vlan_setup(struct net_device *dev)
{
ether_setup(dev);
dev->change_mtu = vlan_dev_change_mtu;
dev->init = vlan_dev_init;
+ dev->uninit = vlan_dev_uninit;
dev->open = vlan_dev_open;
dev->stop = vlan_dev_stop;
dev->set_mac_address = vlan_dev_set_mac_address;
- dev->set_multicast_list = vlan_dev_set_multicast_list;
+ dev->set_rx_mode = vlan_dev_set_rx_mode;
+ dev->set_multicast_list = vlan_dev_set_rx_mode;
dev->change_rx_flags = vlan_dev_change_rx_flags;
dev->do_ioctl = vlan_dev_ioctl;
dev->destructor = free_netdev;