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