diff -u -r linux-2400test10p5/include/linux/if_ether.h linux-2400test10p5vl/include/linux/if_ether.h --- linux-2400test10p5/include/linux/if_ether.h Tue Jul 11 21:12:24 2000 +++ linux-2400test10p5vl/include/linux/if_ether.h Wed Oct 25 22:17:44 2000 @@ -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_HLEN 4 /* Total octets in header. */ + +/* 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 +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_PPP_DISC 0x8863 /* PPPoE discovery messages */ diff -u -r linux-2400test10p5/include/linux/if_vlan.h linux-2400test10p5vl/include/linux/if_vlan.h --- linux-2400test10p5/include/linux/if_vlan.h Wed Oct 25 23:51:31 2000 +++ linux-2400test10p5vl/include/linux/if_vlan.h Wed Oct 25 22:21:07 2000 @@ -0,0 +1,258 @@ +/* + * VLAN An implementation of 802.1Q VLAN tagging. + * + * For some idea of the architecture, see the web page at (curently): + * http://scry.wanfear.com/~greear/vlan.html + * + * Also, I will put comments in this file to try to explain some general + * architecture. --Ben + * + * + * Authors: Ben Greear + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#ifndef _LINUX_IF_VLAN_H_ +#define _LINUX_IF_VLAN_H_ + +#ifdef __KERNEL__ + + +/* externally defined structs */ +struct vlan_group; +struct net_device; +struct sk_buff; +struct packet_type; +struct vlan_collection; + +/* early declaration of structs in this file */ +struct vlan_port; +struct vlan_port_collection; + +#include /* for proc_dir_entry */ +#include + +/* Find a VLAN device by the MAC address of it's Ethernet device, and + * it's VLAN ID. The default configuration is to have VLAN's scope + * to be box-wide, so the MAC will be ignored. The mac will only be + * looked at if we are configured to have a seperate set of VLANs per + * each MAC addressable interface. Note that this latter option does + * NOT follow the spec for VLANs, but may be useful for doing very + * large quantities of VLAN MUX/DEMUX onto FrameRelay or ATM PVCs. + */ +struct net_device *find_802_1Q_vlan_dev(const char* real_mac_addr, + unsigned short VID); /* vlan.c */ + + + +int dev_new_index(void); /* dev.c */ + +/* found in vlan_dev.c */ +struct net_device_stats* vlan_dev_get_stats(struct net_device* dev); +int vlan_dev_rebuild_header(struct sk_buff *skb); +int vlan_rx_skb(struct sk_buff *skb, struct net_device *dev, + struct packet_type* ptype); +int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, void *daddr, void *saddr, + unsigned len); +int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev); +int vlan_dev_change_mtu(struct net_device *dev, int new_mtu); +int vlan_dev_set_mac_address(struct net_device *dev, void* addr); +int vlan_dev_open(struct net_device* dev); +int vlan_dev_stop(struct net_device* dev); +int vlan_dev_init(struct net_device* dev); +void vlan_dev_destruct(struct net_device* dev); +/* I'm ignorant of these right now. --BLG +int vlan_dev_header_cache(struct neighbour *neigh, struct hh_cache *hh); +void vlan_dev_header_cache_update(struct hh_cache *hh, struct device *dev, + unsigned char * haddr); +*/ +void vlan_dev_copy_and_sum(struct sk_buff *dest, unsigned char *src, + int length, int base); +int vlan_dev_set_dflt_vid(char* dev_name, unsigned short vid); +int vlan_dev_set_dflt_priority(char* dev_name, unsigned short vid); +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); + +/* 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 net_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 net_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 net_device* vlan_dev); +int vlan_collection_remove_vlan_id(struct vlan_collection* vc, unsigned short vlan_id); + + + +/* found in vlan.c */ +/* Our listing of VLAN group(s) */ +extern struct vlan_group* p802_1Q_vlan_list; + + +#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 look-up, but it many cases it wastes memory. + */ +#define VLAN_GROUP_ARRAY_LEN 4096 + +struct vlan_group { + unsigned char real_mac[6]; /* The mac of the ethernet card the vlan is attached to. */ + struct net_device* vlan_devices[VLAN_GROUP_ARRAY_LEN]; + + struct vlan_group* next; /* the next in the list */ +}; + + +/* __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 net_device* real_dev; /* the underlying device/interface */ + struct proc_dir_entry* dent; /* Holds the proc data */ +}; + +/* inline functions */ + +static inline unsigned short vlan_dev_get_egress_qos_mask(struct net_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_DEFAULT_VLAN_ID_IOCTL, + SET_INGRESS_PRIORITY_IOCTL, + SET_EGRESS_PRIORITY_IOCTL, + GET_INGRESS_PRIORITY_IOCTL, + GET_EGRESS_PRIORITY_IOCTL, + SET_NAME_TYPE_IOCTL, + SET_BIND_TYPE_IOCTL, + SET_VLAN_FLAG_IOCTL, + SET_DEFAULT_VLAN_PRIORITY_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 +}; + +enum vlan_bind_type { + VLAN_BIND_PER_KERNEL, + VLAN_BIND_PER_INTERFACE +}; + +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; +}; + + +#endif diff -u -r linux-2400test10p5/include/linux/netdevice.h linux-2400test10p5vl/include/linux/netdevice.h --- linux-2400test10p5/include/linux/netdevice.h Wed Oct 25 23:49:45 2000 +++ linux-2400test10p5vl/include/linux/netdevice.h Wed Oct 25 22:17:53 2000 @@ -38,8 +38,15 @@ #include #ifdef CONFIG_NET_PROFILE #include + +#if defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE) +struct vlan_dev_info; +#endif + #endif + + #define NET_XMIT_SUCCESS 0 #define NET_XMIT_DROP 1 /* skb dropped */ #define NET_XMIT_CN 2 /* congestion notification */ @@ -178,12 +185,23 @@ { 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_len; /* length of header */ int (*hh_output)(struct sk_buff *skb); rwlock_t hh_lock; /* cached hardware header; allow for machine alignment needs. */ - unsigned long hh_data[16/sizeof(unsigned long)]; + +#if defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE) + /* we need 4 extra bytes for VLAN headers */ + /* Odd looking arithmetic for 64-bit machines */ + unsigned long hh_data[1+16/sizeof(unsigned long)]; +#else + unsigned long hh_data[16/sizeof(unsigned long)]; +#endif + }; /* These flag bits are private to the generic network queueing @@ -390,6 +408,27 @@ rwlock_t fastpath_lock; 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; + + /* If this device is not a VLAN device, but we are bridging VLANs, + * then we must have a default VID that can be assigned (if configured + * to do so) to all frames from this device. If zero, then we + * WILL NOT turn the packet into a vlan packet. + */ + unsigned short default_vid; /* VID to be assigned by default. */ + + /* If the default-priority is set, and the default_vid is set, + * then the default_priority will be used for the default-VLAN. + */ + /* Priority to be assigned on a default VLAN */ + unsigned short default_priority; + +#endif + #ifdef CONFIG_NET_DIVERT /* this will get initialized at each interface type init routine */ struct divert_blk *divert; diff -u -r linux-2400test10p5/include/linux/skbuff.h linux-2400test10p5vl/include/linux/skbuff.h --- linux-2400test10p5/include/linux/skbuff.h Mon Oct 2 22:04:52 2000 +++ linux-2400test10p5vl/include/linux/skbuff.h Wed Oct 25 22:17:46 2000 @@ -146,6 +146,15 @@ #ifdef CONFIG_NET_SCHED __u32 tc_index; /* traffic control index */ #endif + +#if 0 && (defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE)) + /* I think this will help keep me from sending the packet + * back down the port that it arrived on. Kind of a special + * case in the egress rules. (Used when bridging only.) --BLG + * TODO: Make sure this is used or deleted. + */ + struct vlan_port* arriving_port; +#endif }; #define SK_WMEM_MAX 65535 diff -u -r linux-2400test10p5/net/8021q/Makefile linux-2400test10p5vl/net/8021q/Makefile --- linux-2400test10p5/net/8021q/Makefile Wed Oct 25 23:52:25 2000 +++ linux-2400test10p5vl/net/8021q/Makefile Wed Oct 25 21:47:18 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 := 8021q.o +O_OBJS = + +M_OBJS := $(O_TARGET) + +ifeq ($(CONFIG_NET),y) +O_OBJS = vlan.o vlanproc.o vlan_dev.o + +ifeq ($(CONFIG_SYSCTL),y) +O_OBJS += sysctl_net_vlan.o +endif +endif + +include $(TOPDIR)/Rules.make + +tar: + tar -cvf /dev/f1 . diff -u -r linux-2400test10p5/net/8021q/sysctl_net_vlan.c linux-2400test10p5vl/net/8021q/sysctl_net_vlan.c --- linux-2400test10p5/net/8021q/sysctl_net_vlan.c Wed Oct 25 23:52:25 2000 +++ linux-2400test10p5vl/net/8021q/sysctl_net_vlan.c Wed Oct 25 20:00:57 2000 @@ -0,0 +1,14 @@ +/* + * sysctl_net_vlan.c: sysctl interface to net Ethernet VLAN subsystem. + * + * Begun Dec 20, 1998, Ben Greear + * + * TODO: What, if anything, should this do?? + */ + +#include +#include + +ctl_table ether_vlan_table[] = { + {0} +}; diff -u -r linux-2400test10p5/net/8021q/vlan.c linux-2400test10p5vl/net/8021q/vlan.c --- linux-2400test10p5/net/8021q/vlan.c Wed Oct 25 23:52:25 2000 +++ linux-2400test10p5vl/net/8021q/vlan.c Wed Oct 25 22:56:20 2000 @@ -0,0 +1,456 @@ +/* + * 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 +#include "vlan.h" +#include "vlanproc.h" + + +extern int eth_header_parse(struct sk_buff *skb, unsigned char *haddr); /* eth.c */ + +/* 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 = 0; +static unsigned int vlan_release = 13; +static char vlan_copyright[] = " Ben Greear "; + + +/** These may be changed at run-time through IOCTLs */ +int vlan_configured_multiple_groups = 1; /* boolean */ +int vlan_name_type = 0; /* determines interface naming scheme */ + + +static struct packet_type vlan_packet_type = +{ + type: __constant_htons(ETH_P_802_1Q), + dev: NULL, + func: vlan_rx_skb, /* VLAN receive method */ + data: NULL, + next: NULL, +}; + +/* End of global variables definitions. */ + +/* + * Function vlan_proto_init () + * + * Initialize VLAN protocol layer, + * + */ +static void vlan_proto_init(void); +static void __init vlan_proto_init(void) { + + 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); +} + +/* + * Module 'remove' entry point. + * o delete /proc/net/router directory and static entries. + */ +static void vlan_cleanup_module (void) { + // TODO: Define this so modules work. Clean up ports. + // vlan_proto_cleanup(); + dev_remove_pack(&vlan_packet_type); + vlan_proc_cleanup(); +} + + +static int __init vlan_system_init(void) { + /* protocol initialization */ + vlan_proto_init(); + return 0; +} + +module_init(vlan_system_init); +module_exit(vlan_cleanup_module); + + + + +/** At one time, I was going to let each Physical device have + * it's own 4096 VLANs. The spec says I should do otherwise. + * However, for my own personal amusement, I'm going to give + * one the ability to configure this. The default will be to + * just return the first one (as per the spec). + */ +struct vlan_group* vlan_find_group(const char* mac) { + struct vlan_group* grp = NULL; + + if (vlan_configured_multiple_groups && mac) { + for (grp = p802_1Q_vlan_list; + ((grp != NULL) && (memcmp(mac, grp->real_mac, ETH_ALEN))); + grp = grp->next) { + ; + } /* for */ + + return grp; + } + else { + return p802_1Q_vlan_list; + } +} + +/* Find the protocol handler. Assumes VID < 0xFFF. + */ +struct net_device *find_802_1Q_vlan_dev(const char* real_mac_addr, + unsigned short VID) { + + struct vlan_group* grp = vlan_find_group(real_mac_addr); + + /* When here, we have found the correct group, if it exists. */ + + if (grp) { /* then we found one */ + return grp->vlan_devices[VID]; /* return the vlan device */ + }//if + + return NULL; +}/* find_802_1Q_vlan_client */ + + +/** This method will explicitly do a dev_put on the device if do_dev_put + * is TRUE. This gets around a difficulty with reference counting, and + * the unregister-by-name (below). --Ben + */ +int unregister_802_1Q_vlan_dev(const char* mac, unsigned short vlan_id, + int do_dev_put) { + struct vlan_group* grp; + struct net_device* dev = NULL; + +#ifdef VLAN_DEBUG + printk(VLAN_DBG __FUNCTION__ ": VID: %i\n", vlan_id); +#endif + + /* sanity check */ + if ((vlan_id >= 0xFFF) || (vlan_id <= 0)) { + return -EINVAL; + } + + grp = vlan_find_group(mac); + /* When here, we have found the correct group, if it exists. */ + + if (grp) { + dev = grp->vlan_devices[vlan_id]; + if (dev) { + + /* Remove proc entry */ + vlan_proc_rem_dev(dev); + + /* take it out of our own structures */ + grp->vlan_devices[vlan_id] = NULL; + + /* Take it out of the global list of devices. + * NOTE: This deletes dev, don't access it again!! + */ + + if (do_dev_put) { + dev_put(dev); + } + + rtnl_lock(); + unregister_netdevice(dev); + rtnl_unlock(); + + MOD_DEC_USE_COUNT; + }/* if */ + }/* if */ + + return 0; +}/* unregister vlan device */ + + + +int unregister_802_1Q_vlan_device(const char* vlan_IF_name) { + struct net_device* dev = NULL; + +#ifdef VLAN_DEBUG + printk(VLAN_DBG __FUNCTION__ ": unregister VLAN by name, name -:%s:-\n", + vlan_IF_name); +#endif + + dev = dev_get_by_name(vlan_IF_name); + + if (dev && dev->vlan_dev) { + return unregister_802_1Q_vlan_dev(dev->dev_addr, + (unsigned short)(dev->vlan_dev->vlan_id), + 1 /* do dev_put */); + } + else { +#ifdef VLAN_DEBUG + printk(VLAN_DBG __FUNCTION__ ": WARNING: Could not find dev\n"); +#endif + + if (dev) { + dev_put(dev); /* release it */ + } + return -EINVAL; + } +}/* unregister vlan device */ + + +/* + TODO: This for modules or something?? --BLG + + EXPORT_SYMBOL(register_802_1Q_vlan_device); + EXPORT_SYMBOL(unregister_802_1Q_vlan_device); + +*/ + +/* Attach a VLAN device to a mac address (ie Ethernet Card). + * Returns the device that was created, or NULL if there was + * an error of some kind. + */ +struct net_device *register_802_1Q_vlan_device(const char* eth_IF_name, + unsigned short VLAN_ID) { + struct vlan_group* grp; + struct net_device *new_dev; + struct net_device *real_dev; /* the ethernet device */ + char* mac; + int malloc_size = 0; + + MOD_INC_USE_COUNT; + +#ifdef VLAN_DEBUG + printk(VLAN_DBG __FUNCTION__ ": if_name -:%s:- vid: %i\n", + eth_IF_name, VLAN_ID); +#endif + + /* find the device relating to eth_IF_name. + * TODO: Make sure it's an ethernet device. */ + real_dev = dev_get_by_name(eth_IF_name); + + if (real_dev != NULL) { + /* printk(KERN_ALERT "Found real_dev"); */ + + if ((VLAN_ID > 0) && (VLAN_ID < 0xFFF)) { + + /* printk(KERN_ALERT "VID is in range"); */ + + mac = real_dev->dev_addr; + + if (find_802_1Q_vlan_dev(mac, VLAN_ID)) { + /* was already registered. */ + printk(VLAN_DBG __FUNCTION__ ": ALREADY had VLAN registered\n"); + dev_put(real_dev); + MOD_DEC_USE_COUNT; + return NULL; + } + + malloc_size = (sizeof(struct net_device)); + + new_dev = (struct net_device*) kmalloc(malloc_size, GFP_KERNEL); + VLAN_MEM_DBG("net_device malloc, addr: %p size: %i\n", new_dev, malloc_size); + + if (new_dev != NULL) { + /* printk(KERN_ALERT "Got a new device.."); */ + + memset(new_dev, 0, malloc_size); /* zero everything out */ + + /* set us up to not use a Qdisc, as the underlying Hardware device + * can do all the queueing we could want. + */ + { + extern struct Qdisc noqueue_qdisc; + + new_dev->qdisc_sleeping = &noqueue_qdisc; + } + /* TODO: Understand the implications of 'new_style'. */ + new_dev->new_style = 0; + + /* Gotta set up the fields for the device. */ + + 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 = ETH_HLEN+VLAN_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); + VLAN_FMEM_DBG("new_dev free, addr: %p\n", new_dev); + dev_put(real_dev); + MOD_DEC_USE_COUNT; + return NULL; + } + else { + memset(new_dev->vlan_dev, 0, sizeof(struct vlan_dev_info)); + new_dev->vlan_dev->vlan_id = VLAN_ID; /* 1 through 0xFFF */ + + /* TODO: have to be careful deleting real devices now. */ + new_dev->vlan_dev->real_dev = real_dev; + + new_dev->vlan_dev->dent = NULL; + } + + /* So, got the sucker initialized, now lets place it into our local + * structure. + */ + + grp = vlan_find_group(real_dev->dev_addr); + + /* When here, we have found the correct group, if it exists. */ + + if (!grp) { /* need to add a new group */ + /* printk(VLAN_DBG "VLAN REGISTER: " + "Need to add new vlan group.\n");*/ + + grp = kmalloc(sizeof(struct vlan_group), GFP_KERNEL); + VLAN_MEM_DBG("grp malloc, addr: %p size: %i\n", grp, sizeof(struct vlan_group)); + + if (grp) { + printk(KERN_ALERT "VLAN REGISTER: Allocated new group.\n"); + memset(grp, 0, sizeof(struct vlan_group)); + memcpy(grp->real_mac, real_dev->dev_addr, ETH_ALEN); + grp->next = p802_1Q_vlan_list; + p802_1Q_vlan_list = grp; + } + else { + kfree(new_dev->priv); + VLAN_FMEM_DBG("new_dev->priv free, addr: %p\n", new_dev->priv); + kfree(new_dev); + VLAN_FMEM_DBG("new_dev free, addr: %p\n", new_dev); + dev_put(real_dev); + MOD_DEC_USE_COUNT; + 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."); */ + + /* TODO: Please check this: RTNL --Ben */ + rtnl_lock(); + register_netdevice(new_dev); + rtnl_unlock(); + + vlan_proc_add_dev(new_dev); /* create it's proc entry */ + + /* NOTE: We have a reference to the real device, so hold on to the reference. + */ + /* dev_put(real_dev); */ + return new_dev; + } + }//if + /* If we are here, then something is wrong, so release the real_dev + */ + dev_put(real_dev); + }//if we found real_dev + + MOD_DEC_USE_COUNT; + return NULL; +}/* register (create) VLAN device */ diff -u -r linux-2400test10p5/net/8021q/vlan.h linux-2400test10p5vl/net/8021q/vlan.h --- linux-2400test10p5/net/8021q/vlan.h Wed Oct 25 23:52:25 2000 +++ linux-2400test10p5vl/net/8021q/vlan.h Wed Oct 25 22:21:07 2000 @@ -0,0 +1,41 @@ +#ifndef __BEN_VLAN_802_1Q_INC__ +#define __BEN_VLAN_802_1Q_INC__ + +#include + + +/* 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 int vlan_configured_multiple_groups; /* boolean */ +extern int vlan_name_type; + + +/* Add some headers for the public VLAN methods. */ +int unregister_802_1Q_vlan_device(const char* vlan_IF_name); +struct net_device *register_802_1Q_vlan_device(const char* eth_IF_name, + unsigned short VID); + +#endif diff -u -r linux-2400test10p5/net/8021q/vlan_dev.c linux-2400test10p5vl/net/8021q/vlan_dev.c --- linux-2400test10p5/net/8021q/vlan_dev.c Wed Oct 25 23:52:25 2000 +++ linux-2400test10p5vl/net/8021q/vlan_dev.c Wed Oct 25 21:42:57 2000 @@ -0,0 +1,752 @@ +/* + * 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 net_device* dev) { + return (struct net_device_stats*)(dev->priv); +} + + +/* + * Rebuild the Ethernet MAC header. This is called after an ARP + * (or in future other address resolution) has completed on this + * sk_buff. We now let ARP fill in the other fields. + * + * This routine CANNOT use cached dst->neigh! + * Really, it is used only when dst->neigh is wrong. + * + * TODO: This needs a checkup, I'm ignorant here. --BLG + */ +int vlan_dev_rebuild_header(struct sk_buff *skb) { + + struct net_device *dev = skb->dev; + struct vlan_ethhdr *veth = (struct vlan_ethhdr*)(skb->data); + + switch (veth->h_vlan_encapsulated_proto) + { +#ifdef CONFIG_INET + case __constant_htons(ETH_P_IP): + + /* TODO: Confirm this will work with VLAN headers... */ + return arp_find(veth->h_dest, skb); +#endif + default: + printk(VLAN_DBG + "%s: unable to resolve type %X addresses.\n", + dev->name, (int)veth->h_vlan_encapsulated_proto); + + memcpy(veth->h_source, dev->dev_addr, ETH_ALEN); + break; + }/* switch */ + + return 0; +}/* vlan_dev_rebuild_header */ + + +/* + * Determine the packet's protocol ID. The rule here is that we + * assume 802.3 if the type field is short enough to be a length. + * This is normal practice and works for any 'now in use' protocol. + * + * Also, at this point we assume that we ARE dealing exclusively with + * VLAN packets, or packets that should be made into VLAN packets based + * on a default VLAN ID. + * + * NOTE: Should be similar to ethernet/eth.c. + * + * SANITY NOTE: This method is called when a packet is moving up the stack + * towards userland. To get here, it would have already passed + * through the ethernet/eth.c eth_type_trans() method. + */ +int vlan_rx_skb(struct sk_buff *skb, struct net_device *dev, + struct packet_type* ptype) { + unsigned char* rawp = NULL; + struct vlan_ethhdr *veth = (struct vlan_ethhdr*)(skb->mac.ethernet); + unsigned short vid = 0; + unsigned int is_1q_skb = 1; + struct net_device_stats* stats; + unsigned short proto = ((unsigned short *)skb->data) [-1]; + + /* Do we have a VLAN packet? If not, then it was sent to us + * because the port had a default vlan ID. + */ + if (proto == __constant_htons(ETH_P_802_1Q)) { + skb_pull(skb, VLAN_HLEN); /* take off the VLAN header */ + vid = ((unsigned short)(ntohs(veth->h_vlan_TCI)) & 0xFFF); +#if 0 + } else if (proto == __constant_htons(ETH_P_802_3)) { + // 802.3 NSAP ?? +#endif + } else { /* Something else ??? */ + vid = dev->default_vid; + is_1q_skb = 0; /* FALSE */ + } + +#ifdef VLAN_DEBUG + printk(VLAN_DBG __FUNCTION__ ": skb: %p is_1Q_Protocol: %i vlan_id: %hx\n", + skb, is_1q_skb, vid); +#endif + + /* Ok, we will find the correct VLAN device, strip the header, + and then go on as usual. + */ + + /* we have 12 bits of vlan ID. */ + /* If it's NULL, we will tag it to be junked below */ + skb->dev = find_802_1Q_vlan_dev(dev->dev_addr, vid); + + if (!skb->dev) { +#ifdef VLAN_DEBUG + printk(VLAN_DBG __FUNCTION__ ": ERROR: No net_device for VID: %i\n", + (unsigned int)(vid)); +#endif + kfree_skb(skb); + return -1; + } + + /* Bump the rx counters for the VLAN device. */ + stats = (struct net_device_stats*)(skb->dev->priv); + + /** Ok, lets check to make sure the device (dev) we + * came in on is what this VLAN is attached to. + */ + if (dev != skb->dev->vlan_dev->real_dev) { +#ifdef VLAN_DEBUG + printk(VLAN_DBG __FUNCTION__ ": dropping skb: %p because came in on wrong device.\n", skb); +#endif + kfree_skb(skb); + stats->rx_errors++; + return -1; + } + + /* + * Deal with ingress priority mapping. + */ + if (is_1q_skb) { + skb->priority = skb->dev->vlan_dev->ingress_priority_map[(ntohs(veth->h_vlan_TCI) >> 13) & 0x7]; + } + else { + skb->priority = dev->default_priority; + } + +#ifdef VLAN_DEBUG + printk(VLAN_DBG __FUNCTION__ ": priority: %lu for TCI: %hu (hbo)\n", + (unsigned long)(skb->priority), ntohs(veth->h_vlan_TCI)); +#endif + + stats->rx_packets++; + stats->rx_bytes += skb->len; + + /* 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?? (Yes!) + */ + if (*(veth->h_dest) & 1) { + stats->multicast++; + if (memcmp(veth->h_dest, dev->broadcast, ETH_ALEN) == 0) + skb->pkt_type = PACKET_BROADCAST; + else + skb->pkt_type = PACKET_MULTICAST; + } + + /* + * This ALLMULTI check should be redundant by 1.4 + * so don't forget to remove it. + * + * Seems, you forgot to remove it. All silly devices + * seems to set IFF_PROMISC. + */ + + else if (dev->flags & (IFF_PROMISC/*|IFF_ALLMULTI*/)) { + if (memcmp(veth->h_dest, dev->dev_addr, ETH_ALEN) != 0) + skb->pkt_type = PACKET_OTHERHOST; + } + + if (!is_1q_skb) { + /* NOT a VLAN packet, which is still a valid receipt to a VLAN + * device, it will just use the default VID, assigned above. + */ + if (ntohs(veth->h_vlan_proto) >= 1536) { + + skb->protocol = veth->h_vlan_proto; + /* place it back on the queue to be handled by true layer 3 protocols. + */ + netif_rx(skb); + return 0; + } + } + else { + /* Was a VLAN packet, grab the encapsulated protocol, which the layer + * three protocols care about. + */ + if (ntohs(veth->h_vlan_encapsulated_proto) >= 1536) { + + skb->protocol = veth->h_vlan_encapsulated_proto; + /* place it back on the queue to be handled by true layer 3 protocols. + */ + + /* 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 - ETH_HLEN, skb->data - ETH_HLEN - 4, 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 (is_1q_skb && (skb->dev->vlan_dev->flags & 1)) { + /* Lifted from Gleb's VLAN code... */ + memmove(skb->data - (ETH_HLEN - 4), skb->data - 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 (is_1q_skb && (skb->dev->vlan_dev->flags & 1)) { + /* Lifted from Gleb's VLAN code... */ + memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - VLAN_HLEN, 12); + skb->mac.raw += VLAN_HLEN; + } + 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. + * + * NOTE: We support DIX framed 802.1Q frames carrying whatever, + * specifically we don't support 802.3 framed 802.1Q frames! + */ +int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, void *daddr, void *saddr, + unsigned len) { + char *pushptr; + 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 + + pushptr = skb_push(skb, VLAN_HLEN); + veth = (struct vlan_ethhdr*)(pushptr - ETH_HLEN); + + /* build the four bytes that make this a VLAN header. */ + + /* Now, construct the VLAN header 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); + + return eth_header(skb, dev, ETH_P_802_1Q, daddr, saddr, len); + +} /* vlan_hard_header, put on the VLAN hardware header */ + + +int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { + struct net_device_stats* stats = (struct net_device_stats*)(dev->priv); + + 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 net_device *dev, int new_mtu) { + /* TODO: gotta make sure the underlying layer can handle it, + * maybe an IFF_VLAN_CAPABLE flag for devices? + */ + + dev->mtu = new_mtu; + return new_mtu; +} + +int vlan_dev_open(struct net_device* dev) { + if (! dev->vlan_dev->real_dev->flags & IFF_UP) { + return -ENETDOWN; + } + + dev->flags |= IFF_UP; + return 0; +} + +int vlan_dev_stop(struct net_device* dev) { + dev->flags &= ~IFF_UP; + vlan_flush_mc_list(dev); + return 0; +} + +int vlan_dev_init(struct net_device* dev) { + /* TODO: figure this out, maybe do nothing?? */ + return 0; +} + +void vlan_dev_destruct(struct net_device* dev) { + + if (dev) { + if (dev->vlan_dev) { + dev_put(dev->vlan_dev->real_dev); /* release our reference */ + + if (dev->vlan_dev->dent) { + printk(KERN_ERR __FUNCTION__ ": dev->vlan_dev->dent is NOT NULL!\n"); + /* If we ever get here, there is a serious bug that must be fixed. */ + } + kfree(dev->vlan_dev); + VLAN_FMEM_DBG("dev->vlan_dev free, addr: %p\n", dev->vlan_dev); + dev->vlan_dev = NULL; + + } + + kfree(dev->priv); + VLAN_FMEM_DBG("dev->priv free, addr: %p\n", dev->priv); + dev->priv = NULL; + + kfree(dev); + VLAN_FMEM_DBG("net_device free, addr: %p\n", dev); + dev = NULL; + } + return; +} + + +/* TODO: Not to sure if the VLAN stuff works here. Need to understand + * this better. --BLG + */ +/* +int vlan_dev_header_cache(struct neighbour *neigh, struct hh_cache *hh) { + unsigned short type = hh->hh_type; + struct vlan_ethhdr *veth = (struct vlan_ethhdr*)(((u8*)hh->hh_data) + 2); + struct device *dev = neigh->dev; + + if (type == __constant_htons(ETH_P_802_3)) { + return -1; + } + + veth->h_vlan_proto = __constant_htons(ETH_P_802_1Q); + memcpy(veth->h_source, dev->dev_addr, ETH_ALEN); + memcpy(veth->h_dest, neigh->ha, ETH_ALEN); + + * VLAN specific attributes. * + veth->h_vlan_TCI = htons(dev->VLAN_id); * TODO: Add priority control (high 3 bits.) * + veth->h_vlan_encapsulated_proto = type; * should already be in network order * + + return 0; +} +*/ + +/* + * Called by Address Resolution module to notify changes in address. + */ +/* +void vlan_dev_header_cache_update(struct hh_cache *hh, struct device *dev, + unsigned char * haddr) { + memcpy(((u8*)hh->hh_data) + 2, haddr, VLAN_ETH_HLEN); +} +*/ + +#if 0 && !defined(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_dflt_vid(char* dev_name, unsigned short vid) { + struct net_device* dev = dev_get_by_name(dev_name); + int retval = -EINVAL; + if (dev) { + if (!dev->vlan_dev) { /* can't put a dflt ID on a vlan device */ + if ((vid > 0) && (vid <= 0xFFF)) { + dev->default_vid = (vid & 0xFFF); + retval = 0; + } + } + dev_put(dev); + } + return retval; +} + + +int vlan_dev_set_dflt_priority(char* dev_name, unsigned short priority) { + struct net_device* dev = dev_get_by_name(dev_name); + int retval = -EINVAL; + if (dev) { + if (!dev->vlan_dev) { /* can't put a dflt ID on a vlan device */ + dev->default_priority = priority; + retval = 0; + } + dev_put(dev); + } + return retval; +} + + +int vlan_dev_set_ingress_priority(char* dev_name, __u32 skb_prio, short vlan_prio) { + struct net_device* dev = dev_get_by_name(dev_name); + + if (dev) { + if (dev->vlan_dev) { + /* see if a priority mapping exists.. */ + dev->vlan_dev->ingress_priority_map[vlan_prio & 0x7] = skb_prio; + dev_put(dev); + return 0; + } + dev_put(dev); + } + return -EINVAL; +} + +int vlan_dev_set_egress_priority(char* dev_name, __u32 skb_prio, short vlan_prio) { + struct net_device* dev = dev_get_by_name(dev_name); + struct vlan_priority_tci_mapping* mp = NULL; + struct vlan_priority_tci_mapping* np; + + if (dev) { + if (dev->vlan_dev) { + /* 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); + dev_put(dev); + 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; + dev_put(dev); + return 0; + } + else { + dev_put(dev); + return -ENOBUFS; + } + } + dev_put(dev); + } + 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 net_device* dev = dev_get_by_name(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; + } + dev_put(dev); + return 0; + } + else { + dev_put(dev); + return -EINVAL; + } + } + dev_put(dev); + } + return -EINVAL; +} + + +int vlan_dev_set_mac_address(struct net_device *dev, void* addr) { + int i; + printk("%s: Setting MAC address to ", dev->name); + for (i = 0; i < 6; i++) + printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]); + printk(".\n"); + printk(" Make sure the underlying device is in promiscious mode!\n"); + + /* Copy the address. */ + memcpy(dev->dev_addr, addr, 6); + + return 0; +} + + +/** Taken from Gleb + Lennert's VLAN code, and modified... */ +void vlan_dev_set_multicast_list(struct net_device *vlan_dev) { + struct dev_mc_list *dmi; + struct net_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 net_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 linux-2400test10p5/net/8021q/vlanproc.c linux-2400test10p5vl/net/8021q/vlanproc.c --- linux-2400test10p5/net/8021q/vlanproc.c Wed Oct 25 23:52:25 2000 +++ linux-2400test10p5vl/net/8021q/vlanproc.c Wed Oct 25 22:59:49 2000 @@ -0,0 +1,621 @@ +/***************************************************************************** +* vlanproc.c VLAN Module. /proc filesystem interface. +* +* This module is completely hardware-independent and provides +* access to the router using Linux /proc filesystem. +* +* Author: Ben Greear, coppied from wanproc.c +* by: Gene Kozin +* +* Copyright: (c) 1998 Ben Greear +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version +* 2 of the License, or (at your option) any later version. +* ============================================================================ +* Jan 20, 1998 Ben Greear Initial Version +*****************************************************************************/ + +#include +#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 ssize_t vlan_proc_read(struct file* file, char* buf, size_t count, + loff_t *ppos); + +/* Methods for preparing data for reading proc entries */ + +static int vlan_config_get_info(char* buf, char** start, off_t offs, int len); +static int vlandev_get_info(char* buf, char** start, off_t offs, int len); + + +/* 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_proc_ioctl( + struct inode* inode, + struct file* file, + unsigned int cmd, + unsigned long arg +) { + int err = 0; + 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; + + 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_BIND_TYPE_IOCTL: + if (args.u.bind_type == VLAN_BIND_PER_KERNEL) { + vlan_configured_multiple_groups = 0; + } + else if (args.u.bind_type == VLAN_BIND_PER_INTERFACE) { + vlan_configured_multiple_groups = 1; + } + else { + err = -EINVAL; + } + err = 0; + 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; + printk(VLAN_INF "VLAN_SET_NAME_TYPE: new type: %i\n", (int)(args.u.name_type)); + err = 0; + } + else { + printk(VLAN_INF "VLAN_SET_NAME_TYPE, type: %i is invalid.\n", (int)(args.u.name_type)); + 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; + + case SET_DEFAULT_VLAN_ID_IOCTL: + err = vlan_dev_set_dflt_vid(args.dev1, args.u.VID); + break; + + case SET_DEFAULT_VLAN_PRIORITY_IOCTL: + err = vlan_dev_set_dflt_priority(args.dev1, args.u.VID); + break; + + default: + /* pass on to underlying device instead?? */ + printk(VLAN_DBG __FUNCTION__ ": Unknown VLAN IOCTL: %x \n", + cmd); + return -EINVAL; + } + + 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 = { + read: vlan_proc_read, + ioctl: vlan_proc_ioctl, +}; + +/* + * /proc/net/vlan/ file and inode operations + */ + +static struct file_operations vlandev_fops = { + read: vlan_proc_read, + ioctl: vlan_proc_ioctl, +}; + + +/* + * Proc filesystem derectory entries. + */ + +/* + * /proc/net/vlan + */ + +static struct proc_dir_entry* proc_vlan_dir = NULL; + +/* + * /proc/net/vlan/config + */ + +static struct proc_dir_entry* proc_vlan_conf = NULL; + + +/* Strings */ +static char conf_hdr[] = "VLAN Dev name | VLAN ID\n"; + + +/* + * Interface functions + */ + +/* + * Clean up /proc/net/vlan entries + */ + +void vlan_proc_cleanup (void) { + /* TODO: Take care of dynamically added entries?? + * Probably only a problem if we ever become a module though.. + */ + + if (proc_vlan_conf) remove_proc_entry(name_conf, proc_vlan_dir); + if (proc_vlan_dir) proc_net_remove(name_root); +} + +/* + * Create /proc/net/vlan entries + */ + +int __init vlan_proc_init (void) +{ + proc_vlan_dir = proc_mkdir(name_root, proc_net); + if (proc_vlan_dir) { + proc_vlan_conf = create_proc_entry(name_conf, + S_IFREG|S_IRUGO|S_IWUSR, + proc_vlan_dir); + if (proc_vlan_conf) { + proc_vlan_conf->proc_fops = &vlan_fops; + proc_vlan_conf->get_info = vlan_config_get_info; + return 0; + } + } + vlan_proc_cleanup(); + return -ENOBUFS; +} + +/* + * Add directory entry for VLAN device. + */ + +int vlan_proc_add_dev (struct net_device* vlandev) +{ + if (!vlandev->vlan_dev) { + printk(KERN_ERR + "ERROR: vlan_proc_add, device -:%s:- is NOT a VLAN\n", + vlandev->name); + return -EINVAL; + } + + vlandev->vlan_dev->dent = create_proc_entry(vlandev->name, + S_IFREG|S_IRUGO|S_IWUSR, + proc_vlan_dir); + if (!vlandev->vlan_dev->dent) { + return -ENOBUFS; + } + + vlandev->vlan_dev->dent->proc_fops = &vlandev_fops; + vlandev->vlan_dev->dent->get_info = &vlandev_get_info; + vlandev->vlan_dev->dent->data = vlandev; + +#ifdef VLAN_DEBUG + printk(KERN_ERR "vlan_proc_add, device -:%s:- being added.\n", + vlandev->name); +#endif + return 0; +} + + +/* + * Delete directory entry for VLAN device. + */ +int vlan_proc_rem_dev(struct net_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__ ": dev: %p\n", vlandev); +#endif + + /** NOTE: This will consume the memory pointed to by dent, it seems. */ + remove_proc_entry(vlandev->name, proc_vlan_dir); + vlandev->vlan_dev->dent = NULL; + + return 0; +} + + +/****** Proc filesystem entry points ****************************************/ + +/* + * Read VLAN proc directory entry. + * This is universal routine for reading all entries in /proc/net/vlan + * directory. Each directory entry contains a pointer to the 'method' for + * preparing data for that entry. + * o verify arguments + * o allocate kernel buffer + * o call get_info() to prepare data + * o copy data to user space + * o release kernel buffer + * + * Return: number of bytes copied to user space (0, if no data) + * <0 error + */ +static ssize_t vlan_proc_read( + struct file* file, + char* buf, + size_t count, + loff_t *ppos +) { + struct inode *inode = file->f_dentry->d_inode; + struct proc_dir_entry* dent; + char* page; + int pos, offs, len; + + if (count <= 0) + return 0; + + dent = inode->u.generic_ip; + if ((dent == NULL) || (dent->get_info == NULL)) + return 0; + + page = kmalloc(VLAN_PROC_BUFSZ, GFP_KERNEL); + VLAN_MEM_DBG("page malloc, addr: %p size: %i\n", + page, VLAN_PROC_BUFSZ); + + if (page == NULL) + return -ENOBUFS; + + pos = dent->get_info(page, dent->data, 0, 0); + offs = file->f_pos; + if (offs < pos) { + len = min(pos - offs, count); + if (copy_to_user(buf, (page + offs), len)) { + return -EFAULT; + } + file->f_pos += len; + } else { + len = 0; + } + + kfree(page); + VLAN_FMEM_DBG("page free, addr: %p\n", page); + return len; +}/* vlan_proc_read */ + +/* + * The following few functions build the content of /proc/net/vlan/config + */ + +static int vlan_proc_get_vlan_info( + char* buf, + unsigned int cnt +) { + struct net_device* vlandev = NULL; + struct vlan_group* grp = NULL; + int i = 0; + char* nm_type = NULL; + char* bind_type = NULL; + +#ifdef VLAN_DEBUG + printk(VLAN_DBG __FUNCTION__ ": cnt == %i\n", cnt); +#endif + + if (vlan_configured_multiple_groups) { + bind_type = "VLAN_BIND_PER_INTERFACE"; + } + else { + bind_type = "VLAN_BIND_PER_KERNEL"; + } + + 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 Bind-Type: %s\n", nm_type, bind_type); + + 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++) { + vlandev = grp->vlan_devices[i]; + if (!vlandev) + continue; + 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) { + cnt += sprintf(buf+cnt, "%s", term_msg); + } + return cnt; + } + if (!vlandev->vlan_dev) { + printk(KERN_ERR __FUNCTION__ + ": ERROR: vlandev->vlan_dev is NULL\n"); + continue; + } + 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); + } + } + return cnt; +} + +/* + * Prepare data for reading 'Config' entry. + * Return length of data. + */ + +static int vlan_config_get_info( + char* buf, + char** start, + off_t offs, + int len +) { + strcpy(buf, conf_hdr); + return vlan_proc_get_vlan_info(buf, (unsigned int)(strlen(conf_hdr))); +} + + +/* + * Prepare data for reading entry. + * Return length of data. + * + * On entry, the 'start' argument will contain a pointer to VLAN device + * data space. + */ + +static int vlandev_get_info( + char* buf, + char** start, + off_t offs, + int len +) { + struct net_device* vlandev = (void*)start; + struct net_device_stats* stats; + int cnt = 0; + struct vlan_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, "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]); + + 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 */ + + 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 + */ + +int __init vlan_proc_init (void) +{ + return 0; +} + +void vlan_proc_cleanup(void) +{ + return; +} + + +int vlan_proc_add_dev(struct net_device *vlandev) +{ + return 0; +} + +int vlan_proc_rem_dev(struct net_device *vlandev) +{ + return 0; +} + +#endif diff -u -r linux-2400test10p5/net/8021q/vlanproc.h linux-2400test10p5vl/net/8021q/vlanproc.h --- linux-2400test10p5/net/8021q/vlanproc.h Wed Oct 25 23:52:25 2000 +++ linux-2400test10p5vl/net/8021q/vlanproc.h Wed Oct 25 17:13:47 2000 @@ -0,0 +1,29 @@ + +#ifndef __BEN_VLAN_PROC_INC__ +#define __BEN_VLAN_PROC_INC__ + + +int vlan_proc_init(void); + +int vlan_proc_rem_dev(struct net_device* vlandev); +int vlan_proc_add_dev (struct net_device* vlandev); +int vlan_proc_rem_port(struct vlan_port* vlandev); +int vlan_proc_add_port (struct vlan_port* vlandev); +void vlan_proc_cleanup (void); + + +#define VLAN_PROC_BUFSZ (4096) /* buffer size for printing proc info */ + +/****** Data Types **********************************************************/ + +/* +typedef struct vlan_stat_entry { + struct vlan_stat_entry * next; + char *description; * description string * + void *data; * -> data * + unsigned data_type; * data type * +} vlan_stat_entry_t; +*/ + + +#endif diff -u -r linux-2400test10p5/net/Config.in linux-2400test10p5vl/net/Config.in --- linux-2400test10p5/net/Config.in Wed Oct 25 23:49:45 2000 +++ linux-2400test10p5vl/net/Config.in Wed Oct 25 19:44:47 2000 @@ -40,12 +40,16 @@ if [ "$CONFIG_ATM_CLIP" = "y" ]; then bool ' Do NOT send ICMP if no neighbour' CONFIG_ATM_CLIP_NO_ICMP fi + fi tristate ' LAN Emulation (LANE) support' CONFIG_ATM_LANE if [ "$CONFIG_INET" = "y" -a "$CONFIG_ATM_LANE" != "n" ]; then tristate ' Multi-Protocol Over ATM (MPOA) support' CONFIG_ATM_MPOA fi fi + + tristate '802.1Q VLAN Support (EXPERIMENTAL)' CONFIG_VLAN_802_1Q + fi comment ' ' diff -u -r linux-2400test10p5/net/Makefile linux-2400test10p5vl/net/Makefile --- linux-2400test10p5/net/Makefile Wed Oct 25 23:49:45 2000 +++ linux-2400test10p5vl/net/Makefile Wed Oct 25 21:45:55 2000 @@ -7,7 +7,7 @@ O_TARGET := network.o -mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda atm +mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda atm 8021q export-objs := netsyms.o subdir-y := core ethernet @@ -44,6 +44,7 @@ subdir-$(CONFIG_ATM) += atm subdir-$(CONFIG_DECNET) += decnet subdir-$(CONFIG_ECONET) += econet +subdir-$(CONFIG_VLAN_802_1Q) += 8021q obj-y := socket.o $(join $(subdir-y), $(patsubst %,/%.o,$(notdir $(subdir-y)))) diff -u -r linux-2400test10p5/net/core/dev.c linux-2400test10p5vl/net/core/dev.c --- linux-2400test10p5/net/core/dev.c Wed Oct 25 23:49:45 2000 +++ linux-2400test10p5vl/net/core/dev.c Wed Oct 25 19:46:26 2000 @@ -130,9 +130,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 @@ -171,6 +179,250 @@ #endif +#define BENS_FAST_DEV_LOOKUP +#ifdef BENS_FAST_DEV_LOOKUP +/* Flash 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 net_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 net_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 net_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 net_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 @@ -389,12 +641,32 @@ struct net_device *__dev_get_by_name(const char *name) { struct net_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) return dev; } return NULL; + } /** @@ -465,6 +737,20 @@ { struct net_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) return dev; @@ -542,15 +828,17 @@ /* * 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(buf,name,i); if (__dev_get_by_name(buf) == NULL) { strcpy(dev->name, buf); return i; } } - return -ENFILE; /* Over 100 of the things .. bail out! */ + return -ENFILE; /* Over 8192 of the things .. bail out! */ } /** @@ -2299,6 +2587,12 @@ } dev->next = NULL; write_lock_bh(&dev_base_lock); +#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; dev_hold(dev); write_unlock_bh(&dev_base_lock); @@ -2353,6 +2647,12 @@ dev->next = NULL; dev_init_scheduler(dev); write_lock_bh(&dev_base_lock); +#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; dev_hold(dev); dev->deadbeaf = 0; @@ -2424,7 +2724,10 @@ if (d == dev) { write_lock_bh(&dev_base_lock); *dp = d->next; - write_unlock_bh(&dev_base_lock); +#ifdef BENS_FAST_DEV_LOOKUP + __fdl_unregister_netdevice(dev); +#endif + write_unlock_bh(&dev_base_lock); break; } } diff -u -r linux-2400test10p5/net/ethernet/eth.c linux-2400test10p5vl/net/ethernet/eth.c --- linux-2400test10p5/net/ethernet/eth.c Tue Aug 22 18:59:00 2000 +++ linux-2400test10p5vl/net/ethernet/eth.c Wed Oct 25 22:42:25 2000 @@ -174,17 +174,22 @@ * 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_rx_skb() in + * 8021q/vlan.c if you change anything here. */ unsigned short eth_type_trans(struct sk_buff *skb, struct net_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; + + skb_pull(skb,dev->hard_header_len); + + eth= skb->mac.ethernet; + if(*eth->h_dest&1) { if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0) @@ -206,7 +211,17 @@ if(memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN)) skb->pkt_type=PACKET_OTHERHOST; } - + +#if defined(CONFIG_VLAN_802_1Q) || defined(CONFIG_VLAN_802_1Q_MODULE) + if ((eth->h_proto == __constant_htons(ETH_P_802_1Q)) || + (dev->default_vid > 0)) { + /* then we have to convert this into a VLAN looking packet. + * We'll wait to do that in the VLAN protocol handler. + */ + return __constant_htons(ETH_P_802_1Q); + } +#endif + if (ntohs(eth->h_proto) >= 1536) return eth->h_proto; diff -u -r linux-2400test10p5/net/netsyms.c linux-2400test10p5vl/net/netsyms.c --- linux-2400test10p5/net/netsyms.c Wed Oct 25 23:49:45 2000 +++ linux-2400test10p5vl/net/netsyms.c Wed Oct 25 22:07:22 2000 @@ -466,6 +466,8 @@ EXPORT_SYMBOL(netdev_finish_unregister); EXPORT_SYMBOL(netdev_set_master); EXPORT_SYMBOL(eth_type_trans); +EXPORT_SYMBOL(eth_header); +EXPORT_SYMBOL(eth_header_parse); #ifdef CONFIG_FDDI EXPORT_SYMBOL(fddi_type_trans); EXPORT_SYMBOL(fddi_setup); @@ -534,6 +536,8 @@ EXPORT_SYMBOL(qdisc_restart); EXPORT_SYMBOL(qdisc_create_dflt); EXPORT_SYMBOL(noop_qdisc); +extern struct Qdisc noqueue_qdisc; +EXPORT_SYMBOL(noqueue_qdisc); EXPORT_SYMBOL(qdisc_tree_lock); #ifdef CONFIG_NET_SCHED PSCHED_EXPORTLIST;