[ATM]: [br2864] routed support

Signed-off-by: Chas Williams <chas@cmf.nrl.navy.mil>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Eric Kinzie 2007-12-30 23:17:53 -08:00 committed by David S. Miller
parent 900092a45e
commit 097b19a998
2 changed files with 133 additions and 33 deletions

View file

@ -14,6 +14,9 @@
#define BR2684_MEDIA_FDDI (3) #define BR2684_MEDIA_FDDI (3)
#define BR2684_MEDIA_802_6 (4) /* 802.6 */ #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. * Is there FCS inbound on this VC? This currently isn't supported.
*/ */
@ -35,6 +38,14 @@
#define BR2684_ENCAPS_LLC (1) #define BR2684_ENCAPS_LLC (1)
#define BR2684_ENCAPS_AUTODETECT (2) /* Unsuported */ #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: * 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 * the first element of the structure is the backend number and the rest
@ -42,7 +53,7 @@
*/ */
struct atm_newif_br2684 { struct atm_newif_br2684 {
atm_backend_t backend_num; /* ATM_BACKEND_BR2684 */ atm_backend_t backend_num; /* ATM_BACKEND_BR2684 */
int media; /* BR2684_MEDIA_* */ int media; /* BR2684_MEDIA_*, flags in upper bits */
char ifname[IFNAMSIZ]; char ifname[IFNAMSIZ];
int mtu; int mtu;
}; };
@ -95,6 +106,11 @@ struct br2684_filter_set {
struct br2684_filter filter; 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, \ #define BR2684_SETFILT _IOW( 'a', ATMIOC_BACKEND + 0, \
struct br2684_filter_set) struct br2684_filter_set)

View file

@ -1,7 +1,8 @@
/* /*
Experimental ethernet netdevice using ATM AAL5 as underlying carrier Ethernet netdevice using ATM AAL5 as underlying carrier
(RFC1483 obsoleted by RFC2684) for Linux 2.4 (RFC1483 obsoleted by RFC2684) for Linux
Author: Marcell GAL, 2000, XDSL Ltd, Hungary Authors: Marcell GAL, 2000, XDSL Ltd, Hungary
Eric Kinzie, 2006-2007, US Naval Research Laboratory
*/ */
#include <linux/module.h> #include <linux/module.h>
@ -39,9 +40,27 @@ static void skb_debug(const struct sk_buff *skb)
#define skb_debug(skb) do {} while (0) #define skb_debug(skb) do {} while (0)
#endif #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[] = static unsigned char llc_oui_pid_pad[] =
{ 0xAA, 0xAA, 0x03, 0x00, 0x80, 0xC2, 0x00, 0x07, 0x00, 0x00 }; { LLC, SNAP_BRIDGED, PID_ETHERNET, PAD_BRIDGED };
#define PADLEN (2) 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 { enum br2684_encaps {
e_vc = BR2684_ENCAPS_VC, e_vc = BR2684_ENCAPS_VC,
@ -69,6 +88,7 @@ struct br2684_dev {
struct list_head brvccs; /* one device <=> one vcc (before xmas) */ struct list_head brvccs; /* one device <=> one vcc (before xmas) */
struct net_device_stats stats; struct net_device_stats stats;
int mac_was_set; 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; struct atm_vcc *atmvcc;
int minheadroom = (brvcc->encaps == e_llc) ? 10 : 2; int minheadroom = (brvcc->encaps == e_llc) ? 10 : 2;
if (skb_headroom(skb) < minheadroom) { if (skb_headroom(skb) < minheadroom) {
struct sk_buff *skb2 = skb_realloc_headroom(skb, minheadroom); struct sk_buff *skb2 = skb_realloc_headroom(skb, minheadroom);
brvcc->copies_needed++; brvcc->copies_needed++;
@ -146,11 +167,32 @@ static int br2684_xmit_vcc(struct sk_buff *skb, struct br2684_dev *brdev,
} }
skb = skb2; skb = skb2;
} }
skb_push(skb, minheadroom);
if (brvcc->encaps == e_llc) if (brvcc->encaps == e_llc) {
skb_copy_to_linear_data(skb, llc_oui_pid_pad, 10); if (brdev->payload == p_bridged) {
else skb_push(skb, sizeof(llc_oui_pid_pad));
memset(skb->data, 0, 2); 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); skb_debug(skb);
ATM_SKB(skb)->vcc = atmvcc = brvcc->atmvcc; 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 br2684_vcc *brvcc = BR2684_VCC(atmvcc);
struct net_device *net_dev = brvcc->device; struct net_device *net_dev = brvcc->device;
struct br2684_dev *brdev = BRPRIV(net_dev); struct br2684_dev *brdev = BRPRIV(net_dev);
int plen = sizeof(llc_oui_pid_pad) + ETH_HLEN;
pr_debug("br2684_push\n"); 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); atm_return(atmvcc, skb->truesize);
pr_debug("skb from brdev %p\n", brdev); pr_debug("skb from brdev %p\n", brdev);
if (brvcc->encaps == e_llc) { 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. /* let us waste some time for checking the encapsulation.
Note, that only 7 char is checked so frames with a valid FCS Note, that only 7 char is checked so frames with a valid FCS
are also accepted (but FCS is not checked of course) */ 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++; brdev->stats.rx_errors++;
dev_kfree_skb(skb); dev_kfree_skb(skb);
return; return;
} }
/* Strip FCS if present */
if (skb->len > 7 && skb->data[7] == 0x01)
__skb_trim(skb, skb->len - 4);
} else { } else {
plen = PADLEN + ETH_HLEN; /* pad, dstmac,srcmac, ethtype */
/* first 2 chars should be 0 */ /* first 2 chars should be 0 */
if (*((u16 *) (skb->data)) != 0) { if (*((u16 *) (skb->data)) != 0) {
brdev->stats.rx_errors++; brdev->stats.rx_errors++;
dev_kfree_skb(skb); dev_kfree_skb(skb);
return; return;
} }
} skb_pull(skb, BR2684_PAD_LEN + ETH_HLEN); /* pad, dstmac, srcmac, ethtype */
if (skb->len < plen) { skb->protocol = eth_type_trans(skb, net_dev);
brdev->stats.rx_errors++;
dev_kfree_skb(skb); /* dev_ not needed? */
return;
} }
skb_pull(skb, plen - ETH_HLEN);
skb->protocol = eth_type_trans(skb, net_dev);
#ifdef CONFIG_ATM_BR2684_IPFILTER #ifdef CONFIG_ATM_BR2684_IPFILTER
if (unlikely(packet_fails_filter(skb->protocol, brvcc, skb))) { if (unlikely(packet_fails_filter(skb->protocol, brvcc, skb))) {
brdev->stats.rx_dropped++; brdev->stats.rx_dropped++;
@ -482,25 +538,52 @@ static void br2684_setup(struct net_device *netdev)
INIT_LIST_HEAD(&brdev->brvccs); 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) static int br2684_create(void __user *arg)
{ {
int err; int err;
struct net_device *netdev; struct net_device *netdev;
struct br2684_dev *brdev; struct br2684_dev *brdev;
struct atm_newif_br2684 ni; struct atm_newif_br2684 ni;
enum br2684_payload payload;
pr_debug("br2684_create\n"); pr_debug("br2684_create\n");
if (copy_from_user(&ni, arg, sizeof ni)) { if (copy_from_user(&ni, arg, sizeof ni)) {
return -EFAULT; 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) { if (ni.media != BR2684_MEDIA_ETHERNET || ni.mtu != 1500) {
return -EINVAL; return -EINVAL;
} }
netdev = alloc_netdev(sizeof(struct br2684_dev), netdev = alloc_netdev(sizeof(struct br2684_dev),
ni.ifname[0] ? ni.ifname : "nas%d", ni.ifname[0] ? ni.ifname : "nas%d",
br2684_setup); (payload == p_routed) ?
br2684_setup_routed : br2684_setup);
if (!netdev) if (!netdev)
return -ENOMEM; return -ENOMEM;
@ -516,6 +599,7 @@ static int br2684_create(void __user *arg)
} }
write_lock_irq(&devs_lock); write_lock_irq(&devs_lock);
brdev->payload = payload;
brdev->number = list_empty(&br2684_devs) ? 1 : brdev->number = list_empty(&br2684_devs) ? 1 :
BRPRIV(list_entry_brdev(br2684_devs.prev))->number + 1; BRPRIV(list_entry_brdev(br2684_devs.prev))->number + 1;
list_add_tail(&brdev->br2684_devs, &br2684_devs); 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"); brdev->mac_was_set ? "set" : "auto");
list_for_each_entry(brvcc, &brdev->brvccs, brvccs) { 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" ", failed copies %u/%u"
"\n", brvcc->atmvcc->dev->number, "\n", brvcc->atmvcc->dev->number,
brvcc->atmvcc->vpi, brvcc->atmvcc->vci, brvcc->atmvcc->vpi, brvcc->atmvcc->vci,
(brvcc->encaps == e_llc) ? "LLC" : "VC" (brvcc->encaps == e_llc) ? "LLC" : "VC",
, brvcc->copies_failed (brdev->payload == p_bridged) ? "bridged" : "routed",
, brvcc->copies_needed brvcc->copies_failed,
); brvcc->copies_needed);
#ifdef CONFIG_ATM_BR2684_IPFILTER #ifdef CONFIG_ATM_BR2684_IPFILTER
#define b1(var, byte) ((u8 *) &brvcc->filter.var)[byte] #define b1(var, byte) ((u8 *) &brvcc->filter.var)[byte]
#define bs(var) b1(var, 0), b1(var, 1), b1(var, 2), b1(var, 3) #define bs(var) b1(var, 0), b1(var, 1), b1(var, 2), b1(var, 3)