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 <dane@aiinet.com> _____ .
Software Engineer | _ |/|
Applied Innovation Inc. | |_| | |
http://www.aiinet.com/ |__/|_|_|
-------------- next part --------------
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);