diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-08-25 09:12:35 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-08-25 09:12:35 +0000 |
commit | c7fc24dc4420057f103afe8fc64524ebc25c5d37 (patch) | |
tree | 3682407a599b8f9f03fc096298134cafba1c9b2f /net/ipv6/ip6_output.c | |
parent | 1d793fade8b063fde3cf275bf1a5c2d381292cd9 (diff) |
o Merge with Linux 2.1.116.
o New Newport console code.
o New G364 console code.
Diffstat (limited to 'net/ipv6/ip6_output.c')
-rw-r--r-- | net/ipv6/ip6_output.c | 102 |
1 files changed, 93 insertions, 9 deletions
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index eb3984f55..aa13c2074 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -5,7 +5,7 @@ * Authors: * Pedro Roque <roque@di.fc.ul.pt> * - * $Id: ip6_output.c,v 1.12 1998/04/11 22:11:06 davem Exp $ + * $Id: ip6_output.c,v 1.13 1998/07/15 05:05:38 davem Exp $ * * Based on linux/net/ipv4/ip_output.c * @@ -32,6 +32,7 @@ #include <net/protocol.h> #include <net/ip6_route.h> #include <net/addrconf.h> +#include <net/rawv6.h> static u32 ipv6_fragmentation_id = 1; @@ -519,25 +520,104 @@ int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data, return err; } +int ip6_call_ra_chain(struct sk_buff *skb, int sel) +{ + struct ip6_ra_chain *ra; + struct sock *last = NULL; + + for (ra = ip6_ra_chain; ra; ra = ra->next) { + struct sock *sk = ra->sk; + if (sk && ra->sel == sel) { + if (last) { + struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2) { + skb2->sk = last; + rawv6_rcv(skb2, skb2->dev, &skb2->nh.ipv6h->saddr, + &skb2->nh.ipv6h->daddr, NULL, skb2->len); + } + } + last = sk; + } + } + + if (last) { + skb->sk = last; + rawv6_rcv(skb, skb->dev, &skb->nh.ipv6h->saddr, + &skb->nh.ipv6h->daddr, NULL, skb->len); + return 1; + } + return 0; +} + int ip6_forward(struct sk_buff *skb) { struct dst_entry *dst = skb->dst; struct ipv6hdr *hdr = skb->nh.ipv6h; int size; - if (ipv6_devconf.forwarding == 0) { - kfree_skb(skb); - return -EINVAL; - } + if (ipv6_devconf.forwarding == 0) + goto drop; /* * check hop-by-hop options present */ -#if 0 - if (hdr->nexthdr == NEXTHDR_HOP) - { + /* + * Note, that NEXTHDR_HOP header must be checked + * always at the most beginning of ipv6_rcv. + * The result should be saved somewhere, but + * we do not it for now. Alas. Let's do it here. --ANK + * + * Second note: we DO NOT make any processing on + * RA packets, pushing them to user level AS IS + * without ane WARRANTY that application will able + * to interpret them. The reson is that we + * cannot make anything clever here. + * + * We are not end-node, so that if packet contains + * AH/ESP, we cannot make anything. + * Defragmentation also would be mistake, RA packets + * cannot be fragmented, because there is no warranty + * that different fragments will go along one path. --ANK + */ + if (hdr->nexthdr == NEXTHDR_HOP) { + int ra_value = -1; + u8 *ptr = (u8*)(skb->nh.ipv6h+1); + int len = (ptr[1]+1)<<3; + + if (len + sizeof(struct ipv6hdr) > skb->len) + goto drop; + + ptr += 2; + len -= 2; + while (len > 0) { + u8 *opt; + int optlen; + + if (ptr[0] == 0) { + len--; + ptr++; + continue; + } + opt = ptr; + optlen = ptr[1]+1; + + len -= optlen; + ptr += optlen; + if (len < 0) + goto drop; + + if (opt[0] == 20) { + /* Router Alert as of draft-ietf-ipngwg-ipv6router-alert-04 */ + if (optlen < 4) + goto drop; + ra_value = opt[2] + (opt[3]<<8); + } else if (!ip6_dstopt_unknown(skb, (struct ipv6_tlvtype*)opt)) + goto drop; + } + if (ra_value>=0 && ip6_call_ra_chain(skb, ra_value)) + return 0; } -#endif + /* * check and decrement ttl */ @@ -589,4 +669,8 @@ int ip6_forward(struct sk_buff *skb) dst->output(skb); return 0; + +drop: + kfree_skb(skb); + return -EINVAL; } |