[Bridge] (no subject)

Dan Eble dane at aiinet.com
Wed Feb 25 06:30:07 PST 2004


Attached is the BCP patch for pppd 2.4.1.  In addition to BCP, there are
also a few changes to support a pppd plugin for devices using the kernel
generic HDLC layer.  The plugin itself is not included in this patch.

There were some changes to log messages that I tried to remove by hand.  
The patch applies cleanly and compiles on 686.  It has never been tested
on 686, though, only on powerpc.  I would appreciate hearing about any
difficulties you encounter.

You must patch the kernel too.  I will send those changes in a separate
mail.

-- 
Dan Eble <dane at aiinet.com>  _____  .
Software Engineer          |  _  |/|
Applied Innovation Inc.    | |_| | |
http://www.aiinet.com/     |__/|_|_|
-------------- next part --------------
diff -wbBurN pppd-2.4.1/include/linux/ppp_defs.h pppd-ai/include/linux/ppp_defs.h
--- pppd-2.4.1/include/linux/ppp_defs.h	2004-02-25 08:27:34.000000000 -0500
+++ pppd-ai/include/linux/ppp_defs.h	2004-02-25 08:34:47.000000000 -0500
@@ -70,13 +70,16 @@
 #define PPP_IPX		0x2b	/* IPX protocol */
 #define	PPP_VJC_COMP	0x2d	/* VJ compressed TCP */
 #define	PPP_VJC_UNCOMP	0x2f	/* VJ uncompressed TCP */
+#define PPP_BRIDGE	0x31	/* Bridged LAN traffic or BPDU */
 #define PPP_MP		0x3d	/* Multilink protocol */
 #define PPP_IPV6	0x57	/* Internet Protocol Version 6 */
 #define PPP_COMPFRAG	0xfb	/* fragment compressed below bundle */
 #define PPP_COMP	0xfd	/* compressed packet */
+#define PPP_BPDU_IEEE	0x0201	/* IEEE 802.1 (D or G) bridge PDU */
 #define PPP_IPCP	0x8021	/* IP Control Protocol */
 #define PPP_ATCP	0x8029	/* AppleTalk Control Protocol */
 #define PPP_IPXCP	0x802b	/* IPX Control Protocol */
+#define PPP_BCP		0x8031	/* Bridging Control Protocol */
 #define PPP_IPV6CP	0x8057	/* IPv6 Control Protocol */
 #define PPP_CCPFRAG	0x80fb	/* CCP at link level (below MP bundle) */
 #define PPP_CCP		0x80fd	/* Compression Control Protocol */
diff -wbBurN pppd-2.4.1/include/net/ppp_defs.h pppd-ai/include/net/ppp_defs.h
--- pppd-2.4.1/include/net/ppp_defs.h	2004-02-25 08:27:34.000000000 -0500
+++ pppd-ai/include/net/ppp_defs.h	2004-02-25 08:34:47.000000000 -0500
@@ -72,11 +72,14 @@
 #define PPP_IPX		0x2b	/* IPX protocol */
 #define	PPP_VJC_COMP	0x2d	/* VJ compressed TCP */
 #define	PPP_VJC_UNCOMP	0x2f	/* VJ uncompressed TCP */
+#define PPP_BRIDGE	0x31	/* Bridged LAN traffic or BPDU */
 #define PPP_IPV6	0x57	/* Internet Protocol Version 6 */
 #define PPP_COMP	0xfd	/* compressed packet */
+#define PPP_BPDU_IEEE	0x0201	/* IEEE 802.1 (D or G) bridge PDU */
 #define PPP_IPCP	0x8021	/* IP Control Protocol */
 #define PPP_ATCP	0x8029	/* AppleTalk Control Protocol */
 #define PPP_IPXCP	0x802b	/* IPX Control Protocol */
+#define PPP_BCP		0x8031	/* Bridging Control Protocol */
 #define PPP_IPV6CP	0x8057	/* IPv6 Control Protocol */
 #define PPP_CCP		0x80fd	/* Compression Control Protocol */
 #define PPP_LCP		0xc021	/* Link Control Protocol */
diff -wbBurN pppd-2.4.1/pppd/auth.c pppd-ai/pppd/auth.c
--- pppd-2.4.1/pppd/auth.c	2004-02-25 08:27:34.000000000 -0500
+++ pppd-ai/pppd/auth.c	2004-02-25 08:34:47.000000000 -0500
@@ -103,9 +103,31 @@
 /* Extra options to apply, from the secrets file entry for the peer. */
 static struct wordlist *extra_options;
 
+/* Bits 0, 8, 14, and 15 are the same in all network protocol numbers, so
+ * remove them to save space in np_running[][]. */
+#define NP_BRIEF(np) ((int)((((np) & 0x3E00) >> 2) | (((np) & 0x00FE) >> 1)))
+#define NP_ARRAY_BYTE(np)	(NP_BRIEF(np) / 8)
+#define NP_ARRAY_BIT(np)	((unsigned char)(1 << (NP_BRIEF(np) % 8)))
+
+/* Flags for running protocols.  One bit per protocol per PPP unit. */
+static unsigned char np_running[NP_ARRAY_BYTE(0xBEFF)+1][NUM_PPP];
+
 /* Number of network protocols which we have opened. */
 static int num_np_open;
 
+#define NP_RUNNING(unit, np) \
+    (np_running[unit][NP_ARRAY_BYTE(np)] & NP_ARRAY_BIT(np))
+
+#define MARK_NP_RUNNING(unit, np) do {	\
+    np_running[unit][NP_ARRAY_BYTE(np)] |= NP_ARRAY_BIT(np);	\
+    ++num_np_open;	\
+    } while (0)
+
+#define MARK_NP_FINISHED(unit, np) do {	\
+    np_running[unit][NP_ARRAY_BYTE(np)] &= ~NP_ARRAY_BIT(np);	\
+    --num_np_open;	\
+    } while (0)
+
 /* Number of network protocols which have come up. */
 static int num_np_up;
 
@@ -421,6 +443,8 @@
         if (protp->protocol < 0xC000 && protp->close != NULL)
 	    (*protp->close)(unit, "LCP down");
     }
+
+    memset(np_running, 0, sizeof(np_running));
     num_np_open = 0;
     num_np_up = 0;
     if (phase != PHASE_DEAD)
@@ -565,7 +589,7 @@
 	    && protp->open != NULL) {
 	    (*protp->open)(0);
 	    if (protp->protocol != PPP_CCP)
-		++num_np_open;
+                MARK_NP_RUNNING(0, protp->protocol);
 	}
 
     if (num_np_open == 0)
@@ -732,17 +756,32 @@
 }
 
 /*
+ * np_start - a network protocol is starting to use the link.
+ */
+void
+np_start(unit, proto)
+    int unit, proto;
+{
+    if (!NP_RUNNING(unit, proto))
+        MARK_NP_RUNNING(unit, proto);
+}
+
+/*
  * np_finished - a network protocol has finished using the link.
  */
 void
 np_finished(unit, proto)
     int unit, proto;
 {
-    if (--num_np_open <= 0) {
+    if (NP_RUNNING(unit, proto)) {
+        MARK_NP_FINISHED(unit, proto);
+
+        if (num_np_open <= 0) {
 	/* no further use for the link: shut up shop. */
 	lcp_close(0, "No network protocols running");
     }
 }
+}
 
 /*
  * check_idle - check whether the link has been idle for long
diff -wbBurN pppd-2.4.1/pppd/bcp.c pppd-ai/pppd/bcp.c
--- pppd-2.4.1/pppd/bcp.c	1969-12-31 19:00:00.000000000 -0500
+++ pppd-ai/pppd/bcp.c	2004-02-25 08:34:47.000000000 -0500
@@ -0,0 +1,1743 @@
+/*
+ * bcp.c - PPP Bridge Control Protocol.
+ *
+ * Copyright (c) 2001-2004 Applied Innovation Inc.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms.  The name of Applied Innovation Inc.
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "bcp.h"
+#include "pathnames.h"
+
+/* global vars */
+bcp_options bcp_wantoptions[NUM_PPP];	/* Options that we want to request */
+bcp_options bcp_gotoptions[NUM_PPP];	/* Options that peer ack'd */
+bcp_options bcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+bcp_options bcp_hisoptions[NUM_PPP];	/* Options that we ack'd */
+
+/* local vars */
+static int bcp_is_up;			/* have called np_up() */
+
+static bool bcp_maclocal_valid;
+static u_char bcp_maclocal[ETH_ALEN];
+static char bcp_maclocal_str[3*ETH_ALEN]; /* string form of "maclocal" arg */
+
+/*
+ * Callbacks for fsm code.  (CI = Configuration Information)
+ */
+static void bcp_resetci __P((fsm *));	/* Reset our CI */
+static int  bcp_cilen __P((fsm *));	        /* Return length of our CI */
+static void bcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
+static int  bcp_ackci __P((fsm *, u_char *, int));	/* Peer ack'd our CI */
+static int  bcp_nakci __P((fsm *, u_char *, int));	/* Peer nak'd our CI */
+static int  bcp_rejci __P((fsm *, u_char *, int));	/* Peer rej'd our CI */
+static int  bcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
+static void bcp_up __P((fsm *));		/* We're UP */
+static void bcp_down __P((fsm *));		/* We're DOWN */
+static void bcp_start __P((fsm *));	/* Need lower layer */
+static void bcp_finished __P((fsm *));	/* Don't need lower layer */
+
+fsm bcp_fsm[NUM_PPP];		/* BCP fsm structure */
+
+static fsm_callbacks bcp_callbacks = { /* BCP callback routines */
+    bcp_resetci,		/* Reset our Configuration Information */
+    bcp_cilen,			/* Length of our Configuration Information */
+    bcp_addci,			/* Add our Configuration Information */
+    bcp_ackci,			/* ACK our Configuration Information */
+    bcp_nakci,			/* NAK our Configuration Information */
+    bcp_rejci,			/* Reject our Configuration Information */
+    bcp_reqci,			/* Request peer's Configuration Information */
+    bcp_up,			/* Called when fsm reaches OPENED state */
+    bcp_down,			/* Called when fsm leaves OPENED state */
+    bcp_start,			/* Called when we want the lower layer up */
+    bcp_finished,		/* Called when we want the lower layer down */
+    NULL,			/* Called when Protocol-Reject received */
+    NULL,			/* Retransmission is necessary */
+    NULL,			/* Called to handle protocol-specific codes */
+    "BCP"			/* String name of protocol */
+};
+
+/*
+ * Command-line options.
+ */
+static int bcp_setmaclocal __P((char **));
+
+static option_t bcp_option_list[] = {
+    { "nobcp", o_bool, &bcp_protent.enabled_flag, "Disable BCP" },
+    { "maclocal", o_special, (void *)bcp_setmaclocal, "set local MAC address",
+      OPT_PRIO | OPT_A2STRVAL | OPT_STATIC, bcp_maclocal_str },
+    { NULL }			/* terminating entry */
+};
+/*
+ * Protocol entry points from main code.
+ */
+static void bcp_init __P((int));
+static void bcp_open __P((int));
+static void bcp_close __P((int, char *));
+static void bcp_lowerup __P((int));
+static void bcp_lowerdown __P((int));
+static void bcp_input __P((int, u_char *, int));
+static void bcp_protrej __P((int));
+static int  bcp_printpkt __P((u_char *, int,
+			       void (*) __P((void *, char *, ...)), void *));
+static void bcp_check_options __P((void));
+
+struct protent bcp_protent = {
+    PPP_BCP,
+    bcp_init,
+    bcp_input,
+    bcp_protrej,
+    bcp_lowerup,
+    bcp_lowerdown,
+    bcp_open,
+    bcp_close,
+    bcp_printpkt,
+    NULL,
+    1,
+    "BCP",
+    "Bridging",
+    bcp_option_list,
+    bcp_check_options,
+    NULL,
+    NULL
+};
+
+static void bcp_script __P((fsm *, char *));	/* Run an up/down script */
+static void bcp_script_done __P((void *));
+
+/*
+ * Lengths of configuration options.
+ */
+#define CILEN_VOID	2
+#define CILEN_BRIDGELINEID	4
+#define CILEN_MACSUPPORT 3
+#define CILEN_TINYGRAM 3
+#define CILEN_LANID 3
+#define CILEN_MACADDR 8
+#define CILEN_SPANTREE 3
+#define CILEN_IEEE_802_TAGGED_FRAME 3
+#define CILEN_MGMT_INLINE 2
+
+#define CODENAME(x)	((x) == CONFACK ? "ACK" : \
+			 (x) == CONFNAK ? "NAK" : "REJ")
+
+/*
+ * This state variable is used to ensure that we don't
+ * run an bcp-up/down script while one is already running.
+ */
+static enum script_state {
+    s_down,
+    s_up,
+} bcp_script_state;
+static pid_t bcp_script_pid;
+
+/*
+ * Convert a MAC address from a string to an array of bytes.
+ */
+static int
+mac_aton(const char *cp, u_char *mac)
+{
+    unsigned int args[ETH_ALEN];
+    int n;
+
+    if (sscanf(cp, "%2x:%2x:%2x:%2x:%2x:%2x%n",
+               &args[0], &args[1], &args[2],
+               &args[3], &args[4], &args[5], &n) >= ETH_ALEN)
+    {
+        if (!cp[n]) /* expect to have reached the end of the string */
+        {
+            int i;
+            for (i = 0; i < ETH_ALEN; ++i)
+            {
+                mac[i] = args[i];
+            }
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+/*
+ * Print a MAC address into a buffer and return the length.
+ */
+static int
+slprintmac(char *buf, int buflen, const u_char *mac)
+{
+    return slprintf(buf, buflen, "%x:%x:%x:%x:%x:%x",
+		    mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+}
+
+/*
+ * bcp_setmaclocal - set the MAC address to be used on the local interface.
+ * Returns 0 on error, 1 on success.
+ */
+static int
+bcp_setmaclocal(argv)
+    char **argv;
+{
+    bcp_maclocal_valid = mac_aton(argv[0], bcp_maclocal);
+
+    if (bcp_maclocal_valid && (bcp_maclocal[0] & BCP_MULTICAST))
+	bcp_maclocal_valid = 0;
+
+    if (!bcp_maclocal_valid)
+    {
+	option_error("invalid maclocal value '%s'", argv[0]);
+	return 0;
+    }
+
+    slprintmac(bcp_maclocal_str, sizeof(bcp_maclocal_str), bcp_maclocal);
+    return 1;
+}
+
+/*
+ * bcp_init - Initialize BCP.
+ */
+static void
+bcp_init(unit)
+    int unit;
+{
+    fsm *f = &bcp_fsm[unit];
+    bcp_options *wo = &bcp_wantoptions[unit];
+    bcp_options *ao = &bcp_allowoptions[unit];
+
+    f->unit = unit;
+    f->protocol = PPP_BCP;
+    f->callbacks = &bcp_callbacks;
+    fsm_init(&bcp_fsm[unit]);
+
+    memset(wo, 0, sizeof(*wo));
+    memset(ao, 0, sizeof(*ao));
+
+    wo->neg_macsupport = 1;
+    wo->macsupport[0] = MAC_IEEE_802_3;
+
+    /* Try Management-Inline first, but prepare to fall back on
+     * Spanning-Tree-Protocol if it fails. */
+    wo->neg_mgmt_inline = 1;
+    wo->neg_spantree = 0;
+    wo->spantree = SPAN_IEEE_802_1D;
+
+    wo->neg_tinygram = 1;
+    wo->tinygram = 1;			/* Linux can receive Tinygrams. */
+
+    ao->neg_macsupport = 1;
+    ao->neg_tinygram = 1;
+    ao->neg_macaddr = 1;
+
+    ao->neg_mgmt_inline = 1;
+    ao->neg_spantree = 1;
+}
+
+
+/*
+ * bcp_open - BCP is allowed to come up.
+ */
+static void
+bcp_open(unit)
+    int unit;
+{
+    fsm_open(&bcp_fsm[unit]);
+}
+
+
+/*
+ * bcp_close - Take BCP down.
+ */
+static void
+bcp_close(unit, reason)
+    int unit;
+    char *reason;
+{
+    fsm_close(&bcp_fsm[unit], reason);
+}
+
+
+/*
+ * bcp_lowerup - The lower layer is up.
+ */
+static void
+bcp_lowerup(unit)
+    int unit;
+{
+    fsm_lowerup(&bcp_fsm[unit]);
+}
+
+
+/*
+ * bcp_lowerdown - The lower layer is down.
+ */
+static void
+bcp_lowerdown(unit)
+    int unit;
+{
+    fsm_lowerdown(&bcp_fsm[unit]);
+}
+
+
+/*
+ * bcp_input - Input BCP packet.
+ */
+static void
+bcp_input(unit, p, len)
+    int unit;
+    u_char *p;
+    int len;
+{
+    fsm_input(&bcp_fsm[unit], p, len);
+}
+
+
+/*
+ * bcp_protrej - A Protocol-Reject was received for BCP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void
+bcp_protrej(unit)
+    int unit;
+{
+  fsm_lowerdown(&bcp_fsm[unit]);
+}
+
+
+/*
+ * bcp_resetci - Reset our CI.
+ * Called by fsm_sconfreq, Send Configure Request.
+ */
+static void
+bcp_resetci(f)
+    fsm *f;
+{
+    bcp_options *wo = &bcp_wantoptions[f->unit];
+    bcp_options *go = &bcp_gotoptions[f->unit];
+
+    /* Either announce our MAC address to the other side, or request one from
+     * the other side.  (Another possibility is not to negotiate at all, but
+     * that seems useless.) */
+    wo->neg_macaddr = 1;
+    if (bcp_maclocal_valid)
+	memcpy(wo->macaddr, bcp_maclocal, sizeof(wo->macaddr));
+    else
+	memset(wo->macaddr, 0, sizeof(wo->macaddr));
+
+    *go = *wo;
+}
+
+
+/*
+ * bcp_cilen - Return length of our CI.
+ * Called by fsm_sconfreq, Send Configure Request.
+ */
+static int
+bcp_cilen(f)
+    fsm *f;
+{
+    bcp_options *go = &bcp_gotoptions[f->unit];
+
+    int macsupport_len = 0;
+    int mac = 0;
+    for (mac=0; mac<ETH_ALEN; mac++)
+    {
+      if (go->macsupport[mac])
+      {
+        macsupport_len += CILEN_MACSUPPORT;
+      }
+    }
+
+#define LENCIBRIDGELINEID(neg)	((neg) ? CILEN_BRIDGELINEID : 0)
+#define LENCITINYGRAM(neg)	((neg) ? CILEN_TINYGRAM : 0)
+#define LENCILANID(neg)	((neg) ? CILEN_LANID : 0)
+#define LENCIMACADDR(neg)	((neg) ? CILEN_MACADDR : 0)
+#define LENCISPANTREE(neg)	((neg) ? CILEN_SPANTREE : 0)
+#define LENCIMGMTINLINE(neg)	((neg) ? CILEN_MGMT_INLINE : 0)
+
+
+    return (LENCIBRIDGELINEID(go->neg_bridgeid) +
+            LENCIBRIDGELINEID(go->neg_lineid) +
+            macsupport_len +
+            LENCITINYGRAM(go->neg_tinygram) +
+            LENCILANID(go->neg_lanid) +
+            LENCIMACADDR(go->neg_macaddr) +
+            LENCISPANTREE(go->neg_spantree) +
+	    LENCIMGMTINLINE(go->neg_mgmt_inline));
+}
+
+/*
+ * bcp_addci - Add our desired CIs to a packet.
+ * Called by fsm_sconfreq, Send Configure Request.
+ */
+static void
+bcp_addci(f, ucp, lenp)
+    fsm *f;
+    u_char *ucp;
+    int *lenp;
+{
+    bcp_options *go = &bcp_gotoptions[f->unit];
+    int len = *lenp;
+
+#define ADDCIBRIDGELINEID(opt, neg, lan_bridge_segno) \
+    if (neg) { \
+	if (len >= CILEN_BRIDGELINEID) { \
+	    PUTCHAR(opt, ucp); \
+	    PUTCHAR(CILEN_BRIDGELINEID, ucp); \
+	    PUTSHORT(lan_bridge_segno, ucp); \
+	    len -= CILEN_BRIDGELINEID; \
+	} else \
+	    neg = 0; \
+    }
+
+#define ADDCIMACSUPPORT(opt, neg, macsupport) \
+    if (neg && macsupport) { \
+	if (len >= CILEN_MACSUPPORT) { \
+	    PUTCHAR(opt, ucp); \
+	    PUTCHAR(CILEN_MACSUPPORT, ucp); \
+	    PUTCHAR(macsupport, ucp); \
+	    len -= CILEN_MACSUPPORT; \
+	} else \
+	    neg = 0; \
+    }
+
+#define ADDCITINYGRAM(opt, neg, tinygram) \
+    if (neg) { \
+	if (len >= CILEN_TINYGRAM) { \
+	    PUTCHAR(opt, ucp); \
+	    PUTCHAR(CILEN_TINYGRAM, ucp); \
+	    PUTCHAR(tinygram, ucp); \
+	    len -= CILEN_TINYGRAM; \
+	} else \
+	    neg = 0; \
+    }
+
+#define ADDCILANID(opt, neg, lanid) \
+    if (neg) { \
+	if (len >= CILEN_LANID) { \
+	    PUTCHAR(opt, ucp); \
+	    PUTCHAR(CILEN_LANID, ucp); \
+	    PUTCHAR(lanid, ucp); \
+	    len -= CILEN_LANID; \
+	} else \
+	    neg = 0; \
+    }
+
+#define ADDCIMACADDR(opt, neg, macaddr) \
+    if (neg) { \
+	if (len >= CILEN_MACADDR) { \
+	    PUTCHAR(opt, ucp); \
+	    PUTCHAR(CILEN_MACADDR, ucp); \
+	    PUTCHAR(macaddr[0], ucp); \
+	    PUTCHAR(macaddr[1], ucp); \
+	    PUTCHAR(macaddr[2], ucp); \
+	    PUTCHAR(macaddr[3], ucp); \
+	    PUTCHAR(macaddr[4], ucp); \
+	    PUTCHAR(macaddr[5], ucp); \
+	    len -= CILEN_MACADDR; \
+	} else \
+	    neg = 0; \
+    }
+
+#define ADDCISPANTREE(opt, neg, spantree) \
+    if (neg) { \
+	if (len >= CILEN_SPANTREE) { \
+	    PUTCHAR(opt, ucp); \
+	    PUTCHAR(CILEN_SPANTREE, ucp); \
+	    PUTCHAR(spantree, ucp); \
+	    len -= CILEN_SPANTREE; \
+	} else \
+	    neg = 0; \
+    }
+
+#define ADDCIMGMTINLINE(opt, neg) \
+    if (neg) { \
+	if (len >= CILEN_MGMT_INLINE) { \
+	    PUTCHAR(opt, ucp); \
+	    PUTCHAR(CILEN_MGMT_INLINE, ucp); \
+	    len -= CILEN_MGMT_INLINE; \
+	} else \
+	    neg = 0; \
+    }
+
+    ADDCIBRIDGELINEID(CI_BRIDGE_IDENTIFICATION, go->neg_bridgeid, go->lan_bridge_segno);
+
+    ADDCIBRIDGELINEID(CI_LINE_IDENTIFICATION, go->neg_lineid, go->lan_bridge_segno);
+
+    ADDCIMACSUPPORT(CI_MAC_SUPPORT, go->neg_macsupport, go->macsupport[0]);
+    ADDCIMACSUPPORT(CI_MAC_SUPPORT, go->neg_macsupport, go->macsupport[1]);
+    ADDCIMACSUPPORT(CI_MAC_SUPPORT, go->neg_macsupport, go->macsupport[2]);
+    ADDCIMACSUPPORT(CI_MAC_SUPPORT, go->neg_macsupport, go->macsupport[3]);
+    ADDCIMACSUPPORT(CI_MAC_SUPPORT, go->neg_macsupport, go->macsupport[4]);
+    ADDCIMACSUPPORT(CI_MAC_SUPPORT, go->neg_macsupport, go->macsupport[5]);
+
+    ADDCITINYGRAM(CI_TINYGRAM_COMPRESSION, go->neg_tinygram, go->tinygram);
+
+    ADDCILANID(CI_LAN_IDENTIFICATION, go->neg_lanid, go->lanid);
+
+    ADDCIMACADDR(CI_MAC_ADDRESS, go->neg_macaddr, go->macaddr);
+
+    ADDCISPANTREE(CI_SPANNING_TREE_PROTOCOL, go->neg_spantree, go->spantree);
+
+    ADDCIMGMTINLINE(CI_MANAGEMENT_INLINE, go->neg_mgmt_inline);
+
+    *lenp -= len;
+}
+
+
+/*
+ * bcp_ackci - Ack our CIs.
+ * Called by fsm_rconfack, Receive Configure ACK.
+ *
+ * Returns:
+ *	0 - Ack was bad.
+ *	1 - Ack was good.
+ */
+static int
+bcp_ackci(f, p, len)
+    fsm *f;
+    u_char *p;
+    int len;
+{
+    bcp_options *go = &bcp_gotoptions[f->unit];
+    u_char cilen, citype, cichar;
+    u_short cishort;
+
+    /*
+     * CIs must be in exactly the same order that we sent...
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+#define ACKCIBRIDGELINEID(opt, neg, lan_bridge_segno) \
+    if (neg) { \
+	if ((len -= CILEN_BRIDGELINEID) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_BRIDGELINEID || citype != opt) \
+	    goto bad; \
+	GETSHORT(cishort, p); \
+	if (lan_bridge_segno != cishort) \
+	    goto bad; \
+    }
+
+#define ACKCIMACSUPPORT(opt, neg, macsupport) \
+    if (neg && macsupport) { \
+	if ((len -= CILEN_MACSUPPORT) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_MACSUPPORT || citype != opt) \
+	    goto bad; \
+	GETCHAR(cichar, p); \
+	if (macsupport != cichar) \
+	    goto bad; \
+    }
+
+#define ACKCITINYGRAM(opt, neg, tinygram) \
+    if (neg) { \
+	if ((len -= CILEN_TINYGRAM) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_TINYGRAM || citype != opt) \
+	    goto bad; \
+	GETCHAR(cichar, p); \
+	if (tinygram != cichar) \
+	    goto bad; \
+    }
+
+#define ACKCILANID(opt, neg, lanid) \
+    if (neg) { \
+	if ((len -= CILEN_LANID) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_LANID || citype != opt) \
+	    goto bad; \
+	GETCHAR(cichar, p); \
+	if (lanid != cichar) \
+	    goto bad; \
+    }
+
+#define ACKCIMACADDR(opt, neg, macaddr) \
+    if (neg) { \
+	if ((len -= CILEN_MACADDR) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_MACADDR || citype != opt) \
+	    goto bad; \
+	GETCHAR(cichar, p); \
+	if (macaddr[0] != cichar) \
+	    goto bad; \
+	GETCHAR(cichar, p); \
+	if (macaddr[1] != cichar) \
+	    goto bad; \
+	GETCHAR(cichar, p); \
+	if (macaddr[2] != cichar) \
+	    goto bad; \
+	GETCHAR(cichar, p); \
+	if (macaddr[3] != cichar) \
+	    goto bad; \
+	GETCHAR(cichar, p); \
+	if (macaddr[4] != cichar) \
+	    goto bad; \
+	GETCHAR(cichar, p); \
+	if (macaddr[5] != cichar) \
+	    goto bad; \
+    }
+
+#define ACKCISPANTREE(opt, neg, spantree) \
+    if (neg) { \
+	if ((len -= CILEN_SPANTREE) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_SPANTREE || citype != opt) \
+	    goto bad; \
+	GETCHAR(cichar, p); \
+	if (spantree != cichar) \
+	    goto bad; \
+    }
+
+#define ACKMGMTINLINE(opt, neg) \
+    if (neg) { \
+	if ((len -= CILEN_MGMT_INLINE) < 0) \
+	    goto bad; \
+	GETCHAR(citype, p); \
+	GETCHAR(cilen, p); \
+	if (cilen != CILEN_MGMT_INLINE || citype != opt) \
+	    goto bad; \
+    }
+
+
+
+    ACKCIBRIDGELINEID(CI_BRIDGE_IDENTIFICATION, go->neg_bridgeid, go->lan_bridge_segno);
+
+    ACKCIBRIDGELINEID(CI_LINE_IDENTIFICATION, go->neg_lineid, go->lan_bridge_segno);
+
+    ACKCIMACSUPPORT(CI_MAC_SUPPORT, go->neg_macsupport, go->macsupport[0]);
+    ACKCIMACSUPPORT(CI_MAC_SUPPORT, go->neg_macsupport, go->macsupport[1]);
+    ACKCIMACSUPPORT(CI_MAC_SUPPORT, go->neg_macsupport, go->macsupport[2]);
+    ACKCIMACSUPPORT(CI_MAC_SUPPORT, go->neg_macsupport, go->macsupport[3]);
+    ACKCIMACSUPPORT(CI_MAC_SUPPORT, go->neg_macsupport, go->macsupport[4]);
+    ACKCIMACSUPPORT(CI_MAC_SUPPORT, go->neg_macsupport, go->macsupport[5]);
+
+    ACKCITINYGRAM(CI_TINYGRAM_COMPRESSION, go->neg_tinygram, go->tinygram);
+
+    ACKCILANID(CI_LAN_IDENTIFICATION, go->neg_lanid, go->lanid);
+
+    ACKCIMACADDR(CI_MAC_ADDRESS, go->neg_macaddr, go->macaddr);
+
+    ACKCISPANTREE(CI_SPANNING_TREE_PROTOCOL, go->neg_spantree, go->spantree);
+
+    ACKMGMTINLINE(CI_MANAGEMENT_INLINE, go->neg_mgmt_inline);
+
+    /*
+     * If there are any remaining CIs, then this packet is bad.
+     */
+    if (len != 0)
+	goto bad;
+    return (1);
+
+bad:
+    BCPDEBUG(("bcp_ackci: received bad Ack!"));
+    return (0);
+}
+
+/*
+ * bcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if BCP is in the OPENED state.
+ * Calback from fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
+ *
+ * Returns:
+ *	0 - Nak was bad.
+ *	1 - Nak was good.
+ */
+static int
+bcp_nakci(f, p, len)
+    fsm *f;
+    u_char *p;
+    int len;
+{
+    bcp_options *go = &bcp_gotoptions[f->unit];
+    u_char cichar;
+    u_char citype, cilen, *next;
+    u_short cishort;
+    bcp_options no;		/* options we've seen Naks for */
+    bcp_options try;		/* options to request next time */
+    u_char bridge_id;
+    u_short lan_segno;
+    u_char macaddr[ETH_ALEN];
+    int mac;
+
+    BZERO(&no, sizeof(no));
+    try = *go;
+
+    /*
+     * Any Nak'd CIs must be in exactly the same order that we sent.
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+#define NAKCIBRIDGELINEID(opt, neg, code) \
+    if (go->neg && \
+	((cilen = p[1]) == CILEN_BRIDGELINEID) && \
+	len >= cilen && \
+	p[0] == opt) { \
+	len -= cilen; \
+	INCPTR(2, p); \
+	GETSHORT(cishort, p); \
+	no.neg = 1; \
+	code \
+    }
+
+#define NAKCIMACADDR(opt, neg, code) \
+    if (go->neg && \
+	((cilen = p[1]) == CILEN_MACADDR) && \
+	len >= cilen && \
+	p[0] == opt) { \
+	len -= cilen; \
+	INCPTR(2, p); \
+	no.neg = 1; \
+  GETCHAR(macaddr[0], p); \
+  GETCHAR(macaddr[1], p); \
+  GETCHAR(macaddr[2], p); \
+  GETCHAR(macaddr[3], p); \
+  GETCHAR(macaddr[4], p); \
+  GETCHAR(macaddr[5], p); \
+	code \
+    }
+
+#define NAKCISPANTREE(opt, neg, code) \
+    if (go->neg && \
+	((cilen = p[1]) == CILEN_SPANTREE) && \
+	len >= cilen && \
+	p[0] == opt) { \
+	len -= cilen; \
+	INCPTR(2, p); \
+	no.neg = 1; \
+	GETCHAR(cichar, p); \
+	code \
+    }
+
+#define NAKCIMGMTINLINE(opt, neg, code) \
+    if (go->neg && \
+	((cilen = p[1]) == CILEN_MGMT_INLINE) && \
+	len >= cilen && \
+	p[0] == opt) { \
+	len -= cilen; \
+	INCPTR(2, p); \
+	no.neg = 1; \
+	code \
+    }
+
+    /*
+     * Accept the peer's value of bridge_id provided that it
+     * is greater than what we asked for.
+     */
+    NAKCIBRIDGELINEID(CI_BRIDGE_IDENTIFICATION, neg_bridgeid,
+      bridge_id = cishort & 0xf;
+      if (bridge_id > 0 &&
+          bridge_id > (go->lan_bridge_segno & 0xf))
+      {
+        try.lan_bridge_segno = cishort;
+      }
+	    );
+
+    /*
+     * Accept the peer's value of lan_segno provided that it
+     * is greater than what we asked for.
+     */
+    NAKCIBRIDGELINEID(CI_LINE_IDENTIFICATION, neg_lineid,
+      lan_segno = cishort >> 4;
+      if (lan_segno > 0 &&
+          lan_segno > (go->lan_bridge_segno >> 4))
+      {
+        try.lan_bridge_segno = cishort;
+      }
+	    );
+
+    /*
+     * Peer is not supposed to send Mac-Support in a Configure-Nak.
+     */
+
+    /*
+     * Peer is not supposed to send TinyGram-Compression in a Configure-Nak
+     * if we already sent it in a Configure-Request already.
+     */
+
+
+    /*
+     * Peer is not supposed to send LAN-Identification in a Configure-Nak.
+     */
+
+    /*
+     * Accept the peer's value of macaddr provided that we requested
+     * an address of 00-00-00-00-00-00 and the suggested address does
+     * NOT have the multicast bit set.
+     */
+    NAKCIMACADDR(CI_MAC_ADDRESS, neg_macaddr,
+      for (mac=0; mac<ETH_ALEN; mac++)
+      {
+        if (go->macaddr[mac] != 0)
+          break;
+      }
+      if (mac == ETH_ALEN)
+      {
+        if (!(macaddr[0] & BCP_MULTICAST))
+        {
+          try.macaddr[0] = macaddr[0];
+          try.macaddr[1] = macaddr[1];
+          try.macaddr[2] = macaddr[2];
+          try.macaddr[3] = macaddr[3];
+          try.macaddr[4] = macaddr[4];
+          try.macaddr[5] = macaddr[5];
+        }
+      }
+	    );
+
+    /*
+     * Accept the peer's value of spantree provided it has a lower
+     * protocol Id than the one we requested.
+     */
+    NAKCISPANTREE(CI_SPANNING_TREE_PROTOCOL, neg_spantree,
+      if (cichar < go->spantree)
+      {
+        try.spantree = cichar;
+      }
+	    );
+
+    NAKCIMGMTINLINE(CI_MANAGEMENT_INLINE, neg_mgmt_inline,
+      do {} while(0);
+	    );
+
+    /*
+     * There may be remaining CIs, if the peer is requesting negotiation
+     * on an option that we didn't include in our request packet.
+     * If we see an option that we requested, or one we've already seen
+     * in this packet, then this packet is bad.
+     * If we wanted to respond by starting to negotiate on the requested
+     * option(s), we could, but we don't, because if we are not negotiating
+     * an option, it is because we were told not to.
+     */
+    while (len > CILEN_VOID) {
+	GETCHAR(citype, p);
+	GETCHAR(cilen, p);
+	if( (len -= cilen) < 0 )
+	    goto bad;
+	next = p + cilen - 2;
+
+	switch (citype) {
+	case CI_BRIDGE_IDENTIFICATION:
+	    if (go->neg_bridgeid || no.neg_bridgeid || cilen != CILEN_BRIDGELINEID)
+		goto bad;
+	    break;
+	case CI_LINE_IDENTIFICATION:
+	    if (go->neg_lineid || no.neg_lineid || cilen != CILEN_BRIDGELINEID)
+		goto bad;
+	    break;
+	case CI_TINYGRAM_COMPRESSION:
+	    if (go->neg_tinygram || cilen != CILEN_TINYGRAM)
+		goto bad;
+	    break;
+	case CI_LAN_IDENTIFICATION:
+	    if (go->neg_lanid || cilen != CILEN_LANID)
+		goto bad;
+	    break;
+	case CI_MAC_ADDRESS:
+	    if (go->neg_macaddr || cilen != CILEN_MACADDR)
+		goto bad;
+	    break;
+	case CI_SPANNING_TREE_PROTOCOL:
+	    if (go->neg_spantree || cilen != CILEN_SPANTREE)
+		goto bad;
+	    break;
+	case CI_MANAGEMENT_INLINE:
+	    if (go->neg_mgmt_inline || cilen != CILEN_MGMT_INLINE)
+		goto bad;
+	    break;
+	}
+	p = next;
+    }
+
+    /*
+     * OK, the Nak is good.  Now we can update state.
+     * If there are any remaining options, we ignore them.
+     */
+    if (f->state != OPENED)
+	*go = try;
+
+    return 1;
+
+bad:
+    BCPDEBUG(("bcp_nakci: received bad Nak!"));
+    return 0;
+}
+
+
+/*
+ * bcp_rejci - Reject some of our CIs.
+ * Callback from fsm_rconfnakrej.
+ */
+static int
+bcp_rejci(f, p, len)
+    fsm *f;
+    u_char *p;
+    int len;
+{
+    bcp_options *go = &bcp_gotoptions[f->unit];
+    u_char cilen, cichar;
+    u_short cishort;
+    bcp_options try;		/* options to request next time */
+
+    try = *go;
+
+    /*
+     * Any Rejected CIs must be in exactly the same order that we sent.
+     * Check packet length and CI length at each step.
+     * If we find any deviations, then this packet is bad.
+     */
+#define REJCIBRIDGELINEID(opt, neg, lan_bridge_segno) \
+    if (go->neg && \
+	((cilen = p[1]) == CILEN_BRIDGELINEID) && \
+	len >= cilen && \
+	p[0] == opt) { \
+	len -= cilen; \
+	INCPTR(2, p); \
+	GETSHORT(cishort, p); \
+	/* Check rejected value. */ \
+	if (cishort != lan_bridge_segno) \
+	    goto bad; \
+	try.neg = 0; \
+    }
+
+#define REJCIMACSUPPORT(opt, neg, macsupport) \
+    if (go->neg && \
+	((cilen = p[1]) == CILEN_MACSUPPORT) && \
+	len >= cilen && \
+	p[0] == opt) { \
+	len -= cilen; \
+	INCPTR(2, p); \
+	GETCHAR(cichar, p); \
+	/* Check rejected value. */ \
+	if (cichar != macsupport) \
+	    goto bad; \
+	try.neg = 0; \
+    }
+
+#define REJCITINYGRAM(opt, neg, tinygram) \
+    if (go->neg && \
+	((cilen = p[1]) == CILEN_TINYGRAM) && \
+	len >= cilen && \
+	p[0] == opt) { \
+	len -= cilen; \
+	INCPTR(2, p); \
+	GETCHAR(cichar, p); \
+	/* Check rejected value. */ \
+	if (cichar != tinygram) \
+	    goto bad; \
+	try.neg = 0; \
+    }
+
+#define REJCILANID(opt, neg, lanid) \
+    if (go->neg && \
+	((cilen = p[1]) == CILEN_LANID) && \
+	len >= cilen && \
+	p[0] == opt) { \
+	len -= cilen; \
+	INCPTR(2, p); \
+	GETCHAR(cichar, p); \
+	/* Check rejected value. */ \
+	if (cichar != lanid) \
+	    goto bad; \
+	try.neg = 0; \
+    }
+
+#define REJCIMACADDR(opt, neg, macaddr) \
+    if (go->neg && \
+	((cilen = p[1]) == CILEN_MACADDR) && \
+	len >= cilen && \
+	p[0] == opt) { \
+	len -= cilen; \
+	INCPTR(2, p); \
+	/* Check rejected value. */ \
+	GETCHAR(cichar, p); \
+	if (macaddr[0] != cichar) \
+	    goto bad; \
+	GETCHAR(cichar, p); \
+	if (macaddr[1] != cichar) \
+	    goto bad; \
+	GETCHAR(cichar, p); \
+	if (macaddr[2] != cichar) \
+	    goto bad; \
+	GETCHAR(cichar, p); \
+	if (macaddr[3] != cichar) \
+	    goto bad; \
+	GETCHAR(cichar, p); \
+	if (macaddr[4] != cichar) \
+	    goto bad; \
+	GETCHAR(cichar, p); \
+	if (macaddr[5] != cichar) \
+	    goto bad; \
+	try.neg = 0; \
+    }
+
+#define REJCISPANTREE(opt, neg, spantree) \
+    if (go->neg && \
+	((cilen = p[1]) == CILEN_SPANTREE) && \
+	len >= cilen && \
+	p[0] == opt) { \
+	len -= cilen; \
+	INCPTR(2, p); \
+	GETCHAR(cichar, p); \
+	/* Check rejected value. */ \
+	if (cichar != spantree) \
+	    goto bad; \
+	try.neg = 0; \
+    }
+
+#define REJCIMGMTINLINE(opt, neg) \
+    if (go->neg && \
+	((cilen = p[1]) == CILEN_MGMT_INLINE) && \
+	len >= cilen && \
+	p[0] == opt) { \
+	len -= cilen; \
+	INCPTR(2, p); \
+	try.neg = 0; \
+    }
+
+    REJCIBRIDGELINEID(CI_BRIDGE_IDENTIFICATION, neg_bridgeid, go->lan_bridge_segno);
+
+    REJCIBRIDGELINEID(CI_LINE_IDENTIFICATION, neg_lineid, go->lan_bridge_segno);
+
+    REJCIMACSUPPORT(CI_MAC_SUPPORT, neg_macsupport, go->macsupport[0]);
+    REJCIMACSUPPORT(CI_MAC_SUPPORT, neg_macsupport, go->macsupport[1]);
+    REJCIMACSUPPORT(CI_MAC_SUPPORT, neg_macsupport, go->macsupport[2]);
+    REJCIMACSUPPORT(CI_MAC_SUPPORT, neg_macsupport, go->macsupport[3]);
+    REJCIMACSUPPORT(CI_MAC_SUPPORT, neg_macsupport, go->macsupport[4]);
+    REJCIMACSUPPORT(CI_MAC_SUPPORT, neg_macsupport, go->macsupport[5]);
+
+    REJCITINYGRAM(CI_TINYGRAM_COMPRESSION, neg_tinygram, go->tinygram);
+
+    REJCILANID(CI_LAN_IDENTIFICATION, neg_lanid, go->lanid);
+
+    REJCIMACADDR(CI_MAC_ADDRESS, neg_macaddr, go->macaddr);
+
+    REJCISPANTREE(CI_SPANNING_TREE_PROTOCOL, neg_spantree, go->spantree);
+
+    REJCIMGMTINLINE(CI_MANAGEMENT_INLINE, neg_mgmt_inline);
+
+    /*
+     * If there are any remaining CIs, then this packet is bad.
+     */
+    if (len != 0)
+	goto bad;
+
+    /* If Management-Inline was rejected, try to negotiate a
+     * Spanning-Tree-Protocol instead. */
+    if (go->neg_mgmt_inline && !try.neg_mgmt_inline)
+	try.neg_spantree = 1;
+
+    /*
+     * Now we can update state.
+     */
+    if (f->state != OPENED)
+	*go = try;
+    return 1;
+
+bad:
+    BCPDEBUG(("bcp_rejci: received bad Reject!"));
+    return 0;
+}
+
+
+/*
+ * bcp_reqci - Check the peer's requested CIs and send appropriate response.
+ * Callback from fsm_rconfreq, Receive Configure Request
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately.  If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+bcp_reqci(f, inp, len, reject_if_disagree)
+    fsm *f;
+    u_char *inp;		/* Requested CIs */
+    int *len;			/* Length of requested CIs */
+    int reject_if_disagree;
+{
+    bcp_options *ho = &bcp_hisoptions[f->unit];
+    bcp_options *ao = &bcp_allowoptions[f->unit];
+    bcp_options *go = &bcp_gotoptions[f->unit];
+    u_char *cip, *next;		/* Pointer to current and next CIs */
+    u_short cilen, citype;	/* Parsed len, type */
+    u_short cishort;		/* Parsed short value */
+    u_char cichar;		/* Parsed char value */
+    u_char bridge_id;
+    u_short lan_segno;
+    int mac;
+    u_char macaddr[ETH_ALEN];
+
+    int rc = CONFACK;		/* Final packet return code */
+    int orc;			/* Individual option return code */
+    u_char *p;			/* Pointer to next char to parse */
+    u_char *ucp = inp;		/* Pointer to current output char */
+    int l = *len;		/* Length left */
+
+    /*
+     * Reset all his options.
+     */
+    BZERO(ho, sizeof(*ho));
+
+    /*
+     * Process all his options.
+     */
+    next = inp;
+    while (l) {
+	orc = CONFACK;			/* Assume success */
+	cip = p = next;			/* Remember begining of CI */
+	if (l < 2 ||			/* Not enough data for CI header or */
+	    p[1] < 2 ||			/*  CI length too small or */
+	    p[1] > l) {			/*  CI length too big? */
+	    BCPDEBUG(("bcp_reqci: bad CI length!"));
+	    orc = CONFREJ;		/* Reject bad CI */
+	    cilen = l;			/* Reject till end of packet */
+	    l = 0;			/* Don't loop again */
+	    goto endswitch;
+	}
+	GETCHAR(citype, p);		/* Parse CI type */
+	GETCHAR(cilen, p);		/* Parse CI length */
+	l -= cilen;			/* Adjust remaining length */
+	next += cilen;			/* Step to next CI */
+
+	switch (citype) {		/* Check CI type */
+  case CI_BRIDGE_IDENTIFICATION:
+     if (!ao->neg_bridgeid ||		/* Allow option? */
+         cilen != CILEN_BRIDGELINEID)   /* Check CI length */
+     {
+       orc = CONFREJ;		/* Reject CI */
+       break;
+     }
+     GETSHORT(cishort, p); /* Parse lan_bridge_segno */
+
+     /*
+      * If we are also negotiating bridgeid, then
+      * both sides must agree on the higher bridgeid.
+      */
+     bridge_id = cishort & 0xf;
+     if ((bridge_id < (go->lan_bridge_segno & 0xf)) &&
+         go->neg_bridgeid)
+     {
+       orc = CONFNAK;		/* Nak CI */
+       DECPTR(sizeof(u_short), p);
+       PUTSHORT(go->lan_bridge_segno, p);	/* Give him a hint */
+       break;
+     }
+     ho->neg_bridgeid = 1;		/* Remember he negotiated this item */
+     ho->lan_bridge_segno = cishort;		/* And remember value */
+     break;
+
+  case CI_LINE_IDENTIFICATION:
+     if (!ao->neg_lineid ||		/* Allow option? */
+         cilen != CILEN_BRIDGELINEID)   /* Check CI length */
+     {
+       orc = CONFREJ;		/* Reject CI */
+       break;
+     }
+     GETSHORT(cishort, p); /* Parse lan_bridge_segno */
+
+     /*
+      * If we are also negotiating lineid, then
+      * both sides must agree on the higher lan_segno.
+      */
+     lan_segno = cishort >> 4;
+     if ((lan_segno < (go->lan_bridge_segno >> 4)) &&
+         go->neg_lineid)
+     {
+       orc = CONFNAK;		/* Nak CI */
+       DECPTR(sizeof(u_short), p);
+       PUTSHORT(go->lan_bridge_segno, p);	/* Give him a hint */
+       break;
+     }
+     ho->neg_lineid = 1;		/* Remember he negotiated this item */
+     ho->lan_bridge_segno = cishort;		/* And remember value */
+     break;
+
+   case CI_MAC_SUPPORT:
+     if (!ao->neg_macsupport ||		/* Allow option? */
+         cilen != CILEN_MACSUPPORT)   /* Check CI length */
+     {
+       orc = CONFREJ;		/* Reject CI */
+       break;
+     }
+     GETCHAR(cichar, p); /* Parse macsupport value */
+
+     /*
+      * Ensure a valid value, else reject.
+      */
+     switch (cichar)
+     {
+     case MAC_IEEE_802_3:
+     case MAC_IEEE_802_4:
+     case MAC_IEEE_802_5_NON:
+     case MAC_FDDI_NON:
+     case MAC_IEEE_802_5:
+     case MAC_FDDI:
+       ho->neg_macsupport = 1;
+       for (mac=0; mac<ETH_ALEN; mac++)
+       {
+         if (ho->macsupport[mac] == 0)
+           ho->macsupport[mac] = cichar;
+       }
+       break;
+     default:
+       orc = CONFREJ;		/* Reject CI */
+       break;
+     }
+     break;
+
+   case CI_TINYGRAM_COMPRESSION:
+     if (!ao->neg_tinygram ||		/* Allow option? */
+         cilen != CILEN_TINYGRAM)   /* Check CI length */
+     {
+       orc = CONFREJ;		/* Reject CI */
+       break;
+     }
+     GETCHAR(cichar, p); /* Parse tinygram value */
+
+     /*
+      * Ensure a valid value, else reject.
+      */
+     if (cichar != 1 && cichar !=2)
+     {
+       orc = CONFREJ;  /* Reject CI */
+       break;
+     }
+     ho->neg_tinygram = 1;		/* Remember he negotiated this item */
+     ho->tinygram = cichar;		/* And remember value */
+     break;
+
+   case CI_LAN_IDENTIFICATION:
+     if (!ao->neg_lanid ||		/* Allow option? */
+         cilen != CILEN_LANID)   /* Check CI length */
+     {
+       orc = CONFREJ;		/* Reject CI */
+       break;
+     }
+     GETCHAR(cichar, p); /* Parse lanid value */
+
+     /*
+      * Ensure a valid value, else reject.
+      */
+     if (cichar != 1 && cichar !=2)
+     {
+       orc = CONFREJ;  /* Reject CI */
+       break;
+     }
+     ho->neg_lanid = 1;		/* Remember he negotiated this item */
+     ho->lanid = cichar;		/* And remember value */
+     break;
+
+
+   case CI_MAC_ADDRESS:
+     if (!ao->neg_macaddr ||		/* Allow option? */
+         cilen != CILEN_MACADDR)   /* Check CI length */
+     {
+       orc = CONFREJ;		/* Reject CI */
+       break;
+     }
+     GETCHAR(macaddr[0], p); /* Parse macaddr value */
+     GETCHAR(macaddr[1], p);
+     GETCHAR(macaddr[2], p);
+     GETCHAR(macaddr[3], p);
+     GETCHAR(macaddr[4], p);
+     GETCHAR(macaddr[5], p);
+
+    /*
+     * Reject macaddr if it has the multicast bit set or
+     * if it is all zeroes.  (All zeroes is a request for
+     * us to assign a MAC address, which we could do, but...)
+     */
+     for (mac=0; mac<ETH_ALEN; mac++)
+     {
+       if (go->macaddr[mac] != 0)
+         break;
+     }
+     if ((mac == ETH_ALEN) || (macaddr[0] & BCP_MULTICAST))
+     {
+       orc = CONFREJ;		/* Reject CI */
+       break;
+     }
+
+     ho->neg_macaddr = 1;		/* Remember he negotiated this item */
+     ho->macaddr[0] = macaddr[0];		/* And remember value */
+     ho->macaddr[1] = macaddr[1];
+     ho->macaddr[2] = macaddr[2];
+     ho->macaddr[3] = macaddr[3];
+     ho->macaddr[4] = macaddr[4];
+     ho->macaddr[5] = macaddr[5];
+     break;
+
+   case CI_SPANNING_TREE_PROTOCOL:
+     if (!ao->neg_spantree ||		/* Allow option? */
+         cilen != CILEN_SPANTREE)   /* Check CI length */
+     {
+       orc = CONFREJ;		/* Reject CI */
+       break;
+     }
+     GETCHAR(cichar, p); /* Parse spantree value */
+
+     /*
+      * Ensure a valid value, else reject.
+      */
+     if (cichar > MAX_SPANTREE)
+     {
+       orc = CONFREJ;  /* Reject CI */
+       break;
+     }
+
+     if ((cichar > go->spantree) && go->neg_spantree)
+     {
+       orc = CONFNAK;		/* Nak CI */
+       DECPTR(sizeof(u_short), p);
+       PUTSHORT(go->spantree, p);	/* Give him a hint */
+       break;
+     }
+
+     ho->neg_spantree = 1;		/* Remember he negotiated this item */
+     ho->spantree = cichar;		/* And remember value */
+     break;
+
+   case CI_MANAGEMENT_INLINE:
+     if (!ao->neg_mgmt_inline ||	/* Allow option? */
+         cilen != CILEN_MGMT_INLINE)	/* Check CI length */
+     {
+       orc = CONFREJ;		/* Reject CI */
+       break;
+     }
+
+     ho->neg_mgmt_inline = 1;		/* Remember he negotiated this item */
+     break;
+
+	default:
+	    orc = CONFREJ;
+	    break;
+	}
+endswitch:
+	if (orc == CONFACK &&		/* Good CI */
+	    rc != CONFACK)		/*  but prior CI wasnt? */
+	    continue;			/* Don't send this one */
+
+	if (orc == CONFNAK) {		/* Nak this CI? */
+	    if (reject_if_disagree)	/* Getting fed up with sending NAKs? */
+		orc = CONFREJ;		/* Get tough if so */
+	    else {
+		if (rc == CONFREJ)	/* Rejecting prior CI? */
+		    continue;		/* Don't send this one */
+		if (rc == CONFACK) {	/* Ack'd all prior CIs? */
+		    rc = CONFNAK;	/* Not anymore... */
+		    ucp = inp;		/* Backup */
+		}
+	    }
+	}
+
+	if (orc == CONFREJ &&		/* Reject this CI */
+	    rc != CONFREJ) {		/*  but no prior ones? */
+	    rc = CONFREJ;
+	    ucp = inp;			/* Backup */
+	}
+
+	/* Need to move CI? */
+	if (ucp != cip)
+	    BCOPY(cip, ucp, cilen);	/* Move it */
+
+	/* Update output pointer */
+	INCPTR(cilen, ucp);
+    }
+
+    *len = ucp - inp;			/* Compute output length */
+    BCPDEBUG(("bcp: returning Configure-%s", CODENAME(rc)));
+    return (rc);			/* Return final code */
+}
+
+
+/*
+ * bcp_up - BCP has come UP.
+ *
+ * Configure the IP network interface appropriately and bring it up.
+ */
+static void
+bcp_up(f)
+    fsm *f;
+{
+    bcp_options *go = &bcp_gotoptions[f->unit];
+    bcp_options *ho = &bcp_hisoptions[f->unit];
+    bcp_options *wo = &bcp_wantoptions[f->unit];
+    u_char *maclocal;
+    char buf[32];
+
+    BCPDEBUG(("bcp: up"));
+
+    /* Choose which MAC address (if any) to assign to the interface. */
+    if (go->neg_macaddr)
+	maclocal = go->macaddr;
+    else if (wo->neg_macaddr) {
+	int i;
+
+	/* If the wanted MAC address is all zeroes, it means we wanted the peer
+	 * to assign us one.  Since we didn't get one, there's a problem. */
+	for (i = 0; i < ETH_ALEN; ++i) {
+	    if (wo->macaddr[i] != 0)
+		break;
+	}
+
+	if (i == ETH_ALEN) {
+	    error("Could not determine local MAC address");
+	    bcp_close(f->unit, "Could not determine local MAC address");
+	    return;
+	}
+
+	maclocal = wo->macaddr;
+    } else
+	maclocal = NULL;
+
+    /* If Management-Inline is not supported, tell the kernel to encapsulate
+     * bridge PDUs in the old RFC 1638 format. */
+    if (!go->neg_mgmt_inline)
+    {
+        sifnpmode(ifunit, PPP_BPDU_IEEE, NPMODE_PASS);
+    }
+
+    /* After this the "bcp%d" device will exist. */
+    sifnpmode(ifunit, PPP_BRIDGE, NPMODE_PASS);
+
+    if (maclocal) {
+        char eth_ifname[16];
+        slprintf(eth_ifname, sizeof(eth_ifname), "bcp%d", ifunit);
+	if (set_if_hwaddr(maclocal, eth_ifname) < 0) {
+	    if (debug)
+		warn("Failed to set hardware address");
+	    bcp_close(f->unit, "Interface configuration failed");
+	    return;
+        }
+    }
+
+    /*
+     * Set up /etc/ppp/eth-up environment.
+     */
+
+    /* MACLOCAL contains the MAC address of the BCP interface, if it has one.
+     * Otherwise, MACLOCAL is defined as an empty string. */
+    if (maclocal)
+	slprintmac(buf, sizeof(buf), maclocal);
+    else
+	buf[0] = '\0';
+    script_setenv("MACLOCAL", buf, 0);
+
+    /* MACREMOTE contains the MAC address of the peer's BCP interface, if the
+     * peer published it during negotiation.  Otherwise, MACREMOTE is defined
+     * as an empty string. */
+    if (ho->neg_macaddr)
+	slprintmac(buf, sizeof(buf), ho->macaddr);
+    else
+	buf[0] = '\0';
+    script_setenv("MACREMOTE", buf, 0);
+
+    np_up(f->unit, PPP_BRIDGE);
+    bcp_is_up = 1;
+
+    /*
+     * Execute the eth-up script, like this:
+     *	/etc/ppp/eth-up interface tty speed local-IP remote-IP
+     */
+    if (bcp_script_state == s_down && bcp_script_pid == 0) {
+	bcp_script_state = s_up;
+	bcp_script(f, _PATH_ETHUP);
+    }
+}
+
+
+/*
+ * bcp_down - BCP has gone DOWN.
+ *
+ */
+static void
+bcp_down(f)
+    fsm *f;
+{
+    /* Execute the eth-down script */
+    if (bcp_script_state == s_up && bcp_script_pid == 0) {
+	bcp_script_state = s_down;
+	bcp_script(f, _PATH_ETHDOWN);
+    }
+
+    /* After this the "bcp%d" device will not exist. */
+    sifnpmode(ifunit, PPP_BRIDGE, NPMODE_DROP);
+
+    sifnpmode(ifunit, PPP_BPDU_IEEE, NPMODE_DROP);
+
+    BCPDEBUG(("bcp: down"));
+    if (bcp_is_up)
+    {
+      bcp_is_up = 0;
+      np_down(f->unit, PPP_BRIDGE);
+    }
+}
+
+
+/*
+ * bcp_start - called when we want the lower layer up.
+ */
+static void
+bcp_start(f)
+    fsm *f;
+{
+    np_start(f->unit, PPP_BRIDGE);
+}
+
+
+/*
+ * bcp_finished - possibly shut down the lower layers.
+ */
+static void
+bcp_finished(f)
+    fsm *f;
+{
+    np_finished(f->unit, PPP_BRIDGE);
+}
+
+
+/*
+ * bcp_script_done - called when the ip-up or ip-down script
+ * has finished.
+ */
+static void
+bcp_script_done(vp_f)
+    void *vp_f;
+{
+    fsm *f = vp_f;
+
+    bcp_script_pid = 0;
+    switch (bcp_script_state) {
+    case s_up:
+	if (f->state != OPENED) {
+	    bcp_script_state = s_down;
+	    bcp_script(f, _PATH_ETHDOWN);
+	}
+	break;
+    case s_down:
+	if (f->state == OPENED) {
+	    bcp_script_state = s_up;
+	    bcp_script(f, _PATH_ETHUP);
+	}
+	break;
+    }
+}
+
+
+/*
+ * bcp_script - Execute a script with arguments
+ * bcp-interface-name phys-interface-name
+ */
+static void
+bcp_script(f, script)
+    fsm *f;
+    char *script;
+{
+    char eth_ifname[16];
+    char *argv[8];
+
+    slprintf(eth_ifname, sizeof(eth_ifname), "bcp%d", ifunit);
+
+    argv[0] = script;
+    argv[1] = eth_ifname;	/* bridge device (e.g. bcp<N>) */
+    argv[2] = devnam;		/* physical device (e.g. hdlc<N>) */
+    argv[3] = NULL;
+    argv[4] = NULL;
+    argv[5] = NULL;
+    argv[6] = NULL;
+    argv[7] = NULL;
+    bcp_script_pid = run_program(script, argv, 0, bcp_script_done, f);
+}
+
+/*
+ * bcp_printpkt - print the contents of an BCP packet.
+ */
+static char *bcp_codenames[] = {
+    "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+    "TermReq", "TermAck", "CodeRej"
+};
+
+static int
+bcp_printpkt(p, plen, printer, arg)
+    u_char *p;
+    int plen;
+    void (*printer) __P((void *, char *, ...));
+    void *arg;
+{
+    int code, id, len, olen;
+    u_char *pstart, *optend;
+    u_short cishort;
+
+    if (plen < HEADERLEN)
+	return 0;
+    pstart = p;
+    GETCHAR(code, p);
+    GETCHAR(id, p);
+    GETSHORT(len, p);
+    if (len < HEADERLEN || len > plen)
+	return 0;
+
+    if (code >= 1 && code <= sizeof(bcp_codenames) / sizeof(char *))
+	printer(arg, " %s", bcp_codenames[code-1]);
+    else
+	printer(arg, " code=0x%x", code);
+    printer(arg, " id=0x%x", id);
+    len -= HEADERLEN;
+    switch (code) {
+    case CONFREQ:
+    case CONFACK:
+    case CONFNAK:
+    case CONFREJ:
+	/* print option list */
+	while (len >= 2) {
+	    GETCHAR(code, p);
+	    GETCHAR(olen, p);
+	    p -= 2;
+	    if (olen < 2 || olen > len) {
+		break;
+	    }
+	    printer(arg, " <");
+	    len -= olen;
+	    optend = p + olen;
+	    switch (code) {
+            case CI_BRIDGE_IDENTIFICATION:
+                if (olen == CILEN_BRIDGELINEID) {
+                    p += 2;
+                    GETSHORT(cishort, p);
+                    printer(arg, "Bridge-ID LAN=0x%03x Bridge=0x%X",
+                            (cishort & 0xFFF0) >> 4,
+                            cishort & 0x000F);
+                }
+                break;
+
+            case CI_LINE_IDENTIFICATION:
+                if (olen == CILEN_BRIDGELINEID) {
+                    p += 2;
+                    GETSHORT(cishort, p);
+                    printer(arg, "Line-ID LAN=0x%03x Bridge=0x%X",
+                            (cishort & 0xFFF0) >> 4,
+                            cishort & 0x000F);
+                }
+                break;
+
+            case CI_MAC_SUPPORT:
+                if (olen == CILEN_MACSUPPORT) {
+                    p += 2;
+                    printer(arg, "MAC-Support");
+                }
+                break;
+
+            case CI_TINYGRAM_COMPRESSION:
+                if (olen == CILEN_TINYGRAM) {
+                    p += 2;
+                    printer(arg, "Tinygram-Compression");
+                }
+                break;
+
+            case CI_LAN_IDENTIFICATION:
+                if (olen == CILEN_LANID) {
+                    p += 2;
+                    printer(arg, "LAN-ID (obsolete)");
+                }
+                break;
+
+            case CI_MAC_ADDRESS:
+                if (olen == CILEN_MACADDR) {
+                    p += 2;
+                    printer(arg, "MAC-Address");
+                }
+                break;
+
+            case CI_SPANNING_TREE_PROTOCOL:
+                if (olen >= CILEN_SPANTREE) {
+                    p += 2;
+                    printer(arg, "Spanning-Tree-Protocol (old format)");
+                }
+                break;
+
+            case CI_IEEE_802_TAGGED_FRAME:
+                if (olen == CILEN_IEEE_802_TAGGED_FRAME) {
+                    p += 2;
+                    printer(arg, "IEEE-802-Tagged-Frame");
+                }
+                break;
+
+            case CI_MANAGEMENT_INLINE:
+                if (olen == CILEN_VOID) {
+                    p += 2;
+                    printer(arg, "Management-Inline");
+                }
+                break;
+
+            default:
+		break;
+	    }
+	    while (p < optend) {
+		GETCHAR(code, p);
+		printer(arg, " %.2x", code);
+	    }
+	    printer(arg, ">");
+	}
+	break;
+
+    case TERMACK:
+    case TERMREQ:
+	if (len > 0 && *p >= ' ' && *p < 0x7f) {
+	    printer(arg, " ");
+	    print_string((char *)p, len, printer, arg);
+	    p += len;
+	    len = 0;
+	}
+	break;
+    }
+
+    /* print the rest of the bytes in the packet */
+    for (; len > 0; --len) {
+	GETCHAR(code, p);
+	printer(arg, " %.2x", code);
+    }
+
+    return p - pstart;
+}
+
+/*
+ * bcp_check_options - check that any IP-related options are OK,
+ * and assign appropriate defaults.
+ */
+static void
+bcp_check_options()
+{
+}
+
+
diff -wbBurN pppd-2.4.1/pppd/bcp.h pppd-ai/pppd/bcp.h
--- pppd-2.4.1/pppd/bcp.h	1969-12-31 19:00:00.000000000 -0500
+++ pppd-ai/pppd/bcp.h	2004-02-25 08:34:47.000000000 -0500
@@ -0,0 +1,73 @@
+/*
+ * bcp.h - Bridge Control Protocol definitions.
+ *
+ * Copyright (c) 2001-2004 Applied Innovation Inc.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms.  The name of Applied Innovation Inc.
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * Options.
+ */
+#define CI_BRIDGE_IDENTIFICATION	1
+#define CI_LINE_IDENTIFICATION		2
+#define CI_MAC_SUPPORT			3
+#define CI_TINYGRAM_COMPRESSION		4
+#define CI_LAN_IDENTIFICATION		5	/* obsolete (since RFC 2878) */
+#define CI_MAC_ADDRESS			6
+#define CI_SPANNING_TREE_PROTOCOL	7	/* old format (RFC 1638) */
+#define CI_IEEE_802_TAGGED_FRAME	8
+#define CI_MANAGEMENT_INLINE		9	/* new format (RFC 2878) */
+
+/* values for MAC Support */
+#define MAC_IEEE_802_3			1
+#define MAC_IEEE_802_4			2
+#define MAC_IEEE_802_5_NON		3
+#define MAC_FDDI_NON			4
+#define MAC_IEEE_802_5			11
+#define MAC_FDDI			12
+
+/* Multicast bit */
+#define BCP_MULTICAST			1
+
+/* values for Spanning Tree protocol */
+#define SPAN_NONE			0
+#define SPAN_IEEE_802_1D		1
+#define SPAN_IEEE_802_1G		2
+#define SPAN_IBM			3
+#define SPAN_DEC			4
+#define MAX_SPANTREE			SPAN_DEC
+
+#define ETH_ALEN			6	/* ethernet address length */
+
+typedef struct bcp_options {
+    u_int16_t	lan_bridge_segno;
+    u_char	macsupport[6];	/* MAC support */
+    u_char	tinygram;	/* Tinygram Compression 1=enable, 2=disable */
+    u_char	lanid;		/* Lan ID 1=enable, 2=disable*/
+    u_char	macaddr[ETH_ALEN];	/* MAC Address */
+    u_char	spantree;	/* Spanning Tree Protocol */
+    bool	neg_bridgeid;
+    bool	neg_lineid;
+    bool	neg_macsupport;
+    bool	neg_tinygram;
+    bool	neg_lanid;
+    bool	neg_macaddr;
+    bool	neg_spantree;
+    bool	neg_mgmt_inline;
+} bcp_options;
+
+extern fsm bcp_fsm[];
+extern bcp_options bcp_wantoptions[];
+extern bcp_options bcp_gotoptions[];
+extern bcp_options bcp_allowoptions[];
+extern bcp_options bcp_hisoptions[];
+
+extern struct protent bcp_protent;
diff -wbBurN pppd-2.4.1/pppd/fsm.c pppd-ai/pppd/fsm.c
--- pppd-2.4.1/pppd/fsm.c	2004-02-25 08:27:34.000000000 -0500
+++ pppd-ai/pppd/fsm.c	2004-02-25 08:34:48.000000000 -0500
@@ -195,6 +195,8 @@
     switch( f->state ){
     case STARTING:
 	f->state = INITIAL;
+        if( f->callbacks->finished )
+	    (*f->callbacks->finished)(f);
 	break;
     case STOPPED:
 	f->state = CLOSED;
diff -wbBurN pppd-2.4.1/pppd/ipcp.c pppd-ai/pppd/ipcp.c
--- pppd-2.4.1/pppd/ipcp.c	2004-02-25 08:27:34.000000000 -0500
+++ pppd-ai/pppd/ipcp.c	2004-02-25 08:34:48.000000000 -0500
@@ -80,6 +80,7 @@
 static int  ipcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
 static void ipcp_up __P((fsm *));		/* We're UP */
 static void ipcp_down __P((fsm *));		/* We're DOWN */
+static void ipcp_start __P((fsm *));	/* Need lower layer */
 static void ipcp_finished __P((fsm *));	/* Don't need lower layer */
 
 fsm ipcp_fsm[NUM_PPP];		/* IPCP fsm structure */
@@ -94,7 +95,7 @@
     ipcp_reqci,			/* Request peer's Configuration Information */
     ipcp_up,			/* Called when fsm reaches OPENED state */
     ipcp_down,			/* Called when fsm leaves OPENED state */
-    NULL,			/* Called when we want the lower layer up */
+    ipcp_start,			/* Called when we want the lower layer up */
     ipcp_finished,		/* Called when we want the lower layer down */
     NULL,			/* Called when Protocol-Reject received */
     NULL,			/* Retransmission is necessary */
@@ -1789,6 +1790,17 @@
 
 
 /*
+ * ipcp_start - called when we want the lower layer up.
+ */
+static void
+ipcp_start(f)
+    fsm *f;
+{
+    np_start(f->unit, PPP_IP);
+}
+
+
+/*
  * ipcp_finished - possibly shut down the lower layers.
  */
 static void
diff -wbBurN pppd-2.4.1/pppd/ipv6cp.c pppd-ai/pppd/ipv6cp.c
--- pppd-2.4.1/pppd/ipv6cp.c	2004-02-25 08:27:34.000000000 -0500
+++ pppd-ai/pppd/ipv6cp.c	2004-02-25 08:34:48.000000000 -0500
@@ -146,6 +146,7 @@
 static int  ipv6cp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
 static void ipv6cp_up __P((fsm *));		/* We're UP */
 static void ipv6cp_down __P((fsm *));		/* We're DOWN */
+static void ipv6cp_start __P((fsm *));		/* Need lower layer */
 static void ipv6cp_finished __P((fsm *));	/* Don't need lower layer */
 
 fsm ipv6cp_fsm[NUM_PPP];		/* IPV6CP fsm structure */
@@ -160,7 +161,7 @@
     ipv6cp_reqci,		/* Request peer's Configuration Information */
     ipv6cp_up,			/* Called when fsm reaches OPENED state */
     ipv6cp_down,		/* Called when fsm leaves OPENED state */
-    NULL,			/* Called when we want the lower layer up */
+    ipv6cp_start,		/* Called when we want the lower layer up */
     ipv6cp_finished,		/* Called when we want the lower layer down */
     NULL,			/* Called when Protocol-Reject received */
     NULL,			/* Retransmission is necessary */
@@ -1309,6 +1310,17 @@
 
 
 /*
+ * ipv6cp_start - called when we want the lower layer up.
+ */
+static void
+ipv6cp_start(f)
+    fsm *f;
+{
+    np_start(f->unit, PPP_IPV6);
+}
+
+
+/*
  * ipv6cp_finished - possibly shut down the lower layers.
  */
 static void
diff -wbBurN pppd-2.4.1/pppd/ipxcp.c pppd-ai/pppd/ipxcp.c
--- pppd-2.4.1/pppd/ipxcp.c	2004-02-25 08:27:34.000000000 -0500
+++ pppd-ai/pppd/ipxcp.c	2004-02-25 08:34:48.000000000 -0500
@@ -64,6 +64,7 @@
 static int  ipxcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
 static void ipxcp_up __P((fsm *));		/* We're UP */
 static void ipxcp_down __P((fsm *));		/* We're DOWN */
+static void ipxcp_start __P((fsm *));		/* Need lower layer */
 static void ipxcp_finished __P((fsm *));	/* Don't need lower layer */
 static void ipxcp_script __P((fsm *, char *)); /* Run an up/down script */
 
@@ -79,7 +80,7 @@
     ipxcp_reqci,		/* Request peer's Configuration Information */
     ipxcp_up,			/* Called when fsm reaches OPENED state */
     ipxcp_down,			/* Called when fsm leaves OPENED state */
-    NULL,			/* Called when we want the lower layer up */
+    ipxcp_start,		/* Called when we want the lower layer up */
     ipxcp_finished,		/* Called when we want the lower layer down */
     NULL,			/* Called when Protocol-Reject received */
     NULL,			/* Retransmission is necessary */
@@ -1358,6 +1359,17 @@
 
 
 /*
+ * ipxcp_start - called when we want the lower layer up.
+ */
+static void
+ipxcp_start(f)
+    fsm *f;
+{
+    np_start(f->unit, PPP_IPX);
+}
+
+
+/*
  * ipxcp_finished - possibly shut down the lower layers.
  */
 static void
diff -wbBurN pppd-2.4.1/pppd/main.c pppd-ai/pppd/main.c
--- pppd-2.4.1/pppd/main.c	2004-02-25 08:27:34.000000000 -0500
+++ pppd-ai/pppd/main.c	2004-02-25 08:34:48.000000000 -0500
@@ -45,6 +45,7 @@
 #include "pppd.h"
 #include "magic.h"
 #include "fsm.h"
+#include "bcp.h"
 #include "lcp.h"
 #include "ipcp.h"
 #ifdef INET6
@@ -212,6 +213,7 @@
 #ifdef AT_CHANGE
     &atcp_protent,
 #endif
+    &bcp_protent,
     NULL
 };
 
diff -wbBurN pppd-2.4.1/pppd/Makefile.linux pppd-ai/pppd/Makefile.linux
--- pppd-2.4.1/pppd/Makefile.linux	2004-02-25 08:27:34.000000000 -0500
+++ pppd-ai/pppd/Makefile.linux	2004-02-25 08:34:47.000000000 -0500
@@ -7,13 +7,13 @@
 BINDIR = /usr/sbin
 MANDIR = /usr/man
 
-PPPDSRCS = main.c magic.c fsm.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \
+PPPDSRCS = main.c magic.c fsm.c bcp.c lcp.c ipcp.c upap.c chap.c md5.c ccp.c \
 	   ipxcp.c auth.c options.c sys-linux.c md4.c chap_ms.c cbcp.c \
 	   demand.c utils.c multilink.c tdb.c tty.c
 HEADERS =  callout.h pathnames.h patchlevel.h chap.h md5.h chap_ms.h md4.h \
 	   ipxcp.h cbcp.h tdb.h
 MANPAGES = pppd.8
-PPPDOBJS = main.o magic.o fsm.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
+PPPDOBJS = main.o magic.o fsm.o bcp.o lcp.o ipcp.o upap.o chap.o md5.o ccp.o \
 	   auth.o options.o demand.o utils.o sys-linux.o ipxcp.o multilink.o \
 	   tdb.o tty.o
 
diff -wbBurN pppd-2.4.1/pppd/pathnames.h pppd-ai/pppd/pathnames.h
--- pppd-2.4.1/pppd/pathnames.h	2004-02-25 08:27:34.000000000 -0500
+++ pppd-ai/pppd/pathnames.h	2004-02-25 08:34:48.000000000 -0500
@@ -29,6 +29,8 @@
 #define _PATH_CONNERRS	 _ROOT_PATH "/etc/ppp/connect-errors"
 #define _PATH_PEERFILES	 _ROOT_PATH "/etc/ppp/peers/"
 #define _PATH_RESOLV	 _ROOT_PATH "/etc/ppp/resolv.conf"
+#define _PATH_ETHUP	 _ROOT_PATH "/etc/ppp/eth-up"
+#define _PATH_ETHDOWN	 _ROOT_PATH "/etc/ppp/eth-down"
 
 #define _PATH_USEROPT	 ".ppprc"
 
diff -wbBurN pppd-2.4.1/pppd/pppd.h pppd-ai/pppd/pppd.h
--- pppd-2.4.1/pppd/pppd.h	2004-02-25 08:27:34.000000000 -0500
+++ pppd-ai/pppd/pppd.h	2004-02-25 08:34:48.000000000 -0500
@@ -450,6 +450,7 @@
 void start_networks __P((void));  /* start all the network control protos */
 void np_up __P((int, int));	  /* a network protocol has come up */
 void np_down __P((int, int));	  /* a network protocol has gone down */
+void np_start __P((int, int));    /* a network protocol needs link */
 void np_finished __P((int, int)); /* a network protocol no longer needs link */
 void auth_peer_fail __P((int, int));
 				/* peer failed to authenticate itself */
@@ -496,6 +497,8 @@
 int  open_ppp_loopback __P((void)); /* Open loopback for demand-dialling */
 int  tty_establish_ppp __P((int));  /* Turn serial port into a ppp interface */
 void tty_disestablish_ppp __P((int)); /* Restore port to normal operation */
+void generic_disestablish_ppp __P((int dev_fd)); /* Restore device setting */
+int  generic_establish_ppp __P((int dev_fd, int chindex)); /* Make a ppp interface */
 void make_new_bundle __P((int, int, int, int)); /* Create new bundle */
 int  bundle_attach __P((int));	/* Attach link to existing bundle */
 void cfg_bundle __P((int, int, int, int)); /* Configure existing bundle */
@@ -566,6 +569,7 @@
 int  sipxfaddr __P((int, unsigned long, unsigned char *));
 int  cipxfaddr __P((int));
 #endif
+int  set_if_hwaddr __P((const u_char *addr, const char *name));
 int  get_if_hwaddr __P((u_char *addr, char *name));
 char *get_first_ethernet __P((void));
 
@@ -700,6 +704,7 @@
 #ifdef DEBUGALL
 #define DEBUGMAIN	1
 #define DEBUGFSM	1
+#define DEBUGBCP	1
 #define DEBUGLCP	1
 #define DEBUGIPCP	1
 #define DEBUGIPV6CP	1
@@ -735,6 +740,12 @@
 #define FSMDEBUG(x)
 #endif
 
+#ifdef DEBUGBCP
+#define BCPDEBUG(x)	if (debug) dbglog x
+#else
+#define BCPDEBUG(x)
+#endif
+
 #ifdef DEBUGLCP
 #define LCPDEBUG(x)	if (debug) dbglog x
 #else
diff -wbBurN pppd-2.4.1/pppd/sys-linux.c pppd-ai/pppd/sys-linux.c
--- pppd-2.4.1/pppd/sys-linux.c	2004-02-25 08:27:34.000000000 -0500
+++ pppd-ai/pppd/sys-linux.c	2004-02-25 08:34:48.000000000 -0500
@@ -128,7 +128,6 @@
 static int sock6_fd = -1;
 #endif /* INET6 */
 static int ppp_dev_fd = -1;	/* fd for /dev/ppp (new style driver) */
-static int chindex;		/* channel index (new style driver) */
 
 static fd_set in_fds;		/* set of fds that wait_input waits for */
 static int max_in_fd;		/* highest fd set in in_fds */
@@ -141,7 +140,7 @@
 static int restore_term        = 0;	/* 1 => we've munged the terminal */
 static struct termios inittermios;	/* Initial TTY termios */
 
-static int new_style_driver = 0;
+int new_style_driver = 0;
 
 static char loop_name[20];
 static unsigned char inbuf[512]; /* buffer for chars read from loopback */
@@ -359,9 +359,7 @@
 
 int tty_establish_ppp (int tty_fd)
 {
-    int x;
-    int fd = -1;
-
+    int ret_fd;
 /*
  * Ensure that the tty device is in exclusive mode.
  */
@@ -370,14 +368,6 @@
 	    warn("Couldn't make tty exclusive: %m");
     }
 /*
- * Demand mode - prime the old ppp device to relinquish the unit.
- */
-    if (!new_style_driver && looped
-	&& ioctl(slave_fd, PPPIOCXFERUNIT, 0) < 0) {
-	error("ioctl(transfer ppp unit): %m");
-	return -1;
-    }
-/*
  * Set the current tty to the PPP discpline
  */
 
@@ -393,21 +383,62 @@
     }
 
     if (new_style_driver) {
-	/* Open another instance of /dev/ppp and connect the channel to it */
-	int flags;
-
+        int chindex;
 	if (ioctl(tty_fd, PPPIOCGCHAN, &chindex) == -1) {
 	    error("Couldn't get channel number: %m");
-	    goto err;
+            ret_fd = -1;
+        } else {
+            ret_fd = generic_establish_ppp(tty_fd, chindex);
+        }
+    } else {
+        ret_fd = generic_establish_ppp(tty_fd, -1);
+    }
+
+    if (ret_fd < 0) {
+        if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0 && !ok_error(errno))
+            warn("Couldn't reset tty to normal line discipline: %m");
+    }
+    else
+    {
+#define SC_RCVB	(SC_RCV_B7_0 | SC_RCV_B7_1 | SC_RCV_EVNP | SC_RCV_ODDP)
+#define SC_LOGB	(SC_DEBUG | SC_LOG_INPKT | SC_LOG_OUTPKT | SC_LOG_RAWIN \
+		 | SC_LOG_FLUSH)
+
+    set_flags(ppp_fd, ((get_flags(ppp_fd) & ~(SC_RCVB | SC_LOGB))
+		       | ((kdebugflag * SC_DEBUG) & SC_LOGB)));
+    }
+
+    return ret_fd;
+}
+
+/********************************************************************
+ *
+ * generic_establish_ppp - Turn the fd into a ppp interface.
+ */
+int generic_establish_ppp (int fd, int channel)
+{
+    int x;
+/*
+ * Demand mode - prime the old ppp device to relinquish the unit.
+ */
+    if (!new_style_driver && looped
+	&& ioctl(slave_fd, PPPIOCXFERUNIT, 0) < 0) {
+	error("ioctl(transfer ppp unit): %m");
+	return -1;
 	}
-	dbglog("using channel %d", chindex);
+
+    if (new_style_driver) {
+	/* Open another instance of /dev/ppp and connect the channel to it */
+	int flags;
+
+	dbglog("using channel %d", channel);
 	fd = open("/dev/ppp", O_RDWR);
 	if (fd < 0) {
 	    error("Couldn't reopen /dev/ppp: %m");
 	    goto err;
 	}
-	if (ioctl(fd, PPPIOCATTCHAN, &chindex) < 0) {
-	    error("Couldn't attach to channel %d: %m", chindex);
+	if (ioctl(fd, PPPIOCATTCHAN, &channel) < 0) {
+	    error("Couldn't attach to channel %d: %m", channel);
 	    goto err_close;
 	}
 	flags = fcntl(fd, F_GETFL);
@@ -440,8 +472,8 @@
 	/*
 	 * Old-style driver: find out which interface we were given.
 	 */
-	set_ppp_fd (tty_fd);
-	if (ioctl(tty_fd, PPPIOCGUNIT, &x) < 0) {	
+	set_ppp_fd (fd);
+	if (ioctl(fd, PPPIOCGUNIT, &x) < 0) {
 	    if (ok_error (errno))
 		goto err;
 	    fatal("ioctl(PPPIOCGUNIT): %m(%d)", errno);
@@ -454,9 +486,9 @@
 	/*
 	 * Fetch the initial file flags and reset blocking mode on the file.
 	 */
-	initfdflags = fcntl(tty_fd, F_GETFL);
+	initfdflags = fcntl(fd, F_GETFL);
 	if (initfdflags == -1 ||
-	    fcntl(tty_fd, F_SETFL, initfdflags | O_NONBLOCK) == -1) {
+	    fcntl(fd, F_SETFL, initfdflags | O_NONBLOCK) == -1) {
 	    if ( ! ok_error (errno))
 		warn("Couldn't set device to non-blocking mode: %m");
 	}
@@ -470,13 +503,6 @@
     if (!looped)
 	set_kdebugflag (kdebugflag);
 
-#define SC_RCVB	(SC_RCV_B7_0 | SC_RCV_B7_1 | SC_RCV_EVNP | SC_RCV_ODDP)
-#define SC_LOGB	(SC_DEBUG | SC_LOG_INPKT | SC_LOG_OUTPKT | SC_LOG_RAWIN \
-		 | SC_LOG_FLUSH)
-
-    set_flags(ppp_fd, ((get_flags(ppp_fd) & ~(SC_RCVB | SC_LOGB))
-		       | ((kdebugflag * SC_DEBUG) & SC_LOGB)));
-
     SYSDEBUG ((LOG_NOTICE, "Using version %d.%d.%d of PPP driver",
 	    driver_version, driver_modification, driver_patch));
 
@@ -485,22 +511,19 @@
  err_close:
     close(fd);
  err:
-    if (ioctl(tty_fd, TIOCSETD, &tty_disc) < 0 && !ok_error(errno))
-	warn("Couldn't reset tty to normal line discipline: %m");
     return -1;
 }
 
 /********************************************************************
  *
- * tty_disestablish_ppp - Restore the serial port to normal operation,
- * and reconnect the ppp unit to the loopback if in demand mode.
+ * tty_disestablish_ppp - Restore the serial port to normal operation.
  * This shouldn't call die() because it's called from die().
  */
 
 void tty_disestablish_ppp(int tty_fd)
 {
-    if (demand)
-	restore_loop();
+    generic_disestablish_ppp(tty_fd);
+
     if (!hungup) {
 /*
  * Flush the tty output buffer so that the TIOCSETD doesn't hang.
@@ -526,13 +549,47 @@
 		warn("Couldn't restore device fd flags: %m");
 	}
     }
+}
+
+/********************************************************************
+ *
+ * generic_disestablish_ppp - Restore device components to normal
+ * operation, and reconnect the ppp unit to the loopback if in demand
+ * mode.  This shouldn't call die() because it's called from die().
+*/
+void generic_disestablish_ppp(int dev_fd){
+
+    /* Restore loop if needed */
+    if(demand)
+	restore_loop();
+
+    /* Finally detach the device */
     initfdflags = -1;
 
     if (new_style_driver) {
+	if (!multilink && ioctl(ppp_fd, PPPIOCDISCONN) < 0)
+	    error("Couldn't detach from PPP unit %d: %m", ifunit);
+
 	close(ppp_fd);
 	ppp_fd = -1;
-	if (!looped && ifunit >= 0 && ioctl(ppp_dev_fd, PPPIOCDETACH) < 0)
+
+	if (!looped && ifunit >= 0) {
+	    if (ioctl(ppp_dev_fd, PPPIOCDETACH) < 0) {
+		/* linux/Documentation/networking/ppp_generic.txt says,
+		 *
+		 *     This ioctl is deprecated since the same effect can be
+		 *     achieved by closing the instance.  In order to prevent
+		 *     possible races this ioctl will fail with an EINVAL error
+		 *     if more than one file descriptor refers to this instance
+		 *     (i.e. as a result of dup(), dup2() or fork()).
+		 *
+		 * Testers report seeing this message, therefore I have quelled
+		 * the error when EINVAL is returned. -- Dan Eble
+		 */
+		if (errno != EINVAL)
 	    error("Couldn't release PPP unit: %m");
+	    }
+	}
 	if (!multilink)
 	    remove_fd(ppp_dev_fd);
     }
@@ -1712,6 +1771,30 @@
 }
 
 /*
+ * set_if_hwaddr - set the hardware address for the specified
+ * network interface device.
+ */
+int
+set_if_hwaddr(const u_char *addr, const char *name)
+{
+	struct ifreq ifreq;
+	int ret, sock_fd;
+
+	sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sock_fd < 0)
+		return 0;
+	memset(&ifreq.ifr_hwaddr, 0, sizeof(struct sockaddr));
+	strlcpy(ifreq.ifr_name, name, sizeof(ifreq.ifr_name));
+	ret = ioctl(sock_fd, SIOCGIFHWADDR, &ifreq);
+        if (ret >= 0) {
+            memcpy(ifreq.ifr_hwaddr.sa_data, addr, 6);
+            ret = ioctl(sock_fd, SIOCSIFHWADDR, &ifreq);
+        }
+	close(sock_fd);
+	return ret;
+}
+
+/*
  * get_if_hwaddr - get the hardware address for the specified
  * network interface device.
  */
@@ -2567,7 +2650,7 @@
  * Just to be sure, set the real serial port to the normal discipline.
  */
 
-static void
+void
 restore_loop(void)
 {
     looped = 1;


More information about the Bridge mailing list