Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 097b19a9 authored by Eric Kinzie's avatar Eric Kinzie Committed by David S. Miller
Browse files

[ATM]: [br2864] routed support

parent 900092a4
Loading
Loading
Loading
Loading
+17 −1
Original line number Diff line number Diff line
@@ -14,6 +14,9 @@
#define BR2684_MEDIA_FDDI	(3)
#define BR2684_MEDIA_802_6	(4)	/* 802.6 */

					/* used only at device creation:  */
#define BR2684_FLAG_ROUTED	(1<<16) /* payload is routed, not bridged */

/*
 * Is there FCS inbound on this VC?  This currently isn't supported.
 */
@@ -35,6 +38,14 @@
#define BR2684_ENCAPS_LLC	(1)
#define BR2684_ENCAPS_AUTODETECT (2)	/* Unsuported */

/*
 * Is this VC bridged or routed?
 */

#define BR2684_PAYLOAD_ROUTED   (0)
#define BR2684_PAYLOAD_BRIDGED  (1)


/*
 * This is for the ATM_NEWBACKENDIF call - these are like socket families:
 * the first element of the structure is the backend number and the rest
@@ -42,7 +53,7 @@
 */
struct atm_newif_br2684 {
	atm_backend_t	backend_num;	/* ATM_BACKEND_BR2684 */
	int		media;		/* BR2684_MEDIA_* */
	int		media;		/* BR2684_MEDIA_*, flags in upper bits */
	char		ifname[IFNAMSIZ];
	int		mtu;
};
@@ -95,6 +106,11 @@ struct br2684_filter_set {
	struct br2684_filter filter;
};

enum br2684_payload {
        p_routed = BR2684_PAYLOAD_ROUTED,
        p_bridged = BR2684_PAYLOAD_BRIDGED,
};

#define BR2684_SETFILT	_IOW( 'a', ATMIOC_BACKEND + 0, \
				struct br2684_filter_set)

+116 −32
Original line number Diff line number Diff line
/*
Experimental ethernet netdevice using ATM AAL5 as underlying carrier
(RFC1483 obsoleted by RFC2684) for Linux 2.4
Author: Marcell GAL, 2000, XDSL Ltd, Hungary
Ethernet netdevice using ATM AAL5 as underlying carrier
(RFC1483 obsoleted by RFC2684) for Linux
Authors: Marcell GAL, 2000, XDSL Ltd, Hungary
         Eric Kinzie, 2006-2007, US Naval Research Laboratory
*/

#include <linux/module.h>
@@ -39,9 +40,27 @@ static void skb_debug(const struct sk_buff *skb)
#define skb_debug(skb)	do {} while (0)
#endif

#define BR2684_ETHERTYPE_LEN	2
#define BR2684_PAD_LEN		2

#define LLC		0xaa, 0xaa, 0x03
#define SNAP_BRIDGED	0x00, 0x80, 0xc2
#define SNAP_ROUTED	0x00, 0x00, 0x00
#define PID_ETHERNET	0x00, 0x07
#define ETHERTYPE_IPV4	0x08, 0x00
#define ETHERTYPE_IPV6	0x86, 0xdd
#define PAD_BRIDGED	0x00, 0x00

static unsigned char ethertype_ipv4[] =
	{ ETHERTYPE_IPV4 };
static unsigned char ethertype_ipv6[] =
	{ ETHERTYPE_IPV6 };
static unsigned char llc_oui_pid_pad[] =
    { 0xAA, 0xAA, 0x03, 0x00, 0x80, 0xC2, 0x00, 0x07, 0x00, 0x00 };
#define PADLEN	(2)
	{ LLC, SNAP_BRIDGED, PID_ETHERNET, PAD_BRIDGED };
static unsigned char llc_oui_ipv4[] =
	{ LLC, SNAP_ROUTED, ETHERTYPE_IPV4 };
static unsigned char llc_oui_ipv6[] =
	{ LLC, SNAP_ROUTED, ETHERTYPE_IPV6 };

enum br2684_encaps {
	e_vc  = BR2684_ENCAPS_VC,
@@ -69,6 +88,7 @@ struct br2684_dev {
	struct list_head brvccs; /* one device <=> one vcc (before xmas) */
	struct net_device_stats stats;
	int mac_was_set;
	enum br2684_payload payload;
};

/*
@@ -136,6 +156,7 @@ static int br2684_xmit_vcc(struct sk_buff *skb, struct br2684_dev *brdev,
{
	struct atm_vcc *atmvcc;
	int minheadroom = (brvcc->encaps == e_llc) ? 10 : 2;

	if (skb_headroom(skb) < minheadroom) {
		struct sk_buff *skb2 = skb_realloc_headroom(skb, minheadroom);
		brvcc->copies_needed++;
@@ -146,11 +167,32 @@ static int br2684_xmit_vcc(struct sk_buff *skb, struct br2684_dev *brdev,
		}
		skb = skb2;
	}
	skb_push(skb, minheadroom);
	if (brvcc->encaps == e_llc)
		skb_copy_to_linear_data(skb, llc_oui_pid_pad, 10);
	else

	if (brvcc->encaps == e_llc) {
		if (brdev->payload == p_bridged) {
			skb_push(skb, sizeof(llc_oui_pid_pad));
			skb_copy_to_linear_data(skb, llc_oui_pid_pad, sizeof(llc_oui_pid_pad));
		} else if (brdev->payload == p_routed) {
			unsigned short prot = ntohs(skb->protocol);

			skb_push(skb, sizeof(llc_oui_ipv4));
			switch (prot) {
				case ETH_P_IP:
					skb_copy_to_linear_data(skb, llc_oui_ipv4, sizeof(llc_oui_ipv4));
					break;
				case ETH_P_IPV6:
					skb_copy_to_linear_data(skb, llc_oui_ipv6, sizeof(llc_oui_ipv6));
					break;
				default:
					dev_kfree_skb(skb);
					return 0;
			}
		}
	} else {
		skb_push(skb, 2);
		if (brdev->payload == p_bridged)
			memset(skb->data, 0, 2);
	}
	skb_debug(skb);

	ATM_SKB(skb)->vcc = atmvcc = brvcc->atmvcc;
@@ -299,7 +341,6 @@ static void br2684_push(struct atm_vcc *atmvcc, struct sk_buff *skb)
	struct br2684_vcc *brvcc = BR2684_VCC(atmvcc);
	struct net_device *net_dev = brvcc->device;
	struct br2684_dev *brdev = BRPRIV(net_dev);
	int plen = sizeof(llc_oui_pid_pad) + ETH_HLEN;

	pr_debug("br2684_push\n");

@@ -320,35 +361,50 @@ static void br2684_push(struct atm_vcc *atmvcc, struct sk_buff *skb)
	atm_return(atmvcc, skb->truesize);
	pr_debug("skb from brdev %p\n", brdev);
	if (brvcc->encaps == e_llc) {

		if (skb->len > 7 && skb->data[7] == 0x01)
			__skb_trim(skb, skb->len - 4);

		/* accept packets that have "ipv[46]" in the snap header */
		if ((skb->len >= (sizeof(llc_oui_ipv4)))
		    && (memcmp(skb->data, llc_oui_ipv4, sizeof(llc_oui_ipv4) - BR2684_ETHERTYPE_LEN) == 0)) {
			if (memcmp(skb->data + 6, ethertype_ipv6, sizeof(ethertype_ipv6)) == 0)
				skb->protocol = __constant_htons(ETH_P_IPV6);
			else if (memcmp(skb->data + 6, ethertype_ipv4, sizeof(ethertype_ipv4)) == 0)
				skb->protocol = __constant_htons(ETH_P_IP);
			else {
				brdev->stats.rx_errors++;
				dev_kfree_skb(skb);
				return;
			}
			skb_pull(skb, sizeof(llc_oui_ipv4));
			skb_reset_network_header(skb);
			skb->pkt_type = PACKET_HOST;

		/* let us waste some time for checking the encapsulation.
		   Note, that only 7 char is checked so frames with a valid FCS
		   are also accepted (but FCS is not checked of course) */
		if (memcmp(skb->data, llc_oui_pid_pad, 7)) {
		} else if ((skb->len >= sizeof(llc_oui_pid_pad)) &&
		           (memcmp(skb->data, llc_oui_pid_pad, 7) == 0)) {
			skb_pull(skb, sizeof(llc_oui_pid_pad));
			skb->protocol = eth_type_trans(skb, net_dev);
		} else {
			brdev->stats.rx_errors++;
			dev_kfree_skb(skb);
			return;
		}

		/* Strip FCS if present */
		if (skb->len > 7 && skb->data[7] == 0x01)
			__skb_trim(skb, skb->len - 4);
	} else {
		plen = PADLEN + ETH_HLEN;	/* pad, dstmac,srcmac, ethtype */
		/* first 2 chars should be 0 */
		if (*((u16 *) (skb->data)) != 0) {
			brdev->stats.rx_errors++;
			dev_kfree_skb(skb);
			return;
		}
	}
	if (skb->len < plen) {
		brdev->stats.rx_errors++;
		dev_kfree_skb(skb);	/* dev_ not needed? */
		return;
		skb_pull(skb, BR2684_PAD_LEN + ETH_HLEN); /* pad, dstmac, srcmac, ethtype */
		skb->protocol = eth_type_trans(skb, net_dev);
	}

	skb_pull(skb, plen - ETH_HLEN);
	skb->protocol = eth_type_trans(skb, net_dev);
#ifdef CONFIG_ATM_BR2684_IPFILTER
	if (unlikely(packet_fails_filter(skb->protocol, brvcc, skb))) {
		brdev->stats.rx_dropped++;
@@ -482,25 +538,52 @@ static void br2684_setup(struct net_device *netdev)
	INIT_LIST_HEAD(&brdev->brvccs);
}

static void br2684_setup_routed(struct net_device *netdev)
{
	struct br2684_dev *brdev = BRPRIV(netdev);
	brdev->net_dev = netdev;

	netdev->hard_header_len = 0;
	my_eth_mac_addr = netdev->set_mac_address;
	netdev->set_mac_address = br2684_mac_addr;
	netdev->hard_start_xmit = br2684_start_xmit;
	netdev->get_stats = br2684_get_stats;
	netdev->addr_len = 0;
	netdev->mtu = 1500;
	netdev->type = ARPHRD_PPP;
	netdev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
	netdev->tx_queue_len = 100;
	INIT_LIST_HEAD(&brdev->brvccs);
}

static int br2684_create(void __user *arg)
{
	int err;
	struct net_device *netdev;
	struct br2684_dev *brdev;
	struct atm_newif_br2684 ni;
	enum br2684_payload payload;

	pr_debug("br2684_create\n");

	if (copy_from_user(&ni, arg, sizeof ni)) {
		return -EFAULT;
	}

	if (ni.media & BR2684_FLAG_ROUTED)
		payload = p_routed;
	else
		payload = p_bridged;
	ni.media &= 0xffff; /* strip flags */

	if (ni.media != BR2684_MEDIA_ETHERNET || ni.mtu != 1500) {
		return -EINVAL;
	}

	netdev = alloc_netdev(sizeof(struct br2684_dev),
			      ni.ifname[0] ? ni.ifname : "nas%d",
			      br2684_setup);
			      (payload == p_routed) ?
			       br2684_setup_routed : br2684_setup);
	if (!netdev)
		return -ENOMEM;

@@ -516,6 +599,7 @@ static int br2684_create(void __user *arg)
	}

	write_lock_irq(&devs_lock);
	brdev->payload = payload;
	brdev->number = list_empty(&br2684_devs) ? 1 :
	    BRPRIV(list_entry_brdev(br2684_devs.prev))->number + 1;
	list_add_tail(&brdev->br2684_devs, &br2684_devs);
@@ -601,14 +685,14 @@ static int br2684_seq_show(struct seq_file *seq, void *v)
		   brdev->mac_was_set ? "set" : "auto");

	list_for_each_entry(brvcc, &brdev->brvccs, brvccs) {
		seq_printf(seq, "  vcc %d.%d.%d: encaps=%s"
		seq_printf(seq, "  vcc %d.%d.%d: encaps=%s payload=%s"
			        ", failed copies %u/%u"
			        "\n", brvcc->atmvcc->dev->number,
				      brvcc->atmvcc->vpi, brvcc->atmvcc->vci,
				    (brvcc->encaps == e_llc) ? "LLC" : "VC"
				    , brvcc->copies_failed
				    , brvcc->copies_needed
				    );
				      (brvcc->encaps == e_llc) ? "LLC" : "VC",
				      (brdev->payload == p_bridged) ? "bridged" : "routed",
				      brvcc->copies_failed,
				      brvcc->copies_needed);
#ifdef CONFIG_ATM_BR2684_IPFILTER
#define b1(var, byte)	((u8 *) &brvcc->filter.var)[byte]
#define bs(var)		b1(var, 0), b1(var, 1), b1(var, 2), b1(var, 3)