1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * INET An implementation of the TCP/IP protocol suite for the LINUX 41da177e4SLinus Torvalds * operating system. INET is implemented using the BSD Socket 51da177e4SLinus Torvalds * interface as the means of communication with the user level. 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * The IP fragmentation functionality. 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * Authors: Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG> 10113aa838SAlan Cox * Alan Cox <alan@lxorguk.ukuu.org.uk> 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * Fixes: 131da177e4SLinus Torvalds * Alan Cox : Split from ip.c , see ip_input.c for history. 141da177e4SLinus Torvalds * David S. Miller : Begin massive cleanup... 151da177e4SLinus Torvalds * Andi Kleen : Add sysctls. 161da177e4SLinus Torvalds * xxxx : Overlapfrag bug. 171da177e4SLinus Torvalds * Ultima : ip_expire() kernel panic. 181da177e4SLinus Torvalds * Bill Hawes : Frag accounting and evictor fixes. 191da177e4SLinus Torvalds * John McDonald : 0 length frag bug. 201da177e4SLinus Torvalds * Alexey Kuznetsov: SMP races, threading, cleanup. 211da177e4SLinus Torvalds * Patrick McHardy : LRU queue of frag heads for evictor. 221da177e4SLinus Torvalds */ 231da177e4SLinus Torvalds 24afd46503SJoe Perches #define pr_fmt(fmt) "IPv4: " fmt 25afd46503SJoe Perches 2689cee8b1SHerbert Xu #include <linux/compiler.h> 271da177e4SLinus Torvalds #include <linux/module.h> 281da177e4SLinus Torvalds #include <linux/types.h> 291da177e4SLinus Torvalds #include <linux/mm.h> 301da177e4SLinus Torvalds #include <linux/jiffies.h> 311da177e4SLinus Torvalds #include <linux/skbuff.h> 321da177e4SLinus Torvalds #include <linux/list.h> 331da177e4SLinus Torvalds #include <linux/ip.h> 341da177e4SLinus Torvalds #include <linux/icmp.h> 351da177e4SLinus Torvalds #include <linux/netdevice.h> 361da177e4SLinus Torvalds #include <linux/jhash.h> 371da177e4SLinus Torvalds #include <linux/random.h> 385a0e3ad6STejun Heo #include <linux/slab.h> 39e9017b55SShan Wei #include <net/route.h> 40e9017b55SShan Wei #include <net/dst.h> 411da177e4SLinus Torvalds #include <net/sock.h> 421da177e4SLinus Torvalds #include <net/ip.h> 431da177e4SLinus Torvalds #include <net/icmp.h> 441da177e4SLinus Torvalds #include <net/checksum.h> 4589cee8b1SHerbert Xu #include <net/inetpeer.h> 465ab11c98SPavel Emelyanov #include <net/inet_frag.h> 471da177e4SLinus Torvalds #include <linux/tcp.h> 481da177e4SLinus Torvalds #include <linux/udp.h> 491da177e4SLinus Torvalds #include <linux/inet.h> 501da177e4SLinus Torvalds #include <linux/netfilter_ipv4.h> 516623e3b2SEric Dumazet #include <net/inet_ecn.h> 52385add90SDavid Ahern #include <net/l3mdev.h> 531da177e4SLinus Torvalds 541da177e4SLinus Torvalds /* NOTE. Logic of IP defragmentation is parallel to corresponding IPv6 551da177e4SLinus Torvalds * code now. If you change something here, _PLEASE_ update ipv6/reassembly.c 561da177e4SLinus Torvalds * as well. Or notify me, at least. --ANK 571da177e4SLinus Torvalds */ 58d4ad4d22SNikolay Aleksandrov static const char ip_frag_cache_name[] = "ip4-frags"; 5989cee8b1SHerbert Xu 601da177e4SLinus Torvalds /* Describe an entry in the "incomplete datagrams" queue. */ 611da177e4SLinus Torvalds struct ipq { 625ab11c98SPavel Emelyanov struct inet_frag_queue q; 635ab11c98SPavel Emelyanov 646623e3b2SEric Dumazet u8 ecn; /* RFC3168 support */ 65d6b915e2SFlorian Westphal u16 max_df_size; /* largest frag with DF set seen */ 6689cee8b1SHerbert Xu int iif; 6789cee8b1SHerbert Xu unsigned int rid; 6889cee8b1SHerbert Xu struct inet_peer *peer; 691da177e4SLinus Torvalds }; 701da177e4SLinus Torvalds 71aa1f731eSFabian Frederick static u8 ip4_frag_ecn(u8 tos) 726623e3b2SEric Dumazet { 735173cc05SEric Dumazet return 1 << (tos & INET_ECN_MASK); 746623e3b2SEric Dumazet } 756623e3b2SEric Dumazet 767eb95156SPavel Emelyanov static struct inet_frags ip4_frags; 771da177e4SLinus Torvalds 78a4fd284aSPeter Oskolkov static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb, 79a4fd284aSPeter Oskolkov struct sk_buff *prev_tail, struct net_device *dev); 801706d587SHerbert Xu 81abd6523dSPavel Emelyanov 8236c77782SFlorian Westphal static void ip4_frag_init(struct inet_frag_queue *q, const void *a) 83c6fda282SPavel Emelyanov { 84c6fda282SPavel Emelyanov struct ipq *qp = container_of(q, struct ipq, q); 85a39aca67SEric Dumazet struct net *net = q->fqdir->net; 8654db0cc2SGao feng 87648700f7SEric Dumazet const struct frag_v4_compare_key *key = a; 88c6fda282SPavel Emelyanov 89648700f7SEric Dumazet q->key.v4 = *key; 90648700f7SEric Dumazet qp->ecn = 0; 916ce3b4dcSEric Dumazet qp->peer = q->fqdir->max_dist ? 92648700f7SEric Dumazet inet_getpeer_v4(net->ipv4.peers, key->saddr, key->vif, 1) : 93192132b9SDavid Ahern NULL; 94c6fda282SPavel Emelyanov } 95c6fda282SPavel Emelyanov 96aa1f731eSFabian Frederick static void ip4_frag_free(struct inet_frag_queue *q) 971da177e4SLinus Torvalds { 981e4b8287SPavel Emelyanov struct ipq *qp; 991e4b8287SPavel Emelyanov 1001e4b8287SPavel Emelyanov qp = container_of(q, struct ipq, q); 1011e4b8287SPavel Emelyanov if (qp->peer) 1021e4b8287SPavel Emelyanov inet_putpeer(qp->peer); 1031da177e4SLinus Torvalds } 1041da177e4SLinus Torvalds 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds /* Destruction primitives. */ 1071da177e4SLinus Torvalds 108aa1f731eSFabian Frederick static void ipq_put(struct ipq *ipq) 1091da177e4SLinus Torvalds { 110093ba729SEric Dumazet inet_frag_put(&ipq->q); 1111da177e4SLinus Torvalds } 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds /* Kill ipq entry. It is not destroyed immediately, 1141da177e4SLinus Torvalds * because caller (and someone more) holds reference count. 1151da177e4SLinus Torvalds */ 1161da177e4SLinus Torvalds static void ipq_kill(struct ipq *ipq) 1171da177e4SLinus Torvalds { 118093ba729SEric Dumazet inet_frag_kill(&ipq->q); 1191da177e4SLinus Torvalds } 1201da177e4SLinus Torvalds 1215cf42280SAndy Zhou static bool frag_expire_skip_icmp(u32 user) 1225cf42280SAndy Zhou { 1235cf42280SAndy Zhou return user == IP_DEFRAG_AF_PACKET || 1245cf42280SAndy Zhou ip_defrag_user_in_between(user, IP_DEFRAG_CONNTRACK_IN, 1258bc04864SAndy Zhou __IP_DEFRAG_CONNTRACK_IN_END) || 1268bc04864SAndy Zhou ip_defrag_user_in_between(user, IP_DEFRAG_CONNTRACK_BRIDGE_IN, 1278bc04864SAndy Zhou __IP_DEFRAG_CONNTRACK_BRIDGE_IN); 1285cf42280SAndy Zhou } 1295cf42280SAndy Zhou 1301da177e4SLinus Torvalds /* 1311da177e4SLinus Torvalds * Oops, a fragment queue timed out. Kill it and send an ICMP reply. 1321da177e4SLinus Torvalds */ 13378802011SKees Cook static void ip_expire(struct timer_list *t) 1341da177e4SLinus Torvalds { 13578802011SKees Cook struct inet_frag_queue *frag = from_timer(frag, t, timer); 136399d1404SEric Dumazet const struct iphdr *iph; 137fa0f5273SPeter Oskolkov struct sk_buff *head = NULL; 13884a3aa00SPavel Emelyanov struct net *net; 139399d1404SEric Dumazet struct ipq *qp; 140399d1404SEric Dumazet int err; 141e521db9dSPavel Emelyanov 14278802011SKees Cook qp = container_of(frag, struct ipq, q); 143a39aca67SEric Dumazet net = qp->q.fqdir->net; 1441da177e4SLinus Torvalds 145ec4fbd64SEric Dumazet rcu_read_lock(); 146d5dd8879SEric Dumazet 147d5dd8879SEric Dumazet if (qp->q.fqdir->dead) 148d5dd8879SEric Dumazet goto out_rcu_unlock; 149d5dd8879SEric Dumazet 1505ab11c98SPavel Emelyanov spin_lock(&qp->q.lock); 1511da177e4SLinus Torvalds 15206aa8b8aSNikolay Aleksandrov if (qp->q.flags & INET_FRAG_COMPLETE) 1531da177e4SLinus Torvalds goto out; 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds ipq_kill(qp); 156b45386efSEric Dumazet __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS); 157b45386efSEric Dumazet __IP_INC_STATS(net, IPSTATS_MIB_REASMTIMEOUT); 1582e404f63SNikolay Aleksandrov 15970837ffeSDan Carpenter if (!(qp->q.flags & INET_FRAG_FIRST_IN)) 1602e404f63SNikolay Aleksandrov goto out; 1612e404f63SNikolay Aleksandrov 162fa0f5273SPeter Oskolkov /* sk_buff::dev and sk_buff::rbnode are unionized. So we 163fa0f5273SPeter Oskolkov * pull the head out of the tree in order to be able to 164fa0f5273SPeter Oskolkov * deal with head->dev. 165fa0f5273SPeter Oskolkov */ 166c23f35d1SPeter Oskolkov head = inet_frag_pull_head(&qp->q); 167fa0f5273SPeter Oskolkov if (!head) 168fa0f5273SPeter Oskolkov goto out; 16969df9d59SEric Dumazet head->dev = dev_get_by_index_rcu(net, qp->iif); 170e9017b55SShan Wei if (!head->dev) 171ec4fbd64SEric Dumazet goto out; 172ec4fbd64SEric Dumazet 173e9017b55SShan Wei 17497599dc7SEric Dumazet /* skb has no dst, perform route lookup again */ 17564f3b9e2SEric Dumazet iph = ip_hdr(head); 176c6cffba4SDavid S. Miller err = ip_route_input_noref(head, iph->daddr, iph->saddr, 177c10237e0SDavid S. Miller iph->tos, head->dev); 17864f3b9e2SEric Dumazet if (err) 179ec4fbd64SEric Dumazet goto out; 180e9017b55SShan Wei 1812e404f63SNikolay Aleksandrov /* Only an end host needs to send an ICMP 182e9017b55SShan Wei * "Fragment Reassembly Timeout" message, per RFC792. 183e9017b55SShan Wei */ 184648700f7SEric Dumazet if (frag_expire_skip_icmp(qp->q.key.v4.user) && 1855cf42280SAndy Zhou (skb_rtable(head)->rt_type != RTN_LOCAL)) 186ec4fbd64SEric Dumazet goto out; 187ec4fbd64SEric Dumazet 188ec4fbd64SEric Dumazet spin_unlock(&qp->q.lock); 1891eec5d56SEric Dumazet icmp_send(head, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0); 190ec4fbd64SEric Dumazet goto out_rcu_unlock; 1911eec5d56SEric Dumazet 1921da177e4SLinus Torvalds out: 1935ab11c98SPavel Emelyanov spin_unlock(&qp->q.lock); 194ec4fbd64SEric Dumazet out_rcu_unlock: 195ec4fbd64SEric Dumazet rcu_read_unlock(); 196fa0f5273SPeter Oskolkov kfree_skb(head); 1974b6cb5d8SPavel Emelyanov ipq_put(qp); 1981da177e4SLinus Torvalds } 1991da177e4SLinus Torvalds 200abd6523dSPavel Emelyanov /* Find the correct entry in the "incomplete datagrams" queue for 201abd6523dSPavel Emelyanov * this IP datagram, and create new one, if nothing is found. 202abd6523dSPavel Emelyanov */ 2039972f134SDavid Ahern static struct ipq *ip_find(struct net *net, struct iphdr *iph, 2049972f134SDavid Ahern u32 user, int vif) 2051da177e4SLinus Torvalds { 206648700f7SEric Dumazet struct frag_v4_compare_key key = { 207648700f7SEric Dumazet .saddr = iph->saddr, 208648700f7SEric Dumazet .daddr = iph->daddr, 209648700f7SEric Dumazet .user = user, 210648700f7SEric Dumazet .vif = vif, 211648700f7SEric Dumazet .id = iph->id, 212648700f7SEric Dumazet .protocol = iph->protocol, 213648700f7SEric Dumazet }; 214c6fda282SPavel Emelyanov struct inet_frag_queue *q; 2151da177e4SLinus Torvalds 2164907abc6SEric Dumazet q = inet_frag_find(net->ipv4.fqdir, &key); 2172d44ed22SEric Dumazet if (!q) 2181da177e4SLinus Torvalds return NULL; 2192d44ed22SEric Dumazet 2205a3da1feSHannes Frederic Sowa return container_of(q, struct ipq, q); 2215a3da1feSHannes Frederic Sowa } 2221da177e4SLinus Torvalds 22389cee8b1SHerbert Xu /* Is the fragment too far ahead to be part of ipq? */ 224aa1f731eSFabian Frederick static int ip_frag_too_far(struct ipq *qp) 22589cee8b1SHerbert Xu { 22689cee8b1SHerbert Xu struct inet_peer *peer = qp->peer; 2276ce3b4dcSEric Dumazet unsigned int max = qp->q.fqdir->max_dist; 22889cee8b1SHerbert Xu unsigned int start, end; 22989cee8b1SHerbert Xu 23089cee8b1SHerbert Xu int rc; 23189cee8b1SHerbert Xu 23289cee8b1SHerbert Xu if (!peer || !max) 23389cee8b1SHerbert Xu return 0; 23489cee8b1SHerbert Xu 23589cee8b1SHerbert Xu start = qp->rid; 23689cee8b1SHerbert Xu end = atomic_inc_return(&peer->rid); 23789cee8b1SHerbert Xu qp->rid = end; 23889cee8b1SHerbert Xu 239fa0f5273SPeter Oskolkov rc = qp->q.fragments_tail && (end - start) > max; 24089cee8b1SHerbert Xu 241a39aca67SEric Dumazet if (rc) 242a39aca67SEric Dumazet __IP_INC_STATS(qp->q.fqdir->net, IPSTATS_MIB_REASMFAILS); 24389cee8b1SHerbert Xu 24489cee8b1SHerbert Xu return rc; 24589cee8b1SHerbert Xu } 24689cee8b1SHerbert Xu 24789cee8b1SHerbert Xu static int ip_frag_reinit(struct ipq *qp) 24889cee8b1SHerbert Xu { 249d433673eSJesper Dangaard Brouer unsigned int sum_truesize = 0; 25089cee8b1SHerbert Xu 2516ce3b4dcSEric Dumazet if (!mod_timer(&qp->q.timer, jiffies + qp->q.fqdir->timeout)) { 252edcb6918SReshetova, Elena refcount_inc(&qp->q.refcnt); 25389cee8b1SHerbert Xu return -ETIMEDOUT; 25489cee8b1SHerbert Xu } 25589cee8b1SHerbert Xu 256a4fd284aSPeter Oskolkov sum_truesize = inet_frag_rbtree_purge(&qp->q.rb_fragments); 2576ce3b4dcSEric Dumazet sub_frag_mem_limit(qp->q.fqdir, sum_truesize); 25889cee8b1SHerbert Xu 25906aa8b8aSNikolay Aleksandrov qp->q.flags = 0; 2605ab11c98SPavel Emelyanov qp->q.len = 0; 2615ab11c98SPavel Emelyanov qp->q.meat = 0; 262fa0f5273SPeter Oskolkov qp->q.rb_fragments = RB_ROOT; 263d6bebca9SChangli Gao qp->q.fragments_tail = NULL; 264a4fd284aSPeter Oskolkov qp->q.last_run_head = NULL; 26589cee8b1SHerbert Xu qp->iif = 0; 2666623e3b2SEric Dumazet qp->ecn = 0; 26789cee8b1SHerbert Xu 26889cee8b1SHerbert Xu return 0; 26989cee8b1SHerbert Xu } 27089cee8b1SHerbert Xu 2711da177e4SLinus Torvalds /* Add new segment to existing queue. */ 2721706d587SHerbert Xu static int ip_frag_queue(struct ipq *qp, struct sk_buff *skb) 2731da177e4SLinus Torvalds { 274a39aca67SEric Dumazet struct net *net = qp->q.fqdir->net; 275c23f35d1SPeter Oskolkov int ihl, end, flags, offset; 276c23f35d1SPeter Oskolkov struct sk_buff *prev_tail; 2771706d587SHerbert Xu struct net_device *dev; 278d6b915e2SFlorian Westphal unsigned int fragsize; 2791706d587SHerbert Xu int err = -ENOENT; 2806623e3b2SEric Dumazet u8 ecn; 2811da177e4SLinus Torvalds 28206aa8b8aSNikolay Aleksandrov if (qp->q.flags & INET_FRAG_COMPLETE) 2831da177e4SLinus Torvalds goto err; 2841da177e4SLinus Torvalds 28589cee8b1SHerbert Xu if (!(IPCB(skb)->flags & IPSKB_FRAG_COMPLETE) && 2861706d587SHerbert Xu unlikely(ip_frag_too_far(qp)) && 2871706d587SHerbert Xu unlikely(err = ip_frag_reinit(qp))) { 28889cee8b1SHerbert Xu ipq_kill(qp); 28989cee8b1SHerbert Xu goto err; 29089cee8b1SHerbert Xu } 29189cee8b1SHerbert Xu 2926623e3b2SEric Dumazet ecn = ip4_frag_ecn(ip_hdr(skb)->tos); 293eddc9ec5SArnaldo Carvalho de Melo offset = ntohs(ip_hdr(skb)->frag_off); 2941da177e4SLinus Torvalds flags = offset & ~IP_OFFSET; 2951da177e4SLinus Torvalds offset &= IP_OFFSET; 2961da177e4SLinus Torvalds offset <<= 3; /* offset is in 8-byte chunks */ 297c9bdd4b5SArnaldo Carvalho de Melo ihl = ip_hdrlen(skb); 2981da177e4SLinus Torvalds 2991da177e4SLinus Torvalds /* Determine the position of this fragment. */ 3000848f642SEdward Hyunkoo Jee end = offset + skb->len - skb_network_offset(skb) - ihl; 3011706d587SHerbert Xu err = -EINVAL; 3021da177e4SLinus Torvalds 3031da177e4SLinus Torvalds /* Is this the final fragment? */ 3041da177e4SLinus Torvalds if ((flags & IP_MF) == 0) { 3051da177e4SLinus Torvalds /* If we already have some bits beyond end 30642b2aa86SJustin P. Mattock * or have different end, the segment is corrupted. 3071da177e4SLinus Torvalds */ 3085ab11c98SPavel Emelyanov if (end < qp->q.len || 30906aa8b8aSNikolay Aleksandrov ((qp->q.flags & INET_FRAG_LAST_IN) && end != qp->q.len)) 3100ff89efbSPeter Oskolkov goto discard_qp; 31106aa8b8aSNikolay Aleksandrov qp->q.flags |= INET_FRAG_LAST_IN; 3125ab11c98SPavel Emelyanov qp->q.len = end; 3131da177e4SLinus Torvalds } else { 3141da177e4SLinus Torvalds if (end&7) { 3151da177e4SLinus Torvalds end &= ~7; 3161da177e4SLinus Torvalds if (skb->ip_summed != CHECKSUM_UNNECESSARY) 3171da177e4SLinus Torvalds skb->ip_summed = CHECKSUM_NONE; 3181da177e4SLinus Torvalds } 3195ab11c98SPavel Emelyanov if (end > qp->q.len) { 3201da177e4SLinus Torvalds /* Some bits beyond end -> corruption. */ 32106aa8b8aSNikolay Aleksandrov if (qp->q.flags & INET_FRAG_LAST_IN) 3220ff89efbSPeter Oskolkov goto discard_qp; 3235ab11c98SPavel Emelyanov qp->q.len = end; 3241da177e4SLinus Torvalds } 3251da177e4SLinus Torvalds } 3261da177e4SLinus Torvalds if (end == offset) 3270ff89efbSPeter Oskolkov goto discard_qp; 3281da177e4SLinus Torvalds 3291706d587SHerbert Xu err = -ENOMEM; 3300848f642SEdward Hyunkoo Jee if (!pskb_pull(skb, skb_network_offset(skb) + ihl)) 3310ff89efbSPeter Oskolkov goto discard_qp; 3321706d587SHerbert Xu 3331706d587SHerbert Xu err = pskb_trim_rcsum(skb, end - offset); 3341706d587SHerbert Xu if (err) 3350ff89efbSPeter Oskolkov goto discard_qp; 3361da177e4SLinus Torvalds 337fa0f5273SPeter Oskolkov /* Note : skb->rbnode and skb->dev share the same location. */ 338fa0f5273SPeter Oskolkov dev = skb->dev; 339fa0f5273SPeter Oskolkov /* Makes sure compiler wont do silly aliasing games */ 340fa0f5273SPeter Oskolkov barrier(); 3411da177e4SLinus Torvalds 342a4fd284aSPeter Oskolkov prev_tail = qp->q.fragments_tail; 343c23f35d1SPeter Oskolkov err = inet_frag_queue_insert(&qp->q, skb, offset, end); 344c23f35d1SPeter Oskolkov if (err) 345c23f35d1SPeter Oskolkov goto insert_error; 3461da177e4SLinus Torvalds 347bf663371SEric Dumazet if (dev) 348bf663371SEric Dumazet qp->iif = dev->ifindex; 3491da177e4SLinus Torvalds 3505ab11c98SPavel Emelyanov qp->q.stamp = skb->tstamp; 3515ab11c98SPavel Emelyanov qp->q.meat += skb->len; 3526623e3b2SEric Dumazet qp->ecn |= ecn; 3536ce3b4dcSEric Dumazet add_frag_mem_limit(qp->q.fqdir, skb->truesize); 3541da177e4SLinus Torvalds if (offset == 0) 35506aa8b8aSNikolay Aleksandrov qp->q.flags |= INET_FRAG_FIRST_IN; 3561da177e4SLinus Torvalds 357d6b915e2SFlorian Westphal fragsize = skb->len + ihl; 358d6b915e2SFlorian Westphal 359d6b915e2SFlorian Westphal if (fragsize > qp->q.max_size) 360d6b915e2SFlorian Westphal qp->q.max_size = fragsize; 361d6b915e2SFlorian Westphal 3625f2d04f1SPatrick McHardy if (ip_hdr(skb)->frag_off & htons(IP_DF) && 363d6b915e2SFlorian Westphal fragsize > qp->max_df_size) 364d6b915e2SFlorian Westphal qp->max_df_size = fragsize; 3655f2d04f1SPatrick McHardy 36606aa8b8aSNikolay Aleksandrov if (qp->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && 36797599dc7SEric Dumazet qp->q.meat == qp->q.len) { 36897599dc7SEric Dumazet unsigned long orefdst = skb->_skb_refdst; 3691706d587SHerbert Xu 37097599dc7SEric Dumazet skb->_skb_refdst = 0UL; 371a4fd284aSPeter Oskolkov err = ip_frag_reasm(qp, skb, prev_tail, dev); 37297599dc7SEric Dumazet skb->_skb_refdst = orefdst; 3730ff89efbSPeter Oskolkov if (err) 3740ff89efbSPeter Oskolkov inet_frag_kill(&qp->q); 37597599dc7SEric Dumazet return err; 37697599dc7SEric Dumazet } 37797599dc7SEric Dumazet 37897599dc7SEric Dumazet skb_dst_drop(skb); 3791706d587SHerbert Xu return -EINPROGRESS; 3801da177e4SLinus Torvalds 381c23f35d1SPeter Oskolkov insert_error: 382c23f35d1SPeter Oskolkov if (err == IPFRAG_DUP) { 383c23f35d1SPeter Oskolkov kfree_skb(skb); 384c23f35d1SPeter Oskolkov return -EINVAL; 385c23f35d1SPeter Oskolkov } 386c23f35d1SPeter Oskolkov err = -EINVAL; 3870ff89efbSPeter Oskolkov __IP_INC_STATS(net, IPSTATS_MIB_REASM_OVERLAPS); 3887969e5c4SPeter Oskolkov discard_qp: 3897969e5c4SPeter Oskolkov inet_frag_kill(&qp->q); 390c23f35d1SPeter Oskolkov __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS); 3911da177e4SLinus Torvalds err: 3921da177e4SLinus Torvalds kfree_skb(skb); 3931706d587SHerbert Xu return err; 3941da177e4SLinus Torvalds } 3951da177e4SLinus Torvalds 396*891584f4SGuillaume Nault static bool ip_frag_coalesce_ok(const struct ipq *qp) 397*891584f4SGuillaume Nault { 398*891584f4SGuillaume Nault return qp->q.key.v4.user == IP_DEFRAG_LOCAL_DELIVER; 399*891584f4SGuillaume Nault } 400*891584f4SGuillaume Nault 4011da177e4SLinus Torvalds /* Build a new IP datagram from all its fragments. */ 402fa0f5273SPeter Oskolkov static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb, 403a4fd284aSPeter Oskolkov struct sk_buff *prev_tail, struct net_device *dev) 4041da177e4SLinus Torvalds { 405a39aca67SEric Dumazet struct net *net = qp->q.fqdir->net; 4061da177e4SLinus Torvalds struct iphdr *iph; 407c23f35d1SPeter Oskolkov void *reasm_data; 408c23f35d1SPeter Oskolkov int len, err; 4095173cc05SEric Dumazet u8 ecn; 4101da177e4SLinus Torvalds 4111da177e4SLinus Torvalds ipq_kill(qp); 4121da177e4SLinus Torvalds 413be991971SHannes Frederic Sowa ecn = ip_frag_ecn_table[qp->ecn]; 4145173cc05SEric Dumazet if (unlikely(ecn == 0xff)) { 4155173cc05SEric Dumazet err = -EINVAL; 4165173cc05SEric Dumazet goto out_fail; 4175173cc05SEric Dumazet } 418c23f35d1SPeter Oskolkov 4191706d587SHerbert Xu /* Make the one we just received the head. */ 420c23f35d1SPeter Oskolkov reasm_data = inet_frag_reasm_prepare(&qp->q, skb, prev_tail); 421c23f35d1SPeter Oskolkov if (!reasm_data) 4221706d587SHerbert Xu goto out_nomem; 4231706d587SHerbert Xu 424c23f35d1SPeter Oskolkov len = ip_hdrlen(skb) + qp->q.len; 4251706d587SHerbert Xu err = -E2BIG; 4261da177e4SLinus Torvalds if (len > 65535) 4271da177e4SLinus Torvalds goto out_oversize; 4281da177e4SLinus Torvalds 429*891584f4SGuillaume Nault inet_frag_reasm_finish(&qp->q, skb, reasm_data, 430*891584f4SGuillaume Nault ip_frag_coalesce_ok(qp)); 431ebaf39e6SJiri Wiesner 432c23f35d1SPeter Oskolkov skb->dev = dev; 433c23f35d1SPeter Oskolkov IPCB(skb)->frag_max_size = max(qp->max_df_size, qp->q.max_size); 4341da177e4SLinus Torvalds 435c23f35d1SPeter Oskolkov iph = ip_hdr(skb); 4361da177e4SLinus Torvalds iph->tot_len = htons(len); 4375173cc05SEric Dumazet iph->tos |= ecn; 438d6b915e2SFlorian Westphal 439d6b915e2SFlorian Westphal /* When we set IP_DF on a refragmented skb we must also force a 440d6b915e2SFlorian Westphal * call to ip_fragment to avoid forwarding a DF-skb of size s while 441d6b915e2SFlorian Westphal * original sender only sent fragments of size f (where f < s). 442d6b915e2SFlorian Westphal * 443d6b915e2SFlorian Westphal * We only set DF/IPSKB_FRAG_PMTU if such DF fragment was the largest 444d6b915e2SFlorian Westphal * frag seen to avoid sending tiny DF-fragments in case skb was built 445d6b915e2SFlorian Westphal * from one very small df-fragment and one large non-df frag. 446d6b915e2SFlorian Westphal */ 447d6b915e2SFlorian Westphal if (qp->max_df_size == qp->q.max_size) { 448c23f35d1SPeter Oskolkov IPCB(skb)->flags |= IPSKB_FRAG_PMTU; 449d6b915e2SFlorian Westphal iph->frag_off = htons(IP_DF); 450d6b915e2SFlorian Westphal } else { 451d6b915e2SFlorian Westphal iph->frag_off = 0; 452d6b915e2SFlorian Westphal } 453d6b915e2SFlorian Westphal 4540848f642SEdward Hyunkoo Jee ip_send_check(iph); 4550848f642SEdward Hyunkoo Jee 456b45386efSEric Dumazet __IP_INC_STATS(net, IPSTATS_MIB_REASMOKS); 457fa0f5273SPeter Oskolkov qp->q.rb_fragments = RB_ROOT; 458d6bebca9SChangli Gao qp->q.fragments_tail = NULL; 459a4fd284aSPeter Oskolkov qp->q.last_run_head = NULL; 4601706d587SHerbert Xu return 0; 4611da177e4SLinus Torvalds 4621da177e4SLinus Torvalds out_nomem: 463ba7a46f1SJoe Perches net_dbg_ratelimited("queue_glue: no memory for gluing queue %p\n", qp); 46445542479SDavid Howells err = -ENOMEM; 4651da177e4SLinus Torvalds goto out_fail; 4661da177e4SLinus Torvalds out_oversize: 467648700f7SEric Dumazet net_info_ratelimited("Oversized IP packet from %pI4\n", &qp->q.key.v4.saddr); 4681da177e4SLinus Torvalds out_fail: 469b45386efSEric Dumazet __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS); 4701706d587SHerbert Xu return err; 4711da177e4SLinus Torvalds } 4721da177e4SLinus Torvalds 4731da177e4SLinus Torvalds /* Process an incoming IP datagram fragment. */ 47419bcf9f2SEric W. Biederman int ip_defrag(struct net *net, struct sk_buff *skb, u32 user) 4751da177e4SLinus Torvalds { 4769972f134SDavid Ahern struct net_device *dev = skb->dev ? : skb_dst(skb)->dev; 477385add90SDavid Ahern int vif = l3mdev_master_ifindex_rcu(dev); 4781da177e4SLinus Torvalds struct ipq *qp; 4791da177e4SLinus Torvalds 480b45386efSEric Dumazet __IP_INC_STATS(net, IPSTATS_MIB_REASMREQDS); 4818282f274SJoe Stringer skb_orphan(skb); 4821da177e4SLinus Torvalds 4831da177e4SLinus Torvalds /* Lookup (or create) queue header */ 4849972f134SDavid Ahern qp = ip_find(net, ip_hdr(skb), user, vif); 48500db4124SIan Morris if (qp) { 4861706d587SHerbert Xu int ret; 4871da177e4SLinus Torvalds 4885ab11c98SPavel Emelyanov spin_lock(&qp->q.lock); 4891da177e4SLinus Torvalds 4901706d587SHerbert Xu ret = ip_frag_queue(qp, skb); 4911da177e4SLinus Torvalds 4925ab11c98SPavel Emelyanov spin_unlock(&qp->q.lock); 4934b6cb5d8SPavel Emelyanov ipq_put(qp); 494776c729eSHerbert Xu return ret; 4951da177e4SLinus Torvalds } 4961da177e4SLinus Torvalds 497b45386efSEric Dumazet __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS); 4981da177e4SLinus Torvalds kfree_skb(skb); 499776c729eSHerbert Xu return -ENOMEM; 5001da177e4SLinus Torvalds } 5014bc2f18bSEric Dumazet EXPORT_SYMBOL(ip_defrag); 5021da177e4SLinus Torvalds 50319bcf9f2SEric W. Biederman struct sk_buff *ip_check_defrag(struct net *net, struct sk_buff *skb, u32 user) 504bc416d97SEric Dumazet { 5051bf3751eSJohannes Berg struct iphdr iph; 5063e32e733SAlexander Drozdov int netoff; 507bc416d97SEric Dumazet u32 len; 508bc416d97SEric Dumazet 509bc416d97SEric Dumazet if (skb->protocol != htons(ETH_P_IP)) 510bc416d97SEric Dumazet return skb; 511bc416d97SEric Dumazet 5123e32e733SAlexander Drozdov netoff = skb_network_offset(skb); 5133e32e733SAlexander Drozdov 5143e32e733SAlexander Drozdov if (skb_copy_bits(skb, netoff, &iph, sizeof(iph)) < 0) 515bc416d97SEric Dumazet return skb; 516bc416d97SEric Dumazet 5171bf3751eSJohannes Berg if (iph.ihl < 5 || iph.version != 4) 518bc416d97SEric Dumazet return skb; 519bc416d97SEric Dumazet 5201bf3751eSJohannes Berg len = ntohs(iph.tot_len); 5213e32e733SAlexander Drozdov if (skb->len < netoff + len || len < (iph.ihl * 4)) 5221bf3751eSJohannes Berg return skb; 5231bf3751eSJohannes Berg 5241bf3751eSJohannes Berg if (ip_is_fragment(&iph)) { 525bc416d97SEric Dumazet skb = skb_share_check(skb, GFP_ATOMIC); 526bc416d97SEric Dumazet if (skb) { 5277de414a9SCong Wang if (!pskb_may_pull(skb, netoff + iph.ihl * 4)) { 5287de414a9SCong Wang kfree_skb(skb); 5297de414a9SCong Wang return NULL; 5307de414a9SCong Wang } 5317de414a9SCong Wang if (pskb_trim_rcsum(skb, netoff + len)) { 5327de414a9SCong Wang kfree_skb(skb); 5337de414a9SCong Wang return NULL; 5347de414a9SCong Wang } 535bc416d97SEric Dumazet memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); 53619bcf9f2SEric W. Biederman if (ip_defrag(net, skb, user)) 537bc416d97SEric Dumazet return NULL; 5387539fadcSTom Herbert skb_clear_hash(skb); 539bc416d97SEric Dumazet } 540bc416d97SEric Dumazet } 541bc416d97SEric Dumazet return skb; 542bc416d97SEric Dumazet } 543bc416d97SEric Dumazet EXPORT_SYMBOL(ip_check_defrag); 544bc416d97SEric Dumazet 5458d8354d2SPavel Emelyanov #ifdef CONFIG_SYSCTL 5463d234012SEric Dumazet static int dist_min; 5478d8354d2SPavel Emelyanov 5480a64b4b8SPavel Emelyanov static struct ctl_table ip4_frags_ns_ctl_table[] = { 5498d8354d2SPavel Emelyanov { 5508d8354d2SPavel Emelyanov .procname = "ipfrag_high_thresh", 5513e67f106SEric Dumazet .maxlen = sizeof(unsigned long), 5528d8354d2SPavel Emelyanov .mode = 0644, 5533e67f106SEric Dumazet .proc_handler = proc_doulongvec_minmax, 5548d8354d2SPavel Emelyanov }, 5558d8354d2SPavel Emelyanov { 5568d8354d2SPavel Emelyanov .procname = "ipfrag_low_thresh", 5573e67f106SEric Dumazet .maxlen = sizeof(unsigned long), 5588d8354d2SPavel Emelyanov .mode = 0644, 5593e67f106SEric Dumazet .proc_handler = proc_doulongvec_minmax, 5608d8354d2SPavel Emelyanov }, 5618d8354d2SPavel Emelyanov { 5628d8354d2SPavel Emelyanov .procname = "ipfrag_time", 5638d8354d2SPavel Emelyanov .maxlen = sizeof(int), 5648d8354d2SPavel Emelyanov .mode = 0644, 5656d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 5668d8354d2SPavel Emelyanov }, 5670fbf4cb2SNikolay Borisov { 5680fbf4cb2SNikolay Borisov .procname = "ipfrag_max_dist", 5690fbf4cb2SNikolay Borisov .maxlen = sizeof(int), 5700fbf4cb2SNikolay Borisov .mode = 0644, 5710fbf4cb2SNikolay Borisov .proc_handler = proc_dointvec_minmax, 5723d234012SEric Dumazet .extra1 = &dist_min, 5730fbf4cb2SNikolay Borisov }, 5747d291ebbSPavel Emelyanov { } 5757d291ebbSPavel Emelyanov }; 5767d291ebbSPavel Emelyanov 577e3a57d18SFlorian Westphal /* secret interval has been deprecated */ 578e3a57d18SFlorian Westphal static int ip4_frags_secret_interval_unused; 5797d291ebbSPavel Emelyanov static struct ctl_table ip4_frags_ctl_table[] = { 5808d8354d2SPavel Emelyanov { 5818d8354d2SPavel Emelyanov .procname = "ipfrag_secret_interval", 582e3a57d18SFlorian Westphal .data = &ip4_frags_secret_interval_unused, 5838d8354d2SPavel Emelyanov .maxlen = sizeof(int), 5848d8354d2SPavel Emelyanov .mode = 0644, 5856d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 5868d8354d2SPavel Emelyanov }, 5878d8354d2SPavel Emelyanov { } 5888d8354d2SPavel Emelyanov }; 5898d8354d2SPavel Emelyanov 5902c8c1e72SAlexey Dobriyan static int __net_init ip4_frags_ns_ctl_register(struct net *net) 5918d8354d2SPavel Emelyanov { 592e4a2d5c2SPavel Emelyanov struct ctl_table *table; 5938d8354d2SPavel Emelyanov struct ctl_table_header *hdr; 5948d8354d2SPavel Emelyanov 5950a64b4b8SPavel Emelyanov table = ip4_frags_ns_ctl_table; 59609ad9bc7SOctavian Purdila if (!net_eq(net, &init_net)) { 5970a64b4b8SPavel Emelyanov table = kmemdup(table, sizeof(ip4_frags_ns_ctl_table), GFP_KERNEL); 59851456b29SIan Morris if (!table) 599e4a2d5c2SPavel Emelyanov goto err_alloc; 600e4a2d5c2SPavel Emelyanov 6018dfdb313SEric Dumazet } 6024907abc6SEric Dumazet table[0].data = &net->ipv4.fqdir->high_thresh; 6034907abc6SEric Dumazet table[0].extra1 = &net->ipv4.fqdir->low_thresh; 6044907abc6SEric Dumazet table[1].data = &net->ipv4.fqdir->low_thresh; 6054907abc6SEric Dumazet table[1].extra2 = &net->ipv4.fqdir->high_thresh; 6064907abc6SEric Dumazet table[2].data = &net->ipv4.fqdir->timeout; 6074907abc6SEric Dumazet table[3].data = &net->ipv4.fqdir->max_dist; 608e4a2d5c2SPavel Emelyanov 609ec8f23ceSEric W. Biederman hdr = register_net_sysctl(net, "net/ipv4", table); 61051456b29SIan Morris if (!hdr) 611e4a2d5c2SPavel Emelyanov goto err_reg; 612e4a2d5c2SPavel Emelyanov 613e4a2d5c2SPavel Emelyanov net->ipv4.frags_hdr = hdr; 614e4a2d5c2SPavel Emelyanov return 0; 615e4a2d5c2SPavel Emelyanov 616e4a2d5c2SPavel Emelyanov err_reg: 61709ad9bc7SOctavian Purdila if (!net_eq(net, &init_net)) 618e4a2d5c2SPavel Emelyanov kfree(table); 619e4a2d5c2SPavel Emelyanov err_alloc: 620e4a2d5c2SPavel Emelyanov return -ENOMEM; 621e4a2d5c2SPavel Emelyanov } 622e4a2d5c2SPavel Emelyanov 6232c8c1e72SAlexey Dobriyan static void __net_exit ip4_frags_ns_ctl_unregister(struct net *net) 624e4a2d5c2SPavel Emelyanov { 625e4a2d5c2SPavel Emelyanov struct ctl_table *table; 626e4a2d5c2SPavel Emelyanov 627e4a2d5c2SPavel Emelyanov table = net->ipv4.frags_hdr->ctl_table_arg; 628e4a2d5c2SPavel Emelyanov unregister_net_sysctl_table(net->ipv4.frags_hdr); 629e4a2d5c2SPavel Emelyanov kfree(table); 6308d8354d2SPavel Emelyanov } 6317d291ebbSPavel Emelyanov 63257a02c39SFabian Frederick static void __init ip4_frags_ctl_register(void) 6337d291ebbSPavel Emelyanov { 63443444757SEric W. Biederman register_net_sysctl(&init_net, "net/ipv4", ip4_frags_ctl_table); 6357d291ebbSPavel Emelyanov } 6368d8354d2SPavel Emelyanov #else 637aa1f731eSFabian Frederick static int ip4_frags_ns_ctl_register(struct net *net) 6388d8354d2SPavel Emelyanov { 6398d8354d2SPavel Emelyanov return 0; 6408d8354d2SPavel Emelyanov } 641e4a2d5c2SPavel Emelyanov 642aa1f731eSFabian Frederick static void ip4_frags_ns_ctl_unregister(struct net *net) 643e4a2d5c2SPavel Emelyanov { 644e4a2d5c2SPavel Emelyanov } 6457d291ebbSPavel Emelyanov 646aa1f731eSFabian Frederick static void __init ip4_frags_ctl_register(void) 6477d291ebbSPavel Emelyanov { 6487d291ebbSPavel Emelyanov } 6498d8354d2SPavel Emelyanov #endif 6508d8354d2SPavel Emelyanov 6512c8c1e72SAlexey Dobriyan static int __net_init ipv4_frags_init_net(struct net *net) 6528d8354d2SPavel Emelyanov { 653787bea77SEric Dumazet int res; 654787bea77SEric Dumazet 6554907abc6SEric Dumazet res = fqdir_init(&net->ipv4.fqdir, &ip4_frags, net); 6564907abc6SEric Dumazet if (res < 0) 6574907abc6SEric Dumazet return res; 658c2a93660SJesper Dangaard Brouer /* Fragment cache limits. 659c2a93660SJesper Dangaard Brouer * 660c2a93660SJesper Dangaard Brouer * The fragment memory accounting code, (tries to) account for 661c2a93660SJesper Dangaard Brouer * the real memory usage, by measuring both the size of frag 662c2a93660SJesper Dangaard Brouer * queue struct (inet_frag_queue (ipv4:ipq/ipv6:frag_queue)) 663c2a93660SJesper Dangaard Brouer * and the SKB's truesize. 664c2a93660SJesper Dangaard Brouer * 665c2a93660SJesper Dangaard Brouer * A 64K fragment consumes 129736 bytes (44*2944)+200 666c2a93660SJesper Dangaard Brouer * (1500 truesize == 2944, sizeof(struct ipq) == 200) 667c2a93660SJesper Dangaard Brouer * 668c2a93660SJesper Dangaard Brouer * We will commit 4MB at one time. Should we cross that limit 669c2a93660SJesper Dangaard Brouer * we will prune down to 3MB, making room for approx 8 big 64K 670c2a93660SJesper Dangaard Brouer * fragments 8x128k. 671e31e0bdcSPavel Emelyanov */ 6724907abc6SEric Dumazet net->ipv4.fqdir->high_thresh = 4 * 1024 * 1024; 6734907abc6SEric Dumazet net->ipv4.fqdir->low_thresh = 3 * 1024 * 1024; 674e31e0bdcSPavel Emelyanov /* 675b2fd5321SPavel Emelyanov * Important NOTE! Fragment queue must be destroyed before MSL expires. 676b2fd5321SPavel Emelyanov * RFC791 is wrong proposing to prolongate timer each fragment arrival 677b2fd5321SPavel Emelyanov * by TTL. 678b2fd5321SPavel Emelyanov */ 6794907abc6SEric Dumazet net->ipv4.fqdir->timeout = IP_FRAG_TIME; 680b2fd5321SPavel Emelyanov 6814907abc6SEric Dumazet net->ipv4.fqdir->max_dist = 64; 6820fbf4cb2SNikolay Borisov 683787bea77SEric Dumazet res = ip4_frags_ns_ctl_register(net); 684787bea77SEric Dumazet if (res < 0) 6854907abc6SEric Dumazet fqdir_exit(net->ipv4.fqdir); 686787bea77SEric Dumazet return res; 6878d8354d2SPavel Emelyanov } 6888d8354d2SPavel Emelyanov 689d5dd8879SEric Dumazet static void __net_exit ipv4_frags_pre_exit_net(struct net *net) 690d5dd8879SEric Dumazet { 691d5dd8879SEric Dumazet fqdir_pre_exit(net->ipv4.fqdir); 692d5dd8879SEric Dumazet } 693d5dd8879SEric Dumazet 6942c8c1e72SAlexey Dobriyan static void __net_exit ipv4_frags_exit_net(struct net *net) 69581566e83SPavel Emelyanov { 6960a64b4b8SPavel Emelyanov ip4_frags_ns_ctl_unregister(net); 6974907abc6SEric Dumazet fqdir_exit(net->ipv4.fqdir); 69881566e83SPavel Emelyanov } 69981566e83SPavel Emelyanov 70081566e83SPavel Emelyanov static struct pernet_operations ip4_frags_ops = { 70181566e83SPavel Emelyanov .init = ipv4_frags_init_net, 702d5dd8879SEric Dumazet .pre_exit = ipv4_frags_pre_exit_net, 70381566e83SPavel Emelyanov .exit = ipv4_frags_exit_net, 70481566e83SPavel Emelyanov }; 70581566e83SPavel Emelyanov 706648700f7SEric Dumazet 707648700f7SEric Dumazet static u32 ip4_key_hashfn(const void *data, u32 len, u32 seed) 708648700f7SEric Dumazet { 709648700f7SEric Dumazet return jhash2(data, 710648700f7SEric Dumazet sizeof(struct frag_v4_compare_key) / sizeof(u32), seed); 711648700f7SEric Dumazet } 712648700f7SEric Dumazet 713648700f7SEric Dumazet static u32 ip4_obj_hashfn(const void *data, u32 len, u32 seed) 714648700f7SEric Dumazet { 715648700f7SEric Dumazet const struct inet_frag_queue *fq = data; 716648700f7SEric Dumazet 717648700f7SEric Dumazet return jhash2((const u32 *)&fq->key.v4, 718648700f7SEric Dumazet sizeof(struct frag_v4_compare_key) / sizeof(u32), seed); 719648700f7SEric Dumazet } 720648700f7SEric Dumazet 721648700f7SEric Dumazet static int ip4_obj_cmpfn(struct rhashtable_compare_arg *arg, const void *ptr) 722648700f7SEric Dumazet { 723648700f7SEric Dumazet const struct frag_v4_compare_key *key = arg->key; 724648700f7SEric Dumazet const struct inet_frag_queue *fq = ptr; 725648700f7SEric Dumazet 726648700f7SEric Dumazet return !!memcmp(&fq->key, key, sizeof(*key)); 727648700f7SEric Dumazet } 728648700f7SEric Dumazet 729648700f7SEric Dumazet static const struct rhashtable_params ip4_rhash_params = { 730648700f7SEric Dumazet .head_offset = offsetof(struct inet_frag_queue, node), 731648700f7SEric Dumazet .key_offset = offsetof(struct inet_frag_queue, key), 732648700f7SEric Dumazet .key_len = sizeof(struct frag_v4_compare_key), 733648700f7SEric Dumazet .hashfn = ip4_key_hashfn, 734648700f7SEric Dumazet .obj_hashfn = ip4_obj_hashfn, 735648700f7SEric Dumazet .obj_cmpfn = ip4_obj_cmpfn, 736648700f7SEric Dumazet .automatic_shrinking = true, 737648700f7SEric Dumazet }; 738648700f7SEric Dumazet 739b7aa0bf7SEric Dumazet void __init ipfrag_init(void) 7401da177e4SLinus Torvalds { 741c6fda282SPavel Emelyanov ip4_frags.constructor = ip4_frag_init; 7421e4b8287SPavel Emelyanov ip4_frags.destructor = ip4_frag_free; 7431e4b8287SPavel Emelyanov ip4_frags.qsize = sizeof(struct ipq); 744e521db9dSPavel Emelyanov ip4_frags.frag_expire = ip_expire; 745d4ad4d22SNikolay Aleksandrov ip4_frags.frags_cache_name = ip_frag_cache_name; 746648700f7SEric Dumazet ip4_frags.rhash_params = ip4_rhash_params; 747d4ad4d22SNikolay Aleksandrov if (inet_frags_init(&ip4_frags)) 748d4ad4d22SNikolay Aleksandrov panic("IP: failed to allocate ip4_frags cache\n"); 749483a6e4fSEric Dumazet ip4_frags_ctl_register(); 750483a6e4fSEric Dumazet register_pernet_subsys(&ip4_frags_ops); 7511da177e4SLinus Torvalds } 752