]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/ipv4/icmp.c
ipv4: Deliver ICMP redirects to sockets too.
[karo-tx-linux.git] / net / ipv4 / icmp.c
index 2cb2bf845641da6bee915edfb73240c9e675c565..588514627aa7060fba774d65ff0693a1c999739e 100644 (file)
@@ -95,6 +95,7 @@
 #include <net/checksum.h>
 #include <net/xfrm.h>
 #include <net/inet_common.h>
+#include <net/ip_fib.h>
 
 /*
  *     Build xmit assembly blocks
@@ -253,10 +254,10 @@ static inline bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt,
 
        /* Limit if icmp type is enabled in ratemask. */
        if ((1 << type) & net->ipv4.sysctl_icmp_ratemask) {
-               if (!rt->peer)
-                       rt_bind_peer(rt, fl4->daddr, 1);
-               rc = inet_peer_xrlim_allow(rt->peer,
+               struct inet_peer *peer = inet_getpeer_v4(net->ipv4.peers, fl4->daddr, 1);
+               rc = inet_peer_xrlim_allow(peer,
                                           net->ipv4.sysctl_icmp_ratelimit);
+               inet_putpeer(peer);
        }
 out:
        return rc;
@@ -334,7 +335,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
        struct flowi4 fl4;
        struct sock *sk;
        struct inet_sock *inet;
-       __be32 daddr;
+       __be32 daddr, saddr;
 
        if (ip_options_echo(&icmp_param->replyopts.opt.opt, skb))
                return;
@@ -348,6 +349,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
 
        inet->tos = ip_hdr(skb)->tos;
        daddr = ipc.addr = ip_hdr(skb)->saddr;
+       saddr = fib_compute_spec_dst(skb);
        ipc.opt = NULL;
        ipc.tx_flags = 0;
        if (icmp_param->replyopts.opt.opt.optlen) {
@@ -357,7 +359,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
        }
        memset(&fl4, 0, sizeof(fl4));
        fl4.daddr = daddr;
-       fl4.saddr = rt->rt_spec_dst;
+       fl4.saddr = saddr;
        fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos);
        fl4.flowi4_proto = IPPROTO_ICMP;
        security_skb_classify_flow(skb, flowi4_to_flowi(&fl4));
@@ -632,6 +634,21 @@ out:;
 EXPORT_SYMBOL(icmp_send);
 
 
+static void icmp_socket_deliver(struct sk_buff *skb, u32 info)
+{
+       const struct iphdr *iph = (const struct iphdr *) skb->data;
+       const struct net_protocol *ipprot;
+       int protocol = iph->protocol;
+
+       raw_icmp_error(skb, protocol, info);
+
+       rcu_read_lock();
+       ipprot = rcu_dereference(inet_protos[protocol]);
+       if (ipprot && ipprot->err_handler)
+               ipprot->err_handler(skb, info);
+       rcu_read_unlock();
+}
+
 /*
  *     Handle ICMP_DEST_UNREACH, ICMP_TIME_EXCEED, and ICMP_QUENCH.
  */
@@ -640,10 +657,8 @@ static void icmp_unreach(struct sk_buff *skb)
 {
        const struct iphdr *iph;
        struct icmphdr *icmph;
-       int hash, protocol;
-       const struct net_protocol *ipprot;
-       u32 info = 0;
        struct net *net;
+       u32 info = 0;
 
        net = dev_net(skb_dst(skb)->dev);
 
@@ -674,9 +689,7 @@ static void icmp_unreach(struct sk_buff *skb)
                                LIMIT_NETDEBUG(KERN_INFO pr_fmt("%pI4: fragmentation needed and DF set\n"),
                                               &iph->daddr);
                        } else {
-                               info = ip_rt_frag_needed(net, iph,
-                                                        ntohs(icmph->un.frag.mtu),
-                                                        skb->dev);
+                               info = ntohs(icmph->un.frag.mtu);
                                if (!info)
                                        goto out;
                        }
@@ -713,11 +726,10 @@ static void icmp_unreach(struct sk_buff *skb)
 
        if (!net->ipv4.sysctl_icmp_ignore_bogus_error_responses &&
            inet_addr_type(net, iph->daddr) == RTN_BROADCAST) {
-               if (net_ratelimit())
-                       pr_warn("%pI4 sent an invalid ICMP type %u, code %u error to a broadcast: %pI4 on %s\n",
-                               &ip_hdr(skb)->saddr,
-                               icmph->type, icmph->code,
-                               &iph->daddr, skb->dev->name);
+               net_warn_ratelimited("%pI4 sent an invalid ICMP type %u, code %u error to a broadcast: %pI4 on %s\n",
+                                    &ip_hdr(skb)->saddr,
+                                    icmph->type, icmph->code,
+                                    &iph->daddr, skb->dev->name);
                goto out;
        }
 
@@ -727,20 +739,7 @@ static void icmp_unreach(struct sk_buff *skb)
        if (!pskb_may_pull(skb, iph->ihl * 4 + 8))
                goto out;
 
-       iph = (const struct iphdr *)skb->data;
-       protocol = iph->protocol;
-
-       /*
-        *      Deliver ICMP message to raw sockets. Pretty useless feature?
-        */
-       raw_icmp_error(skb, protocol, info);
-
-       hash = protocol & (MAX_INET_PROTOS - 1);
-       rcu_read_lock();
-       ipprot = rcu_dereference(inet_protos[hash]);
-       if (ipprot && ipprot->err_handler)
-               ipprot->err_handler(skb, info);
-       rcu_read_unlock();
+       icmp_socket_deliver(skb, info);
 
 out:
        return;
@@ -783,13 +782,7 @@ static void icmp_redirect(struct sk_buff *skb)
                break;
        }
 
-       /* Ping wants to see redirects.
-         * Let's pretend they are errors of sorts... */
-       if (iph->protocol == IPPROTO_ICMP &&
-           iph->ihl >= 5 &&
-           pskb_may_pull(skb, (iph->ihl<<2)+8)) {
-               ping_err(skb, icmp_hdr(skb)->un.gateway);
-       }
+       icmp_socket_deliver(skb, icmp_hdr(skb)->un.gateway);
 
 out:
        return;
@@ -906,8 +899,7 @@ out_err:
 static void icmp_address(struct sk_buff *skb)
 {
 #if 0
-       if (net_ratelimit())
-               printk(KERN_DEBUG "a guy asks for address mask. Who is it?\n");
+       net_dbg_ratelimited("a guy asks for address mask. Who is it?\n");
 #endif
 }
 
@@ -943,10 +935,10 @@ static void icmp_address_reply(struct sk_buff *skb)
                            inet_ifa_match(ip_hdr(skb)->saddr, ifa))
                                break;
                }
-               if (!ifa && net_ratelimit()) {
-                       pr_info("Wrong address mask %pI4 from %s/%pI4\n",
-                               mp, dev->name, &ip_hdr(skb)->saddr);
-               }
+               if (!ifa)
+                       net_info_ratelimited("Wrong address mask %pI4 from %s/%pI4\n",
+                                            mp,
+                                            dev->name, &ip_hdr(skb)->saddr);
        }
 }