[Bridge] (no subject)

Dan Eble dane at aiinet.com
Wed Feb 25 07:36:11 PST 2004


Attached are the kernel changes (ppc 2.4.21-pre4) that match the pppd 
changes I posted earlier today.

I hope I found all the relevant changes. :-)  If you have any problems,
feel free to contact me, especially if you find any bugs.

Some documentation:

When pppd negotiates BCP, it tells the kernel PPP driver to enable
bridging.  The kernel creates a device named "bcpX" where X matches the
"pppX" device that would be used for IP communication (assuming I did not
make a mistake).  Pppd runs the script /etc/ppp/eth-up, which is a good
place to add bcpX to your bridge and "ifconfig up" it.  If the link goes
down, the kernel should handle all cleanup, but pppd also calls
/etc/ppp/eth-down in case you should find something you want to run at
that time.  Don't access the bcpX device from eth-down, as it is probably
already gone.

It should be possible to run BCP and IPCP (and other protocols)  
simultaneously, although we haven't tested it much because it seems silly.  
The options "noip", "nobcp", etc. are helpful to restrict the behavior of
pppd.

The pppd option "maclocal" will tell pppd what hardware address to assign
bcpX.  Without this option, pppd will attempt to use an address assigned
by the peer.  BCP negotiation will fail if it can not determine a hardware
address.  Change the source code if that doesn't suit your application.  
I don't know how the bridge driver handles interfaces without hardware
addresses, though.  You might have to turn off STP in that case to avoid
unintended consequences.

Happy hacking,

-- 
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/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