--- /dev/null
+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.)
--- /dev/null
+/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////
--- /dev/null
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
--- /dev/null
+# -*-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
--- /dev/null
+# 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 *~
+
+
+
+
+
--- /dev/null
+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
--- /dev/null
+--- 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(¤t->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
+
--- /dev/null
+/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
--- /dev/null
+vlan/contrib
--- /dev/null
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
--- /dev/null
+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)
--- /dev/null
+#! /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
--- /dev/null
+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 */
+ };
--- /dev/null
+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);
--- /dev/null
+<!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 <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 < $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 <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: 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 <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" <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\.]+$' | \
+< 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>
--- /dev/null
+D/SUNOS4////
+D/bpf////
+D/lbl////
+D/linux-include////
+D/net////
--- /dev/null
+vlan/libpcap-0.4
--- /dev/null
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
--- /dev/null
+vlan/libpcap-0.4/SUNOS4
--- /dev/null
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
--- /dev/null
+vlan/libpcap-0.4/bpf
--- /dev/null
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
--- /dev/null
+vlan/libpcap-0.4/bpf/net
--- /dev/null
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
--- /dev/null
+vlan/libpcap-0.4/lbl
--- /dev/null
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
--- /dev/null
+D/netinet////
--- /dev/null
+vlan/libpcap-0.4/linux-include
--- /dev/null
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
--- /dev/null
+vlan/libpcap-0.4/linux-include/netinet
--- /dev/null
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
--- /dev/null
+vlan/libpcap-0.4/net
--- /dev/null
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
--- /dev/null
+/*
+#######################################################################
+#
+# (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;
+}
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_"/>
+ </request>
+</manifest>
--- /dev/null
+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*
--- /dev/null
+D/lbl////
+D/linux-include////
--- /dev/null
+vlan/tcpdump-3.4
--- /dev/null
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
--- /dev/null
+vlan/tcpdump-3.4/lbl
--- /dev/null
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
--- /dev/null
+D/net////
+D/netinet////
+D/sys////
--- /dev/null
+vlan/tcpdump-3.4/linux-include
--- /dev/null
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
--- /dev/null
+vlan/tcpdump-3.4/linux-include/net
--- /dev/null
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
--- /dev/null
+vlan/tcpdump-3.4/linux-include/netinet
--- /dev/null
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
--- /dev/null
+vlan/tcpdump-3.4/linux-include/sys
--- /dev/null
+:pserver:greear@ns1.wanfear.com:/home/cvs/vlan
--- /dev/null
+.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>
--- /dev/null
+//
+//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 */
--- /dev/null
+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
--- /dev/null
+<!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: 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> For Kernel: 2.4.21+ 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> For Kernel: 2.4.14+ 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> For Kernel: 2.4.14+ 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> For Kernel: 2.4.14+ 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> For Kernel: 2.4.12-pre5 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> For Kernel: 2.4.8 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> For Kernel: 2.4.7 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> For Kernel: 2.2.18/19, 2.4.3-pre3 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> For Kernel: 2.2.18, 2.4.0 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> For Kernel: 2.2.18, 2.4.prerelease 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> For Kernel: 2.2.17, 2.4.pre9 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> For Kernel: 2.2.17, 2.4.pre9 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> For Kernel: 2.2.16, 2.4.pre7 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> For Kernel: 2.2.13/14, 2.3.99 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 (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 < $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<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>
+ <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>
--- /dev/null
+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 */
+ };
--- /dev/null
+#!/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`;
+
--- /dev/null
+#!/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";