Merge remote-tracking branch 'torvalds/master' into perf/core
[linux-2.6-microblaze.git] / net / ipv4 / icmp.c
index cf36f95..396b492 100644 (file)
@@ -239,7 +239,7 @@ static struct {
 /**
  * icmp_global_allow - Are we allowed to send one more ICMP message ?
  *
- * Uses a token bucket to limit our ICMP messages to sysctl_icmp_msgs_per_sec.
+ * Uses a token bucket to limit our ICMP messages to ~sysctl_icmp_msgs_per_sec.
  * Returns false if we reached the limit and can not send another packet.
  * Note: called with BH disabled
  */
@@ -267,7 +267,10 @@ bool icmp_global_allow(void)
        }
        credit = min_t(u32, icmp_global.credit + incr, sysctl_icmp_msgs_burst);
        if (credit) {
-               credit--;
+               /* We want to use a credit of one in average, but need to randomize
+                * it for security reasons.
+                */
+               credit = max_t(int, credit - prandom_u32_max(3), 0);
                rc = true;
        }
        WRITE_ONCE(icmp_global.credit, credit);
@@ -352,7 +355,7 @@ static int icmp_glue_bits(void *from, char *to, int offset, int len, int odd,
 
        csum = skb_copy_and_csum_bits(icmp_param->skb,
                                      icmp_param->offset + offset,
-                                     to, len, 0);
+                                     to, len);
 
        skb->csum = csum_block_add(skb->csum, csum, odd);
        if (icmp_pointers[icmp_param->data.icmph.type].error)
@@ -376,15 +379,15 @@ static void icmp_push_reply(struct icmp_bxm *icmp_param,
                ip_flush_pending_frames(sk);
        } else if ((skb = skb_peek(&sk->sk_write_queue)) != NULL) {
                struct icmphdr *icmph = icmp_hdr(skb);
-               __wsum csum = 0;
+               __wsum csum;
                struct sk_buff *skb1;
 
+               csum = csum_partial_copy_nocheck((void *)&icmp_param->data,
+                                                (char *)icmph,
+                                                icmp_param->head_len);
                skb_queue_walk(&sk->sk_write_queue, skb1) {
                        csum = csum_add(csum, skb1->csum);
                }
-               csum = csum_partial_copy_nocheck((void *)&icmp_param->data,
-                                                (char *)icmph,
-                                                icmp_param->head_len, csum);
                icmph->checksum = csum_fold(csum);
                skb->ip_summed = CHECKSUM_NONE;
                ip_push_pending_frames(sk, fl4);
@@ -444,7 +447,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
        fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos);
        fl4.flowi4_proto = IPPROTO_ICMP;
        fl4.flowi4_oif = l3mdev_master_ifindex(skb->dev);
-       security_skb_classify_flow(skb, flowi4_to_flowi(&fl4));
+       security_skb_classify_flow(skb, flowi4_to_flowi_common(&fl4));
        rt = ip_route_output_key(net, &fl4);
        if (IS_ERR(rt))
                goto out_unlock;
@@ -457,6 +460,23 @@ out_bh_enable:
        local_bh_enable();
 }
 
+/*
+ * The device used for looking up which routing table to use for sending an ICMP
+ * error is preferably the source whenever it is set, which should ensure the
+ * icmp error can be sent to the source host, else lookup using the routing
+ * table of the destination device, else use the main routing table (index 0).
+ */
+static struct net_device *icmp_get_route_lookup_dev(struct sk_buff *skb)
+{
+       struct net_device *route_lookup_dev = NULL;
+
+       if (skb->dev)
+               route_lookup_dev = skb->dev;
+       else if (skb_dst(skb))
+               route_lookup_dev = skb_dst(skb)->dev;
+       return route_lookup_dev;
+}
+
 static struct rtable *icmp_route_lookup(struct net *net,
                                        struct flowi4 *fl4,
                                        struct sk_buff *skb_in,
@@ -465,6 +485,7 @@ static struct rtable *icmp_route_lookup(struct net *net,
                                        int type, int code,
                                        struct icmp_bxm *param)
 {
+       struct net_device *route_lookup_dev;
        struct rtable *rt, *rt2;
        struct flowi4 fl4_dec;
        int err;
@@ -479,9 +500,10 @@ static struct rtable *icmp_route_lookup(struct net *net,
        fl4->flowi4_proto = IPPROTO_ICMP;
        fl4->fl4_icmp_type = type;
        fl4->fl4_icmp_code = code;
-       fl4->flowi4_oif = l3mdev_master_ifindex(skb_dst(skb_in)->dev);
+       route_lookup_dev = icmp_get_route_lookup_dev(skb_in);
+       fl4->flowi4_oif = l3mdev_master_ifindex(route_lookup_dev);
 
-       security_skb_classify_flow(skb_in, flowi4_to_flowi(fl4));
+       security_skb_classify_flow(skb_in, flowi4_to_flowi_common(fl4));
        rt = ip_route_output_key_hash(net, fl4, skb_in);
        if (IS_ERR(rt))
                return rt;
@@ -503,7 +525,7 @@ static struct rtable *icmp_route_lookup(struct net *net,
        if (err)
                goto relookup_failed;
 
-       if (inet_addr_type_dev_table(net, skb_dst(skb_in)->dev,
+       if (inet_addr_type_dev_table(net, route_lookup_dev,
                                     fl4_dec.saddr) == RTN_LOCAL) {
                rt2 = __ip_route_output_key(net, &fl4_dec);
                if (IS_ERR(rt2))
@@ -690,9 +712,9 @@ void __icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info,
                rcu_read_unlock();
        }
 
-       tos = icmp_pointers[type].error ? ((iph->tos & IPTOS_TOS_MASK) |
+       tos = icmp_pointers[type].error ? (RT_TOS(iph->tos) |
                                           IPTOS_PREC_INTERNETCONTROL) :
-                                         iph->tos;
+                                          iph->tos;
        mark = IP4_REPLY_MARK(net, skb_in->mark);
 
        if (__ip_options_echo(net, &icmp_param.replyopts.opt.opt, skb_in, opt))
@@ -784,7 +806,7 @@ EXPORT_SYMBOL(icmp_ndo_send);
 
 static void icmp_socket_deliver(struct sk_buff *skb, u32 info)
 {
-       const struct iphdr *iph = (const struct iphdr *) skb->data;
+       const struct iphdr *iph = (const struct iphdr *)skb->data;
        const struct net_protocol *ipprot;
        int protocol = iph->protocol;