The BCP patch I posted last week was missing the definition of
ETH_FCS_LEN.  This one includes it.

The original message, which includes some explanation, is archived at
http://marc.theaimsgroup.com/?l=linux-ppp&r=1&b=200402&w=2 (and many 
other places, I expect).

-- 
Dan Eble <[EMAIL PROTECTED]>  _____  .
Software Engineer          |  _  |/|
Applied Innovation Inc.    | |_| | |
http://www.aiinet.com/     |__/|_|_|
diff -wbBurN linux-2.4.21-pre4/include/linux/if_ether.h 
linux-ai/include/linux/if_ether.h
--- linux-2.4.21-pre4/include/linux/if_ether.h  2004-02-25 08:26:45.000000000 -0500
+++ linux-ai/include/linux/if_ether.h   2004-02-25 08:35:18.000000000 -0500
@@ -31,6 +31,7 @@
 #define ETH_ZLEN       60              /* Min. octets in frame sans FCS */
 #define ETH_DATA_LEN   1500            /* Max. octets in payload        */
 #define ETH_FRAME_LEN  1514            /* Max. octets in frame sans FCS */
+#define ETH_FCS_LEN    4               /* Frame Check Sequence Length   */
 
 /*
  *     These are the defined Ethernet Protocol ID's.
diff -wbBurN linux-2.4.21-pre4/include/linux/ppp_defs.h 
linux-ai/include/linux/ppp_defs.h
--- linux-2.4.21-pre4/include/linux/ppp_defs.h  2004-02-25 08:26:46.000000000 -0500
+++ linux-ai/include/linux/ppp_defs.h   2004-02-25 08:35:18.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 */
@@ -174,6 +177,34 @@
     time_t recv_idle;          /* time since last NP packet received */
 };
 
+/*
+ * Bridging Control Protocol (BCP)
+ */
+struct bcp_hdr {
+       __u8    flags;
+       __u8    mactype;
+       __u8    padbyte;        /* not used (present when "control" is also) */
+       __u8    control;        /* 802.4, 802.5, and FDDI only */
+};
+#define BCP_802_3_HDRLEN       2
+
+/*
+ * Fields in bcp_hdr::flags.
+ */
+#define BCP_LAN_FCS            0x80    /* set when LAN FCS is present */
+#define BCP_ZERO_PAD           0x20    /* set to pad 802.3 to min size */
+#define BCP_PADS_MASK          0x0F    /* 0-15 bytes padding before PPP FCS */
+
+/*
+ * Values for bcp_hdr::mactype.
+ */
+#define BCP_MAC_802_3          0x01    /* 802.3 / Ethernet */
+#define BCP_MAC_802_4          0x02
+#define BCP_MAC_802_5_NC       0x03    /* with non-canonical address */
+#define BCP_MAC_FDDI_NC                0x04    /* with non-canonical address */
+#define BCP_MAC_802_5          0x0B    /* with canonical address */
+#define BCP_MAC_FDDI           0x0C    /* with canonical address */
+
 #ifndef __P
 #ifdef __STDC__
 #define __P(x) x
diff -wbBurN linux-2.4.21-pre4/drivers/net/ppp_generic.c 
linux-ai/drivers/net/ppp_generic.c
--- linux-2.4.21-pre4/drivers/net/ppp_generic.c 2004-02-25 08:26:18.000000000 -0500
+++ linux-ai/drivers/net/ppp_generic.c  2004-02-25 08:35:08.000000000 -0500
@@ -30,6 +30,7 @@
 #include <linux/list.h>
 #include <linux/devfs_fs_kernel.h>
 #include <linux/netdevice.h>
+#include <linux/etherdevice.h>
 #include <linux/poll.h>
 #include <linux/ppp_defs.h>
 #include <linux/filter.h>
@@ -37,6 +38,7 @@
 #include <linux/ppp_channel.h>
 #include <linux/ppp-comp.h>
 #include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
 #include <linux/rtnetlink.h>
 #include <linux/if_arp.h>
 #include <linux/ip.h>
@@ -57,7 +59,9 @@
 #define NP_IPV6        1               /* Internet Protocol V6 */
 #define NP_IPX 2               /* IPX protocol */
 #define NP_AT  3               /* Appletalk protocol */
-#define NUM_NP 4               /* Number of NPs. */
+#define NP_BRIDGE      4       /* Bridged LAN packets */
+#define NP_BPDU_IEEE   5       /* IEEE 802.1 (D or G) bridge PDU */
+#define NUM_NP 6               /* Number of NPs. */
 
 #define MPHDRLEN       6       /* multilink protocol header length */
 #define MPHDRLEN_SSN   4       /* ditto with short sequence numbers */
@@ -88,6 +92,8 @@
 
 #define ROUNDUP(n, x)          (((n) + (x) - 1) / (x))
 
+struct bcp_device;
+
 /*
  * Data structure describing one ppp unit.
  * A ppp unit corresponds to a ppp network interface device
@@ -116,6 +122,7 @@
        unsigned long   last_xmit;      /* jiffies when last pkt sent 9c */
        unsigned long   last_recv;      /* jiffies when last pkt rcvd a0 */
        struct net_device *dev;         /* network interface device a4 */
+       struct bcp_device *bcp;         /* net. interface for bridging */
 #ifdef CONFIG_PPP_MULTILINK
        int             nxchan;         /* next channel to send something on */
        u32             nxseq;          /* next sequence number to send */
@@ -131,6 +138,28 @@
 #endif /* CONFIG_PPP_FILTER */
 };
 
+struct bcp_device {
+       struct net_device dev;          /* ->priv points to struct ppp */
+       struct net_device_stats stats;  /* statistics */
+};
+
+/* this looks better than a cast in the code */
+static __inline__ struct bcp_device* dev_to_bcp(struct net_device* dev)
+{
+       return (struct bcp_device*)dev;
+}
+
+/* this looks better than a cast in the code */
+static __inline__ struct net_device* bcp_to_dev(struct bcp_device* bcp)
+{
+       return (struct net_device*)bcp;
+}
+
+static __inline__ struct ppp* bcp_to_ppp(struct bcp_device* bcp)
+{
+       return bcp ? (struct ppp *)bcp_to_dev(bcp)->priv : NULL;
+}
+
 /*
  * Bits in flags: SC_NO_TCP_CCID, SC_CCP_OPEN, SC_CCP_UP, SC_LOOP_TRAFFIC,
  * SC_MULTILINK, SC_MP_SHORTSEQ, SC_MP_XSHORTSEQ, SC_COMP_TCP, SC_REJ_COMP_TCP.
@@ -237,6 +266,9 @@
 /* Prototypes. */
 static int ppp_unattached_ioctl(struct ppp_file *pf, struct file *file,
                                unsigned int cmd, unsigned long arg);
+static int ppp_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static int ppp_common_start_xmit(int npi, struct sk_buff *skb,
+                                struct net_device *dev);
 static void ppp_xmit_process(struct ppp *ppp);
 static void ppp_send_frame(struct ppp *ppp, struct sk_buff *skb);
 static void ppp_push(struct ppp *ppp);
@@ -268,6 +300,12 @@
 static int ppp_connect_channel(struct channel *pch, int unit);
 static int ppp_disconnect_channel(struct channel *pch);
 static void ppp_destroy_channel(struct channel *pch);
+static int ppp_create_bcp(struct ppp *ppp);
+static void ppp_shutdown_bcp(struct ppp *ppp);
+static struct net_device_stats *bcp_net_stats(struct net_device *dev);
+static int bcp_net_ioctl(struct net_device *, struct ifreq *ifr, int cmd);
+static int bcp_net_change_mtu(struct net_device *dev, int new_mtu);
+static int bcp_start_xmit(struct sk_buff *skb, struct net_device *dev);
 
 /* Translates a PPP protocol number to a NP index (NP == network protocol) */
 static inline int proto_to_npindex(int proto)
@@ -281,6 +319,10 @@
                return NP_IPX;
        case PPP_AT:
                return NP_AT;
+       case PPP_BRIDGE:
+               return NP_BRIDGE;
+       case PPP_BPDU_IEEE:
+               return NP_BPDU_IEEE;
        }
        return -EINVAL;
 }
@@ -291,6 +333,8 @@
        PPP_IPV6,
        PPP_IPX,
        PPP_AT,
+       PPP_BRIDGE,
+       PPP_BPDU_IEEE,
 };
        
 /* Translates an ethertype into an NP index */
@@ -318,6 +362,13 @@
        ETH_P_PPPTALK,
 };
 
+/* IEEE 802.1D bridge PDU destination address */
+static const __u8 IEEE_802_1D_DSTADDR[ETH_ALEN] = { 1, 0x80, 0xC2, 0, 0, 0 };
+
+/* A few bytes that are present when and IEEE 802.1D bridge PDU is
+ * packed into an IEEE 802.3 frame. */
+static const __u8 IEEE_802_1D_802_3_HDR[] = { 0x42, 0x42, 0x03 };
+
 /*
  * Locking shorthand.
  */
@@ -440,6 +491,10 @@
                goto err1;
        }
 
+       /* Increase the priority of control protocol packets so that
+        * they are not impeded by plain old data packets. */
+       skb->priority = TC_PRIO_CONTROL;
+
        skb_queue_tail(&pf->xq, skb);
 
        switch (pf->kind) {
@@ -642,7 +697,20 @@
                        if (copy_to_user((void *) arg, &npi, sizeof(npi)))
                                break;
                } else {
+                       if (i == NP_BRIDGE) {
+                               if (npi.mode == NPMODE_PASS) {
+                                       err = ppp_create_bcp(ppp);
+                                       if (err < 0)
+                                               break;
+                                       ppp->npmode[i] = npi.mode;
+                               } else {
+                                       ppp->npmode[NP_BRIDGE] = npi.mode;
+                                       ppp->npmode[NP_BPDU_IEEE] = npi.mode;
+                                       ppp_shutdown_bcp(ppp);
+                               }
+                       } else {
                        ppp->npmode[i] = npi.mode;
+                       }
                        /* we may be able to transmit more packets now (??) */
                        netif_wake_queue(ppp->dev);
                }
@@ -800,11 +868,18 @@
 static int
 ppp_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
+       int npi = ethertype_to_npindex(ntohs(skb->protocol));
+       return ppp_common_start_xmit(npi, skb, dev);
+}
+
+/* Called by PPP and BCP transmission routines. */
+static int
+ppp_common_start_xmit(int npi, struct sk_buff *skb, struct net_device *dev)
+{
        struct ppp *ppp = (struct ppp *) dev->priv;
-       int npi, proto;
+       int proto;
        unsigned char *pp;
 
-       npi = ethertype_to_npindex(ntohs(skb->protocol));
        if (npi < 0)
                goto err1;
 
@@ -1104,7 +1179,8 @@
 
                spin_lock_bh(&pch->downl);
                if (pch->chan) {
-                       if (pch->chan->ops->start_xmit(pch->chan, skb))
+                       if (skb_queue_len(&pch->file.xq) == 0
+                           && pch->chan->ops->start_xmit(pch->chan, skb))
                                ppp->xmit_pending = 0;
                } else {
                        /* channel got unregistered */
@@ -1411,6 +1487,104 @@
                slhc_toss(ppp->vj);
 }
 
+/* Decapsulate a packet from BCP. */
+static __inline__ struct sk_buff* bcp_decap(struct sk_buff *skb)
+{
+       /** @todo cope with PADS field in BCP header? */
+
+       struct net_device *const dev = skb->dev;
+       __u8 bcp_flags;
+
+        /* Make sure that the data we examine are present. */
+       if (!pskb_may_pull(skb, BCP_802_3_HDRLEN))
+               goto drop;
+
+       /* Currently, only 802.3/Ethernet bridging is supported. */
+       if (((struct bcp_hdr*)skb->data)->mactype != BCP_MAC_802_3)
+               goto drop;
+
+       bcp_flags = ((struct bcp_hdr*)skb->data)->flags;
+
+       skb_pull(skb, BCP_802_3_HDRLEN);
+       skb->mac.raw = skb->data;
+
+       /* remove LAN FCS */
+       if (bcp_flags & BCP_LAN_FCS)
+       {
+               if (skb->len < ETH_FCS_LEN)
+                       goto drop;
+               skb_trim(skb, skb->len - ETH_FCS_LEN);
+       }
+
+       /* decompress "Tinygrams" */
+       if ((bcp_flags & BCP_ZERO_PAD) && (skb->len < ETH_ZLEN)) {
+               const int pad_len = ETH_ZLEN - skb->len;
+
+               if ((skb_tailroom(skb) < pad_len) || skb_cloned(skb)) {
+                       struct sk_buff *old_skb = skb;
+                       skb = skb_copy_expand(old_skb, 0, pad_len, GFP_ATOMIC);
+                       kfree_skb(old_skb);
+                       if (!skb)
+                               goto dropped;
+               }
+
+               skb_put(skb, pad_len);
+               memset(skb->tail - pad_len, 0, pad_len);
+       }
+
+       /* Parse the ethernet header.  Because of the increased
+        * hard_header_len, eth_type_trans() skips too much, so push
+        * some back afterward. */
+       skb->protocol = eth_type_trans(skb, dev);
+       skb_push(skb, dev->hard_header_len - ETH_HLEN);
+
+       return skb;
+
+ drop:
+       kfree_skb(skb);
+ dropped:
+       return 0;
+}
+
+/* Decapsulate an IEEE 802.1 (D or G) PDU. */
+static __inline__ struct sk_buff* bpdu_ieee_decap(struct sk_buff *skb)
+{
+       struct net_device *const dev = skb->dev;
+       struct ethhdr *eth;
+
+       if ((skb_headroom(skb) < ETH_HLEN + sizeof(IEEE_802_1D_802_3_HDR))
+           || skb_cloned(skb)) {
+               struct sk_buff *old_skb = skb;
+               skb = skb_copy_expand(old_skb,
+                                     ETH_HLEN + sizeof(IEEE_802_1D_802_3_HDR),
+                                     0, GFP_ATOMIC);
+               kfree_skb(old_skb);
+               if (!skb)
+                       goto dropped;
+       }
+
+       /* Prepend the 802.3 SAP and control byte. */
+       memcpy(skb_push(skb, sizeof(IEEE_802_1D_802_3_HDR)),
+              IEEE_802_1D_802_3_HDR, sizeof(IEEE_802_1D_802_3_HDR));
+
+       /* Prepend an ethernet header. */
+       eth = (struct ethhdr *)skb_push(skb, ETH_HLEN);
+       memcpy(eth->h_dest, IEEE_802_1D_DSTADDR, ETH_ALEN);
+       memset(eth->h_source, 0, ETH_ALEN);
+       eth->h_proto = htons(skb->len - ETH_HLEN);
+
+       /* Parse the ethernet header.  Because of the increased
+        * hard_header_len, eth_type_trans() skips too much, so push
+        * some back afterward. */
+       skb->protocol = eth_type_trans(skb, dev);
+       skb_push(skb, dev->hard_header_len - ETH_HLEN);
+
+       return skb;
+
+ dropped:
+       return 0;
+}
+
 static void
 ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
 {
@@ -1488,6 +1662,7 @@
 
        } else {
                /* network protocol frame - give it to the kernel */
+               struct net_device *rxdev;
 
 #ifdef CONFIG_PPP_FILTER
                /* check if the packet passes the pass and active filters */
@@ -1511,22 +1686,58 @@
                ppp->last_recv = jiffies;
 #endif /* CONFIG_PPP_FILTER */
 
-               if ((ppp->dev->flags & IFF_UP) == 0
-                   || ppp->npmode[npi] != NPMODE_PASS) {
+               switch (npi) {
+               case NP_BRIDGE:
+               case NP_BPDU_IEEE:
+                       rxdev = bcp_to_dev(ppp->bcp);
+                       break;
+               default:
+                       rxdev = ppp->dev;
+                       break;
+               }
+
+               if ((ppp->npmode[npi] != NPMODE_PASS)
+                   || !rxdev
+                   || (rxdev->flags & IFF_UP) == 0) {
                        kfree_skb(skb);
                } else {
                        skb_pull(skb, 2);       /* chop off protocol */
-                       skb->dev = ppp->dev;
+                       skb->dev = rxdev;
+
+                       switch (npi) {
+                       case NP_BRIDGE:
+                               skb = bcp_decap(skb);
+                               if (!skb) {
+                                       ++ppp->bcp->stats.rx_dropped;
+                                       goto dropped;
+                               }
+                               ++ppp->bcp->stats.rx_packets;
+                               break;
+
+                       case NP_BPDU_IEEE:
+                               skb = bpdu_ieee_decap(skb);
+                               if (!skb) {
+                                       ++ppp->bcp->stats.rx_dropped;
+                                       goto dropped;
+                               }
+                               ++ppp->bcp->stats.rx_packets;
+                               break;
+
+                       default:
                        skb->protocol = htons(npindex_to_ethertype[npi]);
                        skb->mac.raw = skb->data;
+                               break;
+                       }
+
                        netif_rx(skb);
-                       ppp->dev->last_rx = jiffies;
+                       rxdev->last_rx = jiffies;
                }
        }
        return;
 
  err:
        kfree_skb(skb);
+ dropped:
        ppp_receive_error(ppp);
 }
 
@@ -2255,6 +2466,8 @@
        ppp->file.hdrlen = PPP_HDRLEN - 2;      /* don't count proto bytes */
        for (i = 0; i < NUM_NP; ++i)
                ppp->npmode[i] = NPMODE_PASS;
+       ppp->npmode[NP_BRIDGE] = NPMODE_DROP;
+       ppp->npmode[NP_BPDU_IEEE] = NPMODE_DROP;
        INIT_LIST_HEAD(&ppp->channels);
        spin_lock_init(&ppp->rlock);
        spin_lock_init(&ppp->wlock);
@@ -2316,6 +2529,8 @@
 {
        struct net_device *dev;
 
+       ppp_shutdown_bcp(ppp);
+
        down(&all_ppp_sem);
        ppp_lock(ppp);
        dev = ppp->dev;
@@ -2620,6 +2835,230 @@
        *pmap = NULL;
 }
 
+/*
+ * Create interface for bridging.
+ */
+static int
+ppp_create_bcp(struct ppp *ppp)
+{
+       struct bcp_device *bcp;
+       struct net_device *dev;
+       int ret;
+
+       /* If it already exists, ignore the request. */
+       if (ppp->bcp)
+               return 0;
+
+       /* create a new BCP dev */
+       ret = -ENOMEM;
+       bcp = kmalloc(sizeof(struct bcp_device), GFP_KERNEL);
+       if (!bcp)
+               goto err;
+       memset(bcp, 0, sizeof(struct bcp_device));
+       dev = bcp_to_dev(bcp);
+
+       ether_setup(dev);
+
+       dev->hard_header_len = ppp->dev->hard_header_len
+               + BCP_802_3_HDRLEN + ETH_HLEN;
+
+       /* ETH_FCS_LEN is not subtracted from the PPP MTU here because
+        * bcp_start_xmit() never adds the FCS. */
+       dev->mtu = ppp->dev->mtu - (BCP_802_3_HDRLEN + ETH_HLEN);
+
+       if (dev->mtu > ETH_DATA_LEN)
+               dev->mtu = ETH_DATA_LEN;
+
+       dev->hard_start_xmit = bcp_start_xmit;
+       dev->get_stats = bcp_net_stats;
+       dev->do_ioctl = bcp_net_ioctl;
+       dev->change_mtu = bcp_net_change_mtu;
+       dev->tx_queue_len = 0; /* let PPP device queue packets */
+       dev->features |= NETIF_F_DYNALLOC;
+       sprintf(dev->name, "bcp%d", ppp->file.index);
+
+       rtnl_lock();
+       ret = register_netdevice(dev);
+       rtnl_unlock();
+       if (ret != 0) {
+               printk(KERN_ERR "PPP: couldn't register device %s (%d)\n",
+                      dev->name, ret);
+               goto err;
+       }
+       else {
+               /* Associate the devices.  Since register_netdevice()
+                * would fail if there were already a bcp<n> device
+                * registered for this ppp<n>, there is no need to
+                * worry about overwriting a valid ppp->bcp.dev. */
+               ppp_lock(ppp);
+               ppp->bcp = bcp;
+               dev->priv = ppp;
+               ppp_unlock(ppp);
+       }
+
+       return 0;
+
+ err:
+       if (bcp)
+               kfree(bcp);
+       return ret;
+}
+
+/*
+ * Take down a bcp interface.
+ */
+static void
+ppp_shutdown_bcp(struct ppp *ppp)
+{
+       struct bcp_device *bcp;
+
+       ppp_lock(ppp);
+       bcp = ppp->bcp;
+       ppp->bcp = 0;
+       ppp_unlock(ppp);
+
+       if (bcp) {
+               rtnl_lock();
+               dev_close(bcp_to_dev(bcp));
+               unregister_netdevice(bcp_to_dev(bcp));
+               rtnl_unlock();
+       }
+}
+
+static struct net_device_stats *
+bcp_net_stats(struct net_device *dev)
+{
+       struct bcp_device *const bcp = dev_to_bcp(dev);
+       return &bcp->stats;
+}
+
+static int
+bcp_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+       return -EOPNOTSUPP;
+}
+
+static int
+bcp_net_change_mtu(struct net_device *dev, int new_mtu)
+{
+       /* MTU is negotiated by the PPP daemon and should not be changed. */
+       return -EOPNOTSUPP;
+}
+
+/* input:  ethernet frame in non-shared skbuff
+ * output: bcp-encapsulated frame in non-shared and non-cloned skbuff
+ */
+static __inline__
+struct sk_buff* bcp_encap(struct sk_buff *skb, struct net_device *dev)
+{
+       struct bcp_hdr *bhdr;
+       int pad_len;
+
+       /* @todo Calculate FCS?  NB: If you add this, be sure to
+        * change the MTU calculation in ppp_create_bcp() to account
+        * for it. */
+
+       /* There is currently no communication from pppd regarding the
+        * peer's Tinygram-Compression option.  Therefore, we always
+        * pad short frames to minimum Ethernet size in case the peer
+        * does not support Tinygrams. */
+       pad_len = (skb->len < ETH_ZLEN) ? (ETH_ZLEN - skb->len) : 0;
+
+       /* Add a BCP header and pad to minimum frame size.
+        *
+        * Observations:
+        *   - Tailroom is always inadequate for short packets like ARP.
+        *   - Headroom is usually adequate, because the kernel usually
+        *     checks dev->hard_header_len; however, it is possible to
+        *     be given a buffer that was originally allocated for another
+        *     device.  (I have not seen it during testing.)
+        *   - It is not possible for a buffer to be shared, because
+        *     bcp_start_xmit() calls skb_share_check().
+        *   - I do not know when a buffer might be cloned; perhaps it
+        *     is possible in a bridging application where the same
+        *     packet needs to be transmitted to multiple interfaces.
+        */
+       if ((skb_tailroom(skb) < pad_len)
+           || (skb_headroom(skb) < dev->hard_header_len)
+           || skb_cloned(skb)) {
+               struct sk_buff *old_skb = skb;
+               skb = skb_copy_expand(old_skb,
+                                     dev->hard_header_len, pad_len,
+                                     GFP_ATOMIC);
+               kfree_skb(old_skb);
+               if (!skb)
+                       goto dropped;
+       }
+
+       bhdr = (struct bcp_hdr*)skb_push(skb, BCP_802_3_HDRLEN);
+       bhdr->flags = 0;                /* no LAN FCS, not a tinygram */
+       bhdr->mactype = BCP_MAC_802_3;  /* 802.3 / Ethernet */
+
+       return skb;
+
+ dropped:
+       return 0;
+}
+
+/* input:  BPDU ethernet frame in non-shared skbuff
+ * output: naked BPDU in non-shared and non-cloned skbuff
+ */
+static __inline__
+struct sk_buff* bpdu_ieee_encap(struct sk_buff *skb, struct net_device *dev)
+{
+       /* pull off the link header */
+       skb_pull(skb, ETH_HLEN + sizeof(IEEE_802_1D_802_3_HDR));
+
+       if (skb_cloned(skb)) {
+               struct sk_buff *old_skb = skb;
+               skb = skb_copy_expand(old_skb, dev->hard_header_len, 0,
+                                     GFP_ATOMIC);
+               kfree_skb(old_skb);
+       }
+
+       return skb;
+}
+
+static int
+bcp_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct bcp_device *const bcp = dev_to_bcp(dev);
+       struct ppp *const ppp = bcp_to_ppp(bcp);
+       int npi;
+
+       /* make sure we can push/pull without side effects */
+       skb = skb_share_check(skb, GFP_ATOMIC);
+       if (!skb)
+               goto dropped;
+
+       /* When configured to encapsulate 802.1D bridge PDUs in the
+        * obsolete manner of RFC 1638, do it.  Otherwise, send it
+        * like any other packet. */
+       if ((ppp->npmode[NP_BPDU_IEEE] == NPMODE_PASS) &&
+           pskb_may_pull(skb, ETH_HLEN + sizeof(IEEE_802_1D_802_3_HDR)) &&
+           (0 == memcmp(skb->data, IEEE_802_1D_DSTADDR, ETH_ALEN)) &&
+           (0 == memcmp(&skb->data[ETH_HLEN], IEEE_802_1D_802_3_HDR,
+                        sizeof(IEEE_802_1D_802_3_HDR)))) {
+               skb = bpdu_ieee_encap(skb, dev);
+               npi = NP_BPDU_IEEE;
+       } else {
+               skb = bcp_encap(skb, dev);
+               npi = NP_BRIDGE;
+       }
+
+       if (!skb)
+               goto dropped;
+
+       /* send packet through the PPP interface */
+       skb->dev = ppp->dev;
+       ++bcp->stats.tx_packets;
+       return ppp_common_start_xmit(npi, skb, skb->dev);
+
+ dropped:
+       ++bcp->stats.tx_dropped;
+       return 0;
+}
+
 /* Module/initialization stuff */
 
 module_init(ppp_init);
_______________________________________________
Bridge mailing list
[EMAIL PROTECTED]
http://lists.osdl.org/mailman/listinfo/bridge

Reply via email to