]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - net/ipv6/ip6_output.c
[IPV6] MROUTE: Support multicast forwarding.
[net-next-2.6.git] / net / ipv6 / ip6_output.c
index 9ac6ca2521c32fba05dd4c99e9f8e68bbf762c32..c0dbe549cc4258289c18442c3bb516ba1f476beb 100644 (file)
@@ -55,6 +55,7 @@
 #include <net/icmp.h>
 #include <net/xfrm.h>
 #include <net/checksum.h>
+#include <linux/mroute6.h>
 
 static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *));
 
@@ -137,8 +138,9 @@ static int ip6_output2(struct sk_buff *skb)
                struct inet6_dev *idev = ip6_dst_idev(skb->dst);
 
                if (!(dev->flags & IFF_LOOPBACK) && (!np || np->mc_loop) &&
-                   ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr,
-                                       &ipv6_hdr(skb)->saddr)) {
+                   ((mroute6_socket && !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) ||
+                    ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr,
+                                        &ipv6_hdr(skb)->saddr))) {
                        struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
 
                        /* Do not check for IFF_ALLMULTI; multicast routing
@@ -237,9 +239,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
        if (np)
                hlimit = np->hop_limit;
        if (hlimit < 0)
-               hlimit = dst_metric(dst, RTAX_HOPLIMIT);
-       if (hlimit < 0)
-               hlimit = ipv6_get_hoplimit(dst->dev);
+               hlimit = ip6_dst_hoplimit(dst);
 
        tclass = -1;
        if (np)
@@ -404,6 +404,7 @@ int ip6_forward(struct sk_buff *skb)
        struct dst_entry *dst = skb->dst;
        struct ipv6hdr *hdr = ipv6_hdr(skb);
        struct inet6_skb_parm *opt = IP6CB(skb);
+       struct net *net = dev_net(dst->dev);
 
        if (ipv6_devconf.forwarding == 0)
                goto error;
@@ -450,7 +451,7 @@ int ip6_forward(struct sk_buff *skb)
 
        /* XXX: idev->cnf.proxy_ndp? */
        if (ipv6_devconf.proxy_ndp &&
-           pneigh_lookup(&nd_tbl, &init_net, &hdr->daddr, skb->dev, 0)) {
+           pneigh_lookup(&nd_tbl, net, &hdr->daddr, skb->dev, 0)) {
                int proxied = ip6_forward_proxy_check(skb);
                if (proxied > 0)
                        return ip6_input(skb);
@@ -596,7 +597,6 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
 
        return offset;
 }
-EXPORT_SYMBOL_GPL(ip6_find_1stfragopt);
 
 static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
 {
@@ -621,7 +621,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
         * or if the skb it not generated by a local socket.  (This last
         * check should be redundant, but it's free.)
         */
-       if (!np || np->pmtudisc >= IPV6_PMTUDISC_DO) {
+       if (!skb->local_df) {
                skb->dev = skb->dst->dev;
                icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
                IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_FRAGFAILS);
@@ -912,15 +912,19 @@ static int ip6_dst_lookup_tail(struct sock *sk,
                               struct dst_entry **dst, struct flowi *fl)
 {
        int err;
+       struct net *net = sock_net(sk);
 
        if (*dst == NULL)
-               *dst = ip6_route_output(sk, fl);
+               *dst = ip6_route_output(net, sk, fl);
 
        if ((err = (*dst)->error))
                goto out_err_release;
 
        if (ipv6_addr_any(&fl->fl6_src)) {
-               err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src);
+               err = ipv6_dev_get_saddr(ip6_dst_idev(*dst)->dev,
+                                        &fl->fl6_dst,
+                                        sk ? inet6_sk(sk)->srcprefs : 0,
+                                        &fl->fl6_src);
                if (err)
                        goto out_err_release;
        }
@@ -939,7 +943,7 @@ static int ip6_dst_lookup_tail(struct sock *sk,
                        struct flowi fl_gw;
                        int redirect;
 
-                       ifp = ipv6_get_ifaddr(&init_net, &fl->fl6_src,
+                       ifp = ipv6_get_ifaddr(net, &fl->fl6_src,
                                              (*dst)->dev, 1);
 
                        redirect = (ifp && ifp->flags & IFA_F_OPTIMISTIC);
@@ -954,7 +958,7 @@ static int ip6_dst_lookup_tail(struct sock *sk,
                                dst_release(*dst);
                                memcpy(&fl_gw, fl, sizeof(struct flowi));
                                memset(&fl_gw.fl6_dst, 0, sizeof(struct in6_addr));
-                               *dst = ip6_route_output(sk, &fl_gw);
+                               *dst = ip6_route_output(net, sk, &fl_gw);
                                if ((err = (*dst)->error))
                                        goto out_err_release;
                        }
@@ -1113,7 +1117,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
                        /* need source address above miyazawa*/
                }
                dst_hold(&rt->u.dst);
-               np->cork.rt = rt;
+               inet->cork.dst = &rt->u.dst;
                inet->cork.fl = *fl;
                np->cork.hop_limit = hlimit;
                np->cork.tclass = tclass;
@@ -1134,7 +1138,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
                length += exthdrlen;
                transhdrlen += exthdrlen;
        } else {
-               rt = np->cork.rt;
+               rt = (struct rt6_info *)inet->cork.dst;
                fl = &inet->cork.fl;
                if (inet->cork.flags & IPCORK_OPT)
                        opt = np->cork.opt;
@@ -1379,9 +1383,9 @@ static void ip6_cork_release(struct inet_sock *inet, struct ipv6_pinfo *np)
        inet->cork.flags &= ~IPCORK_OPT;
        kfree(np->cork.opt);
        np->cork.opt = NULL;
-       if (np->cork.rt) {
-               dst_release(&np->cork.rt->u.dst);
-               np->cork.rt = NULL;
+       if (inet->cork.dst) {
+               dst_release(inet->cork.dst);
+               inet->cork.dst = NULL;
                inet->cork.flags &= ~IPCORK_ALLFRAG;
        }
        memset(&inet->cork.fl, 0, sizeof(inet->cork.fl));
@@ -1396,7 +1400,7 @@ int ip6_push_pending_frames(struct sock *sk)
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct ipv6hdr *hdr;
        struct ipv6_txoptions *opt = np->cork.opt;
-       struct rt6_info *rt = np->cork.rt;
+       struct rt6_info *rt = (struct rt6_info *)inet->cork.dst;
        struct flowi *fl = &inet->cork.fl;
        unsigned char proto = fl->proto;
        int err = 0;
@@ -1420,6 +1424,10 @@ int ip6_push_pending_frames(struct sock *sk)
                tmp_skb->sk = NULL;
        }
 
+       /* Allow local fragmentation. */
+       if (np->pmtudisc < IPV6_PMTUDISC_DO)
+               skb->local_df = 1;
+
        ipv6_addr_copy(final_dst, &fl->fl6_dst);
        __skb_pull(skb, skb_network_header_len(skb));
        if (opt && opt->opt_flen)