diff options
Diffstat (limited to 'net/802')
-rw-r--r-- | net/802/Makefile | 15 | ||||
-rw-r--r-- | net/802/fc.c | 130 | ||||
-rw-r--r-- | net/802/fddi.c | 210 | ||||
-rw-r--r-- | net/802/hippi.c | 234 | ||||
-rw-r--r-- | net/802/p8022.c | 65 | ||||
-rw-r--r-- | net/802/p8023.c | 61 | ||||
-rw-r--r-- | net/802/psnap.c | 159 | ||||
-rw-r--r-- | net/802/sysctl_net_802.c | 33 | ||||
-rw-r--r-- | net/802/tr.c | 645 |
9 files changed, 1552 insertions, 0 deletions
diff --git a/net/802/Makefile b/net/802/Makefile new file mode 100644 index 0000000..0186192 --- /dev/null +++ b/net/802/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for the Linux 802.x protocol layers. +# + +obj-y := p8023.o + +# Check the p8022 selections against net/core/Makefile. +obj-$(CONFIG_SYSCTL) += sysctl_net_802.o +obj-$(CONFIG_LLC) += p8022.o psnap.o +obj-$(CONFIG_TR) += p8022.o psnap.o tr.o sysctl_net_802.o +obj-$(CONFIG_NET_FC) += fc.o +obj-$(CONFIG_FDDI) += fddi.o +obj-$(CONFIG_HIPPI) += hippi.o +obj-$(CONFIG_IPX) += p8022.o psnap.o +obj-$(CONFIG_ATALK) += p8022.o psnap.o diff --git a/net/802/fc.c b/net/802/fc.c new file mode 100644 index 0000000..640d34e --- /dev/null +++ b/net/802/fc.c @@ -0,0 +1,130 @@ +/* + * NET3: Fibre Channel device handling subroutines + * + * 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. + * + * Vineet Abraham <vma@iol.unh.edu> + * v 1.0 03/22/99 + */ + +#include <linux/config.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include <linux/fcdevice.h> +#include <linux/skbuff.h> +#include <linux/errno.h> +#include <linux/timer.h> +#include <linux/net.h> +#include <linux/proc_fs.h> +#include <linux/init.h> +#include <net/arp.h> + +/* + * Put the headers on a Fibre Channel packet. + */ + +static int fc_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, + void *daddr, void *saddr, unsigned len) +{ + struct fch_hdr *fch; + int hdr_len; + + /* + * Add the 802.2 SNAP header if IP as the IPv4 code calls + * dev->hard_header directly. + */ + if (type == ETH_P_IP || type == ETH_P_ARP) + { + struct fcllc *fcllc; + + hdr_len = sizeof(struct fch_hdr) + sizeof(struct fcllc); + fch = (struct fch_hdr *)skb_push(skb, hdr_len); + fcllc = (struct fcllc *)(fch+1); + fcllc->dsap = fcllc->ssap = EXTENDED_SAP; + fcllc->llc = UI_CMD; + fcllc->protid[0] = fcllc->protid[1] = fcllc->protid[2] = 0x00; + fcllc->ethertype = htons(type); + } + else + { + hdr_len = sizeof(struct fch_hdr); + fch = (struct fch_hdr *)skb_push(skb, hdr_len); + } + + if(saddr) + memcpy(fch->saddr,saddr,dev->addr_len); + else + memcpy(fch->saddr,dev->dev_addr,dev->addr_len); + + if(daddr) + { + memcpy(fch->daddr,daddr,dev->addr_len); + return(hdr_len); + } + return -hdr_len; +} + +/* + * A neighbour discovery of some species (eg arp) has completed. We + * can now send the packet. + */ + +static int fc_rebuild_header(struct sk_buff *skb) +{ + struct fch_hdr *fch=(struct fch_hdr *)skb->data; + struct fcllc *fcllc=(struct fcllc *)(skb->data+sizeof(struct fch_hdr)); + if(fcllc->ethertype != htons(ETH_P_IP)) { + printk("fc_rebuild_header: Don't know how to resolve type %04X addresses ?\n",(unsigned int)htons(fcllc->ethertype)); + return 0; + } +#ifdef CONFIG_INET + return arp_find(fch->daddr, skb); +#else + return 0; +#endif +} + +static void fc_setup(struct net_device *dev) +{ + dev->hard_header = fc_header; + dev->rebuild_header = fc_rebuild_header; + + dev->type = ARPHRD_IEEE802; + dev->hard_header_len = FC_HLEN; + dev->mtu = 2024; + dev->addr_len = FC_ALEN; + dev->tx_queue_len = 100; /* Long queues on fc */ + dev->flags = IFF_BROADCAST; + + memset(dev->broadcast, 0xFF, FC_ALEN); +} + +/** + * alloc_fcdev - Register fibre channel device + * @sizeof_priv: Size of additional driver-private structure to be allocated + * for this fibre channel device + * + * Fill in the fields of the device structure with fibre channel-generic values. + * + * Constructs a new net device, complete with a private data area of + * size @sizeof_priv. A 32-byte (not bit) alignment is enforced for + * this private data area. + */ +struct net_device *alloc_fcdev(int sizeof_priv) +{ + return alloc_netdev(sizeof_priv, "fc%d", fc_setup); +} +EXPORT_SYMBOL(alloc_fcdev); diff --git a/net/802/fddi.c b/net/802/fddi.c new file mode 100644 index 0000000..f9a31a9 --- /dev/null +++ b/net/802/fddi.c @@ -0,0 +1,210 @@ +/* + * 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. + * + * FDDI-type device handling. + * + * Version: @(#)fddi.c 1.0.0 08/12/96 + * + * Authors: Lawrence V. Stefani, <stefani@lkg.dec.com> + * + * fddi.c is based on previous eth.c and tr.c work by + * Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Mark Evans, <evansmp@uhura.aston.ac.uk> + * Florian La Roche, <rzsfl@rz.uni-sb.de> + * Alan Cox, <gw4pts@gw4pts.ampr.org> + * + * 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. + * + * Changes + * Alan Cox : New arp/rebuild header + * Maciej W. Rozycki : IPv6 support + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <asm/system.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include <linux/fddidevice.h> +#include <linux/if_ether.h> +#include <linux/skbuff.h> +#include <linux/errno.h> +#include <net/arp.h> +#include <net/sock.h> + +/* + * Create the FDDI MAC header for an arbitrary protocol layer + * + * saddr=NULL means use device source address + * daddr=NULL means leave destination address (eg unresolved arp) + */ + +static int fddi_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, + void *daddr, void *saddr, unsigned len) +{ + int hl = FDDI_K_SNAP_HLEN; + struct fddihdr *fddi; + + if(type != ETH_P_IP && type != ETH_P_IPV6 && type != ETH_P_ARP) + hl=FDDI_K_8022_HLEN-3; + fddi = (struct fddihdr *)skb_push(skb, hl); + fddi->fc = FDDI_FC_K_ASYNC_LLC_DEF; + if(type == ETH_P_IP || type == ETH_P_IPV6 || type == ETH_P_ARP) + { + fddi->hdr.llc_snap.dsap = FDDI_EXTENDED_SAP; + fddi->hdr.llc_snap.ssap = FDDI_EXTENDED_SAP; + fddi->hdr.llc_snap.ctrl = FDDI_UI_CMD; + fddi->hdr.llc_snap.oui[0] = 0x00; + fddi->hdr.llc_snap.oui[1] = 0x00; + fddi->hdr.llc_snap.oui[2] = 0x00; + fddi->hdr.llc_snap.ethertype = htons(type); + } + + /* Set the source and destination hardware addresses */ + + if (saddr != NULL) + memcpy(fddi->saddr, saddr, dev->addr_len); + else + memcpy(fddi->saddr, dev->dev_addr, dev->addr_len); + + if (daddr != NULL) + { + memcpy(fddi->daddr, daddr, dev->addr_len); + return(hl); + } + + return(-hl); +} + + +/* + * Rebuild the FDDI 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. + */ + +static int fddi_rebuild_header(struct sk_buff *skb) +{ + struct fddihdr *fddi = (struct fddihdr *)skb->data; + +#ifdef CONFIG_INET + if (fddi->hdr.llc_snap.ethertype == __constant_htons(ETH_P_IP)) + /* Try to get ARP to resolve the header and fill destination address */ + return arp_find(fddi->daddr, skb); + else +#endif + { + printk("%s: Don't know how to resolve type %02X addresses.\n", + skb->dev->name, htons(fddi->hdr.llc_snap.ethertype)); + return(0); + } +} + + +/* + * Determine the packet's protocol ID and fill in skb fields. + * This routine is called before an incoming packet is passed + * up. It's used to fill in specific skb fields and to set + * the proper pointer to the start of packet data (skb->data). + */ + +unsigned short fddi_type_trans(struct sk_buff *skb, struct net_device *dev) +{ + struct fddihdr *fddi = (struct fddihdr *)skb->data; + unsigned short type; + + /* + * Set mac.raw field to point to FC byte, set data field to point + * to start of packet data. Assume 802.2 SNAP frames for now. + */ + + skb->mac.raw = skb->data; /* point to frame control (FC) */ + + if(fddi->hdr.llc_8022_1.dsap==0xe0) + { + skb_pull(skb, FDDI_K_8022_HLEN-3); + type = __constant_htons(ETH_P_802_2); + } + else + { + skb_pull(skb, FDDI_K_SNAP_HLEN); /* adjust for 21 byte header */ + type=fddi->hdr.llc_snap.ethertype; + } + + /* Set packet type based on destination address and flag settings */ + + if (*fddi->daddr & 0x01) + { + if (memcmp(fddi->daddr, dev->broadcast, FDDI_K_ALEN) == 0) + skb->pkt_type = PACKET_BROADCAST; + else + skb->pkt_type = PACKET_MULTICAST; + } + + else if (dev->flags & IFF_PROMISC) + { + if (memcmp(fddi->daddr, dev->dev_addr, FDDI_K_ALEN)) + skb->pkt_type = PACKET_OTHERHOST; + } + + /* Assume 802.2 SNAP frames, for now */ + + return(type); +} + +EXPORT_SYMBOL(fddi_type_trans); + +static int fddi_change_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < FDDI_K_SNAP_HLEN) || (new_mtu > FDDI_K_SNAP_DLEN)) + return(-EINVAL); + dev->mtu = new_mtu; + return(0); +} + +static void fddi_setup(struct net_device *dev) +{ + dev->change_mtu = fddi_change_mtu; + dev->hard_header = fddi_header; + dev->rebuild_header = fddi_rebuild_header; + + dev->type = ARPHRD_FDDI; + dev->hard_header_len = FDDI_K_SNAP_HLEN+3; /* Assume 802.2 SNAP hdr len + 3 pad bytes */ + dev->mtu = FDDI_K_SNAP_DLEN; /* Assume max payload of 802.2 SNAP frame */ + dev->addr_len = FDDI_K_ALEN; + dev->tx_queue_len = 100; /* Long queues on FDDI */ + dev->flags = IFF_BROADCAST | IFF_MULTICAST; + + memset(dev->broadcast, 0xFF, FDDI_K_ALEN); +} + +/** + * alloc_fddidev - Register FDDI device + * @sizeof_priv: Size of additional driver-private structure to be allocated + * for this FDDI device + * + * Fill in the fields of the device structure with FDDI-generic values. + * + * Constructs a new net device, complete with a private data area of + * size @sizeof_priv. A 32-byte (not bit) alignment is enforced for + * this private data area. + */ +struct net_device *alloc_fddidev(int sizeof_priv) +{ + return alloc_netdev(sizeof_priv, "fddi%d", fddi_setup); +} +EXPORT_SYMBOL(alloc_fddidev); diff --git a/net/802/hippi.c b/net/802/hippi.c new file mode 100644 index 0000000..4eb135c --- /dev/null +++ b/net/802/hippi.c @@ -0,0 +1,234 @@ +/* + * 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. + * + * HIPPI-type device handling. + * + * Version: @(#)hippi.c 1.0.0 05/29/97 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Mark Evans, <evansmp@uhura.aston.ac.uk> + * Florian La Roche, <rzsfl@rz.uni-sb.de> + * Alan Cox, <gw4pts@gw4pts.ampr.org> + * Jes Sorensen, <Jes.Sorensen@cern.ch> + * + * 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 <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include <linux/hippidevice.h> +#include <linux/skbuff.h> +#include <linux/errno.h> +#include <net/arp.h> +#include <net/sock.h> +#include <asm/uaccess.h> +#include <asm/checksum.h> +#include <asm/system.h> + +/* + * Create the HIPPI MAC header for an arbitrary protocol layer + * + * saddr=NULL means use device source address + * daddr=NULL means leave destination address (eg unresolved arp) + */ + +static int hippi_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, void *daddr, void *saddr, + unsigned len) +{ + struct hippi_hdr *hip = (struct hippi_hdr *)skb_push(skb, HIPPI_HLEN); + + if (!len){ + len = skb->len - HIPPI_HLEN; + printk("hippi_header(): length not supplied\n"); + } + + /* + * Due to the stupidity of the little endian byte-order we + * have to set the fp field this way. + */ + hip->fp.fixed = __constant_htonl(0x04800018); + hip->fp.d2_size = htonl(len + 8); + hip->le.fc = 0; + hip->le.double_wide = 0; /* only HIPPI 800 for the time being */ + hip->le.message_type = 0; /* Data PDU */ + + hip->le.dest_addr_type = 2; /* 12 bit SC address */ + hip->le.src_addr_type = 2; /* 12 bit SC address */ + + memcpy(hip->le.src_switch_addr, dev->dev_addr + 3, 3); + memset(&hip->le.reserved, 0, 16); + + hip->snap.dsap = HIPPI_EXTENDED_SAP; + hip->snap.ssap = HIPPI_EXTENDED_SAP; + hip->snap.ctrl = HIPPI_UI_CMD; + hip->snap.oui[0] = 0x00; + hip->snap.oui[1] = 0x00; + hip->snap.oui[2] = 0x00; + hip->snap.ethertype = htons(type); + + if (daddr) + { + memcpy(hip->le.dest_switch_addr, daddr + 3, 3); + memcpy(&skb->private.ifield, daddr + 2, 4); + return HIPPI_HLEN; + } + return -((int)HIPPI_HLEN); +} + + +/* + * Rebuild the HIPPI MAC header. This is called after an ARP has + * completed on this sk_buff. We now let ARP fill in the other fields. + */ + +static int hippi_rebuild_header(struct sk_buff *skb) +{ + struct hippi_hdr *hip = (struct hippi_hdr *)skb->data; + + /* + * Only IP is currently supported + */ + + if(hip->snap.ethertype != __constant_htons(ETH_P_IP)) + { + printk(KERN_DEBUG "%s: unable to resolve type %X addresses.\n",skb->dev->name,ntohs(hip->snap.ethertype)); + return 0; + } + + /* + * We don't support dynamic ARP on HIPPI, but we use the ARP + * static ARP tables to hold the I-FIELDs. + */ + return arp_find(hip->le.daddr, skb); +} + + +/* + * Determine the packet's protocol ID. + */ + +unsigned short hippi_type_trans(struct sk_buff *skb, struct net_device *dev) +{ + struct hippi_hdr *hip; + + hip = (struct hippi_hdr *) skb->data; + + /* + * This is actually wrong ... question is if we really should + * set the raw address here. + */ + skb->mac.raw = skb->data; + skb_pull(skb, HIPPI_HLEN); + + /* + * No fancy promisc stuff here now. + */ + + return hip->snap.ethertype; +} + +EXPORT_SYMBOL(hippi_type_trans); + +static int hippi_change_mtu(struct net_device *dev, int new_mtu) +{ + /* + * HIPPI's got these nice large MTUs. + */ + if ((new_mtu < 68) || (new_mtu > 65280)) + return -EINVAL; + dev->mtu = new_mtu; + return(0); +} + +/* + * For HIPPI we will actually use the lower 4 bytes of the hardware + * address as the I-FIELD rather than the actual hardware address. + */ +static int hippi_mac_addr(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + if (netif_running(dev)) + return -EBUSY; + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + return 0; +} + +static int hippi_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p) +{ + /* Never send broadcast/multicast ARP messages */ + p->mcast_probes = 0; + + /* In IPv6 unicast probes are valid even on NBMA, + * because they are encapsulated in normal IPv6 protocol. + * Should be a generic flag. + */ + if (p->tbl->family != AF_INET6) + p->ucast_probes = 0; + return 0; +} + +static void hippi_setup(struct net_device *dev) +{ + dev->set_multicast_list = NULL; + dev->change_mtu = hippi_change_mtu; + dev->hard_header = hippi_header; + dev->rebuild_header = hippi_rebuild_header; + dev->set_mac_address = hippi_mac_addr; + dev->hard_header_parse = NULL; + dev->hard_header_cache = NULL; + dev->header_cache_update = NULL; + dev->neigh_setup = hippi_neigh_setup_dev; + + /* + * We don't support HIPPI `ARP' for the time being, and probably + * never will unless someone else implements it. However we + * still need a fake ARPHRD to make ifconfig and friends play ball. + */ + dev->type = ARPHRD_HIPPI; + dev->hard_header_len = HIPPI_HLEN; + dev->mtu = 65280; + dev->addr_len = HIPPI_ALEN; + dev->tx_queue_len = 25 /* 5 */; + memset(dev->broadcast, 0xFF, HIPPI_ALEN); + + + /* + * HIPPI doesn't support broadcast+multicast and we only use + * static ARP tables. ARP is disabled by hippi_neigh_setup_dev. + */ + dev->flags = 0; +} + +/** + * alloc_hippi_dev - Register HIPPI device + * @sizeof_priv: Size of additional driver-private structure to be allocated + * for this HIPPI device + * + * Fill in the fields of the device structure with HIPPI-generic values. + * + * Constructs a new net device, complete with a private data area of + * size @sizeof_priv. A 32-byte (not bit) alignment is enforced for + * this private data area. + */ + +struct net_device *alloc_hippi_dev(int sizeof_priv) +{ + return alloc_netdev(sizeof_priv, "hip%d", hippi_setup); +} + +EXPORT_SYMBOL(alloc_hippi_dev); diff --git a/net/802/p8022.c b/net/802/p8022.c new file mode 100644 index 0000000..5ae6341 --- /dev/null +++ b/net/802/p8022.c @@ -0,0 +1,65 @@ +/* + * NET3: Support for 802.2 demultiplexing off Ethernet (Token ring + * is kept separate see p8022tr.c) + * 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. + * + * Demultiplex 802.2 encoded protocols. We match the entry by the + * SSAP/DSAP pair and then deliver to the registered datalink that + * matches. The control byte is ignored and handling of such items + * is up to the routine passed the frame. + * + * Unlike the 802.3 datalink we have a list of 802.2 entries as + * there are multiple protocols to demux. The list is currently + * short (3 or 4 entries at most). The current demux assumes this. + */ +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <net/datalink.h> +#include <linux/mm.h> +#include <linux/in.h> +#include <linux/init.h> +#include <net/llc.h> +#include <net/p8022.h> + +static int p8022_request(struct datalink_proto *dl, struct sk_buff *skb, + unsigned char *dest) +{ + llc_build_and_send_ui_pkt(dl->sap, skb, dest, dl->sap->laddr.lsap); + return 0; +} + +struct datalink_proto *register_8022_client(unsigned char type, + int (*func)(struct sk_buff *skb, + struct net_device *dev, + struct packet_type *pt)) +{ + struct datalink_proto *proto; + + proto = kmalloc(sizeof(*proto), GFP_ATOMIC); + if (proto) { + proto->type[0] = type; + proto->header_length = 3; + proto->request = p8022_request; + proto->sap = llc_sap_open(type, func); + if (!proto->sap) { + kfree(proto); + proto = NULL; + } + } + return proto; +} + +void unregister_8022_client(struct datalink_proto *proto) +{ + llc_sap_close(proto->sap); + kfree(proto); +} + +EXPORT_SYMBOL(register_8022_client); +EXPORT_SYMBOL(unregister_8022_client); + +MODULE_LICENSE("GPL"); diff --git a/net/802/p8023.c b/net/802/p8023.c new file mode 100644 index 0000000..a0b61b4 --- /dev/null +++ b/net/802/p8023.c @@ -0,0 +1,61 @@ +/* + * NET3: 802.3 data link hooks used for IPX 802.3 + * + * 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. + * + * 802.3 isn't really a protocol data link layer. Some old IPX stuff + * uses it however. Note that there is only one 802.3 protocol layer + * in the system. We don't currently support different protocols + * running raw 802.3 on different devices. Thankfully nobody else + * has done anything like the old IPX. + */ + +#include <linux/in.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/skbuff.h> + +#include <net/datalink.h> + +/* + * Place an 802.3 header on a packet. The driver will do the mac + * addresses, we just need to give it the buffer length. + */ +static int p8023_request(struct datalink_proto *dl, + struct sk_buff *skb, unsigned char *dest_node) +{ + struct net_device *dev = skb->dev; + + dev->hard_header(skb, dev, ETH_P_802_3, dest_node, NULL, skb->len); + return dev_queue_xmit(skb); +} + +/* + * Create an 802.3 client. Note there can be only one 802.3 client + */ +struct datalink_proto *make_8023_client(void) +{ + struct datalink_proto *proto = kmalloc(sizeof(*proto), GFP_ATOMIC); + + if (proto) { + proto->header_length = 0; + proto->request = p8023_request; + } + return proto; +} + +/* + * Destroy the 802.3 client. + */ +void destroy_8023_client(struct datalink_proto *dl) +{ + if (dl) + kfree(dl); +} + +EXPORT_SYMBOL(destroy_8023_client); +EXPORT_SYMBOL(make_8023_client); diff --git a/net/802/psnap.c b/net/802/psnap.c new file mode 100644 index 0000000..1053821 --- /dev/null +++ b/net/802/psnap.c @@ -0,0 +1,159 @@ +/* + * SNAP data link layer. Derived from 802.2 + * + * Alan Cox <Alan.Cox@linux.org>, + * from the 802.2 layer by Greg Page. + * Merged in additions from Greg Page's psnap.c. + * + * 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 <linux/module.h> +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <net/datalink.h> +#include <net/llc.h> +#include <net/psnap.h> +#include <linux/mm.h> +#include <linux/in.h> +#include <linux/init.h> + +static LIST_HEAD(snap_list); +static DEFINE_SPINLOCK(snap_lock); +static struct llc_sap *snap_sap; + +/* + * Find a snap client by matching the 5 bytes. + */ +static struct datalink_proto *find_snap_client(unsigned char *desc) +{ + struct list_head *entry; + struct datalink_proto *proto = NULL, *p; + + list_for_each_rcu(entry, &snap_list) { + p = list_entry(entry, struct datalink_proto, node); + if (!memcmp(p->type, desc, 5)) { + proto = p; + break; + } + } + return proto; +} + +/* + * A SNAP packet has arrived + */ +static int snap_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt) +{ + int rc = 1; + struct datalink_proto *proto; + static struct packet_type snap_packet_type = { + .type = __constant_htons(ETH_P_SNAP), + }; + + rcu_read_lock(); + proto = find_snap_client(skb->h.raw); + if (proto) { + /* Pass the frame on. */ + skb->h.raw += 5; + skb_pull(skb, 5); + rc = proto->rcvfunc(skb, dev, &snap_packet_type); + } else { + skb->sk = NULL; + kfree_skb(skb); + rc = 1; + } + + rcu_read_unlock(); + return rc; +} + +/* + * Put a SNAP header on a frame and pass to 802.2 + */ +static int snap_request(struct datalink_proto *dl, + struct sk_buff *skb, u8 *dest) +{ + memcpy(skb_push(skb, 5), dl->type, 5); + llc_build_and_send_ui_pkt(snap_sap, skb, dest, snap_sap->laddr.lsap); + return 0; +} + +/* + * Set up the SNAP layer + */ +EXPORT_SYMBOL(register_snap_client); +EXPORT_SYMBOL(unregister_snap_client); + +static char snap_err_msg[] __initdata = + KERN_CRIT "SNAP - unable to register with 802.2\n"; + +static int __init snap_init(void) +{ + snap_sap = llc_sap_open(0xAA, snap_rcv); + + if (!snap_sap) + printk(snap_err_msg); + + return 0; +} + +module_init(snap_init); + +static void __exit snap_exit(void) +{ + llc_sap_close(snap_sap); +} + +module_exit(snap_exit); + + +/* + * Register SNAP clients. We don't yet use this for IP. + */ +struct datalink_proto *register_snap_client(unsigned char *desc, + int (*rcvfunc)(struct sk_buff *, + struct net_device *, + struct packet_type *)) +{ + struct datalink_proto *proto = NULL; + + spin_lock_bh(&snap_lock); + + if (find_snap_client(desc)) + goto out; + + proto = kmalloc(sizeof(*proto), GFP_ATOMIC); + if (proto) { + memcpy(proto->type, desc,5); + proto->rcvfunc = rcvfunc; + proto->header_length = 5 + 3; /* snap + 802.2 */ + proto->request = snap_request; + list_add_rcu(&proto->node, &snap_list); + } +out: + spin_unlock_bh(&snap_lock); + + synchronize_net(); + return proto; +} + +/* + * Unregister SNAP clients. Protocols no longer want to play with us ... + */ +void unregister_snap_client(struct datalink_proto *proto) +{ + spin_lock_bh(&snap_lock); + list_del_rcu(&proto->node); + spin_unlock_bh(&snap_lock); + + synchronize_net(); + + kfree(proto); +} + +MODULE_LICENSE("GPL"); diff --git a/net/802/sysctl_net_802.c b/net/802/sysctl_net_802.c new file mode 100644 index 0000000..3607963 --- /dev/null +++ b/net/802/sysctl_net_802.c @@ -0,0 +1,33 @@ +/* -*- linux-c -*- + * sysctl_net_802.c: sysctl interface to net 802 subsystem. + * + * Begun April 1, 1996, Mike Shaver. + * Added /proc/sys/net/802 directory entry (empty =) ). [MS] + * + * 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 <linux/mm.h> +#include <linux/sysctl.h> +#include <linux/config.h> + +#ifdef CONFIG_TR +extern int sysctl_tr_rif_timeout; +#endif + +struct ctl_table tr_table[] = { +#ifdef CONFIG_TR + { + .ctl_name = NET_TR_RIF_TIMEOUT, + .procname = "rif_timeout", + .data = &sysctl_tr_rif_timeout, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, +#endif /* CONFIG_TR */ + { 0 }, +}; diff --git a/net/802/tr.c b/net/802/tr.c new file mode 100644 index 0000000..85293cc --- /dev/null +++ b/net/802/tr.c @@ -0,0 +1,645 @@ +/* + * NET3: Token ring device handling subroutines + * + * 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. + * + * Fixes: 3 Feb 97 Paul Norton <pnorton@cts.com> Minor routing fixes. + * Added rif table to /proc/net/tr_rif and rif timeout to + * /proc/sys/net/token-ring/rif_timeout. + * 22 Jun 98 Paul Norton <p.norton@computer.org> Rearranged + * tr_header and tr_type_trans to handle passing IPX SNAP and + * 802.2 through the correct layers. Eliminated tr_reformat. + * + */ + +#include <asm/uaccess.h> +#include <asm/system.h> +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/jiffies.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/socket.h> +#include <linux/in.h> +#include <linux/inet.h> +#include <linux/netdevice.h> +#include <linux/trdevice.h> +#include <linux/skbuff.h> +#include <linux/errno.h> +#include <linux/timer.h> +#include <linux/net.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/init.h> +#include <net/arp.h> + +static void tr_add_rif_info(struct trh_hdr *trh, struct net_device *dev); +static void rif_check_expire(unsigned long dummy); + +#define TR_SR_DEBUG 0 + +/* + * Each RIF entry we learn is kept this way + */ + +struct rif_cache_s { + unsigned char addr[TR_ALEN]; + int iface; + __u16 rcf; + __u16 rseg[8]; + struct rif_cache_s *next; + unsigned long last_used; + unsigned char local_ring; +}; + +#define RIF_TABLE_SIZE 32 + +/* + * We hash the RIF cache 32 ways. We do after all have to look it + * up a lot. + */ + +static struct rif_cache_s *rif_table[RIF_TABLE_SIZE]; + +static DEFINE_SPINLOCK(rif_lock); + + +/* + * Garbage disposal timer. + */ + +static struct timer_list rif_timer; + +int sysctl_tr_rif_timeout = 60*10*HZ; + +static inline unsigned long rif_hash(const unsigned char *addr) +{ + unsigned long x; + + x = addr[0]; + x = (x << 2) ^ addr[1]; + x = (x << 2) ^ addr[2]; + x = (x << 2) ^ addr[3]; + x = (x << 2) ^ addr[4]; + x = (x << 2) ^ addr[5]; + + x ^= x >> 8; + + return x & (RIF_TABLE_SIZE - 1); +} + +/* + * Put the headers on a token ring packet. Token ring source routing + * makes this a little more exciting than on ethernet. + */ + +static int tr_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, + void *daddr, void *saddr, unsigned len) +{ + struct trh_hdr *trh; + int hdr_len; + + /* + * Add the 802.2 SNAP header if IP as the IPv4/IPv6 code calls + * dev->hard_header directly. + */ + if (type == ETH_P_IP || type == ETH_P_IPV6 || type == ETH_P_ARP) + { + struct trllc *trllc; + + hdr_len = sizeof(struct trh_hdr) + sizeof(struct trllc); + trh = (struct trh_hdr *)skb_push(skb, hdr_len); + trllc = (struct trllc *)(trh+1); + trllc->dsap = trllc->ssap = EXTENDED_SAP; + trllc->llc = UI_CMD; + trllc->protid[0] = trllc->protid[1] = trllc->protid[2] = 0x00; + trllc->ethertype = htons(type); + } + else + { + hdr_len = sizeof(struct trh_hdr); + trh = (struct trh_hdr *)skb_push(skb, hdr_len); + } + + trh->ac=AC; + trh->fc=LLC_FRAME; + + if(saddr) + memcpy(trh->saddr,saddr,dev->addr_len); + else + memcpy(trh->saddr,dev->dev_addr,dev->addr_len); + + /* + * Build the destination and then source route the frame + */ + + if(daddr) + { + memcpy(trh->daddr,daddr,dev->addr_len); + tr_source_route(skb,trh,dev); + return(hdr_len); + } + + return -hdr_len; +} + +/* + * A neighbour discovery of some species (eg arp) has completed. We + * can now send the packet. + */ + +static int tr_rebuild_header(struct sk_buff *skb) +{ + struct trh_hdr *trh=(struct trh_hdr *)skb->data; + struct trllc *trllc=(struct trllc *)(skb->data+sizeof(struct trh_hdr)); + struct net_device *dev = skb->dev; + + /* + * FIXME: We don't yet support IPv6 over token rings + */ + + if(trllc->ethertype != htons(ETH_P_IP)) { + printk("tr_rebuild_header: Don't know how to resolve type %04X addresses ?\n",(unsigned int)htons(trllc->ethertype)); + return 0; + } + +#ifdef CONFIG_INET + if(arp_find(trh->daddr, skb)) { + return 1; + } + else +#endif + { + tr_source_route(skb,trh,dev); + return 0; + } +} + +/* + * Some of this is a bit hackish. We intercept RIF information + * used for source routing. We also grab IP directly and don't feed + * it via SNAP. + */ + +unsigned short tr_type_trans(struct sk_buff *skb, struct net_device *dev) +{ + + struct trh_hdr *trh=(struct trh_hdr *)skb->data; + struct trllc *trllc; + unsigned riflen=0; + + skb->mac.raw = skb->data; + + if(trh->saddr[0] & TR_RII) + riflen = (ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8; + + trllc = (struct trllc *)(skb->data+sizeof(struct trh_hdr)-TR_MAXRIFLEN+riflen); + + skb_pull(skb,sizeof(struct trh_hdr)-TR_MAXRIFLEN+riflen); + + if(*trh->daddr & 0x80) + { + if(!memcmp(trh->daddr,dev->broadcast,TR_ALEN)) + skb->pkt_type=PACKET_BROADCAST; + else + skb->pkt_type=PACKET_MULTICAST; + } + else if ( (trh->daddr[0] & 0x01) && (trh->daddr[1] & 0x00) && (trh->daddr[2] & 0x5E)) + { + skb->pkt_type=PACKET_MULTICAST; + } + else if(dev->flags & IFF_PROMISC) + { + if(memcmp(trh->daddr, dev->dev_addr, TR_ALEN)) + skb->pkt_type=PACKET_OTHERHOST; + } + + if ((skb->pkt_type != PACKET_BROADCAST) && + (skb->pkt_type != PACKET_MULTICAST)) + tr_add_rif_info(trh,dev) ; + + /* + * Strip the SNAP header from ARP packets since we don't + * pass them through to the 802.2/SNAP layers. + */ + + if (trllc->dsap == EXTENDED_SAP && + (trllc->ethertype == ntohs(ETH_P_IP) || + trllc->ethertype == ntohs(ETH_P_IPV6) || + trllc->ethertype == ntohs(ETH_P_ARP))) + { + skb_pull(skb, sizeof(struct trllc)); + return trllc->ethertype; + } + + return ntohs(ETH_P_802_2); +} + +/* + * We try to do source routing... + */ + +void tr_source_route(struct sk_buff *skb,struct trh_hdr *trh,struct net_device *dev) +{ + int slack; + unsigned int hash; + struct rif_cache_s *entry; + unsigned char *olddata; + static const unsigned char mcast_func_addr[] + = {0xC0,0x00,0x00,0x04,0x00,0x00}; + + spin_lock_bh(&rif_lock); + + /* + * Broadcasts are single route as stated in RFC 1042 + */ + if( (!memcmp(&(trh->daddr[0]),&(dev->broadcast[0]),TR_ALEN)) || + (!memcmp(&(trh->daddr[0]),&(mcast_func_addr[0]), TR_ALEN)) ) + { + trh->rcf=htons((((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK) + | TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST); + trh->saddr[0]|=TR_RII; + } + else + { + hash = rif_hash(trh->daddr); + /* + * Walk the hash table and look for an entry + */ + for(entry=rif_table[hash];entry && memcmp(&(entry->addr[0]),&(trh->daddr[0]),TR_ALEN);entry=entry->next); + + /* + * If we found an entry we can route the frame. + */ + if(entry) + { +#if TR_SR_DEBUG +printk("source routing for %02X:%02X:%02X:%02X:%02X:%02X\n",trh->daddr[0], + trh->daddr[1],trh->daddr[2],trh->daddr[3],trh->daddr[4],trh->daddr[5]); +#endif + if(!entry->local_ring && (ntohs(entry->rcf) & TR_RCF_LEN_MASK) >> 8) + { + trh->rcf=entry->rcf; + memcpy(&trh->rseg[0],&entry->rseg[0],8*sizeof(unsigned short)); + trh->rcf^=htons(TR_RCF_DIR_BIT); + trh->rcf&=htons(0x1fff); /* Issam Chehab <ichehab@madge1.demon.co.uk> */ + + trh->saddr[0]|=TR_RII; +#if TR_SR_DEBUG + printk("entry found with rcf %04x\n", entry->rcf); + } + else + { + printk("entry found but without rcf length, local=%02x\n", entry->local_ring); +#endif + } + entry->last_used=jiffies; + } + else + { + /* + * Without the information we simply have to shout + * on the wire. The replies should rapidly clean this + * situation up. + */ + trh->rcf=htons((((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK) + | TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST); + trh->saddr[0]|=TR_RII; +#if TR_SR_DEBUG + printk("no entry in rif table found - broadcasting frame\n"); +#endif + } + } + + /* Compress the RIF here so we don't have to do it in the driver(s) */ + if (!(trh->saddr[0] & 0x80)) + slack = 18; + else + slack = 18 - ((ntohs(trh->rcf) & TR_RCF_LEN_MASK)>>8); + olddata = skb->data; + spin_unlock_bh(&rif_lock); + + skb_pull(skb, slack); + memmove(skb->data, olddata, sizeof(struct trh_hdr) - slack); +} + +/* + * We have learned some new RIF information for our source + * routing. + */ + +static void tr_add_rif_info(struct trh_hdr *trh, struct net_device *dev) +{ + unsigned int hash, rii_p = 0; + struct rif_cache_s *entry; + + + spin_lock_bh(&rif_lock); + + /* + * Firstly see if the entry exists + */ + + if(trh->saddr[0] & TR_RII) + { + trh->saddr[0]&=0x7f; + if (((ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8) > 2) + { + rii_p = 1; + } + } + + hash = rif_hash(trh->saddr); + for(entry=rif_table[hash];entry && memcmp(&(entry->addr[0]),&(trh->saddr[0]),TR_ALEN);entry=entry->next); + + if(entry==NULL) + { +#if TR_SR_DEBUG +printk("adding rif_entry: addr:%02X:%02X:%02X:%02X:%02X:%02X rcf:%04X\n", + trh->saddr[0],trh->saddr[1],trh->saddr[2], + trh->saddr[3],trh->saddr[4],trh->saddr[5], + ntohs(trh->rcf)); +#endif + /* + * Allocate our new entry. A failure to allocate loses + * use the information. This is harmless. + * + * FIXME: We ought to keep some kind of cache size + * limiting and adjust the timers to suit. + */ + entry=kmalloc(sizeof(struct rif_cache_s),GFP_ATOMIC); + + if(!entry) + { + printk(KERN_DEBUG "tr.c: Couldn't malloc rif cache entry !\n"); + spin_unlock_bh(&rif_lock); + return; + } + + memcpy(&(entry->addr[0]),&(trh->saddr[0]),TR_ALEN); + entry->iface = dev->ifindex; + entry->next=rif_table[hash]; + entry->last_used=jiffies; + rif_table[hash]=entry; + + if (rii_p) + { + entry->rcf = trh->rcf & htons((unsigned short)~TR_RCF_BROADCAST_MASK); + memcpy(&(entry->rseg[0]),&(trh->rseg[0]),8*sizeof(unsigned short)); + entry->local_ring = 0; + trh->saddr[0]|=TR_RII; /* put the routing indicator back for tcpdump */ + } + else + { + entry->local_ring = 1; + } + } + else /* Y. Tahara added */ + { + /* + * Update existing entries + */ + if (!entry->local_ring) + if (entry->rcf != (trh->rcf & htons((unsigned short)~TR_RCF_BROADCAST_MASK)) && + !(trh->rcf & htons(TR_RCF_BROADCAST_MASK))) + { +#if TR_SR_DEBUG +printk("updating rif_entry: addr:%02X:%02X:%02X:%02X:%02X:%02X rcf:%04X\n", + trh->saddr[0],trh->saddr[1],trh->saddr[2], + trh->saddr[3],trh->saddr[4],trh->saddr[5], + ntohs(trh->rcf)); +#endif + entry->rcf = trh->rcf & htons((unsigned short)~TR_RCF_BROADCAST_MASK); + memcpy(&(entry->rseg[0]),&(trh->rseg[0]),8*sizeof(unsigned short)); + } + entry->last_used=jiffies; + } + spin_unlock_bh(&rif_lock); +} + +/* + * Scan the cache with a timer and see what we need to throw out. + */ + +static void rif_check_expire(unsigned long dummy) +{ + int i; + unsigned long next_interval = jiffies + sysctl_tr_rif_timeout/2; + + spin_lock_bh(&rif_lock); + + for(i =0; i < RIF_TABLE_SIZE; i++) { + struct rif_cache_s *entry, **pentry; + + pentry = rif_table+i; + while((entry=*pentry) != NULL) { + unsigned long expires + = entry->last_used + sysctl_tr_rif_timeout; + + if (time_before_eq(expires, jiffies)) { + *pentry = entry->next; + kfree(entry); + } else { + pentry = &entry->next; + + if (time_before(expires, next_interval)) + next_interval = expires; + } + } + } + + spin_unlock_bh(&rif_lock); + + mod_timer(&rif_timer, next_interval); + +} + +/* + * Generate the /proc/net information for the token ring RIF + * routing. + */ + +#ifdef CONFIG_PROC_FS + +static struct rif_cache_s *rif_get_idx(loff_t pos) +{ + int i; + struct rif_cache_s *entry; + loff_t off = 0; + + for(i = 0; i < RIF_TABLE_SIZE; i++) + for(entry = rif_table[i]; entry; entry = entry->next) { + if (off == pos) + return entry; + ++off; + } + + return NULL; +} + +static void *rif_seq_start(struct seq_file *seq, loff_t *pos) +{ + spin_lock_bh(&rif_lock); + + return *pos ? rif_get_idx(*pos - 1) : SEQ_START_TOKEN; +} + +static void *rif_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + int i; + struct rif_cache_s *ent = v; + + ++*pos; + + if (v == SEQ_START_TOKEN) { + i = -1; + goto scan; + } + + if (ent->next) + return ent->next; + + i = rif_hash(ent->addr); + scan: + while (++i < RIF_TABLE_SIZE) { + if ((ent = rif_table[i]) != NULL) + return ent; + } + return NULL; +} + +static void rif_seq_stop(struct seq_file *seq, void *v) +{ + spin_unlock_bh(&rif_lock); +} + +static int rif_seq_show(struct seq_file *seq, void *v) +{ + int j, rcf_len, segment, brdgnmb; + struct rif_cache_s *entry = v; + + if (v == SEQ_START_TOKEN) + seq_puts(seq, + "if TR address TTL rcf routing segments\n"); + else { + struct net_device *dev = dev_get_by_index(entry->iface); + long ttl = (long) (entry->last_used + sysctl_tr_rif_timeout) + - (long) jiffies; + + seq_printf(seq, "%s %02X:%02X:%02X:%02X:%02X:%02X %7li ", + dev?dev->name:"?", + entry->addr[0],entry->addr[1],entry->addr[2], + entry->addr[3],entry->addr[4],entry->addr[5], + ttl/HZ); + + if (entry->local_ring) + seq_puts(seq, "local\n"); + else { + + seq_printf(seq, "%04X", ntohs(entry->rcf)); + rcf_len = ((ntohs(entry->rcf) & TR_RCF_LEN_MASK)>>8)-2; + if (rcf_len) + rcf_len >>= 1; + for(j = 1; j < rcf_len; j++) { + if(j==1) { + segment=ntohs(entry->rseg[j-1])>>4; + seq_printf(seq," %03X",segment); + }; + segment=ntohs(entry->rseg[j])>>4; + brdgnmb=ntohs(entry->rseg[j-1])&0x00f; + seq_printf(seq,"-%01X-%03X",brdgnmb,segment); + } + seq_putc(seq, '\n'); + } + } + return 0; +} + + +static struct seq_operations rif_seq_ops = { + .start = rif_seq_start, + .next = rif_seq_next, + .stop = rif_seq_stop, + .show = rif_seq_show, +}; + +static int rif_seq_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &rif_seq_ops); +} + +static struct file_operations rif_seq_fops = { + .owner = THIS_MODULE, + .open = rif_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +#endif + +static void tr_setup(struct net_device *dev) +{ + /* + * Configure and register + */ + + dev->hard_header = tr_header; + dev->rebuild_header = tr_rebuild_header; + + dev->type = ARPHRD_IEEE802_TR; + dev->hard_header_len = TR_HLEN; + dev->mtu = 2000; + dev->addr_len = TR_ALEN; + dev->tx_queue_len = 100; /* Long queues on tr */ + + memset(dev->broadcast,0xFF, TR_ALEN); + + /* New-style flags. */ + dev->flags = IFF_BROADCAST | IFF_MULTICAST ; +} + +/** + * alloc_trdev - Register token ring device + * @sizeof_priv: Size of additional driver-private structure to be allocated + * for this token ring device + * + * Fill in the fields of the device structure with token ring-generic values. + * + * Constructs a new net device, complete with a private data area of + * size @sizeof_priv. A 32-byte (not bit) alignment is enforced for + * this private data area. + */ +struct net_device *alloc_trdev(int sizeof_priv) +{ + return alloc_netdev(sizeof_priv, "tr%d", tr_setup); +} + +/* + * Called during bootup. We don't actually have to initialise + * too much for this. + */ + +static int __init rif_init(void) +{ + init_timer(&rif_timer); + rif_timer.expires = sysctl_tr_rif_timeout; + rif_timer.data = 0L; + rif_timer.function = rif_check_expire; + add_timer(&rif_timer); + + proc_net_fops_create("tr_rif", S_IRUGO, &rif_seq_fops); + return 0; +} + +module_init(rif_init); + +EXPORT_SYMBOL(tr_source_route); +EXPORT_SYMBOL(tr_type_trans); +EXPORT_SYMBOL(alloc_trdev); |