diff -ur --new-file old/atm/.kernel new/atm/.kernel
--- old/atm/.kernel	Sun Nov  1 19:39:09 1998
+++ new/atm/.kernel	Tue Feb  9 16:36:57 1999
@@ -1,2 +1,2 @@
 # this file is used to control automated generation of differences
-2.1.126
+2.2.1
diff -ur --new-file old/atm/BUGS new/atm/BUGS
--- old/atm/BUGS	Sat Dec  5 01:40:57 1998
+++ new/atm/BUGS	Tue Feb  9 16:46:46 1999
@@ -1,4 +1,4 @@
-Known bugs and restrictions in version 0.52
+Known bugs and restrictions in version 0.53
 ===========================================
 
  - ENI driver: closing an AAL0 socket while data is arriving at a high rate
diff -ur --new-file old/atm/CHANGES new/atm/CHANGES
--- old/atm/CHANGES	Sat Dec  5 01:44:40 1998
+++ new/atm/CHANGES	Tue Feb  9 18:48:53 1999
@@ -1,3 +1,37 @@
+Version 0.52 to 0.53 (9-FEB-1999)
+====================
+
+Bug fixes
+---------
+
+ - atmsigd: selecting the UNI version via compile-time options didn't yield
+   the desired result in some cases (reported by Vinay Kulkarni and others)
+ - ATM VCCs now use struct sock, as required by protocol-independent layer
+   starting with recent 2.1 kernels (by Mitchell Blank)
+ - led fixes: htons/htonl bugs in LANEv2 code, one duplicate close() removed
+   (by Heikki Vatiainen)
+
+New features
+------------
+
+ - upgraded to the 2.2.1 kernel (by Mitchell Blank)
+ - LANE: added bridging support (by Heikki Vatiainen)
+ - complete rewrite of led (in led.new), which is now leaner and no longer
+   contains code (c) Digital (by Heikki Vatiainen)
+ - added macros for local AESA format and group addresses, and support in
+   atm2text (by Heikki Vatiainen)
+
+Other changes
+-------------
+
+ - ENI: buffer sizes are now limited to MID_MAX_BUF_SIZE even if max_sdu >
+   MID_MAX_BUF_SIZE/3 (reported by Andrew Lunn)
+ - plenty of NICStAR changes (Rui Prior and Mitchell Blank)
+ - LANE interface to upper layer looks more like Ethernet, so adding bridge and
+   802.1Q support is easier, and tcpdump does not need any extra patches (by
+   Heikki Vatiainen)
+
+
 Version 0.51 to 0.52 (5-DEC-1998)
 ====================
 
diff -ur --new-file old/atm/COPYING new/atm/COPYING
--- old/atm/COPYING	Tue Aug 18 12:52:07 1998
+++ new/atm/COPYING	Tue Feb  9 17:21:30 1999
@@ -1,6 +1,6 @@
 Program code, documentation and auxiliary programs, except for the parts
 listen below, are
-Copyright 1995-1998 EPFL-LRC/ICA
+Copyright 1995-1999 EPFL-LRC/ICA
 All rights reserved.
 
 This package is free software; you can redistribute it and/or modify
@@ -35,12 +35,12 @@
 
 The LAN Emulation code is Copyright by Tampere University of Technology
 - Telecommunications Laboratory. In addition to that, portions of the
-LAN Emulation client code are Copyright (C) 1995 by Digital Equipment
-Corporation.
-See lane/COPYRIGHT.TUT, led/COPYRIGHT.TUT, and led/COPYRIGHT.DEC for
-copying terms.
+LAN Emulation client code in led/ are Copyright (C) 1995 by Digital
+Equipment Corporation.
+See lane/COPYRIGHT.TUT, led.new/COPYRIGHT.TUT, led/COPYRIGHT.TUT, and
+led/COPYRIGHT.DEC for copying terms.
 
-The Multi-Protocol Over ATM (MOPA) code was developed at Tampere
+The Multi-Protocol Over ATM (MPOA) code was developed at Tampere
 University of Technology - Telecommunications Laboratory and is
 Copyright by Heikki Vatiainen and Sampo Saaristo. It is released under
 the GNU General Public License. See the file COPYING.GPL for details.
diff -ur --new-file old/atm/Makefile new/atm/Makefile
--- old/atm/Makefile	Sun Nov  1 16:04:04 1998
+++ new/atm/Makefile	Tue Feb  9 17:19:03 1999
@@ -2,8 +2,8 @@
 # "lib" must appear before anything else
 # "maint" must appear after "qgen"
 
-DIRS=lib test debug qgen saal sigd maint arpd ilmid aqd man led lane mpoad \
-  switch sigd.old # extra
+DIRS=lib test debug qgen saal sigd maint arpd ilmid aqd man led led.new lane \
+  mpoad switch sigd.old # extra
 
 all:
 		for n in $(DIRS); do $(MAKE) -C $$n || exit; done
diff -ur --new-file old/atm/README new/atm/README
--- old/atm/README	Sat Dec  5 01:27:05 1998
+++ new/atm/README	Tue Feb  9 17:52:25 1999
@@ -1,4 +1,4 @@
-ATM on Linux, release 0.52 (alpha)        by Werner Almesberger, EPFL ICA
+ATM on Linux, release 0.53 (alpha)        by Werner Almesberger, EPFL ICA
 ============================================== Werner.Almesberger@epfl.ch
 
 This is experimental software. There are known major bugs and certainly
@@ -9,7 +9,7 @@
 device drivers, source for demons, management and test tools, and some
 documentation.
 
-The kernel patch is relative to the "standard" 2.1.126 kernel.
+The kernel patch is relative to the "standard" 2.2.1 kernel.
 
 Please see  http://lrcwww.epfl.ch/linux-atm/info.html  for a list of
 features supported by ATM on Linux.
diff -ur --new-file old/atm/USAGE new/atm/USAGE
--- old/atm/USAGE	Sat Dec  5 01:58:08 1998
+++ new/atm/USAGE	Tue Feb  9 18:22:36 1999
@@ -1,4 +1,4 @@
-Usage instructions  -  ATM on Linux, release 0.52
+Usage instructions  -  ATM on Linux, release 0.53
 -------------------------------------------------
 
 For updates of ATM on Linux, please check the Web page at  
@@ -17,9 +17,9 @@
 In order to install this package, you need 
 
   - the package itself  
-    ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.52.tar.gz  
-  - the Linux kernel, version 2.1.126, e.g. from  
-    ftp://ftp.kernel.org/pub/linux/kernel/v2.1/linux-2.1.126.tar.gz  
+    ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.53.tar.gz  
+  - the Linux kernel, version 2.2.1, e.g. from  
+    ftp://ftp.kernel.org/pub/linux/kernel/v2.1/linux-2.2.1.tar.gz  
   - Perl, version 4 or 5 
   - if you want memory debugging: MPR version 1.6, e.g. from  
     ftp://sunsite.unc.edu/pub/Linux/devel/lang/c/mpr-1.6.tar.gz  
@@ -33,11 +33,11 @@
 all the files listed above there. Then extract the ATM on Linux 
 distribution:
 
-tar xfz atm-0.52.tar.gz
+tar xfz atm-0.53.tar.gz
 
 and the kernel source:
 
-tar xfz linux-2.1.126.tar.gz
+tar xfz linux-2.2.1.tar.gz
 
 Finally, you can extract the ATM-related patches:
 
@@ -49,7 +49,7 @@
   atm/  Documentation in ASCII format, kernel patch, top-level Makefile, 
     and distribution scripts 
   atm/sigd/  UNI 3.0, UNI 3.1, and UNI 4.0 signaling demon: atmsigd 
-  atm/sigd.new/  Experimental version: atmsigd.new 
+  atm/sigd.old/  Older version: atmsigd.old 
   atm/saal/  Signaling AAL library (SSCOP, SSCF, and SAAL) 
   atm/qgen/  Q.2931-style message handling 
   atm/ilmid/  ILMI address registration demon: ilmid 
@@ -59,6 +59,7 @@
     ttcp_atm, window 
   atm/arpd/  ATMARP tools and demon: atmarp, atmarpd 
   atm/led/  LAN Emulation demon: zeppelin 
+  atm/led.new/  Experimental version: zeppelin.new 
   atm/lane/  LAN Emulation servers: bus, lecs, les 
   atm/mpoad/  Multi-Protocol Over ATM demon: mpcd 
   atm/aqd/  Arequipa demon: aqpvc, arequipad 
diff -ur --new-file old/atm/VERSION new/atm/VERSION
--- old/atm/VERSION	Sat Dec  5 01:27:00 1998
+++ new/atm/VERSION	Tue Feb  9 18:22:29 1999
@@ -1 +1 @@
-0.52
+0.53
diff -ur --new-file old/atm/atm.patch new/atm/atm.patch
--- old/atm/atm.patch	Sat Dec  5 01:58:02 1998
+++ new/atm/atm.patch	Tue Feb  9 18:22:15 1999
@@ -1,6 +1,6 @@
---- ref/Makefile	Sat Oct 17 05:33:22 1998
-+++ work/Makefile	Sun Nov  1 15:31:52 1998
-@@ -128,6 +128,10 @@
+--- ref/Makefile	Tue Jan 26 02:46:35 1999
++++ work/Makefile	Tue Feb  9 15:49:57 1999
+@@ -122,6 +122,10 @@
  
  DRIVERS := $(DRIVERS) drivers/net/net.a
  
@@ -11,7 +11,7 @@
  ifeq ($(CONFIG_SCSI),y)
  DRIVERS := $(DRIVERS) drivers/scsi/scsi.a
  endif
-@@ -312,6 +316,7 @@
+@@ -321,6 +325,7 @@
  	if [ -f NET_MODULES   ]; then inst_mod NET_MODULES   net;   fi; \
  	if [ -f IPV4_MODULES  ]; then inst_mod IPV4_MODULES  ipv4;  fi; \
  	if [ -f IPV6_MODULES  ]; then inst_mod IPV6_MODULES  ipv6;  fi; \
@@ -19,11 +19,11 @@
  	if [ -f SCSI_MODULES  ]; then inst_mod SCSI_MODULES  scsi;  fi; \
  	if [ -f FS_MODULES    ]; then inst_mod FS_MODULES    fs;    fi; \
  	if [ -f NLS_MODULES   ]; then inst_mod NLS_MODULES   fs;    fi; \
---- ref/Documentation/Configure.help	Thu Oct 22 00:31:04 1998
-+++ work/Documentation/Configure.help	Sun Nov  1 15:31:52 1998
-@@ -3235,6 +3235,164 @@
-   remember to enable the driver for your HIPPI card below). Most
-   people will say N here.
+--- ref/Documentation/Configure.help	Wed Jan 20 20:05:32 1999
++++ work/Documentation/Configure.help	Tue Feb  9 15:49:57 1999
+@@ -3460,6 +3460,164 @@
+   This is a backward compatibility option, choose Y for now.
+   This option will be removed soon.
  
 +Asynchronous Transfer Mode (ATM)
 +CONFIG_ATM
@@ -187,15 +187,15 @@
  CONFIG_SCSI
    If you want to use a SCSI hard disk, SCSI tape drive, SCSI CDROM or
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/Documentation/atm.txt	Sun Nov  1 15:31:52 1998
++++ work/Documentation/atm.txt	Tue Feb  9 15:49:57 1999
 @@ -0,0 +1,4 @@
 +In order to use anything but the most primitive functions of ATM,
 +several user-mode programs are required to assist the kernel. These
 +programs and related material can be found via the ATM on Linux Web
 +page at http://lrcwww.epfl.ch/linux-atm/
---- ref/arch/i386/config.in	Thu Oct  1 19:02:21 1998
-+++ work/arch/i386/config.in	Sun Nov  1 15:31:52 1998
-@@ -101,6 +101,9 @@
+--- ref/arch/i386/config.in	Wed Jan 20 19:18:53 1999
++++ work/arch/i386/config.in	Tue Feb  9 15:49:57 1999
+@@ -139,6 +139,9 @@
    bool 'Network device support' CONFIG_NETDEVICES
    if [ "$CONFIG_NETDEVICES" = "y" ]; then
      source drivers/net/Config.in
@@ -205,8 +205,8 @@
    fi
    endmenu
  fi
---- ref/arch/alpha/config.in	Mon Oct 12 20:40:12 1998
-+++ work/arch/alpha/config.in	Sun Nov  1 15:31:52 1998
+--- ref/arch/alpha/config.in	Thu Jan 14 19:29:28 1999
++++ work/arch/alpha/config.in	Tue Feb  9 15:49:57 1999
 @@ -227,6 +227,9 @@
    bool 'Network device support' CONFIG_NETDEVICES
    if [ "$CONFIG_NETDEVICES" = "y" ]; then
@@ -217,18 +217,18 @@
    fi
    endmenu
  fi
---- ref/drivers/Makefile	Sat Oct  3 03:12:55 1998
-+++ work/drivers/Makefile	Sun Nov  1 15:31:52 1998
+--- ref/drivers/Makefile	Fri Nov 20 17:59:01 1998
++++ work/drivers/Makefile	Tue Feb  9 15:49:57 1999
 @@ -10,7 +10,7 @@
  SUB_DIRS     := block char net misc sound
- MOD_SUB_DIRS := $(SUB_DIRS) sbus
+ MOD_SUB_DIRS := $(SUB_DIRS)
  ALL_SUB_DIRS := $(SUB_DIRS) pci scsi sbus cdrom isdn pnp \
 -				 macintosh video dio zorro fc4
 +				 macintosh video dio zorro fc4 atm
  
  ifdef CONFIG_DIO
  SUB_DIRS += dio
-@@ -75,6 +75,11 @@
+@@ -76,6 +76,11 @@
    ifeq ($(CONFIG_ISDN),m)
    MOD_SUB_DIRS += isdn
    endif
@@ -241,7 +241,7 @@
  
  ifeq ($(CONFIG_AP1000),y)
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/Config.in	Sun Nov  1 15:31:53 1998
++++ work/drivers/atm/Config.in	Tue Feb  9 15:49:57 1999
 @@ -0,0 +1,34 @@
 +#
 +# ATM device configuration
@@ -278,7 +278,7 @@
 +  tristate 'IDT 77201 (NICStAR)' CONFIG_ATM_NICSTAR y
 +fi
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/Makefile	Sun Nov  1 15:31:53 1998
++++ work/drivers/atm/Makefile	Tue Feb  9 15:49:57 1999
 @@ -0,0 +1,57 @@
 +# File: drivers/atm/Makefile
 +#
@@ -338,7 +338,7 @@
 +
 +include $(TOPDIR)/Rules.make
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/atmdev_init.c	Sun Nov  1 15:31:53 1998
++++ work/drivers/atm/atmdev_init.c	Tue Feb  9 15:49:57 1999
 @@ -0,0 +1,48 @@
 +/* drivers/atm/atmdev_init.c - ATM device driver initialization */
 + 
@@ -389,7 +389,7 @@
 +	return devs;
 +}
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/atmtcp.c	Sun Nov  1 15:31:53 1998
++++ work/drivers/atm/atmtcp.c	Tue Feb  9 15:49:57 1999
 @@ -0,0 +1,339 @@
 +/* drivers/atm/atmtcp.c - ATM over TCP "device" driver */
 +
@@ -731,11 +731,11 @@
 +#endif
 +    
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/eni.c	Sun Nov  1 15:31:53 1998
-@@ -0,0 +1,2250 @@
++++ work/drivers/atm/eni.c	Tue Feb  9 16:53:05 1999
+@@ -0,0 +1,2258 @@
 +/* drivers/atm/eni.c - Efficient Networks ENI155P device driver */
 + 
-+/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */
++/* Written 1995-1999 by Werner Almesberger, EPFL LRC/ICA */
 + 
 +
 +#include <linux/module.h>
@@ -1289,11 +1289,11 @@
 +		else {
 +			static unsigned long silence = 0;
 +
-+			if (jiffies > silence) {
++			if (time_after(jiffies, silence) || silence == 0) {
 +				printk(KERN_WARNING DEV_LABEL "(itf %d): "
 +				    "discarding PDU(s) with CRC error\n",
 +				    vcc->dev->number);
-+				silence = jiffies+2*HZ;
++				silence = (jiffies+2*HZ)|1;
 +			}
 +			size = (descr & MID_RED_COUNT)*(ATM_CELL_PAYLOAD >> 2);
 +			EVENT("CRC error (descr=0x%lx,size=%ld)\n",descr,
@@ -1512,6 +1512,9 @@
 +	eni_vcc->rx = NULL;
 +	if (vcc->qos.rxtp.traffic_class == ATM_NONE) return 0;
 +	size = vcc->qos.rxtp.max_sdu*3; /* @@@ improve this */
++	if (size > MID_MAX_BUF_SIZE && vcc->qos.rxtp.max_sdu <=
++	    MID_MAX_BUF_SIZE)
++		size = MID_MAX_BUF_SIZE;
 +	eni_vcc->recv = (u32 *) eni_alloc_mem(eni_dev,&size);
 +	DPRINTK("rx at 0x%p\n",eni_vcc->recv);
 +	eni_vcc->words = size >> 2;
@@ -1991,7 +1994,12 @@
 +	ubr = txtp->traffic_class == ATM_UBR;
 +	unlimited = ubr && (!rate || rate <= -ATM_OC3_PCR ||
 +	    rate >= ATM_OC3_PCR);
-+	if (!unlimited) size = txtp->max_sdu*3; /* @@@ improve */
++	if (!unlimited) {
++		size = txtp->max_sdu*3; /* @@@ improve */
++		if (size > MID_MAX_BUF_SIZE && txtp->max_sdu <=
++		    MID_MAX_BUF_SIZE)
++			size = MID_MAX_BUF_SIZE;
++	}
 +	else {
 +		if (eni_dev->ubr) {
 +			eni_vcc->tx = eni_dev->ubr;
@@ -2984,7 +2992,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/eni.h	Sun Nov  1 15:31:53 1998
++++ work/drivers/atm/eni.h	Tue Feb  9 18:04:33 1999
 @@ -0,0 +1,115 @@
 +/* drivers/atm/eni.h - Efficient Networks ENI155P device driver declarations */
 + 
@@ -3102,7 +3110,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/midway.h	Sun Nov  1 15:31:53 1998
++++ work/drivers/atm/midway.h	Tue Feb  9 15:49:57 1999
 @@ -0,0 +1,265 @@
 +/* drivers/atm/midway.h - Efficient Networks Midway (SAR) description */
 + 
@@ -3370,8 +3378,8 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/nicstar.h	Sun Nov  1 15:31:53 1998
-@@ -0,0 +1,753 @@
++++ work/drivers/atm/nicstar.h	Tue Feb  9 15:49:57 1999
+@@ -0,0 +1,746 @@
 +/******************************************************************************
 + *
 + * nicstar.h
@@ -3401,9 +3409,9 @@
 +
 +/* Options ********************************************************************/
 +
-+#define ESI_FROM_EPROM 1	/* Undef to use hardcoded ESI */
 +#define NS_MAX_CARDS 1		/* Maximum number of NICStAR based cards
-+				   controlled by the device driver*/
++				   controlled by the device driver. Must
++                                   be <= 5 */
 +
 +#undef RCQ_SUPPORT		/* Do not define this for now */
 +
@@ -3466,13 +3474,6 @@
 +
 +/* ESI stuff ******************************************************************/
 +
-+#define NS_ESI0 0x00	/* Hardcoded ESI, in case your card doesn't have */
-+#define NS_ESI1 0x00	/* it on the EPROM */
-+#define NS_ESI2 0x00
-+#define NS_ESI3 0x00
-+#define NS_ESI4 0x00
-+#define NS_ESI5 0x00
-+
 +#define NICSTAR_EPROM_MAC_ADDR_OFFSET 0x6C
 +
 +
@@ -4126,13 +4127,13 @@
 +
 +#endif /* _LINUX_NICSTAR_H_ */
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/nicstar.c	Sun Nov  1 15:31:53 1998
-@@ -0,0 +1,2884 @@
++++ work/drivers/atm/nicstar.c	Tue Feb  9 15:49:57 1999
+@@ -0,0 +1,2945 @@
 +/******************************************************************************
 + *
 + * nicstar.c
 + *
-+ * Device driver supporting CBR for NICStAR based cards.
++ * Device driver supporting CBR for IDT 77211 "NICStAR" based cards.
 + *
 + * IMPORTANT: The included file nicstarmac.c was NOT WRITTEN BY ME.
 + *            It was taken from the frle-0.22 device driver.
@@ -4143,6 +4144,13 @@
 + *
 + * Author: Rui Prior
 + *
++ * URLs:
++ *   NICStAR driver homepage
++ *     http://jaguar.inescn.pt/~rprior/nicstar/nicstar.html
++ *
++ *   IDT 77211 documentation
++ *     http://www.idt.com/product_files/77211.html
++ *
 + * (C) INESC 1998
 + *
 + ******************************************************************************/
@@ -4227,6 +4235,10 @@
 +
 +#undef CEIL(d)
 +
++#ifndef ATM_SKB
++#define ATM_SKB(s) (&(s)->atm)
++#endif
++
 +
 +/* Version definition *********************************************************/
 +/*
@@ -4269,6 +4281,8 @@
 +static int ns_ioctl(struct atm_dev *dev, unsigned int cmd, void *arg);
 +static void which_list(ns_dev *card, struct sk_buff *skb);
 +static void ns_poll(unsigned long arg);
++static int ns_parse_mac(char *mac, unsigned char *esi);
++static short ns_h2i(char c);
 +
 +
 +/* Global variables ***********************************************************/
@@ -4294,6 +4308,24 @@
 +   ns_proc_read		/* proc_read */
 +};
 +static struct timer_list ns_timer;
++static char *mac[NS_MAX_CARDS] = { NULL
++#if NS_MAX_CARDS > 1
++                                 , NULL
++#endif /* NS_MAX_CARDS > 1 */
++#if NS_MAX_CARDS > 2
++                                 , NULL
++#endif /* NS_MAX_CARDS > 2 */
++#if NS_MAX_CARDS > 3
++                                 , NULL
++#endif /* NS_MAX_CARDS > 3 */
++#if NS_MAX_CARDS > 4
++                                 , NULL
++#endif /* NS_MAX_CARDS > 4 */
++                                        };
++
++#ifdef MODULE
++MODULE_PARM(mac, "1-" __MODULE_STRING(NS_MAX_CARDS) "s");
++#endif /* MODULE */
 +
 +
 +/* Functions*******************************************************************/
@@ -4326,7 +4358,7 @@
 +
 +      error = ns_init_card(i, pcidev);
 +      if (error)
-+         i--;		/* Try to find another card but don't increment index */
++         cards[i--] = NULL;	/* Try to find another card but don't increment index */
 +   }
 +
 +   if (i == 0)
@@ -4464,7 +4496,7 @@
 +
 +      error = ns_init_card(i, pcidev);
 +      if (error)
-+         i--;		/* Try to find another card but don't increment index */
++         cards[i--] = NULL;	/* Try to find another card but don't increment index */
 +   }
 +
 +   if (i == 0 && error)
@@ -4591,7 +4623,7 @@
 +         if (pci_write_config_byte(pcidev, PCI_LATENCY_TIMER, NS_PCI_LATENCY) != 0);
 +	    break;
 +      }
-+      if (j == 10)
++      if (j == 4)
 +      {
 +         printk("nicstar%d: can't set PCI latency timer to %d.\n", i, NS_PCI_LATENCY);
 +         error = 7;
@@ -4624,38 +4656,36 @@
 +   writel(NS_CMD_READ_UTILITY | 0x00000200, card->membase + CMD);
 +   while (CMD_BUSY(card));
 +   data = readl(card->membase + DR0);
-+   if (data == 0x00000009)
-+   {
-+      printk("nicstar%d: PHY seems to be 25 Mbps.\n", i);
-+      card->max_pcr = IDT_25_PCR;
-+      while(CMD_BUSY(card));
-+      writel(0x00000008, card->membase + DR0);
-+      writel(NS_CMD_WRITE_UTILITY | 0x00000200, card->membase + CMD);
-+      /* Clear an eventual pending interrupt */
-+      writel(NS_STAT_SFBQF, card->membase + STAT);
++   switch(data) {
++      case 0x00000009:
++         printk("nicstar%d: PHY seems to be 25 Mbps.\n", i);
++         card->max_pcr = IDT_25_PCR;
++         while(CMD_BUSY(card));
++         writel(0x00000008, card->membase + DR0);
++         writel(NS_CMD_WRITE_UTILITY | 0x00000200, card->membase + CMD);
++         /* Clear an eventual pending interrupt */
++         writel(NS_STAT_SFBQF, card->membase + STAT);
 +#ifdef PHY_LOOPBACK
-+      while(CMD_BUSY(card));
-+      writel(0x00000022, card->membase + DR0);
-+      writel(NS_CMD_WRITE_UTILITY | 0x00000202, card->membase + CMD);
++         while(CMD_BUSY(card));
++         writel(0x00000022, card->membase + DR0);
++         writel(NS_CMD_WRITE_UTILITY | 0x00000202, card->membase + CMD);
 +#endif /* PHY_LOOPBACK */
-+   }
-+   else if (data == 0x00000030)
-+   {
-+      printk("nicstar%d: PHY seems to be 155 Mbps.\n", i);
-+      card->max_pcr = ATM_OC3_PCR;
++	 break;
++      case 0x00000030:
++      case 0x00000031:
++         printk("nicstar%d: PHY seems to be 155 Mbps.\n", i);
++         card->max_pcr = ATM_OC3_PCR;
 +#ifdef PHY_LOOPBACK
-+      while(CMD_BUSY(card));
-+      writel(0x00000002, card->membase + DR0);
-+      writel(NS_CMD_WRITE_UTILITY | 0x00000205, card->membase + CMD);
++         while(CMD_BUSY(card));
++         writel(0x00000002, card->membase + DR0);
++         writel(NS_CMD_WRITE_UTILITY | 0x00000205, card->membase + CMD);
 +#endif /* PHY_LOOPBACK */
-+   }
-+   else
-+   {
-+      printk("nicstar%d: can't determine PHY type.\n", i);
-+      error = 8;
-+      ns_init_card_error(card, error);
-+      return error;
-+   }
++	 break;
++      default:
++         printk("nicstar%d: unknown PHY type (0x%08X).\n", i, data);
++         error = 8;
++         ns_init_card_error(card, error);
++         return error; }
 +   writel(0x00000000, card->membase + GP);
 +
 +   /* Determine SRAM size */
@@ -4691,10 +4721,9 @@
 +   else /* card->rct_size == 16384 */
 +      card->vcibits = 14 - NS_VPIBITS;
 +
-+#ifdef ESI_FROM_EPROM
 +   /* Initialize the nicstar eeprom/eprom stuff, for the MAC addr */
-+   nicstar_init_eprom(card->membase);
-+#endif /* ESI_FROM_EPROM */
++   if (mac[i] == NULL)
++      nicstar_init_eprom(card->membase);
 +
 +   if (request_irq(pcidev->irq, &ns_irq_handler, SA_INTERRUPT, "nicstar", card) != 0)
 +   {
@@ -4820,35 +4849,25 @@
 +   
 +   card->efbie = 1;	/* To prevent push_rxbufs from enabling the interrupt */
 +
-+   /* Allocate small buffers */
-+   skb_queue_head_init(&card->sbpool.queue);
-+   card->sbpool.count = 0;			/* Not used */
-+   for (j = 0; j < NUM_SB; j++)
++   /* Pre-allocate some huge buffers */
++   skb_queue_head_init(&card->hbpool.queue);
++   card->hbpool.count = 0;
++   for (j = 0; j < NUM_HB; j++)
 +   {
-+      struct sk_buff *sb;
-+      sb = alloc_skb(NS_SMSKBSIZE, GFP_KERNEL);
-+      if (sb == NULL)
++      struct sk_buff *hb;
++      hb = alloc_skb(NS_HBUFSIZE, GFP_KERNEL);
++      if (hb == NULL)
 +      {
-+         printk("nicstar%d: can't allocate %dth of %d small buffers.\n",
-+                i, j, NUM_SB);
++         printk("nicstar%d: can't allocate %dth of %d huge buffers.\n",
++                i, j, NUM_HB);
 +         error = 13;
 +         ns_init_card_error(card, error);
 +	 return error;
 +      }
-+      skb_queue_tail(&card->sbpool.queue, sb);
-+      skb_reserve(sb, NS_AAL0_HEADER);
-+      push_rxbufs(card, BUF_SM, (u32) sb, (u32) virt_to_bus(sb->data), 0, 0);
-+   }
-+   /* Test for strange behaviour which leads to crashes */
-+   if ((bcount = ns_stat_sfbqc_get(readl(card->membase + STAT))) < card->sbnr.min)
-+   {
-+      printk("nicstar%d: Strange... Just allocated %d small buffers and sfbqc = %d.\n",
-+             i, j, bcount);
-+      error = 13;
-+      ns_init_card_error(card, error);
-+      return error;
++      skb_queue_tail(&card->hbpool.queue, hb);
++      card->hbpool.count++;
 +   }
-+      
++
 +
 +   /* Allocate large buffers */
 +   skb_queue_head_init(&card->lbpool.queue);
@@ -4886,6 +4905,36 @@
 +   }
 +      
 +
++   /* Allocate small buffers */
++   skb_queue_head_init(&card->sbpool.queue);
++   card->sbpool.count = 0;			/* Not used */
++   for (j = 0; j < NUM_SB; j++)
++   {
++      struct sk_buff *sb;
++      sb = alloc_skb(NS_SMSKBSIZE, GFP_KERNEL);
++      if (sb == NULL)
++      {
++         printk("nicstar%d: can't allocate %dth of %d small buffers.\n",
++                i, j, NUM_SB);
++         error = 15;
++         ns_init_card_error(card, error);
++	 return error;
++      }
++      skb_queue_tail(&card->sbpool.queue, sb);
++      skb_reserve(sb, NS_AAL0_HEADER);
++      push_rxbufs(card, BUF_SM, (u32) sb, (u32) virt_to_bus(sb->data), 0, 0);
++   }
++   /* Test for strange behaviour which leads to crashes */
++   if ((bcount = ns_stat_sfbqc_get(readl(card->membase + STAT))) < card->sbnr.min)
++   {
++      printk("nicstar%d: Strange... Just allocated %d small buffers and sfbqc = %d.\n",
++             i, j, bcount);
++      error = 15;
++      ns_init_card_error(card, error);
++      return error;
++   }
++      
++
 +   /* Allocate iovec buffers */
 +   skb_queue_head_init(&card->iovpool.queue);
 +   card->iovpool.count = 0;
@@ -4897,7 +4946,7 @@
 +      {
 +         printk("nicstar%d: can't allocate %dth of %d iovec buffers.\n",
 +                i, j, NUM_IOVB);
-+         error = 15;
++         error = 16;
 +         ns_init_card_error(card, error);
 +	 return error;
 +      }
@@ -4906,25 +4955,6 @@
 +   }
 +
 +
-+   /* Pre-allocate some huge buffers */
-+   skb_queue_head_init(&card->hbpool.queue);
-+   card->hbpool.count = 0;
-+   for (j = 0; j < NUM_HB; j++)
-+   {
-+      struct sk_buff *hb;
-+      hb = alloc_skb(NS_HBUFSIZE, GFP_KERNEL);
-+      if (hb == NULL)
-+      {
-+         printk("nicstar%d: can't allocate %dth of %d huge buffers.\n",
-+                i, j, NUM_HB);
-+         error = 16;
-+         ns_init_card_error(card, error);
-+	 return error;
-+      }
-+      skb_queue_tail(&card->hbpool.queue, hb);
-+      card->hbpool.count++;
-+   }
-+
 +   card->in_handler = 0;
 +   card->in_poll = 0;
 +   card->intcnt = 0;
@@ -4961,20 +4991,13 @@
 +      return error;
 +   }
 +      
-+#ifdef ESI_FROM_EPROM
-+   nicstar_read_eprom(card->membase, NICSTAR_EPROM_MAC_ADDR_OFFSET,
-+                      card->atmdev->esi, 6);
++   if (ns_parse_mac(mac[i], card->atmdev->esi))
++      nicstar_read_eprom(card->membase, NICSTAR_EPROM_MAC_ADDR_OFFSET,
++                         card->atmdev->esi, 6);
++
 +   printk("nicstar%d: MAC address %02X:%02X:%02X:%02X:%02X:%02X\n", i,
 +          card->atmdev->esi[0], card->atmdev->esi[1], card->atmdev->esi[2],
 +          card->atmdev->esi[3], card->atmdev->esi[4], card->atmdev->esi[5]);
-+#else
-+   card->atmdev->esi[0] = NS_ESI0;
-+   card->atmdev->esi[1] = NS_ESI1;
-+   card->atmdev->esi[2] = NS_ESI2;
-+   card->atmdev->esi[3] = NS_ESI3;
-+   card->atmdev->esi[4] = NS_ESI4;
-+   card->atmdev->esi[5] = NS_ESI5;
-+#endif /* ESI_FROM_EPROM */
 +
 +   card->atmdev->dev_data = card;
 +   card->atmdev->ci_range.vpi_bits = card->vpibits;
@@ -4995,16 +5018,17 @@
 +   }
 +   if (error >= 16)
 +   {
-+      struct sk_buff *hb;
-+      while ((hb = skb_dequeue(&card->hbpool.queue)) != NULL)
-+         kfree_skb(hb);
-+   }
-+   if (error >= 15)
-+   {
 +      struct sk_buff *iovb;
 +      while ((iovb = skb_dequeue(&card->iovpool.queue)) != NULL)
 +         kfree_skb(iovb);
 +   }
++   if (error >= 15)
++   {
++      struct sk_buff *sb;
++      while ((sb = skb_dequeue(&card->sbpool.queue)) != NULL)
++         kfree_skb(sb);
++      free_scq(card->scq0, NULL);
++   }
 +   if (error >= 14)
 +   {
 +      struct sk_buff *lb;
@@ -5013,10 +5037,9 @@
 +   }
 +   if (error >= 13)
 +   {
-+      struct sk_buff *sb;
-+      while ((sb = skb_dequeue(&card->sbpool.queue)) != NULL)
-+         kfree_skb(sb);
-+      free_scq(card->scq0, NULL);
++      struct sk_buff *hb;
++      while ((hb = skb_dequeue(&card->hbpool.queue)) != NULL)
++         kfree_skb(hb);
 +   }
 +   if (error >= 12)
 +   {
@@ -5943,8 +5966,7 @@
 +   {
 +      save_flags(flags); cli();
 +      scq->full = 1;
-+      current->timeout = jiffies + SCQFULL_TIMEOUT;
-+      interruptible_sleep_on(&scq->scqfull_waitq);
++      interruptible_sleep_on_timeout(&scq->scqfull_waitq, SCQFULL_TIMEOUT);
 +      restore_flags(flags);
 +
 +      if (scq->full)
@@ -5978,8 +6000,7 @@
 +      {
 +         save_flags(flags); cli();
 +         scq->full = 1;
-+         current->timeout = jiffies + SCQFULL_TIMEOUT;
-+         interruptible_sleep_on(&scq->scqfull_waitq);
++         interruptible_sleep_on_timeout(&scq->scqfull_waitq, SCQFULL_TIMEOUT);
 +         restore_flags(flags);
 +      }
 +
@@ -6984,6 +7005,7 @@
 +      if (card->in_poll)
 +      {
 +         printk("nicstar: Re-entering ns_poll()???\n");
++         restore_flags(flags);
 +         continue;
 +      }
 +      card->in_poll = 1;
@@ -6992,6 +7014,7 @@
 +         card->in_poll = 0;
 +         printk("nicstar%d: ns_poll called while in interrupt handler!?\n",
 +                card->index);
++         restore_flags(flags);
 +         continue;
 +      }
 +
@@ -7012,8 +7035,47 @@
 +   mod_timer(&ns_timer, jiffies + NS_POLL_PERIOD);
 +   PRINTK("nicstar: Leaving ns_poll().\n");
 +}
++
++
++
++static int ns_parse_mac(char *mac, unsigned char *esi)
++{
++   int i, j;
++   short byte1, byte0;
++
++   if (mac == NULL || esi == NULL)
++      return -1;
++   j = 0;
++   for (i = 0; i < 6; i++)
++   {
++      if ((byte1 = ns_h2i(mac[j++])) < 0)
++         return -1;
++      if ((byte0 = ns_h2i(mac[j++])) < 0)
++         return -1;
++      esi[i] = (unsigned char) (byte1 * 16 + byte0);
++      if (i < 5)
++      {
++         if (mac[j++] != ':')
++            return -1;
++      }
++   }
++   return 0;
++}
++
++
++
++static short ns_h2i(char c)
++{
++   if (c >= '0' && c <= '9')
++      return (short) (c - '0');
++   if (c >= 'A' && c <= 'A')
++      return (short) (c - 'A' + 10);
++   if (c >= 'a' && c <= 'f')
++      return (short) (c - 'a' + 10);
++   return -1;
++}
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/nicstar.c.old_skb	Sun Nov  1 15:31:53 1998
++++ work/drivers/atm/nicstar.c.old_skb	Tue Feb  9 15:49:57 1999
 @@ -0,0 +1,2883 @@
 +/******************************************************************************
 + *
@@ -9899,7 +9961,7 @@
 +   PRINTK("nicstar: Leaving ns_poll().\n");
 +}
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/nicstarmac.h	Sun Nov  1 15:31:53 1998
++++ work/drivers/atm/nicstarmac.h	Tue Feb  9 15:49:57 1999
 @@ -0,0 +1,14 @@
 +/******************************************************************************
 + *
@@ -9916,7 +9978,7 @@
 +void nicstar_init_eprom( virt_addr_t base );
 +void nicstar_read_eprom( virt_addr_t, u_int8_t, u_int8_t *, u_int32_t);
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/nicstarmac.c	Sun Nov  1 15:31:53 1998
++++ work/drivers/atm/nicstarmac.c	Tue Feb  9 15:49:57 1999
 @@ -0,0 +1,269 @@
 +/*
 + * this file included by nicstar.c
@@ -10188,7 +10250,7 @@
 +*/
 +
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/nicstarmac.copyright	Sun Nov  1 15:31:53 1998
++++ work/drivers/atm/nicstarmac.copyright	Tue Feb  9 15:49:57 1999
 @@ -0,0 +1,61 @@
 +/* nicstar.c  v0.22  Jawaid Bazyar (bazyar@hypermall.com)
 + * nicstar.c, M. Welsh (matt.welsh@cl.cam.ac.uk)
@@ -10252,7 +10314,7 @@
 + *
 + */
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/suni.c	Sun Nov  1 15:31:53 1998
++++ work/drivers/atm/suni.c	Tue Feb  9 15:49:57 1999
 @@ -0,0 +1,303 @@
 +/* drivers/atm/suni.c - PMC SUNI (PHY) driver */
 + 
@@ -10558,7 +10620,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/suni.h	Sun Nov  1 15:31:53 1998
++++ work/drivers/atm/suni.h	Tue Feb  9 18:04:31 1999
 @@ -0,0 +1,210 @@
 +/* drivers/atm/suni.h - PMC SUNI (PHY) declarations */
 + 
@@ -10771,7 +10833,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/tonga.h	Sun Nov  1 15:31:53 1998
++++ work/drivers/atm/tonga.h	Tue Feb  9 15:49:57 1999
 @@ -0,0 +1,20 @@
 +/* drivers/atm/tonga.h - Efficient Networks Tonga (PCI bridge) declarations */
 + 
@@ -10794,7 +10856,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/uPD98401.h	Sun Nov  1 15:31:53 1998
++++ work/drivers/atm/uPD98401.h	Tue Feb  9 15:49:57 1999
 @@ -0,0 +1,292 @@
 +/* drivers/atm/uPD98401.h - NEC uPD98401 (SAR) declarations */
 + 
@@ -11089,8 +11151,8 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/uPD98402.c	Sun Nov  1 15:31:53 1998
-@@ -0,0 +1,224 @@
++++ work/drivers/atm/uPD98402.c	Tue Feb  9 15:49:57 1999
+@@ -0,0 +1,225 @@
 +/* drivers/atm/uPD98402.c - NEC uPD98402 (PHY) declarations */
 + 
 +/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */
@@ -11254,10 +11316,11 @@
 +			(void) GET(PCOCR); /* clear interrupt cause */
 +			PRIV(dev)->sonet_stats.uncorr_hcs += GET(HECCT);
 +		}
-+		if ((reason & uPD98402_INT_RFO) && jiffies > silence) {
++		if ((reason & uPD98402_INT_RFO) && 
++		    (time_after(jiffies, silence) || silence == 0)) {
 +			printk(KERN_WARNING "%s(itf %d): uPD98402 receive "
 +			    "FIFO overflow\n",dev->type,dev->number);
-+			silence = jiffies+HZ/2;
++			silence = (jiffies+HZ/2)|1;
 +		}
 +	}
 +}
@@ -11316,7 +11379,7 @@
 + 
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/uPD98402.h	Sun Nov  1 15:31:53 1998
++++ work/drivers/atm/uPD98402.h	Tue Feb  9 15:49:57 1999
 @@ -0,0 +1,106 @@
 +/* drivers/atm/uPD98402.h - NEC uPD98402 (PHY) declarations */
 + 
@@ -11425,8 +11488,8 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/zatm.c	Sun Nov  1 15:31:53 1998
-@@ -0,0 +1,1875 @@
++++ work/drivers/atm/zatm.c	Tue Feb  9 15:49:57 1999
+@@ -0,0 +1,1876 @@
 +/* drivers/atm/zatm.c - ZeitNet ZN122x device driver */
 + 
 +/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */
@@ -12041,12 +12104,13 @@
 +			static unsigned long silence = 0;
 +			static int last_error = 0;
 +
-+			if (error != last_error || jiffies > silence) {
++			if (error != last_error ||
++			    time_after(jiffies, silence)  || silence == 0){
 +				printk(KERN_WARNING DEV_LABEL "(itf %d): "
 +				    "chan %d error %s\n",dev->number,chan,
 +				    err_txt[error]);
 +				last_error = error;
-+				silence = jiffies+2*HZ;
++				silence = (jiffies+2*HZ)|1;
 +			}
 +			size = 0;
 +		}
@@ -13303,7 +13367,7 @@
 + 
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/zatm.h	Sun Nov  1 15:31:53 1998
++++ work/drivers/atm/zatm.h	Tue Feb  9 18:04:37 1999
 @@ -0,0 +1,137 @@
 +/* drivers/atm/zatm.h - ZeitNet ZN122x device driver declarations */
 +
@@ -13443,7 +13507,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/drivers/atm/zeprom.h	Sun Nov  1 15:31:53 1998
++++ work/drivers/atm/zeprom.h	Tue Feb  9 15:49:57 1999
 @@ -0,0 +1,34 @@
 +/* drivers/atm/zeprom.h - ZeitNet ZN122x EEPROM (NM93C46) declarations */
 +
@@ -13479,17 +13543,17 @@
 +/* No other commands are needed. */
 +
 +#endif
---- ref/drivers/block/genhd.c	Tue Oct 20 22:52:01 1998
-+++ work/drivers/block/genhd.c	Sun Nov  1 15:31:53 1998
+--- ref/drivers/block/genhd.c	Sat Jan  9 07:54:17 1999
++++ work/drivers/block/genhd.c	Tue Feb  9 15:49:57 1999
 @@ -60,6 +60,7 @@
  extern int blk_dev_init(void);
  extern int scsi_dev_init(void);
  extern int net_dev_init(void);
 +extern int atmdev_init(void);
  
- /*
-  * disk_name() is used by genhd.c and md.c.
-@@ -1207,6 +1208,9 @@
+ #ifdef CONFIG_PPC
+ extern void note_bootable_part(kdev_t dev, int part);
+@@ -1310,6 +1311,9 @@
  #endif
  #ifdef CONFIG_INET
  	net_dev_init();
@@ -13500,7 +13564,7 @@
  #ifdef CONFIG_VT
  	console_map_init();
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/include/linux/arequipa.h	Sun Nov  1 15:31:53 1998
++++ work/include/linux/arequipa.h	Tue Feb  9 18:09:24 1999
 @@ -0,0 +1,63 @@
 +/* arequipa.h - Arequipa interface definitions */
 + 
@@ -13566,8 +13630,8 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/include/linux/atm.h	Sun Nov  1 15:31:53 1998
-@@ -0,0 +1,239 @@
++++ work/include/linux/atm.h	Tue Feb  9 17:40:13 1999
+@@ -0,0 +1,245 @@
 +/* atm.h - general ATM declarations */
 + 
 +/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */
@@ -13750,6 +13814,12 @@
 +#define ATM_AFI_DCC	0x39		/* DCC ATM Format */
 +#define ATM_AFI_ICD	0x47		/* ICD ATM Format */
 +#define ATM_AFI_E164	0x45		/* E.164 ATM Format */
++#define ATM_AFI_LOCAL	0x49		/* Local ATM Format */ 
++
++#define ATM_AFI_DCC_GROUP	0xBD	/* DCC ATM Group Format */
++#define ATM_AFI_ICD_GROUP	0xC5	/* ICD ATM Group Format */
++#define ATM_AFI_E164_GROUP	0xC3	/* E.164 ATM Group Format */
++#define ATM_AFI_LOCAL_GROUP	0xC7	/* Local ATM Group Format */
 +
 +#define ATM_LIJ_NONE	0		/* no leaf-initiated join */
 +#define ATM_LIJ		1		/* request joining */
@@ -13808,7 +13878,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/include/linux/atm_eni.h	Sun Nov  1 15:31:53 1998
++++ work/include/linux/atm_eni.h	Tue Feb  9 18:04:33 1999
 @@ -0,0 +1,15 @@
 +/* atm_eni.h - Driver-specific declarations of the ENI driver (for use by
 +	       driver-specific utilities) */
@@ -13826,7 +13896,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/include/linux/atm_nicstar.h	Sun Nov  1 15:31:53 1998
++++ work/include/linux/atm_nicstar.h	Tue Feb  9 15:49:57 1999
 @@ -0,0 +1,52 @@
 +/******************************************************************************
 + *
@@ -13881,7 +13951,7 @@
 +
 +#endif /* LINUX_ATM_NICSTAR_H */
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/include/linux/atm_suni.h	Sun Nov  1 15:31:53 1998
++++ work/include/linux/atm_suni.h	Tue Feb  9 18:04:31 1999
 @@ -0,0 +1,19 @@
 +/* atm_suni.h - Driver-specific declarations of the SUNI driver (for use by
 +		driver-specific utilities) */
@@ -13903,7 +13973,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/include/linux/atm_tcp.h	Sun Nov  1 15:31:53 1998
++++ work/include/linux/atm_tcp.h	Tue Feb  9 15:49:58 1999
 @@ -0,0 +1,46 @@
 +/* atm_tcp.h - Driver-specific declarations of the ATMTCP driver (for use by
 +	       driver-specific utilities) */
@@ -13952,7 +14022,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/include/linux/atm_zatm.h	Sun Nov  1 15:31:53 1998
++++ work/include/linux/atm_zatm.h	Tue Feb  9 15:49:58 1999
 @@ -0,0 +1,54 @@
 +/* atm_zatm.h - Driver-specific declarations of the ZATM driver (for use by
 +		driver-specific utilities) */
@@ -14009,7 +14079,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/include/linux/atmarp.h	Sun Nov  1 15:31:53 1998
++++ work/include/linux/atmarp.h	Tue Feb  9 15:49:58 1999
 @@ -0,0 +1,42 @@
 +/* atmarp.h - ATM ARP protocol and kernel-demon interface definitions */
 + 
@@ -14054,7 +14124,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/include/linux/atmclip.h	Sun Nov  1 15:31:53 1998
++++ work/include/linux/atmclip.h	Tue Feb  9 15:49:58 1999
 @@ -0,0 +1,25 @@
 +/* atmclip.h - Classical IP over ATM */
 + 
@@ -14082,7 +14152,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/include/linux/atmdev.h	Sun Nov  1 15:31:53 1998
++++ work/include/linux/atmdev.h	Tue Feb  9 18:04:31 1999
 @@ -0,0 +1,324 @@
 +/* atmdev.h - ATM device driver declarations */
 + 
@@ -14105,7 +14175,7 @@
 +			   bits per cell:  /8/53
 +			   max cell rate:  353207.547 cells/sec */
 +
-+#define ATM_SD(s)	((struct atm_vcc *) ((s)->sk))
++#define ATM_SD(s)	((s)->sk->protinfo.af_atm)
 +
 +
 +struct atm_aal_stats {
@@ -14409,7 +14479,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/include/linux/atmioc.h	Sun Nov  1 15:31:53 1998
++++ work/include/linux/atmioc.h	Tue Feb  9 15:49:58 1999
 @@ -0,0 +1,39 @@
 +/* atmioc.h - ranges for ATM-related ioctl numbers */
 + 
@@ -14451,8 +14521,8 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/include/linux/atmlec.h	Sun Nov  1 15:31:53 1998
-@@ -0,0 +1,70 @@
++++ work/include/linux/atmlec.h	Tue Feb  9 18:09:24 1999
+@@ -0,0 +1,78 @@
 +/*
 + * 
 + * ATM Lan Emulation Daemon vs. driver interface
@@ -14473,7 +14543,7 @@
 +#define ATMLEC_MCAST _IO('a',ATMIOC_LANE+2)
 +
 +/* Maximum number of LEC interfaces (tweakable) */
-+#define MAX_LEC_ITF 4
++#define MAX_LEC_ITF 16
 +
 +typedef enum { 
 +        l_set_mac_addr,   l_del_mac_addr, 
@@ -14483,10 +14553,11 @@
 +        l_narp_req, /* LANE2 mandates the use of this */
 +        l_config,         l_flush_tran_id, 
 +        l_set_lecid,      l_arp_xmt,
-+        l_associate_req
++        l_associate_req,
++        l_should_bridge   /* should we bridge this MAC? */
 +} atmlec_msg_type;
 +
-+#define ATMLEC_MSG_TYPE_MAX l_associate_req
++#define ATMLEC_MSG_TYPE_MAX l_should_bridge
 +
 +struct atmlec_config_msg {
 +        unsigned int maximum_unknown_frame_count;
@@ -14498,6 +14569,7 @@
 +        unsigned long flush_timeout;
 +        unsigned long path_switching_delay;
 +        unsigned int  lane_version; /* LANE2: 1 for LANEv1, 2 for LANEv2 */
++        int is_proxy;               /* bridging support */
 +};
 + 
 +struct atmlec_msg {
@@ -14514,7 +14586,13 @@
 +                        unsigned int no_source_le_narp; /* LANE2 */
 +                } normal;
 +                struct atmlec_config_msg config;
-+        } content;
++                struct {
++                        uint16_t lec_id;                     /* requestor lec_id  */
++                        uint32_t tran_id;                    /* transaction id    */
++                        unsigned char mac_addr[ETH_ALEN];    /* dst mac addr      */
++                        unsigned char atm_addr[ATM_ESA_LEN]; /* reqestor ATM addr */
++                } proxy; /* For mapping LE_ARP requests to responses. Filled by   */
++        } content;       /* zeppelin, returned by kernel. Used only when proxying */ 
 +};
 +
 +struct atmlec_ioc {
@@ -14524,7 +14602,7 @@
 +};
 +#endif /* _ATMLEC_H_ */
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/include/linux/atmmpc.h	Sat Dec  5 01:45:34 1998
++++ work/include/linux/atmmpc.h	Tue Feb  9 18:09:24 1999
 @@ -0,0 +1,124 @@
 +#ifndef _ATMMPC_H_
 +#define _ATMMPC_H_
@@ -14651,7 +14729,7 @@
 +#endif /* _ATMMPC_H_ */
 +
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/include/linux/atmsap.h	Sun Nov  1 15:31:53 1998
++++ work/include/linux/atmsap.h	Tue Feb  9 15:49:58 1999
 @@ -0,0 +1,161 @@
 +/* atmsap.h - ATM Service Access Point addressing definitions */
 +
@@ -14815,7 +14893,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/include/linux/atmsvc.h	Sun Nov  1 15:31:53 1998
++++ work/include/linux/atmsvc.h	Tue Feb  9 18:09:24 1999
 @@ -0,0 +1,52 @@
 +/* atmsvc.h - ATM signaling kernel-demon interface definitions */
 + 
@@ -14869,8 +14947,8 @@
 +  (tp).max_pcr : (tp).min_pcr ? (tp).min_pcr : ATM_MAX_PCR)
 +
 +#endif
---- ref/include/linux/capability.h	Fri Oct 23 19:14:47 1998
-+++ work/include/linux/capability.h	Sun Nov  1 15:34:05 1998
+--- ref/include/linux/capability.h	Thu Jan 28 21:43:10 1999
++++ work/include/linux/capability.h	Tue Feb  9 18:02:00 1999
 @@ -131,6 +131,7 @@
  #define CAP_LINUX_IMMUTABLE  9
  
@@ -14887,8 +14965,8 @@
  
  #define CAP_NET_ADMIN        12
  
---- ref/include/linux/if_arp.h	Fri Oct 23 19:16:30 1998
-+++ work/include/linux/if_arp.h	Sun Nov  1 15:31:53 1998
+--- ref/include/linux/if_arp.h	Thu Jan 28 21:43:16 1999
++++ work/include/linux/if_arp.h	Tue Feb  9 18:03:14 1999
 @@ -35,6 +35,7 @@
  #define	ARPHRD_ARCNET	7		/* ARCnet			*/
  #define	ARPHRD_APPLETLK	8		/* APPLEtalk			*/
@@ -14897,7 +14975,7 @@
  #define ARPHRD_METRICOM	23		/* Metricom STRIP (new IANA id)	*/
  
  /* Dummy types for non ARP hardware */
-@@ -71,6 +72,9 @@
+@@ -72,6 +73,9 @@
  #define	ARPOP_REPLY	2		/* ARP reply			*/
  #define	ARPOP_RREQUEST	3		/* RARP request			*/
  #define	ARPOP_RREPLY	4		/* RARP reply			*/
@@ -14908,7 +14986,7 @@
  
  /* ARP ioctl request. */
 --- ref/include/linux/pkt_sched.h	Tue Apr 28 20:10:10 1998
-+++ work/include/linux/pkt_sched.h	Sun Nov  1 15:31:53 1998
++++ work/include/linux/pkt_sched.h	Tue Feb  9 15:49:58 1999
 @@ -274,4 +274,17 @@
  
  #define TCA_CBQ_MAX	TCA_CBQ_POLICE
@@ -14927,8 +15005,8 @@
 +#define TCA_ATM_MAX	TCA_ATM_ADDR
 +
  #endif
---- ref/include/linux/skbuff.h	Fri Oct 23 19:15:48 1998
-+++ work/include/linux/skbuff.h	Sun Nov  1 15:34:09 1998
+--- ref/include/linux/skbuff.h	Thu Jan 28 21:43:15 1999
++++ work/include/linux/skbuff.h	Tue Feb  9 18:02:02 1999
 @@ -112,6 +112,25 @@
  		__u32	ifield;
  	} private;
@@ -14956,7 +15034,7 @@
  
  /* These are just the default values. This is run time configurable.
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/include/linux/sonet.h	Sun Nov  1 15:31:53 1998
++++ work/include/linux/sonet.h	Tue Feb  9 15:49:58 1999
 @@ -0,0 +1,52 @@
 +/* sonet.h - SONET/SHD physical layer control */
 + 
@@ -15011,7 +15089,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/include/net/atmclip.h	Sun Nov  1 15:31:53 1998
++++ work/include/net/atmclip.h	Tue Feb  9 18:08:04 1999
 @@ -0,0 +1,62 @@
 +/* net/atm/atmarp.h - RFC1577 ATM ARP */
 + 
@@ -15075,8 +15153,31 @@
 +int clip_encap(struct atm_vcc *vcc,int mode);
 +
 +#endif
---- ref/net/Config.in	Sun Sep 13 19:22:17 1998
-+++ work/net/Config.in	Sun Nov  1 15:31:53 1998
+--- ref/include/net/sock.h	Thu Jan 28 21:43:16 1999
++++ work/include/net/sock.h	Tue Feb  9 18:03:17 1999
+@@ -81,6 +81,10 @@
+ #include <net/dn.h>
+ #endif
+ 
++#if defined(CONFIG_ATM) || defined(CONFIG_ATM_MODULE)
++struct atm_vcc;
++#endif
++
+ #ifdef CONFIG_FILTER
+ #include <linux/filter.h>
+ #endif
+@@ -492,6 +496,9 @@
+ #endif
+ #if defined(CONFIG_ECONET) || defined(CONFIG_ECONET_MODULE)
+ 		struct econet_opt	*af_econet;
++#endif
++#if defined(CONFIG_ATM) || defined(CONFIG_ATM_MODULE)
++		struct atm_vcc		*af_atm;
+ #endif
+ 	} protinfo;  		
+ 
+--- ref/net/Config.in	Tue Dec 29 20:21:49 1998
++++ work/net/Config.in	Tue Feb  9 15:49:58 1999
 @@ -25,6 +25,27 @@
      fi
    fi
@@ -15105,18 +15206,18 @@
  
  comment ' '
  tristate 'The IPX protocol' CONFIG_IPX
---- ref/net/Makefile	Mon Jul 27 08:35:57 1998
-+++ work/net/Makefile	Sun Nov  1 15:31:53 1998
+--- ref/net/Makefile	Thu Dec 17 18:03:57 1998
++++ work/net/Makefile	Tue Feb  9 15:49:58 1999
 @@ -10,7 +10,7 @@
  MOD_SUB_DIRS := ipv4
  ALL_SUB_DIRS := 802 ax25 bridge core ethernet ipv4 ipv6 ipx unix appletalk \
  		netrom rose lapb x25 wanrouter netlink sched packet sunrpc \
--		econet #decnet
-+		econet atm #decnet
+-		econet irda #decnet
++		econet irda atm #decnet
  SUB_DIRS     := core ethernet sched
  MOD_LIST_NAME := NET_MISC_MODULES
  
-@@ -131,6 +131,17 @@
+@@ -139,6 +139,17 @@
    ifeq ($(CONFIG_SUNRPC),m)
    MOD_SUB_DIRS += sunrpc
    endif
@@ -15134,9 +15235,20 @@
  endif
  
  ifeq ($(CONFIG_DECNET),y)
---- ref/net/protocols.c	Fri Jul 10 22:51:41 1998
-+++ work/net/protocols.c	Sun Nov  1 15:31:53 1998
-@@ -79,6 +79,10 @@
+--- ref/net/netsyms.c	Thu Jan  7 18:26:42 1999
++++ work/net/netsyms.c	Tue Feb  9 17:15:25 1999
+@@ -214,6 +214,8 @@
+ 
+ #ifdef CONFIG_BRIDGE 
+ EXPORT_SYMBOL(br_ioctl);
++EXPORT_SYMBOL(port_info);
++EXPORT_SYMBOL(br_avl_find_addr);
+ #endif
+ 
+ #ifdef CONFIG_INET
+--- ref/net/protocols.c	Thu Dec 17 18:03:57 1998
++++ work/net/protocols.c	Tue Feb  9 15:49:58 1999
+@@ -87,6 +87,10 @@
  extern void rif_init(struct net_proto *);
  #endif
  
@@ -15147,7 +15259,7 @@
  #ifdef NEED_LLC
  #define NEED_802
  #include <net/llccall.h>
-@@ -113,6 +117,11 @@
+@@ -121,6 +125,11 @@
  #ifdef CONFIG_TR
    { "RIF",	rif_init },				/* RIF for Token ring		*/
  #endif  
@@ -15159,8 +15271,8 @@
  
  #ifdef NEED_LLC
    { "802.2LLC", llc_init },				/* 802.2 LLC */
---- ref/net/ipv4/arp.c	Wed Oct  7 20:13:49 1998
-+++ work/net/ipv4/arp.c	Sun Nov  1 15:31:53 1998
+--- ref/net/ipv4/arp.c	Wed Nov 18 18:06:05 1998
++++ work/net/ipv4/arp.c	Tue Feb  9 15:49:58 1999
 @@ -115,6 +115,9 @@
  #include <net/netrom.h>
  #endif
@@ -15171,7 +15283,7 @@
  
  #include <asm/system.h>
  #include <asm/uaccess.h>
-@@ -405,7 +408,11 @@
+@@ -402,7 +405,11 @@
  	if (dev == NULL)
  		return 0;
  	if (dst->neighbour == NULL)
@@ -15185,7 +15297,7 @@
  }
  
 --- ref/net/sched/Config.in	Thu May 14 19:26:23 1998
-+++ work/net/sched/Config.in	Sun Nov  1 15:31:53 1998
++++ work/net/sched/Config.in	Tue Feb  9 15:49:58 1999
 @@ -7,6 +7,9 @@
  tristate 'CSZ packet scheduler' CONFIG_NET_SCH_CSZ
  #tristate 'H-PFQ packet scheduler' CONFIG_NET_SCH_HPFQ
@@ -15197,7 +15309,7 @@
  tristate 'RED queue' CONFIG_NET_SCH_RED
  tristate 'SFQ queue' CONFIG_NET_SCH_SFQ
 --- ref/net/sched/Makefile	Tue Apr 28 20:10:11 1998
-+++ work/net/sched/Makefile	Sun Nov  1 15:31:53 1998
++++ work/net/sched/Makefile	Tue Feb  9 15:49:58 1999
 @@ -7,6 +7,8 @@
  #
  # Note 2! The CFLAGS definition is now in the main makefile...
@@ -15218,8 +15330,8 @@
  endif
  
  ifeq ($(CONFIG_NET_CLS_U32), y)
---- ref/net/sched/sch_api.c	Fri Aug 28 04:33:09 1998
-+++ work/net/sched/sch_api.c	Sun Nov  1 15:31:53 1998
+--- ref/net/sched/sch_api.c	Thu Jan  7 18:26:42 1999
++++ work/net/sched/sch_api.c	Tue Feb  9 15:49:58 1999
 @@ -987,6 +987,9 @@
  #ifdef CONFIG_NET_SCH_PRIO
  	INIT_QDISC(prio);
@@ -15231,7 +15343,7 @@
  	tc_filter_init();
  #endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/sched/sch_atm.c	Sun Nov  1 15:31:53 1998
++++ work/net/sched/sch_atm.c	Tue Feb  9 15:49:58 1999
 @@ -0,0 +1,611 @@
 +/* net/sched/sch_atm.c - ATM VC selection "queueing discipline" */
 +
@@ -15845,7 +15957,7 @@
 +}
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/Makefile	Sun Nov  1 15:31:53 1998
++++ work/net/atm/Makefile	Tue Feb  9 15:49:58 1999
 @@ -0,0 +1,64 @@
 +#
 +# Makefile for the ATM Protocol Families.
@@ -15912,7 +16024,7 @@
 +mpoa.o: mpc.o mpoa_caches.o mpoa_proc.o
 +	ld -r -o mpoa.o mpc.o mpoa_caches.o mpoa_proc.o
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/addr.c	Sun Nov  1 15:31:53 1998
++++ work/net/atm/addr.c	Tue Feb  9 15:49:58 1999
 @@ -0,0 +1,159 @@
 +/* net/atm/addr.c - Local ATM address registry */
 +
@@ -16074,7 +16186,7 @@
 +	return total;
 +}
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/addr.h	Sun Nov  1 15:31:53 1998
++++ work/net/atm/addr.h	Tue Feb  9 18:09:24 1999
 @@ -0,0 +1,18 @@
 +/* net/atm/addr.h - Local ATM address registry */
 +
@@ -16095,8 +16207,8 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/clip.c	Sun Nov  1 15:31:53 1998
-@@ -0,0 +1,652 @@
++++ work/net/atm/clip.c	Tue Feb  9 15:49:58 1999
+@@ -0,0 +1,654 @@
 +/* net/atm/clip.c - RFC1577 Classical IP over ATM */
 +
 +/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */
@@ -16227,15 +16339,16 @@
 +			for (clip_vcc = entry->vccs; clip_vcc;
 +			    clip_vcc = clip_vcc->next)
 +				if (clip_vcc->idle_timeout &&
-+				    clip_vcc->last_use+clip_vcc->idle_timeout <
-+				    jiffies) {
++				    time_after(jiffies, clip_vcc->last_use+
++				    clip_vcc->idle_timeout)) {
 +					DPRINTK("releasing vcc %p->%p of "
 +					    "entry %p\n",clip_vcc,clip_vcc->vcc,
 +					    entry);
 +					atm_async_release_vcc(clip_vcc->vcc,
 +					    -ETIMEDOUT);
 +				}
-+			if (entry->vccs || entry->expires > jiffies) {
++			if (entry->vccs ||
++			    time_before(jiffies, entry->expires)) {
 +				np = &n->next;
 +				continue;
 +			}
@@ -16451,7 +16564,8 @@
 +	}
 +	entry = NEIGH2ENTRY(skb->dst->neighbour);
 +	if (!entry->vccs) {
-+		if (entry->expires < jiffies) {/* should be resolved */
++		if (time_after(jiffies, entry->expires)) {
++			/* should be resolved */
 +			entry->expires = jiffies+ATMARP_RETRY_DELAY*HZ;
 +			to_atmarpd(act_need,PRIV(dev)->number,entry->ip);
 +		}
@@ -16750,8 +16864,8 @@
 +	return 0;
 +}
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/common.c	Sun Nov  1 15:31:53 1998
-@@ -0,0 +1,926 @@
++++ work/net/atm/common.c	Tue Feb  9 15:49:58 1999
+@@ -0,0 +1,928 @@
 +/* net/atm/common.c - ATM sockets (common part for PVC and SVC) */
 +
 +/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */
@@ -16773,6 +16887,7 @@
 +#include <linux/sched.h>
 +#include <linux/time.h>		/* struct timeval */
 +#include <linux/skbuff.h>
++#include <net/sock.h>		/* struct sock */
 +
 +#include <asm/uaccess.h>
 +#include <asm/poll.h>
@@ -16851,13 +16966,15 @@
 +}
 +
 +
-+int atm_create(struct socket *sock,int protocol)
++int atm_create(struct socket *sock,int protocol,int family)
 +{
++	struct sock *sk;
 +	struct atm_vcc *vcc;
 +
-+	ATM_SD(sock) = NULL;
++	sock->sk = NULL;
 +	if (sock->type == SOCK_STREAM) return -EINVAL;
-+	if (!(vcc = alloc_atm_vcc())) return -ENOMEM;
++	if (!(sk = alloc_atm_vcc_sk(family))) return -ENOMEM;
++	vcc = sk->protinfo.af_atm;
 +#ifdef CONFIG_AREQUIPA
 +	vcc->upper = NULL;
 +	vcc->sock = sock;
@@ -16882,15 +16999,17 @@
 +	vcc->sleep = vcc->wsleep = NULL;
 +	skb_queue_head_init(&vcc->recvq);
 +	skb_queue_head_init(&vcc->listenq);
-+	ATM_SD(sock) = vcc;
++	sock->sk = sk;
 +	return 0;
 +}
 +
 +
-+int atm_release_vcc(struct atm_vcc *vcc,int free_vcc)
++void atm_release_vcc_sk(struct sock *sk,int free_sk)
 +{
++	struct atm_vcc *vcc;
 +	struct sk_buff *skb;
 +
++	vcc = sk->protinfo.af_atm;
 +	vcc->flags &= ~ATM_VF_READY;
 +	if (vcc->dev) {
 +		if (vcc->dev->ops->close) vcc->dev->ops->close(vcc);
@@ -16907,18 +17026,15 @@
 +			    atomic_read(&vcc->rx_inuse));
 +		bind_vcc(vcc,NULL);
 +	}
-+	if (free_vcc) free_atm_vcc(vcc);
-+	return 0;
++	if (free_sk) free_atm_vcc_sk(sk);
 +}
 +
 +
 +int atm_release(struct socket *sock,struct socket *peer)
 +{
-+	struct atm_vcc *vcc;
-+
-+	vcc = ATM_SD(sock);
-+	if (!vcc) return 0;
-+	return atm_release_vcc(vcc,1);
++	if (sock->sk)
++		atm_release_vcc_sk(sock->sk,1);
++	return 0;
 +}
 +
 +
@@ -17679,7 +17795,7 @@
 +	return atm_do_getsockopt(sock,level,optname,optval,len);
 +}
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/common.h	Sun Nov  1 15:31:53 1998
++++ work/net/atm/common.h	Tue Feb  9 18:09:24 1999
 @@ -0,0 +1,46 @@
 +/* net/atm/common.h - ATM sockets (common part for PVC and SVC) */
 + 
@@ -17693,7 +17809,7 @@
 +#include <linux/poll.h> /* for poll_table */
 +
 +
-+int atm_create(struct socket *sock,int protocol);
++int atm_create(struct socket *sock,int protocol,int family);
 +int atm_release(struct socket *sock,struct socket *peer);
 +int atm_connect(struct socket *sock,int itf,short vpi,int vci);
 +int atm_recvmsg(struct socket *sock,struct msghdr *m,int total_len,
@@ -17708,7 +17824,7 @@
 +    int *optlen);
 +
 +int atm_connect_vcc(struct atm_vcc *vcc,int itf,short vpi,int vci);
-+int atm_release_vcc(struct atm_vcc *vcc,int free_vcc);
++void atm_release_vcc_sk(struct sock *sk,int free_sk);
 +int atm_change_qos(struct atm_vcc *vcc,struct atm_qos *qos);
 +/* -- now in atmdev.h:
 +void atm_async_release_vcc(struct atm_vcc *vcc,int reply);
@@ -17728,7 +17844,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/ipcommon.c	Sun Nov  1 15:31:53 1998
++++ work/net/atm/ipcommon.c	Tue Feb  9 15:49:58 1999
 @@ -0,0 +1,52 @@
 +/* net/atm/ipcommon.c - Common items for all ways of doing IP over ATM */
 +
@@ -17783,7 +17899,7 @@
 +	skb_queue_head_init(from);
 +}
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/ipcommon.h	Sun Nov  1 15:31:53 1998
++++ work/net/atm/ipcommon.h	Tue Feb  9 18:09:30 1999
 @@ -0,0 +1,21 @@
 +/* net/atm/ipcommon.h - Common items for all ways of doing IP over ATM */
 +
@@ -17807,7 +17923,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/misc.c	Sun Nov  1 15:31:53 1998
++++ work/net/atm/misc.c	Tue Feb  9 15:49:58 1999
 @@ -0,0 +1,184 @@
 +/* net/atm/misc.c - Various functions for use by ATM drivers */
 +
@@ -17994,8 +18110,8 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/lec.c	Sat Dec  5 01:49:36 1998
-@@ -0,0 +1,2054 @@
++++ work/net/atm/lec.c	Tue Feb  9 17:15:25 1999
+@@ -0,0 +1,2076 @@
 +/*
 + * lec.c: Lan Emulation driver 
 + * Marko Kiiskila carnil@cs.tut.fi
@@ -18021,6 +18137,11 @@
 +#include <linux/atmdev.h>
 +#include <linux/atmlec.h>
 +
++/* Bridge */
++#ifdef CONFIG_BRIDGE
++#include <net/br.h>
++#endif
++
 +/* Modular too */
 +#include <linux/config.h>
 +#include <linux/module.h>
@@ -18078,6 +18199,43 @@
 +        return &dev_lec[0];
 +}
 +
++#ifdef CONFIG_BRIDGE
++static void handle_bridge(struct sk_buff *skb, struct device *dev)
++{
++        struct ethhdr *eth;
++        char *buff;
++        struct lec_priv *priv;
++        unsigned char bridge_ula[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
++
++        /* Check if this is a BPDU. If so, ask zeppelin to send
++         * LE_TOPOLOGY_REQUEST with the value of Topology Change bit
++         * in the Config BPDU*/
++        eth = (struct ethhdr *)skb->data;
++        buff = skb->data + skb->dev->hard_header_len;
++        if ((memcmp(eth->h_dest, bridge_ula, ETH_ALEN) == 0) &&
++            *buff++ == BRIDGE_LLC1_DSAP &&
++            *buff++ == BRIDGE_LLC1_SSAP &&
++            *buff++ == BRIDGE_LLC1_CTRL) {
++                struct sk_buff *skb2;
++                struct atmlec_msg *mesg;
++
++                skb2 = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC);
++                if (skb2 == NULL) return;
++                skb2->len = sizeof(struct atmlec_msg);
++                mesg = (struct atmlec_msg *)skb2->data;
++                mesg->type = l_topology_change;
++                mesg->content.normal.flag = *(skb->nh.raw + BRIDGE_BPDU_8021_CONFIG_FLAG_OFFSET) & TOPOLOGY_CHANGE;
++
++                priv = (struct lec_priv *)dev->priv;
++                if (atm_charge(priv->lecd, skb2->truesize) == 0) return;
++                skb_queue_tail(&priv->lecd->recvq, skb2);
++                wake_up(&priv->lecd->sleep);
++        }
++
++        return;
++}
++#endif /* CONFIG_BRIDGE */
++
 +/*
 + * Open/initialize the netdevice. This is called (in the current kernel)
 + * sometime after booting when the 'ifconfig' program is run.
@@ -18107,7 +18265,7 @@
 +        struct lec_priv *priv = (struct lec_priv *)dev->priv;
 +        struct lecdatahdr_8023 *lec_h;
 +        struct atm_vcc *send_vcc;
-+	struct lec_arp_table *entry = NULL;
++	struct lec_arp_table *entry;
 +        unsigned char *nb;
 +#if DUMP_PACKETS > 0
 +        char buf[300];
@@ -18126,17 +18284,8 @@
 +                 * If we get here, some higher level has decided we are broken.
 +                 * There should really be a "kick me" function call instead.
 +                 */
-+#if 0 /* 24.8.1998 hessu@cs.tut.fi */
-+                priv->stats.tx_dropped++;
-+                printk("%s: lec_send_packet: transmit timed out, dropping packet...\n", dev->name);
-+                dev_kfree_skb(skb);
-+                dev->tbusy=0;
-+                return 0;
-+#endif
-+#if 1 /* 24.8.1998 hessu@cs.tut.fi */
 +                printk("%s: transmit timed out\n", dev->name);
 +                dev->tbusy = 0;
-+#endif
 +        }
 +
 +        /*
@@ -18151,12 +18300,26 @@
 +                DPRINTK("skbuff head:%lx data:%lx tail:%lx end:%lx\n",
 +                        (long)skb->head, (long)skb->data, (long)skb->tail,
 +                        (long)skb->end);
-+                
++#ifdef CONFIG_BRIDGE
++                if (skb->pkt_bridged == IS_BRIDGED)
++                        handle_bridge(skb, dev);
++#endif /* CONFIG_BRIDGE */
++
++                /* Make sure we have room for lec_id */
++                if (skb_headroom(skb) < 2) {
++
++                        DPRINTK("lec_send_packet: reallocating skb\n");
++                        skb2 = skb_realloc_headroom(skb, LEC_HEADER_LEN);
++                        kfree_skb(skb);
++                        if (skb2 == NULL) return 0;
++                        skb = skb2;
++                }
++                skb_push(skb, 2);
++
 +                /* Put le header to place */
 +                lec_h = (struct lecdatahdr_8023*)skb->data;
-+                /*
 +                lec_h->le_header = htons(priv->lecid); 
-+                */
++
 +#if DUMP_PACKETS > 0
 +                printk("%s: send datalen:%ld lecid:%4.4x\n", dev->name,
 +                        skb->len, priv->lecid);
@@ -18174,48 +18337,7 @@
 +                else
 +                        printk("%s...\n",buf);
 +#endif /* DUMP_PACKETS > 0 */
-+                /* Send to right vcc */
-+                send_vcc = lec_arp_resolve(priv, lec_h->h_dest, &entry);
-+                DPRINTK("%s:send_vcc:%p vcc_flags:%x, entry:%p\n", dev->name,
-+                        send_vcc, send_vcc?send_vcc->flags:0, entry);
-+                if (!send_vcc || !(send_vcc->flags & ATM_VF_READY)) {    
-+#if 0 /* 24.8.1998 hessu@cs.tut.fi */
-+                        priv->stats.tx_dropped++;
-+                        printk("%s:lec_send_packet: bad vcc, dropping packet...\n",
-+                                dev->name);
-+                        dev_kfree_skb(skb);
-+                        dev->tbusy=0;
-+                        return 0;
-+#endif
-+#if 0 /* 26.8.1998 hessu@cs.tut.fi */
-+			priv->stats.tx_errors++;
-+                        printk("%s:lec_send_packet: handing back ", dev->name);
-+			printk("MAC address 0x%02x:%02x:%02x:%02x:%02x:%02x\n",
-+			       lec_h->h_dest[0], lec_h->h_dest[1], lec_h->h_dest[2],
-+			       lec_h->h_dest[3], lec_h->h_dest[4], lec_h->h_dest[5]);
-+                        dev->tbusy = 1;
-+                        return 1;
-+#endif
-+#if 1 /* 30.10.1998 s156953@cs.tut.fi hessu@cs.tut.fi */
-+                        if (entry && (entry->tx_wait.qlen < LEC_UNRES_QUE_LEN)) {
-+                                printk("%s:lec_send_packet: queuing packet, ", dev->name);
-+                                printk("MAC address 0x%02x:%02x:%02x:%02x:%02x:%02x\n",
-+                                       lec_h->h_dest[0], lec_h->h_dest[1], lec_h->h_dest[2],
-+                                       lec_h->h_dest[3], lec_h->h_dest[4], lec_h->h_dest[5]);
-+                                skb_queue_tail(&entry->tx_wait, skb);
-+                        } else {
-+                                printk("%s:lec_send_packet: tx queue full or no arp entry, dropping, ", dev->name);
-+                                printk("MAC address 0x%02x:%02x:%02x:%02x:%02x:%02x\n",
-+                                       lec_h->h_dest[0], lec_h->h_dest[1], lec_h->h_dest[2],
-+                                       lec_h->h_dest[3], lec_h->h_dest[4], lec_h->h_dest[5]);
-+                                priv->stats.tx_dropped++;
-+                                dev_kfree_skb(skb);
-+                                dev->tbusy = 0;
-+                                return 0;
-+                        }
-+#endif
-+                        
-+                }
++
 +                /* Minimum ethernet-frame size */
 +                if (skb->len <62) {
 +                        if (skb->truesize < 62) {
@@ -18234,7 +18356,27 @@
 +                                skb->len = 62;
 +                        }
 +                }
-+                if(!send_vcc){
++
++                /* Send to right vcc */
++                entry = NULL;
++                send_vcc = lec_arp_resolve(priv, lec_h->h_dest, &entry);
++                DPRINTK("%s:send_vcc:%p vcc_flags:%x, entry:%p\n", dev->name,
++                        send_vcc, send_vcc?send_vcc->flags:0, entry);
++                if (!send_vcc || !(send_vcc->flags & ATM_VF_READY)) {    
++                        if (entry && (entry->tx_wait.qlen < LEC_UNRES_QUE_LEN)) {
++                                DPRINTK("%s:lec_send_packet: queuing packet, ", dev->name);
++                                DPRINTK("MAC address 0x%02x:%02x:%02x:%02x:%02x:%02x\n",
++                                       lec_h->h_dest[0], lec_h->h_dest[1], lec_h->h_dest[2],
++                                       lec_h->h_dest[3], lec_h->h_dest[4], lec_h->h_dest[5]);
++                                skb_queue_tail(&entry->tx_wait, skb);
++                        } else {
++                                DPRINTK("%s:lec_send_packet: tx queue full or no arp entry, dropping, ", dev->name);
++                                DPRINTK("MAC address 0x%02x:%02x:%02x:%02x:%02x:%02x\n",
++                                       lec_h->h_dest[0], lec_h->h_dest[1], lec_h->h_dest[2],
++                                       lec_h->h_dest[3], lec_h->h_dest[4], lec_h->h_dest[5]);
++                                priv->stats.tx_dropped++;
++                                dev_kfree_skb(skb);
++                        }
 +                        dev->tbusy=0;
 +                        return 0;
 +                }
@@ -18245,14 +18387,16 @@
 +#endif /* DUMP_PACKETS > 0 */
 +                
 +                while (entry && (skb2 = skb_dequeue(&entry->tx_wait))) {
-+                        printk("lec.c: emptying tx queue, ");
-+                        printk("MAC address 0x%02x:%02x:%02x:%02x:%02x:%02x\n",
++                        DPRINTK("lec.c: emptying tx queue, ");
++                        DPRINTK("MAC address 0x%02x:%02x:%02x:%02x:%02x:%02x\n",
 +                               lec_h->h_dest[0], lec_h->h_dest[1], lec_h->h_dest[2],
 +                               lec_h->h_dest[3], lec_h->h_dest[4], lec_h->h_dest[5]);
 +                        ATM_SKB(skb2)->vcc = send_vcc;
 +                        atomic_add(skb2->truesize, &send_vcc->tx_inuse);
 +                        ATM_SKB(skb2)->iovcnt = 0;
 +                        ATM_SKB(skb2)->atm_options = send_vcc->atm_options;
++                        DPRINTK("%s:sending to vpi:%d vci:%d\n", dev->name,
++                               send_vcc->vpi, send_vcc->vci);       
 +                        send_vcc->dev->ops->send(send_vcc, skb2);
 +                        priv->stats.tx_packets++;
 +                }
@@ -18291,83 +18435,6 @@
 +        return (struct enet_statistics *)&priv->stats;
 +}
 +
-+int 
-+lec_hard_header(struct sk_buff *skb, struct device *dev, 
-+               unsigned short type, void *daddr, void *saddr, 
-+               unsigned len)
-+{
-+        struct lec_priv *priv = (struct lec_priv *)dev->priv;
-+        struct lecdatahdr_8023 *hdr = 
-+                (struct lecdatahdr_8023 *)skb_push(skb, LEC_HEADER_LEN); 
-+                
-+        /* Set lecid header */
-+        if (priv)
-+                hdr->le_header = htons(priv->lecid);
-+        else
-+                hdr->le_header = 0;
-+        
-+        /* Set the protocol type. */
-+        if(type!=ETH_P_802_3)
-+                hdr->h_type = htons(type);
-+        else
-+                hdr->h_type = htons(len);
-+
-+        /* Source hw address */
-+        if (saddr)
-+                memcpy(hdr->h_source, saddr, dev->addr_len);
-+        else
-+                memcpy(hdr->h_source, dev->dev_addr, dev->addr_len);
-+        
-+        /* Destination addr */
-+        if (daddr) {
-+                memcpy(hdr->h_dest, daddr, dev->addr_len);
-+                return dev->hard_header_len;
-+        }
-+        return -dev->hard_header_len;
-+}
-+
-+int 
-+lec_rebuild_header(struct sk_buff *skb)
-+{
-+        struct lecdatahdr_8023 *hdr = (struct lecdatahdr_8023*)skb->data;
-+        struct device *dev = skb->dev;
-+
-+        switch (hdr->h_type)
-+        {
-+#ifdef CONFIG_INET
-+        case __constant_htons(ETH_P_IP):
-+                return arp_find(hdr->h_dest, skb);
-+#endif
-+        default:
-+                printk(KERN_DEBUG
-+                       "%s: unable to resolve type %X addresses.\n",
-+                       dev->name, (int)hdr->h_type);
-+
-+                memcpy(hdr->h_source, dev->dev_addr, dev->addr_len);
-+                return 0;
-+                break;
-+        }
-+
-+        return 0;
-+}        
-+
-+/*
-+ * Called by Address Resolution module to notify changes in address.
-+ */
-+void 
-+lec_header_cache_update(struct hh_cache *hh, struct device *dev, 
-+                       unsigned char *haddr)
-+{
-+        u16 *u16ptr = (u16 *)hh->hh_data;
-+  
-+        if (hh->hh_type != ETH_P_IP) {
-+                printk("lec_header_cache_update: %04x cache is not implemented\n", 
-+                       hh->hh_type);
-+                return;
-+        }
-+        memcpy(u16ptr+1, haddr, ETH_ALEN);
-+}
-+
 +static int 
 +lec_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
 +{
@@ -18418,7 +18485,7 @@
 +                               mesg->content.normal.targetless_le_arp);
 +                DPRINTK("lec: in l_arp_update\n");
 +                if (mesg->sizeoftlvs != 0) { /* LANE2 3.1.5 */
-+                        DPRINTK("lec: LANE2 3.1.5, got tlvs\n");
++                        DPRINTK("lec: LANE2 3.1.5, got tlvs, size %d\n", mesg->sizeoftlvs);
 +                        lane2_associate_ind(dev,
 +                                            mesg->content.normal.mac_addr,
 +                                            tmp, mesg->sizeoftlvs);
@@ -18443,6 +18510,7 @@
 +		priv->lane2_ops = NULL;
 +		if (priv->lane_version > 1)
 +			priv->lane2_ops = &lane2_ops;
++                priv->is_proxy = mesg->content.config.is_proxy;
 +                break;
 +        case l_flush_tran_id:
 +                lec_set_flush_tran_id(priv, mesg->content.normal.atm_addr,
@@ -18451,6 +18519,35 @@
 +        case l_set_lecid:
 +                priv->lecid=(unsigned short)(0xffff&mesg->content.normal.flag);
 +                break;
++        case l_should_bridge: {
++#ifdef CONFIG_BRIDGE
++                struct fdb *f;
++                extern Port_data port_info[];
++
++                DPRINTK("%s: bridge zeppelin asks about 0x%02x:%02x:%02x:%02x:%02x:%02x\n",
++                        dev->name,
++                        mesg->content.proxy.mac_addr[0], mesg->content.proxy.mac_addr[1],
++                        mesg->content.proxy.mac_addr[2], mesg->content.proxy.mac_addr[3],
++                        mesg->content.proxy.mac_addr[4], mesg->content.proxy.mac_addr[5]);
++                f = br_avl_find_addr(mesg->content.proxy.mac_addr); /* bridge/br.c */
++                if (f != NULL &&
++                    port_info[f->port].dev != dev &&
++                    port_info[f->port].state == Forwarding) {
++                                /* hit from bridge table, send LE_ARP_RESPONSE */
++                        struct sk_buff *skb2;
++
++                        DPRINTK("%s: entry found, responding to zeppelin\n", dev->name);
++                        skb2 = alloc_skb(sizeof(struct atmlec_msg), GFP_ATOMIC);
++                        if (skb2 == NULL) break;
++                        skb2->len = sizeof(struct atmlec_msg);
++                        memcpy(skb2->data, mesg, sizeof(struct atmlec_msg));
++                        if (atm_charge(priv->lecd, skb2->truesize) == 0) break;
++                        skb_queue_tail(&priv->lecd->recvq, skb2);
++                        wake_up(&priv->lecd->sleep);
++                }
++#endif /* CONFIG_BRIDGE */
++                }
++                break;
 +        default:
 +                printk("%s: Unknown message type %d\n", dev->name, mesg->type);
 +                dev_kfree_skb(skb);
@@ -18587,14 +18684,9 @@
 +
 +        ether_setup(dev);
 +        dev->change_mtu = lec_change_mtu;
-+        dev->hard_header = lec_hard_header;
-+        dev->rebuild_header = lec_rebuild_header;
-+        dev->hard_header_cache = NULL;
-+        dev->header_cache_update = lec_header_cache_update;
 +        dev->open = lec_open;
 +        dev->stop = lec_close;
 +        dev->hard_start_xmit = lec_send_packet;
-+        dev->hard_header_len = LEC_HEADER_LEN;
 +
 +        dev->get_stats = lec_get_stats;
 +        dev->set_multicast_list = NULL;
@@ -18615,7 +18707,7 @@
 +        struct device *dev = (struct device *)vcc->proto_data;
 +        struct lec_priv *priv = (struct lec_priv *)dev->priv; 
 +        struct lecdatahdr_8023 *hdr;
-+        unsigned char *rawp;
++
 +#if DUMP_PACKETS >0
 +        int i=0;
 +        char buf[300];
@@ -18664,25 +18756,8 @@
 +                        lec_arp_check_empties(priv, vcc, skb);
 +                }
 +                skb->dev = dev;
-+                skb->mac.raw = (unsigned char *)skb->data;
-+                if (*hdr->h_dest&1) {
-+                        if (memcmp(hdr->h_dest,dev->broadcast, ETH_ALEN)==0)
-+                                skb->pkt_type=PACKET_BROADCAST;
-+                        else
-+                                skb->pkt_type=PACKET_MULTICAST;
-+                } else if (dev->flags&(IFF_PROMISC|IFF_ALLMULTI)) {
-+                        if (memcmp(hdr->h_dest,dev->dev_addr, ETH_ALEN))
-+                                skb->pkt_type=PACKET_OTHERHOST;
-+                } 
-+                if (ntohs(hdr->h_type)>=1536)
-+                        skb->protocol = hdr->h_type;
-+                else 
-+                        skb->protocol = htons(ETH_P_802_2);
-+                skb->h.raw = skb_pull(skb, LEC_HEADER_LEN);
-+                rawp = skb->data;
-+                /* Magic hack for IPX ... */
-+                if (*(unsigned short*)rawp == 0xFFFF)
-+                        skb->protocol = htons(ETH_P_802_3);
++                skb->data += 2; /* skip lec_id */
++                skb->protocol = eth_type_trans(skb, dev);
 +                netif_rx(skb);
 +                priv->stats.rx_packets++;
 +        }
@@ -18919,6 +18994,8 @@
 +{
 +        int i = 0;
 +	struct lec_priv *priv = (struct lec_priv *)dev->priv;
++#if 0 /* Why have the TLVs in LE_ARP entries since we do not use them? When you
++         uncomment this code, make sure the TLVs get freed when entry is killed */
 +        struct lec_arp_table *entry = lec_arp_find(priv, mac_addr);
 +
 +        if (entry == NULL)
@@ -18932,7 +19009,7 @@
 +
 +        entry->sizeoftlvs = sizeoftlvs;
 +        memcpy(entry->tlvs, tlvs, sizeoftlvs);
-+
++#endif
 +#if 0
 +        printk("lec.c: lane2_associate_ind()\n");
 +        printk("dump of tlvs, sizeoftlvs=%d\n", sizeoftlvs);
@@ -19027,8 +19104,9 @@
 +#if 0 /* August 6, 1998 */
 +                entry->vcc->flags |= ATM_VF_RELEASED;
 +                entry->vcc->flags &= ~ATM_VF_READY;
-+#endif
 +		atm_async_release_vcc(entry->vcc, -EPIPE);
++#endif
++                entry->vcc->push(entry->vcc, NULL);
 +                entry->vcc = NULL;
 +        }
 +        if (entry->recv_vcc) {
@@ -19036,8 +19114,9 @@
 +#if 0
 +                entry->recv_vcc->flags |= ATM_VF_RELEASED;
 +                entry->recv_vcc->flags &= ~ATM_VF_READY;
-+#endif
 +		atm_async_release_vcc(entry->recv_vcc, -EPIPE);
++#endif
++                entry->recv_vcc->push(entry->recv_vcc, NULL);
 +                entry->recv_vcc = NULL;
 +        }        
 +}
@@ -19172,6 +19251,8 @@
 +                (struct lec_arp_table *)priv->lec_arp_empty_ones;
 +        struct lec_arp_table *lec_no_forward =
 +                (struct lec_arp_table *)priv->lec_no_forward;
++        struct lec_arp_table *mcast_fwds = priv->mcast_fwds;
++
 +
 +        printk("Dump %p:\n",priv);
 +        for (i=0;i<LEC_ARP_TABLE_SIZE;i++) {
@@ -19271,6 +19352,38 @@
 +                printk("%s",buf);
 +        }
 +
++        rulla = mcast_fwds;
++        if (rulla)
++                printk("Multicast Forward VCCs\n");  
++        while(rulla) {
++                offset=0;
++                offset += sprintf(buf+offset,"Mac:");
++                for(j=0;j<ETH_ALEN;j++) {
++                        offset+=sprintf(buf+offset,"%2.2x ",
++                                        rulla->mac_addr[j]&0xff);
++                }
++                offset +=sprintf(buf+offset,"Atm:");
++                for(j=0;j<ATM_ESA_LEN;j++) {
++                        offset+=sprintf(buf+offset,"%2.2x ",
++                                        rulla->atm_addr[j]&0xff);
++                }      
++                offset+=sprintf(buf+offset,
++                                "Vcc vpi:%d vci:%d, Recv_vcc vpi:%d vci:%d Last_used:%lx, Timestamp:%lx, No_tries:%d ",
++                                rulla->vcc?rulla->vcc->vpi:0, 
++                                rulla->vcc?rulla->vcc->vci:0, 
++                                rulla->recv_vcc?rulla->recv_vcc->vpi:0,
++                                rulla->recv_vcc?rulla->recv_vcc->vci:0,
++                                rulla->last_used, 
++                                rulla->timestamp, rulla->no_tries);
++                offset+=sprintf(buf+offset,
++                                "Flags:%x, Packets_flooded:%x, Status: %s ",
++                                rulla->flags, rulla->packets_flooded, 
++                                get_status_string(rulla->status));
++                offset+=sprintf(buf+offset,"->%lx\n",(long)rulla->next);
++                rulla = rulla->next;
++                printk("%s",buf);
++        }
++
 +#endif
 +}
 +
@@ -19304,7 +19417,6 @@
 +                next = entry->next;
 +                del_timer(&entry->timer);
 +                lec_arp_clear_vccs(entry);
-+                skb_queue_purge(&entry->tx_wait);
 +                kfree(entry);
 +                entry = next;
 +        }
@@ -19314,11 +19426,19 @@
 +                next = entry->next;
 +                del_timer(&entry->timer);
 +                lec_arp_clear_vccs(entry);
-+                skb_queue_purge(&entry->tx_wait);
 +                kfree(entry);
 +                entry = next;
 +        }
 +        priv->lec_no_forward = NULL;
++        entry = priv->mcast_fwds;
++        while(entry) {
++                next = entry->next;
++                del_timer(&entry->timer);
++                lec_arp_clear_vccs(entry);
++                kfree(entry);
++                entry = next;
++        }
++        priv->mcast_fwds = NULL;
 +        priv->mcast_vcc = NULL;
 +        memset(priv->lec_arp_tables, 0, 
 +               sizeof(struct lec_arp_table*)*LEC_ARP_TABLE_SIZE);
@@ -19490,7 +19610,8 @@
 +
 +                                DPRINTK("About to expire: %lx - %lx > %lx\n",
 +                                        now,entry->last_used, time_to_check);
-+                                if( (now-entry->last_used > time_to_check) && 
++                                if( time_after(now, entry->last_used+
++                                   time_to_check) && 
 +                                    !(entry->flags & LEC_PERMANENT_FLAG) &&
 +                                    !(entry->mac_addr[0] & 0x01) ) { /* LANE2: 7.1.20 */
 +                                        /* Remove entry */
@@ -19503,16 +19624,18 @@
 +                                        /* Something else */
 +                                        if ((entry->status == ESI_VC_PENDING ||
 +                                             entry->status == ESI_ARP_PENDING) 
-+                                            &&
-+                                            now-entry->timestamp >= 
-+                                            priv->max_unknown_frame_time) {
++                                            && time_after_eq(now,
++                                            entry->timestamp +
++                                            priv->max_unknown_frame_time)) {
 +                                                entry->timestamp = jiffies;
 +                                                entry->packets_flooded = 0;
++                                                if (entry->status == ESI_VC_PENDING)
++                                                        send_to_lecd(priv, l_svc_setup, entry->mac_addr, entry->atm_addr, NULL);
 +                                        }
 +                                        if (entry->status == ESI_FLUSH_PENDING 
-+                                            &&
-+                                            now-entry->timestamp >= 
-+                                            priv->path_switching_delay) {
++                                           &&
++                                           time_after_eq(now, entry->timestamp+
++                                           priv->path_switching_delay)) {
 +                                                entry->last_used = jiffies;
 +                                                entry->status = 
 +                                                        ESI_FORWARD_DIRECT;
@@ -19543,7 +19666,7 @@
 +                        return priv->mcast_vcc;
 +                        break;
 +                case 2:  /* LANE2 wants arp for multicast addresses */
-+                        if ( memcmp(mac_to_find, &bus_mac, ETH_ALEN) == 0)
++                        if ( memcmp(mac_to_find, bus_mac, ETH_ALEN) == 0)
 +                                return priv->mcast_vcc;
 +                        break;
 +                default:
@@ -19560,10 +19683,6 @@
 +                        *ret_entry = entry;
 +                        return entry->vcc;
 +                }
-+                if (entry->status == ESI_UNKNOWN) {
-+                        del_timer(&entry->timer);
-+                        goto make_arp_entry;
-+                }
 +                /* Data direct VC not yet set up, check to see if the unknown
 +                   frame count is greater than the limit. If the limit has
 +                   not been reached, allow the caller to send packet to
@@ -19574,7 +19693,12 @@
 +                        DPRINTK("LEC_ARP: Flooding..\n");
 +                        return priv->mcast_vcc;
 +                }
++		/* We got here because entry->status == ESI_FLUSH_PENDING
++		 * or BUS flood limit was reached for an entry which is
++		 * in ESI_ARP_PENDING or ESI_VC_PENDING state.
++		 */
 +                *ret_entry = entry;
++                DPRINTK("lec: entry->status %d entry->vcc %p\n", entry->status, entry->vcc);
 +                return NULL;
 +        } else {
 +                /* No matching entry was found */
@@ -19584,7 +19708,6 @@
 +                        return priv->mcast_vcc;
 +                }
 +                lec_arp_put(priv->lec_arp_tables, entry);
-+        make_arp_entry:
 +                /* We want arp-request(s) to be sent */
 +                entry->packets_flooded =1;
 +                entry->status = ESI_ARP_PENDING;
@@ -19594,7 +19717,6 @@
 +                entry->timer.expires = jiffies + (1*HZ);
 +                entry->timer.function = lec_arp_expire_arp;
 +                add_timer(&entry->timer);
-+                *ret_entry = entry;
 +                return priv->mcast_vcc;
 +        }
 +}
@@ -19634,7 +19756,6 @@
 +               unsigned int targetless_le_arp)
 +{
 +        struct lec_arp_table *entry, *tmp;
-+        struct sk_buff *skb;
 +        int i;
 +
 +        DPRINTK("lec:%s", (targetless_le_arp) ? "targetless ": " ");
@@ -19674,8 +19795,7 @@
 +                                tmp->vcc = entry->vcc;
 +                                tmp->old_push = entry->old_push;
 +                                tmp->last_used = jiffies;
-+                                while((skb = skb_dequeue(&entry->tx_wait)) != NULL)
-+                                        skb_queue_tail(&tmp->tx_wait, skb);
++                                del_timer(&entry->timer);
 +                                kfree(entry);
 +                                entry=tmp;
 +                        } else {
@@ -19729,7 +19849,7 @@
 +        if (entry->status == ESI_ARP_PENDING ||
 +            entry->status == ESI_UNKNOWN) {
 +                entry->status = ESI_VC_PENDING;
-+                send_to_lecd(priv, l_svc_setup, NULL, atm_addr, NULL);
++                send_to_lecd(priv, l_svc_setup, entry->mac_addr, atm_addr, NULL);
 +        }
 +        DPRINTK("After update2\n");
 +        dump_arp_table(priv);
@@ -19750,8 +19870,10 @@
 +
 +        lec_arp_lock(priv);
 +        if (ioc_data->receive == 2) {
-+                /* Vcc for BUS distribute */
-+                DPRINTK("LEC_ARP: Attaching mcast distribute\n");
++                /* Vcc for Multicast Forward. No timer, LANEv2 7.1.20 and 2.3.5.3 */
++
++                DPRINTK("LEC_ARP: Attaching mcast forward\n");
++#if 0
 +                entry = lec_arp_find(priv, bus_mac);
 +                if (!entry) {
 +                        printk("LEC_ARP: Multicast entry not found!\n");
@@ -19761,6 +19883,13 @@
 +                memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN);
 +                entry->recv_vcc = vcc;
 +                entry->old_recv_push = old_push;
++#endif
++                entry = make_entry(priv, bus_mac);
++                memcpy(entry->atm_addr, ioc_data->atm_addr, ATM_ESA_LEN);
++                entry->recv_vcc = vcc;
++                entry->old_recv_push = old_push;
++                entry->next = priv->mcast_fwds;
++                priv->mcast_fwds = entry;
 +                lec_arp_unlock(priv);
 +                return;
 +        } else if (ioc_data->receive == 1) {
@@ -19793,15 +19922,15 @@
 +                return;
 +        }
 +        DPRINTK("LEC_ARP:Attaching data direct, default:%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
-+                        ioc_data->atm_addr[0],ioc_data->atm_addr[1],
-+                        ioc_data->atm_addr[2],ioc_data->atm_addr[3],
-+                        ioc_data->atm_addr[4],ioc_data->atm_addr[5],
-+                        ioc_data->atm_addr[6],ioc_data->atm_addr[7],
-+                        ioc_data->atm_addr[8],ioc_data->atm_addr[9],
-+                        ioc_data->atm_addr[10],ioc_data->atm_addr[11],
-+                        ioc_data->atm_addr[12],ioc_data->atm_addr[13],
-+                        ioc_data->atm_addr[14],ioc_data->atm_addr[15],
-+                        ioc_data->atm_addr[16],ioc_data->atm_addr[17],
++                ioc_data->atm_addr[0],ioc_data->atm_addr[1],
++                ioc_data->atm_addr[2],ioc_data->atm_addr[3],
++                ioc_data->atm_addr[4],ioc_data->atm_addr[5],
++                ioc_data->atm_addr[6],ioc_data->atm_addr[7],
++                ioc_data->atm_addr[8],ioc_data->atm_addr[9],
++                ioc_data->atm_addr[10],ioc_data->atm_addr[11],
++                ioc_data->atm_addr[12],ioc_data->atm_addr[13],
++                ioc_data->atm_addr[14],ioc_data->atm_addr[15],
++                ioc_data->atm_addr[16],ioc_data->atm_addr[17],
 +                ioc_data->atm_addr[18],ioc_data->atm_addr[19]);
 +        for (i=0;i<LEC_ARP_TABLE_SIZE;i++) {
 +                for (entry = priv->lec_arp_tables[i];entry;entry=entry->next) {
@@ -19813,7 +19942,7 @@
 +                                        entry->recv_vcc?entry->recv_vcc->vci:0);
 +                                found_entry=1;
 +                                del_timer(&entry->timer);
-+                                if (vcc) {
++                                if (entry->vcc) {
 +                                        lec_arp_clear_vccs(entry);
 +                                }
 +                                entry->vcc = vcc;
@@ -19918,6 +20047,7 @@
 +        to_add = make_entry(priv, mac_addr);
 +        if (!to_add)
 +                return -ENOMEM;
++        memcpy(to_add->atm_addr, vcc->remote.sas_addr.prv, ATM_ESA_LEN);
 +        to_add->status = ESI_FORWARD_DIRECT;
 +        to_add->flags |= LEC_PERMANENT_FLAG;
 +        to_add->vcc = vcc;
@@ -19947,14 +20077,6 @@
 +                                if (priv->mcast_vcc == vcc) {
 +                                        priv->mcast_vcc = NULL;
 +                                }
-+                        } else if (vcc == entry->recv_vcc) { 
-+                                /* Bus distribution closed */
-+                                priv->mcast_vcc = NULL;
-+                                lec_arp_remove(priv->lec_arp_tables,entry);
-+				kfree(entry);
-+                                lec_arp_unlock(priv);
-+                                vcc->push(vcc, NULL);
-+                                return;
 +                        }
 +                }
 +        }
@@ -19991,6 +20113,22 @@
 +                entry = next;
 +        }
 +
++        entry = priv->mcast_fwds;
++        priv->mcast_fwds = NULL;
++        while (entry != NULL) {
++                next = entry->next;
++                if (entry->recv_vcc == vcc) {
++                        lec_arp_clear_vccs(entry);
++                        /* No timer, LANEv2 7.1.20 and 2.3.5.3 */
++                        kfree(entry);
++                }
++                else {
++                        entry->next = priv->mcast_fwds;
++                        priv->mcast_fwds = entry;
++                }
++                entry = next;
++        }
++
 +        lec_arp_unlock(priv);
 +	dump_arp_table(priv);
 +}
@@ -20051,8 +20189,8 @@
 +}
 +
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/lec.h	Sun Nov  1 15:31:53 1998
-@@ -0,0 +1,143 @@
++++ work/net/atm/lec.h	Tue Feb  9 18:09:24 1999
+@@ -0,0 +1,149 @@
 +/*
 + *
 + * Lan Emulation client header file
@@ -20140,8 +20278,13 @@
 +           age out by not using them to forward packets. 
 +           This is because to some LE clients there will be 2 VCCs. Only
 +           one of them gets used. */
++        struct lec_arp_table *mcast_fwds;
++        /* With LANEv2 it is possible that BUS (or a special multicast server)
++           establishes multiple Multicast Forward VCCs to us. This list
++           collects all those VCCs. LANEv1 client has only one item in this
++           list. These entries are not aged out. */
 +        atomic_t lec_arp_lock_var;
-+        struct atm_vcc *mcast_vcc;
++        struct atm_vcc *mcast_vcc; /* Default Multicast Send VCC */
 +        struct atm_vcc *lecd;
 +        struct timer_list lec_arp_timer;
 +        /* C10 */
@@ -20180,6 +20323,7 @@
 +        int lane_version;  /* LANE2                              */
 +	int itfnum;        /* e.g. 2 for lec2, 5 for lec5        */
 +        struct lane2_ops *lane2_ops; /* can be NULL for LANE v1  */
++        int is_proxy;      /* bridge between ATM and Ethernet    */
 +};
 +
 +int lecd_attach(struct atm_vcc *vcc, int arg);
@@ -20197,7 +20341,7 @@
 +#endif _LEC_H_
 +
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/lec_arpc.h	Tue Nov  3 18:41:56 1998
++++ work/net/atm/lec_arpc.h	Tue Feb  9 18:09:24 1999
 @@ -0,0 +1,114 @@
 +/*
 + * Lec arp cache
@@ -20314,8 +20458,8 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/mpc.c	Sat Dec  5 01:50:04 1998
-@@ -0,0 +1,1519 @@
++++ work/net/atm/mpc.c	Tue Feb  9 17:15:25 1999
+@@ -0,0 +1,1515 @@
 +#include <linux/kernel.h>
 +#include <linux/string.h>
 +#include <linux/timer.h>
@@ -20692,12 +20836,20 @@
 +        dprintk("mpoa: (%s) lane2_assoc_ind: received TLV(s), ", dev->name);
 +        dprintk("total length of all TLVs %d\n", sizeoftlvs);
 +	mpc = find_mpc_by_lec(dev); /* Sampo-Fix: moved here from below */
++        if (mpc == NULL) {
++                printk("mpoa: (%s) lane2_assoc_ind: no mpc\n", dev->name);
++                return;
++        }
 +        end_of_tlvs = tlvs + sizeoftlvs;
 +        while (end_of_tlvs - tlvs >= 5) {
 +                type = (tlvs[0] << 24) | (tlvs[1] << 16) | (tlvs[2] << 8) | tlvs[3];
 +                length = tlvs[4];
 +                tlvs += 5;
 +                dprintk("    type 0x%x length %02x\n", type, length);
++                if (tlvs + length > end_of_tlvs) {
++                        printk("TLV value extends past its buffer, aborting parse\n");
++                        return;
++                }
 +                
 +                if (type == 0) {
 +                        printk("mpoa: (%s) lane2_assoc_ind: TLV type was 0, returning\n", dev->name);
@@ -20747,8 +20899,9 @@
 +}
 +
 +/*
-+ * store either advertizing router's MAC address
-+ * or the MAC address(es) in this TLV
++ * Store at least advertizing router's MAC address
++ * plus the possible MAC address(es) to mpc->mps_macs.
++ * For a freshly allocated MPOA client mpc->mps_macs == 0.
 + */
 +static uint8_t *copy_macs(struct mpoa_client *mpc, uint8_t *router_mac,
 +                          uint8_t *tlvs, uint8_t mps_macs, uint8_t device_type)
@@ -20756,27 +20909,21 @@
 +        int num_macs;
 +        num_macs = (mps_macs > 1) ? mps_macs : 1;
 +
-+        if (mpc->number_of_mps_macs != num_macs) {
-+                kfree(mpc->mps_macs);
-+                mpc->number_of_mps_macs = num_macs;
++        if (mpc->number_of_mps_macs != num_macs) { /* need to reallocate? */
++                if (mpc->number_of_mps_macs != 0) kfree(mpc->mps_macs);
++                mpc->number_of_mps_macs = 0;
 +                mpc->mps_macs = kmalloc(num_macs*ETH_ALEN, GFP_KERNEL);
 +                if (mpc->mps_macs == NULL) {
 +                        printk("mpoa: (%s) copy_macs: out of mem\n", mpc->dev->name);
 +                        return NULL;
 +                }
 +        }
-+
-+        /* copy the advertizing routers MAC address */
 +        memcpy(mpc->mps_macs, router_mac, ETH_ALEN);
-+        
 +        tlvs += 20; if (device_type == MPS_AND_MPC) tlvs += 20;
-+        /*
-+         * collect the MPS MAC addresses.
-+         * however, it is possible that TLV includes no MACs
-+         */
 +        if (mps_macs > 0)
 +                memcpy(mpc->mps_macs, tlvs, mps_macs*ETH_ALEN);
 +        tlvs += mps_macs*ETH_ALEN;
++        mpc->number_of_mps_macs = num_macs;
 +
 +        return tlvs;
 +}
@@ -20803,7 +20950,6 @@
 +
 +        ddprintk("mpoa: (%s) send_via_shortcut: ipaddr 0x%x\n", mpc->dev->name, ipaddr);        
 +
-+
 +        entry = mpc->in_ops->search(ipaddr, mpc);
 +        if (entry == NULL) {
 +                mpc->in_ops->new_entry(ipaddr, mpc);
@@ -20827,11 +20973,11 @@
 +        if (entry->ctrl_info.tag != 0) {
 +                ddprintk("mpoa: (%s) send_via_shortcut() adding tag 0x%x\n", mpc->dev->name, entry->ctrl_info.tag);
 +                tagged_llc_snap_hdr.tag = entry->ctrl_info.tag;
-+                skb_pull(skb, LEC_HEADER_LEN);                 /* get rid of LANE+Eth header */
-+                skb_push(skb, sizeof(tagged_llc_snap_hdr));    /* add LLC/SNAP header        */
++                skb_pull(skb, ETH_HLEN);                       /* get rid of Eth header */
++                skb_push(skb, sizeof(tagged_llc_snap_hdr));    /* add LLC/SNAP header   */
 +                memcpy(skb->data, &tagged_llc_snap_hdr, sizeof(tagged_llc_snap_hdr));
 +        } else {
-+                skb_pull(skb, LEC_HEADER_LEN);                  /* get rid of LANE+Eth header */
++                skb_pull(skb, ETH_HLEN);                        /* get rid of Eth header */
 +                skb_push(skb, sizeof(struct llc_snap_hdr));     /* add LLC/SNAP header + tag  */
 +                memcpy(skb->data, &llc_snap_mpoa_data, sizeof(struct llc_snap_hdr));
 +        }
@@ -20852,6 +20998,7 @@
 +{
 +        int retval;
 +        struct mpoa_client *mpc;
++        struct ethhdr *eth;
 +        int i = 0;
 +        
 +        mpc = find_mpc_by_lec(dev); /* this should NEVER fail */
@@ -20860,11 +21007,12 @@
 +                goto non_ip;
 +	}
 +
-+        if ( ((struct ethhdr *)(skb->data + 2))->h_proto != htons(ETH_P_IP) )
++        eth = (struct ethhdr *)skb->data;
++        if (eth->h_proto != htons(ETH_P_IP))
 +                goto non_ip; /* Multi-Protocol Over ATM :-) */
 +
 +        while (i < mpc->number_of_mps_macs) {
-+                if (memcmp(skb->data + 2, (mpc->mps_macs + i*ETH_ALEN), ETH_ALEN) == 0)
++                if (memcmp(eth->h_dest, (mpc->mps_macs + i*ETH_ALEN), ETH_ALEN) == 0)
 +                        if ( send_via_shortcut(skb, mpc) == 0 )           /* try shortcut */
 +                                return 0;                                 /* success!     */
 +                i++;
@@ -21025,21 +21173,13 @@
 +        skb_pull(skb, sizeof(struct llc_snap_hdr) + sizeof(tag)); /* get rid of LLC/SNAP header */
 +        new_skb = skb_realloc_headroom(skb, eg->ctrl_info.DH_length); /* LLC/SNAP is shorter than MAC header :( */
 +        kfree_skb(skb);
-+        skb_push(new_skb, eg->ctrl_info.DH_length);     /* add MAC header             */
++        if (new_skb == NULL) return;
++        skb_push(new_skb, eg->ctrl_info.DH_length);     /* add MAC header */
 +        memcpy(new_skb->data, eg->ctrl_info.DLL_header, eg->ctrl_info.DH_length);
-+        new_skb->mac.raw = new_skb->data;
-+        skb_pull(new_skb, ETH_HLEN);
-+
-+        tmp = eg->ctrl_info.DLL_header + 2*ETH_ALEN;
-+        memcpy(&new_skb->protocol, tmp, sizeof(uint16_t));
-+        if ( ntohs(new_skb->protocol) < 1536 ) {
-+                printk("mpoa: (%s) mpc_push() received non-DIX Ethernet frame, purging\n",
-+                       dev->name);
-+		kfree_skb(new_skb);
-+                return;
-+        }
++        new_skb->protocol = eth_type_trans(new_skb, dev);
++        new_skb->nh.raw = new_skb->data;
 +
-+	eg->latest_ip_addr = ((struct iphdr *)new_skb->data)->saddr;
++	eg->latest_ip_addr = new_skb->nh.iph->saddr;
 +        eg->packets_rcvd++;
 +                
 +        netif_rx(new_skb);
@@ -21836,7 +21976,7 @@
 +}
 +#endif /* MODULE */
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/mpc.h	Sat Dec  5 01:50:17 1998
++++ work/net/atm/mpc.h	Tue Feb  9 18:09:24 1999
 @@ -0,0 +1,65 @@
 +#ifndef _MPC_H_
 +#define _MPC_H_
@@ -21904,7 +22044,7 @@
 +
 +#endif /* _MPC_H_ */
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/mpoa_caches.c	Sat Dec  5 01:50:28 1998
++++ work/net/atm/mpoa_caches.c	Tue Feb  9 15:49:58 1999
 @@ -0,0 +1,557 @@
 +#include <linux/types.h>
 +#include <linux/atmmpc.h>
@@ -22464,7 +22604,7 @@
 +        return;
 +}
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/mpoa_caches.h	Sun Nov  1 15:31:53 1998
++++ work/net/atm/mpoa_caches.h	Tue Feb  9 18:09:24 1999
 @@ -0,0 +1,90 @@
 +#ifndef MPOA_CACHES_H
 +#define MPOA_CACHES_H
@@ -22557,8 +22697,8 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/mpoa_proc.c	Sun Nov  1 15:31:53 1998
-@@ -0,0 +1,388 @@
++++ work/net/atm/mpoa_proc.c	Tue Feb  9 17:15:25 1999
+@@ -0,0 +1,395 @@
 +#include <linux/config.h>
 +
 +#ifdef CONFIG_PROC_FS
@@ -22883,26 +23023,34 @@
 +        /* next receive values */
 +	tmp = strstr(buff, "rx=");
 +	if(tmp == NULL) return 0;
-+	tmp += 3;
-+	prev = tmp;
-+	for( i = 0; i < 2; i++){
-+	         tmp = strchr(prev, ',');
-+		 if (tmp == NULL) return 0;
-+		 memset(temp, '\0', 256);
-+		 memcpy(temp, prev, tmp-prev);
-+		 value[i] = (int)simple_strtoul(temp, NULL, 0);
-+		 tmp ++; 
-+		 prev = tmp;
-+	}
-+	tmp = strchr(prev, '\0');
-+	if (tmp == NULL) return 0;
-+	memset(temp, '\0', 256);
-+        memcpy(temp, prev, tmp-prev);
-+        value[i] = (int)simple_strtoul(temp, NULL, 0);
-+	qos.rxtp.traffic_class = ATM_CBR;
-+	qos.rxtp.max_pcr = value[0];
-+	qos.rxtp.max_cdv = value[1];
-+	qos.rxtp.max_sdu = value[2];
++        if (strstr(buff, "rx=tx")) { /* rx == tx */
++                qos.rxtp.traffic_class = qos.txtp.traffic_class;
++                qos.rxtp.max_pcr = qos.txtp.max_pcr;
++                qos.rxtp.max_cdv = qos.txtp.max_cdv;
++                qos.rxtp.max_sdu = qos.txtp.max_sdu;
++        } else {
++                tmp += 3;
++                prev = tmp;
++                for( i = 0; i < 2; i++){
++                        tmp = strchr(prev, ',');
++                        if (tmp == NULL) return 0;
++                        memset(temp, '\0', 256);
++                        memcpy(temp, prev, tmp-prev);
++                        value[i] = (int)simple_strtoul(temp, NULL, 0);
++                        tmp ++; 
++                        prev = tmp;
++                }
++                tmp = strchr(prev, '\0');
++                if (tmp == NULL) return 0;
++                memset(temp, '\0', 256);
++                memcpy(temp, prev, tmp-prev);
++                value[i] = (int)simple_strtoul(temp, NULL, 0);
++                qos.rxtp.traffic_class = ATM_CBR;
++                qos.rxtp.max_pcr = value[0];
++                qos.rxtp.max_cdv = value[1];
++                qos.rxtp.max_sdu = value[2];
++        }
++        qos.aal = ATM_AAL5;
 +	dprintk("mpoa: mpoa_proc.c: parse_qos(): setting qos paramameters to tx=%d,%d,%d rx=%d,%d,%d\n",
 +		qos.txtp.max_pcr,
 +		qos.txtp.max_cdv,
@@ -22912,7 +23060,6 @@
 +		qos.rxtp.max_sdu
 +		);
 +
-+        qos.aal = ATM_AAL5;
 +	atm_mpoa_add_qos(ipaddr, &qos);
 +	return 1;
 +}
@@ -22948,7 +23095,7 @@
 +
 +
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/lane_mpoa_init.c	Sun Nov  1 15:31:53 1998
++++ work/net/atm/lane_mpoa_init.c	Tue Feb  9 15:49:58 1999
 @@ -0,0 +1,48 @@
 +#include <linux/config.h>
 +#include <linux/module.h>
@@ -22999,8 +23146,8 @@
 +}        
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/proc.c	Sun Nov  1 15:31:53 1998
-@@ -0,0 +1,610 @@
++++ work/net/atm/proc.c	Tue Feb  9 17:15:25 1999
+@@ -0,0 +1,618 @@
 +/* net/atm/proc.c - ATM /proc interface */
 +
 +/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */
@@ -23227,7 +23374,8 @@
 +	off += ip_len;
 +	while (ip_len++ < 16) buf[off++] = ' ';
 +	if (!clip_vcc)
-+		if (entry->expires > jiffies) strcpy(buf+off,"(resolving)\n");
++		if (time_before(jiffies, entry->expires))
++			strcpy(buf+off,"(resolving)\n");
 +		else sprintf(buf+off,"(expired, ref %d)\n",
 +			    atomic_read(&entry->neigh->refcnt));
 +	else if (!svc)
@@ -23513,6 +23661,13 @@
 +				lec_info(entry, buf+e);
 +				return strlen(buf);
 +			}
++			for(entry=priv->mcast_fwds; entry;
++			    entry=entry->next) {
++				if (--count) continue;
++				e=sprintf(buf,"%s ",dev_lec[d]->name);
++				lec_info(entry, buf+e);
++				return strlen(buf);
++			}
 +		}
 +		return 0;
 +	}
@@ -23612,7 +23767,7 @@
 +	return error;
 +}
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/protocols.h	Sun Nov  1 15:31:53 1998
++++ work/net/atm/protocols.h	Tue Feb  9 15:49:58 1999
 @@ -0,0 +1,16 @@
 +/* net/atm/protocols.h - ATM protocol handler entry points */
 +
@@ -23631,7 +23786,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/pvc.c	Sun Nov  1 15:31:53 1998
++++ work/net/atm/pvc.c	Tue Feb  9 15:49:58 1999
 @@ -0,0 +1,158 @@
 +/* net/atm/pvc.c - ATM PVC sockets */
 +
@@ -23756,7 +23911,7 @@
 +static int pvc_create(struct socket *sock,int protocol)
 +{
 +	sock->ops = &pvc_proto_ops;
-+	return atm_create(sock,protocol);
++	return atm_create(sock,protocol,PF_ATMPVC);
 +}
 +
 +
@@ -23792,7 +23947,7 @@
 +#endif
 +}
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/raw.c	Sun Nov  1 15:31:53 1998
++++ work/net/atm/raw.c	Tue Feb  9 15:49:58 1999
 @@ -0,0 +1,80 @@
 +/* net/atm/raw.c - Raw AAL0 and AAL5 transports */
 +
@@ -23875,11 +24030,11 @@
 +	return 0;
 +}
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/resources.c	Sun Nov  1 15:31:53 1998
-@@ -0,0 +1,186 @@
++++ work/net/atm/resources.c	Tue Feb  9 16:45:37 1999
+@@ -0,0 +1,201 @@
 +/* net/atm/resources.c - Staticly allocated resources */
 +
-+/* Written 1995-1998 by Werner Almesberger, EPFL LRC */
++/* Written 1995-1999 by Werner Almesberger, EPFL LRC */
 +
 +
 +#include <linux/config.h>
@@ -23888,6 +24043,7 @@
 +#include <linux/atmdev.h>
 +#include <linux/kernel.h> /* for barrier */
 +#include <linux/module.h>
++#include <net/sock.h>	 /* for struct sock */
 +#include <asm/segment.h> /* for get_fs_long and put_fs_long */
 +
 +#include "common.h"
@@ -24003,18 +24159,32 @@
 +}
 +
 +
-+struct atm_vcc *alloc_atm_vcc(void)
++/* Handler for sk->destruct, invoked by sk_free() */
++static void atm_free_sock(struct sock *sk)
++{
++	kfree(sk->protinfo.af_atm);
++}
++
++
++struct sock *alloc_atm_vcc_sk(int family)
 +{
++	struct sock *sk;
 +	struct atm_vcc *vcc;
 +
-+	vcc = kmalloc(sizeof(*vcc),GFP_KERNEL);
-+	if (!vcc) return NULL;
++	sk = sk_alloc(family, GFP_KERNEL, 1);
++	if (!sk) return NULL;
++	vcc = sk->protinfo.af_atm = kmalloc(sizeof(*vcc),GFP_KERNEL);
++	if (!vcc) {
++		sk_free(sk);
++		return NULL;
++	}
++	sk->destruct = atm_free_sock;
 +	memset(vcc,0,sizeof(*vcc));
 +	if (nodev_vccs) nodev_vccs->prev = vcc;
 +	vcc->prev = NULL;
 +	vcc->next = nodev_vccs;
 +	nodev_vccs = vcc;
-+	return vcc;
++	return sk;
 +}
 +
 +
@@ -24031,10 +24201,10 @@
 +}
 +
 +
-+void free_atm_vcc(struct atm_vcc *vcc)
++void free_atm_vcc_sk(struct sock *sk)
 +{
-+	unlink_vcc(vcc,NULL);
-+	kfree(vcc);
++	unlink_vcc(sk->protinfo.af_atm,NULL);
++	sk_free(sk);
 +}
 +
 +
@@ -24064,7 +24234,7 @@
 +EXPORT_SYMBOL(shutdown_atm_dev);
 +EXPORT_SYMBOL(bind_vcc);
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/resources.h	Sun Nov  1 15:31:53 1998
++++ work/net/atm/resources.h	Tue Feb  9 18:09:24 1999
 @@ -0,0 +1,32 @@
 +/* net/atm/resources.h - ATM-related resources */
 +
@@ -24082,8 +24252,8 @@
 +extern struct atm_vcc *nodev_vccs; /* VCCs not linked to any device */
 +
 +
-+struct atm_vcc *alloc_atm_vcc(void);
-+void free_atm_vcc(struct atm_vcc *vcc);
++struct sock *alloc_atm_vcc_sk(int family);
++void free_atm_vcc_sk(struct sock *sk);
 +
 +
 +#ifdef CONFIG_PROC_FS
@@ -24099,7 +24269,7 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/signaling.c	Sun Nov  1 15:31:53 1998
++++ work/net/atm/signaling.c	Tue Feb  9 15:49:58 1999
 @@ -0,0 +1,252 @@
 +/* net/atm/signaling.c - ATM signaling */
 +
@@ -24145,10 +24315,10 @@
 +
 +	while (!sigd) {
 +#ifdef WAIT_FOR_DEMON
-+		if (silence < jiffies) {
++		if (time_after(jiffies, silence) || silence == 0) {
 +			printk(KERN_INFO "atmsvc: waiting for signaling demon "
 +			    "...\n");
-+			silence = jiffies+30*HZ;
++			silence = (jiffies+30*HZ)|1;
 +		}
 +		sleep_on(&sigd_sleep);
 +#else
@@ -24354,7 +24524,7 @@
 +	return 0;
 +}
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/signaling.h	Sun Nov  1 15:31:53 1998
++++ work/net/atm/signaling.h	Tue Feb  9 18:09:24 1999
 @@ -0,0 +1,25 @@
 +/* net/atm/signaling.h - ATM signaling */
 + 
@@ -24382,8 +24552,8 @@
 +
 +#endif
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/svc.c	Sun Nov  1 15:31:53 1998
-@@ -0,0 +1,387 @@
++++ work/net/atm/svc.c	Tue Feb  9 15:49:58 1999
+@@ -0,0 +1,388 @@
 +/* net/atm/svc.c - ATM SVC sockets */
 +
 +/* Written 1995-1998 by Werner Almesberger, EPFL LRC/ICA */
@@ -24471,17 +24641,18 @@
 +
 +static int svc_release(struct socket *sock,struct socket *peer)
 +{
-+	struct atm_vcc *vcc = ATM_SD(sock);
++	struct atm_vcc *vcc;
 +
++	if (!sock->sk) return 0;
++	vcc = ATM_SD(sock);
 +	DPRINTK("svc_release %p\n",vcc);
-+	if (!vcc) return 0;
 +	vcc->flags &= ~ATM_VF_READY;
-+	atm_release_vcc(vcc,0);
++	atm_release_vcc_sk(sock->sk,0);
 +	svc_disconnect(vcc);
 +	    /* VCC pointer is used as a reference, so we must not free it
 +	       (thereby subjecting it to re-use) before all pending connections
 +	        are closed */
-+	free_atm_vcc(vcc);
++	free_atm_vcc_sk(sock->sk);
 +	return 0;
 +}
 +
@@ -24742,7 +24913,7 @@
 +	int error;
 +
 +	sock->ops = &svc_proto_ops;
-+	error = atm_create(sock,protocol);
++	error = atm_create(sock,protocol,AF_ATMSVC);
 +	if (error) return error;
 +	ATM_SD(sock)->callback = svc_callback;
 +	ATM_SD(sock)->local.sas_family = AF_ATMSVC;
@@ -24772,7 +24943,7 @@
 +	}
 +}
 --- /dev/null	Tue Jan  1 05:00:00 1980
-+++ work/net/atm/tunable.h	Sun Nov  1 15:31:53 1998
++++ work/net/atm/tunable.h	Tue Feb  9 15:49:58 1999
 @@ -0,0 +1,26 @@
 +/* net/atm/tunable.h - Tunable parameters of ATM support */
 +
@@ -24801,7 +24972,7 @@
 +
 +#endif
 --- ref/net/core/skbuff.c	Tue Sep 15 07:52:10 1998
-+++ work/net/core/skbuff.c	Sun Nov  1 15:31:53 1998
++++ work/net/core/skbuff.c	Tue Feb  9 15:49:58 1999
 @@ -61,6 +61,10 @@
  #include <asm/uaccess.h>
  #include <asm/system.h>
diff -ur --new-file old/atm/doc/usage.tex new/atm/doc/usage.tex
--- old/atm/doc/usage.tex	Sat Dec  5 01:27:24 1998
+++ new/atm/doc/usage.tex	Tue Feb  9 17:54:34 1999
@@ -1,7 +1,7 @@
 %%def%:=
 
 %:\begin{verbatim}
-%:Usage instructions  -  ATM on Linux, release 0.52
+%:Usage instructions  -  ATM on Linux, release 0.53
 %:-------------------------------------------------
 %:
 %:\end{verbatim}
@@ -38,7 +38,7 @@
 
 \title{ATM on Linux \\
   User's guide \\
-  Release 0.52 (alpha)}
+  Release 0.53 (alpha)}
 \author{Werner Almesberger \\
   {\tt Werner.Almesberger@epfl.ch} \\
   \\
@@ -82,9 +82,9 @@
 In order to install this package, you need
 \begin{itemize}
   \item the package itself
-    \url{ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.52.tar.gz}
-  \item the Linux kernel, version 2.1.126, e.g. from
-    \url{ftp://ftp.kernel.org/pub/linux/kernel/v2.1/linux-2.1.126.tar.gz}
+    \url{ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.53.tar.gz}
+  \item the Linux kernel, version 2.2.1, e.g. from
+    \url{ftp://ftp.kernel.org/pub/linux/kernel/v2.1/linux-2.2.1.tar.gz}
   \item Perl, version 4 or 5
   \item if you want memory debugging: MPR version 1.6, e.g. from
     \url{ftp://sunsite.unc.edu/pub/Linux/devel/lang/c/mpr-1.6.tar.gz}
@@ -99,13 +99,13 @@
 distribution:
 
 \begin{verbatim}
-tar xfz atm-0.52.tar.gz
+tar xfz atm-0.53.tar.gz
 \end{verbatim}
 
 and the kernel source:
 
 \begin{verbatim}
-tar xfz linux-2.1.126.tar.gz
+tar xfz linux-2.2.1.tar.gz
 \end{verbatim}
 
 Finally, you can extract the ATM-related patches:
@@ -120,9 +120,9 @@
 \begin{description}
   \item[\path{atm/}] Documentation in ASCII format, kernel patch, top-level
     \name{Makefile}, and distribution scripts
-  \item[\path{atm/sigd/}] UNI 3.0, UNI 3.1, and UNI 4.0 signaling demon:
+  \item[\path{atm/sigd/}]  UNI 3.0, UNI 3.1, and UNI 4.0 signaling demon:
     \name{atmsigd}
-  \item[\path{atm/sigd.new/}] Experimental version: \name{atmsigd.new}
+  \item[\path{atm/sigd.old/}] Older version: \name{atmsigd.old}
   \item[\path{atm/saal/}] Signaling AAL library (SSCOP, SSCF, and SAAL)
   \item[\path{atm/qgen/}] Q.2931-style message handling
   \item[\path{atm/ilmid/}] ILMI address registration demon: \name{ilmid}
@@ -134,6 +134,7 @@
     \name{ttcp\_atm}, \name{window}
   \item[\path{atm/arpd/}] ATMARP tools and demon: \name{atmarp}, \name{atmarpd}
   \item[\path{atm/led/}] LAN Emulation demon: \name{zeppelin}
+  \item[\path{atm/led.new/}] Experimental version: \name{zeppelin.new}
   \item[\path{atm/lane/}] LAN Emulation servers: \name{bus}, \name{lecs},
     \name{les}
   \item[\path{atm/mpoad/}] Multi-Protocol Over ATM demon: \name{mpcd}
diff -ur --new-file old/atm/doc/usage.txt new/atm/doc/usage.txt
--- old/atm/doc/usage.txt	Sat Dec  5 01:58:08 1998
+++ new/atm/doc/usage.txt	Tue Feb  9 18:22:36 1999
@@ -1,4 +1,4 @@
-Usage instructions  -  ATM on Linux, release 0.52
+Usage instructions  -  ATM on Linux, release 0.53
 -------------------------------------------------
 
 For updates of ATM on Linux, please check the Web page at  
@@ -17,9 +17,9 @@
 In order to install this package, you need 
 
   - the package itself  
-    ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.52.tar.gz  
-  - the Linux kernel, version 2.1.126, e.g. from  
-    ftp://ftp.kernel.org/pub/linux/kernel/v2.1/linux-2.1.126.tar.gz  
+    ftp://lrcftp.epfl.ch/pub/linux/atm/dist/atm-0.53.tar.gz  
+  - the Linux kernel, version 2.2.1, e.g. from  
+    ftp://ftp.kernel.org/pub/linux/kernel/v2.1/linux-2.2.1.tar.gz  
   - Perl, version 4 or 5 
   - if you want memory debugging: MPR version 1.6, e.g. from  
     ftp://sunsite.unc.edu/pub/Linux/devel/lang/c/mpr-1.6.tar.gz  
@@ -33,11 +33,11 @@
 all the files listed above there. Then extract the ATM on Linux 
 distribution:
 
-tar xfz atm-0.52.tar.gz
+tar xfz atm-0.53.tar.gz
 
 and the kernel source:
 
-tar xfz linux-2.1.126.tar.gz
+tar xfz linux-2.2.1.tar.gz
 
 Finally, you can extract the ATM-related patches:
 
@@ -49,7 +49,7 @@
   atm/  Documentation in ASCII format, kernel patch, top-level Makefile, 
     and distribution scripts 
   atm/sigd/  UNI 3.0, UNI 3.1, and UNI 4.0 signaling demon: atmsigd 
-  atm/sigd.new/  Experimental version: atmsigd.new 
+  atm/sigd.old/  Older version: atmsigd.old 
   atm/saal/  Signaling AAL library (SSCOP, SSCF, and SAAL) 
   atm/qgen/  Q.2931-style message handling 
   atm/ilmid/  ILMI address registration demon: ilmid 
@@ -59,6 +59,7 @@
     ttcp_atm, window 
   atm/arpd/  ATMARP tools and demon: atmarp, atmarpd 
   atm/led/  LAN Emulation demon: zeppelin 
+  atm/led.new/  Experimental version: zeppelin.new 
   atm/lane/  LAN Emulation servers: bus, lecs, les 
   atm/mpoad/  Multi-Protocol Over ATM demon: mpcd 
   atm/aqd/  Arequipa demon: aqpvc, arequipad 
diff -ur --new-file old/atm/led/conn.c new/atm/led/conn.c
--- old/atm/led/conn.c	Sat Dec  5 01:35:52 1998
+++ new/atm/led/conn.c	Tue Feb  9 17:12:00 1999
@@ -234,7 +234,6 @@
   }
   for(conn=connlist;conn;conn=next) {
     next=conn->next;
-    close(conn->fd);
     if (conn->conn_info)
       mem_free(EINST, conn->conn_info);
     close(conn->fd);
diff -ur --new-file old/atm/led/kernel_itf.c new/atm/led/kernel_itf.c
--- old/atm/led/kernel_itf.c	Tue Aug 11 16:53:57 1998
+++ new/atm/led/kernel_itf.c	Tue Feb  9 17:12:01 1999
@@ -109,7 +109,15 @@
 
   memset(mesg, 0, sizeof(struct atmlec_msg) + datalen);
   mesg->type = type;
-  if (type != l_config) {
+
+  if (type == l_config)
+    memcpy(&mesg->content.config, config, sizeof(struct atmlec_config_msg));
+  else if (type == l_should_bridge) {
+      memcpy(mesg->content.proxy.mac_addr, mac_addr, ETH_ALEN);
+      mesg->content.proxy.tran_id = flag;
+      mesg->content.proxy.lec_id = no_source_le_narp; /* cheat with arguments */
+  }
+  else {
     if (mac_addr)
       memcpy(&mesg->content.normal.mac_addr, mac_addr, ETH_ALEN);
     if (atm_addr)
@@ -117,8 +125,7 @@
     mesg->content.normal.flag = flag;
     mesg->content.normal.targetless_le_arp = targetless_le_arp; /* LANE2 */
     mesg->content.normal.no_source_le_narp = no_source_le_narp;
-  } else
-    memcpy(&mesg->content.config, config, sizeof(struct atmlec_config_msg));
+  }
 
   mesg->sizeoftlvs = datalen;
   if (datalen > 0)
@@ -167,6 +174,8 @@
     return "CONFIG";
   case l_associate_req:
     return "LANE2_ASSOCIATE_REQ";
+  case l_should_bridge:
+    return "SHOULD_BRIDGE";
   default:
     return "UNKNOWN MSG TYPE";
   }
diff -ur --new-file old/atm/led/lec_arp.c new/atm/led/lec_arp.c
--- old/atm/led/lec_arp.c	Tue Aug 11 16:53:57 1998
+++ new/atm/led/lec_arp.c	Tue Feb  9 17:12:01 1999
@@ -77,6 +77,10 @@
   SVC_SETUP_CALLBACK   svc_setup_callback;
   void                 (*associate_req_callback) (HANDLE lc_elan_handle,
                                                   int sizeoftlvs); /* LANE2 */
+  void                 (*proxy_arp_rsp_callback) (HANDLE lc_elan_handle,
+                                                  UINT8 *mac_addr, UINT32 tran_id,
+                                                  UINT16 lec_id, UINT8 *atm_addr);
+  void                (*topo_req_xmt_callback)(HANDLE lc_elan_handle, UINT32 flag);
   LA_ELAN_LIST         elan_list;
 } LA_CONTEXT;
 
@@ -141,11 +145,39 @@
   }
 }
 
+static void
+kernel_proxy_arp_rsp_callback(struct atmlec_msg *mesg)
+{
+  LA_ELAN_CONTEXT *p_elan;
+
+  utl_list_traverse(lec_arp_context.elan_list, p_elan) {
+    lec_arp_context.proxy_arp_rsp_callback(p_elan->lc_elan_handle,
+                                           mesg->content.proxy.mac_addr,
+                                           mesg->content.proxy.tran_id,
+                                           mesg->content.proxy.lec_id,
+                                           mesg->content.proxy.atm_addr);
+  }
+}
+
+static void
+kernel_topo_req_xmt_callback(struct atmlec_msg *mesg)
+{
+  LA_ELAN_CONTEXT *p_elan;
+
+  utl_list_traverse(lec_arp_context.elan_list, p_elan) {
+    lec_arp_context.topo_req_xmt_callback(p_elan->lc_elan_handle,
+                                           mesg->content.normal.flag);
+  }
+}
+
 STATUS 
 la_create (HANDLE lc_handle, ARP_XMT_CALLBACK arp_xmt_callback,
 	   FLUSH_XMT_CALLBACK flush_xmt_callback,
 	   SVC_SETUP_CALLBACK svc_setup_callback,
            void (*associate_req_callback)(HANDLE lc_elan_handle, int sizeoftlvs), /* LANE2 */
+           void (*proxy_arp_rsp_callback)(HANDLE lc_elan_handle, UINT8 *mac_addr,
+                                          UINT32 tran_id, UINT16 lec_id, UINT8 *atm_addr),
+           void (*topo_req_xmt_callback)(HANDLE lc_elan_handle, UINT32 flag),
 	   HANDLE *p_la_handle)
 {
   lec_arp_context.lc_handle = lc_handle;
@@ -153,12 +185,16 @@
   lec_arp_context.flush_xmt_callback  = flush_xmt_callback;
   lec_arp_context.svc_setup_callback  = svc_setup_callback;
   lec_arp_context.associate_req_callback = associate_req_callback; /* LANE2 */
+  lec_arp_context.proxy_arp_rsp_callback = proxy_arp_rsp_callback;
+  lec_arp_context.topo_req_xmt_callback = topo_req_xmt_callback;
   
   utl_list_init(lec_arp_context.elan_list);
 
   kernel_register_callback(l_arp_xmt, kernel_arp_xmt_callback);
   kernel_register_callback(l_svc_setup, kernel_svc_setup_xmt_callback);
   kernel_register_callback(l_associate_req, kernel_associate_req_callback);
+  kernel_register_callback(l_should_bridge, kernel_proxy_arp_rsp_callback);
+  kernel_register_callback(l_topology_change, kernel_topo_req_xmt_callback);
 
   *p_la_handle = (HANDLE)&lec_arp_context;
 
@@ -249,6 +285,7 @@
   conf_mesg.flush_timeout = flush_timeout;
   conf_mesg.path_switching_delay = path_switching_delay;
   conf_mesg.lane_version = lane_version_number; /* LANE2 */
+  conf_mesg.is_proxy = (proxy_flag == TRUE) ? 1 : 0;
   kernel_sendmsg(l_config, NULL, NULL, &conf_mesg, 0, NULL, 0, 0, 0);
 }
 
diff -ur --new-file old/atm/led/lec_arp.h new/atm/led/lec_arp.h
--- old/atm/led/lec_arp.h	Tue Aug 11 16:53:57 1998
+++ new/atm/led/lec_arp.h	Tue Feb  9 17:12:01 1999
@@ -313,6 +313,13 @@
                   SVC_SETUP_CALLBACK  svc_setup_callback,
                   void                (*associate_req_callback)(HANDLE lc_elan_handle,
                                                                 int sizeoftlvs),  /* LANE2 */
+                  void                (*proxy_arp_rsp_callback)(HANDLE lc_elan_handle,
+                                                                UINT8 *mac_addr,
+                                                                UINT32 tran_id,
+                                                                UINT16 lec_id,
+                                                                UINT8 *atm_addr),
+                  void                (*topo_req_xmt_callback)(HANDLE lc_elan_handle,
+                                                               UINT32 flag),
                   HANDLE              *p_la_handle);
 
 /*++
diff -ur --new-file old/atm/led/lec_ctrl.c new/atm/led/lec_ctrl.c
--- old/atm/led/lec_ctrl.c	Sat Dec  5 01:36:13 1998
+++ new/atm/led/lec_ctrl.c	Tue Feb  9 17:12:04 1999
@@ -1478,7 +1478,7 @@
        *tlvdata++ = 0x00; *tlvdata++ = 0xa0;
        *tlvdata++ = 0x3e; *tlvdata++ = 0x2c;
 #endif
-       *(UINT32 *)tlvdata = TLV_X5_ADJUSTEMENT;
+       *(UINT32 *)tlvdata = hton32(TLV_X5_ADJUSTEMENT);
        tlvdata += sizeof(TLV_X5_ADJUSTEMENT); /* should be 4 */
        *tlvdata++ = 0x00; /* length 0 */
        frame_size += 5;
@@ -1494,7 +1494,7 @@
        *tlvdata++ = 0x00; *tlvdata++ = 0xa0;
        *tlvdata++ = 0x3e; *tlvdata++ = 0x11;
 #endif
-       *(UINT32 *)tlvdata = TLV_L3_ADDRESS;
+       *(UINT32 *)tlvdata = hton32(TLV_L3_ADDRESS);
        tlvdata += sizeof(TLV_L3_ADDRESS);
        *tlvdata++ = l3_address_size;
        memcpy(tlvdata, &l3_addr, l3_address_size);
@@ -1632,7 +1632,7 @@
        *tlvdata++ = 0x00; *tlvdata++ = 0xa0;
        *tlvdata++ = 0x3e; *tlvdata++ = 0x2c;
 #endif
-       *(UINT32 *)tlvdata = TLV_X5_ADJUSTEMENT;
+       *(UINT32 *)tlvdata = hton32(TLV_X5_ADJUSTEMENT);
        tlvdata += sizeof(TLV_X5_ADJUSTEMENT); /* should be 4 */
        *tlvdata++ = 0x00; /* length 0 */
        frame_size += 5;
@@ -1648,7 +1648,7 @@
        *tlvdata++ = 0x00; *tlvdata++ = 0xa0;
        *tlvdata++ = 0x3e; *tlvdata++ = 0x11;
 #endif
-       *(UINT32 *)tlvdata = TLV_L3_ADDRESS;
+       *(UINT32 *)tlvdata = hton32(TLV_L3_ADDRESS);
        tlvdata += sizeof(TLV_L3_ADDRESS);
        *tlvdata++ = l3_address_size;
        memcpy(tlvdata, &l3_addr, l3_address_size);
@@ -2087,6 +2087,75 @@
 }
     
 
+/*++
+* l_should_bridge msg from kernel causes this function to be called.
+* We are acting as a bridge and know mac_addr is behind us.
+*
+* =================
+* = proxy_arp_rsp =
+* =================
+--*/
+static void proxy_arp_rsp(HANDLE lc_elan_handle, UINT8 *mac_addr,
+                          UINT32 tran_id, UINT16 lec_id, UINT8 *atm_addr)
+{
+   LE_ARP_FRAME *frame;
+   size_t framelength;
+   LC_ELAN_CONTEXT *p_elan = (LC_ELAN_CONTEXT *) lc_elan_handle;
+
+   framelength = sizeof(LE_ARP_FRAME) + p_elan->lec_state.sizeoftlvs;
+   frame = (LE_ARP_FRAME *)os_mem_alloc(framelength);
+   memset(frame, 0 , framelength);
+
+   /* construct frame with the parameters supplied by requestor */
+   lc_ctrl_hdr_make(lc_elan_handle, &frame->hdr, LE_ARP_RSP);
+   frame->hdr.tran_id = tran_id;
+   frame->hdr.req_lec_id = lec_id;
+   frame->hdr.flags = hton16(LE_FLAG_REMOTE_ADDR);
+   frame->target_lan_dst.tag = hton16(TAG_MAC_ADDR);
+   ESI_COPY(mac_addr, frame->target_lan_dst.mac_addr);
+   ATM_COPY (p_elan->lec_state.c1n_my_atm_addr, frame->target_atm_addr);
+   memcpy(&frame->src_atm_addr, atm_addr, ATM_ESA_LEN);
+
+   /* add TLVs, if any */
+   if (p_elan->lec_state.sizeoftlvs != 0) {
+      memcpy(frame + 1, p_elan->lec_state.tlvs, p_elan->lec_state.sizeoftlvs);
+      frame->tlv_count++;
+   }
+
+   cm_sap_xmt_vc(p_elan->ctrl_direct_conn_handle,
+                 frame, framelength,
+                 USER_DATA_INTERNAL, NULL);
+
+   os_mem_dealloc(frame);
+   return;
+}
+
+
+/*++
+* Kernel sent a Configuration Bridge PDU to BUS and asks us to send a
+* respective LE_TOPOLOGY_REQUEST to LES
+*
+* ====================
+* = lc_topo_req_xmt =
+* ====================
+--*/
+static void lc_topo_req_xmt (HANDLE   lc_elan_handle,
+                             UINT32   flag)
+{
+  LE_TOPOLOGY_FRAME frame;
+  LC_ELAN_CONTEXT *p_elan = (LC_ELAN_CONTEXT *) lc_elan_handle;
+
+  memset(&frame, 0, sizeof(LE_TOPOLOGY_FRAME));
+  lc_ctrl_hdr_make(lc_elan_handle, &frame.hdr, LE_TOPOLOGY_REQ);
+
+  if (flag)
+    frame.hdr.flags = hton16(LE_FLAG_TOPOLOGY_CHANGE);
+
+  cm_sap_xmt_vc(p_elan->ctrl_direct_conn_handle, &frame,
+                sizeof(LE_TOPOLOGY_FRAME), USER_DATA_INTERNAL, NULL);
+
+  return;
+}
 
 /*++
 * ====================
@@ -2162,6 +2231,8 @@
                        lc_flush_xmt,
                        lc_svc_setup,
                        associate_req,     /* LANE2 */
+                       proxy_arp_rsp,
+                       lc_topo_req_xmt,
                        &p_context->la_handle);
    if (status != STATUS_K_SUCCESS)
       {
@@ -2413,7 +2484,7 @@
 	     p_elan->lec_state.c20_le_arp_response_time,
 	     p_elan->lec_state.c21_flush_timeout,
 	     p_elan->lec_state.c22_path_switching_delay,
-             (v2_capable)? 2:1);
+             (v2_capable)? 2 : 1);
   
   /* Register as a client to the Address Registration module. */
   
@@ -3245,12 +3316,12 @@
 
   
   /* LANE2: If LES is not v2 capable we can't be either */
-  if (frame->hdr.flags & LE_FLAG_V2_REQUIRED &&
+  if (frame->hdr.flags & hton16(LE_FLAG_V2_REQUIRED) &&
       p_elan->lec_state.c29_v2_capable == FALSE) {
     EVENT (EM_NERR, ("LES requires LANEv2 but I'm not v2 capable\n"));
     return;
   }
-  if (!frame->hdr.flags & LE_FLAG_V2_REQUIRED) { /* LES not v2 */
+  if (!frame->hdr.flags & hton16(LE_FLAG_V2_REQUIRED)) { /* LES not v2 */
     p_elan->lec_state.c29_v2_capable = FALSE;
     p_elan->lec_state.c32_selective_mcast = FALSE;
   }
@@ -3487,6 +3558,12 @@
 				user_data,
 				NULL);
 	break;
+      } else { /* check if our bridging tables knows this MAC */
+        if (p_elan->lec_state.c4_proxy_flag == TRUE)
+          kernel_sendmsg(l_should_bridge, frame->target_lan_dst.mac_addr,
+                         NULL, NULL, frame->hdr.tran_id,
+                         NULL, 0, 0, frame->hdr.req_lec_id);
+        break;
       }
     
     break;
diff -ur --new-file old/atm/led/main.c new/atm/led/main.c
--- old/atm/led/main.c	Sat Dec  5 01:36:26 1998
+++ new/atm/led/main.c	Tue Feb  9 17:12:04 1999
@@ -271,7 +271,7 @@
   printf("Usage: %s [-c LECS_address]|[-s LES_address] [-e esi] [-n VLAN_name]"
     " [-m mesg_mask] [-l listen_address] [-i interface_number]"
     " [-q qos_spec] [-1] [-2] [-f Fore specific name]"
-    " [-t 1516|1580|4544|9234|18190]\n", progname);
+    " [-t 1516|1580|4544|9234|18190] [-p]\n", progname);
 }
 
 static int
@@ -372,7 +372,7 @@
   memset(foreId, '\0', FORE_ID_LEN);
   memset(&preferred_les, 0, sizeof(ADDR_ATM));
   while(poll_ret != -1) {
-    poll_ret = getopt(argc, argv, "c:e:n:s:m:l:i:q:12f:t:");
+    poll_ret = getopt(argc, argv, "c:e:n:s:m:l:i:q:12pf:t:");
     switch(poll_ret) {
     case 'c':
       if (atm_set) {
@@ -454,6 +454,9 @@
     case '2':
       spec_version = 2;
       v2_capable = TRUE;
+      break;
+    case 'p':
+      proxy_flag = TRUE;
       break;
     case 'f':
       memcpy (foreId, optarg, FORE_ID_LEN);
diff -ur --new-file old/atm/led/zeppelin.8 new/atm/led/zeppelin.8
--- old/atm/led/zeppelin.8	Sat Dec  5 01:36:31 1998
+++ new/atm/led/zeppelin.8	Tue Feb  9 17:12:05 1999
@@ -1,15 +1,20 @@
-.TH zeppelin 8 "Dec 2, 1998" "Linux" "Maintenance Commands"
+.TH zeppelin 8 "Feb 6, 1998" "Linux" "Maintenance Commands"
 .SH NAME
 zeppelin \- ATM LAN Emulation client demon (LED) Zeppelin
 .SH SYNOPSIS
 .B zeppelin
-.RB [ \-c\ \fILECS_address\fP ] |
-.RB [ \-s\ \fILES_address\fP ]
+.RB [ \-c\ \fILECS_address\fP\ |\ \-s\ \fILES_address\fP ]
 .RB [ \-e\ \fIesi\fP ]
 .RB [ \-n\ \fIVLAN_name\fP ]
 .RB [ \-m\ \fImesg_mask\fP ]
+.RB [ \-l\ \fIlisten_address\fP ]
 .RB [ \-i\ \fIinterface_number\fP ]
 .RB [ \-q\ \fIqos_spec\fP ]
+.RB [ \-1\ ]
+.RB [ \-2\ ]
+.RB [ \-t\ \fI1516|1580|4544|9234|18190\fP ]
+.RB [ \-p\ ]
+.RB [ \-f\ \fIFore_specific_name\fP ]
 .SH DESCRIPTION
 A LAN Emulation Client is an entity in an ATM endstation that performs 
 data forwarding, address resolution and other control functions. It 
@@ -51,9 +56,9 @@
 signalling. Use this if you are running more than one client or
 a set of LE servers.
 .IP \fB\-i\ \fIinterface_number\fP
-Linux LEC supports up to 4 network interfaces. This number tells
+Linux LEC supports up to 16 network interfaces. This number tells
 zeppelin to which of these to attach. Network interfaces are
-numbered from "lec0" to "lec3".
+numbered from "lec0" to "lec15".
 .IP \fB\-t\ \fIMTU\fP
 The MTU of ELAN to join. You need to also use \fBifconfig(8)\fP to
 set the MTU of the LANE interface.
@@ -61,12 +66,17 @@
 Run as LANEv1 client. This is the default.
 .IP \fB\-2\fP
 Run as LANEv2 client. This is required by MPOA.
+.IP \fB\-p\fP
+Enable proxy. When started with this option, it is possible to bridge
+packets between ATM and Ethernet. That is, you can use LANE interfaces
+with normal bridging. See the Bridging mini-Howto for more info.
 .IP \fB\-f\ \fIFore\ specific\ name\fP
 The LANE servers on Fore ATM switches can display a special
 name if a client can supply one. This name shows with command
 \'conf lane les show advanced\'.
 .SH BUGS
-Supports only IEEE 802.3 / Ethernet type of ELANs.
+Supports only IEEE 802.3 / Ethernet type of ELANs. Please report any
+new bugs to Heikki Vatiainen <hessu@cs.tut.fi>
 .PP
 John Bonham died 1980 and Led Zeppelin broke.
 .SH AUTHOR
diff -ur --new-file old/atm/led.new/COPYRIGHT.TUT new/atm/led.new/COPYRIGHT.TUT
--- old/atm/led.new/COPYRIGHT.TUT	Thu Jan  1 01:00:00 1970
+++ new/atm/led.new/COPYRIGHT.TUT	Sat Feb  6 14:49:41 1999
@@ -0,0 +1,21 @@
+/*
+ * Marko Kiiskila carnil@cs.tut.fi 
+ * 
+ * Tampere University of Technology - Telecommunications Laboratory
+ *
+ * Permission to use, copy, modify and distribute this
+ * software and its documentation is hereby granted,
+ * provided that both the copyright notice and this
+ * permission notice appear in all copies of the software,
+ * derivative works or modified versions, and any portions
+ * thereof, that both notices appear in supporting
+ * documentation, and that the use of this software is
+ * acknowledged in any publications resulting from using
+ * the software.
+ * 
+ * TUT ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION AND DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS
+ * SOFTWARE.
+ * 
+ */
diff -ur --new-file old/atm/led.new/Makefile new/atm/led.new/Makefile
--- old/atm/led.new/Makefile	Thu Jan  1 01:00:00 1970
+++ new/atm/led.new/Makefile	Tue Feb  9 17:19:11 1999
@@ -0,0 +1,10 @@
+LIBS=-latm -latmd
+OBJS=join.o conn.o main.o address.o frames.o display.o kernel.c 
+BOOTPGMS=zeppelin.new
+MAN8=zeppelin.new.8
+CFLAGS_PRIVATE=
+
+include ../Rules.make
+
+zeppelin.new:$(OBJS)
+	$(CC) $(LDFLAGS) -o $(BOOTPGMS) $(OBJS) $(LIBD) $(LDLIBS) $(LIBS)
diff -ur --new-file old/atm/led.new/address.c new/atm/led.new/address.c
--- old/atm/led.new/address.c	Thu Jan  1 01:00:00 1970
+++ new/atm/led.new/address.c	Sat Feb  6 15:11:34 1999
@@ -0,0 +1,92 @@
+/* address.c - functions to query ESI and local ATM address from kernel */
+
+/*
+ * Marko Kiiskila carnil@cs.tut.fi 
+ * 
+ * Copyright (c) 1996
+ * Tampere University of Technology - Telecommunications Laboratory
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this
+ * software and its documentation is hereby granted,
+ * provided that both the copyright notice and this
+ * permission notice appear in all copies of the software,
+ * derivative works or modified versions, and any portions
+ * thereof, that both notices appear in supporting
+ * documentation, and that the use of this software is
+ * acknowledged in any publications resulting from using
+ * the software.
+ * 
+ * TUT ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION AND DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS
+ * SOFTWARE.
+ * 
+ */
+
+/*  Copyright (C) 1999  Heikki Vatiainen hessu@cs.tut.fi */
+
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <atm.h>
+#include <linux/atmdev.h>
+#include <atmd.h>
+
+#include "address.h"
+
+#define COMPONENT "address.c"
+
+/* Gets End System Identifier (MAC address) from kernel
+ * Returns < 0 for error
+ */
+int addr_getesi(unsigned char *mac_addr)
+{
+        int fd, retval;
+        struct atmif_sioc req;
+        
+        fd = socket(PF_ATMSVC, SOCK_DGRAM, 0);
+        if (fd < 0) {
+                diag(COMPONENT, DIAG_ERROR, "addr_getesi: socket: %s\n",
+                     strerror(errno));
+                return -1;
+        }
+        req.number = 0;
+        req.arg = mac_addr;
+        req.length = ESI_LEN;
+        retval = ioctl(fd, ATM_GETESI, &req);
+        if (retval < 0) diag(COMPONENT, DIAG_ERROR, "ioctl ATM_GETESI: %s\n",
+                             strerror(errno));
+        close(fd);
+
+        return retval;
+}
+
+/* Gets our ATM address from kernel. Useful for binding listen sockets.
+ * Returns < 0 for error
+ */
+int get_listenaddr(unsigned char *atm_addr)
+{
+        int fd, retval;
+        struct atmif_sioc req;
+        struct sockaddr_atmsvc listen_addr;
+        
+        fd = socket(PF_ATMSVC, SOCK_DGRAM, 0);
+        if (fd < 0) {
+                diag(COMPONENT, DIAG_ERROR, "get_listenaddr: socket: %s\n",
+                     strerror(errno));
+                return -1;
+        }
+        req.number = 0;
+        req.arg = &listen_addr;
+        req.length = sizeof(struct sockaddr_atmsvc);
+        retval = ioctl(fd, ATM_GETADDR, &req);
+        if (retval < 0) diag(COMPONENT, DIAG_ERROR, "ioctl ATM_GETADDR: %s\n",
+                             strerror(errno));
+        close(fd);
+
+        memcpy(atm_addr, listen_addr.sas_addr.prv, ATM_ESA_LEN);
+
+        return retval;
+}
diff -ur --new-file old/atm/led.new/address.h new/atm/led.new/address.h
--- old/atm/led.new/address.h	Thu Jan  1 01:00:00 1970
+++ new/atm/led.new/address.h	Sat Feb  6 15:06:08 1999
@@ -0,0 +1,31 @@
+/*
+ * Marko Kiiskila carnil@cs.tut.fi 
+ * 
+ * Tampere University of Technology - Telecommunications Laboratory
+ *
+ * Permission to use, copy, modify and distribute this
+ * software and its documentation is hereby granted,
+ * provided that both the copyright notice and this
+ * permission notice appear in all copies of the software,
+ * derivative works or modified versions, and any portions
+ * thereof, that both notices appear in supporting
+ * documentation, and that the use of this software is
+ * acknowledged in any publications resulting from using
+ * the software.
+ * 
+ * TUT ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION AND DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS
+ * SOFTWARE.
+ * 
+ */
+
+/* Copyright (C) 1999 Heikki Vatiainen hessu@cs.tut.fi */
+
+#ifndef ADDRESS_H
+#define ADDRESS_H
+
+int addr_getesi(unsigned char *mac_addr);
+int get_listenaddr(unsigned char *atm_addr);
+
+#endif /* ADDRESS_H */
diff -ur --new-file old/atm/led.new/conn.c new/atm/led.new/conn.c
--- old/atm/led.new/conn.c	Thu Jan  1 01:00:00 1970
+++ new/atm/led.new/conn.c	Sat Feb  6 21:21:58 1999
@@ -0,0 +1,899 @@
+/* conn.c - functions for handling SVCs, create, accept, send, etc. */
+
+/*
+ * Marko Kiiskila carnil@cs.tut.fi 
+ * 
+ * Copyright (c) 1996
+ * Tampere University of Technology - Telecommunications Laboratory
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this
+ * software and its documentation is hereby granted,
+ * provided that both the copyright notice and this
+ * permission notice appear in all copies of the software,
+ * derivative works or modified versions, and any portions
+ * thereof, that both notices appear in supporting
+ * documentation, and that the use of this software is
+ * acknowledged in any publications resulting from using
+ * the software.
+ * 
+ * TUT ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION AND DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS
+ * SOFTWARE.
+ * 
+ */
+
+/* Copyright (C) 1999 Heikki Vatiainen hessu@cs.tut.fi */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include <atm.h>
+#include <atmsap.h>
+#include <linux/atmlec.h>
+
+#include <atmd.h>
+
+#include "conn.h"
+#include "display.h"
+#include "lec.h"
+#include "frames.h"
+#include "kernel.h"
+
+#define COMPONENT "conn.c"
+
+/* status */
+#define CONNECTED  42    /* Operational socket */
+#define CONNECTING 43    /* Non-blocking socket, not yet connected */
+
+/* type */
+#define WEMADE 7
+#define THEYMADE 8
+#define LISTENING 9
+#define KERNEL_SOCK 10
+
+static Conn_t *connlist = NULL;
+
+/* Local protos */
+static void list_remove_conn(Conn_t *conn);
+static Conn_t *list_add_conn(unsigned char *dest_atm_addr);
+static Conn_t *conn_already_exists(unsigned char *atm_addr, Conn_t *current);
+static const char *get_type_string(int type);
+static int maxmtu2maxsdu(uint8_t mtu);
+
+static void delete_addr(unsigned char *atm_addr)
+{
+        struct atmlec_msg msg;
+
+        msg.type = l_addr_delete;
+        memcpy(msg.content.normal.atm_addr, atm_addr, ATM_ESA_LEN);
+        msg_to_kernel(&msg, sizeof(struct atmlec_msg));
+
+        return;
+}
+
+/* Checks if connection to atm_addr already exists. Does not
+ * check against current though.
+ * Returns NULL for no connection, or Conn_t of existing connection.
+ */
+static Conn_t *conn_already_exists(unsigned char *atm_addr, Conn_t *current)
+{
+        Conn_t *conn;
+
+        conn = connlist;
+        while (conn) {
+                if (conn != current &&
+                    conn->type != LISTENING && 
+                    conn->type != KERNEL_SOCK) {
+                        if (memcmp(conn->atm_address, atm_addr, ATM_ESA_LEN) == 0)
+                                return conn;
+                }
+                conn = conn->next;
+        }
+        
+        return NULL;
+}
+
+/* Initializes and fills in *sap and *qos according to Blli
+ * code point value specified in conn_type.
+ */
+void init_conn_params(struct atm_sap *sap, struct atm_qos *qos,
+                      uint16_t conn_type)
+{
+
+        unsigned int bllicode;
+        int i, sdu;
+        char qos_text[MAX_ATM_QOS_LEN + 1];
+  
+        diag(COMPONENT, DIAG_DEBUG, "init_conn_params, conn_type %x\n", conn_type);
+
+        memset(qos, 0, sizeof(struct atm_qos));
+        memset(sap, 0, sizeof(struct atm_sap));
+        qos->aal = ATM_AAL5;
+  
+        /* Set the forward and backward Max CPCS-SDU Size */
+        switch(conn_type) {
+        case CONTROL_CONN:
+                qos->rxtp.max_sdu = 1516;
+                qos->txtp.max_sdu = 1516;
+                break;
+        case DATA_DIRECT_CONN:
+        case MCAST_CONN:
+                sdu = maxmtu2maxsdu(lec_params.c3_max_frame_size);
+                qos->rxtp.max_sdu = sdu;
+                qos->txtp.max_sdu = sdu;
+                break;
+        default:
+                diag(COMPONENT, DIAG_ERROR, "unknown conn_type %x\n", conn_type);
+                break;
+        }
+        
+        /* ATM User Cell Rate/ATM Traffic Descriptor. */
+        qos->txtp.traffic_class = ATM_UBR;
+        qos->rxtp.traffic_class = ATM_UBR;
+   
+        if (get_verbosity(COMPONENT) >= DIAG_DEBUG) {
+                if (qos2text(qos_text, sizeof(qos_text), qos, 0) < 0)
+                        sprintf(qos_text, "<bad qos>");
+                diag(COMPONENT, DIAG_DEBUG, "init_conn_params, QoS '%s'\n", qos_text);
+        }
+
+        /* No Broadband High Layer Information in LANE. */
+        sap->bhli.hl_type = ATM_HL_NONE;
+
+        /* Broadband Lower Layer Information. */
+        sap->blli[0].l3_proto = ATM_L3_TR9577;
+        sap->blli[0].l3.tr9577.ipi = NLPID_IEEE802_1_SNAP;
+        sap->blli[0].l3.tr9577.snap[0] = 0x00;
+        sap->blli[0].l3.tr9577.snap[1] = 0xa0;
+        sap->blli[0].l3.tr9577.snap[2] = 0x3e;
+        bllicode = htons(conn_type);
+        sap->blli[0].l3.tr9577.snap[3] = (unsigned char)(0xff&bllicode);
+        sap->blli[0].l3.tr9577.snap[4] = (unsigned char)(0xff&(bllicode>>8));
+
+        if (get_verbosity(COMPONENT) == DIAG_DEBUG) {
+                for(i=0; i < 5; i++) {
+                        diag(COMPONENT, DIAG_DEBUG, "snap[%d] = 0x%2.2x",
+                             i, sap->blli[0].l3.tr9577.snap[i]);
+                }
+        }
+
+        return;
+}
+
+/* Returns != 0 if blli indicates Data Direct
+ * connection
+ */
+static int is_data_direct(struct atm_blli *blli)
+{
+        return (blli->l3.tr9577.snap[4] == DATA_DIRECT_CONN);
+}
+
+/* Creates a socket with the specified parameters.
+ * If listen_addr is non NULL binds to it.
+ * Returns < 0 for error or new socket descriptor.
+ */
+static int get_socket(struct sockaddr_atmsvc *listen_addr,
+                      struct atm_sap *sap, struct atm_qos *qos)
+{
+        int s, ret;
+
+        s = socket(PF_ATMSVC, SOCK_DGRAM, 0);
+        if (s < 0) {
+                diag(COMPONENT, DIAG_ERROR, "socket creation failure: %s\n",
+                     strerror(errno));
+                return -1;
+        }
+        diag(COMPONENT, DIAG_DEBUG, "get_socket: got fd %d\n", s);
+        
+        if (setsockopt(s, SOL_ATM, SO_ATMQOS, qos, sizeof(struct atm_qos)) < 0) {
+                diag(COMPONENT, DIAG_ERROR, "get_socket: setsockopt SO_ATMQOS: %s\n", strerror(errno));
+                close(s);
+                return -1;
+        }
+        if (setsockopt(s, SOL_ATM, SO_ATMSAP, sap, sizeof(struct atm_sap)) < 0) {
+                diag(COMPONENT, DIAG_ERROR, "setup_svc setsockop(SO_ATMSAP)\n");
+                close(s);
+                return -1;
+        }
+        
+        /* Bind the socket to our local address */
+        if (listen_addr == NULL)
+                return s;
+        ret = bind(s, (struct sockaddr *)listen_addr, sizeof(struct sockaddr_atmsvc));
+        if (ret < 0) {
+                diag(COMPONENT, DIAG_ERROR, "bind error: %s\n", strerror(errno));
+                close(s);
+                return -1;
+        }
+        
+        return s;
+}
+
+
+/*
+ * You need to check this if setup_svc() returns NULL
+ */
+#if 0
+        if (is_data_direct(&sap->blli[0])) {
+                /* Try to remove possible entry in kernel */
+                delete_addr(conn->atm_address);
+        }
+#endif
+/* Does an active open to dst_addr using pre-filled
+ * parameters in sap and qos.
+ * If listen_addr is non NULL, binds to it.
+ * Data direct SVCs are non-blocking, others block
+ * Returns NULL for error or new connections.
+ */
+Conn_t *setup_svc(struct sockaddr_atmsvc *dst_addr,
+                  struct sockaddr_atmsvc *listen_addr,
+                  struct atm_sap *sap, struct atm_qos *qos)
+{
+        Conn_t *conn;
+        int s, ret;
+        char buff[MAX_ATM_ADDR_LEN+1];
+
+        diag(COMPONENT, DIAG_DEBUG, "Outgoing call setup\n");
+        
+        /* The code below is commented out due to the following scenario:
+           We have made a connection to another LEC and our address was
+           numerically lower than theirs (LANEv2 8.1.13).
+           The other end has also connected us, but we must not use that
+           VCC.
+           However, if the connection we made gets closed, we can not open
+           it again since a connection to the destination LEC already exists.
+        */
+#if 0
+        /* We don't create connection to an entity where we already have
+           a connection. */
+        if (conn_already_exists(dst_addr->sas_addr.prv, NULL) &&
+            is_data_direct(&sap->blli[0]))
+                return NULL;
+#endif
+
+        dst_addr->sas_family = AF_ATMSVC;
+        listen_addr->sas_family = AF_ATMSVC;
+        
+        switch(sap->blli[0].l3.tr9577.snap[4]) { /* Kludge.  Eh? */
+        case CONTROL_CONN:
+                diag(COMPONENT, DIAG_DEBUG, "LE Control SVC setup\n");
+                break;
+        case DATA_DIRECT_CONN:
+                diag(COMPONENT, DIAG_DEBUG, "Data direct 802.3\n");
+                break;
+        case MCAST_CONN:
+                diag(COMPONENT, DIAG_DEBUG, "Multicast 802.3\n");
+                break;
+        default:
+                diag(COMPONENT, DIAG_ERROR, "Unknown codepoint in svc setup\n");
+        }
+ 
+        s = get_socket(listen_addr, sap, qos);
+        if (s < 0) return NULL;
+        if (atm2text(buff, sizeof(buff), (struct sockaddr *)dst_addr, A2T_PRETTY | A2T_NAME | A2T_LOCAL) < 0)
+                sprintf(buff, "<Unknown ATM address>");
+        diag(COMPONENT, DIAG_DEBUG, "Call to %s", buff);
+
+        /* Make data direct SVCs non-blocking */
+        if (is_data_direct(&sap->blli[0])) {
+                ret = fcntl(s, F_GETFL);
+                if (ret < 0) {
+                        diag(COMPONENT, DIAG_ERROR, "fcntl(s, F_GETFL)\n");
+                        close(s);
+                } else if (fcntl(s, F_SETFL, ret|O_NONBLOCK) < 0) {
+                        diag(COMPONENT, DIAG_ERROR, "fcntl(s, F_SETFL, x|O_NONBLOCK)\n");
+                        close(s);
+                        return NULL;
+                }
+        }
+
+        ret = connect(s, (struct sockaddr *)dst_addr, sizeof(struct sockaddr_atmsvc));
+        if (ret < 0 && errno != EINPROGRESS) {
+                diag(COMPONENT, DIAG_ERROR, "connect error: %s\n", strerror(errno));
+                close(s);
+                return NULL;
+        }
+
+        conn = list_add_conn(dst_addr->sas_addr.prv);
+        diag(COMPONENT, DIAG_DEBUG, "Conn:%p\n", conn);
+        if (conn == NULL) {
+                close(s);
+                return NULL;
+        }
+        conn->fd = s;
+        conn->type = WEMADE;
+        conn->codepoint = sap->blli[0].l3.tr9577.snap[4];
+
+        if (is_data_direct(&sap->blli[0]))
+                conn->status = CONNECTING;
+        else
+                conn->status = CONNECTED;
+        
+        return conn;
+}
+
+/* Creates listen socket for incoming data direct connections.
+ * Only for data direct, not for Control or Multicast listen sockets.
+ * Returns < 0 for error
+ */
+int create_data_listen(void)
+{
+        struct atm_sap sap;
+        struct atm_qos qos;
+        struct sockaddr_atmsvc addr;
+
+        memset(&addr, 0, sizeof(struct sockaddr_atmsvc));
+        memcpy(addr.sas_addr.prv, lec_params.c1n_my_atm_addr, ATM_ESA_LEN);
+        addr.sas_family = AF_ATMSVC;
+        init_conn_params(&sap, &qos, DATA_DIRECT_CONN);
+
+        lec_params.data_listen = create_listensocket(&addr, &sap, &qos);
+        if (lec_params.data_listen == NULL) {
+                diag(COMPONENT, DIAG_FATAL, "Could not create listen socket for incoming Data Direct VCCs\n");
+                return -1;
+        }
+
+        return 0;
+}
+
+/* Opens a non-blocking Data Direct VCC to atm_addr.
+ * Not for Control or Multicast connections.
+ * Returns < 0 for error
+ */
+int create_data_svc(unsigned char *atm_addr, int codepoint)
+{
+        struct atm_sap sap;
+        struct atm_qos qos;
+        struct sockaddr_atmsvc my_addr, dst_addr;
+        Conn_t *conn;
+
+        memset(&my_addr, 0, sizeof(struct sockaddr_atmsvc));
+        memcpy(my_addr.sas_addr.prv, lec_params.c1n_my_atm_addr, ATM_ESA_LEN);
+        memset(&dst_addr, 0, sizeof(struct sockaddr_atmsvc));
+        memcpy(dst_addr.sas_addr.prv, atm_addr, ATM_ESA_LEN);
+        my_addr.sas_family = dst_addr.sas_family = AF_ATMSVC;
+        init_conn_params(&sap, &qos, codepoint);
+
+        conn = setup_svc(&dst_addr, &my_addr, &sap, &qos);
+        if (conn == NULL) {
+                diag(COMPONENT, DIAG_ERROR, "Could not create Data Direct VCC\n");
+                delete_addr(dst_addr.sas_addr.prv);
+                return -1;
+        }
+
+        return 0;
+}
+
+/* Creates a listen socket with parameters specified with
+ * arguments.
+ * Returns NULL for error or Conn_t for new listen socket.
+ */
+Conn_t *create_listensocket(struct sockaddr_atmsvc *listen_addr,
+                            struct atm_sap *sap, struct atm_qos *qos)
+{
+        int fd, ret;
+        Conn_t *conn;
+
+        diag(COMPONENT, DIAG_DEBUG, "conn_create_listensocket\n");
+        
+        fd = get_socket(listen_addr, sap, qos);
+        if (fd < 0) return NULL;
+        
+        ret = listen(fd, 5);
+        if (ret != 0) {
+                diag(COMPONENT, DIAG_DEBUG, "Listen failed: %s\n", strerror(errno));
+                close(fd);
+                return NULL;
+        }
+        
+        conn = list_add_conn(NULL);
+        if (conn == NULL) {
+                diag(COMPONENT, DIAG_ERROR, "List_add_conn failed\n");
+                close(fd);    
+                return NULL;
+        }
+
+        conn->type = LISTENING;
+        conn->fd = fd;
+        diag(COMPONENT, DIAG_DEBUG, "Listen socket created blli:%2.2x %2.2x fd: %d\n",
+             sap->blli[0].l3.tr9577.snap[3],
+             sap->blli[0].l3.tr9577.snap[4],
+             conn->fd);
+        
+        return conn;
+}
+
+/* Accepts a new connection from listen socket in conn.
+ * Returns NULL for error
+ */
+Conn_t *accept_conn(Conn_t *conn)
+{
+        Conn_t *new;
+        struct sockaddr_atmsvc addr;
+        size_t len;
+        int fd;
+        char buff[MAX_ATM_ADDR_LEN+1];
+
+        diag(COMPONENT, DIAG_DEBUG, "Accepting connection on fd %d\n", conn->fd);
+        len = sizeof(addr);
+        fd = accept(conn->fd, (struct sockaddr *)&addr, &len);
+        diag(COMPONENT, DIAG_DEBUG, "accept returned %d\n", fd);
+        if (fd < 0) {
+                diag(COMPONENT, DIAG_ERROR, "accept: %s\n", strerror(errno));
+                return NULL;
+        }
+        if (atm2text(buff, sizeof(buff), (struct sockaddr *)&addr, A2T_PRETTY | A2T_NAME | A2T_LOCAL) < 0)
+                sprintf(buff, "<Unknown ATM address>");
+        diag(COMPONENT, DIAG_DEBUG, "Call from %s", buff);
+
+        new = list_add_conn(addr.sas_addr.prv);
+        if (new == NULL) return NULL;
+        new->fd = fd;
+        new->status = CONNECTED;
+        new->type = THEYMADE;
+        if (conn == lec_params.ctrl_listen) new->codepoint  = CONTROL_CONN;
+        if (conn == lec_params.mcast_listen) new->codepoint = MCAST_CONN;
+        if (conn == lec_params.data_listen) new->codepoint  = DATA_DIRECT_CONN;
+
+        return new;
+}
+
+/* Close all connections, important or not.
+ */
+void close_connections(void)
+{
+        Conn_t *conn, *next;
+
+        for(conn = connlist; conn; conn = next) {
+                diag(COMPONENT, DIAG_DEBUG, "Destroying:%p fd:%d type:%d\n",
+                     conn, conn->fd, conn->type);
+                next = conn->next;
+                close(conn->fd);
+                list_remove_conn(conn);
+                free(conn);
+        }
+
+        return;
+}
+
+/* Closes a connection and checks its importance.
+ * Important connections are kernel socket, LES connections,
+ * BUS Default Multicast Send VCC, last Multicast Forward VCC from Bus
+ * and any of the listen sockets.
+ * Returns < 0 for important connection.
+ */
+int close_connection(Conn_t *conn)
+{
+        int bad = 0;
+        Conn_t *mcast;
+
+        diag(COMPONENT, DIAG_DEBUG, "close_connection %p\n", conn);
+
+        if (conn == lec_params.kernel ||
+            conn == lec_params.ctrl_direct  ||
+            conn == lec_params.ctrl_dist    ||
+            conn == lec_params.mcast_send   ||
+            conn == lec_params.mcast_listen ||
+            conn == lec_params.data_listen)
+                bad = -1;
+        else {
+                bad = -1;
+                for (mcast = connlist; mcast; mcast = mcast->next)
+                        if (mcast != conn &&
+                            mcast->type == THEYMADE &&
+                            mcast->codepoint == MCAST_CONN)
+                                bad = 0;
+        }
+
+        close(conn->fd);
+        list_remove_conn(conn);
+        free(conn);
+        
+        return bad;
+}
+
+/* Accepts a new incoming Data Direct or Multicast Forward connection.
+ * Control connections (LECS/LES) are accepted during configuration/join.
+ * Returns < 0 for serious error such as broken listen socket.
+ */
+static int handle_accept(Conn_t *conn)
+{
+        Conn_t *new;
+        struct atmlec_ioc ioc;
+        
+        new = accept_conn(conn);
+        if (new == NULL) return -1;
+
+        if (conn == lec_params.mcast_listen) {
+                diag(COMPONENT, DIAG_DEBUG, "Multicast Forward VCC accepted\n");
+                ioc.receive = 2;
+        } else {
+                diag(COMPONENT, DIAG_DEBUG, "Data Direct VCC accepted\n");
+                ioc.receive = 0;
+                if (conn_already_exists(new->atm_address, new) &&
+                    memcmp(lec_params.c1n_my_atm_addr, new->atm_address,  ATM_ESA_LEN) < 0) {
+                        diag(COMPONENT, DIAG_DEBUG, "Using it only to receive, spec 8.1.1\n");
+                        ioc.receive = 1;
+                }
+        }
+        memcpy(ioc.atm_addr, new->atm_address, ATM_ESA_LEN);
+        ioc.dev_num = lec_params.itf_num;
+        diag(COMPONENT, DIAG_DEBUG, "Attaching a new VCC, fd %d\n", new->fd);
+        if (ioctl(new->fd, ATMLEC_DATA, &ioc) < 0) {
+                diag(COMPONENT, DIAG_ERROR, "VCC attach failed: ioctl: %s\n", strerror(errno));
+                return -1;
+        }
+
+        return 0;
+}
+
+/* Reads a LE control frame from conn, usually Data Direct or
+ * Multicast Forward connection. Calls the incoming packet
+ * handler function.
+ * Returns < 0 for serious error such as broken LES connection
+ */
+static int handle_data(Conn_t *conn)
+{
+        char buff[MAX_CTRL_FRAME];
+        int retval;
+
+        retval = recv_frame(conn, buff, sizeof(buff));
+        if (retval < 0) {
+                diag(COMPONENT, DIAG_ERROR, "handle_data: read: %s\n", strerror(errno));
+                return (close_connection(conn));
+        }
+        if (retval == 0) {
+                diag(COMPONENT, DIAG_DEBUG, "fd %d, Data or Multicast VCC closed\n", conn->fd);
+                return (close_connection(conn));
+        }
+
+        return handle_frame(conn, buff, retval);
+}
+
+/* Checks connections in *fds. The only allowed sockets
+ * in *fds are listen sockets, data direct and control
+ * sockets.
+ * Returns < 0 for serious error such as broken LES connection
+ */
+int check_connections(fd_set *fds)
+{
+        Conn_t *conn, *next;
+
+        conn = connlist;
+        while (conn != NULL) {
+                next = conn->next;
+                if (!FD_ISSET(conn->fd, fds)) {
+                        conn = next;
+                        continue;
+                }
+
+                switch (conn->type) {
+                case LISTENING:
+                        if (handle_accept(conn) < 0)
+                                return -1;
+                        break;
+                case WEMADE:
+                case THEYMADE:
+                        if (handle_data(conn) < 0)
+                                return -1;
+                        break;
+                default:
+                        diag(COMPONENT, DIAG_ERROR, "check_connections: bad_type '%s'\n",
+                             get_type_string(conn->type));
+                        break;
+                }
+                
+                conn = next;
+        }
+        
+        return 0;
+}
+
+/* Completes a non-blocking connect.
+ * Returns < 0 for serious error
+ */
+static int handle_connect(Conn_t *conn)
+{
+        int retval;
+        struct sockaddr_atmsvc dummy;
+        struct atmlec_msg msg;
+        struct atmlec_ioc ioc;
+
+        diag(COMPONENT, DIAG_DEBUG, "handle_connect: completing fd %d\n", conn->fd);
+        /* this seems to be common method in Linux-ATM
+         * making sure that nonblocking connect was
+         * completed successfully
+         */
+        conn->status = CONNECTED;
+        retval = connect(conn->fd, (struct sockaddr *)&dummy, sizeof(struct sockaddr_atmsvc));
+        if (retval < 0) {
+                diag(COMPONENT, DIAG_DEBUG, "handle_connect: connect: %s\n", strerror(errno));
+                delete_addr(conn->atm_address);
+                close_connection(conn);
+                return 0;
+        }
+
+        send_ready_ind(conn);
+
+        memset(&msg, 0, sizeof(struct atmlec_msg));
+        msg.type = l_flush_tran_id;
+        memcpy(msg.content.normal.atm_addr, conn->atm_address, ATM_ESA_LEN);
+        msg.content.normal.flag = send_flush_req(conn);
+        
+        msg_to_kernel(&msg, sizeof(struct atmlec_msg));
+
+        memcpy(ioc.atm_addr, conn->atm_address, ATM_ESA_LEN);
+        ioc.dev_num = lec_params.itf_num;
+        ioc.receive = 0;
+        diag(COMPONENT, DIAG_DEBUG, "Attaching a new active VCC, fd %d\n", conn->fd);
+        if (ioctl(conn->fd, ATMLEC_DATA, &ioc) < 0) {
+                diag(COMPONENT, DIAG_ERROR, "VCC attach failed: ioctl: %s\n", strerror(errno));
+                return -1;
+        }
+
+        return 0;
+}
+
+/* Complete non-blocking connections in *fds.
+ * Returns < 0 for serious error (problems with kernel). 
+ */
+int complete_connections(fd_set *fds)
+{
+        Conn_t *conn, *next;
+        int retval;
+
+        conn = connlist;
+        while (conn) {
+                next = conn->next;
+                if (FD_ISSET(conn->fd, fds)) {
+                        retval = handle_connect(conn);
+                        if (retval < 0) return -1;
+                }
+                conn = next;
+        }
+                
+        return 0;
+}
+
+/* Send a LE control frame using *conn.
+ * Returns < 0 for serious error
+ */
+int send_frame(Conn_t *conn, void *frame, int length)
+{
+        struct frame_hdr *hdr;
+        int ret;
+
+        diag(COMPONENT, DIAG_DEBUG, "send_frame: fd:%d len:%ld\n", conn->fd, length);
+        hdr = (struct frame_hdr *)frame;
+        if (hdr->opcode == htons(READY_QUERY) ||
+            hdr->opcode == htons(READY_IND))
+                diag(COMPONENT, DIAG_DEBUG, "%s\n", opcode2text(hdr->opcode));
+        else 
+                display_frame(frame);
+
+        ret = write(conn->fd, frame, length);
+        if (ret < 0) {
+                diag(COMPONENT, DIAG_ERROR, "send_frame: write: %s\n", strerror(errno));
+                return -1;
+        } 
+
+        return ret;
+}
+
+/* Receive a LE control frame from *conn.
+ * Returns < 0 for serious error.
+ */
+int recv_frame(Conn_t *conn, void *buff, int length)
+{
+        int ret;
+
+        diag(COMPONENT, DIAG_DEBUG, "recv_frame: fd:%d\n", conn->fd);
+        ret = read(conn->fd, buff, length);
+        if (ret < 0) {
+                diag(COMPONENT, DIAG_ERROR, "Read failed: %s\n", strerror(errno));
+                return -1;
+        } 
+
+#if 0
+        diag(COMPONENT, DIAG_DEBUG, "recv_frame: read %d bytes\n", ret);
+        if (get_verbosity(COMPONENT) >= DIAG_DEBUG) {
+                int i;
+                for (i = 0; i < 11; i++)
+                        diag(COMPONENT, DIAG_DEBUG, "0x%2x", ((unsigned char *)buff)[i]);
+        }
+#endif
+
+        return ret;
+}
+
+
+/*
+ * LANE2: 5.2.1.4 and others, sleep random time before trying to reconnect
+ */
+void random_delay(void)
+{
+        struct timeval tv;
+        int millis, interval;
+        
+        srand(time(NULL));
+        interval = lec_params.c38_max_reconfig_delay - lec_params.c37_min_reconfig_delay;
+        millis = (rand() % interval) + lec_params.c37_min_reconfig_delay;
+        tv.tv_sec  = (millis - (millis % 1000)) / 1000;
+        tv.tv_usec = (millis % 1000) * 1000;
+        
+        diag(COMPONENT, DIAG_DEBUG, "random_delay: sleeping %d.%d seconds\n", tv.tv_sec, tv.tv_usec);
+        (void)select(0, NULL, NULL, NULL, &tv);
+        
+        return;
+}
+
+/* Collect already connected sockets in *fds
+ */
+void conn_get_fds(fd_set *fds)
+{
+        Conn_t *conn;
+
+        diag(COMPONENT, DIAG_DEBUG, "collecting ready fds ");
+        conn = connlist;
+        while (conn) {
+                if (conn->status != CONNECTING) {
+                        FD_SET(conn->fd, fds);
+                        diag(COMPONENT, DIAG_DEBUG, "%d type %s", conn->fd, get_type_string(conn->type));
+                }
+                conn = conn->next;
+        }
+        
+        return;
+}
+
+/* Collect non-blocking connecting sockets in *fds
+ */
+void conn_get_connecting_fds(fd_set *fds)
+{
+        Conn_t *conn;
+
+        diag(COMPONENT, DIAG_DEBUG, "collecting connecting fds ");
+        conn = connlist;
+        while (conn) {
+                if (conn->status == CONNECTING) {
+                        FD_SET(conn->fd, fds);
+                        diag(COMPONENT, DIAG_DEBUG, "%d", conn->fd);
+                }
+                conn = conn->next;
+        }
+        
+}
+
+/* Creates Conn_t for fd and marks it as kernel socket
+ * Returns < 0 for error
+ */
+int conn_set_kernel_socket(int fd)
+{
+        Conn_t *conn;
+
+        conn = list_add_conn(NULL);
+        if (conn == NULL) {
+                diag(COMPONENT, DIAG_ERROR, "conn_set_kernel_socket: list_add_conn failed\n");
+                return -1;
+        }
+        conn->type = KERNEL_SOCK;
+        conn->status = CONNECTED;
+        conn->fd = fd;
+        lec_params.kernel = conn;
+
+        return fd;
+}
+
+/* Creates new Conn_t object and allocates memory for it.
+ * atm_addr should be the ATM address of the other end
+ * or NULL if not applicable
+ */
+static Conn_t *list_add_conn(unsigned char *atm_addr)
+{
+        Conn_t *conn;
+
+        conn = (Conn_t *)malloc(sizeof(Conn_t));
+        if (!conn)
+                return NULL;
+
+        memset(conn, 0, sizeof(Conn_t));
+        if (atm_addr)
+                memcpy(conn->atm_address, atm_addr, ATM_ESA_LEN);
+
+        conn->next = connlist;
+        conn->previous = NULL;
+        if (connlist)
+                connlist->previous = conn;
+        connlist = conn;
+        diag(COMPONENT, DIAG_DEBUG, "Added conn:%p\n", conn);
+
+        return conn;
+}
+
+/* Helper for close_connection and close_connections
+ */
+static void list_remove_conn(Conn_t *conn)
+{
+
+        if (conn->next == NULL && conn->previous == NULL
+            && connlist != conn) return;
+        diag(COMPONENT, DIAG_DEBUG, "Removing conn:%p fd:%d previous:%p next:%p ",
+             conn, conn->fd, conn->previous, conn->next);
+
+        if (conn->previous) 
+                diag(COMPONENT, DIAG_DEBUG, "Previous:%p, fd:%d, next:%p, previous:%p ",
+                     conn->previous, conn->previous->fd,
+                     conn->previous->next, conn->previous->previous);
+        if (conn->next)
+                diag(COMPONENT, DIAG_DEBUG, "Next:%p, fd:%d next:%p, previous:%p ",
+                     conn->next, conn->next->fd,
+                     conn->next->next, conn->next->previous);  
+        if (conn->previous) {
+                conn->previous->next = conn->next;
+        } else /* First in line */
+                connlist = conn->next;
+        if (conn->next)
+                conn->next->previous = conn->previous;  
+        diag(COMPONENT, DIAG_DEBUG, "Connlist: %p\n", connlist);
+        conn->next=conn->previous= NULL;
+        
+        return;
+}
+
+static const char *get_type_string(int type)
+{
+        switch(type) {
+        case WEMADE:
+                return "WEMADE";
+                break;
+        case THEYMADE:
+                return "THEYMADE";
+                break;
+        case LISTENING:
+                return "LISTENING";
+                break;
+        case KERNEL_SOCK:
+                return "KERNEL_SOCK";
+                break;
+        default:
+                break;
+        }
+        
+        return "UNKNOWN";
+}
+
+static int maxmtu2maxsdu(uint8_t mtu)
+{
+
+    int sdu;
+
+    switch (mtu) {
+    case MTU_1516:
+        sdu = 1516;
+        break;
+    case MTU_1580:  /* LANE2: MTU can be 1580 too (IEEE 802.1p/Q) */
+        sdu = 1580;
+        break;
+    case MTU_4544:
+        sdu = 4544;
+        break;
+    case MTU_9234:
+        sdu = 9234;
+        break;
+    case MTU_18190:
+        sdu = 18190;
+        break;
+    default:
+        sdu = 1516;
+        break;
+    }
+
+    return sdu;
+}
diff -ur --new-file old/atm/led.new/conn.h new/atm/led.new/conn.h
--- old/atm/led.new/conn.h	Thu Jan  1 01:00:00 1970
+++ new/atm/led.new/conn.h	Sat Feb  6 21:29:32 1999
@@ -0,0 +1,82 @@
+/*
+ * Marko Kiiskila carnil@cs.tut.fi 
+ * 
+ * Copyright (c) 1996
+ * Tampere University of Technology - Telecommunications Laboratory
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this
+ * software and its documentation is hereby granted,
+ * provided that both the copyright notice and this
+ * permission notice appear in all copies of the software,
+ * derivative works or modified versions, and any portions
+ * thereof, that both notices appear in supporting
+ * documentation, and that the use of this software is
+ * acknowledged in any publications resulting from using
+ * the software.
+ * 
+ * TUT ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION AND DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS
+ * SOFTWARE.
+ * 
+ */
+
+/* Copyright (C) 1999 Heikki Vatiainen hessu@cs.tut.fi */
+
+#ifndef CONN_H
+#define CONN_H
+
+typedef struct _conn_t_ {
+        int fd;                  /* Socket connected to this connection */
+        int status;              /* Connection status */
+        int type;                /* We made, they made, listen socket */
+        int codepoint;           /* One of the below, MCAST_CONN etc. */
+        unsigned char atm_address[ATM_ESA_LEN]; 
+                                 /* Destination address, not in listen or
+                                    kernel sockets */
+        struct _conn_t_ *next;
+        struct _conn_t_ *previous;
+} Conn_t;
+
+void close_connections(void);
+void init_conn_params(struct atm_sap *sap, struct atm_qos *qos,
+                      uint16_t blli_codepoint);
+int create_data_svc(unsigned char *atm_addr, int codepoint);
+Conn_t *setup_svc(struct sockaddr_atmsvc *dst_addr,
+                  struct sockaddr_atmsvc *listen_addr,
+                  struct atm_sap *sap, struct atm_qos *qos);
+int create_data_listen(void);
+Conn_t *create_listensocket(struct sockaddr_atmsvc *listen_addr,
+                            struct atm_sap *sap, struct atm_qos *qos);
+Conn_t *accept_conn(Conn_t *conn);
+int close_connection(Conn_t *conn);
+int check_connections(fd_set *fds);
+int complete_connections(fd_set *fds);
+
+int send_frame(Conn_t *conn, void *frame, int length);
+int recv_frame(Conn_t *conn, void *buff, int length);
+
+void conn_get_fds(fd_set *fds);
+void conn_get_connecting_fds(fd_set *fds);
+
+void random_delay(void);
+
+/*
+ * Connection types for BLLI codepoints.
+ */
+#define CONTROL_CONN      1
+#define DATA_DIRECT_CONN  2
+#define MCAST_CONN        4
+
+/*
+ * MTU sizes
+ */
+#define MTU_UNSPEC 0
+#define MTU_1516   1
+#define MTU_1580   5   /* LANEv2 */
+#define MTU_4544   2
+#define MTU_9234   3
+#define MTU_18190  4
+
+#endif /* CONN_H */
diff -ur --new-file old/atm/led.new/display.c new/atm/led.new/display.c
--- old/atm/led.new/display.c	Thu Jan  1 01:00:00 1970
+++ new/atm/led.new/display.c	Sat Feb  6 21:54:37 1999
@@ -0,0 +1,307 @@
+/* display.c - display frames, addresses, opcodes, etc. */
+
+/* Copyright (C) 1999 Heikki Vatiainen hessu@cs.tut.fi */
+
+#include <atm.h>
+#include <atmd.h>
+
+#include "display.h"
+#include "frame_defs.h"
+
+#define COMPONENT "display.c"
+
+#define MAX_TEXT 1024  /* Buffer size for displaying LE control frames */
+
+static int my_atm2text(unsigned char *atm_addr, char *dest);
+static const char *lan_dst2text(struct lan_dst *dst);
+static void display_ready(void *buff);
+
+/* Prints out more or less human readable summary of
+ * LE Control Frame pointed by frame.
+ */
+void display_frame(void *frame)
+{
+        struct frame_hdr *hdr;
+        struct ctrl_frame *f;
+        char text[MAX_TEXT];
+        char *p;
+
+        hdr = (struct frame_hdr *)frame;
+        if (hdr->opcode == htons(READY_QUERY) ||
+            hdr->opcode == htons(READY_IND))
+                display_ready(frame);
+       
+        p = text;
+        p += sprintf(p, "\n");
+        p += sprintf(p, "Marker         0x%x\n", ntohs(hdr->marker));
+        p += sprintf(p, "Protocol       0x%x\n", hdr->protocol);
+        p += sprintf(p, "Version        0x%x\n", hdr->version);
+        p += sprintf(p, "Op-Code        0x%x (%s)\n", ntohs(hdr->opcode), opcode2text(hdr->opcode));
+        p += sprintf(p, "Status           %d (%s)\n", hdr->status, status2text(hdr->status));
+        p += sprintf(p, "Trans-ID         %d\n", ntohl(hdr->tran_id));
+        p += sprintf(p, "Req Lec-ID       %d\n", ntohs(hdr->lec_id));
+        p += sprintf(p, "Flags          0x%x", ntohs(hdr->flags));
+        if (hdr->flags & htons(REMOTE_ADDRESS)) p+= sprintf(p, " 'Remote Address'");
+        if (hdr->flags & htons(V2_CAPABLE))     p+= sprintf(p, " 'V2 Capable'");
+        if (hdr->flags & htons(V2_REQUIRED))    p+= sprintf(p, " 'V2 Required'");
+        if (hdr->flags & htons(PROXY_FLAG))     p+= sprintf(p, " 'Proxy Flag'");
+        if (hdr->flags & htons(TOPO_CHANGE))    p+= sprintf(p, " 'Topology Change'");
+        p += sprintf(p, "\n");
+        
+        f = (struct ctrl_frame *)frame;
+        p += sprintf(p, "Source Lan     0x%x (%s)\n", ntohs(f->src_lan_dst.tag), lan_dst2text(&f->src_lan_dst));
+        p += sprintf(p, "Target Lan     0x%x (%s)\n", ntohs(f->target_lan_dst.tag), lan_dst2text(&f->target_lan_dst));
+        p += sprintf(p, "Source ATM     "); p += my_atm2text(f->src_atm_addr, p); p += sprintf(p, "\n");
+        p += sprintf(p, "Lan type       0x%x\n", f->lan_type);
+        p += sprintf(p, "Lan MTU        0x%x\n", f->max_frame_size);
+        p += sprintf(p, "# of TLVs      0x%x\n", f->num_tlvs);
+        p += sprintf(p, "Elan Name size 0x%x\n", f->elan_name_size);
+        p += sprintf(p, "Target ATM     "); p += my_atm2text(f->target_atm_addr, p); p += sprintf(p, "\n");
+        p += sprintf(p, "Elan Name          (");
+        memcpy(p, f->elan_name, f->elan_name_size); p += f->elan_name_size;
+        p += sprintf(p, ")\n");
+
+
+        *p = '\0';
+        diag(COMPONENT, DIAG_DEBUG, "%s\n", text);
+
+        return;
+}
+
+static void display_ready(void *ready_frame)
+{
+        diag(COMPONENT, DIAG_DEBUG, "ready frame\n");
+
+        return;
+}
+
+/* Poor man's atm2text */
+static int my_atm2text(unsigned char *atm_addr, char *dest)
+{
+        int i, len;
+        
+        len = 0;
+        for (i = 0; i < ATM_ESA_LEN; i++)
+                len += sprintf(dest + len, "%2.2x ", *(atm_addr + i));
+        
+        return len;
+}
+
+static const char *lan_dst2text(struct lan_dst *dst)
+{
+        static char text[42]; /* big enough for text + MAC */
+        char *p = text;
+
+        switch(ntohs(dst->tag)) {
+        case LAN_DST_NOT_PRESENT:
+                sprintf(text, "Not present");
+                break;
+        case LAN_DST_MAC_ADDR:
+                p += sprintf(p, "MAC address");
+                p += sprintf(p, " ");
+                mac2text(p, dst->mac);
+                break;
+        case LAN_DST_ROUTE_DESC:
+                p += sprintf(p, "Route Descriptor");
+                p += sprintf(p, " ");
+                mac2text(p, dst->mac);
+                break;
+        default:
+                sprintf(text, "<Unknown Lan destination>");
+                break;
+        }
+
+        return text;
+}
+
+void mac2text(char *buff, unsigned char *mac_addr)
+{
+        sprintf(buff, "%02x-%02x-%02x-%02x-%02x-%02x",
+                mac_addr[0], mac_addr[1], mac_addr[2], 
+                mac_addr[3], mac_addr[4], mac_addr[5]);
+        
+        return;
+}
+
+const char *opcode2text(uint16_t opcode) {
+        switch (ntohs(opcode)) {
+        case LE_CONFIG_REQ:
+                return "LE_CONFIG_REQUEST";
+                break;
+        case LE_CONFIG_RSP:
+                return "LE_CONFIG_RESPONSE";
+                break;
+        case LE_JOIN_REQ:
+                return "LE_JOIN_REQUEST";
+                break;
+        case LE_JOIN_RSP:
+                return "LE_JOIN_RESPONSE";
+                break;
+        case LE_REG_REQ:
+                return "LE_REGISTER_REQUEST";
+                break;
+        case LE_REG_RSP:
+                return "LE_REGISTER_RESPONSE";
+                break;
+        case LE_ARP_REQ:
+                return "LE_ARP_REQUEST";
+                break;
+        case LE_ARP_RSP:
+                return "LE_ARP_RESPONSE";
+                break;
+        case LE_FLUSH_REQ:
+                return "LE_FLUSH_REQUEST";
+                break;
+        case LE_FLUSH_RSP:
+                return "LE_FLUSH_RESPONSE";
+                break;
+        case READY_QUERY:
+                return "READY_QUERY";
+                break;
+        case READY_IND:
+                return "READY_INDICATION";
+                break;
+        case LE_TOPO_REQ:
+                return "LE_TOPOLOGY_REQUEST";
+                break;
+        case LE_NARP_REQ:
+                return "LE_NARP_REQUEST";
+                break;
+        default:
+                break;
+        }
+
+        return "<Unknown OP-CODE>";
+}
+
+const char *tlv2text(uint32_t type)
+{
+        switch (type) {
+        case MAX_CUM_CTRL_TIMEOUT:
+                return "Max-Cumulative-Control-Time-out";
+                break;
+        case MAX_UNKNOWN_FRAME_CNT:
+                return "Max-Unknown-Frame-Count";
+                break;
+        case MAX_UNKNOWN_FRAME_TIME:
+                return "Max-Unknown-Frame-Time";
+                break;
+        case VCC_TIMEOUT_PERIOD:
+                return "VCC-Timeout-Period";
+                break;
+        case MAX_RETRY_COUNT:
+                return "Max-Retry-Count";
+                break;
+        case AGING_TIME:
+                return "Aging-Time";
+                break;
+        case FORWARD_DELAY_TIME:
+                return "Forward-Delay-Time";
+                break;
+        case EXPECTED_LE_ARP_TIME:
+                return "Expected-LE_ARP-Response-Time";
+                break;
+        case FLUSH_TIMEOUT:
+                return "Flush-Time-out";
+                break;
+        case PATH_SWITCHING_DELAY:
+                return "Path-Switching-Delay";
+                break;
+        case LOCAL_SEGMENT_ID:
+                return "Local-Segment-ID";
+                break;
+        case DEF_MCAST_SND_VCC_TYPE:
+                return "Default-Mcast-Send-VCC-Type";
+                break;
+        case DEF_MCAST_SND_VCC_AVG:
+                return "Default-Mcast-Send-VCC-AvgRate";
+                break;
+        case DEF_MCAST_SEND_PEAK_RT:
+                return "Default-Mcast-Send-VCC-PeakRate";
+                break;
+        case CONN_COMPLETION_TIMER:
+                return "Connection-Completion-Timer";
+                break;
+        case CONFIG_FRAG_INFO:
+                return "Config-Frag-Info";
+                break;
+        case LAYER3_ADDRESS:
+                return "Layer-3-Address";
+                break;
+        case ELAN_ID:
+                return "ELAN-ID";
+                break;
+        case SERVICE_CATEGORY:
+                return "Service-Category";
+                break;
+        case LLC_MUXED_ATM_ADDR:
+                return "LLC-Muxed-ATM-Address";
+                break;
+        case X5_ADJUSTMENT:
+                return "X5-Adjustment";
+                break;
+        case PREFERRED_LES:
+                return "Preferred-LES";
+                break;
+        case FORE_NAME:
+                return "Fore's LANE client name";
+                break;
+        default:
+                break;
+        }
+
+        return "<Unknown TLV type>";
+}
+
+const char *status2text(uint16_t status)
+{
+
+        switch (ntohs(status)) {
+        case 0:
+                return "Success";
+                break;
+        case 1:
+                return "Version Not Supported";
+                break;
+        case 2:
+                return "Invalid request parameters";
+                break;
+        case 4:
+                return "Duplicate LAN Destination registration";
+                break;
+        case 5:
+                return "Dupliate ATM address";
+                break;
+        case 6:
+                return "Insufficient resources to grant request";
+                break;
+        case 7:
+                return "Access denied";
+                break;
+        case 8:
+                return "Invalid REQUESTOR-ID";
+                break;
+        case 9:
+                return "Invalid LAN Destination";
+                break;
+        case 10:
+                return "Invalid ATM Address";
+                break;
+        case 20:
+                return "No Configuration";
+                break;
+        case 21:
+                return "LE_CONFIGURE Error";
+                break;
+        case 22:
+                return "Insufficient Information";
+                break;
+        case 24:
+                return "TLV Not Found";
+                break;
+        default:
+                break;
+        }
+
+        return "<Something not supported in LANEv2>";
+}
diff -ur --new-file old/atm/led.new/display.h new/atm/led.new/display.h
--- old/atm/led.new/display.h	Thu Jan  1 01:00:00 1970
+++ new/atm/led.new/display.h	Sat Feb  6 14:44:23 1999
@@ -0,0 +1,13 @@
+/* Copyright (C) 1999 Heikki Vatiainen hessu@cs.tut.fi */
+
+#ifndef DISPLAY_H
+#define DISPLAY_H
+
+void display_frame(void *frame);
+
+void mac2text(char *buff, unsigned char *mac_addr);
+const char *opcode2text(uint16_t opcode);
+const char *tlv2text(uint32_t type);
+const char *status2text(uint16_t status);
+
+#endif /* DISPLAY_H */
diff -ur --new-file old/atm/led.new/frame_defs.h new/atm/led.new/frame_defs.h
--- old/atm/led.new/frame_defs.h	Thu Jan  1 01:00:00 1970
+++ new/atm/led.new/frame_defs.h	Sun Feb  7 12:05:21 1999
@@ -0,0 +1,125 @@
+/* frame_defs.h - definitions for LANE control frames, TLVs etc. */
+
+/* Copyright (C) 1999 Heikki Vatiainen hessu@cs.tut.fi */
+
+#ifndef FRAMES_DEFS_H
+#define FRAMES_DEFS_H
+
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+
+/* Try to squeeze out all the padding from the structs so that we
+ * can use them as templates for filling in and examining frames.
+ * From the gcc documentation:
+ *    This attribute, attached to an `enum', `struct', or `union' type
+ *    definition, specified that the minimum required memory be used to
+ *    represent the type.
+ */
+#ifndef PACKED
+#define PACKED __attribute__((packed))
+#endif
+
+/* READY_QUERY and READY_IND frame format
+ */
+struct ready_frame {
+        uint16_t marker;
+        uint8_t  protocol;
+        uint8_t  version;
+        uint16_t opcode;
+};
+
+/* Fields common to all control frames,
+ * not including READY_*
+ */
+struct frame_hdr {
+        uint16_t marker;
+        uint8_t  protocol;
+        uint8_t  version;
+        uint16_t opcode;
+        uint16_t status;
+        uint32_t tran_id;
+        uint16_t lec_id;
+        uint16_t flags;
+} PACKED;
+
+/* values for lan_dst.tag
+ */
+#define LAN_DST_NOT_PRESENT 0x0000
+#define LAN_DST_MAC_ADDR    0x0001
+#define LAN_DST_ROUTE_DESC  0x0002
+
+struct lan_dst { /* Token Ring route descriptors omitted */
+        uint16_t tag;
+        uint8_t  mac[ETH_ALEN];
+} PACKED;
+
+/* All frames except STATUS_INQ and STATUS_REPLY look like this
+ */
+struct ctrl_frame {
+        struct frame_hdr header;
+        struct lan_dst src_lan_dst;
+        struct lan_dst target_lan_dst;
+        uint8_t src_atm_addr[ATM_ESA_LEN];
+        uint8_t lan_type;
+        uint8_t max_frame_size;
+        uint8_t num_tlvs;
+        uint8_t elan_name_size;
+        uint8_t target_atm_addr[ATM_ESA_LEN];
+        uint8_t elan_name[32];
+        /* TLVs if any follow elan_name */
+} PACKED;
+
+/* Frame types
+ */
+#define LE_CONFIG_REQ 0x0001
+#define LE_CONFIG_RSP 0x0101
+#define LE_JOIN_REQ   0x0002
+#define LE_JOIN_RSP   0x0102
+#define LE_REG_REQ    0x0004
+#define LE_REG_RSP    0x0104
+#define LE_ARP_REQ    0x0006
+#define LE_ARP_RSP    0x0106
+#define LE_FLUSH_REQ  0x0007
+#define LE_FLUSH_RSP  0x0107
+#define LE_NARP_REQ   0x0008
+#define LE_TOPO_REQ   0x0009
+#define READY_QUERY   0x0003 /* READY_* are not in ctrl_frame format */
+#define READY_IND     0x0103
+
+/* Flags for LE Control Frames
+ */
+#define REMOTE_ADDRESS  0x0001
+#define V2_CAPABLE      0x0002
+#define V2_REQUIRED     0x0008
+#define PROXY_FLAG      0x0080
+#define TOPO_CHANGE     0x0100
+
+/* TLV types defined in LANEv1 and v2 + one Fore specific TLV
+ */
+#define MAX_CUM_CTRL_TIMEOUT   0x00A03E01
+#define MAX_UNKNOWN_FRAME_CNT  0x00A03E02
+#define MAX_UNKNOWN_FRAME_TIME 0x00A03E03
+#define VCC_TIMEOUT_PERIOD     0x00A03E04
+#define MAX_RETRY_COUNT        0x00A03E05
+#define AGING_TIME             0x00A03E06
+#define FORWARD_DELAY_TIME     0x00A03E07
+#define EXPECTED_LE_ARP_TIME   0x00A03E08
+#define FLUSH_TIMEOUT          0x00A03E09
+#define PATH_SWITCHING_DELAY   0x00A03E0A
+#define LOCAL_SEGMENT_ID       0x00A03E0B
+#define DEF_MCAST_SND_VCC_TYPE 0x00A03E0C
+#define DEF_MCAST_SND_VCC_AVG  0x00A03E0D
+#define DEF_MCAST_SEND_PEAK_RT 0x00A03E0E
+#define CONN_COMPLETION_TIMER  0x00A03E0F
+#define CONFIG_FRAG_INFO       0x00A03E10 /* This and the rest are LANEv2 only */
+#define LAYER3_ADDRESS         0x00A03E11
+#define ELAN_ID                0x00A03E12
+#define SERVICE_CATEGORY       0x00A03E13
+#define LLC_MUXED_ATM_ADDR     0x00A03E2B
+#define X5_ADJUSTMENT          0x00A03E2C /* length 0 */
+#define PREFERRED_LES          0x00A03E2D
+
+#define FORE_NAME              0x00204808 /* check zeppelin(8), -f option */
+
+#endif /* FRAMES_DEFS_H */
diff -ur --new-file old/atm/led.new/frames.c new/atm/led.new/frames.c
--- old/atm/led.new/frames.c	Thu Jan  1 01:00:00 1970
+++ new/atm/led.new/frames.c	Sat Feb  6 21:45:03 1999
@@ -0,0 +1,582 @@
+/* frames.c - handle incoming frames, prefill outgoing frames, parse TLVs etc. */
+
+/* Copyright (C) 1999 Heikki Vatiainen hessu@cs.tut.fi */
+
+/* Functions for handling LANE control frames used when joining an
+ * ELAN are in lec.c
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <atm.h>
+#include <linux/atmlec.h>
+#include <atmd.h>
+
+#include "conn.h"
+#include "lec.h"
+#include "frames.h"
+#include "display.h"
+#include "kernel.h"
+
+#define COMPONENT "frames.c"
+
+static uint32_t transaction_id = 0;
+static void extract_tlv_value(uint16_t opcode, uint32_t type, unsigned char *tlvp, int len);
+static void handle_x5(uint16_t opcode);
+
+/* Initializes LANE Control frame of type 'type'
+ */
+void prefill_frame(void *buff, uint16_t type)
+{
+        struct frame_hdr *header;
+        
+        memset(buff, 0, sizeof(struct ctrl_frame));
+        header = (struct frame_hdr *)buff;
+        header->marker   = htons(0xff00);
+        header->protocol = 0x01;
+        header->version  = 0x01;
+        header->opcode   = htons(type);
+        header->status   = htons(0x0000);
+        header->tran_id  = htonl(transaction_id);
+        header->lec_id   = htons(lec_params.c14_lec_id);
+        header->flags    = htons(0x0000);
+        
+        transaction_id++;
+        
+        return;
+}
+
+/* Validates incoming Control frames except READY_IND and
+ * READY_QUERY which do not start with the common header.
+ * Also calls display_frame() to print out conforming frames.
+ * Returns < 0 for error
+ */
+int validate_frame(unsigned char *buff, int size)
+{
+        struct ready_frame *hdr; /* Ready is the shortest possible */
+
+        if (size < sizeof(struct ready_frame)) {
+                diag(COMPONENT, DIAG_DEBUG, "short frame, size %d\n", size);
+                return -1;
+        }
+
+        hdr = (struct ready_frame *)buff;
+        if (hdr->marker   != htons(0xff00) ||
+            hdr->protocol != 0x01 ||
+            hdr->version  != 0x01)
+                return -1;
+
+        /* READY_* frames are shorter than others */
+        if (hdr->opcode == htons(READY_QUERY) ||
+            hdr->opcode == htons(READY_IND)) {
+                diag(COMPONENT, DIAG_DEBUG, "Received a %s\n", opcode2text(hdr->opcode));
+                return 0;
+        }
+
+        if (size < sizeof(struct ctrl_frame)) {
+                diag(COMPONENT, DIAG_DEBUG, "short frame, size %d\n", size);
+                return -1;
+        }
+
+        display_frame(buff);
+
+        return 0;
+}
+
+/* Handle incoming LE_FLUSH_REQUEST frames.
+ */
+static void handle_flush_req(struct ctrl_frame *f)
+{
+        if (memcmp(lec_params.c1n_my_atm_addr, f->target_atm_addr, ATM_ESA_LEN) != 0)
+                return;
+        f->header.opcode = htons(LE_FLUSH_RSP);
+        if (send_frame(lec_params.ctrl_direct, f, sizeof(struct ctrl_frame)) < 0)
+                diag(COMPONENT, DIAG_DEBUG, "could not send LE_FLUSH_RESPONSE\n");
+
+        return;
+}
+
+/* Handle incoming LE_FLUSH_RESPONSE frames.
+ */
+static void handle_flush_rsp(struct ctrl_frame *f)
+{
+        struct atmlec_msg msg;
+
+        if (f->header.lec_id != htons(lec_params.c14_lec_id)) {
+                diag(COMPONENT, DIAG_DEBUG, "Wrong lec_id, ignoring\n");
+                return;
+        }
+
+        memset(&msg, 0, sizeof(struct atmlec_msg));
+        msg.type = l_flush_complete;
+        msg.content.normal.flag = ntohl(f->header.tran_id);
+        msg_to_kernel(&msg, sizeof(struct atmlec_msg));
+        
+        return;
+}
+
+/* Handle incoming READY_QUERY frames.
+ */
+static void handle_ready_query(Conn_t *conn, struct ready_frame *f)
+{
+        f->opcode = htons(READY_IND);
+        send_frame(conn, f, sizeof(struct ready_frame));
+
+        return;
+}
+
+/* Helper for handle_le_arp_req.
+ * If the target_lan_dst was not our MAC address, try to
+ * see if the bridging table in the kernel knows about it.
+ * Returns < 0 for serious error
+ */
+static int check_bridge(struct ctrl_frame *frame, int size)
+{
+        struct atmlec_msg msg;
+
+        if (lec_params.c4_proxy_flag == 0)
+                return 0;
+
+        memset(&msg, 0, sizeof(struct atmlec_msg));
+        msg.type = l_should_bridge;
+        memcpy(msg.content.proxy.mac_addr, frame->target_lan_dst.mac, ETH_ALEN);
+        memcpy(msg.content.proxy.atm_addr, frame->src_atm_addr, ATM_ESA_LEN);
+        msg.content.proxy.tran_id = frame->header.tran_id;
+        msg.content.proxy.lec_id = frame->header.lec_id;
+
+        return msg_to_kernel(&msg, sizeof(struct atmlec_msg));
+}
+
+/* Handles incoming LE_ARP_REQ and targetless LE_ARP_REQ.
+ * See LANEv2, 7.1.5 and 7.1.30
+ * Returns < 0 for serious error
+ */
+static int handle_le_arp_req(struct ctrl_frame *frame, int size)
+{
+        int sizeoftlvs, sizeofrsp, retval;
+        struct ctrl_frame *rsp;
+        struct atmlec_msg *msg;
+
+        if (frame->header.lec_id == htons(lec_params.c14_lec_id)) {
+                diag(COMPONENT, DIAG_DEBUG, "Ignoring own LE_ARP_REQUEST\n");
+                return 0;
+        }
+
+        retval = 0;
+        if (frame->target_lan_dst.tag == htons(LAN_DST_MAC_ADDR)) {
+                if (memcmp(frame->target_lan_dst.mac, lec_params.c6_mac_addr, ETH_ALEN) != 0)
+                        return (check_bridge(frame, size)); /* target was not us */
+                sizeofrsp = sizeof(struct ctrl_frame) + lec_params.sizeoftlvs;
+                rsp = (struct ctrl_frame *)malloc(sizeofrsp);
+                if (rsp == NULL) return 0;
+                memcpy(rsp, frame, sizeof(struct ctrl_frame));
+                rsp->header.opcode = htons(LE_ARP_RSP);
+                memcpy(rsp->target_atm_addr, lec_params.c1n_my_atm_addr, ATM_ESA_LEN);
+                rsp->num_tlvs = lec_params.num_tlvs;
+                if (lec_params.num_tlvs > 0)
+                        memcpy(rsp + 1, lec_params.tlvs, lec_params.sizeoftlvs);
+
+                retval = send_frame(lec_params.ctrl_direct, rsp, sizeofrsp);
+                free(rsp);
+        } else if (frame->target_lan_dst.tag == htons(LAN_DST_NOT_PRESENT) &&
+                   lec_params.c29_v2_capable) {
+                sizeoftlvs = size - sizeof(struct ctrl_frame);
+                msg = (struct atmlec_msg *)malloc(sizeof(struct atmlec_msg) + sizeoftlvs);
+                if (msg == NULL) return -1;
+                memset(msg, 0, sizeof(struct atmlec_msg));
+                msg->type = l_arp_update;
+                memcpy(msg->content.normal.mac_addr, frame->src_lan_dst.mac, ETH_ALEN);
+                memcpy(msg->content.normal.atm_addr, frame->src_atm_addr, ATM_ESA_LEN);
+                msg->content.normal.flag = (frame->header.flags & REMOTE_ADDRESS) ? 1 : 0;
+                msg->content.normal.targetless_le_arp = 1;
+                msg->sizeoftlvs = sizeoftlvs;
+                if (sizeoftlvs > 0) memcpy(msg + 1, frame + 1, sizeoftlvs);
+
+                retval = msg_to_kernel(msg, sizeof(struct atmlec_msg) + sizeoftlvs);
+                free(msg);
+        }
+        
+        return retval;
+}
+
+/* Handles incoming LE_NARP_REQUESTS frames.
+ * Mandatory only in LANEv2. If we are LANEv1, we'll just ignore these.
+ * See LANEv2, 7.1.31-35 LE_NARP_REQUEST/RESPONSE.
+ * For no-source, i.e. no source ATM address, we remove the LE_ARP cache entry.
+ * If the source is non-zero, we first remove the entry
+ * and then add the new entry in the LE_ARP cache.
+ * Returns < 0 for serious error.
+ */
+static int handle_narp_req(struct ctrl_frame *frame, int size)
+{
+        int sizeoftlvs, no_source = 0, retval;
+        struct atmlec_msg *msg;
+        unsigned char empty[ATM_ESA_LEN];
+
+        if (frame->header.lec_id == htons(lec_params.c14_lec_id) ||
+            lec_params.c29_v2_capable == 0) {
+                diag(COMPONENT, DIAG_DEBUG, "Ignoring LE_NARP_REQUEST\n");
+                return 0;
+        }
+
+        memset(empty, 0, ATM_ESA_LEN);
+        if (memcmp(empty, frame->src_atm_addr, ATM_ESA_LEN) == 0)
+                no_source = 1;
+
+        sizeoftlvs = size - sizeof(struct ctrl_frame);
+        msg = (struct atmlec_msg *)malloc(sizeof(struct atmlec_msg) + sizeoftlvs);
+        if (msg == NULL) return -1;
+        memset(msg, 0, sizeof(struct atmlec_msg));
+        msg->type = l_narp_req;
+        memcpy(msg->content.normal.mac_addr, frame->src_lan_dst.mac, ETH_ALEN);
+        memcpy(msg->content.normal.atm_addr, frame->src_atm_addr, ATM_ESA_LEN);
+        msg->content.normal.flag = (frame->header.flags & REMOTE_ADDRESS) ? 1 : 0;
+        msg->content.normal.no_source_le_narp = no_source;
+        msg->sizeoftlvs = sizeoftlvs;
+        if (sizeoftlvs > 0) memcpy(msg + 1, frame + 1, sizeoftlvs);
+        retval = msg_to_kernel(msg, sizeof(struct atmlec_msg) + sizeoftlvs);
+        
+        free(msg);
+        
+        return retval;
+}
+
+/* Handles incoming LE_ARP_RESPONSE frames.
+ * Returns < 0 for serious error
+ */
+static int handle_arp_rsp(struct ctrl_frame *frame, int size)
+{
+        int sizeoftlvs, msglen, retval;
+        char buff[MAX_CTRL_FRAME];
+        struct atmlec_msg *msg;
+        
+        if (frame->header.lec_id != htons(lec_params.c14_lec_id)) {
+                diag(COMPONENT, DIAG_DEBUG, "Wrong lec_id, ignoring\n");
+                return 0;
+        }
+
+        sizeoftlvs = size - sizeof(struct ctrl_frame);
+        msglen = sizeof(struct atmlec_msg) + sizeoftlvs;
+        msg = (struct atmlec_msg *)buff;
+        memset(msg, 0, msglen);
+        
+        msg->type = l_arp_update;
+        memcpy(msg->content.normal.mac_addr, frame->target_lan_dst.mac, ETH_ALEN);
+        memcpy(msg->content.normal.atm_addr, frame->target_atm_addr, ATM_ESA_LEN);
+        msg->content.normal.flag = (frame->header.flags & REMOTE_ADDRESS) ? 1 : 0;
+        msg->sizeoftlvs = sizeoftlvs;
+        if (sizeoftlvs > 0) memcpy(msg + 1, frame + 1, sizeoftlvs);
+
+        retval = msg_to_kernel(msg, msglen);
+
+        return retval;
+}
+
+/* Handles incoming LE_TOPOLOGY_REQUEST frames.
+ * Returns < 0 for serious error
+ */
+static int handle_topo_req(struct ctrl_frame *frame)
+{
+        struct atmlec_msg msg;
+
+        memset(&msg, 0, sizeof(struct atmlec_msg));
+        msg.type = l_topology_change;
+        if (frame->header.flags & htons(TOPO_CHANGE))
+                msg.content.normal.flag = 1;
+
+        return(msg_to_kernel(&msg, sizeof(struct atmlec_msg)));
+}
+
+/* Processes and validates incoming frames. Calls frame
+ * dependant handler functions.
+ * Returns < 0 for serious error
+ */
+int handle_frame(Conn_t *conn, char *buff, int size)
+{
+        struct ctrl_frame *frame;
+
+        if (validate_frame(buff, size) < 0)
+                return 0;
+
+        frame = (struct ctrl_frame *)buff;
+
+        switch (ntohs(frame->header.opcode)) {
+        case LE_FLUSH_REQ:
+                handle_flush_req(frame);
+                break;
+        case LE_FLUSH_RSP:
+                handle_flush_rsp(frame);
+                break;
+        case READY_QUERY:
+                handle_ready_query(conn, (struct ready_frame *)frame);
+                break;
+        case READY_IND:
+                /* We can ignore these */
+                break;
+        case LE_ARP_REQ:
+                if (handle_le_arp_req(frame, size) < 0)
+                        return -1;
+                break;
+        case LE_ARP_RSP:
+                if (handle_arp_rsp(frame, size) < 0)
+                        return -1;
+                break;
+        case LE_TOPO_REQ:
+                if (handle_topo_req(frame) < 0)
+                        return -1;
+                break;
+        case LE_REG_RSP:
+                /* FIXME: Should we do something? */
+                break;
+        case LE_NARP_REQ:
+                if (handle_narp_req(frame, size) < 0)
+                        return -1;
+                break;
+        default:
+                diag(COMPONENT, DIAG_ERROR,
+                     "Unknown frame, opcode 0x%x %s\n", ntohs(frame->header.opcode),
+                     opcode2text(frame->header.opcode));
+                break;
+        }
+
+        return 0;
+}
+
+/* Sends a READY_INDICATION when a non-blocking connect completes.
+ */
+void send_ready_ind(Conn_t *conn)
+{
+        struct ready_frame frame;
+        int retval;
+
+        frame.marker   = htons(0xff00);
+        frame.protocol = 0x01;
+        frame.version  = 0x01;
+        frame.opcode   = htons(READY_IND);
+
+        retval = send_frame(conn, &frame, sizeof(struct ready_frame));
+        if (retval < 0)
+                diag(COMPONENT, DIAG_DEBUG, "Could not send READY_IND, fd %d\n", conn->fd);
+
+        return;
+}
+
+/* Sends a LE_FLUSH_REQUEST
+ * Returns the transaction used with this flush REQ/RSP pair.
+ */
+uint32_t send_flush_req(Conn_t *conn)
+{
+        struct ctrl_frame frame;
+
+        prefill_frame(&frame, LE_FLUSH_REQ);
+        memcpy(frame.src_atm_addr, lec_params.c1n_my_atm_addr, ATM_ESA_LEN);
+        memcpy(frame.target_atm_addr, conn->atm_address, ATM_ESA_LEN);
+
+        send_frame(lec_params.mcast_send, &frame, sizeof(struct ctrl_frame));
+
+        return ntohl(frame.header.tran_id);
+}
+
+/* Registers our MAC address and associated TLVs with LES.
+ * See LANEv2, 6. Registaration Protocol
+ */
+void send_register_req(void)
+{
+        char buff[MAX_CTRL_FRAME];
+        struct ctrl_frame *frame;
+
+        frame = (struct ctrl_frame *)buff;
+        prefill_frame(frame, LE_REG_REQ);
+        frame->src_lan_dst.tag = htons(LAN_DST_MAC_ADDR);
+        memcpy(frame->src_lan_dst.mac, lec_params.c6_mac_addr, ETH_ALEN);
+        memcpy(frame->src_atm_addr, lec_params.c1n_my_atm_addr, ATM_ESA_LEN);
+        frame->num_tlvs = lec_params.num_tlvs;
+        if (lec_params.sizeoftlvs > 0)
+                memcpy((frame + 1), lec_params.tlvs, lec_params.sizeoftlvs);
+
+        send_frame(lec_params.ctrl_direct, frame, sizeof(struct ctrl_frame) + lec_params.sizeoftlvs);
+        
+        return;
+}
+
+/* Goes through the TLVs trailing a frame while passing them
+ * one by one to the TLV handler.
+ */
+void parse_tlvs(uint16_t opcode, unsigned char *tlvp, int numtlvs, int sizeoftlvs)
+{
+        uint32_t type;
+        uint8_t len;
+        unsigned char *end_of_tlvs;
+
+        end_of_tlvs = tlvp + sizeoftlvs;
+        while (numtlvs-- > 0 && end_of_tlvs - tlvp >= 5) {
+                type = *(uint32_t *)tlvp;
+                type = ntohl(type);
+                len  = tlvp[4];
+                tlvp += 5;
+                diag(COMPONENT, DIAG_DEBUG, "parse_tlvs: type %s len %d\n",
+                     tlv2text(type), len);
+                if (tlvp + len > end_of_tlvs)
+                        return; /* value too long */
+                
+                extract_tlv_value(opcode, type, tlvp, len);
+                tlvp += len;
+        }
+
+        return;
+}
+
+/* Does something depending on the frame type this TLV arrived with,
+ * TLV type, and contents of TLV.
+ */
+static void extract_tlv_value(uint16_t opcode, uint32_t type, unsigned char *tlvp, int len)
+{
+
+        uint16_t value16;
+        uint32_t value32;
+
+        /* LE_JOIN_RESPONSE does not support all the TLVs */
+        if (opcode == htons(LE_JOIN_RSP) && type != htonl(ELAN_ID))
+                return;
+
+        switch(len) {
+        case 0:
+                switch (type) {
+                case X5_ADJUSTMENT:
+                        handle_x5(opcode);
+                        break;
+                default:
+                        goto whine;
+                        break;
+                }
+                break;
+        case 2:
+                value16 = *(uint16_t *)tlvp;
+                value16 = ntohs(value16);
+                diag(COMPONENT, DIAG_DEBUG, "value of TLV %d\n", value16);
+                switch (type) {
+                case MAX_CUM_CTRL_TIMEOUT:
+                        lec_params.c7_ctrl_timeout = value16;
+                        break;
+                case MAX_UNKNOWN_FRAME_CNT:
+                        lec_params.c10_max_unknown_frames = value16;
+                        break;
+                case MAX_UNKNOWN_FRAME_TIME:
+                        lec_params.c11_max_unknown_frame_time = value16;
+                        break;
+                case MAX_RETRY_COUNT:
+                        lec_params.c13_max_retry_count = value16;
+                        break;
+                case FORWARD_DELAY_TIME:
+                        lec_params.c18_forward_delay_time = value16;
+                        break;
+                case EXPECTED_LE_ARP_TIME:
+                        lec_params.c20_le_arp_response_time = value16;
+                        break;
+                case FLUSH_TIMEOUT:
+                        lec_params.c21_flush_timeout = value16;
+                        break;
+                case PATH_SWITCHING_DELAY:
+                        lec_params.c22_path_switching_delay = value16;
+                        break;
+                case LOCAL_SEGMENT_ID:
+                case DEF_MCAST_SND_VCC_TYPE:
+                case CONN_COMPLETION_TIMER:
+                case SERVICE_CATEGORY:
+                        /* do nothing */
+                        break;
+                default:
+                        goto whine;
+                        break;
+                }
+                break;
+        case 4:
+                value32 = *(uint32_t *)tlvp;
+                value32 = ntohl(value32);
+                diag(COMPONENT, DIAG_DEBUG, "value of TLV %d\n", value32);
+                switch (type) {
+                case VCC_TIMEOUT_PERIOD:
+                        lec_params.c12_vcc_timeout = value32;
+                        break;
+                case AGING_TIME:
+                        lec_params.c17_aging_time = value32;
+                        break;
+                case ELAN_ID:
+                        lec_params.c31_elan_id = value32;
+                        break;
+                case DEF_MCAST_SND_VCC_AVG:
+                case DEF_MCAST_SEND_PEAK_RT:
+                        /* do nothing */
+                        break;
+                default:
+                        goto whine;
+                        break;
+                }
+                break;
+        case 20:
+                switch(type) {
+                case PREFERRED_LES:
+                        memcpy(lec_params.c35_preferred_les, tlvp, ATM_ESA_LEN);
+                        lec_params.c35_contains_address = 1;
+                        break;
+                case LLC_MUXED_ATM_ADDR:
+                        /* do nothing */
+                        break;
+                default:
+                        goto whine;
+                        break;
+                }
+                break;
+        default:
+                /* handle variable length TLVs */
+                switch(type) {
+                case CONFIG_FRAG_INFO:
+                        diag(COMPONENT, DIAG_INFO, "Got Config-Frag-Info TLV\n");
+                        break;
+                case LAYER3_ADDRESS:
+                        /* do nothing */
+                        break;
+                default:
+                        goto whine;
+                        break;
+                }
+                break;
+        }
+
+        return;
+
+ whine:
+        diag(COMPONENT, DIAG_DEBUG, "Unknown TLV, type 0x%x, len %d\n", type, len);
+
+        return;
+}
+
+/* Figures out what to do when we get a X5-Adjustment TLV
+ */
+static void handle_x5(uint16_t opcode)
+{
+        if (!lec_params.c29_v2_capable)
+                return;
+        if (opcode != ntohs(LE_CONFIG_RSP)) {
+                diag(COMPONENT, DIAG_WARN, "X5-Adjustment TLV received but not with LE_CONFIG_RSP\n");
+                return;
+        }
+
+        switch(lec_params.c3_max_frame_size) {
+        case 1:
+                lec_params.c3_max_frame_size = 5; /* 1580 */
+                return;
+                break;
+        case 2:
+                lec_params.c3_max_frame_size = 5; /* 1580 */
+                return;
+                break;
+        default:
+                /* rest of the values are not affected by X5 */
+                break;
+        }
+
+        return;
+}
diff -ur --new-file old/atm/led.new/frames.h new/atm/led.new/frames.h
--- old/atm/led.new/frames.h	Thu Jan  1 01:00:00 1970
+++ new/atm/led.new/frames.h	Sat Feb  6 21:54:23 1999
@@ -0,0 +1,21 @@
+/* Copyright (C) 1999 Heikki Vatiainen hessu@cs.tut.fi */
+
+#ifndef FRAMES_H
+#define FRAMES_H
+
+#include "frame_defs.h"
+
+#define MAX_CTRL_FRAME 512
+
+void prefill_frame(void *ctrl_frame, uint16_t type);
+int validate_frame(unsigned char *buff, int size);
+
+void send_ready_ind(Conn_t *conn);
+void send_register_req(void);
+
+int handle_frame(Conn_t *conn, char *buff, int size);
+uint32_t send_flush_req(Conn_t *conn);
+
+void parse_tlvs(uint16_t opcode, unsigned char *tlvp, int numtlvs, int sizeoftlvs);
+
+#endif /* FRAMES_H */
diff -ur --new-file old/atm/led.new/join.c new/atm/led.new/join.c
--- old/atm/led.new/join.c	Thu Jan  1 01:00:00 1970
+++ new/atm/led.new/join.c	Sat Feb  6 21:58:04 1999
@@ -0,0 +1,766 @@
+/* join.c - functions which are only needed when joining an ELAN */
+
+/* Copyright (C) 1999 Heikki Vatiainen hessu@cs.tut.fi */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h> /* for rand() */
+#include <sys/ioctl.h>
+#include <atm.h>
+
+#include <linux/atmlec.h>
+
+#include <atmd.h>
+
+#include "conn.h"
+#include "lec.h"
+#include "join.h"
+#include "frames.h"
+#include "display.h"
+
+#define COMPONENT "lec.c"
+
+struct lec_params lec_params;
+
+static unsigned char bus_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+static unsigned char well_known_lecs[ATM_ESA_LEN] = {0x47,0x00,0x79,0x00,0x00,
+                                                     0x00,0x00,0x00,0x00,0x00,
+                                                     0x00,0x00,0x00,0x00,0xA0,
+                                                     0x3E,0x00,0x00,0x01,0x00};
+static int do_lec_configure(Conn_t *conn);
+static int send_config_req(Conn_t *conn);
+static int read_config_rsp(Conn_t *conn, char *buff, int buffsize);
+static int parse_config_rsp(unsigned char *buff, int size);
+
+static int send_join_req(Conn_t *conn);
+static int read_join_rsp(char *buff, int buffsize);
+static int parse_join_rsp(unsigned char *buff, int size);
+
+static int get_bus_addr(struct sockaddr_atmsvc *addr);
+static int read_bus_arp(Conn_t *conn, struct sockaddr_atmsvc *addr, char *buff, int buffsize);
+
+/*
+ * 5.1, Initial state
+ */
+void init_lec_params(unsigned char *mac_addr, char *elan_name,
+                     unsigned char *listen_addr, int itf, char *foreId,
+                     int max_frame_size, int proxy_flag, int lane_version)
+{
+        memcpy(lec_params.c6_mac_addr, mac_addr, ETH_ALEN);
+        strcpy(lec_params.c5_elan_name, elan_name);
+        memcpy(lec_params.c1n_my_atm_addr, listen_addr, ATM_ESA_LEN);
+        lec_params.itf_num = itf;
+        strcpy(lec_params.foreId, foreId);
+        lec_params.c3_max_frame_size = max_frame_size;
+        lec_params.c4_proxy_flag = proxy_flag;
+
+        if (lane_version > 1 ) lec_params.c29_v2_capable = 1;
+        else lec_params.c29_v2_capable = 0;
+        
+        /* then come the defaults */
+        lec_params.c2_lan_type = 0x01; /* Ethernet/IEEE 802.3 */
+        if (lec_params.c29_v2_capable) {
+                lec_params.c7_ctrl_timeout = 30;
+                lec_params.c10_max_unknown_frames = 10;
+        }
+        else {
+                lec_params.c7_ctrl_timeout = 10;
+                lec_params.c10_max_unknown_frames = 1;
+        }
+        lec_params.c7i_initial_ctrl_timeout = 5;
+        lec_params.c7x_timeout_multiplier = 2;
+        lec_params.c7c_current_timeout = lec_params.c7i_initial_ctrl_timeout;
+        lec_params.c11_max_unknown_frame_time = 1;
+        lec_params.c12_vcc_timeout            = 1200;
+        lec_params.c13_max_retry_count        = 2;
+        lec_params.c14_lec_id                 = 0;
+        lec_params.c17_aging_time             = 300;
+        lec_params.c18_forward_delay_time     = 15;
+        lec_params.c19_topology_change        = 0;
+        lec_params.c20_le_arp_response_time   = 1;
+        lec_params.c21_flush_timeout          = 4;
+        lec_params.c22_path_switching_delay   = 6;
+        /* LANE2 added the following, only the ones used are listed */
+        memset(lec_params.c35_preferred_les, 0, ATM_ESA_LEN);
+        lec_params.c35_contains_address       = 0;
+        lec_params.c37_min_reconfig_delay     = 1;    /* milliseconds */
+        lec_params.c38_max_reconfig_delay     = 5000; /* milliseconds */
+
+        if (lec_params.tlvs != NULL) free (lec_params.tlvs);
+        lec_params.tlvs       = NULL;
+        lec_params.sizeoftlvs = 0;
+        lec_params.num_tlvs   = 0;
+
+        return;
+}
+
+/* ------------- Configure phase specific stuff starts ------------- */
+
+/*
+ * 5.2 LECS connect phase
+ * Returns < 0 for error
+ */
+int lec_configure(int lecs_method, struct sockaddr_atmsvc *manual_atm_addr,
+                  struct sockaddr_atmsvc *listen_addr)
+{
+        int retval;
+        struct sockaddr_atmsvc addr_c5, addr_47;
+        struct atm_sap sap;
+        struct atm_qos qos;
+        Conn_t *conn;
+        
+        diag(COMPONENT, DIAG_DEBUG, "entering lec_configure\n");
+        
+        /* initialize well known LECS addresses */
+        memset(&addr_c5, 0, sizeof(struct sockaddr_atmsvc));
+        memset(&addr_47, 0, sizeof(struct sockaddr_atmsvc));
+        addr_c5.sas_family = addr_47.sas_family = AF_ATMSVC;
+        memcpy(addr_c5.sas_addr.prv, well_known_lecs, ATM_ESA_LEN);
+        memcpy(addr_47.sas_addr.prv, well_known_lecs, ATM_ESA_LEN);
+        addr_c5.sas_addr.prv[0] = 0xC5;
+
+        /* see if the user wants to skip LECS */
+        if (lecs_method == LECS_NONE)
+                return 0;
+        
+        init_conn_params(&sap, &qos, CONTROL_CONN);
+
+        while(1) {
+                if (lecs_method == LECS_MANUAL) {
+                        diag(COMPONENT, DIAG_DEBUG, "trying manual LECS address\n");
+                        conn = setup_svc(manual_atm_addr, listen_addr, &sap, &qos);
+                        if (conn) {
+                                retval = do_lec_configure(conn);
+                                close_connection(conn);
+                                return retval;
+                        }
+                        else random_delay();
+                }
+                diag(COMPONENT, DIAG_DEBUG, "trying well-known anycast LECS address\n");
+                conn = setup_svc(&addr_c5, listen_addr, &sap, &qos);
+                if (conn) {
+                        retval = do_lec_configure(conn);
+                        close_connection(conn);
+                        return retval;
+                }
+                else random_delay();
+                diag(COMPONENT, DIAG_DEBUG, "trying well-known LECS address\n");
+                conn = setup_svc(&addr_47, listen_addr, &sap, &qos);
+                if (conn) {
+                        retval = do_lec_configure(conn);
+                        close_connection(conn);
+                        return retval;
+                }
+                else random_delay();
+        }
+        
+        return -1; /* not reached */
+}
+
+/*
+ * 5.3 Configuration phase, get configuration from LECS
+ * Returns < 0 for error
+ */
+static int do_lec_configure(Conn_t *conn)
+{
+        int frame_size = 0;
+        char buff[MAX_CTRL_FRAME];
+
+        lec_params.c7c_current_timeout = lec_params.c7i_initial_ctrl_timeout; /* reset it */
+        while (lec_params.c7c_current_timeout <= lec_params.c7_ctrl_timeout) {
+                if (send_config_req(conn) < 0) return -1;
+                frame_size = read_config_rsp(conn, buff, sizeof(buff));
+                if (frame_size < 0) return -1;
+                if (frame_size == 0) /* timeout */
+                        lec_params.c7c_current_timeout *= lec_params.c7x_timeout_multiplier;
+                else
+                        break; /* frame in */
+        }
+        lec_params.c7c_current_timeout = lec_params.c7i_initial_ctrl_timeout; /* reset it */
+        if (frame_size == 0) {
+                diag(COMPONENT, DIAG_INFO, "Timed out while waiting for LE_CONFIGURE_RESPONSE\n");
+                return -1; /* timeout */
+        }
+
+        if (parse_config_rsp(buff, frame_size) < 0) {
+                diag(COMPONENT, DIAG_ERROR, "Parsing LE_CONFIG_RESPONSE indicates failure\n");
+                return -1;
+        }
+
+        return 0;
+}
+
+/*
+ * Compose a LE Config request frame and send it to LECS
+ * Returns < 0 for error
+ */
+static int send_config_req(Conn_t *conn)
+{
+        int frame_size;
+        struct ctrl_frame *config_req;
+        char buff[MAX_CTRL_FRAME];
+
+        diag(COMPONENT, DIAG_DEBUG, "Sending LE_CONFIGURE_REQUEST\n");
+
+        /* TLVs make config frame variable length */
+        frame_size = sizeof(struct ctrl_frame);
+        if (lec_params.c3_max_frame_size == MTU_1580) frame_size += 5;
+        config_req = (struct ctrl_frame *)buff;
+        memset(config_req, 0, frame_size);
+
+        prefill_frame(config_req, LE_CONFIG_REQ);
+        if (lec_params.c29_v2_capable)
+                config_req->header.flags = htons(V2_CAPABLE);
+        config_req->src_lan_dst.tag = htons(LAN_DST_MAC_ADDR);
+        memcpy(config_req->src_lan_dst.mac, lec_params.c6_mac_addr, ETH_ALEN);
+        memcpy(config_req->src_atm_addr, lec_params.c1n_my_atm_addr, ATM_ESA_LEN);
+        config_req->lan_type = lec_params.c2_lan_type;
+        config_req->max_frame_size = lec_params.c3_max_frame_size;
+        config_req->elan_name_size = strlen(lec_params.c5_elan_name);
+        if (strlen(lec_params.c5_elan_name) > 0)
+                strcpy(config_req->elan_name, lec_params.c5_elan_name);
+        if (lec_params.c3_max_frame_size == MTU_1580) {
+                lec_params.c3_max_frame_size = MTU_1516;
+                *(uint32_t *)(config_req + 1) = htonl(X5_ADJUSTMENT);
+                config_req->num_tlvs++;
+        }
+
+        if (send_frame(conn, buff, frame_size) != frame_size)
+                return -1;
+
+        return 0;
+}
+
+/*
+ * Reads in Config frame or timeouts
+ * Returns Config frame length, < 0 for error, 0 for timeout
+ */
+static int read_config_rsp(Conn_t *conn, char *buff, int buffsize)
+{
+        int frame_size, retval;
+        struct timeval tv;
+        fd_set rfds;
+
+        tv.tv_sec = lec_params.c7c_current_timeout;
+        tv.tv_usec = 0;
+        FD_ZERO(&rfds);
+        FD_SET(conn->fd, &rfds);
+
+        retval = select(conn->fd + 1, &rfds, NULL, NULL, &tv);
+        if (retval < 0) {
+                diag(COMPONENT, DIAG_ERROR, "read_ctrl_rsp: select: %s\n",
+                     strerror(errno));
+                return retval;
+        }
+        if (retval == 0) return 0; /* timeout */
+        
+        frame_size = recv_frame(conn, buff, buffsize);
+        if (frame_size < 0) {
+                diag(COMPONENT, DIAG_ERROR, "read_ctrl_rsp: recv_frame: %s\n",
+                     strerror(errno));
+                return frame_size;
+        }
+        if (frame_size == 0) {
+                diag(COMPONENT, DIAG_ERROR, "read_ctrl_rsp: conn closed: %s\n",
+                     strerror(errno));
+                return -1;
+        }
+
+        return frame_size;
+}
+
+/* Validates and parses a LE_CONFIGURE_RESPONSE.
+ * See LANEv2, 5.3.x
+ * Returns < 0 for error
+ */
+static int parse_config_rsp(unsigned char *buff, int size)
+{
+        struct ctrl_frame *frame;
+
+        diag(COMPONENT, DIAG_DEBUG, "Parsing LE_CONFIG_RESPONSE\n");
+        if (validate_frame(buff, size) < 0) {
+                diag(COMPONENT, DIAG_ERROR, "parse_config_rsp: bad frame\n");
+                return -1;
+        }
+
+        frame = (struct ctrl_frame *)buff;
+        if (frame->header.opcode != htons(LE_CONFIG_RSP)) {
+                diag(COMPONENT, DIAG_ERROR, "parse_config_rsp: not a LE_CONFIG_RESPONSE\n");
+                return -1;
+        }
+        
+        if (frame->header.status != 0) {
+                diag(COMPONENT, DIAG_ERROR, "LECS said: %s\n",
+                     status2text(frame->header.status));
+                return -1;
+        }
+        if (frame->elan_name_size > 32) return -1;
+
+        /* looks good, now extract the information */
+        lec_params.c2_lan_type = frame->lan_type;
+        lec_params.c3_max_frame_size = frame->max_frame_size;
+        if (frame->elan_name_size != 0)
+                strncpy(lec_params.c5_elan_name, frame->elan_name, frame->elan_name_size);
+        lec_params.c5_elan_name[frame->elan_name_size] = '\0';
+        memcpy(lec_params.c9_les_atm_addr, frame->target_atm_addr, ATM_ESA_LEN);
+
+        parse_tlvs(frame->header.opcode, buff + sizeof(struct ctrl_frame),
+                   frame->num_tlvs, size - sizeof(struct ctrl_frame));
+
+        return 0;
+}
+
+/* ------------- Configure phase specific stuff ends ------------- */
+
+
+/* -------------- Join phase specific stuff starts --------------- */
+
+/*
+ * 5.4 Join phase
+ * Create control direct VCC and accept possible control distribute VCC
+ * Returns < 0 for error
+ */
+int les_connect(int lecs_method, struct sockaddr_atmsvc *manual_atm_addr,
+                  struct sockaddr_atmsvc *listen_addr)
+{
+        struct sockaddr_atmsvc les_addr;
+        struct atm_sap sap;
+        struct atm_qos qos;
+        char buff[MAX_CTRL_FRAME];
+        int frame_size = 0; /* shut up, GCC */
+
+        diag(COMPONENT, DIAG_DEBUG, "Entering Join phase\n");
+
+        if (lecs_method == LECS_NONE) {
+                diag(COMPONENT, DIAG_DEBUG, "Skipping LECS, connecting straight to LES\n");
+                memcpy(les_addr.sas_addr.prv, manual_atm_addr->sas_addr.prv, ATM_ESA_LEN);
+        } else memcpy(les_addr.sas_addr.prv, lec_params.c9_les_atm_addr, ATM_ESA_LEN);
+
+        init_conn_params(&sap, &qos, CONTROL_CONN);
+        lec_params.ctrl_direct = setup_svc(&les_addr, listen_addr, &sap, &qos);
+        if (lec_params.ctrl_direct == NULL) {
+                diag(COMPONENT, DIAG_ERROR, "Control direct SVC failed\n");
+                random_delay();
+                return -1;
+        }
+        lec_params.ctrl_listen = create_listensocket(listen_addr, &sap, &qos);
+        if (lec_params.ctrl_listen == NULL) {
+                diag(COMPONENT, DIAG_ERROR, "Control distribute listen socket failed\n");
+                random_delay();
+                return -1;
+        }
+
+        lec_params.c7c_current_timeout = lec_params.c7i_initial_ctrl_timeout; /* reset it */
+        while (lec_params.c7c_current_timeout <= lec_params.c7_ctrl_timeout) {
+                if (send_join_req(lec_params.ctrl_direct) < 0) {
+                        diag(COMPONENT, DIAG_ERROR, "Sending LE_JOIN_REQUEST failed\n");
+                        random_delay();
+                        return -1;
+                }
+                frame_size = read_join_rsp(buff, sizeof(buff));
+                if (frame_size < 0) {
+                        diag(COMPONENT, DIAG_ERROR, "Receiving LE_JOIN_RESPONSE failed\n");
+                        random_delay();
+                        return -1;
+                } else if (frame_size == 0) /* timeout */
+                        lec_params.c7c_current_timeout *= lec_params.c7x_timeout_multiplier;
+                else
+                        break; /* frame in */
+        }
+        lec_params.c7c_current_timeout = lec_params.c7i_initial_ctrl_timeout; /* reset it */
+        if (frame_size == 0) {
+                diag(COMPONENT, DIAG_ERROR, "LE_JOIN_RESPONSE timed out\n");
+                return -1;
+        }
+        
+        if (parse_join_rsp(buff, frame_size) < 0) {
+                diag(COMPONENT, DIAG_ERROR, "Parsing LE_JOIN_RESPONSE failed\n");
+                return -1;
+        }
+
+        return 0;
+}
+
+/* 5.4.1.2 Transmitting LE_JOIN_REQUEST
+ * Note that the TLVs should be the same as in LE_CONFIG_REQUEST
+ * excluding X5-Adjustment and Preferred-LES
+ */
+static int send_join_req(Conn_t *conn)
+{
+        char buff[MAX_CTRL_FRAME], *tlvp;
+        struct ctrl_frame *frame;
+        int frame_size;
+
+        frame_size = sizeof(struct ctrl_frame);
+        if (lec_params.c3_max_frame_size == 5) frame_size += 5;
+        if (lec_params.c35_contains_address) frame_size += (5 + ATM_ESA_LEN);
+        if (strlen(lec_params.foreId) > 0) frame_size += (5 + strlen(lec_params.foreId));
+
+        frame = (struct ctrl_frame *)buff;
+        tlvp = (char *)(frame + 1);
+        prefill_frame(frame, LE_JOIN_REQ);
+        frame->lan_type = lec_params.c2_lan_type;
+        if (lec_params.c4_proxy_flag) frame->header.flags |= htons(PROXY_FLAG);
+        frame->src_lan_dst.tag = htons(LAN_DST_MAC_ADDR);
+        strcpy(frame->elan_name, lec_params.c5_elan_name);
+        frame->elan_name_size = strlen(lec_params.c5_elan_name);
+        if (lec_params.c29_v2_capable) frame->header.flags |= htons(V2_CAPABLE);
+        memcpy(frame->src_atm_addr, lec_params.c1n_my_atm_addr, ATM_ESA_LEN);
+        memcpy(frame->src_lan_dst.mac, lec_params.c6_mac_addr, ETH_ALEN);
+        frame->max_frame_size = lec_params.c3_max_frame_size;
+        if (lec_params.c3_max_frame_size == 5) {
+                frame->max_frame_size = 1;
+                *(uint32_t *)tlvp = htonl(X5_ADJUSTMENT);
+                tlvp += 4; *tlvp = 0; tlvp++;
+                frame->num_tlvs++;
+        }
+        if (lec_params.c35_contains_address) {
+                *(uint32_t *)tlvp = htonl(PREFERRED_LES);
+                tlvp +=4; *tlvp = (uint8_t)ATM_ESA_LEN; tlvp++;
+                memcpy(tlvp, lec_params.c35_preferred_les, ATM_ESA_LEN);
+                tlvp += 20;
+                frame->num_tlvs++;
+        }
+        if (strlen(lec_params.foreId) > 0) {
+                *(uint32_t *)tlvp = htonl(FORE_NAME);
+                tlvp +=4; *tlvp = (uint8_t)strlen(lec_params.foreId); tlvp++;
+                memcpy(tlvp, lec_params.foreId, strlen(lec_params.foreId));
+                tlvp += strlen(lec_params.foreId);
+                frame->num_tlvs++;
+        }
+
+        if (send_frame(conn, frame, frame_size) != frame_size)
+                return -1;
+
+        return 0;
+}
+
+/* LE_JOIN_RESPONSE can either come over ctrl_direct or ctrl_dist.
+ * However, if LES uses Control Direct VCC, we can ignore connections
+ * to ctrl_listen and if it connects to ctrl_listen we can wait
+ * the reponse to arrive that route. Simple :)
+ * 
+ * Returns < 0 for error, 0 for timeout, > 0 for frame size
+ */
+static int read_join_rsp(char *buff, int buffsize)
+{
+        Conn_t *dist;
+        int n, retval, frame_size = 0;
+        struct timeval tv;
+        fd_set rfds;
+
+        /* Idea here is always to listen for two sockets. One of the
+           sockets is always Control Direct and the other is either
+           listen socket for Control Distribute or the Control
+           Distribute itself. We can do it like this, since listen
+           socket gets closed as soon as it creates a new connection
+        */
+
+        dist = (lec_params.ctrl_listen != NULL) ? lec_params.ctrl_listen : lec_params.ctrl_dist;
+        n = (lec_params.ctrl_direct->fd > dist->fd) ? lec_params.ctrl_direct->fd : dist->fd;
+        n++;
+
+        tv.tv_sec = lec_params.c7c_current_timeout;
+        tv.tv_usec = 0;
+        FD_ZERO(&rfds);
+        FD_SET(lec_params.ctrl_direct->fd, &rfds);
+        FD_SET(dist->fd, &rfds);
+        
+        retval = select(n, &rfds, NULL, NULL, &tv);
+        if (retval < 0) {
+                diag(COMPONENT, DIAG_ERROR, "get_join_rsp: select: %s\n",
+                     strerror(errno));
+                return -1;
+        }
+        if (retval == 0) return 0; /* timeout */
+
+        /* Be careful here. The both sockets might be readable
+         * and the response can come from either one
+         */
+        if (FD_ISSET(lec_params.ctrl_direct->fd, &rfds)) {
+                /* Control Direct was changed */
+                frame_size = recv_frame(lec_params.ctrl_direct, buff, buffsize);
+                if (frame_size < 0) {
+                        diag(COMPONENT, DIAG_ERROR, "get_join_rsp: recv_frame: %s\n",
+                             strerror(errno));
+                        return -1;
+                }
+                if (frame_size == 0) {
+                        diag(COMPONENT, DIAG_ERROR, "get_join_rsp: Control direct VCC closed\n");
+                        return -1;
+                }
+                diag(COMPONENT, DIAG_DEBUG, "LE_JOIN_RESPONSE over Control direct VCC\n");
+        }
+        if (FD_ISSET(dist->fd, &rfds)) {
+                /* Event in listen socket or Control Distribute */
+                if (dist == lec_params.ctrl_listen) {
+                        /* Connection to control listen */
+                        lec_params.ctrl_dist = accept_conn(lec_params.ctrl_listen);
+                        if (lec_params.ctrl_dist == NULL) {
+                                diag(COMPONENT, DIAG_ERROR, "accept of Ctrl distribute failed\n");
+                                return -1;
+                        }
+                        diag(COMPONENT, DIAG_DEBUG, "Closing listen socket for Ctrl distribute VCC\n");
+                        close_connection(lec_params.ctrl_listen);
+                        lec_params.ctrl_listen = NULL;
+
+                        /* let's see if this new socket has something for us */
+                        return (read_join_rsp(buff, buffsize));
+                }
+                /* Event in Control distribute */
+                frame_size = recv_frame(lec_params.ctrl_dist, buff, buffsize);
+                if (frame_size < 0) {
+                        diag(COMPONENT, DIAG_ERROR, "get_join_rsp: recv_frame: %s\n",
+                             strerror(errno));
+                        return -1;
+                }
+                if (frame_size == 0) {
+                        diag(COMPONENT, DIAG_ERROR, "Control distribute VCC closed\n");
+                        return -1;
+                }
+                diag(COMPONENT, DIAG_DEBUG, "LE_JOIN_RESPONSE over Control distribute VCC\n");
+        }
+
+        return frame_size;
+}
+
+/* Validates and parses a LE_JOIN_RESPONSE.
+ * See LANEv2 5.4.x
+ * Returns < 0 for error
+ */
+static int parse_join_rsp(unsigned char *buff, int size)
+{
+        struct ctrl_frame *frame;
+
+        diag(COMPONENT, DIAG_DEBUG, "Parsing LE_JOIN_RESPONSE\n");
+        if (validate_frame(buff, size) < 0)
+                return -1;
+
+        frame = (struct ctrl_frame *)buff;
+        if (frame->header.opcode != htons(LE_JOIN_RSP))
+                return -1;
+        
+        if (frame->header.status != 0) {
+                diag(COMPONENT, DIAG_ERROR, "LES said: %s\n",
+                     status2text(frame->header.status));
+                return -1;
+        }
+        if (frame->elan_name_size > 32) return -1;
+
+        /* looks good, now extract the information */
+        lec_params.c2_lan_type = frame->lan_type;
+        lec_params.c3_max_frame_size = frame->max_frame_size;
+        if (frame->elan_name_size != 0)
+                strncpy(lec_params.c5_elan_name, frame->elan_name, frame->elan_name_size);
+        lec_params.c5_elan_name[frame->elan_name_size] = '\0';
+        lec_params.c14_lec_id = ntohs(frame->header.lec_id);
+
+        if (!(frame->header.flags & htons(V2_REQUIRED)) && lec_params.c29_v2_capable) {
+                diag(COMPONENT, DIAG_INFO, "LES did not return V2 Required Flag, acting as V1 client\n");
+                lec_params.c29_v2_capable = 0;
+        }
+        if (!(frame->header.flags & htons(V2_REQUIRED)) &&
+            (lec_params.c3_max_frame_size == MTU_1580)) {
+                /* Against spec, but we'll accept the MTU and clear the flag */
+                diag(COMPONENT, DIAG_ERROR, "LES not LANEv2 but uses MTU of 1580 bytes\n");
+                lec_params.c29_v2_capable = 0;
+        }
+        parse_tlvs(frame->header.opcode, buff + sizeof(struct ctrl_frame),
+                   frame->num_tlvs, size - sizeof(struct ctrl_frame));
+
+        return 0;
+}
+
+/* --------------- Join phase specific stuff ends ---------------- */
+
+
+/* -------------- Bus connect specific stuff starts --------------- */
+
+int bus_connect(void)
+{
+        struct ctrl_frame *frame;
+        char buff[MAX_CTRL_FRAME];
+        struct sockaddr_atmsvc bus_addr, listen_addr;
+        int retval, tries, n;
+        struct atm_sap sap;
+        struct atm_qos qos;
+        struct timeval tv;
+        fd_set rfds;
+        Conn_t *mcast_fwd;
+        struct atmlec_ioc ioc_data;
+
+        frame = (struct ctrl_frame *)buff;
+        memset(&bus_addr, 0, sizeof(struct sockaddr_atmsvc));
+        memset(&listen_addr, 0, sizeof(struct sockaddr_atmsvc));
+        
+        /* try to arp BUS two times */
+        tries = 2;
+        while (tries > 0) {
+                prefill_frame(frame, LE_ARP_REQ);
+                frame->header.lec_id = htons(lec_params.c14_lec_id);
+                frame->src_lan_dst.tag = htons(LAN_DST_MAC_ADDR);
+                memcpy(frame->src_lan_dst.mac, lec_params.c6_mac_addr, ETH_ALEN);
+                memcpy(frame->src_atm_addr, lec_params.c1n_my_atm_addr, ATM_ESA_LEN);
+                frame->target_lan_dst.tag = htons(LAN_DST_MAC_ADDR);
+                memcpy(frame->target_lan_dst.mac, bus_mac, ETH_ALEN);
+                retval = send_frame(lec_params.ctrl_direct, frame, sizeof(struct ctrl_frame));
+                if (retval < 0) {
+                        diag(COMPONENT, DIAG_ERROR, "LE_ARP_REQUEST for BUS failed\n");
+                        return -1;
+                }
+                retval = get_bus_addr(&bus_addr);
+                if (retval < 0) {
+                        diag(COMPONENT, DIAG_ERROR, "LE_ARP_RESPONSE for BUS failed\n");
+                        return -1;
+                } else if (retval > 0)
+                        break; /* got it */
+                tries--;
+        }
+        if (tries == 0) {
+                diag(COMPONENT, DIAG_ERROR, "LE_ARP_RESPONSE for BUS timed out\n");
+                return -1;
+        }
+
+        /* We got address for BUS. Make the listen socket for Multicast
+         * Forward first and then contact BUS.
+         */
+        memcpy(listen_addr.sas_addr.prv, lec_params.c1n_my_atm_addr, ATM_ESA_LEN);
+        listen_addr.sas_family = bus_addr.sas_family = AF_ATMSVC;
+        init_conn_params(&sap, &qos, MCAST_CONN);
+        lec_params.mcast_listen = create_listensocket(&listen_addr, &sap, &qos);
+        if (lec_params.mcast_listen == NULL) {
+                diag(COMPONENT, DIAG_ERROR, "Listen socket for BUS failed\n");
+                return -1;
+        }
+
+        lec_params.mcast_send = setup_svc(&bus_addr, &listen_addr, &sap, &qos);
+        if (lec_params.mcast_send == NULL) {
+                diag(COMPONENT, DIAG_ERROR, "Connect to BUS failed\n");
+                return -1;
+        }
+        /* Default Multicast send VCC to BUS ready, notify kernel */
+        if (ioctl(lec_params.mcast_send->fd, ATMLEC_MCAST, lec_params.itf_num) < 0) {
+                diag(COMPONENT, DIAG_FATAL, "Can't change socket into LE mcast socket: %s\n", strerror(errno));
+                return -1;
+        }
+
+        diag(COMPONENT, DIAG_DEBUG, "About to wait for BUS to connect\n");
+        tv.tv_sec = lec_params.c7_ctrl_timeout;
+        tv.tv_usec = 0;
+        FD_ZERO(&rfds);
+        FD_SET(lec_params.mcast_listen->fd, &rfds);
+        n = lec_params.mcast_listen->fd + 1;
+        retval = select(n, &rfds, NULL, NULL, &tv);
+        if (retval == 0) {
+                diag(COMPONENT, DIAG_ERROR, "BUS connect to Multicast Forward listen socket timed out\n");
+                return -1;
+        }
+        if (retval < 0) {
+                diag(COMPONENT, DIAG_ERROR, "while waiting for Multicast Forward VCC: select: %s\n",
+                     strerror(errno));
+                return -1;
+        }
+        mcast_fwd = accept_conn(lec_params.mcast_listen);
+        if (mcast_fwd == NULL) {
+                diag(COMPONENT, DIAG_ERROR, "BUS connect to Multicast Forward listen socket failed\n");
+                return -1;
+        }
+        
+        memcpy(ioc_data.atm_addr, mcast_fwd->atm_address, ATM_ESA_LEN);
+        ioc_data.dev_num = lec_params.itf_num;
+        ioc_data.receive = 2; /* Multicast distribute */
+        diag(COMPONENT, DIAG_DEBUG, "About to notify kernel about Multicast Forward VCC\n");
+        if (ioctl(mcast_fwd->fd, ATMLEC_DATA, &ioc_data) < 0) {
+                diag(COMPONENT, DIAG_DEBUG, "Could not notify kernel: %s\n", strerror(errno));
+                return -1;
+        }
+
+        /* All done. We're in! */
+        return 0;
+}
+
+/*
+ * Waits for LE_ARP_RESPONSE for BUS' ATM address to arrive.
+ * Returns < 0 for error, 0 for timeout > 0 for success
+ * BUS ATM address will be stored in *addr */
+static int get_bus_addr(struct sockaddr_atmsvc *addr)
+{
+
+        fd_set rfds;
+        struct timeval tv;
+        int n = 0, retval, timeout;
+        char buff[MAX_CTRL_FRAME];
+
+        timeout = 4; /* wait response for 4 seconds */
+        lec_params.c7c_current_timeout = 1;
+        while (lec_params.c7c_current_timeout <= timeout) {
+                tv.tv_sec = lec_params.c7c_current_timeout; /* actually not specified exactly */
+                tv.tv_usec = 0;
+                FD_ZERO(&rfds);
+                FD_SET(lec_params.ctrl_direct->fd, &rfds);
+                if (lec_params.ctrl_dist != NULL) {
+                        FD_SET(lec_params.ctrl_dist->fd, &rfds);
+                        n = lec_params.ctrl_dist->fd;
+                }
+                n = (lec_params.ctrl_direct->fd > n) ? lec_params.ctrl_direct->fd : n;
+                n++;
+                
+                retval = select(n, &rfds, NULL, NULL, &tv);
+                if (retval == 0) {
+                        lec_params.c7c_current_timeout++;
+                        continue; /* back to waiting */
+                }
+                if (retval < 0) {
+                        diag(COMPONENT, DIAG_ERROR, "get_bus_addr: select: %s\n", strerror(errno));
+                        return -1;
+                }
+                if (FD_ISSET(lec_params.ctrl_direct->fd, &rfds)) {
+                        diag(COMPONENT, DIAG_DEBUG, "get_bus_addr: ctrl.direct changed:\n");
+                        retval = read_bus_arp(lec_params.ctrl_direct, addr, buff, sizeof(buff));
+                        if (retval < 0) return -1;
+                        if (retval > 0) return retval;
+                }
+                if (lec_params.ctrl_dist != NULL && FD_ISSET(lec_params.ctrl_dist->fd, &rfds)) {
+                        diag(COMPONENT, DIAG_DEBUG, "get_bus_addr: ctrl.dist changed:\n");
+                        retval = read_bus_arp(lec_params.ctrl_dist, addr, buff, sizeof(buff));
+                        if (retval < 0) return -1;
+                        if (retval > 0) return retval;
+                }
+                diag(COMPONENT, DIAG_DEBUG, "get_bus_addr: consumed a packet\n");
+        }
+
+        diag(COMPONENT, DIAG_ERROR, "Timeout while waiting for BUS LE_ARP response\n");
+        return 0;
+}
+/*
+ * Tries to read BUS ATM address in *addr
+ * returns < 0 for error, 0 for not found > 0 for success
+ */
+static int read_bus_arp(Conn_t *conn, struct sockaddr_atmsvc *addr, char *buff, int buffsize)
+{
+        int frame_size;
+        struct ctrl_frame *frame;
+
+        frame_size = recv_frame(conn, buff, buffsize);
+        if (frame_size == 0) {
+                diag(COMPONENT, DIAG_ERROR, "LES Control connection closed\n");
+                return -1;
+        }
+        if (frame_size < 0) {
+                diag(COMPONENT, DIAG_ERROR, "get_bus_arp: recv_frame: %s\n", strerror(errno));
+                return -1;
+        }
+        frame = (struct ctrl_frame *)buff;
+        if (validate_frame(buff, frame_size) >= 0 &&
+            frame->header.opcode == htons(LE_ARP_RSP) &&
+            memcmp(frame->src_lan_dst.mac, lec_params.c6_mac_addr, ETH_ALEN) == 0) {
+                memcpy(addr->sas_addr.prv, frame->target_atm_addr, ATM_ESA_LEN);
+                return frame_size;
+        }
+
+        return 0; /* not found */
+}
+
+/* --------------- Bus connect specific stuff ends --------------- */
diff -ur --new-file old/atm/led.new/join.h new/atm/led.new/join.h
--- old/atm/led.new/join.h	Thu Jan  1 01:00:00 1970
+++ new/atm/led.new/join.h	Sat Feb  6 21:20:44 1999
@@ -0,0 +1,20 @@
+/* Copyright (C) 1999 Heikki Vatiainen hessu@cs.tut.fi */
+
+#ifndef JOIN_H
+#define JOIN_H
+
+void init_lec_params(unsigned char *mac_addr, char *elan_name,
+                     unsigned char *listen_addr, int itf, char *foreId,
+                     int max_frame_size, int proxy_flag, int lane_version);
+int lec_configure(int lecs_method, struct sockaddr_atmsvc *manual_atm_addr,
+                  struct sockaddr_atmsvc *listen_addr);
+int les_connect(int lecs_method, struct sockaddr_atmsvc *manual_atm_addr,
+                struct sockaddr_atmsvc *listen_addr);
+int bus_connect(void);
+
+/* Different ways to contact LECS */
+#define LECS_NONE      0
+#define LECS_WELLKNOWN 1
+#define LECS_MANUAL    2
+
+#endif /* JOIN_H */
diff -ur --new-file old/atm/led.new/kernel.c new/atm/led.new/kernel.c
--- old/atm/led.new/kernel.c	Thu Jan  1 01:00:00 1970
+++ new/atm/led.new/kernel.c	Sun Feb  7 12:11:57 1999
@@ -0,0 +1,327 @@
+/* kernel.c - send and receive messages from kernel and act accordingly */
+
+/*
+ * Marko Kiiskila carnil@cs.tut.fi 
+ * 
+ * Copyright (c) 1996
+ * Tampere University of Technology - Telecommunications Laboratory
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this
+ * software and its documentation is hereby granted,
+ * provided that both the copyright notice and this
+ * permission notice appear in all copies of the software,
+ * derivative works or modified versions, and any portions
+ * thereof, that both notices appear in supporting
+ * documentation, and that the use of this software is
+ * acknowledged in any publications resulting from using
+ * the software.
+ * 
+ * TUT ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION AND DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS
+ * SOFTWARE.
+ * 
+ */
+
+/* Copyright (C) 1999 Heikki Vatiainen hessu@cs.tut.fi */
+
+/* System includes */
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* Atm includes */
+#include <atm.h>
+#include <linux/atmdev.h>
+#include <linux/atmlec.h>
+
+#include <atmd.h>
+
+/* Local includes */
+#include "kernel.h"
+#include "lec.h"
+#include "frames.h"
+
+#define COMPONENT "kernel.c"
+
+/* Local vars */
+static int lec_socket;
+
+static const char* get_mesg_type_str(atmlec_msg_type type);
+static void send_le_arp_req(unsigned char *mac_addr, int sizeoftlvs);
+static void send_proxy_arp_rsp(unsigned char *target_mac, uint32_t tran_id,
+                               uint16_t lec_id, unsigned char *src_atm_addr);
+static void send_topology_req(int flag);
+static void associate_tlvs(int sizeoftlvs);
+
+/* Notify kernel that zeppelin is present and tell
+ * kernel what MAC address this LEC uses
+ */
+int kernel_init(unsigned char *mac_addr, int itf)
+{
+        int rvalue;
+        struct atmlec_msg msg;
+
+        lec_socket = socket(PF_ATMSVC, SOCK_DGRAM, 0);
+        if (lec_socket < 0) {
+                diag(COMPONENT, DIAG_FATAL, "Kernel socket creation failed: %s\n",
+                     strerror(errno));
+                return -1;
+        }
+        itf = ioctl(lec_socket, ATMLEC_CTRL, itf);
+        if (itf < 0) {
+                diag(COMPONENT, DIAG_FATAL, "Socket ioctl to lecd_ctrl failed:%s\n",
+                     strerror(errno));
+                close(lec_socket);
+                return -1;
+        }
+        msg.type = l_set_mac_addr;
+        memcpy(msg.content.normal.mac_addr, mac_addr, ETH_ALEN);
+        
+        rvalue = write(lec_socket, &msg, sizeof(msg));
+        if (rvalue < 0) {
+                diag(COMPONENT, DIAG_FATAL, "Can't write mac address to LEC:%s\n",
+                     strerror(errno));
+                close(lec_socket);
+                return -1;
+        }
+        diag(COMPONENT, DIAG_DEBUG, "Kernel interface initialized\n");
+        
+        if (conn_set_kernel_socket(lec_socket) < 0) {
+                close(lec_socket);
+                return -1;
+        }
+        
+        return itf;
+}
+
+/* Send a message to kernel
+ * Returns < 0 for error
+ */
+int msg_to_kernel(struct atmlec_msg *msg, int msg_size)
+{
+        int retval;
+
+        diag(COMPONENT, DIAG_DEBUG, "msg_to_kernel, type %s\n", get_mesg_type_str(msg->type));
+        retval = write(lec_params.kernel->fd, msg, msg_size);
+        if (retval < 0) {
+                diag(COMPONENT, DIAG_ERROR, "msg_to_kernel: write: %s\n", strerror(errno));
+                return -1;
+        }
+        if (retval != msg_size) {
+                diag(COMPONENT, DIAG_ERROR, "msg_to_kernel: partial write\n");
+                return -1;
+        }
+
+        return retval;
+}
+
+/* Read and process message from kernel
+ * Returns < 0 for error
+ */
+int msg_from_kernel(void)
+{
+        int retval;
+        struct atmlec_msg msg;
+        int codepoint;
+
+        retval = read(lec_params.kernel->fd, &msg, sizeof(struct atmlec_msg));
+        if (retval < 0) {
+                diag(COMPONENT, DIAG_FATAL, "msg_from_kernel: read: %s\n", strerror(errno));
+                return -1;
+        }
+        if (retval != sizeof(struct atmlec_msg)) {
+                diag(COMPONENT, DIAG_ERROR, "msg_from_kernel: msg size != sizeof(atmlec_msg)\n");
+                return 0;
+        }
+
+        diag(COMPONENT, DIAG_DEBUG, "msg_from_kernel: type %s\n", get_mesg_type_str(msg.type));
+        switch (msg.type) {
+        case l_arp_xmt:
+                if (msg.content.normal.targetless_le_arp)
+                        send_le_arp_req(NULL, msg.sizeoftlvs);
+                else
+                        send_le_arp_req(msg.content.normal.mac_addr, msg.sizeoftlvs);
+                break;
+        case l_svc_setup:
+                codepoint = (msg.content.normal.mac_addr[0] & 0x01) ? MCAST_CONN : DATA_DIRECT_CONN;
+                if (create_data_svc(msg.content.normal.atm_addr, codepoint) < 0)
+                        diag(COMPONENT, DIAG_ERROR, "Data direct VCC failed\n");
+                break;
+        case l_associate_req:
+                associate_tlvs(msg.sizeoftlvs);
+                break;
+        case l_should_bridge:
+                send_proxy_arp_rsp(msg.content.proxy.mac_addr, msg.content.proxy.tran_id,
+                                   msg.content.proxy.lec_id, msg.content.proxy.atm_addr);
+                break;
+        case l_topology_change:
+                send_topology_req(msg.content.normal.flag);
+                break;
+        default:
+                diag(COMPONENT, DIAG_ERROR, "no handler for kernel msg %s\n", get_mesg_type_str(msg.type));
+                break;
+        }
+
+        return 0;
+}
+
+/* Send LE_ARP_REQUEST. If mac_addr is NULL, sends a targetless LE_ARP.
+ * If sizeoftlvs != 0, reads TLVs waiting in the kernel socket and
+ * adds them to the frame. If sizeoftlvs == 0 then the TLVs (if any)
+ * associated with this LEC are used.
+ * FIXME: add TLV count in kernel messages
+ */
+static void send_le_arp_req(unsigned char *mac_addr, int sizeoftlvs)
+{
+        struct ctrl_frame *frame;
+        int frame_size;
+        char buff[MAX_CTRL_FRAME];
+
+        diag(COMPONENT, DIAG_DEBUG, "Sending LE_ARP_REQUEST\n");
+
+        if (sizeoftlvs == 0) frame_size = sizeof(struct ctrl_frame) + lec_params.sizeoftlvs;
+        else frame_size = sizeof(struct ctrl_frame) + sizeoftlvs;
+        frame = (struct ctrl_frame *)buff;
+        memset(frame, 0, frame_size);
+
+        prefill_frame(frame, LE_ARP_REQ);
+        frame->header.lec_id = htons(lec_params.c14_lec_id);
+        frame->src_lan_dst.tag = htons(LAN_DST_MAC_ADDR);
+        memcpy(frame->src_lan_dst.mac, lec_params.c6_mac_addr, ETH_ALEN);
+        memcpy(frame->src_atm_addr, lec_params.c1n_my_atm_addr, ATM_ESA_LEN);
+        if (mac_addr != NULL) {
+                frame->target_lan_dst.tag = htons(LAN_DST_MAC_ADDR);
+                memcpy(frame->target_lan_dst.mac, mac_addr, ETH_ALEN);
+        } else diag(COMPONENT, DIAG_DEBUG, "Sending targetless LE_ARP\n");
+
+        if (sizeoftlvs != 0) {
+                if (read(lec_params.kernel->fd, (frame + 1), sizeoftlvs) < 0) {
+                        diag(COMPONENT, DIAG_ERROR, "reading TLVs from kernel failed: %s\n", strerror(errno));
+                        return;
+                }
+                frame->num_tlvs++;
+        } else if (lec_params.sizeoftlvs != 0) {
+                memcpy((frame + 1), lec_params.tlvs, lec_params.sizeoftlvs);
+                frame->num_tlvs += lec_params.num_tlvs;
+        }
+
+        send_frame(lec_params.ctrl_direct, frame, frame_size);
+
+        return;
+}
+
+/* Associate the set of TLVs available in kernel socket
+ * with this LEC. The old TLVs, if any, are removed.
+ * Also, send a LE_REGISTER_REQUEST to register the TLVs
+ * we just got.
+ */
+static void associate_tlvs(int sizeoftlvs)
+{
+
+        if (lec_params.tlvs != NULL) free (lec_params.tlvs);
+        lec_params.sizeoftlvs = 0;
+        lec_params.num_tlvs = 0;
+        lec_params.tlvs = malloc(sizeoftlvs);
+        if (lec_params.tlvs == NULL) {
+                diag(COMPONENT, DIAG_ERROR, "Could not associate TLVs, out of memory\n");
+                lec_params.sizeoftlvs = 0;
+                lec_params.num_tlvs = 0;
+                return;
+        }
+        if (read(lec_params.kernel->fd, lec_params.tlvs, sizeoftlvs) < 0) {
+                diag(COMPONENT, DIAG_ERROR, "reading TLVs from kernel failed: %s\n", strerror(errno));
+                return;
+        }
+
+        lec_params.sizeoftlvs = sizeoftlvs;
+        lec_params.num_tlvs = 1; /* FIXME, add TLV count in messages */
+
+        send_register_req();
+
+        return;
+}
+
+/* Send a LE_ARP_RESPONSE for a LAN destination (MAC address) when
+ * the LAN destination is present in kernel bridging table and we
+ * are acting as a proxy lane client
+ */
+static void send_proxy_arp_rsp(unsigned char *target_mac, uint32_t tran_id,
+                               uint16_t lec_id, unsigned char *src_atm_addr)
+{
+        struct ctrl_frame *frame;
+        int frame_size;
+
+        frame_size = sizeof(struct ctrl_frame) + lec_params.sizeoftlvs;
+        frame = malloc(frame_size);
+        if (frame == NULL) return;
+        memset(frame, 0, frame_size);
+        prefill_frame(frame, LE_ARP_RSP);
+        frame->header.tran_id = tran_id;
+        frame->header.lec_id = lec_id;
+        frame->header.flags = htons(REMOTE_ADDRESS);
+        memcpy(frame->target_atm_addr, lec_params.c1n_my_atm_addr, ATM_ESA_LEN);
+        frame->target_lan_dst.tag = htons(LAN_DST_MAC_ADDR);
+        memcpy(frame->target_lan_dst.mac, target_mac, ETH_ALEN);
+        memcpy(frame->src_atm_addr, src_atm_addr, ATM_ESA_LEN);
+        frame->num_tlvs = lec_params.num_tlvs;
+        if (lec_params.num_tlvs > 0)
+                memcpy(frame + 1, lec_params.tlvs, lec_params.sizeoftlvs);
+
+        if (send_frame(lec_params.ctrl_direct, frame, frame_size) < 0)
+                diag(COMPONENT, DIAG_ERROR, "send_proxy_arp_rsp: send_frame() failed");
+
+        return;
+}
+
+/* 7.1.25 Send a LE_TOPOLOGY_REQUEST */
+static void send_topology_req(int flag)
+{
+        struct ctrl_frame frame;
+
+        prefill_frame(&frame, LE_TOPO_REQ);
+        if (flag) frame.header.flags = htons(TOPO_CHANGE);
+
+        send_frame(lec_params.ctrl_direct, &frame, sizeof(struct ctrl_frame));
+
+        return;
+}
+
+static const char *get_mesg_type_str(atmlec_msg_type type)
+{
+        switch(type) {
+        case l_set_mac_addr:
+                return "SET_MAC_ADDR";
+        case l_del_mac_addr:
+                return "DEL_MAC_ADDR";
+        case l_svc_setup:
+                return "SVC_SETUP";
+        case l_arp_xmt:
+                return "ARP_XMT";
+        case l_addr_delete:
+                return "ADDR_DELETE";
+        case l_topology_change:
+                return "TOPOLOGY_CHANGE";
+        case l_flush_tran_id:
+                return "FLUSH_TRANSACTION_ID";
+        case l_flush_complete:
+                return "FLUSH_COMPLETE";
+        case l_arp_update:
+                return "ARP_UPDATE";
+        case l_config:
+                return "CONFIG";
+        case l_associate_req:
+                return "LANE2_ASSOCIATE_REQ";
+        case l_set_lecid:
+                return "SET_LEC_ID";
+        case l_narp_req:
+                return "LE_NARP_REQUEST";
+        case l_should_bridge:
+                return "SHOULD_BRIDGE";
+        default:
+                return "<Unknown message type>";
+        }
+}
diff -ur --new-file old/atm/led.new/kernel.h new/atm/led.new/kernel.h
--- old/atm/led.new/kernel.h	Thu Jan  1 01:00:00 1970
+++ new/atm/led.new/kernel.h	Sat Feb  6 14:45:20 1999
@@ -0,0 +1,36 @@
+/*
+ * Marko Kiiskila carnil@cs.tut.fi 
+ * 
+ * Copyright (c) 1996
+ * Tampere University of Technology - Telecommunications Laboratory
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this
+ * software and its documentation is hereby granted,
+ * provided that both the copyright notice and this
+ * permission notice appear in all copies of the software,
+ * derivative works or modified versions, and any portions
+ * thereof, that both notices appear in supporting
+ * documentation, and that the use of this software is
+ * acknowledged in any publications resulting from using
+ * the software.
+ * 
+ * TUT ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION AND DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS
+ * SOFTWARE.
+ * 
+ */
+
+/* Copyright (C) 1999 Heikki Vatiainen hessu@cs.tut.fi */
+
+#ifndef KERNEL_H
+#define KERNEL_H
+#include <linux/atmlec.h>
+
+int kernel_init(unsigned char *mac_addr, int itf);
+int msg_to_kernel(struct atmlec_msg *msg, int msg_size);
+int msg_from_kernel(void);
+int read_from_kernel (char *buff, int size);
+
+#endif /* KERNEL_H */
diff -ur --new-file old/atm/led.new/lec.h new/atm/led.new/lec.h
--- old/atm/led.new/lec.h	Thu Jan  1 01:00:00 1970
+++ new/atm/led.new/lec.h	Sat Feb  6 21:49:04 1999
@@ -0,0 +1,64 @@
+/* Copyright (C) 1999 Heikki Vatiainen hessu@cs.tut.fi */
+
+#ifndef LEC_H
+#define LEC_H
+
+#include "conn.h"
+
+/*
+ * LANE client configuration and operation values.
+ * These are in host byte order since there are
+ * some values coming from network and some values
+ * which are used by the host only
+ */
+struct lec_params {
+        unsigned char c1n_my_atm_addr[ATM_ESA_LEN];
+        uint8_t  c2_lan_type;
+        uint8_t  c3_max_frame_size;
+        int      c4_proxy_flag;
+        char     c5_elan_name[32 + 1];
+        char     c6_mac_addr[ETH_ALEN];
+        int      c7_ctrl_timeout;
+        int      c7i_initial_ctrl_timeout;
+        int      c7x_timeout_multiplier;
+        int      c7c_current_timeout; /* sum of c7i and c7x, LANEv2 5.3.1.7 */
+        unsigned char c9_les_atm_addr[ATM_ESA_LEN];
+        int      c10_max_unknown_frames;
+        int      c11_max_unknown_frame_time;
+        int      c12_vcc_timeout;
+        int      c13_max_retry_count;
+        uint16_t c14_lec_id;
+        int      c17_aging_time;
+        int      c18_forward_delay_time;
+        int      c19_topology_change;
+        int      c20_le_arp_response_time;
+        int      c21_flush_timeout;
+        int      c22_path_switching_delay;
+        /* LANE2 variables follow */
+        int      c29_v2_capable;
+        uint32_t c31_elan_id;
+        unsigned char c35_preferred_les[ATM_ESA_LEN];
+        int      c35_contains_address;
+        int      c37_min_reconfig_delay; /* milliseconds */
+        int      c38_max_reconfig_delay; /* milliseconds */
+
+        /* other stuff */
+        int  itf_num;    /* 1 for lec1 and so forth */
+        int  sizeoftlvs; /* total size of TLVs associated with this LEC */
+        int  num_tlvs;   /* number of the TLVs */
+        unsigned char *tlvs; /* the TLVs */
+        char foreId[255];
+
+        /* connections to and from LES/BUS plus listen sockets */
+        Conn_t *kernel;
+        Conn_t *ctrl_direct;
+        Conn_t *ctrl_listen; /* Closed when join phase is over */
+        Conn_t *ctrl_dist;
+        Conn_t *mcast_send;  /* LANEv2 calls this Default Mcast Send VCC */
+        Conn_t *mcast_listen;
+        Conn_t *data_listen;
+};
+
+extern struct lec_params lec_params;
+
+#endif /* LEC_H */
diff -ur --new-file old/atm/led.new/main.c new/atm/led.new/main.c
--- old/atm/led.new/main.c	Thu Jan  1 01:00:00 1970
+++ new/atm/led.new/main.c	Sun Feb  7 12:07:52 1999
@@ -0,0 +1,451 @@
+/* main.c - Do what ever a LANE client does */
+
+/*
+ * Marko Kiiskila carnil@cs.tut.fi 
+ * 
+ * Copyright (c) 1996
+ * Tampere University of Technology - Telecommunications Laboratory
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this
+ * software and its documentation is hereby granted,
+ * provided that both the copyright notice and this
+ * permission notice appear in all copies of the software,
+ * derivative works or modified versions, and any portions
+ * thereof, that both notices appear in supporting
+ * documentation, and that the use of this software is
+ * acknowledged in any publications resulting from using
+ * the software.
+ * 
+ * TUT ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION AND DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS
+ * SOFTWARE.
+ * 
+ */
+
+/* Copyright (C) 1999 Heikki Vatiainen hessu@cs.tut.fi */
+
+/* Global includes */
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+
+#include <atm.h>
+#include <atmd.h>
+
+#include <linux/atmlec.h>
+
+/* Local incs */
+#include "join.h"
+#include "lec.h"
+#include "address.h"
+#include "display.h"
+#include "kernel.h"
+
+#define COMPONENT "main.c"
+
+static void main_loop(void);
+static int reset = 0;
+
+void sig_reset(int a)
+{
+        reset = 1;  
+
+        return;
+}
+
+static void usage(const char *progname)
+{
+  printf("Usage: %s [-c LECS_address | -s LES_address] [-e esi] [-n VLAN_name]"
+    " [-m mesg_mask] [-l listen_address] [-i interface_number]"
+    " [-t 1516|1580|4544|9234|18190] [-1] [-2] [-p] [-F logfile]"
+    " [-f Fore specific name]\n", progname);
+}
+
+/*
+ * My First C function (TM), hessu@cs.tut.fi
+ */
+static int esi_convert(char *parsestring, unsigned char *mac_addr)
+{
+        const char *hexchars = "abcdefABCDEF0123456789";
+        char hexnum [17+1], curr;
+        int i = 0, j = -1, hexindex = 0, tmp;
+        char *k, *string;
+        
+        if (strchr(parsestring,'.') ||     /* do we have separators like */
+            strchr(parsestring,':')) {     /* 00:20:22:23:04:05 */
+                k = parsestring;
+                for (i = 0; i < strlen(parsestring); i++) {
+                        curr = *k;
+                        if (curr == ':' || curr == '.') {   /* separator ? */
+                                if (i - j == 3) {  /* there were 2 hex characters */
+                                        ;
+                                }
+                                else if (i - j == 2) {  /* there was only 1 hex char */
+                                        hexnum [hexindex] = hexnum [hexindex-1];
+                                        hexnum [hexindex-1] = '0';
+                                        hexindex +=1;
+                                }
+                                else   /* too many hexchars in a byte */
+                                        return -1;
+                                j = i;  /* j is the location of the last separator */
+                        }
+                        else if (strchr(hexchars, curr) == NULL) /* not a hexchar ? */
+                                return -1;
+                        else {  /* we have a hex character */
+                                hexnum [hexindex] = curr;
+                                hexindex +=1;
+                        }
+                        k++;
+                }
+                hexnum [hexindex] = '\0';
+                string = hexnum;
+        } else {   /* no separators */
+                k = parsestring;
+                while (*k != '\0') {
+                        if (strchr(hexchars, *k) == NULL)
+                                return -1;
+                        k++;
+                }
+                
+                string = parsestring;
+        }
+        
+        /* the esi now looks like 002022230405 */
+        i = strlen(string);
+        if (i != 12)
+                return -1;
+        for(i=0; i<6; i++) {
+                sscanf(&string[i*2], "%2x", &tmp);
+                mac_addr[i] = (unsigned char)tmp;
+        }
+        return 0;
+}
+
+/* Tells kernel what our LEC_ID is.
+ * Returns < 0 for serisous error
+ */
+static int set_lec_id(uint16_t lec_id)
+{
+        struct atmlec_msg msg;
+
+        memset(&msg, 0, sizeof(struct atmlec_msg));
+        msg.type = l_set_lecid;
+        msg.content.normal.flag = lec_id;
+        if (msg_to_kernel(&msg, sizeof(struct atmlec_msg)) < 0) {
+                diag(COMPONENT, DIAG_ERROR, "Could not tell kernel LEC_ID\n");
+                return -1;
+        }
+
+        return 0;
+}
+
+/* Tell kernel the parameters this ELAN has.
+ * Returns < 0 for serious error
+ */
+static int config_kernel(void)
+{
+        struct atmlec_msg msg;
+
+        memset(&msg, 0, sizeof(struct atmlec_msg));
+        msg.type = l_config;
+        msg.content.config.maximum_unknown_frame_count = lec_params.c10_max_unknown_frames;
+        msg.content.config.max_unknown_frame_time = lec_params.c11_max_unknown_frame_time;
+        msg.content.config.max_retry_count = lec_params.c13_max_retry_count;
+        msg.content.config.aging_time = lec_params.c17_aging_time;
+        msg.content.config.forward_delay_time = lec_params.c18_forward_delay_time;
+        msg.content.config.arp_response_time = lec_params.c20_le_arp_response_time;
+        msg.content.config.flush_timeout = lec_params.c21_flush_timeout;
+        msg.content.config.path_switching_delay = lec_params.c22_path_switching_delay;
+        msg.content.config.lane_version = (lec_params.c29_v2_capable) ? 2 : 1;
+
+        if (msg_to_kernel(&msg, sizeof(struct atmlec_msg)) < 0) {
+                diag(COMPONENT, DIAG_ERROR, "Could not tell kernel ELAN parameters\n");
+                return -1;
+        }
+
+        return 0;
+}
+
+int main(int argc, char **argv)
+{
+        char mac_addr[ETH_ALEN];
+        char elan_name[32 + 1];
+        char preferred_les[ATM_ESA_LEN]; /* LANE2 */
+        char foreId[255]; /* Max size for a TLV */
+        char atm2textbuff[100];
+        char esibuff[20];
+        int esi_set = 0;
+        int atm_set=0;
+        int proxy_flag = 0;
+        int lane_version = 0;              /* LANE2 */
+        int max_frame_size = MTU_UNSPEC;
+        int lecs_method = LECS_WELLKNOWN;
+        int poll_ret = 0, itf = 0;
+        struct sockaddr_atmsvc manual_atm_addr;
+        struct sockaddr_atmsvc listen_addr;
+        
+        memset(elan_name, '\0', sizeof(elan_name));
+        memset(foreId, '\0', sizeof(foreId));
+        memset(preferred_les, 0, ATM_ESA_LEN);
+        memset(&manual_atm_addr, 0, sizeof(struct sockaddr_atmsvc));
+        memset(&listen_addr, 0, sizeof(struct sockaddr_atmsvc));
+
+        set_application("zeppelin"); /* for debug msgs */
+
+        if (get_listenaddr(listen_addr.sas_addr.prv) < 0) {
+                diag(COMPONENT, DIAG_FATAL, "Could not figure out my ATM address\n");
+                exit(-1);
+        }
+
+        while(poll_ret != -1) {
+                poll_ret = getopt(argc, argv, "c:e:n:s:m:l:i:q:12pf:t:F:");
+                switch(poll_ret) {
+                case 'c':
+                        if (atm_set) {
+                                usage(argv[0]);
+                                exit(-1);
+                        }
+                        if (text2atm(optarg, (struct sockaddr *)&manual_atm_addr,
+                                     sizeof(struct sockaddr_atmsvc), T2A_NAME) < 0) {
+                                diag(COMPONENT, DIAG_ERROR, "Invalid LECS address\n");
+                                usage(argv[0]);
+                                exit(-1);
+                        }
+                        atm2text(atm2textbuff, sizeof(atm2textbuff),
+                                 (struct sockaddr *)&manual_atm_addr, 0);
+                        diag(COMPONENT, DIAG_INFO, "LECS address: %s\n", atm2textbuff);
+                        lecs_method = LECS_MANUAL;
+                        atm_set=1;
+                        break;
+                case 'e':
+                        if(esi_convert(optarg, mac_addr)<0) {
+                                diag(COMPONENT, DIAG_ERROR, "Invalid ESI format\n");
+                                usage(argv[0]);
+                                exit(-1);
+                        }
+                        mac2text(esibuff, mac_addr);
+                        diag(COMPONENT, DIAG_DEBUG, "LEC ESI:%s\n", esibuff);
+                        esi_set=1;
+                        break;
+                case 'n':
+                        if (strlen(optarg) > 32) {
+                                diag(COMPONENT, DIAG_ERROR, "ELAN name too long\n");
+                                exit(-1);
+                        }
+                        strcpy(elan_name, optarg);
+                        diag(COMPONENT, DIAG_INFO, "Vlan name :'%s'\n", elan_name);
+                        break;
+                case 's':
+                        if (atm_set) {
+                                usage(argv[0]);
+                                exit(-1);
+                        }
+                        if (text2atm(optarg, (struct sockaddr *)&manual_atm_addr,
+                                     sizeof(struct sockaddr_atmsvc), T2A_NAME) < 0) {
+                                diag(COMPONENT, DIAG_ERROR, "Invalid LES address\n");
+                                usage(argv[0]);
+                                exit(-1);
+                        }
+                        atm2text(atm2textbuff, sizeof(atm2textbuff),
+                                 (struct sockaddr *)&manual_atm_addr, 0);
+                        diag(COMPONENT, DIAG_INFO, "LES address: %s\n", atm2textbuff);
+                        lecs_method = LECS_NONE;
+                        atm_set=1;
+                        break;
+                case 'm':
+                        set_verbosity(NULL, DIAG_DEBUG);
+                        break;
+                case 'l':
+                        if (text2atm(optarg, (struct sockaddr *)&listen_addr,
+                                     sizeof(struct sockaddr_atmsvc), T2A_NAME) < 0) {
+                                diag(COMPONENT, DIAG_ERROR, "Invalid ATM listen address\n");
+                                usage(argv[0]);
+                                exit(-1);
+                        }
+                        atm2text(atm2textbuff, sizeof(atm2textbuff),
+                                 (struct sockaddr *)&listen_addr, 0);
+                        diag(COMPONENT, DIAG_INFO, "Our ATM address: %s\n", atm2textbuff);
+                        break;
+                case 'i':
+                        if (sscanf(optarg, "%d", &itf) < 0 || itf >= MAX_LEC_ITF) {
+                                diag(COMPONENT, DIAG_ERROR, "Invalid interface number\n");
+                                usage(argv[0]);
+                                exit(-1);
+                        }
+                        diag(COMPONENT, DIAG_INFO, "Interface number set to %d\n", itf);
+                        break;
+                case 'q':
+#if 0
+                        if (text2qos(optarg,NULL,0) < 0) {
+                                diag(COMPONENT, DIAG_ERROR, "Invalid QOS specification\n");
+                                usage(argv[0]);
+                                exit(-1);
+                        }
+                        qos_spec = optarg;
+#endif
+                        diag(COMPONENT, DIAG_INFO, "-q is deprecated, ignoring it\n");
+                        break;
+                case '1':
+                        lane_version = 1;
+                        break;
+                case '2':
+                        lane_version = 2;
+                        break;
+                case 'p':
+                        proxy_flag = 1;
+                        break;
+                case 'f':
+                        if (strlen(optarg) > 255) {
+                                diag(COMPONENT, DIAG_ERROR, "foreId too long\n");
+                                exit(-1);
+                        }
+                        memcpy (foreId, optarg, strlen(optarg));
+                        foreId[strlen(optarg)] = '\0';
+                        diag(COMPONENT, DIAG_INFO, "foreId :'%s'\n", foreId);
+                        break;
+                case 't':	/* ERIC */
+                        if( !strncmp( optarg, "1516", 4 )) max_frame_size = MTU_1516;
+                        else if( !strncmp( optarg, "1580", 4 )) max_frame_size = MTU_1580;
+                        else if( !strncmp( optarg, "4544", 4 )) max_frame_size = MTU_4544;
+                        else if( !strncmp( optarg, "9234", 4 )) max_frame_size = MTU_9234;
+                        else if( !strncmp( optarg, "18190", 5 )) max_frame_size = MTU_18190;
+                        break;
+                case 'F':
+                        set_logfile(optarg);
+                        diag(COMPONENT, DIAG_DEBUG, "logfile set to %s\n", optarg);
+                        break;
+                case -1:
+                        break;
+                default:
+                        usage(argv[0]);
+                        exit(-1);
+                }
+        }
+        if (argc != optind) {
+                usage(argv[0]);
+                exit(1);
+        }
+        if (lane_version == 1 && max_frame_size == MTU_1580) {
+                diag(COMPONENT, DIAG_ERROR, "MTU 1580 not defined with LANEv1\n");
+                exit(-1);
+        }
+
+        /* Reserve signals */
+        signal(SIGHUP, sig_reset);
+
+        if (!esi_set) {
+                if(addr_getesi(mac_addr) < 0) {
+                        diag(COMPONENT, DIAG_ERROR, "Can't get ESI from kernel!\n");
+                        return -1;
+                }
+                mac2text(esibuff, mac_addr);
+                diag(COMPONENT, DIAG_DEBUG, "LEC ESI:%s\n", esibuff);
+        }
+
+        /* Loop here until the Sun gets cold */
+        while (1) {
+                if ((itf = kernel_init(mac_addr, itf)) < 0 ) {
+                        diag(COMPONENT, DIAG_FATAL, "Kernel interface creation failed, exiting...\n");
+                        return -1;
+                } 
+
+                diag(COMPONENT, DIAG_DEBUG, "initializing lec parameters\n");
+                init_lec_params(mac_addr, elan_name, listen_addr.sas_addr.prv,
+                                itf, foreId, max_frame_size, proxy_flag, lane_version);
+
+                diag(COMPONENT, DIAG_DEBUG, "About to connect LECS\n");
+                if (lec_configure(lecs_method, &manual_atm_addr, &listen_addr) < 0) {
+                        close_connections();
+                        random_delay();
+                        continue;
+                }
+                diag(COMPONENT, DIAG_DEBUG, "About to connect LES\n");
+                if (les_connect(lecs_method, &manual_atm_addr, &listen_addr) < 0) {
+                        close_connections();
+                        random_delay();
+                        continue;
+                }
+                diag(COMPONENT, DIAG_DEBUG, "About to connect BUS\n");
+                if (bus_connect() < 0) {
+                        close_connections();
+                        random_delay();
+                        continue;
+                }
+                diag(COMPONENT, DIAG_DEBUG, "About to create data direct listen socket\n");
+                if (create_data_listen() < 0) {
+                        close_connections();
+                        random_delay();
+                        continue;
+                }
+                diag(COMPONENT, DIAG_DEBUG, "About to tell kernel our LEC_ID %d\n", lec_params.c14_lec_id);
+                if (set_lec_id(lec_params.c14_lec_id) < 0) {
+                        close_connections();
+                        continue;
+                }
+                diag(COMPONENT, DIAG_DEBUG, "About to tell kernel LEC parameters\n");
+                if (config_kernel() < 0) {
+                        close_connections();
+                        continue;
+                }
+
+                diag(COMPONENT, DIAG_DEBUG, "Joined ELAN '%s' successfully\n", lec_params.c5_elan_name);
+
+                main_loop();
+                diag(COMPONENT, DIAG_INFO, "Resetting...\n");
+                close_connections();
+                random_delay();
+
+                reset = 0;
+        }
+
+        return 0; /* not reached */
+}
+
+/* zeppelin loops here when it is in operational state.  The check
+ * against reset variable is probably not needed since select() will
+ * return < 0 when a signal interrupts it.
+ */
+static void main_loop(void)
+{
+        fd_set rfds, cfds;
+        int retval, ret1, ret2, ret3;
+
+        while(!reset) {
+                retval = ret1 = ret2 = ret3 = 0;
+                FD_ZERO(&rfds);
+                conn_get_fds(&rfds);
+                FD_ZERO(&cfds);
+                conn_get_connecting_fds(&cfds);
+
+                retval = select(FD_SETSIZE, &rfds, &cfds, NULL, NULL);
+                diag(COMPONENT, DIAG_DEBUG, "main_loop: select returned %d\n", retval);
+                if (retval < 0) {
+                        diag(COMPONENT, DIAG_ERROR, "main_loop: select: %s\n", strerror(errno));
+                        break; /* leave main_loop */
+                }
+                if (retval == 0) {
+                        /* Timeout, funny, since we have no timers */
+                        continue;
+                }
+                if (FD_ISSET(lec_params.kernel->fd, &rfds)) {
+                        ret1 = msg_from_kernel();
+                        FD_CLR(lec_params.kernel->fd, &rfds);
+                }
+                ret2 = complete_connections(&cfds);
+                ret3 = check_connections(&rfds);
+                
+                if (ret1 < 0 || ret2 < 0 || ret3 < 0)
+                        break; /* leave main_loop */
+        }
+        
+        diag(COMPONENT, DIAG_DEBUG, "exiting main_loop\n");
+
+        return;
+}
diff -ur --new-file old/atm/led.new/zeppelin.new.8 new/atm/led.new/zeppelin.new.8
--- old/atm/led.new/zeppelin.new.8	Thu Jan  1 01:00:00 1970
+++ new/atm/led.new/zeppelin.new.8	Sat Feb  6 23:48:20 1999
@@ -0,0 +1,91 @@
+.TH zeppelin 8 "Feb 6, 1999" "Linux" "Maintenance Commands"
+.SH NAME
+zeppelin \- ATM LAN Emulation client demon (LED) Zeppelin
+.SH SYNOPSIS
+.B zeppelin
+.RB [ \-c\ \fILECS_address\fP\ |\ \-s\ \fILES_address\fP ]
+.RB [ \-e\ \fIesi\fP ]
+.RB [ \-n\ \fIVLAN_name\fP ]
+.RB [ \-m\ \fImesg_mask\fP ]
+.RB [ \-l\ \fIlisten_address\fP ]
+.RB [ \-i\ \fIinterface_number\fP ]
+.RB [ \-t\ \fI1516|1580|4544|9234|18190\fP ]
+.RB [ \-1\ ]
+.RB [ \-2\ ]
+.RB [ \-p\ ]
+.RB [ \-F\ \fIlogfile\fP ]
+.RB [ \-f\ \fIFore_specific_name\fP ]
+.SH DESCRIPTION
+A LAN Emulation Client is an entity in an ATM endstation that performs 
+data forwarding, address resolution and other control functions. It 
+uses the LUNI interface when communicating with other components in 
+emulated LANs. It provides upper protocol layers a MAC like 
+interface similar to IEEE 802.3/Ethernet or IEEE 802.5/Token Ring LAN.
+.PP
+LAN Emulation client code is divided into two parts: user space 
+application LAN Emulation Demon called (LED) \fBzeppelin(8)\fP, and the 
+kernel component. \fBZeppelin\fP is responsible for control operations needed 
+in LAN Emulation clienthood. It forms the necessary VCCs and receives 
+all the LE control frames and acts accordingly. It also controls the 
+operation of the LEC kernel component.
+.PP
+Linux LEC supports only Ethernet type of emulated LAN.
+.PP
+\fBSIGHUP\fP causes restart of the LEC. All resources are 
+released and \fBzeppelin\fP is started. 
+.SH OPTIONS
+.IP \fB\-c\ \fILECS_address\fP
+ATM address of \fBlecs(8)\fP (Lan Emulation Configuration Server), if not
+set, Well-Known server address is used.
+.IP \fB\-s\ \fILES_address\fP
+ATM address of \fBles(8)\fP (Lan Emulation Server), can be used in
+bypassing configuration phase in joining emulated Lan i.e \fBlecs\fP address
+is not used. 
+.IP \fB\-e\ \fIesi\fP
+Mac address to use when communicating in Emulated LAN. E.g. 00:20:22:23:04:05 .
+.IP \fB\-n\ \fIVLAN_name\fP
+Name of the virtual LAN to which joining is requested.
+This is used in LE_CONFIGURE_REQUEST to LECS or
+LE_JOIN_RESPONSE to LES, if configuration phase is bypassed.
+.IP \fB\-m\ \fImesg_mask\fP
+Sometimes one wants to know more what is happening in LE
+daemon e.g. when nothing works. This is a hexadecimal long value
+setting global message mask. 0 = No messages, ffff = All messages.
+.IP \fB\-l\ \fIlisten_address\fP
+Local ATM address that zeppelin uses as local binding point in
+signalling. Use this if you are running more than one client or
+a set of LE servers.
+.IP \fB\-i\ \fIinterface_number\fP
+Linux LEC supports up to 16 network interfaces. This number tells
+zeppelin to which of these to attach. Network interfaces are
+numbered from "lec0" to "lec15".
+.IP \fB\-t\ \fIMTU\fP
+The MTU of ELAN to join. You need to also use \fBifconfig(8)\fP to
+set the MTU of the LANE interface.
+.IP \fB\-1\fP
+Run as LANEv1 client. This is the default.
+.IP \fB\-2\fP
+Run as LANEv2 client. This is required by MPOA.
+.IP \fB\-p\fP
+Enable proxy. When started with this option, it is possible to bridge
+packets between ATM and Ethernet. That is, you can use LANE interfaces
+with normal bridging. See the Bridging mini-Howto for more info.
+.IP \fB\-F\ \fIlogfile\fP
+Instead of writing debug messages to \fBstderr\fP, write the messages
+to the file \fBlogfile\fP. Use \fBsyslog\fP as the file name to use
+the \fBsyslog(3)\fP facility.
+.IP \fB\-f\ \fIFore\ specific\ name\fP
+The LANE servers on Fore ATM switches can display a special
+name if a client can supply one. This name shows with command
+\'conf lane les show advanced\'.
+.SH BUGS
+Supports only IEEE 802.3 / Ethernet type of ELANs. Please report any
+new bugs to Heikki Vatiainen <hessu@cs.tut.fi>
+.PP
+John Bonham died 1980 and Led Zeppelin broke.
+.SH AUTHORS
+Marko Kiiskila, TUT <carnil@cs.tut.fi> and Heikki Vatiainen, TUT
+<hessu@cs.tut.fi>
+.SH "SEE ALSO"
+lecs(8), mpcd(8), atmsigd(8), les(8), qos(7)
+.\"{{{}}}
diff -ur --new-file old/atm/lib/ans.c new/atm/lib/ans.c
--- old/atm/lib/ans.c	Sat Jun  6 01:52:45 1998
+++ new/atm/lib/ans.c	Tue Feb  9 17:31:56 1999
@@ -137,9 +137,14 @@
     switch (*addr) {
 	case ATM_AFI_DCC:
 	case ATM_AFI_ICD:
+	case ATM_AFI_LOCAL:
+	case ATM_AFI_DCC_GROUP:
+	case ATM_AFI_ICD_GROUP:
+	case ATM_AFI_LOCAL_GROUP:
 	    fmt = fmt_dcc;
 	    break;
 	case ATM_AFI_E164:
+	case ATM_AFI_E164_GROUP:
 	    fmt = fmt_e164;
 	    break;
 	default:
diff -ur --new-file old/atm/lib/atm2text.c new/atm/lib/atm2text.c
--- old/atm/lib/atm2text.c	Wed Sep  9 21:04:49 1998
+++ new/atm/lib/atm2text.c	Tue Feb  9 17:31:56 1999
@@ -65,6 +65,7 @@
 {
     static int pure[] = { 20 };
     static int bin[] = { 1,2,10,6,1 };
+    static int local[] = { 1,12,6,1 };
     static int e164[] = { 4,6,1 };
 
     int orig_len,len,i,left,value;
@@ -94,9 +95,16 @@
 	    switch (*addr->sas_addr.prv) {
 		case ATM_AFI_DCC:
 		case ATM_AFI_ICD:
+		case ATM_AFI_DCC_GROUP:
+		case ATM_AFI_ICD_GROUP:
 		    fmt = bin;
 		    break;
+		case ATM_AFI_LOCAL:
+		case ATM_AFI_LOCAL_GROUP:
+		    fmt = local;
+		    break;
 		case ATM_AFI_E164:
+		case ATM_AFI_E164_GROUP:
 		    for (i = 2; i < 17; i++)
 			if (addr->sas_addr.prv[i >> 1] & (0xf0 >> 4*(i & 1)))
 			    break;
diff -ur --new-file old/atm/mkdist new/atm/mkdist
--- old/atm/mkdist	Sat Dec  5 01:43:59 1998
+++ new/atm/mkdist	Tue Feb  9 17:18:50 1999
@@ -101,6 +101,13 @@
     atm/led/address.c atm/led/conn.c atm/led/main.c atm/led/kernel_itf.c \
     atm/led/lane2.h \
     atm/led/Makefile atm/led/zeppelin.8 \
+  atm/led.new/COPYRIGHT.TUT atm/led.new/Makefile \
+    atm/led.new/address.c atm/led.new/address.h atm/led.new/conn.c \
+    atm/led.new/conn.h atm/led.new/display.c atm/led.new/display.h \
+    atm/led.new/frame_defs.h atm/led.new/frames.c atm/led.new/frames.h \
+    atm/led.new/join.c atm/led.new/join.h atm/led.new/kernel.c \
+    atm/led.new/kernel.h atm/led.new/lec.h atm/led.new/main.c \
+    atm/led.new/zeppelin.new.8 \
   atm/lane/COPYRIGHT.TUT atm/lane/Makefile atm/lane/USAGE \
     atm/lane/bus.8 atm/lane/lecs.8 atm/lane/les.8 \
     atm/lane/atm.c \
diff -ur --new-file old/atm/sigd/atmsigd.c new/atm/sigd/atmsigd.c
--- old/atm/sigd/atmsigd.c	Tue Nov  3 17:23:38 1998
+++ new/atm/sigd/atmsigd.c	Tue Jan  5 16:35:04 1999
@@ -1,6 +1,6 @@
 /* atmsigd.c - ATM signaling demon */
 
-/* Written 1995-1998 by Werner Almesberger, EPFL-LRC/ICA */
+/* Written 1995-1999 by Werner Almesberger, EPFL-LRC/ICA */
 
 
 #include <stdlib.h>
@@ -339,7 +339,22 @@
 	    diag(COMPONENT,DIAG_FATAL,"Error in config file. - Aborting.");
     if (!atmpvc_addr_in_use(_entity.signaling_pvc))
 	_entity.signaling_pvc.sap_addr.vci = 5;
-    if (!_entity.uni) _entity.uni = S_UNI30;
+    if (!_entity.uni)
+	_entity.uni =
+#ifdef UNI31
+	    S_UNI31
+#ifdef ALLOW_UNI30
+	    | S_UNI30
+#endif
+#elif defined(UNI40)
+	    S_UNI40
+#ifdef Q2963_1
+	    | S_Q2963_1
+#endif
+#else
+	    S_UNI30
+#endif
+	    ;
     /* process all other options but -c */
     optind = 0;
     while ((c = getopt(argc,argv,"Abc:dD:l:m:nNP:q:t:")) != EOF)
diff -ur --new-file old/atm/sigd/uni.c new/atm/sigd/uni.c
--- old/atm/sigd/uni.c	Wed Dec  2 18:45:17 1998
+++ new/atm/sigd/uni.c	Tue Jan  5 20:01:23 1999
@@ -1,6 +1,6 @@
 /* uni.c - Processing of incoming UNI signaling messages */
  
-/* Written 1995-1998 by Werner Almesberger, EPFL-LRC/ICA */
+/* Written 1995-1999 by Werner Almesberger, EPFL-LRC/ICA */
  
 
 #include <stdarg.h>
@@ -754,7 +754,7 @@
     }
     if (curr && q_present(&in_dsc,QF_ep_ref) && mid != ATM_MSG_ADD_PARTY &&
       mid != ATM_MSG_DROP_PARTY_ACK)
-	if (curr->ep_ref != q_fetch(&in_dsc,QF_ep_ref) ^ 0x8000) {
+	if (curr->ep_ref != (q_fetch(&in_dsc,QF_ep_ref) ^ 0x8000)) {
 	    send_drop_party_ack(sig,call_ref,
 	      q_fetch(&in_dsc,QF_ep_ref) ^ 0x8000,ATM_CV_INV_EPR);
 	    return;