net: skb->dst accessors
[safe/jmp/linux-2.6] / net / ipv4 / ip_output.c
index 03b9b06..3d6167f 100644 (file)
@@ -5,8 +5,6 @@
  *
  *             The Internet Protocol (IP) output module.
  *
- * Version:    $Id: ip_output.c,v 1.100 2002/02/01 22:01:03 davem Exp $
- *
  * Authors:    Ross Biro
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *             Donald Becker, <becker@super.org>
@@ -97,7 +95,7 @@ int __ip_local_out(struct sk_buff *skb)
 
        iph->tot_len = htons(skb->len);
        ip_send_check(iph);
-       return nf_hook(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, skb->dst->dev,
+       return nf_hook(PF_INET, NF_INET_LOCAL_OUT, skb, NULL, skb_dst(skb)->dev,
                       dst_output);
 }
 
@@ -120,7 +118,7 @@ static int ip_dev_loopback_xmit(struct sk_buff *newskb)
        __skb_pull(newskb, skb_network_offset(newskb));
        newskb->pkt_type = PACKET_LOOPBACK;
        newskb->ip_summed = CHECKSUM_UNNECESSARY;
-       BUG_TRAP(newskb->dst);
+       WARN_ON(!skb_dst(newskb));
        netif_rx(newskb);
        return 0;
 }
@@ -142,7 +140,7 @@ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
                          __be32 saddr, __be32 daddr, struct ip_options *opt)
 {
        struct inet_sock *inet = inet_sk(sk);
-       struct rtable *rt = (struct rtable *)skb->dst;
+       struct rtable *rt = skb_rtable(skb);
        struct iphdr *iph;
 
        /* Build the IP header. */
@@ -168,6 +166,7 @@ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
        }
 
        skb->priority = sk->sk_priority;
+       skb->mark = sk->sk_mark;
 
        /* Send it out. */
        return ip_local_out(skb);
@@ -177,15 +176,15 @@ EXPORT_SYMBOL_GPL(ip_build_and_send_pkt);
 
 static inline int ip_finish_output2(struct sk_buff *skb)
 {
-       struct dst_entry *dst = skb->dst;
+       struct dst_entry *dst = skb_dst(skb);
        struct rtable *rt = (struct rtable *)dst;
        struct net_device *dev = dst->dev;
        unsigned int hh_len = LL_RESERVED_SPACE(dev);
 
-       if (rt->rt_type == RTN_MULTICAST)
-               IP_INC_STATS(IPSTATS_MIB_OUTMCASTPKTS);
-       else if (rt->rt_type == RTN_BROADCAST)
-               IP_INC_STATS(IPSTATS_MIB_OUTBCASTPKTS);
+       if (rt->rt_type == RTN_MULTICAST) {
+               IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTMCAST, skb->len);
+       else if (rt->rt_type == RTN_BROADCAST)
+               IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUTBCAST, skb->len);
 
        /* Be paranoid, rather than too clever. */
        if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
@@ -218,14 +217,14 @@ static inline int ip_skb_dst_mtu(struct sk_buff *skb)
        struct inet_sock *inet = skb->sk ? inet_sk(skb->sk) : NULL;
 
        return (inet && inet->pmtudisc == IP_PMTUDISC_PROBE) ?
-              skb->dst->dev->mtu : dst_mtu(skb->dst);
+              skb_dst(skb)->dev->mtu : dst_mtu(skb_dst(skb));
 }
 
 static int ip_finish_output(struct sk_buff *skb)
 {
 #if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
        /* Policy lookup after SNAT yielded a new policy */
-       if (skb->dst->xfrm != NULL) {
+       if (skb_dst(skb)->xfrm != NULL) {
                IPCB(skb)->flags |= IPSKB_REROUTED;
                return dst_output(skb);
        }
@@ -239,13 +238,13 @@ static int ip_finish_output(struct sk_buff *skb)
 int ip_mc_output(struct sk_buff *skb)
 {
        struct sock *sk = skb->sk;
-       struct rtable *rt = (struct rtable*)skb->dst;
+       struct rtable *rt = skb_rtable(skb);
        struct net_device *dev = rt->u.dst.dev;
 
        /*
         *      If the indicated interface is up and running, send the packet.
         */
-       IP_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
+       IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len);
 
        skb->dev = dev;
        skb->protocol = htons(ETH_P_IP);
@@ -270,8 +269,8 @@ int ip_mc_output(struct sk_buff *skb)
                ) {
                        struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
                        if (newskb)
-                               NF_HOOK(PF_INET, NF_IP_POST_ROUTING, newskb, NULL,
-                                       newskb->dev,
+                               NF_HOOK(PF_INET, NF_INET_POST_ROUTING, newskb,
+                                       NULL, newskb->dev,
                                        ip_dev_loopback_xmit);
                }
 
@@ -286,25 +285,25 @@ int ip_mc_output(struct sk_buff *skb)
        if (rt->rt_flags&RTCF_BROADCAST) {
                struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
                if (newskb)
-                       NF_HOOK(PF_INET, NF_IP_POST_ROUTING, newskb, NULL,
+                       NF_HOOK(PF_INET, NF_INET_POST_ROUTING, newskb, NULL,
                                newskb->dev, ip_dev_loopback_xmit);
        }
 
-       return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, skb->dev,
+       return NF_HOOK_COND(PF_INET, NF_INET_POST_ROUTING, skb, NULL, skb->dev,
                            ip_finish_output,
                            !(IPCB(skb)->flags & IPSKB_REROUTED));
 }
 
 int ip_output(struct sk_buff *skb)
 {
-       struct net_device *dev = skb->dst->dev;
+       struct net_device *dev = skb_dst(skb)->dev;
 
-       IP_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
+       IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len);
 
        skb->dev = dev;
        skb->protocol = htons(ETH_P_IP);
 
-       return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev,
+       return NF_HOOK_COND(PF_INET, NF_INET_POST_ROUTING, skb, NULL, dev,
                            ip_finish_output,
                            !(IPCB(skb)->flags & IPSKB_REROUTED));
 }
@@ -320,7 +319,7 @@ int ip_queue_xmit(struct sk_buff *skb, int ipfragok)
        /* Skip all of this if the packet is already routed,
         * f.e. by something like SCTP.
         */
-       rt = (struct rtable *) skb->dst;
+       rt = skb_rtable(skb);
        if (rt != NULL)
                goto packet_routed;
 
@@ -341,6 +340,7 @@ int ip_queue_xmit(struct sk_buff *skb, int ipfragok)
                                                        .saddr = inet->saddr,
                                                        .tos = RT_CONN_FLAGS(sk) } },
                                            .proto = sk->sk_protocol,
+                                           .flags = inet_sk_flowi_flags(sk),
                                            .uli_u = { .ports =
                                                       { .sport = inet->sport,
                                                         .dport = inet->dport } } };
@@ -350,12 +350,12 @@ int ip_queue_xmit(struct sk_buff *skb, int ipfragok)
                         * itself out.
                         */
                        security_sk_classify_flow(sk, &fl);
-                       if (ip_route_output_flow(&rt, &fl, sk, 0))
+                       if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk, 0))
                                goto no_route;
                }
                sk_setup_caps(sk, &rt->u.dst);
        }
-       skb->dst = dst_clone(&rt->u.dst);
+       skb_dst_set(skb, dst_clone(&rt->u.dst));
 
 packet_routed:
        if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway)
@@ -385,11 +385,12 @@ packet_routed:
                             (skb_shinfo(skb)->gso_segs ?: 1) - 1);
 
        skb->priority = sk->sk_priority;
+       skb->mark = sk->sk_mark;
 
        return ip_local_out(skb);
 
 no_route:
-       IP_INC_STATS(IPSTATS_MIB_OUTNOROUTES);
+       IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
        kfree_skb(skb);
        return -EHOSTUNREACH;
 }
@@ -400,8 +401,8 @@ static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from)
        to->pkt_type = from->pkt_type;
        to->priority = from->priority;
        to->protocol = from->protocol;
-       dst_release(to->dst);
-       to->dst = dst_clone(from->dst);
+       skb_dst_drop(to);
+       skb_dst_set(to, dst_clone(skb_dst(from)));
        to->dev = from->dev;
        to->mark = from->mark;
 
@@ -429,7 +430,7 @@ static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from)
  *     single device frame, and queue such a frame for sending.
  */
 
-int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
+int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
 {
        struct iphdr *iph;
        int raw = 0;
@@ -439,7 +440,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
        unsigned int mtu, hlen, left, len, ll_rs, pad;
        int offset;
        __be16 not_last_frag;
-       struct rtable *rt = (struct rtable*)skb->dst;
+       struct rtable *rt = skb_rtable(skb);
        int err = 0;
 
        dev = rt->u.dst.dev;
@@ -451,7 +452,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
        iph = ip_hdr(skb);
 
        if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) {
-               IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);
+               IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
                icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
                          htonl(ip_skb_dst_mtu(skb)));
                kfree_skb(skb);
@@ -476,6 +477,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
        if (skb_shinfo(skb)->frag_list) {
                struct sk_buff *frag;
                int first_len = skb_pagelen(skb);
+               int truesizes = 0;
 
                if (first_len - hlen > mtu ||
                    ((first_len - hlen) & 7) ||
@@ -499,7 +501,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
                                sock_hold(skb->sk);
                                frag->sk = skb->sk;
                                frag->destructor = sock_wfree;
-                               skb->truesize -= frag->truesize;
+                               truesizes += frag->truesize;
                        }
                }
 
@@ -510,6 +512,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
                frag = skb_shinfo(skb)->frag_list;
                skb_shinfo(skb)->frag_list = NULL;
                skb->data_len = first_len - skb_headlen(skb);
+               skb->truesize -= truesizes;
                skb->len = first_len;
                iph->tot_len = htons(first_len);
                iph->frag_off = htons(IP_MF);
@@ -540,7 +543,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
                        err = output(skb);
 
                        if (!err)
-                               IP_INC_STATS(IPSTATS_MIB_FRAGCREATES);
+                               IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGCREATES);
                        if (err || !frag)
                                break;
 
@@ -550,7 +553,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
                }
 
                if (err == 0) {
-                       IP_INC_STATS(IPSTATS_MIB_FRAGOKS);
+                       IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGOKS);
                        return 0;
                }
 
@@ -559,7 +562,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
                        kfree_skb(frag);
                        frag = skb;
                }
-               IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);
+               IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
                return err;
        }
 
@@ -671,15 +674,15 @@ slow_path:
                if (err)
                        goto fail;
 
-               IP_INC_STATS(IPSTATS_MIB_FRAGCREATES);
+               IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGCREATES);
        }
        kfree_skb(skb);
-       IP_INC_STATS(IPSTATS_MIB_FRAGOKS);
+       IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGOKS);
        return err;
 
 fail:
        kfree_skb(skb);
-       IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);
+       IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
        return err;
 }
 
@@ -717,7 +720,7 @@ static inline int ip_ufo_append_data(struct sock *sk,
                        int getfrag(void *from, char *to, int offset, int len,
                               int odd, struct sk_buff *skb),
                        void *from, int length, int hh_len, int fragheaderlen,
-                       int transhdrlen, int mtu,unsigned int flags)
+                       int transhdrlen, int mtu, unsigned int flags)
 {
        struct sk_buff *skb;
        int err;
@@ -738,7 +741,7 @@ static inline int ip_ufo_append_data(struct sock *sk,
                skb_reserve(skb, hh_len);
 
                /* create space for UDP/IP header */
-               skb_put(skb,fragheaderlen + transhdrlen);
+               skb_put(skb, fragheaderlen + transhdrlen);
 
                /* initialize network header pointer */
                skb_reset_network_header(skb);
@@ -749,23 +752,15 @@ static inline int ip_ufo_append_data(struct sock *sk,
                skb->ip_summed = CHECKSUM_PARTIAL;
                skb->csum = 0;
                sk->sk_sndmsg_off = 0;
-       }
 
-       err = skb_append_datato_frags(sk,skb, getfrag, from,
-                              (length - transhdrlen));
-       if (!err) {
-               /* specify the length of each IP datagram fragment*/
+               /* specify the length of each IP datagram fragment */
                skb_shinfo(skb)->gso_size = mtu - fragheaderlen;
                skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
                __skb_queue_tail(&sk->sk_write_queue, skb);
-
-               return 0;
        }
-       /* There is not enough support do UFO ,
-        * so follow normal path
-        */
-       kfree_skb(skb);
-       return err;
+
+       return skb_append_datato_frags(sk, skb, getfrag, from,
+                                      (length - transhdrlen));
 }
 
 /*
@@ -783,7 +778,7 @@ int ip_append_data(struct sock *sk,
                   int getfrag(void *from, char *to, int offset, int len,
                               int odd, struct sk_buff *skb),
                   void *from, int length, int transhdrlen,
-                  struct ipcm_cookie *ipc, struct rtable *rt,
+                  struct ipcm_cookie *ipc, struct rtable **rtp,
                   unsigned int flags)
 {
        struct inet_sock *inet = inet_sk(sk);
@@ -798,6 +793,7 @@ int ip_append_data(struct sock *sk,
        int offset = 0;
        unsigned int maxfraglen, fragheaderlen;
        int csummode = CHECKSUM_NONE;
+       struct rtable *rt;
 
        if (flags&MSG_PROBE)
                return 0;
@@ -817,11 +813,15 @@ int ip_append_data(struct sock *sk,
                        inet->cork.flags |= IPCORK_OPT;
                        inet->cork.addr = ipc->addr;
                }
-               dst_hold(&rt->u.dst);
+               rt = *rtp;
+               /*
+                * We steal reference to this route, caller should not release it
+                */
+               *rtp = NULL;
                inet->cork.fragsize = mtu = inet->pmtudisc == IP_PMTUDISC_PROBE ?
                                            rt->u.dst.dev->mtu :
                                            dst_mtu(rt->u.dst.path);
-               inet->cork.rt = rt;
+               inet->cork.dst = &rt->u.dst;
                inet->cork.length = 0;
                sk->sk_sndmsg_page = NULL;
                sk->sk_sndmsg_off = 0;
@@ -830,7 +830,7 @@ int ip_append_data(struct sock *sk,
                        transhdrlen += exthdrlen;
                }
        } else {
-               rt = inet->cork.rt;
+               rt = (struct rtable *)inet->cork.dst;
                if (inet->cork.flags & IPCORK_OPT)
                        opt = inet->cork.opt;
 
@@ -859,9 +859,9 @@ int ip_append_data(struct sock *sk,
                csummode = CHECKSUM_PARTIAL;
 
        inet->cork.length += length;
-       if (((length > mtu) && (sk->sk_protocol == IPPROTO_UDP)) &&
-                       (rt->u.dst.dev->features & NETIF_F_UFO)) {
-
+       if (((length> mtu) || !skb_queue_empty(&sk->sk_write_queue)) &&
+           (sk->sk_protocol == IPPROTO_UDP) &&
+           (rt->u.dst.dev->features & NETIF_F_UFO)) {
                err = ip_ufo_append_data(sk, getfrag, from, length, hh_len,
                                         fragheaderlen, transhdrlen, mtu,
                                         flags);
@@ -935,6 +935,10 @@ alloc_new_skb:
                                                           sk->sk_allocation);
                                if (unlikely(skb == NULL))
                                        err = -ENOBUFS;
+                               else
+                                       /* only the initial fragment is
+                                          time stamped */
+                                       ipc->shtx.flags = 0;
                        }
                        if (skb == NULL)
                                goto error;
@@ -945,6 +949,7 @@ alloc_new_skb:
                        skb->ip_summed = csummode;
                        skb->csum = 0;
                        skb_reserve(skb, hh_len);
+                       *skb_tx(skb) = ipc->shtx;
 
                        /*
                         *      Find where to start putting bytes.
@@ -1053,7 +1058,7 @@ alloc_new_skb:
 
 error:
        inet->cork.length -= length;
-       IP_INC_STATS(IPSTATS_MIB_OUTDISCARDS);
+       IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTDISCARDS);
        return err;
 }
 
@@ -1079,7 +1084,7 @@ ssize_t   ip_append_page(struct sock *sk, struct page *page,
        if (skb_queue_empty(&sk->sk_write_queue))
                return -EINVAL;
 
-       rt = inet->cork.rt;
+       rt = (struct rtable *)inet->cork.dst;
        if (inet->cork.flags & IPCORK_OPT)
                opt = inet->cork.opt;
 
@@ -1195,7 +1200,7 @@ ssize_t   ip_append_page(struct sock *sk, struct page *page,
 
 error:
        inet->cork.length -= size;
-       IP_INC_STATS(IPSTATS_MIB_OUTDISCARDS);
+       IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTDISCARDS);
        return err;
 }
 
@@ -1204,10 +1209,8 @@ static void ip_cork_release(struct inet_sock *inet)
        inet->cork.flags &= ~IPCORK_OPT;
        kfree(inet->cork.opt);
        inet->cork.opt = NULL;
-       if (inet->cork.rt) {
-               ip_rt_put(inet->cork.rt);
-               inet->cork.rt = NULL;
-       }
+       dst_release(inet->cork.dst);
+       inet->cork.dst = NULL;
 }
 
 /*
@@ -1219,8 +1222,9 @@ int ip_push_pending_frames(struct sock *sk)
        struct sk_buff *skb, *tmp_skb;
        struct sk_buff **tail_skb;
        struct inet_sock *inet = inet_sk(sk);
+       struct net *net = sock_net(sk);
        struct ip_options *opt = NULL;
-       struct rtable *rt = inet->cork.rt;
+       struct rtable *rt = (struct rtable *)inet->cork.dst;
        struct iphdr *iph;
        __be16 df = 0;
        __u8 ttl;
@@ -1284,10 +1288,16 @@ int ip_push_pending_frames(struct sock *sk)
        iph->daddr = rt->rt_dst;
 
        skb->priority = sk->sk_priority;
-       skb->dst = dst_clone(&rt->u.dst);
+       skb->mark = sk->sk_mark;
+       /*
+        * Steal rt from cork.dst to avoid a pair of atomic_inc/atomic_dec
+        * on dst refcount
+        */
+       inet->cork.dst = NULL;
+       skb_dst_set(skb, &rt->u.dst);
 
        if (iph->protocol == IPPROTO_ICMP)
-               icmp_out_count(((struct icmphdr *)
+               icmp_out_count(net, ((struct icmphdr *)
                        skb_transport_header(skb))->type);
 
        /* Netfilter gets whole the not fragmented skb. */
@@ -1304,7 +1314,7 @@ out:
        return err;
 
 error:
-       IP_INC_STATS(IPSTATS_MIB_OUTDISCARDS);
+       IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS);
        goto out;
 }
 
@@ -1341,8 +1351,6 @@ static int ip_reply_glue_bits(void *dptr, char *to, int offset,
  *
  *     Should run single threaded per socket because it uses the sock
  *             structure to pass arguments.
- *
- *     LATER: switch from ip_build_xmit to ip_append_*
  */
 void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *arg,
                   unsigned int len)
@@ -1354,13 +1362,14 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar
        } replyopts;
        struct ipcm_cookie ipc;
        __be32 daddr;
-       struct rtable *rt = (struct rtable*)skb->dst;
+       struct rtable *rt = skb_rtable(skb);
 
        if (ip_options_echo(&replyopts.opt, skb))
                return;
 
        daddr = ipc.addr = rt->rt_src;
        ipc.opt = NULL;
+       ipc.shtx.flags = 0;
 
        if (replyopts.opt.optlen) {
                ipc.opt = &replyopts.opt;
@@ -1379,9 +1388,10 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar
                                    .uli_u = { .ports =
                                               { .sport = tcp_hdr(skb)->dest,
                                                 .dport = tcp_hdr(skb)->source } },
-                                   .proto = sk->sk_protocol };
+                                   .proto = sk->sk_protocol,
+                                   .flags = ip_reply_arg_flowi_flags(arg) };
                security_skb_classify_flow(skb, &fl);
-               if (ip_route_output_key(&rt, &fl))
+               if (ip_route_output_key(sock_net(sk), &rt, &fl))
                        return;
        }
 
@@ -1397,7 +1407,7 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar
        sk->sk_protocol = ip_hdr(skb)->protocol;
        sk->sk_bound_dev_if = arg->bound_dev_if;
        ip_append_data(sk, ip_reply_glue_bits, arg->iov->iov_base, len, 0,
-                      &ipc, rt, MSG_DONTWAIT);
+                      &ipc, &rt, MSG_DONTWAIT);
        if ((skb = skb_peek(&sk->sk_write_queue)) != NULL) {
                if (arg->csumoffset >= 0)
                        *((__sum16 *)skb_transport_header(skb) +