net_cls_act: Make act_simple use of netlink policy.
[safe/jmp/linux-2.6] / net / ipv4 / ip_output.c
index 34606ef..e527628 100644 (file)
@@ -75,7 +75,6 @@
 #include <net/icmp.h>
 #include <net/checksum.h>
 #include <net/inetpeer.h>
-#include <net/checksum.h>
 #include <linux/igmp.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/netfilter_bridge.h>
@@ -92,6 +91,28 @@ __inline__ void ip_send_check(struct iphdr *iph)
        iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
 }
 
+int __ip_local_out(struct sk_buff *skb)
+{
+       struct iphdr *iph = ip_hdr(skb);
+
+       iph->tot_len = htons(skb->len);
+       ip_send_check(iph);
+       return nf_hook(PF_INET, NF_INET_LOCAL_OUT, skb, NULL, skb->dst->dev,
+                      dst_output);
+}
+
+int ip_local_out(struct sk_buff *skb)
+{
+       int err;
+
+       err = __ip_local_out(skb);
+       if (likely(err == 1))
+               err = dst_output(skb);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(ip_local_out);
+
 /* dev_loopback_xmit for use with netfilter. */
 static int ip_dev_loopback_xmit(struct sk_buff *newskb)
 {
@@ -121,7 +142,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;
        struct iphdr *iph;
 
        /* Build the IP header. */
@@ -139,20 +160,18 @@ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
        iph->daddr    = rt->rt_dst;
        iph->saddr    = rt->rt_src;
        iph->protocol = sk->sk_protocol;
-       iph->tot_len  = htons(skb->len);
        ip_select_ident(iph, &rt->u.dst, sk);
 
        if (opt && opt->optlen) {
                iph->ihl += opt->optlen>>2;
                ip_options_build(skb, opt, daddr, rt, 0);
        }
-       ip_send_check(iph);
 
        skb->priority = sk->sk_priority;
+       skb->mark = sk->sk_mark;
 
        /* Send it out. */
-       return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
-                      dst_output);
+       return ip_local_out(skb);
 }
 
 EXPORT_SYMBOL_GPL(ip_build_and_send_pkt);
@@ -160,11 +179,17 @@ 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 rtable *rt = (struct rtable *)dst;
        struct net_device *dev = dst->dev;
-       int hh_len = LL_RESERVED_SPACE(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);
 
        /* Be paranoid, rather than too clever. */
-       if (unlikely(skb_headroom(skb) < hh_len && dev->hard_header)) {
+       if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
                struct sk_buff *skb2;
 
                skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));
@@ -189,7 +214,15 @@ static inline int ip_finish_output2(struct sk_buff *skb)
        return -EINVAL;
 }
 
-static inline int ip_finish_output(struct sk_buff *skb)
+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);
+}
+
+static int ip_finish_output(struct sk_buff *skb)
 {
 #if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
        /* Policy lookup after SNAT yielded a new policy */
@@ -198,7 +231,7 @@ static inline int ip_finish_output(struct sk_buff *skb)
                return dst_output(skb);
        }
 #endif
-       if (skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb))
+       if (skb->len > ip_skb_dst_mtu(skb) && !skb_is_gso(skb))
                return ip_fragment(skb, ip_finish_output2);
        else
                return ip_finish_output2(skb);
@@ -207,7 +240,7 @@ static inline 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;
        struct net_device *dev = rt->u.dst.dev;
 
        /*
@@ -238,8 +271,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);
                }
 
@@ -254,11 +287,11 @@ 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));
 }
@@ -272,7 +305,7 @@ int ip_output(struct sk_buff *skb)
        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));
 }
@@ -288,7 +321,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;
        if (rt != NULL)
                goto packet_routed;
 
@@ -318,7 +351,7 @@ 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);
@@ -334,7 +367,6 @@ packet_routed:
        skb_reset_network_header(skb);
        iph = ip_hdr(skb);
        *((__be16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff));
-       iph->tot_len = htons(skb->len);
        if (ip_dont_fragment(sk, &rt->u.dst) && !ipfragok)
                iph->frag_off = htons(IP_DF);
        else
@@ -353,13 +385,10 @@ packet_routed:
        ip_select_ident_more(iph, &rt->u.dst, sk,
                             (skb_shinfo(skb)->gso_segs ?: 1) - 1);
 
-       /* Add an IP checksum. */
-       ip_send_check(iph);
-
        skb->priority = sk->sk_priority;
+       skb->mark = sk->sk_mark;
 
-       return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
-                      dst_output);
+       return ip_local_out(skb);
 
 no_route:
        IP_INC_STATS(IPSTATS_MIB_OUTNOROUTES);
@@ -385,6 +414,10 @@ static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from)
        to->tc_index = from->tc_index;
 #endif
        nf_copy(to, from);
+#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
+    defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
+       to->nf_trace = from->nf_trace;
+#endif
 #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE)
        to->ipvs_property = from->ipvs_property;
 #endif
@@ -408,7 +441,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;
        int err = 0;
 
        dev = rt->u.dst.dev;
@@ -422,7 +455,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
        if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) {
                IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);
                icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
-                         htonl(dst_mtu(&rt->u.dst)));
+                         htonl(ip_skb_dst_mtu(skb)));
                kfree_skb(skb);
                return -EMSGSIZE;
        }
@@ -445,6 +478,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) ||
@@ -468,7 +502,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;
                        }
                }
 
@@ -479,6 +513,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);
@@ -718,23 +753,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));
 }
 
 /*
@@ -787,8 +814,10 @@ int ip_append_data(struct sock *sk,
                        inet->cork.addr = ipc->addr;
                }
                dst_hold(&rt->u.dst);
-               inet->cork.fragsize = mtu = dst_mtu(rt->u.dst.path);
-               inet->cork.rt = rt;
+               inet->cork.fragsize = mtu = inet->pmtudisc == IP_PMTUDISC_PROBE ?
+                                           rt->u.dst.dev->mtu :
+                                           dst_mtu(rt->u.dst.path);
+               inet->cork.dst = &rt->u.dst;
                inet->cork.length = 0;
                sk->sk_sndmsg_page = NULL;
                sk->sk_sndmsg_off = 0;
@@ -797,7 +826,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;
 
@@ -821,14 +850,14 @@ int ip_append_data(struct sock *sk,
         */
        if (transhdrlen &&
            length + fragheaderlen <= mtu &&
-           rt->u.dst.dev->features & NETIF_F_ALL_CSUM &&
+           rt->u.dst.dev->features & NETIF_F_V4_CSUM &&
            !exthdrlen)
                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);
@@ -997,8 +1026,6 @@ alloc_new_skb:
 
                                skb_fill_page_desc(skb, i, page, 0, 0);
                                frag = &skb_shinfo(skb)->frags[i];
-                               skb->truesize += PAGE_SIZE;
-                               atomic_add(PAGE_SIZE, &sk->sk_wmem_alloc);
                        } else {
                                err = -EMSGSIZE;
                                goto error;
@@ -1011,6 +1038,8 @@ alloc_new_skb:
                        frag->size += copy;
                        skb->len += copy;
                        skb->data_len += copy;
+                       skb->truesize += copy;
+                       atomic_add(copy, &sk->sk_wmem_alloc);
                }
                offset += copy;
                length -= copy;
@@ -1046,7 +1075,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;
 
@@ -1153,6 +1182,8 @@ ssize_t   ip_append_page(struct sock *sk, struct page *page,
 
                skb->len += len;
                skb->data_len += len;
+               skb->truesize += len;
+               atomic_add(len, &sk->sk_wmem_alloc);
                offset += len;
                size -= len;
        }
@@ -1164,6 +1195,15 @@ error:
        return err;
 }
 
+static void ip_cork_release(struct inet_sock *inet)
+{
+       inet->cork.flags &= ~IPCORK_OPT;
+       kfree(inet->cork.opt);
+       inet->cork.opt = NULL;
+       dst_release(inet->cork.dst);
+       inet->cork.dst = NULL;
+}
+
 /*
  *     Combined all pending IP fragments on the socket as one IP datagram
  *     and push them out.
@@ -1174,7 +1214,7 @@ int ip_push_pending_frames(struct sock *sk)
        struct sk_buff **tail_skb;
        struct inet_sock *inet = inet_sk(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;
@@ -1203,13 +1243,13 @@ int ip_push_pending_frames(struct sock *sk)
         * to fragment the frame generated here. No matter, what transforms
         * how transforms change size of the packet, it will come out.
         */
-       if (inet->pmtudisc != IP_PMTUDISC_DO)
+       if (inet->pmtudisc < IP_PMTUDISC_DO)
                skb->local_df = 1;
 
        /* DF bit is set when we want to see DF on outgoing frames.
         * If local_df is set too, we still allow to fragment this frame
         * locally. */
-       if (inet->pmtudisc == IP_PMTUDISC_DO ||
+       if (inet->pmtudisc >= IP_PMTUDISC_DO ||
            (skb->len <= dst_mtu(&rt->u.dst) &&
             ip_dont_fragment(sk, &rt->u.dst)))
                df = htons(IP_DF);
@@ -1230,21 +1270,23 @@ int ip_push_pending_frames(struct sock *sk)
                ip_options_build(skb, opt, inet->cork.addr, rt, 0);
        }
        iph->tos = inet->tos;
-       iph->tot_len = htons(skb->len);
        iph->frag_off = df;
        ip_select_ident(iph, &rt->u.dst, sk);
        iph->ttl = ttl;
        iph->protocol = sk->sk_protocol;
        iph->saddr = rt->rt_src;
        iph->daddr = rt->rt_dst;
-       ip_send_check(iph);
 
        skb->priority = sk->sk_priority;
+       skb->mark = sk->sk_mark;
        skb->dst = dst_clone(&rt->u.dst);
 
+       if (iph->protocol == IPPROTO_ICMP)
+               icmp_out_count(((struct icmphdr *)
+                       skb_transport_header(skb))->type);
+
        /* Netfilter gets whole the not fragmented skb. */
-       err = NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL,
-                     skb->dst->dev, dst_output);
+       err = ip_local_out(skb);
        if (err) {
                if (err > 0)
                        err = inet->recverr ? net_xmit_errno(err) : 0;
@@ -1253,13 +1295,7 @@ int ip_push_pending_frames(struct sock *sk)
        }
 
 out:
-       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;
-       }
+       ip_cork_release(inet);
        return err;
 
 error:
@@ -1272,19 +1308,12 @@ error:
  */
 void ip_flush_pending_frames(struct sock *sk)
 {
-       struct inet_sock *inet = inet_sk(sk);
        struct sk_buff *skb;
 
        while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL)
                kfree_skb(skb);
 
-       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;
-       }
+       ip_cork_release(inet_sk(sk));
 }
 
 
@@ -1307,8 +1336,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)
@@ -1320,7 +1347,7 @@ 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;
 
        if (ip_options_echo(&replyopts.opt, skb))
                return;
@@ -1336,7 +1363,8 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar
        }
 
        {
-               struct flowi fl = { .nl_u = { .ip4_u =
+               struct flowi fl = { .oif = arg->bound_dev_if,
+                                   .nl_u = { .ip4_u =
                                              { .daddr = daddr,
                                                .saddr = rt->rt_spec_dst,
                                                .tos = RT_TOS(ip_hdr(skb)->tos) } },
@@ -1346,7 +1374,7 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar
                                                 .dport = tcp_hdr(skb)->source } },
                                    .proto = sk->sk_protocol };
                security_skb_classify_flow(skb, &fl);
-               if (ip_route_output_key(&rt, &fl))
+               if (ip_route_output_key(sock_net(sk), &rt, &fl))
                        return;
        }
 
@@ -1360,6 +1388,7 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar
        inet->tos = ip_hdr(skb)->tos;
        sk->sk_priority = skb->priority;
        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);
        if ((skb = skb_peek(&sk->sk_write_queue)) != NULL) {