summaryrefslogtreecommitdiffstats
path: root/net/atm
diff options
context:
space:
mode:
Diffstat (limited to 'net/atm')
-rw-r--r--net/atm/Makefile18
-rw-r--r--net/atm/addr.c134
-rw-r--r--net/atm/addr.h18
-rw-r--r--net/atm/atm_misc.c106
-rw-r--r--net/atm/br2684.c824
-rw-r--r--net/atm/clip.c1045
-rw-r--r--net/atm/common.c804
-rw-r--r--net/atm/common.h50
-rw-r--r--net/atm/ioctl.c139
-rw-r--r--net/atm/ipcommon.c61
-rw-r--r--net/atm/ipcommon.h22
-rw-r--r--net/atm/lec.c2538
-rw-r--r--net/atm/lec.h142
-rw-r--r--net/atm/lec_arpc.h92
-rw-r--r--net/atm/mpc.c1514
-rw-r--r--net/atm/mpc.h53
-rw-r--r--net/atm/mpoa_caches.c576
-rw-r--r--net/atm/mpoa_caches.h96
-rw-r--r--net/atm/mpoa_proc.c305
-rw-r--r--net/atm/pppoatm.c369
-rw-r--r--net/atm/proc.c514
-rw-r--r--net/atm/protocols.h13
-rw-r--r--net/atm/pvc.c155
-rw-r--r--net/atm/raw.c98
-rw-r--r--net/atm/resources.c432
-rw-r--r--net/atm/resources.h46
-rw-r--r--net/atm/signaling.c280
-rw-r--r--net/atm/signaling.h30
-rw-r--r--net/atm/svc.c674
29 files changed, 11148 insertions, 0 deletions
diff --git a/net/atm/Makefile b/net/atm/Makefile
new file mode 100644
index 000000000000..d5818751f6ba
--- /dev/null
+++ b/net/atm/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for the ATM Protocol Families.
+#
+
+atm-y := addr.o pvc.o signaling.o svc.o ioctl.o common.o atm_misc.o raw.o resources.o
+mpoa-objs := mpc.o mpoa_caches.o mpoa_proc.o
+
+obj-$(CONFIG_ATM) += atm.o
+obj-$(CONFIG_ATM_CLIP) += clip.o
+atm-$(subst m,y,$(CONFIG_ATM_CLIP)) += ipcommon.o
+obj-$(CONFIG_ATM_BR2684) += br2684.o
+atm-$(subst m,y,$(CONFIG_ATM_BR2684)) += ipcommon.o
+atm-$(subst m,y,$(CONFIG_NET_SCH_ATM)) += ipcommon.o
+atm-$(CONFIG_PROC_FS) += proc.o
+
+obj-$(CONFIG_ATM_LANE) += lec.o
+obj-$(CONFIG_ATM_MPOA) += mpoa.o
+obj-$(CONFIG_PPPOATM) += pppoatm.o
diff --git a/net/atm/addr.c b/net/atm/addr.c
new file mode 100644
index 000000000000..1c8867f7f54a
--- /dev/null
+++ b/net/atm/addr.c
@@ -0,0 +1,134 @@
+/* net/atm/addr.c - Local ATM address registry */
+
+/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
+
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+
+#include "signaling.h"
+#include "addr.h"
+
+static int check_addr(struct sockaddr_atmsvc *addr)
+{
+ int i;
+
+ if (addr->sas_family != AF_ATMSVC)
+ return -EAFNOSUPPORT;
+ if (!*addr->sas_addr.pub)
+ return *addr->sas_addr.prv ? 0 : -EINVAL;
+ for (i = 1; i < ATM_E164_LEN + 1; i++) /* make sure it's \0-terminated */
+ if (!addr->sas_addr.pub[i])
+ return 0;
+ return -EINVAL;
+}
+
+static int identical(struct sockaddr_atmsvc *a, struct sockaddr_atmsvc *b)
+{
+ if (*a->sas_addr.prv)
+ if (memcmp(a->sas_addr.prv, b->sas_addr.prv, ATM_ESA_LEN))
+ return 0;
+ if (!*a->sas_addr.pub)
+ return !*b->sas_addr.pub;
+ if (!*b->sas_addr.pub)
+ return 0;
+ return !strcmp(a->sas_addr.pub, b->sas_addr.pub);
+}
+
+static void notify_sigd(struct atm_dev *dev)
+{
+ struct sockaddr_atmpvc pvc;
+
+ pvc.sap_addr.itf = dev->number;
+ sigd_enq(NULL, as_itf_notify, NULL, &pvc, NULL);
+}
+
+void atm_reset_addr(struct atm_dev *dev)
+{
+ unsigned long flags;
+ struct atm_dev_addr *this, *p;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ list_for_each_entry_safe(this, p, &dev->local, entry)
+ kfree(this);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ notify_sigd(dev);
+}
+
+int atm_add_addr(struct atm_dev *dev, struct sockaddr_atmsvc *addr)
+{
+ unsigned long flags;
+ struct atm_dev_addr *this;
+ int error;
+
+ error = check_addr(addr);
+ if (error)
+ return error;
+ spin_lock_irqsave(&dev->lock, flags);
+ list_for_each_entry(this, &dev->local, entry) {
+ if (identical(&this->addr, addr)) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -EEXIST;
+ }
+ }
+ this = kmalloc(sizeof(struct atm_dev_addr), GFP_ATOMIC);
+ if (!this) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -ENOMEM;
+ }
+ this->addr = *addr;
+ list_add(&this->entry, &dev->local);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ notify_sigd(dev);
+ return 0;
+}
+
+int atm_del_addr(struct atm_dev *dev, struct sockaddr_atmsvc *addr)
+{
+ unsigned long flags;
+ struct atm_dev_addr *this;
+ int error;
+
+ error = check_addr(addr);
+ if (error)
+ return error;
+ spin_lock_irqsave(&dev->lock, flags);
+ list_for_each_entry(this, &dev->local, entry) {
+ if (identical(&this->addr, addr)) {
+ list_del(&this->entry);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ kfree(this);
+ notify_sigd(dev);
+ return 0;
+ }
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -ENOENT;
+}
+
+int atm_get_addr(struct atm_dev *dev, struct sockaddr_atmsvc __user * buf,
+ size_t size)
+{
+ unsigned long flags;
+ struct atm_dev_addr *this;
+ int total = 0, error;
+ struct sockaddr_atmsvc *tmp_buf, *tmp_bufp;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ list_for_each_entry(this, &dev->local, entry)
+ total += sizeof(struct sockaddr_atmsvc);
+ tmp_buf = tmp_bufp = kmalloc(total, GFP_ATOMIC);
+ if (!tmp_buf) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -ENOMEM;
+ }
+ list_for_each_entry(this, &dev->local, entry)
+ memcpy(tmp_bufp++, &this->addr, sizeof(struct sockaddr_atmsvc));
+ spin_unlock_irqrestore(&dev->lock, flags);
+ error = total > size ? -E2BIG : total;
+ if (copy_to_user(buf, tmp_buf, total < size ? total : size))
+ error = -EFAULT;
+ kfree(tmp_buf);
+ return error;
+}
diff --git a/net/atm/addr.h b/net/atm/addr.h
new file mode 100644
index 000000000000..3099d21feeaa
--- /dev/null
+++ b/net/atm/addr.h
@@ -0,0 +1,18 @@
+/* net/atm/addr.h - Local ATM address registry */
+
+/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
+
+
+#ifndef NET_ATM_ADDR_H
+#define NET_ATM_ADDR_H
+
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+
+
+void atm_reset_addr(struct atm_dev *dev);
+int atm_add_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr);
+int atm_del_addr(struct atm_dev *dev,struct sockaddr_atmsvc *addr);
+int atm_get_addr(struct atm_dev *dev,struct sockaddr_atmsvc __user *buf,size_t size);
+
+#endif
diff --git a/net/atm/atm_misc.c b/net/atm/atm_misc.c
new file mode 100644
index 000000000000..b2113c3454ae
--- /dev/null
+++ b/net/atm/atm_misc.c
@@ -0,0 +1,106 @@
+/* net/atm/atm_misc.c - Various functions for use by ATM drivers */
+
+/* Written 1995-2000 by Werner Almesberger, EPFL ICA */
+
+
+#include <linux/module.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/skbuff.h>
+#include <linux/sonet.h>
+#include <linux/bitops.h>
+#include <asm/atomic.h>
+#include <asm/errno.h>
+
+
+int atm_charge(struct atm_vcc *vcc,int truesize)
+{
+ atm_force_charge(vcc,truesize);
+ if (atomic_read(&sk_atm(vcc)->sk_rmem_alloc) <= sk_atm(vcc)->sk_rcvbuf)
+ return 1;
+ atm_return(vcc,truesize);
+ atomic_inc(&vcc->stats->rx_drop);
+ return 0;
+}
+
+
+struct sk_buff *atm_alloc_charge(struct atm_vcc *vcc,int pdu_size,
+ int gfp_flags)
+{
+ struct sock *sk = sk_atm(vcc);
+ int guess = atm_guess_pdu2truesize(pdu_size);
+
+ atm_force_charge(vcc,guess);
+ if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf) {
+ struct sk_buff *skb = alloc_skb(pdu_size,gfp_flags);
+
+ if (skb) {
+ atomic_add(skb->truesize-guess,
+ &sk->sk_rmem_alloc);
+ return skb;
+ }
+ }
+ atm_return(vcc,guess);
+ atomic_inc(&vcc->stats->rx_drop);
+ return NULL;
+}
+
+
+/*
+ * atm_pcr_goal returns the positive PCR if it should be rounded up, the
+ * negative PCR if it should be rounded down, and zero if the maximum available
+ * bandwidth should be used.
+ *
+ * The rules are as follows (* = maximum, - = absent (0), x = value "x",
+ * (x+ = x or next value above x, x- = x or next value below):
+ *
+ * min max pcr result min max pcr result
+ * - - - * (UBR only) x - - x+
+ * - - * * x - * *
+ * - - z z- x - z z-
+ * - * - * x * - x+
+ * - * * * x * * *
+ * - * z z- x * z z-
+ * - y - y- x y - x+
+ * - y * y- x y * y-
+ * - y z z- x y z z-
+ *
+ * All non-error cases can be converted with the following simple set of rules:
+ *
+ * if pcr == z then z-
+ * else if min == x && pcr == - then x+
+ * else if max == y then y-
+ * else *
+ */
+
+
+int atm_pcr_goal(struct atm_trafprm *tp)
+{
+ if (tp->pcr && tp->pcr != ATM_MAX_PCR) return -tp->pcr;
+ if (tp->min_pcr && !tp->pcr) return tp->min_pcr;
+ if (tp->max_pcr != ATM_MAX_PCR) return -tp->max_pcr;
+ return 0;
+}
+
+
+void sonet_copy_stats(struct k_sonet_stats *from,struct sonet_stats *to)
+{
+#define __HANDLE_ITEM(i) to->i = atomic_read(&from->i)
+ __SONET_ITEMS
+#undef __HANDLE_ITEM
+}
+
+
+void sonet_subtract_stats(struct k_sonet_stats *from,struct sonet_stats *to)
+{
+#define __HANDLE_ITEM(i) atomic_sub(to->i,&from->i)
+ __SONET_ITEMS
+#undef __HANDLE_ITEM
+}
+
+
+EXPORT_SYMBOL(atm_charge);
+EXPORT_SYMBOL(atm_alloc_charge);
+EXPORT_SYMBOL(atm_pcr_goal);
+EXPORT_SYMBOL(sonet_copy_stats);
+EXPORT_SYMBOL(sonet_subtract_stats);
diff --git a/net/atm/br2684.c b/net/atm/br2684.c
new file mode 100644
index 000000000000..e6954cf1459d
--- /dev/null
+++ b/net/atm/br2684.c
@@ -0,0 +1,824 @@
+/*
+Experimental ethernet netdevice using ATM AAL5 as underlying carrier
+(RFC1483 obsoleted by RFC2684) for Linux 2.4
+Author: Marcell GAL, 2000, XDSL Ltd, Hungary
+*/
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/ip.h>
+#include <asm/uaccess.h>
+#include <net/arp.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/seq_file.h>
+
+#include <linux/atmbr2684.h>
+
+#include "common.h"
+#include "ipcommon.h"
+
+/*
+ * Define this to use a version of the code which interacts with the higher
+ * layers in a more intellegent way, by always reserving enough space for
+ * our header at the begining of the packet. However, there may still be
+ * some problems with programs like tcpdump. In 2.5 we'll sort out what
+ * we need to do to get this perfect. For now we just will copy the packet
+ * if we need space for the header
+ */
+/* #define FASTER_VERSION */
+
+#ifdef DEBUG
+#define DPRINTK(format, args...) printk(KERN_DEBUG "br2684: " format, ##args)
+#else
+#define DPRINTK(format, args...)
+#endif
+
+#ifdef SKB_DEBUG
+static void skb_debug(const struct sk_buff *skb)
+{
+#define NUM2PRINT 50
+ char buf[NUM2PRINT * 3 + 1]; /* 3 chars per byte */
+ int i = 0;
+ for (i = 0; i < skb->len && i < NUM2PRINT; i++) {
+ sprintf(buf + i * 3, "%2.2x ", 0xff & skb->data[i]);
+ }
+ printk(KERN_DEBUG "br2684: skb: %s\n", buf);
+}
+#else
+#define skb_debug(skb) do {} while (0)
+#endif
+
+static unsigned char llc_oui_pid_pad[] =
+ { 0xAA, 0xAA, 0x03, 0x00, 0x80, 0xC2, 0x00, 0x07, 0x00, 0x00 };
+#define PADLEN (2)
+
+enum br2684_encaps {
+ e_vc = BR2684_ENCAPS_VC,
+ e_llc = BR2684_ENCAPS_LLC,
+};
+
+struct br2684_vcc {
+ struct atm_vcc *atmvcc;
+ struct net_device *device;
+ /* keep old push,pop functions for chaining */
+ void (*old_push)(struct atm_vcc *vcc,struct sk_buff *skb);
+ /* void (*old_pop)(struct atm_vcc *vcc,struct sk_buff *skb); */
+ enum br2684_encaps encaps;
+ struct list_head brvccs;
+#ifdef CONFIG_ATM_BR2684_IPFILTER
+ struct br2684_filter filter;
+#endif /* CONFIG_ATM_BR2684_IPFILTER */
+#ifndef FASTER_VERSION
+ unsigned copies_needed, copies_failed;
+#endif /* FASTER_VERSION */
+};
+
+struct br2684_dev {
+ struct net_device *net_dev;
+ struct list_head br2684_devs;
+ int number;
+ struct list_head brvccs; /* one device <=> one vcc (before xmas) */
+ struct net_device_stats stats;
+ int mac_was_set;
+};
+
+/*
+ * This lock should be held for writing any time the list of devices or
+ * their attached vcc's could be altered. It should be held for reading
+ * any time these are being queried. Note that we sometimes need to
+ * do read-locking under interrupt context, so write locking must block
+ * the current CPU's interrupts
+ */
+static DEFINE_RWLOCK(devs_lock);
+
+static LIST_HEAD(br2684_devs);
+
+static inline struct br2684_dev *BRPRIV(const struct net_device *net_dev)
+{
+ return (struct br2684_dev *) net_dev->priv;
+}
+
+static inline struct net_device *list_entry_brdev(const struct list_head *le)
+{
+ return list_entry(le, struct br2684_dev, br2684_devs)->net_dev;
+}
+
+static inline struct br2684_vcc *BR2684_VCC(const struct atm_vcc *atmvcc)
+{
+ return (struct br2684_vcc *) (atmvcc->user_back);
+}
+
+static inline struct br2684_vcc *list_entry_brvcc(const struct list_head *le)
+{
+ return list_entry(le, struct br2684_vcc, brvccs);
+}
+
+/* Caller should hold read_lock(&devs_lock) */
+static struct net_device *br2684_find_dev(const struct br2684_if_spec *s)
+{
+ struct list_head *lh;
+ struct net_device *net_dev;
+ switch (s->method) {
+ case BR2684_FIND_BYNUM:
+ list_for_each(lh, &br2684_devs) {
+ net_dev = list_entry_brdev(lh);
+ if (BRPRIV(net_dev)->number == s->spec.devnum)
+ return net_dev;
+ }
+ break;
+ case BR2684_FIND_BYIFNAME:
+ list_for_each(lh, &br2684_devs) {
+ net_dev = list_entry_brdev(lh);
+ if (!strncmp(net_dev->name, s->spec.ifname, IFNAMSIZ))
+ return net_dev;
+ }
+ break;
+ }
+ return NULL;
+}
+
+/*
+ * Send a packet out a particular vcc. Not to useful right now, but paves
+ * the way for multiple vcc's per itf. Returns true if we can send,
+ * otherwise false
+ */
+static int br2684_xmit_vcc(struct sk_buff *skb, struct br2684_dev *brdev,
+ struct br2684_vcc *brvcc)
+{
+ struct atm_vcc *atmvcc;
+#ifdef FASTER_VERSION
+ if (brvcc->encaps == e_llc)
+ memcpy(skb_push(skb, 8), llc_oui_pid_pad, 8);
+ /* last 2 bytes of llc_oui_pid_pad are managed by header routines;
+ yes, you got it: 8 + 2 = sizeof(llc_oui_pid_pad)
+ */
+#else
+ int minheadroom = (brvcc->encaps == e_llc) ? 10 : 2;
+ if (skb_headroom(skb) < minheadroom) {
+ struct sk_buff *skb2 = skb_realloc_headroom(skb, minheadroom);
+ brvcc->copies_needed++;
+ dev_kfree_skb(skb);
+ if (skb2 == NULL) {
+ brvcc->copies_failed++;
+ return 0;
+ }
+ skb = skb2;
+ }
+ skb_push(skb, minheadroom);
+ if (brvcc->encaps == e_llc)
+ memcpy(skb->data, llc_oui_pid_pad, 10);
+ else
+ memset(skb->data, 0, 2);
+#endif /* FASTER_VERSION */
+ skb_debug(skb);
+
+ ATM_SKB(skb)->vcc = atmvcc = brvcc->atmvcc;
+ DPRINTK("atm_skb(%p)->vcc(%p)->dev(%p)\n", skb, atmvcc, atmvcc->dev);
+ if (!atm_may_send(atmvcc, skb->truesize)) {
+ /* we free this here for now, because we cannot know in a higher
+ layer whether the skb point it supplied wasn't freed yet.
+ now, it always is.
+ */
+ dev_kfree_skb(skb);
+ return 0;
+ }
+ atomic_add(skb->truesize, &sk_atm(atmvcc)->sk_wmem_alloc);
+ ATM_SKB(skb)->atm_options = atmvcc->atm_options;
+ brdev->stats.tx_packets++;
+ brdev->stats.tx_bytes += skb->len;
+ atmvcc->send(atmvcc, skb);
+ return 1;
+}
+
+static inline struct br2684_vcc *pick_outgoing_vcc(struct sk_buff *skb,
+ struct br2684_dev *brdev)
+{
+ return list_empty(&brdev->brvccs) ? NULL :
+ list_entry_brvcc(brdev->brvccs.next); /* 1 vcc/dev right now */
+}
+
+static int br2684_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct br2684_dev *brdev = BRPRIV(dev);
+ struct br2684_vcc *brvcc;
+
+ DPRINTK("br2684_start_xmit, skb->dst=%p\n", skb->dst);
+ read_lock(&devs_lock);
+ brvcc = pick_outgoing_vcc(skb, brdev);
+ if (brvcc == NULL) {
+ DPRINTK("no vcc attached to dev %s\n", dev->name);
+ brdev->stats.tx_errors++;
+ brdev->stats.tx_carrier_errors++;
+ /* netif_stop_queue(dev); */
+ dev_kfree_skb(skb);
+ read_unlock(&devs_lock);
+ return -EUNATCH;
+ }
+ if (!br2684_xmit_vcc(skb, brdev, brvcc)) {
+ /*
+ * We should probably use netif_*_queue() here, but that
+ * involves added complication. We need to walk before
+ * we can run
+ */
+ /* don't free here! this pointer might be no longer valid!
+ dev_kfree_skb(skb);
+ */
+ brdev->stats.tx_errors++;
+ brdev->stats.tx_fifo_errors++;
+ }
+ read_unlock(&devs_lock);
+ return 0;
+}
+
+static struct net_device_stats *br2684_get_stats(struct net_device *dev)
+{
+ DPRINTK("br2684_get_stats\n");
+ return &BRPRIV(dev)->stats;
+}
+
+#ifdef FASTER_VERSION
+/*
+ * These mirror eth_header and eth_header_cache. They are not usually
+ * exported for use in modules, so we grab them from net_device
+ * after ether_setup() is done with it. Bit of a hack.
+ */
+static int (*my_eth_header)(struct sk_buff *, struct net_device *,
+ unsigned short, void *, void *, unsigned);
+static int (*my_eth_header_cache)(struct neighbour *, struct hh_cache *);
+
+static int
+br2684_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+ u16 *pad_before_eth;
+ int t = my_eth_header(skb, dev, type, daddr, saddr, len);
+ if (t > 0) {
+ pad_before_eth = (u16 *) skb_push(skb, 2);
+ *pad_before_eth = 0;
+ return dev->hard_header_len; /* or return 16; ? */
+ } else
+ return t;
+}
+
+static int
+br2684_header_cache(struct neighbour *neigh, struct hh_cache *hh)
+{
+/* hh_data is 16 bytes long. if encaps is ether-llc we need 24, so
+xmit will add the additional header part in that case */
+ u16 *pad_before_eth = (u16 *)(hh->hh_data);
+ int t = my_eth_header_cache(neigh, hh);
+ DPRINTK("br2684_header_cache, neigh=%p, hh_cache=%p\n", neigh, hh);
+ if (t < 0)
+ return t;
+ else {
+ *pad_before_eth = 0;
+ hh->hh_len = PADLEN + ETH_HLEN;
+ }
+ return 0;
+}
+
+/*
+ * This is similar to eth_type_trans, which cannot be used because of
+ * our dev->hard_header_len
+ */
+static inline unsigned short br_type_trans(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct ethhdr *eth;
+ unsigned char *rawp;
+ eth = eth_hdr(skb);
+
+ if (*eth->h_dest & 1) {
+ if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN) == 0)
+ skb->pkt_type = PACKET_BROADCAST;
+ else
+ skb->pkt_type = PACKET_MULTICAST;
+ }
+
+ else if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN))
+ skb->pkt_type = PACKET_OTHERHOST;
+
+ if (ntohs(eth->h_proto) >= 1536)
+ return eth->h_proto;
+
+ 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)
+ return htons(ETH_P_802_3);
+
+ /*
+ * Real 802.2 LLC
+ */
+ return htons(ETH_P_802_2);
+}
+#endif /* FASTER_VERSION */
+
+/*
+ * We remember when the MAC gets set, so we don't override it later with
+ * the ESI of the ATM card of the first VC
+ */
+static int (*my_eth_mac_addr)(struct net_device *, void *);
+static int br2684_mac_addr(struct net_device *dev, void *p)
+{
+ int err = my_eth_mac_addr(dev, p);
+ if (!err)
+ BRPRIV(dev)->mac_was_set = 1;
+ return err;
+}
+
+#ifdef CONFIG_ATM_BR2684_IPFILTER
+/* this IOCTL is experimental. */
+static int br2684_setfilt(struct atm_vcc *atmvcc, void __user *arg)
+{
+ struct br2684_vcc *brvcc;
+ struct br2684_filter_set fs;
+
+ if (copy_from_user(&fs, arg, sizeof fs))
+ return -EFAULT;
+ if (fs.ifspec.method != BR2684_FIND_BYNOTHING) {
+ /*
+ * This is really a per-vcc thing, but we can also search
+ * by device
+ */
+ struct br2684_dev *brdev;
+ read_lock(&devs_lock);
+ brdev = BRPRIV(br2684_find_dev(&fs.ifspec));
+ if (brdev == NULL || list_empty(&brdev->brvccs) ||
+ brdev->brvccs.next != brdev->brvccs.prev) /* >1 VCC */
+ brvcc = NULL;
+ else
+ brvcc = list_entry_brvcc(brdev->brvccs.next);
+ read_unlock(&devs_lock);
+ if (brvcc == NULL)
+ return -ESRCH;
+ } else
+ brvcc = BR2684_VCC(atmvcc);
+ memcpy(&brvcc->filter, &fs.filter, sizeof(brvcc->filter));
+ return 0;
+}
+
+/* Returns 1 if packet should be dropped */
+static inline int
+packet_fails_filter(u16 type, struct br2684_vcc *brvcc, struct sk_buff *skb)
+{
+ if (brvcc->filter.netmask == 0)
+ return 0; /* no filter in place */
+ if (type == __constant_htons(ETH_P_IP) &&
+ (((struct iphdr *) (skb->data))->daddr & brvcc->filter.
+ netmask) == brvcc->filter.prefix)
+ return 0;
+ if (type == __constant_htons(ETH_P_ARP))
+ return 0;
+ /* TODO: we should probably filter ARPs too.. don't want to have
+ * them returning values that don't make sense, or is that ok?
+ */
+ return 1; /* drop */
+}
+#endif /* CONFIG_ATM_BR2684_IPFILTER */
+
+static void br2684_close_vcc(struct br2684_vcc *brvcc)
+{
+ DPRINTK("removing VCC %p from dev %p\n", brvcc, brvcc->device);
+ write_lock_irq(&devs_lock);
+ list_del(&brvcc->brvccs);
+ write_unlock_irq(&devs_lock);
+ brvcc->atmvcc->user_back = NULL; /* what about vcc->recvq ??? */
+ brvcc->old_push(brvcc->atmvcc, NULL); /* pass on the bad news */
+ kfree(brvcc);
+ module_put(THIS_MODULE);
+}
+
+/* when AAL5 PDU comes in: */
+static void br2684_push(struct atm_vcc *atmvcc, struct sk_buff *skb)
+{
+ struct br2684_vcc *brvcc = BR2684_VCC(atmvcc);
+ struct net_device *net_dev = brvcc->device;
+ struct br2684_dev *brdev = BRPRIV(net_dev);
+ int plen = sizeof(llc_oui_pid_pad) + ETH_HLEN;
+
+ DPRINTK("br2684_push\n");
+
+ if (unlikely(skb == NULL)) {
+ /* skb==NULL means VCC is being destroyed */
+ br2684_close_vcc(brvcc);
+ if (list_empty(&brdev->brvccs)) {
+ read_lock(&devs_lock);
+ list_del(&brdev->br2684_devs);
+ read_unlock(&devs_lock);
+ unregister_netdev(net_dev);
+ free_netdev(net_dev);
+ }
+ return;
+ }
+
+ skb_debug(skb);
+ atm_return(atmvcc, skb->truesize);
+ DPRINTK("skb from brdev %p\n", brdev);
+ if (brvcc->encaps == e_llc) {
+ /* let us waste some time for checking the encapsulation.
+ Note, that only 7 char is checked so frames with a valid FCS
+ are also accepted (but FCS is not checked of course) */
+ if (memcmp(skb->data, llc_oui_pid_pad, 7)) {
+ brdev->stats.rx_errors++;
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ /* Strip FCS if present */
+ if (skb->len > 7 && skb->data[7] == 0x01)
+ __skb_trim(skb, skb->len - 4);
+ } else {
+ plen = PADLEN + ETH_HLEN; /* pad, dstmac,srcmac, ethtype */
+ /* first 2 chars should be 0 */
+ if (*((u16 *) (skb->data)) != 0) {
+ brdev->stats.rx_errors++;
+ dev_kfree_skb(skb);
+ return;
+ }
+ }
+ if (skb->len < plen) {
+ brdev->stats.rx_errors++;
+ dev_kfree_skb(skb); /* dev_ not needed? */
+ return;
+ }
+
+#ifdef FASTER_VERSION
+ /* FIXME: tcpdump shows that pointer to mac header is 2 bytes earlier,
+ than should be. What else should I set? */
+ skb_pull(skb, plen);
+ skb->mac.raw = ((char *) (skb->data)) - ETH_HLEN;
+ skb->pkt_type = PACKET_HOST;
+#ifdef CONFIG_BR2684_FAST_TRANS
+ skb->protocol = ((u16 *) skb->data)[-1];
+#else /* some protocols might require this: */
+ skb->protocol = br_type_trans(skb, net_dev);
+#endif /* CONFIG_BR2684_FAST_TRANS */
+#else
+ skb_pull(skb, plen - ETH_HLEN);
+ skb->protocol = eth_type_trans(skb, net_dev);
+#endif /* FASTER_VERSION */
+#ifdef CONFIG_ATM_BR2684_IPFILTER
+ if (unlikely(packet_fails_filter(skb->protocol, brvcc, skb))) {
+ brdev->stats.rx_dropped++;
+ dev_kfree_skb(skb);
+ return;
+ }
+#endif /* CONFIG_ATM_BR2684_IPFILTER */
+ skb->dev = net_dev;
+ ATM_SKB(skb)->vcc = atmvcc; /* needed ? */
+ DPRINTK("received packet's protocol: %x\n", ntohs(skb->protocol));
+ skb_debug(skb);
+ if (unlikely(!(net_dev->flags & IFF_UP))) {
+ /* sigh, interface is down */
+ brdev->stats.rx_dropped++;
+ dev_kfree_skb(skb);
+ return;
+ }
+ brdev->stats.rx_packets++;
+ brdev->stats.rx_bytes += skb->len;
+ memset(ATM_SKB(skb), 0, sizeof(struct atm_skb_data));
+ netif_rx(skb);
+}
+
+static int br2684_regvcc(struct atm_vcc *atmvcc, void __user *arg)
+{
+/* assign a vcc to a dev
+Note: we do not have explicit unassign, but look at _push()
+*/
+ int err;
+ struct br2684_vcc *brvcc;
+ struct sk_buff_head copy;
+ struct sk_buff *skb;
+ struct br2684_dev *brdev;
+ struct net_device *net_dev;
+ struct atm_backend_br2684 be;
+
+ if (copy_from_user(&be, arg, sizeof be))
+ return -EFAULT;
+ brvcc = kmalloc(sizeof(struct br2684_vcc), GFP_KERNEL);
+ if (!brvcc)
+ return -ENOMEM;
+ memset(brvcc, 0, sizeof(struct br2684_vcc));
+ write_lock_irq(&devs_lock);
+ net_dev = br2684_find_dev(&be.ifspec);
+ if (net_dev == NULL) {
+ printk(KERN_ERR
+ "br2684: tried to attach to non-existant device\n");
+ err = -ENXIO;
+ goto error;
+ }
+ brdev = BRPRIV(net_dev);
+ if (atmvcc->push == NULL) {
+ err = -EBADFD;
+ goto error;
+ }
+ if (!list_empty(&brdev->brvccs)) {
+ /* Only 1 VCC/dev right now */
+ err = -EEXIST;
+ goto error;
+ }
+ if (be.fcs_in != BR2684_FCSIN_NO || be.fcs_out != BR2684_FCSOUT_NO ||
+ be.fcs_auto || be.has_vpiid || be.send_padding || (be.encaps !=
+ BR2684_ENCAPS_VC && be.encaps != BR2684_ENCAPS_LLC) ||
+ be.min_size != 0) {
+ err = -EINVAL;
+ goto error;
+ }
+ DPRINTK("br2684_regvcc vcc=%p, encaps=%d, brvcc=%p\n", atmvcc, be.encaps,
+ brvcc);
+ if (list_empty(&brdev->brvccs) && !brdev->mac_was_set) {
+ unsigned char *esi = atmvcc->dev->esi;
+ if (esi[0] | esi[1] | esi[2] | esi[3] | esi[4] | esi[5])
+ memcpy(net_dev->dev_addr, esi, net_dev->addr_len);
+ else
+ net_dev->dev_addr[2] = 1;
+ }
+ list_add(&brvcc->brvccs, &brdev->brvccs);
+ write_unlock_irq(&devs_lock);
+ brvcc->device = net_dev;
+ brvcc->atmvcc = atmvcc;
+ atmvcc->user_back = brvcc;
+ brvcc->encaps = (enum br2684_encaps) be.encaps;
+ brvcc->old_push = atmvcc->push;
+ barrier();
+ atmvcc->push = br2684_push;
+ skb_queue_head_init(&copy);
+ skb_migrate(&sk_atm(atmvcc)->sk_receive_queue, &copy);
+ while ((skb = skb_dequeue(&copy)) != NULL) {
+ BRPRIV(skb->dev)->stats.rx_bytes -= skb->len;
+ BRPRIV(skb->dev)->stats.rx_packets--;
+ br2684_push(atmvcc, skb);
+ }
+ __module_get(THIS_MODULE);
+ return 0;
+ error:
+ write_unlock_irq(&devs_lock);
+ kfree(brvcc);
+ return err;
+}
+
+static void br2684_setup(struct net_device *netdev)
+{
+ struct br2684_dev *brdev = BRPRIV(netdev);
+
+ ether_setup(netdev);
+ brdev->net_dev = netdev;
+
+#ifdef FASTER_VERSION
+ my_eth_header = netdev->hard_header;
+ netdev->hard_header = br2684_header;
+ my_eth_header_cache = netdev->hard_header_cache;
+ netdev->hard_header_cache = br2684_header_cache;
+ netdev->hard_header_len = sizeof(llc_oui_pid_pad) + ETH_HLEN; /* 10 + 14 */
+#endif
+ my_eth_mac_addr = netdev->set_mac_address;
+ netdev->set_mac_address = br2684_mac_addr;
+ netdev->hard_start_xmit = br2684_start_xmit;
+ netdev->get_stats = br2684_get_stats;
+
+ INIT_LIST_HEAD(&brdev->brvccs);
+}
+
+static int br2684_create(void __user *arg)
+{
+ int err;
+ struct net_device *netdev;
+ struct br2684_dev *brdev;
+ struct atm_newif_br2684 ni;
+
+ DPRINTK("br2684_create\n");
+
+ if (copy_from_user(&ni, arg, sizeof ni)) {
+ return -EFAULT;
+ }
+ if (ni.media != BR2684_MEDIA_ETHERNET || ni.mtu != 1500) {
+ return -EINVAL;
+ }
+
+ netdev = alloc_netdev(sizeof(struct br2684_dev),
+ ni.ifname[0] ? ni.ifname : "nas%d",
+ br2684_setup);
+ if (!netdev)
+ return -ENOMEM;
+
+ brdev = BRPRIV(netdev);
+
+ DPRINTK("registered netdev %s\n", netdev->name);
+ /* open, stop, do_ioctl ? */
+ err = register_netdev(netdev);
+ if (err < 0) {
+ printk(KERN_ERR "br2684_create: register_netdev failed\n");
+ free_netdev(netdev);
+ return err;
+ }
+
+ write_lock_irq(&devs_lock);
+ brdev->number = list_empty(&br2684_devs) ? 1 :
+ BRPRIV(list_entry_brdev(br2684_devs.prev))->number + 1;
+ list_add_tail(&brdev->br2684_devs, &br2684_devs);
+ write_unlock_irq(&devs_lock);
+ return 0;
+}
+
+/*
+ * This handles ioctls actually performed on our vcc -