diff options
author | Eric Kinzie <ekinzie@cmf.nrl.navy.mil> | 2007-12-31 02:17:53 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 18:00:13 -0500 |
commit | 097b19a9987204b898299260ee3ebff4cf716800 (patch) | |
tree | 03e8d7e60e7b5d2bc2987ebbce7a7a132519311b /net/atm | |
parent | 900092a45e73fc192c223761b85005503a813975 (diff) |
[ATM]: [br2864] routed support
Signed-off-by: Chas Williams <chas@cmf.nrl.navy.mil>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/atm')
-rw-r--r-- | net/atm/br2684.c | 148 |
1 files changed, 116 insertions, 32 deletions
diff --git a/net/atm/br2684.c b/net/atm/br2684.c index ba6428f204f..d9bb2a16b7c 100644 --- a/net/atm/br2684.c +++ b/net/atm/br2684.c | |||
@@ -1,7 +1,8 @@ | |||
1 | /* | 1 | /* |
2 | Experimental ethernet netdevice using ATM AAL5 as underlying carrier | 2 | Ethernet netdevice using ATM AAL5 as underlying carrier |
3 | (RFC1483 obsoleted by RFC2684) for Linux 2.4 | 3 | (RFC1483 obsoleted by RFC2684) for Linux |
4 | Author: Marcell GAL, 2000, XDSL Ltd, Hungary | 4 | Authors: Marcell GAL, 2000, XDSL Ltd, Hungary |
5 | Eric Kinzie, 2006-2007, US Naval Research Laboratory | ||
5 | */ | 6 | */ |
6 | 7 | ||
7 | #include <linux/module.h> | 8 | #include <linux/module.h> |
@@ -39,9 +40,27 @@ static void skb_debug(const struct sk_buff *skb) | |||
39 | #define skb_debug(skb) do {} while (0) | 40 | #define skb_debug(skb) do {} while (0) |
40 | #endif | 41 | #endif |
41 | 42 | ||
43 | #define BR2684_ETHERTYPE_LEN 2 | ||
44 | #define BR2684_PAD_LEN 2 | ||
45 | |||
46 | #define LLC 0xaa, 0xaa, 0x03 | ||
47 | #define SNAP_BRIDGED 0x00, 0x80, 0xc2 | ||
48 | #define SNAP_ROUTED 0x00, 0x00, 0x00 | ||
49 | #define PID_ETHERNET 0x00, 0x07 | ||
50 | #define ETHERTYPE_IPV4 0x08, 0x00 | ||
51 | #define ETHERTYPE_IPV6 0x86, 0xdd | ||
52 | #define PAD_BRIDGED 0x00, 0x00 | ||
53 | |||
54 | static unsigned char ethertype_ipv4[] = | ||
55 | { ETHERTYPE_IPV4 }; | ||
56 | static unsigned char ethertype_ipv6[] = | ||
57 | { ETHERTYPE_IPV6 }; | ||
42 | static unsigned char llc_oui_pid_pad[] = | 58 | static unsigned char llc_oui_pid_pad[] = |
43 | { 0xAA, 0xAA, 0x03, 0x00, 0x80, 0xC2, 0x00, 0x07, 0x00, 0x00 }; | 59 | { LLC, SNAP_BRIDGED, PID_ETHERNET, PAD_BRIDGED }; |
44 | #define PADLEN (2) | 60 | static unsigned char llc_oui_ipv4[] = |
61 | { LLC, SNAP_ROUTED, ETHERTYPE_IPV4 }; | ||
62 | static unsigned char llc_oui_ipv6[] = | ||
63 | { LLC, SNAP_ROUTED, ETHERTYPE_IPV6 }; | ||
45 | 64 | ||
46 | enum br2684_encaps { | 65 | enum br2684_encaps { |
47 | e_vc = BR2684_ENCAPS_VC, | 66 | e_vc = BR2684_ENCAPS_VC, |
@@ -69,6 +88,7 @@ struct br2684_dev { | |||
69 | struct list_head brvccs; /* one device <=> one vcc (before xmas) */ | 88 | struct list_head brvccs; /* one device <=> one vcc (before xmas) */ |
70 | struct net_device_stats stats; | 89 | struct net_device_stats stats; |
71 | int mac_was_set; | 90 | int mac_was_set; |
91 | enum br2684_payload payload; | ||
72 | }; | 92 | }; |
73 | 93 | ||
74 | /* | 94 | /* |
@@ -136,6 +156,7 @@ static int br2684_xmit_vcc(struct sk_buff *skb, struct br2684_dev *brdev, | |||
136 | { | 156 | { |
137 | struct atm_vcc *atmvcc; | 157 | struct atm_vcc *atmvcc; |
138 | int minheadroom = (brvcc->encaps == e_llc) ? 10 : 2; | 158 | int minheadroom = (brvcc->encaps == e_llc) ? 10 : 2; |
159 | |||
139 | if (skb_headroom(skb) < minheadroom) { | 160 | if (skb_headroom(skb) < minheadroom) { |
140 | struct sk_buff *skb2 = skb_realloc_headroom(skb, minheadroom); | 161 | struct sk_buff *skb2 = skb_realloc_headroom(skb, minheadroom); |
141 | brvcc->copies_needed++; | 162 | brvcc->copies_needed++; |
@@ -146,11 +167,32 @@ static int br2684_xmit_vcc(struct sk_buff *skb, struct br2684_dev *brdev, | |||
146 | } | 167 | } |
147 | skb = skb2; | 168 | skb = skb2; |
148 | } | 169 | } |
149 | skb_push(skb, minheadroom); | 170 | |
150 | if (brvcc->encaps == e_llc) | 171 | if (brvcc->encaps == e_llc) { |
151 | skb_copy_to_linear_data(skb, llc_oui_pid_pad, 10); | 172 | if (brdev->payload == p_bridged) { |
152 | else | 173 | skb_push(skb, sizeof(llc_oui_pid_pad)); |
153 | memset(skb->data, 0, 2); | 174 | skb_copy_to_linear_data(skb, llc_oui_pid_pad, sizeof(llc_oui_pid_pad)); |
175 | } else if (brdev->payload == p_routed) { | ||
176 | unsigned short prot = ntohs(skb->protocol); | ||
177 | |||
178 | skb_push(skb, sizeof(llc_oui_ipv4)); | ||
179 | switch (prot) { | ||
180 | case ETH_P_IP: | ||
181 | skb_copy_to_linear_data(skb, llc_oui_ipv4, sizeof(llc_oui_ipv4)); | ||
182 | break; | ||
183 | case ETH_P_IPV6: | ||
184 | skb_copy_to_linear_data(skb, llc_oui_ipv6, sizeof(llc_oui_ipv6)); | ||
185 | break; | ||
186 | default: | ||
187 | dev_kfree_skb(skb); | ||
188 | return 0; | ||
189 | } | ||
190 | } | ||
191 | } else { | ||
192 | skb_push(skb, 2); | ||
193 | if (brdev->payload == p_bridged) | ||
194 | memset(skb->data, 0, 2); | ||
195 | } | ||
154 | skb_debug(skb); | 196 | skb_debug(skb); |
155 | 197 | ||
156 | ATM_SKB(skb)->vcc = atmvcc = brvcc->atmvcc; | 198 | ATM_SKB(skb)->vcc = atmvcc = brvcc->atmvcc; |
@@ -299,7 +341,6 @@ static void br2684_push(struct atm_vcc *atmvcc, struct sk_buff *skb) | |||
299 | struct br2684_vcc *brvcc = BR2684_VCC(atmvcc); | 341 | struct br2684_vcc *brvcc = BR2684_VCC(atmvcc); |
300 | struct net_device *net_dev = brvcc->device; | 342 | struct net_device *net_dev = brvcc->device; |
301 | struct br2684_dev *brdev = BRPRIV(net_dev); | 343 | struct br2684_dev *brdev = BRPRIV(net_dev); |
302 | int plen = sizeof(llc_oui_pid_pad) + ETH_HLEN; | ||
303 | 344 | ||
304 | pr_debug("br2684_push\n"); | 345 | pr_debug("br2684_push\n"); |
305 | 346 | ||
@@ -320,35 +361,50 @@ static void br2684_push(struct atm_vcc *atmvcc, struct sk_buff *skb) | |||
320 | atm_return(atmvcc, skb->truesize); | 361 | atm_return(atmvcc, skb->truesize); |
321 | pr_debug("skb from brdev %p\n", brdev); | 362 | pr_debug("skb from brdev %p\n", brdev); |
322 | if (brvcc->encaps == e_llc) { | 363 | if (brvcc->encaps == e_llc) { |
364 | |||
365 | if (skb->len > 7 && skb->data[7] == 0x01) | ||
366 | __skb_trim(skb, skb->len - 4); | ||
367 | |||
368 | /* accept packets that have "ipv[46]" in the snap header */ | ||
369 | if ((skb->len >= (sizeof(llc_oui_ipv4))) | ||
370 | && (memcmp(skb->data, llc_oui_ipv4, sizeof(llc_oui_ipv4) - BR2684_ETHERTYPE_LEN) == 0)) { | ||
371 | if (memcmp(skb->data + 6, ethertype_ipv6, sizeof(ethertype_ipv6)) == 0) | ||
372 | skb->protocol = __constant_htons(ETH_P_IPV6); | ||
373 | else if (memcmp(skb->data + 6, ethertype_ipv4, sizeof(ethertype_ipv4)) == 0) | ||
374 | skb->protocol = __constant_htons(ETH_P_IP); | ||
375 | else { | ||
376 | brdev->stats.rx_errors++; | ||
377 | dev_kfree_skb(skb); | ||
378 | return; | ||
379 | } | ||
380 | skb_pull(skb, sizeof(llc_oui_ipv4)); | ||
381 | skb_reset_network_header(skb); | ||
382 | skb->pkt_type = PACKET_HOST; | ||
383 | |||
323 | /* let us waste some time for checking the encapsulation. | 384 | /* let us waste some time for checking the encapsulation. |
324 | Note, that only 7 char is checked so frames with a valid FCS | 385 | Note, that only 7 char is checked so frames with a valid FCS |
325 | are also accepted (but FCS is not checked of course) */ | 386 | are also accepted (but FCS is not checked of course) */ |
326 | if (memcmp(skb->data, llc_oui_pid_pad, 7)) { | 387 | } else if ((skb->len >= sizeof(llc_oui_pid_pad)) && |
388 | (memcmp(skb->data, llc_oui_pid_pad, 7) == 0)) { | ||
389 | skb_pull(skb, sizeof(llc_oui_pid_pad)); | ||
390 | skb->protocol = eth_type_trans(skb, net_dev); | ||
391 | } else { | ||
327 | brdev->stats.rx_errors++; | 392 | brdev->stats.rx_errors++; |
328 | dev_kfree_skb(skb); | 393 | dev_kfree_skb(skb); |
329 | return; | 394 | return; |
330 | } | 395 | } |
331 | 396 | ||
332 | /* Strip FCS if present */ | ||
333 | if (skb->len > 7 && skb->data[7] == 0x01) | ||
334 | __skb_trim(skb, skb->len - 4); | ||
335 | } else { | 397 | } else { |
336 | plen = PADLEN + ETH_HLEN; /* pad, dstmac,srcmac, ethtype */ | ||
337 | /* first 2 chars should be 0 */ | 398 | /* first 2 chars should be 0 */ |
338 | if (*((u16 *) (skb->data)) != 0) { | 399 | if (*((u16 *) (skb->data)) != 0) { |
339 | brdev->stats.rx_errors++; | 400 | brdev->stats.rx_errors++; |
340 | dev_kfree_skb(skb); | 401 | dev_kfree_skb(skb); |
341 | return; | 402 | return; |
342 | } | 403 | } |
343 | } | 404 | skb_pull(skb, BR2684_PAD_LEN + ETH_HLEN); /* pad, dstmac, srcmac, ethtype */ |
344 | if (skb->len < plen) { | 405 | skb->protocol = eth_type_trans(skb, net_dev); |
345 | brdev->stats.rx_errors++; | ||
346 | dev_kfree_skb(skb); /* dev_ not needed? */ | ||
347 | return; | ||
348 | } | 406 | } |
349 | 407 | ||
350 | skb_pull(skb, plen - ETH_HLEN); | ||
351 | skb->protocol = eth_type_trans(skb, net_dev); | ||
352 | #ifdef CONFIG_ATM_BR2684_IPFILTER | 408 | #ifdef CONFIG_ATM_BR2684_IPFILTER |
353 | if (unlikely(packet_fails_filter(skb->protocol, brvcc, skb))) { | 409 | if (unlikely(packet_fails_filter(skb->protocol, brvcc, skb))) { |
354 | brdev->stats.rx_dropped++; | 410 | brdev->stats.rx_dropped++; |
@@ -482,25 +538,52 @@ static void br2684_setup(struct net_device *netdev) | |||
482 | INIT_LIST_HEAD(&brdev->brvccs); | 538 | INIT_LIST_HEAD(&brdev->brvccs); |
483 | } | 539 | } |
484 | 540 | ||
541 | static void br2684_setup_routed(struct net_device *netdev) | ||
542 | { | ||
543 | struct br2684_dev *brdev = BRPRIV(netdev); | ||
544 | brdev->net_dev = netdev; | ||
545 | |||
546 | netdev->hard_header_len = 0; | ||
547 | my_eth_mac_addr = netdev->set_mac_address; | ||
548 | netdev->set_mac_address = br2684_mac_addr; | ||
549 | netdev->hard_start_xmit = br2684_start_xmit; | ||
550 | netdev->get_stats = br2684_get_stats; | ||
551 | netdev->addr_len = 0; | ||
552 | netdev->mtu = 1500; | ||
553 | netdev->type = ARPHRD_PPP; | ||
554 | netdev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; | ||
555 | netdev->tx_queue_len = 100; | ||
556 | INIT_LIST_HEAD(&brdev->brvccs); | ||
557 | } | ||
558 | |||
485 | static int br2684_create(void __user *arg) | 559 | static int br2684_create(void __user *arg) |
486 | { | 560 | { |
487 | int err; | 561 | int err; |
488 | struct net_device *netdev; | 562 | struct net_device *netdev; |
489 | struct br2684_dev *brdev; | 563 | struct br2684_dev *brdev; |
490 | struct atm_newif_br2684 ni; | 564 | struct atm_newif_br2684 ni; |
565 | enum br2684_payload payload; | ||
491 | 566 | ||
492 | pr_debug("br2684_create\n"); | 567 | pr_debug("br2684_create\n"); |
493 | 568 | ||
494 | if (copy_from_user(&ni, arg, sizeof ni)) { | 569 | if (copy_from_user(&ni, arg, sizeof ni)) { |
495 | return -EFAULT; | 570 | return -EFAULT; |
496 | } | 571 | } |
572 | |||
573 | if (ni.media & BR2684_FLAG_ROUTED) | ||
574 | payload = p_routed; | ||
575 | else | ||
576 | payload = p_bridged; | ||
577 | ni.media &= 0xffff; /* strip flags */ | ||
578 | |||
497 | if (ni.media != BR2684_MEDIA_ETHERNET || ni.mtu != 1500) { | 579 | if (ni.media != BR2684_MEDIA_ETHERNET || ni.mtu != 1500) { |
498 | return -EINVAL; | 580 | return -EINVAL; |
499 | } | 581 | } |
500 | 582 | ||
501 | netdev = alloc_netdev(sizeof(struct br2684_dev), | 583 | netdev = alloc_netdev(sizeof(struct br2684_dev), |
502 | ni.ifname[0] ? ni.ifname : "nas%d", | 584 | ni.ifname[0] ? ni.ifname : "nas%d", |
503 | br2684_setup); | 585 | (payload == p_routed) ? |
586 | br2684_setup_routed : br2684_setup); | ||
504 | if (!netdev) | 587 | if (!netdev) |
505 | return -ENOMEM; | 588 | return -ENOMEM; |
506 | 589 | ||
@@ -516,6 +599,7 @@ static int br2684_create(void __user *arg) | |||
516 | } | 599 | } |
517 | 600 | ||
518 | write_lock_irq(&devs_lock); | 601 | write_lock_irq(&devs_lock); |
602 | brdev->payload = payload; | ||
519 | brdev->number = list_empty(&br2684_devs) ? 1 : | 603 | brdev->number = list_empty(&br2684_devs) ? 1 : |
520 | BRPRIV(list_entry_brdev(br2684_devs.prev))->number + 1; | 604 | BRPRIV(list_entry_brdev(br2684_devs.prev))->number + 1; |
521 | list_add_tail(&brdev->br2684_devs, &br2684_devs); | 605 | list_add_tail(&brdev->br2684_devs, &br2684_devs); |
@@ -601,14 +685,14 @@ static int br2684_seq_show(struct seq_file *seq, void *v) | |||
601 | brdev->mac_was_set ? "set" : "auto"); | 685 | brdev->mac_was_set ? "set" : "auto"); |
602 | 686 | ||
603 | list_for_each_entry(brvcc, &brdev->brvccs, brvccs) { | 687 | list_for_each_entry(brvcc, &brdev->brvccs, brvccs) { |
604 | seq_printf(seq, " vcc %d.%d.%d: encaps=%s" | 688 | seq_printf(seq, " vcc %d.%d.%d: encaps=%s payload=%s" |
605 | ", failed copies %u/%u" | 689 | ", failed copies %u/%u" |
606 | "\n", brvcc->atmvcc->dev->number, | 690 | "\n", brvcc->atmvcc->dev->number, |
607 | brvcc->atmvcc->vpi, brvcc->atmvcc->vci, | 691 | brvcc->atmvcc->vpi, brvcc->atmvcc->vci, |
608 | (brvcc->encaps == e_llc) ? "LLC" : "VC" | 692 | (brvcc->encaps == e_llc) ? "LLC" : "VC", |
609 | , brvcc->copies_failed | 693 | (brdev->payload == p_bridged) ? "bridged" : "routed", |
610 | , brvcc->copies_needed | 694 | brvcc->copies_failed, |
611 | ); | 695 | brvcc->copies_needed); |
612 | #ifdef CONFIG_ATM_BR2684_IPFILTER | 696 | #ifdef CONFIG_ATM_BR2684_IPFILTER |
613 | #define b1(var, byte) ((u8 *) &brvcc->filter.var)[byte] | 697 | #define b1(var, byte) ((u8 *) &brvcc->filter.var)[byte] |
614 | #define bs(var) b1(var, 0), b1(var, 1), b1(var, 2), b1(var, 3) | 698 | #define bs(var) b1(var, 0), b1(var, 1), b1(var, 2), b1(var, 3) |