From: Jinkun Jang Date: Tue, 12 Mar 2013 16:45:28 +0000 (+0900) Subject: Tizen 2.1 base X-Git-Tag: 2.1b_release^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fheads%2Ftizen_2.1;p=platform%2Fupstream%2Fvconfig.git Tizen 2.1 base --- diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..85c60bf --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,363 @@ +This file should be prepended to each time a release is made. + +Date: Sept 30, 2003 +Version: 1.8 +Kernel Version: 2.4.21+ +Changes: Compile fixes for newer gcc, VLAN rework, inclusion of entire + Candela Technologies kernel networking patch (MAC-VLANs too). + +Date: March 11, 2003 +Version: 1.7 +Kernel Version: 2.4.14+ +Changes: Added Alex's MAC-Vlan code to the repository. See README for more. + + +Date: March 24, 2002 +Version: 1.6 +Kernel Version: 2.4.14+ +Changes: Removed 2.4 kernel patch from VLAN distribution..it's now in the + standard linux kernel. Other updates include vconfig changes + to fix some compile problems, and to enable cross-compiling to + ARM (this assumes you are using the Intrinsyc cross-compiler in + it's standard location). + + +Date: Oct 20, 2001 +Version: 1.5 +Kernel Version: 2.4.12-pre5 +Changes: + Mostly added other peoples fixes and patches (thanks folks!) + Finally fixed mc-list leakage (Ard van Breemen) + Flush mc-list at vlan-destory (Ard van Breemen) + Add vconfig man page to distribution (Ard van Breemen) + Fix problem with /proc and renaming VLAN devices (af AT devcon D.T net) + Add relatively large change by Nick Eggelston that makes VLAN + devices more transparent to tools like tcpdump and other raw + packet snoopers. This will only be enabled when the REORDER_HDR + flag is set. + + +Date: August 16, 2001 +Version: 1.4 +Kernel Version: 2.4.9-pre4 +Status: Should be stable, but a decent amount of rework went into + this one... +Changes: + Code should no longer require /proc interface in order to + get at the IOCTLs. The IOCTLs are now tied to sockets. + When using modules, it may auto-load now, too... + + Fixed format string error in proc fs display. + + Fixed crash bug relating to memory allocation with locks + held (we now use GF_ATOMIC). + + hard_start_xmit will now grow the packet header if there + is not enough headroom. This may fix an MPLS-over-VLAN problem, + though the real solution is to make MPLS allocate + more headroom anyway... + + vconfig was changed to use the new IOCTL API, and the old + vconfig WILL NOT WORK with this or any newer patches... + + +Date: August 5, 2001 +Version: 1.0.3 +Kernel Version: 2.4.7 +Status: Should be stable, but a decent amount of rework went into + this one... +Changes: + Re-worked code to comply with linux network code gurus' + wishes. This included several boundary case fixes, including + some that could crash your kernel. + + The default naming scheme is eth0.5 now, for VID == 5 on + eth0. Use vconfig to change the naming scheme if you want. + + There were *NO* changes to the 2.2 series patch, and there + probably won't be any more changes to it, ever! + + +Date: April 16, 2001 +Version: 1.0.1 +Kernel Version: 2.2.18/19, 2.4.3-pre3 +Status: Very similar to 1.0.0, should be relatively stable. +Changes: + Incorporated a fix for changing a MAC on a VLAN, it now + correctly sets PACKET_HOST. + Thanks to Martin Bokaemper for this one. + + The 2.4 series patch should now compile as a module, thanks + to a tweak from someone who's mail I have lost! Anyway, 3 + cheers to the un-named coder! + + There were *NO* changes to the 2.2 series patch, though I did + verify that it seems to work fine with the 2.2.19 kernel. + + + +Date: January 14, 2001 +Version: 1.0.0 +Kernel Version: 2.2.18, 2.4.0 +Status: Fairly similar to 0.0.15, should be relatively stable. +Changes: + + Really fixed (and tested) MAC change-ability. When you set the + MAC address on a VLAN, it will also attempt to set the underlying + device to PROMISCious mode (otherwise, the VLAN will not + receive any packets.) + + Hashed-device lookup is disabled by default because some people + had trouble with the 'lo' device. Please feel free to re-enable by + editing the line in net/core/dev. + (search for #define BEN_FAST_DEV_LOOKUP). + + vconfig should warn when creating VLAN 1, because that VLAN is + not compatible with many switches. + + +Date: December 31, 2000 +Version: 0.0.15 +Kernel Version: 2.2.18, 2.4.prerelease +Status: This one is pretty fresh..beware, especially the 2.4 patch. +Changes: + + Merged most of Matti Aarnio's patches. This means no significant patch + to eth.c now, and will help port VLANs to non-ethernet devices + (ie ppp, TokenRing??). + + Setting the MAC address should work now..I think it was broken before. + + Miscellaneous code re-organization to make patches to existing files smaller. + + +Date: October 26, 2000 +Version: 0.0.14 +Kernel Version: 2.2.17, 2.4.pre9 +Status: Seems stable. +Changes: + Removed vlan-space-per-machine, so vlan-space-per-NIC is mandatory now. + + DHCP might work now, as I've added support for encapsulating regular ethernet + frames if they are sent to the vlan driver. + + Fixed up the name/index hashing stuff to handle changing the name on a device. + + Took out default VID & default priority, as their usefullness was in question, + and the code was broken anyway. + + +Date: October 11, 2000 +Version: 0.0.13 +Kernel Version: 2.2.17, 2.4.pre9 +Status: BUSTED!! Don't use it. +Changes: + Added support for MULTICAST to the VLAN devices. Thanks to + Gleb & Co for most of that code. + + Added the ability to set the MAC address on the VLAN. For now, + you'll either need to set your Ethernet NIC into PROMISC mode, or + maybe figure out some multi-cast ethernet address to set on the + NIC. This has not been tested well at all. + + Added a hashed device-name lookup scheme. This greatly speeds + up ifconfig -a. I was able to run an ifconfig -a in 20 seconds on a + Celeron 500, with 4000 vlan devices configured!! + + Added vlan_test.pl to help me find dumb bugs. Feel free to make this + much more powerful, and send the code back to me! + + vconfig.c has been converted to C code now, instead of C++. + Thanks to MATHIEU. + + Significantly cleaned up the code w/out decreasing any useful + functionality, I believe. + + Removed the dhcp stuff from the VLAN distribution. + + +Date: August 27, 2000 +Version: 0.0.12 +Kernel Version: 2.2.16, 2.4.pre7 +Status: This one turned out pretty stable, no known bugs. +Changes: + + Added ability to re-order the VLAN packet so that it looks + like a real ethernet packet for the ingress pathway. This + should help DHCP and other programs that insist on reading + the raw buffer and then make assumptions about byte offsets. + I don't have a good way to test this fully, so consider it + experimental :) This behavior can be changed at run-time, + and is set on a per-VLAN basis. The default is NOT to reorder + the header, which has been the only behavior up untill this + point. The vconfig program can set/clear the flag, by using + a VLAN IOCTL. You can read the flag's value from the + /proc/net/vlan/vlan* files. + + You can also set a default priority on a NON-VLAN device. + This priority will only be used when the default_VID for the + device is set as well. This priority won't be mapped anywhere, + just copied straight into the skb->priority. It is a uint16. + + The 2.3 patch is now the 2.4 patch, and it has been tested + against 2.4.pre7. + + +Date: April 23, 2000 +Version: 0.0.11 +Kernel Version: 2.2.13 & 2.2.14, 2.3.99 +Status: As of August 27, this seems like a very stable patch. +Changes: + Added real support for PRIORITY. Through IOCTL calls (see the + vconfig program), you can set explicit ingress and egress mappings + to/from the VLAN QOS bits and the sk_buff->priority field. This + is not tested very well, as I don't know much about how people really + use the priority field... Took out the round-robin aggretation that + went in in rls 0.10, as it was mainly just a hack, and doing link + aggregation at a lower level and then putting VLAN on top of that + virtual device probably makes more sense. The vconfig program + changed to support the new features..here's it's new usage:
+ + 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/ file. + + +Date: October 20, 1999 +Version: 0.0.6 +Kernel Version: 2.2.10+ +Status: ping -f still kills one of my machines, but it takes longer...and I'm + not sure if its the fault of the VLAN code, or maybe some hardware problem. +Changes: Coded around an extraneous skb alloc/free so that there should be no + extra buffer copying as compared to an ethernet interface, unless the + vlan device spans more than one interface. Put #ifdef around all printk + debugging calls, at least for non-control code (ie no more printk in the + critical paths.) + + +Date: October 19, 1999 +Version: 0.0.6 +Kernel Version: 2.2.10 +Status: Ping & FTP work, ping -f kills it after some time...not sure why yet. +Changes: Got tcpdump working with VLAN pkts (use the -e option). Got basic VLAN + functionality working, though problems remain, including a KERNEL CRASH + that can be induced by ping -f on one of the vlan interfaces. My test + setup consists of two linux boxes, each running my modified kernel. + Since I have no third-party implementation to test against, it is likely + the code is not too right yet!! + Performance isn't all that great: Running a Cyrix 155 <-> a Cyrix 233, + connected through a 10bt hub, I get 910 Mbps on regular ethernet, + and only 650 Mbps on the VLAN device. This was using a 30 MB file. + + +Date: Long time ago. +Version: 0.0.3 +Kernel Version: 2.2.2 +Status: Definately broken, but lots of code in there!! +Changes: Initial partially functional release (not very functional.) diff --git a/CVS/Entries b/CVS/Entries new file mode 100644 index 0000000..d86968c --- /dev/null +++ b/CVS/Entries @@ -0,0 +1,17 @@ +/vlan_2.2.patch/1.7/Mon Jan 15 03:30:45 2001// +/vconfig.8/1.1/Sat Oct 20 22:57:02 2001// +/vconfig.spec/1.1/Thu Apr 18 00:11:42 2002// +/vlan_test.pl/1.5/Wed Aug 28 08:11:49 2002// +/vlan_test2.pl/1.1/Wed Nov 6 08:34:37 2002// +D/libpcap-0.4//// +D/tcpdump-3.4//// +/howto.html/1.2/Fri Jul 4 16:53:40 2003// +/vlan.html/1.26/Tue Apr 8 15:44:00 2003// +/vconfig.c/1.7/Thu Jul 31 22:43:08 2003// +/macvlan_config.c/1.4/Tue Aug 12 19:36:36 2003// +/CHANGELOG/1.19/Tue Sep 30 21:01:55 2003// +/MakeInclude/1.8/Mon Aug 25 17:08:18 2003// +/Makefile/1.7/Tue Sep 30 21:04:14 2003// +/README/1.7/Tue Sep 30 21:03:42 2003// +/candela_2.4.21.patch/1.4/Tue Sep 30 21:05:04 2003// +D/contrib//// diff --git a/CVS/Repository b/CVS/Repository new file mode 100644 index 0000000..d56118f --- /dev/null +++ b/CVS/Repository @@ -0,0 +1 @@ +vlan diff --git a/CVS/Root b/CVS/Root new file mode 100644 index 0000000..469c859 --- /dev/null +++ b/CVS/Root @@ -0,0 +1 @@ +:pserver:greear@ns1.wanfear.com:/home/cvs/vlan diff --git a/MakeInclude b/MakeInclude new file mode 100644 index 0000000..f518572 --- /dev/null +++ b/MakeInclude @@ -0,0 +1,35 @@ +# -*-Makefile-*- + + +# This is the version of the make utility you wish to use. +# On some systems (BSD?) you might want this to be gmake. +#MAKE=gmake +MAKE=make + + +VERSION=1.0.2 + +CUR_DATE=`date '+%y.%m.%d'` + +ifeq "${PLATFORM}" "" + PLATFORM=x86 +endif + +## You may need to change this linux/include part. +CCFLAGS = -g -D_GNU_SOURCE -Wall -I${HOME}/linux/include +LDLIBS = # -lm #-lnsl # -lsocket + +ARM_TC_BIN = ${HOME}/Intrinsyc/bin +ARM_TC_LIB = ${HOME}/Intrinsyc/lib + +ifeq "${PLATFORM}" "ARM" + #echo "Building for ARM platform." + STRIP=${ARM_TC_BIN}/arm-linux-strip + CC = ${ARM_TC_BIN}/arm-linux-gcc # this is generally the c compiler, unused AFAIK + CCC = ${ARM_TC_BIN}/arm-linux-g++ # this is generally the c++ compiler +else + #echo "Building for x86 platform." + STRIP=strip + CC = gcc # this is generally the c compiler, unused AFAIK + CCC = g++ # this is generally the c++ compiler +endif diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..23bb374 --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +# makefile template + +include MakeInclude + +LDLIBS = + +VLAN_OBJS = vconfig.o + +ALL_OBJS = ${VLAN_OBJS} + +VCONFIG = vconfig #program to be created + + +all: ${VCONFIG} macvlan_config + + +#This is pretty silly.. +vconfig.h: Makefile + touch vconfig.h + + +$(VCONFIG): $(VLAN_OBJS) + $(CC) $(CCFLAGS) $(LDFLAGS) -o $(VCONFIG) $(VLAN_OBJS) $(LDLIBS) + $(STRIP) $(VCONFIG) + +macvlan_config: macvlan_config.c + $(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $< + +$(ALL_OBJS): %.o: %.c %.h + @echo " " + @echo "Making $<" + $(CC) $(CCFLAGS) -c $< + +clean: + rm -f *.o + +purge: clean + rm -f *.flc ${VCONFIG} macvlan_config vconfig.h + rm -f *~ + + + + + diff --git a/README b/README new file mode 100644 index 0000000..0f9ce3a --- /dev/null +++ b/README @@ -0,0 +1,12 @@ +The MAC vlan stuff is primarily the work of, and is copy-righted (GPL) +by: + +# (C) Copyright 2001 +# Alex Zeffertt, Cambridge Broadband Ltd, ajz@cambridgebroadband.com + +However, I (Ben) reworked the MAC-VLAN code extensively, including re-writing +all of the locking code. So, any complaints & bugs should come to me. + + +--Ben Greear (greearb@candelatech.com) +http://www.candelatech.com/~greear diff --git a/candela_2.4.21.patch b/candela_2.4.21.patch new file mode 100644 index 0000000..8b2f320 --- /dev/null +++ b/candela_2.4.21.patch @@ -0,0 +1,6911 @@ +--- linux-2.4.21/include/linux/if.h 2003-06-13 07:51:38.000000000 -0700 ++++ linux-2.4.21.amds/include/linux/if.h 2003-07-30 16:27:15.000000000 -0700 +@@ -48,6 +48,12 @@ + + /* Private (from user) interface flags (netdevice->priv_flags). */ + #define IFF_802_1Q_VLAN 0x1 /* 802.1Q VLAN device. */ ++#define IFF_PKTGEN_RCV 0x2 /* Registered to receive & consume Pktgen skbs */ ++#define IFF_ACCEPT_LOCAL_ADDRS 0x4 /** Accept pkts even if they come from a local ++ * address. This lets use send pkts to ourselves ++ * over external interfaces (when used in conjunction ++ * with SO_BINDTODEVICE ++ */ + + + #define IF_GET_IFACE 0x0001 /* for querying only */ +--- linux-2.4.21/include/linux/netdevice.h 2003-06-13 07:51:38.000000000 -0700 ++++ linux-2.4.21.amds/include/linux/netdevice.h 2003-07-30 16:27:20.000000000 -0700 +@@ -296,7 +296,9 @@ + + unsigned short flags; /* interface flags (a la BSD) */ + unsigned short gflags; +- unsigned short priv_flags; /* Like 'flags' but invisible to userspace. */ ++ unsigned short priv_flags; /* Like 'flags' but invisible to userspace, ++ * see: if.h for flag definitions. ++ */ + unsigned short unused_alignment_fixer; /* Because we need priv_flags, + * and we want to be 32-bit aligned. + */ +@@ -422,12 +424,20 @@ + int (*neigh_setup)(struct net_device *dev, struct neigh_parms *); + int (*accept_fastpath)(struct net_device *, struct dst_entry*); + ++#ifdef CONFIG_NET_SKB_RECYCLING ++ int (*skb_recycle) (struct sk_buff *skb); ++ void (*mem_reclaim) (struct net_device *dev); ++#endif + /* open/release and usage marking */ + struct module *owner; + + /* bridge stuff */ + struct net_bridge_port *br_port; + ++#if defined(CONFIG_MACVLAN) || defined(CONFIG_MACVLAN_MODULE) ++ struct macvlan_port *macvlan_priv; ++#endif ++ + #ifdef CONFIG_NET_FASTROUTE + #define NETDEV_FASTROUTE_HMASK 0xF + /* Semi-private data. Keep it at the end of device struct. */ +@@ -438,6 +448,7 @@ + /* this will get initialized at each interface type init routine */ + struct divert_blk *divert; + #endif /* CONFIG_NET_DIVERT */ ++ + }; + + +--- linux-2.4.21/net/core/dev.c 2003-06-13 07:51:39.000000000 -0700 ++++ linux-2.4.21.amds/net/core/dev.c 2003-07-30 16:20:41.000000000 -0700 +@@ -1,4 +1,4 @@ +-/* ++/* -*-linux-c-*- + * NET3 Protocol independent device support routines. + * + * This program is free software; you can redistribute it and/or +@@ -82,6 +82,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -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 + * Uppsala University, Sweden ++ * 2002 Ben Greear + * + * 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 + * + * 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 + #include + #include ++#include + #include + +-#define cycles() ((u32)get_cycles()) ++#include /* for lock kernel */ ++#include /* 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 ++ * 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; ilatency_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; ilatency_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; ilatency_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; iread_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; iread_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"); + 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 + #include + #include ++#include + #include + #include + #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 ++#include ++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 ++# ++# 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_PROC_FS ++#include ++#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= '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; idev->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; iport->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; ihash_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; iport->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; ihash_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 ++ ++*/ ++ ++#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 + #include + #include ++#include + + #ifdef CONFIG_NET_DIVERT + #include +@@ -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 +- * 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 +- * - 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 ++ * - 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 +- * 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 ; ++ * Fix for packet capture - Nick Eggleston ; + * Add HW acceleration hooks - David S. Miller ; + * Correct all the locking - David S. Miller ; + * Use hash table for VLAN groups - David S. Miller +@@ -173,7 +173,7 @@ + *pprev = grp->next; + } + +-/* Find the protocol handler. Assumes VID < VLAN_VID_MASK. ++/* Find the protocol handler. Assumes VID < VLAN_VID_MASK. + * + * Must be invoked with vlan_group_lock held. + */ +@@ -183,7 +183,7 @@ + struct vlan_group *grp = __vlan_find_group(real_dev->ifindex); + + if (grp) +- return grp->vlan_devices[VID]; ++ return grp->vlan_devices[VID]; + + return NULL; + } +@@ -270,7 +270,7 @@ + } + } + +- return ret; ++ return ret; + } + + static int unregister_vlan_device(const char *vlan_IF_name) +@@ -655,17 +655,14 @@ + int vlan_ioctl_handler(unsigned long arg) + { + int err = 0; ++ unsigned short vid = 0; + struct vlan_ioctl_args args; + +- /* everything here needs root permissions, except aguably the +- * hack ioctls for sending packets. However, I know _I_ don't +- * want users running that on my network! --BLG +- */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (copy_from_user(&args, (void*)arg, +- sizeof(struct vlan_ioctl_args))) ++ sizeof(struct vlan_ioctl_args))) + return -EFAULT; + + /* Null terminate this sucker, just in case. */ +@@ -678,24 +675,32 @@ + + switch (args.cmd) { + case SET_VLAN_INGRESS_PRIORITY_CMD: ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; + err = vlan_dev_set_ingress_priority(args.device1, + args.u.skb_priority, + args.vlan_qos); + break; + + case SET_VLAN_EGRESS_PRIORITY_CMD: ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; + err = vlan_dev_set_egress_priority(args.device1, + args.u.skb_priority, + args.vlan_qos); + break; + + case SET_VLAN_FLAG_CMD: ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; + err = vlan_dev_set_vlan_flag(args.device1, + args.u.flag, + args.vlan_qos); + break; + + case SET_VLAN_NAME_TYPE_CMD: ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; + if ((args.u.name_type >= 0) && + (args.u.name_type < VLAN_NAME_TYPE_HIGHEST)) { + vlan_name_type = args.u.name_type; +@@ -705,17 +710,9 @@ + } + break; + +- /* TODO: Figure out how to pass info back... +- case GET_VLAN_INGRESS_PRIORITY_IOCTL: +- err = vlan_dev_get_ingress_priority(args); +- break; +- +- case GET_VLAN_EGRESS_PRIORITY_IOCTL: +- err = vlan_dev_get_egress_priority(args); +- break; +- */ +- + case ADD_VLAN_CMD: ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; + /* we have been given the name of the Ethernet Device we want to + * talk to: args.dev1 We also have the + * VLAN ID: args.u.VID +@@ -728,12 +725,53 @@ + break; + + case DEL_VLAN_CMD: ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; + /* Here, the args.dev1 is the actual VLAN we want + * to get rid of. + */ + err = unregister_vlan_device(args.device1); + break; + ++ case GET_VLAN_INGRESS_PRIORITY_CMD: ++ /* TODO: Implement ++ err = vlan_dev_get_ingress_priority(args); ++ if (copy_to_user((void*)arg, &args, ++ sizeof(struct vlan_ioctl_args))) { ++ err = -EFAULT; ++ } ++ */ ++ err = -EINVAL; ++ break; ++ ++ case GET_VLAN_EGRESS_PRIORITY_CMD: ++ /* TODO: Implement ++ err = vlan_dev_get_egress_priority(args.device1, &(args.args); ++ if (copy_to_user((void*)arg, &args, ++ sizeof(struct vlan_ioctl_args))) { ++ err = -EFAULT; ++ } ++ */ ++ err = -EINVAL; ++ break; ++ ++ case GET_VLAN_REALDEV_NAME_CMD: ++ err = vlan_dev_get_realdev_name(args.device1, args.u.device2); ++ if (copy_to_user((void*)arg, &args, ++ sizeof(struct vlan_ioctl_args))) { ++ err = -EFAULT; ++ } ++ break; ++ ++ case GET_VLAN_VID_CMD: ++ err = vlan_dev_get_vid(args.device1, &vid); ++ args.u.VID = vid; ++ if (copy_to_user((void*)arg, &args, ++ sizeof(struct vlan_ioctl_args))) { ++ err = -EFAULT; ++ } ++ break; ++ + default: + /* pass on to underlying device instead?? */ + printk(VLAN_DBG "%s: Unknown VLAN CMD: %x \n", +--- linux-2.4.21/net/8021q/vlan.h 2002-08-02 17:39:46.000000000 -0700 ++++ linux-2.4.21.amds/net/8021q/vlan.h 2003-08-13 16:29:30.000000000 -0700 +@@ -72,6 +72,8 @@ + int vlan_dev_set_ingress_priority(char* dev_name, __u32 skb_prio, short vlan_prio); + int vlan_dev_set_egress_priority(char* dev_name, __u32 skb_prio, short vlan_prio); + int vlan_dev_set_vlan_flag(char* dev_name, __u32 flag, short flag_val); ++int vlan_dev_get_realdev_name(const char* dev_name, char* result); ++int vlan_dev_get_vid(const char* dev_name, unsigned short* result); + void vlan_dev_set_multicast_list(struct net_device *vlan_dev); + + #endif /* !(__BEN_VLAN_802_1Q_INC__) */ +--- linux-2.4.21/include/linux/if_vlan.h 2002-11-28 15:53:15.000000000 -0800 ++++ linux-2.4.21.amds/include/linux/if_vlan.h 2003-08-13 16:27:39.000000000 -0700 +@@ -212,7 +212,9 @@ + GET_VLAN_INGRESS_PRIORITY_CMD, + GET_VLAN_EGRESS_PRIORITY_CMD, + SET_VLAN_NAME_TYPE_CMD, +- SET_VLAN_FLAG_CMD ++ SET_VLAN_FLAG_CMD, ++ GET_VLAN_REALDEV_NAME_CMD, /* If this works, you know it's a VLAN device, btw */ ++ GET_VLAN_VID_CMD /* Get the VID of this VLAN (specified by name) */ + }; + + enum vlan_name_types { +--- linux-2.4.21/include/linux/ethtool.h 2003-06-13 07:51:38.000000000 -0700 ++++ linux-2.4.21.amds/include/linux/ethtool.h 2003-07-30 16:20:41.000000000 -0700 +@@ -250,6 +250,12 @@ + u64 data[0]; + }; + ++/* for dumping net-device statistics */ ++struct ethtool_ndstats { ++ u32 cmd; /* ETHTOOL_GNDSTATS */ ++ u8 data[0]; /* sizeof(struct net_device_stats) */ ++}; ++ + /* CMDs currently supported */ + #define ETHTOOL_GSET 0x00000001 /* Get settings. */ + #define ETHTOOL_SSET 0x00000002 /* Set settings, privileged. */ +@@ -281,6 +287,7 @@ + #define ETHTOOL_GSTRINGS 0x0000001b /* get specified string set */ + #define ETHTOOL_PHYS_ID 0x0000001c /* identify the NIC */ + #define ETHTOOL_GSTATS 0x0000001d /* get NIC-specific statistics */ ++#define ETHTOOL_GNDSTATS 0x0000001e /* get standard net-device statistics */ + + /* compatibility with older code */ + #define SPARC_ETH_GSET ETHTOOL_GSET +--- linux-2.4.21/Documentation/CodingStyle 2001-09-09 16:40:43.000000000 -0700 ++++ linux-2.4.21.amds/Documentation/CodingStyle 2003-08-05 20:51:17.000000000 -0700 +@@ -184,6 +184,8 @@ + (interactive) + (c-mode) + (c-set-style "K&R") ++ (setq tab-width 8) ++ (setq indent-tabs-mode t) + (setq c-basic-offset 8)) + + This will define the M-x linux-c-mode command. When hacking on a +--- linux-2.4.21/include/linux/proc_fs.h 2002-08-02 17:39:45.000000000 -0700 ++++ linux-2.4.21.amds/include/linux/proc_fs.h 2003-08-13 16:47:29.000000000 -0700 +@@ -25,7 +25,8 @@ + /* Finally, the dynamically allocatable proc entries are reserved: */ + + #define PROC_DYNAMIC_FIRST 4096 +-#define PROC_NDYNAMIC 4096 ++#define PROC_NDYNAMIC 8192 /* was 4096 previously, but was running out of ++ * slots when creating lots of VLANs --Ben */ + + #define PROC_SUPER_MAGIC 0x9fa0 + diff --git a/contrib/CVS/Entries b/contrib/CVS/Entries new file mode 100644 index 0000000..2e0a86d --- /dev/null +++ b/contrib/CVS/Entries @@ -0,0 +1,5 @@ +/vlan_2.2-full.patch/1.1/Mon Jun 18 22:31:13 2001// +/vlan_2.2-module.patch/1.1/Mon Jun 18 22:31:13 2001// +/README/1.2/Tue Sep 30 23:21:29 2003// +/network/1.1/Tue Sep 30 23:19:45 2003// +D diff --git a/contrib/CVS/Repository b/contrib/CVS/Repository new file mode 100644 index 0000000..54d399b --- /dev/null +++ b/contrib/CVS/Repository @@ -0,0 +1 @@ +vlan/contrib diff --git a/contrib/CVS/Root b/contrib/CVS/Root new file mode 100644 index 0000000..469c859 --- /dev/null +++ b/contrib/CVS/Root @@ -0,0 +1 @@ +:pserver:greear@ns1.wanfear.com:/home/cvs/vlan diff --git a/contrib/README b/contrib/README new file mode 100644 index 0000000..5af786d --- /dev/null +++ b/contrib/README @@ -0,0 +1,20 @@ +Here lies contributions from the community at large that for one +reason or another (laziness on my part, and lack of testing, are +valid reasons!), are not in the main distribution. + +If any of these seem particularly stable or useful, let me +know and I will consider adding them to the main patch. + +Descriptions: +network + "Thanks for the great vconfig patch/tool. I submit to you a minor + change to the RedHat 7.3 /etc/rc.d/init.d/network script that + recognizes VLAN interfaces /etc/sysconfig/network-scripts/ifcfg-ethx.y , + and makes appropriate calls to vconfig prior to bringing the + interface up. This makes startup more straightforward, + and may be of use to your website visitors. + -Derek" + + + + Thanks, Ben Greear (greearb@candelatech.com) diff --git a/contrib/network b/contrib/network new file mode 100644 index 0000000..48e4240 --- /dev/null +++ b/contrib/network @@ -0,0 +1,288 @@ +#! /bin/bash +# +# network Bring up/down networking +# +# chkconfig: 2345 10 90 +# description: Activates/Deactivates all network interfaces configured to \ +# start at boot time. +# probe: true +### BEGIN INIT INFO +# Provides: $network +### END INIT INFO + +# Source function library. + +. /etc/init.d/functions + +if [ ! -f /etc/sysconfig/network ]; then + exit 0 +fi + +. /etc/sysconfig/network + +if [ -f /etc/sysconfig/pcmcia ]; then + . /etc/sysconfig/pcmcia +fi + + +# Check that networking is up. +[ "${NETWORKING}" = "no" ] && exit 0 + +# if the ip configuration utility isn't around we can't function. +[ -x /sbin/ip ] || exit 1 + +# Even if IPX is configured, without the utilities we can't do much +[ ! -x /sbin/ipx_internal_net -o ! -x /sbin/ipx_configure ] && IPX= + +# If IPv6 is explicitly configured, make sure it's available. +if [ "$NETWORKING_IPV6" = "yes" ]; then + alias=`modprobe -c | awk '/^alias net-pf-10 / { print $3 }'` + if [ "$alias" != "ipv6" -a ! -f /proc/net/if_inet6 ]; then + echo "alias net-pf-10 ipv6" >> /etc/modules.conf + fi +fi + +CWD=`pwd` +cd /etc/sysconfig/network-scripts + +. network-functions + +# find all the interfaces besides loopback. +# ignore aliases, alternative configurations, and editor backup files +interfaces=`ls ifcfg* | LANG=C egrep -v '(ifcfg-lo|:|\.|rpmsave|rpmorig|rpmnew)' | \ + LANG=C egrep -v '(~|\.bak)$' | \ + LANG=C egrep 'ifcfg-[A-Za-z0-9_-]+$' | \ + sed 's/^ifcfg-//g'` + +vlan_interfaces=`ls ifcfg-eth?.?* | \ + LANG=C egrep -v '(~|\.bak)$' | \ + LANG=C egrep 'ifcfg-[A-Za-z0-9_-\.]+$' | \ + sed 's/^ifcfg-//g'` + +# See how we were called. +case "$1" in + start) + # IPv6 hook (pre IPv4 start) + if [ "$NETWORKING_IPV6" = "yes" ]; then + if [ -x /etc/sysconfig/network-scripts/init.ipv6-global ]; then + /etc/sysconfig/network-scripts/init.ipv6-global start pre + fi + fi + + action $"Setting network parameters: " sysctl -e -p /etc/sysctl.conf + + # bring up loopback interface + action $"Bringing up loopback interface: " ./ifup ifcfg-lo + + case "$IPX" in + yes|true) + /sbin/ipx_configure --auto_primary=$IPXAUTOPRIMARY \ + --auto_interface=$IPXAUTOFRAME + if [ "$IPXINTERNALNETNUM" != "0" ]; then + /sbin/ipx_internal_net add $IPXINTERNALNETNUM $IPXINTERNALNODENUM + fi + ;; + esac + + oldhotplug=`sysctl kernel.hotplug 2>/dev/null | \ + awk '{ print $3 }' 2>/dev/null` + sysctl -w kernel.hotplug="/bin/true" > /dev/null 2>&1 + + cipeinterfaces="" + + # bring up all other interfaces configured to come up at boot time + for i in $interfaces; do + eval $(fgrep "DEVICE=" ifcfg-$i) + if [ -z "$DEVICE" ] ; then DEVICE="$i"; fi + + if [ "${DEVICE##cipcb}" != "$DEVICE" ] ; then + cipeinterfaces="$cipeinterfaces $DEVICE" + continue + fi + if LANG=C egrep -L "^ONBOOT=\"?[Nn][Oo]\"?" ifcfg-$i > /dev/null ; then + # this loads the module, to preserve ordering + is_available $i + continue + fi + # If we're in confirmation mode, get user confirmation + [ -n "$CONFIRM" ] && + { + confirm $i + case $? in + 0) + : + ;; + 2) + CONFIRM= + ;; + *) + continue + ;; + esac + } + + action $"Bringing up interface $i: " ./ifup $i boot + done + + # bring up vlan interfaces configured to come up at boot time + for i in $vlan_interfaces; do + eval $(fgrep "DEVICE=" ifcfg-$i) + if [ -z "$DEVICE" ] ; then DEVICE="$i"; fi + + if LANG=C egrep -L "^ONBOOT=\"?[Nn][Oo]\"?" ifcfg-$i > /dev/null ; then + # this loads the module, to preserve ordering + is_available $i + continue + fi + # If we're in confirmation mode, get user confirmation + [ -n "$CONFIRM" ] && + { + confirm $i + case $? in + 0) + : + ;; + 2) + CONFIRM= + ;; + *) + continue + ;; + esac + } + /usr/local/bin/vconfig add `echo $i | tr '.' ' '` + action $"Bringing up interface $i: " ./ifup $i boot + done + + # Bring up CIPE VPN interfaces + for i in $cipeinterfaces ; do + if ! LANG=C egrep -L "^ONBOOT=\"?[Nn][Oo]\"?" ifcfg-$i >/dev/null 2>&1 ; then + # If we're in confirmation mode, get user confirmation + [ -n "$CONFIRM" ] && + { + confirm $i + case $? in + 0) + : + ;; + 2) + CONFIRM= + ;; + *) + continue + ;; + esac + } + action $"Bringing up interface $i: " ./ifup $i boot + fi + done + + sysctl -w kernel.hotplug=$oldhotplug > /dev/null 2>&1 + + # Add non interface-specific static-routes. + if [ -f /etc/sysconfig/static-routes ]; then + grep "^any" /etc/sysconfig/static-routes | while read ignore args ; do + /sbin/route add -$args + done + fi + + # IPv6 hook (post IPv4 start) + if [ "$NETWORKING_IPV6" = "yes" ]; then + if [ -x /etc/sysconfig/network-scripts/init.ipv6-global ]; then + /etc/sysconfig/network-scripts/init.ipv6-global start post + fi + fi + # Run this again to catch any interface-specific actions + sysctl -e -p /etc/sysctl.conf >/dev/null 2>&1 + + touch /var/lock/subsys/network + ;; + stop) + # If this is a final shutdown/halt, check for network FS, + # and unmount them even if the user didn't turn on netfs + + if [ "$RUNLEVEL" = "6" -o "$RUNLEVEL" = "0" -o "$RUNLEVEL" = "1" ]; then + NFSMTAB=`grep -v '^#' /proc/mounts | awk '{ if ($3 ~ /^nfs$/ ) print $2}'` + SMBMTAB=`grep -v '^#' /proc/mounts | awk '{ if ($3 ~ /^smbfs$/ ) print $2}'` + NCPMTAB=`grep -v '^#' /proc/mounts | awk '{ if ($3 ~ /^ncpfs$/ ) print $2}'` + if [ -n "$NFSMTAB" -o -n "$SMBMTAB" -o -n "$NCPMTAB" ] ; then + /etc/init.d/netfs stop + fi + fi + + # IPv6 hook (pre IPv4 stop) + if [ "$NETWORKING_IPV6" = "yes" ]; then + if [ -x /etc/sysconfig/network-scripts/init.ipv6-global ]; then + /etc/sysconfig/network-scripts/init.ipv6-global stop pre + fi + fi + + # shut down all interfaces (other than loopback) + for i in $interfaces ; do + eval $(fgrep "DEVICE=" ifcfg-$i) + if [ -z "$DEVICE" ] ; then DEVICE="$i"; fi + + if ! check_device_down $i; then + action $"Shutting down interface $i: " ./ifdown $i boot + fi + done + # VLAN + for i in $vlan_interfaces ; do + eval $(fgrep "DEVICE=" ifcfg-$i) + if [ -z "$DEVICE" ] ; then DEVICE="$i"; fi + + if [ -f /proc/net/vlan/$i ]; then + action $"Removing vlan interface interface $i: " /usr/local/bin/vconfig rem $i + fi + done + + case "$IPX" in + yes|true) + if [ "$IPXINTERNALNETNUM" != "0" ]; then + /sbin/ipx_internal_net del + fi + ;; + esac + + action $"Shutting down loopback interface: " ./ifdown ifcfg-lo + + if [ -d /proc/sys/net/ipv4 ]; then + if [ -f /proc/sys/net/ipv4/ip_forward ]; then + if [ `cat /proc/sys/net/ipv4/ip_forward` != 0 ]; then + action $"Disabling IPv4 packet forwarding: " sysctl -w net.ipv4.ip_forward=0 + fi + fi + if [ -f /proc/sys/net/ipv4/ip_always_defrag ]; then + if [ `cat /proc/sys/net/ipv4/ip_always_defrag` != 0 ]; then + action $"Disabling IPv4 automatic defragmentation: " sysctl -w net.ipv4.ip_always_defrag=0 + fi + fi + fi + + # IPv6 hook (post IPv4 stop) + if [ "$NETWORKING_IPV6" = "yes" ]; then + if [ -x /etc/sysconfig/network-scripts/init.ipv6-global ]; then + /etc/sysconfig/network-scripts/init.ipv6-global stop post + fi + fi + + rm -f /var/lock/subsys/network + ;; + status) + echo $"Configured devices:" + echo lo $interfaces + + echo $"Currently active devices:" + echo `/sbin/ip -o link show | awk -F ": " '{ print $2 }'` + ;; + restart|reload) + cd $CWD + $0 stop + $0 start + ;; + *) + echo $"Usage: $0 {start|stop|restart|reload|status}" + exit 1 +esac + +exit 0 diff --git a/contrib/vlan_2.2-full.patch b/contrib/vlan_2.2-full.patch new file mode 100644 index 0000000..4ae3012 --- /dev/null +++ b/contrib/vlan_2.2-full.patch @@ -0,0 +1,2906 @@ +diff -Nurb linux/include/linux/if_ether.h linux.p/include/linux/if_ether.h +--- linux/include/linux/if_ether.h Sun Mar 25 18:31:03 2001 ++++ linux.p/include/linux/if_ether.h Mon Jun 4 16:10:17 2001 +@@ -32,6 +32,33 @@ + #define ETH_DATA_LEN 1500 /* Max. octets in payload */ + #define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ + ++ ++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE)) ++ ++#define VLAN_ETH_ALEN 6 /* Octets in one ethernet addr */ ++#define VLAN_ETH_HLEN 18 /* Total octets in header. */ ++#define VLAN_ETH_ZLEN 64 /* Min. octets in frame sans FCS */ ++ ++/* These could be bumped up by 4, but I'm not sure if all the underlying ++ * drivers would like it. ++ * UPDATE: Bumping it by 4, as per Klika's suggestion below. --BLG ++ * ++ * According to 802.3ac, the packet can be 4 bytes longer. --Klika Jan ++ */ ++#define VLAN_ETH_DATA_LEN 1500 /* Max. octets in payload */ ++#define VLAN_ETH_FRAME_LEN 1518 /* Max. octets in frame sans FCS */ ++ ++struct vlan_ethhdr ++{ ++ unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ ++ unsigned char h_source[ETH_ALEN]; /* source ether addr */ ++ unsigned short h_vlan_proto; /* Should always be 0x8100 */ ++ unsigned short h_vlan_TCI; /* Encapsulates priority and VLAN ID */ ++ unsigned short h_vlan_encapsulated_proto; /* packet type ID field (or len) */ ++}; ++ ++#endif /* CONFIG_VLAN_802_1Q ... */ ++ + /* + * These are the defined Ethernet Protocol ID's. + */ +@@ -54,6 +81,7 @@ + #define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */ + #define ETH_P_ATALK 0x809B /* Appletalk DDP */ + #define ETH_P_AARP 0x80F3 /* Appletalk AARP */ ++#define ETH_P_802_1Q 0x8100 /* 802.1Q VLAN Extended Header */ + #define ETH_P_IPX 0x8137 /* IPX over DIX */ + #define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */ + #define ETH_P_ATMMPOA 0x884c /* MultiProtocol Over ATM */ +diff -Nurb linux/include/linux/if_vlan.h linux.p/include/linux/if_vlan.h +--- linux/include/linux/if_vlan.h Thu Jan 1 01:00:00 1970 ++++ linux.p/include/linux/if_vlan.h Mon Jun 4 16:18:26 2001 +@@ -0,0 +1,238 @@ ++/* -*- linux-c -*- ++ * VLAN An implementation of 802.1Q VLAN tagging. ++ * ++ * Version: 0.0.1 03/06/99 ++ * ++ * Authors: Ben Greear ++ * ++ * 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 /* 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 ++#include ++ ++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 , ++ * ++ * 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 /* for copy_from_user */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#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 ++ ++/* 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 , ++ * ++ * 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 /* for copy_from_user */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "vlan.h" ++#include "vlanproc.h" ++#include ++#include ++#include ++ ++ ++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, coppied from wanproc.c ++ * by: Gene Kozin ++ * ++ * 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 ++#include /* offsetof(), etc. */ ++#include /* return codes */ ++#include ++#include /* kmalloc(), kfree() */ ++#include /* verify_area(), etc. */ ++#include /* inline mem*, str* functions */ ++#include /* __initfunc et al. */ ++#include /* kernel <-> user copy */ ++#include /* htons(), etc. */ ++#include /* copy_to_user */ ++#include ++#include ++#include ++#include ++#include ++#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 ++ * entry for each device ++ */ ++ ++/* ++ * Generic /proc/net/vlan/ 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/ 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 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 + #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; iname, 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 + 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 +@@ -169,5 +173,9 @@ + { "IrDA", irda_proto_init }, /* IrDA protocols */ + #endif + ++#ifdef CONFIG_VLAN_802_1Q ++ { "VLAN", vlan_proto_init }, /* 802.1Q VLAN Support. --BLG */ ++#endif ++ + { NULL, NULL } /* End marker */ + }; diff --git a/contrib/vlan_2.2-module.patch b/contrib/vlan_2.2-module.patch new file mode 100644 index 0000000..f4c4bd0 --- /dev/null +++ b/contrib/vlan_2.2-module.patch @@ -0,0 +1,495 @@ +diff -Nurb linux/include/linux/if_ether.h linux.p/include/linux/if_ether.h +--- linux/include/linux/if_ether.h Mon Jun 4 17:51:51 2001 ++++ linux.p/include/linux/if_ether.h Mon Jun 4 16:10:17 2001 +@@ -33,8 +33,7 @@ + #define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ + + +-#ifdef CONFIG_VLAN_802_1Q +- ++#if (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE)) + + #define VLAN_ETH_ALEN 6 /* Octets in one ethernet addr */ + #define VLAN_ETH_HLEN 18 /* Total octets in header. */ +@@ -58,9 +57,7 @@ + unsigned short h_vlan_encapsulated_proto; /* packet type ID field (or len) */ + }; + +- +-#endif +- ++#endif /* CONFIG_VLAN_802_1Q ... */ + + /* + * These are the defined Ethernet Protocol ID's. +diff -Nurb linux/include/linux/netdevice.h linux.p/include/linux/netdevice.h +--- linux/include/linux/netdevice.h Mon Jun 4 17:51:51 2001 ++++ linux.p/include/linux/netdevice.h Mon Jun 4 16:10:48 2001 +@@ -37,14 +37,11 @@ + #ifdef CONFIG_NET_PROFILE + #include + #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 + #include +@@ -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 /* for copy_from_user */ +-#include + #include + #include + #include +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, coppied from wanproc.c +-* by: Gene Kozin +-* +-* 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, coppied from wanproc.c ++ * by: Gene Kozin ++ * ++ * 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 + #include /* 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 + #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 + EXPORT_SYMBOL(ltalk_setup); + #endif +- + + /* Packet scheduler modules want these. */ + EXPORT_SYMBOL(qdisc_destroy); diff --git a/howto.html b/howto.html new file mode 100644 index 0000000..a23c180 --- /dev/null +++ b/howto.html @@ -0,0 +1,1219 @@ + + + + LINUX VLAN + Cisco HOWTO + + + +

LINUX VLAN + Cisco HOWTO

+

+

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
+
+
+  The Linux VLAN HOWTO
+ 
+  VLAN Mailing list: vlan@candelatech.com
+  Kristjan Kotkas kristjan@data.ee
+  Ben Greear greearb@candelatech.com
+
+
+

+ +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 + +

+

Contents

+
    +
  1. Who, why and where
  2. +
  3. Actual info on how to make it work.
  4. +
  5. Specific work-arounds/patches for certain configurations. + +
  6. Scripts and Recipes.
  7. +
+

+ +

    +
  1. META +
      +
    1. + This is the first HOWTO for the "802.1Q VLAN implementation for Linux"

      + + Homepage: http://scry.wanfear.com/~greear/vlan.html + Mailing List: VLAN@Scry.WANfear.com +

    2. +

      +

    3. Copyright

      + + 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. +

    4. +

      +

    5. Disclaimer

      + + 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 +

    6. +

      +

    7. Credits

      + + Thanks to Ben Greear http://www.candelatech.com/~greear + for the VLAN project and also to all the people who have contributed to the project. +

    8. +

      +

    9. General

      + + What is VLAN is not described in this document. Info on the VLAN protocol + can be found at + http://standards.ieee.org/getieee802/download/802.1Q-1998.pdf. +

    10. +
    +
  2. +

    +

  3. Software/Hardware (Cisco-specific setup, with some general info as well.)

    +

      +
    1. VLAN installation & Configuration on the Linux Side.

      +

      +
      +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
      +
      + +Ben's Note:   Regarding the next section, you can run plain ethernet +and VLAN over the same NIC, but you may not want to..
      +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.
      +
      +
    2. +

      +

    3. Specific Extreme Networks Configuration

      +  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.
      +
      +
    4. +

      +

    5. Cisco-specific configuration

      +

      +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)
      +
      + NOTE: Ethereal also supports VLANs, +and is much more beautiful than tcpdump, if you have GUI capabilities. +
      +
      +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.
      +
      +
    6. +
    +
  4. +

    +

  5. Specific patches and work-arounds for various configurations.

    +

      + + +
    1. My Tulip-based card has MTU problems.

      +

      +
      +   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 */
      +
      +
      +
    2. + +
      +
    3. My eepro100 has MTU problems.

      + +NOTE: Intel's e100 driver works out-of-the-box. --Ben +

      + + Here is a patch sent in by gleb@nbase.co.il
      + +

      +     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. */
      +
      +
    4. +

      + +

    5. My SysKonnect sk98lin doesn't work (submitted by: Patrick Schaaf <bof@bof.de>)

      + +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. +

      +

      +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"));
      +
      +
      +
    6. +

      + +

    7. My 3C59X has MTU problems.

      + +Various contributors, most recently: Richard Fuchs + +

      +
      +--- 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
      + 
      + /* 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
      +
      +
      +
    8. +

      + + +

    9. My natsemi has MTU problems.

      +By Peter Stuge: +

      +
      +--- 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:
      +
      +
    10. +

      + + +

    11. My 3C905B has MTU problems.

      +As found +here +at one point in time.
      +Furnished by: Luis Miguel Cruz Miranda luismi@b2bi.es
      +(I don't know the original author --Ben). + +

      +--- 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
      +
      +
    12. +

      + + +

    13. How to make my PCMCIA ethernet card work with VLANs?

      +Per Peter Stuge:

      + +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. +

      +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.) +

      +Ben Adds:

      +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... + +

    14. + +
    +
  6. +

    +

  7. Scripts and Recipes.

    +

      +
    1. Mandrake (RedHat-style) startup script for VLANs.

      + Contributed by: "MaxiM Basunov" <maxim@idknet.com> +

      +#!/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
      +
      +
    2. + +
    +

    +


    +
    +Terv, 
    +
    +-----------------------------
    +Kristjan Kotkas 
    +KPNQwest Estonia
    +kristjan.kotkas@kpnqwest.ee
    +t + 372 62 66299 m + 372 51 60697 f + 372 62 66292
    +
    +
    + + +
    +
    Ben Greear
    + + +Last modified: Fri Jul 4 09:53:40 PDT 2003 + + + diff --git a/libpcap-0.4/CVS/Entries b/libpcap-0.4/CVS/Entries new file mode 100644 index 0000000..54e26d9 --- /dev/null +++ b/libpcap-0.4/CVS/Entries @@ -0,0 +1,5 @@ +D/SUNOS4//// +D/bpf//// +D/lbl//// +D/linux-include//// +D/net//// diff --git a/libpcap-0.4/CVS/Repository b/libpcap-0.4/CVS/Repository new file mode 100644 index 0000000..0e0ddbb --- /dev/null +++ b/libpcap-0.4/CVS/Repository @@ -0,0 +1 @@ +vlan/libpcap-0.4 diff --git a/libpcap-0.4/CVS/Root b/libpcap-0.4/CVS/Root new file mode 100644 index 0000000..469c859 --- /dev/null +++ b/libpcap-0.4/CVS/Root @@ -0,0 +1 @@ +:pserver:greear@ns1.wanfear.com:/home/cvs/vlan diff --git a/libpcap-0.4/SUNOS4/CVS/Entries b/libpcap-0.4/SUNOS4/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/libpcap-0.4/SUNOS4/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/libpcap-0.4/SUNOS4/CVS/Repository b/libpcap-0.4/SUNOS4/CVS/Repository new file mode 100644 index 0000000..3e7444c --- /dev/null +++ b/libpcap-0.4/SUNOS4/CVS/Repository @@ -0,0 +1 @@ +vlan/libpcap-0.4/SUNOS4 diff --git a/libpcap-0.4/SUNOS4/CVS/Root b/libpcap-0.4/SUNOS4/CVS/Root new file mode 100644 index 0000000..469c859 --- /dev/null +++ b/libpcap-0.4/SUNOS4/CVS/Root @@ -0,0 +1 @@ +:pserver:greear@ns1.wanfear.com:/home/cvs/vlan diff --git a/libpcap-0.4/bpf/CVS/Entries b/libpcap-0.4/bpf/CVS/Entries new file mode 100644 index 0000000..27ad449 --- /dev/null +++ b/libpcap-0.4/bpf/CVS/Entries @@ -0,0 +1 @@ +D/net//// diff --git a/libpcap-0.4/bpf/CVS/Repository b/libpcap-0.4/bpf/CVS/Repository new file mode 100644 index 0000000..e2ce17c --- /dev/null +++ b/libpcap-0.4/bpf/CVS/Repository @@ -0,0 +1 @@ +vlan/libpcap-0.4/bpf diff --git a/libpcap-0.4/bpf/CVS/Root b/libpcap-0.4/bpf/CVS/Root new file mode 100644 index 0000000..469c859 --- /dev/null +++ b/libpcap-0.4/bpf/CVS/Root @@ -0,0 +1 @@ +:pserver:greear@ns1.wanfear.com:/home/cvs/vlan diff --git a/libpcap-0.4/bpf/net/CVS/Entries b/libpcap-0.4/bpf/net/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/libpcap-0.4/bpf/net/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/libpcap-0.4/bpf/net/CVS/Repository b/libpcap-0.4/bpf/net/CVS/Repository new file mode 100644 index 0000000..c8ca0dc --- /dev/null +++ b/libpcap-0.4/bpf/net/CVS/Repository @@ -0,0 +1 @@ +vlan/libpcap-0.4/bpf/net diff --git a/libpcap-0.4/bpf/net/CVS/Root b/libpcap-0.4/bpf/net/CVS/Root new file mode 100644 index 0000000..469c859 --- /dev/null +++ b/libpcap-0.4/bpf/net/CVS/Root @@ -0,0 +1 @@ +:pserver:greear@ns1.wanfear.com:/home/cvs/vlan diff --git a/libpcap-0.4/lbl/CVS/Entries b/libpcap-0.4/lbl/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/libpcap-0.4/lbl/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/libpcap-0.4/lbl/CVS/Repository b/libpcap-0.4/lbl/CVS/Repository new file mode 100644 index 0000000..b351c7f --- /dev/null +++ b/libpcap-0.4/lbl/CVS/Repository @@ -0,0 +1 @@ +vlan/libpcap-0.4/lbl diff --git a/libpcap-0.4/lbl/CVS/Root b/libpcap-0.4/lbl/CVS/Root new file mode 100644 index 0000000..469c859 --- /dev/null +++ b/libpcap-0.4/lbl/CVS/Root @@ -0,0 +1 @@ +:pserver:greear@ns1.wanfear.com:/home/cvs/vlan diff --git a/libpcap-0.4/linux-include/CVS/Entries b/libpcap-0.4/linux-include/CVS/Entries new file mode 100644 index 0000000..7e34cc5 --- /dev/null +++ b/libpcap-0.4/linux-include/CVS/Entries @@ -0,0 +1 @@ +D/netinet//// diff --git a/libpcap-0.4/linux-include/CVS/Repository b/libpcap-0.4/linux-include/CVS/Repository new file mode 100644 index 0000000..6f3b3e1 --- /dev/null +++ b/libpcap-0.4/linux-include/CVS/Repository @@ -0,0 +1 @@ +vlan/libpcap-0.4/linux-include diff --git a/libpcap-0.4/linux-include/CVS/Root b/libpcap-0.4/linux-include/CVS/Root new file mode 100644 index 0000000..469c859 --- /dev/null +++ b/libpcap-0.4/linux-include/CVS/Root @@ -0,0 +1 @@ +:pserver:greear@ns1.wanfear.com:/home/cvs/vlan diff --git a/libpcap-0.4/linux-include/netinet/CVS/Entries b/libpcap-0.4/linux-include/netinet/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/libpcap-0.4/linux-include/netinet/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/libpcap-0.4/linux-include/netinet/CVS/Repository b/libpcap-0.4/linux-include/netinet/CVS/Repository new file mode 100644 index 0000000..2604b84 --- /dev/null +++ b/libpcap-0.4/linux-include/netinet/CVS/Repository @@ -0,0 +1 @@ +vlan/libpcap-0.4/linux-include/netinet diff --git a/libpcap-0.4/linux-include/netinet/CVS/Root b/libpcap-0.4/linux-include/netinet/CVS/Root new file mode 100644 index 0000000..469c859 --- /dev/null +++ b/libpcap-0.4/linux-include/netinet/CVS/Root @@ -0,0 +1 @@ +:pserver:greear@ns1.wanfear.com:/home/cvs/vlan diff --git a/libpcap-0.4/net/CVS/Entries b/libpcap-0.4/net/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/libpcap-0.4/net/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/libpcap-0.4/net/CVS/Repository b/libpcap-0.4/net/CVS/Repository new file mode 100644 index 0000000..bff9ac2 --- /dev/null +++ b/libpcap-0.4/net/CVS/Repository @@ -0,0 +1 @@ +vlan/libpcap-0.4/net diff --git a/libpcap-0.4/net/CVS/Root b/libpcap-0.4/net/CVS/Root new file mode 100644 index 0000000..469c859 --- /dev/null +++ b/libpcap-0.4/net/CVS/Root @@ -0,0 +1 @@ +:pserver:greear@ns1.wanfear.com:/home/cvs/vlan diff --git a/macvlan_config b/macvlan_config new file mode 100755 index 0000000..372ede0 Binary files /dev/null and b/macvlan_config differ diff --git a/macvlan_config.c b/macvlan_config.c new file mode 100644 index 0000000..5bc6723 --- /dev/null +++ b/macvlan_config.c @@ -0,0 +1,635 @@ +/* +####################################################################### +# +# (C) Copyright 2001 +# Alex Zeffertt, Cambridge Broadband Ltd, ajz@cambridgebroadband.com +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +####################################################################### +# Notes: +# +# This configuration utility communicates with macvlan.o, the MAC address +# based VLAN support module. +# +# It uses an IOCTL interface which allows you to +# +# 1. enable/disable MAC address based VLANS over an ether type net_device +# 2. add/remove a MAC address based VLAN - which is an ether type net_device +# layered over the original MACVLAN enabled ether type net_device. +# 3. bind/unbind MAC addresses to/from particular MAC address based VLANs +# 4. discover the state of MAC address based VLANs on the system. +# 5. set/get port flags, including whether to bind to destination MAC +# or source mac. +# 6. Traffic to/from eth0 will not be affected. + + +# +# Example: (Assuming you are using source binding) +# +# If you enable MAC address based VLANS over eth0 +# +# You may then create further VLANs, e.g. eth0#1 eth0#2 .... +# These will not receive any frames until you bind MAC addresses to them. +# If you bind 11:22:33:44:55:66 to eth0#1, then any frames received by +# eth0 with source MAC 11:22:33:44:55:66 will be routed up through eth0#1 +# instead of eth0. +# +# Example: (Assuming you are using destination (local) binding) +# +# If you enable MAC address based VLANS over eth0 +# +# You may then create further VLANs, e.g. eth0#1 eth0#2 .... +# These will not receive any frames until you bind MAC addresses to them. +# If you bind 11:22:33:44:55:66 to eth0#1, then any broadcast/multicast +# frames, or frames with a destination MAC 11:22:33:44:55:66 +# will be routed up through eth0#1 instead of eth0 +# +# For broadcasts, the packet will be duplicated for every VLAN +# with at least one MAC attached. Attaching more than one MAC +# when destination binding makes no sense...don't do it! +# +# +# +####################################################################### +*/ +#include +#include +#include +#include +#include +#include +#include +#include + + +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 "}, + {"enable", "enables mac based vlans over an ethernet device", do_enable, + "enable \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 "}, + {"add", "creates new mac based vlan", do_add, + "add \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 \n" + " - deletes a mac base vlan called \"ifname\"" + }, + {"bind", "binds macaddr to vlan", do_bind, + "bind \n" + " - binds macaddr to vlan called \"ifname\"" + }, + {"unbind", "unbinds macaddr from vlan", do_unbind, + "unbind \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 \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 \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 \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 \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 \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 \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 \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 \n", argv[0]); + return 1; + } + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket"); + return 1; + } + + req.cmd = MACVLAN_UNBIND; + req.ifname = argv[1]; /* name of vlan from which we are deleting a MAC address */ + + /* assemble the macaddr */ + ptr = argv[2]; + if (strlen(ptr) != 17) { + printf("bad macaddr format: need aa:bb:cc:dd:ee:ff\n"); + return 1; + } + for (ptr = argv[2]+2; ptr < argv[2]+16; ptr+=3) + *ptr = 0; + ptr = argv[2]; + macaddr[0] = (unsigned char)htoi(ptr); + macaddr[1] = (unsigned char)htoi(ptr+3); + macaddr[2] = (unsigned char)htoi(ptr+6); + macaddr[3] = (unsigned char)htoi(ptr+9); + macaddr[4] = (unsigned char)htoi(ptr+12); + macaddr[5] = (unsigned char)htoi(ptr+15); + req.macaddr = macaddr; + + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (MACVLAN_UNBIND)"); + return 1; + } + return 0; +} + +int do_info(int argc, char *argv[]) +{ + int s; + struct macvlan_ioctl req; + struct macvlan_ioctl_reply rep; + int nports; + int portidx; + int nifs; + int ifidx; + int nmacs; + int macidx; + unsigned char *p; + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket"); + return 1; + } + /* get the number of ethernet devices which have mac based vlans + * enabled over them + */ + req.cmd = MACVLAN_GET_NUM_PORTS; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (GET_NUM_PORTS)"); + return 1; + } + nports = rep.num; + for (portidx = 0; portidx < nports; portidx++) { + char tmpifname[64]; + /* Get the name of this mac-based-vlan enabled + * ethernet device + */ + req.cmd = MACVLAN_GET_PORT_NAME; + req.portidx = portidx; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (GET_PORT_NAME)"); + return 1; + } + printf("-%s\n", rep.name); + + /* get the port flags */ + req.cmd = MACVLAN_GET_PORT_FLAGS; + req.portidx = portidx; + strcpy(tmpifname, rep.name); + req.ifname = tmpifname; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (GET_PORT_FLAGS)"); + return 1; + } + printf("-%s flag: 0x%x\n", tmpifname, rep.num); + + /* Get the number of mac-based-vlans layered + * over this ethernet device + */ + req.cmd = MACVLAN_GET_NUM_VLANS; + req.portidx = portidx; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (GET_NUM_VLANS)"); + return 1; + } + nifs = rep.num; + for (ifidx = 0; ifidx < nifs; ifidx++) { + /* Get the name of this vlan */ + req.cmd = MACVLAN_GET_VLAN_NAME; + req.portidx = portidx; + req.ifidx = ifidx; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (GET_VLAN_NAME)"); + return 1; + } + /* get the number of mac addresses owned by this vlan */ + printf(" |-%s\n", rep.name); + req.cmd = MACVLAN_GET_NUM_MACS; + req.portidx = portidx; + req.ifidx = ifidx; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (GET_NUM_MACS)"); + return 1; + } + nmacs = rep.num; + for (macidx = 0; macidx < nmacs; macidx++) { + /* get the value of this mac address */ + req.cmd = MACVLAN_GET_MAC_NAME; + req.portidx = portidx; + req.ifidx = ifidx; + req.macaddridx = macidx; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (GET_MAC_NAME)"); + return 1; + } + p = (unsigned char *) rep.name; + printf(" | |-%02x:%02x:%02x:%02x:%02x:%02x\n", + p[0],p[1],p[2],p[3],p[4],p[5]); + } + } + } + return 0; +} + + +int do_unload(int argc, char *argv[]) +{ + int s; + struct macvlan_ioctl req; + struct macvlan_ioctl_reply rep; + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket"); + return 1; + } + + while (get_num_ports(s) > 0) { + char port[64]; + /* Get the name of this mac-based-vlan enabled + * ethernet device + */ + req.cmd = MACVLAN_GET_PORT_NAME; + req.portidx = 0; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (GET_PORT_NAME)"); + return 1; + } + strcpy(port, rep.name); + + while (get_num_vlans(0, s) > 0) { + char cmd[128]; + /* Get the name of this vlan */ + req.cmd = MACVLAN_GET_VLAN_NAME; + req.portidx = 0; + req.ifidx = 0; + req.reply = &rep; + if(ioctl(s, SIOCGIFMACVLAN, &req) < 0) { + perror("ioctl (GET_VLAN_NAME)"); + return 1; + } + + /* Configure down the vlan */ + /* This would be faster using IOCTLs, of course! */ + printf("Configuring down interface: %s with ifconfig...", rep.name); + sprintf(cmd, "ifconfig %s down", rep.name); + system(cmd); + + /* Now, can remove it */ + _do_del(rep.name, s); + } + + /* Now, remove the port */ + _do_disable(port, s); + + } + return 0; +} + +int main(int argc, char *argv[]) +{ + unsigned int cmd; + int err; + + if (argc < 2) + goto usage; + + for (cmd = 0; cmd < NCOMMANDS; cmd++) { + if (!strcmp(command_list[cmd].name,argv[1])) + break; + } + if (cmd == NCOMMANDS) + goto usage; + + if ((err = command_list[cmd].fn(argc-1,argv+1))) + goto usage; + return 0; + + usage: + printf("\n%s subcommands:\n\n", argv[0]); + for (cmd = 0; cmd < NCOMMANDS; cmd++) { + printf("%s %s:\t%s\n",argv[0],command_list[cmd].name,command_list[cmd].short_help); + } + return err; +} diff --git a/packaging/vconfig.manifest b/packaging/vconfig.manifest new file mode 100644 index 0000000..75b0fa5 --- /dev/null +++ b/packaging/vconfig.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/packaging/vconfig.spec b/packaging/vconfig.spec new file mode 100644 index 0000000..a5cb2b8 --- /dev/null +++ b/packaging/vconfig.spec @@ -0,0 +1,39 @@ +Summary: Linux 802.1q VLAN configuration utility +Name: vconfig +Version: 1.9 +Release: 14 +License: GPLv2+ +Group: System Environment/Base +Source: http://www.candelatech.com/~greear/vlan/vlan.%{version}.tar.gz +Source1001: %{name}.manifest +URL: http://www.candelatech.com/~greear/vlan.html +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +%define _sbin /sbin + +%description +The vconfig program configures and adjusts 802.1q VLAN parameters. + +%prep +%setup -q -n vlan.%{version} + +%build +cp %{SOURCE1001} . +make clean +rm -f vconfig +make CCFLAGS="%{optflags}" STRIP=/bin/true vconfig + +%install +rm -rf ${RPM_BUILD_ROOT} +%{__install} -D -m755 vconfig ${RPM_BUILD_ROOT}%{_sbin}/vconfig +%{__install} -D -m644 vconfig.8 ${RPM_BUILD_ROOT}%{_mandir}/man8/vconfig.8 +rm -rf contrib/CVS + +%clean +rm -rf ${RPM_BUILD_ROOT} + +%files +%defattr(-, root, root, 0755) +%manifest %{name}.manifest +%doc CHANGELOG contrib README vlan.html vlan_test.pl +%{_sbin}/vconfig +%{_mandir}/man8/vconfig.8* diff --git a/tcpdump-3.4/CVS/Entries b/tcpdump-3.4/CVS/Entries new file mode 100644 index 0000000..baec772 --- /dev/null +++ b/tcpdump-3.4/CVS/Entries @@ -0,0 +1,2 @@ +D/lbl//// +D/linux-include//// diff --git a/tcpdump-3.4/CVS/Repository b/tcpdump-3.4/CVS/Repository new file mode 100644 index 0000000..7759328 --- /dev/null +++ b/tcpdump-3.4/CVS/Repository @@ -0,0 +1 @@ +vlan/tcpdump-3.4 diff --git a/tcpdump-3.4/CVS/Root b/tcpdump-3.4/CVS/Root new file mode 100644 index 0000000..469c859 --- /dev/null +++ b/tcpdump-3.4/CVS/Root @@ -0,0 +1 @@ +:pserver:greear@ns1.wanfear.com:/home/cvs/vlan diff --git a/tcpdump-3.4/lbl/CVS/Entries b/tcpdump-3.4/lbl/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/tcpdump-3.4/lbl/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/tcpdump-3.4/lbl/CVS/Repository b/tcpdump-3.4/lbl/CVS/Repository new file mode 100644 index 0000000..f9d5b2f --- /dev/null +++ b/tcpdump-3.4/lbl/CVS/Repository @@ -0,0 +1 @@ +vlan/tcpdump-3.4/lbl diff --git a/tcpdump-3.4/lbl/CVS/Root b/tcpdump-3.4/lbl/CVS/Root new file mode 100644 index 0000000..469c859 --- /dev/null +++ b/tcpdump-3.4/lbl/CVS/Root @@ -0,0 +1 @@ +:pserver:greear@ns1.wanfear.com:/home/cvs/vlan diff --git a/tcpdump-3.4/linux-include/CVS/Entries b/tcpdump-3.4/linux-include/CVS/Entries new file mode 100644 index 0000000..d74c534 --- /dev/null +++ b/tcpdump-3.4/linux-include/CVS/Entries @@ -0,0 +1,3 @@ +D/net//// +D/netinet//// +D/sys//// diff --git a/tcpdump-3.4/linux-include/CVS/Repository b/tcpdump-3.4/linux-include/CVS/Repository new file mode 100644 index 0000000..63abf70 --- /dev/null +++ b/tcpdump-3.4/linux-include/CVS/Repository @@ -0,0 +1 @@ +vlan/tcpdump-3.4/linux-include diff --git a/tcpdump-3.4/linux-include/CVS/Root b/tcpdump-3.4/linux-include/CVS/Root new file mode 100644 index 0000000..469c859 --- /dev/null +++ b/tcpdump-3.4/linux-include/CVS/Root @@ -0,0 +1 @@ +:pserver:greear@ns1.wanfear.com:/home/cvs/vlan diff --git a/tcpdump-3.4/linux-include/net/CVS/Entries b/tcpdump-3.4/linux-include/net/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/tcpdump-3.4/linux-include/net/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/tcpdump-3.4/linux-include/net/CVS/Repository b/tcpdump-3.4/linux-include/net/CVS/Repository new file mode 100644 index 0000000..72afc29 --- /dev/null +++ b/tcpdump-3.4/linux-include/net/CVS/Repository @@ -0,0 +1 @@ +vlan/tcpdump-3.4/linux-include/net diff --git a/tcpdump-3.4/linux-include/net/CVS/Root b/tcpdump-3.4/linux-include/net/CVS/Root new file mode 100644 index 0000000..469c859 --- /dev/null +++ b/tcpdump-3.4/linux-include/net/CVS/Root @@ -0,0 +1 @@ +:pserver:greear@ns1.wanfear.com:/home/cvs/vlan diff --git a/tcpdump-3.4/linux-include/netinet/CVS/Entries b/tcpdump-3.4/linux-include/netinet/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/tcpdump-3.4/linux-include/netinet/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/tcpdump-3.4/linux-include/netinet/CVS/Repository b/tcpdump-3.4/linux-include/netinet/CVS/Repository new file mode 100644 index 0000000..3ae1a1b --- /dev/null +++ b/tcpdump-3.4/linux-include/netinet/CVS/Repository @@ -0,0 +1 @@ +vlan/tcpdump-3.4/linux-include/netinet diff --git a/tcpdump-3.4/linux-include/netinet/CVS/Root b/tcpdump-3.4/linux-include/netinet/CVS/Root new file mode 100644 index 0000000..469c859 --- /dev/null +++ b/tcpdump-3.4/linux-include/netinet/CVS/Root @@ -0,0 +1 @@ +:pserver:greear@ns1.wanfear.com:/home/cvs/vlan diff --git a/tcpdump-3.4/linux-include/sys/CVS/Entries b/tcpdump-3.4/linux-include/sys/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/tcpdump-3.4/linux-include/sys/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/tcpdump-3.4/linux-include/sys/CVS/Repository b/tcpdump-3.4/linux-include/sys/CVS/Repository new file mode 100644 index 0000000..6a6e5d2 --- /dev/null +++ b/tcpdump-3.4/linux-include/sys/CVS/Repository @@ -0,0 +1 @@ +vlan/tcpdump-3.4/linux-include/sys diff --git a/tcpdump-3.4/linux-include/sys/CVS/Root b/tcpdump-3.4/linux-include/sys/CVS/Root new file mode 100644 index 0000000..469c859 --- /dev/null +++ b/tcpdump-3.4/linux-include/sys/CVS/Root @@ -0,0 +1 @@ +:pserver:greear@ns1.wanfear.com:/home/cvs/vlan diff --git a/vconfig b/vconfig new file mode 100755 index 0000000..8cfcbcd Binary files /dev/null and b/vconfig differ diff --git a/vconfig.8 b/vconfig.8 new file mode 100644 index 0000000..e9685c2 --- /dev/null +++ b/vconfig.8 @@ -0,0 +1,68 @@ +.TH VCONFIG 8 +.\" NAME should be all caps, SECTION should be 1-8, maybe w/ subsection +.\" other parms are allowed: see man(7), man(1) +.SH NAME +vconfig \- VLAN (802.1q) configuration program. +.SH SYNOPSIS +.B vconfig +.I [lots of long options] +.SH "DESCRIPTION" +The +.B vconfig +program allows you to create and remove vlan\-devices on a vlan enabled +kernel. Vlan\-devices are virtual ethernet devices which represents the +virtual lans on the physical lan. +.SH OPTIONS +.TP +.B add [interface\-name] [vlan\-id] +Creates a vlan-device on [interface\-name]. The resulting vlan\-device +will be called according to the nameing convention set. +.TP +.B rem [vlan\-device] +Removes the named vlan\-device. +.TP +.B set_flag [vlan\-device] 0 | 1 +When 1, ethernet header reorders are turned on. Dumping the device +will appear as a common ethernet device without vlans. When 0(default) +however, ethernet headers are not reordered, which results in vlan tagged +packets when dumping the device. Usually the default gives no problems, +but some packet filtering programs might have problems with it. +.TP + +.B set_egress_map [vlan\-device] [skb\-priority] [vlan\-qos] +This flags that outbound packets with a particular skb\-priority should +be tagged with the particular vlan priority vlan\-qos. The default vlan +priority is 0. +.TP + +.B set_ingress_map [vlan\-device] [skb\-priority] [vlan\-qos] +This flags that inbound packets with the particular vlan priority +vlan\-qos should be queued with a particular skb\-priority. The default +skb\-priority is 0. +.TP + +.B set_name_type VLAN_PLUS_VID | VLAN_PLUS_VID_NO_PAD | DEV_PLUS_VID | DEV_PLUS_VID_NO_PAD +Sets the way vlan\-device names are created. Use vconfig without arguments +to see the different formats. +.TP + +.SH NOTES +VLAN will use Broadcom's NICE interface when the network device supports +it. This is necessary, since usually the hardware of these devices already +removes the vlan tag from the ethernet packet. The set_flag option on +vlan\-devices created on such a physical network device will be ignored. +Dumping the network\-device will show only untagged(non-vlan) traffic, +and dumping the vlan\-devices will only show traffic intended for that +vlan, without the tags. +.br +.SH FILES +.I /proc/net/vlan/config +.br +.I /proc/net/vlan/[vlan\-device] + +.SH SEE ALSO +ip(8), ifconfig(8) +.SH AUTHORS +This manual page was written by Ard van Breemen +.br +The vlan patch is written by Ben Greear diff --git a/vconfig.c b/vconfig.c new file mode 100644 index 0000000..5057cfd --- /dev/null +++ b/vconfig.c @@ -0,0 +1,270 @@ +// +//Copyright (C) 2001 Ben Greear +// +//This program is free software; you can redistribute it and/or +//modify it under the terms of the GNU Library General Public License +//as published by the Free Software Foundation; either version 2 +//of the License, or (at your option) any later version. +// +//This program is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//GNU General Public License for more details. +// +//You should have received a copy of the GNU Library General Public License +//along with this program; if not, write to the Free Software +//Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// To contact the Author, Ben Greear: greearb@candelatech.com +// + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#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) { + return j; /* done */ + } + + hex[1] = hex_str[i]; + bytes[j++] = (char)strtoul(hex, &stop, 16); + } + return j; +} + + +int main(int argc, char** argv) { + int fd; + struct vlan_ioctl_args if_request; + + char* cmd = NULL; + char* if_name = NULL; + unsigned int vid = 0; + unsigned int skb_priority; + unsigned short vlan_qos; + unsigned int nm_type = VLAN_NAME_TYPE_PLUS_VID; + + char* conf_file_name = "/proc/net/vlan/config"; + + memset(&if_request, 0, sizeof(struct vlan_ioctl_args)); + + if ((argc < 3) || (argc > 5)) { + fprintf(stdout,"Expecting argc to be 3-5, inclusive. Was: %d\n",argc); + + show_usage(); + exit(1); + } + else { + cmd = argv[1]; + + if (strcasecmp(cmd, "set_name_type") == 0) { + if (strcasecmp(argv[2], "VLAN_PLUS_VID") == 0) { + nm_type = VLAN_NAME_TYPE_PLUS_VID; + } + else if (strcasecmp(argv[2], "VLAN_PLUS_VID_NO_PAD") == 0) { + nm_type = VLAN_NAME_TYPE_PLUS_VID_NO_PAD; + } + else if (strcasecmp(argv[2], "DEV_PLUS_VID") == 0) { + nm_type = VLAN_NAME_TYPE_RAW_PLUS_VID; + } + else if (strcasecmp(argv[2], "DEV_PLUS_VID_NO_PAD") == 0) { + nm_type = VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD; + } + else { + // MATHIEU + //cerr << "Invalid name type.\n"; + fprintf(stderr,"Invalid name type.\n"); + + show_usage(); + exit(1); + } + if_request.u.name_type = nm_type; + } + else { + if_name = argv[2]; + if (strlen(if_name) > 15) { + // MATHIEU + //cerr << "ERROR: if_name must be 15 characters or less." << endl; + fprintf(stderr,"ERROR: if_name must be 15 characters or less.\n"); + exit(1); + } + strcpy(if_request.device1, if_name); + } + + if (argc == 4) { + vid = atoi(argv[3]); + if_request.u.VID = vid; + } + + if (argc == 5) { + skb_priority = atoi(argv[3]); + vlan_qos = atoi(argv[4]); + if_request.u.skb_priority = skb_priority; + if_request.vlan_qos = vlan_qos; + } + } + + // Open up the /proc/vlan/config + if ((fd = open(conf_file_name, O_RDONLY)) < 0) { + // MATHIEU + //cerr << "ERROR: Could not open /proc/vlan/config.\n"; + fprintf(stderr,"WARNING: Could not open /proc/net/vlan/config. Maybe you need to load the 8021q module, or maybe you are not using PROCFS??\n"); + + } + else { + close(fd); + } + + /* We use sockets now, instead of the file descriptor */ + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + fprintf(stderr, "FATAL: Couldn't open a socket..go figure!\n"); + exit(2); + } + + /* add */ + if (strcasecmp(cmd, "add") == 0) { + if_request.cmd = ADD_VLAN_CMD; + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + fprintf(stderr,"ERROR: trying to add VLAN #%u to IF -:%s:- error: %s\n", + vid, if_name, strerror(errno)); + exit(3); + } + else { + fprintf(stdout,"Added VLAN with VID == %u to IF -:%s:-\n", + vid, if_name); + if (vid == 1) { + fprintf(stdout, "WARNING: VLAN 1 does not work with many switches,\nconsider another number if you have problems.\n"); + } + } + }//if + else if (strcasecmp(cmd, "rem") == 0) { + if_request.cmd = DEL_VLAN_CMD; + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + fprintf(stderr,"ERROR: trying to remove VLAN -:%s:- error: %s\n", + if_name, strerror(errno)); + exit(4); + } + else { + fprintf(stdout,"Removed VLAN -:%s:-\n", if_name); + } + }//if + else if (strcasecmp(cmd, "set_egress_map") == 0) { + if_request.cmd = SET_VLAN_EGRESS_PRIORITY_CMD; + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + fprintf(stderr,"ERROR: trying to set egress map on device -:%s:- error: %s\n", + if_name, strerror(errno)); + exit(5); + } + else { + fprintf(stdout,"Set egress mapping on device -:%s:- " + "Should be visible in /proc/net/vlan/%s\n", + if_name, if_name); + } + } + else if (strcasecmp(cmd, "set_ingress_map") == 0) { + if_request.cmd = SET_VLAN_INGRESS_PRIORITY_CMD; + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + fprintf(stderr,"ERROR: trying to set ingress map on device -:%s:- error: %s\n", + if_name, strerror(errno)); + exit(6); + } + else { + fprintf(stdout,"Set ingress mapping on device -:%s:- " + "Should be visible in /proc/net/vlan/%s\n", + if_name, if_name); + } + } + else if (strcasecmp(cmd, "set_flag") == 0) { + if_request.cmd = SET_VLAN_FLAG_CMD; + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + fprintf(stderr,"ERROR: trying to set flag on device -:%s:- error: %s\n", + if_name, strerror(errno)); + exit(7); + } + else { + fprintf(stdout,"Set flag on device -:%s:- " + "Should be visible in /proc/net/vlan/%s\n", + if_name, if_name); + } + } + else if (strcasecmp(cmd, "set_name_type") == 0) { + if_request.cmd = SET_VLAN_NAME_TYPE_CMD; + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + fprintf(stderr,"ERROR: trying to set name type for VLAN subsystem, error: %s\n", + strerror(errno)); + exit(8); + } + else { + fprintf(stdout,"Set name-type for VLAN subsystem." + " Should be visible in /proc/net/vlan/config\n"); + } + } + else { + fprintf(stderr, "Unknown command -:%s:-\n", cmd); + + show_usage(); + exit(5); + } + + return 0; +}/* main */ diff --git a/vconfig.h b/vconfig.h new file mode 100644 index 0000000..e69de29 diff --git a/vconfig.o b/vconfig.o new file mode 100644 index 0000000..8e8da0e Binary files /dev/null and b/vconfig.o differ diff --git a/vconfig.spec b/vconfig.spec new file mode 100644 index 0000000..c05c7b5 --- /dev/null +++ b/vconfig.spec @@ -0,0 +1,61 @@ +Summary: Linux 802.1q VLAN configuration utility +Name: vconfig +Version: 1.6 +Release: 4 +License: distributable +Group: Applications/System +Source: http://scry.wanfear.com/~greear/vlan/vlan.%{version}.tar.gz +Source1: ifup-vlan +Source2: ifdown-vlan +Source3: vlan.sysconfig +Source4: README.ifup +Source5: ifcfg-vlan2-example +URL: http://scry.wanfear.com/~greear/vlan.html +BuildRoot: %{_tmppath}/%{name}-%{version}-root +Packager: Dale Bewley +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 +- update to 1.6 +- add ifup scripts + +* Tue Dec 11 2001 Dale Bewley +- initial specfile diff --git a/vlan.html b/vlan.html new file mode 100644 index 0000000..0588b61 --- /dev/null +++ b/vlan.html @@ -0,0 +1,436 @@ + + + + 802.1Q VLAN implementation for Linux + + + +

    802.1Q VLAN implementation for Linux

    + +
    +Updated Sept 30, 2003
    +Release: 1.8
    +
    +

    + +MTU problems exist for many ethernet drivers. Other than that, things seem fairly stable! +

    + +

    +PLUG:   Check out my company that makes traffic generation and WAN simulation + test equipment based on the Linux operating system:
    + Candela Technologies +
    +Let us help you test your DSL, Cable Access, Satellite and other network systems!
    +
    + +
    +TIP jar on my home page.

    + + +Join the vlan mailing list, + After that, to post, send mail to +vlan@ns1.wanfear.com. +

    +Submit a bug/issue/enhancement with the: VLAN Bugzilla

  8. +

    + +I hear that the 2.2/2.4 kernel patches have worked +with these (and other, I'm sure) systems:

    +

      +
    • Cisco: {Catalyst: 6509}, + 3Com: {Corebuilder, Netbuilder II, SuperStack II switch 630}, + Alpine: {3804(SMMi,F32Ti)} + Extreme Ntwks {Summit 48, 48i, 5i} + Foundry: {ServerIronXL, FastIron}
    • +
    • Alteon ACENic Gigabit, 3Com 3c509, realtek RTL8029(AS), RTL8139, DEC DC21140 (tulip), + DFE-570TX quad-21143, Intel PRO/1000 with Intel's driver +
    • +
    +

    + +Performance: +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! + +

    VLAN related Resources.
    + +

    + +

    Features
    +
      +
    • Implements 802.1Q VLAN spec.
    • +
    • Implements support for a non-standard (as far as I know) + MAC-based VLAN functionality.
    • +
    • Can support up to 4094 VLANs per ethernet interface.
    • +
    • 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.
    • +
    • Supports MULTICAST
    • +
    • Can change MAC address of VLAN.
    • +
    • Multiple naming conventions supported, and adjustable at runtime.
    • +
    • Optional header-reordering, to make the VLAN interface look JUST LIKE + 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. +
    • +
    +

    + + +


    +Download vconfig binaries (source is more flexible, but this will work for most people). + +

    + +


    +
    Change Log
    +
      +

      + +

    • Release 1.8 (gz)   For Kernel: 2.4.21+   Sept 30, 2003:
      +

      +

        +
      • 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. +
      • +
      +
    • + +

      +

    • Release 1.7m (gz)   For Kernel: 2.4.14+   Feb 27, 2003:
      +

      +

        +
      • 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. +
      • +
      +
    • +

      + +

    • Release 1.7 (gz)   For Kernel: 2.4.14+   Feb 27, 2003:
      +

      +

        +
      • Clarified the license for vconfig (GPL). Other small tweaks.
      • +
      +
    • + +

      +

    • Release 1.6 (gz)   For Kernel: 2.4.14+   March 24, 2002:
      +

      +

        +
      • Removed 2.4 kernel patch: It's in the standard kernel now.
      • +
      • Updated vconfig to fix some compile issues, and enable cross-compilation + to the StrongARM platform (changes should help other cross-compile + attempts too.)
      • +
      +
    • +

      + +

    • Release 1.5 (gz)   For Kernel: 2.4.12-pre5   October 22, 2001:
      +

      +

        +
      • 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.
      • +
      +
    • +

      + +

    • Release 1.4 (gz)   For Kernel: 2.4.8   August 16, 2001:
      +

      +

        +
      • 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...
      • +
      +
    • + +

      +

    • Release 1.0.3 (gz)   For Kernel: 2.4.7   August 5, 2001:
      +

      +

        +
      • 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.
      • +
      • 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.
      • +
      • There were *NO* changes to the 2.2 series patch, and I don't expect to ever make + any more changes there...
      • +
      + +
    • +

      + +

    • Release 1.0.1 (gz)   For Kernel: 2.2.18/19, 2.4.3-pre3   April 16, 2001:
      +

      +

        +
      • 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.
      • +
      + +
    • + +

      +

    • Release 1.0.0 (gz)   For Kernel: 2.2.18, 2.4.0   Jan 14, 2001:
      +

      +

        +
      • 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.c + (search for #define BEN_FAST_DEV_LOOKUP).
      • +
      • vconfig should warn when creating VLAN 1, because that VLAN is not compatible with many + switches.
      • +
      + +
    • + +

      +

    • Release 0.0.15 (gz)   For Kernel: 2.2.18, 2.4.prerelease   Dec 31, 2000:
      +

      +

        +
      • 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.
      • +
      + +
    • + +

      +

    • Release 0.0.14 (gz)   For Kernel: 2.2.17, 2.4.pre9   Oct 26, 2000:
      +

      + This code seems pretty stable. +

        +
      • 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.
      • +
      + +
    • + +

      +

    • Release 0.0.13 (gz)   For Kernel: 2.2.17, 2.4.pre9   Oct 11, 2000:
      +
      KNOWN TO BE BUSTED, here for posterity's sake.
      +

      +

        +
      • 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.
      • +
      + +
    • +

      + +

    • Release 0.0.12 (gz)   For Kernel: 2.2.16, 2.4.pre7   August 27, 2000:
      + 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. +

    • +

      + +

    • Release 0.0.11 (gz)   For Kernel: 2.2.13/14, 2.3.99   April 23, 2000:
      + 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:
      +
      +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! +

    • +

      + +


    +

    + +

    +VLAN Setup and Configuration

    + +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: tar -xvzf vlan.1.6.tar.gz +Alternatively, you can get it from the CVS Repository using something like this:
    +
      +
    1. Install and configure + cvs + on your machine.
    2. +
    3. Specify the vlan repository:
      + export CVSROOT=:pserver:anonymous@ns1.wanfear.com:/home/cvs/vlan +
    4. +
    5. Log in to the repository:
      + cvs login     (PASSWORD: anonymous) +
    6. +
    7. Check out the source:
      + mkdir vlan; cd vlan; cvs -z3 checkout vlan +
    8. +
    +

    + +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 www.kernel.org. +Unzip and un-tar this in your home directory as well, which should +create a linux directory in your $HOME directory. Example: +tar -xvzf linux-2.2.14.tar.gz

    + +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:

    + +cd $HOME/linux
    +patch -p 1 < $HOME/vlan/[vlan.patch]
    +
    +

    + +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: +cd $HOME; ln -s /home/greear/kernel/2.4/linux.dev linux +

    + +Build the vconfig program in the $HOME/vlan directory:
    +cd $HOME/vlan
    +make
    +
    +

    + +Now, time to compile your new kernel! Use the make xconfig +command in your $HOME/linux directory to select your kernel options. The +option related to 802.1Q VLANs is found under the Networking options. +If the option is not highlighted, make sure you select "Experimental Drivers" +in one of the first xconfig menus. +

    + +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 /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, other than a line spit out during the boot process. +There should be a config programs in your $HOME/vlan +directory: vconfig. vconfig 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: +

    + +vconfig add eth0 5 +

    + +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: +

    +vconfig rem eth0.5 +

    + +You will also need to give it an ip, eg: ifconfig -i eth0.5 192.168.2.1
    +and configure it UP: ifconfig -i eth0.5 up +

    + +NOTE: You can get lots of VLAN related configuration information from +the /proc/net/vlan/* files by using 'cat' or 'more' to look at them. +

    + +Please get in contact with me if you have suggestions, patches, or other +comments. +

    + +


    +
    greearb@candelatech.com   + Ben Greear's Home Page
    + + +Last modified: Tue Sep 30 14:16:14 PDT 2003 + + + diff --git a/vlan_2.2.patch b/vlan_2.2.patch new file mode 100644 index 0000000..e038825 --- /dev/null +++ b/vlan_2.2.patch @@ -0,0 +1,2927 @@ +diff -u -r -N -X /home/greear/exclude.list linux/include/linux/if_ether.h linux.dev/include/linux/if_ether.h +--- linux/include/linux/if_ether.h Sun Dec 10 17:49:44 2000 ++++ linux.dev/include/linux/if_ether.h Thu Jan 4 20:41:19 2001 +@@ -32,6 +32,36 @@ + #define ETH_DATA_LEN 1500 /* Max. octets in payload */ + #define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ + ++ ++#ifdef CONFIG_VLAN_802_1Q ++ ++ ++#define VLAN_ETH_ALEN 6 /* Octets in one ethernet addr */ ++#define VLAN_ETH_HLEN 18 /* Total octets in header. */ ++#define VLAN_ETH_ZLEN 64 /* Min. octets in frame sans FCS */ ++ ++/* These could be bumped up by 4, but I'm not sure if all the underlying ++ * drivers would like it. ++ * UPDATE: Bumping it by 4, as per Klika's suggestion below. --BLG ++ * ++ * According to 802.3ac, the packet can be 4 bytes longer. --Klika Jan ++ */ ++#define VLAN_ETH_DATA_LEN 1500 /* Max. octets in payload */ ++#define VLAN_ETH_FRAME_LEN 1518 /* Max. octets in frame sans FCS */ ++ ++struct vlan_ethhdr ++{ ++ unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ ++ unsigned char h_source[ETH_ALEN]; /* source ether addr */ ++ unsigned short h_vlan_proto; /* Should always be 0x8100 */ ++ unsigned short h_vlan_TCI; /* Encapsulates priority and VLAN ID */ ++ unsigned short h_vlan_encapsulated_proto; /* packet type ID field (or len) */ ++}; ++ ++ ++#endif ++ ++ + /* + * These are the defined Ethernet Protocol ID's. + */ +@@ -54,6 +84,7 @@ + #define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */ + #define ETH_P_ATALK 0x809B /* Appletalk DDP */ + #define ETH_P_AARP 0x80F3 /* Appletalk AARP */ ++#define ETH_P_802_1Q 0x8100 /* 802.1Q VLAN Extended Header */ + #define ETH_P_IPX 0x8137 /* IPX over DIX */ + #define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */ + #define ETH_P_ATMMPOA 0x884c /* MultiProtocol Over ATM */ +diff -u -r -N -X /home/greear/exclude.list linux/include/linux/if_vlan.h linux.dev/include/linux/if_vlan.h +--- linux/include/linux/if_vlan.h Wed Dec 31 17:00:00 1969 ++++ linux.dev/include/linux/if_vlan.h Sun Jan 14 14:30:56 2001 +@@ -0,0 +1,238 @@ ++/* -*- linux-c -*- ++ * VLAN An implementation of 802.1Q VLAN tagging. ++ * ++ * Version: 0.0.1 03/06/99 ++ * ++ * Authors: Ben Greear ++ * ++ * 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 /* 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 + #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 ++#include ++ ++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 , ++ * ++ * 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 /* for copy_from_user */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#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 ++ ++/* 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 , ++ * ++ * 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 /* for copy_from_user */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "vlan.h" ++#include "vlanproc.h" ++#include ++#include ++#include ++ ++ ++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, coppied from wanproc.c ++* by: Gene Kozin ++* ++* 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 ++#include /* offsetof(), etc. */ ++#include /* return codes */ ++#include ++#include /* kmalloc(), kfree() */ ++#include /* verify_area(), etc. */ ++#include /* inline mem*, str* functions */ ++#include /* __initfunc et al. */ ++#include /* kernel <-> user copy */ ++#include /* htons(), etc. */ ++#include /* copy_to_user */ ++#include ++#include ++#include ++#include ++#include ++#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 ++ * entry for each device ++ */ ++ ++/* ++ * Generic /proc/net/vlan/ 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/ 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 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 + #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; iname, 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 +@@ -169,5 +173,9 @@ + { "IrDA", irda_proto_init }, /* IrDA protocols */ + #endif + ++#ifdef CONFIG_VLAN_802_1Q ++ { "VLAN", vlan_proto_init }, /* 802.1Q VLAN Support. --BLG */ ++#endif ++ + { NULL, NULL } /* End marker */ + }; diff --git a/vlan_test.pl b/vlan_test.pl new file mode 100644 index 0000000..f723089 --- /dev/null +++ b/vlan_test.pl @@ -0,0 +1,223 @@ +#!/usr/bin/perl + +# For now, this just tests the addition and removal of 1000 VLAN interfaces on eth0 + +# Arguments: +# graph Generate a graph. +# clean Remove interfaces. + +use strict; + +$| = 1; + +if ($ARGV[0] eq "graph") { + my $dev_cnt = 0; + my $user = 0; + my $system = 0; + my $real = 0; + my $prog = ""; + + open(IPNH, ">/tmp/ip_rpt_no_hash.txt") || die("Can't open /tmp/ip_rpt_no_hash.txt\n"); + open(IFCFGNH, ">/tmp/ifconfig_rpt_no_hash.txt") || die("Can't open /tmp/ifconfig_rpt_no_hash.txt\n"); + open(IP, ">/tmp/ip_rpt.txt") || die("Can't open /tmp/ip_rpt.txt\n"); + open(IFCFG, ">/tmp/ifconfig_rpt.txt") || die("Can't open /tmp/ifconfig_rpt.txt\n"); + + my $hash = $ARGV[1]; + my $no_hash = $ARGV[2]; + + open(IF, "$no_hash"); + while () { + 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 () { + my $ln = $_; + chomp($ln); + + #print "LINE: -:$ln:-\n"; + + if ($ln =~ /Doing ip addr show for (\S+)/) { + $dev_cnt = $1; + $prog = "ip"; + } + elsif ($ln =~ /Doing ifconfig -a for (\S+)/) { + $dev_cnt = $1; + $prog = "ifconfig"; + } + elsif ($ln =~ /^real (\S+)/) { + $real = $1; + } + elsif ($ln =~ /^user (\S+)/) { + $user = $1; + } + elsif ($ln =~ /^sys (\S+)/) { + $system = $1; + #print "prog: $prog $dev_cnt\t$user\t$system\t$real\n"; + if ($prog eq "ip") { + print IP "$dev_cnt\t$user\t$system\t$real\n"; + } + else { + print IFCFG "$dev_cnt\t$user\t$system\t$real\n"; + } + } + else { + #print "INFO: Didn't match anything -:$ln:-\n"; + } + } + + close(IP); + close(IFCFG); + + my $plot_cmd = "set title \"ip addr show V/S ifconfig -a\" +set terminal png color +set output \"ip_addr_show.png\" +set size 1,2 +set xlabel \"Interface Count\" +set ylabel \"Seconds\" +set grid +plot \'/tmp/ip_rpt.txt\' using 1:3 title \"ip_system\" with lines, \\ + \'/tmp/ip_rpt.txt\' using 1:2 title \"ip_user\" with lines, \\ + \'/tmp/ifconfig_rpt.txt\' using 1:3 title \"ifconfig_system\" with lines, \\ + \'/tmp/ifconfig_rpt.txt\' using 1:2 title \"ifconfig_user\" with lines, \\ + \'/tmp/ip_rpt_no_hash.txt\' using 1:3 title \"ip_system_no_hash\" with lines, \\ + \'/tmp/ip_rpt_no_hash.txt\' using 1:2 title \"ip_user_no_hash\" with lines, \\ + \'/tmp/ifconfig_rpt_no_hash.txt\' using 1:3 title \"ifconfig_system_no_hash\" with lines, \\ + \'/tmp/ifconfig_rpt_no_hash.txt\' using 1:2 title \"ifconfig_user_no_hash\" with lines"; + print "Plotting with cmd -:$plot_cmd:-\n"; + + open(GP, "| gnuplot") or die ("Can't open gnuplot pipe(2).\n"); + print GP "$plot_cmd"; + close(GP); + + exit(0); +} + +my $num_if = 4000; + +`/usr/local/bin/vconfig set_name_type VLAN_PLUS_VID_NO_PAD`; + +my $d = 5; +my $c = 5; + +if ($ARGV[0] ne "clean") { + + my $i; + print "Adding VLAN interfaces 1 through $num_if\n"; + + print "Turnning off /sbin/hotplug"; + `echo > /proc/sys/kernel/hotplug`; + + my $p = time(); + for ($i = 1; $i<=$num_if; $i++) { + `/usr/local/bin/vconfig add eth0 $i`; + #`ip address flush dev vlan$i`; + `ip address add 192.168.$c.$c/24 dev vlan$i`; + `ip link set dev vlan$i up`; + + if (($i <= 4000) && (($i % 250) == 0)) { + print "Doing ifconfig -a for $i devices.\n"; + `time -p ifconfig -a > /tmp/vlan_test_ifconfig_a_$i.txt`; + print "Doing ip addr show for $i devices.\n"; + `time -p ip addr show > /tmp/vlan_test_ip_addr_$i.txt`; + } + + $d++; + if ($d > 250) { + $d = 5; + $c++; + } + } + my $n = time(); + my $diff = $n - $p; + + print "Done adding $num_if VLAN interfaces in $diff seconds.\n"; + + sleep 2; +} + +print "Removing VLAN interfaces 1 through $num_if\n"; +$d = 5; +$c = 5; +my $p = time(); +my $i; +for ($i = 1; $i<=$num_if; $i++) { + `/usr/local/bin/vconfig rem vlan$i`; + + $d++; + if ($d > 250) { + $d = 5; + $c++; + } +} +my $n = time(); +my $diff = $n - $p; +print "Done deleting $num_if VLAN interfaces in $diff seconds.\n"; + +sleep 2; + + +if ($ARGV[0] ne "clean") { + + my $tmp = $num_if / 4; + print "\nGoing to add and remove 2 interfaces $tmp times.\n"; + $p = time(); + + + for ($i = 1; $i<=$tmp; $i++) { + `/usr/local/bin/vconfig add eth0 1`; + `ifconfig vlan1 192.168.200.200`; + `ifconfig vlan1 up`; + `ifconfig vlan1 down`; + + `/usr/local/bin/vconfig add eth0 2`; + `ifconfig vlan2 192.168.202.202`; + `ifconfig vlan2 up`; + `ifconfig vlan2 down`; + + `/usr/local/bin/vconfig rem vlan2`; + `/usr/local/bin/vconfig rem vlan1`; + } + $n = time(); + $diff = $n - $p; + print "Done adding/removing 2 VLAN interfaces $tmp times in $diff seconds.\n"; +} + +print "Re-installing /sbin/hotplug"; +`echo /sbin/hotplug > /proc/sys/kernel/hotplug`; + diff --git a/vlan_test2.pl b/vlan_test2.pl new file mode 100644 index 0000000..4b73a81 --- /dev/null +++ b/vlan_test2.pl @@ -0,0 +1,116 @@ +#!/usr/bin/perl + +# Arguments: +# clean Remove interfaces. + +use strict; + +$| = 1; + +my $num_if = 4000; + +`modprobe 8021q`; +print "Memory after loading 8021q module: "; +print `free`; +print "\n"; + +`/usr/local/bin/vconfig set_name_type VLAN_PLUS_VID_NO_PAD`; + +my $d = 5; +my $c = 5; + +if ($ARGV[0] ne "clean") { + + my $i; + print "Adding VLAN interfaces 1 through $num_if\n"; + + print "Turnning off /sbin/hotplug...\n"; + `echo > /proc/sys/kernel/hotplug`; + + my $p = time(); + for ($i = 1; $i<=$num_if; $i++) { + `/usr/local/bin/vconfig add eth0 $i`; + #`ip address flush dev vlan$i`; + `ip address add 192.168.$c.$c/24 dev vlan$i`; + `ip link set dev vlan$i up`; + + $d++; + if ($d > 250) { + print "."; + $d = 5; + $c++; + } + } + + print "\nMemory after creating $i vlan devices: "; + print `free`; + print "\n"; + + print "Doing ifconfig -a for $i devices.\n"; + `time -p ifconfig -a > /tmp/vlan_test_ifconfig_a_$i.txt`; + print "Doing ip addr show for $i devices.\n"; + `time -p ip addr show > /tmp/vlan_test_ip_addr_$i.txt`; + + my $n = time(); + my $diff = $n - $p; + + print "Done adding $num_if VLAN interfaces in $diff seconds.\n"; + + sleep 2; +} + +print "Removing VLAN interfaces 1 through $num_if\n"; +$d = 5; +$c = 5; +my $p = time(); +my $i; +for ($i = 1; $i<=$num_if; $i++) { + `/usr/local/bin/vconfig rem vlan$i`; +} +my $n = time(); +my $diff = $n - $p; +print "Done deleting $num_if VLAN interfaces in $diff seconds.\n"; + +print "Memory after deleting $i vlan devices: "; +print `free`; +print "\n"; + +sleep 2; + + +if ($ARGV[0] ne "clean") { + + my $tmp = $num_if * 4; + print "\nGoing to add and remove 2 interfaces $tmp times.\n"; + $p = time(); + + + for ($i = 1; $i<=$tmp; $i++) { + `/usr/local/bin/vconfig add eth0 1`; + `ifconfig vlan1 192.168.200.200`; + `ifconfig vlan1 up`; + `ifconfig vlan1 down`; + + `/usr/local/bin/vconfig add eth0 2`; + `ifconfig vlan2 192.168.202.202`; + `ifconfig vlan2 up`; + `ifconfig vlan2 down`; + + `/usr/local/bin/vconfig rem vlan2`; + `/usr/local/bin/vconfig rem vlan1`; + + if (($i % 125) == 0) { + print "."; + } + } + $n = time(); + $diff = $n - $p; + print "\nDone adding/removing 2 VLAN interfaces $tmp times in $diff seconds.\n"; +} + +print "Re-installing /sbin/hotplug...\n"; +`echo /sbin/hotplug > /proc/sys/kernel/hotplug`; + +print "Memory at end of the run: "; +print `free`; +print "\n";