]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - net/ipv4/ip_fragment.c
IP: Send an ICMP "Fragment Reassembly Timeout" message when enabling connection track
[net-next-2.6.git] / net / ipv4 / ip_fragment.c
index 86964b353c31167fb613be24fd1779e0272ffa57..9f41bd31175420ae85e54c06a3268ba618410c07 100644 (file)
@@ -32,6 +32,8 @@
 #include <linux/netdevice.h>
 #include <linux/jhash.h>
 #include <linux/random.h>
+#include <net/route.h>
+#include <net/dst.h>
 #include <net/sock.h>
 #include <net/ip.h>
 #include <net/icmp.h>
@@ -205,13 +207,37 @@ static void ip_expire(unsigned long arg)
        if ((qp->q.last_in & INET_FRAG_FIRST_IN) && qp->q.fragments != NULL) {
                struct sk_buff *head = qp->q.fragments;
 
-               /* Send an ICMP "Fragment Reassembly Timeout" message. */
                rcu_read_lock();
                head->dev = dev_get_by_index_rcu(net, qp->iif);
-               if (head->dev)
-                       icmp_send(head, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0);
-               rcu_read_unlock();
+               if (!head->dev)
+                       goto out_rcu_unlock;
+
+               /*
+                * Only search router table for the head fragment,
+                * when defraging timeout at PRE_ROUTING HOOK.
+                */
+               if (qp->user == IP_DEFRAG_CONNTRACK_IN && !skb_dst(head)) {
+                       const struct iphdr *iph = ip_hdr(head);
+                       int err = ip_route_input(head, iph->daddr, iph->saddr,
+                                                iph->tos, head->dev);
+                       if (unlikely(err))
+                               goto out_rcu_unlock;
+
+                       /*
+                        * Only an end host needs to send an ICMP
+                        * "Fragment Reassembly Timeout" message, per RFC792.
+                        */
+                       if (skb_rtable(head)->rt_type != RTN_LOCAL)
+                               goto out_rcu_unlock;
+
+               }
+
+               /* Send an ICMP "Fragment Reassembly Timeout" message. */
+               icmp_send(head, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0);
        }
+
+out_rcu_unlock:
+       rcu_read_unlock();
 out:
        spin_unlock(&qp->q.lock);
        ipq_put(qp);
@@ -646,7 +672,7 @@ static struct ctl_table ip4_frags_ctl_table[] = {
        { }
 };
 
-static int ip4_frags_ns_ctl_register(struct net *net)
+static int __net_init ip4_frags_ns_ctl_register(struct net *net)
 {
        struct ctl_table *table;
        struct ctl_table_header *hdr;
@@ -676,7 +702,7 @@ err_alloc:
        return -ENOMEM;
 }
 
-static void ip4_frags_ns_ctl_unregister(struct net *net)
+static void __net_exit ip4_frags_ns_ctl_unregister(struct net *net)
 {
        struct ctl_table *table;
 
@@ -704,7 +730,7 @@ static inline void ip4_frags_ctl_register(void)
 }
 #endif
 
-static int ipv4_frags_init_net(struct net *net)
+static int __net_init ipv4_frags_init_net(struct net *net)
 {
        /*
         * Fragment cache limits. We will commit 256K at one time. Should we
@@ -726,7 +752,7 @@ static int ipv4_frags_init_net(struct net *net)
        return ip4_frags_ns_ctl_register(net);
 }
 
-static void ipv4_frags_exit_net(struct net *net)
+static void __net_exit ipv4_frags_exit_net(struct net *net)
 {
        ip4_frags_ns_ctl_unregister(net);
        inet_frags_exit_net(&net->ipv4.frags, &ip4_frags);