[Bridge] update: kernel bcp patch (ppc 2.4.21-pre4)

Dan Eble dane at aiinet.com
Mon Mar 1 05:56:57 PST 2004


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 at 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);


More information about the Bridge mailing list