]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/ipv6/ip6_output.c
Merge master.kernel.org:/pub/scm/linux/kernel/git/davej/cpufreq
[linux-2.6-omap-h63xx.git] / net / ipv6 / ip6_output.c
index 2a376b7d91b49d9241536c0ec5ab0a7e80e5e0f7..66716911962eb967aa487b964895548c3542ea8c 100644 (file)
@@ -308,6 +308,56 @@ static int ip6_call_ra_chain(struct sk_buff *skb, int sel)
        return 0;
 }
 
+static int ip6_forward_proxy_check(struct sk_buff *skb)
+{
+       struct ipv6hdr *hdr = skb->nh.ipv6h;
+       u8 nexthdr = hdr->nexthdr;
+       int offset;
+
+       if (ipv6_ext_hdr(nexthdr)) {
+               offset = ipv6_skip_exthdr(skb, sizeof(*hdr), &nexthdr);
+               if (offset < 0)
+                       return 0;
+       } else
+               offset = sizeof(struct ipv6hdr);
+
+       if (nexthdr == IPPROTO_ICMPV6) {
+               struct icmp6hdr *icmp6;
+
+               if (!pskb_may_pull(skb, skb->nh.raw + offset + 1 - skb->data))
+                       return 0;
+
+               icmp6 = (struct icmp6hdr *)(skb->nh.raw + offset);
+
+               switch (icmp6->icmp6_type) {
+               case NDISC_ROUTER_SOLICITATION:
+               case NDISC_ROUTER_ADVERTISEMENT:
+               case NDISC_NEIGHBOUR_SOLICITATION:
+               case NDISC_NEIGHBOUR_ADVERTISEMENT:
+               case NDISC_REDIRECT:
+                       /* For reaction involving unicast neighbor discovery
+                        * message destined to the proxied address, pass it to
+                        * input function.
+                        */
+                       return 1;
+               default:
+                       break;
+               }
+       }
+
+       /*
+        * The proxying router can't forward traffic sent to a link-local
+        * address, so signal the sender and discard the packet. This
+        * behavior is clarified by the MIPv6 specification.
+        */
+       if (ipv6_addr_type(&hdr->daddr) & IPV6_ADDR_LINKLOCAL) {
+               dst_link_failure(skb);
+               return -1;
+       }
+
+       return 0;
+}
+
 static inline int ip6_forward_finish(struct sk_buff *skb)
 {
        return dst_output(skb);
@@ -362,6 +412,18 @@ int ip6_forward(struct sk_buff *skb)
                return -ETIMEDOUT;
        }
 
+       /* XXX: idev->cnf.proxy_ndp? */
+       if (ipv6_devconf.proxy_ndp &&
+           pneigh_lookup(&nd_tbl, &hdr->daddr, skb->dev, 0)) {
+               int proxied = ip6_forward_proxy_check(skb);
+               if (proxied > 0)
+                       return ip6_input(skb);
+               else if (proxied < 0) {
+                       IP6_INC_STATS(IPSTATS_MIB_INDISCARDS);
+                       goto drop;
+               }
+       }
+
        if (!xfrm6_route_forward(skb)) {
                IP6_INC_STATS(IPSTATS_MIB_INDISCARDS);
                goto drop;
@@ -475,17 +537,25 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
                switch (**nexthdr) {
 
                case NEXTHDR_HOP:
+                       break;
                case NEXTHDR_ROUTING:
+                       found_rhdr = 1;
+                       break;
                case NEXTHDR_DEST:
-                       if (**nexthdr == NEXTHDR_ROUTING) found_rhdr = 1;
-                       if (**nexthdr == NEXTHDR_DEST && found_rhdr) return offset;
-                       offset += ipv6_optlen(exthdr);
-                       *nexthdr = &exthdr->nexthdr;
-                       exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
+#ifdef CONFIG_IPV6_MIP6
+                       if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0)
+                               break;
+#endif
+                       if (found_rhdr)
+                               return offset;
                        break;
                default :
                        return offset;
                }
+
+               offset += ipv6_optlen(exthdr);
+               *nexthdr = &exthdr->nexthdr;
+               exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
        }
 
        return offset;
@@ -971,7 +1041,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
 
        hh_len = LL_RESERVED_SPACE(rt->u.dst.dev);
 
-       fragheaderlen = sizeof(struct ipv6hdr) + (opt ? opt->opt_nflen : 0);
+       fragheaderlen = sizeof(struct ipv6hdr) + rt->u.dst.nfheader_len + (opt ? opt->opt_nflen : 0);
        maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen - sizeof(struct frag_hdr);
 
        if (mtu <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN) {