datapath: Fix IPv6 fragment expiry crash.
[cascardo/ovs.git] / datapath / linux / compat / reassembly.c
1 /*
2  * Backported from upstream commit a72a5e2d34ec
3  * ("inet: kill unused skb_free op")
4  *
5  *      IPv6 fragment reassembly
6  *      Linux INET6 implementation
7  *
8  *      Authors:
9  *      Pedro Roque             <roque@di.fc.ul.pt>
10  *
11  *      Based on: net/ipv4/ip_fragment.c
12  *
13  *      This program is free software; you can redistribute it and/or
14  *      modify it under the terms of the GNU General Public License
15  *      as published by the Free Software Foundation; either version
16  *      2 of the License, or (at your option) any later version.
17  */
18
19 /*
20  *      Fixes:
21  *      Andi Kleen      Make it work with multiple hosts.
22  *                      More RFC compliance.
23  *
24  *      Horst von Brand Add missing #include <linux/string.h>
25  *      Alexey Kuznetsov        SMP races, threading, cleanup.
26  *      Patrick McHardy         LRU queue of frag heads for evictor.
27  *      Mitsuru KANDA @USAGI    Register inet6_protocol{}.
28  *      David Stevens and
29  *      YOSHIFUJI,H. @USAGI     Always remove fragment header to
30  *                              calculate ICV correctly.
31  */
32
33 #define pr_fmt(fmt) "IPv6: " fmt
34
35 #if defined(OVS_FRAGMENT_BACKPORT) && \
36     LINUX_VERSION_CODE < KERNEL_VERSION(3,17,0)
37
38 #include <linux/errno.h>
39 #include <linux/types.h>
40 #include <linux/string.h>
41 #include <linux/socket.h>
42 #include <linux/sockios.h>
43 #include <linux/jiffies.h>
44 #include <linux/net.h>
45 #include <linux/list.h>
46 #include <linux/netdevice.h>
47 #include <linux/in6.h>
48 #include <linux/ipv6.h>
49 #include <linux/icmpv6.h>
50 #include <linux/random.h>
51 #include <linux/jhash.h>
52 #include <linux/skbuff.h>
53 #include <linux/slab.h>
54 #include <linux/export.h>
55
56 #include <net/sock.h>
57 #include <net/snmp.h>
58
59 #include <net/ipv6.h>
60 #include <net/ip6_route.h>
61 #include <net/protocol.h>
62 #include <net/transp_v6.h>
63 #include <net/rawv6.h>
64 #include <net/ndisc.h>
65 #include <net/addrconf.h>
66 #include <net/inet_frag.h>
67 #include <net/inet_ecn.h>
68
69 void ip6_expire_frag_queue(struct net *net, struct frag_queue *fq,
70                            struct inet_frags *frags)
71 {
72         struct net_device *dev = NULL;
73
74         spin_lock(&fq->q.lock);
75
76         if (qp_flags(fq) & INET_FRAG_COMPLETE)
77                 goto out;
78
79         inet_frag_kill(&fq->q, frags);
80
81         rcu_read_lock();
82         dev = dev_get_by_index_rcu(net, fq->iif);
83         if (!dev)
84                 goto out_rcu_unlock;
85
86         IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS);
87
88         if (inet_frag_evicting(&fq->q))
89                 goto out_rcu_unlock;
90
91         IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMTIMEOUT);
92
93         /* Don't send error if the first segment did not arrive. */
94         if (!(qp_flags(fq) & INET_FRAG_FIRST_IN) || !fq->q.fragments)
95                 goto out_rcu_unlock;
96
97         /* But use as source device on which LAST ARRIVED
98          * segment was received. And do not use fq->dev
99          * pointer directly, device might already disappeared.
100          */
101         fq->q.fragments->dev = dev;
102         icmpv6_send(fq->q.fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0);
103 out_rcu_unlock:
104         rcu_read_unlock();
105 out:
106         spin_unlock(&fq->q.lock);
107         inet_frag_put(&fq->q, frags);
108 }
109
110 #endif /* OVS_FRAGMENT_BACKPORT */