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@xxxxxxxxxx> _____ . 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;