162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * INET An implementation of the TCP/IP protocol suite for the LINUX 462306a36Sopenharmony_ci * operating system. INET is implemented using the BSD Socket 562306a36Sopenharmony_ci * interface as the means of communication with the user level. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * The IP fragmentation functionality. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Authors: Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG> 1062306a36Sopenharmony_ci * Alan Cox <alan@lxorguk.ukuu.org.uk> 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Fixes: 1362306a36Sopenharmony_ci * Alan Cox : Split from ip.c , see ip_input.c for history. 1462306a36Sopenharmony_ci * David S. Miller : Begin massive cleanup... 1562306a36Sopenharmony_ci * Andi Kleen : Add sysctls. 1662306a36Sopenharmony_ci * xxxx : Overlapfrag bug. 1762306a36Sopenharmony_ci * Ultima : ip_expire() kernel panic. 1862306a36Sopenharmony_ci * Bill Hawes : Frag accounting and evictor fixes. 1962306a36Sopenharmony_ci * John McDonald : 0 length frag bug. 2062306a36Sopenharmony_ci * Alexey Kuznetsov: SMP races, threading, cleanup. 2162306a36Sopenharmony_ci * Patrick McHardy : LRU queue of frag heads for evictor. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define pr_fmt(fmt) "IPv4: " fmt 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <linux/compiler.h> 2762306a36Sopenharmony_ci#include <linux/module.h> 2862306a36Sopenharmony_ci#include <linux/types.h> 2962306a36Sopenharmony_ci#include <linux/mm.h> 3062306a36Sopenharmony_ci#include <linux/jiffies.h> 3162306a36Sopenharmony_ci#include <linux/skbuff.h> 3262306a36Sopenharmony_ci#include <linux/list.h> 3362306a36Sopenharmony_ci#include <linux/ip.h> 3462306a36Sopenharmony_ci#include <linux/icmp.h> 3562306a36Sopenharmony_ci#include <linux/netdevice.h> 3662306a36Sopenharmony_ci#include <linux/jhash.h> 3762306a36Sopenharmony_ci#include <linux/random.h> 3862306a36Sopenharmony_ci#include <linux/slab.h> 3962306a36Sopenharmony_ci#include <net/route.h> 4062306a36Sopenharmony_ci#include <net/dst.h> 4162306a36Sopenharmony_ci#include <net/sock.h> 4262306a36Sopenharmony_ci#include <net/ip.h> 4362306a36Sopenharmony_ci#include <net/icmp.h> 4462306a36Sopenharmony_ci#include <net/checksum.h> 4562306a36Sopenharmony_ci#include <net/inetpeer.h> 4662306a36Sopenharmony_ci#include <net/inet_frag.h> 4762306a36Sopenharmony_ci#include <linux/tcp.h> 4862306a36Sopenharmony_ci#include <linux/udp.h> 4962306a36Sopenharmony_ci#include <linux/inet.h> 5062306a36Sopenharmony_ci#include <linux/netfilter_ipv4.h> 5162306a36Sopenharmony_ci#include <net/inet_ecn.h> 5262306a36Sopenharmony_ci#include <net/l3mdev.h> 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* NOTE. Logic of IP defragmentation is parallel to corresponding IPv6 5562306a36Sopenharmony_ci * code now. If you change something here, _PLEASE_ update ipv6/reassembly.c 5662306a36Sopenharmony_ci * as well. Or notify me, at least. --ANK 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_cistatic const char ip_frag_cache_name[] = "ip4-frags"; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* Describe an entry in the "incomplete datagrams" queue. */ 6162306a36Sopenharmony_cistruct ipq { 6262306a36Sopenharmony_ci struct inet_frag_queue q; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci u8 ecn; /* RFC3168 support */ 6562306a36Sopenharmony_ci u16 max_df_size; /* largest frag with DF set seen */ 6662306a36Sopenharmony_ci int iif; 6762306a36Sopenharmony_ci unsigned int rid; 6862306a36Sopenharmony_ci struct inet_peer *peer; 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic u8 ip4_frag_ecn(u8 tos) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci return 1 << (tos & INET_ECN_MASK); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic struct inet_frags ip4_frags; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb, 7962306a36Sopenharmony_ci struct sk_buff *prev_tail, struct net_device *dev); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void ip4_frag_init(struct inet_frag_queue *q, const void *a) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct ipq *qp = container_of(q, struct ipq, q); 8562306a36Sopenharmony_ci struct net *net = q->fqdir->net; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci const struct frag_v4_compare_key *key = a; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci q->key.v4 = *key; 9062306a36Sopenharmony_ci qp->ecn = 0; 9162306a36Sopenharmony_ci qp->peer = q->fqdir->max_dist ? 9262306a36Sopenharmony_ci inet_getpeer_v4(net->ipv4.peers, key->saddr, key->vif, 1) : 9362306a36Sopenharmony_ci NULL; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic void ip4_frag_free(struct inet_frag_queue *q) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct ipq *qp; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci qp = container_of(q, struct ipq, q); 10162306a36Sopenharmony_ci if (qp->peer) 10262306a36Sopenharmony_ci inet_putpeer(qp->peer); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* Destruction primitives. */ 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic void ipq_put(struct ipq *ipq) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci inet_frag_put(&ipq->q); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* Kill ipq entry. It is not destroyed immediately, 11462306a36Sopenharmony_ci * because caller (and someone more) holds reference count. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_cistatic void ipq_kill(struct ipq *ipq) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci inet_frag_kill(&ipq->q); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic bool frag_expire_skip_icmp(u32 user) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci return user == IP_DEFRAG_AF_PACKET || 12462306a36Sopenharmony_ci ip_defrag_user_in_between(user, IP_DEFRAG_CONNTRACK_IN, 12562306a36Sopenharmony_ci __IP_DEFRAG_CONNTRACK_IN_END) || 12662306a36Sopenharmony_ci ip_defrag_user_in_between(user, IP_DEFRAG_CONNTRACK_BRIDGE_IN, 12762306a36Sopenharmony_ci __IP_DEFRAG_CONNTRACK_BRIDGE_IN); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* 13162306a36Sopenharmony_ci * Oops, a fragment queue timed out. Kill it and send an ICMP reply. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_cistatic void ip_expire(struct timer_list *t) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct inet_frag_queue *frag = from_timer(frag, t, timer); 13662306a36Sopenharmony_ci const struct iphdr *iph; 13762306a36Sopenharmony_ci struct sk_buff *head = NULL; 13862306a36Sopenharmony_ci struct net *net; 13962306a36Sopenharmony_ci struct ipq *qp; 14062306a36Sopenharmony_ci int err; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci qp = container_of(frag, struct ipq, q); 14362306a36Sopenharmony_ci net = qp->q.fqdir->net; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci rcu_read_lock(); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* Paired with WRITE_ONCE() in fqdir_pre_exit(). */ 14862306a36Sopenharmony_ci if (READ_ONCE(qp->q.fqdir->dead)) 14962306a36Sopenharmony_ci goto out_rcu_unlock; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci spin_lock(&qp->q.lock); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (qp->q.flags & INET_FRAG_COMPLETE) 15462306a36Sopenharmony_ci goto out; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci qp->q.flags |= INET_FRAG_DROP; 15762306a36Sopenharmony_ci ipq_kill(qp); 15862306a36Sopenharmony_ci __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS); 15962306a36Sopenharmony_ci __IP_INC_STATS(net, IPSTATS_MIB_REASMTIMEOUT); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (!(qp->q.flags & INET_FRAG_FIRST_IN)) 16262306a36Sopenharmony_ci goto out; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* sk_buff::dev and sk_buff::rbnode are unionized. So we 16562306a36Sopenharmony_ci * pull the head out of the tree in order to be able to 16662306a36Sopenharmony_ci * deal with head->dev. 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ci head = inet_frag_pull_head(&qp->q); 16962306a36Sopenharmony_ci if (!head) 17062306a36Sopenharmony_ci goto out; 17162306a36Sopenharmony_ci head->dev = dev_get_by_index_rcu(net, qp->iif); 17262306a36Sopenharmony_ci if (!head->dev) 17362306a36Sopenharmony_ci goto out; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* skb has no dst, perform route lookup again */ 17762306a36Sopenharmony_ci iph = ip_hdr(head); 17862306a36Sopenharmony_ci err = ip_route_input_noref(head, iph->daddr, iph->saddr, 17962306a36Sopenharmony_ci iph->tos, head->dev); 18062306a36Sopenharmony_ci if (err) 18162306a36Sopenharmony_ci goto out; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Only an end host needs to send an ICMP 18462306a36Sopenharmony_ci * "Fragment Reassembly Timeout" message, per RFC792. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci if (frag_expire_skip_icmp(qp->q.key.v4.user) && 18762306a36Sopenharmony_ci (skb_rtable(head)->rt_type != RTN_LOCAL)) 18862306a36Sopenharmony_ci goto out; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci spin_unlock(&qp->q.lock); 19162306a36Sopenharmony_ci icmp_send(head, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0); 19262306a36Sopenharmony_ci goto out_rcu_unlock; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ciout: 19562306a36Sopenharmony_ci spin_unlock(&qp->q.lock); 19662306a36Sopenharmony_ciout_rcu_unlock: 19762306a36Sopenharmony_ci rcu_read_unlock(); 19862306a36Sopenharmony_ci kfree_skb_reason(head, SKB_DROP_REASON_FRAG_REASM_TIMEOUT); 19962306a36Sopenharmony_ci ipq_put(qp); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/* Find the correct entry in the "incomplete datagrams" queue for 20362306a36Sopenharmony_ci * this IP datagram, and create new one, if nothing is found. 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_cistatic struct ipq *ip_find(struct net *net, struct iphdr *iph, 20662306a36Sopenharmony_ci u32 user, int vif) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct frag_v4_compare_key key = { 20962306a36Sopenharmony_ci .saddr = iph->saddr, 21062306a36Sopenharmony_ci .daddr = iph->daddr, 21162306a36Sopenharmony_ci .user = user, 21262306a36Sopenharmony_ci .vif = vif, 21362306a36Sopenharmony_ci .id = iph->id, 21462306a36Sopenharmony_ci .protocol = iph->protocol, 21562306a36Sopenharmony_ci }; 21662306a36Sopenharmony_ci struct inet_frag_queue *q; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci q = inet_frag_find(net->ipv4.fqdir, &key); 21962306a36Sopenharmony_ci if (!q) 22062306a36Sopenharmony_ci return NULL; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return container_of(q, struct ipq, q); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci/* Is the fragment too far ahead to be part of ipq? */ 22662306a36Sopenharmony_cistatic int ip_frag_too_far(struct ipq *qp) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct inet_peer *peer = qp->peer; 22962306a36Sopenharmony_ci unsigned int max = qp->q.fqdir->max_dist; 23062306a36Sopenharmony_ci unsigned int start, end; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci int rc; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (!peer || !max) 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci start = qp->rid; 23862306a36Sopenharmony_ci end = atomic_inc_return(&peer->rid); 23962306a36Sopenharmony_ci qp->rid = end; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci rc = qp->q.fragments_tail && (end - start) > max; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (rc) 24462306a36Sopenharmony_ci __IP_INC_STATS(qp->q.fqdir->net, IPSTATS_MIB_REASMFAILS); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return rc; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic int ip_frag_reinit(struct ipq *qp) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci unsigned int sum_truesize = 0; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (!mod_timer(&qp->q.timer, jiffies + qp->q.fqdir->timeout)) { 25462306a36Sopenharmony_ci refcount_inc(&qp->q.refcnt); 25562306a36Sopenharmony_ci return -ETIMEDOUT; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci sum_truesize = inet_frag_rbtree_purge(&qp->q.rb_fragments, 25962306a36Sopenharmony_ci SKB_DROP_REASON_FRAG_TOO_FAR); 26062306a36Sopenharmony_ci sub_frag_mem_limit(qp->q.fqdir, sum_truesize); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci qp->q.flags = 0; 26362306a36Sopenharmony_ci qp->q.len = 0; 26462306a36Sopenharmony_ci qp->q.meat = 0; 26562306a36Sopenharmony_ci qp->q.rb_fragments = RB_ROOT; 26662306a36Sopenharmony_ci qp->q.fragments_tail = NULL; 26762306a36Sopenharmony_ci qp->q.last_run_head = NULL; 26862306a36Sopenharmony_ci qp->iif = 0; 26962306a36Sopenharmony_ci qp->ecn = 0; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return 0; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci/* Add new segment to existing queue. */ 27562306a36Sopenharmony_cistatic int ip_frag_queue(struct ipq *qp, struct sk_buff *skb) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct net *net = qp->q.fqdir->net; 27862306a36Sopenharmony_ci int ihl, end, flags, offset; 27962306a36Sopenharmony_ci struct sk_buff *prev_tail; 28062306a36Sopenharmony_ci struct net_device *dev; 28162306a36Sopenharmony_ci unsigned int fragsize; 28262306a36Sopenharmony_ci int err = -ENOENT; 28362306a36Sopenharmony_ci SKB_DR(reason); 28462306a36Sopenharmony_ci u8 ecn; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* If reassembly is already done, @skb must be a duplicate frag. */ 28762306a36Sopenharmony_ci if (qp->q.flags & INET_FRAG_COMPLETE) { 28862306a36Sopenharmony_ci SKB_DR_SET(reason, DUP_FRAG); 28962306a36Sopenharmony_ci goto err; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (!(IPCB(skb)->flags & IPSKB_FRAG_COMPLETE) && 29362306a36Sopenharmony_ci unlikely(ip_frag_too_far(qp)) && 29462306a36Sopenharmony_ci unlikely(err = ip_frag_reinit(qp))) { 29562306a36Sopenharmony_ci ipq_kill(qp); 29662306a36Sopenharmony_ci goto err; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci ecn = ip4_frag_ecn(ip_hdr(skb)->tos); 30062306a36Sopenharmony_ci offset = ntohs(ip_hdr(skb)->frag_off); 30162306a36Sopenharmony_ci flags = offset & ~IP_OFFSET; 30262306a36Sopenharmony_ci offset &= IP_OFFSET; 30362306a36Sopenharmony_ci offset <<= 3; /* offset is in 8-byte chunks */ 30462306a36Sopenharmony_ci ihl = ip_hdrlen(skb); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* Determine the position of this fragment. */ 30762306a36Sopenharmony_ci end = offset + skb->len - skb_network_offset(skb) - ihl; 30862306a36Sopenharmony_ci err = -EINVAL; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* Is this the final fragment? */ 31162306a36Sopenharmony_ci if ((flags & IP_MF) == 0) { 31262306a36Sopenharmony_ci /* If we already have some bits beyond end 31362306a36Sopenharmony_ci * or have different end, the segment is corrupted. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci if (end < qp->q.len || 31662306a36Sopenharmony_ci ((qp->q.flags & INET_FRAG_LAST_IN) && end != qp->q.len)) 31762306a36Sopenharmony_ci goto discard_qp; 31862306a36Sopenharmony_ci qp->q.flags |= INET_FRAG_LAST_IN; 31962306a36Sopenharmony_ci qp->q.len = end; 32062306a36Sopenharmony_ci } else { 32162306a36Sopenharmony_ci if (end&7) { 32262306a36Sopenharmony_ci end &= ~7; 32362306a36Sopenharmony_ci if (skb->ip_summed != CHECKSUM_UNNECESSARY) 32462306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci if (end > qp->q.len) { 32762306a36Sopenharmony_ci /* Some bits beyond end -> corruption. */ 32862306a36Sopenharmony_ci if (qp->q.flags & INET_FRAG_LAST_IN) 32962306a36Sopenharmony_ci goto discard_qp; 33062306a36Sopenharmony_ci qp->q.len = end; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci if (end == offset) 33462306a36Sopenharmony_ci goto discard_qp; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci err = -ENOMEM; 33762306a36Sopenharmony_ci if (!pskb_pull(skb, skb_network_offset(skb) + ihl)) 33862306a36Sopenharmony_ci goto discard_qp; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci err = pskb_trim_rcsum(skb, end - offset); 34162306a36Sopenharmony_ci if (err) 34262306a36Sopenharmony_ci goto discard_qp; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Note : skb->rbnode and skb->dev share the same location. */ 34562306a36Sopenharmony_ci dev = skb->dev; 34662306a36Sopenharmony_ci /* Makes sure compiler wont do silly aliasing games */ 34762306a36Sopenharmony_ci barrier(); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci prev_tail = qp->q.fragments_tail; 35062306a36Sopenharmony_ci err = inet_frag_queue_insert(&qp->q, skb, offset, end); 35162306a36Sopenharmony_ci if (err) 35262306a36Sopenharmony_ci goto insert_error; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (dev) 35562306a36Sopenharmony_ci qp->iif = dev->ifindex; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci qp->q.stamp = skb->tstamp; 35862306a36Sopenharmony_ci qp->q.mono_delivery_time = skb->mono_delivery_time; 35962306a36Sopenharmony_ci qp->q.meat += skb->len; 36062306a36Sopenharmony_ci qp->ecn |= ecn; 36162306a36Sopenharmony_ci add_frag_mem_limit(qp->q.fqdir, skb->truesize); 36262306a36Sopenharmony_ci if (offset == 0) 36362306a36Sopenharmony_ci qp->q.flags |= INET_FRAG_FIRST_IN; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci fragsize = skb->len + ihl; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (fragsize > qp->q.max_size) 36862306a36Sopenharmony_ci qp->q.max_size = fragsize; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (ip_hdr(skb)->frag_off & htons(IP_DF) && 37162306a36Sopenharmony_ci fragsize > qp->max_df_size) 37262306a36Sopenharmony_ci qp->max_df_size = fragsize; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (qp->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) && 37562306a36Sopenharmony_ci qp->q.meat == qp->q.len) { 37662306a36Sopenharmony_ci unsigned long orefdst = skb->_skb_refdst; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci skb->_skb_refdst = 0UL; 37962306a36Sopenharmony_ci err = ip_frag_reasm(qp, skb, prev_tail, dev); 38062306a36Sopenharmony_ci skb->_skb_refdst = orefdst; 38162306a36Sopenharmony_ci if (err) 38262306a36Sopenharmony_ci inet_frag_kill(&qp->q); 38362306a36Sopenharmony_ci return err; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci skb_dst_drop(skb); 38762306a36Sopenharmony_ci return -EINPROGRESS; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ciinsert_error: 39062306a36Sopenharmony_ci if (err == IPFRAG_DUP) { 39162306a36Sopenharmony_ci SKB_DR_SET(reason, DUP_FRAG); 39262306a36Sopenharmony_ci err = -EINVAL; 39362306a36Sopenharmony_ci goto err; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci err = -EINVAL; 39662306a36Sopenharmony_ci __IP_INC_STATS(net, IPSTATS_MIB_REASM_OVERLAPS); 39762306a36Sopenharmony_cidiscard_qp: 39862306a36Sopenharmony_ci inet_frag_kill(&qp->q); 39962306a36Sopenharmony_ci __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS); 40062306a36Sopenharmony_cierr: 40162306a36Sopenharmony_ci kfree_skb_reason(skb, reason); 40262306a36Sopenharmony_ci return err; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic bool ip_frag_coalesce_ok(const struct ipq *qp) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci return qp->q.key.v4.user == IP_DEFRAG_LOCAL_DELIVER; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci/* Build a new IP datagram from all its fragments. */ 41162306a36Sopenharmony_cistatic int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb, 41262306a36Sopenharmony_ci struct sk_buff *prev_tail, struct net_device *dev) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct net *net = qp->q.fqdir->net; 41562306a36Sopenharmony_ci struct iphdr *iph; 41662306a36Sopenharmony_ci void *reasm_data; 41762306a36Sopenharmony_ci int len, err; 41862306a36Sopenharmony_ci u8 ecn; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci ipq_kill(qp); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci ecn = ip_frag_ecn_table[qp->ecn]; 42362306a36Sopenharmony_ci if (unlikely(ecn == 0xff)) { 42462306a36Sopenharmony_ci err = -EINVAL; 42562306a36Sopenharmony_ci goto out_fail; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* Make the one we just received the head. */ 42962306a36Sopenharmony_ci reasm_data = inet_frag_reasm_prepare(&qp->q, skb, prev_tail); 43062306a36Sopenharmony_ci if (!reasm_data) 43162306a36Sopenharmony_ci goto out_nomem; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci len = ip_hdrlen(skb) + qp->q.len; 43462306a36Sopenharmony_ci err = -E2BIG; 43562306a36Sopenharmony_ci if (len > 65535) 43662306a36Sopenharmony_ci goto out_oversize; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci inet_frag_reasm_finish(&qp->q, skb, reasm_data, 43962306a36Sopenharmony_ci ip_frag_coalesce_ok(qp)); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci skb->dev = dev; 44262306a36Sopenharmony_ci IPCB(skb)->frag_max_size = max(qp->max_df_size, qp->q.max_size); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci iph = ip_hdr(skb); 44562306a36Sopenharmony_ci iph->tot_len = htons(len); 44662306a36Sopenharmony_ci iph->tos |= ecn; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* When we set IP_DF on a refragmented skb we must also force a 44962306a36Sopenharmony_ci * call to ip_fragment to avoid forwarding a DF-skb of size s while 45062306a36Sopenharmony_ci * original sender only sent fragments of size f (where f < s). 45162306a36Sopenharmony_ci * 45262306a36Sopenharmony_ci * We only set DF/IPSKB_FRAG_PMTU if such DF fragment was the largest 45362306a36Sopenharmony_ci * frag seen to avoid sending tiny DF-fragments in case skb was built 45462306a36Sopenharmony_ci * from one very small df-fragment and one large non-df frag. 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_ci if (qp->max_df_size == qp->q.max_size) { 45762306a36Sopenharmony_ci IPCB(skb)->flags |= IPSKB_FRAG_PMTU; 45862306a36Sopenharmony_ci iph->frag_off = htons(IP_DF); 45962306a36Sopenharmony_ci } else { 46062306a36Sopenharmony_ci iph->frag_off = 0; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci ip_send_check(iph); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci __IP_INC_STATS(net, IPSTATS_MIB_REASMOKS); 46662306a36Sopenharmony_ci qp->q.rb_fragments = RB_ROOT; 46762306a36Sopenharmony_ci qp->q.fragments_tail = NULL; 46862306a36Sopenharmony_ci qp->q.last_run_head = NULL; 46962306a36Sopenharmony_ci return 0; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ciout_nomem: 47262306a36Sopenharmony_ci net_dbg_ratelimited("queue_glue: no memory for gluing queue %p\n", qp); 47362306a36Sopenharmony_ci err = -ENOMEM; 47462306a36Sopenharmony_ci goto out_fail; 47562306a36Sopenharmony_ciout_oversize: 47662306a36Sopenharmony_ci net_info_ratelimited("Oversized IP packet from %pI4\n", &qp->q.key.v4.saddr); 47762306a36Sopenharmony_ciout_fail: 47862306a36Sopenharmony_ci __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS); 47962306a36Sopenharmony_ci return err; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci/* Process an incoming IP datagram fragment. */ 48362306a36Sopenharmony_ciint ip_defrag(struct net *net, struct sk_buff *skb, u32 user) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct net_device *dev = skb->dev ? : skb_dst(skb)->dev; 48662306a36Sopenharmony_ci int vif = l3mdev_master_ifindex_rcu(dev); 48762306a36Sopenharmony_ci struct ipq *qp; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci __IP_INC_STATS(net, IPSTATS_MIB_REASMREQDS); 49062306a36Sopenharmony_ci skb_orphan(skb); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* Lookup (or create) queue header */ 49362306a36Sopenharmony_ci qp = ip_find(net, ip_hdr(skb), user, vif); 49462306a36Sopenharmony_ci if (qp) { 49562306a36Sopenharmony_ci int ret; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci spin_lock(&qp->q.lock); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci ret = ip_frag_queue(qp, skb); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci spin_unlock(&qp->q.lock); 50262306a36Sopenharmony_ci ipq_put(qp); 50362306a36Sopenharmony_ci return ret; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS); 50762306a36Sopenharmony_ci kfree_skb(skb); 50862306a36Sopenharmony_ci return -ENOMEM; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ciEXPORT_SYMBOL(ip_defrag); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cistruct sk_buff *ip_check_defrag(struct net *net, struct sk_buff *skb, u32 user) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci struct iphdr iph; 51562306a36Sopenharmony_ci int netoff; 51662306a36Sopenharmony_ci u32 len; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (skb->protocol != htons(ETH_P_IP)) 51962306a36Sopenharmony_ci return skb; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci netoff = skb_network_offset(skb); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (skb_copy_bits(skb, netoff, &iph, sizeof(iph)) < 0) 52462306a36Sopenharmony_ci return skb; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (iph.ihl < 5 || iph.version != 4) 52762306a36Sopenharmony_ci return skb; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci len = ntohs(iph.tot_len); 53062306a36Sopenharmony_ci if (skb->len < netoff + len || len < (iph.ihl * 4)) 53162306a36Sopenharmony_ci return skb; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (ip_is_fragment(&iph)) { 53462306a36Sopenharmony_ci skb = skb_share_check(skb, GFP_ATOMIC); 53562306a36Sopenharmony_ci if (skb) { 53662306a36Sopenharmony_ci if (!pskb_may_pull(skb, netoff + iph.ihl * 4)) { 53762306a36Sopenharmony_ci kfree_skb(skb); 53862306a36Sopenharmony_ci return NULL; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci if (pskb_trim_rcsum(skb, netoff + len)) { 54162306a36Sopenharmony_ci kfree_skb(skb); 54262306a36Sopenharmony_ci return NULL; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); 54562306a36Sopenharmony_ci if (ip_defrag(net, skb, user)) 54662306a36Sopenharmony_ci return NULL; 54762306a36Sopenharmony_ci skb_clear_hash(skb); 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci return skb; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ciEXPORT_SYMBOL(ip_check_defrag); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 55562306a36Sopenharmony_cistatic int dist_min; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic struct ctl_table ip4_frags_ns_ctl_table[] = { 55862306a36Sopenharmony_ci { 55962306a36Sopenharmony_ci .procname = "ipfrag_high_thresh", 56062306a36Sopenharmony_ci .maxlen = sizeof(unsigned long), 56162306a36Sopenharmony_ci .mode = 0644, 56262306a36Sopenharmony_ci .proc_handler = proc_doulongvec_minmax, 56362306a36Sopenharmony_ci }, 56462306a36Sopenharmony_ci { 56562306a36Sopenharmony_ci .procname = "ipfrag_low_thresh", 56662306a36Sopenharmony_ci .maxlen = sizeof(unsigned long), 56762306a36Sopenharmony_ci .mode = 0644, 56862306a36Sopenharmony_ci .proc_handler = proc_doulongvec_minmax, 56962306a36Sopenharmony_ci }, 57062306a36Sopenharmony_ci { 57162306a36Sopenharmony_ci .procname = "ipfrag_time", 57262306a36Sopenharmony_ci .maxlen = sizeof(int), 57362306a36Sopenharmony_ci .mode = 0644, 57462306a36Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 57562306a36Sopenharmony_ci }, 57662306a36Sopenharmony_ci { 57762306a36Sopenharmony_ci .procname = "ipfrag_max_dist", 57862306a36Sopenharmony_ci .maxlen = sizeof(int), 57962306a36Sopenharmony_ci .mode = 0644, 58062306a36Sopenharmony_ci .proc_handler = proc_dointvec_minmax, 58162306a36Sopenharmony_ci .extra1 = &dist_min, 58262306a36Sopenharmony_ci }, 58362306a36Sopenharmony_ci { } 58462306a36Sopenharmony_ci}; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci/* secret interval has been deprecated */ 58762306a36Sopenharmony_cistatic int ip4_frags_secret_interval_unused; 58862306a36Sopenharmony_cistatic struct ctl_table ip4_frags_ctl_table[] = { 58962306a36Sopenharmony_ci { 59062306a36Sopenharmony_ci .procname = "ipfrag_secret_interval", 59162306a36Sopenharmony_ci .data = &ip4_frags_secret_interval_unused, 59262306a36Sopenharmony_ci .maxlen = sizeof(int), 59362306a36Sopenharmony_ci .mode = 0644, 59462306a36Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 59562306a36Sopenharmony_ci }, 59662306a36Sopenharmony_ci { } 59762306a36Sopenharmony_ci}; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic int __net_init ip4_frags_ns_ctl_register(struct net *net) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci struct ctl_table *table; 60262306a36Sopenharmony_ci struct ctl_table_header *hdr; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci table = ip4_frags_ns_ctl_table; 60562306a36Sopenharmony_ci if (!net_eq(net, &init_net)) { 60662306a36Sopenharmony_ci table = kmemdup(table, sizeof(ip4_frags_ns_ctl_table), GFP_KERNEL); 60762306a36Sopenharmony_ci if (!table) 60862306a36Sopenharmony_ci goto err_alloc; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci table[0].data = &net->ipv4.fqdir->high_thresh; 61262306a36Sopenharmony_ci table[0].extra1 = &net->ipv4.fqdir->low_thresh; 61362306a36Sopenharmony_ci table[1].data = &net->ipv4.fqdir->low_thresh; 61462306a36Sopenharmony_ci table[1].extra2 = &net->ipv4.fqdir->high_thresh; 61562306a36Sopenharmony_ci table[2].data = &net->ipv4.fqdir->timeout; 61662306a36Sopenharmony_ci table[3].data = &net->ipv4.fqdir->max_dist; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci hdr = register_net_sysctl_sz(net, "net/ipv4", table, 61962306a36Sopenharmony_ci ARRAY_SIZE(ip4_frags_ns_ctl_table)); 62062306a36Sopenharmony_ci if (!hdr) 62162306a36Sopenharmony_ci goto err_reg; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci net->ipv4.frags_hdr = hdr; 62462306a36Sopenharmony_ci return 0; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cierr_reg: 62762306a36Sopenharmony_ci if (!net_eq(net, &init_net)) 62862306a36Sopenharmony_ci kfree(table); 62962306a36Sopenharmony_cierr_alloc: 63062306a36Sopenharmony_ci return -ENOMEM; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic void __net_exit ip4_frags_ns_ctl_unregister(struct net *net) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci struct ctl_table *table; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci table = net->ipv4.frags_hdr->ctl_table_arg; 63862306a36Sopenharmony_ci unregister_net_sysctl_table(net->ipv4.frags_hdr); 63962306a36Sopenharmony_ci kfree(table); 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic void __init ip4_frags_ctl_register(void) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci register_net_sysctl(&init_net, "net/ipv4", ip4_frags_ctl_table); 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci#else 64762306a36Sopenharmony_cistatic int ip4_frags_ns_ctl_register(struct net *net) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci return 0; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic void ip4_frags_ns_ctl_unregister(struct net *net) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic void __init ip4_frags_ctl_register(void) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci#endif 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic int __net_init ipv4_frags_init_net(struct net *net) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci int res; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci res = fqdir_init(&net->ipv4.fqdir, &ip4_frags, net); 66662306a36Sopenharmony_ci if (res < 0) 66762306a36Sopenharmony_ci return res; 66862306a36Sopenharmony_ci /* Fragment cache limits. 66962306a36Sopenharmony_ci * 67062306a36Sopenharmony_ci * The fragment memory accounting code, (tries to) account for 67162306a36Sopenharmony_ci * the real memory usage, by measuring both the size of frag 67262306a36Sopenharmony_ci * queue struct (inet_frag_queue (ipv4:ipq/ipv6:frag_queue)) 67362306a36Sopenharmony_ci * and the SKB's truesize. 67462306a36Sopenharmony_ci * 67562306a36Sopenharmony_ci * A 64K fragment consumes 129736 bytes (44*2944)+200 67662306a36Sopenharmony_ci * (1500 truesize == 2944, sizeof(struct ipq) == 200) 67762306a36Sopenharmony_ci * 67862306a36Sopenharmony_ci * We will commit 4MB at one time. Should we cross that limit 67962306a36Sopenharmony_ci * we will prune down to 3MB, making room for approx 8 big 64K 68062306a36Sopenharmony_ci * fragments 8x128k. 68162306a36Sopenharmony_ci */ 68262306a36Sopenharmony_ci net->ipv4.fqdir->high_thresh = 4 * 1024 * 1024; 68362306a36Sopenharmony_ci net->ipv4.fqdir->low_thresh = 3 * 1024 * 1024; 68462306a36Sopenharmony_ci /* 68562306a36Sopenharmony_ci * Important NOTE! Fragment queue must be destroyed before MSL expires. 68662306a36Sopenharmony_ci * RFC791 is wrong proposing to prolongate timer each fragment arrival 68762306a36Sopenharmony_ci * by TTL. 68862306a36Sopenharmony_ci */ 68962306a36Sopenharmony_ci net->ipv4.fqdir->timeout = IP_FRAG_TIME; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci net->ipv4.fqdir->max_dist = 64; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci res = ip4_frags_ns_ctl_register(net); 69462306a36Sopenharmony_ci if (res < 0) 69562306a36Sopenharmony_ci fqdir_exit(net->ipv4.fqdir); 69662306a36Sopenharmony_ci return res; 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic void __net_exit ipv4_frags_pre_exit_net(struct net *net) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci fqdir_pre_exit(net->ipv4.fqdir); 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic void __net_exit ipv4_frags_exit_net(struct net *net) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci ip4_frags_ns_ctl_unregister(net); 70762306a36Sopenharmony_ci fqdir_exit(net->ipv4.fqdir); 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic struct pernet_operations ip4_frags_ops = { 71162306a36Sopenharmony_ci .init = ipv4_frags_init_net, 71262306a36Sopenharmony_ci .pre_exit = ipv4_frags_pre_exit_net, 71362306a36Sopenharmony_ci .exit = ipv4_frags_exit_net, 71462306a36Sopenharmony_ci}; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic u32 ip4_key_hashfn(const void *data, u32 len, u32 seed) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci return jhash2(data, 72062306a36Sopenharmony_ci sizeof(struct frag_v4_compare_key) / sizeof(u32), seed); 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic u32 ip4_obj_hashfn(const void *data, u32 len, u32 seed) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci const struct inet_frag_queue *fq = data; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci return jhash2((const u32 *)&fq->key.v4, 72862306a36Sopenharmony_ci sizeof(struct frag_v4_compare_key) / sizeof(u32), seed); 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic int ip4_obj_cmpfn(struct rhashtable_compare_arg *arg, const void *ptr) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci const struct frag_v4_compare_key *key = arg->key; 73462306a36Sopenharmony_ci const struct inet_frag_queue *fq = ptr; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci return !!memcmp(&fq->key, key, sizeof(*key)); 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic const struct rhashtable_params ip4_rhash_params = { 74062306a36Sopenharmony_ci .head_offset = offsetof(struct inet_frag_queue, node), 74162306a36Sopenharmony_ci .key_offset = offsetof(struct inet_frag_queue, key), 74262306a36Sopenharmony_ci .key_len = sizeof(struct frag_v4_compare_key), 74362306a36Sopenharmony_ci .hashfn = ip4_key_hashfn, 74462306a36Sopenharmony_ci .obj_hashfn = ip4_obj_hashfn, 74562306a36Sopenharmony_ci .obj_cmpfn = ip4_obj_cmpfn, 74662306a36Sopenharmony_ci .automatic_shrinking = true, 74762306a36Sopenharmony_ci}; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_civoid __init ipfrag_init(void) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci ip4_frags.constructor = ip4_frag_init; 75262306a36Sopenharmony_ci ip4_frags.destructor = ip4_frag_free; 75362306a36Sopenharmony_ci ip4_frags.qsize = sizeof(struct ipq); 75462306a36Sopenharmony_ci ip4_frags.frag_expire = ip_expire; 75562306a36Sopenharmony_ci ip4_frags.frags_cache_name = ip_frag_cache_name; 75662306a36Sopenharmony_ci ip4_frags.rhash_params = ip4_rhash_params; 75762306a36Sopenharmony_ci if (inet_frags_init(&ip4_frags)) 75862306a36Sopenharmony_ci panic("IP: failed to allocate ip4_frags cache\n"); 75962306a36Sopenharmony_ci ip4_frags_ctl_register(); 76062306a36Sopenharmony_ci register_pernet_subsys(&ip4_frags_ops); 76162306a36Sopenharmony_ci} 762