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 */ };