Tizen 2.1 base tizen_2.1 2.1b_release 2.2_release accepted/tizen_2.1/20130425.040517 submit/tizen_2.1/20130424.230137 submit/tizen_2.2/20130714.154732
authorJinkun Jang <jinkun.jang@samsung.com>
Tue, 12 Mar 2013 16:45:28 +0000 (01:45 +0900)
committerJinkun Jang <jinkun.jang@samsung.com>
Tue, 12 Mar 2013 16:45:28 +0000 (01:45 +0900)
72 files changed:
CHANGELOG [new file with mode: 0644]
CVS/Entries [new file with mode: 0644]
CVS/Repository [new file with mode: 0644]
CVS/Root [new file with mode: 0644]
MakeInclude [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
candela_2.4.21.patch [new file with mode: 0644]
contrib/CVS/Entries [new file with mode: 0644]
contrib/CVS/Repository [new file with mode: 0644]
contrib/CVS/Root [new file with mode: 0644]
contrib/README [new file with mode: 0644]
contrib/network [new file with mode: 0644]
contrib/vlan_2.2-full.patch [new file with mode: 0644]
contrib/vlan_2.2-module.patch [new file with mode: 0644]
howto.html [new file with mode: 0644]
libpcap-0.4/CVS/Entries [new file with mode: 0644]
libpcap-0.4/CVS/Repository [new file with mode: 0644]
libpcap-0.4/CVS/Root [new file with mode: 0644]
libpcap-0.4/SUNOS4/CVS/Entries [new file with mode: 0644]
libpcap-0.4/SUNOS4/CVS/Repository [new file with mode: 0644]
libpcap-0.4/SUNOS4/CVS/Root [new file with mode: 0644]
libpcap-0.4/bpf/CVS/Entries [new file with mode: 0644]
libpcap-0.4/bpf/CVS/Repository [new file with mode: 0644]
libpcap-0.4/bpf/CVS/Root [new file with mode: 0644]
libpcap-0.4/bpf/net/CVS/Entries [new file with mode: 0644]
libpcap-0.4/bpf/net/CVS/Repository [new file with mode: 0644]
libpcap-0.4/bpf/net/CVS/Root [new file with mode: 0644]
libpcap-0.4/lbl/CVS/Entries [new file with mode: 0644]
libpcap-0.4/lbl/CVS/Repository [new file with mode: 0644]
libpcap-0.4/lbl/CVS/Root [new file with mode: 0644]
libpcap-0.4/linux-include/CVS/Entries [new file with mode: 0644]
libpcap-0.4/linux-include/CVS/Repository [new file with mode: 0644]
libpcap-0.4/linux-include/CVS/Root [new file with mode: 0644]
libpcap-0.4/linux-include/netinet/CVS/Entries [new file with mode: 0644]
libpcap-0.4/linux-include/netinet/CVS/Repository [new file with mode: 0644]
libpcap-0.4/linux-include/netinet/CVS/Root [new file with mode: 0644]
libpcap-0.4/net/CVS/Entries [new file with mode: 0644]
libpcap-0.4/net/CVS/Repository [new file with mode: 0644]
libpcap-0.4/net/CVS/Root [new file with mode: 0644]
macvlan_config [new file with mode: 0755]
macvlan_config.c [new file with mode: 0644]
packaging/vconfig.manifest [new file with mode: 0644]
packaging/vconfig.spec [new file with mode: 0644]
tcpdump-3.4/CVS/Entries [new file with mode: 0644]
tcpdump-3.4/CVS/Repository [new file with mode: 0644]
tcpdump-3.4/CVS/Root [new file with mode: 0644]
tcpdump-3.4/lbl/CVS/Entries [new file with mode: 0644]
tcpdump-3.4/lbl/CVS/Repository [new file with mode: 0644]
tcpdump-3.4/lbl/CVS/Root [new file with mode: 0644]
tcpdump-3.4/linux-include/CVS/Entries [new file with mode: 0644]
tcpdump-3.4/linux-include/CVS/Repository [new file with mode: 0644]
tcpdump-3.4/linux-include/CVS/Root [new file with mode: 0644]
tcpdump-3.4/linux-include/net/CVS/Entries [new file with mode: 0644]
tcpdump-3.4/linux-include/net/CVS/Repository [new file with mode: 0644]
tcpdump-3.4/linux-include/net/CVS/Root [new file with mode: 0644]
tcpdump-3.4/linux-include/netinet/CVS/Entries [new file with mode: 0644]
tcpdump-3.4/linux-include/netinet/CVS/Repository [new file with mode: 0644]
tcpdump-3.4/linux-include/netinet/CVS/Root [new file with mode: 0644]
tcpdump-3.4/linux-include/sys/CVS/Entries [new file with mode: 0644]
tcpdump-3.4/linux-include/sys/CVS/Repository [new file with mode: 0644]
tcpdump-3.4/linux-include/sys/CVS/Root [new file with mode: 0644]
vconfig [new file with mode: 0755]
vconfig.8 [new file with mode: 0644]
vconfig.c [new file with mode: 0644]
vconfig.h [new file with mode: 0644]
vconfig.o [new file with mode: 0644]
vconfig.spec [new file with mode: 0644]
vlan.html [new file with mode: 0644]
vlan_2.2.patch [new file with mode: 0644]
vlan_test.pl [new file with mode: 0644]
vlan_test2.pl [new file with mode: 0644]

diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644 (file)
index 0000000..85c60bf
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,363 @@
+This file should be prepended to each time a release is made.
+
+Date:            Sept 30, 2003
+Version:         1.8
+Kernel Version:  2.4.21+
+Changes:         Compile fixes for newer gcc, VLAN rework, inclusion of entire
+                 Candela Technologies kernel networking patch (MAC-VLANs too).
+
+Date:            March 11, 2003
+Version:         1.7
+Kernel Version:  2.4.14+
+Changes:         Added Alex's MAC-Vlan code to the repository.  See README for more.
+
+
+Date:            March 24, 2002
+Version:         1.6
+Kernel Version:  2.4.14+
+Changes:         Removed 2.4 kernel patch from VLAN distribution..it's now in the
+                 standard linux kernel.  Other updates include vconfig changes
+                 to fix some compile problems, and to enable cross-compiling to
+                 ARM (this assumes you are using the Intrinsyc cross-compiler in
+                 it's standard location).
+
+
+Date:            Oct 20, 2001
+Version:         1.5
+Kernel Version:  2.4.12-pre5
+Changes:
+                 Mostly added other peoples fixes and patches (thanks folks!)
+                 Finally fixed mc-list leakage (Ard van Breemen)
+                 Flush mc-list at vlan-destory (Ard van Breemen)
+                 Add vconfig man page to distribution (Ard van Breemen)
+                 Fix problem with /proc and renaming VLAN devices (af AT devcon D.T net)
+                 Add relatively large change by Nick Eggelston that makes VLAN
+                    devices more transparent to tools like tcpdump and other raw
+                    packet snoopers.  This will only be enabled when the REORDER_HDR
+                    flag is set.
+
+
+Date:            August 16, 2001
+Version:         1.4
+Kernel Version:  2.4.9-pre4
+Status:          Should be stable, but a decent amount of rework went into
+                 this one...
+Changes:         
+                 Code should no longer require /proc interface in order to
+                 get at the IOCTLs. The IOCTLs are now tied to sockets.
+                 When using modules, it may auto-load now, too...
+
+                 Fixed format string error in proc fs display.
+
+                 Fixed crash bug relating to memory allocation with locks
+                 held (we now use GF_ATOMIC).
+
+                 hard_start_xmit will now grow the packet header if there
+                 is not enough headroom.  This may fix an MPLS-over-VLAN problem,
+                 though the real solution is to make MPLS allocate
+                 more headroom anyway...
+
+                 vconfig was changed to use the new IOCTL API, and the old
+                 vconfig WILL NOT WORK with this or any newer patches...
+
+
+Date:            August 5, 2001
+Version:         1.0.3
+Kernel Version:  2.4.7
+Status:          Should be stable, but a decent amount of rework went into
+                 this one...
+Changes:         
+                 Re-worked code to comply with linux network code gurus'
+                 wishes.  This included several boundary case fixes, including
+                 some that could crash your kernel.
+
+                 The default naming scheme is eth0.5 now, for VID == 5 on
+                 eth0.  Use vconfig to change the naming scheme if you want.
+
+                 There were *NO* changes to the 2.2 series patch, and there
+                 probably won't be any more changes to it, ever!
+
+
+Date:            April 16, 2001
+Version:         1.0.1
+Kernel Version:  2.2.18/19, 2.4.3-pre3
+Status:          Very similar to 1.0.0, should be relatively stable.
+Changes:         
+                 Incorporated a fix for changing a MAC on a VLAN, it now
+                 correctly sets PACKET_HOST.
+                   Thanks to Martin Bokaemper for this one.
+
+                 The 2.4 series patch should now compile as a module, thanks
+                 to a tweak from someone who's mail I have lost!  Anyway, 3
+                 cheers to the un-named coder!
+
+                 There were *NO* changes to the 2.2 series patch, though I did
+                 verify that it seems to work fine with the 2.2.19 kernel.
+
+
+
+Date:            January 14, 2001
+Version:         1.0.0
+Kernel Version:  2.2.18, 2.4.0
+Status:          Fairly similar to 0.0.15, should be relatively stable.
+Changes:         
+
+                Really fixed (and tested) MAC change-ability. When you set the
+                MAC address on a VLAN, it will also attempt to set the underlying
+                device to PROMISCious mode (otherwise, the VLAN will not
+                receive any packets.) 
+
+                Hashed-device lookup is disabled by default because some people
+                had trouble with the 'lo' device. Please feel free to re-enable by
+                editing the line in net/core/dev.
+                 (search for #define BEN_FAST_DEV_LOOKUP). 
+
+                vconfig should warn when creating VLAN 1, because that VLAN is
+                not compatible with many switches.
+
+
+Date:            December 31, 2000
+Version:         0.0.15
+Kernel Version:  2.2.18, 2.4.prerelease
+Status:          This one is pretty fresh..beware, especially the 2.4 patch.
+Changes:         
+
+                Merged most of Matti Aarnio's patches. This means no significant patch
+                 to eth.c now, and will help port VLANs to non-ethernet devices
+                 (ie ppp, TokenRing??).
+
+                 Setting the MAC address should work now..I think it was broken before.
+
+                 Miscellaneous code re-organization to make patches to existing files smaller. 
+
+
+Date:            October 26, 2000
+Version:         0.0.14
+Kernel Version:  2.2.17, 2.4.pre9
+Status:          Seems stable.
+Changes:         
+                Removed vlan-space-per-machine, so vlan-space-per-NIC is mandatory now.
+
+                DHCP might work now, as I've added support for encapsulating regular ethernet
+                        frames if they are sent to the vlan driver.
+
+                Fixed up the name/index hashing stuff to handle changing the name on a device.
+
+                Took out default VID & default priority, as their usefullness was in question,
+                 and the code was broken anyway.
+
+
+Date:            October 11, 2000
+Version:         0.0.13
+Kernel Version:  2.2.17, 2.4.pre9
+Status:          BUSTED!!  Don't use it.
+Changes:         
+               Added support for MULTICAST to the VLAN devices. Thanks to
+               Gleb & Co for most of that code. 
+        
+               Added the ability to set the MAC address on the VLAN. For now,
+               you'll either need to set your Ethernet NIC into PROMISC mode, or
+               maybe figure out some multi-cast ethernet address to set on the
+               NIC. This has not been tested well at all. 
+        
+               Added a hashed device-name lookup scheme. This greatly speeds
+               up ifconfig -a. I was able to run an ifconfig -a in 20 seconds on a
+               Celeron 500, with 4000 vlan devices configured!! 
+        
+               Added vlan_test.pl to help me find dumb bugs. Feel free to make this
+               much more powerful, and send the code back to me! 
+        
+               vconfig.c has been converted to C code now, instead of C++.
+               Thanks to MATHIEU. 
+        
+               Significantly cleaned up the code w/out decreasing any useful
+               functionality, I believe.
+
+               Removed the dhcp stuff from the VLAN distribution.
+
+
+Date:            August 27, 2000
+Version:         0.0.12
+Kernel Version:  2.2.16, 2.4.pre7
+Status:          This one turned out pretty stable, no known bugs.
+Changes:         
+
+               Added ability to re-order the VLAN packet so that it looks
+               like a real ethernet packet for the ingress pathway.  This
+               should help DHCP and other programs that insist on reading
+               the raw buffer and then make assumptions about byte offsets.
+               I don't have a good way to test this fully, so consider it
+               experimental :)  This behavior can be changed at run-time,
+               and is set on a per-VLAN basis.  The default is NOT to reorder
+               the header, which has been the only behavior up untill this
+               point.  The vconfig program can set/clear the flag, by using
+               a VLAN IOCTL.  You can read the flag's value from  the
+               /proc/net/vlan/vlan* files.
+               
+               You can also set a default priority on a NON-VLAN device.
+               This priority will only be used when the default_VID for the
+               device is set as well.  This priority won't be mapped anywhere,
+               just copied straight into the skb->priority.  It is a uint16.
+
+               The 2.3 patch is now the 2.4 patch, and it has been tested
+               against 2.4.pre7.
+
+
+Date:            April 23, 2000
+Version:         0.0.11
+Kernel Version:  2.2.13 & 2.2.14, 2.3.99
+Status:          As of August 27, this seems like a very stable patch.
+Changes:         
+                Added real support for PRIORITY.  Through IOCTL calls (see the
+                vconfig program), you can set explicit ingress and egress mappings
+                to/from the VLAN QOS bits and the sk_buff->priority field.  This
+                is not tested very well, as I don't know much about how people really
+                use the priority field...  Took out the round-robin aggretation that
+                went in in rls 0.10, as it was mainly just a hack, and doing link
+                aggregation at a lower level and then putting VLAN on top of that
+                virtual device probably makes more sense.  The vconfig program
+                changed to support the new features..here's it's new usage:<br>
+
+       Usage: add             [interface-name] [vlan_id]
+              rem             [vlan-name]
+              set_dflt        [interface-name] [vlan_id]
+              add_port        [port-name]      [vlan_id]
+              rem_port        [port-name]      [vlan_id]
+              set_egress_map  [vlan-name]      [skb_priority]   [vlan_qos]
+              set_ingress_map [vlan-name]      [skb_priority]   [vlan_qos]
+              set_name_type   [name-type]
+              set_bind_mode   [bind-type]
+
+       * The [interface-name] is the name of the ethernet card that hosts
+         the VLAN you are talking about.
+       * The port-name is the name of the physical interface that a VLAN
+         may be attached to.
+       * The vlan_id is the identifier (0-4095) of the VLAN you are operating on.
+       * skb_priority is the priority in the socket buffer (sk_buff).
+       * vlan_qos is the 3 bit priority in the VLAN header
+       * name-type:  VLAN_PLUS_VID (vlan0005), VLAN_PLUS_VID_NO_PAD (vlan5),
+                     DEV_PLUS_VID (eth0.0005), DEV_PLUS_VID_NO_PAD (eth0.5)
+       * bind-type:  PER_DEVICE  # Allows vlan 5 on eth0 and eth1 to be unique.
+                     PER_KERNEL  # Forces vlan 5 to be unique across all devices.
+
+                The 2.3 patches have been ported foward to 2.3.99, thanks to
+                Patrick for the vlanproc.c updates!
+
+
+
+Date:            February 26, 2000
+Version:         0.0.10
+Kernel Version:  2.2.13 & 2.2.14, 2.3.47
+Status:          Added several new features in the critical path...beware!
+Changes:         
+                Added support for PRIORITY.  The way it works is that the lower
+                3 bits of the skb->priority are set into the PRIORITY field in
+                the VLAN header.  No special handling is done with priority,
+                but it should be handled by other switches and such.  This has
+                not been tested, but the default case (no priority in the skb)
+                seems to work at least.
+
+                The big change is that you can now aggregate several ethernet
+                ports in a single VLAN.  The packets will be transmitted in a
+                round robin fashion.  In order for this to work, you have
+                to change the MAC addresses on all cards to the same thing,
+                and put the cards in promiscious mode (because most drivers don't
+                __really__ honor the request to set the MAC all the way to
+                the NIC.  This works with two different speed NICs, but I think
+                it will only be really useful if they are the same speed.  Here
+                is how I set them up in my test environment:
+
+               ifdown eth1
+               ifconfig eth1 hw ether 00:40:05:41:00:5e  # This is the MAC of eth0
+               ifup eth1
+       
+               ifconfig eth1 promisc
+
+               /usr/local/bin/vconfig add eth0 5
+               /usr/local/bin/vconfig add_port eth1 5
+               ifconfig vlan0005 192.168.2.1
+
+         On my other machine, I have this:
+               ifdown eth1
+               ifconfig eth1 hw ether 00:48:54:66:68:68  # This is the MAC of eth0
+               ifup eth1
+
+               ifconfig eth1 promisc
+
+               /usr/local/bin/vconfig add eth0 5
+               /usr/local/bin/vconfig add_port eth1 5
+               ifconfig vlan0005 192.168.2.3
+
+                Note that there are now two patches, one for the 2.2 series,
+                and one for the 2.3 series.
+
+
+Date:            February 6, 2000
+Version:         0.0.9
+Kernel Version:  2.2.13 & 2.2.14
+Status:          Mostly solid.  May be issues with adding/removing, but it
+                 works at least most of the time.
+Changes:         Changed the way vlan names are created:  They now have the
+                 VID in the name.  You can revert to the old behavior by
+                 changing an #define in the 802_18/vlan.h file.  Changed
+                 the destruction process for vlans.  Not sure if this fixed the
+                kernel lock problem I found while adding/removing VLAN devices,
+                 and also hacking with DHCP, but the problem seemed to go away.
+                Added patch to dhcp to allow it to work with VLANs.  However,
+                 I don't grok DHCP as well as might be desired, so use at your
+                 own risk!!  Added some debugging code (you have to compile
+                 it in if you need it)
+                
+
+Date:            December 22, 1999
+Version:         0.0.8
+Kernel Version:  2.2.13+
+Status:          ARP seems to fail in certain cases (but not on my machines.)
+Changes:         Fixed compile warnings and a linking problem due to #ifdef's.
+                No major changes in functionality or performance.
+
+Date:            December 5, 1999
+Version:         0.0.7
+Kernel Version:  2.2.13+
+Status:          ARP seems to fail in certain cases (but not on my machines.)
+                Several (many?) ethernet drivers can't handle the extra 4 bytes
+                of VLAN, so the MTU on the network may have to be set to 1496,
+                or fix the ethernet drivers!!
+Changes:         Re-wrote the /proc code to never go above 4k buffers.  This means
+                that each port now has it's own file entry.  Fixed crash bug with
+                removing VLAN devices.  Byte and pkt counters are now updated correctly,
+                and are found in the /proc/net/vlan/<device> file.
+
+
+Date:            October 20, 1999
+Version:         0.0.6
+Kernel Version:  2.2.10+
+Status:          ping -f still kills one of my machines, but it takes longer...and I'm
+                not sure if its the fault of the VLAN code, or maybe some hardware problem.
+Changes:         Coded around an extraneous skb alloc/free so that there should be no
+                 extra buffer copying as compared to an ethernet interface, unless the
+                vlan device spans more than one interface.  Put #ifdef around all printk
+                debugging calls, at least for non-control code (ie no more printk in the
+                critical paths.)
+
+
+Date:            October 19, 1999
+Version:         0.0.6
+Kernel Version:  2.2.10
+Status:          Ping & FTP work, ping -f kills it after some time...not sure why yet.
+Changes:         Got tcpdump working with VLAN pkts (use the -e option).  Got basic VLAN
+                functionality working, though problems remain, including a KERNEL CRASH
+                that can be induced by ping -f on one of the vlan interfaces.  My test
+                setup consists of two linux boxes, each running my modified kernel.
+                Since I have no third-party implementation to test against, it is likely
+                the code is not too right yet!!
+                Performance isn't all that great:  Running a Cyrix 155 <-> a Cyrix 233,
+                connected through a 10bt hub, I get 910 Mbps on regular ethernet,
+                and only 650 Mbps on the VLAN device.  This was using a 30 MB file.
+
+
+Date:            Long time ago.
+Version:         0.0.3
+Kernel Version:  2.2.2
+Status:          Definately broken, but lots of code in there!!
+Changes:         Initial partially functional release (not very functional.)
diff --git a/CVS/Entries b/CVS/Entries
new file mode 100644 (file)
index 0000000..d86968c
--- /dev/null
@@ -0,0 +1,17 @@
+/vlan_2.2.patch/1.7/Mon Jan 15 03:30:45 2001//
+/vconfig.8/1.1/Sat Oct 20 22:57:02 2001//
+/vconfig.spec/1.1/Thu Apr 18 00:11:42 2002//
+/vlan_test.pl/1.5/Wed Aug 28 08:11:49 2002//
+/vlan_test2.pl/1.1/Wed Nov  6 08:34:37 2002//
+D/libpcap-0.4////
+D/tcpdump-3.4////
+/howto.html/1.2/Fri Jul  4 16:53:40 2003//
+/vlan.html/1.26/Tue Apr  8 15:44:00 2003//
+/vconfig.c/1.7/Thu Jul 31 22:43:08 2003//
+/macvlan_config.c/1.4/Tue Aug 12 19:36:36 2003//
+/CHANGELOG/1.19/Tue Sep 30 21:01:55 2003//
+/MakeInclude/1.8/Mon Aug 25 17:08:18 2003//
+/Makefile/1.7/Tue Sep 30 21:04:14 2003//
+/README/1.7/Tue Sep 30 21:03:42 2003//
+/candela_2.4.21.patch/1.4/Tue Sep 30 21:05:04 2003//
+D/contrib////
diff --git a/CVS/Repository b/CVS/Repository
new file mode 100644 (file)
index 0000000..d56118f
--- /dev/null
@@ -0,0 +1 @@
+vlan
diff --git a/CVS/Root b/CVS/Root
new file mode 100644 (file)
index 0000000..469c859
--- /dev/null
+++ b/CVS/Root
@@ -0,0 +1 @@
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
diff --git a/MakeInclude b/MakeInclude
new file mode 100644 (file)
index 0000000..f518572
--- /dev/null
@@ -0,0 +1,35 @@
+#  -*-Makefile-*-
+
+
+#  This is the version of the make utility you wish to use.
+#  On some systems (BSD?) you might want this to be gmake.
+#MAKE=gmake
+MAKE=make
+
+
+VERSION=1.0.2
+
+CUR_DATE=`date '+%y.%m.%d'`
+
+ifeq "${PLATFORM}" ""
+   PLATFORM=x86
+endif
+
+## You may need to change this linux/include part.
+CCFLAGS = -g -D_GNU_SOURCE -Wall -I${HOME}/linux/include
+LDLIBS = # -lm #-lnsl # -lsocket
+
+ARM_TC_BIN = ${HOME}/Intrinsyc/bin
+ARM_TC_LIB = ${HOME}/Intrinsyc/lib
+
+ifeq "${PLATFORM}" "ARM"
+   #echo "Building for ARM platform."
+   STRIP=${ARM_TC_BIN}/arm-linux-strip
+   CC = ${ARM_TC_BIN}/arm-linux-gcc       # this is generally the c compiler, unused AFAIK
+   CCC = ${ARM_TC_BIN}/arm-linux-g++      # this is generally the c++ compiler
+else
+   #echo "Building for x86 platform."
+   STRIP=strip
+   CC = gcc       # this is generally the c compiler, unused AFAIK
+   CCC = g++      # this is generally the c++ compiler
+endif
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..23bb374
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,44 @@
+# makefile template
+
+include MakeInclude
+
+LDLIBS =  
+
+VLAN_OBJS = vconfig.o
+
+ALL_OBJS = ${VLAN_OBJS}
+
+VCONFIG = vconfig                  #program to be created
+
+
+all: ${VCONFIG} macvlan_config
+
+
+#This is pretty silly..
+vconfig.h: Makefile
+       touch vconfig.h
+
+
+$(VCONFIG): $(VLAN_OBJS)
+       $(CC) $(CCFLAGS) $(LDFLAGS) -o $(VCONFIG) $(VLAN_OBJS) $(LDLIBS)
+       $(STRIP) $(VCONFIG)
+
+macvlan_config: macvlan_config.c
+       $(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $<
+
+$(ALL_OBJS): %.o: %.c %.h
+       @echo " "
+       @echo "Making $<"
+       $(CC) $(CCFLAGS) -c $<
+
+clean:
+       rm -f *.o
+
+purge: clean
+       rm -f *.flc ${VCONFIG} macvlan_config vconfig.h
+       rm -f *~
+
+
+
+
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..0f9ce3a
--- /dev/null
+++ b/README
@@ -0,0 +1,12 @@
+The MAC vlan stuff is primarily the work of, and is copy-righted (GPL)
+by:
+
+# (C) Copyright 2001
+# Alex Zeffertt, Cambridge Broadband Ltd, ajz@cambridgebroadband.com
+
+However, I (Ben) reworked the MAC-VLAN code extensively, including re-writing
+all of the locking code.  So, any complaints & bugs should come to me.
+
+
+--Ben Greear (greearb@candelatech.com)
+http://www.candelatech.com/~greear
diff --git a/candela_2.4.21.patch b/candela_2.4.21.patch
new file mode 100644 (file)
index 0000000..8b2f320
--- /dev/null
@@ -0,0 +1,6911 @@
+--- linux-2.4.21/include/linux/if.h    2003-06-13 07:51:38.000000000 -0700
++++ linux-2.4.21.amds/include/linux/if.h       2003-07-30 16:27:15.000000000 -0700
+@@ -48,6 +48,12 @@
+ /* Private (from user) interface flags (netdevice->priv_flags). */
+ #define IFF_802_1Q_VLAN 0x1             /* 802.1Q VLAN device.          */
++#define IFF_PKTGEN_RCV  0x2             /* Registered to receive & consume  Pktgen skbs */
++#define IFF_ACCEPT_LOCAL_ADDRS 0x4      /**  Accept pkts even if they come from a local
++                                         * address.  This lets use send pkts to ourselves
++                                         * over external interfaces (when used in conjunction
++                                         * with SO_BINDTODEVICE
++                                         */
+ #define IF_GET_IFACE  0x0001          /* for querying only */
+--- linux-2.4.21/include/linux/netdevice.h     2003-06-13 07:51:38.000000000 -0700
++++ linux-2.4.21.amds/include/linux/netdevice.h        2003-07-30 16:27:20.000000000 -0700
+@@ -296,7 +296,9 @@
+       unsigned short          flags;  /* interface flags (a la BSD)   */
+       unsigned short          gflags;
+-        unsigned short          priv_flags; /* Like 'flags' but invisible to userspace. */
++        unsigned short          priv_flags; /* Like 'flags' but invisible to userspace,
++                                             * see: if.h for flag definitions.
++                                             */
+         unsigned short          unused_alignment_fixer; /* Because we need priv_flags,
+                                                          * and we want to be 32-bit aligned.
+                                                          */
+@@ -422,12 +424,20 @@
+       int                     (*neigh_setup)(struct net_device *dev, struct neigh_parms *);
+       int                     (*accept_fastpath)(struct net_device *, struct dst_entry*);
++#ifdef CONFIG_NET_SKB_RECYCLING
++      int                     (*skb_recycle) (struct sk_buff *skb);
++      void                    (*mem_reclaim) (struct net_device *dev);
++#endif
+       /* open/release and usage marking */
+       struct module *owner;
+       /* bridge stuff */
+       struct net_bridge_port  *br_port;
++#if defined(CONFIG_MACVLAN) || defined(CONFIG_MACVLAN_MODULE)
++      struct macvlan_port *macvlan_priv;
++#endif
++
+ #ifdef CONFIG_NET_FASTROUTE
+ #define NETDEV_FASTROUTE_HMASK 0xF
+       /* Semi-private data. Keep it at the end of device struct. */
+@@ -438,6 +448,7 @@
+       /* this will get initialized at each interface type init routine */
+       struct divert_blk       *divert;
+ #endif /* CONFIG_NET_DIVERT */
++
+ };
+--- linux-2.4.21/net/core/dev.c        2003-06-13 07:51:39.000000000 -0700
++++ linux-2.4.21.amds/net/core/dev.c   2003-07-30 16:20:41.000000000 -0700
+@@ -1,4 +1,4 @@
+-/*
++/* -*-linux-c-*-
+  *    NET3    Protocol independent device support routines.
+  *
+  *            This program is free software; you can redistribute it and/or
+@@ -82,6 +82,7 @@
+ #include <linux/interrupt.h>
+ #include <linux/if_ether.h>
+ #include <linux/netdevice.h>
++#include <linux/ethtool.h>
+ #include <linux/etherdevice.h>
+ #include <linux/notifier.h>
+ #include <linux/skbuff.h>
+@@ -109,6 +110,11 @@
+ #endif
++#if defined(CONFIG_NET_PKTGEN) || defined(CONFIG_NET_PKTGEN_MODULE)
++#include "pktgen.h"
++#endif
++
++
+ /* This define, if set, will randomly drop a packet when congestion
+  * is more than moderate.  It helps fairness in the multi-interface
+  * case when one of them is a hog, but it kills performance for the
+@@ -1131,7 +1137,7 @@
+   =======================================================================*/
+ int netdev_max_backlog = 300;
+-int weight_p = 64;            /* old backlog weight */
++int weight_p = 64;          /* old backlog weight */
+ /* These numbers are selected based on intuition and some
+  * experimentatiom, if you have more scientific way of doing this
+  * please go ahead and fix things.
+@@ -1423,6 +1429,19 @@
+ }
++#if defined(CONFIG_NET_PKTGEN) || defined(CONFIG_NET_PKTGEN_MODULE)
++#warning "Compiling dev.c for pktgen.";
++
++int (*handle_pktgen_hook)(struct sk_buff *skb) = NULL;
++
++static __inline__ int handle_pktgen_rcv(struct sk_buff* skb) {
++        if (handle_pktgen_hook) {
++                return handle_pktgen_hook(skb);
++        }
++        return -1;
++}
++#endif
++
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ void (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
+ #endif
+@@ -1445,6 +1464,20 @@
+       return ret;
+ }
++#if defined(CONFIG_MACVLAN) || defined(CONFIG_MACVLAN_MODULE)
++/* Returns >= 0 if we consume the packet.  Otherwise, let
++ * it fall through the rest of the packet processing.
++ */
++int (*macvlan_handle_frame_hook)(struct sk_buff *skb) = NULL;
++#endif
++
++/* Returns >= 0 if we consume the packet.  Otherwise, let
++ * it fall through the rest of the packet processing.
++ */
++static __inline__ int handle_macvlan(struct sk_buff *skb)
++{
++      return macvlan_handle_frame_hook(skb);
++}
+ #ifdef CONFIG_NET_DIVERT
+ static inline int handle_diverter(struct sk_buff *skb)
+@@ -1493,11 +1526,23 @@
+               }
+       }
++#if defined(CONFIG_NET_PKTGEN) || defined(CONFIG_NET_PKTGEN_MODULE)
++        if ((skb->dev->priv_flags & IFF_PKTGEN_RCV) &&
++            (handle_pktgen_rcv(skb) >= 0)) {
++                /* Pktgen may consume the packet, no need to send
++                 * to further protocols.
++                 */
++                return 0;
++        }
++#endif
++
++        
+ #ifdef CONFIG_NET_DIVERT
+       if (skb->dev->divert && skb->dev->divert->divert)
+               ret = handle_diverter(skb);
+ #endif /* CONFIG_NET_DIVERT */
+-                      
++
++        
+ #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+       if (skb->dev->br_port != NULL &&
+           br_handle_frame_hook != NULL) {
+@@ -1505,6 +1550,22 @@
+       }
+ #endif
++#if defined(CONFIG_MACVLAN) || defined(CONFIG_MACVLAN_MODULE)
++      if (skb->dev->macvlan_priv != NULL &&
++          macvlan_handle_frame_hook != NULL) {
++                if (handle_macvlan(skb) >= 0) {
++                        /* consumed by mac-vlan...it would have been
++                         * re-sent to this method with a different
++                         * device...
++                         */
++                        return 0;
++                }
++                else {
++                        /* Let it fall through and be processed normally */
++                }
++      }
++#endif
++        
+       for (ptype=ptype_base[ntohs(type)&15];ptype;ptype=ptype->next) {
+               if (ptype->type == type &&
+                   (!ptype->dev || ptype->dev == skb->dev)) {
+@@ -1618,20 +1679,45 @@
+               local_irq_enable();
+               dev = list_entry(queue->poll_list.next, struct net_device, poll_list);
+-
++#define ORIGINAL_NAPI_ALGORITHM
++#ifdef ORIGINAL_NAPI_ALGORITHM
+               if (dev->quota <= 0 || dev->poll(dev, &budget)) {
+                       local_irq_disable();
+                       list_del(&dev->poll_list);
+                       list_add_tail(&dev->poll_list, &queue->poll_list);
+                       if (dev->quota < 0)
+-                              dev->quota += dev->weight;
+-                      else
+-                              dev->quota = dev->weight;
++                                dev->quota += dev->weight;
++                        else
++                                dev->quota = dev->weight;
+               } else {
+                       dev_put(dev);
+                       local_irq_disable();
+               }
+-      }
++#else
++                /* This scheme should allow devices to build up 2x their weight in quota
++                 * credit.  Heavy users will only get their normal quota.  This should
++                 * help let bursty traffic get higher priority. --Ben
++                 */
++                if (dev->poll(dev, &budget)) {
++                        /* More to do, put these guys back on the poll list */
++                      local_irq_disable();
++                      list_del(&dev->poll_list);
++                      list_add_tail(&dev->poll_list, &queue->poll_list);
++                        dev->quota = dev->weight;
++                }  
++                else {
++                        /* These guys are done, they come off of the poll list */
++                        if (dev->quota >= dev->weight) {
++                                dev->quota = (dev->weight << 1); /* max quota of 2x weight */
++                        }
++                        else {
++                                dev->quota += dev->weight;
++                        }
++                      dev_put(dev);
++                      local_irq_disable();
++              }
++#endif
++        }
+       local_irq_enable();
+       br_read_unlock(BR_NETPROTO_LOCK);
+@@ -2183,11 +2269,70 @@
+                       notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev);
+                       return 0;
++              case SIOCSIFWEIGHT:
++                      if (ifr->ifr_qlen < 0)
++                              return -EINVAL;
++                      dev->weight = ifr->ifr_qlen;
++                      return 0;
++                        
++              case SIOCGIFWEIGHT:
++                      ifr->ifr_qlen = dev->weight;
++                      return 0;
++                        
++                case SIOCSACCEPTLOCALADDRS:
++                        if (ifr->ifr_flags) {
++                                dev->priv_flags |= IFF_ACCEPT_LOCAL_ADDRS;
++                        }
++                        else {
++                                dev->priv_flags &= ~IFF_ACCEPT_LOCAL_ADDRS;
++                        }
++                        return 0;
++
++                case SIOCGACCEPTLOCALADDRS:
++                        if (dev->priv_flags & IFF_ACCEPT_LOCAL_ADDRS) {
++                                ifr->ifr_flags = 1;
++                        }
++                        else {
++                                ifr->ifr_flags = 0;
++                        }
++                        return 0;
++
+               /*
+                *      Unknown or private ioctl
+                */
+               default:
++                        /* Handle some generic ethtool commands here */
++                        if (cmd == SIOCETHTOOL) {
++                                u32 cmd = 0;
++                                if (copy_from_user(&cmd, ifr->ifr_data, sizeof(cmd))) {
++                                        return -EFAULT;
++                                }
++                                
++                                if (cmd == ETHTOOL_GNDSTATS) {
++                                        
++                                        struct ethtool_ndstats* nds = (struct ethtool_ndstats*)(ifr->ifr_data);
++                                        
++                                        /* Get net-device stats struct, will save it in the space
++                                         * pointed to by the ifr->flags number.  Would like to use
++                                         * ethtool, but it seems to require specific driver support,
++                                         * when this is a general purpose netdevice request...
++                                         */
++                                        struct net_device_stats *stats = dev->get_stats(dev);
++                                        if (stats) {
++                                                if (copy_to_user(nds->data, stats, sizeof(*stats))) {
++                                                        return -EFAULT;
++                                                }
++                                        }
++                                        else {
++                                                return -EOPNOTSUPP;
++                                        }
++                                        return 0;
++                                }
++                        }
++
++                                
++                                                
+                       if ((cmd >= SIOCDEVPRIVATE &&
+                           cmd <= SIOCDEVPRIVATE + 15) ||
+                           cmd == SIOCBONDENSLAVE ||
+@@ -2280,6 +2425,8 @@
+               case SIOCGIFMAP:
+               case SIOCGIFINDEX:
+               case SIOCGIFTXQLEN:
++                case SIOCGIFWEIGHT:
++                case SIOCGACCEPTLOCALADDRS:
+                       dev_load(ifr.ifr_name);
+                       read_lock(&dev_base_lock);
+                       ret = dev_ifsioc(&ifr, cmd);
+@@ -2343,6 +2490,8 @@
+               case SIOCBONDSLAVEINFOQUERY:
+               case SIOCBONDINFOQUERY:
+               case SIOCBONDCHANGEACTIVE:
++                case SIOCSIFWEIGHT:
++                case SIOCSACCEPTLOCALADDRS:
+                       if (!capable(CAP_NET_ADMIN))
+                               return -EPERM;
+                       dev_load(ifr.ifr_name);
+--- linux-2.4.21/net/core/pktgen.c     2002-11-28 15:53:15.000000000 -0800
++++ linux-2.4.21.amds/net/core/pktgen.c        2003-07-30 16:20:41.000000000 -0700
+@@ -1,9 +1,8 @@
+ /* -*-linux-c-*-
+- * $Id: candela_2.4.21.patch,v 1.4 2003/09/30 21:05:04 greear Exp $
+- * pktgen.c: Packet Generator for performance evaluation.
+  *
+  * Copyright 2001, 2002 by Robert Olsson <robert.olsson@its.uu.se>
+  *                                 Uppsala University, Sweden
++ *                 2002  Ben Greear <greearb@candelatech.com>
+  *
+  * A tool for loading the network with preconfigurated packets.
+  * The tool is implemented as a linux module.  Parameters are output 
+@@ -21,30 +20,32 @@
+  * Added multiskb option 020301 --DaveM
+  * Scaling of results. 020417--sigurdur@linpro.no
+  * Significant re-work of the module:
+- *   *  Updated to support generation over multiple interfaces at once
+- *       by creating 32 /proc/net/pg* files.  Each file can be manipulated
+- *       individually.
++ *   *  Convert to threaded model to more efficiently be able to transmit
++ *       and receive on multiple interfaces at once.
+  *   *  Converted many counters to __u64 to allow longer runs.
+  *   *  Allow configuration of ranges, like min/max IP address, MACs,
+  *       and UDP-ports, for both source and destination, and can
+  *       set to use a random distribution or sequentially walk the range.
+- *   *  Can now change some values after starting.
++ *   *  Can now change most values after starting.
+  *   *  Place 12-byte packet in UDP payload with magic number,
+- *       sequence number, and timestamp.  Will write receiver next.
+- *   *  The new changes seem to have a performance impact of around 1%,
+- *       as far as I can tell.
++ *       sequence number, and timestamp.
++ *   *  Add receiver code that detects dropped pkts, re-ordered pkts, and
++ *       latencies (with micro-second) precision.
++ *   *  Add IOCTL interface to easily get counters & configuration.
+  *   --Ben Greear <greearb@candelatech.com>
+  *
+  * Renamed multiskb to clone_skb and cleaned up sending core for two distinct 
+  * skb modes. A clone_skb=0 mode for Ben "ranges" work and a clone_skb != 0 
+  * as a "fastpath" with a configurable number of clones after alloc's.
+- *
+  * clone_skb=0 means all packets are allocated this also means ranges time 
+  * stamps etc can be used. clone_skb=100 means 1 malloc is followed by 100 
+  * clones.
+  *
+  * Also moved to /proc/net/pktgen/ 
+- * --ro 
++ * --ro
++ *
++ * Sept 10:  Fixed threading/locking.  Lots of bone-headed and more clever
++ *    mistakes.  Also merged in DaveM's patch in the -pre6 patch.
+  *
+  * See Documentation/networking/pktgen.txt for how to use this.
+  */
+@@ -79,172 +80,533 @@
+ #include <linux/proc_fs.h>
+ #include <linux/if_arp.h>
+ #include <net/checksum.h>
++#include <net/profile.h>
+ #include <asm/timex.h>
+-#define cycles()      ((u32)get_cycles())
++#include <linux/smp_lock.h> /* for lock kernel */
++#include <asm/div64.h> /* do_div */
++
++#include "pktgen.h"
+-#define VERSION "pktgen version 1.2"
+ static char version[] __initdata = 
+-  "pktgen.c: v1.2: Packet Generator for packet performance testing.\n";
++  "pktgen.c: v1.6: Packet Generator for packet performance testing.\n";
+ /* Used to help with determining the pkts on receive */
+ #define PKTGEN_MAGIC 0xbe9be955
++/* #define PG_DEBUG(a) a */
++#define PG_DEBUG(a) /* a */
+-/* Keep information per interface */
+-struct pktgen_info {
+-        /* Parameters */
++/* cycles per micro-second */
++static u32 pg_cycles_per_ns;
++static u32 pg_cycles_per_us;
++static u32 pg_cycles_per_ms;
+-        /* If min != max, then we will either do a linear iteration, or
+-         * we will do a random selection from within the range.
+-         */
+-        __u32 flags;     
++/* Module parameters, defaults. */
++static int pg_count_d = 0; /* run forever by default */
++static int pg_ipg_d = 0;
++static int pg_multiskb_d = 0;
++static int pg_thread_count = 1; /* Initial threads to create */
++static int debug = 0;
+-#define F_IPSRC_RND   (1<<0)  /* IP-Src Random  */
+-#define F_IPDST_RND   (1<<1)  /* IP-Dst Random  */
+-#define F_UDPSRC_RND  (1<<2)  /* UDP-Src Random */
+-#define F_UDPDST_RND  (1<<3)  /* UDP-Dst Random */
+-#define F_MACSRC_RND  (1<<4)  /* MAC-Src Random */
+-#define F_MACDST_RND  (1<<5)  /* MAC-Dst Random */
+-#define F_SET_SRCMAC  (1<<6)  /* Specify-Src-Mac 
+-                               (default is to use Interface's MAC Addr) */
+-#define F_SET_SRCIP   (1<<7)  /*  Specify-Src-IP
+-                                (default is to use Interface's IP Addr) */ 
+-
+-        
+-        int pkt_size;    /* = ETH_ZLEN; */
+-        int nfrags;
+-        __u32 ipg;       /* Default Interpacket gap in nsec */
+-        __u64 count;     /* Default No packets to send */
+-        __u64 sofar;     /* How many pkts we've sent so far */
+-        __u64 errors;    /* Errors when trying to transmit, pkts will be re-sent */
+-        struct timeval started_at;
+-        struct timeval stopped_at;
+-        __u64 idle_acc;
+-        __u32 seq_num;
+-        
+-        int clone_skb;   /* Use multiple SKBs during packet gen.  If this number
+-                          * is greater than 1, then that many coppies of the same
+-                          * packet will be sent before a new packet is allocated.
+-                          * For instance, if you want to send 1024 identical packets
+-                          * before creating a new packet, set clone_skb to 1024.
+-                          */
+-        int busy;
+-        int do_run_run;   /* if this changes to false, the test will stop */
+-        
+-        char outdev[32];
+-        char dst_min[32];
+-        char dst_max[32];
+-        char src_min[32];
+-        char src_max[32];
+-        /* If we're doing ranges, random or incremental, then this
+-         * defines the min/max for those ranges.
+-         */
+-        __u32 saddr_min; /* inclusive, source IP address */
+-        __u32 saddr_max; /* exclusive, source IP address */
+-        __u32 daddr_min; /* inclusive, dest IP address */
+-        __u32 daddr_max; /* exclusive, dest IP address */
+-
+-        __u16 udp_src_min; /* inclusive, source UDP port */
+-        __u16 udp_src_max; /* exclusive, source UDP port */
+-        __u16 udp_dst_min; /* inclusive, dest UDP port */
+-        __u16 udp_dst_max; /* exclusive, dest UDP port */
+-
+-        __u32 src_mac_count; /* How many MACs to iterate through */
+-        __u32 dst_mac_count; /* How many MACs to iterate through */
+-        
+-        unsigned char dst_mac[6];
+-        unsigned char src_mac[6];
+-        
+-        __u32 cur_dst_mac_offset;
+-        __u32 cur_src_mac_offset;
+-        __u32 cur_saddr;
+-        __u32 cur_daddr;
+-        __u16 cur_udp_dst;
+-        __u16 cur_udp_src;
+-        
+-        __u8 hh[14];
+-        /* = { 
+-           0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB, 
+-           
+-           We fill in SRC address later
+-           0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+-           0x08, 0x00
+-           };
+-        */
+-        __u16 pad; /* pad out the hh struct to an even 16 bytes */
+-        char result[512];
+-        /* proc file names */
+-        char fname[80];
+-        char busy_fname[80];
+-        
+-        struct proc_dir_entry *proc_ent;
+-        struct proc_dir_entry *busy_proc_ent;
+-};
++/* List of all running threads */
++static struct pktgen_thread_info* pktgen_threads = NULL;
++spinlock_t _pg_threadlist_lock = SPIN_LOCK_UNLOCKED;
++
++/* Holds interfaces for all threads */
++#define PG_INFO_HASH_MAX 32
++static struct pktgen_interface_info* pg_info_hash[PG_INFO_HASH_MAX];
++spinlock_t _pg_hash_lock = SPIN_LOCK_UNLOCKED;
++
++#define PG_PROC_DIR "pktgen"
++static struct proc_dir_entry *pg_proc_dir = NULL;
++
++char module_fname[128];
++struct proc_dir_entry *module_proc_ent = NULL;
+-struct pktgen_hdr {
+-        __u32 pgh_magic;
+-        __u32 seq_num;
+-        struct timeval timestamp;
++
++static void init_pktgen_kthread(struct pktgen_thread_info *kthread, char *name);
++static int pg_rem_interface_info(struct pktgen_thread_info* pg_thread,
++                                 struct pktgen_interface_info* i);
++static int pg_add_interface_info(struct pktgen_thread_info* pg_thread,
++                                 const char* ifname);
++static void exit_pktgen_kthread(struct pktgen_thread_info *kthread);
++static void stop_pktgen_kthread(struct pktgen_thread_info *kthread);
++static struct pktgen_thread_info* pg_find_thread(const char* name);
++static int pg_add_thread_info(const char* name);
++static struct pktgen_interface_info* pg_find_interface(struct pktgen_thread_info* pg_thread,
++                                                       const char* ifname);
++static int pktgen_device_event(struct notifier_block *, unsigned long, void *);
++
++
++struct notifier_block pktgen_notifier_block = {
++      notifier_call: pktgen_device_event,
+ };
+-static int cpu_speed;
+-static int debug;
++/*  This code works around the fact that do_div cannot handle two 64-bit
++    numbers, and regular 64-bit division doesn't work on x86 kernels.
++    --Ben
++*/
+-/* Module parameters, defaults. */
+-static int count_d = 100000;
+-static int ipg_d = 0;
+-static int clone_skb_d = 0;
++#define PG_DIV 0
++#define PG_REM 1
++
++/* This was emailed to LMKL by: Chris Caputo <ccaputo@alt.net>
++ * Function copied/adapted/optimized from:
++ *
++ *  nemesis.sourceforge.net/browse/lib/static/intmath/ix86/intmath.c.html
++ *
++ * Copyright 1994, University of Cambridge Computer Laboratory
++ * All Rights Reserved.
++ *
++ * TODO: When running on a 64-bit CPU platform, this should no longer be
++ * TODO: necessary.
++ */
++inline static s64 divremdi3(s64 x, s64 y, int type) {
++        u64 a = (x < 0) ? -x : x;
++        u64 b = (y < 0) ? -y : y;
++        u64 res = 0, d = 1;
++
++        if (b > 0) {
++                while (b < a) {
++                        b <<= 1;
++                        d <<= 1;
++                }
++        }
++        
++        do {
++                if ( a >= b ) {
++                        a -= b;
++                        res += d;
++                }
++                b >>= 1;
++                d >>= 1;
++        }
++        while (d);
++
++        if (PG_DIV == type) {
++                return (((x ^ y) & (1ll<<63)) == 0) ? res : -(s64)res;
++        }
++        else {
++                return ((x & (1ll<<63)) == 0) ? a : -(s64)a;
++        }
++}/* divremdi3 */
++
++/* End of hacks to deal with 64-bit math on x86 */
+-#define MAX_PKTGEN 8
+-static struct pktgen_info pginfos[MAX_PKTGEN];
++inline static void pg_lock_thread_list(char* msg) {
++        if (debug > 1) {
++                printk("before pg_lock_thread_list, msg: %s\n", msg);
++        }
++        spin_lock(&_pg_threadlist_lock);
++        if (debug > 1) {
++                printk("after pg_lock_thread_list, msg: %s\n", msg);
++        }
++}
++
++inline static void pg_unlock_thread_list(char* msg) {
++        if (debug > 1) {
++                printk("before pg_unlock_thread_list, msg: %s\n", msg);
++        }
++        spin_unlock(&_pg_threadlist_lock);
++        if (debug > 1) {
++                printk("after pg_unlock_thread_list, msg: %s\n", msg);
++        }
++}
++
++inline static void pg_lock_hash(char* msg) {
++        if (debug > 1) {
++                printk("before pg_lock_hash, msg: %s\n", msg);
++        }
++        spin_lock(&_pg_hash_lock);
++        if (debug > 1) {
++                printk("before pg_lock_hash, msg: %s\n", msg);
++        }
++}
++
++inline static void pg_unlock_hash(char* msg) {
++        if (debug > 1) {
++                printk("before pg_unlock_hash, msg: %s\n", msg);
++        }
++        spin_unlock(&_pg_hash_lock);
++        if (debug > 1) {
++                printk("after pg_unlock_hash, msg: %s\n", msg);
++        }
++}
++
++inline static void pg_lock(struct pktgen_thread_info* pg_thread, char* msg) {
++        if (debug > 1) {
++                printk("before pg_lock thread, msg: %s\n", msg);
++        }
++        spin_lock(&(pg_thread->pg_threadlock));
++        if (debug > 1) {
++                printk("after pg_lock thread, msg: %s\n", msg);
++        }
++}
++
++inline static void pg_unlock(struct pktgen_thread_info* pg_thread, char* msg) {
++        if (debug > 1) {
++                printk("before pg_unlock thread, thread: %p  msg: %s\n",
++                       pg_thread, msg);
++        }
++        spin_unlock(&(pg_thread->pg_threadlock));
++        if (debug > 1) {
++                printk("after pg_unlock thread, thread: %p  msg: %s\n",
++                       pg_thread, msg);
++        }
++}
+ /** Convert to miliseconds */
+-inline __u64 tv_to_ms(const struct timeval* tv) {
++static inline __u64 tv_to_ms(const struct timeval* tv) {
+         __u64 ms = tv->tv_usec / 1000;
+         ms += (__u64)tv->tv_sec * (__u64)1000;
+         return ms;
+ }
+-inline __u64 getCurMs(void) {
++
++/** Convert to micro-seconds */
++static inline __u64 tv_to_us(const struct timeval* tv) {
++        __u64 us = tv->tv_usec;
++        us += (__u64)tv->tv_sec * (__u64)1000000;
++        return us;
++}
++
++
++static inline __u64 pg_div(__u64 n, __u32 base) {
++        __u64 tmp = n;
++        do_div(tmp, base);
++        /* printk("pg_div, n: %llu  base: %d  rv: %llu\n",
++                  n, base, tmp); */
++        return tmp;
++}
++
++/* Fast, not horribly accurate, since the machine started. */
++static inline __u64 getRelativeCurMs(void) {
++        return pg_div(get_cycles(), pg_cycles_per_ms);
++}
++
++/* Since the epoc.  More precise over long periods of time than
++ * getRelativeCurMs
++ */
++static inline __u64 getCurMs(void) {
+         struct timeval tv;
+         do_gettimeofday(&tv);
+         return tv_to_ms(&tv);
+ }
+-#define PG_PROC_DIR "pktgen"
+-static struct proc_dir_entry *proc_dir = 0;
++/* Since the epoc.  More precise over long periods of time than
++ * getRelativeCurMs
++ */
++static inline __u64 getCurUs(void) {
++        struct timeval tv;
++        do_gettimeofday(&tv);
++        return tv_to_us(&tv);
++}
+-static struct net_device *setup_inject(struct pktgen_info* info)
+-{
++/* Since the machine booted. */
++static inline __u64 getRelativeCurUs(void) {
++        return pg_div(get_cycles(), pg_cycles_per_us);
++}
++
++/* Since the machine booted. */
++static inline __u64 getRelativeCurNs(void) {
++        return pg_div(get_cycles(), pg_cycles_per_ns);
++}
++
++static inline __u64 tv_diff(const struct timeval* a, const struct timeval* b) {
++        return tv_to_us(a) - tv_to_us(b);
++}
++
++
++
++int pktgen_proc_ioctl(struct inode* inode, struct file* file, unsigned int cmd,
++                      unsigned long arg) {
++        int err = 0;
++        struct pktgen_ioctl_info args;
++        struct pktgen_thread_info* targ = NULL;
++
++        /*
++        if (!capable(CAP_NET_ADMIN)){
++                return -EPERM;
++        }
++        */
++        
++        if (copy_from_user(&args, (void*)arg, sizeof(args))) {
++                return -EFAULT;
++        }
++
++        /* Null terminate the names */
++        args.thread_name[31] = 0;
++        args.interface_name[31] = 0;
++
++        /* printk("pktgen:  thread_name: %s  interface_name: %s\n",
++         *        args.thread_name, args.interface_name);
++         */
++        
++        switch (cmd) {
++         case GET_PKTGEN_INTERFACE_INFO: {
++                 targ = pg_find_thread(args.thread_name);
++                 if (targ) {
++                         struct pktgen_interface_info* info;
++                         info = pg_find_interface(targ, args.interface_name);
++                         if (info) {
++                                 memcpy(&(args.info), info, sizeof(args.info));
++                                 if (copy_to_user((void*)(arg), &args, sizeof(args))) {
++                                         printk("ERROR: pktgen:  copy_to_user failed.\n");
++                                         err = -EFAULT;
++                                 }
++                                 else {
++                                         err = 0;
++                                 }
++                         }
++                         else {
++                                 /* printk("ERROR: pktgen:  Could not find interface -:%s:-\n",
++                                           args.interface_name);*/
++                                 err = -ENODEV;
++                         }
++                 }
++                 else {
++                         printk("ERROR: pktgen:  Could not find thread -:%s:-.\n",
++                                args.thread_name);
++                         err = -ENODEV;
++                 }
++                 break;
++         }
++         default:
++                /* pass on to underlying device instead?? */
++                printk(__FUNCTION__ ": Unknown pktgen IOCTL: %x \n",
++                       cmd);
++                return -EINVAL;
++        }
++        
++        return err;
++}/* pktgen_proc_ioctl */
++
++static struct file_operations pktgen_fops = {
++        ioctl:     pktgen_proc_ioctl,
++};
++
++static void remove_pg_info_from_hash(struct pktgen_interface_info* info) {
++        pg_lock_hash(__FUNCTION__);
++        {
++                int device_idx = info->odev ? info->odev->ifindex : 0;
++                int b = device_idx % PG_INFO_HASH_MAX;
++                struct pktgen_interface_info* p = pg_info_hash[b];
++                struct pktgen_interface_info* prev = pg_info_hash[b];
++
++                PG_DEBUG(printk("remove_pg_info_from_hash, p: %p info: %p  device_idx: %i\n",
++                                p, info, device_idx));
++
++                if (p != NULL) {
++                
++                        if (p == info) {
++                                pg_info_hash[b] = p->next_hash;
++                                p->next_hash = NULL;
++                        }
++                        else {
++                                while (prev->next_hash) {
++                                        p = prev->next_hash;
++                                        if (p == info) {
++                                                prev->next_hash = p->next_hash;
++                                                p->next_hash = NULL;
++                                                break;
++                                        }
++                                        prev = p;
++                                }
++                        }
++                }
++                
++                if (info->odev) {
++                        info->odev->priv_flags &= ~(IFF_PKTGEN_RCV);
++                }
++        }
++        pg_unlock_hash(__FUNCTION__);
++}/* remove_pg_info_from_hash */
++
++
++static void add_pg_info_to_hash(struct pktgen_interface_info* info) {
++        /* First remove it, just in case it's already there. */
++        remove_pg_info_from_hash(info);
++        
++        pg_lock_hash(__FUNCTION__);
++        {
++                int device_idx = info->odev ? info->odev->ifindex : 0;
++                int b = device_idx % PG_INFO_HASH_MAX;
++
++                PG_DEBUG(printk("add_pg_info_from_hash, b: %i info: %p  device_idx: %i\n",
++                                b, info, device_idx));
++
++                info->next_hash = pg_info_hash[b];
++                pg_info_hash[b] = info;
++
++
++                if (info->odev) {
++                        info->odev->priv_flags |= (IFF_PKTGEN_RCV);
++                }
++        }
++        pg_unlock_hash(__FUNCTION__);
++}/* add_pg_info_to_hash */
++
++
++/* Find the pktgen_interface_info for a device idx */
++struct pktgen_interface_info* find_pg_info(int device_idx) {
++        struct pktgen_interface_info* p = NULL;
++        if (debug > 1) {
++                printk("in find_pg_info...\n");
++        }
++        pg_lock_hash(__FUNCTION__);
++        {
++                int b = device_idx % PG_INFO_HASH_MAX;
++                p = pg_info_hash[b];
++                while (p) {
++                        if (p->odev && (p->odev->ifindex == device_idx)) {
++                                break;
++                        }
++                        p = p->next_hash;
++                }
++        }
++        pg_unlock_hash(__FUNCTION__);
++        return p;
++}
++
++
++/* Remove an interface from our hash, dissassociate pktgen_interface_info
++ * from interface
++ */
++static void check_remove_device(struct pktgen_interface_info* info) {
++        struct pktgen_interface_info* pi = NULL;
++        if (info->odev) {
++                pi = find_pg_info(info->odev->ifindex);
++                if (pi != info) {
++                        printk("ERROR: pi != info, pi: %p  info: %p\n", pi, info);
++                }
++                else {
++                        /* Remove info from our hash */
++                        remove_pg_info_from_hash(info);
++                }
++
++                rtnl_lock();
++                info->odev->priv_flags &= ~(IFF_PKTGEN_RCV);
++                atomic_dec(&(info->odev->refcnt));
++                info->odev = NULL;
++                rtnl_unlock();
++        }
++}/* check_remove_device */
++
++
++static int pg_remove_interface_from_all_threads(const char* dev_name) {
++        int cnt = 0;
++        pg_lock_thread_list(__FUNCTION__);
++        {
++                struct pktgen_thread_info* tmp = pktgen_threads;
++                struct pktgen_interface_info* info = NULL;
++                
++                while (tmp) {
++                        info = pg_find_interface(tmp, dev_name);
++                        if (info) {
++                                printk("pktgen:  Removing interface: %s from pktgen control.\n",
++                                       dev_name);
++                                pg_rem_interface_info(tmp, info);
++                                cnt++;
++                        }
++                        else {
++                                printk("pktgen:  Could not find interface: %s in rem_from_all.\n",
++                                       dev_name);
++                        }
++                        tmp = tmp->next;
++                }
++        }
++        pg_unlock_thread_list(__FUNCTION__);
++        return cnt;
++}/* pg_rem_interface_from_all_threads */
++
++
++static int pktgen_device_event(struct notifier_block *unused, unsigned long event, void *ptr) {
++      struct net_device *dev = (struct net_device *)(ptr);
++
++      /* It is OK that we do not hold the group lock right now,
++       * as we run under the RTNL lock.
++       */
++
++      switch (event) {
++      case NETDEV_CHANGEADDR:
++      case NETDEV_GOING_DOWN:
++      case NETDEV_DOWN:
++      case NETDEV_UP:
++              /* Ignore for now */
++              break;
++              
++      case NETDEV_UNREGISTER:
++                pg_remove_interface_from_all_threads(dev->name);
++              break;
++      };
++
++      return NOTIFY_DONE;
++}
++
++
++/* Associate pktgen_interface_info with a device.
++ */
++static struct net_device* pg_setup_interface(struct pktgen_interface_info* info) {
+       struct net_device *odev;
++        check_remove_device(info);                
++        
+       rtnl_lock();
+-      odev = __dev_get_by_name(info->outdev);
++      odev = __dev_get_by_name(info->ifname);
+       if (!odev) {
+-              sprintf(info->result, "No such netdevice: \"%s\"", info->outdev);
+-              goto out_unlock;
++              printk("No such netdevice: \"%s\"\n", info->ifname);
+       }
+-
+-      if (odev->type != ARPHRD_ETHER) {
+-              sprintf(info->result, "Not ethernet device: \"%s\"", info->outdev);
+-              goto out_unlock;
++      else if (odev->type != ARPHRD_ETHER) {
++              printk("Not an ethernet device: \"%s\"\n", info->ifname);
+       }
+-
+-      if (!netif_running(odev)) {
+-              sprintf(info->result, "Device is down: \"%s\"", info->outdev);
+-              goto out_unlock;
++      else if (!netif_running(odev)) {
++              printk("Device is down: \"%s\"\n", info->ifname);
+       }
++      else if (odev->priv_flags & IFF_PKTGEN_RCV) {
++              printk("ERROR: Device: \"%s\" is already assigned to a pktgen interface.\n",
++                       info->ifname);
++      }
++        else {
++                atomic_inc(&odev->refcnt);
++                info->odev = odev;
++                info->odev->priv_flags |= (IFF_PKTGEN_RCV);
++        }
++        
++      rtnl_unlock();
++
++        if (info->odev) {
++                add_pg_info_to_hash(info);
++        }
++        
++        return info->odev;
++}
++/* Read info from the interface and set up internal pktgen_interface_info
++ * structure to have the right information to create/send packets
++ */
++static void pg_setup_inject(struct pktgen_interface_info* info)
++{
++        if (!info->odev) {
++                /* Try once more, just in case it works now. */
++                pg_setup_interface(info);
++        }
++        
++        if (!info->odev) {
++                printk("ERROR: info->odev == NULL in setup_inject.\n");
++                sprintf(info->result, "ERROR: info->odev == NULL in setup_inject.\n");
++                return;
++        }
++        
+         /* Default to the interface's mac if not explicitly set. */
+         if (!(info->flags & F_SET_SRCMAC)) {
+-                memcpy(&(info->hh[6]), odev->dev_addr, 6);
++                memcpy(&(info->hh[6]), info->odev->dev_addr, 6);
+         }
+         else {
+                 memcpy(&(info->hh[6]), info->src_mac, 6);
+@@ -252,12 +614,15 @@
+         /* Set up Dest MAC */
+         memcpy(&(info->hh[0]), info->dst_mac, 6);
++
++        /* Set up pkt size */
++        info->cur_pkt_size = info->min_pkt_size;
+         
+       info->saddr_min = 0;
+       info->saddr_max = 0;
+         if (strlen(info->src_min) == 0) {
+-                if (odev->ip_ptr) {
+-                        struct in_device *in_dev = odev->ip_ptr;
++                if (info->odev->ip_ptr) {
++                        struct in_device *in_dev = info->odev->ip_ptr;
+                         if (in_dev->ifa_list) {
+                                 info->saddr_min = in_dev->ifa_list->ifa_address;
+@@ -280,65 +645,131 @@
+         info->cur_daddr = info->daddr_min;
+         info->cur_udp_dst = info->udp_dst_min;
+         info->cur_udp_src = info->udp_src_min;
+-        
+-      atomic_inc(&odev->refcnt);
+-      rtnl_unlock();
+-
+-      return odev;
+-
+-out_unlock:
+-      rtnl_unlock();
+-      return NULL;
+ }
+-static void nanospin(int ipg, struct pktgen_info* info)
++/* ipg is in nano-seconds */
++static void nanospin(__u32 ipg, struct pktgen_interface_info* info)
+ {
+-      u32 idle_start, idle;
+-
+-      idle_start = cycles();
++      u64 idle_start = get_cycles();
++        u64 idle;
+       for (;;) {
+               barrier();
+-              idle = cycles() - idle_start;
+-              if (idle * 1000 >= ipg * cpu_speed)
++              idle = get_cycles() - idle_start;
++              if (idle * 1000 >= ipg * pg_cycles_per_us)
+                       break;
+       }
+       info->idle_acc += idle;
+ }
++
++/* ipg is in micro-seconds (usecs) */
++static void pg_udelay(__u32 delay_us, struct pktgen_interface_info* info,
++                      struct pktgen_thread_info* pg_thread)
++{
++        u64 start = getRelativeCurUs();
++        u64 now;
++        if (delay_us > (1000000 / HZ)) {
++                /* fall asleep for a bit */
++                __u32 us_per_tick = 1000000 / HZ;
++                __u32 ticks = delay_us / us_per_tick;
++                interruptible_sleep_on_timeout(&(pg_thread->queue), ticks);
++        }
++        
++      for (;;) {
++                now = getRelativeCurUs();
++                if (start + delay_us <= (now - 10)) {
++                        break;
++                }
++
++                if (!info->do_run_run) {
++                        return;
++                }
++                
++                if (current->need_resched) {
++                        schedule();
++                }
++                        
++                now = getRelativeCurUs();
++                if (start + delay_us <= (now - 10)) {
++                        break;
++                }
++
++                do_softirq();
++      }
++
++        info->idle_acc += (1000 * (now - start));
++
++        /* We can break out of the loop up to 10us early, so spend the rest of
++         * it spinning to increase accuracy.
++         */
++        if (start + delay_us > now) {
++                nanospin((start + delay_us) - now, info);
++        }
++}
++
++
++
++
++/* Returns: cycles per micro-second */
+ static int calc_mhz(void)
+ {
+       struct timeval start, stop;
+-      u32 start_s, elapsed;
+-
++      u64 start_s;
++        u64 t1, t2;
++        u32 elapsed;
++        u32 clock_time = 0;
++        
+       do_gettimeofday(&start);
+-      start_s = cycles();
++      start_s = get_cycles();
++        /* Spin for 50,000,000 cycles */
+       do {
+               barrier();
+-              elapsed = cycles() - start_s;
++              elapsed = (u32)(get_cycles() - start_s);
+               if (elapsed == 0)
+                       return 0;
+-      } while (elapsed < 1000 * 50000);
++      } while (elapsed < 50000000);
+       do_gettimeofday(&stop);
+-      return elapsed/(stop.tv_usec-start.tv_usec+1000000*(stop.tv_sec-start.tv_sec));
++
++        t1 = tv_to_us(&start);
++        t2 = tv_to_us(&stop);
++
++        clock_time = (u32)(t2 - t1);
++        if (clock_time == 0) {
++                printk("pktgen: ERROR:  clock_time was zero..things may not work right, t1: %u  t2: %u ...\n",
++                       (u32)(t1), (u32)(t2));
++                return 0x7FFFFFFF;
++        }
++      return elapsed / clock_time;
+ }
++/* Calibrate cycles per micro-second */
+ static void cycles_calibrate(void)
+ {
+       int i;
+       for (i = 0; i < 3; i++) {
+-              int res = calc_mhz();
+-              if (res > cpu_speed)
+-                      cpu_speed = res;
++              u32 res = calc_mhz();
++              if (res > pg_cycles_per_us)
++                      pg_cycles_per_us = res;
+       }
++
++        /* Set these up too, only need to calculate these once. */
++        pg_cycles_per_ns = pg_cycles_per_us / 1000;
++        if (pg_cycles_per_ns == 0) {
++                pg_cycles_per_ns = 1;
++        }
++        pg_cycles_per_ms = pg_cycles_per_us * 1000;
++        
++        printk("pktgen: cycles_calibrate, cycles_per_ns: %d  per_us: %d  per_ms: %d\n",
++               pg_cycles_per_ns, pg_cycles_per_us, pg_cycles_per_ms);
+ }
+ /* Increment/randomize headers according to flags and current values
+  * for IP src/dest, UDP src/dst port, MAC-Addr src/dst
+  */
+-static void mod_cur_headers(struct pktgen_info* info) {        
++static void mod_cur_headers(struct pktgen_interface_info* info) {        
+         __u32 imn;
+         __u32 imx;
+         
+@@ -428,7 +859,7 @@
+                 else {
+                      t = ntohl(info->cur_saddr);
+                      t++;
+-                     if (t >= imx) {
++                     if (t > imx) {
+                              t = imn;
+                      }
+                 }
+@@ -443,16 +874,31 @@
+                 else {
+                      t = ntohl(info->cur_daddr);
+                      t++;
+-                     if (t >= imx) {
++                     if (t > imx) {
+                              t = imn;
+                      }
+                 }
+                 info->cur_daddr = htonl(t);
+         }
++
++        if (info->min_pkt_size < info->max_pkt_size) {
++                __u32 t;
++                if (info->flags & F_TXSIZE_RND) {
++                        t = ((net_random() % (info->max_pkt_size - info->min_pkt_size))
++                             + info->min_pkt_size);
++                }
++                else {
++                     t = info->cur_pkt_size + 1;
++                     if (t > info->max_pkt_size) {
++                             t = info->min_pkt_size;
++                     }
++                }
++                info->cur_pkt_size = t;
++        }
+ }/* mod_cur_headers */
+-static struct sk_buff *fill_packet(struct net_device *odev, struct pktgen_info* info)
++static struct sk_buff *fill_packet(struct net_device *odev, struct pktgen_interface_info* info)
+ {
+       struct sk_buff *skb = NULL;
+       __u8 *eth;
+@@ -461,7 +907,7 @@
+       struct iphdr *iph;
+         struct pktgen_hdr *pgh = NULL;
+         
+-      skb = alloc_skb(info->pkt_size + 64 + 16, GFP_ATOMIC);
++      skb = alloc_skb(info->cur_pkt_size + 64 + 16, GFP_ATOMIC);
+       if (!skb) {
+               sprintf(info->result, "No memory");
+               return NULL;
+@@ -481,7 +927,7 @@
+       memcpy(eth, info->hh, 14);
+         
+-      datalen = info->pkt_size - 14 - 20 - 8; /* Eth + IPh + UDPh */
++      datalen = info->cur_pkt_size - 14 - 20 - 8; /* Eth + IPh + UDPh */
+       if (datalen < sizeof(struct pktgen_hdr)) {
+               datalen = sizeof(struct pktgen_hdr);
+         }
+@@ -493,7 +939,7 @@
+       iph->ihl = 5;
+       iph->version = 4;
+-      iph->ttl = 3;
++      iph->ttl = 32;
+       iph->tos = 0;
+       iph->protocol = IPPROTO_UDP; /* UDP */
+       iph->saddr = info->cur_saddr;
+@@ -514,7 +960,6 @@
+               int frags = info->nfrags;
+               int i;
+-                /* TODO: Verify this is OK...it sure is ugly. --Ben */
+                 pgh = (struct pktgen_hdr*)(((char*)(udph)) + 8);
+                 
+               if (frags > MAX_SKB_FRAGS)
+@@ -562,234 +1007,855 @@
+         /* Stamp the time, and sequence number, convert them to network byte order */
+         if (pgh) {
+-                pgh->pgh_magic = htonl(PKTGEN_MAGIC);
++                pgh->pgh_magic = __constant_htonl(PKTGEN_MAGIC);
+                 do_gettimeofday(&(pgh->timestamp));
+                 pgh->timestamp.tv_usec = htonl(pgh->timestamp.tv_usec);
+                 pgh->timestamp.tv_sec = htonl(pgh->timestamp.tv_sec);
+                 pgh->seq_num = htonl(info->seq_num);
+         }
++        info->seq_num++;
+         
+       return skb;
+ }
+-static void inject(struct pktgen_info* info)
+-{
+-      struct net_device *odev = NULL;
+-      struct sk_buff *skb = NULL;
+-      __u64 total = 0;
+-        __u64 idle = 0;
+-      __u64 lcount = 0;
+-        int nr_frags = 0;
+-      int last_ok = 1;           /* Was last skb sent? 
+-                                  * Or a failed transmit of some sort?  This will keep
+-                                    * sequence numbers in order, for example.
+-                                    */
+-        __u64 fp = 0;
+-        __u32 fp_tmp = 0;
+-
+-      odev = setup_inject(info);
+-      if (!odev)
+-              return;
+-
+-        info->do_run_run = 1; /* Cranke yeself! */
+-      info->idle_acc = 0;
+-      info->sofar = 0;
+-      lcount = info->count;
+-
+-
+-        /* Build our initial pkt and place it as a re-try pkt. */
+-      skb = fill_packet(odev, info);
+-      if (skb == NULL) goto out_reldev;
++static void record_latency(struct pktgen_interface_info* info, int latency) {
++        /* NOTE:  Latency can be negative */
++        int div = 100;
++        int diff;
++        int vl;
++        int i;
+-      do_gettimeofday(&(info->started_at));
++        info->pkts_rcvd_since_clear++;
++        
++        if (info->pkts_rcvd_since_clear < 100) {
++                div = info->pkts_rcvd;
++                if (info->pkts_rcvd_since_clear == 1) {
++                        info->avg_latency = latency;
++                }
++        }
+-      while(info->do_run_run) {
++        if ((div + 1) == 0) {
++                info->avg_latency = 0;
++        }
++        else {
++                info->avg_latency = ((info->avg_latency * div + latency) / (div + 1));
++        }
+-                /* Set a time-stamp, so build a new pkt each time */
++        if (latency < info->min_latency) {
++                info->min_latency = latency;
++        }
++        if (latency > info->max_latency) {
++                info->max_latency = latency;
++        }
+-                if (last_ok) {
+-                        if (++fp_tmp >= info->clone_skb ) {
+-                                kfree_skb(skb);
+-                                skb = fill_packet(odev, info);
+-                                if (skb == NULL) {
+-                                        break;
+-                                }
+-                                fp++;
+-                                fp_tmp = 0; /* reset counter */
+-                        }
+-                        atomic_inc(&skb->users);
++        /* Place the latency in the right 'bucket' */
++        diff = (latency - info->min_latency);        
++        for (i = 0; i<LAT_BUCKETS_MAX; i++) {
++                vl = (1<<i);
++                if (latency <= vl) {
++                        info->latency_bkts[i]++;
++                        break;
+                 }
++        }
++}/* record latency */
+-                nr_frags = skb_shinfo(skb)->nr_frags;
+-                   
+-              spin_lock_bh(&odev->xmit_lock);
+-              if (!netif_queue_stopped(odev)) {
+-                      if (odev->hard_start_xmit(skb, odev)) {
+-                              if (net_ratelimit()) {
+-                                   printk(KERN_INFO "Hard xmit error\n");
+-                                }
+-                                info->errors++;
+-                              last_ok = 0;
+-                      }
+-                        else {
+-                         last_ok = 1; 
+-                           info->sofar++;
+-                           info->seq_num++;
++/* Returns < 0 if the skb is not a pktgen buffer. */
++int pktgen_receive(struct sk_buff* skb) {
++        /* See if we have a pktgen packet */
++        if ((skb->len >= (20 + 8 + sizeof(struct pktgen_hdr))) &&
++            (skb->protocol == __constant_htons(ETH_P_IP))) {
++                
++                /* It's IP, and long enough, lets check the magic number.
++                 * TODO:  This is a hack not always guaranteed to catch the right
++                 * packets.
++                 */
++                /*int i;
++                  char* tmp; */
++                struct pktgen_hdr* pgh;
++                /* printk("Length & protocol passed, skb->data: %p, raw: %p\n",
++                          skb->data, skb->h.raw); */
++                pgh = (struct pktgen_hdr*)(skb->data + 20 + 8);
++                /*
++                tmp = (char*)(skb->data);
++                for (i = 0; i<60; i++) {
++                        printk("%02hx ", tmp[i]);
++                        if (((i + 1) % 15) == 0) {
++                                printk("\n");
+                         }
+-              }
+-              else {
+-                        /* Re-try it next time */
+-                      last_ok = 0;
+                 }
++                printk("\n");
++                */
+                 
++                if (pgh->pgh_magic == __constant_ntohl(PKTGEN_MAGIC)) {
++                        struct net_device* dev = skb->dev;
++                        struct pktgen_interface_info* info = find_pg_info(dev->ifindex);
++                        
++                        /* Got one! */
++                        /* TODO:  Check UDP checksum ?? */
++                        __u32 seq = ntohl(pgh->seq_num);
+-              spin_unlock_bh(&odev->xmit_lock);
+-
+-              if (info->ipg) {
+-                        /* Try not to busy-spin if we have larger sleep times.
+-                         * TODO:  Investigate better ways to do this.
+-                         */
+-                        if (info->ipg < 10000) { /* 10 usecs or less */
+-                                nanospin(info->ipg, info);
++                        if (!info) {
++                                return -1;
+                         }
+-                        else if (info->ipg < 10000000) { /* 10ms or less */
+-                                udelay(info->ipg / 1000);
++
++                        info->pkts_rcvd++;
++                        info->bytes_rcvd += (skb->len + 4); /* +4 for the checksum */
++                        
++                        /* Check for out-of-sequence packets */
++                        if (info->last_seq_rcvd == seq) {
++                                info->dup_rcvd++;
++                                info->dup_since_incr++;
+                         }
+                         else {
+-                                mdelay(info->ipg / 1000000);
++                                __s64 rx = tv_to_us(&(skb->stamp));
++                                __s64 tx;
++                                struct timeval txtv;
++                                txtv.tv_usec = ntohl(pgh->timestamp.tv_usec);
++                                txtv.tv_sec = ntohl(pgh->timestamp.tv_sec);
++                                tx = tv_to_us(&txtv);
++                                record_latency(info, rx - tx);
++                                
++                                if ((info->last_seq_rcvd + 1) == seq) {
++                                        if ((info->peer_multiskb > 1) &&
++                                            (info->peer_multiskb > (info->dup_since_incr + 1))) {
++                                                
++                                                info->seq_gap_rcvd += (info->peer_multiskb -
++                                                                       info->dup_since_incr - 1);
++                                        }
++                                        /* Great, in order...all is well */
++                                }
++                                else if (info->last_seq_rcvd < seq) {
++                                        /* sequence gap, means we dropped a pkt most likely */
++                                        if (info->peer_multiskb > 1) {
++                                                /* We dropped more than one sequence number's worth,
++                                                 * and if we're using multiskb, then this is quite
++                                                 * a few.  This number still will not be exact, but
++                                                 * it will be closer.
++                                                 */
++                                                info->seq_gap_rcvd += (((seq - info->last_seq_rcvd) *
++                                                                        info->peer_multiskb) -
++                                                                       info->dup_since_incr);
++                                        }
++                                        else {
++                                                info->seq_gap_rcvd += (seq - info->last_seq_rcvd - 1);
++                                        }
++                                }
++                                else {
++                                        info->ooo_rcvd++; /* out-of-order */
++                                }
++                                
++                                info->dup_since_incr = 0;
+                         }
++                        info->last_seq_rcvd = seq;
++                        kfree_skb(skb);
++                        if (debug > 1) {
++                                printk("done with pktgen_receive, free'd pkt\n");
++                        }
++                        return 0;
+                 }
+-                
+-              if (signal_pending(current)) {
+-                        break;
+-                }
++        }
++        return -1; /* Let another protocol handle it, it's not for us! */
++}/* pktgen_receive */
+-                /* If lcount is zero, then run forever */
+-              if ((lcount != 0) && (--lcount == 0)) {
+-                      if (atomic_read(&skb->users) != 1) {
+-                              u32 idle_start, idle;
+-
+-                              idle_start = cycles();
+-                              while (atomic_read(&skb->users) != 1) {
+-                                      if (signal_pending(current)) {
+-                                                break;
+-                                        }
+-                                      schedule();
+-                              }
+-                              idle = cycles() - idle_start;
+-                              info->idle_acc += idle;
+-                      }
+-                      break;
+-              }
++static void pg_reset_latency_counters(struct pktgen_interface_info* info) {
++        int i;
++        info->avg_latency = 0;
++        info->min_latency = 0x7fffffff; /* largest integer */
++        info->max_latency = 0x80000000; /* smallest integer */
++        info->pkts_rcvd_since_clear = 0;
++        for (i = 0; i<LAT_BUCKETS_MAX; i++) {
++                info->latency_bkts[i] = 0;
++        }
++}
+-              if (netif_queue_stopped(odev) || current->need_resched) {
+-                      u32 idle_start, idle;
++static void pg_clear_counters(struct pktgen_interface_info* info, int seq_too) {
++        info->idle_acc = 0;
++      info->sofar = 0;
++        info->tx_bytes = 0;
++        info->errors = 0;
++        info->ooo_rcvd = 0;
++        info->dup_rcvd = 0;
++        info->pkts_rcvd = 0;
++        info->bytes_rcvd = 0;
++        info->non_pg_pkts_rcvd = 0;
++        info->seq_gap_rcvd = 0; /* dropped */
+-                      idle_start = cycles();
+-                      do {
+-                              if (signal_pending(current)) {
+-                                        info->do_run_run = 0;
+-                                        break;
+-                                }
+-                              if (!netif_running(odev)) {
+-                                        info->do_run_run = 0;
+-                                      break;
++        /* This is a bit of a hack, but it gets the dup counters
++         * in line so we don't have false alarms on dropped pkts.
++         */
++        if (seq_too) {
++                info->dup_since_incr = info->peer_multiskb - 1;
++                info->seq_num = 1;
++                info->last_seq_rcvd = 0;
++        }
++        
++        pg_reset_latency_counters(info);
++}
++
++/* Adds an interface to the thread.  The interface will be in
++ * the stopped queue untill started.
++ */
++static int add_interface_to_thread(struct pktgen_thread_info* pg_thread,
++                                   struct pktgen_interface_info* info) {
++        int rv = 0;
++        /* grab lock & insert into the stopped list */
++        pg_lock(pg_thread, __FUNCTION__);
++
++        if (info->pg_thread) {
++                printk("pktgen: ERROR:  Already assigned to a thread.\n");
++                rv = -EBUSY;
++                goto out;
++        }
++        
++        info->next = pg_thread->stopped_if_infos;
++        pg_thread->stopped_if_infos = info;
++        info->pg_thread = pg_thread;
++
++ out:
++        pg_unlock(pg_thread, __FUNCTION__);        
++        return rv;
++}
++
++/* Set up structure for sending pkts, clear counters, add to rcv hash,
++ * create initial packet, and move from the stopped to the running
++ * interface_info list
++ */
++static int pg_start_interface(struct pktgen_thread_info* pg_thread,
++                              struct pktgen_interface_info* info) {
++        PG_DEBUG(printk("Entering pg_start_interface..\n"));
++        pg_setup_inject(info);
++
++      if (!info->odev) {
++              return -1;
++        }
++
++        PG_DEBUG(printk("About to clean counters..\n"));
++        pg_clear_counters(info, 1);
++
++        info->do_run_run = 1; /* Cranke yeself! */
++
++      info->skb = NULL;
++
++      info->started_at = getCurUs();
++
++        pg_lock(pg_thread, __FUNCTION__);
++        {
++                /* Remove from the stopped list */
++                struct pktgen_interface_info* p = pg_thread->stopped_if_infos;
++                if (p == info) {
++                        pg_thread->stopped_if_infos = p->next;
++                        p->next = NULL;
++                }
++                else {
++                        while (p) {
++                                if (p->next == info) {
++                                        p->next = p->next->next;
++                                        info->next = NULL;
++                                        break;
+                                 }
+-                              if (current->need_resched)
+-                                      schedule();
+-                              else
+-                                      do_softirq();
+-                      } while (netif_queue_stopped(odev));
+-                      idle = cycles() - idle_start;
+-                      info->idle_acc += idle;
+-              }
+-      }/* while we should be running */
++                                p = p->next;
++                        }
++                }
+-      do_gettimeofday(&(info->stopped_at));
++                info->next_tx_ns = 0; /* Transmit immediately */
++                
++                /* Move to the front of the running list */
++                info->next = pg_thread->running_if_infos;
++                pg_thread->running_if_infos = info;
++                pg_thread->running_if_sz++;
++        }
++        pg_unlock(pg_thread, __FUNCTION__);
++        PG_DEBUG(printk("Leaving pg_start_interface..\n"));
++        return 0;
++}/* pg_start_interface */
+-      total = (info->stopped_at.tv_sec - info->started_at.tv_sec) * 1000000 +
+-              info->stopped_at.tv_usec - info->started_at.tv_usec;
+-      idle = (__u32)(info->idle_acc)/(__u32)(cpu_speed);
++/* set stopped-at timer, remove from running list, do counters & statistics
++ * NOTE:  We do not remove from the rcv hash.
++ */
++static int pg_stop_interface(struct pktgen_thread_info* pg_thread,
++                             struct pktgen_interface_info* info) {
++        __u64 total_us;
++        if (!info->do_run_run) {
++                printk("pktgen interface: %s is already stopped\n", info->ifname);
++                return -EINVAL;
++        }
++        
++        info->stopped_at = getCurMs();
++        info->do_run_run = 0;
++
++        /* The main worker loop will place it onto the stopped list if needed,
++         * next time this interface is asked to be re-inserted into the
++         * list.
++         */
++        
++      total_us = info->stopped_at - info->started_at;
+         {
++                __u64 idle = pg_div(info->idle_acc, pg_cycles_per_us);
+               char *p = info->result;
+-                __u64 pps = (__u32)(info->sofar * 1000) / ((__u32)(total) / 1000);
+-                __u64 bps = pps * 8 * (info->pkt_size + 4); /* take 32bit ethernet CRC into account */
+-              p += sprintf(p, "OK: %llu(c%llu+d%llu) usec, %llu (%dbyte,%dfrags) %llupps %lluMb/sec (%llubps)  errors: %llu",
+-                           (unsigned long long) total,
+-                           (unsigned long long) (total - idle),
+-                           (unsigned long long) idle,
+-                           (unsigned long long) info->sofar,
+-                             skb->len + 4, /* Add 4 to account for the ethernet checksum */
+-                             nr_frags,
+-                           (unsigned long long) pps,
+-                           (unsigned long long) (bps / (u64) 1024 / (u64) 1024),
+-                           (unsigned long long) bps,
+-                           (unsigned long long) info->errors
++                __u64 pps = divremdi3(info->sofar * 1000, pg_div(total_us, 1000), PG_DIV);
++                __u64 bps = pps * 8 * (info->cur_pkt_size + 4); /* take 32bit ethernet CRC into account */
++                
++              p += sprintf(p, "OK: %llu(c%llu+d%llu) usec, %llu (%dbyte) %llupps %lluMb/sec (%llubps)  errors: %llu",
++                           total_us, total_us - idle, idle,
++                           info->sofar,
++                             info->cur_pkt_size + 4, /* Add 4 to account for the ethernet checksum */
++                           pps,
++                           bps >> 20, bps, info->errors
+                            );
+       }
++        return 0;
++}/* pg_stop_interface */
++
++
++/* Re-inserts 'last' into the pg_thread's list.  Calling code should
++ * make sure that 'last' is not already in the list.
++ */
++static struct pktgen_interface_info* pg_resort_pginfos(struct pktgen_thread_info* pg_thread,
++                                                       struct pktgen_interface_info* last,
++                                                       int setup_cur_if) {
++        struct pktgen_interface_info* rv = NULL;
++        
++        pg_lock(pg_thread, __FUNCTION__);
++        {
++                struct pktgen_interface_info* p = pg_thread->running_if_infos;
+         
+-out_reldev:
+-        if (odev) {
+-                dev_put(odev);
+-                odev = NULL;
++                if (last) {
++                        if (!last->do_run_run) {
++                                /* If this guy was stopped while 'current', then
++                                 * we'll want to place him on the stopped list
++                                 * here.
++                                 */
++                                last->next = pg_thread->stopped_if_infos;
++                                pg_thread->stopped_if_infos = last;
++                                pg_thread->running_if_sz--;
++                        }
++                        else {
++                                /* re-insert */
++                                if (!p) {
++                                        pg_thread->running_if_infos = last;
++                                        last->next = NULL;
++                                }
++                                else {
++                                        /* Another special case, check to see if we should go at the
++                                         * front of the queue.
++                                         */
++                                        if (p->next_tx_ns > last->next_tx_ns) {
++                                                last->next = p;
++                                                pg_thread->running_if_infos = last;
++                                        }
++                                        else {
++                                                int inserted = 0;
++                                                while (p->next) {
++                                                        if (p->next->next_tx_ns > last->next_tx_ns) {
++                                                                /* Insert into the list */
++                                                                last->next = p->next;
++                                                                p->next = last;
++                                                                inserted = 1;
++                                                                break;
++                                                        }
++                                                        p = p->next;
++                                                }
++                                                if (!inserted) {
++                                                        /* place at the end */
++                                                        last->next = NULL;
++                                                        p->next = last;
++                                                }
++                                        }
++                                }
++                        }
++                }
++
++                /* List is re-sorted, so grab the first one to return */
++                rv = pg_thread->running_if_infos;
++                if (rv) {
++                        /* Pop him off of the list.  We do this here because we already
++                         * have the lock.  Calling code just has to be aware of this
++                         * feature.
++                         */
++                        pg_thread->running_if_infos = rv->next;
++                }
++        }
++
++        if (setup_cur_if) {
++                pg_thread->cur_if = rv;
++        }
++        
++        pg_unlock(pg_thread, __FUNCTION__);
++        return rv;
++}/* pg_resort_pginfos */
++
++
++void pg_stop_all_ifs(struct pktgen_thread_info* pg_thread) {
++        struct pktgen_interface_info* next = NULL;
++
++        pg_lock(pg_thread, __FUNCTION__);
++        if (pg_thread->cur_if) {
++                /* Move it onto the stopped list */
++                pg_stop_interface(pg_thread, pg_thread->cur_if);
++                pg_thread->cur_if->next = pg_thread->stopped_if_infos;
++                pg_thread->stopped_if_infos = pg_thread->cur_if;
++                pg_thread->cur_if = NULL;
++        }
++        pg_unlock(pg_thread, __FUNCTION__);
++
++        /* These have their own locking */
++        next = pg_resort_pginfos(pg_thread, NULL, 0);
++        while (next) {
++                pg_stop_interface(pg_thread, next);
++                next = pg_resort_pginfos(pg_thread, NULL, 0);
+         }
++}/* pg_stop_all_ifs */
+-        /* TODO:  Is this worth printing out (other than for debug?) */
+-        printk("fp = %llu\n", (unsigned long long) fp);
+-      return;
++void pg_rem_all_ifs(struct pktgen_thread_info* pg_thread) {
++        struct pktgen_interface_info* next = NULL;
++        
++        /* Remove all interfaces, clean up memory */
++        while ((next = pg_thread->stopped_if_infos)) {
++                int rv = pg_rem_interface_info(pg_thread, next);
++                if (rv >= 0) {
++                        kfree(next);
++                }
++                else {
++                        printk("ERROR: failed to rem_interface: %i\n", rv);
++                }
++        }
++}/* pg_rem_all_ifs */
++
++
++void pg_rem_from_thread_list(struct pktgen_thread_info* pg_thread) {
++        /* Remove from the thread list */
++        pg_lock_thread_list(__FUNCTION__);
++        {
++                struct pktgen_thread_info* tmp = pktgen_threads;
++                if (tmp == pg_thread) {
++                        pktgen_threads = tmp->next;
++                }
++                else {
++                        while (tmp) {
++                                if (tmp->next == pg_thread) {
++                                        tmp->next = pg_thread->next;
++                                        pg_thread->next = NULL;
++                                        break;
++                                }
++                                tmp = tmp->next;
++                        }
++                }
++        }
++        pg_unlock_thread_list(__FUNCTION__);
++}/* pg_rem_from_thread_list */
++
++
++/* Main loop of the thread.  Send pkts.
++ */
++void pg_thread_worker(struct pktgen_thread_info* pg_thread) {
++      struct net_device *odev = NULL;
++        __u64 idle_start = 0;
++        struct pktgen_interface_info* next = NULL;
++        u32 next_ipg = 0;
++        u64 now = 0; /* in nano-seconds */
++        u32 tx_since_softirq = 0;
++        u32 queue_stopped = 0;
++        
++        /* setup the thread environment */
++        init_pktgen_kthread(pg_thread, "kpktgend");
++        
++        PG_DEBUG(printk("Starting up pktgen thread: %s\n", pg_thread->name));
++        
++        /* an endless loop in which we are doing our work */
++        while (1) {
++
++                /* Re-sorts the list, inserting 'next' (which is really the last one
++                 * we used).  It pops the top one off of the queue and returns it.
++                 * Calling code must make sure to re-insert the returned value
++                 */
++                next = pg_resort_pginfos(pg_thread, next, 1);
++                
++                if (next) {
++
++                        odev = next->odev;
++
++                        if (next->ipg) {
++
++                                now = getRelativeCurNs();
++                                if (now < next->next_tx_ns) {
++                                        next_ipg = (u32)(next->next_tx_ns - now);
++                                        
++                                        /* Try not to busy-spin if we have larger sleep times.
++                                        * TODO:  Investigate better ways to do this.
++                                        */
++                                        if (next_ipg < 10000) { /* 10 usecs or less */
++                                                nanospin(next_ipg, next);
++                                        }
++                                        else if (next_ipg < 10000000) { /* 10ms or less */
++                                                pg_udelay(next_ipg / 1000, next, pg_thread);
++                                        }
++                                        else {
++                                                /* fall asleep for 10ms or more. */
++                                                pg_udelay(next_ipg / 1000, next, pg_thread);
++                                        }
++                                }
++
++                                /* This is max IPG, this has special meaning of
++                                 * "never transmit"
++                                 */
++                                if (next->ipg == 0x7FFFFFFF) {
++                                        next->next_tx_ns = getRelativeCurNs() + next->ipg;
++                                        continue;
++                                }
++                        }
++                
++                        if (netif_queue_stopped(odev) || current->need_resched) {
++                                
++                                idle_start = get_cycles();
++
++                                if (!netif_running(odev)) {
++                                        pg_stop_interface(pg_thread, next);
++                                        continue;
++                                }
++                                if (current->need_resched) {
++                                        schedule();
++                                }
++                                else {
++                                        do_softirq();
++                                        tx_since_softirq = 0;
++                                }
++                                next->idle_acc += get_cycles() - idle_start;
++
++                                if (netif_queue_stopped(odev)) {
++                                        queue_stopped++;
++                                        continue; /* Try the next interface */
++                                }
++                        }
++                        
++                        if (next->last_ok || !next->skb) {
++                                if ((++next->fp_tmp >= next->multiskb ) || (!next->skb)) {
++                                        /* build a new pkt */
++                                        if (next->skb) {
++                                                kfree_skb(next->skb);
++                                        }
++                                        next->skb = fill_packet(odev, next);
++                                        if (next->skb == NULL) {
++                                                printk("ERROR:  Couldn't allocate skb in fill_packet.\n");
++                                                schedule();
++                                                next->fp_tmp--; /* back out increment, OOM */
++                                                continue;
++                                        }
++                                        next->fp++;
++                                        next->fp_tmp = 0; /* reset counter */
++                                        /* Not sure what good knowing nr_frags is...
++                                        next->nr_frags = skb_shinfo(skb)->nr_frags;
++                                        */
++                                }
++                                atomic_inc(&(next->skb->users));
++                        }
++                
++                        spin_lock_bh(&odev->xmit_lock);
++                        if (!netif_queue_stopped(odev)) {
++                                if (odev->hard_start_xmit(next->skb, odev)) {
++                                        if (net_ratelimit()) {
++                                                printk(KERN_INFO "Hard xmit error\n");
++                                        }
++                                        next->errors++;
++                                        next->last_ok = 0;
++                                        queue_stopped++;
++                                }
++                                else {
++                                        queue_stopped = 0;
++                                        next->last_ok = 1;
++                                        next->sofar++;
++                                        next->tx_bytes += (next->cur_pkt_size + 4); /* count csum */
++                                }
++
++                                next->next_tx_ns = getRelativeCurNs() + next->ipg;
++                        }
++                        else {  /* Re-try it next time */
++                                queue_stopped++;
++                                next->last_ok = 0;
++                        }
++
++                        spin_unlock_bh(&odev->xmit_lock);
++
++                        if (++tx_since_softirq > pg_thread->max_before_softirq) {
++                                do_softirq();
++                                tx_since_softirq = 0;
++                        }
++
++                        /* If next->count is zero, then run forever */
++                        if ((next->count != 0) && (next->sofar >= next->count)) {
++                                if (atomic_read(&(next->skb->users)) != 1) {
++                                        idle_start = get_cycles();
++                                        while (atomic_read(&(next->skb->users)) != 1) {
++                                                if (signal_pending(current)) {
++                                                        break;
++                                                }
++                                                schedule();
++                                        }
++                                        next->idle_acc += get_cycles() - idle_start;
++                                }
++                                pg_stop_interface(pg_thread, next);
++                        }/* if we're done with a particular interface. */
++
++                }/* if could find the next interface to send on. */
++                else {
++                        /* fall asleep for a bit */
++                        interruptible_sleep_on_timeout(&(pg_thread->queue), HZ/10);
++                        queue_stopped = 0;
++                }
++                
++                /* here we are back from sleep, either due to the timeout
++                   (one second), or because we caught a signal.
++                */
++                if (pg_thread->terminate || signal_pending(current)) {
++                        /* we received a request to terminate ourself */
++                        break;
++                }
++
++                if (queue_stopped > pg_thread->running_if_sz) {
++                        /* All our devices are all fulled up, schedule and hope to run
++                         * again soon.
++                         */
++                        schedule();
++                        queue_stopped = 0;
++                }
++        }//while true
++
++        /* here we go only in case of termination of the thread */
++
++        PG_DEBUG(printk("pgthread: %s  stopping all Interfaces.\n", pg_thread->name));
++        pg_stop_all_ifs(pg_thread);
++
++        PG_DEBUG(printk("pgthread: %s  removing all Interfaces.\n", pg_thread->name));
++        pg_rem_all_ifs(pg_thread);
++
++        pg_rem_from_thread_list(pg_thread);
++        
++        /* cleanup the thread, leave */
++        PG_DEBUG(printk("pgthread: %s  calling exit_pktgen_kthread.\n", pg_thread->name));
++        exit_pktgen_kthread(pg_thread);
++}
++
++/* private functions */
++static void kthread_launcher(void *data) {
++        struct pktgen_thread_info *kthread = data;
++        kernel_thread((int (*)(void *))kthread->function, (void *)kthread, 0);       
+ }
+-/* proc/net/pktgen/pg */
++/* create a new kernel thread. Called by the creator. */
++void start_pktgen_kthread(struct pktgen_thread_info *kthread) {
+-static int proc_busy_read(char *buf , char **start, off_t offset,
+-                           int len, int *eof, void *data)
+-{
+-      char *p;
+-        int idx = (int)(long)(data);
+-        struct pktgen_info* info = NULL;
++        /* initialize the semaphore:
++           we start with the semaphore locked. The new kernel
++           thread will setup its stuff and unlock it. This
++           control flow (the one that creates the thread) blocks
++           in the down operation below until the thread has reached
++           the up() operation.
++         */
++        init_MUTEX_LOCKED(&kthread->startstop_sem);
++
++        /* store the function to be executed in the data passed to
++           the launcher */
++        kthread->function = pg_thread_worker;
+         
+-        if ((idx < 0) || (idx >= MAX_PKTGEN)) {
+-                printk("ERROR: idx: %i is out of range in proc_write\n", idx);
+-                return -EINVAL;
++        /* create the new thread my running a task through keventd */
++
++        /* initialize the task queue structure */
++        kthread->tq.sync = 0;
++        INIT_LIST_HEAD(&kthread->tq.list);
++        kthread->tq.routine = kthread_launcher;
++        kthread->tq.data = kthread;
++
++        /* and schedule it for execution */
++        schedule_task(&kthread->tq);
++
++        /* wait till it has reached the setup_thread routine */
++        down(&kthread->startstop_sem);
++}
++
++/* stop a kernel thread. Called by the removing instance */
++static void stop_pktgen_kthread(struct pktgen_thread_info *kthread) {
++        PG_DEBUG(printk("pgthread: %s  stop_pktgen_kthread.\n", kthread->name));
++
++        if (kthread->thread == NULL) {
++                printk("stop_kthread: killing non existing thread!\n");
++                return;
+         }
+-        info = &(pginfos[idx]);
+-  
+-      p = buf;
+-      p += sprintf(p, "%d\n", info->busy);
+-      *eof = 1;
+-  
+-      return p-buf;
++
++        /* Stop each interface */
++        pg_lock(kthread, __FUNCTION__);
++        {
++                struct pktgen_interface_info* tmp = kthread->running_if_infos;
++                while (tmp) {
++                        tmp->do_run_run = 0;
++                        tmp->next_tx_ns = 0;
++                        tmp = tmp->next;
++                }
++                if (kthread->cur_if) {
++                        kthread->cur_if->do_run_run = 0;
++                        kthread->cur_if->next_tx_ns = 0;
++                }
++        }
++        pg_unlock(kthread, __FUNCTION__);
++
++        /* Wait for everything to fully stop */
++        while (1) {
++                pg_lock(kthread, __FUNCTION__);
++                if (kthread->cur_if || kthread->running_if_infos) {
++                        pg_unlock(kthread, __FUNCTION__);
++                        if (current->need_resched) {
++                                schedule();
++                        }
++                        mdelay(1);
++                }
++                else {
++                        pg_unlock(kthread, __FUNCTION__);
++                        break;
++                }
++        }
++        
++        /* this function needs to be protected with the big
++         kernel lock (lock_kernel()). The lock must be
++           grabbed before changing the terminate
++         flag and released after the down() call. */
++        lock_kernel();
++        
++        /* initialize the semaphore. We lock it here, the
++           leave_thread call of the thread to be terminated
++           will unlock it. As soon as we see the semaphore
++           unlocked, we know that the thread has exited.
++      */
++        init_MUTEX_LOCKED(&kthread->startstop_sem);
++
++        /* We need to do a memory barrier here to be sure that
++           the flags are visible on all CPUs. 
++        */
++        mb();
++
++        /* set flag to request thread termination */
++        kthread->terminate = 1;
++
++        /* We need to do a memory barrier here to be sure that
++           the flags are visible on all CPUs. 
++        */
++        mb();
++        kill_proc(kthread->thread->pid, SIGKILL, 1);
++       
++        /* block till thread terminated */
++        down(&kthread->startstop_sem);
++        kthread->in_use = 0;
++        
++        /* release the big kernel lock */
++        unlock_kernel();
++
++        /* now we are sure the thread is in zombie state. We
++           notify keventd to clean the process up.
++        */
++        kill_proc(2, SIGCHLD, 1);
++
++        PG_DEBUG(printk("pgthread: %s  done with stop_pktgen_kthread.\n", kthread->name));
++}/* stop_pktgen_kthread */
++
++
++/* initialize new created thread. Called by the new thread. */
++void init_pktgen_kthread(struct pktgen_thread_info *kthread, char *name) {
++        /* lock the kernel. A new kernel thread starts without
++           the big kernel lock, regardless of the lock state
++           of the creator (the lock level is *not* inheritated)
++        */
++        lock_kernel();
++
++        /* fill in thread structure */
++        kthread->thread = current;
++
++        /* set signal mask to what we want to respond */
++        siginitsetinv(&current->blocked, sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM));
++
++        /* initialise wait queue */
++        init_waitqueue_head(&kthread->queue);
++
++        /* initialise termination flag */
++        kthread->terminate = 0;
++
++        /* set name of this process (max 15 chars + 0 !) */
++        sprintf(current->comm, name);
++        
++        /* let others run */
++        unlock_kernel();
++
++        /* tell the creator that we are ready and let him continue */
++        up(&kthread->startstop_sem);
++}/* init_pktgen_kthread */
++
++/* cleanup of thread. Called by the exiting thread. */
++static void exit_pktgen_kthread(struct pktgen_thread_info *kthread) {
++        /* we are terminating */
++
++      /* lock the kernel, the exit will unlock it */
++        lock_kernel();
++        kthread->thread = NULL;
++        mb();
++
++        /* Clean up proc file system */
++        if (strlen(kthread->fname)) {
++                remove_proc_entry(kthread->fname, NULL);
++        }
++
++        /* notify the stop_kthread() routine that we are terminating. */
++      up(&kthread->startstop_sem);
++      /* the kernel_thread that called clone() does a do_exit here. */
++
++      /* there is no race here between execution of the "killer" and real termination
++         of the thread (race window between up and do_exit), since both the
++         thread and the "killer" function are running with the kernel lock held.
++         The kernel lock will be freed after the thread exited, so the code
++         is really not executed anymore as soon as the unload functions gets
++         the kernel lock back.
++         The init process may not have made the cleanup of the process here,
++         but the cleanup can be done safely with the module unloaded.
++      */
++}/* exit_pktgen_kthread */
++
++
++/* proc/net/pg */
++
++static char* pg_display_latency(struct pktgen_interface_info* info, char* p, int reset_latency) {
++        int i;
++        p += sprintf(p, "     avg_latency: %dus  min_lat: %dus  max_lat: %dus  pkts_in_sample: %llu\n",
++                     info->avg_latency, info->min_latency, info->max_latency,
++                     info->pkts_rcvd_since_clear);
++        p += sprintf(p, "      Buckets(us) [ ");
++        for (i = 0; i<LAT_BUCKETS_MAX; i++) {
++                p += sprintf(p, "%llu  ", info->latency_bkts[i]);
++        }
++        p += sprintf(p, "]\n");        
++
++        if (reset_latency) {
++                pg_reset_latency_counters(info);
++        }
++        return p;
+ }
+-static int proc_read(char *buf , char **start, off_t offset,
+-                      int len, int *eof, void *data)
++static int proc_pg_if_read(char *buf , char **start, off_t offset,
++                           int len, int *eof, void *data)
+ {
+       char *p;
+       int i;
+-        int idx = (int)(long)(data);
+-        struct pktgen_info* info = NULL;
++        struct pktgen_interface_info* info = (struct pktgen_interface_info*)(data);
+         __u64 sa;
+         __u64 stopped;
+-        __u64 now = getCurMs();
++        __u64 now = getCurUs();
++        __u64 now_rel_ns = getRelativeCurNs();
+         
+-        if ((idx < 0) || (idx >= MAX_PKTGEN)) {
+-                printk("ERROR: idx: %i is out of range in proc_write\n", idx);
+-                return -EINVAL;
+-        }
+-        info = &(pginfos[idx]);
+-  
+       p = buf;
+-        p += sprintf(p, "%s\n", VERSION); /* Help with parsing compatibility */
+-      p += sprintf(p, "Params: count %llu  pkt_size: %u  frags: %d  ipg: %u  clone_skb: %d odev \"%s\"\n",
+-                   (unsigned long long) info->count,
+-                   info->pkt_size, info->nfrags, info->ipg,
+-                     info->clone_skb, info->outdev);
+-        p += sprintf(p, "     dst_min: %s  dst_max: %s  src_min: %s  src_max: %s\n",
++        p += sprintf(p, "VERSION-1\n"); /* Help with parsing compatibility */
++      p += sprintf(p, "Params: count %llu  min_pkt_size: %u  max_pkt_size: %u\n     frags: %d  ipg: %u  multiskb: %d  ifname: %s\n",
++                   info->count, info->min_pkt_size, info->max_pkt_size,
++                     info->nfrags, info->ipg, info->multiskb, info->ifname);
++        p += sprintf(p, "     dst_min: %s  dst_max: %s\n     src_min: %s  src_max: %s\n",
+                      info->dst_min, info->dst_max, info->src_min, info->src_max);
+         p += sprintf(p, "     src_mac: ");
+       for (i = 0; i < 6; i++) {
+@@ -802,14 +1868,17 @@
+         p += sprintf(p, "     udp_src_min: %d  udp_src_max: %d  udp_dst_min: %d  udp_dst_max: %d\n",
+                      info->udp_src_min, info->udp_src_max, info->udp_dst_min,
+                      info->udp_dst_max);
+-        p += sprintf(p, "     src_mac_count: %d  dst_mac_count: %d\n     Flags: ",
+-                     info->src_mac_count, info->dst_mac_count);
++        p += sprintf(p, "     src_mac_count: %d  dst_mac_count: %d  peer_multiskb: %d\n     Flags: ",
++                     info->src_mac_count, info->dst_mac_count, info->peer_multiskb);
+         if (info->flags &  F_IPSRC_RND) {
+                 p += sprintf(p, "IPSRC_RND  ");
+         }
+         if (info->flags & F_IPDST_RND) {
+                 p += sprintf(p, "IPDST_RND  ");
+         }
++        if (info->flags & F_TXSIZE_RND) {
++                p += sprintf(p, "TXSIZE_RND  ");
++        }
+         if (info->flags & F_UDPSRC_RND) {
+                 p += sprintf(p, "UDPSRC_RND  ");
+         }
+@@ -824,22 +1893,24 @@
+         }
+         p += sprintf(p, "\n");
+         
+-        sa = tv_to_ms(&(info->started_at));
+-        stopped = tv_to_ms(&(info->stopped_at));
++        sa = info->started_at;
++        stopped = info->stopped_at;
+         if (info->do_run_run) {
+                 stopped = now; /* not really stopped, more like last-running-at */
+         }
+-        p += sprintf(p, "Current:\n     pkts-sofar: %llu  errors: %llu\n     started: %llums  stopped: %llums  now: %llums  idle: %lluns\n",
+-                     (unsigned long long) info->sofar,
+-                   (unsigned long long) info->errors,
+-                   (unsigned long long) sa,
+-                   (unsigned long long) stopped,
+-                   (unsigned long long) now,
+-                   (unsigned long long) info->idle_acc);
++        p += sprintf(p, "Current:\n     pkts-sofar: %llu  errors: %llu\n     started: %lluus  elapsed: %lluus\n     idle: %lluns  next_tx: %llu(%lli)ns\n",
++                     info->sofar, info->errors, sa, (stopped - sa), info->idle_acc,
++                     info->next_tx_ns, (long long)(info->next_tx_ns) - (long long)(now_rel_ns));
+         p += sprintf(p, "     seq_num: %d  cur_dst_mac_offset: %d  cur_src_mac_offset: %d\n",
+                      info->seq_num, info->cur_dst_mac_offset, info->cur_src_mac_offset);
+         p += sprintf(p, "     cur_saddr: 0x%x  cur_daddr: 0x%x  cur_udp_dst: %d  cur_udp_src: %d\n",
+                      info->cur_saddr, info->cur_daddr, info->cur_udp_dst, info->cur_udp_src);
++        p += sprintf(p, "     pkts_rcvd: %llu  bytes_rcvd: %llu  last_seq_rcvd: %d  ooo_rcvd: %llu\n",
++                     info->pkts_rcvd, info->bytes_rcvd, info->last_seq_rcvd, info->ooo_rcvd);
++        p += sprintf(p, "     dup_rcvd: %llu  seq_gap_rcvd(dropped): %llu  non_pg_rcvd: %llu\n",
++                     info->dup_rcvd, info->seq_gap_rcvd, info->non_pg_pkts_rcvd);
++
++        p = pg_display_latency(info, p, 0);
+         
+       if (info->result[0])
+               p += sprintf(p, "Result: %s\n", info->result);
+@@ -850,16 +1921,94 @@
+       return p - buf;
+ }
++
++static int proc_pg_thread_read(char *buf , char **start, off_t offset,
++                               int len, int *eof, void *data)
++{
++      char *p;
++        struct pktgen_thread_info* pg_thread = (struct pktgen_thread_info*)(data);
++        struct pktgen_interface_info* info = NULL;
++
++        if (!pg_thread) {
++                printk("ERROR: could not find pg_thread in proc_pg_thread_read\n");
++                return -EINVAL;
++        }
++        
++      p = buf;
++        p += sprintf(p, "VERSION-1\n"); /* Help with parsing compatibility */
++      p += sprintf(p, "Name: %s  max_before_softirq: %d\n",
++                     pg_thread->name, pg_thread->max_before_softirq);
++
++        pg_lock(pg_thread, __FUNCTION__);
++        if (pg_thread->cur_if) {
++                p += sprintf(p, "Current: %s\n", pg_thread->cur_if->ifname);
++        }
++        else {
++                p += sprintf(p, "Current: NULL\n");
++        }
++        pg_unlock(pg_thread, __FUNCTION__);
++        
++        p += sprintf(p, "Running: ");
++        
++        pg_lock(pg_thread, __FUNCTION__);
++        info = pg_thread->running_if_infos;
++        while (info) {
++                p += sprintf(p, "%s ", info->ifname);
++                info = info->next;
++        }
++        p += sprintf(p, "\nStopped: ");
++        info = pg_thread->stopped_if_infos;
++        while (info) {
++                p += sprintf(p, "%s ", info->ifname);
++                info = info->next;
++        }
++
++      if (pg_thread->result[0])
++              p += sprintf(p, "\nResult: %s\n", pg_thread->result);
++      else
++              p += sprintf(p, "\nResult: NA\n");
++      *eof = 1;
++
++        pg_unlock(pg_thread, __FUNCTION__);
++
++      return p - buf;
++}/* proc_pg_thread_read */
++
++
++static int proc_pg_ctrl_read(char *buf , char **start, off_t offset,
++                             int len, int *eof, void *data)
++{
++      char *p;
++        struct pktgen_thread_info* pg_thread = NULL;
++        
++      p = buf;
++        p += sprintf(p, "VERSION-1\n"); /* Help with parsing compatibility */
++        p += sprintf(p, "Threads: ");
++        
++        pg_lock_thread_list(__FUNCTION__);
++        pg_thread = pktgen_threads;
++        while (pg_thread) {
++                p += sprintf(p, "%s ", pg_thread->name);
++                pg_thread = pg_thread->next;
++        }
++        p += sprintf(p, "\n");
++        
++      *eof = 1;
++
++        pg_unlock_thread_list(__FUNCTION__);
++      return p - buf;
++}/* proc_pg_ctrl_read */
++
++
+ static int count_trail_chars(const char *user_buffer, unsigned int maxlen)
+ {
+       int i;
+       for (i = 0; i < maxlen; i++) {
+-              char c;
+-
+-              if (get_user(c, &user_buffer[i]))
+-                      return -EFAULT;
+-              switch (c) {
++                char c;
++                if (get_user(c, &user_buffer[i]))
++                        return -EFAULT;
++                switch (c) {
+               case '\"':
+               case '\n':
+               case '\r':
+@@ -875,7 +2024,7 @@
+       return i;
+ }
+-static unsigned long num_arg(const char *user_buffer, unsigned long maxlen,
++static unsigned long num_arg(const char *user_buffer, unsigned long maxlen, 
+                            unsigned long *num)
+ {
+       int i = 0;
+@@ -883,11 +2032,10 @@
+       *num = 0;
+   
+       for(; i < maxlen; i++) {
+-              char c;
+-
+-              if (get_user(c, &user_buffer[i]))
+-                      return -EFAULT;
+-              if ((c >= '0') && (c <= '9')) {
++                char c;
++                if (get_user(c, &user_buffer[i]))
++                        return -EFAULT;
++                if ((c >= '0') && (c <= '9')) {
+                       *num *= 10;
+                       *num += c -'0';
+               } else
+@@ -901,11 +2049,10 @@
+       int i = 0;
+       for(; i < maxlen; i++) {
+-              char c;
+-
+-              if (get_user(c, &user_buffer[i]))
+-                      return -EFAULT;
+-              switch (c) {
++                char c;
++                if (get_user(c, &user_buffer[i]))
++                        return -EFAULT;
++                switch (c) {
+               case '\"':
+               case '\n':
+               case '\r':
+@@ -913,189 +2060,220 @@
+               case ' ':
+                       goto done_str;
+               default:
+-                      break;
+               };
+       }
+ done_str:
+       return i;
+ }
+-static int proc_write(struct file *file, const char *user_buffer,
+-                       unsigned long count, void *data)
++static int proc_pg_if_write(struct file *file, const char *user_buffer,
++                            unsigned long count, void *data)
+ {
+       int i = 0, max, len;
+       char name[16], valstr[32];
+       unsigned long value = 0;
+-        int idx = (int)(long)(data);
+-        struct pktgen_info* info = NULL;
+-        char* result = NULL;
+-      int tmp;
++        struct pktgen_interface_info* info = (struct pktgen_interface_info*)(data);
++        char* pg_result = NULL;
++        int tmp = 0;
+         
+-        if ((idx < 0) || (idx >= MAX_PKTGEN)) {
+-                printk("ERROR: idx: %i is out of range in proc_write\n", idx);
+-                return -EINVAL;
+-        }
+-        info = &(pginfos[idx]);
+-        result = &(info->result[0]);
++        pg_result = &(info->result[0]);
+         
+       if (count < 1) {
+-              sprintf(result, "Wrong command format");
++              sprintf(pg_result, "Wrong command format");
+               return -EINVAL;
+       }
+   
+       max = count - i;
+       tmp = count_trail_chars(&user_buffer[i], max);
+-      if (tmp < 0)
+-              return tmp;
+-      i += tmp;
+-  
++        if (tmp < 0) { return tmp; }
++        i += tmp;
++        
+       /* Read variable name */
+       len = strn_len(&user_buffer[i], sizeof(name) - 1);
+-      if (len < 0)
+-              return len;
++        if (len < 0) { return len; }
+       memset(name, 0, sizeof(name));
+       copy_from_user(name, &user_buffer[i], len);
+       i += len;
+   
+       max = count -i;
+       len = count_trail_chars(&user_buffer[i], max);
+-      if (len < 0)
+-              return len;
++        if (len < 0) {
++                return len;
++        }
+       i += len;
+-      if (debug)
+-              printk("pg: %s,%lu\n", name, count);
++      if (debug) {
++                char tb[count + 1];
++                copy_from_user(tb, user_buffer, count);
++                tb[count] = 0;
++              printk("pg: %s,%lu  buffer -:%s:-\n", name, count, tb);
++        }
+       if (!strcmp(name, "stop")) {
+               if (info->do_run_run) {
+-                      strcpy(result, "Stopping");
++                      strcpy(pg_result, "Stopping");
++                        pg_stop_interface(info->pg_thread, info);
+                 }
+                 else {
+-                        strcpy(result, "Already stopped...\n");
++                        strcpy(pg_result, "Already stopped...\n");
++                }
++              return count;
++      }
++
++      if (!strcmp(name, "min_pkt_size")) {
++              len = num_arg(&user_buffer[i], 10, &value);
++                if (len < 0) { return len; }
++              i += len;
++              if (value < 14+20+8)
++                      value = 14+20+8;
++                if (value != info->min_pkt_size) {
++                        info->min_pkt_size = value;
++                        info->cur_pkt_size = value;
+                 }
+-                info->do_run_run = 0;
++              sprintf(pg_result, "OK: min_pkt_size=%u", info->min_pkt_size);
++              return count;
++      }
++
++        if (!strcmp(name, "debug")) {
++              len = num_arg(&user_buffer[i], 10, &value);
++                if (len < 0) { return len; }
++              i += len;
++                debug = value;
++              sprintf(pg_result, "OK: debug=%u", debug);
+               return count;
+       }
+-      if (!strcmp(name, "pkt_size")) {
++        if (!strcmp(name, "max_pkt_size")) {
+               len = num_arg(&user_buffer[i], 10, &value);
+-              if (len < 0)
+-                      return len;
++                if (len < 0) { return len; }
+               i += len;
+               if (value < 14+20+8)
+                       value = 14+20+8;
+-              info->pkt_size = value;
+-              sprintf(result, "OK: pkt_size=%u", info->pkt_size);
++                if (value != info->max_pkt_size) {
++                        info->max_pkt_size = value;
++                        info->cur_pkt_size = value;
++                }
++              sprintf(pg_result, "OK: max_pkt_size=%u", info->max_pkt_size);
+               return count;
+       }
+-      if (!strcmp(name, "frags")) {
++        
++        if (!strcmp(name, "frags")) {
+               len = num_arg(&user_buffer[i], 10, &value);
+-              if (len < 0)
+-                      return len;
++                if (len < 0) { return len; }
+               i += len;
+               info->nfrags = value;
+-              sprintf(result, "OK: frags=%u", info->nfrags);
++              sprintf(pg_result, "OK: frags=%u", info->nfrags);
+               return count;
+       }
+       if (!strcmp(name, "ipg")) {
+               len = num_arg(&user_buffer[i], 10, &value);
+-              if (len < 0)
+-                      return len;
++                if (len < 0) { return len; }
+               i += len;
+               info->ipg = value;
+-              sprintf(result, "OK: ipg=%u", info->ipg);
++                if ((getRelativeCurNs() + info->ipg) > info->next_tx_ns) {
++                        info->next_tx_ns = getRelativeCurNs() + info->ipg;
++                }
++              sprintf(pg_result, "OK: ipg=%u", info->ipg);
+               return count;
+       }
+       if (!strcmp(name, "udp_src_min")) {
+               len = num_arg(&user_buffer[i], 10, &value);
+-              if (len < 0)
+-                      return len;
++                if (len < 0) { return len; }
+               i += len;
+-              info->udp_src_min = value;
+-              sprintf(result, "OK: udp_src_min=%u", info->udp_src_min);
++                if (value != info->udp_src_min) {
++                        info->udp_src_min = value;
++                        info->cur_udp_src = value;
++                }       
++              sprintf(pg_result, "OK: udp_src_min=%u", info->udp_src_min);
+               return count;
+       }
+       if (!strcmp(name, "udp_dst_min")) {
+               len = num_arg(&user_buffer[i], 10, &value);
+-              if (len < 0)
+-                      return len;
++                if (len < 0) { return len; }
+               i += len;
+-              info->udp_dst_min = value;
+-              sprintf(result, "OK: udp_dst_min=%u", info->udp_dst_min);
++                if (value != info->udp_dst_min) {
++                        info->udp_dst_min = value;
++                        info->cur_udp_dst = value;
++                }
++              sprintf(pg_result, "OK: udp_dst_min=%u", info->udp_dst_min);
+               return count;
+       }
+       if (!strcmp(name, "udp_src_max")) {
+               len = num_arg(&user_buffer[i], 10, &value);
+-              if (len < 0)
+-                      return len;
++                if (len < 0) { return len; }
+               i += len;
+-              info->udp_src_max = value;
+-              sprintf(result, "OK: udp_src_max=%u", info->udp_src_max);
++                if (value != info->udp_src_max) {
++                        info->udp_src_max = value;
++                        info->cur_udp_src = value;
++                }
++              sprintf(pg_result, "OK: udp_src_max=%u", info->udp_src_max);
+               return count;
+       }
+       if (!strcmp(name, "udp_dst_max")) {
+               len = num_arg(&user_buffer[i], 10, &value);
+-              if (len < 0)
+-                      return len;
++                if (len < 0) { return len; }
+               i += len;
+-              info->udp_dst_max = value;
+-              sprintf(result, "OK: udp_dst_max=%u", info->udp_dst_max);
++                if (value != info->udp_dst_max) {
++                        info->udp_dst_max = value;
++                        info->cur_udp_dst = value;
++                }
++              sprintf(pg_result, "OK: udp_dst_max=%u", info->udp_dst_max);
+               return count;
+       }
+-      if (!strcmp(name, "clone_skb")) {
++      if (!strcmp(name, "multiskb")) {
+               len = num_arg(&user_buffer[i], 10, &value);
+-              if (len < 0)
+-                      return len;
++                if (len < 0) { return len; }
+               i += len;
+-                info->clone_skb = value;
++                info->multiskb = value;
+       
+-              sprintf(result, "OK: clone_skb=%d", info->clone_skb);
++              sprintf(pg_result, "OK: multiskb=%d", info->multiskb);
++              return count;
++      }
++      if (!strcmp(name, "peer_multiskb")) {
++              len = num_arg(&user_buffer[i], 10, &value);
++                if (len < 0) { return len; }
++              i += len;
++                info->peer_multiskb = value;
++      
++              sprintf(pg_result, "OK: peer_multiskb=%d", info->peer_multiskb);
+               return count;
+       }
+       if (!strcmp(name, "count")) {
+               len = num_arg(&user_buffer[i], 10, &value);
+-              if (len < 0)
+-                      return len;
++                if (len < 0) { return len; }
+               i += len;
+               info->count = value;
+-              sprintf(result, "OK: count=%llu", (unsigned long long) info->count);
++              sprintf(pg_result, "OK: count=%llu", info->count);
+               return count;
+       }
+       if (!strcmp(name, "src_mac_count")) {
+               len = num_arg(&user_buffer[i], 10, &value);
+-              if (len < 0)
+-                      return len;
++                if (len < 0) { return len; }
+               i += len;
+-              info->src_mac_count = value;
+-              sprintf(result, "OK: src_mac_count=%d", info->src_mac_count);
++              if (info->src_mac_count != value) {
++                        info->src_mac_count = value;
++                        info->cur_src_mac_offset = 0;
++                }
++              sprintf(pg_result, "OK: src_mac_count=%d", info->src_mac_count);
+               return count;
+       }
+       if (!strcmp(name, "dst_mac_count")) {
+               len = num_arg(&user_buffer[i], 10, &value);
+-              if (len < 0)
+-                      return len;
++                if (len < 0) { return len; }
+               i += len;
+-              info->dst_mac_count = value;
+-              sprintf(result, "OK: dst_mac_count=%d", info->dst_mac_count);
+-              return count;
+-      }
+-      if (!strcmp(name, "odev")) {
+-              len = strn_len(&user_buffer[i], sizeof(info->outdev) - 1);
+-              if (len < 0)
+-                      return len;
+-              memset(info->outdev, 0, sizeof(info->outdev));
+-              copy_from_user(info->outdev, &user_buffer[i], len);
+-              i += len;
+-              sprintf(result, "OK: odev=%s", info->outdev);
++              if (info->dst_mac_count != value) {
++                        info->dst_mac_count = value;
++                        info->cur_dst_mac_offset = 0;
++                }
++              sprintf(pg_result, "OK: dst_mac_count=%d", info->dst_mac_count);
+               return count;
+       }
+       if (!strcmp(name, "flag")) {
+                 char f[32];
+                 memset(f, 0, 32);
+               len = strn_len(&user_buffer[i], sizeof(f) - 1);
+-              if (len < 0)
+-                      return len;
++                if (len < 0) { return len; }
+               copy_from_user(f, &user_buffer[i], len);
+               i += len;
+                 if (strcmp(f, "IPSRC_RND") == 0) {
+@@ -1104,6 +2282,12 @@
+                 else if (strcmp(f, "!IPSRC_RND") == 0) {
+                         info->flags &= ~F_IPSRC_RND;
+                 }
++                else if (strcmp(f, "TXSIZE_RND") == 0) {
++                        info->flags |= F_TXSIZE_RND;
++                }
++                else if (strcmp(f, "!TXSIZE_RND") == 0) {
++                        info->flags &= ~F_TXSIZE_RND;
++                }
+                 else if (strcmp(f, "IPDST_RND") == 0) {
+                         info->flags |= F_IPDST_RND;
+                 }
+@@ -1135,69 +2319,94 @@
+                         info->flags &= ~F_MACDST_RND;
+                 }
+                 else {
+-                        sprintf(result, "Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
++                        sprintf(pg_result, "Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
+                                 f,
+-                                "IPSRC_RND, IPDST_RND, UDPSRC_RND, UDPDST_RND, MACSRC_RND, MACDST_RND\n");
++                                "IPSRC_RND, IPDST_RND, TXSIZE_RND, UDPSRC_RND, UDPDST_RND, MACSRC_RND, MACDST_RND\n");
+                         return count;
+                 }
+-              sprintf(result, "OK: flags=0x%x", info->flags);
++              sprintf(pg_result, "OK: flags=0x%x", info->flags);
+               return count;
+       }
+       if (!strcmp(name, "dst_min") || !strcmp(name, "dst")) {
++                char buf[IP_NAME_SZ];
+               len = strn_len(&user_buffer[i], sizeof(info->dst_min) - 1);
+-              if (len < 0)
+-                      return len;
+-              memset(info->dst_min, 0, sizeof(info->dst_min));
+-              copy_from_user(info->dst_min, &user_buffer[i], len);
+-              if(debug)
+-                      printk("pg: dst_min set to: %s\n", info->dst_min);
+-              i += len;
+-              sprintf(result, "OK: dst_min=%s", info->dst_min);
++                if (len < 0) { return len; }
++                copy_from_user(buf, &user_buffer[i], len);
++                buf[len] = 0;
++                if (strcmp(buf, info->dst_min) != 0) {
++                        memset(info->dst_min, 0, sizeof(info->dst_min));
++                        strncpy(info->dst_min, buf, len);
++                        info->daddr_min = in_aton(info->dst_min);
++                        info->cur_daddr = info->daddr_min;
++                }
++                if(debug)
++                        printk("pg: dst_min set to: %s\n", info->dst_min);
++                i += len;
++              sprintf(pg_result, "OK: dst_min=%s", info->dst_min);
+               return count;
+       }
+       if (!strcmp(name, "dst_max")) {
++                char buf[IP_NAME_SZ];
+               len = strn_len(&user_buffer[i], sizeof(info->dst_max) - 1);
+-              if (len < 0)
+-                      return len;
+-              memset(info->dst_max, 0, sizeof(info->dst_max));
+-              copy_from_user(info->dst_max, &user_buffer[i], len);
++                if (len < 0) { return len; }
++                copy_from_user(buf, &user_buffer[i], len);
++                buf[len] = 0;
++                if (strcmp(buf, info->dst_max) != 0) {
++                        memset(info->dst_max, 0, sizeof(info->dst_max));
++                        strncpy(info->dst_max, buf, len);
++                        info->daddr_max = in_aton(info->dst_max);
++                        info->cur_daddr = info->daddr_max;
++                }
+               if(debug)
+                       printk("pg: dst_max set to: %s\n", info->dst_max);
+               i += len;
+-              sprintf(result, "OK: dst_max=%s", info->dst_max);
++              sprintf(pg_result, "OK: dst_max=%s", info->dst_max);
+               return count;
+       }
+       if (!strcmp(name, "src_min")) {
++                char buf[IP_NAME_SZ];
+               len = strn_len(&user_buffer[i], sizeof(info->src_min) - 1);
+-              if (len < 0)
+-                      return len;
+-              memset(info->src_min, 0, sizeof(info->src_min));
+-              copy_from_user(info->src_min, &user_buffer[i], len);
++                if (len < 0) { return len; }
++                copy_from_user(buf, &user_buffer[i], len);
++                buf[len] = 0;
++                if (strcmp(buf, info->src_min) != 0) {
++                        memset(info->src_min, 0, sizeof(info->src_min));
++                        strncpy(info->src_min, buf, len);
++                        info->saddr_min = in_aton(info->src_min);
++                        info->cur_saddr = info->saddr_min;
++                }
+               if(debug)
+                       printk("pg: src_min set to: %s\n", info->src_min);
+               i += len;
+-              sprintf(result, "OK: src_min=%s", info->src_min);
++              sprintf(pg_result, "OK: src_min=%s", info->src_min);
+               return count;
+       }
+       if (!strcmp(name, "src_max")) {
++                char buf[IP_NAME_SZ];
+               len = strn_len(&user_buffer[i], sizeof(info->src_max) - 1);
+-              if (len < 0)
+-                      return len;
+-              memset(info->src_max, 0, sizeof(info->src_max));
+-              copy_from_user(info->src_max, &user_buffer[i], len);
++                if (len < 0) { return len; }
++                copy_from_user(buf, &user_buffer[i], len);
++                buf[len] = 0;
++                if (strcmp(buf, info->src_max) != 0) {
++                        memset(info->src_max, 0, sizeof(info->src_max));
++                        strncpy(info->src_max, buf, len);
++                        info->saddr_max = in_aton(info->src_max);
++                        info->cur_saddr = info->saddr_max;
++                }
+               if(debug)
+                       printk("pg: src_max set to: %s\n", info->src_max);
+               i += len;
+-              sprintf(result, "OK: src_max=%s", info->src_max);
++              sprintf(pg_result, "OK: src_max=%s", info->src_max);
+               return count;
+       }
+-      if (!strcmp(name, "dstmac")) {
++      if (!strcmp(name, "dst_mac")) {
+               char *v = valstr;
++                unsigned char old_dmac[6];
+               unsigned char *m = info->dst_mac;
+-
++                memcpy(old_dmac, info->dst_mac, 6);
++                
+               len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
+-              if (len < 0)
+-                      return len;
++                if (len < 0) { return len; }
+               memset(valstr, 0, sizeof(valstr));
+               copy_from_user(valstr, &user_buffer[i], len);
+               i += len;
+@@ -1219,17 +2428,24 @@
+                               m++;
+                               *m = 0;
+                       }
+-              }         
+-              sprintf(result, "OK: dstmac");
++              }
++
++                if (memcmp(old_dmac, info->dst_mac, 6) != 0) {
++                        /* Set up Dest MAC */
++                        memcpy(&(info->hh[0]), info->dst_mac, 6);
++                }
++                
++              sprintf(pg_result, "OK: dstmac");
+               return count;
+       }
+-      if (!strcmp(name, "srcmac")) {
++      if (!strcmp(name, "src_mac")) {
+               char *v = valstr;
++                unsigned char old_smac[6];
+               unsigned char *m = info->src_mac;
++                memcpy(old_smac, info->src_mac, 6);
+               len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
+-              if (len < 0)
+-                      return len;
++                if (len < 0) { return len; }
+               memset(valstr, 0, sizeof(valstr));
+               copy_from_user(valstr, &user_buffer[i], len);
+               i += len;
+@@ -1252,28 +2468,186 @@
+                               *m = 0;
+                       }
+               }         
+-              sprintf(result, "OK: srcmac");
++
++                if (memcmp(old_smac, info->src_mac, 6) != 0) {
++                        /* Default to the interface's mac if not explicitly set. */
++                        if ((!(info->flags & F_SET_SRCMAC)) && info->odev) {
++                                memcpy(&(info->hh[6]), info->odev->dev_addr, 6);
++                        }
++                        else {
++                                memcpy(&(info->hh[6]), info->src_mac, 6);
++                        }
++                }
++
++                sprintf(pg_result, "OK: srcmac");
+               return count;
+       }
++        if (!strcmp(name, "clear_counters")) {
++                pg_clear_counters(info, 0);
++                sprintf(pg_result, "OK: Clearing counters...\n");
++                return count;
++        }
++        
+       if (!strcmp(name, "inject") || !strcmp(name, "start")) {
+-              MOD_INC_USE_COUNT;
+-                if (info->busy) {
++                if (info->do_run_run) {
+                         strcpy(info->result, "Already running...\n");
+                 }
+                 else {
+-                        info->busy = 1;
+-                        strcpy(info->result, "Starting");
+-                        inject(info);
+-                        info->busy = 0;
++                        int rv;
++                        if ((rv = pg_start_interface(info->pg_thread, info)) >= 0) {
++                                strcpy(info->result, "Starting");
++                        }
++                        else {
++                                sprintf(info->result, "Error starting: %i\n", rv);
++                        }
+                 }
+-              MOD_DEC_USE_COUNT;
+               return count;
+       }
+       sprintf(info->result, "No such parameter \"%s\"", name);
+       return -EINVAL;
+-}
++}/* proc_pg_if_write */
++
++
++static int proc_pg_ctrl_write(struct file *file, const char *user_buffer,
++                              unsigned long count, void *data)
++{
++      int i = 0, max, len;
++      char name[16];
++        struct pktgen_thread_info* pg_thread = NULL;
++        
++      if (count < 1) {
++              printk("Wrong command format");
++              return -EINVAL;
++      }
++  
++      max = count - i;
++      len = count_trail_chars(&user_buffer[i], max);
++        if (len < 0) { return len; }
++        i += len;
++        
++      /* Read variable name */
++
++      len = strn_len(&user_buffer[i], sizeof(name) - 1);
++        if (len < 0) { return len; }
++      memset(name, 0, sizeof(name));
++      copy_from_user(name, &user_buffer[i], len);
++      i += len;
++  
++      max = count -i;
++      len = count_trail_chars(&user_buffer[i], max);
++        if (len < 0) { return len; }
++      i += len;
++
++      if (debug)
++              printk("pg_thread: %s,%lu\n", name, count);
++
++      if (!strcmp(name, "stop")) {
++                char f[32];
++                memset(f, 0, 32);
++              len = strn_len(&user_buffer[i], sizeof(f) - 1);
++                if (len < 0) { return len; }
++              copy_from_user(f, &user_buffer[i], len);
++              i += len;
++                pg_thread = pg_find_thread(f);
++                if (pg_thread) {
++                        printk("pktgen INFO: stopping thread: %s\n", pg_thread->name);
++                        stop_pktgen_kthread(pg_thread);
++                }
++                return count;
++      }
++
++        if (!strcmp(name, "start")) {
++                char f[32];
++                memset(f, 0, 32);
++              len = strn_len(&user_buffer[i], sizeof(f) - 1);
++                if (len < 0) { return len; }
++              copy_from_user(f, &user_buffer[i], len);
++              i += len;
++                pg_add_thread_info(f);
++                return count;
++      }
++
++      return -EINVAL;
++}/* proc_pg_ctrl_write */
++
++
++static int proc_pg_thread_write(struct file *file, const char *user_buffer,
++                                unsigned long count, void *data)
++{
++      int i = 0, max, len;
++      char name[16];
++        struct pktgen_thread_info* pg_thread = (struct pktgen_thread_info*)(data);
++        char* pg_result = &(pg_thread->result[0]);
++        unsigned long value = 0;
++        
++      if (count < 1) {
++              sprintf(pg_result, "Wrong command format");
++              return -EINVAL;
++      }
++  
++      max = count - i;
++        len = count_trail_chars(&user_buffer[i], max);
++        if (len < 0) { return len; }
++      i += len;
++  
++      /* Read variable name */
++
++      len = strn_len(&user_buffer[i], sizeof(name) - 1);
++        if (len < 0) { return len; }
++      memset(name, 0, sizeof(name));
++      copy_from_user(name, &user_buffer[i], len);
++      i += len;
++  
++      max = count -i;
++      len = count_trail_chars(&user_buffer[i], max);
++        if (len < 0) { return len; }
++      i += len;
++
++      if (debug) {
++              printk("pg_thread: %s,%lu\n", name, count);
++        }
++        
++        if (!strcmp(name, "add_interface")) {
++                char f[32];
++                memset(f, 0, 32);
++              len = strn_len(&user_buffer[i], sizeof(f) - 1);
++                if (len < 0) { return len; }
++              copy_from_user(f, &user_buffer[i], len);
++              i += len;
++                pg_add_interface_info(pg_thread, f);
++                return count;
++      }
++
++        if (!strcmp(name, "rem_interface")) {
++                struct pktgen_interface_info* info = NULL;
++                char f[32];
++                memset(f, 0, 32);
++                len = strn_len(&user_buffer[i], sizeof(f) - 1);
++                if (len < 0) { return len; }
++                copy_from_user(f, &user_buffer[i], len);
++                i += len;
++                info = pg_find_interface(pg_thread, f);
++                if (info) {
++                        pg_rem_interface_info(pg_thread, info);
++                        return count;
++                }
++                else {
++                        printk("ERROR:  That interface is not found.\n");
++                        return -ENODEV;
++                }
++      }
++
++        if (!strcmp(name, "max_before_softirq")) {
++                len = num_arg(&user_buffer[i], 10, &value);
++                pg_thread->max_before_softirq = value;
++                return count;
++      }
++
++
++      return -EINVAL;
++}/* proc_pg_thread_write */
+ int create_proc_dir(void)
+@@ -1282,109 +2656,348 @@
+         /*  does proc_dir already exists */
+         len = strlen(PG_PROC_DIR);
+-        for (proc_dir = proc_net->subdir; proc_dir;
+-             proc_dir=proc_dir->next) {
+-                if ((proc_dir->namelen == len) &&
+-                    (! memcmp(proc_dir->name, PG_PROC_DIR, len)))
++        for (pg_proc_dir = proc_net->subdir; pg_proc_dir; pg_proc_dir=pg_proc_dir->next) {
++                if ((pg_proc_dir->namelen == len) &&
++                    (! memcmp(pg_proc_dir->name, PG_PROC_DIR, len))) {
+                         break;
++                }
++        }
++        
++        if (!pg_proc_dir) {
++                pg_proc_dir = create_proc_entry(PG_PROC_DIR, S_IFDIR, proc_net);
+         }
+-        if (!proc_dir)
+-                proc_dir = create_proc_entry(PG_PROC_DIR, S_IFDIR, proc_net);
+-        if (!proc_dir) return -ENODEV;
+-        return 1;
++        
++        if (!pg_proc_dir) {
++                return -ENODEV;
++        }
++        
++        return 0;
+ }
+ int remove_proc_dir(void)
+ {
+         remove_proc_entry(PG_PROC_DIR, proc_net);
+-        return 1;
++        return 0;
+ }
+-static int __init init(void)
+-{
++static struct pktgen_interface_info* pg_find_interface(struct pktgen_thread_info* pg_thread,
++                                                       const char* ifname) {
++        struct pktgen_interface_info* rv = NULL;
++        pg_lock(pg_thread, __FUNCTION__);
++
++        if (pg_thread->cur_if && (strcmp(pg_thread->cur_if->ifname, ifname) == 0)) {
++                rv = pg_thread->cur_if;
++                goto found;
++        }
++        
++        rv = pg_thread->running_if_infos;
++        while (rv) {
++                if (strcmp(rv->ifname, ifname) == 0) {
++                        goto found;
++                }
++                rv = rv->next;
++        }
++
++        rv = pg_thread->stopped_if_infos;
++        while (rv) {
++                if (strcmp(rv->ifname, ifname) == 0) {
++                        goto found;
++                }
++                rv = rv->next;
++        }
++ found:
++        pg_unlock(pg_thread, __FUNCTION__);
++        return rv;
++}/* pg_find_interface */
++
++
++static int pg_add_interface_info(struct pktgen_thread_info* pg_thread, const char* ifname) {
++        struct pktgen_interface_info* i = pg_find_interface(pg_thread, ifname);
++        if (!i) {
++                i = kmalloc(sizeof(struct pktgen_interface_info), GFP_KERNEL);
++                if (!i) {
++                        return -ENOMEM;
++                }
++                memset(i, 0, sizeof(struct pktgen_interface_info));
++                
++                i->min_pkt_size = ETH_ZLEN;
++                i->max_pkt_size = ETH_ZLEN;
++                i->nfrags = 0;
++                i->multiskb = pg_multiskb_d;
++                i->peer_multiskb = 0;
++                i->ipg = pg_ipg_d;
++                i->count = pg_count_d;
++                i->sofar = 0;
++                i->hh[12] = 0x08; /* fill in protocol.  Rest is filled in later. */
++                i->hh[13] = 0x00;
++                i->udp_src_min = 9; /* sink NULL */
++                i->udp_src_max = 9;
++                i->udp_dst_min = 9;
++                i->udp_dst_max = 9;
++                i->rcv = pktgen_receive;
++
++                strncpy(i->ifname, ifname, 31);
++                sprintf(i->fname, "net/%s/%s", PG_PROC_DIR, ifname);
++
++                if (! pg_setup_interface(i)) {
++                        printk("ERROR: pg_setup_interface failed.\n");
++                        kfree(i);
++                        return -ENODEV;
++                }
++
++                i->proc_ent = create_proc_entry(i->fname, 0600, 0);
++                if (!i->proc_ent) {
++                        printk("pktgen: Error: cannot create %s procfs entry.\n", i->fname);
++                        kfree(i);
++                        return -EINVAL;
++                }
++                i->proc_ent->read_proc = proc_pg_if_read;
++                i->proc_ent->write_proc = proc_pg_if_write;
++                i->proc_ent->data = (void*)(i);
++
++                return add_interface_to_thread(pg_thread, i);
++        }
++        else {
++                printk("ERROR: interface already exists.\n");
++                return -EBUSY;
++        }
++}/* pg_add_interface_info */
++
++
++/* return the first !in_use thread structure */
++static struct pktgen_thread_info* pg_gc_thread_list_helper(void) {
++        struct pktgen_thread_info* rv = NULL;
++        
++        pg_lock_thread_list(__FUNCTION__);
++
++        rv = pktgen_threads;
++        while (rv) {
++                if (!rv->in_use) {
++                        break;
++                }
++                rv = rv->next;
++        }
++        pg_unlock_thread_list(__FUNCTION__);
++        return rv;
++}/* pg_find_thread */
++
++static void pg_gc_thread_list(void) {
++        struct pktgen_thread_info* t = NULL;
++        struct pktgen_thread_info* w = NULL;
++
++        while ((t = pg_gc_thread_list_helper())) {
++                pg_lock_thread_list(__FUNCTION__);
++                if (pktgen_threads == t) {
++                        pktgen_threads = t->next;
++                        kfree(t);
++                }
++                else {
++                        w = pktgen_threads;
++                        while (w) {
++                                if (w->next == t) {
++                                        w->next = t->next;
++                                        t->next = NULL;
++                                        kfree(t);
++                                        break;
++                                }
++                                w = w->next;
++                        }
++                }
++                pg_unlock_thread_list(__FUNCTION__);
++        }
++}/* pg_gc_thread_list */        
++
++
++static struct pktgen_thread_info* pg_find_thread(const char* name) {
++        struct pktgen_thread_info* rv = NULL;
++
++        pg_gc_thread_list();
++        
++        pg_lock_thread_list(__FUNCTION__);
++
++        rv = pktgen_threads;
++        while (rv) {
++                if (strcmp(rv->name, name) == 0) {
++                        break;
++                }
++                rv = rv->next;
++        }
++        pg_unlock_thread_list(__FUNCTION__);
++        return rv;
++}/* pg_find_thread */
++
++
++static int pg_add_thread_info(const char* name) {
++        struct pktgen_thread_info* pg_thread = NULL;
++
++        if (strlen(name) > 31) {
++                printk("pktgen ERROR:  Thread name cannot be more than 31 characters.\n");
++                return -EINVAL;
++        }
++        
++        if (pg_find_thread(name)) {
++                printk("pktgen ERROR: Thread: %s already exists\n", name);
++                return -EINVAL;
++        }
++
++        pg_thread = (struct pktgen_thread_info*)(kmalloc(sizeof(struct pktgen_thread_info), GFP_KERNEL));
++        if (!pg_thread) {
++                printk("pktgen: ERROR: out of memory, can't create new thread.\n");
++                return -ENOMEM;
++        }
++
++        memset(pg_thread, 0, sizeof(struct pktgen_thread_info));
++        strcpy(pg_thread->name, name);
++        spin_lock_init(&(pg_thread->pg_threadlock));
++        pg_thread->in_use = 1;
++        pg_thread->max_before_softirq = 100;
++        
++        sprintf(pg_thread->fname, "net/%s/%s", PG_PROC_DIR, pg_thread->name);
++        pg_thread->proc_ent = create_proc_entry(pg_thread->fname, 0600, 0);
++        if (!pg_thread->proc_ent) {
++                printk("pktgen: Error: cannot create %s procfs entry.\n", pg_thread->fname);
++                kfree(pg_thread);
++                return -EINVAL;
++        }
++        pg_thread->proc_ent->read_proc = proc_pg_thread_read;
++        pg_thread->proc_ent->write_proc = proc_pg_thread_write;
++        pg_thread->proc_ent->data = (void*)(pg_thread);
++
++        pg_thread->next = pktgen_threads;
++        pktgen_threads = pg_thread;
++
++        /* Start the thread running */
++        start_pktgen_kthread(pg_thread);
++        
++        return 0;
++}/* pg_add_thread_info */
++
++
++/* interface_info must be stopped and on the pg_thread stopped list
++ */
++static int pg_rem_interface_info(struct pktgen_thread_info* pg_thread,
++                                 struct pktgen_interface_info* info) {
++        if (info->do_run_run) {
++                printk("WARNING: trying to remove a running interface, stopping it now.\n");
++                pg_stop_interface(pg_thread, info);
++        }
++        
++        /* Diss-associate from the interface */
++        check_remove_device(info);
++        
++        /* Clean up proc file system */
++        if (strlen(info->fname)) {
++                remove_proc_entry(info->fname, NULL);
++        }
++        
++        pg_lock(pg_thread, __FUNCTION__);
++        {
++                /* Remove from the stopped list */
++                struct pktgen_interface_info* p = pg_thread->stopped_if_infos;
++                if (p == info) {
++                        pg_thread->stopped_if_infos = p->next;
++                        p->next = NULL;
++                }
++                else {
++                        while (p) {
++                                if (p->next == info) {
++                                        p->next = p->next->next;
++                                        info->next = NULL;
++                                        break;
++                                }
++                                p = p->next;
++                        }
++                }
++                
++                info->pg_thread = NULL;
++        }
++        pg_unlock(pg_thread, __FUNCTION__);
++        
++        return 0;
++}/* pg_rem_interface_info */
++
++
++static int __init pg_init(void) {
+         int i;
+       printk(version);
++
++        /* Initialize our global variables */
++        for (i = 0; i<PG_INFO_HASH_MAX; i++) {
++                pg_info_hash[i] = NULL;
++        }
++        module_fname[0] = 0;
++        
++        if (handle_pktgen_hook) {
++                printk("pktgen: ERROR: pktgen is already loaded it seems..\n");
++                /* Already loaded */
++                return -EEXIST;
++        }
++
+       cycles_calibrate();
+-      if (cpu_speed == 0) {
++      if (pg_cycles_per_us == 0) {
+               printk("pktgen: Error: your machine does not have working cycle counter.\n");
+               return -EINVAL;
+       }
+       create_proc_dir();
+-        for (i = 0; i<MAX_PKTGEN; i++) {
+-                memset(&(pginfos[i]), 0, sizeof(pginfos[i]));
+-                pginfos[i].pkt_size = ETH_ZLEN;
+-                pginfos[i].nfrags = 0;
+-                pginfos[i].clone_skb = clone_skb_d;
+-                pginfos[i].ipg = ipg_d;
+-                pginfos[i].count = count_d;
+-                pginfos[i].sofar = 0;
+-                pginfos[i].hh[12] = 0x08; /* fill in protocol.  Rest is filled in later. */
+-                pginfos[i].hh[13] = 0x00;
+-                pginfos[i].udp_src_min = 9; /* sink NULL */
+-                pginfos[i].udp_src_max = 9;
+-                pginfos[i].udp_dst_min = 9;
+-                pginfos[i].udp_dst_max = 9;
+-                
+-                sprintf(pginfos[i].fname, "net/%s/pg%i", PG_PROC_DIR, i);
+-                pginfos[i].proc_ent = create_proc_entry(pginfos[i].fname, 0600, 0);
+-                if (!pginfos[i].proc_ent) {
+-                        printk("pktgen: Error: cannot create net/%s/pg procfs entry.\n", PG_PROC_DIR);
+-                        goto cleanup_mem;
+-                }
+-                pginfos[i].proc_ent->read_proc = proc_read;
+-                pginfos[i].proc_ent->write_proc = proc_write;
+-                pginfos[i].proc_ent->data = (void*)(long)(i);
+-
+-                sprintf(pginfos[i].busy_fname, "net/%s/pg_busy%i",  PG_PROC_DIR, i);
+-                pginfos[i].busy_proc_ent = create_proc_entry(pginfos[i].busy_fname, 0, 0);
+-                if (!pginfos[i].busy_proc_ent) {
+-                        printk("pktgen: Error: cannot create net/%s/pg_busy procfs entry.\n", PG_PROC_DIR);
+-                        goto cleanup_mem;
+-                }
+-                pginfos[i].busy_proc_ent->read_proc = proc_busy_read;
+-                pginfos[i].busy_proc_ent->data = (void*)(long)(i);
++        sprintf(module_fname, "net/%s/pgctrl", PG_PROC_DIR);
++        module_proc_ent = create_proc_entry(module_fname, 0600, 0);
++        if (!module_proc_ent) {
++                printk("pktgen: Error: cannot create %s procfs entry.\n", module_fname);
++                return -EINVAL;
+         }
+-        return 0;
+-        
+-cleanup_mem:
+-        for (i = 0; i<MAX_PKTGEN; i++) {
+-                if (strlen(pginfos[i].fname)) {
+-                        remove_proc_entry(pginfos[i].fname, NULL);
+-                }
+-                if (strlen(pginfos[i].busy_fname)) {
+-                        remove_proc_entry(pginfos[i].busy_fname, NULL);
+-                }
++        module_proc_ent->read_proc = proc_pg_ctrl_read;
++        module_proc_ent->write_proc = proc_pg_ctrl_write;
++        module_proc_ent->proc_fops = &(pktgen_fops); /* IOCTL hook */
++        module_proc_ent->data = NULL;
++
++      /* Register us to receive netdevice events */
++      register_netdevice_notifier(&pktgen_notifier_block);
++        
++        /* Register handler */
++        handle_pktgen_hook = pktgen_receive;
++
++        for (i = 0; i<pg_thread_count; i++) {
++                char buf[30];
++                sprintf(buf, "kpktgend_%i", i);
++                pg_add_thread_info(buf);
+         }
+-      return -ENOMEM;
+-}
++                
++        
++        return 0;        
++}/* pg_init */
+-static void __exit cleanup(void)
++static void __exit pg_cleanup(void)
+ {
+-        int i;
+-        for (i = 0; i<MAX_PKTGEN; i++) {
+-                if (strlen(pginfos[i].fname)) {
+-                        remove_proc_entry(pginfos[i].fname, NULL);
+-                }
+-                if (strlen(pginfos[i].busy_fname)) {
+-                        remove_proc_entry(pginfos[i].busy_fname, NULL);
+-                }
++        /* Un-register handler */
++        handle_pktgen_hook = NULL;
++
++        /* Stop all interfaces & threads */        
++        while (pktgen_threads) {
++                stop_pktgen_kthread(pktgen_threads);
+         }
++        
++        /* Un-register us from receiving netdevice events */
++      unregister_netdevice_notifier(&pktgen_notifier_block);
++
++        /* Clean up proc file system */
++        remove_proc_entry(module_fname, NULL);
++        
+       remove_proc_dir();
++
+ }
+-module_init(init);
+-module_exit(cleanup);
+-MODULE_AUTHOR("Robert Olsson <robert.olsson@its.uu.se");
++module_init(pg_init);
++module_exit(pg_cleanup);
++
++MODULE_AUTHOR("Robert Olsson <robert.olsson@its.uu.se, Ben Greear<greearb@candelatech.com>");
+ MODULE_DESCRIPTION("Packet Generator tool");
+ MODULE_LICENSE("GPL");
+-MODULE_PARM(count_d, "i");
+-MODULE_PARM(ipg_d, "i");
+-MODULE_PARM(cpu_speed, "i");
+-MODULE_PARM(clone_skb_d, "i");
+-
+-
+-
++MODULE_PARM(pg_count_d, "i");
++MODULE_PARM(pg_ipg_d, "i");
++MODULE_PARM(pg_thread_count, "i");
++MODULE_PARM(pg_multiskb_d, "i");
++MODULE_PARM(debug, "i");
+--- linux-2.4.21/net/core/pktgen.h     1969-12-31 16:00:00.000000000 -0800
++++ linux-2.4.21.amds/net/core/pktgen.h        2003-07-30 16:20:41.000000000 -0700
+@@ -0,0 +1,241 @@
++/* -*-linux-c-*-
++ * $Id: candela_2.4.21.patch,v 1.4 2003/09/30 21:05:04 greear Exp $
++ * pktgen.c: Packet Generator for performance evaluation.
++ *
++ * See pktgen.c for details of changes, etc.
++*/
++
++
++#ifndef PKTGEN_H_INCLUDE_KERNEL__
++#define PKTGEN_H_INCLUDE_KERNEL__
++
++
++/* The buckets are exponential in 'width' */
++#define LAT_BUCKETS_MAX 32
++
++#define IP_NAME_SZ 32
++
++/* Keep information per interface */
++struct pktgen_interface_info {
++        char ifname[32];
++        
++        /* Parameters */
++
++        /* If min != max, then we will either do a linear iteration, or
++         * we will do a random selection from within the range.
++         */
++        __u32 flags;     
++
++#define F_IPSRC_RND   (1<<0)  /* IP-Src Random  */
++#define F_IPDST_RND   (1<<1)  /* IP-Dst Random  */
++#define F_UDPSRC_RND  (1<<2)  /* UDP-Src Random */
++#define F_UDPDST_RND  (1<<3)  /* UDP-Dst Random */
++#define F_MACSRC_RND  (1<<4)  /* MAC-Src Random */
++#define F_MACDST_RND  (1<<5)  /* MAC-Dst Random */
++#define F_SET_SRCMAC  (1<<6)  /* Specify-Src-Mac 
++                               (default is to use Interface's MAC Addr) */
++#define F_SET_SRCIP   (1<<7)  /*  Specify-Src-IP
++                                (default is to use Interface's IP Addr) */ 
++#define F_TXSIZE_RND  (1<<8)  /* Transmit size is random */
++
++        int min_pkt_size;    /* = ETH_ZLEN; */
++        int max_pkt_size;    /* = ETH_ZLEN; */
++        int nfrags;
++        __u32 ipg;    /* Default Interpacket gap in nsec */
++        __u64 count;  /* Default No packets to send */
++        __u64 sofar;  /* How many pkts we've sent so far */
++        __u64 tx_bytes; /* How many bytes we've transmitted */
++        __u64 errors;    /* Errors when trying to transmit, pkts will be re-sent */
++
++        /* runtime counters relating to multiskb */
++        __u64 next_tx_ns;          /* timestamp of when to tx next, in nano-seconds */
++        
++        __u64 fp;
++        __u32 fp_tmp;
++      int last_ok;           /* Was last skb sent? 
++                              * Or a failed transmit of some sort?  This will keep
++                                * sequence numbers in order, for example.
++                                */
++        /* Fields relating to receiving pkts */
++        __u32 last_seq_rcvd;
++        __u64 ooo_rcvd;  /* out-of-order packets received */
++        __u64 pkts_rcvd; /* packets received */
++        __u64 dup_rcvd;  /* duplicate packets received */
++        __u64 bytes_rcvd; /* total bytes received, as obtained from the skb */
++        __u64 seq_gap_rcvd; /* how many gaps we received.  This coorelates to
++                             * dropped pkts, except perhaps in cases where we also
++                             * have re-ordered pkts.  In that case, you have to tie-break
++                             * by looking at send v/s received pkt totals for the interfaces
++                             * involved.
++                             */
++        __u64 non_pg_pkts_rcvd; /* Count how many non-pktgen skb's we are sent to check. */
++        __u64 dup_since_incr; /* How many dumplicates since the last seq number increment,
++                               * used to detect gaps when multiskb > 1
++                               */
++        int avg_latency; /* in micro-seconds */
++        int min_latency;
++        int max_latency;
++        __u64 latency_bkts[LAT_BUCKETS_MAX];
++        __u64 pkts_rcvd_since_clear; /* with regard to clearing/resetting the latency logic */
++        
++        __u64 started_at; /* micro-seconds */
++        __u64 stopped_at; /* micro-seconds */
++        __u64 idle_acc;
++        __u32 seq_num;
++        
++        int multiskb; /* Use multiple SKBs during packet gen.  If this number
++                          * is greater than 1, then that many coppies of the same
++                          * packet will be sent before a new packet is allocated.
++                          * For instance, if you want to send 1024 identical packets
++                          * before creating a new packet, set multiskb to 1024.
++                          */
++        int peer_multiskb; /* Helps detect drops when multiskb > 1 on peer */
++        int do_run_run;  /* if this changes to false, the test will stop */
++        
++        char dst_min[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
++        char dst_max[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
++        char src_min[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
++        char src_max[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
++
++        /* If we're doing ranges, random or incremental, then this
++         * defines the min/max for those ranges.
++         */
++        __u32 saddr_min; /* inclusive, source IP address */
++        __u32 saddr_max; /* exclusive, source IP address */
++        __u32 daddr_min; /* inclusive, dest IP address */
++        __u32 daddr_max; /* exclusive, dest IP address */
++
++        __u16 udp_src_min; /* inclusive, source UDP port */
++        __u16 udp_src_max; /* exclusive, source UDP port */
++        __u16 udp_dst_min; /* inclusive, dest UDP port */
++        __u16 udp_dst_max; /* exclusive, dest UDP port */
++
++        __u32 src_mac_count; /* How many MACs to iterate through */
++        __u32 dst_mac_count; /* How many MACs to iterate through */
++        
++        unsigned char dst_mac[6];
++        unsigned char src_mac[6];
++        
++        __u32 cur_dst_mac_offset;
++        __u32 cur_src_mac_offset;
++        __u32 cur_saddr;
++        __u32 cur_daddr;
++        __u16 cur_udp_dst;
++        __u16 cur_udp_src;
++        __u32 cur_pkt_size;
++        
++        __u8 hh[14];
++        /* = { 
++           0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB, 
++           
++           We fill in SRC address later
++           0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++           0x08, 0x00
++           };
++        */
++        __u16 pad; /* pad out the hh struct to an even 16 bytes */
++        char result[512];
++        /* proc file names */
++        char fname[80];
++
++        /* End of stuff that user-space should care about */
++
++        struct sk_buff* skb; /* skb we are to transmit next, mainly used for when we
++                              * are transmitting the same one multiple times
++                              */
++        struct pktgen_thread_info* pg_thread; /* the owner */
++
++        struct pktgen_interface_info* next_hash; /* Used for chaining in the hash buckets */
++        struct pktgen_interface_info* next; /* Used for chaining in the thread's run-queue */
++
++        
++
++        struct net_device* odev; /* The out-going device.  Note that the device should
++                                  * have it's pg_info pointer pointing back to this
++                                  * device.  This will be set when the user specifies
++                                  * the out-going device name (not when the inject is
++                                  * started as it used to do.)
++                                  */
++        
++        struct proc_dir_entry *proc_ent;
++        
++        int (*rcv) (struct sk_buff *skb);
++}; /* pktgen_interface_info */
++
++
++struct pktgen_hdr {
++        __u32 pgh_magic;
++        __u32 seq_num;
++        struct timeval timestamp;
++};
++
++
++/* Define some IOCTLs.  Just picking random numbers, basically. */
++#define GET_PKTGEN_INTERFACE_INFO 0x7450
++
++struct pktgen_ioctl_info {
++        char thread_name[32];
++        char interface_name[32];
++        struct pktgen_interface_info info;
++};
++
++
++struct pktgen_thread_info {
++        struct pktgen_interface_info* running_if_infos; /* list of running interfaces, current will
++                                                         * not be in this list.
++                                                         */
++        struct pktgen_interface_info* stopped_if_infos; /* list of stopped interfaces. */
++        struct pktgen_interface_info* cur_if;           /* Current (running) interface we are servicing in
++                                                         * the main thread loop.
++                                                         */
++
++        int running_if_sz;
++        struct pktgen_thread_info* next;
++        char name[32];
++        char fname[128]; /* name of proc file */
++        struct proc_dir_entry *proc_ent;
++        char result[512];
++        u32 max_before_softirq; /* We'll call do_softirq to prevent starvation. */
++        
++        spinlock_t pg_threadlock;
++        
++        /* Linux task structure of thread */
++        struct task_struct *thread;
++        
++        /* Task queue need to launch thread */
++        struct tq_struct tq;
++        
++        /* function to be started as thread */
++        void (*function) (struct pktgen_thread_info *kthread);
++        
++        /* semaphore needed on start and creation of thread. */
++        struct semaphore startstop_sem;
++
++        /* public data */
++
++        /* queue thread is waiting on. Gets initialized by
++           init_kthread, can be used by thread itself.
++        */
++        wait_queue_head_t queue;
++        
++        /* flag to tell thread whether to die or not.
++           When the thread receives a signal, it must check
++           the value of terminate and call exit_kthread and terminate
++           if set.
++        */
++        int terminate;
++
++        int in_use; /* if 0, then we can delete or re-use this struct */
++        
++        /* additional data to pass to kernel thread */
++        void *arg;
++};/* struct pktgen_thread_info */
++
++/* Defined in dev.c */
++extern int (*handle_pktgen_hook)(struct sk_buff *skb);
++
++/* Returns < 0 if the skb is not a pktgen buffer. */
++int pktgen_receive(struct sk_buff* skb);
++
++
++#endif
+--- linux-2.4.21/net/netsyms.c 2003-06-13 07:51:39.000000000 -0700
++++ linux-2.4.21.amds/net/netsyms.c    2003-07-30 16:20:41.000000000 -0700
+@@ -30,6 +30,7 @@
+ #include <net/pkt_sched.h>
+ #include <net/scm.h>
+ #include <linux/if_bridge.h>
++#include <linux/if_macvlan.h>
+ #include <linux/if_vlan.h>
+ #include <linux/random.h>
+ #ifdef CONFIG_NET_DIVERT
+@@ -90,6 +91,14 @@
+ extern int sysctl_max_syn_backlog;
+ #endif
++#ifdef CONFIG_NET_PKTGEN_MODULE
++#warning "EXPORT_SYMBOL(handle_pktgen_hook);";
++extern int (*handle_pktgen_hook)(struct sk_buff *skb);
++/* Would be OK to export as EXPORT_SYMBOL_GPL, but can't get that to work for
++ * some reason. --Ben */
++EXPORT_SYMBOL(handle_pktgen_hook);
++#endif
++
+ /* Skbuff symbols. */
+ EXPORT_SYMBOL(skb_over_panic);
+ EXPORT_SYMBOL(skb_under_panic);
+@@ -234,6 +243,13 @@
+ #endif
+ #endif
++#if defined(CONFIG_MACVLAN) || defined(CONFIG_MACVLAN_MODULE)
++EXPORT_SYMBOL(macvlan_handle_frame_hook);
++#ifdef CONFIG_INET
++EXPORT_SYMBOL(macvlan_ioctl_hook);
++#endif
++#endif
++
+ #ifdef CONFIG_NET_DIVERT
+ EXPORT_SYMBOL(alloc_divert_blk);
+ EXPORT_SYMBOL(free_divert_blk);
+--- linux-2.4.21/Documentation/networking/pktgen.txt   2003-06-13 07:51:29.000000000 -0700
++++ linux-2.4.21.amds/Documentation/networking/pktgen.txt      2003-07-30 16:20:41.000000000 -0700
+@@ -1,76 +1,118 @@
+ How to use the Linux packet generator module.
+-1. Enable CONFIG_NET_PKTGEN to compile and build pktgen.o, install it
+-   in the place where insmod may find it.
+-2. Cut script "ipg" (see below).
+-3. Edit script to set preferred device and destination IP address.
+-3a.  Create more scripts for different interfaces.  Up to thirty-two
+-     pktgen processes can be configured and run at once by using the
+-     32 /proc/net/pktgen/pg* files.
+-4. Run in shell: ". ipg"
+-5. After this two commands are defined:
+-   A. "pg" to start generator and to get results.
+-   B. "pgset" to change generator parameters. F.e.
+-      pgset "multiskb 1"      use multiple SKBs for packet generation
+-      pgset "multiskb 0"      use single SKB for all transmits
+-      pgset "pkt_size 9014"   sets packet size to 9014
+-      pgset "frags 5"         packet will consist of 5 fragments
+-      pgset "count 200000"    sets number of packets to send, set to zero
+-                              for continious sends untill explicitly
+-                              stopped.
+-      pgset "ipg 5000"        sets artificial gap inserted between packets
+-                              to 5000 nanoseconds
+-      pgset "dst 10.0.0.1"    sets IP destination address
+-                              (BEWARE! This generator is very aggressive!)
+-      pgset "dst_min 10.0.0.1"            Same as dst
+-      pgset "dst_max 10.0.0.254"          Set the maximum destination IP.
+-      pgset "src_min 10.0.0.1"            Set the minimum (or only) source IP.
+-      pgset "src_max 10.0.0.254"          Set the maximum source IP.
+-      pgset "dstmac 00:00:00:00:00:00"    sets MAC destination address
+-      pgset "srcmac 00:00:00:00:00:00"    sets MAC source address
+-      pgset "src_mac_count 1" Sets the number of MACs we'll range through.  The
+-                              'minimum' MAC is what you set with srcmac.
+-      pgset "dst_mac_count 1" Sets the number of MACs we'll range through.  The
+-                              'minimum' MAC is what you set with dstmac.
+-      pgset "flag [name]"     Set a flag to determine behaviour.  Current flags
+-                              are: IPSRC_RND #IP Source is random (between min/max),
+-                                   IPDST_RND, UDPSRC_RND,
+-                                   UDPDST_RND, MACSRC_RND, MACDST_RND 
+-      pgset "udp_src_min 9"   set UDP source port min, If < udp_src_max, then
+-                              cycle through the port range.
+-      pgset "udp_src_max 9"   set UDP source port max.
+-      pgset "udp_dst_min 9"   set UDP destination port min, If < udp_dst_max, then
+-                              cycle through the port range.
+-      pgset "udp_dst_max 9"   set UDP destination port max.
+-      pgset stop            aborts injection
++1.  Enable CONFIG_NET_PKTGEN to compile and build pktgen.o, install it
++     in the place where insmod may find it.
++2.  Add an interface to the kpktgend_0 thread:
++     echo "add_interface eth1" > /proc/net/pktgen/kpktgend_0
++2a. Add more interfaces as needed.
++3.  Configure interfaces by setting values as defined below.  The
++     general strategy is: echo "command" > /proc/net/pktgen/[device]
++     For example: echo "multiskb 100" > /proc/net/pktgen/eth1
++
++      "multiskb 100"    Will send 100 identical pkts before creating
++                              new packet with new timestamp, etc.
++      "multiskb 0"      Will create new skb for all transmits.
++      "peer_multiskb 100"   Helps us determine dropped & dup pkts, sender's multiskb.
++      "min_pkt_size 60"     sets packet minimum size to 60 (64 counting CRC)
++      "max_pkt_size 1514"   sets packet size to 1514 (1518 counting CRC)
++      "frags 5"         packet will consist of 5 fragments
++      "count 200000"    sets number of packets to send, set to zero
++                         for continious sends untill explicitly
++                         stopped.
++      "ipg 5000"        sets artificial gap inserted between packets
++                         to 5000 nanoseconds
++      "dst 10.0.0.1"    sets IP destination address
++                         (BEWARE! This generator is very aggressive!)
++      "dst_min 10.0.0.1"            Same as dst
++      "dst_max 10.0.0.254"          Set the maximum destination IP.
++      "src_min 10.0.0.1"            Set the minimum (or only) source IP.
++      "src_max 10.0.0.254"          Set the maximum source IP.
++      "dst_mac 00:00:00:00:00:00"   sets MAC destination address
++      "src_mac 00:00:00:00:00:00"   sets MAC source address
++      "src_mac_count 1" Sets the number of MACs we'll range through.  The
++                         'minimum' MAC is what you set with srcmac.
++      "dst_mac_count 1" Sets the number of MACs we'll range through.  The
++                         'minimum' MAC is what you set with dstmac.
++      "flag [name]"     Set a flag to determine behaviour.  Prepend '!' to the
++                         flag to turn it off.  Current flags are:
++                          IPSRC_RND #IP Source is random (between min/max),
++                          IPDST_RND, UDPSRC_RND, TXSIZE_RND
++                          UDPDST_RND, MACSRC_RND, MACDST_RND 
++      "udp_src_min 9"   set UDP source port min, If < udp_src_max, then
++                         cycle through the port range.
++      "udp_src_max 9"   set UDP source port max.
++      "udp_dst_min 9"   set UDP destination port min, If < udp_dst_max, then
++                         cycle through the port range.
++      "udp_dst_max 9"   set UDP destination port max.
++      "stop"            Stops this interface from transmitting.  It will still
++                         receive packets and record their latency, etc.
++      "start"           Starts the interface transmitting packets.
++      "clear_counters"  Clear the packet and latency counters.
++
++You can start and stop threads by echoing commands to the /proc/net/pktgen/pgctrl
++file.  Supported commands are:
++      "stop kpktgend_0"  Stop thread 0.
++      "start threadXX"   Start (create) thread XX.  You may wish to create one thread
++                          per CPU.
+       
+-  Also, ^C aborts generator.
+----- cut here
++You can control manage the interfaces on a thread by echoing commands to
++the /proc/net/pktgen/[thread] file.  Supported commands are:
++      "add_interface eth1"  Add interface eth1 to the chosen thread.
++      "rem_interface eth1"  Remove interface eth1 from the chosen thread.
++      "max_before_softirq"  Maximum loops before we cause a call to do_softirq,
++                             this is to help mitigate starvatation on the RX side.
++
++
++You can examine various counters and parameters by reading the appropriate
++proc file:
++
++[root@localhost lanforge]# cat /proc/net/pktgen/kpktgend_0 
++VERSION-1
++Name: kpktgend_0
++Current: eth2
++Running: eth6 
++Stopped: eth1 eth5 
++Result: NA
++
++
++[root@localhost lanforge]# cat /proc/net/pktgen/eth2
++VERSION-1
++Params: count 0  pkt_size: 300  frags: 0  ipg: 0  multiskb: 0 ifname "eth2"
++     dst_min: 172.2.1.1  dst_max: 172.2.1.6  src_min: 172.1.1.4  src_max: 172.1.1.8
++     src_mac: 00:00:00:00:00:00  dst_mac: 00:00:00:00:00:00
++     udp_src_min: 99  udp_src_max: 1005  udp_dst_min: 9  udp_dst_max: 9
++     src_mac_count: 0  dst_mac_count: 0
++     Flags: IPSRC_RND  IPDST_RND  UDPSRC_RND  
++Current:
++     pkts-sofar: 158835950  errors: 0
++     started: 1026024703542360us  elapsed: 4756326418us
++     idle: 1723232054307ns  next_tx: 27997154666566(-3202934)ns
++     seq_num: 158835951  cur_dst_mac_offset: 0  cur_src_mac_offset: 0
++     cur_saddr: 0x60101ac  cur_daddr: 0x30102ac  cur_udp_dst: 9  cur_udp_src: 966
++     pkts_rcvd: 476002  bytes_rcvd: 159929440  last_seq_rcvd: 476002  ooo_rcvd: 0
++     dup_rcvd: 0  seq_gap_rcvd(dropped): 0  non_pg_rcvd: 0
++     avg_latency: 41us  min_latency: 40us  max_latency: 347us  pkts_in_sample: 476002
++      Buckets(us) [ 0  0  0  0  0  0  311968  164008  23  3  0  0  0  0  0  0  0  0  0  0  ]
++Result: OK: ipg=0
++
++[root@localhost lanforge]# cat /proc/net/pktgen/eth6
++VERSION-1
++Params: count 0  pkt_size: 300  frags: 0  ipg: 11062341  multiskb: 0 ifname "eth6"
++     dst_min: 90  dst_max: 90  src_min: 90  src_max: 90
++     src_mac: 00:00:00:00:00:00  dst_mac: 00:00:00:00:00:00
++     udp_src_min: 9  udp_src_max: 9  udp_dst_min: 9  udp_dst_max: 9
++     src_mac_count: 0  dst_mac_count: 0
++     Flags: 
++Current:
++     pkts-sofar: 479940  errors: 0
++     started: 1026024703542707us  elapsed: 4795667656us
++     idle: 109585100905ns  next_tx: 28042807786397(-79364)ns
++     seq_num: 479941  cur_dst_mac_offset: 0  cur_src_mac_offset: 0
++     cur_saddr: 0x0  cur_daddr: 0x0  cur_udp_dst: 9  cur_udp_src: 9
++     pkts_rcvd: 160323509  bytes_rcvd: 50392479910  last_seq_rcvd: 160323509  ooo_rcvd: 0
++     dup_rcvd: 0  seq_gap_rcvd(dropped): 0  non_pg_rcvd: 0
++     avg_latency: 230us  min_latency: 36us  max_latency: 1837us  pkts_in_sample: 160323509
++      Buckets(us) [ 0  0  0  0  0  0  287725  2618755  54130607  98979415  80358  4226649  0  0  0  0  0  0  0  0  ]
++Result: OK: ipg=11062341
+-#! /bin/sh
+-
+-modprobe pktgen
+-
+-PGDEV=/proc/net/pktgen/pg0
+-
+-function pgset() {
+-    local result
+-
+-    echo $1 > $PGDEV
+-
+-    result=`cat $PGDEV | fgrep "Result: OK:"`
+-    if [ "$result" = "" ]; then
+-         cat $PGDEV | fgrep Result:
+-    fi
+-}
+-
+-function pg() {
+-    echo inject > $PGDEV
+-    cat $PGDEV
+-}
+-
+-pgset "odev eth0"
+-pgset "dst 0.0.0.0"
+-
+----- cut here
+--- linux-2.4.21/include/linux/if_macvlan.h    1969-12-31 16:00:00.000000000 -0800
++++ linux-2.4.21.amds/include/linux/if_macvlan.h       2003-07-30 16:28:27.000000000 -0700
+@@ -0,0 +1,57 @@
++/* -*- linux-c -*- */
++#ifndef _LINUX_IF_MACVLAN_H
++#define _LINUX_IF_MACVLAN_H
++
++/* the ioctl commands */
++
++/* actions */
++#define MACVLAN_ENABLE         1
++#define MACVLAN_DISABLE        2
++#define MACVLAN_ADD            3
++#define MACVLAN_DEL            4
++#define MACVLAN_BIND           5
++#define MACVLAN_UNBIND         6
++
++/* informative */
++#define MACVLAN_GET_NUM_PORTS  7
++#define MACVLAN_GET_PORT_NAME  8
++#define MACVLAN_GET_NUM_VLANS  9
++#define MACVLAN_GET_VLAN_NAME  10
++#define MACVLAN_GET_NUM_MACS   11
++#define MACVLAN_GET_MAC_NAME   12
++
++#define MACVLAN_SET_PORT_FLAGS 13
++#define MACVLAN_GET_PORT_FLAGS 14
++
++/* If this IOCTL succeedes, we are a MAC-VLAN interface, otherwise, we are not. */
++#define MACVLAN_IS_MACVLAN     15
++
++
++#ifdef __KERNEL__
++#include <linux/if.h>
++#include <linux/netdevice.h>
++extern int (*macvlan_ioctl_hook)(unsigned long arg);
++
++/* Returns >= 0 if it consumed the packet, otherwise let the pkt
++ * be processed by the netif_rx method, as if macvlan's didn't
++ * exist.
++ */
++extern int (*macvlan_handle_frame_hook)(struct sk_buff *skb);
++#endif
++
++struct macvlan_ioctl_reply {
++        int num;
++        char name[IFNAMSIZ];
++};
++
++struct macvlan_ioctl {
++        int  cmd;
++        int portidx;
++        char *ifname;
++        int ifidx; /* flags when setting port flags */
++        unsigned char *macaddr;
++        int macaddridx;
++        struct macvlan_ioctl_reply *reply;
++};
++
++#endif
+--- linux-2.4.21/include/linux/sockios.h       2003-06-13 07:51:39.000000000 -0700
++++ linux-2.4.21.amds/include/linux/sockios.h  2003-07-30 16:20:41.000000000 -0700
+@@ -65,6 +65,8 @@
+ #define SIOCDIFADDR   0x8936          /* delete PA address            */
+ #define       SIOCSIFHWBROADCAST      0x8937  /* set hardware broadcast addr  */
+ #define SIOCGIFCOUNT  0x8938          /* get number of devices */
++#define SIOCGIFWEIGHT 0x8939          /* get weight of device, in stones */
++#define SIOCSIFWEIGHT 0x893a          /* set weight of device, in stones */
+ #define SIOCGIFBR     0x8940          /* Bridging support             */
+ #define SIOCSIFBR     0x8941          /* Set bridging options         */
+@@ -94,6 +96,10 @@
+ #define SIOCGRARP     0x8961          /* get RARP table entry         */
+ #define SIOCSRARP     0x8962          /* set RARP table entry         */
++/* MAC address based VLAN control calls */
++#define SIOCGIFMACVLAN        0x8965          /* Mac address multiplex/demultiplex support */
++#define SIOCSIFMACVLAN        0x8966          /* Set macvlan options  */
++
+ /* Driver configuration calls */
+ #define SIOCGIFMAP    0x8970          /* Get device parameters        */
+@@ -116,6 +122,15 @@
+ #define SIOCBONDINFOQUERY      0x8994 /* rtn info about bond state    */
+ #define SIOCBONDCHANGEACTIVE   0x8995   /* update to a new active slave */
+                       
++
++/* Ben's little hack land */
++#define SIOCSACCEPTLOCALADDRS  0x89a0   /*  Allow interfaces to accept pkts from
++                                         * local interfaces...use with SO_BINDTODEVICE
++                                         */
++#define SIOCGACCEPTLOCALADDRS  0x89a1   /*  Allow interfaces to accept pkts from
++                                         * local interfaces...use with SO_BINDTODEVICE
++                                         */
++
+ /* Device private ioctl calls */
+ /*
+--- linux-2.4.21/net/Config.in 2002-08-02 17:39:46.000000000 -0700
++++ linux-2.4.21.amds/net/Config.in    2003-07-30 16:20:41.000000000 -0700
+@@ -48,6 +48,7 @@
+             bool '    Per-VC IP filter kludge' CONFIG_ATM_BR2684_IPFILTER
+       fi
+    fi
++   tristate 'MAC address based VLANs (EXPERIMENTAL)' CONFIG_MACVLAN
+ fi
+ tristate '802.1Q VLAN Support' CONFIG_VLAN_8021Q
+--- linux-2.4.21/net/Makefile  2002-08-02 17:39:46.000000000 -0700
++++ linux-2.4.21.amds/net/Makefile     2003-07-30 16:20:41.000000000 -0700
+@@ -44,7 +44,8 @@
+ subdir-$(CONFIG_ATM)          += atm
+ subdir-$(CONFIG_DECNET)               += decnet
+ subdir-$(CONFIG_ECONET)               += econet
+-subdir-$(CONFIG_VLAN_8021Q)           += 8021q
++subdir-$(CONFIG_VLAN_8021Q) += 8021q
++subdir-$(CONFIG_MACVLAN)      += macvlan
+ obj-y := socket.o $(join $(subdir-y), $(patsubst %,/%.o,$(notdir $(subdir-y))))
+--- linux-2.4.21/net/ipv4/af_inet.c    2003-06-13 07:51:39.000000000 -0700
++++ linux-2.4.21.amds/net/ipv4/af_inet.c       2003-07-30 16:20:41.000000000 -0700
+@@ -143,6 +143,10 @@
+ int (*br_ioctl_hook)(unsigned long);
+ #endif
++#if defined(CONFIG_MACVLAN) || defined(CONFIG_MACVLAN_MODULE)
++int (*macvlan_ioctl_hook)(unsigned long) = NULL;
++#endif
++
+ #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+ int (*vlan_ioctl_hook)(unsigned long arg);
+ #endif
+@@ -879,6 +883,18 @@
+ #endif
+                       return -ENOPKG;
++              case SIOCGIFMACVLAN:
++              case SIOCSIFMACVLAN:
++#if defined(CONFIG_MACVLAN) || defined(CONFIG_MACVLAN_MODULE)
++#ifdef CONFIG_KMOD
++                      if (macvlan_ioctl_hook == NULL)
++                              request_module("macvlan");
++#endif
++                      if (macvlan_ioctl_hook != NULL)
++                              return macvlan_ioctl_hook(arg);
++#endif
++                      return -ENOPKG;
++
+               case SIOCGIFVLAN:
+               case SIOCSIFVLAN:
+ #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+--- linux-2.4.21/net/macvlan/Makefile  1969-12-31 16:00:00.000000000 -0800
++++ linux-2.4.21.amds/net/macvlan/Makefile     2003-07-30 16:20:41.000000000 -0700
+@@ -0,0 +1,11 @@
++#
++# 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      := mac-mux.o
++obj-$(CONFIG_MACVLAN) := macvlan.o
++
++include $(TOPDIR)/Rules.make
+--- linux-2.4.21/net/macvlan/macvlan.c 1969-12-31 16:00:00.000000000 -0800
++++ linux-2.4.21.amds/net/macvlan/macvlan.c    2003-08-13 16:26:11.000000000 -0700
+@@ -0,0 +1,2051 @@
++/* -*- linux-c -*-
++#######################################################################
++#
++# (C) Copyright 2001-2003
++# Alex Zeffertt, Cambridge Broadband Ltd, ajz@cambridgebroadband.com
++# Re-worked by Ben Greear <greearb@candelatech.com>
++#
++# 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.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.        See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++# MA 02111-1307 USA
++#######################################################################
++# Notes:
++# 
++# This file implements the macvlan.o MAC address based VLAN support 
++# module.
++#
++# This provides an IOCTL interface which allows you to
++# It uses an IOCTL interface which allows you to
++#
++# 1. enable/disable MAC address based VLANS over an ether type net_device
++# 2. add/remove a MAC address based VLAN - which is an ether type net_device
++#    layered over the original MACVLAN enabled ether type net_device.
++# 3. bind/unbind MAC addresses to/from particular MAC address based VLANs
++# 4. discover the state of MAC address based VLANs on the system.
++# 5. set/get port flags, including whether to bind to destination MAC
++#    or source mac.
++# 6. Traffic to/from eth0 will not be affected.
++
++# Example: (Assuming you are using source binding)
++#
++# If you enable MAC address based VLANS over eth0
++#
++# You may then create further VLANs, e.g. eth0#1 eth0#2 ....
++# These will not receive any frames until you bind MAC addresses to them.
++# If you bind 11:22:33:44:55:66 to eth0#1, then any frames received by
++# eth0 with source MAC 11:22:33:44:55:66 will be routed up through eth0#1
++# instead of eth0.
++#
++# Example: (Assuming you are using destination (local) binding)
++#
++# If you enable MAC address based VLANS over eth0
++#
++# You may then create further VLANs, e.g. eth0#1 eth0#2 ....
++# These will not receive any frames until you bind MAC addresses to them.
++# If you bind 11:22:33:44:55:66 to eth0#1, then any broadcast/multicast
++# frames, or frames with a destination MAC 11:22:33:44:55:66
++# will be routed up through eth0#1 instead of eth0
++#
++# For broadcasts, the packet will be duplicated for every VLAN
++# with at least one MAC attached.  Attaching more than one MAC
++# when destination binding makes no sense...don't do it!
++#
++# 
++#######################################################################
++*/
++#include <linux/config.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/sched.h>
++#include <linux/kernel.h> 
++#include <linux/fs.h>   
++#include <linux/errno.h>  
++#include <linux/delay.h>  
++#include <linux/slab.h>
++#include <linux/mm.h>
++#include <linux/ioport.h>
++#include <linux/interrupt.h>
++#include <linux/tqueue.h>
++#include <linux/poll.h>
++#include <linux/types.h>
++#include <linux/string.h>
++#include <linux/if_macvlan.h>
++#include <linux/if_arp.h>
++#include <linux/etherdevice.h>
++#include <net/arp.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/uaccess.h>
++#include <asm/semaphore.h>
++
++#ifdef CONFIG_PROC_FS
++#include <linux/proc_fs.h>
++#define MVL_PROC_DIR "macvlan"
++#define MVL_PROC_CFG "config"
++#define PORT_CFG_FILE_NAME "config"
++static struct proc_dir_entry *mvl_proc_dir;
++static struct proc_dir_entry *mvl_proc_cfg;
++#endif
++
++#include "macvlan.h"
++
++
++/*********************************************************/
++/*                    defines                          */
++/*********************************************************/
++
++#if 0
++#define DEBUG(format,args...) printk(KERN_ERR format, ##args);
++#else
++#define DEBUG(format,args...)
++#endif
++
++
++#undef MVL_USE_RW_LOCKS
++#ifdef MVL_USE_RW_LOCKS
++/*  Must hold this lock to make any changes to the macvlan structures.
++ */
++static rwlock_t mvl_cfg_lock = RW_LOCK_UNLOCKED;
++
++#define MVL_READ_LOCK /* printk("%i: read-lock port list\n", __LINE__); */ \
++                            BUG_ON(in_interrupt()); \
++                          read_lock(&mvl_cfg_lock);
++#define MVL_READ_UNLOCK /* printk("%i: read-unlock port list\n", __LINE__); */ \
++                            BUG_ON(in_interrupt()); \
++                          read_unlock(&mvl_cfg_lock);
++
++#define MVL_WRITE_LOCK /* printk("%i: write-lock port list\n", __LINE__); */ \
++                            BUG_ON(in_interrupt()); \
++                          write_lock(&mvl_cfg_lock);
++#define MVL_WRITE_UNLOCK /* printk("%i: write-unlock port list\n", __LINE__); */ \
++                            BUG_ON(in_interrupt()); \
++                          write_unlock(&mvl_cfg_lock);
++
++
++#define MVL_IRQ_RLOCK(a) /* printk("%i: read-unlock port list\n", __LINE__); */ { \
++                            __u64 now = getCurUs(); \
++                          __u64 later; \
++                          read_lock_irqsave(&mvl_cfg_lock, a); \
++                            later = getCurUs(); \
++                            if ((later - now) > 100) { \
++                             printk("took: %lluus to acquire read lock, line: %i\n", \
++                                    later - now, __LINE__); \
++                            }}
++
++#define MVL_IRQ_RUNLOCK(a) /* printk("%i: read-unlock port list\n", __LINE__); */ \
++                          read_unlock_irqrestore(&mvl_cfg_lock, a);
++#else
++/*  Must hold this lock to make any changes to the macvlan structures.
++ */
++static spinlock_t mvl_cfg_lock = SPIN_LOCK_UNLOCKED;
++
++#define MVL_READ_LOCK(a) MVL_WRITE_LOCK(a)
++#define MVL_READ_UNLOCK(a) MVL_WRITE_UNLOCK(a)
++
++#define MVL_WRITE_LOCK(a) /* printk("%i: write-lock port list\n", __LINE__); */ \
++                         spin_lock_irqsave(&mvl_cfg_lock, a);
++#define MVL_WRITE_UNLOCK(a) /* printk("%i: write-unlock port list\n", __LINE__); */ \
++                           spin_unlock_irqrestore(&mvl_cfg_lock, a); \
++
++
++#define MVL_IRQ_RLOCK(a) /* printk("%i: read-unlock port list\n", __LINE__); */ \
++                          spin_lock_irqsave(&mvl_cfg_lock, a); \
++
++#define MVL_IRQ_RUNLOCK(a) /* printk("%i: read-unlock port list\n", __LINE__); */ \
++                          spin_unlock_irqrestore(&mvl_cfg_lock, a);
++#endif
++
++
++/*********************************************************/
++/*                   file scope variables              */
++/*********************************************************/
++
++static struct macvlan_port *port_list = NULL;
++
++static atomic_t macvlan_nports;
++static atomic_t mvl_vlan_counter;
++
++static int debug_lvl = 0;
++
++
++/*********************************************************/
++/*               forward declarations                  */
++/*********************************************************/
++static int macvlan_hash_rem(const char* vlan_ifname,
++                          const unsigned char* mac);
++
++/*********************************************************/
++/*               function definitions                  */
++/*********************************************************/
++
++/** Convert to micro-seconds */
++static inline __u64 tv_to_us(const struct timeval* tv) {
++        __u64 us = tv->tv_usec;
++        us += (__u64)tv->tv_sec * (__u64)1000000;
++        return us;
++}
++
++
++/* Since the epoc.  More precise over long periods of time than
++ * getRelativeCurMs
++ */
++static inline __u64 getCurUs(void) {
++        struct timeval tv;
++        do_gettimeofday(&tv);
++        return tv_to_us(&tv);
++}
++
++
++char toupper(char in) {
++      if ((in >= 'a') && (in <= 'z')) {
++              in -= ('a' - 'A');
++      }
++      return in;
++}
++
++#define iswhitespace(x)\
++      ((x) == ' ' || (x) == '\n' || (x) == '\r' || (x) == '\r' )
++
++#define skip_whitespace(x)    { while (iswhitespace(*x)) (x)++; }
++
++static int copy_next_word(char *dst, char *src, int len) {
++      char *p;
++      for (p=src; p < src + len ; p++) {
++              if ( iswhitespace(*p))
++                      break;
++              *dst++ = *p;
++      }
++      return p - src;
++}
++
++
++static int toMacString(unsigned char* rslt_mac, const char* raw_mac) {
++      // Turn HEX into bytes.  First, gather all the useful HEX
++      char tmp[12]; //More than 12 is useless, at least right now
++      char c;
++      int j = 0; //tmp's index.
++      int i;
++      char tmp_bt[3];
++      for (i = 0; i<strlen(raw_mac); i++) {
++              c = toupper(raw_mac[i]);
++              if (((c >= '0') && (c <= '9')) || ((c >= 'A') && (c <= 'F'))) {
++                      tmp[j] = c;
++                      //VLOG_ERR(VLOG << " c: " << c << endl);
++                      if (j == 11) {
++                              break; //done
++                      }
++                      j++;
++              }
++              else {
++                      if ((c == ':') || (c == ' ') || (c == '.')) {
++                              // Ok, valid divider
++                      }
++                      else {
++                              // Invalid header
++                              return -EINVAL;
++                      }
++              }
++      }
++      
++      if (j != 11) {
++              //msg->append("ERROR:  Not enough HEX values in the input string.\n");
++              return -EINVAL;
++      }
++      
++      for (i = 0; i<6; i++) {
++              tmp_bt[0] = tmp[i*2];
++              tmp_bt[1] = tmp[i*2 +1];
++              tmp_bt[2] = 0;
++              //VLOG_ERR(VLOG << " tmp_bt -:" << tmp_bt << ":- i: " << i << endl);
++              rslt_mac[i] = (unsigned char)(simple_strtol(tmp_bt, NULL, 16) & 0xFF);
++              //VLOG_ERR(VLOG << " rslt_mac[" << i << "]  -:" << rslt_mac[i] << ":-\n");
++      }
++      return 0;
++}//toMacString
++
++
++struct macvlan_vlan* macvlan_find_vlan_in_port(struct macvlan_port* port,
++                                             const char* ifname) {
++      struct macvlan_vlan* vlan;
++      for (vlan = port->vlan_list; vlan; vlan = vlan->next) {
++              if (!strcmp(vlan->dev->name, ifname)) {
++                      return vlan;
++              }
++      }
++      return NULL;
++}       
++
++
++/* Find port by mac-vlan interface name (eth1#777) */
++struct macvlan_port* macvlan_find_port_for_mvlan_ifname(const char* ifname) {
++      struct macvlan_port* port;
++      for (port = port_list; port; port = port->next) {
++              if (macvlan_find_vlan_in_port(port, ifname)) {
++                      break;
++              }
++      }
++      return port;
++}
++
++struct macvlan_port* macvlan_find_port_for_underlying_ifname(const char* ifname) {
++      struct macvlan_port* port;
++      //printk("finding port for underlying ifname: %s\n", ifname);
++      for (port = port_list; port; port = port->next) {
++              //printk("Testing port: %p name: %s\n", port, port->dev->name);
++              if (strcmp(port->dev->name, ifname) == 0) {
++                      break;
++              }
++      }
++      //printk("done finding port: %p\n", port);
++      return port;
++}      
++
++/*
++ *    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.
++ *
++ */
++int macvlan_dev_rebuild_header(struct sk_buff *skb) {
++      struct net_device *dev = skb->dev;
++      struct ethhdr *veth = (struct ethhdr *)(skb->data);
++
++      switch (veth->h_proto) {
++#ifdef CONFIG_INET
++      case __constant_htons(ETH_P_IP):
++
++              return arp_find(veth->h_dest, skb);
++#endif        
++      default:
++              DEBUG("%s: unable to resolve type %X addresses.\n", 
++                    dev->name, (int)veth->h_proto);
++       
++              memcpy(veth->h_source, dev->dev_addr, ETH_ALEN);
++              break;
++      };
++
++      return 0;
++}
++
++
++
++static struct net_device_stats *macvlan_get_stats(struct net_device *dev)
++{
++      struct macvlan_vlan *vlan = dev->priv;
++
++      return &vlan->statistics;
++}
++
++static int macvlan_xmit(struct sk_buff *skb, struct net_device *dev)
++{
++      struct macvlan_vlan *vlan = dev->priv;
++      DEBUG("%s: \n", __PRETTY_FUNCTION__);
++      vlan->statistics.tx_packets++;
++      vlan->statistics.tx_bytes += skb->len;
++
++      skb->dev = vlan->lowerdev;
++      dev_queue_xmit(skb);
++      return 0;
++}
++
++static int macvlan_open(struct net_device *dev)
++{
++      MOD_INC_USE_COUNT;
++      netif_start_queue(dev);
++      return 0;
++}
++
++static void macvlan_set_multicast_list(struct net_device *dev)
++{
++      /* TODO ??? */
++}
++
++static int macvlan_stop(struct net_device *dev)
++{
++      netif_stop_queue(dev);
++      MOD_DEC_USE_COUNT;
++      return 0;
++}
++
++static int macvlan_accept_fastpath(struct net_device *dev, struct dst_entry *dst)
++{
++      return -1;
++}
++
++
++/*
++ *    Create the VLAN 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 macvlan_hard_header(struct sk_buff *skb, struct net_device *dev,
++                      unsigned short type, void *daddr, void *saddr,
++                      unsigned len)
++{
++      struct macvlan_vlan *vlan = dev->priv;
++      
++      DEBUG("%s: \n", __PRETTY_FUNCTION__);
++
++      /* Before delegating work to the lower layer, enter our MAC-address */
++      saddr = dev->dev_addr;
++
++      dev = vlan->lowerdev;
++
++      /* Now make the underlying real hard header */
++      return dev->hard_header(skb, dev, type, daddr, saddr, len);
++}
++
++
++void macvlan_dev_destructor(struct net_device *dev) {
++      atomic_dec(&mvl_vlan_counter);
++      if (dev->priv) {
++              //printk("dst: %s", dev->name);
++              kfree(dev->priv);
++              dev->priv = NULL;
++      }
++      else {
++              //printk("dst2: %s", dev->name);
++      }
++}
++
++
++static int macvlan_vlan_create(const char* port_name, int newifidx) {
++      struct macvlan_vlan *vlan = NULL;
++      struct macvlan_port* port;
++      char newifname[IFNAMSIZ+1];
++      struct net_device* td = NULL;
++      unsigned long flags;
++      int rv;
++      
++      MVL_WRITE_LOCK(flags);
++
++      //printk("--*-- ");
++      /* find the port to which ifname belongs */
++      port = macvlan_find_port_for_underlying_ifname(port_name);
++      if (!port) {
++              MVL_WRITE_UNLOCK(flags);
++              rv = -ENODEV;
++              goto unlockout;
++      }
++
++      BUG_ON(!port->dev);
++
++      //printk("1 ");
++      if (newifidx < 0) {
++              /* Find the next free index */
++              int i;
++              for (i = 0; i<MAX_MACVLANS_PER_PORT; i++) {
++                      snprintf(newifname, IFNAMSIZ, "%s#%d", port->dev->name, i);
++                      newifname[IFNAMSIZ] = 0;
++                      if ((td = dev_get_by_name(newifname)) == NULL) {
++                              newifidx = i;
++                              break;
++                      }
++                      dev_put(td);
++              }
++
++              if (newifidx < 0) {
++                      printk("macvlan: Could not find a free index, reached max: %i\n", i);
++              }
++      }
++      
++      //printk("2 ");
++      /* generate a name for the new vlan */
++      snprintf(newifname, IFNAMSIZ, "%s#%d", port->dev->name, newifidx);
++      newifname[IFNAMSIZ] = 0;
++
++      if ((td = dev_get_by_name(newifname)) != NULL) {
++              DEBUG("macvlan: vlan by that name already exists\n");
++              dev_put(td);
++              rv = -EEXIST;
++              goto unlockout;
++      }
++
++      //printk("3 ");
++      if ((vlan = kmalloc(sizeof(*vlan), GFP_KERNEL)) == NULL) {
++              DEBUG("macvlan: kmalloc failure\n");
++              rv = -ENOMEM;
++              goto unlockout;
++      }
++
++      memset(vlan, 0, sizeof(*vlan));
++
++      //printk("4 ");
++      if ((vlan->dev = kmalloc(sizeof(struct net_device), GFP_KERNEL)) == NULL) {
++              rv = -ENOMEM;
++              kfree(vlan);
++              goto unlockout;
++      }
++      memset(vlan->dev, 0, sizeof(struct net_device));
++      
++      //printk("5 ");
++      strcpy(vlan->dev->name, newifname);
++      ether_setup(vlan->dev);
++      
++      dev_hold(vlan->dev); /* MVL code holds reference */
++      
++      vlan->dev->priv = vlan;
++      vlan->port = port;
++      vlan->lowerdev = port->dev;
++
++      //printk("6 ");
++      /* dev->do_ioctl = macvlan_do_ioctl; */
++      vlan->dev->get_stats = macvlan_get_stats;
++      vlan->dev->hard_start_xmit = macvlan_xmit;
++      vlan->dev->hard_header = macvlan_hard_header;
++      vlan->dev->rebuild_header = macvlan_dev_rebuild_header;
++      vlan->dev->open = macvlan_open;
++      vlan->dev->set_multicast_list = macvlan_set_multicast_list;
++      vlan->dev->stop = macvlan_stop;
++      vlan->dev->accept_fastpath = macvlan_accept_fastpath;
++      vlan->dev->tx_queue_len = 0;
++      vlan->dev->set_mac_address = NULL;
++      vlan->dev->priv = vlan;
++      vlan->dev->destructor = macvlan_dev_destructor;
++      
++      /* This will change if you are using Destination (local) binding,
++       * when you add a MAC to it..
++       */
++      memcpy(vlan->dev->dev_addr, vlan->lowerdev->dev_addr, ETH_ALEN);
++
++      DEBUG("macvlan: created vlan %p\n", vlan);
++
++#ifdef MVL_CONFIG_PROC_FS
++      //printk("7 ");
++      if (vlan->port->proc_dir) {
++              vlan->proc_ent = create_proc_read_entry(vlan->dev->name, S_IRUGO,
++                                                      vlan->port->proc_dir,
++                                                      read_mvl, vlan);
++              if (!vlan->proc_ent) {
++                      printk("ERROR:  Could not create proc entry for device: %s\n",
++                             vlan->dev->name);
++              }
++              else {
++                      vlan->proc_ent->write_proc = write_mvl;
++              }
++      }
++#endif
++
++      atomic_inc(&port->ndevs);
++      
++      /* link to list */
++      //printk("8 ");
++      vlan->next = port->vlan_list;
++      port->vlan_list = vlan;
++
++      //printk("End of mac_vlan create1, ref-cnt: %i\n", atomic_read(&dev->refcnt));
++      
++      MVL_WRITE_UNLOCK(flags);
++      register_netdev(vlan->dev);
++
++      //printk("End of mac_vlan create2, ref-cnt: %i\n", atomic_read(&dev->refcnt));
++
++      atomic_inc(&mvl_vlan_counter);
++      //printk("9\n");
++      rv = 0;
++      goto out;
++
++ unlockout:
++      MVL_WRITE_UNLOCK(flags);
++ out:
++      return rv;
++} /* macvlan_vlan_create */
++
++
++/* Has locking internally */
++int macvlan_vlan_cleanup(const char* ifname) {
++      int i;
++      struct macvlan_port* port;
++      struct macvlan_vlan* vlan;
++      struct macvlan_vlan* walker;
++      struct macvlan_vlan* prev;
++      unsigned long flags;
++      int rv;
++      
++      DEBUG(__FUNCTION__"(%p)\n",vlan);
++      //printk("mvl_cln: %s", ifname);
++
++      MVL_WRITE_LOCK(flags);
++      /* NOTE:  Cannot depend on device name, it can be changed. --Ben */
++      port = macvlan_find_port_for_mvlan_ifname(ifname);
++      if (!port) {
++              rv = -ENODEV;
++              goto unlockout;
++      }
++
++      //printk("1 ");
++      vlan = macvlan_find_vlan_in_port(port, ifname);
++      BUG_ON(!vlan);
++
++      if (vlan->dev->flags & IFF_UP) {
++              rv = -EBUSY;
++              goto unlockout;
++      }
++
++      //printk("2 ");
++      for (i = 0; i<MACVLAN_HASH_LEN; i++) {
++              struct macvlan_hash_entry* tmp = vlan->port->hash_table[i];
++              struct macvlan_hash_entry* prev = NULL;
++              while (tmp) {
++                      if (tmp->vlan == vlan) {
++                              if (prev) {
++                                      prev->next = tmp->next;
++                                      kfree(tmp);
++                                      tmp = prev->next;
++                              }
++                              else {
++                                      vlan->port->hash_table[i] = tmp->next;
++                                      kfree(tmp);
++                                      tmp = vlan->port->hash_table[i];
++                              }
++                      }
++                      else {
++                              prev = tmp;
++                              tmp = tmp->next;
++                      }
++              }
++      }/* for all hash buckets */
++      //printk("3 ");
++
++#ifdef MVL_CONFIG_PROC_FS
++      if (vlan->proc_ent) {
++              remove_proc_entry(vlan->dev->name, vlan->port->proc_dir);
++              vlan->proc_ent = NULL;
++      }
++#endif
++
++
++      /*
++       * remove the vlan in question from the list
++       */
++      prev = NULL;
++      walker = port->vlan_list;
++      while (walker) {
++              if (walker == vlan) {
++                      if (prev) {
++                              prev->next = walker->next;
++                      }
++                      else {
++                              port->vlan_list = walker->next;
++                      }
++                      break;
++              }
++              prev = walker;
++              walker = walker->next;
++      }/* while */
++      BUG_ON(walker != vlan);
++      
++      atomic_dec(&port->ndevs);
++
++      //printk("4 ");
++      //printk("End of mac_vlan cleanup1, ref-cnt: %i\n", atomic_read(&vlan->dev->refcnt));
++      dev_put(vlan->dev);
++
++      MVL_WRITE_UNLOCK(flags);
++
++      //printk("End of mac_vlan cleanup2, ref-cnt: %i\n", atomic_read(&vlan->dev->refcnt));
++      unregister_netdev(vlan->dev);
++      
++      /* VLAN will be deleted when the device is deleted */
++      
++      //printk("5 ");
++      rv = 0;
++      goto out;
++
++ unlockout:
++      MVL_WRITE_UNLOCK(flags);
++
++ out:
++      return rv;
++      
++} /* mac_vlan cleanup */
++
++
++
++static int macvlan_port_set_flags(const char* ifname, int flags) {
++      struct macvlan_port *port;
++                      
++      /* find the port to which ifname belongs */
++      port = macvlan_find_port_for_underlying_ifname(ifname);
++      if (!port) {
++              return -ENODEV;
++      }
++      else {
++              port->flags = flags;
++      }
++      return 0;
++}/* macvlan_port_set_flags */
++
++static int macvlan_port_create(const char* ifname) {
++      struct macvlan_port *port;
++      struct net_device* dev;
++      
++      port = macvlan_find_port_for_underlying_ifname(ifname);
++      if (port != NULL) {
++              return -EEXIST;
++      }
++              
++      dev = dev_get_by_name(ifname);
++      if (dev == NULL) {
++              return -ENODEV;
++      }
++
++      if ((dev->macvlan_priv != NULL)
++          || (dev->flags & IFF_LOOPBACK)
++          || (dev->type != ARPHRD_ETHER)) {
++              printk("macvlan: lower layer failed"
++                    " dev->macvlan_priv=%p dev->flags=%08x dev->type=%08x\n",
++                    dev->macvlan_priv, dev->flags, dev->type);
++              dev_put(dev);
++              return -EINVAL;
++      }
++
++      if ((port = kmalloc(sizeof(*port), GFP_KERNEL)) == NULL) {
++              dev_put(dev);
++              return -ENOBUFS;
++      }
++
++      memset(port, 0, sizeof(*port));
++      port->dev = dev;
++
++      /* TODO:  Could use multicast filters in some NICs at least. */
++      dev_set_promiscuity(dev, 1);
++      dev->macvlan_priv = port;
++
++#ifdef MVL_CONFIG_PROC_FS
++      if (mvl_proc_dir) {
++              port->proc_dir = proc_mkdir(port->dev->name, mvl_proc_dir);
++
++              if (port->proc_dir) {
++                      port->proc_ent = create_proc_read_entry(PORT_CFG_FILE_NAME, S_IRUGO,
++                                                              port->proc_dir,
++                                                              read_mvl_port, port);
++                      if (port->proc_ent) {
++                              port->proc_ent->write_proc = write_mvl_port;
++                      }
++                      else {
++                              printk("macvlan: ERROR: failed to create proc entry for port: %s\n",
++                                     port->dev->name);
++                      }
++              }
++      }
++#endif
++
++      atomic_inc(&macvlan_nports);
++      
++      /* Link into our list */
++      port->next = port_list;
++      port_list = port;
++      
++      DEBUG("macvlan: created port=%p\n", port);
++      return 0;
++}/* macvlan_port_create */
++
++
++/* Clears all memory, kfree's it if possible.
++ */
++static int macvlan_port_cleanup(const char* ifname) {
++      struct macvlan_port *port;
++      struct macvlan_port *prev;
++      struct macvlan_port *walker;
++      int i;
++      
++      port = macvlan_find_port_for_underlying_ifname(ifname);
++      if (!port) {
++              return -ENODEV;
++      }
++
++      if (port->vlan_list) {
++              return -EBUSY;
++      }
++
++      /* hash table should be empty at this point */
++      for (i = 0 ; i < MACVLAN_HASH_LEN; i++) {
++              BUG_ON(port->hash_table[i]);
++      }
++
++      /* Remove from our port list */
++      prev = NULL;
++      walker = port_list;
++      while (walker) {
++              if (walker == port) {
++                      if (prev) {
++                              prev->next = walker->next;
++                      }
++                      else {
++                              port_list = walker->next;
++                      }
++                      break;
++              }
++              prev = walker;
++              walker = walker->next;
++      }
++      BUG_ON(walker != port);
++      
++      
++#ifdef MVL_CONFIG_PROC_FS
++      if (port->proc_dir) {
++              if (port->proc_ent) {
++                      remove_proc_entry(PORT_CFG_FILE_NAME, port->proc_dir);
++                      port->proc_ent = NULL;
++              }
++              
++              remove_proc_entry(port->dev->name, mvl_proc_dir);
++              port->proc_dir = NULL;
++      }
++#endif
++      
++      dev_set_promiscuity(port->dev, -1);
++      port->dev->macvlan_priv = NULL;
++      dev_put(port->dev);
++
++      atomic_dec(&macvlan_nports);
++
++      kfree(port);
++      
++      return 0;
++}/* macvlan_port_cleanup */
++
++
++static inline struct macvlan_vlan *macvlan_hash_lookup(struct macvlan_port *port,
++                                                     const unsigned char *src) {
++      /* 
++       * The hashing function is to simply
++       * take the bottom source address byte
++       */
++      struct macvlan_hash_entry *entry;
++      unsigned int bucket = VLAN_BUCKET(src);
++      for (entry = port->hash_table[bucket]; entry; entry = entry->next) {
++              if (memcmp(entry->mac, src, ETH_ALEN) == 0) {
++                      /*DEBUG("macvlan: matched %02x:%02x:%02x:%02x:%02x:%02x to vlan %p\n", 
++                        src[0],src[1],src[2],src[3],src[4],src[5],entry->vlan); */
++                      return entry->vlan;
++              }
++      }
++      return NULL;
++}
++
++
++static int macvlan_hash_add(const char* ifname,
++                          const unsigned char* macaddr) {
++      
++      struct macvlan_port *port;
++      struct macvlan_vlan *vlan;
++      unsigned int bucket = VLAN_BUCKET(macaddr);
++      struct macvlan_hash_entry* entry;
++      
++
++      /* find the port in question */
++      port = macvlan_find_port_for_mvlan_ifname(ifname);
++      if (!port) {
++              return -ENODEV;
++      }
++          
++      /* find the vlan layered over this port */
++      vlan = macvlan_find_vlan_in_port(port, ifname);
++      BUG_ON(!vlan);
++      
++      /* check it's not already in the hash lookup table */
++      if (macvlan_hash_lookup(port, macaddr)) {
++              DEBUG("macvlan: user tried to add mac addr twice!\n");
++              return -EEXIST;
++      }
++
++      if ((atomic_read(&vlan->nmacs) > 0)
++          && (port->flags & MVL_FILTER_ON_DEST)) {
++              printk("macvlan:  Already have a MAC on this vlan: %s and we are filtering on DEST, so no more are allowed!\n",
++                     ifname);
++              return -EINVAL;
++      }
++
++      entry = kmalloc(sizeof(*entry), GFP_KERNEL);
++      if (!entry) {
++              return -ENOBUFS;
++      }
++      memset(entry, 0, sizeof(*entry));
++      
++      memcpy(entry->mac, macaddr, sizeof(entry->mac));
++      entry->vlan = vlan;
++      entry->next = port->hash_table[bucket];
++      port->hash_table[bucket] = entry;
++      DEBUG("macvlan: added %02x:%02x:%02x:%02x:%02x:%02x to vlan %p\n", 
++            entry->src[0],entry->src[1],entry->src[2],
++            entry->src[3],entry->src[4],entry->src[5],
++            vlan);
++
++      atomic_inc(&vlan->nmacs);
++      
++      if (port->flags & MVL_FILTER_ON_DEST) {
++              /* Set the MAC on the vlan device so that it sends pkts correctly. */
++              memcpy(vlan->dev->dev_addr, macaddr, ETH_ALEN);
++      }
++      
++      return 0;
++} /* macvlan_hash_add */
++
++/* cleans up the mac hash entry memory (kfree). */
++static int macvlan_hash_rem(const char* vlan_ifname,
++                          const unsigned char* mac) {
++      int bucket = VLAN_BUCKET(mac);
++      struct macvlan_port *port;
++      struct macvlan_hash_entry *entry;
++      struct macvlan_hash_entry* prev;
++          
++      /* find the port in question */
++      port = macvlan_find_port_for_mvlan_ifname(vlan_ifname);
++
++      if (!port) {
++              return -ENODEV;
++      }
++
++      entry = port->hash_table[bucket];
++      prev = NULL;
++      //printk("hash_rem, found port: %p  bucket: %i  entry: %p\n",
++      //       port, bucket, entry);
++      while (entry) {
++              //printk("Testing entry: %p\n", entry);
++              if (memcmp(entry->mac, mac, ETH_ALEN) == 0) {
++                      if (prev) {
++                              prev->next = entry->next;
++                      }
++                      else {
++                              port->hash_table[bucket] = entry->next;
++                      }
++                      atomic_dec(&entry->vlan->nmacs);
++                      kfree(entry);
++                      return 0;
++              }
++              prev = entry;
++              entry = entry->next;
++      }
++      
++      return -EINVAL;
++}/* macvlan_hash_rem */
++
++
++static int macvlan_ioctl_deviceless_stub(unsigned long arg) {
++      int err = 0;
++      struct macvlan_ioctl req;
++      struct macvlan_ioctl_reply rep;
++      unsigned long flags;
++      
++      if (!capable(CAP_NET_ADMIN))
++              return -EPERM;
++
++      if (copy_from_user(&req, (void *)arg, sizeof(req)))
++              return -EFAULT;
++
++      memset(&rep, 0, sizeof(rep));
++
++      switch (req.cmd)
++      {
++      case MACVLAN_ENABLE:
++      {
++              /* 
++               * enable creation of mac based vlans
++               * layered over an ethernet device
++               */
++              char ifname[IFNAMSIZ];
++          
++              /* Get name of ethernet device */
++              if(copy_from_user(ifname, (void *)req.ifname, sizeof(ifname))) {
++                      err = -EFAULT;
++                      break;
++              }
++              ifname[IFNAMSIZ-1] = '\0';
++
++              MVL_WRITE_LOCK(flags);
++              err = macvlan_port_create(ifname);
++              MVL_WRITE_UNLOCK(flags);
++              
++              break;
++      }
++      case MACVLAN_DISABLE:
++      {
++              /*
++               * disable creation of mac based vlans
++               * layered over an ethernet device
++               */
++              char ifname[IFNAMSIZ];
++              
++              /* Get name of ethernet device */
++              if(copy_from_user(ifname, (void *)req.ifname, sizeof(ifname))) {
++                      err = -EFAULT;
++                      break;
++              }
++              ifname[IFNAMSIZ-1] = '\0';
++
++              MVL_WRITE_LOCK(flags);
++              err = macvlan_port_cleanup(ifname);
++              MVL_WRITE_UNLOCK(flags);
++
++              break;
++      }
++      case MACVLAN_ADD:
++      {
++              /* 
++               * create a new mac based vlan
++               */
++              char ifname[IFNAMSIZ];
++              int ifidx;
++              
++              /* Get name of port over which we are creating a vlan */
++              if(copy_from_user(ifname, (void *)req.ifname, sizeof(ifname))) {
++                      err = -EFAULT;
++                      break;
++              }
++              ifname[IFNAMSIZ-1] = '\0';
++              
++              /* Get index of new vlan we are creating */
++              ifidx = req.ifidx;
++
++              /* Has internal locking. */
++              err = macvlan_vlan_create(ifname, ifidx);
++              
++              break;
++      }
++      case MACVLAN_SET_PORT_FLAGS:
++      {
++              /* 
++               * Set a macvlan_port's flags
++               */
++              char ifname[IFNAMSIZ];
++              
++              /* Get name of port over which we are creating a vlan */
++              if(copy_from_user(ifname, (void *)req.ifname, sizeof(ifname))) {
++                      err = -EFAULT;
++                      break;
++              }
++              ifname[IFNAMSIZ-1] = '\0';
++
++              MVL_WRITE_LOCK(flags);
++              err = macvlan_port_set_flags(ifname, req.ifidx);
++              MVL_WRITE_UNLOCK(flags);
++              
++              break;
++      }
++      case MACVLAN_GET_PORT_FLAGS:
++      {
++              /* 
++               * Set a macvlan_port's flags
++               */
++              struct macvlan_port *port;
++              char ifname[IFNAMSIZ];
++              
++              /* Get name of port over which we are creating a vlan */
++              if(copy_from_user(ifname, (void *)req.ifname, sizeof(ifname))) {
++                      err = -EFAULT;
++                      break;
++              }
++              ifname[IFNAMSIZ-1] = '\0';
++              
++              MVL_READ_LOCK(flags);
++              /* find the port to which ifname belongs */
++              port = macvlan_find_port_for_mvlan_ifname(ifname);
++              if (!port) {
++                      err = -ENODEV;
++              }
++              else {
++                      rep.num = port->flags;
++              }
++              MVL_READ_UNLOCK(flags);
++              
++              if (copy_to_user((void *)req.reply, &rep, sizeof(rep))) {
++                      err = -EFAULT;
++              }
++
++              break;
++      }
++      case MACVLAN_DEL:
++      {
++              /*
++               * destroy a mac based vlan
++               */
++              char ifname[IFNAMSIZ];
++              
++              /* Get name of vlan to remove */
++              if (copy_from_user(ifname, (void *)req.ifname, sizeof(ifname))) {
++                      err = -EFAULT;
++                      break;
++              }
++              ifname[IFNAMSIZ-1] = '\0';
++
++              /* Has internal locking */
++              err = macvlan_vlan_cleanup(ifname);
++              break;
++      }
++      
++      case MACVLAN_BIND:
++      {
++              /*
++               * Bind a MAC address to vlan
++               */
++              char ifname[IFNAMSIZ];
++              unsigned char macaddr[ETH_ALEN];
++              
++              /* Get name of vlan */
++              if (copy_from_user(ifname, (void *)req.ifname, sizeof(ifname))) {
++                      err = -EFAULT;
++                      break;
++              }
++              ifname[IFNAMSIZ-1] = '\0';
++              
++              /* Get mac address to bind to vlan */
++              if (copy_from_user(macaddr, (void *)req.macaddr, sizeof(macaddr))) {
++                      err = -EFAULT;
++                      break;
++              }
++              
++              MVL_WRITE_LOCK(flags);
++              err = macvlan_hash_add(ifname, macaddr);
++              MVL_WRITE_UNLOCK(flags);
++              break;
++      }
++      case MACVLAN_UNBIND:
++      {
++              /* 
++               * Unbind a MAC address from a vlan
++               */
++              char ifname[IFNAMSIZ];
++              unsigned char macaddr[ETH_ALEN];
++          
++              /* Get name of vlan */
++              if (copy_from_user(ifname, (void *)req.ifname, sizeof(ifname))) {
++                      err = -EFAULT;
++                      break;
++              }
++              ifname[IFNAMSIZ-1] = '\0';
++          
++              /* Get mac address to unbind */
++              if (copy_from_user(macaddr, (void *)req.macaddr, sizeof(macaddr))) {
++                      err = -EFAULT;
++                      break;
++              }
++
++              MVL_WRITE_LOCK(flags);
++              err = macvlan_hash_rem(ifname, macaddr);
++              MVL_WRITE_UNLOCK(flags);
++              break;
++      }
++
++      case MACVLAN_IS_MACVLAN:
++      {
++              /* 
++               * Give user-space a chance of determining if we are a MAC-VLAN nor not.
++               *  (If the IOCTL fails, we are not, otherwise we are.)
++               */
++              struct macvlan_port *port;
++              char ifname[IFNAMSIZ];
++          
++              /* Get name of vlan */
++              if(copy_from_user(ifname, (void *)req.ifname, sizeof(ifname))) {
++                      err = -EFAULT;
++                      break;
++              }
++              ifname[IFNAMSIZ-1] = '\0';
++          
++              MVL_READ_LOCK(flags);
++              /* find the port in question */
++              port = macvlan_find_port_for_mvlan_ifname(ifname);
++              MVL_READ_UNLOCK(flags);
++
++              if (!port) {
++                      /* printk("device: %s is NOT a MAC-VLAN\n", ifname); */
++                      err = -ENODEV;
++              }
++              else {
++                      /* printk("device: %s IS a MAC-VLAN\n", ifname); */
++                      err = 0;
++              }
++              break;
++      }
++      case MACVLAN_GET_NUM_PORTS:
++      {
++              /* 
++               * how many ethernet devices have mac based vlans enabled over them
++               */
++              rep.num = atomic_read(&macvlan_nports);
++              if (copy_to_user((void *)req.reply, &rep, sizeof(rep))) {
++                      err = -EFAULT;
++                      break;
++              }
++              break;
++      }
++      case MACVLAN_GET_PORT_NAME:
++      {
++              /* 
++               * name the nth device which has mac based vlans enabled over it
++               */
++              struct macvlan_port *port;
++              int n = req.portidx;
++
++              MVL_READ_LOCK(flags);
++              /* find the port in question */
++              for (port = port_list; port && n; port = port->next, n--);
++              if (!port) {
++                      err = -ENODEV;
++              }
++              else {
++                      memcpy(rep.name, port->dev->name, IFNAMSIZ);
++                                      
++                      if (copy_to_user((void *)req.reply, &rep, sizeof(rep))) {
++                              err = -EFAULT;
++                      }
++              }
++              MVL_READ_UNLOCK(flags);
++              break;
++      }
++      case MACVLAN_GET_NUM_VLANS:
++      {
++              /*
++               * how many vlans are layered over the nth mac-based
++               * vlan enabled device
++               */
++
++              struct macvlan_port *port;
++              int n = req.portidx;
++
++              MVL_READ_LOCK(flags);
++              /* find the port in question */
++              for (port = port_list; port && n; port = port->next, n--);
++
++              if (!port) {
++                      err = -ENODEV;
++              }
++              else {
++                      rep.num = atomic_read(&port->ndevs);
++                      if (copy_to_user((void *)req.reply, &rep, sizeof(rep))) {
++                              err = -EFAULT;
++                      }
++              }
++              MVL_READ_UNLOCK(flags);
++      
++              break;
++      }
++      case MACVLAN_GET_VLAN_NAME:
++      {
++              /* 
++               * what's the name of the mth vlan layered over the nth
++               * mac-based-vlan enabled ethernet device
++               */
++              struct macvlan_port *port;
++              struct macvlan_vlan *vlan;
++              int n = req.portidx;
++              int m = req.ifidx;
++
++              
++              MVL_READ_LOCK(flags);
++              /* find the port in question */
++              for (port = port_list; port && n; port = port->next, n--);
++              if (!port) {
++                      err = -EINVAL;
++              }
++              else {
++                      /* find the vlan in question */
++                      for (vlan = port->vlan_list; vlan && m; vlan = vlan->next, m--);
++
++                      if (!vlan) {
++                              err = -ENODEV;
++                      }
++                      else {
++                              memcpy(rep.name, vlan->dev->name, IFNAMSIZ);
++                      }
++                      if (copy_to_user((void *)req.reply, &rep, sizeof(rep))) {
++                              err = -EFAULT;
++                      }
++              }
++              MVL_READ_UNLOCK(flags);
++              break;
++      }
++      case MACVLAN_GET_NUM_MACS:
++      {
++              /* 
++               * how many mac addresses are owned by the mth vlan
++               * layered over the nth mac-based-vlan enabled 
++               * ethernet device
++               */
++              struct macvlan_port *port;
++              struct macvlan_vlan *vlan;
++              int n = req.portidx;
++              int m = req.ifidx;
++
++
++              MVL_READ_LOCK(flags);
++              /* find the port in question */
++              for (port = port_list; port && n; port = port->next, n--);
++
++              if (!port) {
++                      err = -EINVAL;
++              }
++              else {
++                      /* find the vlan in question */
++                      for (vlan = port->vlan_list; vlan && m; vlan = vlan->next, m--);
++
++                      if (!vlan) {
++                              err = -ENODEV;
++                      }
++                      else {
++                              rep.num = atomic_read(&vlan->nmacs);
++                      }
++                      if (copy_to_user((void *)req.reply, &rep, sizeof(rep))) {
++                              err = -EFAULT;
++                      }
++              }
++              MVL_READ_UNLOCK(flags);
++              break;
++      }
++      case MACVLAN_GET_MAC_NAME:
++      {
++              /* 
++               * what's the pth mac address owned by the mth vlan
++               * layered over the nth mac-based-vlan enabled 
++               * ethernet device
++               */
++              struct macvlan_port *port;
++              struct macvlan_vlan *vlan;
++              struct macvlan_hash_entry *entry;
++              int n = req.portidx;
++              int m = req.ifidx;
++              int p = req.macaddridx;
++
++              MVL_READ_LOCK(flags);
++              /* find the port in question */
++              for (port = port_list; port && n; port = port->next, n--);
++
++              if (!port) {
++                      err = -EINVAL;
++              }
++              else {
++                      /* find the vlan in question */
++                      for (vlan = port->vlan_list; vlan && m; vlan = vlan->next, m--);
++
++                      if (!vlan) {
++                              err = -ENODEV;
++                      }
++                      else {
++                              /* find the mac addr in question */
++                              int i;
++                              for (i = 0; i<MACVLAN_HASH_LEN; i++) {
++                                      entry = port->hash_table[i];
++                                      while (entry) {
++                                              if (entry->vlan == vlan) {
++                                                      if (--p == 0) {
++                                                              memcpy(rep.name, entry->mac, sizeof(entry->mac));
++                                                              goto found_one;
++                                                      }
++                                              }
++                                              entry = entry->next;
++                                      } /* while */
++                              }/* for */
++                              
++                              /* Didn't find one */
++                              err = -ENODEV;
++                      }
++
++              found_one:              
++                      
++                      if (copy_to_user((void *)req.reply, &rep, sizeof(rep))) {
++                              err = -EFAULT;
++                      }
++              }
++              MVL_READ_UNLOCK(flags);
++              break;
++      }
++      default:
++              err = -EOPNOTSUPP;
++              break;
++      }
++
++      /* printk("Returning err: %i\n", err); */
++      return err;
++}/* ioctl handler */
++
++
++/* Return >= 0 if packet is consumed, otherwise return < 0. */
++static inline int mvl_handle_frame_fos(struct macvlan_port* port, struct sk_buff* skb) {
++      struct macvlan_vlan *vlan; /* the higher layer i/f to which skbuff is mapped */
++      int rv;
++      unsigned long flags;
++      
++      DEBUG("%s:  got port: %p, not filtering on DEST\n", __PRETTY_FUNCTION__, port);
++
++      MVL_IRQ_RLOCK(flags);
++      if (!(vlan = macvlan_hash_lookup(port, skb->mac.ethernet->h_source))) {
++              /* not for us, but don't delete it, others may consume it */
++              rv = -ENODEV;
++      }
++      else {
++              if (!(vlan->dev->flags & IFF_UP)) {
++                      rv = 1;  /* was consumed */
++                      kfree_skb(skb);
++              }
++              else {
++                      vlan->statistics.rx_packets++;
++                      /*  Count the lower-level's header to make our counters look more
++                       *  like an ethernet device. */
++                      vlan->statistics.rx_bytes += (skb->len + vlan->lowerdev->hard_header_len);
++
++                      skb->dev = vlan->dev;
++                      dev_hold(skb->dev);
++                      if (memcmp(vlan->dev->dev_addr, skb->mac.ethernet->h_dest, ETH_ALEN)) {
++                              skb->pkt_type=PACKET_OTHERHOST;
++                      }
++                      else {
++                              skb->pkt_type = PACKET_HOST;
++                      }
++                      MVL_IRQ_RUNLOCK(flags);
++                      netif_rx(skb);
++                      dev_put(skb->dev);
++                      rv = 0;
++                      goto out;
++              }
++      }
++      
++      MVL_IRQ_RLOCK(flags);
++ out:
++      return rv;
++} /* filter on source */
++
++
++/* Return >= 0 if packet is consumed, otherwise return < 0. */
++static inline int mvl_handle_frame_fod(struct macvlan_port* port, struct sk_buff* skb) {
++      struct macvlan_vlan *vlan; /* the higher layer i/f to which skbuff is mapped */
++      int rv;
++      unsigned long flags;
++
++      /* Filtering on destination.. */
++      /* If it's a broadcast pkt, send it to all of them.  Otherwise,
++       * send it to just one of them.
++       */
++      if ((skb->pkt_type == PACKET_BROADCAST) || (skb->pkt_type == PACKET_MULTICAST)) {
++              /* never consume if we take this code branch, because it's bcast */
++              DEBUG("%s:  got port: %p, filtering on DEST, type is bcast or multicast\n",
++                    __PRETTY_FUNCTION__, port);
++              //printk("fod: ");
++              MVL_IRQ_RLOCK(flags);
++              //printk("1 ");
++              for (vlan = port->vlan_list; vlan; vlan = vlan->next) {
++                      //printk(".");
++                      DEBUG("%s:  got vlan: %s, nmacs: %i, up: %i\n",
++                            __PRETTY_FUNCTION__, vlan->dev->name,
++                            vlan->nmacs, (vlan->dev->flags & IFF_UP));
++                      if (atomic_read(&vlan->nmacs) && (vlan->dev->flags & IFF_UP)) {
++                              struct sk_buff* nskb;
++                              
++                              atomic_inc(&skb->users);
++                              nskb = skb_share_check(skb, GFP_ATOMIC);
++                              if (!nskb) {
++                                      vlan->statistics.rx_fifo_errors++;
++                                      vlan->statistics.rx_errors++;
++                              }
++                              else {
++                                      vlan->statistics.rx_packets++;
++                                      /*  Count the lower-level's header to make our counters
++                                       *  look more like an ethernet device. */
++                                      vlan->statistics.rx_bytes +=
++                                              (nskb->len + vlan->lowerdev->hard_header_len);
++                                      vlan->statistics.multicast++;
++                                      
++                                      nskb->dev = vlan->dev;
++                                      netif_rx(nskb);
++                              }
++                      }
++              }
++              //printk("2 ");
++              rv = -1; /* did not consume this pkt, merely tasted it */
++              MVL_IRQ_RUNLOCK(flags);
++              goto out;
++      }
++      else {
++              struct ethhdr *eth = skb->mac.ethernet;
++              char* d = eth->h_dest;
++              /* Not a broadcast, try to find our port based on DESTINATION */
++              //printk("fodNB ");
++              MVL_IRQ_RLOCK(flags);
++              if (!(vlan = macvlan_hash_lookup(port, d))) {
++                      /* not for us */
++                      DEBUG("%s:  not a broadcast, and could not find vlan for dest: %2hx:%2hx:%2hx:%2hx:%2hx:%2hx\n",
++                            __PRETTY_FUNCTION__, d[0], d[1], d[2], d[3], d[4], d[5]);
++                      
++                      rv = -ENODEV;
++                      //printk("1 ");
++              }
++              else {
++                      DEBUG("%s:  not a broadcast, found vlan for dest: "
++                            "%2hx:%2hx:%2hx:%2hx:%2hx:%2hx, up: %i\n",
++                            __PRETTY_FUNCTION__, d[0], d[1], d[2], d[3], d[4], d[5],
++                            (vlan->dev->flags & IFF_UP));
++              
++                      if (!(vlan->dev->flags & IFF_UP)) {
++                              kfree_skb(skb);
++                              rv = 0; /* consume */
++                      }
++                      else {
++                              vlan->statistics.rx_packets++;
++                              /*  Count the lower-level's header to make our counters
++                               *  look more like an ethernet device. */
++                              vlan->statistics.rx_bytes +=
++                                      (skb->len + vlan->lowerdev->hard_header_len);
++              
++                              skb->dev = vlan->dev;
++                              if (!(eth->h_dest[0] & 1)) {
++                                      /* if it's not multicast, see if it's
++                                       * for us, or not.
++                                       */
++                                      if (memcmp(vlan->dev->dev_addr, eth->h_dest, ETH_ALEN)) {
++                                              skb->pkt_type = PACKET_OTHERHOST;
++                                      }
++                                      else {
++                                              skb->pkt_type = PACKET_HOST;
++                                      }
++                              }
++                              dev_hold(skb->dev);
++                              MVL_IRQ_RUNLOCK(flags);
++                              //printk("2 ");
++                              netif_rx(skb);
++                              dev_put(skb->dev);
++                              //printk("3 ");
++                              rv = 0;
++                              goto out;
++                      }
++              }
++      }/* else, was not broadcast */
++
++      MVL_IRQ_RUNLOCK(flags);
++      //printk("4 ");
++
++ out:
++      //printk("5 ");
++      return rv;
++}/* filter on dest */
++
++
++/* global entry point when receiving a pkt from lower-level devices.  Return
++ * >= 0 if we consume, otherwise packet will be sent to the rest of the stack
++ * as normal.
++ *
++ */
++static int macvlan_handle_frame(struct sk_buff *skb)
++{
++      struct macvlan_port *port;  /* maps skbuffs arriving from a lower layer
++                                   * i/f to a higher layer i/f */
++      int rv = 0;
++
++      port = skb->dev->macvlan_priv;
++      if (port->flags & MVL_FILTER_ON_DEST) {
++              rv = mvl_handle_frame_fod(port, skb);
++      }
++      else {
++              rv = mvl_handle_frame_fos(port, skb);
++      }
++
++      return rv;
++}
++
++
++#ifdef MVL_CONFIG_PROC_FS
++
++static int read_mvl_glbl(char *page, char **start, off_t off,
++                       int count, int *eof, void *data) {
++      int     ret = -1;
++      char    *p = page;
++      int mx_len = (4096 - (p - page));
++      
++      if (! *eof ) {
++              struct macvlan_port* port;
++              int cnt;
++              unsigned long flags;
++              
++                /* Global counts here... */
++              p += sprintf(p, "MAC-VLAN module:\n");
++
++              p += sprintf(p, " port count: %i  vlan_counter: %i\n",
++                           atomic_read(&macvlan_nports),
++                           atomic_read(&mvl_vlan_counter));
++
++              MVL_READ_LOCK(flags);
++              port = port_list;
++              while (port) {
++                      p += sprintf(p, " %s  num_vlans: %i  flags: %x\n",
++                                   port->dev->name, atomic_read(&port->ndevs), port->flags);
++
++                      /* catch overflow */
++                      cnt = p - page;
++                      if (cnt > (mx_len - 60)) {
++                              if (mx_len - cnt >= 20) {
++                                      p += sprintf(p, "OUT_OF_SPACE!\n");
++                              }
++                              break;
++                      }
++
++                      port = port->next;
++              }
++
++              ret = p - page;
++              MVL_READ_UNLOCK(flags);
++      }
++      return ret;
++} /* read_mvl_glbl */
++
++static int write_mvl_glbl(struct file *file, const char *buffer,
++                        unsigned long count, void *data) {
++      char            *p;
++      const char      *end;
++      int             ret=count;
++      int             len;
++      char            dev_name[2][IFNAMSIZ];
++        char* tmps = NULL;
++      unsigned long flags;
++        
++        MVL_WRITE_LOCK(flags);
++        
++      end = buffer+count;
++
++      for (p= (char *) buffer; p< end ; ) {
++              if (iswhitespace(*p)) {
++                      p++;
++                      continue;
++              }
++
++              memset(dev_name[0], 0 ,IFNAMSIZ);
++              memset(dev_name[1], 0 ,IFNAMSIZ);
++
++              len = strlen("add_port ");
++              if (strncmp(p, "add_port ", len)==0)
++              {
++                      p += len;
++
++                      if ( (p + IFNAMSIZ) <= end)
++                              p += copy_next_word(dev_name[0], p, IFNAMSIZ);
++                      else
++                              p += copy_next_word(dev_name[0], p, end-p );
++
++                      skip_whitespace(p);
++
++                        /* This can fail, but not sure how to return failure
++                         * to user-space here.
++                         */
++                        macvlan_port_create(dev_name[0]);
++                        goto forend;
++              }
++                
++              len = strlen("remove_port ");
++              if (strncmp(p,"remove_port ",len)==0) {
++                      p += len;
++
++                      if ( (p + IFNAMSIZ) <= end)
++                              p += copy_next_word(dev_name[0], p, IFNAMSIZ);
++                      else
++                              p += copy_next_word(dev_name[0], p, end-p );
++
++                        skip_whitespace(p);
++
++                        macvlan_port_cleanup(dev_name[0]);
++                        goto forend;
++              }
++
++                len = strlen("debug_lvl ");
++              if (strncmp(p,"debug_lvl ",len)==0)
++              {
++                      p += len;
++
++                      if ( (p + IFNAMSIZ) <= end)
++                              p += copy_next_word(dev_name[0], p, IFNAMSIZ);
++                      else
++                              p += copy_next_word(dev_name[0], p, end-p );
++
++                        skip_whitespace(p);
++
++                        debug_lvl = simple_strtoul(dev_name[0], &tmps, 10);
++                        goto forend;
++              }
++
++                printk("ERROR:  Unsupported command\n");
++
++        forend:
++              p++;
++      }
++
++        MVL_WRITE_UNLOCK(flags);
++
++      return ret;
++} /* write_mvl_glbl */
++
++/* Proc file read for mac-vlan. */
++static int read_mvl(char *page, char **start, off_t off,
++                  int count, int *eof, void *data) {
++      int     ret = -1;
++      if (! *eof ) {
++              char    *p = page;
++              struct macvlan_vlan* vlan = (struct macvlan_vlan*)(data);
++              struct macvlan_hash_entry* entry;
++              int i;
++              int count = 0;
++              int cnt;
++              int mx_len = 4096;
++              unsigned long flags;
++              
++              
++              MVL_READ_LOCK(flags);
++              
++                /* Global counts here... */
++              p += sprintf(p, "MAC-VLAN %s:\n", vlan->dev->name);
++
++              p += sprintf(p, " MAC count: %i  lower_dev: %s  macvlan-port: %s\n",
++                           atomic_read(&vlan->nmacs), vlan->lowerdev->name,
++                           vlan->port->dev->name);
++
++              for (i = 0; i<MACVLAN_HASH_LEN; i++) {
++                      entry = vlan->port->hash_table[i];
++                      while (entry) {
++                              if (entry->vlan == vlan) {
++                                      /* catch overflow */
++                                      cnt = p - page;
++                                      if (cnt > (mx_len - 40)) {
++                                              if (mx_len - cnt >= 20) {
++                                                      p += sprintf(p, "OUT_OF_SPACE!\n");
++                                              }
++                                              goto outofspace;
++                                      }
++
++                                      p += sprintf(p, "  [%i] %02hx:%02hx:%02hx:%02hx:%02hx:%02hx\n",
++                                                   count, entry->mac[0], entry->mac[1], entry->mac[2], 
++                                                   entry->mac[3], entry->mac[4], entry->mac[5]);
++                                      count++;
++
++                              }
++                              entry = entry->next;
++                      }/* while */
++              }/* for */
++
++      outofspace:
++
++              ret = p - page;
++
++              MVL_READ_UNLOCK(flags);
++      }
++      return ret;
++} /* read_mvl_glbl */
++
++
++static int write_mvl(struct file *file, const char *buffer,
++                   unsigned long count, void *data) {
++      char            *p;
++      const char      *end;
++      int             ret=count;
++      int             len;
++      char            arg[MVL_MX_ARG_LEN+1];
++      
++      struct macvlan_vlan* vlan = (struct macvlan_vlan*)(data);
++        char mac[ETH_ALEN];
++      unsigned long flags;
++      
++        MVL_WRITE_LOCK(flags);
++        
++      end = buffer+count;
++
++      for (p= (char *) buffer; p< end ; ) {
++              if (iswhitespace(*p)) {
++                      p++;
++                      continue;
++              }
++
++              memset(arg, 0, MVL_MX_ARG_LEN+1);
++
++              len = strlen("add_mac ");
++              if (strncmp(p, "add_mac ", len)==0) {
++                      p += len;
++
++                      if ( (p + MVL_MX_ARG_LEN) <= end)
++                              p += copy_next_word(arg, p, MVL_MX_ARG_LEN);
++                      else
++                              p += copy_next_word(arg, p, end-p);
++
++                      skip_whitespace(p);
++
++                      if (toMacString(mac, arg) < 0) {
++                              printk("macvlan:  MAC format is incorrect: %s\n",
++                                     arg);
++                      }
++                      else {
++                              /* This can fail, but not sure how to return failure
++                               * to user-space here.
++                               */
++                              macvlan_hash_add(vlan->dev->name, mac);
++                      }
++                        goto forend;
++              }
++                
++              len = strlen("remove_mac ");
++              if (strncmp(p,"remove_mac ",len)==0) {
++                      p += len;
++
++                      if ( (p + MVL_MX_ARG_LEN) <= end)
++                              p += copy_next_word(arg, p, MVL_MX_ARG_LEN);
++                      else
++                              p += copy_next_word(arg, p, end-p);
++
++                      skip_whitespace(p);
++
++                      if (toMacString(mac, arg) < 0) {
++                              printk("macvlan:  MAC format is incorrect: %s\n",
++                                     arg);
++                      }
++                      else {
++                              /* This can fail, but not sure how to return failure
++                               * to user-space here.
++                               */
++                              macvlan_hash_rem(vlan->dev->name, mac);
++                      }
++                        goto forend;
++              }
++
++                printk("ERROR:  Unsupported command\n");
++
++        forend:
++              p++;
++      }
++
++        MVL_WRITE_UNLOCK(flags);
++
++      return ret;
++} /* write_mvl */
++
++
++static int read_mvl_port(char *page, char **start, off_t off,
++                       int count, int *eof, void *data) {
++      int     ret = -1;
++      char    *p = page;
++      int mx_len = (4096 - (p - page));
++      int i;
++      
++      if (! *eof ) {
++              struct macvlan_port* port = (struct macvlan_port*)(data);
++              int cnt;
++              struct macvlan_vlan* vlan;
++              struct macvlan_hash_entry* entry;
++              unsigned long flags;
++
++              MVL_READ_LOCK(flags);
++              
++                /* Global counts here... */
++              p += sprintf(p, "MAC-VLAN Port: %s\n", port->dev->name);
++
++              p += sprintf(p, " vlan count: %i\n", atomic_read(&port->ndevs));
++
++              vlan = port->vlan_list;
++              while (vlan) {
++                      p += sprintf(p, " %s\n", vlan->dev->name);
++
++                      /* catch overflow */
++                      cnt = p - page;
++                      if (cnt > (mx_len - 40)) {
++                              if (mx_len - cnt >= 20) {
++                                      p += sprintf(p, "OUT_OF_SPACE!\n");
++                              }
++                              goto outofspace;
++                      }
++
++                      vlan = vlan->next;
++              }
++
++              /* MAC addr hash */
++
++              for (i = 0; i<MACVLAN_HASH_LEN; i++) {
++                      if (port->hash_table[i]) {
++                              p += sprintf(p, " [%i] ", i);
++                              entry = port->hash_table[i];
++                              while (entry) {
++                                      /* catch overflow */
++                                      cnt = p - page;
++                                      if (cnt > (mx_len - 40)) {
++                                              if (mx_len - cnt >= 20) {
++                                                      p += sprintf(p, "OUT_OF_SPACE!\n");
++                                              }
++                                              goto outofspace;
++                                      }
++                                      
++                                      p += sprintf(p, " %02hx:%02hx:%02hx:%02hx:%02hx:%02hx",
++                                                   entry->mac[0], entry->mac[1], entry->mac[2], 
++                                                   entry->mac[3], entry->mac[4], entry->mac[5]);
++
++                                      entry = entry->next;
++                              }
++                              p += sprintf(p, "\n");
++                      }
++              }
++              
++      outofspace:
++              ret = p - page;
++              MVL_READ_UNLOCK(flags);
++      }
++      return ret;
++} /* read_mvl_glbl */
++
++
++static int write_mvl_port(struct file *file, const char *buffer,
++                        unsigned long count, void *data) {
++      char            *p;
++      const char      *end;
++      int             ret=count;
++      int             len;
++      char            dev_name[2][IFNAMSIZ];
++        char* tmps = NULL;
++        struct macvlan_port* port = (struct macvlan_port*)(data);
++      unsigned long flags;
++      
++      end = buffer+count;
++
++      for (p= (char *) buffer; p< end ; ) {
++              if (iswhitespace(*p)) {
++                      p++;
++                      continue;
++              }
++
++              memset(dev_name[0], 0 ,IFNAMSIZ);
++              memset(dev_name[1], 0 ,IFNAMSIZ);
++
++              len = strlen("add_vlan ");
++              if (strncmp(p, "add_vlan ", len)==0) {
++                      p += len;
++
++                      if ( (p + IFNAMSIZ) <= end)
++                              p += copy_next_word(dev_name[0], p, IFNAMSIZ);
++                      else
++                              p += copy_next_word(dev_name[0], p, end-p );
++
++                      skip_whitespace(p);
++
++                        /* This can fail, but not sure how to return failure
++                         * to user-space here.
++                         */
++                      /* has internal locking */
++                        macvlan_vlan_create(port->dev->name,
++                                          simple_strtoul(dev_name[0], &tmps, 10));
++                        goto forend;
++              }
++
++              len = strlen("set_flags ");
++              if (strncmp(p, "set_flags ", len)==0) {
++                      p += len;
++
++                      if ( (p + IFNAMSIZ) <= end)
++                              p += copy_next_word(dev_name[0], p, IFNAMSIZ);
++                      else
++                              p += copy_next_word(dev_name[0], p, end-p );
++
++                      skip_whitespace(p);
++
++                        /* This can fail, but not sure how to return failure
++                         * to user-space here.
++                         */
++
++                      MVL_WRITE_LOCK(flags);
++                        macvlan_port_set_flags(port->dev->name,
++                                             simple_strtoul(dev_name[0], &tmps, 16));
++                      MVL_WRITE_UNLOCK(flags);
++                      goto forend;
++              }
++
++              len = strlen("remove_vlan ");
++              if (strncmp(p,"remove_vlan ",len)==0) {
++                      p += len;
++
++                      if ( (p + IFNAMSIZ) <= end)
++                              p += copy_next_word(dev_name[0], p, IFNAMSIZ);
++                      else
++                              p += copy_next_word(dev_name[0], p, end-p );
++
++                        skip_whitespace(p);
++
++                      /* Has internal locking */
++                        macvlan_vlan_cleanup(dev_name[0]);
++                        goto forend;
++              }
++
++                printk("ERROR:  Unsupported command\n");
++
++        forend:
++              p++;
++      }
++
++      return ret;
++} /* write_mvl_port */
++
++
++#endif
++
++
++static int __init macvlan_init(void) {
++      printk (KERN_INFO "MAC address based VLAN support Revision: 1.3\n");
++
++      port_list = NULL;
++
++      macvlan_ioctl_hook = macvlan_ioctl_deviceless_stub;
++      macvlan_handle_frame_hook = macvlan_handle_frame;
++
++#ifdef MVL_CONFIG_PROC_FS
++
++        mvl_proc_dir = proc_mkdir(MVL_PROC_DIR, proc_net);
++        if (mvl_proc_dir) {
++              mvl_proc_cfg = create_proc_read_entry(MVL_PROC_CFG, S_IRUGO, mvl_proc_dir,
++                                                    read_mvl_glbl, NULL);
++              if (mvl_proc_cfg) {
++                      mvl_proc_cfg->write_proc = write_mvl_glbl;
++              }
++      }
++#endif
++
++      
++      return 0;
++}
++
++static void macvlan_cleanup(void) {
++      struct macvlan_port *port;
++      
++      macvlan_handle_frame_hook = NULL;
++      macvlan_ioctl_hook = NULL;
++
++      /* destroy all existing ports */
++      while ((port = port_list)) {
++              if (macvlan_port_cleanup(port->dev->name) < 0) {
++                      BUG_ON(1);
++              }
++      }
++
++#ifdef MVL_CONFIG_PROC_FS
++      if (mvl_proc_cfg) {
++              remove_proc_entry(MVL_PROC_CFG, mvl_proc_dir);
++              mvl_proc_cfg = NULL;
++      }
++      if (mvl_proc_dir) {
++              remove_proc_entry(MVL_PROC_DIR, proc_net);
++              mvl_proc_dir = NULL;
++      }
++#endif
++
++}/* macvlan_cleanup */
++
++
++module_init(macvlan_init);
++module_exit(macvlan_cleanup);
++MODULE_LICENSE("GPL");
+--- linux-2.4.21/net/macvlan/macvlan.h 1969-12-31 16:00:00.000000000 -0800
++++ linux-2.4.21.amds/net/macvlan/macvlan.h    2003-08-13 16:26:08.000000000 -0700
+@@ -0,0 +1,104 @@
++/* -*- linux-c -*-
++
++# (C) Copyright 2001-2003
++# Alex Zeffertt, Cambridge Broadband Ltd, ajz@cambridgebroadband.com
++# Re-worked by Ben Greear <greearb@candelatech.com>
++
++*/
++
++#ifndef MACVLAN_KERNEL_H_FILE__
++#define MACVLAN_KERNEL_H_FILE__
++
++
++/* NOTE:  If you change this below, you should probably change macvlan_hash_lookup as
++ * well.  Especially if you make this bigger.
++ */
++#define MACVLAN_HASH_LEN 256
++
++#define VLAN_BUCKET(a) a[5] % MACVLAN_HASH_LEN;
++
++/* This can be made as large as desired, and mainly helps keep bad
++ * IOCTL arguments from taking down the box.
++ */
++#define MAX_MACVLANS_PER_PORT 10000
++
++/* Proc file related */
++#define MVL_MX_ARG_LEN 80
++
++#ifdef CONFIG_PROC_FS
++
++/* To use or not to use the PROC-FS */
++#define MVL_CONFIG_PROC_FS
++
++#endif
++
++
++/*********************************************************/
++/*                   types                             */
++/*********************************************************/
++/* a macvlan_vlan represents an upper layer interface */
++struct macvlan_vlan {
++      struct net_device* dev;
++      struct net_device_stats statistics;
++      struct macvlan_vlan *next;
++      struct macvlan_port *port;
++      struct net_device *lowerdev;
++      atomic_t nmacs;  /* the number of mac addresses bound to this vlan */
++
++#ifdef MVL_CONFIG_PROC_FS
++        struct proc_dir_entry* proc_ent;
++#endif        
++
++};
++
++struct macvlan_hash_entry {
++      unsigned char mac[ETH_ALEN];    /* the eth hdr source to match.  Can
++                                       * match as destination too, see flags in
++                                       * macvlan_port.  Cannot match on both. */
++      struct macvlan_vlan *vlan;      /* the vlan target */
++      struct macvlan_hash_entry *next;/* next entry in list (same hash, any dev) */
++};
++
++
++/*
++ * a macvlan_port represents a mux/demux between a mac-
++ * based-vlan enabled ethernet device and vlans
++ * layered on top of it
++ */
++struct macvlan_port {
++      /* MAC to vlan lookup */
++      struct macvlan_hash_entry *hash_table[MACVLAN_HASH_LEN];
++      struct net_device *dev;  /* the mac-based-vlan enabled ethernet device */
++      atomic_t ndevs;    /* number of vlans layered over dev */
++      struct macvlan_vlan *vlan_list; /* list of vlans layered over this port */
++      struct macvlan_port *next;    /* next port */
++      
++#define MVL_FILTER_ON_DEST 0x1        /* 0x1  filter-on-destination (instead of source) */
++      int flags;
++
++#ifdef MVL_CONFIG_PROC_FS
++        struct proc_dir_entry* proc_dir;
++        struct proc_dir_entry* proc_ent;
++#endif        
++
++};
++
++
++#ifdef MVL_CONFIG_PROC_FS
++static int read_mvl_glbl(char *page, char **start, off_t off,
++                       int count, int *eof, void *data);
++static int write_mvl_glbl(struct file *file, const char *buffer,
++                        unsigned long count, void *data);
++static int read_mvl(char *page, char **start, off_t off,
++                  int count, int *eof, void *data);
++static int write_mvl(struct file *file, const char *buffer,
++                   unsigned long count, void *data);
++static int read_mvl_port(char *page, char **start, off_t off,
++                       int count, int *eof, void *data);
++static int write_mvl_port(struct file *file, const char *buffer,
++                        unsigned long count, void *data);
++#endif
++
++
++#endif
++
+--- linux-2.4.21/net/packet/af_packet.c        2002-08-02 17:39:46.000000000 -0700
++++ linux-2.4.21.amds/net/packet/af_packet.c   2003-07-30 16:20:41.000000000 -0700
+@@ -68,6 +68,7 @@
+ #include <linux/module.h>
+ #include <linux/init.h>
+ #include <linux/if_bridge.h>
++#include <linux/if_macvlan.h>
+ #ifdef CONFIG_NET_DIVERT
+ #include <linux/divert.h>
+@@ -1504,6 +1505,20 @@
+ #endif                                
+                       return -ENOPKG;
++              case SIOCGIFMACVLAN:
++              case SIOCSIFMACVLAN:
++#if defined(CONFIG_MACVLAN) || defined(CONFIG_MACVLAN_MODULE)
++#ifdef CONFIG_INET
++#ifdef CONFIG_KMOD
++                      if (macvlan_ioctl_hook == NULL)
++                              request_module("macvlan");
++#endif
++                      if (macvlan_ioctl_hook != NULL)
++                              return macvlan_ioctl_hook(arg);
++#endif
++#endif
++                      return -ENOPKG;
++
+               case SIOCGIFDIVERT:
+               case SIOCSIFDIVERT:
+ #ifdef CONFIG_NET_DIVERT
+--- linux-2.4.21/net/ipv4/arp.c        2002-11-28 15:53:15.000000000 -0800
++++ linux-2.4.21.amds/net/ipv4/arp.c   2003-07-30 16:20:41.000000000 -0700
+@@ -1,4 +1,4 @@
+-/* linux/net/inet/arp.c
++/* linux/net/inet/arp.c  -*-linux-c-*-
+  *
+  * Version:   $Id: candela_2.4.21.patch,v 1.4 2003/09/30 21:05:04 greear Exp $
+  *
+@@ -351,12 +351,22 @@
+       int flag = 0; 
+       /*unsigned long now; */
+-      if (ip_route_output(&rt, sip, tip, 0, 0) < 0) 
++      if (ip_route_output(&rt, sip, tip, 0, 0) < 0)
+               return 1;
+-      if (rt->u.dst.dev != dev) { 
+-              NET_INC_STATS_BH(ArpFilter);
+-              flag = 1;
+-      } 
++        
++      if (rt->u.dst.dev != dev) {
++                if ((dev->priv_flags & IFF_ACCEPT_LOCAL_ADDRS) &&
++                    (rt->u.dst.dev == &loopback_dev))  {
++                        /* OK, we'll let this special case slide, so that we can arp from one
++                         * local interface to another.  This seems to work, but could use some
++                         * review. --Ben
++                         */
++                }
++                else {
++                        NET_INC_STATS_BH(ArpFilter);
++                        flag = 1;
++                }
++        }
+       ip_rt_put(rt); 
+       return flag; 
+ } 
+--- linux-2.4.21/net/ipv4/fib_frontend.c       2002-08-02 17:39:46.000000000 -0700
++++ linux-2.4.21.amds/net/ipv4/fib_frontend.c  2003-07-30 16:20:41.000000000 -0700
+@@ -233,8 +233,17 @@
+       if (fib_lookup(&key, &res))
+               goto last_resort;
+-      if (res.type != RTN_UNICAST)
+-              goto e_inval_res;
++        
++      if (res.type != RTN_UNICAST) {
++                if ((res.type == RTN_LOCAL) &&
++                    (dev->priv_flags & IFF_ACCEPT_LOCAL_ADDRS)) {
++                        /* All is OK */
++                }
++                else {
++                        goto e_inval_res;
++                }
++        }
++        
+       *spec_dst = FIB_RES_PREFSRC(res);
+       fib_combine_itag(itag, &res);
+ #ifdef CONFIG_IP_ROUTE_MULTIPATH
+--- linux-2.4.21/net/ipv4/tcp_ipv4.c   2003-06-13 07:51:39.000000000 -0700
++++ linux-2.4.21.amds/net/ipv4/tcp_ipv4.c      2003-07-30 16:20:41.000000000 -0700
+@@ -1403,7 +1403,7 @@
+ #define want_cookie 0 /* Argh, why doesn't gcc optimize this :( */
+ #endif
+-      /* Never answer to SYNs send to broadcast or multicast */
++      /* Never answer to SYNs sent to broadcast or multicast */
+       if (((struct rtable *)skb->dst)->rt_flags & 
+           (RTCF_BROADCAST|RTCF_MULTICAST))
+               goto drop; 
+--- linux-2.4.21/net/8021q/vlan_dev.c  2003-06-13 07:51:39.000000000 -0700
++++ linux-2.4.21.amds/net/8021q/vlan_dev.c     2003-08-05 20:38:25.000000000 -0700
+@@ -1,18 +1,18 @@
+-/*
++/* -*- linux-c -*-
+  * INET               802.1Q VLAN
+  *            Ethernet-type device handling.
+  *
+  * Authors:   Ben Greear <greearb@candelatech.com>
+- *              Please send support related email to: vlan@scry.wanfear.com
+- *              VLAN Home Page: http://www.candelatech.com/~greear/vlan.html
++ *            Please send support related email to: vlan@scry.wanfear.com
++ *            VLAN Home Page: http://www.candelatech.com/~greear/vlan.html
+  * 
+- * Fixes:       Mar 22 2001: Martin Bokaemper <mbokaemper@unispherenetworks.com>
+- *                - reset skb->pkt_type on incoming packets when MAC was changed
+- *                - see that changed MAC is saddr for outgoing packets
+- *              Oct 20, 2001:  Ard van Breeman:
+- *                - Fix MC-list, finally.
+- *                - Flush MC-list on VLAN destroy.
+- *                
++ * Fixes:     Mar 22 2001: Martin Bokaemper <mbokaemper@unispherenetworks.com>
++ *              - reset skb->pkt_type on incoming packets when MAC was changed
++ *              - see that changed MAC is saddr for outgoing packets
++ *            Oct 20, 2001:  Ard van Breeman:
++ *              - Fix MC-list, finally.
++ *              - Flush MC-list on VLAN destroy.
++ *              
+  *
+  *            This program is free software; you can redistribute it and/or
+  *            modify it under the terms of the GNU General Public License
+@@ -99,18 +99,18 @@
+  *  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.
++ *              towards userland.  To get here, it would have already passed
++ *              through the ethernet/eth.c eth_type_trans() method.
+  *  SANITY NOTE 2: We are referencing to the VLAN_HDR frields, which MAY be
+- *                 stored UNALIGNED in the memory.  RISC systems don't like
+- *                 such cases very much...
++ *               stored UNALIGNED in the memory.  RISC systems don't like
++ *               such cases very much...
+  *  SANITY NOTE 2a:  According to Dave Miller & Alexey, it will always be aligned,
+- *                 so there doesn't need to be any of the unaligned stuff.  It has
+- *                 been commented out now...  --Ben
++ *               so there doesn't need to be any of the unaligned stuff.  It has
++ *               been commented out now...  --Ben
+  *
+  */
+ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
+-                  struct packet_type* ptype)
++                struct packet_type* ptype)
+ {
+       unsigned char *rawp = NULL;
+       struct vlan_hdr *vhdr = (struct vlan_hdr *)(skb->data);
+@@ -170,7 +170,7 @@
+               spin_unlock_bh(&vlan_group_lock);
+ #ifdef VLAN_DEBUG
+-              printk(VLAN_DBG "%s: dropping skb: %p because came in on wrong device, dev: %s  real_dev: %s, skb_dev: %s\n",
++              printk(VLAN_DBG "%s: dropping skb: %p because came in on wrong device, dev: %s  real_dev: %s, skb_dev: %s\n",
+                       __FUNCTION__ skb, dev->name, 
+                       VLAN_DEV_INFO(skb->dev)->real_dev->name, 
+                       skb->dev->name);
+@@ -324,8 +324,8 @@
+  *  physical devices.
+  */
+ int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev,
+-                         unsigned short type, void *daddr, void *saddr,
+-                         unsigned len)
++                       unsigned short type, void *daddr, void *saddr,
++                       unsigned len)
+ {
+       struct vlan_hdr *vhdr;
+       unsigned short veth_TCI = 0;
+@@ -613,7 +613,7 @@
+                               dev_put(dev);
+                               return 0;
+                       } else {
+-                              printk(KERN_ERR  "%s: flag %i is not valid.\n",
++                              printk(KERN_ERR  "%s: flag %i is not valid.\n",
+                                       __FUNCTION__, (int)(flag));
+                               dev_put(dev);
+                               return -EINVAL;
+@@ -625,13 +625,66 @@
+                       dev_put(dev);
+               }
+       } else {
+-              printk(KERN_ERR  "%s: Could not find device: %s\n", 
++              printk(KERN_ERR  "%s: Could not find device: %s\n", 
+                       __FUNCTION__, dev_name);
+       }
+       return -EINVAL;
+ }
++
++int vlan_dev_get_realdev_name(const char *dev_name, char* result)
++{
++      struct net_device *dev = dev_get_by_name(dev_name);
++      int rv = 0;
++      
++      if (dev) {
++              if (dev->priv_flags & IFF_802_1Q_VLAN) {
++                      strncpy(result, VLAN_DEV_INFO(dev)->real_dev->name, 23);
++                      dev_put(dev);
++                      rv = 0;
++              } else {
++                      printk(KERN_ERR 
++                             "%s: %s is not a vlan device, priv_flags: %hX.\n",
++                             __FUNCTION__, dev->name, dev->priv_flags);
++                      dev_put(dev);
++                      rv = -EINVAL;
++              }
++      } else {
++              printk(KERN_ERR  "%s: Could not find device: %s\n", 
++                      __FUNCTION__, dev_name);
++              rv = -ENODEV;
++      }
++
++      return rv;
++}
++
++int vlan_dev_get_vid(const char *dev_name, unsigned short* result)
++{
++      struct net_device *dev = dev_get_by_name(dev_name);
++      int rv = 0;
++      
++      if (dev) {
++              if (dev->priv_flags & IFF_802_1Q_VLAN) {
++                      *result = VLAN_DEV_INFO(dev)->vlan_id;
++                      dev_put(dev);
++                      rv = 0;
++              } else {
++                      printk(KERN_ERR 
++                             "%s: %s is not a vlan device, priv_flags: %hX.\n",
++                             __FUNCTION__, dev->name, dev->priv_flags);
++                      dev_put(dev);
++                      rv = -EINVAL;
++              }
++      } else {
++              printk(KERN_ERR  "%s: Could not find device: %s\n", 
++                      __FUNCTION__, dev_name);
++              rv = -ENODEV;
++      }
++
++      return rv;
++}
++
+ int vlan_dev_set_mac_address(struct net_device *dev, void *addr_struct_p)
+ {
+       struct sockaddr *addr = (struct sockaddr *)(addr_struct_p);
+@@ -671,7 +724,7 @@
+ }
+ static inline int vlan_dmi_equals(struct dev_mc_list *dmi1,
+-                                  struct dev_mc_list *dmi2)
++                                struct dev_mc_list *dmi2)
+ {
+       return ((dmi1->dmi_addrlen == dmi2->dmi_addrlen) &&
+               (memcmp(dmi1->dmi_addr, dmi2->dmi_addr, dmi1->dmi_addrlen) == 0));
+--- linux-2.4.21/net/8021q/vlan.c      2003-06-13 07:51:39.000000000 -0700
++++ linux-2.4.21.amds/net/8021q/vlan.c 2003-08-11 16:43:09.000000000 -0700
+@@ -1,13 +1,13 @@
+-/*
++/* -*- linux-c -*-
+  * INET               802.1Q VLAN
+  *            Ethernet-type device handling.
+  *
+  * Authors:   Ben Greear <greearb@candelatech.com>
+- *              Please send support related email to: vlan@scry.wanfear.com
+- *              VLAN Home Page: http://www.candelatech.com/~greear/vlan.html
++ *            Please send support related email to: vlan@scry.wanfear.com
++ *            VLAN Home Page: http://www.candelatech.com/~greear/vlan.html
+  * 
+  * Fixes:
+- *              Fix for packet capture - Nick Eggleston <nick@dccinc.com>;
++ *            Fix for packet capture - Nick Eggleston <nick@dccinc.com>;
+  *            Add HW acceleration hooks - David S. Miller <davem@redhat.com>;
+  *            Correct all the locking - David S. Miller <davem@redhat.com>;
+  *            Use hash table for VLAN groups - David S. Miller <davem@redhat.com>
+@@ -173,7 +173,7 @@
+       *pprev = grp->next;
+ }
+-/*  Find the protocol handler.  Assumes VID < VLAN_VID_MASK.
++/*  Find the protocol handler.        Assumes VID < VLAN_VID_MASK.
+  *
+  * Must be invoked with vlan_group_lock held.
+  */
+@@ -183,7 +183,7 @@
+       struct vlan_group *grp = __vlan_find_group(real_dev->ifindex);
+       if (grp)
+-                return grp->vlan_devices[VID];
++              return grp->vlan_devices[VID];
+       return NULL;
+ }
+@@ -270,7 +270,7 @@
+               }
+       }
+-        return ret;
++      return ret;
+ }
+ static int unregister_vlan_device(const char *vlan_IF_name)
+@@ -655,17 +655,14 @@
+ int vlan_ioctl_handler(unsigned long arg)
+ {
+       int err = 0;
++      unsigned short vid = 0;
+       struct vlan_ioctl_args args;
+-      /* 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 (copy_from_user(&args, (void*)arg,
+-                           sizeof(struct vlan_ioctl_args)))
++                         sizeof(struct vlan_ioctl_args)))
+               return -EFAULT;
+       /* Null terminate this sucker, just in case. */
+@@ -678,24 +675,32 @@
+       switch (args.cmd) {
+       case SET_VLAN_INGRESS_PRIORITY_CMD:
++              if (!capable(CAP_NET_ADMIN))
++                      return -EPERM;
+               err = vlan_dev_set_ingress_priority(args.device1,
+                                                   args.u.skb_priority,
+                                                   args.vlan_qos);
+               break;
+       case SET_VLAN_EGRESS_PRIORITY_CMD:
++              if (!capable(CAP_NET_ADMIN))
++                      return -EPERM;
+               err = vlan_dev_set_egress_priority(args.device1,
+                                                  args.u.skb_priority,
+                                                  args.vlan_qos);
+               break;
+       case SET_VLAN_FLAG_CMD:
++              if (!capable(CAP_NET_ADMIN))
++                      return -EPERM;
+               err = vlan_dev_set_vlan_flag(args.device1,
+                                            args.u.flag,
+                                            args.vlan_qos);
+               break;
+       case SET_VLAN_NAME_TYPE_CMD:
++              if (!capable(CAP_NET_ADMIN))
++                      return -EPERM;
+               if ((args.u.name_type >= 0) &&
+                   (args.u.name_type < VLAN_NAME_TYPE_HIGHEST)) {
+                       vlan_name_type = args.u.name_type;
+@@ -705,17 +710,9 @@
+               }
+               break;
+-              /* TODO:  Figure out how to pass info back...
+-                 case GET_VLAN_INGRESS_PRIORITY_IOCTL:
+-                 err = vlan_dev_get_ingress_priority(args);
+-                 break;
+-
+-                 case GET_VLAN_EGRESS_PRIORITY_IOCTL:
+-                 err = vlan_dev_get_egress_priority(args);
+-                 break;
+-              */
+-
+       case ADD_VLAN_CMD:
++              if (!capable(CAP_NET_ADMIN))
++                      return -EPERM;
+               /* 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
+@@ -728,12 +725,53 @@
+               break;
+       case DEL_VLAN_CMD:
++              if (!capable(CAP_NET_ADMIN))
++                      return -EPERM;
+               /* Here, the args.dev1 is the actual VLAN we want
+                * to get rid of.
+                */
+               err = unregister_vlan_device(args.device1);
+               break;
++      case GET_VLAN_INGRESS_PRIORITY_CMD:
++              /* TODO:  Implement
++              err = vlan_dev_get_ingress_priority(args);
++              if (copy_to_user((void*)arg, &args,
++                               sizeof(struct vlan_ioctl_args))) {
++                      err = -EFAULT;
++              }
++              */
++              err = -EINVAL;
++              break;
++
++      case GET_VLAN_EGRESS_PRIORITY_CMD:
++              /* TODO:  Implement
++              err = vlan_dev_get_egress_priority(args.device1, &(args.args);
++              if (copy_to_user((void*)arg, &args,
++                               sizeof(struct vlan_ioctl_args))) {
++                      err = -EFAULT;
++              }
++              */
++              err = -EINVAL;
++              break;
++
++      case GET_VLAN_REALDEV_NAME_CMD:
++              err = vlan_dev_get_realdev_name(args.device1, args.u.device2);
++              if (copy_to_user((void*)arg, &args,
++                               sizeof(struct vlan_ioctl_args))) {
++                      err = -EFAULT;
++              }
++              break;
++
++      case GET_VLAN_VID_CMD:
++              err = vlan_dev_get_vid(args.device1, &vid);
++              args.u.VID = vid;
++              if (copy_to_user((void*)arg, &args,
++                               sizeof(struct vlan_ioctl_args))) {
++                      err = -EFAULT;
++              }
++              break;
++
+       default:
+               /* pass on to underlying device instead?? */
+               printk(VLAN_DBG "%s: Unknown VLAN CMD: %x \n",
+--- linux-2.4.21/net/8021q/vlan.h      2002-08-02 17:39:46.000000000 -0700
++++ linux-2.4.21.amds/net/8021q/vlan.h 2003-08-13 16:29:30.000000000 -0700
+@@ -72,6 +72,8 @@
+ int vlan_dev_set_ingress_priority(char* dev_name, __u32 skb_prio, short vlan_prio);
+ int vlan_dev_set_egress_priority(char* dev_name, __u32 skb_prio, short vlan_prio);
+ int vlan_dev_set_vlan_flag(char* dev_name, __u32 flag, short flag_val);
++int vlan_dev_get_realdev_name(const char* dev_name, char* result);
++int vlan_dev_get_vid(const char* dev_name, unsigned short* result);
+ void vlan_dev_set_multicast_list(struct net_device *vlan_dev);
+ #endif /* !(__BEN_VLAN_802_1Q_INC__) */
+--- linux-2.4.21/include/linux/if_vlan.h       2002-11-28 15:53:15.000000000 -0800
++++ linux-2.4.21.amds/include/linux/if_vlan.h  2003-08-13 16:27:39.000000000 -0700
+@@ -212,7 +212,9 @@
+       GET_VLAN_INGRESS_PRIORITY_CMD,
+       GET_VLAN_EGRESS_PRIORITY_CMD,
+       SET_VLAN_NAME_TYPE_CMD,
+-      SET_VLAN_FLAG_CMD
++      SET_VLAN_FLAG_CMD,
++        GET_VLAN_REALDEV_NAME_CMD, /* If this works, you know it's a VLAN device, btw */
++        GET_VLAN_VID_CMD /* Get the VID of this VLAN (specified by name) */
+ };
+ enum vlan_name_types {
+--- linux-2.4.21/include/linux/ethtool.h       2003-06-13 07:51:38.000000000 -0700
++++ linux-2.4.21.amds/include/linux/ethtool.h  2003-07-30 16:20:41.000000000 -0700
+@@ -250,6 +250,12 @@
+       u64     data[0];
+ };
++/* for dumping net-device statistics */
++struct ethtool_ndstats {
++      u32     cmd;            /* ETHTOOL_GNDSTATS */
++      u8      data[0];        /* sizeof(struct net_device_stats) */
++};
++
+ /* CMDs currently supported */
+ #define ETHTOOL_GSET          0x00000001 /* Get settings. */
+ #define ETHTOOL_SSET          0x00000002 /* Set settings, privileged. */
+@@ -281,6 +287,7 @@
+ #define ETHTOOL_GSTRINGS      0x0000001b /* get specified string set */
+ #define ETHTOOL_PHYS_ID               0x0000001c /* identify the NIC */
+ #define ETHTOOL_GSTATS                0x0000001d /* get NIC-specific statistics */
++#define ETHTOOL_GNDSTATS              0x0000001e /* get standard net-device statistics */
+ /* compatibility with older code */
+ #define SPARC_ETH_GSET                ETHTOOL_GSET
+--- linux-2.4.21/Documentation/CodingStyle     2001-09-09 16:40:43.000000000 -0700
++++ linux-2.4.21.amds/Documentation/CodingStyle        2003-08-05 20:51:17.000000000 -0700
+@@ -184,6 +184,8 @@
+   (interactive)
+   (c-mode)
+   (c-set-style "K&R")
++  (setq tab-width 8)
++  (setq       indent-tabs-mode t)
+   (setq c-basic-offset 8))
+ This will define the M-x linux-c-mode command.  When hacking on a
+--- linux-2.4.21/include/linux/proc_fs.h       2002-08-02 17:39:45.000000000 -0700
++++ linux-2.4.21.amds/include/linux/proc_fs.h  2003-08-13 16:47:29.000000000 -0700
+@@ -25,7 +25,8 @@
+ /* Finally, the dynamically allocatable proc entries are reserved: */
+ #define PROC_DYNAMIC_FIRST 4096
+-#define PROC_NDYNAMIC      4096
++#define PROC_NDYNAMIC      8192 /* was 4096 previously, but was running out of
++                                 * slots when creating lots of VLANs --Ben */
+ #define PROC_SUPER_MAGIC 0x9fa0
diff --git a/contrib/CVS/Entries b/contrib/CVS/Entries
new file mode 100644 (file)
index 0000000..2e0a86d
--- /dev/null
@@ -0,0 +1,5 @@
+/vlan_2.2-full.patch/1.1/Mon Jun 18 22:31:13 2001//
+/vlan_2.2-module.patch/1.1/Mon Jun 18 22:31:13 2001//
+/README/1.2/Tue Sep 30 23:21:29 2003//
+/network/1.1/Tue Sep 30 23:19:45 2003//
+D
diff --git a/contrib/CVS/Repository b/contrib/CVS/Repository
new file mode 100644 (file)
index 0000000..54d399b
--- /dev/null
@@ -0,0 +1 @@
+vlan/contrib
diff --git a/contrib/CVS/Root b/contrib/CVS/Root
new file mode 100644 (file)
index 0000000..469c859
--- /dev/null
@@ -0,0 +1 @@
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
diff --git a/contrib/README b/contrib/README
new file mode 100644 (file)
index 0000000..5af786d
--- /dev/null
@@ -0,0 +1,20 @@
+Here lies contributions from the community at large that for one
+reason or another (laziness on my part, and lack of testing, are
+valid reasons!), are not in the main distribution.
+
+If any of these seem particularly stable or useful, let me
+know and I will consider adding them to the main patch.
+
+Descriptions:
+network
+  "Thanks for the great vconfig patch/tool.  I submit to you a minor
+  change to the RedHat 7.3 /etc/rc.d/init.d/network script that
+  recognizes VLAN interfaces /etc/sysconfig/network-scripts/ifcfg-ethx.y , 
+  and makes appropriate calls to vconfig prior to bringing the
+  interface up.  This makes startup more straightforward,
+  and may be of use to your website visitors.
+  -Derek"
+
+
+
+ Thanks, Ben Greear (greearb@candelatech.com)
diff --git a/contrib/network b/contrib/network
new file mode 100644 (file)
index 0000000..48e4240
--- /dev/null
@@ -0,0 +1,288 @@
+#! /bin/bash
+#
+# network       Bring up/down networking
+#
+# chkconfig: 2345 10 90
+# description: Activates/Deactivates all network interfaces configured to \
+#              start at boot time.
+# probe: true
+### BEGIN INIT INFO
+# Provides: $network
+### END INIT INFO
+
+# Source function library.
+
+. /etc/init.d/functions
+
+if [ ! -f /etc/sysconfig/network ]; then
+    exit 0
+fi
+
+. /etc/sysconfig/network
+
+if [ -f /etc/sysconfig/pcmcia ]; then
+       . /etc/sysconfig/pcmcia
+fi
+
+
+# Check that networking is up.
+[ "${NETWORKING}" = "no" ] && exit 0
+
+# if the ip configuration utility isn't around we can't function.
+[ -x /sbin/ip ] || exit 1
+
+# Even if IPX is configured, without the utilities we can't do much
+[ ! -x /sbin/ipx_internal_net -o ! -x /sbin/ipx_configure ] && IPX=
+
+# If IPv6 is explicitly configured, make sure it's available.
+if [ "$NETWORKING_IPV6" = "yes" ]; then
+       alias=`modprobe -c | awk '/^alias net-pf-10 / { print $3 }'`
+       if [ "$alias" != "ipv6" -a ! -f /proc/net/if_inet6 ]; then
+               echo "alias net-pf-10 ipv6" >> /etc/modules.conf
+       fi
+fi
+
+CWD=`pwd`
+cd /etc/sysconfig/network-scripts
+
+. network-functions
+
+# find all the interfaces besides loopback.
+# ignore aliases, alternative configurations, and editor backup files
+interfaces=`ls ifcfg* | LANG=C egrep -v '(ifcfg-lo|:|\.|rpmsave|rpmorig|rpmnew)' | \
+           LANG=C egrep -v '(~|\.bak)$' | \
+            LANG=C egrep 'ifcfg-[A-Za-z0-9_-]+$' | \
+            sed 's/^ifcfg-//g'`
+
+vlan_interfaces=`ls ifcfg-eth?.?* | \
+           LANG=C egrep -v '(~|\.bak)$' | \
+            LANG=C egrep 'ifcfg-[A-Za-z0-9_-\.]+$' | \
+            sed 's/^ifcfg-//g'`
+
+# See how we were called.
+case "$1" in
+  start)
+       # IPv6 hook (pre IPv4 start)
+       if [ "$NETWORKING_IPV6" = "yes" ]; then
+               if [ -x /etc/sysconfig/network-scripts/init.ipv6-global ]; then
+                       /etc/sysconfig/network-scripts/init.ipv6-global start pre
+               fi
+       fi
+  
+       action $"Setting network parameters: " sysctl -e -p /etc/sysctl.conf
+
+       # bring up loopback interface
+       action $"Bringing up loopback interface: " ./ifup ifcfg-lo
+
+       case "$IPX" in
+         yes|true)
+           /sbin/ipx_configure --auto_primary=$IPXAUTOPRIMARY \
+                                  --auto_interface=$IPXAUTOFRAME
+           if [ "$IPXINTERNALNETNUM" != "0" ]; then
+              /sbin/ipx_internal_net add $IPXINTERNALNETNUM $IPXINTERNALNODENUM
+           fi
+           ;;
+       esac
+
+       oldhotplug=`sysctl kernel.hotplug  2>/dev/null | \
+           awk '{ print $3 }' 2>/dev/null`
+       sysctl -w kernel.hotplug="/bin/true" > /dev/null 2>&1
+
+        cipeinterfaces=""
+
+       # bring up all other interfaces configured to come up at boot time
+       for i in $interfaces; do
+               eval $(fgrep "DEVICE=" ifcfg-$i)
+               if [ -z "$DEVICE" ] ; then DEVICE="$i"; fi
+
+               if [ "${DEVICE##cipcb}" != "$DEVICE" ] ; then
+                       cipeinterfaces="$cipeinterfaces $DEVICE"
+                       continue
+               fi
+               if LANG=C egrep -L "^ONBOOT=\"?[Nn][Oo]\"?" ifcfg-$i > /dev/null ; then
+                       # this loads the module, to preserve ordering
+                       is_available $i
+                       continue
+               fi
+               # If we're in confirmation mode, get user confirmation
+               [ -n "$CONFIRM" ]  && 
+                       { 
+                           confirm $i
+                           case $? in
+                               0)
+                                   :
+                               ;;
+                               2)
+                                   CONFIRM=
+                               ;;
+                               *)
+                                   continue
+                               ;;
+                           esac 
+               }
+
+               action $"Bringing up interface $i: " ./ifup $i boot
+       done
+       
+       # bring up vlan interfaces configured to come up at boot time
+       for i in $vlan_interfaces; do
+               eval $(fgrep "DEVICE=" ifcfg-$i)
+               if [ -z "$DEVICE" ] ; then DEVICE="$i"; fi
+
+               if LANG=C egrep -L "^ONBOOT=\"?[Nn][Oo]\"?" ifcfg-$i > /dev/null ; then
+                       # this loads the module, to preserve ordering
+                       is_available $i
+                       continue
+               fi
+               # If we're in confirmation mode, get user confirmation
+               [ -n "$CONFIRM" ]  && 
+                       { 
+                           confirm $i
+                           case $? in
+                               0)
+                                   :
+                               ;;
+                               2)
+                                   CONFIRM=
+                               ;;
+                               *)
+                                   continue
+                               ;;
+                           esac 
+               }
+               /usr/local/bin/vconfig add `echo $i | tr '.' ' '`
+               action $"Bringing up interface $i: " ./ifup $i boot
+       done
+
+       # Bring up CIPE VPN interfaces
+       for i in $cipeinterfaces ; do 
+            if ! LANG=C egrep -L "^ONBOOT=\"?[Nn][Oo]\"?" ifcfg-$i >/dev/null 2>&1 ; then
+               # If we're in confirmation mode, get user confirmation
+               [ -n "$CONFIRM" ]  && 
+               { 
+                       confirm $i
+                       case $? in
+                               0)
+                                   :
+                               ;;
+                               2)
+                                   CONFIRM=
+                               ;;
+                               *)
+                                   continue
+                               ;;
+                       esac 
+               }
+               action $"Bringing up interface $i: " ./ifup $i boot
+           fi
+        done
+
+       sysctl -w kernel.hotplug=$oldhotplug > /dev/null 2>&1
+       
+       # Add non interface-specific static-routes.
+       if [ -f /etc/sysconfig/static-routes ]; then
+          grep "^any" /etc/sysconfig/static-routes | while read ignore args ; do
+              /sbin/route add -$args
+          done
+       fi    
+
+       # IPv6 hook (post IPv4 start)
+       if [ "$NETWORKING_IPV6" = "yes" ]; then
+               if [ -x /etc/sysconfig/network-scripts/init.ipv6-global ]; then
+                       /etc/sysconfig/network-scripts/init.ipv6-global start post
+               fi
+       fi
+       # Run this again to catch any interface-specific actions
+       sysctl -e -p /etc/sysctl.conf >/dev/null 2>&1
+        touch /var/lock/subsys/network
+        ;;
+  stop)
+       # If this is a final shutdown/halt, check for network FS,
+       # and unmount them even if the user didn't turn on netfs
+       
+       if [ "$RUNLEVEL" = "6" -o "$RUNLEVEL" = "0" -o "$RUNLEVEL" = "1" ]; then
+               NFSMTAB=`grep -v '^#' /proc/mounts | awk '{ if ($3 ~ /^nfs$/ ) print $2}'`
+               SMBMTAB=`grep -v '^#' /proc/mounts | awk '{ if ($3 ~ /^smbfs$/ ) print $2}'`
+               NCPMTAB=`grep -v '^#' /proc/mounts | awk '{ if ($3 ~ /^ncpfs$/ ) print $2}'`
+               if [ -n "$NFSMTAB" -o -n "$SMBMTAB" -o -n "$NCPMTAB" ] ; then
+                       /etc/init.d/netfs stop
+               fi
+       fi
+       
+       # IPv6 hook (pre IPv4 stop)
+       if [ "$NETWORKING_IPV6" = "yes" ]; then
+               if [ -x /etc/sysconfig/network-scripts/init.ipv6-global ]; then
+                       /etc/sysconfig/network-scripts/init.ipv6-global stop pre
+               fi
+       fi
+       # shut down all interfaces (other than loopback)
+       for i in $interfaces ; do
+               eval $(fgrep "DEVICE=" ifcfg-$i)
+               if [ -z "$DEVICE" ] ; then DEVICE="$i"; fi
+
+               if ! check_device_down $i; then
+                  action $"Shutting down interface $i: " ./ifdown $i boot
+               fi
+       done
+       # VLAN
+       for i in $vlan_interfaces ; do
+               eval $(fgrep "DEVICE=" ifcfg-$i)
+               if [ -z "$DEVICE" ] ; then DEVICE="$i"; fi
+
+               if [ -f /proc/net/vlan/$i ]; then
+                  action $"Removing vlan interface interface $i: " /usr/local/bin/vconfig rem $i
+               fi
+       done
+
+       case "$IPX" in
+         yes|true)
+           if [ "$IPXINTERNALNETNUM" != "0" ]; then
+              /sbin/ipx_internal_net del
+           fi
+           ;;
+       esac
+
+       action $"Shutting down loopback interface: " ./ifdown ifcfg-lo
+
+       if [ -d /proc/sys/net/ipv4 ]; then
+         if [ -f /proc/sys/net/ipv4/ip_forward ]; then
+               if [ `cat /proc/sys/net/ipv4/ip_forward` != 0 ]; then
+                       action $"Disabling IPv4 packet forwarding: " sysctl -w net.ipv4.ip_forward=0
+               fi
+         fi
+         if [ -f /proc/sys/net/ipv4/ip_always_defrag ]; then
+               if [ `cat /proc/sys/net/ipv4/ip_always_defrag` != 0 ]; then
+                       action $"Disabling IPv4 automatic defragmentation: " sysctl -w net.ipv4.ip_always_defrag=0
+               fi
+         fi
+       fi
+
+       # IPv6 hook (post IPv4 stop)
+       if [ "$NETWORKING_IPV6" = "yes" ]; then
+               if [ -x /etc/sysconfig/network-scripts/init.ipv6-global ]; then
+                       /etc/sysconfig/network-scripts/init.ipv6-global stop post
+               fi
+       fi
+       
+        rm -f /var/lock/subsys/network
+        ;;
+  status)
+       echo $"Configured devices:"
+       echo lo $interfaces
+
+       echo $"Currently active devices:"
+       echo `/sbin/ip -o link show | awk -F ": " '{ print $2 }'`
+       ;;
+  restart|reload)
+        cd $CWD
+       $0 stop
+       $0 start
+       ;;
+  *)
+        echo $"Usage: $0 {start|stop|restart|reload|status}"
+        exit 1
+esac
+
+exit 0
diff --git a/contrib/vlan_2.2-full.patch b/contrib/vlan_2.2-full.patch
new file mode 100644 (file)
index 0000000..4ae3012
--- /dev/null
@@ -0,0 +1,2906 @@
+diff -Nurb linux/include/linux/if_ether.h linux.p/include/linux/if_ether.h
+--- linux/include/linux/if_ether.h     Sun Mar 25 18:31:03 2001
++++ linux.p/include/linux/if_ether.h   Mon Jun  4 16:10:17 2001
+@@ -32,6 +32,33 @@
+ #define ETH_DATA_LEN  1500            /* Max. octets in payload        */
+ #define ETH_FRAME_LEN 1514            /* Max. octets in frame sans FCS */
++
++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE))
++
++#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 /* CONFIG_VLAN_802_1Q ... */
++
+ /*
+  *    These are the defined Ethernet Protocol ID's.
+  */
+@@ -54,6 +81,7 @@
+ #define ETH_P_RARP      0x8035                /* Reverse Addr Res packet      */
+ #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           */
+ #define ETH_P_ATMMPOA 0x884c          /* MultiProtocol Over ATM       */
+diff -Nurb linux/include/linux/if_vlan.h linux.p/include/linux/if_vlan.h
+--- linux/include/linux/if_vlan.h      Thu Jan  1 01:00:00 1970
++++ linux.p/include/linux/if_vlan.h    Mon Jun  4 16:18:26 2001
+@@ -0,0 +1,238 @@
++/* -*- linux-c -*-
++ * VLAN               An implementation of 802.1Q VLAN tagging.
++ *
++ * Version:   0.0.1   03/06/99
++ *
++ * Authors:   Ben Greear <greearb@candelatech.com>
++ *
++ *            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 device;
++struct sk_buff;
++struct packet_type;
++struct vlan_collection;
++
++
++#include <linux/proc_fs.h> /* for proc_dir_entry */
++
++
++
++/*  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 device *find_802_1Q_vlan_dev(struct device* real_dev,
++                                    unsigned short VID); /* vlan.c */
++
++
++int register_netdevice(struct device *dev); /* found in dev.c */
++int unregister_netdevice(struct device *dev); /* found in dev.c */
++int dev_new_index(void); /* dev.c */
++
++/* found in vlan_dev.c */
++struct net_device_stats* vlan_dev_get_stats(struct device* dev);
++int vlan_dev_rebuild_header(struct sk_buff *skb);
++int vlan_dev_type_trans(struct sk_buff *skb, struct device *dev,
++                        struct packet_type* ptype);
++int vlan_dev_hard_header(struct sk_buff *skb, struct device *dev,
++                         unsigned short type, void *daddr, void *saddr,
++                         unsigned len);
++int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct device *dev);
++int vlan_dev_change_mtu(struct device *dev, int new_mtu);
++int vlan_dev_set_mac_address(struct device *dev, void* addr);
++int vlan_dev_open(struct device* dev);
++int vlan_dev_stop(struct device* dev);
++int vlan_dev_init(struct device* dev);
++void vlan_dev_destruct(struct device* dev);
++int vlan_dev_set_vlan_flag(char* dev_name, __u32 flag, short flag_val);
++/*  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_ingress_priority(char* dev_name, __u32 skb_prio, short vlan_prio);
++int vlan_dev_set_egress_priority(char* dev_name, __u32 skb_prio, short vlan_prio);
++
++/* VLAN multicast stuff */
++/* Delete all of the MC list entries from this vlan device.  Also deals
++ * with the underlying device...
++ */
++void vlan_flush_mc_list(struct device* dev);
++/* copy the mc_list into the vlan_info structure. */
++void vlan_copy_mc_list(struct dev_mc_list* mc_list, struct vlan_dev_info* vlan_info);
++/** dmi is a single entry into a dev_mc_list, a single node.  mc_list is
++ *  an entire list, and we'll iterate through it.
++ */
++int vlan_should_add_mc(struct dev_mc_list *dmi, struct dev_mc_list *mc_list);
++/** Taken from Gleb + Lennert's VLAN code, and modified... */
++void vlan_dev_set_multicast_list(struct device *vlan_dev);
++
++
++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 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;
++
++
++#define VLAN_NAME "vlan"
++
++/* 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 {
++        int real_dev_ifindex; /* The index of the ethernet(like?) device the vlan is attached to. */
++        struct device* vlan_devices[VLAN_GROUP_ARRAY_LEN];
++   
++        struct vlan_group* next; /* the next in the list */
++};
++
++
++/* __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
++
++
++struct vlan_priority_tci_mapping {
++        unsigned long priority;
++        unsigned short vlan_qos; /* This should be shifted when first set, so we only do it
++                                  * at provisioning time.
++                                  * ((skb->priority << 13) & 0xE000)
++                                  */
++        struct vlan_priority_tci_mapping* next;
++};
++
++/* Holds information that makes sense if this device is a VLAN device. */
++struct vlan_dev_info {
++        /** This will be the mapping that correlates skb->priority to
++         * 3 bits of VLAN QOS tags...
++         */
++        unsigned long ingress_priority_map[8];
++        struct vlan_priority_tci_mapping* egress_priority_map[16]; /* hash table */
++
++        unsigned short vlan_id;        /*  The VLAN Identifier for this interface. */
++        unsigned short flags;          /* (1 << 0) re_order_header   This option will cause the
++                                        *   VLAN code to move around the ethernet header on
++                                        *   ingress to make the skb look **exactly** like it
++                                        *   came in from an ethernet port.  This destroys some of
++                                        *   the VLAN information in the skb, but it fixes programs
++                                        *   like DHCP that use packet-filtering and don't understand
++                                        *   802.1Q
++                                        */
++        struct dev_mc_list* old_mc_list;  /* old multi-cast list for the VLAN interface..
++                                           * we save this so we can tell what changes were
++                                           * made, in order to feed the right changes down
++                                           * to the real hardware...
++                                           */
++        int old_allmulti;               /* similar to above. */
++        int old_promiscuity;            /* similar to above. */
++        struct device* real_dev;        /* the underlying device/interface */
++        struct proc_dir_entry dent;    /* Holds the proc data */
++        unsigned long cnt_inc_headroom_on_tx; /* How many times did we have to grow the skb on TX. */
++        unsigned long cnt_encap_on_xmit;      /* How many times did we have to encapsulate the skb on TX. */
++};
++
++static inline unsigned short vlan_dev_get_egress_qos_mask(struct device* dev, struct sk_buff* skb) {
++        struct vlan_priority_tci_mapping* mp = dev->vlan_dev->egress_priority_map[(skb->priority & 0xF)];
++        while (mp) {
++                if (mp->priority == skb->priority) {
++                        return mp->vlan_qos; /* This should already be shifted to mask correctly with
++                                              * the VLAN's TCI
++                                              */
++                }
++                mp = mp->next;
++        }
++        return 0;
++}
++
++static inline int vlan_dmi_equals(struct dev_mc_list *dmi1,
++                                  struct dev_mc_list *dmi2) {
++        return ((dmi1->dmi_addrlen == dmi2->dmi_addrlen) &&
++                (memcmp(dmi1->dmi_addr, dmi2->dmi_addr, dmi1->dmi_addrlen) == 0));
++}
++
++static inline void vlan_destroy_mc_list(struct dev_mc_list *mc_list) {
++        struct dev_mc_list *dmi = mc_list, *next;
++
++        while(dmi) {
++                next = dmi->next;
++                kfree(dmi);
++                dmi = next;
++        }
++}
++
++#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,
++        SET_INGRESS_PRIORITY_IOCTL,
++        SET_EGRESS_PRIORITY_IOCTL,
++        GET_INGRESS_PRIORITY_IOCTL,
++        GET_EGRESS_PRIORITY_IOCTL,
++        SET_NAME_TYPE_IOCTL,
++        SET_VLAN_FLAG_IOCTL
++}; /* vlan_ioctl enum */
++
++enum vlan_name_types {
++        VLAN_NAME_TYPE_PLUS_VID, /* Name will look like:  vlan0005 */
++        VLAN_NAME_TYPE_RAW_PLUS_VID, /* name will look like:  eth1.0005 */
++        VLAN_NAME_TYPE_PLUS_VID_NO_PAD, /* Name will look like:  vlan5 */
++        VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, /* Name will look like:  eth0.5 */
++        VLAN_NAME_TYPE_HIGHEST
++};
++
++struct vlan_ioctl_args {
++        char dev1[24];
++
++        union {
++                char dev2[24];
++                int VID;
++                unsigned long skb_priority;
++                unsigned long name_type;
++                unsigned long bind_type;
++                unsigned long flag; /* Matches vlan_dev_info flags */
++        } u;
++
++        short vlan_qos;   /* Can also be flag-value, 1 to set, 0 to clear. */
++};
++
++
++#endif
+diff -Nurb linux/include/linux/netdevice.h linux.p/include/linux/netdevice.h
+--- linux/include/linux/netdevice.h    Sun Mar 25 18:31:03 2001
++++ linux.p/include/linux/netdevice.h  Mon Jun  4 16:10:48 2001
+@@ -39,6 +39,10 @@
+ #endif
+ #endif
++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE))
++struct vlan_dev_info;
++#endif /* CONFIG_VLAN_802_1Q ... */
++
+ struct divert_blk;
+ /*
+@@ -53,7 +57,11 @@
+  */
+  
+ #if !defined(CONFIG_AX25) && !defined(CONFIG_AX25_MODULE) && !defined(CONFIG_TR)
++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE))
++#define LL_MAX_HEADER 36
++#else
+ #define LL_MAX_HEADER 32
++#endif /* CONFIG_VLAN_802_1Q ... */
+ #else
+ #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+ #define LL_MAX_HEADER 96
+@@ -155,11 +163,18 @@
+ {
+       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 */
++      unsigned short  hh_type;        /* protocol identifier, f.e ETH_P_IP
++                                         *  NOTE:  For VLANs, this will be the
++                                         *  encapsulated type. --BLG
++                                         */
+       int             (*hh_output)(struct sk_buff *skb);
+       rwlock_t        hh_lock;
+       /* cached hardware header; allow for machine alignment needs.        */
++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE)) /* 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 /* CONFIG_VLAN_802_1Q ... */
+ };
+@@ -319,6 +334,11 @@
+       /* Semi-private data. Keep it at the end of device struct. */
+       struct dst_entry        *fastpath[NETDEV_FASTROUTE_HMASK+1];
+ #endif
++
++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE))
++  /*  Holds information that makes sense if this device is a VLAN device. */
++    struct vlan_dev_info* vlan_dev;
++#endif /* CONFIG_VLAN_802_1Q ... */
+ #ifdef CONFIG_NET_DIVERT
+       /* this will get initialized at each interface type init routine */
+diff -Nurb linux/net/802_1Q/Makefile linux.p/net/802_1Q/Makefile
+--- linux/net/802_1Q/Makefile  Thu Jan  1 01:00:00 1970
++++ linux.p/net/802_1Q/Makefile        Mon Jun  4 16:08:04 2001
+@@ -0,0 +1,21 @@
++#
++# Makefile for the Linux 802.1q protocol 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
++O_OBJS   := vlan.o vlanproc.o vlan_dev.o
++
++ifeq ($(CONFIG_VLAN_802_1Q),m)
++M_OBJS  := $(O_TARGET)
++endif
++
++ifeq ($(CONFIG_SYSCTL),y)
++O_OBJS += sysctl_net_vlan.o
++endif
++
++include $(TOPDIR)/Rules.make
+diff -Nurb linux/net/802_1Q/sysctl_net_vlan.c linux.p/net/802_1Q/sysctl_net_vlan.c
+--- linux/net/802_1Q/sysctl_net_vlan.c Thu Jan  1 01:00:00 1970
++++ linux.p/net/802_1Q/sysctl_net_vlan.c       Mon Jun  4 16:08:04 2001
+@@ -0,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??
++ */
++
++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE))
++
++#include <linux/mm.h>
++#include <linux/sysctl.h>
++
++ctl_table ether_vlan_table[] = {
++      {0}
++};
++
++#endif /* CONFIG_VLAN_802_1Q ... */
+diff -Nurb linux/net/802_1Q/vlan.c linux.p/net/802_1Q/vlan.c
+--- linux/net/802_1Q/vlan.c    Thu Jan  1 01:00:00 1970
++++ linux.p/net/802_1Q/vlan.c  Mon Jun  4 17:46:31 2001
+@@ -0,0 +1,445 @@
++/*           -*- linux-c -*-
++ * 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 <greearb@candelatech.com>, <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/if_vlan.h>
++#include "vlan.h"
++#include "vlanproc.h"
++
++extern int register_netdevice(struct device *dev); /* found in dev.c */
++extern int unregister_netdevice(struct device *dev); /* found in dev.c */
++extern int dev_new_index(void); /* dev.c */
++
++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;
++
++static char vlan_fullname[] = "802.1Q VLAN Support";
++static unsigned int vlan_version = 1;
++static unsigned int vlan_release = 0;
++static char vlan_copyright[] = "(c) 2000 Ben Greear (GPL)";
++
++/** These may be changed at run-time through IOCTLs */
++unsigned short vlan_name_type = 0; /* determines interface naming scheme */
++unsigned long vlan_bad_proto_recvd = 0; /* Counter for how many NON-VLAN protos we've received on a VLAN. */
++
++
++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) {
++        vlan_proto_init(NULL);
++        return 0;
++}
++
++/*
++ *    Module 'remove' entry point.
++ *    o delete /proc/net/router directory and static entries.
++ */ 
++void cleanup_module (void) {
++        dev_remove_pack(&vlan_packet_type);
++        vlan_proc_cleanup();
++}
++
++#else
++
++
++/** Non-module init entry point. */
++__initfunc(void vlan_system_init(void)) {
++        /* 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);
++
++        /* 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);
++}
++
++
++
++/**  Will search linearly for now, based on device index.  Could
++ * hash, or directly link, this some day. --Ben
++ */
++struct vlan_group* vlan_find_group(int real_dev_ifindex) {
++        struct vlan_group* grp = NULL;
++
++        for (grp = p802_1Q_vlan_list;
++             ((grp != NULL) && (grp->real_dev_ifindex != real_dev_ifindex));
++             grp = grp->next) {
++#ifdef VLAN_DEBUG
++                printk(VLAN_DBG __FUNCTION__ ": grp_idx: %i  real_dev_idx: %i\n",
++                       grp->real_dev_ifindex, real_dev_ifindex);
++#endif
++           ;
++        } /* for */
++         
++        return grp;
++}
++
++/*  Find the protocol handler.  Assumes VID < 0xFFF.
++ */
++struct device *find_802_1Q_vlan_dev(struct device* real_dev, unsigned short VID) {
++
++        struct vlan_group* grp = vlan_find_group(real_dev->ifindex);
++
++#ifdef VLAN_DEBUG
++        printk(VLAN_DBG __FUNCTION__ ": idx: %i  grp: %p\n", real_dev->ifindex, grp);
++#endif
++
++        /*  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_dev */
++
++
++
++int unregister_802_1Q_vlan_dev(int real_dev_ifindex, unsigned short vlan_id) {
++        struct vlan_group* grp;
++        struct 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(real_dev_ifindex);
++        /*  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);
++         
++                        /* 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!!
++                         */
++                        unregister_netdevice(dev);
++                      MOD_DEC_USE_COUNT;
++
++                }/* if */
++        }/* if */
++        return 0;
++}/* unregister vlan device */
++
++
++
++int unregister_802_1Q_vlan_device(const char* vlan_IF_name) {
++        struct device* dev = NULL;
++
++#ifdef VLAN_DEBUG
++        printk(VLAN_DBG __FUNCTION__ ": unregister VLAN by name, name -:%s:-\n",
++               vlan_IF_name);
++#endif
++   
++        dev = dev_get(vlan_IF_name);
++
++        if (dev && dev->vlan_dev) {
++                return unregister_802_1Q_vlan_dev(dev->vlan_dev->real_dev->ifindex,
++                                                  (unsigned short)(dev->vlan_dev->vlan_id));
++        }
++        else {
++#ifdef VLAN_DEBUG
++                printk(VLAN_DBG __FUNCTION__ ": WARNING: Could not find dev\n");
++#endif
++                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 device *register_802_1Q_vlan_device(const char* eth_IF_name,
++                                           unsigned short VLAN_ID) {
++        struct vlan_group* grp;
++        struct device *new_dev;
++        struct device *real_dev; /* the ethernet device */
++        int malloc_size = 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(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"); */
++
++                        if (find_802_1Q_vlan_dev(real_dev, VLAN_ID)) {
++                                /* was already registered. */
++                                printk(VLAN_DBG __FUNCTION__ ": ALREADY had VLAN registered\n");
++                                return NULL;
++                        }
++
++                        malloc_size = (sizeof(struct device));
++
++                        new_dev = (struct device*) kmalloc(malloc_size, GFP_KERNEL);
++                        VLAN_MEM_DBG("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;
++            
++                                /* 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);
++                                        return NULL;
++                                }
++
++                                if (vlan_name_type == VLAN_NAME_TYPE_RAW_PLUS_VID) {
++                                        /* name will look like:  eth1.0005 */
++                                        sprintf(new_dev->name, "%s.%.4i", real_dev->name, VLAN_ID);
++                                }
++                                else if (vlan_name_type == VLAN_NAME_TYPE_PLUS_VID_NO_PAD) {
++                                        /* Put our vlan.VID in the name.  Name will look like:  vlan5 */
++                                        sprintf(new_dev->name, "vlan%i", VLAN_ID);
++                                }
++                                else if (vlan_name_type == VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD) {
++                                        /* Put our vlan.VID in the name.  Name will look like:  eth0.5 */
++                                        sprintf(new_dev->name, "%s.%i", real_dev->name, VLAN_ID);
++                                }
++                                else { /* (vlan_name_type == VLAN_NAME_TYPE_PLUS_VID) { */
++                                        /* Put our vlan.VID in the name.  Name will look like:  vlan0005 */
++                                        /* default case */
++                                        sprintf(new_dev->name, "vlan%.4i", VLAN_ID);
++                                }
++            
++            
++#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->set_mac_address = vlan_dev_set_mac_address;
++                                new_dev->set_multicast_list = vlan_dev_set_multicast_list;
++
++                                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);
++                                        return NULL;
++                                }
++                                else {
++                                        /* Initialize it. */
++                                        memset(new_dev->vlan_dev, 0, sizeof(struct vlan_dev_info));
++               
++                                        new_dev->vlan_dev->vlan_id = VLAN_ID; /* 1 through 0xFFF */
++                                        /* TODO: have to be careful deleting real devices now. */
++                                        new_dev->vlan_dev->real_dev = real_dev;
++
++                                        memset(&(new_dev->vlan_dev->dent), 0, sizeof(struct proc_dir_entry));
++                                }
++            
++                                /* So, got the sucker initialized, now lets place it into our local
++                                 * structure.
++                                 */
++
++                                grp = vlan_find_group(real_dev->ifindex);
++
++                                /*  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, idx: %i\n",
++                                                       real_dev->ifindex);
++                                                memset(grp, 0, sizeof(struct vlan_group));
++                                                grp->real_dev_ifindex = real_dev->ifindex;
++                                                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);
++                                                return NULL;
++                                        }
++                                }/* if */
++            
++                                grp->vlan_devices[VLAN_ID] = new_dev;
++
++                                /*  Now, add it to the global list of devices. */
++                                /* printk(KERN_ALERT "Registering new device."); */
++                                register_netdevice(new_dev);
++                                vlan_proc_add_dev(new_dev); /* create it's proc entry */
++                                MOD_INC_USE_COUNT; /* Add was a success!! */
++                                return new_dev;
++                        }
++                }//if
++        }//if
++
++        return NULL;
++}/* register (create) VLAN device */
+diff -Nurb linux/net/802_1Q/vlan.h linux.p/net/802_1Q/vlan.h
+--- linux/net/802_1Q/vlan.h    Thu Jan  1 01:00:00 1970
++++ linux.p/net/802_1Q/vlan.h  Mon Jun  4 16:18:27 2001
+@@ -0,0 +1,44 @@
++#ifndef __BEN_VLAN_802_1Q_INC__
++#define __BEN_VLAN_802_1Q_INC__
++
++#include <linux/if_vlan.h>
++
++/* If this is undefined, the name will look like:  vlan0005 */
++/* #define USE_RAW_IN_NAME   Use this one if you like it:  eth.0005 */
++
++/*  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_DEBUG /* 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 short vlan_name_type;
++extern unsigned long vlan_bad_proto_recvd; /* Counter for how many NON-VLAN protos we've received on a VLAN. */
++
++/* Add some headers for the public VLAN methods. */
++int unregister_802_1Q_vlan_device(const char* vlan_IF_name);
++struct 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 -Nurb linux/net/802_1Q/vlan_dev.c linux.p/net/802_1Q/vlan_dev.c
+--- linux/net/802_1Q/vlan_dev.c        Thu Jan  1 01:00:00 1970
++++ linux.p/net/802_1Q/vlan_dev.c      Mon Jun  4 16:08:04 2001
+@@ -0,0 +1,765 @@
++/* -*- linux-c -*-
++ * 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 <greearb@candelatech.com>, <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/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 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 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 device *dev,
++                        struct packet_type* ptype) {
++        unsigned char* rawp = NULL;
++        struct vlan_ethhdr *veth = (struct vlan_ethhdr*)(skb->mac.ethernet);
++        unsigned short vid = 0;
++        struct net_device_stats* stats;
++   
++        /* Do we have a VLAN packet?  If not, then throw it away, after printing an error.
++         *
++         */
++        if (veth->h_vlan_proto != __constant_htons(ETH_P_802_1Q)) {
++                printk(VLAN_INF __FUNCTION__ ": VLAN device received NON-VLAN protocol: %hx\n", htons(veth->h_vlan_proto));
++                vlan_bad_proto_recvd++;
++                kfree_skb(skb);
++                return -EINVAL;
++        }
++        else {
++                vid = ((unsigned short)(ntohs(veth->h_vlan_TCI)) & 0xFFF);
++        }
++
++#ifdef VLAN_DEBUG
++        printk(VLAN_DBG __FUNCTION__ ": skb: %p vlan_id: %hx dev: %s, encap_proto: %hx\n",
++               skb, vid, dev->name, veth->h_vlan_encapsulated_proto);
++#endif
++   
++        /*  Ok, we will find the correct VLAN device, strip the header,
++            and then go on as usual.
++        */
++        
++        /* we have 12 bits of vlan ID. */
++        /* If it's NULL, we will tag the skb to be junked below */
++        skb->dev = find_802_1Q_vlan_dev(dev, vid);
++
++        if (!skb->dev) {
++#ifdef VLAN_DEBUG
++                printk(VLAN_DBG __FUNCTION__ ": ERROR:  No device for VID: %i on dev: %s [%i]\n",
++                       (unsigned int)(vid), dev->name, dev->ifindex);
++#endif
++                kfree_skb(skb);
++                return -1;
++        }
++
++        stats = (struct net_device_stats*)(skb->dev->priv);
++
++        /*
++         * Deal with ingress priority mapping.
++         */
++        skb->priority = skb->dev->vlan_dev->ingress_priority_map[(ntohs(veth->h_vlan_TCI) >> 13) & 0x7];
++   
++#ifdef VLAN_DEBUG
++        printk(VLAN_DBG __FUNCTION__ ": priority: %lu  for TCI: %hu (hbo) on vlan_dev: %s\n",
++               (unsigned long)(skb->priority), ntohs(veth->h_vlan_TCI), skb->dev->name);
++#endif
++
++        /* Bump the rx counters for the VLAN device. */
++        stats->rx_packets++;
++        stats->rx_bytes += skb->len;
++      
++        /* NOTE: The underlying device SHOULD NOT PULL THE MAC BYTES OFF.
++           (it doesn't seem to.)
++        */
++        skb_pull(skb, VLAN_ETH_HLEN); /* 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;
++        }
++
++        /*  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.
++            */
++           
++           /* See if we are configured to re-write the VLAN header to make it look like
++            * ethernet...
++            */
++           if (skb->dev->vlan_dev->flags & 1) {
++                   /* Lifted from Gleb's VLAN code... */
++                   memmove(skb->data - (VLAN_ETH_HLEN - 4), skb->data - VLAN_ETH_HLEN, 12);
++                   skb->mac.raw += 4;
++           }
++           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.
++                 */
++      
++                /* See if we are configured to re-write the VLAN header to make it look like
++                 * ethernet...
++                 */
++                if (skb->dev->vlan_dev->flags & 1) {
++                        /* Lifted from Gleb's VLAN code... */
++                        memmove(skb->data - (VLAN_ETH_HLEN - 4), skb->data - VLAN_ETH_HLEN, 12);
++                        skb->mac.raw += 4;
++                }
++                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.
++         */
++
++        /* See if we are configured to re-write the VLAN header to make it look like
++         * ethernet...
++         */
++        if (skb->dev->vlan_dev->flags & 1) {
++                /* Lifted from Gleb's VLAN code... */
++                memmove(skb->data - (VLAN_ETH_HLEN - 4), skb->data - VLAN_ETH_HLEN, 12);
++                skb->mac.raw += 4;
++        }
++        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 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 |= vlan_dev_get_egress_qos_mask(dev, skb);
++   
++        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 device *dev) {
++        struct net_device_stats* stats = (struct net_device_stats*)(dev->priv);
++        struct vlan_ethhdr *veth = (struct vlan_ethhdr*)(skb->data);
++        
++                /* Handle non-VLAN frames if they are sent to us, for example by DHCP. */
++        if (veth->h_vlan_proto != __constant_htons(ETH_P_802_1Q)) {
++                /* This is not a VLAN frame...but we can fix that! */
++                unsigned short veth_TCI = 0;
++                dev->vlan_dev->cnt_encap_on_xmit++;
++                
++                if (skb_headroom(skb) < 4) {
++                        struct sk_buff* sk_tmp = skb;
++                        skb = skb_realloc_headroom(sk_tmp, 4);
++                        kfree_skb(sk_tmp);
++                        if (skb == NULL) {
++                                stats->tx_dropped++;
++                                kfree_skb(sk_tmp);
++                                return -ENOMEM;
++                        }
++                        dev->vlan_dev->cnt_inc_headroom_on_tx++;
++                }
++                else {
++                        if( !(skb = skb_unshare(skb, GFP_ATOMIC)) ) {
++                                printk(KERN_ERR "vlan: failed to unshare skbuff\n");
++                                stats->tx_dropped++;
++                                return -ENOMEM;
++                        }
++                }
++                veth = (struct vlan_ethhdr*)skb_push(skb, 4);
++
++                /* Move the mac addresses to the beginning of the new header. */
++                memmove(skb->data, skb->data + 4, 12);
++
++                /* 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 |= vlan_dev_get_egress_qos_mask(dev, skb);
++   
++                veth->h_vlan_TCI = htons(veth_TCI);
++        }/* If we needed to encapsulate the frame */
++
++        skb->dev = dev->vlan_dev->real_dev;
++
++
++#ifdef VLAN_DEBUG
++        printk(VLAN_DBG __FUNCTION__ ": about to send skb: %p  to dev: %s\n", skb, skb->dev->name);
++#endif
++
++        dev_queue_xmit(skb);
++        stats->tx_packets++; /* for statics only */
++        stats->tx_bytes += skb->len;
++        return 0;
++}/* vlan_dev_hard_start_xmit */
++
++
++int vlan_dev_change_mtu(struct 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 device* dev) {
++        dev->flags |= IFF_UP;
++        return 0;
++}
++
++int vlan_dev_stop(struct device* dev) {
++        dev->flags &= ~IFF_UP;
++        return 0;
++}
++
++int vlan_dev_init(struct device* dev) {
++        /* TODO:  figure this out, maybe do nothing?? */
++        return 0;
++}
++
++void vlan_dev_destruct(struct 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;
++   
++        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("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_set_ingress_priority(char* dev_name, __u32 skb_prio, short vlan_prio) {
++        struct device* dev = dev_get(dev_name);
++   
++        if (dev) {
++                if (dev->vlan_dev) { /* can't put a dflt ID on a vlan device */
++                        /* see if a priority mapping exists.. */
++                        dev->vlan_dev->ingress_priority_map[vlan_prio & 0x7] = skb_prio;
++                        return 0;
++                }
++        }
++        return -EINVAL;
++}
++
++int vlan_dev_set_egress_priority(char* dev_name, __u32 skb_prio, short vlan_prio) {
++        struct device* dev = dev_get(dev_name);
++        struct vlan_priority_tci_mapping* mp = NULL;
++        struct vlan_priority_tci_mapping* np;
++   
++        if (dev) {
++                if (dev->vlan_dev) { /* can't put a dflt ID on a vlan device */
++                        /* see if a priority mapping exists.. */
++                        mp = dev->vlan_dev->egress_priority_map[skb_prio & 0xF];
++                        while (mp) {
++                                if (mp->priority == skb_prio) {
++                                        mp->vlan_qos = ((vlan_prio << 13) & 0xE000);
++                                        return 0;
++                                }
++                        }
++                        /* create a new mapping then. */
++                        mp = dev->vlan_dev->egress_priority_map[skb_prio & 0xF];
++                        np = kmalloc(sizeof(struct vlan_priority_tci_mapping), GFP_KERNEL);
++                        if (np) {
++                                np->next = mp;
++                                np->priority = skb_prio;
++                                np->vlan_qos = ((vlan_prio << 13) & 0xE000);
++                                dev->vlan_dev->egress_priority_map[skb_prio & 0xF] = np;
++                                return 0;
++                        }
++                        else {
++                                return -ENOBUFS;
++                        }
++                }
++        }
++        return -EINVAL;
++}
++
++/* Flags are defined in the vlan_dev_info class in include/linux/if_vlan.h file. */
++int vlan_dev_set_vlan_flag(char* dev_name, __u32 flag, short flag_val) {
++        struct device* dev = dev_get(dev_name);
++   
++        if (dev) {
++                if (dev->vlan_dev) {
++                        /* verify flag is supported */
++                        if (flag == 1) {
++                                if (flag_val) {
++                                        dev->vlan_dev->flags |= 1;
++                                }
++                                else {
++                                        dev->vlan_dev->flags &= ~1;
++                                }
++                                return 0;
++                        }
++                        else {
++                                return -EINVAL;
++                        }
++                }/* if it's a vlan device */
++        }/* if we found the device */
++        return -EINVAL;
++}
++
++
++int vlan_dev_set_mac_address(struct device *dev, void* addr_struct_p) {
++        int i;
++        struct sockaddr *addr = (struct sockaddr*)(addr_struct_p);
++
++        if (dev->start) {
++                return -EBUSY;
++        }
++
++      memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
++        
++        printk("%s: Setting MAC address to ", dev->name);
++        for (i = 0; i < 6; i++) {
++                printk(" %2.2x", dev->dev_addr[i]);
++        }
++        printk(".\n");
++
++        if (memcmp(dev->vlan_dev->real_dev->dev_addr, dev->dev_addr, dev->addr_len) != 0) {
++                if (dev->vlan_dev->real_dev->flags & IFF_PROMISC) {
++                        /* Already promiscious...leave it alone. */
++                        printk("VLAN (%s):  Good, underlying device (%s) is already promiscious.\n",
++                               dev->name, dev->vlan_dev->real_dev->name);
++                }
++                else {
++                        int flgs = dev->vlan_dev->real_dev->flags;
++                        printk("VLAN (%s):  Setting underlying device (%s) to promiscious mode.\n",
++                               dev->name, dev->vlan_dev->real_dev->name);
++                        flgs |= IFF_PROMISC;
++                        dev_change_flags(dev->vlan_dev->real_dev, flgs);
++                        /*  This should work, but doesn't:
++                            dev_set_promiscuity(dev->vlan_dev->real_dev, 1);
++                        */
++                }
++        }
++        else {
++                printk("VLAN (%s):  Underlying device (%s) has same MAC, not checking promiscious mode.\n",
++                       dev->name, dev->vlan_dev->real_dev->name);
++        }
++        return 0;
++}
++
++
++/** Taken from Gleb + Lennert's VLAN code, and modified... */
++void vlan_dev_set_multicast_list(struct device *vlan_dev) {
++        struct dev_mc_list *dmi;
++        struct device *real_dev;
++        int inc;
++
++        if (vlan_dev && vlan_dev->vlan_dev) {
++                /* Then it's a real vlan device, as far as we can tell.. */
++                real_dev = vlan_dev->vlan_dev->real_dev;
++
++                /* compare the current promiscuity to the last promisc we had.. */
++                inc = vlan_dev->promiscuity - vlan_dev->vlan_dev->old_promiscuity;
++   
++                if (inc) {
++                        printk(KERN_INFO "vlan: dev_set_promiscuity(master, %d)\n", inc);
++                        dev_set_promiscuity(real_dev, inc); /* found in dev.c */
++                        vlan_dev->vlan_dev->old_promiscuity = vlan_dev->promiscuity;
++                }
++   
++                inc = vlan_dev->allmulti - vlan_dev->vlan_dev->old_allmulti;
++   
++                if (inc) {
++                        printk(KERN_INFO "vlan: dev_set_allmulti(master, %d)\n", inc);
++                        dev_set_allmulti(real_dev, inc); /* dev.c */
++                        vlan_dev->vlan_dev->old_allmulti = vlan_dev->allmulti;
++                }
++   
++                /* looking for addresses to add to master's list */
++                for (dmi = vlan_dev->mc_list; dmi!=NULL; dmi=dmi->next) {
++                        if (vlan_should_add_mc(dmi, vlan_dev->vlan_dev->old_mc_list)) {
++                                dev_mc_add(real_dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
++                                printk(KERN_INFO "vlan: add %.2x:%.2x:%.2x:%.2x:%.2x:%.2x mcast address to master interface\n",
++                                       dmi->dmi_addr[0],
++                                       dmi->dmi_addr[1],
++                                       dmi->dmi_addr[2],
++                                       dmi->dmi_addr[3],
++                                       dmi->dmi_addr[4],
++                                       dmi->dmi_addr[5]);
++                        }
++                }
++   
++                /* looking for addresses to delete from master's list */
++                for (dmi = vlan_dev->mc_list; dmi!=NULL; dmi=dmi->next) {
++                        if (vlan_should_add_mc(dmi, vlan_dev->mc_list)) {
++                                /* if we think we should add it to the new list, then we should really
++                                 * delete it from the real list on the underlying device.
++                                 */
++                                dev_mc_delete(real_dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
++                                printk(KERN_INFO "vlan: del %.2x:%.2x:%.2x:%.2x:%.2x:%.2x mcast address from master interface\n",
++                                       dmi->dmi_addr[0],
++                                       dmi->dmi_addr[1],
++                                       dmi->dmi_addr[2],
++                                       dmi->dmi_addr[3],
++                                       dmi->dmi_addr[4],
++                                       dmi->dmi_addr[5]);
++                        }
++                }
++   
++                /* save multicast list */
++                vlan_copy_mc_list(vlan_dev->mc_list, vlan_dev->vlan_dev);
++        }/* if we were sent a valid device */
++}/* vlan_dev_set_multicast */
++
++
++/** dmi is a single entry into a dev_mc_list, a single node.  mc_list is
++ *  an entire list, and we'll iterate through it.
++ */
++int vlan_should_add_mc(struct dev_mc_list *dmi, struct dev_mc_list *mc_list) {
++        struct dev_mc_list *idmi; /* iterator */
++
++        for (idmi=mc_list; idmi!=NULL;) {
++                if (vlan_dmi_equals(dmi, idmi)) {
++                        if (dmi->dmi_users > idmi->dmi_users)
++                                return 1;
++                        else
++                                return 0;
++                }
++                else {
++                        idmi = idmi->next;
++                }
++        }
++   
++        return 1;
++}
++
++
++void vlan_copy_mc_list(struct dev_mc_list *mc_list, struct vlan_dev_info *vlan_info) {
++        struct dev_mc_list *dmi, *new_dmi;
++   
++        vlan_destroy_mc_list(vlan_info->old_mc_list);
++        vlan_info->old_mc_list = NULL;
++
++        for (dmi=mc_list; dmi!=NULL; dmi=dmi->next) {
++                new_dmi = kmalloc(sizeof(*new_dmi), GFP_KERNEL);
++                if (new_dmi == NULL) {
++                        printk(KERN_ERR "vlan: cannot allocate memory. Multicast may not work properly from now.\n");
++                        return;
++                }
++      
++                new_dmi->next = vlan_info->old_mc_list;
++                vlan_info->old_mc_list = new_dmi;
++      
++                new_dmi->dmi_addrlen = dmi->dmi_addrlen;
++                memcpy(new_dmi->dmi_addr, dmi->dmi_addr, dmi->dmi_addrlen);
++                new_dmi->dmi_users = dmi->dmi_users;
++                new_dmi->dmi_gusers = dmi->dmi_gusers;
++        }
++}
++
++void vlan_flush_mc_list(struct device *dev) {
++        struct dev_mc_list *dmi = dev->mc_list;
++
++        while (dmi) {
++                dev_mc_delete(dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
++                printk(KERN_INFO "vlan: del %.2x:%.2x:%.2x:%.2x:%.2x:%.2x mcast address from master interface\n",
++                       dmi->dmi_addr[0],
++                       dmi->dmi_addr[1],
++                       dmi->dmi_addr[2],
++                       dmi->dmi_addr[3],
++                       dmi->dmi_addr[4],
++                       dmi->dmi_addr[5]);
++                dmi = dev->mc_list;
++        }
++   
++        vlan_destroy_mc_list(dev->mc_list);
++        if (dev->vlan_dev) {
++                vlan_destroy_mc_list(dev->vlan_dev->old_mc_list);
++                dev->vlan_dev->old_mc_list = NULL;
++        }
++        dev->mc_list = NULL;
++}/* vlan_flush_mc_list */
+diff -Nurb linux/net/802_1Q/vlanproc.c linux.p/net/802_1Q/vlanproc.c
+--- linux/net/802_1Q/vlanproc.c        Thu Jan  1 01:00:00 1970
++++ linux.p/net/802_1Q/vlanproc.c      Mon Jun  4 16:08:04 2001
+@@ -0,0 +1,654 @@
++/* * -*- linux-c -*- */
++/*****************************************************************************
++ * vlanproc.c VLAN Module. /proc filesystem interface.
++ *
++ * Author:    Ben Greear, <greearb@candelatech.com> coppied from wanproc.c
++ *               by: Gene Kozin       <genek@compuserve.com>
++ *
++ * Copyright: (c) 1998-2000 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,
++                                int dummy);
++static int vlandev_get_info(char* buf, char** start, off_t offs, int len,
++                            int dummy);
++
++
++/* 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";
++
++
++/*
++ *    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[23] = 0;
++        args.u.dev2[23] = 0;
++
++   /*
++   dent = inode->u.generic_ip;
++   if ((dent == NULL) || (dent->data == NULL))
++     return -EINVAL;
++
++   dev = dent->data;
++   */
++   
++        switch (cmd)
++        {
++        case SET_INGRESS_PRIORITY_IOCTL:
++                err = vlan_dev_set_ingress_priority(args.dev1, args.u.skb_priority, args.vlan_qos);
++                break;
++
++        case SET_EGRESS_PRIORITY_IOCTL:
++                err = vlan_dev_set_egress_priority(args.dev1, args.u.skb_priority, args.vlan_qos);
++                break;
++
++        case SET_VLAN_FLAG_IOCTL:
++                err = vlan_dev_set_vlan_flag(args.dev1, args.u.flag, args.vlan_qos);
++                break;
++
++        case SET_NAME_TYPE_IOCTL:
++                if ((args.u.name_type >= 0) && (args.u.name_type < VLAN_NAME_TYPE_HIGHEST)) {
++                        vlan_name_type = args.u.name_type;
++                        err = 0;
++                }
++                else {
++                        err = -EINVAL;
++                }
++                break;
++         
++                /* TODO:  Figure out how to pass info back...
++                   case GET_INGRESS_PRIORITY_IOCTL:
++                   err = vlan_dev_get_ingress_priority(args);
++                   break;
++
++                   case GET_EGRESS_PRIORITY_IOCTL:
++                   err = vlan_dev_get_egress_priority(args);
++                   break;
++                */
++         
++        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;
++
++        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
++ *    <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,                 /* follow link */
++        NULL,                 /* readlink */
++        NULL,                 /* readpage */
++        NULL,                 /* writepage */
++        NULL,                 /* bmap */
++        NULL,                 /* truncate */
++        vlan_proc_perms
++};
++
++/*
++ *    /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,                 /* readpage */
++        NULL,                 /* writepage */
++        NULL,                 /* bmap */
++        NULL,                 /* truncate */
++        vlan_proc_perms
++};
++
++
++/*
++ * Proc filesystem derectory entries.
++ */
++
++/*
++ *    /proc/net/vlan 
++ */
++
++static struct proc_dir_entry proc_vlan = {
++        0,                    /* .low_ino */
++        sizeof(name_root) - 1,        /* .namelen */
++        name_root,            /* .name */
++        0555 | S_IFDIR,               /* .mode */
++        2,                    /* .nlink */
++        0,                    /* .uid */
++        0,                    /* .gid */
++        0,                    /* .size */
++        &proc_dir_inode_operations, /* .ops */
++        NULL,                 /* .get_info */
++        NULL,                 /* .fill_node */
++        NULL,                 /* .next */
++        NULL,                 /* .parent */
++        NULL,                 /* .subdir */
++        NULL,                 /* .data */
++};
++
++/*
++ *    /proc/net/vlan/config 
++ */
++ 
++static struct proc_dir_entry proc_vlan_conf = {
++        0,                            /* .low_ino */
++        sizeof(name_conf) - 1,        /* .namelen */
++        name_conf,            /* .name */
++        0444 | S_IFREG,               /* .mode */
++        1,                    /* .nlink */
++        0,                            /* .uid */
++        0,                            /* .gid */
++        0,                    /* .size */
++        &vlan_inode,                  /* .ops */
++        &vlan_config_get_info,        /* .get_info */
++        NULL,                 /* .fill_node */
++        NULL,                 /* .next */
++        NULL,                 /* .parent */
++        NULL,                 /* .subdir */
++        NULL,                 /* .data */
++};
++
++
++/* Strings */
++static char conf_hdr[] = "VLAN Dev name  | VLAN ID\n";
++
++
++/*
++ *    Interface functions
++ */
++
++/*
++ *    Initialize vlan proc interface.
++ */
++
++__initfunc(int vlan_proc_init (void)) {
++        int err = proc_register(proc_net, &proc_vlan);
++
++        if (!err) {
++                proc_register(&proc_vlan, &proc_vlan_conf);
++        }
++        return err;
++}
++
++/*
++ *    Clean up router proc interface.
++ */
++
++void vlan_proc_cleanup (void) {
++        proc_unregister(&proc_vlan, proc_vlan_conf.low_ino);
++        proc_unregister(proc_net, proc_vlan.low_ino);
++}
++
++
++/*
++ *    Add directory entry for VLAN device.
++ */
++
++int vlan_proc_add_dev (struct device* vlandev) {
++        if (!vlandev->vlan_dev) {
++                printk(KERN_ERR "ERROR:  vlan_proc_add, device -:%s:- is NOT a VLAN\n",
++                       vlandev->name);
++                return -EINVAL;
++        }
++   
++        memset(&(vlandev->vlan_dev->dent), 0, sizeof(vlandev->vlan_dev->dent));
++        vlandev->vlan_dev->dent.namelen = strlen(vlandev->name);
++        vlandev->vlan_dev->dent.name = vlandev->name;
++        vlandev->vlan_dev->dent.mode = 0444 | S_IFREG;
++        vlandev->vlan_dev->dent.nlink = 1;
++        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 proc_register(&proc_vlan, &vlandev->vlan_dev->dent);
++}
++
++
++
++/*
++ *    Delete directory entry for VLAN device.
++ */
++int vlan_proc_rem_dev(struct device* vlandev) {
++        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__ ": calling proc_unregister for dev: %p\n",
++               vlandev);
++#endif
++        return proc_unregister(&proc_vlan, vlandev->vlan_dev->dent.low_ino);
++}
++
++
++/****** 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, 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 device* vlandev = NULL;
++        struct vlan_group* grp = NULL;
++        int i = 0;
++        char* nm_type = NULL;
++   
++        printk(VLAN_DBG __FUNCTION__ ": cnt == %i\n", cnt);
++
++        if (vlan_name_type == VLAN_NAME_TYPE_RAW_PLUS_VID) {
++                nm_type = "VLAN_NAME_TYPE_RAW_PLUS_VID";
++        }
++        else if (vlan_name_type == VLAN_NAME_TYPE_PLUS_VID_NO_PAD) {
++                nm_type = "VLAN_NAME_TYPE_PLUS_VID_NO_PAD";
++        }
++        else if (vlan_name_type == VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD) {
++                nm_type = "VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD";
++        }
++        else if (vlan_name_type == VLAN_NAME_TYPE_PLUS_VID) {
++                nm_type = "VLAN_NAME_TYPE_PLUS_VID";
++        }
++        else {
++                nm_type = "UNKNOWN";
++        }
++   
++        cnt += sprintf(buf + cnt, "Name-Type: %s  bad_proto_recvd: %lu\n",
++                       nm_type, vlan_bad_proto_recvd);
++   
++        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 | %s\n",
++                                                               vlandev->name, vlandev->vlan_dev->vlan_id, vlandev->vlan_dev->real_dev->name);
++                                        }/* 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, 
++                                int dummy) {
++        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, 
++                            int dummy) {
++        struct device* vlandev = (void*)start;
++        struct net_device_stats* stats;
++        int cnt = 0;
++        struct vlan_priority_tci_mapping* mp;
++        int i;
++   
++#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  REORDER_HDR: %i\n",
++                       vlandev->name, vlandev->vlan_dev->vlan_id, (int)(vlandev->vlan_dev->flags & 1));
++        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);
++
++        cnt += sprintf(buf + cnt, "%30s: %12lu\n",
++                       "total headroom inc", vlandev->vlan_dev->cnt_inc_headroom_on_tx);
++        
++        cnt += sprintf(buf + cnt, "%30s: %12lu\n",
++                       "total encap on xmit", vlandev->vlan_dev->cnt_encap_on_xmit);
++
++        cnt += sprintf(buf + cnt, "Device: %s", vlandev->vlan_dev->real_dev->name);
++
++   /* now show all PRIORITY mappings relating to this VLAN */
++        cnt += sprintf(buf + cnt, "\nINGRESS priority mappings: 0:%lu  1:%lu  2:%lu  3:%lu  4:%lu  5:%lu  6:%lu 7:%lu\n",
++                       vlandev->vlan_dev->ingress_priority_map[0],
++                       vlandev->vlan_dev->ingress_priority_map[1],
++                       vlandev->vlan_dev->ingress_priority_map[2],
++                       vlandev->vlan_dev->ingress_priority_map[3],
++                       vlandev->vlan_dev->ingress_priority_map[4],
++                       vlandev->vlan_dev->ingress_priority_map[5],
++                       vlandev->vlan_dev->ingress_priority_map[6],
++                       vlandev->vlan_dev->ingress_priority_map[7]);
++
++        cnt += sprintf(buf + cnt, "EGRESSS priority Mappings: ");
++   
++        for (i = 0; i<16; i++) {
++                mp = vlandev->vlan_dev->egress_priority_map[i];
++                while (mp) {
++                        cnt += sprintf(buf + cnt, "%lu:%hu ", mp->priority, ((mp->vlan_qos >> 13) & 0x7));
++
++                        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 */
++                        mp = mp->next;
++                }
++        }/* 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_dev(struct device *vlandev)
++{
++        return 0;
++}
++
++int vlan_proc_rem_dev(struct device *vlandev)
++{
++        return 0;
++}
++
++#endif
+diff -Nurb linux/net/802_1Q/vlanproc.h linux.p/net/802_1Q/vlanproc.h
+--- linux/net/802_1Q/vlanproc.h        Thu Jan  1 01:00:00 1970
++++ linux.p/net/802_1Q/vlanproc.h      Mon Jun  4 16:08:04 2001
+@@ -0,0 +1,27 @@
++
++#ifndef __BEN_VLAN_PROC_INC__
++#define __BEN_VLAN_PROC_INC__
++
++
++int vlan_proc_init(void);
++
++int vlan_proc_rem_dev(struct device* vlandev);
++int vlan_proc_add_dev (struct device* 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 -Nurb linux/net/Config.in linux.p/net/Config.in
+--- linux/net/Config.in        Mon Jun  4 17:48:17 2001
++++ linux.p/net/Config.in      Mon Jun  4 16:08:04 2001
+@@ -51,6 +51,9 @@
+ #  if [ "$CONFIG_LLC" = "y" ]; then
+ #   bool 'Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI
+ #  fi
++
++  tristate '802.1Q VLAN Support (EXPERIMENTAL)' CONFIG_VLAN_802_1Q
++
+   tristate 'Acorn Econet/AUN protocols (EXPERIMENTAL)' CONFIG_ECONET
+   if [ "$CONFIG_ECONET" != "n" ]; then
+     bool '  AUN over UDP' CONFIG_ECONET_AUNUDP
+diff -Nurb linux/net/Makefile linux.p/net/Makefile
+--- linux/net/Makefile Mon Jun  4 17:48:17 2001
++++ linux.p/net/Makefile       Mon Jun  4 16:08:04 2001
+@@ -10,7 +10,7 @@
+ MOD_SUB_DIRS := ipv4
+ 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
++              econet irda 802_1Q #decnet
+ SUB_DIRS     := core ethernet sched
+ MOD_LIST_NAME := NET_MISC_MODULES
+@@ -59,6 +59,14 @@
+ ifeq ($(CONFIG_BRIDGE),y)
+ SUB_DIRS += bridge
++endif
++
++ifeq ($(CONFIG_VLAN_802_1Q),y)
++SUB_DIRS += 802_1Q
++else
++  ifeq ($(CONFIG_VLAN_802_1Q),m)
++    MOD_SUB_DIRS += 802_1Q
++  endif
+ endif
+ ifeq ($(CONFIG_IPX),y)
+diff -Nurb linux/net/core/dev.c linux.p/net/core/dev.c
+--- linux/net/core/dev.c       Sun Mar 25 18:37:41 2001
++++ linux.p/net/core/dev.c     Mon Jun  4 16:08:04 2001
+@@ -94,6 +94,9 @@
+ #ifdef CONFIG_NET_RADIO
+ #include <linux/wireless.h>
+ #endif        /* CONFIG_NET_RADIO */
++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE))
++#include "../802_1Q/vlan.h"
++#endif /* CONFIG_VLAN_802_1Q ... */
+ #ifdef CONFIG_PLIP
+ extern int plip_init(void);
+ #endif
+@@ -125,6 +128,13 @@
+  *    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
+  *            0001    802.3
+  *            0002    AX.25
+@@ -133,6 +143,7 @@
+  *            0005    SNAP
+  *            0805    X.25
+  *            0806    ARP
++ *            8100    802.1Q VLAN
+  *            8137    IPX
+  *            0009    Localtalk
+  *            86DD    IPv6
+@@ -170,6 +181,257 @@
+ static void dev_clear_backlog(struct device *dev);
++/* Taking this out, because lo has problems for some people.  Feel
++ * free to turn it back on and give me (greearb@candelatech.com) bug
++ * reports if you can re-produce the problem. --Ben
++ *
++ * #define BENS_FAST_DEV_LOOKUP
++ *
++ */
++
++#ifdef BENS_FAST_DEV_LOOKUP
++/* Fast Device Lookup code.  Should give much better than
++ * linear speed when looking for devices by idx or name.
++ * --Ben (greearb@candelatech.com)
++ */
++#define FDL_HASH_LEN 256
++
++/* #define FDL_DEBUG */
++
++struct dev_hash_node {
++        struct device* dev;
++        struct dev_hash_node* next;
++};
++
++struct dev_hash_node* fdl_name_base[FDL_HASH_LEN];/* hashed by name */
++struct dev_hash_node* fdl_idx_base[FDL_HASH_LEN]; /* hashed by index */
++int fdl_initialized_yet = 0;
++
++/* TODO:  Make these inline methods */
++/* Nice cheesy little hash method to be used on device-names (eth0, ppp0, etc) */
++int fdl_calc_name_idx(const char* dev_name) {
++        int tmp = 0;
++        int i;
++#ifdef FDL_DEBUG
++        printk(KERN_ERR "fdl_calc_name_idx, name: %s\n", dev_name);
++#endif
++        for (i = 0; dev_name[i]; i++) {
++                tmp += (int)(dev_name[i]);
++        }
++        if (i > 3) {
++                tmp += (dev_name[i-2] * 10); /* might add a little spread to the hash */
++                tmp += (dev_name[i-3] * 100); /* might add a little spread to the hash */
++        }
++#ifdef FDL_DEBUG
++        printk(KERN_ERR "fdl_calc_name_idx, rslt: %i\n", (int)(tmp % FDL_HASH_LEN));
++#endif
++        return (tmp % FDL_HASH_LEN);
++}
++
++int fdl_calc_index_idx(const int ifindex) {
++        return (ifindex % FDL_HASH_LEN);
++}
++
++
++/* Better have a lock on the dev_base before calling this... */
++int __fdl_ensure_init(void) {
++#ifdef FDL_DEBUG
++        printk(KERN_ERR "__fdl_ensure_init, enter\n");
++#endif
++        if (! fdl_initialized_yet) {
++                /* only do this once.. */
++                int i;
++                int idx = 0; /* into the hash table */
++                struct device* dev = dev_base;
++                struct dev_hash_node* dhn;
++
++#ifdef FDL_DEBUG
++                printk(KERN_ERR "__fdl_ensure_init, doing real work...");
++#endif
++
++                fdl_initialized_yet = 1; /* it has been attempted at least... */
++
++                for (i = 0; i<FDL_HASH_LEN; i++) {
++                        fdl_name_base[i] = NULL;
++                        fdl_idx_base[i] = NULL;
++                }
++
++                /* add any current devices to the hash tables at this time.  Note that
++                 * this method must be called with locks on the dev_base acquired.
++                 */
++                while (dev) {
++
++#ifdef FDL_DEBUG
++                        printk(KERN_ERR "__fdl_ensure_init, dev: %p dev: %s, idx: %i\n", dev, dev->name, idx);
++#endif
++                        /* first, take care of the hash-by-name */
++                        idx = fdl_calc_name_idx(dev->name);
++                        dhn = kmalloc(sizeof(struct dev_hash_node), GFP_ATOMIC);
++                        if (dhn) {
++                                dhn->dev = dev;
++                                dhn->next = fdl_name_base[idx];
++                                fdl_name_base[idx] = dhn;
++                        }
++                        else {
++                                /* Nasty..couldn't get memory... */
++                                return -ENOMEM;
++                        }
++
++                        /* now, do the hash-by-idx */
++                        idx = fdl_calc_index_idx(dev->ifindex);
++                        dhn = kmalloc(sizeof(struct dev_hash_node), GFP_ATOMIC);
++                        if (dhn) {
++                                dhn->dev = dev;
++                                dhn->next = fdl_idx_base[idx];
++                                fdl_idx_base[idx] = dhn;
++                        }
++                        else {
++                                /* Nasty..couldn't get memory... */
++                                return -ENOMEM;
++                        }
++         
++                        dev = dev->next;
++                }
++                fdl_initialized_yet = 2; /* initialization actually worked */
++        }
++#ifdef FDL_DEBUG
++        printk(KERN_ERR "__fdl_ensure_init, end, fdl_initialized_yet: %i\n", fdl_initialized_yet);
++#endif
++        if (fdl_initialized_yet == 2) {
++                return 0;
++        }
++        else {
++                return -1;
++        }
++}/* fdl_ensure_init */
++
++
++/* called from register_netdevice, assumes dev is locked, and that no one
++ * will be calling __find_dev_by_name before this exits.. etc.
++ */
++int __fdl_register_netdevice(struct device* dev) {
++        if (__fdl_ensure_init() == 0) {
++                /* first, take care of the hash-by-name */
++                int idx = fdl_calc_name_idx(dev->name);
++                struct dev_hash_node* dhn = kmalloc(sizeof(struct dev_hash_node), GFP_ATOMIC);
++
++#ifdef FDL_DEBUG
++                printk(KERN_ERR "__fdl_register_netdevice, dev: %p dev: %s, idx: %i", dev, dev->name, idx);
++#endif
++
++                if (dhn) {
++                        dhn->dev = dev;
++                        dhn->next = fdl_name_base[idx];
++                        fdl_name_base[idx] = dhn;
++                }
++                else {
++                        /* Nasty..couldn't get memory... */
++                        /* Don't try to use these hash tables any more... */
++                        fdl_initialized_yet = 1; /* tried, but failed */
++                        return -ENOMEM;
++                }
++      
++                /* now, do the hash-by-idx */
++                idx = fdl_calc_index_idx(dev->ifindex);
++                dhn = kmalloc(sizeof(struct dev_hash_node), GFP_ATOMIC);
++
++#ifdef FDL_DEBUG
++                printk(KERN_ERR "__fdl_register_netdevice, ifindex: %i, idx: %i", dev->ifindex, idx);
++#endif
++
++                if (dhn) {
++                        dhn->dev = dev;
++                        dhn->next = fdl_idx_base[idx];
++                        fdl_idx_base[idx] = dhn;
++                }
++                else {
++                        /* Nasty..couldn't get memory... */
++                        /* Don't try to use these hash tables any more... */
++                        fdl_initialized_yet = 1; /* tried, but failed */
++                        return -ENOMEM;
++                }
++        }
++        return 0;
++} /* fdl_register_netdevice */
++
++
++/* called from register_netdevice, assumes dev is locked, and that no one
++ * will be calling __find_dev_by_name, etc.  Returns 0 if found & removed one,
++ * returns -1 otherwise.
++ */
++int __fdl_unregister_netdevice(struct device* dev) {
++        int retval = -1;
++        if (fdl_initialized_yet == 2) { /* If we've been initialized correctly... */
++                /* first, take care of the hash-by-name */
++                int idx = fdl_calc_name_idx(dev->name);
++                struct dev_hash_node* prev = fdl_name_base[idx];
++                struct dev_hash_node* cur = NULL;
++
++#ifdef FDL_DEBUG
++                printk(KERN_ERR "__fdl_unregister_netdevice, dev: %p dev: %s, idx: %i", dev, dev->name, idx);
++#endif
++
++                if (prev) {
++                        if (strcmp(dev->name, prev->dev->name) == 0) {
++                                /* it's the first one... */
++                                fdl_name_base[idx] = prev->next;
++                                kfree(prev);
++                                retval = 0;
++                        }
++                        else {
++                                cur = prev->next;
++                                while (cur) {
++                                        if (strcmp(dev->name, cur->dev->name) == 0) {
++                                                prev->next = cur->next;
++                                                kfree(cur);
++                                                retval = 0;
++                                                break;
++                                        }
++                                        else {
++                                                prev = cur;
++                                                cur = cur->next;
++                                        }
++                                }
++                        }
++                }
++
++                /* Now, the hash-by-index */
++                idx = fdl_calc_index_idx(dev->ifindex);
++                prev = fdl_idx_base[idx];
++                cur = NULL;
++                if (prev) {
++                        if (dev->ifindex == prev->dev->ifindex) {
++                                /* it's the first one... */
++                                fdl_idx_base[idx] = prev->next;
++                                kfree(prev);
++                                retval = 0;
++                        }
++                        else {
++                                cur = prev->next;
++                                while (cur) {
++                                        if (dev->ifindex == cur->dev->ifindex) {
++                                                prev->next = cur->next;
++                                                kfree(cur);
++                                                retval = 0;
++                                                break;
++                                        }
++                                        else {
++                                                prev = cur;
++                                                cur = cur->next;
++                                        }
++                                }
++                        }
++                }
++        }/* if we ensured init OK */
++        return retval;
++} /* fdl_unregister_netdevice */
++
++
++
++#endif   /* BENS_FAST_DEV_LOOKUP */
++
++
++
+ /******************************************************************************************
+               Protocol management and registration routines
+@@ -267,6 +529,26 @@
+ {
+       struct device *dev;
++#ifdef BENS_FAST_DEV_LOOKUP
++        int idx = fdl_calc_name_idx(name);
++        struct dev_hash_node* dhn;
++        if (fdl_initialized_yet == 2) {
++#ifdef FDL_DEBUG
++           printk(KERN_ERR "__dev_get_by_name, name: %s  idx: %i\n", name, idx);
++#endif
++           dhn = fdl_name_base[idx];
++           while (dhn) {
++              if (strcmp(dhn->dev->name, name) == 0) {
++                 /* printk(KERN_ERR "__dev_get_by_name, found it: %p\n", dhn->dev); */
++                 return dhn->dev;
++              }
++              dhn = dhn->next;
++           }
++           /* printk(KERN_ERR "__dev_get_by_name, didn't find it for name: %s\n", name); */
++           return NULL;
++        }
++#endif  /* BENS_FAST_DEV_LOOKUP */
++              
+       for (dev = dev_base; dev != NULL; dev = dev->next) 
+       {
+               if (strcmp(dev->name, name) == 0)
+@@ -279,6 +561,20 @@
+ {
+       struct device *dev;
++#ifdef BENS_FAST_DEV_LOOKUP
++        int idx = fdl_calc_index_idx(ifindex);
++        struct dev_hash_node* dhn;
++        if (fdl_initialized_yet == 2) { /* have we gone through initialization before... */
++           dhn = fdl_idx_base[idx];
++           while (dhn) {
++              if (dhn->dev->ifindex == ifindex)
++                 return dhn->dev;
++              dhn = dhn->next;
++           }
++           return NULL;
++        }
++#endif /* BENS_FAST_DEV_LOOKUP */
++
+       for (dev = dev_base; dev != NULL; dev = dev->next) 
+       {
+               if (dev->ifindex == ifindex)
+@@ -310,14 +606,17 @@
+       int i;
+       /*
+        *      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<100;i++)
++      for(i=0;i<8192;i++)
+       {
+               sprintf(dev->name,name,i);
+               if(dev_get(dev->name)==NULL)
+                       return i;
+       }
+-      return -ENFILE; /* Over 100 of the things .. bail out! */
++      return -ENFILE; /* Over 8192 of the things .. bail out! */
+ }
+  
+ struct device *dev_alloc(const char *name, int *err)
+@@ -1603,8 +1902,15 @@
+                               return -EBUSY;
+                       if (dev_get(ifr->ifr_newname))
+                               return -EEXIST;
++#ifdef BENS_FAST_DEV_LOOKUP
++                        /* Doesn't seem to need any additional locking in kernel 2.2 series... --Ben */
++                        __fdl_unregister_netdevice(dev); /* take it out of the name hash table */
++#endif /* BENS_FAST_DEV_LOOKUP */
+                       memcpy(dev->name, ifr->ifr_newname, IFNAMSIZ);
+                       dev->name[IFNAMSIZ-1] = 0;
++#ifdef BENS_FAST_DEV_LOOKUP
++                        __fdl_register_netdevice(dev); /* put it back in the name hash table, with the new name */
++#endif /* BENS_FAST_DEV_LOOKUP */
+                       notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev);
+                       return 0;
+@@ -1809,6 +2115,15 @@
+                               return -EEXIST;
+               }
+               dev->next = NULL;
++
++#ifdef BENS_FAST_DEV_LOOKUP
++              /* Must do this before dp is set to dev, or it could be added twice,
++               * once on initialization based on dev_base, and once again after
++               * that...
++               */
++              __fdl_register_netdevice(dev);
++#endif /* BENS_FAST_DEV_LOOKUP */
++
+               *dp = dev;
+ #ifdef CONFIG_NET_DIVERT
+               ret=alloc_divert_blk(dev);
+@@ -1834,6 +2149,13 @@
+       dev->ifindex = dev_new_index();
+       if (dev->iflink == -1)
+               dev->iflink = dev->ifindex;
++
++#ifdef BENS_FAST_DEV_LOOKUP
++      /* Must do this before dp is set to dev, or it could be added twice, once
++       * on initialization based on dev_base, and once again after that...
++       */
++      __fdl_register_netdevice(dev);
++#endif /* BENS_FAST_DEV_LOOKUP */
+       *dp = dev;
+       /* Notify protocols, that a new device appeared. */
+@@ -1885,6 +2207,9 @@
+       for (dp = &dev_base; (d=*dp) != NULL; dp=&d->next) {
+               if (d == dev) {
+                       *dp = d->next;
++#ifdef BENS_FAST_DEV_LOOKUP
++                      __fdl_unregister_netdevice(dev);
++#endif /* BENS_FAST_DEV_LOOKUP */
+                       synchronize_bh();
+                       d->next = NULL;
+diff -Nurb linux/net/ethernet/eth.c linux.p/net/ethernet/eth.c
+--- linux/net/ethernet/eth.c   Sun Mar 25 18:31:12 2001
++++ linux.p/net/ethernet/eth.c Mon Jun  4 16:08:04 2001
+@@ -174,6 +174,9 @@
+  *    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.
++ *
++ *  NOTE:  It is likely that you will want to change vlan_type_trans in
++ *         802_1Q/vlan.c if you change anything here.
+  */
+  
+ unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev)
+@@ -182,7 +185,19 @@
+       unsigned char *rawp;
+       
+       skb->mac.raw=skb->data;
++
++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE))
++      /* 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 /* CONFIG_VLAN_802_1Q ... */
++        
+       eth= skb->mac.ethernet;
+       
+       if(*eth->h_dest&1)
+@@ -207,6 +222,20 @@
+                       skb->pkt_type=PACKET_OTHERHOST;
+       }
+       
++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE))
++      if (ntohs(eth->h_proto) == ETH_P_802_1Q) {
++        /* 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 /* CONFIG_VLAN_802_1Q ... */
++        
+       if (ntohs(eth->h_proto) >= 1536)
+               return eth->h_proto;
+               
+diff -Nurb linux/net/netsyms.c linux.p/net/netsyms.c
+--- linux/net/netsyms.c        Mon Jun  4 17:48:17 2001
++++ linux.p/net/netsyms.c      Mon Jun  4 17:39:36 2001
+@@ -403,6 +403,12 @@
+ EXPORT_SYMBOL(rtnl_lock);
+ EXPORT_SYMBOL(rtnl_unlock);
++#if defined(CONFIG_VLAN_802_1Q_MODULE)
++extern struct Qdisc noqueue_qdisc;
++EXPORT_SYMBOL(noqueue_qdisc);
++EXPORT_SYMBOL(dev_change_flags);
++EXPORT_SYMBOL(eth_header_parse);
++#endif
+                   
+ /* Used by at least ipip.c.  */
+ EXPORT_SYMBOL(ipv4_config);
+@@ -533,7 +539,6 @@
+ #include<linux/if_ltalk.h>
+ EXPORT_SYMBOL(ltalk_setup);
+ #endif
+-
+ /* Packet scheduler modules want these. */
+ EXPORT_SYMBOL(qdisc_destroy);
+diff -Nurb linux/net/protocols.c linux.p/net/protocols.c
+--- linux/net/protocols.c      Sun Mar 25 18:31:11 2001
++++ linux.p/net/protocols.c    Mon Jun  4 16:08:04 2001
+@@ -34,6 +34,10 @@
+ extern void packet_proto_init(struct net_proto *pro);
+ #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
+ #include <net/ipxcall.h>
+@@ -169,5 +173,9 @@
+   { "IrDA",     irda_proto_init },                     /* IrDA protocols */
+ #endif
++#ifdef CONFIG_VLAN_802_1Q
++  { "VLAN",     vlan_proto_init },                    /* 802.1Q VLAN Support. --BLG */
++#endif
++  
+   { NULL,     NULL            }                       /* End marker                   */
+ };
diff --git a/contrib/vlan_2.2-module.patch b/contrib/vlan_2.2-module.patch
new file mode 100644 (file)
index 0000000..f4c4bd0
--- /dev/null
@@ -0,0 +1,495 @@
+diff -Nurb linux/include/linux/if_ether.h linux.p/include/linux/if_ether.h
+--- linux/include/linux/if_ether.h     Mon Jun  4 17:51:51 2001
++++ linux.p/include/linux/if_ether.h   Mon Jun  4 16:10:17 2001
+@@ -33,8 +33,7 @@
+ #define ETH_FRAME_LEN 1514            /* Max. octets in frame sans FCS */
+-#ifdef        CONFIG_VLAN_802_1Q
+-
++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE))
+ #define VLAN_ETH_ALEN 6               /* Octets in one ethernet addr   */
+ #define VLAN_ETH_HLEN 18              /* Total octets in header.       */
+@@ -58,9 +57,7 @@
+    unsigned short     h_vlan_encapsulated_proto; /* packet type ID field (or len) */
+ };
+-
+-#endif
+-
++#endif /* CONFIG_VLAN_802_1Q ... */
+ /*
+  *    These are the defined Ethernet Protocol ID's.
+diff -Nurb linux/include/linux/netdevice.h linux.p/include/linux/netdevice.h
+--- linux/include/linux/netdevice.h    Mon Jun  4 17:51:51 2001
++++ linux.p/include/linux/netdevice.h  Mon Jun  4 16:10:48 2001
+@@ -37,14 +37,11 @@
+ #ifdef CONFIG_NET_PROFILE
+ #include <net/profile.h>
+ #endif
+-
+-#if (defined(CONFIG_VLAN_802_1Q))
+-struct vlan_dev_info;
+-#endif
+-
+ #endif
+-
++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE))
++struct vlan_dev_info;
++#endif /* CONFIG_VLAN_802_1Q ... */
+ struct divert_blk;
+@@ -60,11 +57,11 @@
+  */
+  
+ #if !defined(CONFIG_AX25) && !defined(CONFIG_AX25_MODULE) && !defined(CONFIG_TR)
+-#if defined(CONFIG_VLAN_802_1Q)
++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE))
+ #define LL_MAX_HEADER 36
+ #else
+ #define LL_MAX_HEADER 32
+-#endif
++#endif /* CONFIG_VLAN_802_1Q ... */
+ #else
+ #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+ #define LL_MAX_HEADER 96
+@@ -168,17 +165,16 @@
+       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
++                                         *  encapsulated type. --BLG
+                                          */
+       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 */
++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE)) /* 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
++#endif /* CONFIG_VLAN_802_1Q ... */
+ };
+@@ -336,14 +332,13 @@
+       int                     tx_semaphore;
+ #define NETDEV_FASTROUTE_HMASK 0xF
+       /* Semi-private data. Keep it at the end of device struct. */
+-
+       struct dst_entry        *fastpath[NETDEV_FASTROUTE_HMASK+1];
+ #endif
+-#ifdef CONFIG_VLAN_802_1Q
++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE))
+         /*  Holds information that makes sense if this device is a VLAN device. */
+         struct vlan_dev_info* vlan_dev;
+-#endif
++#endif /* CONFIG_VLAN_802_1Q ... */
+ #ifdef CONFIG_NET_DIVERT
+       /* this will get initialized at each interface type init routine */
+diff -Nurb linux/net/802_1Q/Makefile linux.p/net/802_1Q/Makefile
+--- linux/net/802_1Q/Makefile  Mon Jun  4 17:51:51 2001
++++ linux.p/net/802_1Q/Makefile        Mon Jun  4 16:08:04 2001
+@@ -1,5 +1,5 @@
+ #
+-# Makefile for the Linux Ethernet layer.
++# Makefile for the Linux 802.1q protocol layer
+ #
+ # Note! Dependencies are done automagically by 'make dep', which also
+ # removes any old dependencies. DON'T put your own dependencies here
+@@ -8,19 +8,14 @@
+ # Note 2! The CFLAGS definition is now in the main makefile...
+ O_TARGET := 802_1Q.o
++O_OBJS   := vlan.o vlanproc.o vlan_dev.o
+-OBJS  := vlan.o vlanproc.o vlan_dev.o
+-
+-ifeq ($(CONFIG_SYSCTL),y)
+-OBJS += sysctl_net_vlan.o
++ifeq ($(CONFIG_VLAN_802_1Q),m)
++M_OBJS  := $(O_TARGET)
+ endif
+-
+-ifdef CONFIG_NET
+-O_OBJS        := $(OBJS) $(OBJ2)
++ifeq ($(CONFIG_SYSCTL),y)
++O_OBJS += sysctl_net_vlan.o
+ endif
+ include $(TOPDIR)/Rules.make
+-
+-tar:
+-      tar -cvf /dev/f1 .
+diff -Nurb linux/net/802_1Q/sysctl_net_vlan.c linux.p/net/802_1Q/sysctl_net_vlan.c
+--- linux/net/802_1Q/sysctl_net_vlan.c Mon Jun  4 17:51:51 2001
++++ linux.p/net/802_1Q/sysctl_net_vlan.c       Mon Jun  4 16:08:04 2001
+@@ -6,7 +6,7 @@
+  * TODO:  What, if anything, should this do??
+  */
+-#ifdef        CONFIG_VLAN_802_1Q
++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE))
+ #include <linux/mm.h>
+ #include <linux/sysctl.h>
+@@ -15,4 +15,4 @@
+       {0}
+ };
+-#endif
++#endif /* CONFIG_VLAN_802_1Q ... */
+diff -Nurb linux/net/802_1Q/vlan.c linux.p/net/802_1Q/vlan.c
+--- linux/net/802_1Q/vlan.c    Mon Jun  4 17:51:51 2001
++++ linux.p/net/802_1Q/vlan.c  Mon Jun  4 17:46:31 2001
+@@ -81,8 +81,6 @@
+  *    Context:        process
+  */
+ int init_module       (void) {
+-        printk(VLAN_INF __FUNCTION__);
+-
+         vlan_proto_init(NULL);
+         return 0;
+ }
+@@ -92,7 +90,8 @@
+  *    o delete /proc/net/router directory and static entries.
+  */ 
+ void cleanup_module (void) {
+-        vlan_proto_cleanup(); // TODO: Define this so modules work.
++        dev_remove_pack(&vlan_packet_type);
++        vlan_proc_cleanup();
+ }
+ #else
+@@ -100,11 +99,8 @@
+ /** Non-module init entry point. */
+ __initfunc(void vlan_system_init(void)) {
+-        printk(VLAN_INF __FUNCTION__);
+-
+         /* protocol initialization */
+         vlan_proto_init(NULL);
+-
+ }
+ #endif
+@@ -205,6 +201,7 @@
+                          *  NOTE:  This deletes dev, don't access it again!!
+                          */
+                         unregister_netdevice(dev);
++                      MOD_DEC_USE_COUNT;
+                 }/* if */
+         }/* if */
+@@ -438,6 +435,7 @@
+                                 /* printk(KERN_ALERT "Registering new device."); */
+                                 register_netdevice(new_dev);
+                                 vlan_proc_add_dev(new_dev); /* create it's proc entry */
++                                MOD_INC_USE_COUNT; /* Add was a success!! */
+                                 return new_dev;
+                         }
+                 }//if
+diff -Nurb linux/net/802_1Q/vlan_dev.c linux.p/net/802_1Q/vlan_dev.c
+--- linux/net/802_1Q/vlan_dev.c        Mon Jun  4 17:51:51 2001
++++ linux.p/net/802_1Q/vlan_dev.c      Mon Jun  4 16:08:04 2001
+@@ -18,7 +18,6 @@
+  */
+ #include <asm/uaccess.h> /* for copy_from_user */
+-#include <linux/module.h>
+ #include <linux/netdevice.h>
+ #include <linux/skbuff.h>
+ #include <net/datalink.h>
+diff -Nurb linux/net/802_1Q/vlanproc.c linux.p/net/802_1Q/vlanproc.c
+--- linux/net/802_1Q/vlanproc.c        Mon Jun  4 17:51:51 2001
++++ linux.p/net/802_1Q/vlanproc.c      Mon Jun  4 16:08:04 2001
+@@ -1,19 +1,19 @@
+ /* * -*- linux-c -*- */
+ /*****************************************************************************
+  * vlanproc.c VLAN Module. /proc filesystem interface.
+-*
+-* Author:     Ben Greear, <greearb@candelatech.com> coppied from wanproc.c
+-*               by: Gene Kozin        <genek@compuserve.com>
+-*
+-* Copyright:  (c) 1998-2000 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
+-*****************************************************************************/
++ *
++ * Author:    Ben Greear, <greearb@candelatech.com> coppied from wanproc.c
++ *               by: Gene Kozin       <genek@compuserve.com>
++ *
++ * Copyright: (c) 1998-2000 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. */
+diff -Nurb linux/net/Config.in linux.p/net/Config.in
+--- linux/net/Config.in        Mon Jun  4 17:51:51 2001
++++ linux.p/net/Config.in      Mon Jun  4 16:08:04 2001
+@@ -48,12 +48,12 @@
+   fi
+   bool 'Frame Diverter (EXPERIMENTAL)' CONFIG_NET_DIVERT
+   bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC
+-
+-  bool '802.1Q VLAN Support (EXPERIMENTAL)' CONFIG_VLAN_802_1Q
+-
+ #  if [ "$CONFIG_LLC" = "y" ]; then
+ #   bool 'Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI
+ #  fi
++
++  tristate '802.1Q VLAN Support (EXPERIMENTAL)' CONFIG_VLAN_802_1Q
++
+   tristate 'Acorn Econet/AUN protocols (EXPERIMENTAL)' CONFIG_ECONET
+   if [ "$CONFIG_ECONET" != "n" ]; then
+     bool '  AUN over UDP' CONFIG_ECONET_AUNUDP
+diff -Nurb linux/net/Makefile linux.p/net/Makefile
+--- linux/net/Makefile Mon Jun  4 17:51:51 2001
++++ linux.p/net/Makefile       Mon Jun  4 16:08:04 2001
+@@ -63,6 +63,10 @@
+ ifeq ($(CONFIG_VLAN_802_1Q),y)
+ SUB_DIRS += 802_1Q
++else
++  ifeq ($(CONFIG_VLAN_802_1Q),m)
++    MOD_SUB_DIRS += 802_1Q
++  endif
+ endif
+ ifeq ($(CONFIG_IPX),y)
+diff -Nurb linux/net/core/dev.c linux.p/net/core/dev.c
+--- linux/net/core/dev.c       Mon Jun  4 17:51:51 2001
++++ linux.p/net/core/dev.c     Mon Jun  4 16:08:04 2001
+@@ -1,4 +1,4 @@
+-/* -*- linux-c -*-
++/*
+  *    NET3    Protocol independent device support routines.
+  *
+  *            This program is free software; you can redistribute it and/or
+@@ -94,11 +94,9 @@
+ #ifdef CONFIG_NET_RADIO
+ #include <linux/wireless.h>
+ #endif        /* CONFIG_NET_RADIO */
+-
+-#ifdef CONFIG_VLAN_802_1Q
++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE))
+ #include "../802_1Q/vlan.h"
+-#endif
+-
++#endif /* CONFIG_VLAN_802_1Q ... */
+ #ifdef CONFIG_PLIP
+ extern int plip_init(void);
+ #endif
+@@ -138,7 +136,6 @@
+  *             --BLG
+  *
+  *            0800    IP
+- *            8100    802.1Q VLAN
+  *            0001    802.3
+  *            0002    AX.25
+  *            0004    802.2
+@@ -146,6 +143,7 @@
+  *            0005    SNAP
+  *            0805    X.25
+  *            0806    ARP
++ *            8100    802.1Q VLAN
+  *            8137    IPX
+  *            0009    Localtalk
+  *            86DD    IPv6
+@@ -186,10 +184,11 @@
+ /* Taking this out, because lo has problems for some people.  Feel
+  * free to turn it back on and give me (greearb@candelatech.com) bug
+  * reports if you can re-produce the problem. --Ben
++ *
++ * #define BENS_FAST_DEV_LOOKUP
++ *
++ */
+- #define BENS_FAST_DEV_LOOKUP
+-
+-*/
+ #ifdef BENS_FAST_DEV_LOOKUP
+ /* Fast Device Lookup code.  Should give much better than
+  * linear speed when looking for devices by idx or name.
+@@ -548,7 +547,8 @@
+            /* printk(KERN_ERR "__dev_get_by_name, didn't find it for name: %s\n", name); */
+            return NULL;
+         }
+-#endif
++#endif  /* BENS_FAST_DEV_LOOKUP */
++              
+       for (dev = dev_base; dev != NULL; dev = dev->next) 
+       {
+               if (strcmp(dev->name, name) == 0)
+@@ -560,6 +560,7 @@
+ struct device * dev_get_by_index(int ifindex)
+ {
+       struct device *dev;
++
+ #ifdef BENS_FAST_DEV_LOOKUP
+         int idx = fdl_calc_index_idx(ifindex);
+         struct dev_hash_node* dhn;
+@@ -572,7 +573,8 @@
+            }
+            return NULL;
+         }
+-#endif
++#endif /* BENS_FAST_DEV_LOOKUP */
++
+       for (dev = dev_base; dev != NULL; dev = dev->next) 
+       {
+               if (dev->ifindex == ifindex)
+@@ -1127,7 +1129,7 @@
+               if(skb==NULL)           
+                       return;
+                       
+-              offset = skb->data - skb->mac.raw;
++              offset=skb->data-skb->mac.raw;
+               skb_push(skb,offset);   /* Put header back on for bridge */
+               if(br_receive_frame(skb))
+@@ -1253,7 +1255,7 @@
+               }
+               /*
+-               *      Fetch the packet protocol ID. (In Network Byte Order --BLG)
++               *      Fetch the packet protocol ID. 
+                */
+               type = skb->protocol;
+@@ -1903,12 +1905,12 @@
+ #ifdef BENS_FAST_DEV_LOOKUP
+                         /* Doesn't seem to need any additional locking in kernel 2.2 series... --Ben */
+                         __fdl_unregister_netdevice(dev); /* take it out of the name hash table */
+-#endif
++#endif /* BENS_FAST_DEV_LOOKUP */
+                       memcpy(dev->name, ifr->ifr_newname, IFNAMSIZ);
+                       dev->name[IFNAMSIZ-1] = 0;
+ #ifdef BENS_FAST_DEV_LOOKUP
+                         __fdl_register_netdevice(dev); /* put it back in the name hash table, with the new name */
+-#endif
++#endif /* BENS_FAST_DEV_LOOKUP */
+                       notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev);
+                       return 0;
+@@ -2113,12 +2115,15 @@
+                               return -EEXIST;
+               }
+               dev->next = NULL;
++
+ #ifdef BENS_FAST_DEV_LOOKUP
+-                /* Must do this before dp is set to dev, or it could be added twice, once
+-                 * on initialization based on dev_base, and once again after that...
++              /* Must do this before dp is set to dev, or it could be added twice,
++               * once on initialization based on dev_base, and once again after
++               * that...
+                  */
+                 __fdl_register_netdevice(dev);
+-#endif
++#endif /* BENS_FAST_DEV_LOOKUP */
++
+               *dp = dev;
+ #ifdef CONFIG_NET_DIVERT
+               ret=alloc_divert_blk(dev);
+@@ -2150,7 +2155,7 @@
+          * on initialization based on dev_base, and once again after that...
+          */
+         __fdl_register_netdevice(dev);
+-#endif
++#endif /* BENS_FAST_DEV_LOOKUP */
+       *dp = dev;
+       /* Notify protocols, that a new device appeared. */
+@@ -2204,7 +2209,7 @@
+                       *dp = d->next;
+ #ifdef BENS_FAST_DEV_LOOKUP
+                         __fdl_unregister_netdevice(dev);
+-#endif
++#endif /* BENS_FAST_DEV_LOOKUP */
+                       synchronize_bh();
+                       d->next = NULL;
+diff -Nurb linux/net/ethernet/eth.c linux.p/net/ethernet/eth.c
+--- linux/net/ethernet/eth.c   Mon Jun  4 17:51:51 2001
++++ linux.p/net/ethernet/eth.c Mon Jun  4 16:08:04 2001
+@@ -186,7 +186,7 @@
+         skb->mac.raw=skb->data;
+-#ifdef CONFIG_VLAN_802_1Q
++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE))
+       /* 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
+@@ -196,7 +196,7 @@
+         /* skb_pull(skb,dev->hard_header_len); */
+ #else
+         skb_pull(skb,dev->hard_header_len);
+-#endif
++#endif /* CONFIG_VLAN_802_1Q ... */
+         
+         eth= skb->mac.ethernet;
+@@ -222,7 +222,7 @@
+                       skb->pkt_type=PACKET_OTHERHOST;
+       }
+-#ifdef CONFIG_VLAN_802_1Q
++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE))
+         if (ntohs(eth->h_proto) == ETH_P_802_1Q) {
+                 /* then we have to convert this into a VLAN looking packet.
+                  * We'll wait to do that in the VLAN protocol handler.
+@@ -234,7 +234,7 @@
+         else {
+                 skb_pull(skb, dev->hard_header_len);
+         }
+-#endif
++#endif /* CONFIG_VLAN_802_1Q ... */
+         
+       if (ntohs(eth->h_proto) >= 1536)
+               return eth->h_proto;
+diff -Nurb linux/net/netsyms.c linux.p/net/netsyms.c
+--- linux/net/netsyms.c        Mon Jun  4 17:48:17 2001
++++ linux.p/net/netsyms.c      Mon Jun  4 17:39:36 2001
+@@ -403,6 +403,12 @@
+ EXPORT_SYMBOL(rtnl_lock);
+ EXPORT_SYMBOL(rtnl_unlock);
++#if defined(CONFIG_VLAN_802_1Q_MODULE)
++extern struct Qdisc noqueue_qdisc;
++EXPORT_SYMBOL(noqueue_qdisc);
++EXPORT_SYMBOL(dev_change_flags);
++EXPORT_SYMBOL(eth_header_parse);
++#endif
+                   
+ /* Used by at least ipip.c.  */
+ EXPORT_SYMBOL(ipv4_config);
+@@ -533,7 +539,6 @@
+ #include<linux/if_ltalk.h>
+ EXPORT_SYMBOL(ltalk_setup);
+ #endif
+-
+ /* Packet scheduler modules want these. */
+ EXPORT_SYMBOL(qdisc_destroy);
diff --git a/howto.html b/howto.html
new file mode 100644 (file)
index 0000000..a23c180
--- /dev/null
@@ -0,0 +1,1219 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+  <head>
+    <title>LINUX VLAN + Cisco HOWTO</title>
+  </head>
+
+  <body bgcolor=#ffffff text=#000000>
+    <center><h1>LINUX VLAN + Cisco HOWTO</h1></center>
+<P>
+<center>0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0-0</center>
+<pre>
+
+  The Linux VLAN HOWTO
+  VLAN Mailing list: <a href="mailto:vlan@candelatech.com">vlan@candelatech.com</a>
+  Kristjan Kotkas <a href="mailto:kristjan@data.ee">kristjan@data.ee</a>
+  Ben Greear <a href="mailto:greearb@candelatech.com">greearb@candelatech.com</a>
+
+</pre>
+<P>
+
+<b>NOTE:  If you can get ping to work, but telnet/http/ssh/etc hangs, then you most
+likely have a driver that is broken with regard to 802.1Q vlans.  There are various
+patches for different drivers below.  As a last ditch effort, you can set the MTU
+on your VLAN interface to 1496 as opposed to 1500.  If you are unaware of the
+consequences of such a thing, please consider getting supported hardware and/or
+ask the writer of your driver for a patch. --Ben
+</b>
+<P>
+<h2>Contents</h2>
+<ol>
+  <li><a href="#targ1">Who, why and where</a></li>
+  <li><a href="#targ2">Actual info on how to make it work.</a></li>
+  <li><a href="#targ3">Specific work-arounds/patches for certain configurations.</a>
+    <ul>
+     <li><a href="#tulip">Tulip driver patch.</a></li>
+     <li><a href="#eepro">eepro100 driver patch.</a></li>
+     <li><a href="#syskonnect">SysKonnect sk98lin driver patch.</a></li>
+     <li><a href="#3c59x">3c59X driver patch.</a></li>
+     <li><a href="#natsemi">natsemi driver patch.</a></li>
+     <li><a href="#3c905b">3c509b driver patch.</a></li>
+     <li><a href="#pcmcia">pcmcia drivers.</a></li>
+    </ul>
+  <li><a href="#targ4">Scripts and Recipes.</a></li>
+</ol>
+<P>
+
+<ol>
+ <li><a name="targ1">META</a>
+  <ol>
+   <li>
+       This is the first HOWTO for the "802.1Q VLAN implementation for Linux"<P>
+             
+       Homepage: <a href="http://scry.wanfear.com/~greear/vlan.html">http://scry.wanfear.com/~greear/vlan.html</a>
+       Mailing List: VLAN@Scry.WANfear.com
+   </li>  
+   <P>
+   <li>Copyright<P>
+
+  This document is part of the Linux HOWTO project. The copyright notice
+  is the following: Unless otherwise stated, Linux HOWTO documents are
+  copyrighted by their respective authors. Linux HOWTO documents may be
+  reproduced and distributed in whole or in part, in any medium physical
+  or electronic, as long as this copyright notice is retained on all
+  copies. Commercial redistribution is allowed and encouraged; however,
+  the author would like to be notified of any such distributions. All
+  translations, derivative works, or aggregate works incorporating any
+  Linux HOWTO documents must be covered under this copyright notice.
+  That is, you may not produce a derivative work from a HOWTO and impose
+  additional restrictions on its distribution. Exceptions to these rules
+  may be granted under certain conditions; please contact the Linux
+  HOWTO coordinator at the address given below. In short, we wish to
+  promote dissemination of this information through as many channels as
+  possible. However, we do wish to retain copyright on the HOWTO
+  documents, and would like to be notified of any plans to redistribute
+  the HOWTOs. If you have questions, please contact Tim Bynum, the Linux
+  HOWTO coordinator, at linux-howto@sunsite.unc.edu via email.
+   </li>
+   <P>
+   <li>Disclaimer<P>
+
+  As usual: the author IS NOT responsible for any damage. For the correct
+  wording, see the relevant part of the GNU GPL 0.1.1
+   </li>
+   <P>
+   <li>Credits<P>
+
+  Thanks to Ben Greear <a href="http://www.candelatech.com/~greear">http://www.candelatech.com/~greear</a>
+  for the VLAN project and also to all the people who have contributed to the project.
+   </li>
+   <P>
+   <li>General<P>
+
+  What is VLAN is not described in this document. Info on the VLAN protocol
+  can be found at
+   <a href="http://standards.ieee.org/getieee802/download/802.1Q-1998.pdf">http://standards.ieee.org/getieee802/download/802.1Q-1998.pdf</a>.
+   </li>
+  </ol>
+ </li>
+ <P>
+ <li><a name="targ2"><b>Software/Hardware (Cisco-specific setup, with some general info as well.)</b></a><P>
+  <ol>
+   <li>VLAN installation & Configuration on the Linux Side.<P>
+<PRE>
+
+NOTE:  This is fairly old.  I'm leaving it in for historical reasons, but
+be aware that VLAN is included in the later 2.4 kernels, so most folks do
+NOT have to patch their kernel. --Ben
+
+
+* Linux kernel 2.2.14
+* Vlan 0.0.10 Patched into it
+* Cisco Catalyst 2900XL
+* 3Com 3C509B NIC using patched driver 3c59x
+
+
+Currently VLAN is not part of the kernel distribution so you need to patch
+it into a supported Linux kernel and re-compile.
+
+You need the kernel source.  If you don't have it already, you can get from
+ftp.kernel.org or from one of its mirrors.  If this scares you, read the
+KERNEL-HOWTO.
+
+It is assumed that you have the linux kernel source extracted, and found at:
+$HOME/linux  If your setup is different, then some of these commands may
+need to be slightly different.
+
+Download the VLAN package from the vlan homepage and extract it's contents.
+tar -xvzf vlan*.tar.gz
+
+Go to the vlan directory, build the vlan tools by just typing:
+make
+
+After this you get a programm named &lt;vconfig>. This program manages all VLAN
+specific configurations.
+
+Now patch the kernel.
+Go to the linux directory 
+cd linux
+(if you installed the kernel source from some rpm based distribution it is 
+something like /usr/src/linux)
+patch the kernel by typing:
+
+patch -p 1 &lt; $HOME/vlan/vlan.patch (patch is in the vlan directory)
+
+Time to compile your kernel. Use the make menuconfig command in your
+linux directory to select your kernel options. The option related to
+802.1Q VLANs is found under the Networking options.
+
+Additional help for kernel compilation can be found in KERNEL-HOWTO
+
+Assuming your kernel compiled cleanly, you are now ready to use it. 
+Install your kernel in the normal manner (fix up your /etc/lilo.conf file 
+appropriately and run lilo as root.) 
+Reboot your computer and choose your new kernel. 
+
+As your computer comes back to life, there will be little sign that you are
+now 802.1Q capable.
+You should see something like this:
+
+        802.1Q VLAN Support v0.10  Ben Greear &lt;greearb@candelatech.com>
+        vlan Initialization complete.
+
+
+Your system is now vlan ready, lets configure some vlans:
+I'm assuming that your VLAN capable network card is eth0.
+
+First, set the eth0 state to down:
+
+ifconfig eth0 down
+</pre>
+
+<b>Ben's Note: &nbsp;  Regarding the next section, you can run plain ethernet
+and VLAN over the same NIC, but you may not want to..</b><pre>
+Whatever your previous netconf was, you should move everything to vlans.
+This means, that you don't set ip address to the real interface, but set it to
+vlan interface. To set your eth0 with no ip:
+
+ifconfig eth0 0.0.0.0 up
+
+!note!
+YOU MUST SET THE ETH0 TO UP, or it wont work. (ifconfig eth0 up)
+
+Add some vlans; goto your vlan directory where you previously compiled 
+vconfig and type:
+
+vconfig add eth0 2
+
+! Little note about VLAN 1. In Cisco systems it is the default VLAN
+so you MUST start using vlans from 2.
+
+This will create device vlan0002 to your system. Linux will think, that it
+is just another network device, so you can configure it like any other. Also
+you should see the interface by typing
+
+ifconfig -a
+
+Lets make some conf on the vlan then:
+ifconfig -i vlan0002 10.0.231.1 broadcast 10.0.231.0 netmask 255.255.255.0 up
+
+This ends the configuration at the linux side.
+</pre>
+</li>
+<P>
+<li> Specific Extreme Networks Configuration<P><pre>
+  From: Craig Metz: cmetz@inner.net
+
+Extreme configuration example:
+
+  create vlan v42
+  config vlan v42 tag 42
+  config vlan v42 add port 10 tagged
+
+  ... will create a vlan named v42, whose 802.1Q tag is 42, and connect port
+10 (tag 42) to that vlan.
+</pre>
+</li>
+<P>
+<li> Cisco-specific configuration<P>
+<pre>
+Cisco Conf
+configure the port you want to use as the trunk:
+
+telnet switch or use the console port
+
+ena
+(will prompt for password, so have it ready)
+
+conf t
+interface FastEthernet0/24 (it doesnt have to be 0/24)
+ duplex full
+ speed 100
+ switchport trunk encapsulation dot1q
+ switchport trunk allowed vlan 2
+ switchport mode trunk
+
+
+This conf will do the following:
+
+Set the port to full duplex mode; force the port to 100Mb mode; set the port
+vlan encapsulation to support 802.1Q; tell the
+switch that the port is allowed to run vlans through (even if you set just
+VLAN 2, cisco will automatically add VLAN 1 and VLAN 1002-1005) to the port 
+and set the port to trunk mode aswell. Trunk mode tells the switch that 
+a number of VLANS can go through it.
+
+Last line is usually the mother of all screw-ups. If you forget that, you
+won't get your VLAN working. Simple as that.
+
+Now configure some other port to be used as the destination for the vlan:
+
+conf t
+interface FastEthernet0/1
+ duplex half
+ speed 10
+ switchport access vlan 2
+end
+
+Here we tell the switch to force the port 1 to half duplex 10Mb mode (normal
+10 Mb NIC) and only traffic from interface VLAN 2 can go through this port.
+also you can use a number of ports with VLAN 2, like a HUB ;)
+
+You should now connect some other device to port 1.
+
+Let it have an ip of eg. 10.0.231.2 mask 255.255.255.0
+
+Ping linux from it
+
+ping 10.0.231.1
+
+If it replies scream: "YESS!!" This means, that VLAN is working.
+
+Hard truth: It's not over, till its over.
+if this works, then you are out of the woods, if not, well I hear that
+tcpdump is a good tool ;-) and tcpdump that came with the vlan package even
+better tool. (if you want to dump, use the one that came with vlan package)
+</PRE>
+<b> NOTE:  <a href="http://ethereal.zing.org">Ethereal</a> also supports VLANs,
+and is much more beautiful than tcpdump, if you have GUI capabilities.</b>
+<pre>
+
+If you can ping the linux and from linux the host, you should try the
+following at linux side:
+
+ping -s 1476 10.0.231.2
+
+If there is no reply, there is something foggy with the NIC. and you
+should start debugging.  If ping -s 100 10.0.231.2 works, then it is most likely
+an MTU problem with your NIC/Driver.
+</PRE>
+</li>
+</ol>
+</li>
+<P>
+<li><a name="targ3"><h3>Specific patches and work-arounds for various configurations.</h3></a><P>
+ <ol>
+
+<a name="tulip">
+  <li><B>My Tulip-based card has MTU problems.</B><P>
+<pre>
+
+   Here is a patch sent in by Ben McKeegan:
+Dear VLAN list members,
+
+Courtesy of my new employer, I've finally got around to updating my tulip
+vlan patch for use with linux 2.4.x.  I've also taken the opportunity to
+do a rewrite and fix various (non-critical) flaws in my previous patch
+(which was against 2.2.x).  The patch allows the driver to receive vlan
+frames with maximum MTU.
+
+Hopefully this rewrite will help satisfy some of the concerns of the 2.4
+kernel driver maintainers and thus aid inclusion of the patch in the
+main driver.
+
+The old patch would erroneously allow incoming frames sized between 1519
+and 1536 bytes excluding the CRC.  The new patch should correctly limit
+this to 1518, the maximum size for VLANs.  The old patch also needlessly
+increased various constants.
+
+I believe there was some suggestion that the old patch disabled all length
+error checking and opened the system to DoS attacks from massively
+oversized packets.  This is was never really the case, although the above
+mentioned bug did exist.  The patch only disabled checking of the 'Frame
+Too Long' flag which indicates the frame exceeds 1518 bytes (including the
+CRC).  The NIC does not take any special action when this flag is set and
+it does not stop the buffers getting filled up - the protection against
+DoS comes from the receive timer which has a separate error flag that gets
+set when length exceeds 2048 bytes.  The 'Frame Too Long' flag is
+basically just a summary of the length bits that are passed as part of the
+same descriptor as the flag.  The patch just explicitly checks the length
+instead of relying on the flag.
+
+The unpatched driver uses 'magic number' constants to check the receive
+status code.  Having worked out what these meant from the documentation,
+in the old patch I replaced them with a similar magic number.  The new
+patch replaces them with a series of verbose enumerations.  Any worthwhile
+compiler should optimize these down to a single constant, but these make
+the code much easier to read.  (In fact, its a lot easier to tell what the
+patched driver does than what the unpatched driver does. 
+
+I have tested the new patch on our own systems (using chipset 21143 rev
+65), and it works ok, but I would appreciate feedback from other people.  
+With a lot of testing and a bit of luck and persuasion this patch might
+make it into the kernel.
+
+Ben, I would be grateful if you could update the section of your how-to
+containing my old patch, and add a note about the problems with the old
+patch alongside it.  (Perhaps someone else may wish to backport the new
+patch to 2.2)
+
+
+Regards,
+
+Ben McKeegan.
+
+
+diff -ur linux-2.4.19/drivers/net/tulip/interrupt.c linux-2.4.19-tulip-vlan/drivers/net/tulip/interrupt.c
+--- linux-2.4.19/drivers/net/tulip/interrupt.c Fri Nov  9 21:45:35 2001
++++ linux-2.4.19-tulip-vlan/drivers/net/tulip/interrupt.c      Mon Sep 16 13:17:40 2002
+@@ -122,14 +122,36 @@
+       /* If we own the next entry, it is a new packet. Send it up. */
+       while ( ! (tp->rx_ring[entry].status & cpu_to_le32(DescOwned))) {
+               s32 status = le32_to_cpu(tp->rx_ring[entry].status);
++              short pkt_len;
+               if (tulip_debug > 5)
+                       printk(KERN_DEBUG "%s: In tulip_rx(), entry %d %8.8x.\n",
+                                  dev->name, entry, status);
+               if (--rx_work_limit < 0)
+-                      break;
+-              if ((status & 0x38008300) != 0x0300) {
+-                      if ((status & 0x38000300) != 0x0300) {
++                      break;
++                      
++              /*
++                Omit the four octet CRC from the length.
++                (May not be considered valid until we have
++                checked status for RxLengthOver2047 bits)
++                */
++              pkt_len = ((status >> 16) & 0x7ff) - 4;
++
++              /*
++                Maximum pkt_len is 1518 (1514 + vlan header)
++                Anything higher than this is always invalid
++                regardless of RxLengthOver2047 bits
++              */
++
++              if ((status & (RxLengthOver2047 |
++                             RxDescCRCError |
++                             RxDescCollisionSeen |
++                             RxDescRunt |
++                             RxDescDescErr |
++                             RxWholePkt))        != RxWholePkt
++                  || pkt_len > 1518 ) {
++                      if ((status & (RxLengthOver2047 |
++                                     RxWholePkt))         != RxWholePkt) {
+                               /* Ingore earlier buffers. */
+                               if ((status & 0xffff) != 0x7fff) {
+                                       if (tulip_debug > 1)
+@@ -138,31 +160,21 @@
+                                                          dev->name, status);
+                                       tp->stats.rx_length_errors++;
+                               }
+-                      } else if (status & RxDescFatalErr) {
++                      } else {
+                               /* There was a fatal error. */
+                               if (tulip_debug > 2)
+                                       printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n",
+                                                  dev->name, status);
+                               tp->stats.rx_errors++; /* end of a packet.*/
+-                              if (status & 0x0890) tp->stats.rx_length_errors++;
++                              if (pkt_len > 1518 ||
++                                  status & RxDescRunt) tp->stats.rx_length_errors++;
+                               if (status & 0x0004) tp->stats.rx_frame_errors++;
+                               if (status & 0x0002) tp->stats.rx_crc_errors++;
+                               if (status & 0x0001) tp->stats.rx_fifo_errors++;
+                       }
+               } else {
+-                      /* Omit the four octet CRC from the length. */
+-                      short pkt_len = ((status >> 16) & 0x7ff) - 4;
+                       struct sk_buff *skb;
+-#ifndef final_version
+-                      if (pkt_len > 1518) {
+-                              printk(KERN_WARNING "%s: Bogus packet size of %d (%#x).\n",
+-                                         dev->name, pkt_len, pkt_len);
+-                              pkt_len = 1518;
+-                              tp->stats.rx_length_errors++;
+-                      }
+-#endif
+-
+ #ifdef CONFIG_NET_HW_FLOWCONTROL
+                         drop = atomic_read(&netdev_dropping);
+                         if (drop)
+diff -ur linux-2.4.19/drivers/net/tulip/tulip.h linux-2.4.19-tulip-vlan/drivers/net/tulip/tulip.h
+--- linux-2.4.19/drivers/net/tulip/tulip.h     Fri Nov  9 21:45:35 2001
++++ linux-2.4.19-tulip-vlan/drivers/net/tulip/tulip.h  Mon Sep 16 11:55:33 2002
+@@ -186,11 +186,44 @@
+ enum desc_status_bits {
+       DescOwned = 0x80000000,
+-      RxDescFatalErr = 0x8000,
++
++        /* 
++         Error summary flag is logical or of 'CRC Error',
++         'Collision Seen', 'Frame Too Long', 'Runt' and 
++         'Descriptor Error' flags generated within tulip chip.
++      */
++        RxDescErrorSummary = 0x8000, 
++      
++      RxDescCRCError = 0x0002,
++        RxDescCollisionSeen = 0x0040,
++      
++        /* 
++         'Frame Too Long' flag is set if packet length including CRC
++         exceeds 1518.  However, a full sized VLAN tagged frame is
++         1522 bytes including CRC.
++         
++         The tulip chip does not block oversized frames, and if this
++         flag is set on a receive descriptor it does not indicate
++         the frame has been truncated.  The receive descriptor also
++         includes the actual length.  Therefore we can safety ignore
++         this flag and check the length ourselves. 
++        */
++        RxDescFrameTooLong = 0x0080,
++      RxDescRunt = 0x0800,
++      RxDescDescErr = 0x4000,
+       RxWholePkt = 0x0300,
++      
++      /*
++        Top three bits of 14 bit frame length (status bits 27-29) 
++          should never be set as that would make frame over 2047 bytes.
++        The Receive Watchdog flag (bit 4) may indicate the length is
++          over 2048 and the length field is invalid.
++      */
++      RxLengthOver2047 = 0x38000010
+ };
++
+ enum t21041_csr13_bits {
+       csr13_eng = (0xEF0<<4), /* for eng. purposes only, hardcode at EF0h */
+       csr13_aui = (1<<3), /* clear to force 10bT, set to force AUI/BNC */
+
+</pre>
+</li>
+
+<a name="eepro">
+  <li><B>My eepro100 has MTU problems.</B><P>
+
+NOTE:  Intel's e100 driver works out-of-the-box. --Ben
+<P>
+
+   Here is a patch sent in by gleb@nbase.co.il<br>
+
+<pre>
+     filename="linux-2.2.14-eepro100-vlan.patch"
+
+--- linux/drivers/net/eepro100.c        Tue Oct 26 20:53:40 1999
++++ linux1/drivers/net/eepro100.c       Sun May 14 07:47:34 2000
+@@ -377,12 +377,12 @@
+ const char i82557_config_cmd[22] = {
+        22, 0x08, 0, 0,  0, 0x80, 0x32, 0x03,  1, /* 1=Use MII  0=Use AUI */
+        0, 0x2E, 0,  0x60, 0,
+-       0xf2, 0x48,   0, 0x40, 0xf2, 0x80,              /* 0x40=Force full-duplex */
++       0xf2, 0x48,   0, 0x40, 0xfa, 0x80,              /* 0x40=Force full-duplex */
+        0x3f, 0x05, };
+ const char i82558_config_cmd[22] = {
+        22, 0x08, 0, 1,  0, 0x80, 0x22, 0x03,  1, /* 1=Use MII  0=Use AUI */
+        0, 0x2E, 0,  0x60, 0x08, 0x88,
+-       0x68, 0, 0x40, 0xf2, 0xBD,              /* 0xBD->0xFD=Force full-duplex */
++       0x68, 0, 0x40, 0xfa, 0xBD,              /* 0xBD->0xFD=Force full-duplex */
+        0x31, 0x05, };
+ /* PHY media interface chips. */
+</pre>
+</li>
+<P>
+<a name="syskonnect">
+<li><B>My SysKonnect sk98lin doesn't work</b> (submitted by:  Patrick Schaaf &lt;bof@bof.de>)<P>
+
+Here's a piece needed to get SysKonnect sk98lin
+driven cards to play nice; they recognize and drop incoming VLAN tagged
+frames in the driver, the patch below removes that check. Tested a bit
+with a Cisco Catalyst 6509 on the other side, and a fibre link, works
+like a charm. The card and driver already supports MTUs up to over 9000,
+so no problem on that side.
+<P>
+<PRE>
+diff -urN linux/drivers/net/sk98lin/skge.c blues/drivers/net/sk98lin/skge.c
+--- linux/drivers/net/sk98lin/skge.c    Mon Jun 19 20:42:38 2000
++++ blues/drivers/net/sk98lin/skge.c    Mon Aug  7 09:43:18 2000
+@@ -1948,7 +1948,7 @@
+                
+                if ((Control & RX_CTRL_STAT_VALID) == RX_CTRL_STAT_VALID &&
+                        (FrameStat & 
+-                       (XMR_FS_ANY_ERR | XMR_FS_1L_VLAN | XMR_FS_2L_VLAN))
++                       (XMR_FS_ANY_ERR /*| XMR_FS_1L_VLAN*/ | XMR_FS_2L_VLAN))
+                         == 0) {
+                        SK_DBG_MSG(NULL, SK_DBGMOD_DRV,
+                                SK_DBGCAT_DRV_RX_PROGRESS,("V"));
+
+</pre>
+</li>
+<P>
+<a name="3c59x">
+<li><b>My 3C59X has MTU problems.</b><P>
+
+Various contributors, most recently: Richard Fuchs
+
+<pre>
+
+--- 3c59x.c-ori        2003-07-02 15:26:26.000000000 +0200
++++ 3c59x.c    2003-07-02 15:40:26.000000000 +0200
+@@ -308,6 +308,9 @@
+    code size of a per-interface flag is not worthwhile. */
+ static char mii_preamble_required;
++/* The Ethernet Type used for 802.1q tagged frames */
++#define VLAN_ETHER_TYPE 0x8100
++
+ #define PFX DRV_NAME ": "
+@@ -655,7 +658,7 @@
+       Wn2_ResetOptions=12,
+ };
+ enum Window3 {                        /* Window 3: MAC/config bits. */
+-      Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8,
++      Wn3_Config=0, Wn3_MaxPktSize=4, Wn3_MAC_Ctrl=6, Wn3_Options=8,
+ };
+ #define BFEXT(value, offset, bitcount)  \
+@@ -683,7 +686,8 @@
+       Media_LnkBeat = 0x0800,
+ };
+ enum Window7 {                                        /* Window 7: Bus Master control. */
+-      Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
++      Wn7_MasterAddr = 0, Wn7_VlanEtherType=4, Wn7_MasterLen = 6,
++      Wn7_MasterStatus = 12,
+ };
+ /* Boomerang bus master control registers. */
+ enum MasterCtrl {
+@@ -780,7 +784,8 @@
+               pm_state_valid:1,                               /* power_state[] has sane contents */
+               open:1,
+               medialock:1,
+-              must_free_region:1;                             /* Flag: if zero, Cardbus owns the I/O region */
++              must_free_region:1,                             /* Flag: if zero, Cardbus owns the I/O region */
++              large_frames:1;                                 /* accept large frames */
+       int drv_flags;
+       u16 status_enable;
+       u16 intr_enable;
+@@ -848,6 +853,9 @@
+ static int vortex_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+ static void vortex_tx_timeout(struct net_device *dev);
+ static void acpi_set_WOL(struct net_device *dev);
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++static void set_8021q_mode(struct net_device *dev, int enable);
++#endif
\f
+ /* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
+ /* Option count limit only -- unlimited interfaces are supported. */
+@@ -1031,6 +1039,7 @@
+       dev->base_addr = ioaddr;
+       dev->irq = irq;
+       dev->mtu = mtu;
++      vp->large_frames = mtu > 1500;
+       vp->drv_flags = vci->drv_flags;
+       vp->has_nway = (vci->drv_flags & HAS_NWAY) ? 1 : 0;
+       vp->io_size = vci->io_size;
+@@ -1450,7 +1459,7 @@
+       /* Set the full-duplex bit. */
+       outw(   ((vp->info1 & 0x8000) || vp->full_duplex ? 0x20 : 0) |
+-                      (dev->mtu > 1500 ? 0x40 : 0) |
++                      (vp->large_frames ? 0x40 : 0) |
+                       ((vp->full_duplex && vp->flow_ctrl && vp->partner_flow_ctrl) ? 0x100 : 0),
+                       ioaddr + Wn3_MAC_Ctrl);
+@@ -1534,6 +1543,10 @@
+       }
+       /* Set receiver mode: presumably accept b-case and phys addr only. */
+       set_rx_mode(dev);
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++      /* enable 802.1q tagged frames */
++      set_8021q_mode(dev, 1);
++#endif
+       outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
+ //    issue_and_wait(dev, SetTxStart|0x07ff);
+@@ -1674,7 +1687,7 @@
+                                               /* Set the full-duplex bit. */
+                                               EL3WINDOW(3);
+                                               outw(   (vp->full_duplex ? 0x20 : 0) |
+-                                                              (dev->mtu > 1500 ? 0x40 : 0) |
++                                                              (vp->large_frames ? 0x40 : 0) |
+                                                               ((vp->full_duplex && vp->flow_ctrl && vp->partner_flow_ctrl) ? 0x100 : 0),
+                                                               ioaddr + Wn3_MAC_Ctrl);
+                                               if (vortex_debug > 1)
+@@ -1897,6 +1910,10 @@
+                       issue_and_wait(dev, RxReset|0x07);
+                       /* Set the Rx filter to the current state. */
+                       set_rx_mode(dev);
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++                      /* enable 802.1q VLAN tagged frames */
++                      set_8021q_mode(dev, 1);
++#endif
+                       outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
+                       outw(AckIntr | HostError, ioaddr + EL3_CMD);
+               }
+@@ -2494,6 +2511,11 @@
+       outw(RxDisable, ioaddr + EL3_CMD);
+       outw(TxDisable, ioaddr + EL3_CMD);
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++      /* Disable receiving 802.1q tagged frames */
++      set_8021q_mode(dev, 0);
++#endif
++
+       if (dev->if_port == XCVR_10base2)
+               /* Turn off thinnet power.  Green! */
+               outw(StopCoax, ioaddr + EL3_CMD);
+@@ -2758,6 +2780,50 @@
+       outw(new_mode, ioaddr + EL3_CMD);
+ }
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++/* Setup the card so that it can receive frames with an 802.1q VLAN tag.
++   Note that this must be done after each RxReset due to some backwards
++   compatibility logic in the Cyclone and Tornado ASICs */
++static void set_8021q_mode(struct net_device *dev, int enable)
++{
++      struct vortex_private *vp = (struct vortex_private *)dev->priv;
++      long ioaddr = dev->base_addr;
++      int old_window = inw(ioaddr + EL3_CMD);
++      int mac_ctrl;
++
++      if (vp->drv_flags&IS_CYCLONE || vp->drv_flags&IS_TORNADO) {
++              /* cyclone and tornado chipsets can recognize 802.1q
++               * tagged frames and treat them correctly */
++
++              int max_pkt_size = dev->mtu+14; /* MTU+Ethernet header */
++              if (enable)
++                      max_pkt_size += 4;      /* 802.1Q VLAN tag */
++
++              EL3WINDOW(3);
++              outw(max_pkt_size, ioaddr+Wn3_MaxPktSize);
++
++              /* set VlanEtherType to let the hardware checksumming
++                 treat tagged frames correctly */
++              EL3WINDOW(7);
++              outw(VLAN_ETHER_TYPE, ioaddr+Wn7_VlanEtherType);
++      } else {
++              /* on older cards we have to enable large frames */
++
++              vp->large_frames = dev->mtu > 1500 || enable;
++
++              EL3WINDOW(3);
++              mac_ctrl = inw(ioaddr+Wn3_MAC_Ctrl);
++              if (vp->large_frames)
++                      mac_ctrl |= 0x40;
++              else
++                      mac_ctrl &= ~0x40;
++              outw(mac_ctrl, ioaddr+Wn3_MAC_Ctrl);
++      }
++
++      EL3WINDOW(old_window);
++}
++#endif
++
+ /* MII transceiver control section.
+    Read and write the MII registers using software-generated serial
+    MDIO protocol.  See the MII specifications or DP83840A data sheet
+
+</pre>
+</li>
+<P>
+
+<a name="natsemi">
+<li><b>My natsemi has MTU problems.</b><P>
+By Peter Stuge:
+<pre>
+
+--- natsemi.c.orig     2002-12-30 21:38:04.000000000 +0100
++++ natsemi.c  2002-12-30 22:25:19.000000000 +0100
+@@ -233,7 +233,7 @@
+ #define NATSEMI_REGS_SIZE     (NATSEMI_NREGS * sizeof(u32))
+ #define NATSEMI_EEPROM_SIZE   24 /* 12 16-bit values */
+-#define PKT_BUF_SZ            1536 /* Size of each temporary Rx buffer. */
++#define PKT_BUF_SZ            2064 /* Size of each temporary Rx buffer. */
+ /* These identify the driver base version and may not be removed. */
+ static char version[] __devinitdata =
+@@ -1290,7 +1290,7 @@
+       /* DRTH 0x10: start copying to memory if 128 bytes are in the fifo
+        * MXDMA 0: up to 256 byte bursts
+        */
+-      np->rx_config = RxMxdma_256 | 0x20;
++      np->rx_config = RxAcceptLong | RxMxdma_256 | 0x20;
+       writel(np->rx_config, ioaddr + RxConfig);
+       /* Disable PME:
+</pre>
+</li>
+<P>
+
+<a name="3c905b">
+<li><b>My 3C905B has MTU problems.</b><P>
+As found
+<a href="http://www.bewley.net/linux/vlan/patches/vlan-3c59x.patch">here</a>
+at one point in time.<br>
+Furnished by: Luis Miguel Cruz Miranda luismi@b2bi.es<br>
+(I don't know the original author --Ben).
+
+<PRE>
+--- linux.orig/drivers/net/3c59x.c     Sun Sep 30 21:26:06 2001
++++ linux/drivers/net/3c59x.c  Wed Oct 24 21:52:10 2001
+@@ -308,6 +308,9 @@
+    code size of a per-interface flag is not worthwhile. */
+ static char mii_preamble_required;
++/* The Ethernet Type used for 802.1q tagged frames */
++#define VLAN_ETHER_TYPE 0x8100
++
+ #define PFX DRV_NAME ": "
+@@ -651,7 +654,7 @@
+       Wn2_ResetOptions=12,
+ };
+ enum Window3 {                        /* Window 3: MAC/config bits. */
+-      Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8,
++      Wn3_Config=0, Wn3_MaxPktSize=4, Wn3_MAC_Ctrl=6, Wn3_Options=8,
+ };
+ #define BFEXT(value, offset, bitcount)  \
+@@ -679,7 +682,8 @@
+       Media_LnkBeat = 0x0800,
+ };
+ enum Window7 {                                        /* Window 7: Bus Master control. */
+-      Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
++      Wn7_MasterAddr = 0, Wn7_VlanEtherType=4, Wn7_MasterLen = 6,
++      Wn7_MasterStatus = 12,
+ };
+ /* Boomerang bus master control registers. */
+ enum MasterCtrl {
+@@ -776,7 +780,8 @@
+               pm_state_valid:1,                               /* power_state[] has sane contents */
+               open:1,
+               medialock:1,
+-              must_free_region:1;                             /* Flag: if zero, Cardbus owns the I/O region */
++              must_free_region:1,                             /* Flag: if zero, Cardbus owns the I/O region */
++              large_frames:1;                 /* accept large frames */
+       int drv_flags;
+       u16 status_enable;
+       u16 intr_enable;
+@@ -844,6 +849,9 @@
+ static int vortex_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+ static void vortex_tx_timeout(struct net_device *dev);
+ static void acpi_set_WOL(struct net_device *dev);
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++static void set_8021q_mode(struct net_device *dev, int enable);
++#endif
+ /* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
+ /* Option count limit only -- unlimited interfaces are supported. */
+@@ -1030,6 +1038,7 @@
+       dev->base_addr = ioaddr;
+       dev->irq = irq;
+       dev->mtu = mtu;
++      vp->large_frames = mtu > 1500;
+       vp->drv_flags = vci->drv_flags;
+       vp->has_nway = (vci->drv_flags & HAS_NWAY) ? 1 : 0;
+       vp->io_size = vci->io_size;
+@@ -1461,7 +1470,7 @@
+       /* Set the full-duplex bit. */
+       outw(   ((vp->info1 & 0x8000) || vp->full_duplex ? 0x20 : 0) |
+-                      (dev->mtu > 1500 ? 0x40 : 0) |
++                      (vp->large_frames ? 0x40 : 0) |
+                       ((vp->full_duplex && vp->flow_ctrl && vp->partner_flow_ctrl) ? 0x100 : 0),
+                       ioaddr + Wn3_MAC_Ctrl);
+@@ -1545,6 +1554,10 @@
+       }
+       /* Set receiver mode: presumably accept b-case and phys addr only. */
+       set_rx_mode(dev);
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++      /* enable 802.1q tagged frames */
++      set_8021q_mode(dev, 1);
++#endif
+       outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
+ //    issue_and_wait(dev, SetTxStart|0x07ff);
+@@ -1680,7 +1693,7 @@
+                                               /* Set the full-duplex bit. */
+                                               EL3WINDOW(3);
+                                               outw(   (vp->full_duplex ? 0x20 : 0) |
+-                                                              (dev->mtu > 1500 ? 0x40 : 0) |
++                                                              (vp->large_frames ? 0x40 : 0) |
+                                                               ((vp->full_duplex && vp->flow_ctrl && vp->partner_flow_ctrl) ? 0x100 : 0),
+                                                               ioaddr + Wn3_MAC_Ctrl);
+                                               if (vortex_debug > 1)
+@@ -1900,6 +1913,10 @@
+                       issue_and_wait(dev, RxReset|0x07);
+                       /* Set the Rx filter to the current state. */
+                       set_rx_mode(dev);
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++                      /* enable 802.1q VLAN tagged frames */
++                      set_8021q_mode(dev, 1);
++#endif
+                       outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
+                       outw(AckIntr | HostError, ioaddr + EL3_CMD);
+               }
+@@ -2497,6 +2514,11 @@
+       outw(RxDisable, ioaddr + EL3_CMD);
+       outw(TxDisable, ioaddr + EL3_CMD);
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++      /* Disable receiving 802.1q tagged frames */
++      set_8021q_mode(dev, 0);
++#endif
++
+       if (dev->if_port == XCVR_10base2)
+               /* Turn off thinnet power.  Green! */
+               outw(StopCoax, ioaddr + EL3_CMD);
+@@ -2760,6 +2782,50 @@
+       outw(new_mode, ioaddr + EL3_CMD);
+ }
++
++#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
++/* Setup the card so that it can receive frames with an 802.1q VLAN tag.
++   Note that this must be done after each RxReset due to some backwards
++   compatibility logic in the Cyclone and Tornado ASICs */
++static void set_8021q_mode(struct net_device *dev, int enable)
++{
++      struct vortex_private *vp = (struct vortex_private *)dev->priv;
++      long ioaddr = dev->base_addr;
++      int old_window = inw(ioaddr + EL3_CMD);
++      int mac_ctrl;
++      
++      if (vp->drv_flags&IS_CYCLONE || vp->drv_flags&IS_TORNADO) {
++              /* cyclone and tornado chipsets can recognize 802.1q
++               * tagged frames and treat them correctly */
++
++              int max_pkt_size = dev->mtu+14; /* MTU+Ethernet header */
++              if (enable)
++                      max_pkt_size += 4;      /* 802.1Q VLAN tag */
++
++              EL3WINDOW(3);
++              outw(max_pkt_size, ioaddr+Wn3_MaxPktSize);
++
++              /* set VlanEtherType to let the hardware checksumming
++                 treat tagged frames correctly */
++              EL3WINDOW(7);
++              outw(VLAN_ETHER_TYPE, ioaddr+Wn7_VlanEtherType);
++      } else {
++              /* on older cards we have to enable large frames */
++
++              vp->large_frames = dev->mtu > 1500 || enable;
++
++              EL3WINDOW(3);
++              mac_ctrl = inw(ioaddr+Wn3_MAC_Ctrl);
++              if (vp->large_frames)
++                      mac_ctrl |= 0x40;
++              else
++                      mac_ctrl &= ~0x40;
++              outw(mac_ctrl, ioaddr+Wn3_MAC_Ctrl);
++      }
++
++      EL3WINDOW(old_window);
++}
++#endif
+ /* MII transceiver control section.
+    Read and write the MII registers using software-generated serial
+</pre>
+</li>
+<P>
+
+<a name="pcmcia">
+<li><b>How to make my PCMCIA ethernet card work with VLANs?</b><P>
+Per Peter Stuge:<P>
+
+The problem was that the VLAN code in kernel header files weren't included
+properly when compiling the PCMCIA stuff.  Exactly why?  I'm not sure, might
+be because the PCMCIA stuff isn't the actual kernel and that means it's
+missing defines that trigger the VLAN #ifdefs.
+<P>
+Commenting the #ifdefs in linux/netdevice.h and adding code to clear out
+the struct vlan_dev_info* vlan_dev after having created the new network
+device in the PCMCIA client for the networking card did the trick if I
+remember correctly.  (The reason it doesn't work out-of-the-box is that the
+kernel VLAN code has garbage data for the VLAN fields in struct device since
+the device creator (PCMCIA client driver) doesn't know about them.)
+<P>
+Ben Adds:<P>
+To clear out the garbage, the PCMCIA driver needs to mset the net_device structure
+to zero (it should do this anyway..)  If anyone has a patch, please send it to me
+and the owners of the PCMCIA code...
+</pre>
+</li>
+
+</ol>
+</li>
+<P>
+<li><a name="targ4"><h3>Scripts and Recipes.</h3></a><P>
+ <ol>
+  <li><B>Mandrake (RedHat-style) startup script for VLANs.</B><P>
+   Contributed by: "MaxiM Basunov" &lt;maxim@idknet.com>
+<PRE>
+#!/bin/sh
+#
+# network       Bring up/down networking
+#
+# chkconfig: 2345 10 90
+# description: Activates/Deactivates all network interfaces configured to \
+#              start at boot time.
+# probe: true
+
+# Source function library.
+. /etc/init.d/functions
+
+if [ ! -f /etc/sysconfig/network ]; then
+    exit 0
+fi
+
+. /etc/sysconfig/network
+
+if [ -f /etc/sysconfig/pcmcia ]; then
+ . /etc/sysconfig/pcmcia
+fi
+
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+[ -x /sbin/ifconfig ] || exit 0
+
+# Even if IPX is configured, without the utilities we can't do much
+[ ! -x /sbin/ipx_internal_net -o ! -x /sbin/ipx_configure ] && IPX=
+
+CWD=`pwd`
+cd /etc/sysconfig/network-scripts
+
+# find all the interfaces besides loopback.
+# ignore aliases, alternative configurations, and editor backup files
+interfaces=`ls ifcfg* | egrep -v '(ifcfg-lo|:)' | \
+            egrep -v 'ifcfg-ippp[0-9]+$' | \
+>            egrep 'ifcfg-[a-z0-9\.]+$' | \
+&lt;            egrep 'ifcfg-[a-z0-9]+$' | \
+            sed 's/^ifcfg-//g'`
+
+# See how we were called.
+case "$1" in
+  start)
+
+   action "Setting network parameters: " sysctl -p /etc/sysctl.conf
+
+ action "Bringing up interface lo: " ./ifup ifcfg-lo
+
+ case "$IPX" in
+   yes|true)
+     /sbin/ipx_configure --auto_primary=$IPXAUTOPRIMARY \
+       --auto_interface=$IPXAUTOFRAME
+     if [ "$IPXINTERNALNETNUM" != "0" ]; then
+        /sbin/ipx_internal_net add $IPXINTERNALNETNUM $IPXINTERNALNODENUM
+     fi
+     ;;
+ esac
+ # depreciated but we still use it.
+ if [ -f /proc/sys/net/ipv4/ip_forward ] && [ "$FORWARD_IPV4" = "yes" ] ||
+ "$FORWARD_IPV4" = "true" ];
+     then
+  action "Enabling IPv4 packet forwarding" sysctl -w net.ipv4.ip_forward=1
+ fi
+
+> action "Setting VLAN parameters: " vconfig set_name_type DEV_PLUS_VID
+
+ for i in $interfaces; do
+  if egrep -L "^ONBOOT=\"?[Nn][Oo]\"?" ifcfg-$i >/dev/null 2>&1; then
+   # Probe module to preserve interface ordering
+   /sbin/ifconfig $i >/dev/null 2>&1
+  else
+>   vlan=`echo $i | egrep -v '(lo|:)' | \
+>        egrep -v 'ippp[0-9]+$' | \
+>        egrep '[a-z0-9]+\.[0-9][0-9][0-9][0-9]$' | \
+>        sed "s/^[a-z0-9]*\.//g;s/^0*//g"`
+>   ifvlan=`echo $i | egrep -v '(lo|:)' | \
+>        egrep -v 'ippp[0-9]+$' | \
+>           egrep '[a-z0-9]+\.[0-9][0-9][0-9][0-9]$' | \
+>        sed "s/\.[a-z0-9]*$//g"`
+
+>   if [ -n "${vlan}" ]; then
+>    action "Enable ${vlan} on {$ifvlan}: " vconfig add ${ifvlan} ${vlan}
+>   fi
+   action "Bringing up interface $i: " ./ifup $i boot
+  fi
+ done
+
+ # Add non interface-specific static-routes.
+ if [ -f /etc/sysconfig/static-routes ]; then
+    grep "^any" /etc/sysconfig/static-routes | while read ignore type dest
+netmask mask bogus args; do
+       if [ "${bogus}" = "gw" ]; then
+        /sbin/route add -$type $dest $netmask $mask $args
+       else
+        /sbin/route add -$type $dest $netmask $mask $bogus $args
+       fi
+    done
+ fi
+
+        touch /var/lock/subsys/network
+        ;;
+  stop)
+   # If this is a final shutdown/halt, check for network FS,
+ # and unmount them even if the user didn't turn on netfs
+
+ if [ "$RUNLEVEL" = "6" -o "$RUNLEVEL" = "0" -o "$RUNLEVEL" = "1" ]; then
+  NFSMTAB=`grep -v '^#' /proc/mounts | awk '{ if ($3 ~ /^nfs$/ ) print $2}'`
+  SMBMTAB=`grep -v '^#' /proc/mounts | awk '{ if ($3 ~ /^smbfs$/ ) print
+$2}'`
+  NCPMTAB=`grep -v '^#' /proc/mounts | awk '{ if ($3 ~ /^ncpfs$/ ) print
+$2}'`
+  if [ -n "$NFSMTAB" -o -n "$SMBMTAB" -o -n "$NCPMTAB" ] ; then
+   /etc/init.d/netfs stop
+  fi
+ fi
+
+ for i in $interfaces ; do
+         if ifconfig $i 2>/dev/null | grep -q "UP" >/dev/null 2>&1 ; then
+     action "Shutting down interface $i: " ./ifdown $i boot
+  fi
+ done
+ case "$IPX" in
+   yes|true)
+     if [ "$IPXINTERNALNETNUM" != "0" ]; then
+        /sbin/ipx_internal_net del
+     fi
+     ;;
+ esac
+ ./ifdown ifcfg-lo
+ if [ -d /proc/sys/net/ipv4 ]; then
+   if [ -f /proc/sys/net/ipv4/ip_forward ]; then
+  if [ `cat /proc/sys/net/ipv4/ip_forward` != 0 ]; then
+   action "Disabling IPv4 packet forwarding: " sysctl -w
+net.ipv4.ip_forward=0
+  fi
+   fi
+   if [ -f /proc/sys/net/ipv4/ip_always_defrag ]; then
+         if [ `cat /proc/sys/net/ipv4/ip_always_defrag` != 0 ]; then
+          action "Disabling IPv4 automatic defragmentation: " sysctl -w
+net.ipv4.ip_always_defrag=0
+  fi
+   fi
+ fi
+ if [ -f /proc/sys/net/ipv4/tcp_syncookies ];then
+         if [ `cat /proc/sys/net/ipv4/tcp_syncookies` != 0 ]; then
+      sysctl -w net.ipv4.tcp_syncookies=0
+  fi
+ fi
+
+        rm -f /var/lock/subsys/network
+        ;;
+  status)
+ echo "Configured devices:"
+ echo lo $interfaces
+
+ if [ -x /bin/linuxconf ] ; then
+  eval `/bin/linuxconf --hint netdev`
+  echo "Devices that are down:"
+  echo $DEV_UP
+  echo "Devices with modified configuration:"
+  echo $DEV_RECONF
+ else
+  echo "Currently active devices:"
+  echo `/sbin/ifconfig | grep ^[a-z] | awk '{print $1}'`
+ fi
+ ;;
+  restart)
+        cd $CWD
+ $0 stop
+ $0 start
+ ;;
+  reload)
+ if [ -x /bin/linuxconf ] ; then
+  eval `/bin/linuxconf --hint netdev`
+  for device in $DEV_UP ; do
+   action "Bringing up device $device: " ./ifup $device
+  done
+  for device in $DEV_DOWN ; do
+   action "Shutting down device $device: " ./ifdown $device
+  done
+  for device in $DEV_RECONF ; do
+   action "Shutting down device $device: " ./ifdown $device
+   action "Bringing up device $device: " ./ifup $device
+  done
+  for device in $DEV_RECONF_ALIASES ; do
+   action "Briging up alias $device: "
+/etc/sysconfig/network-scripts/ifup-aliases $device
+  done
+  for device in $DEV_RECONF_ROUTES ; do
+   action "Bringing up route $device: "
+/etc/sysconfig/network-scripts/ifup-routes $device
+  done
+  case $IPX in yes|true)
+    case $IPXINTERNALNET in
+      reconf)
+   action "Deleting internal IPX network: " /sbin/ipx_internal_net del
+   action "Adding internal IPX network $IPXINTERNALNETNUM
+$IPXINTERNALNODENUM: " /sbin/ipx_internal_net add $IPXINTERNALNETNUM \
+            $IPXINTERNALNODENUM
+   ;;
+      add)
+   action "Adding internal IPX network $IPXINTERNALNETNUM
+$IPXINTERNALNODENUM: "/sbin/ipx_internal_net add $IPXINTERNALNETNUM \
+            $IPXINTERNALNODENUM
+   ;;
+      del)
+   action "Deleting internal IPX network: " /sbin/ipx_internal_net del
+   ;;
+    esac
+    ;;
+  esac
+ else
+         cd $CWD
+  $0 restart
+ fi
+ ;;
+  probe)
+ if [ -x /bin/linuxconf ] ; then
+  eval `/bin/linuxconf --hint netdev`
+  [ -n "$DEV_UP$DEV_DOWN$DEV_RECONF$DEV_RECONF_ALIASES" -o \
+    -n "$DEV_RECONF_ROUTES$IPXINTERNALNET" ] && \
+   echo reload
+  exit 0
+ else
+  # if linuxconf isn't around to figure stuff out for us,
+  # we punt.  Probably better than completely reloading
+  # networking if user isn't sure which to do.  If user
+  # is sure, they would run restart or reload, not probe.
+  exit 0
+ fi
+ ;;
+  *)
+        echo "Usage: network {start|stop|restart|reload|status|probe}"
+        exit 1
+esac
+
+exit 0
+</pre>
+</li>
+
+</ol>
+<P>
+<HR>
+<pre>
+Terv, 
+
+-----------------------------
+Kristjan Kotkas 
+KPNQwest Estonia
+kristjan.kotkas@kpnqwest.ee
+t + 372 62 66299 m + 372 51 60697 f + 372 62 66292
+
+</pre>
+
+
+    <hr>
+    <address><a href="mailto:greear@cyberhighway.net">Ben Greear</a></address>
+<!-- Created: Mon May 29 12:17:35 MST 2000 -->
+<!-- hhmts start -->
+Last modified: Fri Jul  4 09:53:40 PDT 2003
+<!-- hhmts end -->
+  </body>
+</html>
diff --git a/libpcap-0.4/CVS/Entries b/libpcap-0.4/CVS/Entries
new file mode 100644 (file)
index 0000000..54e26d9
--- /dev/null
@@ -0,0 +1,5 @@
+D/SUNOS4////
+D/bpf////
+D/lbl////
+D/linux-include////
+D/net////
diff --git a/libpcap-0.4/CVS/Repository b/libpcap-0.4/CVS/Repository
new file mode 100644 (file)
index 0000000..0e0ddbb
--- /dev/null
@@ -0,0 +1 @@
+vlan/libpcap-0.4
diff --git a/libpcap-0.4/CVS/Root b/libpcap-0.4/CVS/Root
new file mode 100644 (file)
index 0000000..469c859
--- /dev/null
@@ -0,0 +1 @@
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
diff --git a/libpcap-0.4/SUNOS4/CVS/Entries b/libpcap-0.4/SUNOS4/CVS/Entries
new file mode 100644 (file)
index 0000000..1784810
--- /dev/null
@@ -0,0 +1 @@
+D
diff --git a/libpcap-0.4/SUNOS4/CVS/Repository b/libpcap-0.4/SUNOS4/CVS/Repository
new file mode 100644 (file)
index 0000000..3e7444c
--- /dev/null
@@ -0,0 +1 @@
+vlan/libpcap-0.4/SUNOS4
diff --git a/libpcap-0.4/SUNOS4/CVS/Root b/libpcap-0.4/SUNOS4/CVS/Root
new file mode 100644 (file)
index 0000000..469c859
--- /dev/null
@@ -0,0 +1 @@
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
diff --git a/libpcap-0.4/bpf/CVS/Entries b/libpcap-0.4/bpf/CVS/Entries
new file mode 100644 (file)
index 0000000..27ad449
--- /dev/null
@@ -0,0 +1 @@
+D/net////
diff --git a/libpcap-0.4/bpf/CVS/Repository b/libpcap-0.4/bpf/CVS/Repository
new file mode 100644 (file)
index 0000000..e2ce17c
--- /dev/null
@@ -0,0 +1 @@
+vlan/libpcap-0.4/bpf
diff --git a/libpcap-0.4/bpf/CVS/Root b/libpcap-0.4/bpf/CVS/Root
new file mode 100644 (file)
index 0000000..469c859
--- /dev/null
@@ -0,0 +1 @@
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
diff --git a/libpcap-0.4/bpf/net/CVS/Entries b/libpcap-0.4/bpf/net/CVS/Entries
new file mode 100644 (file)
index 0000000..1784810
--- /dev/null
@@ -0,0 +1 @@
+D
diff --git a/libpcap-0.4/bpf/net/CVS/Repository b/libpcap-0.4/bpf/net/CVS/Repository
new file mode 100644 (file)
index 0000000..c8ca0dc
--- /dev/null
@@ -0,0 +1 @@
+vlan/libpcap-0.4/bpf/net
diff --git a/libpcap-0.4/bpf/net/CVS/Root b/libpcap-0.4/bpf/net/CVS/Root
new file mode 100644 (file)
index 0000000..469c859
--- /dev/null
@@ -0,0 +1 @@
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
diff --git a/libpcap-0.4/lbl/CVS/Entries b/libpcap-0.4/lbl/CVS/Entries
new file mode 100644 (file)
index 0000000..1784810
--- /dev/null
@@ -0,0 +1 @@
+D
diff --git a/libpcap-0.4/lbl/CVS/Repository b/libpcap-0.4/lbl/CVS/Repository
new file mode 100644 (file)
index 0000000..b351c7f
--- /dev/null
@@ -0,0 +1 @@
+vlan/libpcap-0.4/lbl
diff --git a/libpcap-0.4/lbl/CVS/Root b/libpcap-0.4/lbl/CVS/Root
new file mode 100644 (file)
index 0000000..469c859
--- /dev/null
@@ -0,0 +1 @@
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
diff --git a/libpcap-0.4/linux-include/CVS/Entries b/libpcap-0.4/linux-include/CVS/Entries
new file mode 100644 (file)
index 0000000..7e34cc5
--- /dev/null
@@ -0,0 +1 @@
+D/netinet////
diff --git a/libpcap-0.4/linux-include/CVS/Repository b/libpcap-0.4/linux-include/CVS/Repository
new file mode 100644 (file)
index 0000000..6f3b3e1
--- /dev/null
@@ -0,0 +1 @@
+vlan/libpcap-0.4/linux-include
diff --git a/libpcap-0.4/linux-include/CVS/Root b/libpcap-0.4/linux-include/CVS/Root
new file mode 100644 (file)
index 0000000..469c859
--- /dev/null
@@ -0,0 +1 @@
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
diff --git a/libpcap-0.4/linux-include/netinet/CVS/Entries b/libpcap-0.4/linux-include/netinet/CVS/Entries
new file mode 100644 (file)
index 0000000..1784810
--- /dev/null
@@ -0,0 +1 @@
+D
diff --git a/libpcap-0.4/linux-include/netinet/CVS/Repository b/libpcap-0.4/linux-include/netinet/CVS/Repository
new file mode 100644 (file)
index 0000000..2604b84
--- /dev/null
@@ -0,0 +1 @@
+vlan/libpcap-0.4/linux-include/netinet
diff --git a/libpcap-0.4/linux-include/netinet/CVS/Root b/libpcap-0.4/linux-include/netinet/CVS/Root
new file mode 100644 (file)
index 0000000..469c859
--- /dev/null
@@ -0,0 +1 @@
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
diff --git a/libpcap-0.4/net/CVS/Entries b/libpcap-0.4/net/CVS/Entries
new file mode 100644 (file)
index 0000000..1784810
--- /dev/null
@@ -0,0 +1 @@
+D
diff --git a/libpcap-0.4/net/CVS/Repository b/libpcap-0.4/net/CVS/Repository
new file mode 100644 (file)
index 0000000..bff9ac2
--- /dev/null
@@ -0,0 +1 @@
+vlan/libpcap-0.4/net
diff --git a/libpcap-0.4/net/CVS/Root b/libpcap-0.4/net/CVS/Root
new file mode 100644 (file)
index 0000000..469c859
--- /dev/null
@@ -0,0 +1 @@
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
diff --git a/macvlan_config b/macvlan_config
new file mode 100755 (executable)
index 0000000..372ede0
Binary files /dev/null and b/macvlan_config differ
diff --git a/macvlan_config.c b/macvlan_config.c
new file mode 100644 (file)
index 0000000..5bc6723
--- /dev/null
@@ -0,0 +1,635 @@
+/*
+#######################################################################
+#
+# (C) Copyright 2001
+# Alex Zeffertt, Cambridge Broadband Ltd, ajz@cambridgebroadband.com
+#
+# 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.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#######################################################################
+# Notes:
+# 
+# This configuration utility communicates with macvlan.o, the MAC address
+# based VLAN support module.
+#
+# It uses an IOCTL interface which allows you to
+#
+# 1. enable/disable MAC address based VLANS over an ether type net_device
+# 2. add/remove a MAC address based VLAN - which is an ether type net_device
+#    layered over the original MACVLAN enabled ether type net_device.
+# 3. bind/unbind MAC addresses to/from particular MAC address based VLANs
+# 4. discover the state of MAC address based VLANs on the system.
+# 5. set/get port flags, including whether to bind to destination MAC
+#    or source mac.
+# 6. Traffic to/from eth0 will not be affected.
+
+
+#
+# Example: (Assuming you are using source binding)
+#
+# If you enable MAC address based VLANS over eth0
+#
+# You may then create further VLANs, e.g. eth0#1 eth0#2 ....
+# These will not receive any frames until you bind MAC addresses to them.
+# If you bind 11:22:33:44:55:66 to eth0#1, then any frames received by
+# eth0 with source MAC 11:22:33:44:55:66 will be routed up through eth0#1
+# instead of eth0.
+#
+# Example: (Assuming you are using destination (local) binding)
+#
+# If you enable MAC address based VLANS over eth0
+#
+# You may then create further VLANs, e.g. eth0#1 eth0#2 ....
+# These will not receive any frames until you bind MAC addresses to them.
+# If you bind 11:22:33:44:55:66 to eth0#1, then any broadcast/multicast
+# frames, or frames with a destination MAC 11:22:33:44:55:66
+# will be routed up through eth0#1 instead of eth0
+#
+# For broadcasts, the packet will be duplicated for every VLAN
+# with at least one MAC attached.  Attaching more than one MAC
+# when destination binding makes no sense...don't do it!
+#
+#
+# 
+#######################################################################
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/if_macvlan.h>
+#include <linux/sockios.h>
+#include <string.h>
+#include <errno.h>
+
+
+int do_help(int argc, char *argv[]);
+int do_enable(int argc, char *argv[]);
+int do_disable(int argc, char *argv[]);
+int do_add(int argc, char *argv[]);
+int do_del(int argc, char *argv[]);
+int do_bind(int argc, char *argv[]);
+int do_unbind(int argc, char *argv[]);
+int do_info(int argc, char *argv[]);
+int do_setflags(int argc, char *argv[]);
+int do_unload(int argc, char* argv[]);
+
+struct command {
+    char *name;
+    char *short_help;
+    int (*fn)(int argc, char *argv[]);
+    char *long_help;
+} command_list[] = {
+    {"help",   "help on other commands", do_help, "help <command>"},
+    {"enable", "enables mac based vlans over an ethernet device", do_enable,
+         "enable <ifname>\n"
+         " - enables mac based vlans over \"ifname\"\n"
+         " - also creates a default vlan over \"ifname\" called \"ifname#0\""
+    },
+    {"disable", "disables mac based vlans over an ethernet device", do_disable, "disable <ifname>"},
+    {"add", "creates new mac based vlan",  do_add,
+         "add <ifname> <index>\n"
+         " - creates a new mac based vlan called \"ifname#index\" layered over \"ifname\"\n"
+         " - mac based vlans over \"ifname\" must first be enabled with \"enable\"\n"
+         " - \"ifname#index\" is not mapped to any MAC address until \"bind\" is called"
+    },
+    {"del", "destroys a mac based vlan",  do_del,
+         "del <ifname>\n"
+         " - deletes a mac base vlan called \"ifname\""
+    },
+    {"bind", "binds macaddr to vlan",  do_bind,
+         "bind  <ifname> <macaddr>\n"
+         " - binds macaddr to vlan called \"ifname\""
+    },
+    {"unbind", "unbinds macaddr from vlan",  do_unbind,
+         "unbind <ifname> <macaddr>\n"
+         " - unbinds macaddr from vlan called \"ifname\""
+    },
+    {"unload", "Unconfigure all of the macvlan devices",
+     do_unload, "Unconfigure all of the macvlan devices so module can be unloaded"},
+    {"setflags", "Set port flags on a port",
+     do_setflags,
+         "setflags <ifname> <new_flags>\n"
+         "0x01   Bind to Destination instead of source MAC"
+    },
+    {"info", "print state of mac based vlans",  do_info, "info"},
+};
+#define NCOMMANDS (sizeof(command_list)/sizeof(struct command))
+
+
+int parseInt(const char* s) {
+   return strtol(s, NULL, 0); //should parse HEX, Octal, and Decimal.  If not decimal, must start with 0x
+}
+
+
+int do_help(int argc, char *argv[])
+{
+    unsigned int cmd;
+    if (argc < 2)
+        return -1;
+
+    for (cmd = 0; cmd < NCOMMANDS; cmd++) {
+        if (!strcmp(command_list[cmd].name,argv[1]))
+            break;
+    }
+    if (cmd == NCOMMANDS)
+        return -1;
+    puts(command_list[cmd].long_help);
+    return 0;
+}
+
+int do_enable(int argc, char *argv[])
+{
+    struct macvlan_ioctl req;
+    int s;
+
+    if (argc < 2) {
+        printf("usage: %s <ifname>\n", argv[0]);
+        return 1;
+    }
+
+    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+        perror("socket");
+        return 1;
+    }
+
+    req.cmd = MACVLAN_ENABLE;
+    req.ifname = argv[1];   /* 
+                             * name of ethernet device over which we 
+                             * are enabling mac based vlans
+                             */
+
+    if (ioctl(s, SIOCGIFMACVLAN, &req) < 0) {
+       if (errno != EEXIST) {
+          perror("ioctl (SIOCGIFMACVLAN, MACVLAN_ENABLE)");
+          printf("errno: %i\n", errno);
+          return 1;
+       }
+       else {
+          return 0;
+       }
+    }
+    return 0;
+}
+
+
+int do_setflags(int argc, char *argv[])
+{
+    struct macvlan_ioctl req;
+    int s;
+
+    if (argc < 3) {
+        printf("usage: %s <ifname> <flags>\n", argv[0]);
+        return 1;
+    }
+
+    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+        perror("socket");
+        return 1;
+    }
+
+    req.cmd = MACVLAN_SET_PORT_FLAGS;
+    req.ifname = argv[1];   /* 
+                             * name of ethernet device over which we 
+                             * are enabling mac based vlans
+                             */
+    req.ifidx = parseInt(argv[2]);
+
+    if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) {
+        perror("ioctl (SIOCGIFMACVLAN, SET_PORT_FLAGS)");
+        return 1;
+    }
+    return 0;
+}
+
+int _do_disable(char* port, int s) {
+    struct macvlan_ioctl req;
+       
+    req.cmd = MACVLAN_DISABLE;
+    req.ifname = port;   /* 
+                          * name of ethernet device over which we 
+                          * are disabling mac based vlans
+                          */
+
+    if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) {
+        perror("disable-port");
+        return -1;
+    }
+    else {
+       printf("Disabled port: %s\n", port);
+    }
+    return 0;
+}
+
+int do_disable(int argc, char *argv[])
+{
+    int s;
+
+    if (argc < 2) {
+        printf("usage: %s <ifname>\n", argv[0]);
+        return 1;
+    }
+
+    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+        perror("socket");
+        return 1;
+    }
+
+    return _do_disable(argv[1], s);
+}
+
+int do_add(int argc, char *argv[])
+{
+    int s;
+    struct macvlan_ioctl req;
+
+    if (argc < 3) {
+        printf("usage: %s <ifname> <index>\n", argv[0]);
+        return 1;
+    }
+
+    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+        perror("socket");
+        return 1;
+    }
+
+    req.cmd = MACVLAN_ADD;
+    req.ifname = argv[1];       /* name of lower layer i/f over which we are adding an upper layer i/f */
+    req.ifidx = parseInt(argv[2]);
+
+    if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) {
+        perror("ioctl (SIOCGIFMACVLAN, MACVLAN_ADD)");
+        return 1;
+    }
+    return 0;
+}
+
+int _do_del(char* ifname, int s) {
+    struct macvlan_ioctl req;
+   
+    req.cmd = MACVLAN_DEL;
+    req.ifname = ifname; /* name mac based vlan to destroy */
+    
+    if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) {
+        printf("failed to delete interface: %s\n", ifname);
+        perror("ioctl (SIOCGIFMACVLAN, MACVLAN_DEL)");
+        return -1;
+    }
+    else {
+       printf("Deleted interface: %s\n", ifname);
+    }
+    
+    return 0;
+}
+
+int do_del(int argc, char *argv[])
+{
+    int s;
+
+    if (argc < 2) {
+        printf("usage: %s <ifname>\n", argv[0]);
+        return 1;
+    }
+
+    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+        perror("socket");
+        return 1;
+    }
+
+    return _do_del(argv[1], s);
+}
+
+    
+
+int get_num_ports(int s) {
+   struct macvlan_ioctl req;
+   struct macvlan_ioctl_reply rep;
+   
+   req.cmd = MACVLAN_GET_NUM_PORTS;
+   req.reply = &rep;
+   if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) {
+      perror("ioctl (SIOCGIFMACVLAN, GET_NUM_PORTS)");
+      return -1;
+   }
+
+   printf("Found: %i ports\n", rep.num);
+
+   return rep.num;
+}
+
+int get_num_vlans(int portidx, int s) {
+    struct macvlan_ioctl req;
+    struct macvlan_ioctl_reply rep;
+   
+    /* Get the number of mac-based-vlans layered
+     * over this ethernet device 
+     */
+    req.cmd = MACVLAN_GET_NUM_VLANS;
+    req.portidx = portidx;
+    req.reply = &rep;
+    if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) {
+       perror("ioctl (GET_NUM_VLANS)");
+       return -1;
+    }
+    printf("Found: %i vlans for port: %i\n", rep.num, portidx);
+    return rep.num;
+}
+
+
+int htoi(char *s)
+{
+    char ch;
+    int i = 0;
+    while ((ch = *s++)) {
+        i <<= 4;
+        i += (ch>='0'&&ch<='9')?(ch-'0'):((ch>='a'&&ch<='f')?(ch-'a'+10):((ch>='A'&&ch<='F')?(ch-'A'+10):0));
+    }
+    return i;
+}
+
+int do_bind(int argc, char *argv[])
+{
+    int s;
+    struct macvlan_ioctl req;
+    char *ptr;
+    unsigned char macaddr[6];
+
+    if (argc < 3) {
+        printf("usage: %s <ifname> <macaddr>\n", argv[0]);
+        return 1;
+    }
+
+    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+        perror("socket");
+        return 1;
+    }
+
+    req.cmd = MACVLAN_BIND;
+    req.ifname = argv[1]; /* name of vlan to which we are binding a MAC address */
+
+    /* assemble the macaddr */
+    ptr = argv[2];
+    if (strlen(ptr) != 17) {
+        printf("bad macaddr format: need aa:bb:cc:dd:ee:ff\n");
+        return 1;
+    }
+    for (ptr = argv[2]+2; ptr < argv[2]+16; ptr+=3)
+        *ptr = 0;
+    ptr = argv[2];
+    macaddr[0] = (unsigned char)htoi(ptr);
+    macaddr[1] = (unsigned char)htoi(ptr+3);
+    macaddr[2] = (unsigned char)htoi(ptr+6);
+    macaddr[3] = (unsigned char)htoi(ptr+9);
+    macaddr[4] = (unsigned char)htoi(ptr+12);
+    macaddr[5] = (unsigned char)htoi(ptr+15);
+    req.macaddr = macaddr;
+
+    if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) {
+        perror("ioctl (MACVLAN_BIND)");
+        return 1;
+    }
+    return 0;
+}
+
+int do_unbind(int argc, char *argv[])
+{
+    int s;
+    struct macvlan_ioctl req;
+    char *ptr;
+    unsigned char macaddr[6];
+
+    if (argc < 3) {
+        printf("usage: %s <ifname> <macaddr>\n", argv[0]);
+        return 1;
+    }
+
+    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+        perror("socket");
+        return 1;
+    }
+
+    req.cmd = MACVLAN_UNBIND;
+    req.ifname = argv[1]; /* name of vlan from which we are deleting a MAC address */
+
+    /* assemble the macaddr */
+    ptr = argv[2];
+    if (strlen(ptr) != 17) {
+        printf("bad macaddr format: need aa:bb:cc:dd:ee:ff\n");
+        return 1;
+    }
+    for (ptr = argv[2]+2; ptr < argv[2]+16; ptr+=3)
+        *ptr = 0;
+    ptr = argv[2];
+    macaddr[0] = (unsigned char)htoi(ptr);
+    macaddr[1] = (unsigned char)htoi(ptr+3);
+    macaddr[2] = (unsigned char)htoi(ptr+6);
+    macaddr[3] = (unsigned char)htoi(ptr+9);
+    macaddr[4] = (unsigned char)htoi(ptr+12);
+    macaddr[5] = (unsigned char)htoi(ptr+15);
+    req.macaddr = macaddr;
+
+    if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) {
+        perror("ioctl (MACVLAN_UNBIND)");
+        return 1;
+    }
+    return 0;
+}
+
+int do_info(int argc, char *argv[])
+{
+    int s;
+    struct macvlan_ioctl req;
+    struct macvlan_ioctl_reply rep;
+    int nports;
+    int portidx;
+    int nifs;
+    int ifidx;
+    int nmacs;
+    int macidx;
+    unsigned char *p;
+
+    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+        perror("socket");
+        return 1;
+    }
+    /* get the number of ethernet devices which have mac based vlans
+     * enabled over them
+     */
+    req.cmd = MACVLAN_GET_NUM_PORTS;
+    req.reply = &rep;
+    if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) {
+        perror("ioctl (GET_NUM_PORTS)");
+        return 1;
+    }
+    nports = rep.num;
+    for (portidx = 0; portidx < nports; portidx++) {
+        char tmpifname[64];
+        /* Get the name of this mac-based-vlan enabled 
+         * ethernet device
+         */
+        req.cmd = MACVLAN_GET_PORT_NAME;
+        req.portidx = portidx;
+        req.reply = &rep;
+        if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) {
+            perror("ioctl (GET_PORT_NAME)");
+            return 1;
+        }
+        printf("-%s\n", rep.name);
+
+        /* get the port flags */
+        req.cmd = MACVLAN_GET_PORT_FLAGS;
+        req.portidx = portidx;
+        strcpy(tmpifname, rep.name);
+        req.ifname = tmpifname;
+        req.reply = &rep;
+        if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) {
+            perror("ioctl (GET_PORT_FLAGS)");
+            return 1;
+        }
+        printf("-%s flag: 0x%x\n", tmpifname, rep.num);
+
+        /* Get the number of mac-based-vlans layered
+         * over this ethernet device 
+         */
+        req.cmd = MACVLAN_GET_NUM_VLANS;
+        req.portidx = portidx;
+        req.reply = &rep;
+        if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) {
+            perror("ioctl (GET_NUM_VLANS)");
+            return 1;
+        }
+        nifs = rep.num;
+        for (ifidx = 0; ifidx < nifs; ifidx++) {
+            /* Get the name of this vlan */
+            req.cmd = MACVLAN_GET_VLAN_NAME;
+            req.portidx = portidx;
+            req.ifidx = ifidx;
+            req.reply = &rep;
+            if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) {
+                perror("ioctl (GET_VLAN_NAME)");
+                return 1;
+            }
+            /* get the number of mac addresses owned by this vlan */
+            printf(" |-%s\n", rep.name);
+            req.cmd = MACVLAN_GET_NUM_MACS;
+            req.portidx = portidx;
+            req.ifidx = ifidx;
+            req.reply = &rep;
+            if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) {
+                perror("ioctl (GET_NUM_MACS)");
+                return 1;
+            }
+            nmacs = rep.num;
+            for (macidx = 0; macidx < nmacs; macidx++) {
+                /* get the value of this mac address */
+                req.cmd = MACVLAN_GET_MAC_NAME;
+                req.portidx = portidx;
+                req.ifidx = ifidx;
+                req.macaddridx = macidx;
+                req.reply = &rep;
+                if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) {
+                    perror("ioctl (GET_MAC_NAME)");
+                    return 1;
+                }
+                p = (unsigned char *) rep.name;
+                printf(" | |-%02x:%02x:%02x:%02x:%02x:%02x\n", 
+                       p[0],p[1],p[2],p[3],p[4],p[5]);
+            }
+        }
+    }
+    return 0;
+}
+
+
+int do_unload(int argc, char *argv[])
+{
+    int s;
+    struct macvlan_ioctl req;
+    struct macvlan_ioctl_reply rep;
+
+    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+        perror("socket");
+        return 1;
+    }
+
+    while (get_num_ports(s) > 0) {
+       char port[64];
+       /* Get the name of this mac-based-vlan enabled 
+        * ethernet device
+        */
+       req.cmd = MACVLAN_GET_PORT_NAME;
+       req.portidx = 0;
+       req.reply = &rep;
+       if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) {
+          perror("ioctl (GET_PORT_NAME)");
+          return 1;
+       }
+       strcpy(port, rep.name);
+       
+       while (get_num_vlans(0, s) > 0) {
+          char cmd[128];
+          /* Get the name of this vlan */
+          req.cmd = MACVLAN_GET_VLAN_NAME;
+          req.portidx = 0;
+          req.ifidx = 0;
+          req.reply = &rep;
+          if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) {
+             perror("ioctl (GET_VLAN_NAME)");
+             return 1;
+          }
+
+          /* Configure down the vlan */
+          /* This would be faster using IOCTLs, of course! */
+          printf("Configuring down interface: %s with ifconfig...", rep.name);
+          sprintf(cmd, "ifconfig %s down", rep.name);
+          system(cmd);
+
+          /* Now, can remove it */
+          _do_del(rep.name, s);
+       }
+
+       /* Now, remove the port */
+       _do_disable(port, s);
+          
+    }
+    return 0;
+}
+
+int main(int argc, char *argv[])
+{
+    unsigned int cmd;
+    int err;
+    
+    if (argc < 2)
+        goto usage;
+
+    for (cmd = 0; cmd < NCOMMANDS; cmd++) {
+        if (!strcmp(command_list[cmd].name,argv[1]))
+            break;
+    }
+    if (cmd == NCOMMANDS)
+        goto usage;
+
+    if ((err = command_list[cmd].fn(argc-1,argv+1)))
+        goto usage;
+    return 0;
+
+ usage:
+    printf("\n%s subcommands:\n\n", argv[0]);
+    for (cmd = 0; cmd < NCOMMANDS; cmd++) {
+        printf("%s %s:\t%s\n",argv[0],command_list[cmd].name,command_list[cmd].short_help);
+    }
+    return err;
+}
diff --git a/packaging/vconfig.manifest b/packaging/vconfig.manifest
new file mode 100644 (file)
index 0000000..75b0fa5
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+    <request>
+        <domain name="_"/>
+    </request>
+</manifest>
diff --git a/packaging/vconfig.spec b/packaging/vconfig.spec
new file mode 100644 (file)
index 0000000..a5cb2b8
--- /dev/null
@@ -0,0 +1,39 @@
+Summary: Linux 802.1q VLAN configuration utility
+Name: vconfig
+Version: 1.9
+Release: 14
+License: GPLv2+
+Group: System Environment/Base
+Source: http://www.candelatech.com/~greear/vlan/vlan.%{version}.tar.gz
+Source1001: %{name}.manifest
+URL: http://www.candelatech.com/~greear/vlan.html
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+%define _sbin /sbin
+
+%description 
+The vconfig program configures and adjusts 802.1q VLAN parameters.
+
+%prep
+%setup -q -n vlan.%{version}
+
+%build
+cp %{SOURCE1001} .
+make clean
+rm -f vconfig
+make CCFLAGS="%{optflags}" STRIP=/bin/true vconfig
+
+%install
+rm -rf ${RPM_BUILD_ROOT}
+%{__install} -D -m755 vconfig ${RPM_BUILD_ROOT}%{_sbin}/vconfig
+%{__install} -D -m644 vconfig.8 ${RPM_BUILD_ROOT}%{_mandir}/man8/vconfig.8
+rm -rf contrib/CVS
+
+%clean 
+rm -rf ${RPM_BUILD_ROOT}
+
+%files 
+%defattr(-, root, root, 0755)
+%manifest %{name}.manifest
+%doc CHANGELOG contrib README vlan.html vlan_test.pl
+%{_sbin}/vconfig
+%{_mandir}/man8/vconfig.8*
diff --git a/tcpdump-3.4/CVS/Entries b/tcpdump-3.4/CVS/Entries
new file mode 100644 (file)
index 0000000..baec772
--- /dev/null
@@ -0,0 +1,2 @@
+D/lbl////
+D/linux-include////
diff --git a/tcpdump-3.4/CVS/Repository b/tcpdump-3.4/CVS/Repository
new file mode 100644 (file)
index 0000000..7759328
--- /dev/null
@@ -0,0 +1 @@
+vlan/tcpdump-3.4
diff --git a/tcpdump-3.4/CVS/Root b/tcpdump-3.4/CVS/Root
new file mode 100644 (file)
index 0000000..469c859
--- /dev/null
@@ -0,0 +1 @@
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
diff --git a/tcpdump-3.4/lbl/CVS/Entries b/tcpdump-3.4/lbl/CVS/Entries
new file mode 100644 (file)
index 0000000..1784810
--- /dev/null
@@ -0,0 +1 @@
+D
diff --git a/tcpdump-3.4/lbl/CVS/Repository b/tcpdump-3.4/lbl/CVS/Repository
new file mode 100644 (file)
index 0000000..f9d5b2f
--- /dev/null
@@ -0,0 +1 @@
+vlan/tcpdump-3.4/lbl
diff --git a/tcpdump-3.4/lbl/CVS/Root b/tcpdump-3.4/lbl/CVS/Root
new file mode 100644 (file)
index 0000000..469c859
--- /dev/null
@@ -0,0 +1 @@
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
diff --git a/tcpdump-3.4/linux-include/CVS/Entries b/tcpdump-3.4/linux-include/CVS/Entries
new file mode 100644 (file)
index 0000000..d74c534
--- /dev/null
@@ -0,0 +1,3 @@
+D/net////
+D/netinet////
+D/sys////
diff --git a/tcpdump-3.4/linux-include/CVS/Repository b/tcpdump-3.4/linux-include/CVS/Repository
new file mode 100644 (file)
index 0000000..63abf70
--- /dev/null
@@ -0,0 +1 @@
+vlan/tcpdump-3.4/linux-include
diff --git a/tcpdump-3.4/linux-include/CVS/Root b/tcpdump-3.4/linux-include/CVS/Root
new file mode 100644 (file)
index 0000000..469c859
--- /dev/null
@@ -0,0 +1 @@
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
diff --git a/tcpdump-3.4/linux-include/net/CVS/Entries b/tcpdump-3.4/linux-include/net/CVS/Entries
new file mode 100644 (file)
index 0000000..1784810
--- /dev/null
@@ -0,0 +1 @@
+D
diff --git a/tcpdump-3.4/linux-include/net/CVS/Repository b/tcpdump-3.4/linux-include/net/CVS/Repository
new file mode 100644 (file)
index 0000000..72afc29
--- /dev/null
@@ -0,0 +1 @@
+vlan/tcpdump-3.4/linux-include/net
diff --git a/tcpdump-3.4/linux-include/net/CVS/Root b/tcpdump-3.4/linux-include/net/CVS/Root
new file mode 100644 (file)
index 0000000..469c859
--- /dev/null
@@ -0,0 +1 @@
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
diff --git a/tcpdump-3.4/linux-include/netinet/CVS/Entries b/tcpdump-3.4/linux-include/netinet/CVS/Entries
new file mode 100644 (file)
index 0000000..1784810
--- /dev/null
@@ -0,0 +1 @@
+D
diff --git a/tcpdump-3.4/linux-include/netinet/CVS/Repository b/tcpdump-3.4/linux-include/netinet/CVS/Repository
new file mode 100644 (file)
index 0000000..3ae1a1b
--- /dev/null
@@ -0,0 +1 @@
+vlan/tcpdump-3.4/linux-include/netinet
diff --git a/tcpdump-3.4/linux-include/netinet/CVS/Root b/tcpdump-3.4/linux-include/netinet/CVS/Root
new file mode 100644 (file)
index 0000000..469c859
--- /dev/null
@@ -0,0 +1 @@
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
diff --git a/tcpdump-3.4/linux-include/sys/CVS/Entries b/tcpdump-3.4/linux-include/sys/CVS/Entries
new file mode 100644 (file)
index 0000000..1784810
--- /dev/null
@@ -0,0 +1 @@
+D
diff --git a/tcpdump-3.4/linux-include/sys/CVS/Repository b/tcpdump-3.4/linux-include/sys/CVS/Repository
new file mode 100644 (file)
index 0000000..6a6e5d2
--- /dev/null
@@ -0,0 +1 @@
+vlan/tcpdump-3.4/linux-include/sys
diff --git a/tcpdump-3.4/linux-include/sys/CVS/Root b/tcpdump-3.4/linux-include/sys/CVS/Root
new file mode 100644 (file)
index 0000000..469c859
--- /dev/null
@@ -0,0 +1 @@
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
diff --git a/vconfig b/vconfig
new file mode 100755 (executable)
index 0000000..8cfcbcd
Binary files /dev/null and b/vconfig differ
diff --git a/vconfig.8 b/vconfig.8
new file mode 100644 (file)
index 0000000..e9685c2
--- /dev/null
+++ b/vconfig.8
@@ -0,0 +1,68 @@
+.TH VCONFIG 8
+.\" NAME should be all caps, SECTION should be 1-8, maybe w/ subsection
+.\" other parms are allowed: see man(7), man(1)
+.SH NAME
+vconfig \- VLAN (802.1q) configuration program.
+.SH SYNOPSIS
+.B vconfig
+.I [lots of long options]
+.SH "DESCRIPTION"
+The
+.B vconfig
+program allows you to create and remove vlan\-devices on a vlan enabled
+kernel. Vlan\-devices are virtual ethernet devices which represents the
+virtual lans on the physical lan.
+.SH OPTIONS
+.TP
+.B add [interface\-name] [vlan\-id]
+Creates a vlan-device on [interface\-name]. The resulting vlan\-device
+will be called according to the nameing convention set.
+.TP
+.B rem [vlan\-device]
+Removes the named vlan\-device.
+.TP
+.B set_flag [vlan\-device] 0 | 1
+When 1, ethernet header reorders are turned on. Dumping the device
+will appear as a common ethernet device without vlans. When 0(default)
+however, ethernet headers are not reordered, which results in vlan tagged
+packets when dumping the device. Usually the default gives no problems,
+but some packet filtering programs might have problems with it.
+.TP
+
+.B set_egress_map [vlan\-device] [skb\-priority] [vlan\-qos]
+This flags that outbound packets with a particular skb\-priority should
+be tagged with the particular vlan priority vlan\-qos. The default vlan
+priority is 0.
+.TP
+
+.B set_ingress_map [vlan\-device] [skb\-priority] [vlan\-qos]
+This flags that inbound packets with the particular vlan priority
+vlan\-qos should be queued with a particular skb\-priority. The default
+skb\-priority is 0.
+.TP
+
+.B set_name_type VLAN_PLUS_VID | VLAN_PLUS_VID_NO_PAD | DEV_PLUS_VID | DEV_PLUS_VID_NO_PAD
+Sets the way vlan\-device names are created. Use vconfig without arguments
+to see the different formats.
+.TP
+
+.SH NOTES
+VLAN will use Broadcom's NICE interface when the network device supports
+it. This is necessary, since usually the hardware of these devices already
+removes the vlan tag from the ethernet packet. The set_flag option on
+vlan\-devices created on such a physical network device will be ignored.
+Dumping the network\-device will show only untagged(non-vlan) traffic,
+and dumping the vlan\-devices will only show traffic intended for that
+vlan, without the tags.
+.br
+.SH FILES
+.I /proc/net/vlan/config
+.br
+.I /proc/net/vlan/[vlan\-device]
+
+.SH SEE ALSO
+ip(8), ifconfig(8)
+.SH AUTHORS
+This manual page was written by Ard van Breemen <ard@kwaak.net>
+.br
+The vlan patch is written by Ben Greear <greearb@candelatech.com>
diff --git a/vconfig.c b/vconfig.c
new file mode 100644 (file)
index 0000000..5057cfd
--- /dev/null
+++ b/vconfig.c
@@ -0,0 +1,270 @@
+//
+//Copyright (C) 2001  Ben Greear
+//
+//This program is free software; you can redistribute it and/or
+//modify it under the terms of the GNU Library General Public License
+//as published by the Free Software Foundation; either version 2
+//of the License, or (at your option) any later version.
+//
+//This program is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//GNU General Public License for more details.
+//
+//You should have received a copy of the GNU Library General Public License
+//along with this program; if not, write to the Free Software
+//Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// To contact the Author, Ben Greear:  greearb@candelatech.com
+//
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <sys/ioctl.h>
+#include <linux/if_vlan.h>
+#include <linux/sockios.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+
+#define MAX_HOSTNAME 256
+
+
+static char* usage = 
+      "\n"
+"Usage: add             [interface-name] [vlan_id]\n"
+"       rem             [vlan-name]\n"
+"       set_flag        [interface-name] [flag-num]       [0 | 1]\n"
+"       set_egress_map  [vlan-name]      [skb_priority]   [vlan_qos]\n"
+"       set_ingress_map [vlan-name]      [skb_priority]   [vlan_qos]\n"
+"       set_name_type   [name-type]\n"
+"\n"
+"* The [interface-name] is the name of the ethernet card that hosts\n"
+"  the VLAN you are talking about.\n"
+"* The vlan_id is the identifier (0-4095) of the VLAN you are operating on.\n"
+"* skb_priority is the priority in the socket buffer (sk_buff).\n"
+"* vlan_qos is the 3 bit priority in the VLAN header\n"
+"* name-type:  VLAN_PLUS_VID (vlan0005), VLAN_PLUS_VID_NO_PAD (vlan5),\n"
+"              DEV_PLUS_VID (eth0.0005), DEV_PLUS_VID_NO_PAD (eth0.5)\n"
+"* bind-type:  PER_DEVICE  # Allows vlan 5 on eth0 and eth1 to be unique.\n"
+"              PER_KERNEL  # Forces vlan 5 to be unique across all devices.\n"
+"* FLAGS:  1 REORDER_HDR  When this is set, the VLAN device will move the\n"
+"            ethernet header around to make it look exactly like a real\n"
+"            ethernet device.  This may help programs such as DHCPd which\n"
+"            read the raw ethernet packet and make assumptions about the\n"
+"            location of bytes.  If you don't need it, don't turn it on, because\n"
+"            there will be at least a small performance degradation.  Default\n"
+"            is OFF.\n";
+
+void show_usage() {
+   fprintf(stdout,usage);
+}
+
+int hex_to_bytes(char* bytes, int bytes_length, char* hex_str) {
+   int hlen;
+   int i;
+   
+   int j = 0;
+   char hex[3];
+   char* stop; /* not used for any real purpose */
+
+   hlen = strlen(hex_str);
+
+   hex[2] = 0;
+
+   for (i = 0; i<hlen; i++) {
+
+      hex[0] = hex_str[i];
+      i++;
+      if (i >= hlen) {
+         return j; /* done */
+      }
+      
+      hex[1] = hex_str[i];
+      bytes[j++] = (char)strtoul(hex, &stop, 16);
+   }
+   return j;
+}
+
+
+int main(int argc, char** argv) {
+   int fd;
+   struct vlan_ioctl_args if_request;
+   
+   char* cmd = NULL;
+   char* if_name = NULL;
+   unsigned int vid = 0;
+   unsigned int skb_priority;
+   unsigned short vlan_qos;
+   unsigned int nm_type = VLAN_NAME_TYPE_PLUS_VID;
+
+   char* conf_file_name = "/proc/net/vlan/config";
+
+   memset(&if_request, 0, sizeof(struct vlan_ioctl_args));
+   
+   if ((argc < 3) || (argc > 5)) {
+      fprintf(stdout,"Expecting argc to be 3-5, inclusive.  Was: %d\n",argc);
+
+      show_usage();
+      exit(1);
+   }
+   else {
+      cmd = argv[1];
+          
+      if (strcasecmp(cmd, "set_name_type") == 0) {
+         if (strcasecmp(argv[2], "VLAN_PLUS_VID") == 0) {
+            nm_type = VLAN_NAME_TYPE_PLUS_VID;
+         }
+         else if (strcasecmp(argv[2], "VLAN_PLUS_VID_NO_PAD") == 0) {
+            nm_type = VLAN_NAME_TYPE_PLUS_VID_NO_PAD;
+         }
+         else if (strcasecmp(argv[2], "DEV_PLUS_VID") == 0) {
+            nm_type = VLAN_NAME_TYPE_RAW_PLUS_VID;
+         }
+         else if (strcasecmp(argv[2], "DEV_PLUS_VID_NO_PAD") == 0) {
+            nm_type = VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD;
+         }
+         else {
+            // MATHIEU
+            //cerr << "Invalid name type.\n";
+            fprintf(stderr,"Invalid name type.\n");
+                                 
+            show_usage();
+            exit(1);
+         }
+         if_request.u.name_type = nm_type;
+      }
+      else {
+         if_name = argv[2];
+         if (strlen(if_name) > 15) {
+            // MATHIEU
+            //cerr << "ERROR:  if_name must be 15 characters or less." << endl;
+            fprintf(stderr,"ERROR:  if_name must be 15 characters or less.\n");            
+            exit(1);
+         }
+         strcpy(if_request.device1, if_name);
+      }
+
+      if (argc == 4) {
+         vid = atoi(argv[3]);
+         if_request.u.VID = vid;
+      }
+
+      if (argc == 5) {
+         skb_priority = atoi(argv[3]);
+         vlan_qos = atoi(argv[4]);
+         if_request.u.skb_priority = skb_priority;
+         if_request.vlan_qos = vlan_qos;
+      }
+   }
+   
+   // Open up the /proc/vlan/config
+   if ((fd = open(conf_file_name, O_RDONLY)) < 0) {
+      // MATHIEU
+      //cerr << "ERROR:  Could not open /proc/vlan/config.\n";
+      fprintf(stderr,"WARNING:  Could not open /proc/net/vlan/config.  Maybe you need to load the 8021q module, or maybe you are not using PROCFS??\n");
+          
+   }
+   else {
+      close(fd);
+   }
+
+   /* We use sockets now, instead of the file descriptor */
+   if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+      fprintf(stderr, "FATAL:  Couldn't open a socket..go figure!\n");
+      exit(2);
+   }   
+
+   /* add */
+   if (strcasecmp(cmd, "add") == 0) {
+      if_request.cmd = ADD_VLAN_CMD;
+      if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+         fprintf(stderr,"ERROR: trying to add VLAN #%u to IF -:%s:-  error: %s\n",
+                    vid, if_name, strerror(errno));
+         exit(3);
+      }
+      else {
+         fprintf(stdout,"Added VLAN with VID == %u to IF -:%s:-\n",
+                 vid, if_name);
+         if (vid == 1) {
+            fprintf(stdout, "WARNING:  VLAN 1 does not work with many switches,\nconsider another number if you have problems.\n");
+         }
+      }
+   }//if
+   else if (strcasecmp(cmd, "rem") == 0) {
+      if_request.cmd = DEL_VLAN_CMD;
+      if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+         fprintf(stderr,"ERROR: trying to remove VLAN -:%s:- error: %s\n",
+                 if_name, strerror(errno));
+         exit(4);
+      }
+      else {
+         fprintf(stdout,"Removed VLAN -:%s:-\n", if_name);
+      }
+   }//if
+   else if (strcasecmp(cmd, "set_egress_map") == 0) {
+      if_request.cmd = SET_VLAN_EGRESS_PRIORITY_CMD;
+      if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+         fprintf(stderr,"ERROR: trying to set egress map on device -:%s:- error: %s\n",
+                 if_name, strerror(errno));
+         exit(5);
+      }
+      else {
+         fprintf(stdout,"Set egress mapping on device -:%s:- "
+                 "Should be visible in /proc/net/vlan/%s\n",
+                 if_name, if_name);
+      }
+   }
+   else if (strcasecmp(cmd, "set_ingress_map") == 0) {
+      if_request.cmd = SET_VLAN_INGRESS_PRIORITY_CMD;
+      if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+         fprintf(stderr,"ERROR: trying to set ingress map on device -:%s:- error: %s\n",
+                 if_name, strerror(errno));
+         exit(6);
+      }
+      else {
+         fprintf(stdout,"Set ingress mapping on device -:%s:- "
+                 "Should be visible in /proc/net/vlan/%s\n",
+                 if_name, if_name);                
+      }
+   }   
+   else if (strcasecmp(cmd, "set_flag") == 0) {
+      if_request.cmd = SET_VLAN_FLAG_CMD;
+      if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+         fprintf(stderr,"ERROR: trying to set flag on device -:%s:- error: %s\n",
+                 if_name, strerror(errno));
+         exit(7);
+      }
+      else {
+         fprintf(stdout,"Set flag on device -:%s:- "
+                 "Should be visible in /proc/net/vlan/%s\n",
+                 if_name, if_name);
+      }
+   }
+   else if (strcasecmp(cmd, "set_name_type") == 0) {
+      if_request.cmd = SET_VLAN_NAME_TYPE_CMD;
+      if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+         fprintf(stderr,"ERROR: trying to set name type for VLAN subsystem, error: %s\n",
+                 strerror(errno));
+         exit(8);
+      }
+      else {
+         fprintf(stdout,"Set name-type for VLAN subsystem."
+                 " Should be visible in /proc/net/vlan/config\n");         
+      }
+   }
+   else {
+      fprintf(stderr, "Unknown command -:%s:-\n", cmd);
+
+      show_usage();
+      exit(5);
+   }
+
+   return 0;
+}/* main */
diff --git a/vconfig.h b/vconfig.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/vconfig.o b/vconfig.o
new file mode 100644 (file)
index 0000000..8e8da0e
Binary files /dev/null and b/vconfig.o differ
diff --git a/vconfig.spec b/vconfig.spec
new file mode 100644 (file)
index 0000000..c05c7b5
--- /dev/null
@@ -0,0 +1,61 @@
+Summary: Linux 802.1q VLAN configuration utility
+Name: vconfig
+Version: 1.6
+Release: 4
+License: distributable
+Group: Applications/System
+Source: http://scry.wanfear.com/~greear/vlan/vlan.%{version}.tar.gz
+Source1: ifup-vlan
+Source2: ifdown-vlan
+Source3: vlan.sysconfig
+Source4: README.ifup
+Source5: ifcfg-vlan2-example
+URL: http://scry.wanfear.com/~greear/vlan.html
+BuildRoot: %{_tmppath}/%{name}-%{version}-root
+Packager: Dale Bewley <dale@bewley.net>
+BuildRequires: kernel-source >= 2.4.14
+Requires: kernel >= 2.4.14
+
+%description 
+802.1q VLAN support is now in the linux kernel as of 2.4.14. 
+This package provides the vlan configuration utility, vconfig,
+and ifup and ifdown scripts for configuring interfaces.
+
+%prep
+%setup -q -n vlan
+cp %SOURCE4 .
+cp %SOURCE5 .
+
+%build
+make
+
+%clean 
+rm -rf $RPM_BUILD_ROOT
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/%{_sbindir}
+mkdir -p $RPM_BUILD_ROOT/etc/sysconfig/network-scripts
+mkdir -p $RPM_BUILD_ROOT/%{_mandir}/man8
+install -o 0 -g 0 -m 755 vconfig $RPM_BUILD_ROOT/%{_sbindir}/vconfig
+install -o 0 -g 0 -m 755 vconfig.8 $RPM_BUILD_ROOT/%{_mandir}/man8
+install -o 0 -g 0 -m 755 %SOURCE1 $RPM_BUILD_ROOT/etc/sysconfig/network-scripts/ifup-vlan
+install -o 0 -g 0 -m 755 %SOURCE2 $RPM_BUILD_ROOT/etc/sysconfig/network-scripts/ifdown-vlan
+install -o 0 -g 0 -m 644 %SOURCE3 $RPM_BUILD_ROOT/etc/sysconfig/vlan
+
+%files 
+%defattr(-,root,root)
+%doc CHANGELOG contrib README README.ifup vlan.html vlan_test.pl ifcfg-vlan2-example
+%{_sbindir}/vconfig
+%{_mandir}/man8/vconfig.8.gz
+/etc/sysconfig/vlan
+/etc/sysconfig/network-scripts/ifup-vlan
+/etc/sysconfig/network-scripts/ifdown-vlan
+
+%changelog
+* Fri Apr 05 2002 Dale Bewley <dale@bewley.net>
+- update to 1.6
+- add ifup scripts
+
+* Tue Dec 11 2001 Dale Bewley <dale@bewley.net>
+- initial specfile
diff --git a/vlan.html b/vlan.html
new file mode 100644 (file)
index 0000000..0588b61
--- /dev/null
+++ b/vlan.html
@@ -0,0 +1,436 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+  <head>
+    <title>802.1Q VLAN implementation for Linux</title>
+  </head>
+
+  <body bgcolor=#ffffff text=#000000>
+    <h1><center>802.1Q VLAN implementation for Linux</center></h1>
+
+<center><i>
+Updated Sept 30, 2003<br>
+Release:  1.8</br>
+</i></center>
+<P>
+
+MTU problems exist for many ethernet drivers.  Other than that, things seem fairly stable!
+<P>
+
+<center>
+<B>PLUG: &nbsp; Check out my company that makes traffic generation and WAN simulation
+   test equipment based on the Linux operating system:<br>
+    <a target=_top href="http://www.candelatech.com"><img src="http://www.candelatech.com/images/candela_swirl_small.png"
+                                                      alt="Candela Technologies"
+                                                      border=0></a>
+<br>
+Let us help you test your DSL, Cable Access, Satellite and other network systems!</b>
+</center>
+<font size = -1>
+<BR>
+TIP jar on <a href="http://www.candelatech.com/~greear" target="_top"> my home page.</a><P>
+</font>
+
+Join the <a HREF="http://ns1.wanfear.com/mailman/listinfo/vlan">vlan mailing list</a>, 
+ After that, to post, send mail to 
+<A HREF="mailto:vlan@ns1.wanfear.com">vlan@ns1.wanfear.com</a>.
+<P>
+Submit a bug/issue/enhancement with the:  <a href="http://grok.yi.org:8080/~greear/bugzilla/enter_bug.cgi?product=VLAN%20for%20Linux">VLAN Bugzilla</a></li>
+<P>
+
+I hear that the 2.2/2.4 kernel patches have worked
+with these (and other, I'm sure) systems: <P>
+<ul>
+  <li> Cisco: {Catalyst: 6509}, 
+       3Com: {Corebuilder, Netbuilder II, SuperStack II switch 630},
+       Alpine: {3804(SMMi,F32Ti)}
+       Extreme Ntwks {Summit 48, 48i, 5i}
+       Foundry: {ServerIronXL, FastIron}</li>
+  <li> Alteon ACENic Gigabit, 3Com 3c509, realtek RTL8029(AS), RTL8139, DEC DC21140 (tulip),
+       DFE-570TX quad-21143, Intel PRO/1000 with Intel's driver
+  </li>
+</ul>
+<P>
+
+<u><b>Performance:</b></u>
+The difference in running traffic over VLANs v/s regular ethernet is very slight.  If
+someone has done some sort of benchmark, I'll be happy to place it here!
+
+<b><center>VLAN related Resources.</center></b>
+<ul>
+<li> <a href="#setup"> VLAN Installation & Configuration info.</a></li>
+<li> <a href="#cvs_setup"> CVS Access.</a></li>
+<li> <a href="vlan/howto.html"> VLAN HOWTO/FAQ (Some CISCO & 3COM specific info too.)</a></li>
+<li> <a href="http://www.planetconnect.com/vlan/"> Another VLAN Recipe (Some info specific to Intel EEPRO Nics too.)</a></il>
+<li> <a href="http://www.geocities.co.jp/AnimeComic-White/6586/vlan.html"> VLAN Research page in Japanese</a></li>
+<li> <a href="http://www.geocities.co.jp/AnimeComic-White/6586/vlan-e.html"> VLAN page translated to English</a></li>
+<li> <a target=_top href="http://standards.ieee.org/getieee802/download/802.1Q-1998.pdf">
+      IEEE 802.1Q Standard</a></li>
+</ul>
+<P>
+
+<center><b>Features</b></center>
+<ul>
+ <li>Implements 802.1Q VLAN spec.</li>
+ <li>Implements support for a non-standard (as far as I know)
+     MAC-based VLAN functionality.</li>
+ <li>Can support up to 4094 VLANs per ethernet interface.</li>
+ <li>Scales well in critical paths: O(n), where n is the number of PHYSICAL ethernet interfaces,
+     and that is only on ingress.  O(1) in every other critical path, as far as I know.</li>
+ <li>Supports MULTICAST</li>
+ <li>Can change MAC address of VLAN.</li>
+ <li>Multiple naming conventions supported, and adjustable at runtime.</li>
+ <li>Optional header-reordering, to make the VLAN interface look <b>JUST LIKE</b>
+     an Ethernet interface.  This fixes some problems with DHCPd and anything else
+     that uses a SOCK_PACKET socket.  Default setting is off, which works for
+     every other protocol I know about, and is slightly faster.
+ </li>
+</ul>
+<P>
+
+
+<hr>
+Download vconfig binaries (source is more flexible, but this will work for most people).
+<ul>
+  <li> <a href="vconfig">vconfig binary for x86</a></li>
+  <li> <a href="vconfig.arm">vconfig binary for StrongArm</a></li>
+</ul>
+<P>
+
+<hr>
+<center><b>Change Log</b></center>
+<ul>
+<P>
+
+<li> <b><a href="vlan/vlan.1.8.tar.gz">Release 1.8 (gz)</a> &nbsp; For Kernel: 2.4.21+ &nbsp; Sept 30, 2003:</b><br>
+  <P>
+  <ul>
+   <li>Updated MAC-VLAN code and completed testing.  Based on Alex Zeffertt's
+       work but much has been re-written and he cannot be held responsible!
+       Please send all bug reports to the VLAN mailing list.  The Candela Technologies unified
+       patch is the thing to apply now, and it contains various other not-necessarily-VLAN
+       related bits and pieces.
+   </li>
+  </ul>
+</li>
+
+<P>
+<li> <b><a href="vlan/vlan.1.7m.tar.gz">Release 1.7m (gz)</a> &nbsp; For Kernel: 2.4.14+ &nbsp; Feb 27, 2003:</b><br>
+  <P>
+  <ul>
+   <li>Added Alex Zeffertt's MAC-based VLAN code.  Not fully functional
+       yet (mostly because I broke his original work...gonna fix it up
+       soon.  Grab & use his raw patch* files in the meantime.
+   </li>
+  </ul>
+</li>
+<P>
+
+<li> <b><a href="vlan/vlan.1.7.tar.gz">Release 1.7 (gz)</a> &nbsp; For Kernel: 2.4.14+ &nbsp; Feb 27, 2003:</b><br>
+  <P>
+  <ul>
+   <li>Clarified the license for vconfig (GPL).  Other small tweaks. </li>
+  </ul>
+</li>
+
+<P>
+<li> <b><a href="vlan/vlan.1.6.tar.gz">Release 1.6 (gz)</a> &nbsp; For Kernel: 2.4.14+ &nbsp; March 24, 2002:</b><br>
+  <P>
+  <ul>
+   <li>Removed 2.4 kernel patch:  It's in the standard kernel now.</li>
+   <li>Updated vconfig to fix some compile issues, and enable cross-compilation
+       to the StrongARM platform (changes should help other cross-compile
+       attempts too.)</li>
+  </ul>
+</li>
+<P>
+
+<li> <b><a href="vlan/vlan.1.5.tar.gz">Release 1.5 (gz)</a> &nbsp; For Kernel: 2.4.12-pre5 &nbsp; October 22, 2001:</b><br>
+  <P>
+  <ul>
+   <li>Mostly added other peoples fixes and patches (thanks folks!)</li>
+   <li>Finally fixed mc-list leakage (Ard van Breemen)</li>
+   <li>Flush mc-list at vlan-destory (Ard van Breemen)</li>
+   <li>Add vconfig man page to distribution (Ard van Breemen)</li>
+   <li>Fix problem with /proc and renaming VLAN devices (af AT devcon D.T net)</li>
+   <li>Add relatively large change by Nick Eggelston that makes VLAN
+       devices more transparent to tools like tcpdump and other raw
+       packet snoopers.  This will only be enabled when the REORDER_HDR
+       flag is set.</li>
+  </ul>
+</li>
+<P>
+
+<li> <b><a href="vlan/vlan.1.4.tar.gz">Release 1.4 (gz)</a> &nbsp; For Kernel: 2.4.8 &nbsp; August 16, 2001:</b><br>
+  <P>
+  <ul>
+   <li> Code should no longer require /proc interface in order to get at the IOCTLs.
+        The IOCTLs are now tied to sockets.  When using modules, it may auto-load now, too...</li>
+   <li> Fixed format string error in proc fs display.</li>
+   <li> Fixed crash bug relating to memory allocation with locks held (we now use GF_ATOMIC)</li>
+   <li> hard_start_xmit will now grow the packet header if there is not enough headroom.  This
+        may fix an MPLS-over-VLAN problem, though the real solution is to make MPLS allocate
+        more headroom anyway...</li>
+   <li> vconfig was changed to use the new IOCTL API, and the old vconfig WILL NOT WORK
+        with this or any newer patches...</li>
+  </ul>
+</li>
+
+<P>
+<li> <b><a href="vlan/vlan.1.0.3.tar.gz">Release 1.0.3 (gz)</a> &nbsp; For Kernel: 2.4.7 &nbsp; August 5, 2001:</b><br>
+  <P>
+  <ul>
+   <li> Re-worked code to be more stable and more in-line with what the kernel maintainers
+        want to see before the VLAN patch is included into the kernel.</li>
+   <li> One of those requests was to change the default naming scheme to eth0.5, for a VLAN
+        of VID 5 on eth0.  You can over-ride this naming behaviour with the vconfig tool.</li>
+   <li> There were *NO* changes to the 2.2 series patch, and I don't expect to ever make
+        any more changes there...</li>
+  </ul>
+
+</li>
+<P>
+
+<li> <b><a href="vlan/vlan.1.0.1.tar.gz">Release 1.0.1 (gz)</a> &nbsp; For Kernel: 2.2.18/19, 2.4.3-pre3 &nbsp; April 16, 2001:</b><br>
+  <P>
+  <ul>
+   <li> Incorporated a fix for changing a MAC on a VLAN, it now correctly sets PACKET_HOST.
+        Thanks to Martin Bokaemper for this one.</li>
+   <li> The 2.4 series patch should now compile as a module, thanks to a tweak from someone
+        who's mail I have lost!  Anyway, 3 cheers to the un-named coder!</li>
+   <li> There were *NO* changes to the 2.2 series patch, though I did verify that it seems to
+        work fine with the 2.2.19 kernel.</li>
+  </ul>
+
+</li>
+
+<P>
+<li> <b><a href="vlan/vlan.1.0.0.tar.gz">Release 1.0.0 (gz)</a> &nbsp; For Kernel: 2.2.18, 2.4.0 &nbsp; Jan 14, 2001:</b><br>
+  <P>
+  <ul>
+   <li> Really fixed (and tested) MAC change-ability.  When you set the MAC address on
+        a VLAN, it will also attempt to set the underlying device to PROMISCious mode
+        (otherwise, the VLAN will not receive any packets.)</li>
+   <li> Hashed-device lookup is disabled by default because some people had trouble with
+        the 'lo' device.  Please feel free to re-enable by editing the line in net/core/dev.c
+        (search for #define BEN_FAST_DEV_LOOKUP).</li>
+   <li> vconfig should warn when creating VLAN 1, because that VLAN is not compatible with many
+        switches.</li>
+  </ul>
+
+</li>
+
+<P>
+<li> <b><a href="vlan/vlan.0.0.15.tar.gz">Release 0.0.15 (gz)</a> &nbsp; For Kernel: 2.2.18, 2.4.prerelease &nbsp; Dec 31, 2000:</b><br>
+  <P>
+  <ul>
+   <li>Merged most of Matti Aarnio's patches.  This means no significant patch to
+       eth.c now, and will help port VLANs to non-ethernet devices (ie ppp, TokenRing??).</li>
+   <li> Setting the MAC address should work now..I think it was broken before.</li>
+   <li> Miscellaneous code re-organization to make patches to existing files smaller.</li>
+  </ul>
+
+</li>
+
+<P>
+<li> <b><a href="vlan/vlan.0.0.14.tar.gz">Release 0.0.14 (gz)</a> &nbsp; For Kernel: 2.2.17, 2.4.pre9 &nbsp; Oct 26, 2000:</b><br>
+  <P>
+  This code seems pretty stable.
+  <ul>
+   <li>Removed vlan-space-per-machine, so vlan-space-per-NIC is mandatory now.</li>
+   <li>DHCP might work now, as I've added support for encapsulating regular ethernet
+       frames if they are sent to the vlan driver.</li>
+   <li>Fixed up the name/index hashing stuff to handle changing the name on a device.</li>
+   <li>Took out default VID & default priority, as their usefullness was in question,
+       and the code was broken anyway.</li>
+  </ul>
+
+</li>
+
+<P>
+<li> <b><a href="vlan/vlan.0.0.13.tar.gz">Release 0.0.13 (gz)</a> &nbsp; For Kernel: 2.2.17, 2.4.pre9 &nbsp; Oct 11, 2000:</b><br>
+  <center><b>KNOWN TO BE BUSTED, here for posterity's sake.</b></center>
+  <P>
+  <ul>
+   <li>Added support for MULTICAST to the VLAN devices.  Thanks to
+       <a href="http://vlan.sourceforge.net" target=_top>Gleb & Co</a> for most of
+       that code.</li>
+   <li>Added the ability to set the MAC address on the VLAN.  For now, you'll either need
+       to set your Ethernet NIC into PROMISC mode, or maybe figure out some multi-cast
+       ethernet address to set on the NIC.  This has not been tested well at all.</li>
+   <li>Added a hashed device-name lookup scheme.  This greatly speeds up ifconfig -a.
+       I was able to run an ifconfig -a in 20 seconds on a Celeron 500, with 4000
+       vlan devices configured!!</li>
+   <li>Added vlan_test.pl to help me find dumb bugs.  Feel free to make this much
+       more powerful, and send the code back to me!</li>
+   <li>vconfig.c has been converted to C code now, instead of C++.  Thanks to MATHIEU.</li>
+   <li>Significantly cleaned up the code w/out decreasing any useful functionality,
+       I believe.</li>
+   <li>Removed the DHCP stuff from the VLAN distribution.</li>
+  </ul>
+
+</li>
+<P>
+
+<li> <b><a href="vlan/vlan.0.0.12.tar.gz">Release 0.0.12 (gz)</a> &nbsp; For Kernel: 2.2.16, 2.4.pre7 &nbsp; August 27, 2000:</b><br>
+  Added ability to re-order the VLAN packet so that it looks like a real ethernet
+  packet for the ingress pathway.  This should help DHCP and other programs that insist
+  on reading the raw buffer and then make assumptions about byte offsets.  I don't have
+  a good way to test this fully, so consider it experimental :)  This behavior can be
+  changed at run-time, and is set on a per-VLAN basis.  The default is NOT to reorder the
+  header, which has been the only behavior up untill this point.  The <tt>vconfig</tt>
+  program can set/clear the flag, by using a VLAN IOCTL.  You can read the flag's value
+  from  the /proc/net/vlan/vlan* files.
+<P>
+  You can also set a default priority on a NON-VLAN device.  This priority will only
+  be used when the default_VID for the device is set as well.  This priority won't
+  be mapped anywhere, just copied straight into the skb->priority.  It is a uint16.
+<P>
+  The 2.3 patch is now the 2.4 patch, and it has been tested against 2.4.pre7.
+</li>
+<P>
+
+<li> <b><a href="vlan/vlan.0.0.11.tar.gz">Release 0.0.11 (gz)</a> &nbsp; For Kernel: 2.2.13/14, 2.3.99 &nbsp; April 23, 2000:</b><br>
+  Added real support for PRIORITY.  Through IOCTL calls (see the vconfig program), you can set
+  explicit ingress and egress mappings to/from the VLAN QOS bits and the sk_buff->priority
+  field.  This is not tested very well, as I don't know much about how people really use the
+  priority field...  Took out the round-robin aggretation that went in in rls 0.10, as it was
+  mainly just a hack, and doing link aggregation at a lower level and then putting VLAN on
+  top of that virtual device probably makes more sense.  The vconfig program changed to support
+  the new features..here's it's new usage:<br>
+<pre>
+Usage: add             [interface-name] [vlan_id]
+       rem             [vlan-name]
+       set_dflt        [interface-name] [vlan_id]
+       add_port        [port-name]      [vlan_id]
+       rem_port        [port-name]      [vlan_id]
+       set_egress_map  [vlan-name]      [skb_priority]   [vlan_qos]
+       set_ingress_map [vlan-name]      [skb_priority]   [vlan_qos]
+       set_name_type   [name-type]
+       set_bind_mode   [bind-type]
+
+* The [interface-name] is the name of the ethernet card that hosts
+  the VLAN you are talking about.
+* The port-name is the name of the physical interface that a VLAN
+  may be attached to.
+* The vlan_id is the identifier (0-4095) of the VLAN you are operating on.
+* skb_priority is the priority in the socket buffer (sk_buff).
+* vlan_qos is the 3 bit priority in the VLAN header
+* name-type:  VLAN_PLUS_VID (vlan0005), VLAN_PLUS_VID_NO_PAD (vlan5),
+              DEV_PLUS_VID (eth0.0005), DEV_PLUS_VID_NO_PAD (eth0.5)
+* bind-type:  PER_DEVICE  # Allows vlan 5 on eth0 and eth1 to be unique.
+              PER_KERNEL  # Forces vlan 5 to be unique across all devices.
+</pre>
+<P>
+  The 2.3 patches have been ported foward to 2.3.99, thanks to Patrick for the vlanproc.c
+  updates!
+</li>
+<P>
+
+</ul><hr>
+<P>
+
+<center><h3>
+<a name="setup">VLAN Setup and Configuration</a></h3></center>
+
+To get started, you will want to download the latest vlan.X.X.tar.gz
+file (to your $HOME directory.) Unpack it with your favorite commands, for
+example:  <tt> tar -xvzf vlan.1.6.tar.gz </tt>
+<a name="cvs_setup">Alternatively, you can get it from the CVS Repository using something like this:</a><br>
+<ol>
+      <li> Install and configure 
+       <a href="http://www.loria.fr/~molli/cvs-index.html">cvs</a>
+         on your machine.</li>
+       <li> Specify the vlan repository:<br>
+       <b>export CVSROOT=:pserver:anonymous@ns1.wanfear.com:/home/cvs/vlan</b>
+       </li>
+       <li> Log in to the repository:<br>
+       <b>cvs login &nbsp; &nbsp; (PASSWORD: anonymous)</b>
+       </li>
+       <li> Check out the source:<br>
+         <b> mkdir vlan; cd vlan; cvs -z3 checkout vlan</b>
+       </li>
+</ol>
+<P>
+
+Now, you should have a vlan directory in your home directory.  You only have
+to patch the kernel if you are using Linux 2.4.14 or earlier.  Now,
+read the README or other docs to figure out what kernel it patches against.
+A list of mirrors are kept at <a href=http://www.kernel.org>www.kernel.org</a>.
+Unzip and un-tar this in your home directory as well, which should
+create a linux directory in your $HOME directory.  Example:<tt>
+tar -xvzf linux-2.2.14.tar.gz</tt><P>
+
+Now add the VLAN kernel changes to the kernel if your kernel requires it.  I finally figured
+out how to do patches that diff can handle (I think I did it right at least!).  You
+will find the patch in the vlan directory.  It will be called:  vlan.patch,
+or something equally straight-foward. Apply the patch to your kernel:<p>
+
+<tt>cd $HOME/linux<br>
+patch -p 1 &lt; $HOME/vlan/[vlan.patch]</br>
+</tt>
+<P>
+
+Your new, patched, kernel should be in your INCLUDE path before trying to
+compile the vconfig program.  One way to get things working is to link $HOME/linux
+to the 'linux' directory that you just un-zipped and patched.  A command might
+be something like:
+<tt>cd $HOME; ln -s /home/greear/kernel/2.4/linux.dev linux</tt>
+<P>
+
+Build the vconfig program in the $HOME/vlan directory:<br>
+<tt>cd $HOME/vlan<br>
+make<br>
+</tt>
+<P>
+
+Now, time to compile your new kernel!  Use the <tt>make xconfig</tt>
+command in your $HOME/linux directory to select your kernel options.  The
+option related to 802.1Q VLANs is found under the <b>Networking options</b>.
+If the option is not highlighted, make sure you select "Experimental Drivers"
+in one of the first xconfig menus.
+<P>
+
+Assuming your kernel compiled cleanly (yell if it didn't and you think my
+code broke it!!), you are now ready to try it out!!  Install your kernel
+in the normal manner (fix up your <tt>/etc/lilo.conf</tt> file appropriately and
+run <tt>lilo</tt> as root.)  Reboot your computer and choose your new kernel.
+<P>
+As your computer comes back to life, there will be little sign that you are
+now 802.1Q capable, other than a line spit out during the boot process.
+There should be a config programs in your $HOME/vlan
+directory:  <tt>vconfig</tt>.  <b>vconfig</b> is used
+to create and destroy VLAN devices. So, lets create a VLAN device on your
+first ethernet NIC.  vconfig&lt;return> will list a short spiel on how to
+use it.  The vconfig command I usually use is:
+<P>
+
+<tt>vconfig add eth0 5</tt>
+<P>
+
+This attempts to create a VLAN device with VLAN-ID of 5 on the eth0 device.
+If you want to delete a VLAN, use something like:
+<P>
+<tt>vconfig rem eth0.5</tt>
+<P>
+
+You will also need to give it an ip, eg: <tt>ifconfig -i eth0.5 192.168.2.1</tt><br>
+and configure it UP:  <tt>ifconfig -i eth0.5 up</tt>
+<P>
+
+<b>NOTE:</b>  You can get lots of VLAN related configuration information from
+the <b>/proc/net/vlan/*</b> files by using 'cat' or 'more' to look at them.
+<P>
+
+Please get in contact with me if you have suggestions, patches, or other
+comments.
+<P>
+
+    <hr>
+    <address><a href="mailto:greearb@candelatech.com">greearb@candelatech.com</a> &nbsp;
+             <a target=_top href="index.html">Ben Greear's Home Page</a></address>
+<!-- Created: Sat Jan 30 18:27:28 MST 1999 -->
+<!-- hhmts start -->
+Last modified: Tue Sep 30 14:16:14 PDT 2003
+<!-- hhmts end -->
+  </body>
+</html>
diff --git a/vlan_2.2.patch b/vlan_2.2.patch
new file mode 100644 (file)
index 0000000..e038825
--- /dev/null
@@ -0,0 +1,2927 @@
+diff -u -r -N -X /home/greear/exclude.list linux/include/linux/if_ether.h linux.dev/include/linux/if_ether.h
+--- linux/include/linux/if_ether.h     Sun Dec 10 17:49:44 2000
++++ linux.dev/include/linux/if_ether.h Thu Jan  4 20:41:19 2001
+@@ -32,6 +32,36 @@
+ #define ETH_DATA_LEN  1500            /* Max. octets in payload        */
+ #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.
+  */
+@@ -54,6 +84,7 @@
+ #define ETH_P_RARP      0x8035                /* Reverse Addr Res packet      */
+ #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           */
+ #define ETH_P_ATMMPOA 0x884c          /* MultiProtocol Over ATM       */
+diff -u -r -N -X /home/greear/exclude.list 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  Sun Jan 14 14:30:56 2001
+@@ -0,0 +1,238 @@
++/* -*- linux-c -*-
++ * VLAN               An implementation of 802.1Q VLAN tagging.
++ *
++ * Version:   0.0.1   03/06/99
++ *
++ * Authors:   Ben Greear <greearb@candelatech.com>
++ *
++ *            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 device;
++struct sk_buff;
++struct packet_type;
++struct vlan_collection;
++
++
++#include <linux/proc_fs.h> /* for proc_dir_entry */
++
++
++
++/*  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 device *find_802_1Q_vlan_dev(struct device* real_dev,
++                                    unsigned short VID); /* vlan.c */
++
++
++int register_netdevice(struct device *dev); /* found in dev.c */
++int unregister_netdevice(struct device *dev); /* found in dev.c */
++int dev_new_index(void); /* dev.c */
++
++/* found in vlan_dev.c */
++struct net_device_stats* vlan_dev_get_stats(struct device* dev);
++int vlan_dev_rebuild_header(struct sk_buff *skb);
++int vlan_dev_type_trans(struct sk_buff *skb, struct device *dev,
++                        struct packet_type* ptype);
++int vlan_dev_hard_header(struct sk_buff *skb, struct device *dev,
++                         unsigned short type, void *daddr, void *saddr,
++                         unsigned len);
++int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct device *dev);
++int vlan_dev_change_mtu(struct device *dev, int new_mtu);
++int vlan_dev_set_mac_address(struct device *dev, void* addr);
++int vlan_dev_open(struct device* dev);
++int vlan_dev_stop(struct device* dev);
++int vlan_dev_init(struct device* dev);
++void vlan_dev_destruct(struct device* dev);
++int vlan_dev_set_vlan_flag(char* dev_name, __u32 flag, short flag_val);
++/*  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_ingress_priority(char* dev_name, __u32 skb_prio, short vlan_prio);
++int vlan_dev_set_egress_priority(char* dev_name, __u32 skb_prio, short vlan_prio);
++
++/* VLAN multicast stuff */
++/* Delete all of the MC list entries from this vlan device.  Also deals
++ * with the underlying device...
++ */
++void vlan_flush_mc_list(struct device* dev);
++/* copy the mc_list into the vlan_info structure. */
++void vlan_copy_mc_list(struct dev_mc_list* mc_list, struct vlan_dev_info* vlan_info);
++/** dmi is a single entry into a dev_mc_list, a single node.  mc_list is
++ *  an entire list, and we'll iterate through it.
++ */
++int vlan_should_add_mc(struct dev_mc_list *dmi, struct dev_mc_list *mc_list);
++/** Taken from Gleb + Lennert's VLAN code, and modified... */
++void vlan_dev_set_multicast_list(struct device *vlan_dev);
++
++
++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 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;
++
++
++#define VLAN_NAME "vlan"
++
++/* 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 {
++        int real_dev_ifindex; /* The index of the ethernet(like?) device the vlan is attached to. */
++        struct device* vlan_devices[VLAN_GROUP_ARRAY_LEN];
++   
++        struct vlan_group* next; /* the next in the list */
++};
++
++
++/* __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
++
++
++struct vlan_priority_tci_mapping {
++        unsigned long priority;
++        unsigned short vlan_qos; /* This should be shifted when first set, so we only do it
++                                  * at provisioning time.
++                                  * ((skb->priority << 13) & 0xE000)
++                                  */
++        struct vlan_priority_tci_mapping* next;
++};
++
++/* Holds information that makes sense if this device is a VLAN device. */
++struct vlan_dev_info {
++        /** This will be the mapping that correlates skb->priority to
++         * 3 bits of VLAN QOS tags...
++         */
++        unsigned long ingress_priority_map[8];
++        struct vlan_priority_tci_mapping* egress_priority_map[16]; /* hash table */
++
++        unsigned short vlan_id;        /*  The VLAN Identifier for this interface. */
++        unsigned short flags;          /* (1 << 0) re_order_header   This option will cause the
++                                        *   VLAN code to move around the ethernet header on
++                                        *   ingress to make the skb look **exactly** like it
++                                        *   came in from an ethernet port.  This destroys some of
++                                        *   the VLAN information in the skb, but it fixes programs
++                                        *   like DHCP that use packet-filtering and don't understand
++                                        *   802.1Q
++                                        */
++        struct dev_mc_list* old_mc_list;  /* old multi-cast list for the VLAN interface..
++                                           * we save this so we can tell what changes were
++                                           * made, in order to feed the right changes down
++                                           * to the real hardware...
++                                           */
++        int old_allmulti;               /* similar to above. */
++        int old_promiscuity;            /* similar to above. */
++        struct device* real_dev;        /* the underlying device/interface */
++        struct proc_dir_entry dent;    /* Holds the proc data */
++        unsigned long cnt_inc_headroom_on_tx; /* How many times did we have to grow the skb on TX. */
++        unsigned long cnt_encap_on_xmit;      /* How many times did we have to encapsulate the skb on TX. */
++};
++
++static inline unsigned short vlan_dev_get_egress_qos_mask(struct device* dev, struct sk_buff* skb) {
++        struct vlan_priority_tci_mapping* mp = dev->vlan_dev->egress_priority_map[(skb->priority & 0xF)];
++        while (mp) {
++                if (mp->priority == skb->priority) {
++                        return mp->vlan_qos; /* This should already be shifted to mask correctly with
++                                              * the VLAN's TCI
++                                              */
++                }
++                mp = mp->next;
++        }
++        return 0;
++}
++
++static inline int vlan_dmi_equals(struct dev_mc_list *dmi1,
++                                  struct dev_mc_list *dmi2) {
++        return ((dmi1->dmi_addrlen == dmi2->dmi_addrlen) &&
++                (memcmp(dmi1->dmi_addr, dmi2->dmi_addr, dmi1->dmi_addrlen) == 0));
++}
++
++static inline void vlan_destroy_mc_list(struct dev_mc_list *mc_list) {
++        struct dev_mc_list *dmi = mc_list, *next;
++
++        while(dmi) {
++                next = dmi->next;
++                kfree(dmi);
++                dmi = next;
++        }
++}
++
++#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,
++        SET_INGRESS_PRIORITY_IOCTL,
++        SET_EGRESS_PRIORITY_IOCTL,
++        GET_INGRESS_PRIORITY_IOCTL,
++        GET_EGRESS_PRIORITY_IOCTL,
++        SET_NAME_TYPE_IOCTL,
++        SET_VLAN_FLAG_IOCTL
++}; /* vlan_ioctl enum */
++
++enum vlan_name_types {
++        VLAN_NAME_TYPE_PLUS_VID, /* Name will look like:  vlan0005 */
++        VLAN_NAME_TYPE_RAW_PLUS_VID, /* name will look like:  eth1.0005 */
++        VLAN_NAME_TYPE_PLUS_VID_NO_PAD, /* Name will look like:  vlan5 */
++        VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, /* Name will look like:  eth0.5 */
++        VLAN_NAME_TYPE_HIGHEST
++};
++
++struct vlan_ioctl_args {
++        char dev1[24];
++
++        union {
++                char dev2[24];
++                int VID;
++                unsigned long skb_priority;
++                unsigned long name_type;
++                unsigned long bind_type;
++                unsigned long flag; /* Matches vlan_dev_info flags */
++        } u;
++
++        short vlan_qos;   /* Can also be flag-value, 1 to set, 0 to clear. */
++};
++
++
++#endif
+diff -u -r -N -X /home/greear/exclude.list linux/include/linux/netdevice.h linux.dev/include/linux/netdevice.h
+--- linux/include/linux/netdevice.h    Sun Dec 31 14:36:46 2000
++++ linux.dev/include/linux/netdevice.h        Sun Jan 14 14:23:12 2001
+@@ -37,8 +37,15 @@
+ #ifdef CONFIG_NET_PROFILE
+ #include <net/profile.h>
+ #endif
++
++#if (defined(CONFIG_VLAN_802_1Q))
++struct vlan_dev_info;
++#endif
++
+ #endif
++
++
+ struct divert_blk;
+ /*
+@@ -53,7 +60,11 @@
+  */
+  
+ #if !defined(CONFIG_AX25) && !defined(CONFIG_AX25_MODULE) && !defined(CONFIG_TR)
++#if defined(CONFIG_VLAN_802_1Q)
++#define LL_MAX_HEADER 36
++#else
+ #define LL_MAX_HEADER 32
++#endif
+ #else
+ #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+ #define LL_MAX_HEADER 96
+@@ -155,11 +166,19 @@
+ {
+       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 */
++      unsigned short  hh_type;        /* protocol identifier, f.e ETH_P_IP
++                                         *  NOTE:  For VLANs, this will be the
++                                         *  encapuslated type. --BLG
++                                         */
+       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)];
++#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
+ };
+@@ -317,7 +336,13 @@
+       int                     tx_semaphore;
+ #define NETDEV_FASTROUTE_HMASK 0xF
+       /* Semi-private data. Keep it at the end of device struct. */
++
+       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;
+ #endif
+ #ifdef CONFIG_NET_DIVERT
+diff -u -r -N -X /home/greear/exclude.list 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 Dec 29 19:51:33 2000
+@@ -0,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
++
++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 -u -r -N -X /home/greear/exclude.list 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 Dec 29 19:51:33 2000
+@@ -0,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 -u -r -N -X /home/greear/exclude.list 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        Sun Jan 14 14:55:46 2001
+@@ -0,0 +1,447 @@
++/*           -*- linux-c -*-
++ * 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 <greearb@candelatech.com>, <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/if_vlan.h>
++#include "vlan.h"
++#include "vlanproc.h"
++
++extern int register_netdevice(struct device *dev); /* found in dev.c */
++extern int unregister_netdevice(struct device *dev); /* found in dev.c */
++extern int dev_new_index(void); /* dev.c */
++
++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;
++
++static char vlan_fullname[] = "802.1Q VLAN Support";
++static unsigned int vlan_version = 1;
++static unsigned int vlan_release = 0;
++static char vlan_copyright[] = "(c) 2000 Ben Greear (GPL)";
++
++/** These may be changed at run-time through IOCTLs */
++unsigned short vlan_name_type = 0; /* determines interface naming scheme */
++unsigned long vlan_bad_proto_recvd = 0; /* Counter for how many NON-VLAN protos we've received on a VLAN. */
++
++
++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.
++}
++
++#else
++
++
++/** Non-module init entry point. */
++__initfunc(void 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);
++
++        /* 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);
++}
++
++
++
++/**  Will search linearly for now, based on device index.  Could
++ * hash, or directly link, this some day. --Ben
++ */
++struct vlan_group* vlan_find_group(int real_dev_ifindex) {
++        struct vlan_group* grp = NULL;
++
++        for (grp = p802_1Q_vlan_list;
++             ((grp != NULL) && (grp->real_dev_ifindex != real_dev_ifindex));
++             grp = grp->next) {
++#ifdef VLAN_DEBUG
++                printk(VLAN_DBG __FUNCTION__ ": grp_idx: %i  real_dev_idx: %i\n",
++                       grp->real_dev_ifindex, real_dev_ifindex);
++#endif
++           ;
++        } /* for */
++         
++        return grp;
++}
++
++/*  Find the protocol handler.  Assumes VID < 0xFFF.
++ */
++struct device *find_802_1Q_vlan_dev(struct device* real_dev, unsigned short VID) {
++
++        struct vlan_group* grp = vlan_find_group(real_dev->ifindex);
++
++#ifdef VLAN_DEBUG
++        printk(VLAN_DBG __FUNCTION__ ": idx: %i  grp: %p\n", real_dev->ifindex, grp);
++#endif
++
++        /*  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_dev */
++
++
++
++int unregister_802_1Q_vlan_dev(int real_dev_ifindex, unsigned short vlan_id) {
++        struct vlan_group* grp;
++        struct 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(real_dev_ifindex);
++        /*  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);
++         
++                        /* 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!!
++                         */
++                        unregister_netdevice(dev);
++
++                }/* if */
++        }/* if */
++        return 0;
++}/* unregister vlan device */
++
++
++
++int unregister_802_1Q_vlan_device(const char* vlan_IF_name) {
++        struct device* dev = NULL;
++
++#ifdef VLAN_DEBUG
++        printk(VLAN_DBG __FUNCTION__ ": unregister VLAN by name, name -:%s:-\n",
++               vlan_IF_name);
++#endif
++   
++        dev = dev_get(vlan_IF_name);
++
++        if (dev && dev->vlan_dev) {
++                return unregister_802_1Q_vlan_dev(dev->vlan_dev->real_dev->ifindex,
++                                                  (unsigned short)(dev->vlan_dev->vlan_id));
++        }
++        else {
++#ifdef VLAN_DEBUG
++                printk(VLAN_DBG __FUNCTION__ ": WARNING: Could not find dev\n");
++#endif
++                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 device *register_802_1Q_vlan_device(const char* eth_IF_name,
++                                           unsigned short VLAN_ID) {
++        struct vlan_group* grp;
++        struct device *new_dev;
++        struct device *real_dev; /* the ethernet device */
++        int malloc_size = 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(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"); */
++
++                        if (find_802_1Q_vlan_dev(real_dev, VLAN_ID)) {
++                                /* was already registered. */
++                                printk(VLAN_DBG __FUNCTION__ ": ALREADY had VLAN registered\n");
++                                return NULL;
++                        }
++
++                        malloc_size = (sizeof(struct device));
++
++                        new_dev = (struct device*) kmalloc(malloc_size, GFP_KERNEL);
++                        VLAN_MEM_DBG("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;
++            
++                                /* 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);
++                                        return NULL;
++                                }
++
++                                if (vlan_name_type == VLAN_NAME_TYPE_RAW_PLUS_VID) {
++                                        /* name will look like:  eth1.0005 */
++                                        sprintf(new_dev->name, "%s.%.4i", real_dev->name, VLAN_ID);
++                                }
++                                else if (vlan_name_type == VLAN_NAME_TYPE_PLUS_VID_NO_PAD) {
++                                        /* Put our vlan.VID in the name.  Name will look like:  vlan5 */
++                                        sprintf(new_dev->name, "vlan%i", VLAN_ID);
++                                }
++                                else if (vlan_name_type == VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD) {
++                                        /* Put our vlan.VID in the name.  Name will look like:  eth0.5 */
++                                        sprintf(new_dev->name, "%s.%i", real_dev->name, VLAN_ID);
++                                }
++                                else { /* (vlan_name_type == VLAN_NAME_TYPE_PLUS_VID) { */
++                                        /* Put our vlan.VID in the name.  Name will look like:  vlan0005 */
++                                        /* default case */
++                                        sprintf(new_dev->name, "vlan%.4i", VLAN_ID);
++                                }
++            
++            
++#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->set_mac_address = vlan_dev_set_mac_address;
++                                new_dev->set_multicast_list = vlan_dev_set_multicast_list;
++
++                                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);
++                                        return NULL;
++                                }
++                                else {
++                                        /* Initialize it. */
++                                        memset(new_dev->vlan_dev, 0, sizeof(struct vlan_dev_info));
++               
++                                        new_dev->vlan_dev->vlan_id = VLAN_ID; /* 1 through 0xFFF */
++                                        /* TODO: have to be careful deleting real devices now. */
++                                        new_dev->vlan_dev->real_dev = real_dev;
++
++                                        memset(&(new_dev->vlan_dev->dent), 0, sizeof(struct proc_dir_entry));
++                                }
++            
++                                /* So, got the sucker initialized, now lets place it into our local
++                                 * structure.
++                                 */
++
++                                grp = vlan_find_group(real_dev->ifindex);
++
++                                /*  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, idx: %i\n",
++                                                       real_dev->ifindex);
++                                                memset(grp, 0, sizeof(struct vlan_group));
++                                                grp->real_dev_ifindex = real_dev->ifindex;
++                                                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);
++                                                return NULL;
++                                        }
++                                }/* if */
++            
++                                grp->vlan_devices[VLAN_ID] = new_dev;
++
++                                /*  Now, add it to the global list of devices. */
++                                /* printk(KERN_ALERT "Registering new device."); */
++                                register_netdevice(new_dev);
++                                vlan_proc_add_dev(new_dev); /* create it's proc entry */
++                                return new_dev;
++                        }
++                }//if
++        }//if
++
++        return NULL;
++}/* register (create) VLAN device */
+diff -u -r -N -X /home/greear/exclude.list 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        Sun Jan 14 14:30:56 2001
+@@ -0,0 +1,44 @@
++#ifndef __BEN_VLAN_802_1Q_INC__
++#define __BEN_VLAN_802_1Q_INC__
++
++#include <linux/if_vlan.h>
++
++/* If this is undefined, the name will look like:  vlan0005 */
++/* #define USE_RAW_IN_NAME   Use this one if you like it:  eth.0005 */
++
++/*  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_DEBUG /* 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 short vlan_name_type;
++extern unsigned long vlan_bad_proto_recvd; /* Counter for how many NON-VLAN protos we've received on a VLAN. */
++
++/* Add some headers for the public VLAN methods. */
++int unregister_802_1Q_vlan_device(const char* vlan_IF_name);
++struct 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 -u -r -N -X /home/greear/exclude.list 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    Sun Jan 14 19:21:08 2001
+@@ -0,0 +1,766 @@
++/* -*- linux-c -*-
++ * 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 <greearb@candelatech.com>, <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 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 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 device *dev,
++                        struct packet_type* ptype) {
++        unsigned char* rawp = NULL;
++        struct vlan_ethhdr *veth = (struct vlan_ethhdr*)(skb->mac.ethernet);
++        unsigned short vid = 0;
++        struct net_device_stats* stats;
++   
++        /* Do we have a VLAN packet?  If not, then throw it away, after printing an error.
++         *
++         */
++        if (veth->h_vlan_proto != __constant_htons(ETH_P_802_1Q)) {
++                printk(VLAN_INF __FUNCTION__ ": VLAN device received NON-VLAN protocol: %hx\n", htons(veth->h_vlan_proto));
++                vlan_bad_proto_recvd++;
++                kfree_skb(skb);
++                return -EINVAL;
++        }
++        else {
++                vid = ((unsigned short)(ntohs(veth->h_vlan_TCI)) & 0xFFF);
++        }
++
++#ifdef VLAN_DEBUG
++        printk(VLAN_DBG __FUNCTION__ ": skb: %p vlan_id: %hx dev: %s, encap_proto: %hx\n",
++               skb, vid, dev->name, veth->h_vlan_encapsulated_proto);
++#endif
++   
++        /*  Ok, we will find the correct VLAN device, strip the header,
++            and then go on as usual.
++        */
++        
++        /* we have 12 bits of vlan ID. */
++        /* If it's NULL, we will tag the skb to be junked below */
++        skb->dev = find_802_1Q_vlan_dev(dev, vid);
++
++        if (!skb->dev) {
++#ifdef VLAN_DEBUG
++                printk(VLAN_DBG __FUNCTION__ ": ERROR:  No device for VID: %i on dev: %s [%i]\n",
++                       (unsigned int)(vid), dev->name, dev->ifindex);
++#endif
++                kfree_skb(skb);
++                return -1;
++        }
++
++        stats = (struct net_device_stats*)(skb->dev->priv);
++
++        /*
++         * Deal with ingress priority mapping.
++         */
++        skb->priority = skb->dev->vlan_dev->ingress_priority_map[(ntohs(veth->h_vlan_TCI) >> 13) & 0x7];
++   
++#ifdef VLAN_DEBUG
++        printk(VLAN_DBG __FUNCTION__ ": priority: %lu  for TCI: %hu (hbo) on vlan_dev: %s\n",
++               (unsigned long)(skb->priority), ntohs(veth->h_vlan_TCI), skb->dev->name);
++#endif
++
++        /* Bump the rx counters for the VLAN device. */
++        stats->rx_packets++;
++        stats->rx_bytes += skb->len;
++      
++        /* NOTE: The underlying device SHOULD NOT PULL THE MAC BYTES OFF.
++           (it doesn't seem to.)
++        */
++        skb_pull(skb, VLAN_ETH_HLEN); /* 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;
++        }
++
++        /*  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.
++            */
++           
++           /* See if we are configured to re-write the VLAN header to make it look like
++            * ethernet...
++            */
++           if (skb->dev->vlan_dev->flags & 1) {
++                   /* Lifted from Gleb's VLAN code... */
++                   memmove(skb->data - (VLAN_ETH_HLEN - 4), skb->data - VLAN_ETH_HLEN, 12);
++                   skb->mac.raw += 4;
++           }
++           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.
++                 */
++      
++                /* See if we are configured to re-write the VLAN header to make it look like
++                 * ethernet...
++                 */
++                if (skb->dev->vlan_dev->flags & 1) {
++                        /* Lifted from Gleb's VLAN code... */
++                        memmove(skb->data - (VLAN_ETH_HLEN - 4), skb->data - VLAN_ETH_HLEN, 12);
++                        skb->mac.raw += 4;
++                }
++                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.
++         */
++
++        /* See if we are configured to re-write the VLAN header to make it look like
++         * ethernet...
++         */
++        if (skb->dev->vlan_dev->flags & 1) {
++                /* Lifted from Gleb's VLAN code... */
++                memmove(skb->data - (VLAN_ETH_HLEN - 4), skb->data - VLAN_ETH_HLEN, 12);
++                skb->mac.raw += 4;
++        }
++        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 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 |= vlan_dev_get_egress_qos_mask(dev, skb);
++   
++        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 device *dev) {
++        struct net_device_stats* stats = (struct net_device_stats*)(dev->priv);
++        struct vlan_ethhdr *veth = (struct vlan_ethhdr*)(skb->data);
++        
++                /* Handle non-VLAN frames if they are sent to us, for example by DHCP. */
++        if (veth->h_vlan_proto != __constant_htons(ETH_P_802_1Q)) {
++                /* This is not a VLAN frame...but we can fix that! */
++                unsigned short veth_TCI = 0;
++                dev->vlan_dev->cnt_encap_on_xmit++;
++                
++                if (skb_headroom(skb) < 4) {
++                        struct sk_buff* sk_tmp = skb;
++                        skb = skb_realloc_headroom(sk_tmp, 4);
++                        kfree_skb(sk_tmp);
++                        if (skb == NULL) {
++                                stats->tx_dropped++;
++                                kfree_skb(sk_tmp);
++                                return -ENOMEM;
++                        }
++                        dev->vlan_dev->cnt_inc_headroom_on_tx++;
++                }
++                else {
++                        if( !(skb = skb_unshare(skb, GFP_ATOMIC)) ) {
++                                printk(KERN_ERR "vlan: failed to unshare skbuff\n");
++                                stats->tx_dropped++;
++                                return -ENOMEM;
++                        }
++                }
++                veth = (struct vlan_ethhdr*)skb_push(skb, 4);
++
++                /* Move the mac addresses to the beginning of the new header. */
++                memmove(skb->data, skb->data + 4, 12);
++
++                /* 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 |= vlan_dev_get_egress_qos_mask(dev, skb);
++   
++                veth->h_vlan_TCI = htons(veth_TCI);
++        }/* If we needed to encapsulate the frame */
++
++        skb->dev = dev->vlan_dev->real_dev;
++
++
++#ifdef VLAN_DEBUG
++        printk(VLAN_DBG __FUNCTION__ ": about to send skb: %p  to dev: %s\n", skb, skb->dev->name);
++#endif
++
++        dev_queue_xmit(skb);
++        stats->tx_packets++; /* for statics only */
++        stats->tx_bytes += skb->len;
++        return 0;
++}/* vlan_dev_hard_start_xmit */
++
++
++int vlan_dev_change_mtu(struct 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 device* dev) {
++        dev->flags |= IFF_UP;
++        return 0;
++}
++
++int vlan_dev_stop(struct device* dev) {
++        dev->flags &= ~IFF_UP;
++        return 0;
++}
++
++int vlan_dev_init(struct device* dev) {
++        /* TODO:  figure this out, maybe do nothing?? */
++        return 0;
++}
++
++void vlan_dev_destruct(struct 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;
++   
++        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("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_set_ingress_priority(char* dev_name, __u32 skb_prio, short vlan_prio) {
++        struct device* dev = dev_get(dev_name);
++   
++        if (dev) {
++                if (dev->vlan_dev) { /* can't put a dflt ID on a vlan device */
++                        /* see if a priority mapping exists.. */
++                        dev->vlan_dev->ingress_priority_map[vlan_prio & 0x7] = skb_prio;
++                        return 0;
++                }
++        }
++        return -EINVAL;
++}
++
++int vlan_dev_set_egress_priority(char* dev_name, __u32 skb_prio, short vlan_prio) {
++        struct device* dev = dev_get(dev_name);
++        struct vlan_priority_tci_mapping* mp = NULL;
++        struct vlan_priority_tci_mapping* np;
++   
++        if (dev) {
++                if (dev->vlan_dev) { /* can't put a dflt ID on a vlan device */
++                        /* see if a priority mapping exists.. */
++                        mp = dev->vlan_dev->egress_priority_map[skb_prio & 0xF];
++                        while (mp) {
++                                if (mp->priority == skb_prio) {
++                                        mp->vlan_qos = ((vlan_prio << 13) & 0xE000);
++                                        return 0;
++                                }
++                        }
++                        /* create a new mapping then. */
++                        mp = dev->vlan_dev->egress_priority_map[skb_prio & 0xF];
++                        np = kmalloc(sizeof(struct vlan_priority_tci_mapping), GFP_KERNEL);
++                        if (np) {
++                                np->next = mp;
++                                np->priority = skb_prio;
++                                np->vlan_qos = ((vlan_prio << 13) & 0xE000);
++                                dev->vlan_dev->egress_priority_map[skb_prio & 0xF] = np;
++                                return 0;
++                        }
++                        else {
++                                return -ENOBUFS;
++                        }
++                }
++        }
++        return -EINVAL;
++}
++
++/* Flags are defined in the vlan_dev_info class in include/linux/if_vlan.h file. */
++int vlan_dev_set_vlan_flag(char* dev_name, __u32 flag, short flag_val) {
++        struct device* dev = dev_get(dev_name);
++   
++        if (dev) {
++                if (dev->vlan_dev) {
++                        /* verify flag is supported */
++                        if (flag == 1) {
++                                if (flag_val) {
++                                        dev->vlan_dev->flags |= 1;
++                                }
++                                else {
++                                        dev->vlan_dev->flags &= ~1;
++                                }
++                                return 0;
++                        }
++                        else {
++                                return -EINVAL;
++                        }
++                }/* if it's a vlan device */
++        }/* if we found the device */
++        return -EINVAL;
++}
++
++
++int vlan_dev_set_mac_address(struct device *dev, void* addr_struct_p) {
++        int i;
++        struct sockaddr *addr = (struct sockaddr*)(addr_struct_p);
++
++        if (dev->start) {
++                return -EBUSY;
++        }
++
++      memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
++        
++        printk("%s: Setting MAC address to ", dev->name);
++        for (i = 0; i < 6; i++) {
++                printk(" %2.2x", dev->dev_addr[i]);
++        }
++        printk(".\n");
++
++        if (memcmp(dev->vlan_dev->real_dev->dev_addr, dev->dev_addr, dev->addr_len) != 0) {
++                if (dev->vlan_dev->real_dev->flags & IFF_PROMISC) {
++                        /* Already promiscious...leave it alone. */
++                        printk("VLAN (%s):  Good, underlying device (%s) is already promiscious.\n",
++                               dev->name, dev->vlan_dev->real_dev->name);
++                }
++                else {
++                        int flgs = dev->vlan_dev->real_dev->flags;
++                        printk("VLAN (%s):  Setting underlying device (%s) to promiscious mode.\n",
++                               dev->name, dev->vlan_dev->real_dev->name);
++                        flgs |= IFF_PROMISC;
++                        dev_change_flags(dev->vlan_dev->real_dev, flgs);
++                        /*  This should work, but doesn't:
++                            dev_set_promiscuity(dev->vlan_dev->real_dev, 1);
++                        */
++                }
++        }
++        else {
++                printk("VLAN (%s):  Underlying device (%s) has same MAC, not checking promiscious mode.\n",
++                       dev->name, dev->vlan_dev->real_dev->name);
++        }
++        return 0;
++}
++
++
++/** Taken from Gleb + Lennert's VLAN code, and modified... */
++void vlan_dev_set_multicast_list(struct device *vlan_dev) {
++        struct dev_mc_list *dmi;
++        struct device *real_dev;
++        int inc;
++
++        if (vlan_dev && vlan_dev->vlan_dev) {
++                /* Then it's a real vlan device, as far as we can tell.. */
++                real_dev = vlan_dev->vlan_dev->real_dev;
++
++                /* compare the current promiscuity to the last promisc we had.. */
++                inc = vlan_dev->promiscuity - vlan_dev->vlan_dev->old_promiscuity;
++   
++                if (inc) {
++                        printk(KERN_INFO "vlan: dev_set_promiscuity(master, %d)\n", inc);
++                        dev_set_promiscuity(real_dev, inc); /* found in dev.c */
++                        vlan_dev->vlan_dev->old_promiscuity = vlan_dev->promiscuity;
++                }
++   
++                inc = vlan_dev->allmulti - vlan_dev->vlan_dev->old_allmulti;
++   
++                if (inc) {
++                        printk(KERN_INFO "vlan: dev_set_allmulti(master, %d)\n", inc);
++                        dev_set_allmulti(real_dev, inc); /* dev.c */
++                        vlan_dev->vlan_dev->old_allmulti = vlan_dev->allmulti;
++                }
++   
++                /* looking for addresses to add to master's list */
++                for (dmi = vlan_dev->mc_list; dmi!=NULL; dmi=dmi->next) {
++                        if (vlan_should_add_mc(dmi, vlan_dev->vlan_dev->old_mc_list)) {
++                                dev_mc_add(real_dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
++                                printk(KERN_INFO "vlan: add %.2x:%.2x:%.2x:%.2x:%.2x:%.2x mcast address to master interface\n",
++                                       dmi->dmi_addr[0],
++                                       dmi->dmi_addr[1],
++                                       dmi->dmi_addr[2],
++                                       dmi->dmi_addr[3],
++                                       dmi->dmi_addr[4],
++                                       dmi->dmi_addr[5]);
++                        }
++                }
++   
++                /* looking for addresses to delete from master's list */
++                for (dmi = vlan_dev->mc_list; dmi!=NULL; dmi=dmi->next) {
++                        if (vlan_should_add_mc(dmi, vlan_dev->mc_list)) {
++                                /* if we think we should add it to the new list, then we should really
++                                 * delete it from the real list on the underlying device.
++                                 */
++                                dev_mc_delete(real_dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
++                                printk(KERN_INFO "vlan: del %.2x:%.2x:%.2x:%.2x:%.2x:%.2x mcast address from master interface\n",
++                                       dmi->dmi_addr[0],
++                                       dmi->dmi_addr[1],
++                                       dmi->dmi_addr[2],
++                                       dmi->dmi_addr[3],
++                                       dmi->dmi_addr[4],
++                                       dmi->dmi_addr[5]);
++                        }
++                }
++   
++                /* save multicast list */
++                vlan_copy_mc_list(vlan_dev->mc_list, vlan_dev->vlan_dev);
++        }/* if we were sent a valid device */
++}/* vlan_dev_set_multicast */
++
++
++/** dmi is a single entry into a dev_mc_list, a single node.  mc_list is
++ *  an entire list, and we'll iterate through it.
++ */
++int vlan_should_add_mc(struct dev_mc_list *dmi, struct dev_mc_list *mc_list) {
++        struct dev_mc_list *idmi; /* iterator */
++
++        for (idmi=mc_list; idmi!=NULL;) {
++                if (vlan_dmi_equals(dmi, idmi)) {
++                        if (dmi->dmi_users > idmi->dmi_users)
++                                return 1;
++                        else
++                                return 0;
++                }
++                else {
++                        idmi = idmi->next;
++                }
++        }
++   
++        return 1;
++}
++
++
++void vlan_copy_mc_list(struct dev_mc_list *mc_list, struct vlan_dev_info *vlan_info) {
++        struct dev_mc_list *dmi, *new_dmi;
++   
++        vlan_destroy_mc_list(vlan_info->old_mc_list);
++        vlan_info->old_mc_list = NULL;
++
++        for (dmi=mc_list; dmi!=NULL; dmi=dmi->next) {
++                new_dmi = kmalloc(sizeof(*new_dmi), GFP_KERNEL);
++                if (new_dmi == NULL) {
++                        printk(KERN_ERR "vlan: cannot allocate memory. Multicast may not work properly from now.\n");
++                        return;
++                }
++      
++                new_dmi->next = vlan_info->old_mc_list;
++                vlan_info->old_mc_list = new_dmi;
++      
++                new_dmi->dmi_addrlen = dmi->dmi_addrlen;
++                memcpy(new_dmi->dmi_addr, dmi->dmi_addr, dmi->dmi_addrlen);
++                new_dmi->dmi_users = dmi->dmi_users;
++                new_dmi->dmi_gusers = dmi->dmi_gusers;
++        }
++}
++
++void vlan_flush_mc_list(struct device *dev) {
++        struct dev_mc_list *dmi = dev->mc_list;
++
++        while (dmi) {
++                dev_mc_delete(dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
++                printk(KERN_INFO "vlan: del %.2x:%.2x:%.2x:%.2x:%.2x:%.2x mcast address from master interface\n",
++                       dmi->dmi_addr[0],
++                       dmi->dmi_addr[1],
++                       dmi->dmi_addr[2],
++                       dmi->dmi_addr[3],
++                       dmi->dmi_addr[4],
++                       dmi->dmi_addr[5]);
++                dmi = dev->mc_list;
++        }
++   
++        vlan_destroy_mc_list(dev->mc_list);
++        if (dev->vlan_dev) {
++                vlan_destroy_mc_list(dev->vlan_dev->old_mc_list);
++                dev->vlan_dev->old_mc_list = NULL;
++        }
++        dev->mc_list = NULL;
++}/* vlan_flush_mc_list */
+diff -u -r -N -X /home/greear/exclude.list 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    Fri Dec 29 19:51:33 2000
+@@ -0,0 +1,654 @@
++/* * -*- linux-c -*- */
++/*****************************************************************************
++ * vlanproc.c VLAN Module. /proc filesystem interface.
++*
++* Author:     Ben Greear, <greearb@candelatech.com> coppied from wanproc.c
++*               by: Gene Kozin        <genek@compuserve.com>
++*
++* Copyright:  (c) 1998-2000 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,
++                                int dummy);
++static int vlandev_get_info(char* buf, char** start, off_t offs, int len,
++                            int dummy);
++
++
++/* 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";
++
++
++/*
++ *    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[23] = 0;
++        args.u.dev2[23] = 0;
++
++   /*
++   dent = inode->u.generic_ip;
++   if ((dent == NULL) || (dent->data == NULL))
++     return -EINVAL;
++
++   dev = dent->data;
++   */
++   
++        switch (cmd)
++        {
++        case SET_INGRESS_PRIORITY_IOCTL:
++                err = vlan_dev_set_ingress_priority(args.dev1, args.u.skb_priority, args.vlan_qos);
++                break;
++
++        case SET_EGRESS_PRIORITY_IOCTL:
++                err = vlan_dev_set_egress_priority(args.dev1, args.u.skb_priority, args.vlan_qos);
++                break;
++
++        case SET_VLAN_FLAG_IOCTL:
++                err = vlan_dev_set_vlan_flag(args.dev1, args.u.flag, args.vlan_qos);
++                break;
++
++        case SET_NAME_TYPE_IOCTL:
++                if ((args.u.name_type >= 0) && (args.u.name_type < VLAN_NAME_TYPE_HIGHEST)) {
++                        vlan_name_type = args.u.name_type;
++                        err = 0;
++                }
++                else {
++                        err = -EINVAL;
++                }
++                break;
++         
++                /* TODO:  Figure out how to pass info back...
++                   case GET_INGRESS_PRIORITY_IOCTL:
++                   err = vlan_dev_get_ingress_priority(args);
++                   break;
++
++                   case GET_EGRESS_PRIORITY_IOCTL:
++                   err = vlan_dev_get_egress_priority(args);
++                   break;
++                */
++         
++        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;
++
++        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
++ *    <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,                 /* follow link */
++        NULL,                 /* readlink */
++        NULL,                 /* readpage */
++        NULL,                 /* writepage */
++        NULL,                 /* bmap */
++        NULL,                 /* truncate */
++        vlan_proc_perms
++};
++
++/*
++ *    /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,                 /* readpage */
++        NULL,                 /* writepage */
++        NULL,                 /* bmap */
++        NULL,                 /* truncate */
++        vlan_proc_perms
++};
++
++
++/*
++ * Proc filesystem derectory entries.
++ */
++
++/*
++ *    /proc/net/vlan 
++ */
++
++static struct proc_dir_entry proc_vlan = {
++        0,                    /* .low_ino */
++        sizeof(name_root) - 1,        /* .namelen */
++        name_root,            /* .name */
++        0555 | S_IFDIR,               /* .mode */
++        2,                    /* .nlink */
++        0,                    /* .uid */
++        0,                    /* .gid */
++        0,                    /* .size */
++        &proc_dir_inode_operations, /* .ops */
++        NULL,                 /* .get_info */
++        NULL,                 /* .fill_node */
++        NULL,                 /* .next */
++        NULL,                 /* .parent */
++        NULL,                 /* .subdir */
++        NULL,                 /* .data */
++};
++
++/*
++ *    /proc/net/vlan/config 
++ */
++ 
++static struct proc_dir_entry proc_vlan_conf = {
++        0,                            /* .low_ino */
++        sizeof(name_conf) - 1,        /* .namelen */
++        name_conf,            /* .name */
++        0444 | S_IFREG,               /* .mode */
++        1,                    /* .nlink */
++        0,                            /* .uid */
++        0,                            /* .gid */
++        0,                    /* .size */
++        &vlan_inode,                  /* .ops */
++        &vlan_config_get_info,        /* .get_info */
++        NULL,                 /* .fill_node */
++        NULL,                 /* .next */
++        NULL,                 /* .parent */
++        NULL,                 /* .subdir */
++        NULL,                 /* .data */
++};
++
++
++/* Strings */
++static char conf_hdr[] = "VLAN Dev name  | VLAN ID\n";
++
++
++/*
++ *    Interface functions
++ */
++
++/*
++ *    Initialize vlan proc interface.
++ */
++
++__initfunc(int vlan_proc_init (void)) {
++        int err = proc_register(proc_net, &proc_vlan);
++
++        if (!err) {
++                proc_register(&proc_vlan, &proc_vlan_conf);
++        }
++        return err;
++}
++
++/*
++ *    Clean up router proc interface.
++ */
++
++void vlan_proc_cleanup (void) {
++        proc_unregister(&proc_vlan, proc_vlan_conf.low_ino);
++        proc_unregister(proc_net, proc_vlan.low_ino);
++}
++
++
++/*
++ *    Add directory entry for VLAN device.
++ */
++
++int vlan_proc_add_dev (struct device* vlandev) {
++        if (!vlandev->vlan_dev) {
++                printk(KERN_ERR "ERROR:  vlan_proc_add, device -:%s:- is NOT a VLAN\n",
++                       vlandev->name);
++                return -EINVAL;
++        }
++   
++        memset(&(vlandev->vlan_dev->dent), 0, sizeof(vlandev->vlan_dev->dent));
++        vlandev->vlan_dev->dent.namelen = strlen(vlandev->name);
++        vlandev->vlan_dev->dent.name = vlandev->name;
++        vlandev->vlan_dev->dent.mode = 0444 | S_IFREG;
++        vlandev->vlan_dev->dent.nlink = 1;
++        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 proc_register(&proc_vlan, &vlandev->vlan_dev->dent);
++}
++
++
++
++/*
++ *    Delete directory entry for VLAN device.
++ */
++int vlan_proc_rem_dev(struct device* vlandev) {
++        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__ ": calling proc_unregister for dev: %p\n",
++               vlandev);
++#endif
++        return proc_unregister(&proc_vlan, vlandev->vlan_dev->dent.low_ino);
++}
++
++
++/****** 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, 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 device* vlandev = NULL;
++        struct vlan_group* grp = NULL;
++        int i = 0;
++        char* nm_type = NULL;
++   
++        printk(VLAN_DBG __FUNCTION__ ": cnt == %i\n", cnt);
++
++        if (vlan_name_type == VLAN_NAME_TYPE_RAW_PLUS_VID) {
++                nm_type = "VLAN_NAME_TYPE_RAW_PLUS_VID";
++        }
++        else if (vlan_name_type == VLAN_NAME_TYPE_PLUS_VID_NO_PAD) {
++                nm_type = "VLAN_NAME_TYPE_PLUS_VID_NO_PAD";
++        }
++        else if (vlan_name_type == VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD) {
++                nm_type = "VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD";
++        }
++        else if (vlan_name_type == VLAN_NAME_TYPE_PLUS_VID) {
++                nm_type = "VLAN_NAME_TYPE_PLUS_VID";
++        }
++        else {
++                nm_type = "UNKNOWN";
++        }
++   
++        cnt += sprintf(buf + cnt, "Name-Type: %s  bad_proto_recvd: %lu\n",
++                       nm_type, vlan_bad_proto_recvd);
++   
++        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 | %s\n",
++                                                               vlandev->name, vlandev->vlan_dev->vlan_id, vlandev->vlan_dev->real_dev->name);
++                                        }/* 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, 
++                                int dummy) {
++        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, 
++                            int dummy) {
++        struct device* vlandev = (void*)start;
++        struct net_device_stats* stats;
++        int cnt = 0;
++        struct vlan_priority_tci_mapping* mp;
++        int i;
++   
++#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  REORDER_HDR: %i\n",
++                       vlandev->name, vlandev->vlan_dev->vlan_id, (int)(vlandev->vlan_dev->flags & 1));
++        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);
++
++        cnt += sprintf(buf + cnt, "%30s: %12lu\n",
++                       "total headroom inc", vlandev->vlan_dev->cnt_inc_headroom_on_tx);
++        
++        cnt += sprintf(buf + cnt, "%30s: %12lu\n",
++                       "total encap on xmit", vlandev->vlan_dev->cnt_encap_on_xmit);
++
++        cnt += sprintf(buf + cnt, "Device: %s", vlandev->vlan_dev->real_dev->name);
++
++   /* now show all PRIORITY mappings relating to this VLAN */
++        cnt += sprintf(buf + cnt, "\nINGRESS priority mappings: 0:%lu  1:%lu  2:%lu  3:%lu  4:%lu  5:%lu  6:%lu 7:%lu\n",
++                       vlandev->vlan_dev->ingress_priority_map[0],
++                       vlandev->vlan_dev->ingress_priority_map[1],
++                       vlandev->vlan_dev->ingress_priority_map[2],
++                       vlandev->vlan_dev->ingress_priority_map[3],
++                       vlandev->vlan_dev->ingress_priority_map[4],
++                       vlandev->vlan_dev->ingress_priority_map[5],
++                       vlandev->vlan_dev->ingress_priority_map[6],
++                       vlandev->vlan_dev->ingress_priority_map[7]);
++
++        cnt += sprintf(buf + cnt, "EGRESSS priority Mappings: ");
++   
++        for (i = 0; i<16; i++) {
++                mp = vlandev->vlan_dev->egress_priority_map[i];
++                while (mp) {
++                        cnt += sprintf(buf + cnt, "%lu:%hu ", mp->priority, ((mp->vlan_qos >> 13) & 0x7));
++
++                        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 */
++                        mp = mp->next;
++                }
++        }/* 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_dev(struct device *vlandev)
++{
++        return 0;
++}
++
++int vlan_proc_rem_dev(struct device *vlandev)
++{
++        return 0;
++}
++
++#endif
+diff -u -r -N -X /home/greear/exclude.list 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 Dec 29 19:51:33 2000
+@@ -0,0 +1,27 @@
++
++#ifndef __BEN_VLAN_PROC_INC__
++#define __BEN_VLAN_PROC_INC__
++
++
++int vlan_proc_init(void);
++
++int vlan_proc_rem_dev(struct device* vlandev);
++int vlan_proc_add_dev (struct device* 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 -u -r -N -X /home/greear/exclude.list linux/net/Config.in linux.dev/net/Config.in
+--- linux/net/Config.in        Sun Dec 10 17:49:44 2000
++++ linux.dev/net/Config.in    Fri Dec 29 19:51:33 2000
+@@ -44,6 +44,9 @@
+   fi
+   bool 'Frame Diverter (EXPERIMENTAL)' CONFIG_NET_DIVERT
+   bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC
++
++  bool '802.1Q VLAN Support (EXPERIMENTAL)' CONFIG_VLAN_802_1Q
++
+ #  if [ "$CONFIG_LLC" = "y" ]; then
+ #   bool 'Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI
+ #  fi
+diff -u -r -N -X /home/greear/exclude.list linux/net/Makefile linux.dev/net/Makefile
+--- linux/net/Makefile Mon Mar 22 12:18:17 1999
++++ linux.dev/net/Makefile     Fri Dec 29 19:51:33 2000
+@@ -10,7 +10,7 @@
+ MOD_SUB_DIRS := ipv4
+ 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
++              econet irda 802_1Q #decnet
+ SUB_DIRS     := core ethernet sched
+ MOD_LIST_NAME := NET_MISC_MODULES
+@@ -59,6 +59,10 @@
+ ifeq ($(CONFIG_BRIDGE),y)
+ SUB_DIRS += bridge
++endif
++
++ifeq ($(CONFIG_VLAN_802_1Q),y)
++SUB_DIRS += 802_1Q
+ endif
+ ifeq ($(CONFIG_IPX),y)
+diff -u -r -N -X /home/greear/exclude.list linux/net/core/dev.c linux.dev/net/core/dev.c
+--- linux/net/core/dev.c       Sun Dec 10 17:49:44 2000
++++ linux.dev/net/core/dev.c   Sun Jan 14 14:16:43 2001
+@@ -1,4 +1,4 @@
+-/*
++/* -*- linux-c -*-
+  *    NET3    Protocol independent device support routines.
+  *
+  *            This program is free software; you can redistribute it and/or
+@@ -94,6 +94,11 @@
+ #ifdef CONFIG_NET_RADIO
+ #include <linux/wireless.h>
+ #endif        /* CONFIG_NET_RADIO */
++
++#ifdef CONFIG_VLAN_802_1Q
++#include "../802_1Q/vlan.h"
++#endif
++
+ #ifdef CONFIG_PLIP
+ extern int plip_init(void);
+ #endif
+@@ -123,9 +128,17 @@
+  *    and the routines to invoke.
+  *
+  *    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. 
++ *    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
+  *            0004    802.2
+@@ -170,6 +183,256 @@
+ static void dev_clear_backlog(struct device *dev);
++/* Taking this out, because lo has problems for some people.  Feel
++ * free to turn it back on and give me (greearb@candelatech.com) bug
++ * reports if you can re-produce the problem. --Ben
++
++ #define BENS_FAST_DEV_LOOKUP
++
++*/
++#ifdef BENS_FAST_DEV_LOOKUP
++/* Fast Device Lookup code.  Should give much better than
++ * linear speed when looking for devices by idx or name.
++ * --Ben (greearb@candelatech.com)
++ */
++#define FDL_HASH_LEN 256
++
++/* #define FDL_DEBUG */
++
++struct dev_hash_node {
++        struct device* dev;
++        struct dev_hash_node* next;
++};
++
++struct dev_hash_node* fdl_name_base[FDL_HASH_LEN];/* hashed by name */
++struct dev_hash_node* fdl_idx_base[FDL_HASH_LEN]; /* hashed by index */
++int fdl_initialized_yet = 0;
++
++/* TODO:  Make these inline methods */
++/* Nice cheesy little hash method to be used on device-names (eth0, ppp0, etc) */
++int fdl_calc_name_idx(const char* dev_name) {
++        int tmp = 0;
++        int i;
++#ifdef FDL_DEBUG
++        printk(KERN_ERR "fdl_calc_name_idx, name: %s\n", dev_name);
++#endif
++        for (i = 0; dev_name[i]; i++) {
++                tmp += (int)(dev_name[i]);
++        }
++        if (i > 3) {
++                tmp += (dev_name[i-2] * 10); /* might add a little spread to the hash */
++                tmp += (dev_name[i-3] * 100); /* might add a little spread to the hash */
++        }
++#ifdef FDL_DEBUG
++        printk(KERN_ERR "fdl_calc_name_idx, rslt: %i\n", (int)(tmp % FDL_HASH_LEN));
++#endif
++        return (tmp % FDL_HASH_LEN);
++}
++
++int fdl_calc_index_idx(const int ifindex) {
++        return (ifindex % FDL_HASH_LEN);
++}
++
++
++/* Better have a lock on the dev_base before calling this... */
++int __fdl_ensure_init(void) {
++#ifdef FDL_DEBUG
++        printk(KERN_ERR "__fdl_ensure_init, enter\n");
++#endif
++        if (! fdl_initialized_yet) {
++                /* only do this once.. */
++                int i;
++                int idx = 0; /* into the hash table */
++                struct device* dev = dev_base;
++                struct dev_hash_node* dhn;
++
++#ifdef FDL_DEBUG
++                printk(KERN_ERR "__fdl_ensure_init, doing real work...");
++#endif
++
++                fdl_initialized_yet = 1; /* it has been attempted at least... */
++
++                for (i = 0; i<FDL_HASH_LEN; i++) {
++                        fdl_name_base[i] = NULL;
++                        fdl_idx_base[i] = NULL;
++                }
++
++                /* add any current devices to the hash tables at this time.  Note that
++                 * this method must be called with locks on the dev_base acquired.
++                 */
++                while (dev) {
++
++#ifdef FDL_DEBUG
++                        printk(KERN_ERR "__fdl_ensure_init, dev: %p dev: %s, idx: %i\n", dev, dev->name, idx);
++#endif
++                        /* first, take care of the hash-by-name */
++                        idx = fdl_calc_name_idx(dev->name);
++                        dhn = kmalloc(sizeof(struct dev_hash_node), GFP_ATOMIC);
++                        if (dhn) {
++                                dhn->dev = dev;
++                                dhn->next = fdl_name_base[idx];
++                                fdl_name_base[idx] = dhn;
++                        }
++                        else {
++                                /* Nasty..couldn't get memory... */
++                                return -ENOMEM;
++                        }
++
++                        /* now, do the hash-by-idx */
++                        idx = fdl_calc_index_idx(dev->ifindex);
++                        dhn = kmalloc(sizeof(struct dev_hash_node), GFP_ATOMIC);
++                        if (dhn) {
++                                dhn->dev = dev;
++                                dhn->next = fdl_idx_base[idx];
++                                fdl_idx_base[idx] = dhn;
++                        }
++                        else {
++                                /* Nasty..couldn't get memory... */
++                                return -ENOMEM;
++                        }
++         
++                        dev = dev->next;
++                }
++                fdl_initialized_yet = 2; /* initialization actually worked */
++        }
++#ifdef FDL_DEBUG
++        printk(KERN_ERR "__fdl_ensure_init, end, fdl_initialized_yet: %i\n", fdl_initialized_yet);
++#endif
++        if (fdl_initialized_yet == 2) {
++                return 0;
++        }
++        else {
++                return -1;
++        }
++}/* fdl_ensure_init */
++
++
++/* called from register_netdevice, assumes dev is locked, and that no one
++ * will be calling __find_dev_by_name before this exits.. etc.
++ */
++int __fdl_register_netdevice(struct device* dev) {
++        if (__fdl_ensure_init() == 0) {
++                /* first, take care of the hash-by-name */
++                int idx = fdl_calc_name_idx(dev->name);
++                struct dev_hash_node* dhn = kmalloc(sizeof(struct dev_hash_node), GFP_ATOMIC);
++
++#ifdef FDL_DEBUG
++                printk(KERN_ERR "__fdl_register_netdevice, dev: %p dev: %s, idx: %i", dev, dev->name, idx);
++#endif
++
++                if (dhn) {
++                        dhn->dev = dev;
++                        dhn->next = fdl_name_base[idx];
++                        fdl_name_base[idx] = dhn;
++                }
++                else {
++                        /* Nasty..couldn't get memory... */
++                        /* Don't try to use these hash tables any more... */
++                        fdl_initialized_yet = 1; /* tried, but failed */
++                        return -ENOMEM;
++                }
++      
++                /* now, do the hash-by-idx */
++                idx = fdl_calc_index_idx(dev->ifindex);
++                dhn = kmalloc(sizeof(struct dev_hash_node), GFP_ATOMIC);
++
++#ifdef FDL_DEBUG
++                printk(KERN_ERR "__fdl_register_netdevice, ifindex: %i, idx: %i", dev->ifindex, idx);
++#endif
++
++                if (dhn) {
++                        dhn->dev = dev;
++                        dhn->next = fdl_idx_base[idx];
++                        fdl_idx_base[idx] = dhn;
++                }
++                else {
++                        /* Nasty..couldn't get memory... */
++                        /* Don't try to use these hash tables any more... */
++                        fdl_initialized_yet = 1; /* tried, but failed */
++                        return -ENOMEM;
++                }
++        }
++        return 0;
++} /* fdl_register_netdevice */
++
++
++/* called from register_netdevice, assumes dev is locked, and that no one
++ * will be calling __find_dev_by_name, etc.  Returns 0 if found & removed one,
++ * returns -1 otherwise.
++ */
++int __fdl_unregister_netdevice(struct device* dev) {
++        int retval = -1;
++        if (fdl_initialized_yet == 2) { /* If we've been initialized correctly... */
++                /* first, take care of the hash-by-name */
++                int idx = fdl_calc_name_idx(dev->name);
++                struct dev_hash_node* prev = fdl_name_base[idx];
++                struct dev_hash_node* cur = NULL;
++
++#ifdef FDL_DEBUG
++                printk(KERN_ERR "__fdl_unregister_netdevice, dev: %p dev: %s, idx: %i", dev, dev->name, idx);
++#endif
++
++                if (prev) {
++                        if (strcmp(dev->name, prev->dev->name) == 0) {
++                                /* it's the first one... */
++                                fdl_name_base[idx] = prev->next;
++                                kfree(prev);
++                                retval = 0;
++                        }
++                        else {
++                                cur = prev->next;
++                                while (cur) {
++                                        if (strcmp(dev->name, cur->dev->name) == 0) {
++                                                prev->next = cur->next;
++                                                kfree(cur);
++                                                retval = 0;
++                                                break;
++                                        }
++                                        else {
++                                                prev = cur;
++                                                cur = cur->next;
++                                        }
++                                }
++                        }
++                }
++
++                /* Now, the hash-by-index */
++                idx = fdl_calc_index_idx(dev->ifindex);
++                prev = fdl_idx_base[idx];
++                cur = NULL;
++                if (prev) {
++                        if (dev->ifindex == prev->dev->ifindex) {
++                                /* it's the first one... */
++                                fdl_idx_base[idx] = prev->next;
++                                kfree(prev);
++                                retval = 0;
++                        }
++                        else {
++                                cur = prev->next;
++                                while (cur) {
++                                        if (dev->ifindex == cur->dev->ifindex) {
++                                                prev->next = cur->next;
++                                                kfree(cur);
++                                                retval = 0;
++                                                break;
++                                        }
++                                        else {
++                                                prev = cur;
++                                                cur = cur->next;
++                                        }
++                                }
++                        }
++                }
++        }/* if we ensured init OK */
++        return retval;
++} /* fdl_unregister_netdevice */
++
++
++
++#endif   /* BENS_FAST_DEV_LOOKUP */
++
++
++
+ /******************************************************************************************
+               Protocol management and registration routines
+@@ -267,6 +530,25 @@
+ {
+       struct device *dev;
++#ifdef BENS_FAST_DEV_LOOKUP
++        int idx = fdl_calc_name_idx(name);
++        struct dev_hash_node* dhn;
++        if (fdl_initialized_yet == 2) {
++#ifdef FDL_DEBUG
++           printk(KERN_ERR "__dev_get_by_name, name: %s  idx: %i\n", name, idx);
++#endif
++           dhn = fdl_name_base[idx];
++           while (dhn) {
++              if (strcmp(dhn->dev->name, name) == 0) {
++                 /* printk(KERN_ERR "__dev_get_by_name, found it: %p\n", dhn->dev); */
++                 return dhn->dev;
++              }
++              dhn = dhn->next;
++           }
++           /* printk(KERN_ERR "__dev_get_by_name, didn't find it for name: %s\n", name); */
++           return NULL;
++        }
++#endif
+       for (dev = dev_base; dev != NULL; dev = dev->next) 
+       {
+               if (strcmp(dev->name, name) == 0)
+@@ -278,7 +560,19 @@
+ struct device * dev_get_by_index(int ifindex)
+ {
+       struct device *dev;
+-
++#ifdef BENS_FAST_DEV_LOOKUP
++        int idx = fdl_calc_index_idx(ifindex);
++        struct dev_hash_node* dhn;
++        if (fdl_initialized_yet == 2) { /* have we gone through initialization before... */
++           dhn = fdl_idx_base[idx];
++           while (dhn) {
++              if (dhn->dev->ifindex == ifindex)
++                 return dhn->dev;
++              dhn = dhn->next;
++           }
++           return NULL;
++        }
++#endif
+       for (dev = dev_base; dev != NULL; dev = dev->next) 
+       {
+               if (dev->ifindex == ifindex)
+@@ -310,14 +604,17 @@
+       int i;
+       /*
+        *      If you need over 100 please also fix the algorithm...
+-       */
+-      for(i=0;i<100;i++)
++         *
++         *  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(dev->name,name,i);
+               if(dev_get(dev->name)==NULL)
+                       return i;
+       }
+-      return -ENFILE; /* Over 100 of the things .. bail out! */
++      return -ENFILE; /* Over 8192 of the things .. bail out! */
+ }
+  
+ struct device *dev_alloc(const char *name, int *err)
+@@ -830,7 +1127,7 @@
+               if(skb==NULL)           
+                       return;
+                       
+-              offset=skb->data-skb->mac.raw;
++              offset = skb->data - skb->mac.raw;
+               skb_push(skb,offset);   /* Put header back on for bridge */
+               if(br_receive_frame(skb))
+@@ -956,7 +1253,7 @@
+               }
+               /*
+-               *      Fetch the packet protocol ID. 
++               *      Fetch the packet protocol ID. (In Network Byte Order --BLG)
+                */
+               type = skb->protocol;
+@@ -1603,8 +1900,15 @@
+                               return -EBUSY;
+                       if (dev_get(ifr->ifr_newname))
+                               return -EEXIST;
++#ifdef BENS_FAST_DEV_LOOKUP
++                        /* Doesn't seem to need any additional locking in kernel 2.2 series... --Ben */
++                        __fdl_unregister_netdevice(dev); /* take it out of the name hash table */
++#endif
+                       memcpy(dev->name, ifr->ifr_newname, IFNAMSIZ);
+                       dev->name[IFNAMSIZ-1] = 0;
++#ifdef BENS_FAST_DEV_LOOKUP
++                        __fdl_register_netdevice(dev); /* put it back in the name hash table, with the new name */
++#endif
+                       notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev);
+                       return 0;
+@@ -1809,6 +2113,12 @@
+                               return -EEXIST;
+               }
+               dev->next = NULL;
++#ifdef BENS_FAST_DEV_LOOKUP
++                /* Must do this before dp is set to dev, or it could be added twice, once
++                 * on initialization based on dev_base, and once again after that...
++                 */
++                __fdl_register_netdevice(dev);
++#endif
+               *dp = dev;
+ #ifdef CONFIG_NET_DIVERT
+               ret=alloc_divert_blk(dev);
+@@ -1834,6 +2144,13 @@
+       dev->ifindex = dev_new_index();
+       if (dev->iflink == -1)
+               dev->iflink = dev->ifindex;
++
++#ifdef BENS_FAST_DEV_LOOKUP
++        /* Must do this before dp is set to dev, or it could be added twice, once
++         * on initialization based on dev_base, and once again after that...
++         */
++        __fdl_register_netdevice(dev);
++#endif
+       *dp = dev;
+       /* Notify protocols, that a new device appeared. */
+@@ -1885,6 +2202,9 @@
+       for (dp = &dev_base; (d=*dp) != NULL; dp=&d->next) {
+               if (d == dev) {
+                       *dp = d->next;
++#ifdef BENS_FAST_DEV_LOOKUP
++                        __fdl_unregister_netdevice(dev);
++#endif
+                       synchronize_bh();
+                       d->next = NULL;
+diff -u -r -N -X /home/greear/exclude.list linux/net/ethernet/eth.c linux.dev/net/ethernet/eth.c
+--- linux/net/ethernet/eth.c   Tue Jan  4 11:12:26 2000
++++ linux.dev/net/ethernet/eth.c       Fri Dec 29 19:51:33 2000
+@@ -174,17 +174,32 @@
+  *    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.
++ *
++ *  NOTE:  It is likely that you will want to change vlan_type_trans in
++ *         802_1Q/vlan.c if you change anything here.
+  */
+  
+ unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev)
+ {
+       struct ethhdr *eth;
+       unsigned char *rawp;
+-      
+-      skb->mac.raw=skb->data;
+-      skb_pull(skb,dev->hard_header_len);
+-      eth= skb->mac.ethernet;
+-      
++
++        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)
+       {
+               if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
+@@ -206,7 +221,21 @@
+               if(memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN))
+                       skb->pkt_type=PACKET_OTHERHOST;
+       }
+-      
++
++#ifdef CONFIG_VLAN_802_1Q
++        if (ntohs(eth->h_proto) == ETH_P_802_1Q) {
++                /* 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 -u -r -N -X /home/greear/exclude.list linux/net/protocols.c linux.dev/net/protocols.c
+--- linux/net/protocols.c      Thu Dec 17 10:03:57 1998
++++ linux.dev/net/protocols.c  Fri Dec 29 19:51:33 2000
+@@ -34,6 +34,10 @@
+ extern void packet_proto_init(struct net_proto *pro);
+ #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
+ #include <net/ipxcall.h>
+@@ -169,5 +173,9 @@
+   { "IrDA",     irda_proto_init },                     /* IrDA protocols */
+ #endif
++#ifdef CONFIG_VLAN_802_1Q
++  { "VLAN",     vlan_proto_init },                    /* 802.1Q VLAN Support. --BLG */
++#endif
++  
+   { NULL,     NULL            }                       /* End marker                   */
+ };
diff --git a/vlan_test.pl b/vlan_test.pl
new file mode 100644 (file)
index 0000000..f723089
--- /dev/null
@@ -0,0 +1,223 @@
+#!/usr/bin/perl
+
+# For now, this just tests the addition and removal of 1000 VLAN interfaces on eth0
+
+# Arguments:
+#  graph  Generate a graph.
+#  clean  Remove interfaces.
+
+use strict;
+
+$| = 1;
+
+if ($ARGV[0] eq "graph") {
+  my $dev_cnt = 0;
+  my $user = 0;
+  my $system = 0;
+  my $real = 0;
+  my $prog = "";
+
+  open(IPNH, ">/tmp/ip_rpt_no_hash.txt") || die("Can't open /tmp/ip_rpt_no_hash.txt\n");
+  open(IFCFGNH, ">/tmp/ifconfig_rpt_no_hash.txt") || die("Can't open /tmp/ifconfig_rpt_no_hash.txt\n");
+  open(IP, ">/tmp/ip_rpt.txt") || die("Can't open /tmp/ip_rpt.txt\n");
+  open(IFCFG, ">/tmp/ifconfig_rpt.txt") || die("Can't open /tmp/ifconfig_rpt.txt\n");
+
+  my $hash = $ARGV[1];
+  my $no_hash = $ARGV[2];
+
+  open(IF, "$no_hash");
+  while (<IF>) {
+    my $ln = $_;
+    chomp($ln);
+
+    #print "LINE: -:$ln:-\n";
+
+    if ($ln =~ /Doing ip addr show for (\S+)/) {
+      $dev_cnt = $1;
+      $prog = "ip";
+    }
+    elsif ($ln =~ /Doing ifconfig -a for (\S+)/) {
+      $dev_cnt = $1;
+      $prog = "ifconfig";
+    }
+    elsif ($ln =~ /^real (\S+)/) {
+      $real = $1;
+    }
+    elsif ($ln =~ /^user (\S+)/) {
+      $user = $1;
+    }
+    elsif ($ln =~ /^sys (\S+)/) {
+      $system = $1;
+      #print "prog: $prog  $dev_cnt\t$user\t$system\t$real\n";
+      if ($prog eq "ip") {
+       print IPNH "$dev_cnt\t$user\t$system\t$real\n";
+      }
+      else {
+       print IFCFGNH "$dev_cnt\t$user\t$system\t$real\n";
+      }
+    }
+    else {
+      #print "INFO:  Didn't match anything -:$ln:-\n";
+    }
+  }
+
+  close(IPNH);
+  close(IFCFGNH);
+  close(IF);
+
+  open(IF, "$hash");
+  while (<IF>) {
+    my $ln = $_;
+    chomp($ln);
+
+    #print "LINE: -:$ln:-\n";
+
+    if ($ln =~ /Doing ip addr show for (\S+)/) {
+      $dev_cnt = $1;
+      $prog = "ip";
+    }
+    elsif ($ln =~ /Doing ifconfig -a for (\S+)/) {
+      $dev_cnt = $1;
+      $prog = "ifconfig";
+    }
+    elsif ($ln =~ /^real (\S+)/) {
+      $real = $1;
+    }
+    elsif ($ln =~ /^user (\S+)/) {
+      $user = $1;
+    }
+    elsif ($ln =~ /^sys (\S+)/) {
+      $system = $1;
+      #print "prog: $prog  $dev_cnt\t$user\t$system\t$real\n";
+      if ($prog eq "ip") {
+       print IP "$dev_cnt\t$user\t$system\t$real\n";
+      }
+      else {
+       print IFCFG "$dev_cnt\t$user\t$system\t$real\n";
+      }
+    }
+    else {
+      #print "INFO:  Didn't match anything -:$ln:-\n";
+    }
+  }
+
+  close(IP);
+  close(IFCFG);
+
+  my $plot_cmd = "set title \"ip addr show V/S ifconfig -a\"
+set terminal png color
+set output \"ip_addr_show.png\"
+set size 1,2
+set xlabel \"Interface Count\"
+set ylabel \"Seconds\"
+set grid
+plot \'/tmp/ip_rpt.txt\' using 1:3 title \"ip_system\" with lines, \\
+     \'/tmp/ip_rpt.txt\' using 1:2 title \"ip_user\" with lines, \\
+     \'/tmp/ifconfig_rpt.txt\' using 1:3 title \"ifconfig_system\" with lines, \\
+     \'/tmp/ifconfig_rpt.txt\' using 1:2 title \"ifconfig_user\" with lines, \\
+     \'/tmp/ip_rpt_no_hash.txt\' using 1:3 title \"ip_system_no_hash\" with lines, \\
+     \'/tmp/ip_rpt_no_hash.txt\' using 1:2 title \"ip_user_no_hash\" with lines, \\
+     \'/tmp/ifconfig_rpt_no_hash.txt\' using 1:3 title \"ifconfig_system_no_hash\" with lines, \\
+     \'/tmp/ifconfig_rpt_no_hash.txt\' using 1:2 title \"ifconfig_user_no_hash\" with lines";
+  print "Plotting with cmd -:$plot_cmd:-\n";
+
+  open(GP, "| gnuplot") or die ("Can't open gnuplot pipe(2).\n");
+  print GP "$plot_cmd";
+  close(GP);
+
+  exit(0);
+}
+
+my $num_if = 4000;
+
+`/usr/local/bin/vconfig set_name_type VLAN_PLUS_VID_NO_PAD`;
+
+my $d = 5;
+my $c = 5;
+
+if ($ARGV[0] ne "clean") {
+
+  my $i;
+  print "Adding VLAN interfaces 1 through $num_if\n";
+
+  print "Turnning off /sbin/hotplug";
+  `echo  > /proc/sys/kernel/hotplug`;
+
+  my $p = time();
+  for ($i = 1; $i<=$num_if; $i++) {
+    `/usr/local/bin/vconfig add eth0 $i`;
+    #`ip address flush dev vlan$i`;
+    `ip address add 192.168.$c.$c/24 dev vlan$i`;
+    `ip link set dev vlan$i up`;
+
+    if (($i <= 4000) && (($i % 250) == 0)) {
+      print "Doing ifconfig -a for $i devices.\n";
+      `time -p ifconfig -a > /tmp/vlan_test_ifconfig_a_$i.txt`;
+      print "Doing ip addr show for $i devices.\n";
+      `time -p ip addr show > /tmp/vlan_test_ip_addr_$i.txt`;
+    }
+
+    $d++;
+    if ($d > 250) {
+      $d = 5;
+      $c++;
+    }
+  }
+  my $n = time();
+  my $diff = $n - $p;
+
+  print "Done adding $num_if VLAN interfaces in $diff seconds.\n";
+
+  sleep 2;
+}
+
+print "Removing VLAN interfaces 1 through $num_if\n";
+$d = 5;
+$c = 5;
+my $p = time();
+my $i;
+for ($i = 1; $i<=$num_if; $i++) {
+  `/usr/local/bin/vconfig rem vlan$i`;
+
+  $d++;
+  if ($d > 250) {
+    $d = 5;
+    $c++;
+  }
+}
+my $n = time();
+my $diff = $n - $p;
+print "Done deleting $num_if VLAN interfaces in $diff seconds.\n";
+
+sleep 2;
+
+
+if ($ARGV[0] ne "clean") {
+
+  my $tmp = $num_if / 4;
+  print "\nGoing to add and remove 2 interfaces $tmp times.\n";
+  $p = time();
+  
+  
+  for ($i = 1; $i<=$tmp; $i++) {
+    `/usr/local/bin/vconfig add eth0 1`;
+    `ifconfig vlan1 192.168.200.200`;
+    `ifconfig vlan1 up`;
+    `ifconfig vlan1 down`;
+    
+    `/usr/local/bin/vconfig add eth0 2`;
+    `ifconfig vlan2 192.168.202.202`;
+    `ifconfig vlan2 up`;
+    `ifconfig vlan2 down`;
+    
+    `/usr/local/bin/vconfig rem vlan2`;
+    `/usr/local/bin/vconfig rem vlan1`;
+  }
+  $n = time();
+  $diff = $n - $p;
+  print "Done adding/removing 2 VLAN interfaces $tmp times in $diff seconds.\n";
+}
+
+print "Re-installing /sbin/hotplug";
+`echo /sbin/hotplug > /proc/sys/kernel/hotplug`;
+
diff --git a/vlan_test2.pl b/vlan_test2.pl
new file mode 100644 (file)
index 0000000..4b73a81
--- /dev/null
@@ -0,0 +1,116 @@
+#!/usr/bin/perl
+
+# Arguments:
+#  clean  Remove interfaces.
+
+use strict;
+
+$| = 1;
+
+my $num_if = 4000;
+
+`modprobe 8021q`;
+print "Memory after loading 8021q module: ";
+print `free`;
+print "\n";
+
+`/usr/local/bin/vconfig set_name_type VLAN_PLUS_VID_NO_PAD`;
+
+my $d = 5;
+my $c = 5;
+
+if ($ARGV[0] ne "clean") {
+
+  my $i;
+  print "Adding VLAN interfaces 1 through $num_if\n";
+
+  print "Turnning off /sbin/hotplug...\n";
+  `echo  > /proc/sys/kernel/hotplug`;
+
+  my $p = time();
+  for ($i = 1; $i<=$num_if; $i++) {
+    `/usr/local/bin/vconfig add eth0 $i`;
+    #`ip address flush dev vlan$i`;
+    `ip address add 192.168.$c.$c/24 dev vlan$i`;
+    `ip link set dev vlan$i up`;
+
+    $d++;
+    if ($d > 250) {
+      print ".";
+      $d = 5;
+      $c++;
+    }
+  }
+
+  print "\nMemory after creating $i vlan devices: ";
+  print `free`;
+  print "\n";
+
+  print "Doing ifconfig -a for $i devices.\n";
+  `time -p ifconfig -a > /tmp/vlan_test_ifconfig_a_$i.txt`;
+  print "Doing ip addr show for $i devices.\n";
+  `time -p ip addr show > /tmp/vlan_test_ip_addr_$i.txt`;
+
+  my $n = time();
+  my $diff = $n - $p;
+
+  print "Done adding $num_if VLAN interfaces in $diff seconds.\n";
+
+  sleep 2;
+}
+
+print "Removing VLAN interfaces 1 through $num_if\n";
+$d = 5;
+$c = 5;
+my $p = time();
+my $i;
+for ($i = 1; $i<=$num_if; $i++) {
+  `/usr/local/bin/vconfig rem vlan$i`;
+}
+my $n = time();
+my $diff = $n - $p;
+print "Done deleting $num_if VLAN interfaces in $diff seconds.\n";
+
+print "Memory after deleting $i vlan devices: ";
+print `free`;
+print "\n";
+
+sleep 2;
+
+
+if ($ARGV[0] ne "clean") {
+
+  my $tmp = $num_if * 4;
+  print "\nGoing to add and remove 2 interfaces $tmp times.\n";
+  $p = time();
+  
+  
+  for ($i = 1; $i<=$tmp; $i++) {
+    `/usr/local/bin/vconfig add eth0 1`;
+    `ifconfig vlan1 192.168.200.200`;
+    `ifconfig vlan1 up`;
+    `ifconfig vlan1 down`;
+    
+    `/usr/local/bin/vconfig add eth0 2`;
+    `ifconfig vlan2 192.168.202.202`;
+    `ifconfig vlan2 up`;
+    `ifconfig vlan2 down`;
+    
+    `/usr/local/bin/vconfig rem vlan2`;
+    `/usr/local/bin/vconfig rem vlan1`;
+
+    if (($i % 125) == 0) {
+      print ".";
+    }
+  }
+  $n = time();
+  $diff = $n - $p;
+  print "\nDone adding/removing 2 VLAN interfaces $tmp times in $diff seconds.\n";
+}
+
+print "Re-installing /sbin/hotplug...\n";
+`echo /sbin/hotplug > /proc/sys/kernel/hotplug`;
+
+print "Memory at end of the run: ";
+print `free`;
+print "\n";