diff -P -C 2 -X /home/greear/exclude.list --recursive linux/include/linux/if_ether.h linux.dev/include/linux/if_ether.h
*** linux/include/linux/if_ether.h	Tue Sep  7 10:14:37 1999
--- linux.dev/include/linux/if_ether.h	Fri Feb 25 18:12:21 2000
***************
*** 33,36 ****
--- 33,66 ----
  #define ETH_FRAME_LEN	1514		/* Max. octets in frame sans FCS */
  
+ 
+ #ifdef 	CONFIG_VLAN_802_1Q
+ 
+ 
+ #define VLAN_ETH_ALEN	6		/* Octets in one ethernet addr	 */
+ #define VLAN_ETH_HLEN	18		/* Total octets in header.	 */
+ #define VLAN_ETH_ZLEN	64		/* Min. octets in frame sans FCS */
+ 
+ /* These could be bumped up by 4, but I'm not sure if all the underlying
+  * drivers would like it.
+  * UPDATE:  Bumping it by 4, as per Klika's suggestion below. --BLG
+  *
+  * According to 802.3ac, the packet can be 4 bytes longer. --Klika Jan
+  */
+ #define VLAN_ETH_DATA_LEN	1500	/* Max. octets in payload	 */
+ #define VLAN_ETH_FRAME_LEN	1518	/* Max. octets in frame sans FCS */
+ 
+ struct vlan_ethhdr 
+ {
+    unsigned char	h_dest[ETH_ALEN];	/* destination eth addr	*/
+    unsigned char	h_source[ETH_ALEN];	/* source ether addr	*/
+    unsigned short       h_vlan_proto;           /* Should always be 0x8100 */
+    unsigned short       h_vlan_TCI;             /* Encapsulates priority and VLAN ID */
+    unsigned short	h_vlan_encapsulated_proto; /* packet type ID field (or len) */
+ };
+ 
+ 
+ #endif
+ 
+ 
  /*
   *	These are the defined Ethernet Protocol ID's.
***************
*** 55,58 ****
--- 85,89 ----
  #define ETH_P_ATALK	0x809B		/* Appletalk DDP		*/
  #define ETH_P_AARP	0x80F3		/* Appletalk AARP		*/
+ #define ETH_P_802_1Q	0x8100          /* 802.1Q VLAN Extended Header  */
  #define ETH_P_IPX	0x8137		/* IPX over DIX			*/
  #define ETH_P_IPV6	0x86DD		/* IPv6 over bluebook		*/
diff -P -C 2 -X /home/greear/exclude.list --recursive linux/include/linux/if_vlan.h linux.dev/include/linux/if_vlan.h
*** linux/include/linux/if_vlan.h	Wed Dec 31 17:00:00 1969
--- linux.dev/include/linux/if_vlan.h	Sat Feb 26 22:10:09 2000
***************
*** 0 ****
--- 1,291 ----
+ /*
+  * VLAN		An implementation of 802.1Q VLAN tagging.
+  *
+  *  For some idea of the architecture, see the web page at (curently):
+  *    http://scry.wanfear.com/~greear/vlan.html
+  *
+  *  Also, I will put comments in this file to try to explain some general
+  *  architecture. --Ben
+  *
+  *
+  * Authors:	Ben Greear <greear@cyberhighway.net>
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  *
+  */
+ 
+ #ifndef _LINUX_IF_VLAN_H_
+ #define _LINUX_IF_VLAN_H_
+ 
+ #ifdef __KERNEL__
+ 
+ 
+ /* externally defined structs */
+ struct vlan_group;
+ struct net_device;
+ struct sk_buff;
+ struct packet_type;
+ struct vlan_collection;
+ 
+ /* early declaration of structs in this file */
+ struct vlan_port;
+ struct vlan_port_collection;
+ 
+ #include <linux/proc_fs.h> /* for proc_dir_entry */
+ #include <linux/netdevice.h>
+ 
+ /*  Find a VLAN device by the MAC address of it's Ethernet device, and
+  *  it's VLAN ID.  The default configuration is to have VLAN's scope
+  *  to be box-wide, so the MAC will be ignored.  The mac will only be
+  *  looked at if we are configured to have a seperate set of VLANs per
+  *  each MAC addressable interface.  Note that this latter option does
+  *  NOT follow the spec for VLANs, but may be useful for doing very
+  *  large quantities of VLAN MUX/DEMUX onto FrameRelay or ATM PVCs.
+  */
+ struct net_device *find_802_1Q_vlan_dev(const char* real_mac_addr,
+                                     unsigned short VID); /* vlan.c */
+ 
+ 
+ 
+ int dev_new_index(void); /* dev.c */
+ 
+ /* found in vlan_dev.c */
+ struct net_device_stats* vlan_dev_get_stats(struct net_device* dev);
+ int vlan_dev_rebuild_header(struct sk_buff *skb);
+ int vlan_dev_type_trans(struct sk_buff *skb, struct net_device *dev,
+                         struct packet_type* ptype);
+ int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev,
+                          unsigned short type, void *daddr, void *saddr,
+                          unsigned len);
+ int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev);
+ int vlan_dev_change_mtu(struct net_device *dev, int new_mtu);
+ int vlan_dev_open(struct net_device* dev);
+ int vlan_dev_stop(struct net_device* dev);
+ int vlan_dev_init(struct net_device* dev);
+ void vlan_dev_destruct(struct net_device* dev);
+ int vlan_dev_add_dev_by_vid(unsigned int vlan_id, const char* real_dev_name);
+ int vlan_dev_rem_dev_by_vid(unsigned int vlan_id, const char* real_dev_name);
+ int vlan_dev_add_dev(struct net_device* vlan_dev, struct net_device* real_dev);
+ int vlan_dev_rem_dev(struct net_device* vlan_dev, struct net_device* real_dev);
+ int vlan_dev_add_port(struct net_device* vlan_dev, struct vlan_port* port);
+ int vlan_dev_rem_port(struct net_device* vlan_dev, struct vlan_port* port);
+ int vlan_dev_contains_port(struct net_device* vlan_dev, struct vlan_port* port);
+ /*  I'm ignorant of these right now. --BLG
+ int vlan_dev_header_cache(struct neighbour *neigh, struct hh_cache *hh);
+ void vlan_dev_header_cache_update(struct hh_cache *hh, struct device *dev,
+                                   unsigned char * haddr);
+ */
+ void vlan_dev_copy_and_sum(struct sk_buff *dest, unsigned char *src,
+                            int length, int base);
+ int vlan_dev_set_dflt_vid(char* dev_name, unsigned short vid);
+ 
+ /* Found in vlan_port.c */
+ int vlan_port_add_vlan(struct vlan_port* port, struct net_device* vlan_dev,
+                        unsigned short flags);
+ int vlan_port_add_rx_allow(struct vlan_port* port, struct net_device* vlan_dev,
+                            unsigned short flags);
+ int vlan_port_add_tx_allow(struct vlan_port* port, struct net_device* vlan_dev,
+                            unsigned short flags);
+ int vlan_port_init(struct vlan_port* port);
+ int vlan_port_destroy(struct vlan_port* port); /* call this before deleting */
+ int vlan_port_is_empty(struct vlan_port* port);
+ 
+ void vlan_port_oper_equals(struct vlan_port* lhs, struct vlan_port* rhs);
+ int vlan_port_remove_vlan(struct vlan_port* port, struct net_device* vlan_dev);
+ 
+ int vlan_port_coll_init(struct vlan_port_collection* col);
+ struct vlan_port* vlan_port_coll_create_port(struct vlan_port_collection* col,
+                                              struct net_device* real_dev);
+ struct vlan_port* vlan_port_coll_find_port(struct vlan_port_collection* col,
+                                            struct net_device* real_dev);
+ int vlan_port_coll_remove_vlan(struct vlan_port_collection* col,
+                                struct net_device* vlan_dev);
+ int vlan_port_coll_rem_port(struct vlan_port_collection* col, struct vlan_port* vport);
+ 
+ int vlan_collection_add_vlan(struct vlan_collection* vc, unsigned short vlan_id,
+                              unsigned short flags);
+ int vlan_collection_remove_vlan(struct vlan_collection* vc,
+                                 struct net_device* vlan_dev);
+ int vlan_collection_remove_vlan_id(struct vlan_collection* vc, unsigned short vlan_id);
+ 
+ 
+ 
+ /* found in vlan.c */
+ /* Our listing of VLAN group(s) */
+ extern struct vlan_group* p802_1Q_vlan_list;
+ /* Our global list of ports */
+ extern struct vlan_port_collection vlan_ports;
+ 
+ 
+ #define VLAN_NAME "vlan"
+ 
+ /* If you change this, then you'll have to change:
+  * vlan_collection_get_hashed_idx() in vlan_port.c
+  */
+ #define VLAN_FILTER_HASH_TBL_LEN 64 /* hash on low 6 bits.*/
+ 
+ /* if this changes, algorithm will have to be reworked because this
+  * depends on completely exhausting the VLAN identifier space.  Thus
+  * it gives constant time lookup, but it many cases it wastes memory.
+  */
+ #define VLAN_GROUP_ARRAY_LEN 4096
+ 
+ struct vlan_group {
+    unsigned char real_mac[6]; /* The mac of the ethernet card the vlan is attached to. */
+    struct net_device* vlan_devices[VLAN_GROUP_ARRAY_LEN];
+    
+    struct vlan_group* next; /* the next in the list */
+ };
+ 
+ 
+ #define VLAN_ADDED_BY_USER     1
+ #define VLAN_ADDED_BY_CONTROL  2 /* non-user, like maybe Link layer control?? */
+ /** An entry in a vlan_collection that is associated with a given
+  * port.  Used to keep track of which VLAN's belong to which port(s).
+  * We have the flags because user-set entries cannot be over-ridden by
+  * link-layer control or another other automatic type of impetus.
+  */
+ struct vlan_entry {
+    unsigned short flags; /* see above */
+    unsigned short vlan_id;
+    struct vlan_entry* next;
+ };
+ 
+ /** Array of pointers to mac_entries. */
+ struct vlan_collection {
+    struct vlan_entry* vlan_table[VLAN_FILTER_HASH_TBL_LEN];
+ };
+ 
+ /* __Flags__ relating to the vlan ports */
+ #define VLAN_FLAG_ALLOW_802_3   1
+ #define VLAN_FLAG_ALLOW_802_1Q  2
+ #define VLAN_FLAG_IS_IN_USE     4
+ 
+ /* Poor man's enumeration to define __default action__ for vlan ports. */
+ #define VLAN_FILTER_DFLT_DENY   0
+ #define VLAN_FILTER_DFLT_ACCEPT 1
+ 
+ struct vlan_port {
+    struct net_device* real_dev;          /* the underlying device (port) */
+    struct vlan_port* next;        /* used for chaining (in a vlan_port_collection) */
+    struct vlan_collection rx_allow_group;
+    struct vlan_collection rx_deny_group;
+    struct vlan_collection tx_allow_group;
+    struct vlan_collection tx_deny_group;
+    unsigned short default_action;      /* See #defines above for possible values. */
+    unsigned short flags;   /* See #defines above for possible values. */
+    char name[25]; /* will be the name of the proc file: port_%p (address) */
+    struct proc_dir_entry* dent;    /* Holds the proc data for the port */
+ };
+ 
+ /** Singleton, holds all ports for entire kernel. Instantiated in
+  * vlan.c  vlan_ports are stored in no particular order, but I
+  * don't think we'll be searching this collection often.
+  */
+ struct vlan_port_collection {
+    int length;
+    struct vlan_port* ports; //pointer to array of length 'length'.
+ };
+ 
+ /**  Used to create a singly linked list. Ports may be shared by *many*
+  *  vlans.
+  */
+ struct vlan_port_node {
+    struct vlan_port* port;
+    struct vlan_port_node* next;
+ };
+ 
+ 
+ /* Holds information that makes sense if this device is a VLAN device. */
+ struct vlan_dev_info {
+    unsigned short vlan_id;        /*  The VLAN Identifier for this interface. */
+    struct vlan_port_node* ports; /* Info and TX/RX filters on ports this VLAN bridges. */ 
+    struct proc_dir_entry* dent;    /* Holds the proc data */
+    struct vlan_port_node* last_tx_port; /* Used for round-robin type of link aggregation */
+ };
+ 
+ /* inline functions */
+ 
+ /** Hash function for vlan_collection. */
+ static inline unsigned int vlan_collection_get_hashed_idx(unsigned short vlan_id) {
+    /* if VLAN_FILTER_HASH_TBL_LEN is modified, this must change
+     * as well.  For now it's defined to be: 64, ie 6 bits.
+     */
+    return (unsigned int)(vlan_id & 0x3F);
+ }
+ 
+ /** Returns TRUE if the collection contains the vlan specified by vlan_id. */
+ static inline int vlan_collection_has_vlan(struct vlan_collection* vc, unsigned short vlan_id) {
+    struct vlan_entry* ve = vc->vlan_table[vlan_collection_get_hashed_idx(vlan_id)];
+    for ( ; ve; ve = ve->next) {
+       if (ve->vlan_id == vlan_id) {
+          return 1;
+       }
+    }
+    return 0;
+ }
+ 
+ /* returns TRUE if egress should be allowed, FALSE otherwise */
+ static inline int vlan_port_test_egress(struct vlan_port* port, unsigned short vid) {
+    /* First, make sure underlying device is up */
+    if (port->real_dev->flags & IFF_UP) {
+       if (port->default_action == VLAN_FILTER_DFLT_DENY) {
+          return (vlan_collection_has_vlan(&(port->tx_allow_group), vid));
+       }
+       else if (port->default_action == VLAN_FILTER_DFLT_ACCEPT) {
+          return (!vlan_collection_has_vlan(&(port->tx_deny_group), vid));
+       }
+       else {
+          printk(KERN_ERR __FUNCTION__
+                 ": ERROR: Unknown default_action (%i) in vlan_port_test_egress!\n",
+                 port->default_action);
+          return 0;
+       }
+    }
+    else {
+       return 0;
+    }
+ }
+ 
+ 
+ #endif /* __KERNEL__ */
+ 
+ /**  These are the IOCTLs relating to the /proc/net/vlan/ * files.
+  * Not all may be supported at this time, and some may be primarily
+  * used for testing and obtaining non-standard access to kernel
+  * devices.
+  */
+ 
+ #define VLAN_IOCTL 0x52 /* TODO:  Can I just make these up??? */
+ 
+ enum vlan_ioctls {
+    ADD_VLAN_IOCTL = (VLAN_IOCTL << 8),
+    DEL_VLAN_IOCTL,
+    ADD_VLAN_TO_PORT_IOCTL,
+    REM_VLAN_FROM_PORT_IOCTL,
+    SET_DEFAULT_VLAN_ID_IOCTL,
+    HACK_SEND_PKT_IOCTL /* Send a packet to a specific device. */
+ }; /* vlan_ioctl enum */
+ 
+ 
+ struct vlan_ioctl_args {
+    char dev1[25];
+ 
+    union {
+       char dev2[25];
+       int VID;
+       int skb_len; /* used with the HACK_SEND* ioctls */
+    } u;
+ 
+    /* Below here used for the HACK_SEND* ioctls. */
+    unsigned short skb_data_type;
+    char dest_mac[6];
+    char src_mac[6];
+    char skb_data[2000];
+ };
+ 
+ 
+ #endif
diff -P -C 2 -X /home/greear/exclude.list --recursive linux/include/linux/netdevice.h linux.dev/include/linux/netdevice.h
*** linux/include/linux/netdevice.h	Sun Feb 20 21:48:57 2000
--- linux.dev/include/linux/netdevice.h	Fri Feb 25 18:12:21 2000
***************
*** 37,42 ****
--- 37,49 ----
  #ifdef CONFIG_NET_PROFILE
  #include <net/profile.h>
+ 
+ #if (defined(CONFIG_VLAN_802_1Q))
+ struct vlan_dev_info;
+ #endif
+ 
  #endif
  
+ 
+ 
  #define NET_XMIT_SUCCESS	0
  #define NET_XMIT_DROP		1	/* skb dropped			*/
***************
*** 167,176 ****
  	struct hh_cache *hh_next;	/* Next entry			     */
  	atomic_t	hh_refcnt;	/* number of users                   */
! 	unsigned short  hh_type;	/* protocol identifier, f.e ETH_P_IP */
  	int		hh_len;		/* length of header */
  	int		(*hh_output)(struct sk_buff *skb);
  	rwlock_t	hh_lock;
  	/* cached hardware header; allow for machine alignment needs.        */
! 	unsigned long	hh_data[16/sizeof(unsigned long)];
  };
  
--- 174,192 ----
  	struct hh_cache *hh_next;	/* Next entry			     */
  	atomic_t	hh_refcnt;	/* number of users                   */
! 	unsigned short  hh_type;	/* protocol identifier, f.e ETH_P_IP
!                                          *  NOTE:  For VLANs, this will be the
!                                          *  encapuslated type. --BLG
!                                          */
  	int		hh_len;		/* length of header */
  	int		(*hh_output)(struct sk_buff *skb);
  	rwlock_t	hh_lock;
  	/* cached hardware header; allow for machine alignment needs.        */
! 
! #ifdef 	CONFIG_VLAN_802_1Q /* we need 4 extra bytes for VLAN headers */
!   	unsigned long	hh_data[20/sizeof(unsigned long)];
! #else
!   	unsigned long	hh_data[16/sizeof(unsigned long)];
! #endif
! 
  };
  
***************
*** 366,369 ****
--- 382,398 ----
  	struct dst_entry	*fastpath[NETDEV_FASTROUTE_HMASK+1];
  #endif
+ 
+ #ifdef CONFIG_VLAN_802_1Q
+    /*  Holds information that makes sense if this device is a VLAN device. */
+         struct vlan_dev_info* vlan_dev;
+ 
+    /*  If this device is not a VLAN device, but we are bridging VLANs,
+     *  then we must have a default VID that can be assigned (if configured
+     *  to do so) to all frames from this device.  If zero, then we
+     *  WILL NOT turn the packet into a vlan packet.
+     */
+         unsigned short default_vid;  /* VID to be assigned by default. */
+ #endif
+ 
  };
  
diff -P -C 2 -X /home/greear/exclude.list --recursive linux/include/linux/skbuff.h linux.dev/include/linux/skbuff.h
*** linux/include/linux/skbuff.h	Sun Feb 20 21:48:33 2000
--- linux.dev/include/linux/skbuff.h	Fri Feb 25 18:11:49 2000
***************
*** 137,140 ****
--- 137,149 ----
         __u32           tc_index;               /* traffic control index */
  #endif
+ 
+ #if defined(CONFIG_VLAN_802_1Q)
+    /* I think this will help keep me from sending the packet
+     * back down the port that it arrived on.  Kind of a special
+     * case in the egress rules. (Used when bridging only.)  --BLG
+     * TODO:  Make sure this is used or deleted.
+     */
+    struct vlan_port* arriving_port;
+ #endif
  };
  
diff -P -C 2 -X /home/greear/exclude.list --recursive linux/net/802_1Q/Makefile linux.dev/net/802_1Q/Makefile
*** linux/net/802_1Q/Makefile	Wed Dec 31 17:00:00 1969
--- linux.dev/net/802_1Q/Makefile	Fri Feb 25 18:01:57 2000
***************
*** 0 ****
--- 1,26 ----
+ #
+ # Makefile for the Linux Ethernet layer.
+ #
+ # Note! Dependencies are done automagically by 'make dep', which also
+ # removes any old dependencies. DON'T put your own dependencies here
+ # unless it's something special (ie not a .c file).
+ #
+ # Note 2! The CFLAGS definition is now in the main makefile...
+ 
+ O_TARGET := 802_1Q.o
+ 
+ OBJS	:= vlan.o vlanproc.o vlan_dev.o vlan_port.o
+ 
+ ifeq ($(CONFIG_SYSCTL),y)
+ OBJS += sysctl_net_vlan.o
+ endif
+ 
+ 
+ ifdef CONFIG_NET
+ O_OBJS	:= $(OBJS) $(OBJ2)
+ endif
+ 
+ include $(TOPDIR)/Rules.make
+ 
+ tar:
+ 	tar -cvf /dev/f1 .
diff -P -C 2 -X /home/greear/exclude.list --recursive linux/net/802_1Q/sysctl_net_vlan.c linux.dev/net/802_1Q/sysctl_net_vlan.c
*** linux/net/802_1Q/sysctl_net_vlan.c	Wed Dec 31 17:00:00 1969
--- linux.dev/net/802_1Q/sysctl_net_vlan.c	Fri Feb 25 18:01:57 2000
***************
*** 0 ****
--- 1,18 ----
+ /* 
+  * sysctl_net_vlan.c: sysctl interface to net Ethernet VLAN subsystem.
+  *
+  * Begun Dec 20, 1998, Ben Greear
+  *
+  * TODO:  What, if anything, should this do??
+  */
+ 
+ #ifdef 	CONFIG_VLAN_802_1Q
+ 
+ #include <linux/mm.h>
+ #include <linux/sysctl.h>
+ 
+ ctl_table ether_vlan_table[] = {
+ 	{0}
+ };
+ 
+ #endif
diff -P -C 2 -X /home/greear/exclude.list --recursive linux/net/802_1Q/vlan.c linux.dev/net/802_1Q/vlan.c
*** linux/net/802_1Q/vlan.c	Wed Dec 31 17:00:00 1969
--- linux.dev/net/802_1Q/vlan.c	Sat Feb 26 22:03:52 2000
***************
*** 0 ****
--- 1,507 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		Ethernet-type device handling.
+  *
+  * Version:	@(#)vlan.c	started	12/21/98
+  *                              
+  * Authors:	Ben Greear <greear@cyberhighway.net>, <greearb@agcs.com>
+  * 
+  * Fixes:
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ 
+ #include <asm/uaccess.h> /* for copy_from_user */
+ #include <linux/module.h>
+ #include <linux/netdevice.h>
+ #include <linux/skbuff.h>
+ #include <net/datalink.h>
+ #include <linux/mm.h>
+ #include <linux/in.h>
+ #include <linux/init.h>
+ #include <net/p8022.h>
+ #include <net/arp.h>
+ #include <linux/rtnetlink.h>
+ 
+ #include <linux/if_vlan.h>
+ #include "vlan.h"
+ #include "vlanproc.h"
+ 
+ 
+ extern int eth_header_parse(struct sk_buff *skb, unsigned char *haddr); /* eth.c */
+ 
+ extern struct Qdisc noqueue_qdisc;
+ 
+ /* Global VLAN variables */
+ 
+ /* Our listing of VLAN group(s) */
+ struct vlan_group *p802_1Q_vlan_list = NULL;
+ 
+ /* holds all the vlan ports.  Vlan devices will have pointers
+  * into this collection, but will not 'own' the ports.
+  */
+ struct vlan_port_collection vlan_ports;
+ 
+ 
+ static char vlan_fullname[] = "802.1Q VLAN Support";
+ static unsigned int vlan_version = 0;
+ static unsigned int vlan_release = 10;
+ static char vlan_copyright[] = " Ben Greear <greearb@candelatech.com>";
+ 
+ unsigned char vlan_configured_multiple_groups = 0; /* boolean */
+ 
+ 
+ static struct packet_type vlan_packet_type = 
+ {
+    0,      /* MUTTER ntohs(ETH_P_802_1Q),*/
+    NULL,
+    vlan_dev_type_trans, /* VLAN receive method */
+    NULL,
+    NULL,
+ };
+ 
+ /* End of global variables definitions. */
+ 
+ #ifdef MODULE
+ 
+ /*
+  *	Kernel Loadable Module Entry Points
+  *
+  *	Module 'insert' entry point.
+  *	o print announcement
+  *	o initialize static data
+  *	o create /proc/net/vlan directory and static entries
+  *
+  *	Return:	0	Ok
+  *		< 0	error.
+  *	Context:	process
+  */
+ int init_module	(void) {
+    printk(VLAN_INF __FUNCTION__);
+ 
+    vlan_proto_init(NULL);
+    return 0;
+ }
+ 
+ /*
+  *	Module 'remove' entry point.
+  *	o delete /proc/net/router directory and static entries.
+  */ 
+ void cleanup_module (void) {
+    vlan_proto_cleanup(); // TODO: Define this so modules work.  Clean up ports.
+ }
+ 
+ #else
+ 
+ 
+ /** Non-module init entry point. */
+ void __init vlan_system_init(void) {
+    printk(VLAN_INF __FUNCTION__);
+ 
+    /* protocol initialization */
+    vlan_proto_init(NULL);
+ 
+ }
+ #endif
+ 
+ /*
+  * Function vlan_proto_init (pro)
+  *
+  *    Initialize VLAN protocol layer, 
+  *
+  */
+ void vlan_proto_init(struct net_proto *pro) {
+ 
+    int err;
+    printk(VLAN_INF "%s v%u.%u %s\n",
+           vlan_fullname, vlan_version, vlan_release, vlan_copyright);
+ 
+    vlan_port_coll_init(&vlan_ports);
+ 
+    /* proc file system initialization */
+    err = vlan_proc_init();
+    if (err < 0) {
+       printk(KERN_ERR __FUNCTION__
+              "%s: can't create entry in proc filesystem!\n", VLAN_NAME);
+    }
+ 
+    /* network byte order!! */
+    vlan_packet_type.type = htons(ETH_P_802_1Q);
+    dev_add_pack(&vlan_packet_type);
+    printk(VLAN_INF "%s Initialization complete.\n", VLAN_NAME);
+ }
+ 
+ 
+ 
+ /**  At one time, I was going to let each Physical device have
+  * it's own 4096 VLANs.  The spec says I should do otherwise.
+  * However, for my own personal amusement, I'm going to give
+  * one the ability to configure this.  The default will be to
+  * just return the first one (as per the spec).
+  */
+ struct vlan_group* vlan_find_group(const char* mac) {
+    struct vlan_group* grp = NULL;
+ 
+    if (vlan_configured_multiple_groups && mac) {
+       for (grp = p802_1Q_vlan_list;
+            ((grp != NULL) && (memcmp(mac, grp->real_mac, ETH_ALEN)));
+            grp = grp->next) {
+          ;
+       } /* for */
+          
+       return grp;
+    }
+    else {
+       return p802_1Q_vlan_list;
+    }
+ }
+ 
+ /*  Find the protocol handler.  Assumes VID < 0xFFF.
+  */
+ struct net_device *find_802_1Q_vlan_dev(const char* real_mac_addr,
+                                     unsigned short VID) {
+ 
+    struct vlan_group* grp = vlan_find_group(real_mac_addr);
+ 
+    /*  When here, we have found the correct group, if it exists. */
+ 
+    if (grp) { /* then we found one */
+       return grp->vlan_devices[VID]; /* return the vlan device */
+    }//if
+    
+    return NULL;
+ }/* find_802_1Q_vlan_client */
+ 
+ 
+ /** This method will explicitly do a dev_put on the device if do_dev_put
+  * is TRUE.  This gets around a difficulty with reference counting, and
+  * the unregister-by-name (below). --Ben
+  */
+ int unregister_802_1Q_vlan_dev(const char* mac, unsigned short vlan_id,
+                                int do_dev_put) {
+    struct vlan_group* grp;
+    struct net_device* dev = NULL;
+ 
+ #ifdef VLAN_DEBUG
+    printk(VLAN_DBG __FUNCTION__ ": VID: %i\n", vlan_id);
+ #endif
+    
+    /* sanity check */
+    if ((vlan_id >= 0xFFF) || (vlan_id <= 0)) {
+       return -EINVAL;
+    }
+    
+    grp = vlan_find_group(mac);
+    /*  When here, we have found the correct group, if it exists. */
+ 
+    if (grp) {
+       dev = grp->vlan_devices[vlan_id];
+       if (dev) {
+ 
+          /* Remove proc entry */
+          vlan_proc_rem_dev(dev);
+          
+          /* remove it from all the vlan ports, will release reference to
+           * the underlying device
+           */
+          vlan_port_coll_remove_vlan(&vlan_ports, dev);
+          
+          /* take it out of our own structures */
+          grp->vlan_devices[vlan_id] = NULL;
+ 
+          /* Take it out of the global list of devices.
+           *  NOTE:  This deletes dev, don't access it again!!
+           */
+ 
+          if (do_dev_put) {
+             dev_put(dev);
+          }
+          
+          rtnl_lock();
+          unregister_netdevice(dev);
+          rtnl_unlock();
+          
+       }/* if */
+    }/* if */
+    return 0;
+ }/* unregister vlan device */
+ 
+ 
+ 
+ int unregister_802_1Q_vlan_device(const char* vlan_IF_name) {
+    struct net_device* dev = NULL;
+ 
+ #ifdef VLAN_DEBUG
+    printk(VLAN_DBG __FUNCTION__ ": unregister VLAN by name, name -:%s:-\n",
+             vlan_IF_name);
+ #endif
+    
+    dev = dev_get_by_name(vlan_IF_name);
+ 
+    if (dev && dev->vlan_dev) {
+       return unregister_802_1Q_vlan_dev(dev->dev_addr,
+                                         (unsigned short)(dev->vlan_dev->vlan_id),
+                                         1 /* do dev_put */);
+    }
+    else {
+ #ifdef VLAN_DEBUG
+       printk(VLAN_DBG __FUNCTION__ ": WARNING: Could not find dev\n");
+ #endif
+          
+       if (dev) {
+          dev_put(dev); /* release it */
+       }
+       return -EINVAL;
+    }
+ }/* unregister vlan device */
+ 
+ 
+ /*
+   TODO:  This for modules or something?? --BLG
+   
+   EXPORT_SYMBOL(register_802_1Q_vlan_device);
+   EXPORT_SYMBOL(unregister_802_1Q_vlan_device);
+ 
+ */
+ 
+ /*  Attach a VLAN device to a mac address (ie Ethernet Card).
+  *  Returns the device that was created, or NULL if there was
+  *  an error of some kind.
+  */
+ struct net_device *register_802_1Q_vlan_device(const char* eth_IF_name,
+                                                unsigned short VLAN_ID) {
+    struct vlan_group* grp;
+    struct net_device *new_dev;
+    struct net_device *real_dev; /* the ethernet device */
+    char* mac;
+    int malloc_size = 0;
+    int retval = 0;
+    
+ #ifdef VLAN_DEBUG
+    printk(VLAN_DBG __FUNCTION__ ": if_name -:%s:-  vid: %i\n",
+           eth_IF_name, VLAN_ID);
+ #endif
+    
+    /* find the device relating to eth_IF_name.
+     * TODO:  Make sure it's an ethernet device. */
+    real_dev = dev_get_by_name(eth_IF_name);
+ 
+    if (real_dev != NULL) {
+       /* printk(KERN_ALERT "Found real_dev"); */
+             
+       if ((VLAN_ID > 0) && (VLAN_ID < 0xFFF)) {
+ 
+          /* printk(KERN_ALERT "VID is in range"); */
+ 
+          mac = real_dev->dev_addr;
+ 
+          if (find_802_1Q_vlan_dev(mac, VLAN_ID)) {
+             /* was already registered. */
+             printk(VLAN_DBG __FUNCTION__ ": ALREADY had VLAN registered\n");
+             dev_put(real_dev);
+             return NULL;
+          }
+ 
+          malloc_size = (sizeof(struct net_device));
+ 
+          new_dev = (struct net_device*) kmalloc(malloc_size, GFP_KERNEL);
+          VLAN_MEM_DBG("net_device malloc, addr: %p  size: %i\n", new_dev, malloc_size);
+          
+          if (new_dev != NULL) {
+             /* printk(KERN_ALERT "Got a new device.."); */
+             
+             memset(new_dev, 0, malloc_size); /* zero everything out */
+ 
+             /* set us up to not use a Qdisc, as the underlying Hardware device
+              * can do all the queueing we could want.
+              */
+             new_dev->qdisc_sleeping = &noqueue_qdisc;
+ 
+             /* TODO: Understand the implications of 'new_style'. */
+             new_dev->new_style = 0;
+             
+             /* Gotta set up the fields for the device. */
+             new_dev->name = (char*)(kmalloc(IFNAMSIZ + 1, GFP_KERNEL));
+             VLAN_MEM_DBG("new_dev->name malloc, addr: %p  size: %i\n", new_dev->name, IFNAMSIZ + 1);
+             
+             if (new_dev->name) {
+                memset(new_dev->name, 0, IFNAMSIZ + 1); /* zero everything out */
+             }
+             else {
+                kfree(new_dev);
+                VLAN_FMEM_DBG("new_dev free, addr: %p\n", new_dev);
+                dev_put(real_dev);
+                return NULL;
+             }
+ 
+ #ifndef USE_VID_IN_DEV_NAME
+             /*  This is the old way, VID has no corelation with name. */
+             if (dev_alloc_name(new_dev, "vlan%.4i") < 0) { /* if couldn't allocate more */
+                /* then give up */
+                printk(KERN_ALERT "VLAN REGISTER: Couldn't alloc a name.\n");
+                kfree(new_dev->name);
+                VLAN_FMEM_DBG("new_dev->name free, addr: %p\n", new_dev->name);
+                kfree(new_dev);
+                VLAN_FMEM_DBG("new_dev free, addr: %p\n", new_dev);
+                dev_put(real_dev);
+                return NULL;
+             }
+ #else
+             /* Put our VID in the name. */
+             sprintf(new_dev->name, "vlan%.4i", VLAN_ID);
+ #endif
+                     
+ #ifdef VLAN_DEBUG
+             printk(VLAN_DBG "Allocated new name -:%s:-\n", new_dev->name);
+ #endif
+             /* set up method calls */
+             new_dev->init = vlan_dev_init;
+             new_dev->destructor = vlan_dev_destruct;
+             
+             /* new_dev->ifindex = 0;  it will be set when added to
+              * the global list.
+              * iflink is set as well. */
+             
+             new_dev->get_stats = vlan_dev_get_stats;
+             
+             /* IFF_BROADCAST|IFF_MULTICAST; ??? */
+             new_dev->flags = real_dev->flags;
+             new_dev->flags &= ~IFF_UP;
+             
+             /* need 4 bytes for extra VLAN header info, hope
+              * underlying device can handle it. */
+             new_dev->mtu = real_dev->mtu;
+             
+             new_dev->type = real_dev->type; /* TODO: is this true? */
+ 
+             /* Regular ethernet + 4 bytes (18 total). */
+             new_dev->hard_header_len = VLAN_ETH_HLEN;
+ 
+             new_dev->priv = kmalloc(sizeof(struct net_device_stats),
+                                     GFP_KERNEL);
+             VLAN_MEM_DBG("new_dev->priv malloc, addr: %p  size: %i\n", new_dev->priv,
+                          sizeof(struct net_device_stats));
+             
+             if (new_dev->priv) {
+                memset(new_dev->priv, 0, sizeof(struct net_device_stats));
+             }//if
+ 
+             memcpy(new_dev->broadcast, real_dev->broadcast, real_dev->addr_len);
+             memcpy(new_dev->dev_addr, real_dev->dev_addr, real_dev->addr_len);
+             new_dev->addr_len = real_dev->addr_len;
+ 
+             new_dev->open = vlan_dev_open;
+             new_dev->stop = vlan_dev_stop;
+             new_dev->hard_header = vlan_dev_hard_header;
+             /*new_dev->hard_header_cache = vlan_header_cache;*/
+             /*new_dev->header_cache_update = vlan_header_cache_update;*/
+             new_dev->hard_start_xmit = vlan_dev_hard_start_xmit;
+             new_dev->rebuild_header = vlan_dev_rebuild_header;
+             new_dev->hard_header_parse = eth_header_parse; /* trivial. */
+ 
+             new_dev->vlan_dev = (struct vlan_dev_info*) kmalloc(sizeof(struct vlan_dev_info),
+                                                                        GFP_KERNEL);
+             VLAN_MEM_DBG("new_dev->vlan_dev malloc, addr: %p  size: %i\n", new_dev->vlan_dev,
+                          sizeof(struct vlan_dev_info));
+             if (new_dev->vlan_dev == NULL) {
+                kfree(new_dev->priv);
+                VLAN_FMEM_DBG("new_dev->priv free, addr: %p\n", new_dev->priv);
+                kfree(new_dev->name);
+                VLAN_FMEM_DBG("new_dev->name free, addr: %p\n", new_dev->name);
+                kfree(new_dev);
+                VLAN_FMEM_DBG("new_dev free, addr: %p\n", new_dev);
+                dev_put(real_dev);
+                return NULL;
+             }
+             else {
+                memset(new_dev->vlan_dev, 0, sizeof(struct vlan_dev_info));
+                new_dev->vlan_dev->vlan_id = VLAN_ID; /* 1 through 0xFFF */
+                new_dev->vlan_dev->ports = NULL;
+                new_dev->vlan_dev->dent = NULL;
+             }
+             
+             /* So, got the sucker initialized, now lets place it into our local
+              * structure.
+              */
+ 
+             grp = vlan_find_group(real_dev->dev_addr);
+ 
+             /*  When here, we have found the correct group, if it exists. */
+ 
+             if (!grp) { /* need to add a new group */
+                /* printk(VLAN_DBG "VLAN REGISTER: "
+                   "Need to add new vlan group.\n");*/
+ 
+                grp = kmalloc(sizeof(struct vlan_group), GFP_KERNEL);
+                VLAN_MEM_DBG("grp malloc, addr: %p  size: %i\n", grp, sizeof(struct vlan_group));
+ 
+                if (grp) {
+                   printk(KERN_ALERT "VLAN REGISTER:  Allocated new group.\n");
+                   memset(grp, 0, sizeof(struct vlan_group));
+                   memcpy(grp->real_mac, real_dev->dev_addr, ETH_ALEN);
+                   grp->next = p802_1Q_vlan_list;
+                   p802_1Q_vlan_list = grp;
+                }
+                else {
+                   kfree(new_dev->name);
+                   VLAN_FMEM_DBG("new_dev->name free, addr: %p\n", new_dev->name);
+                   kfree(new_dev->priv);
+                   VLAN_FMEM_DBG("new_dev->priv free, addr: %p\n", new_dev->priv);
+                   kfree(new_dev);
+                   VLAN_FMEM_DBG("new_dev free, addr: %p\n", new_dev);
+                   dev_put(real_dev);
+                   return NULL;
+                }
+             }/* if */
+             
+             grp->vlan_devices[VLAN_ID] = new_dev;
+ 
+             /* TODO: have to be careful deleting real devices now. */
+             /* Add the 'real_dev' to the vlan.  This may mean that a new
+              * port is added as well.
+              */
+             if ((retval = vlan_dev_add_dev(new_dev, real_dev)) < 0) {
+                printk(KERN_ALERT "ERROR:  vlan_dev_add_dev failed w/error: %i.\n",
+                       retval);
+                kfree(new_dev->name);
+                VLAN_FMEM_DBG("new_dev->name free, addr: %p\n", new_dev->name);
+                kfree(new_dev->priv);
+                VLAN_FMEM_DBG("new_dev->priv free, addr: %p\n", new_dev->priv);
+                kfree(new_dev);
+                VLAN_FMEM_DBG("new_dev free, addr: %p\n", new_dev);
+                dev_put(real_dev);
+                return NULL;
+             }
+             
+             /*  Now, add it to the global list of devices. */
+             /* printk(KERN_ALERT "Registering new device."); */
+ 
+             /* TODO: Please check this: RTNL   --Ben */
+             rtnl_lock();
+             register_netdevice(new_dev);
+             rtnl_unlock();
+             
+             vlan_proc_add_dev(new_dev); /* create it's proc entry */
+ 
+             /* NOTE:  Since the VLAN device depends on the underlying ethernet
+              * device, we should have a reference to it.  However, I'm letting
+              * the port_collection take care of that, so we'll release it
+              * here.
+              */
+             dev_put(real_dev);
+             return new_dev;
+          }
+       }//if
+       /* If we are here, then something is wrong, so release the real_dev
+        */
+       dev_put(real_dev);
+    }//if we found real_dev
+ 
+    return NULL;
+ }/* register (create) VLAN device */
diff -P -C 2 -X /home/greear/exclude.list --recursive linux/net/802_1Q/vlan.h linux.dev/net/802_1Q/vlan.h
*** linux/net/802_1Q/vlan.h	Wed Dec 31 17:00:00 1969
--- linux.dev/net/802_1Q/vlan.h	Sat Feb 26 22:10:42 2000
***************
*** 0 ****
--- 1,47 ----
+ #ifndef __BEN_VLAN_802_1Q_INC__
+ #define __BEN_VLAN_802_1Q_INC__
+ 
+ #include <linux/if_vlan.h>
+ 
+ /* comment this out if you like the old way --Ben
+  * (rls 0.0.9)
+  */
+ #define USE_VID_IN_DEV_NAME
+ 
+ /*  Uncomment this if you want debug traces to be shown. */
+ /* #define VLAN_DEBUG */
+ 
+ 
+ #define VLAN_ERR KERN_ERR
+ #define VLAN_INF KERN_ALERT
+ #define VLAN_DBG KERN_ALERT /* change these... to debug, having a hard time
+                              * changing the log level at run-time..for some reason.
+                              */
+ 
+ /*
+ 
+ These I use for memory debugging.  I feared a leak at one time, but
+ I never found it..and the problem seems to have dissappeared.  Still,
+ I'll bet they might prove useful again... --Ben
+ 
+ #define VLAN_MEM_DBG(x, y, z) printk(VLAN_DBG __FUNCTION__ ":  "  x, y, z);
+ #define VLAN_FMEM_DBG(x, y) printk(VLAN_DBG __FUNCTION__  ":  " x, y);
+ */
+ 
+ /* This way they don't do anything! */ 
+ #define VLAN_MEM_DBG(x, y, z)
+ #define VLAN_FMEM_DBG(x, y)
+ 
+ 
+ extern unsigned char vlan_configured_multiple_groups; /* boolean */
+ 
+ 
+ /* Add some headers for the public VLAN methods. */
+ int unregister_802_1Q_vlan_device(const char* vlan_IF_name);
+ struct net_device *register_802_1Q_vlan_device(const char* eth_IF_name,
+                                                unsigned short VID);
+ 
+ void vlan_system_init(void);
+ void vlan_proto_init(struct net_proto *pro);
+ 
+ #endif
diff -P -C 2 -X /home/greear/exclude.list --recursive linux/net/802_1Q/vlan_dev.c linux.dev/net/802_1Q/vlan_dev.c
*** linux/net/802_1Q/vlan_dev.c	Wed Dec 31 17:00:00 1969
--- linux.dev/net/802_1Q/vlan_dev.c	Sat Feb 26 21:24:12 2000
***************
*** 0 ****
--- 1,775 ----
+ /*
+  * INET		An implementation of the TCP/IP protocol suite for the LINUX
+  *		operating system.  INET is implemented using the  BSD Socket
+  *		interface as the means of communication with the user level.
+  *
+  *		Ethernet-type device handling.
+  *
+  * Version:	@(#)vlan_dev.c	Started	3/29/99
+  *
+  * Authors:	Ben Greear <greear@cyberhighway.net>, <greearb@agcs.com>
+  * 
+  * Fixes:
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ 
+ #include <asm/uaccess.h> /* for copy_from_user */
+ #include <linux/module.h>
+ #include <linux/netdevice.h>
+ #include <linux/skbuff.h>
+ #include <net/datalink.h>
+ #include <linux/mm.h>
+ #include <linux/in.h>
+ #include <linux/init.h>
+ #include <net/p8022.h>
+ #include <net/arp.h>
+ #include "vlan.h"
+ #include "vlanproc.h"
+ #include <linux/if_vlan.h>
+ #include <net/ip.h>
+ #include <asm/checksum.h>
+ 
+ 
+ struct net_device_stats* vlan_dev_get_stats(struct net_device* dev) {
+    return (struct net_device_stats*)(dev->priv);
+ }
+ 
+ 
+ /*
+  *	Rebuild the Ethernet MAC header. This is called after an ARP
+  *	(or in future other address resolution) has completed on this
+  *	sk_buff. We now let ARP fill in the other fields.
+  *
+  *	This routine CANNOT use cached dst->neigh!
+  *	Really, it is used only when dst->neigh is wrong.
+  *
+  * TODO:  This needs a checkup, I'm ignorant here. --BLG
+  */
+ int vlan_dev_rebuild_header(struct sk_buff *skb) {
+ 
+    struct net_device *dev = skb->dev;
+    struct vlan_ethhdr *veth = (struct vlan_ethhdr*)(skb->data);
+ 
+    switch (veth->h_vlan_encapsulated_proto)
+       {
+ #ifdef CONFIG_INET
+       case __constant_htons(ETH_P_IP):
+ 
+          /* TODO:  Confirm this will work with VLAN headers... */
+          return arp_find(veth->h_dest, skb);
+ #endif	
+       default:
+          printk(VLAN_DBG
+                 "%s: unable to resolve type %X addresses.\n", 
+                 dev->name, (int)veth->h_vlan_encapsulated_proto);
+          
+          memcpy(veth->h_source, dev->dev_addr, ETH_ALEN);
+          break;
+       }/* switch */
+ 
+    return 0;
+ }/* vlan_dev_rebuild_header */
+ 
+ 
+ 
+ /*
+  *	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.
+  */
+ int vlan_dev_type_trans(struct sk_buff *skb, struct net_device *dev,
+                         struct packet_type* ptype) {
+    unsigned char* rawp = NULL;
+    struct vlan_ethhdr *veth = (struct vlan_ethhdr*)(skb->mac.ethernet);
+    unsigned short amt_to_pull = 0;
+    unsigned short vid = 0;
+    unsigned char is_1q_skb = 1;
+    struct net_device_stats* stats;
+    
+    /* Do we have a VLAN packet?  If not, then it was sent to use because
+     * the port had a default vlan ID.
+     */
+    if (veth->h_vlan_proto != __constant_htons(ETH_P_802_1Q)) {
+       amt_to_pull = ETH_HLEN;
+       vid = dev->default_vid;
+       is_1q_skb = 0; /* FALSE */
+    }
+    else {
+       amt_to_pull = VLAN_ETH_HLEN;
+       vid = ((unsigned short)(ntohs(veth->h_vlan_TCI)) & 0xFFF);
+    }
+ 
+ #ifdef VLAN_DEBUG
+    printk(VLAN_DBG __FUNCTION__ ": skb: %p is_1Q_Protocol: %i vlan_id: %hx\n",
+           skb, is_1q_skb, vid);
+ #endif
+    
+    /*  Ok, we will find the correct VLAN device, strip the header,
+        and then go on as usual.
+        TODO:  Take the priority bits into consideration?? --BLG
+    */
+    
+    /* we have 12 bits of vlan ID. */
+    /* If it's NULL, we will tag it to be junked below */
+    skb->dev = find_802_1Q_vlan_dev(dev->dev_addr, vid);
+ 
+    if (!skb->dev) {
+ #ifdef VLAN_DEBUG
+       printk(VLAN_DBG __FUNCTION__ ": ERROR:  No net_device for VID: %i\n",
+              (unsigned int)(vid));
+ #endif
+       kfree_skb(skb);
+       return -1;
+    }
+ 
+    /* Bump the rx counters for the VLAN device. */
+    stats = (struct net_device_stats*)(skb->dev->priv);
+    stats->rx_packets++;
+    stats->rx_bytes += skb->len;
+       
+    /* The underlying device SHOULD NOT PULL THE MAC BYTES OFF.
+       (it doesn't seem to.)
+     */
+    skb_pull(skb, amt_to_pull); /* take off the VLAN header */
+    
+    
+    /*  VLAN and regular Ethernet headers have the addresses in the same place.
+     *  TODO:  Add code to deal with VLAN control packets?? --BLG
+     *         Is there such a thing??
+     */
+    if (*(veth->h_dest) & 1) {
+       stats->multicast++;
+       if (memcmp(veth->h_dest, dev->broadcast, ETH_ALEN) == 0)
+          skb->pkt_type = PACKET_BROADCAST;
+       else
+          skb->pkt_type = PACKET_MULTICAST;
+    }
+ 	
+    /*
+     *	This ALLMULTI check should be redundant by 1.4
+     *	so don't forget to remove it.
+     *
+     *	Seems, you forgot to remove it. All silly devices
+     *	seems to set IFF_PROMISC.
+     */
+    
+    else if (dev->flags & (IFF_PROMISC/*|IFF_ALLMULTI*/)) {
+       if (memcmp(veth->h_dest, dev->dev_addr, ETH_ALEN) != 0)
+          skb->pkt_type = PACKET_OTHERHOST;
+    }
+ 
+    if (!is_1q_skb) {
+       /*  NOT a VLAN packet, which is still a valid receipt to a VLAN
+        * device, it will just use the default VID, assigned above.
+        */
+       if (ntohs(veth->h_vlan_proto) >= 1536) {
+ 
+          skb->protocol = veth->h_vlan_proto;
+          /* place it back on the queue to be handled by true layer 3 protocols.
+           */
+          netif_rx(skb);
+          return 0;
+       }
+    }
+    else {
+       /*  Was a VLAN packet, grab the encapsulated protocol, which the layer
+        * three protocols care about.
+        */
+       if (ntohs(veth->h_vlan_encapsulated_proto) >= 1536) {
+ 
+          skb->protocol = veth->h_vlan_encapsulated_proto;
+          /* place it back on the queue to be handled by true layer 3 protocols.
+           */
+          netif_rx(skb);
+          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 = __constant_htons(ETH_P_802_3);
+       /* place it back on the queue to be handled by true layer 3 protocols.
+        */
+       netif_rx(skb);
+       return 0;
+    }
+ 		
+    /*
+     *	Real 802.2 LLC
+     */
+    skb->protocol = __constant_htons(ETH_P_802_2);
+    /* place it back on the queue to be handled by upper layer protocols.
+     */
+    netif_rx(skb);
+    return 0;
+ }
+ 
+ 
+ /*
+  *	 Create the Ethernet VLAN MAC header for an arbitrary protocol layer 
+  *
+  *	saddr=NULL	means use device source address
+  *	daddr=NULL	means leave destination address (eg unresolved arp)
+  *
+  *  This is called when the SKB is moving down the stack towards the
+  *  physical devices.
+  */
+ int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev,
+                          unsigned short type, void *daddr, void *saddr,
+                          unsigned len) {
+    struct vlan_ethhdr *veth;
+    unsigned short veth_TCI = 0;
+         
+ #ifdef VLAN_DEBUG
+    printk(VLAN_DBG __FUNCTION__ ": skb: %p type: %hx len: %x vlan_id: %hx, daddr: %p\n",
+           skb, type, len, dev->vlan_dev->vlan_id, daddr);
+ #endif
+    
+    veth = (struct vlan_ethhdr*)skb_push(skb, VLAN_ETH_HLEN);
+ 
+    /* build the four bytes that make this a VLAN header. */
+ 
+    /* first, the ethernet type */
+    veth->h_vlan_proto = __constant_htons(ETH_P_802_1Q);
+ 
+    /* 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)
+     *
+     */
+    veth_TCI = dev->vlan_dev->vlan_id;
+    veth_TCI |= ((skb->priority << 13) & 0xE000) ; /* grab low 3 bits of skb->priority for
+                                                    * the VLAN priority.
+                                                    */
+    veth->h_vlan_TCI = htons(veth_TCI);
+            
+    /* Rest should be the same as a normal header. */
+    /* 
+     *  Set the protocol type. For a packet of type ETH_P_802_3 we put the length
+     *  in here instead. It is up to the 802.2 layer to carry protocol information.
+     *
+     */
+ 
+    if (type != ETH_P_802_3) 
+       veth->h_vlan_encapsulated_proto = htons(type);
+    else
+       veth->h_vlan_encapsulated_proto = htons(len);
+ 
+    /*
+     *	Set the source hardware address. 
+     */
+            
+    if (saddr)
+       memcpy(veth->h_source, saddr, ETH_ALEN);
+    else
+       memcpy(veth->h_source, dev->dev_addr, ETH_ALEN);
+    
+    /*
+     *	Anyway, the loopback-device should never use this function... 
+     *   This is especially true with VLAN's. --BLG
+     */
+    
+    if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
+       memset(veth->h_dest, 0, ETH_ALEN);
+       return (VLAN_ETH_HLEN); /* was: dev->hard_header_len */
+    }
+    
+    if (daddr) {
+       memcpy(veth->h_dest, daddr, ETH_ALEN);
+       return (VLAN_ETH_HLEN); /* was: dev->hard_header_len */
+    }
+    
+    return -(VLAN_ETH_HLEN); /* was: dev->hard_header_len */
+    
+ } /* vlan_hard_header, put on the VLAN hardware header */
+ 
+ 
+ int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) {
+    struct net_device_stats* stats = (struct net_device_stats*)(dev->priv);
+    unsigned short vid = dev->vlan_dev->vlan_id;
+ 
+    struct vlan_port_node* tx_port = dev->vlan_dev->last_tx_port;
+    struct vlan_port_node* orig_tx_port = tx_port;
+    
+ #ifdef VLAN_DEBUG
+    printk(VLAN_DBG __FUNCTION__ ": skb: %p\n", skb);
+ #endif
+ 
+    /* No longer copying pkts here.  Now, I'm going to allow a type of link
+     * aggregation where alternate pkts are sent on alternating vlan ports.
+       
+     * TODO:  Should I check this?      if (skb->arriving_port != tx_port->port) {
+     */
+ 
+    /* First, short-circuit this whole mess if we only have one port */
+    if (dev->vlan_dev->ports && (dev->vlan_dev->ports->next == NULL)) {
+       skb->dev = dev->vlan_dev->ports->port->real_dev;
+       /* 
+        * printk(VLAN_DBG __FUNCTION__ ": about to dev_queue_xmit (top)\n");
+        */
+       dev_queue_xmit(skb);
+       stats->tx_packets++; /* for statics only */
+       stats->tx_bytes += skb->len;
+       return 0;
+    }    
+    else { //might be doing link aggregation
+    
+       /** Find next tx_port. */
+       if (tx_port == NULL) {
+          if ((tx_port = dev->vlan_dev->ports) == NULL) {
+             printk(VLAN_ERR __FUNCTION__ ": no ports configured for VLAN: %s\n", dev->name);
+             /* No ports configured for this VLAN. */
+             goto no_vlan_to_tx_on; /* congratz to me..first valid use of goto :) */
+          }
+       }
+       else {
+          tx_port = tx_port->next;
+       }
+       
+       /** When here, we have a potential cantidate.  After this, if we see orig_tx_port,
+        * we've wrapped, and there isn't one around to transmit on.
+        */
+       while (1) {
+          if (tx_port == orig_tx_port) {
+             break;
+          }
+          if (tx_port == NULL) {
+             tx_port = dev->vlan_dev->ports;
+          }
+          if (vlan_port_test_egress(tx_port->port, vid)) {
+             
+             skb->dev = tx_port->port->real_dev;
+             /* 
+              * printk(VLAN_DBG __FUNCTION__ ": about to dev_queue_xmit (top)\n");
+              */
+             dev_queue_xmit(skb);
+             stats->tx_packets++; /* for statics only */
+             stats->tx_bytes += skb->len;
+             dev->vlan_dev->last_tx_port = tx_port;
+             return 0;
+          }
+          else {
+             tx_port = tx_port->next;
+          }
+       }/* while */
+    }//else, port aggregation
+ 
+    /* Looks like we can't round-robin for some reason... */
+    if (vlan_port_test_egress(tx_port->port, vid)) {
+             
+       skb->dev = tx_port->port->real_dev;
+       /* 
+        * printk(VLAN_DBG __FUNCTION__ ": about to dev_queue_xmit (top)\n");
+        */
+       dev_queue_xmit(skb);
+       stats->tx_packets++; /* for statics only */
+       stats->tx_bytes += skb->len;
+       dev->vlan_dev->last_tx_port = tx_port;
+       return 0;
+    }
+    
+   no_vlan_to_tx_on:
+    /* if we are here, then we didn't consume the skb already. */
+    printk(VLAN_INF __FUNCTION__ ": could not find port to transmit on for VLAN: %s\n", dev->name);
+    stats->tx_errors++;
+    kfree_skb(skb);
+    return -1;
+ }/* vlan_dev_hard_start_xmit */
+ 
+ 
+ int vlan_dev_change_mtu(struct net_device *dev, int new_mtu) {
+    /* TODO: gotta make sure the underlying layer can handle it,
+     * maybe an IFF_VLAN_CAPABLE flag for devices?
+     */
+ 
+    dev->mtu = new_mtu;
+    return new_mtu;
+ }
+ 
+ int vlan_dev_open(struct net_device* dev) {
+    dev->flags |= IFF_UP;
+    return 0;
+ }
+ 
+ int vlan_dev_stop(struct net_device* dev) {
+    dev->flags &= ~IFF_UP;
+    return 0;
+ }
+ 
+ int vlan_dev_init(struct net_device* dev) {
+    /* TODO:  figure this out, maybe do nothing?? */
+    return 0;
+ }
+ 
+ void vlan_dev_destruct(struct net_device* dev) {
+ 
+    kfree(dev->name);
+    VLAN_FMEM_DBG("dev->name free, addr: %p\n", dev->name);
+    dev->name = NULL; /* better safe than hosed */
+    
+    kfree(dev->priv);
+    VLAN_FMEM_DBG("dev->priv free, addr: %p\n", dev->priv);
+    dev->priv = NULL;
+ 
+    if (dev->vlan_dev->dent) {
+       printk(KERN_ERR __FUNCTION__ ": dev->vlan_dev->dent is NOT NULL!\n");
+       /* If we ever get here, there is a serious bug that must be fixed. */
+    }
+    
+    kfree(dev->vlan_dev);
+    VLAN_FMEM_DBG("dev->vlan_dev free, addr: %p\n", dev->vlan_dev);
+    dev->vlan_dev = NULL;
+ 
+    kfree(dev);
+    VLAN_FMEM_DBG("net_device free, addr: %p\n", dev);
+    dev = NULL;
+ 
+    return;
+ }
+ 
+ 
+ /* TODO:  Not to sure if the VLAN stuff works here.  Need to understand
+  * this better. --BLG
+  */
+ /*
+ int vlan_dev_header_cache(struct neighbour *neigh, struct hh_cache *hh) {
+    unsigned short type = hh->hh_type;
+    struct vlan_ethhdr *veth = (struct vlan_ethhdr*)(((u8*)hh->hh_data) + 2);
+    struct device *dev = neigh->dev;
+ 
+    if (type == __constant_htons(ETH_P_802_3)) {
+       return -1;
+    }
+ 
+    veth->h_vlan_proto = __constant_htons(ETH_P_802_1Q);
+    memcpy(veth->h_source, dev->dev_addr, ETH_ALEN);
+    memcpy(veth->h_dest, neigh->ha, ETH_ALEN);
+       
+    * VLAN specific attributes. *
+    veth->h_vlan_TCI = htons(dev->VLAN_id); * TODO:  Add priority control (high 3 bits.) * 
+    veth->h_vlan_encapsulated_proto = type; * should already be in network order *
+         
+    return 0;
+ }
+ */
+ 
+ /*
+  * Called by Address Resolution module to notify changes in address.
+  */
+ /*
+ void vlan_dev_header_cache_update(struct hh_cache *hh, struct device *dev,
+                                   unsigned char * haddr) {
+    memcpy(((u8*)hh->hh_data) + 2, haddr, VLAN_ETH_HLEN);
+ }
+ */
+ 
+ #ifndef CONFIG_IP_ROUTER
+ 
+ /*
+  *	Copy from an ethernet device memory space to an sk_buff while
+  *      checksumming if IP
+  *
+  *  TODO:  Find out who calls this:  This was lifted from eth.c, and
+  *         was called eth_copy_and_sum. --BLG
+  */
+ 
+ void vlan_dev_copy_and_sum(struct sk_buff *dest, unsigned char *src,
+                            int length, int base) {
+    struct vlan_ethhdr* veth;
+    struct iphdr *iph;
+    int ip_length;
+    
+    veth = (struct vlan_ethhdr*)(src);
+    
+    /*  This grabs the VLAN part of the header too. */
+    if (veth->h_vlan_encapsulated_proto != __constant_htons(ETH_P_IP)) {
+       memcpy(dest->data, src, length);
+       return;
+    }
+       
+    /*
+     * We have to watch for padded packets. The csum doesn't include the
+     * padding, and there is no point in copying the padding anyway.
+     * We have to use the smaller of length and ip_length because it
+     * can happen that ip_length > length.
+     */
+       
+    /* ethernet is always >= 34 */
+    memcpy(dest->data, src, sizeof(struct iphdr) + VLAN_ETH_HLEN);
+       
+    length -= sizeof(struct iphdr) + VLAN_ETH_HLEN;
+    iph = (struct iphdr*)(src + VLAN_ETH_HLEN);
+    ip_length = ntohs(iph->tot_len) - sizeof(struct iphdr);
+    
+    /* Also watch out for bogons - min IP size is 8 (rfc-1042) */
+    if ((ip_length <= length) && (ip_length > 7))
+       length=ip_length;
+    
+    dest->csum = csum_partial_copy(src + sizeof(struct iphdr) + VLAN_ETH_HLEN,
+                                   dest->data + sizeof(struct iphdr) + VLAN_ETH_HLEN,
+                                   length, base);
+    dest->ip_summed=1;
+ 
+ } /* vlan_copy_and_sum */
+ 
+ #endif //! CONFIG_IP_ROUTER
+ 
+ 
+ int vlan_dev_add_dev_by_vid(unsigned int vlan_id, const char* real_dev_name) {
+    struct net_device* port_dev;
+    struct net_device* vlan_dev;
+    int retval = 0;
+    
+    if (vlan_configured_multiple_groups) {
+       printk(VLAN_ERR __FUNCTION__ ": Can't do this when configured for multiple groups.");
+       return -EINVAL;
+    }
+    
+    port_dev = dev_get_by_name(real_dev_name);
+    if (!port_dev) {
+       printk(VLAN_ERR __FUNCTION__ ": Can't find real device: %s", real_dev_name);
+       return -EINVAL;
+    }
+ 
+    /* Since we are only supporting one group, then the MAC is not needed, because
+     * the MAC is used to determine a group in a multi-group configuration.  So,
+     * we pass NULL for the first argument.
+     */
+    vlan_dev = find_802_1Q_vlan_dev(NULL, vlan_id);
+    if (!vlan_dev) {
+       printk(VLAN_ERR __FUNCTION__ ": Can't find vlan_dev by VID: %i", (int)(vlan_id));
+       dev_put(port_dev);
+       return -EINVAL;
+    }
+ 
+    retval = vlan_dev_add_dev(vlan_dev, port_dev);
+    dev_put(vlan_dev);
+    dev_put(port_dev);
+    return retval;
+ }
+ 
+ 
+ int vlan_dev_rem_dev_by_vid(unsigned int vlan_id, const char* real_dev_name) {
+    struct net_device* port_dev;
+    struct net_device* vlan_dev;
+    int retval = 0;
+    
+    if (vlan_configured_multiple_groups) {
+       printk(VLAN_ERR __FUNCTION__ ": Can't do this when configured for multiple groups.");
+       return -EINVAL;
+    }
+    
+    port_dev = dev_get_by_name(real_dev_name);
+    if (!port_dev) {
+       printk(VLAN_ERR __FUNCTION__ ": Can't find real device: %s", real_dev_name);
+       return -EINVAL;
+    }
+ 
+    /* Since we are only supporting one group, then the MAC is not needed, because
+     * the MAC is used to determine a group in a multi-group configuration.  So,
+     * we pass NULL for the first argument.
+     */
+    vlan_dev = find_802_1Q_vlan_dev(NULL, vlan_id);
+    if (!vlan_dev) {
+       printk(VLAN_ERR __FUNCTION__ ": Can't find vlan_dev by VID: %i", (int)(vlan_id));
+       dev_put(port_dev);
+       return -EINVAL;
+    }
+ 
+    retval = vlan_dev_rem_dev(vlan_dev, port_dev);
+    dev_put(port_dev);
+    dev_put(vlan_dev);
+    return retval;
+ }
+ 
+ 
+ /** Adds a 'real device' to a vlan device.  Creates structures as
+  * needed and does all work.  Specificially, it may create and initialize
+  * a new vlan_port object.
+  */
+ int vlan_dev_add_dev(struct net_device* vlan_dev, struct net_device* real_dev) {
+    struct vlan_port* v_port = NULL;
+    struct vlan_port_collection* ports = &vlan_ports;
+    int retval;
+    
+ #ifdef VLAN_DEBUG
+    printk(VLAN_DBG __FUNCTION__ ": vlan_dev -:%s:- real_dev -:%s:-\n",
+           vlan_dev->name, real_dev->name);
+ #endif
+    
+    if (vlan_dev->vlan_dev) {
+       if (!real_dev->vlan_dev) { /* can't add a VLAN port to a VLAN!! */
+          v_port = vlan_port_coll_find_port(ports, real_dev);
+          if (!v_port) {
+             v_port = vlan_port_coll_create_port(ports, real_dev);
+             if (!v_port) {
+                printk(KERN_ALERT __FUNCTION__ ": Out of Memory: couldn't create v_port\n");
+                return -ENOBUFS;
+             }
+          }
+          
+          if ((retval = vlan_dev_add_port(vlan_dev, v_port)) >= 0) {
+             return vlan_port_add_vlan(v_port, vlan_dev, VLAN_ADDED_BY_USER);
+          }
+          else {
+             return retval;
+          }
+       }
+    }
+    return -EINVAL;
+ } /* vlan_dev_add_dev */ 
+ 
+ 
+ int vlan_dev_rem_dev(struct net_device* vlan_dev, struct net_device* real_dev) {
+    struct vlan_port* v_port = NULL;
+    struct vlan_port_collection* ports = &vlan_ports;
+    int retval;
+    
+ #ifdef VLAN_DEBUG
+    printk(VLAN_DBG __FUNCTION__ ": vlan_dev -:%s:- real_dev -:%s:-\n",
+           vlan_dev->name, real_dev->name);
+ #endif
+    
+    if (vlan_dev->vlan_dev) {
+       if (!real_dev->vlan_dev) { /* can't remove a VLAN port from a VLAN!! */
+          v_port = vlan_port_coll_find_port(ports, real_dev);
+          if (!v_port) {
+             return -EINVAL; /* doesn't exist... */
+          }
+          
+          if ((retval = vlan_dev_rem_port(vlan_dev, v_port)) >= 0) {
+             return vlan_port_remove_vlan(v_port, vlan_dev);
+             /* NOTE:  If any of this changes, should probably change the
+              * vlan_port_remove_vlan method as well.
+              *
+              * If port is empty, then lets remove it too.
+              */
+             if (vlan_port_is_empty(v_port)) {
+                printk(VLAN_INF __FUNCTION__ ": was empty.\n");
+                vlan_port_coll_rem_port(ports, v_port); /* calls vlan_port_destroy */
+             }
+             else {
+                printk(VLAN_INF __FUNCTION__ ": was NOT empty.\n");
+             }
+          }
+          else {
+             return retval;
+          }
+       }
+    }
+    return -EINVAL;
+ } /* vlan_dev_rem_dev */
+ 
+ 
+ int vlan_dev_add_port(struct net_device* vlan_dev, struct vlan_port* port) {
+    struct vlan_port_node* node;
+    
+ #ifdef VLAN_DEBUG
+    printk(VLAN_DBG __FUNCTION__ ": vlan_dev -:%s:-\n", vlan_dev->name);
+ #endif
+    
+    /* make sure we don't have it already. */
+    if (!vlan_dev_contains_port(vlan_dev, port)) {
+       node = (struct vlan_port_node*)kmalloc(sizeof(struct vlan_port_node), GFP_KERNEL);
+       VLAN_MEM_DBG("node malloc, addr: %p  size: %i\n", node, sizeof(struct vlan_port_node));
+       
+       if (node) {
+          memset(node, 0, sizeof(struct vlan_port_node));
+          node->next = vlan_dev->vlan_dev->ports;
+          node->port = port;
+          vlan_dev->vlan_dev->ports = node;
+          return 0;
+       }
+       else {
+          return -ENOBUFS;
+       }
+    }
+    return 0;
+ }
+ 
+ 
+ int vlan_dev_rem_port(struct net_device* vlan_dev, struct vlan_port* port) {
+    struct vlan_port_node* p;
+    struct vlan_port_node* prev;
+    
+ #ifdef VLAN_DEBUG
+    printk(VLAN_DBG __FUNCTION__ ": vlan_dev -:%s:-\n", vlan_dev->name);
+ #endif
+ 
+    if (vlan_dev->vlan_dev && vlan_dev->vlan_dev->ports) {
+       
+       /* see if it's the first one. */
+       if (vlan_dev->vlan_dev->ports->port == port) {
+          prev = vlan_dev->vlan_dev->ports;
+          vlan_dev->vlan_dev->ports = vlan_dev->vlan_dev->ports->next;
+          kfree(prev);
+          return 0;
+       }
+ 
+       prev = vlan_dev->vlan_dev->ports;
+       
+       for (p = vlan_dev->vlan_dev->ports->next; p; p = p->next) {
+          if (p->port == port) {
+             prev->next = p->next;
+             kfree(p);
+             return 0;
+          }
+          else {
+             prev = p;
+          }
+       }
+    }
+    return -EINVAL;
+ }/* vlan_dev_rem_port */
+ 
+ 
+ int vlan_dev_contains_port(struct net_device* vlan_dev, struct vlan_port* port) {
+    struct vlan_port_node* p;
+    
+    if (vlan_dev->vlan_dev) {
+       for (p = vlan_dev->vlan_dev->ports; p; p = p->next) {
+          if (p->port == port) {
+             return 1;
+          }
+       }
+    }
+    return 0;
+ }
+ 
+ int vlan_dev_set_dflt_vid(char* dev_name, unsigned short vid) {
+    struct net_device* dev = dev_get_by_name(dev_name);
+    int retval = -EINVAL;
+    if (dev) {
+       if (!dev->vlan_dev) { /* can't put a dflt ID on a vlan device */
+          if ((vid > 0) && (vid <= 0xFFF)) {
+             dev->default_vid = (vid & 0xFFF);
+             retval = 0;
+          }
+       }
+       dev_put(dev);
+    }
+    return retval;
+ }
diff -P -C 2 -X /home/greear/exclude.list --recursive linux/net/802_1Q/vlan_port.c linux.dev/net/802_1Q/vlan_port.c
*** linux/net/802_1Q/vlan_port.c	Wed Dec 31 17:00:00 1969
--- linux.dev/net/802_1Q/vlan_port.c	Sat Feb 26 22:09:36 2000
***************
*** 0 ****
--- 1,381 ----
+ /*
+  *		VLAN-type device handling.
+  *
+  * Version:	@(#)vlan_port.c 0.0.1	3/20/99
+  *
+  * Authors:	Ben Greear <greear@cyberhighway.net>, <greearb@agcs.com>
+  * 
+  * Fixes:
+  *
+  *		This program is free software; you can redistribute it and/or
+  *		modify it under the terms of the GNU General Public License
+  *		as published by the Free Software Foundation; either version
+  *		2 of the License, or (at your option) any later version.
+  */
+ 
+ #include <asm/uaccess.h> /* for copy_from_user */
+ #include <linux/module.h>
+ #include <linux/netdevice.h>
+ #include <linux/skbuff.h>
+ #include <net/datalink.h>
+ #include <linux/mm.h>
+ #include <linux/in.h>
+ #include <linux/init.h>
+ #include <net/p8022.h>
+ #include <net/arp.h>
+ #include "vlan.h"
+ #include <linux/if_vlan.h>
+ #include "vlanproc.h"
+ 
+ 
+ /***********************************************************************/
+ /*************** Methods relating to VLAN collections. *****************/
+ /***********************************************************************/
+ 
+ 
+ /** Does not check to see if we already have it. */
+ int vlan_collection_add_vlan(struct vlan_collection* vc, unsigned short vlan_id,
+                              unsigned short flags) {
+    struct vlan_entry* ve;
+    int idx = vlan_collection_get_hashed_idx(vlan_id);
+    VLAN_MEM_DBG("vlan_entry, addr: %p  size: %i\n", ve, sizeof(struct vlan_entry));
+ 
+    if (vlan_collection_has_vlan(vc, vlan_id)) {
+       return -EINVAL;
+    }
+ 
+    ve = (struct vlan_entry*)kmalloc(sizeof(struct vlan_entry), GFP_ATOMIC);
+    
+    if (ve) {
+       ve->vlan_id = vlan_id;
+       ve->flags = flags;
+       ve->next = vc->vlan_table[idx];
+       vc->vlan_table[idx] = ve;
+       return 0;
+    }
+    return -ENOBUFS;
+ }
+ 
+ int vlan_collection_remove_vlan_id(struct vlan_collection* vc, unsigned short vlan_id) {
+    int idx = vlan_collection_get_hashed_idx(vlan_id);
+    struct vlan_entry* walker = vc->vlan_table[idx];
+    struct vlan_entry* prev = NULL;
+ 
+    while (walker) {
+       if (walker->vlan_id == vlan_id) {
+          break;
+       }
+       prev = walker;
+       walker = walker->next;
+    }
+ 
+    if (walker) {
+       /* if here, we found one */
+       if (prev) {
+          /* if not the first of the list */
+          prev->next = walker->next;
+          kfree(walker);
+          VLAN_FMEM_DBG("vlan_entry free, addr: %p\n", walker);
+       }
+       else {
+          /* was at the first of the list */
+          vc->vlan_table[idx] = walker->next;
+          kfree(walker);
+          VLAN_FMEM_DBG("vlan_entry free, addr: %p\n", walker);
+       }
+       return 0;
+    }
+    return -EINVAL;
+ } /* vlan_collection_remove_vlan_id */
+ 
+ int vlan_collection_remove_vlan(struct vlan_collection* vc,
+                                 struct net_device* vlan_dev) {
+    if (vlan_dev->vlan_dev) {
+       return vlan_collection_remove_vlan_id(vc, vlan_dev->vlan_dev->vlan_id);
+    }
+    else {
+       return vlan_collection_remove_vlan_id(vc, vlan_dev->default_vid);
+    }
+ }
+ 
+ 
+ /*  Gonna do a shallow copy for now. */
+ inline void vlan_collection_oper_equals(struct vlan_collection* lhs,
+                                         struct vlan_collection* rhs) {
+    *lhs = *rhs;
+ }
+ 
+ 
+ /* This isn't terribly efficient, but shouldn't be in a critical path
+  */
+ int vlan_collection_is_empty(struct vlan_collection* vc) {
+    int i;
+    for (i = 0; i<4096; i++) {
+       if (vlan_collection_has_vlan(vc, i)) {
+          printk(VLAN_DBG __FUNCTION__ ": had vlan: %i\n", i);
+          return 0;
+       }
+    }
+    return 1;
+ }
+          
+ 
+ /***********************************************************************/
+ /****************** Methods relating to VLAN ports. ********************/
+ /***********************************************************************/
+ 
+ /**  Add a VLAN to the RX and TX filters.  Assume this is a
+  * sane thing to do.  Returns zero on success.
+  */
+ int vlan_port_add_vlan(struct vlan_port* port, struct net_device* vlan_dev,
+                        unsigned short flags) {
+    int r1 = vlan_port_add_rx_allow(port, vlan_dev, flags);
+    int r2 = vlan_port_add_tx_allow(port, vlan_dev, flags);
+    return (r1 | r2);
+ }
+ 
+ int vlan_port_add_rx_allow(struct vlan_port* port, struct net_device* vlan_dev,
+                            unsigned short flags) {
+    if (vlan_dev->vlan_dev) {
+       return vlan_collection_add_vlan(&(port->rx_allow_group),
+                                       vlan_dev->vlan_dev->vlan_id, flags);
+    }
+    else {
+       return vlan_collection_add_vlan(&(port->rx_allow_group),
+                                       vlan_dev->default_vid, flags);
+    }
+ }
+ 
+ int vlan_port_add_tx_allow(struct vlan_port* port, struct net_device* vlan_dev,
+                            unsigned short flags) {
+    if (vlan_dev->vlan_dev) {
+       return vlan_collection_add_vlan(&(port->tx_allow_group),
+                                       vlan_dev->vlan_dev->vlan_id, flags);
+    }
+    else {
+       return vlan_collection_add_vlan(&(port->tx_allow_group),
+                                       vlan_dev->default_vid, flags);
+    }
+ }
+ 
+ int vlan_port_init(struct vlan_port* port) {
+    memset(port, 0, sizeof(struct vlan_port));
+    port->flags = (VLAN_FLAG_ALLOW_802_3 & VLAN_FLAG_ALLOW_802_1Q);
+    sprintf(port->name, "port_%p", port);
+    
+    /* add a proc entry */
+    vlan_proc_add_port(port);
+    
+    return 0;
+ }
+ 
+ int vlan_port_destroy(struct vlan_port* port) {
+    
+    /* remove proc entry */
+    vlan_proc_rem_port(port);
+ 
+    /* release the underlying net device */
+    if (port->real_dev) {
+       dev_put(port->real_dev);
+       port->real_dev = NULL; /* better safe than hosed */
+    }
+ 
+    port->flags = 0;
+    
+    return 0;
+ }
+ 
+ int vlan_port_is_empty(struct vlan_port* port) {
+    return (vlan_collection_is_empty(&(port->rx_allow_group)) &&
+            vlan_collection_is_empty(&(port->rx_deny_group)) &&
+            vlan_collection_is_empty(&(port->tx_allow_group)) &&
+            vlan_collection_is_empty(&(port->tx_deny_group)));
+ }
+ 
+ 
+ /* shallow copy for now.   TODO:  See if we need to bump ref-cnts. */
+ inline void vlan_port_oper_equals(struct vlan_port* lhs, struct vlan_port* rhs) {
+    lhs->real_dev = rhs->real_dev;
+    lhs->next = rhs->next;
+    
+    /* these are shallow copies for now, but I think that will be fine. */
+    vlan_collection_oper_equals(&(lhs->rx_allow_group), &(rhs->rx_allow_group));
+    vlan_collection_oper_equals(&(lhs->rx_deny_group), &(rhs->rx_deny_group));
+    vlan_collection_oper_equals(&(lhs->tx_allow_group), &(rhs->tx_allow_group));
+    vlan_collection_oper_equals(&(lhs->tx_deny_group), &(rhs->tx_deny_group));
+ 
+    lhs->default_action = rhs->default_action;
+    lhs->flags = rhs->flags;
+ }
+ 
+ int vlan_port_remove_vlan(struct vlan_port* port, struct net_device* vlan_dev) {
+    int retval = 0;
+    retval |= vlan_collection_remove_vlan(&(port->tx_allow_group), vlan_dev);
+    retval |= vlan_collection_remove_vlan(&(port->rx_allow_group), vlan_dev);
+    retval |= vlan_collection_remove_vlan(&(port->tx_deny_group), vlan_dev);
+    retval |= vlan_collection_remove_vlan(&(port->rx_deny_group), vlan_dev);
+    return retval; /* not horribly correct, but best I can think of. --BLG */
+ }
+ 
+ 
+ /***********************************************************************/
+ /************* Methods relating to the VLAN port collection ************/
+ /***********************************************************************/
+ 
+ int vlan_port_coll_init(struct vlan_port_collection* col) {
+ #ifdef VLAN_DEBUG
+    printk(VLAN_INF __FUNCTION__);
+ #endif
+    
+    col->length = 10;
+    col->ports = (struct vlan_port*)kmalloc(10 * sizeof(struct vlan_port), GFP_KERNEL);
+    VLAN_MEM_DBG("col->ports malloc, addr: %p  size: %i\n", col->ports,
+                 10 * sizeof(struct vlan_port));
+    if (col->ports) {
+       memset(col->ports, 0, 10 * sizeof(struct vlan_port));
+       /* printk(VLAN_INF __FUNCTION__ ": allocated col->ports\n"); */
+       return 0;
+    }
+    col->length = 0;
+    col->ports = NULL;
+    printk(KERN_ALERT __FUNCTION__ ": ERROR: could not allocate col->ports\n");   
+    return -ENOBUFS;
+ }
+ 
+ 
+ static struct vlan_port* vlan_port_coll_create_port_priv(struct vlan_port_collection* col,
+                                                          struct net_device* real_dev,
+                                                          int sanity) {
+    struct vlan_port* new_ports;
+    int i;
+    int j;
+    
+ #ifdef VLAN_DEBUG
+    printk(VLAN_INF __FUNCTION__ ": col->length: %i\n", col->length);
+ #endif
+    
+    /*  This should never be true, but endless loops suck. */
+    if (sanity > 2) {
+       printk(KERN_WARNING __FUNCTION__ ": ERROR: failed sanity check.\n");
+       return NULL;
+    }
+    
+    for (i = 0; i < col->length; i++) {
+ #ifdef VLAN_DEBUG
+       printk(VLAN_INF __FUNCTION__ ": checking i: %i\n", i);
+ #endif
+       if (!(col->ports[i].flags & VLAN_FLAG_IS_IN_USE)) {
+          vlan_port_init(&(col->ports[i]));
+ 
+          /* default is to allow both 802.1Q and 802.3 packets over
+           * the same port.  This will be especially handy when using
+           * default_vid for ports.
+           */
+          col->ports[i].flags |=
+             (VLAN_FLAG_IS_IN_USE | VLAN_FLAG_ALLOW_802_1Q | VLAN_FLAG_ALLOW_802_3);
+ 
+          col->ports[i].real_dev = real_dev;
+          /* bump the reference count on the real_dev */
+          dev_hold(real_dev);
+          
+ #ifdef VLAN_DEBUG
+          printk(VLAN_INF __FUNCTION__ ": found a port, i: %i\n", i);
+ #endif
+          return &(col->ports[i]);
+       }
+    }
+ 
+ #ifdef VLAN_DEBUG
+    printk(VLAN_INF __FUNCTION__ ": need to grow array.\n");
+ #endif
+    /* if here, we need to grow the array. */
+    new_ports = (struct vlan_port*)kmalloc(col->length * 2 * sizeof(struct vlan_port),
+                                           GFP_KERNEL);
+    VLAN_MEM_DBG("ports_array malloc, addr: %p  size: %i\n", new_ports,
+                 col->length * 2 * sizeof(struct vlan_port));
+ 
+    if (new_ports) {
+       memset(new_ports, 0, col->length * 2 * sizeof(struct vlan_port));
+ 
+       for (j = 0; j < col->length; j++) {
+          vlan_port_oper_equals(&(new_ports[i]), &(col->ports[i]));
+       }
+ 
+       /* free the old memory, increase the length. */
+       /* don't call the delete for each port because it will remove the
+        * proc entry.
+        */
+       kfree(col->ports); /* TODO: Can I free arrays like this?? */
+       VLAN_FMEM_DBG("ports_array free, addr: %p\n", col->ports);
+ 
+       col->ports = new_ports;
+       col->length *= 2;
+       
+       /* recurse, bump sanity count */
+       return vlan_port_coll_create_port_priv(col, real_dev, sanity + 1);
+    }
+    else {
+       printk(KERN_ALERT __FUNCTION__ "Couldn't allocate memory for new ports.\n");
+       return NULL;
+    }
+ }/* vlan_port_coll_create_port_priv */
+ 
+ struct vlan_port* vlan_port_coll_create_port(struct vlan_port_collection* col,
+                                              struct net_device* real_dev) {
+    return vlan_port_coll_create_port_priv(col, real_dev, 0);
+ }
+ 
+       
+ struct vlan_port* vlan_port_coll_find_port(struct vlan_port_collection* col,
+                                            struct net_device* real_dev) {
+    int i;
+    for (i = 0; i < col->length; i++) {
+       if (col->ports[i].flags & VLAN_FLAG_IS_IN_USE) {
+          if (col->ports[i].real_dev == real_dev) {
+             return &(col->ports[i]);
+          }
+       }
+    }
+    return NULL;
+ }
+ 
+ 
+ int vlan_port_coll_rem_port(struct vlan_port_collection* col,
+                              struct vlan_port* vport) {
+    int i;
+    for (i = 0; i < col->length; i++) {
+       if (col->ports[i].flags & VLAN_FLAG_IS_IN_USE) {
+          if (&(col->ports[i]) == vport) {
+             vlan_port_destroy(&(col->ports[i])); /* will remove ref cnt from underlying
+                                                   * device and the proc entry.
+                                                   */
+             return 0;
+          }
+       }
+    }
+    return -EINVAL;
+ }
+ 
+ 
+ int vlan_port_coll_remove_vlan(struct vlan_port_collection* col,
+                                struct net_device* vlan_dev) {
+    int i;
+    for (i = 0; i < col->length; i++) {
+       if (col->ports[i].flags & VLAN_FLAG_IS_IN_USE) {
+          vlan_port_remove_vlan(&(col->ports[i]), vlan_dev);
+ 
+          /* NOTE:  If any of this changes, should probably change the
+           * vlan_port_coll_rem_vlan_from_port method as well.
+           */
+          /* If port is empty, then lets remove it too. */
+          if (vlan_port_is_empty(&(col->ports[i]))) {
+             printk(VLAN_INF __FUNCTION__ ": vport was empty.\n");
+             vlan_port_coll_rem_port(col, &(col->ports[i]));
+          }
+          else {
+             printk(VLAN_INF __FUNCTION__ ": vport was NOT empty.\n");
+          }
+       }
+    }
+    return 0;
+ }
diff -P -C 2 -X /home/greear/exclude.list --recursive linux/net/802_1Q/vlanproc.c linux.dev/net/802_1Q/vlanproc.c
*** linux/net/802_1Q/vlanproc.c	Wed Dec 31 17:00:00 1969
--- linux.dev/net/802_1Q/vlanproc.c	Sat Feb 26 22:08:04 2000
***************
*** 0 ****
--- 1,862 ----
+ /*****************************************************************************
+ * vlanproc.c	VLAN Module. /proc filesystem interface.
+ *
+ *		This module is completely hardware-independent and provides
+ *		access to the router using Linux /proc filesystem.
+ *
+ * Author:	Ben Greear, <greear@cyberhighway.net> coppied from wanproc.c
+ *               by: Gene Kozin	<genek@compuserve.com>
+ *
+ * Copyright:	(c) 1998 Ben Greear
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ * ============================================================================
+ * Jan 20, 1998        Ben Greear     Initial Version
+ *****************************************************************************/
+ 
+ #include <linux/config.h>
+ #include <linux/stddef.h>	/* offsetof(), etc. */
+ #include <linux/errno.h>	/* return codes */
+ #include <linux/kernel.h>
+ #include <linux/malloc.h>	/* kmalloc(), kfree() */
+ #include <linux/mm.h>		/* verify_area(), etc. */
+ #include <linux/string.h>	/* inline mem*, str* functions */
+ #include <linux/init.h>		/* __initfunc et al. */
+ #include <asm/segment.h>	/* kernel <-> user copy */
+ #include <asm/byteorder.h>	/* htons(), etc. */
+ #include <asm/uaccess.h>	/* copy_to_user */
+ #include <asm/io.h>
+ #include <linux/proc_fs.h>
+ #include <linux/fs.h>
+ #include <linux/netdevice.h>
+ #include <linux/if_vlan.h>
+ #include "vlanproc.h"
+ #include "vlan.h"
+ 
+ /****** Defines and Macros **************************************************/
+ 
+ #ifndef	min
+ #define min(a,b) (((a)<(b))?(a):(b))
+ #endif
+ #ifndef	max
+ #define max(a,b) (((a)>(b))?(a):(b))
+ #endif
+ 
+ 
+ /****** Function Prototypes *************************************************/
+ 
+ #ifdef CONFIG_PROC_FS
+ 
+ /* Proc filesystem interface */
+ static int vlan_proc_perms(struct inode *, int);
+ static ssize_t vlan_proc_read(struct file* file, char* buf, size_t count,
+                               loff_t *ppos);
+ 
+ /* Methods for preparing data for reading proc entries */
+ 
+ static int vlan_config_get_info(char* buf, char** start, off_t offs, int len);
+ static int vlandev_get_info(char* buf, char** start, off_t offs, int len);
+ static int vlanport_get_info(char* buf, char** start, off_t offs, int len);
+ 
+ 
+ /* Miscellaneous */
+ 
+ /*
+  *	Global Data
+  */
+ 
+ /*
+  *	Names of the proc directory entries 
+  */
+ 
+ static char name_root[]	 = "vlan";
+ static char name_conf[]	 = "config";
+ static char term_msg[]   = "***KERNEL:  Out of buffer space!***\n";
+ 
+ 
+ int vlan_hack_send_pkt(char* dev_name, char* dest_mac, char* src_mac,
+                        char* skb_data, unsigned int skb_data_len,
+                        unsigned short skb_data_type) {
+    struct net_device* dev = dev_get_by_name(dev_name);
+    struct sk_buff* skb;
+    char* payload_ptr;
+    int err;
+    int rsrv = 0;
+    
+    printk(VLAN_DBG __FUNCTION__ "dev -:%s:- data_len: %u data_type: %uhx",
+           dev_name, skb_data_len, skb_data_type);
+ 
+    if (dev) {
+ 
+       if (skb_data_len > (dev->mtu - dev->hard_header_len)) {
+          dev_put(dev);
+          return -EINVAL;
+       }
+ 
+       /* Keep over or underflowing the packet, so padding with 128. */
+       skb = alloc_skb(skb_data_len + dev->hard_header_len + 128, GFP_ATOMIC);
+       
+       if (skb) {
+          skb->mac.raw = skb->data;
+          skb->dev = dev;
+ 
+          rsrv = ((dev->hard_header_len + 15) & ~15);
+          
+          printk(VLAN_DBG __FUNCTION__ ": about to reserve %i.\n", rsrv);
+          skb_reserve(skb, rsrv); /* coppied from ARP code */
+          skb->protocol = skb_data_type;
+          
+          printk(VLAN_DBG __FUNCTION__ ": about to put %i.\n", (int)skb_data_len);
+          payload_ptr = skb_put(skb, skb_data_len); /* make room for the payload data */
+ 
+          printk(VLAN_DBG __FUNCTION__ ": about to memcpy.\n");
+          memcpy(payload_ptr, skb_data, skb_data_len);
+ 
+          printk(VLAN_DBG __FUNCTION__ "about to do hard_header\n");
+ 
+          err = dev->hard_header(skb, dev, skb_data_type,
+                                 dest_mac, src_mac, skb_data_len);
+          if (err >= 0) {
+             int retval;
+             printk(VLAN_DBG __FUNCTION__ "about to do hard_start_xmit\n");
+             retval = dev->hard_start_xmit(skb, dev);
+             dev_put(dev);
+             return retval;
+          }
+          else {
+             dev_put(dev);
+             return err;
+          }
+       }
+       dev_put(dev);
+       return -ENOBUFS;
+    }
+    return -EINVAL;
+ }
+ 
+ 
+ /*
+  *	VLAN device IOCTL.
+  *	o execute requested action or pass command to the device driver
+  */
+ 
+ int vlan_ioctl(struct inode* inode, struct file* file,
+                unsigned int cmd, unsigned long arg) {
+    int err = 0;
+    /*
+      struct proc_dir_entry* dent;
+      struct device* dev;
+    */
+    struct vlan_ioctl_args args;
+    
+    printk(VLAN_DBG __FUNCTION__ ": cmd: %x\n", cmd);
+ 
+    /* everything here needs root permissions, except aguably the
+     * hack ioctls for sending packets.  However, I know _I_ don't
+     * want users running that on my network! --BLG
+     */
+    if (!capable(CAP_NET_ADMIN)){
+       return -EPERM;
+    }
+ 		
+    if ((cmd >> 8) != VLAN_IOCTL) {
+       printk(VLAN_DBG __FUNCTION__ ": Not a VLAN IOCTL: %x \n", cmd);
+       return -EINVAL;
+    }
+    
+    if (copy_from_user(&args, (void*)arg, sizeof(struct vlan_ioctl_args)))
+       return -EFAULT;
+ 
+    /* Null terminate this sucker, just in case. */
+    args.dev1[24] = 0;
+    args.u.dev2[24] = 0;
+ 
+    /*
+    dent = inode->u.generic_ip;
+    if ((dent == NULL) || (dent->data == NULL))
+      return -EINVAL;
+ 
+    dev = dent->data;
+    */
+    
+    switch (cmd)
+       {
+       case ADD_VLAN_IOCTL:
+          /* we have been given the name of the Ethernet Device we want to
+           * talk to:  args.dev1   We also have the
+           * VLAN ID:  args.u.VID
+           */
+          if (register_802_1Q_vlan_device(args.dev1, args.u.VID)) {
+             err = 0;
+          }
+          else {
+             err = -EINVAL;
+          }
+          break;
+ 
+       case DEL_VLAN_IOCTL:
+          /* Here, the args.dev1 is the actual VLAN we want to get rid of. */
+ 
+          err = unregister_802_1Q_vlan_device(args.dev1);
+          break;
+       case ADD_VLAN_TO_PORT_IOCTL:
+          err = vlan_dev_add_dev_by_vid(args.u.VID, args.dev1);
+          break;
+          
+       case REM_VLAN_FROM_PORT_IOCTL:
+          err = vlan_dev_rem_dev_by_vid(args.u.VID, args.dev1);
+          break;
+ 
+       case SET_DEFAULT_VLAN_ID_IOCTL:
+          err = vlan_dev_set_dflt_vid(args.dev1, args.u.VID);
+          break;
+ 
+       case HACK_SEND_PKT_IOCTL:
+          err = vlan_hack_send_pkt(args.dev1, args.dest_mac, args.src_mac,
+                                   args.skb_data, args.u.skb_len, args.skb_data_type);
+          break;
+          
+       default:
+          /* pass on to underlying device instead?? */
+          printk(VLAN_DBG __FUNCTION__ ": Unknown VLAN IOCTL: %x \n", cmd);
+          return -EINVAL;
+       }/* switch */
+    return err;
+ }
+ 
+ /*
+  *	Structures for interfacing with the /proc filesystem.
+  *	VLAN creates its own directory /proc/net/vlan with the folowing
+  *	entries:
+  *	config		device status/configuration
+  *	<port>		entry for each port
+  *	<device>	entry for each  device
+  */
+ 
+ /*
+  *	Generic /proc/net/vlan/<file> file and inode operations 
+  */
+ 
+ static struct file_operations vlan_fops = {
+    NULL,			/* lseek   */
+    vlan_proc_read,	        /* read	   */
+    NULL,        		/* write   */
+    NULL,			/* readdir */
+    NULL,			/* select  */
+    vlan_ioctl,			/* ioctl   */
+    NULL,			/* mmap	   */
+    NULL,			/* no special open code	   */
+    NULL,			/* flush */
+    NULL,			/* no special release code */
+    NULL  			/* can't fsync */
+ };
+ 
+ static struct inode_operations vlan_inode = {
+    &vlan_fops,
+    NULL,			/* create */
+    NULL,			/* lookup */
+    NULL,			/* link */
+    NULL,			/* unlink */
+    NULL,			/* symlink */
+    NULL,			/* mkdir */
+    NULL,			/* rmdir */
+    NULL,			/* mknod */
+    NULL,			/* rename */
+    NULL,			/* readlink */
+    NULL,			/* follow link */
+    NULL,			/* truncate */
+    vlan_proc_perms,             /* permission */
+    NULL                         /* revalidate */
+ };
+ 
+ /*
+  *	/proc/net/vlan/<device> file and inode operations
+  */
+ 
+ static struct file_operations vlandev_fops = {
+    NULL,			/* lseek   */
+    vlan_proc_read,	        /* read	   */
+    NULL,        		/* write   */
+    NULL,			/* readdir */
+    NULL,			/* select  */
+    vlan_ioctl,	                /* ioctl   */
+    NULL,			/* mmap	   */
+    NULL,			/* no special open code	   */
+    NULL,			/* flush */
+    NULL,			/* no special release code */
+    NULL			        /* can't fsync */
+ };
+ 
+ static struct inode_operations vlandev_inode = {
+    &vlandev_fops,
+    NULL,			/* create */
+    NULL,			/* lookup */
+    NULL,			/* link */
+    NULL,			/* unlink */
+    NULL,			/* symlink */
+    NULL,			/* mkdir */
+    NULL,			/* rmdir */
+    NULL,			/* mknod */
+    NULL,			/* rename */
+    NULL,			/* readlink */
+    NULL,			/* follow link */
+    NULL,			/* truncate */
+    vlan_proc_perms,             /* permission */
+    NULL                         /* revalidate */
+ };
+ 
+ 
+ /*
+  *	/proc/net/vlan/<port> file and inode operations
+  */
+ 
+ static struct file_operations vlanport_fops = {
+    NULL,			/* lseek   */
+    vlan_proc_read,	        /* read	   */
+    NULL,        		/* write   */
+    NULL,			/* readdir */
+    NULL,			/* select  */
+    vlan_ioctl,	                /* ioctl   */
+    NULL,			/* mmap	   */
+    NULL,			/* no special open code	   */
+    NULL,			/* flush */
+    NULL,			/* no special release code */
+    NULL			        /* can't fsync */
+ };
+ 
+ static struct inode_operations vlanport_inode = {
+    &vlanport_fops,
+    NULL,			/* create */
+    NULL,			/* lookup */
+    NULL,			/* link */
+    NULL,			/* unlink */
+    NULL,			/* symlink */
+    NULL,			/* mkdir */
+    NULL,			/* rmdir */
+    NULL,			/* mknod */
+    NULL,			/* rename */
+    NULL,			/* readlink */
+    NULL,			/* follow link */
+    NULL,			/* truncate */
+    vlan_proc_perms,             /* permission */
+    NULL                         /* revalidate */
+ };
+ 
+ /*
+  * Proc filesystem derectory entries.
+  */
+ 
+ /*
+  *	/proc/net/vlan 
+  */
+ 
+ static struct proc_dir_entry* proc_vlan;
+ 
+ /*
+  *	/proc/net/vlan/config 
+  */
+  
+ static struct proc_dir_entry* proc_vlan_conf;
+ 
+ 
+ /* Strings */
+ static char conf_hdr[] =
+ "VLAN Dev name  | VLAN ID\n";
+ 	
+ static char ports_hdr[] =
+ "****************************************************************
+ \tDefault Action: (0 DENY, 1 ACCEPT)
+ \tFLAGS (1 ALLOW_802_3, 2 ALLOW 802_1Q, 4 IS_IN_USE)
+ \tFlags specific to an RX/TX VLAN ID are in parentheses.
+ \t\t(1 ADDED_BY_USER, 2 ADDED_BY_CONTROL)
+ ****************************************************************
+ Device name    | Default Action  |  Port FLAGS\n";
+ 
+ /*
+  *	Interface functions
+  */
+ 
+ /*
+  *	Initialize vlan proc interface.
+  */
+ 
+ int __init vlan_proc_init (void) {
+ 
+    proc_vlan = proc_mkdir(name_root, proc_net);
+    if (proc_vlan) {
+       proc_vlan_conf = create_proc_entry(name_conf, 0, proc_vlan);
+       if (proc_vlan_conf) {
+          proc_vlan_conf->ops = &vlan_inode;
+          proc_vlan_conf->get_info = vlan_config_get_info;
+          return 0;
+       }
+       else {
+          return -ENOBUFS;
+       }
+    }
+    else {
+       proc_vlan_conf = NULL;
+       return -ENOBUFS;
+    }
+ }
+ 
+ /*
+  *	Clean up router proc interface.
+  */
+ 
+ void vlan_proc_cleanup (void) {
+    remove_proc_entry(name_conf, proc_vlan);
+    proc_net_remove(name_root);
+ 
+    /* TODO:  Take care of dynamically added entries??
+     * Probably only a problem if we ever become a module though..
+     */
+ }
+ 
+ 
+ /*
+  *	Add directory entry for VLAN device.
+  */
+ 
+ int vlan_proc_add_dev (struct net_device* vlandev) {
+    if (!vlandev->vlan_dev) {
+       printk(KERN_ERR "ERROR:  vlan_proc_add, device -:%s:- is NOT a VLAN\n",
+              vlandev->name);
+       return -EINVAL;
+    }
+    
+    vlandev->vlan_dev->dent = create_proc_entry(vlandev->name, 0, proc_vlan);
+    if (!vlandev->vlan_dev->dent) {
+       return -ENOBUFS;
+    }
+    
+    vlandev->vlan_dev->dent->ops = &vlandev_inode;
+    vlandev->vlan_dev->dent->get_info = &vlandev_get_info;
+    vlandev->vlan_dev->dent->data = vlandev;
+ 
+ #ifdef VLAN_DEBUG
+    printk(KERN_ERR "vlan_proc_add, device -:%s:- being added.\n",
+           vlandev->name);
+ #endif
+    return 0;
+ }
+ 
+ 
+ /*
+  *	Add directory entry for VLAN port.
+  */
+ 
+ int vlan_proc_add_port (struct vlan_port* vport) {
+    if (!vport) {
+       printk(KERN_ERR __FUNCTION__ "ERROR:  vport is NULL.\n");
+       return -EINVAL;
+    }
+ 
+    vport->dent = create_proc_entry(vport->name, 0, proc_vlan);
+    if (!vport->dent) {
+       return -ENOBUFS;
+    }
+    
+    vport->dent->ops = &vlanport_inode;
+    vport->dent->get_info = &vlanport_get_info;
+    vport->dent->data = vport;
+ 
+ #ifdef VLAN_DEBUG
+    printk(VLAN_DBG __FUNCTION__ ": vlan_port: %p being added.\n", vport);
+ #endif
+    
+    return 0;
+ }
+ 
+ 
+ /*
+  *	Delete directory entry for VLAN device.
+  */
+ int vlan_proc_rem_dev(struct net_device* vlandev) {
+    int retval = 0;
+    if (!vlandev || !vlandev->vlan_dev) {
+ #ifdef VLAN_DEBUG
+       printk(VLAN_DBG __FUNCTION__ ": invalid argument: %p\n", vlandev);
+ #endif
+       return -EINVAL;
+    }
+    
+ #ifdef VLAN_DEBUG
+    printk(VLAN_DBG __FUNCTION__ ": dev: %p\n", vlandev);
+ #endif
+ 
+    /** NOTE:  This will consume the memory pointed to by dent, it seems. */
+    remove_proc_entry(vlandev->name, proc_vlan);
+    vlandev->vlan_dev->dent = NULL;
+    return retval;
+ }
+ 
+ 
+ /*
+  *	Delete directory entry for VLAN port.
+  */
+ int vlan_proc_rem_port(struct vlan_port* vport) {
+    if (!vport) {
+ #ifdef VLAN_DEBUG
+       printk(VLAN_DBG __FUNCTION__ ": vport is NULL.\n");
+ #endif
+       return -EINVAL;
+    }
+    
+ #ifdef VLAN_DEBUG
+    printk(VLAN_DBG __FUNCTION__ ": vport: %p\n", vport);
+ #endif
+    
+    /** NOTE:  This will consume the memory pointed to by dent, it seems. */
+    remove_proc_entry(vport->name, proc_vlan);
+    vport->dent = NULL;
+ 
+    return 0;
+ }
+ 
+ 
+ /****** Proc filesystem entry points ****************************************/
+ 
+ /*
+  *	Verify access rights.
+  */
+ 
+ static int vlan_proc_perms (struct inode* inode, int op) {
+    return 0;
+ }
+ 
+ /*
+  *	Read VLAN proc directory entry.
+  *	This is universal routine for reading all entries in /proc/net/vlan
+  *	directory.  Each directory entry contains a pointer to the 'method' for
+  *	preparing data for that entry.
+  *	o verify arguments
+  *	o allocate kernel buffer
+  *	o call get_info() to prepare data
+  *	o copy data to user space
+  *	o release kernel buffer
+  *
+  *	Return:	number of bytes copied to user space (0, if no data)
+  *		<0	error
+  */
+ static ssize_t vlan_proc_read(struct file* file, char* buf, size_t count,
+                               loff_t *ppos) {
+    struct inode *inode = file->f_dentry->d_inode;
+    struct proc_dir_entry* dent;
+    char* page;
+    int pos, offs, len;
+ 
+    if (count <= 0)
+       return 0;
+ 		
+    dent = inode->u.generic_ip;
+    if ((dent == NULL) || (dent->get_info == NULL))
+       return 0;
+ 		
+    page = kmalloc(VLAN_PROC_BUFSZ, GFP_KERNEL);
+    VLAN_MEM_DBG("page malloc, addr: %p  size: %i\n", page, VLAN_PROC_BUFSZ);
+    
+    if (page == NULL)
+       return -ENOBUFS;
+ 		
+    pos = dent->get_info(page, dent->data, 0, 0);
+    offs = file->f_pos;
+    if (offs < pos) {
+       len = min(pos - offs, count);
+       if (copy_to_user(buf, (page + offs), len)) {
+          return -EFAULT;
+       }
+       file->f_pos += len;
+    }
+    else {
+       len = 0;
+    }
+ 
+    kfree(page);
+    VLAN_FMEM_DBG("page free, addr: %p\n", page);
+    return len;
+ }/* vlan_proc_read */
+ 
+ 
+ static int vlan_proc_get_vlan_info(char* buf, unsigned int cnt) {
+    struct net_device* vlandev = NULL;
+    struct vlan_group* grp = NULL;
+    int i = 0;
+    struct vlan_port_node* port = NULL;
+    
+    printk(VLAN_DBG __FUNCTION__ ": cnt == %i\n", cnt);
+ 
+    for (grp = p802_1Q_vlan_list; grp != NULL; grp = grp->next) {
+       /* loop through all devices for this device */
+       printk(VLAN_DBG __FUNCTION__ ": found a group, addr: %p\n", grp);
+       for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
+          /* printk(VLAN_DBG __FUNCTION__ ": checking index[%i]\n", i); */
+          if ((vlandev = grp->vlan_devices[i])) {
+             printk(VLAN_DBG __FUNCTION__ ": found a vlan_dev, addr: %p\n", vlandev);
+             if ((cnt + 100) > VLAN_PROC_BUFSZ) {
+                if ((cnt + strlen(term_msg)) >= VLAN_PROC_BUFSZ) {
+                   /* should never get here */
+                   return cnt;
+                }
+                else {
+                   cnt += sprintf(buf + cnt, "%s", term_msg);
+                   return cnt;
+                }
+             }/* if running out of buffer space */
+             else {
+                if (!vlandev->vlan_dev) {
+                   printk(KERN_ERR __FUNCTION__ ": ERROR: vlandev->vlan_dev is NULL\n");
+                }
+                else {
+                   printk(VLAN_DBG __FUNCTION__ ": got a good vlandev, addr: %p\n", vlandev->vlan_dev);
+                   cnt += sprintf(buf + cnt, "%-15s| %d\n\tPorts:\t",
+                                  vlandev->name, vlandev->vlan_dev->vlan_id);
+                   /* now show all ports relating to this VLAN */
+                   for (port = vlandev->vlan_dev->ports; port; port = port->next) {
+                      /* printk(VLAN_DBG __FUNCTION__ ": found a port\n"); */
+                      cnt += sprintf(buf + cnt, "%s\t", port->port->real_dev->name);
+ 
+                      if ((cnt + 100) > VLAN_PROC_BUFSZ) {
+                         if ((cnt + strlen(term_msg)) >= VLAN_PROC_BUFSZ) {
+                            /* should never get here */
+                            return cnt;
+                         }
+                         else {
+                            cnt += sprintf(buf + cnt, "%s", term_msg);
+                            return cnt;
+                         }
+                      }/* if running out of buffer space */
+                   }/* for */
+                   cnt += sprintf(buf + cnt, "\n");
+                }/* else */
+             }/* else */
+          }/* if we have a vlan of this number */
+       }/* for all VLAN's */
+    }/* for each vlan group, default is only one.*/
+    
+    return cnt;
+ }/* vlan_proc_get_vlan_info */
+ 
+ /*
+  *	Prepare data for reading 'Config' entry.
+  *	Return length of data.
+  */
+ 
+ static int vlan_config_get_info(char* buf, char** start, off_t offs, int len) {
+    strcpy(buf, conf_hdr);
+    return vlan_proc_get_vlan_info(buf, (unsigned int)(strlen(conf_hdr)));
+ }
+ 
+ 
+ /*
+  *	Prepare data for reading <device> entry.
+  *	Return length of data.
+  *
+  *	On entry, the 'start' argument will contain a pointer to VLAN device
+  *	data space.
+  */
+ 
+ static int vlandev_get_info(char* buf, char** start, off_t offs, int len) {
+    struct net_device* vlandev = (void*)start;
+    struct net_device_stats* stats;
+    int cnt = 0;
+    struct vlan_port_node* port = NULL;
+ 
+ #ifdef VLAN_DEBUG
+    printk(VLAN_DBG __FUNCTION__ ": vlandev: %p\n", vlandev);
+ #endif
+    
+    if ((vlandev == NULL) || (!vlandev->vlan_dev)) {
+       return 0;
+    }
+ 
+    cnt += sprintf(buf + cnt, "%s  VID: %d  Ports:  ",
+                   vlandev->name, vlandev->vlan_dev->vlan_id);
+    /* now show all ports relating to this VLAN */
+    for (port = vlandev->vlan_dev->ports; port; port = port->next) {
+       /* printk(VLAN_DBG __FUNCTION__ ": found a port\n"); */
+       cnt += sprintf(buf + cnt, "%s\t", port->port->real_dev->name);
+       
+       if ((cnt + 100) > VLAN_PROC_BUFSZ) {
+          if ((cnt + strlen(term_msg)) >= VLAN_PROC_BUFSZ) {
+             /* should never get here */
+             return cnt;
+          }
+          else {
+             cnt += sprintf(buf + cnt, "%s", term_msg);
+             return cnt;
+          }
+       }/* if running out of buffer space */
+    }/* for */
+    cnt += sprintf(buf + cnt, "\n");
+ 
+    stats = (struct net_device_stats*)(vlandev->priv);
+    
+    cnt += sprintf(buf + cnt, "%30s: %12lu\n",
+                   "total frames received", stats->rx_packets);
+ 
+    cnt += sprintf(buf + cnt, "%30s: %12lu\n",
+                   "total bytes received", stats->rx_bytes);
+    
+    cnt += sprintf(buf + cnt, "%30s: %12lu\n",
+                   "Broadcast/Multicast Rcvd", stats->multicast);
+    
+    cnt += sprintf(buf + cnt, "\n%30s: %12lu\n",
+                   "total frames transmitted", stats->tx_packets);
+    
+    cnt += sprintf(buf + cnt, "%30s: %12lu\n",
+                   "total bytes transmitted", stats->tx_bytes);
+    return cnt;
+ }
+ 
+ 
+ /*
+  *	Prepare data for reading <port> entry.
+  *	Return length of data.
+  *
+  *	On entry, the 'start' argument will contain a pointer to VLAN port
+  *	data space.
+  */
+ 
+ static int vlanport_get_info(char* buf, char** start, off_t offs, int len) {
+    struct vlan_port* port = (void*)start;
+    int cnt = 0;
+    int z = 0;
+    struct vlan_entry* v_entry = NULL;
+    
+    if (port == NULL) {
+       return 0;
+    }
+ 
+    cnt += sprintf(buf + cnt, "%s", ports_hdr);
+    
+    cnt += sprintf(buf + cnt, "%-15s| %i \t\t\t%i\n\tRX Allow: ",
+                   port->real_dev->name, port->default_action,
+                   port->flags);
+             
+    /* show RX Allow group, ie the VLAN ID's we'll receive on this port */
+    for (z = 0; z<VLAN_FILTER_HASH_TBL_LEN; z++) {
+       v_entry = port->rx_allow_group.vlan_table[z];
+       for ( ; v_entry; v_entry = v_entry->next) {
+          cnt += sprintf(buf + cnt, "%i(%hx)\t", v_entry->vlan_id, v_entry->flags);
+          
+          if ((cnt + 100) > VLAN_PROC_BUFSZ) {
+             if ((cnt + strlen(term_msg)) >= VLAN_PROC_BUFSZ) {
+                /* should never get here */
+                return cnt;
+             }
+             else {
+                cnt += sprintf(buf + cnt, "%s", term_msg);
+                return cnt;
+             }
+          }/* if running out of buffer space */
+       }/* for */
+    }//for
+    
+    cnt += sprintf(buf + cnt, "\n\tRX Deny: ");
+    /* show RX Deny group, ie the VLAN ID's we specifically WON'T Receive
+     * on this port */
+    for (z = 0; z<VLAN_FILTER_HASH_TBL_LEN; z++) {
+       v_entry = port->rx_deny_group.vlan_table[z];
+       for ( ; v_entry; v_entry = v_entry->next) {
+          cnt += sprintf(buf + cnt, "%i(%hx)\t", v_entry->vlan_id, v_entry->flags);
+          
+          if ((cnt + 100) > VLAN_PROC_BUFSZ) {
+             if ((cnt + strlen(term_msg)) >= VLAN_PROC_BUFSZ) {
+                /* should never get here */
+                return cnt;
+             }
+             else {
+                cnt += sprintf(buf + cnt, "%s", term_msg);
+                return cnt;
+             }
+          }/* if running out of buffer space */
+       }/* for */
+    }
+    
+    cnt += sprintf(buf + cnt, "\n\tTX Allow: ");
+    for (z = 0; z<VLAN_FILTER_HASH_TBL_LEN; z++) {
+       v_entry = port->tx_allow_group.vlan_table[z];
+       for ( ; v_entry; v_entry = v_entry->next) {
+          cnt += sprintf(buf + cnt, "%i(%hx)\t", v_entry->vlan_id, v_entry->flags);
+          
+          if ((cnt + 100) > VLAN_PROC_BUFSZ) {
+             if ((cnt + strlen(term_msg)) >= VLAN_PROC_BUFSZ) {
+                /* should never get here */
+                return cnt;
+             }
+             else {
+                cnt += sprintf(buf + cnt, "%s", term_msg);
+                return cnt;
+             }
+          }/* if running out of buffer space */
+       }/* for */
+    }
+    
+    cnt += sprintf(buf + cnt, "\n\tTX Deny: ");
+    for (z = 0; z<VLAN_FILTER_HASH_TBL_LEN; z++) {
+       v_entry = port->tx_deny_group.vlan_table[z];
+       for ( ; v_entry; v_entry = v_entry->next) {
+          cnt += sprintf(buf + cnt, "%i(%hx)\t", v_entry->vlan_id, v_entry->flags);
+          
+          if ((cnt + 100) > VLAN_PROC_BUFSZ) {
+             if ((cnt + strlen(term_msg)) >= VLAN_PROC_BUFSZ) {
+                /* should never get here */
+                return cnt;
+             }
+             else {
+                cnt += sprintf(buf + cnt, "%s", term_msg);
+                return cnt;
+             }
+          }/* if running out of buffer space */
+       }/* for */
+    }/* for */
+    cnt += sprintf(buf + cnt, "\n");
+    return cnt;
+ }
+ 
+ /*
+  *	End
+  */
+  
+ #else
+ 
+ /*
+  *	No /proc - output stubs
+  */
+  
+ __initfunc(int vlan_proc_init(void))
+ {
+ 	return 0;
+ }
+ 
+ void vlan_proc_cleanup(void)
+ {
+ 	return;
+ }
+ 
+ int vlan_proc_add_port(struct net_device *vlandev)
+ {
+ 	return 0;
+ }
+ 
+ int vlan_proc_rem_port(struct net_device *vlandev)
+ {
+ 	return 0;
+ }
+ 
+ 
+ int vlan_proc_add_dev(struct net_device *vlandev)
+ {
+ 	return 0;
+ }
+ 
+ int vlan_proc_rem_dev(struct net_device *vlandev)
+ {
+ 	return 0;
+ }
+ 
+ #endif
diff -P -C 2 -X /home/greear/exclude.list --recursive linux/net/802_1Q/vlanproc.h linux.dev/net/802_1Q/vlanproc.h
*** linux/net/802_1Q/vlanproc.h	Wed Dec 31 17:00:00 1969
--- linux.dev/net/802_1Q/vlanproc.h	Fri Feb 25 18:01:57 2000
***************
*** 0 ****
--- 1,29 ----
+ 
+ #ifndef __BEN_VLAN_PROC_INC__
+ #define __BEN_VLAN_PROC_INC__
+ 
+ 
+ int vlan_proc_init(void);
+ 
+ int vlan_proc_rem_dev(struct net_device* vlandev);
+ int vlan_proc_add_dev (struct net_device* vlandev);
+ int vlan_proc_rem_port(struct vlan_port* vlandev);
+ int vlan_proc_add_port (struct vlan_port* vlandev);
+ void vlan_proc_cleanup (void);
+ 
+ 
+ #define	VLAN_PROC_BUFSZ	(4096)	/* buffer size for printing proc info */
+ 
+ /****** Data Types **********************************************************/
+ 
+ /*
+ typedef struct vlan_stat_entry {
+    struct vlan_stat_entry *	next;
+    char *description;		* description string *
+    void *data;			* -> data *
+    unsigned data_type;		* data type *
+ } vlan_stat_entry_t;
+ */
+ 
+ 
+ #endif
diff -P -C 2 -X /home/greear/exclude.list --recursive linux/net/Config.in linux.dev/net/Config.in
*** linux/net/Config.in	Fri Feb 18 15:51:22 2000
--- linux.dev/net/Config.in	Fri Feb 25 18:01:57 2000
***************
*** 41,44 ****
--- 41,45 ----
  	    bool '    Do NOT send ICMP if no neighbour' CONFIG_ATM_CLIP_NO_ICMP
  	 fi
+ 
        fi
        tristate '  LAN Emulation (LANE) support' CONFIG_ATM_LANE
***************
*** 47,50 ****
--- 48,54 ----
        fi
     fi
+ 
+    bool '802.1Q VLAN Support (EXPERIMENTAL)' CONFIG_VLAN_802_1Q
+ 
  fi
  
diff -P -C 2 -X /home/greear/exclude.list --recursive linux/net/Makefile linux.dev/net/Makefile
*** linux/net/Makefile	Fri Feb 18 15:51:22 2000
--- linux.dev/net/Makefile	Fri Feb 25 18:01:57 2000
***************
*** 11,15 ****
  ALL_SUB_DIRS := 802 ax25 bridge core ethernet ipv4 ipv6 ipx unix appletalk \
  		netrom rose lapb x25 wanrouter netlink sched packet sunrpc \
! 		econet irda decnet atm khttpd
  SUB_DIRS     := core ethernet sched
  MOD_LIST_NAME := NET_MISC_MODULES
--- 11,15 ----
  ALL_SUB_DIRS := 802 ax25 bridge core ethernet ipv4 ipv6 ipx unix appletalk \
  		netrom rose lapb x25 wanrouter netlink sched packet sunrpc \
! 		econet irda decnet atm khttpd 802_1Q
  SUB_DIRS     := core ethernet sched
  MOD_LIST_NAME := NET_MISC_MODULES
***************
*** 68,71 ****
--- 68,75 ----
  ifeq ($(CONFIG_BRIDGE),y)
  SUB_DIRS += bridge
+ endif
+ 
+ ifeq ($(CONFIG_VLAN_802_1Q),y)
+ SUB_DIRS += 802_1Q
  else
    ifeq ($(CONFIG_BRIDGE),m)
diff -P -C 2 -X /home/greear/exclude.list --recursive linux/net/core/dev.c linux.dev/net/core/dev.c
*** linux/net/core/dev.c	Sun Feb 20 21:14:55 2000
--- linux.dev/net/core/dev.c	Fri Feb 25 18:07:28 2000
***************
*** 92,95 ****
--- 92,100 ----
  #endif	/* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */
  #ifdef CONFIG_PLIP
+ 
+ #ifdef CONFIG_VLAN_802_1Q
+ #include "../802_1Q/vlan.h"
+ #endif
+ 
  extern int plip_init(void);
  #endif
***************
*** 113,119 ****
   *
   *	Why 16. Because with 16 the only overlap we get on a hash of the
!  *	low nibble of the protocol value is RARP/SNAP/X.25. 
   *
   *		0800	IP
   *		0001	802.3
   *		0002	AX.25
--- 118,132 ----
   *
   *	Why 16. Because with 16 the only overlap we get on a hash of the
!  *	low nibble of the protocol value is RARP/SNAP/X.25.
!  *
!  *      NOTE:  That is no longer true with the addition of VLAN tags.  Not
!  *             sure which should go first, but I bet it won't make much
!  *             difference if we are running VLANs.  The good news is that
!  *             this protocol won't be in the list unless compiled in, so
!  *             the average user (w/out VLANs) will not be adversly affected.
!  *             --BLG
   *
   *		0800	IP
+  *		8100    802.1Q VLAN
   *		0001	802.3
   *		0002	AX.25
***************
*** 355,360 ****
  	/*
  	 *	If you need over 100 please also fix the algorithm...
  	 */
! 	for (i = 0; i < 100; i++) {
  		sprintf(buf,name,i);
  		if (__dev_get_by_name(buf) == NULL) {
--- 368,375 ----
  	/*
  	 *	If you need over 100 please also fix the algorithm...
+          *      Increased it to deal with VLAN interfaces.  It is unlikely
+          *      that this many will ever be added, but it can't hurt! -BLG
  	 */
! 	for (i = 0; i < 8192; i++) {
  		sprintf(buf,name,i);
  		if (__dev_get_by_name(buf) == NULL) {
***************
*** 363,367 ****
  		}
  	}
! 	return -ENFILE;	/* Over 100 of the things .. bail out! */
  }
  
--- 378,382 ----
  		}
  	}
! 	return -ENFILE;	/* Over 8192 of the things .. bail out! */
  }
  
diff -P -C 2 -X /home/greear/exclude.list --recursive linux/net/ethernet/eth.c linux.dev/net/ethernet/eth.c
*** linux/net/ethernet/eth.c	Sat Jan 22 12:54:57 2000
--- linux.dev/net/ethernet/eth.c	Fri Feb 25 18:01:58 2000
***************
*** 181,184 ****
--- 181,187 ----
   *	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.
+  *
+  *  NOTE:  It is likely that you will want to change vlan_type_trans in
+  *         802_1Q/vlan.c if you change anything here.
   */
   
***************
*** 187,195 ****
  	struct ethhdr *eth;
  	unsigned char *rawp;
! 	
! 	skb->mac.raw=skb->data;
! 	skb_pull(skb,dev->hard_header_len);
! 	eth= skb->mac.ethernet;
! 	
  	if(*eth->h_dest&1)
  	{
--- 190,210 ----
  	struct ethhdr *eth;
  	unsigned char *rawp;
! 
!         skb->mac.raw=skb->data;
! 
! #ifdef CONFIG_VLAN_802_1Q
! 	/* Moving this below to be more selective.  Reason is that for VLAN
!          * devices, we do not want to pull the header, we'll let the VLAN
!          * device do that instead.  This makes default vlans (based on incoming
!          * port), much more sane!  --BLG
!          */
!         
!         /* skb_pull(skb,dev->hard_header_len); */
! #else
!         skb_pull(skb,dev->hard_header_len);
! #endif
!         
!         eth= skb->mac.ethernet;
! 
  	if(*eth->h_dest&1)
  	{
***************
*** 213,217 ****
  			skb->pkt_type=PACKET_OTHERHOST;
  	}
! 	
  	if (ntohs(eth->h_proto) >= 1536)
  		return eth->h_proto;
--- 228,247 ----
  			skb->pkt_type=PACKET_OTHERHOST;
  	}
! 
! #ifdef CONFIG_VLAN_802_1Q
!         if ((ntohs(eth->h_proto) == ETH_P_802_1Q) ||
!             (dev->default_vid > 0)) {
!            /* then we have to convert this into a VLAN looking packet.
!             * We'll wait to do that in the VLAN protocol handler.
!             *
!             * NOTE:  We DO NOT PULL ANYTHING FROM THE SKB HERE!!!
!             */
!            return __constant_htons(ETH_P_802_1Q);
!         }
!         else {
!            skb_pull(skb, dev->hard_header_len);
!         }
! #endif
!         
  	if (ntohs(eth->h_proto) >= 1536)
  		return eth->h_proto;
diff -P -C 2 -X /home/greear/exclude.list --recursive linux/net/protocols.c linux.dev/net/protocols.c
*** linux/net/protocols.c	Mon Aug 23 09:56:32 1999
--- linux.dev/net/protocols.c	Fri Feb 25 18:01:58 2000
***************
*** 35,38 ****
--- 35,42 ----
  #endif
  
+ #ifdef CONFIG_VLAN_802_1Q
+ extern void vlan_proto_init(struct net_proto* pro);
+ #endif
+ 
  #if defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE)
  #define NEED_802
***************
*** 179,182 ****
--- 183,190 ----
  #endif
  
+ #ifdef CONFIG_VLAN_802_1Q
+   { "VLAN",     vlan_proto_init },                    /* 802.1Q VLAN Support. --BLG */
+ #endif
+   
    { NULL,	NULL		}			/* End marker			*/
  };
