12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2ea81ac2eSAlexander Aring /*	6LoWPAN fragment reassembly
3ea81ac2eSAlexander Aring  *
4ea81ac2eSAlexander Aring  *	Authors:
5ea81ac2eSAlexander Aring  *	Alexander Aring		<aar@pengutronix.de>
6ea81ac2eSAlexander Aring  *
7ea81ac2eSAlexander Aring  *	Based on: net/ipv6/reassembly.c
8ea81ac2eSAlexander Aring  */
9ea81ac2eSAlexander Aring 
10ea81ac2eSAlexander Aring #define pr_fmt(fmt) "6LoWPAN: " fmt
11ea81ac2eSAlexander Aring 
12ea81ac2eSAlexander Aring #include <linux/net.h>
13ea81ac2eSAlexander Aring #include <linux/list.h>
14ea81ac2eSAlexander Aring #include <linux/netdevice.h>
15ea81ac2eSAlexander Aring #include <linux/random.h>
16ea81ac2eSAlexander Aring #include <linux/jhash.h>
17ea81ac2eSAlexander Aring #include <linux/skbuff.h>
18ea81ac2eSAlexander Aring #include <linux/slab.h>
19ea81ac2eSAlexander Aring #include <linux/export.h>
20ea81ac2eSAlexander Aring 
21ea81ac2eSAlexander Aring #include <net/ieee802154_netdev.h>
22ea81ac2eSAlexander Aring #include <net/6lowpan.h>
2370b095c8SFlorian Westphal #include <net/ipv6_frag.h>
24ea81ac2eSAlexander Aring #include <net/inet_frag.h>
25254c5dbeSPeter Oskolkov #include <net/ip.h>
26ea81ac2eSAlexander Aring 
278691ee59SAlexander Aring #include "6lowpan_i.h"
28ea81ac2eSAlexander Aring 
29ea81ac2eSAlexander Aring static const char lowpan_frags_cache_name[] = "lowpan-frags";
30ea81ac2eSAlexander Aring 
31ea81ac2eSAlexander Aring static struct inet_frags lowpan_frags;
32ea81ac2eSAlexander Aring 
33254c5dbeSPeter Oskolkov static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *skb,
34f4606583SAlexander Aring 			     struct sk_buff *prev,  struct net_device *ldev);
35ea81ac2eSAlexander Aring 
36ea81ac2eSAlexander Aring static void lowpan_frag_init(struct inet_frag_queue *q, const void *a)
37ea81ac2eSAlexander Aring {
38648700f7SEric Dumazet 	const struct frag_lowpan_compare_key *key = a;
39ea81ac2eSAlexander Aring 
40648700f7SEric Dumazet 	BUILD_BUG_ON(sizeof(*key) > sizeof(q->key));
41648700f7SEric Dumazet 	memcpy(&q->key, key, sizeof(*key));
42ea81ac2eSAlexander Aring }
43ea81ac2eSAlexander Aring 
4478802011SKees Cook static void lowpan_frag_expire(struct timer_list *t)
45ea81ac2eSAlexander Aring {
4678802011SKees Cook 	struct inet_frag_queue *frag = from_timer(frag, t, timer);
47ea81ac2eSAlexander Aring 	struct frag_queue *fq;
48ea81ac2eSAlexander Aring 
4978802011SKees Cook 	fq = container_of(frag, struct frag_queue, q);
50ea81ac2eSAlexander Aring 
51ea81ac2eSAlexander Aring 	spin_lock(&fq->q.lock);
52ea81ac2eSAlexander Aring 
53ea81ac2eSAlexander Aring 	if (fq->q.flags & INET_FRAG_COMPLETE)
54ea81ac2eSAlexander Aring 		goto out;
55ea81ac2eSAlexander Aring 
56093ba729SEric Dumazet 	inet_frag_kill(&fq->q);
57ea81ac2eSAlexander Aring out:
58ea81ac2eSAlexander Aring 	spin_unlock(&fq->q.lock);
59093ba729SEric Dumazet 	inet_frag_put(&fq->q);
60ea81ac2eSAlexander Aring }
61ea81ac2eSAlexander Aring 
62ea81ac2eSAlexander Aring static inline struct lowpan_frag_queue *
6372a5e6bbSAlexander Aring fq_find(struct net *net, const struct lowpan_802154_cb *cb,
64ea81ac2eSAlexander Aring 	const struct ieee802154_addr *src,
65ea81ac2eSAlexander Aring 	const struct ieee802154_addr *dst)
66ea81ac2eSAlexander Aring {
67ea81ac2eSAlexander Aring 	struct netns_ieee802154_lowpan *ieee802154_lowpan =
68ea81ac2eSAlexander Aring 		net_ieee802154_lowpan(net);
69f18fa5deSAlexander Aring 	struct frag_lowpan_compare_key key = {};
70648700f7SEric Dumazet 	struct inet_frag_queue *q;
71ea81ac2eSAlexander Aring 
72f18fa5deSAlexander Aring 	key.tag = cb->d_tag;
73f18fa5deSAlexander Aring 	key.d_size = cb->d_size;
74f18fa5deSAlexander Aring 	key.src = *src;
75f18fa5deSAlexander Aring 	key.dst = *dst;
76f18fa5deSAlexander Aring 
77648700f7SEric Dumazet 	q = inet_frag_find(&ieee802154_lowpan->frags, &key);
782d44ed22SEric Dumazet 	if (!q)
79ea81ac2eSAlexander Aring 		return NULL;
802d44ed22SEric Dumazet 
81ea81ac2eSAlexander Aring 	return container_of(q, struct lowpan_frag_queue, q);
82ea81ac2eSAlexander Aring }
83ea81ac2eSAlexander Aring 
84ea81ac2eSAlexander Aring static int lowpan_frag_queue(struct lowpan_frag_queue *fq,
8572a5e6bbSAlexander Aring 			     struct sk_buff *skb, u8 frag_type)
86ea81ac2eSAlexander Aring {
87254c5dbeSPeter Oskolkov 	struct sk_buff *prev_tail;
88f4606583SAlexander Aring 	struct net_device *ldev;
89254c5dbeSPeter Oskolkov 	int end, offset, err;
90254c5dbeSPeter Oskolkov 
91254c5dbeSPeter Oskolkov 	/* inet_frag_queue_* functions use skb->cb; see struct ipfrag_skb_cb
92254c5dbeSPeter Oskolkov 	 * in inet_fragment.c
93254c5dbeSPeter Oskolkov 	 */
94254c5dbeSPeter Oskolkov 	BUILD_BUG_ON(sizeof(struct lowpan_802154_cb) > sizeof(struct inet_skb_parm));
95254c5dbeSPeter Oskolkov 	BUILD_BUG_ON(sizeof(struct lowpan_802154_cb) > sizeof(struct inet6_skb_parm));
96ea81ac2eSAlexander Aring 
97ea81ac2eSAlexander Aring 	if (fq->q.flags & INET_FRAG_COMPLETE)
98ea81ac2eSAlexander Aring 		goto err;
99ea81ac2eSAlexander Aring 
10072a5e6bbSAlexander Aring 	offset = lowpan_802154_cb(skb)->d_offset << 3;
10172a5e6bbSAlexander Aring 	end = lowpan_802154_cb(skb)->d_size;
102ea81ac2eSAlexander Aring 
103ea81ac2eSAlexander Aring 	/* Is this the final fragment? */
104ea81ac2eSAlexander Aring 	if (offset + skb->len == end) {
105ea81ac2eSAlexander Aring 		/* If we already have some bits beyond end
106ea81ac2eSAlexander Aring 		 * or have different end, the segment is corrupted.
107ea81ac2eSAlexander Aring 		 */
108ea81ac2eSAlexander Aring 		if (end < fq->q.len ||
109ea81ac2eSAlexander Aring 		    ((fq->q.flags & INET_FRAG_LAST_IN) && end != fq->q.len))
110ea81ac2eSAlexander Aring 			goto err;
111ea81ac2eSAlexander Aring 		fq->q.flags |= INET_FRAG_LAST_IN;
112ea81ac2eSAlexander Aring 		fq->q.len = end;
113ea81ac2eSAlexander Aring 	} else {
114ea81ac2eSAlexander Aring 		if (end > fq->q.len) {
115ea81ac2eSAlexander Aring 			/* Some bits beyond end -> corruption. */
116ea81ac2eSAlexander Aring 			if (fq->q.flags & INET_FRAG_LAST_IN)
117ea81ac2eSAlexander Aring 				goto err;
118ea81ac2eSAlexander Aring 			fq->q.len = end;
119ea81ac2eSAlexander Aring 		}
120ea81ac2eSAlexander Aring 	}
121ea81ac2eSAlexander Aring 
122f4606583SAlexander Aring 	ldev = skb->dev;
123f4606583SAlexander Aring 	if (ldev)
124ea81ac2eSAlexander Aring 		skb->dev = NULL;
125254c5dbeSPeter Oskolkov 	barrier();
126254c5dbeSPeter Oskolkov 
127254c5dbeSPeter Oskolkov 	prev_tail = fq->q.fragments_tail;
128254c5dbeSPeter Oskolkov 	err = inet_frag_queue_insert(&fq->q, skb, offset, end);
129254c5dbeSPeter Oskolkov 	if (err)
130254c5dbeSPeter Oskolkov 		goto err;
131ea81ac2eSAlexander Aring 
132ea81ac2eSAlexander Aring 	fq->q.stamp = skb->tstamp;
13372a5e6bbSAlexander Aring 	if (frag_type == LOWPAN_DISPATCH_FRAG1)
134ea81ac2eSAlexander Aring 		fq->q.flags |= INET_FRAG_FIRST_IN;
13572a5e6bbSAlexander Aring 
136ea81ac2eSAlexander Aring 	fq->q.meat += skb->len;
1370e60d245SFlorian Westphal 	add_frag_mem_limit(fq->q.net, skb->truesize);
138ea81ac2eSAlexander Aring 
139ea81ac2eSAlexander Aring 	if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
140ea81ac2eSAlexander Aring 	    fq->q.meat == fq->q.len) {
141ea81ac2eSAlexander Aring 		int res;
142ea81ac2eSAlexander Aring 		unsigned long orefdst = skb->_skb_refdst;
143ea81ac2eSAlexander Aring 
144ea81ac2eSAlexander Aring 		skb->_skb_refdst = 0UL;
145254c5dbeSPeter Oskolkov 		res = lowpan_frag_reasm(fq, skb, prev_tail, ldev);
146ea81ac2eSAlexander Aring 		skb->_skb_refdst = orefdst;
147ea81ac2eSAlexander Aring 		return res;
148ea81ac2eSAlexander Aring 	}
149254c5dbeSPeter Oskolkov 	skb_dst_drop(skb);
150ea81ac2eSAlexander Aring 
151ea81ac2eSAlexander Aring 	return -1;
152ea81ac2eSAlexander Aring err:
153ea81ac2eSAlexander Aring 	kfree_skb(skb);
154ea81ac2eSAlexander Aring 	return -1;
155ea81ac2eSAlexander Aring }
156ea81ac2eSAlexander Aring 
157ea81ac2eSAlexander Aring /*	Check if this packet is complete.
158ea81ac2eSAlexander Aring  *
159ea81ac2eSAlexander Aring  *	It is called with locked fq, and caller must check that
160ea81ac2eSAlexander Aring  *	queue is eligible for reassembly i.e. it is not COMPLETE,
161ea81ac2eSAlexander Aring  *	the last and the first frames arrived and all the bits are here.
162ea81ac2eSAlexander Aring  */
163254c5dbeSPeter Oskolkov static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *skb,
164254c5dbeSPeter Oskolkov 			     struct sk_buff *prev_tail, struct net_device *ldev)
165ea81ac2eSAlexander Aring {
166254c5dbeSPeter Oskolkov 	void *reasm_data;
167ea81ac2eSAlexander Aring 
168093ba729SEric Dumazet 	inet_frag_kill(&fq->q);
169ea81ac2eSAlexander Aring 
170254c5dbeSPeter Oskolkov 	reasm_data = inet_frag_reasm_prepare(&fq->q, skb, prev_tail);
171254c5dbeSPeter Oskolkov 	if (!reasm_data)
172ea81ac2eSAlexander Aring 		goto out_oom;
173254c5dbeSPeter Oskolkov 	inet_frag_reasm_finish(&fq->q, skb, reasm_data);
174ea81ac2eSAlexander Aring 
175254c5dbeSPeter Oskolkov 	skb->dev = ldev;
176254c5dbeSPeter Oskolkov 	skb->tstamp = fq->q.stamp;
177254c5dbeSPeter Oskolkov 	fq->q.rb_fragments = RB_ROOT;
178ea81ac2eSAlexander Aring 	fq->q.fragments_tail = NULL;
179254c5dbeSPeter Oskolkov 	fq->q.last_run_head = NULL;
180ea81ac2eSAlexander Aring 
181ea81ac2eSAlexander Aring 	return 1;
182ea81ac2eSAlexander Aring out_oom:
183ea81ac2eSAlexander Aring 	net_dbg_ratelimited("lowpan_frag_reasm: no memory for reassembly\n");
184ea81ac2eSAlexander Aring 	return -1;
185ea81ac2eSAlexander Aring }
186ea81ac2eSAlexander Aring 
18772a5e6bbSAlexander Aring static int lowpan_frag_rx_handlers_result(struct sk_buff *skb,
18872a5e6bbSAlexander Aring 					  lowpan_rx_result res)
18972a5e6bbSAlexander Aring {
19072a5e6bbSAlexander Aring 	switch (res) {
19172a5e6bbSAlexander Aring 	case RX_QUEUED:
19272a5e6bbSAlexander Aring 		return NET_RX_SUCCESS;
19372a5e6bbSAlexander Aring 	case RX_CONTINUE:
19472a5e6bbSAlexander Aring 		/* nobody cared about this packet */
19572a5e6bbSAlexander Aring 		net_warn_ratelimited("%s: received unknown dispatch\n",
19672a5e6bbSAlexander Aring 				     __func__);
19772a5e6bbSAlexander Aring 
19872a5e6bbSAlexander Aring 		/* fall-through */
19972a5e6bbSAlexander Aring 	default:
20072a5e6bbSAlexander Aring 		/* all others failure */
20172a5e6bbSAlexander Aring 		return NET_RX_DROP;
20272a5e6bbSAlexander Aring 	}
20372a5e6bbSAlexander Aring }
20472a5e6bbSAlexander Aring 
20572a5e6bbSAlexander Aring static lowpan_rx_result lowpan_frag_rx_h_iphc(struct sk_buff *skb)
20672a5e6bbSAlexander Aring {
20772a5e6bbSAlexander Aring 	int ret;
20872a5e6bbSAlexander Aring 
20972a5e6bbSAlexander Aring 	if (!lowpan_is_iphc(*skb_network_header(skb)))
21072a5e6bbSAlexander Aring 		return RX_CONTINUE;
21172a5e6bbSAlexander Aring 
21272a5e6bbSAlexander Aring 	ret = lowpan_iphc_decompress(skb);
21372a5e6bbSAlexander Aring 	if (ret < 0)
21472a5e6bbSAlexander Aring 		return RX_DROP;
21572a5e6bbSAlexander Aring 
21672a5e6bbSAlexander Aring 	return RX_QUEUED;
21772a5e6bbSAlexander Aring }
21872a5e6bbSAlexander Aring 
21972a5e6bbSAlexander Aring static int lowpan_invoke_frag_rx_handlers(struct sk_buff *skb)
22072a5e6bbSAlexander Aring {
22172a5e6bbSAlexander Aring 	lowpan_rx_result res;
22272a5e6bbSAlexander Aring 
22372a5e6bbSAlexander Aring #define CALL_RXH(rxh)			\
22472a5e6bbSAlexander Aring 	do {				\
22572a5e6bbSAlexander Aring 		res = rxh(skb);	\
22672a5e6bbSAlexander Aring 		if (res != RX_CONTINUE)	\
22772a5e6bbSAlexander Aring 			goto rxh_next;	\
22872a5e6bbSAlexander Aring 	} while (0)
22972a5e6bbSAlexander Aring 
23072a5e6bbSAlexander Aring 	/* likely at first */
23172a5e6bbSAlexander Aring 	CALL_RXH(lowpan_frag_rx_h_iphc);
23272a5e6bbSAlexander Aring 	CALL_RXH(lowpan_rx_h_ipv6);
23372a5e6bbSAlexander Aring 
23472a5e6bbSAlexander Aring rxh_next:
23572a5e6bbSAlexander Aring 	return lowpan_frag_rx_handlers_result(skb, res);
23672a5e6bbSAlexander Aring #undef CALL_RXH
23772a5e6bbSAlexander Aring }
23872a5e6bbSAlexander Aring 
23972a5e6bbSAlexander Aring #define LOWPAN_FRAG_DGRAM_SIZE_HIGH_MASK	0x07
24072a5e6bbSAlexander Aring #define LOWPAN_FRAG_DGRAM_SIZE_HIGH_SHIFT	8
24172a5e6bbSAlexander Aring 
24272a5e6bbSAlexander Aring static int lowpan_get_cb(struct sk_buff *skb, u8 frag_type,
24372a5e6bbSAlexander Aring 			 struct lowpan_802154_cb *cb)
244ea81ac2eSAlexander Aring {
245ea81ac2eSAlexander Aring 	bool fail;
24672a5e6bbSAlexander Aring 	u8 high = 0, low = 0;
247ea81ac2eSAlexander Aring 	__be16 d_tag = 0;
248ea81ac2eSAlexander Aring 
24972a5e6bbSAlexander Aring 	fail = lowpan_fetch_skb(skb, &high, 1);
250ea81ac2eSAlexander Aring 	fail |= lowpan_fetch_skb(skb, &low, 1);
25172a5e6bbSAlexander Aring 	/* remove the dispatch value and use first three bits as high value
25272a5e6bbSAlexander Aring 	 * for the datagram size
25372a5e6bbSAlexander Aring 	 */
25472a5e6bbSAlexander Aring 	cb->d_size = (high & LOWPAN_FRAG_DGRAM_SIZE_HIGH_MASK) <<
25572a5e6bbSAlexander Aring 		LOWPAN_FRAG_DGRAM_SIZE_HIGH_SHIFT | low;
256ea81ac2eSAlexander Aring 	fail |= lowpan_fetch_skb(skb, &d_tag, 2);
25772a5e6bbSAlexander Aring 	cb->d_tag = ntohs(d_tag);
258ea81ac2eSAlexander Aring 
259ea81ac2eSAlexander Aring 	if (frag_type == LOWPAN_DISPATCH_FRAGN) {
26072a5e6bbSAlexander Aring 		fail |= lowpan_fetch_skb(skb, &cb->d_offset, 1);
261ea81ac2eSAlexander Aring 	} else {
262ea81ac2eSAlexander Aring 		skb_reset_network_header(skb);
26372a5e6bbSAlexander Aring 		cb->d_offset = 0;
26472a5e6bbSAlexander Aring 		/* check if datagram_size has ipv6hdr on FRAG1 */
26572a5e6bbSAlexander Aring 		fail |= cb->d_size < sizeof(struct ipv6hdr);
26672a5e6bbSAlexander Aring 		/* check if we can dereference the dispatch value */
26772a5e6bbSAlexander Aring 		fail |= !skb->len;
268ea81ac2eSAlexander Aring 	}
269ea81ac2eSAlexander Aring 
270ea81ac2eSAlexander Aring 	if (unlikely(fail))
271ea81ac2eSAlexander Aring 		return -EIO;
272ea81ac2eSAlexander Aring 
273ea81ac2eSAlexander Aring 	return 0;
274ea81ac2eSAlexander Aring }
275ea81ac2eSAlexander Aring 
27672a5e6bbSAlexander Aring int lowpan_frag_rcv(struct sk_buff *skb, u8 frag_type)
277ea81ac2eSAlexander Aring {
278ea81ac2eSAlexander Aring 	struct lowpan_frag_queue *fq;
279ea81ac2eSAlexander Aring 	struct net *net = dev_net(skb->dev);
28072a5e6bbSAlexander Aring 	struct lowpan_802154_cb *cb = lowpan_802154_cb(skb);
281f18fa5deSAlexander Aring 	struct ieee802154_hdr hdr = {};
282ea81ac2eSAlexander Aring 	int err;
283ea81ac2eSAlexander Aring 
28472a5e6bbSAlexander Aring 	if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0)
28572a5e6bbSAlexander Aring 		goto err;
286ea81ac2eSAlexander Aring 
28772a5e6bbSAlexander Aring 	err = lowpan_get_cb(skb, frag_type, cb);
288ea81ac2eSAlexander Aring 	if (err < 0)
289ea81ac2eSAlexander Aring 		goto err;
290ea81ac2eSAlexander Aring 
29172a5e6bbSAlexander Aring 	if (frag_type == LOWPAN_DISPATCH_FRAG1) {
29272a5e6bbSAlexander Aring 		err = lowpan_invoke_frag_rx_handlers(skb);
29372a5e6bbSAlexander Aring 		if (err == NET_RX_DROP)
29472a5e6bbSAlexander Aring 			goto err;
29572a5e6bbSAlexander Aring 	}
29672a5e6bbSAlexander Aring 
29772a5e6bbSAlexander Aring 	if (cb->d_size > IPV6_MIN_MTU) {
298ea81ac2eSAlexander Aring 		net_warn_ratelimited("lowpan_frag_rcv: datagram size exceeds MTU\n");
299ea81ac2eSAlexander Aring 		goto err;
300ea81ac2eSAlexander Aring 	}
301ea81ac2eSAlexander Aring 
30272a5e6bbSAlexander Aring 	fq = fq_find(net, cb, &hdr.source, &hdr.dest);
303ea81ac2eSAlexander Aring 	if (fq != NULL) {
304ea81ac2eSAlexander Aring 		int ret;
305ea81ac2eSAlexander Aring 
306ea81ac2eSAlexander Aring 		spin_lock(&fq->q.lock);
307ea81ac2eSAlexander Aring 		ret = lowpan_frag_queue(fq, skb, frag_type);
308ea81ac2eSAlexander Aring 		spin_unlock(&fq->q.lock);
309ea81ac2eSAlexander Aring 
310093ba729SEric Dumazet 		inet_frag_put(&fq->q);
311ea81ac2eSAlexander Aring 		return ret;
312ea81ac2eSAlexander Aring 	}
313ea81ac2eSAlexander Aring 
314ea81ac2eSAlexander Aring err:
315ea81ac2eSAlexander Aring 	kfree_skb(skb);
316ea81ac2eSAlexander Aring 	return -1;
317ea81ac2eSAlexander Aring }
318ea81ac2eSAlexander Aring 
319ea81ac2eSAlexander Aring #ifdef CONFIG_SYSCTL
320ea81ac2eSAlexander Aring 
321ea81ac2eSAlexander Aring static struct ctl_table lowpan_frags_ns_ctl_table[] = {
322ea81ac2eSAlexander Aring 	{
323ea81ac2eSAlexander Aring 		.procname	= "6lowpanfrag_high_thresh",
324ea81ac2eSAlexander Aring 		.data		= &init_net.ieee802154_lowpan.frags.high_thresh,
3253e67f106SEric Dumazet 		.maxlen		= sizeof(unsigned long),
326ea81ac2eSAlexander Aring 		.mode		= 0644,
3273e67f106SEric Dumazet 		.proc_handler	= proc_doulongvec_minmax,
328ea81ac2eSAlexander Aring 		.extra1		= &init_net.ieee802154_lowpan.frags.low_thresh
329ea81ac2eSAlexander Aring 	},
330ea81ac2eSAlexander Aring 	{
331ea81ac2eSAlexander Aring 		.procname	= "6lowpanfrag_low_thresh",
332ea81ac2eSAlexander Aring 		.data		= &init_net.ieee802154_lowpan.frags.low_thresh,
3333e67f106SEric Dumazet 		.maxlen		= sizeof(unsigned long),
334ea81ac2eSAlexander Aring 		.mode		= 0644,
3353e67f106SEric Dumazet 		.proc_handler	= proc_doulongvec_minmax,
336ea81ac2eSAlexander Aring 		.extra2		= &init_net.ieee802154_lowpan.frags.high_thresh
337ea81ac2eSAlexander Aring 	},
338ea81ac2eSAlexander Aring 	{
339ea81ac2eSAlexander Aring 		.procname	= "6lowpanfrag_time",
340ea81ac2eSAlexander Aring 		.data		= &init_net.ieee802154_lowpan.frags.timeout,
341ea81ac2eSAlexander Aring 		.maxlen		= sizeof(int),
342ea81ac2eSAlexander Aring 		.mode		= 0644,
343ea81ac2eSAlexander Aring 		.proc_handler	= proc_dointvec_jiffies,
344ea81ac2eSAlexander Aring 	},
345ea81ac2eSAlexander Aring 	{ }
346ea81ac2eSAlexander Aring };
347ea81ac2eSAlexander Aring 
348ea81ac2eSAlexander Aring /* secret interval has been deprecated */
349ea81ac2eSAlexander Aring static int lowpan_frags_secret_interval_unused;
350ea81ac2eSAlexander Aring static struct ctl_table lowpan_frags_ctl_table[] = {
351ea81ac2eSAlexander Aring 	{
352ea81ac2eSAlexander Aring 		.procname	= "6lowpanfrag_secret_interval",
353ea81ac2eSAlexander Aring 		.data		= &lowpan_frags_secret_interval_unused,
354ea81ac2eSAlexander Aring 		.maxlen		= sizeof(int),
355ea81ac2eSAlexander Aring 		.mode		= 0644,
356ea81ac2eSAlexander Aring 		.proc_handler	= proc_dointvec_jiffies,
357ea81ac2eSAlexander Aring 	},
358ea81ac2eSAlexander Aring 	{ }
359ea81ac2eSAlexander Aring };
360ea81ac2eSAlexander Aring 
361ea81ac2eSAlexander Aring static int __net_init lowpan_frags_ns_sysctl_register(struct net *net)
362ea81ac2eSAlexander Aring {
363ea81ac2eSAlexander Aring 	struct ctl_table *table;
364ea81ac2eSAlexander Aring 	struct ctl_table_header *hdr;
365ea81ac2eSAlexander Aring 	struct netns_ieee802154_lowpan *ieee802154_lowpan =
366ea81ac2eSAlexander Aring 		net_ieee802154_lowpan(net);
367ea81ac2eSAlexander Aring 
368ea81ac2eSAlexander Aring 	table = lowpan_frags_ns_ctl_table;
369ea81ac2eSAlexander Aring 	if (!net_eq(net, &init_net)) {
370ea81ac2eSAlexander Aring 		table = kmemdup(table, sizeof(lowpan_frags_ns_ctl_table),
371ea81ac2eSAlexander Aring 				GFP_KERNEL);
372ea81ac2eSAlexander Aring 		if (table == NULL)
373ea81ac2eSAlexander Aring 			goto err_alloc;
374ea81ac2eSAlexander Aring 
375ea81ac2eSAlexander Aring 		table[0].data = &ieee802154_lowpan->frags.high_thresh;
376ea81ac2eSAlexander Aring 		table[0].extra1 = &ieee802154_lowpan->frags.low_thresh;
377ea81ac2eSAlexander Aring 		table[1].data = &ieee802154_lowpan->frags.low_thresh;
378ea81ac2eSAlexander Aring 		table[1].extra2 = &ieee802154_lowpan->frags.high_thresh;
379ea81ac2eSAlexander Aring 		table[2].data = &ieee802154_lowpan->frags.timeout;
380ea81ac2eSAlexander Aring 
381ea81ac2eSAlexander Aring 		/* Don't export sysctls to unprivileged users */
382ea81ac2eSAlexander Aring 		if (net->user_ns != &init_user_ns)
383ea81ac2eSAlexander Aring 			table[0].procname = NULL;
384ea81ac2eSAlexander Aring 	}
385ea81ac2eSAlexander Aring 
386ea81ac2eSAlexander Aring 	hdr = register_net_sysctl(net, "net/ieee802154/6lowpan", table);
387ea81ac2eSAlexander Aring 	if (hdr == NULL)
388ea81ac2eSAlexander Aring 		goto err_reg;
389ea81ac2eSAlexander Aring 
390ea81ac2eSAlexander Aring 	ieee802154_lowpan->sysctl.frags_hdr = hdr;
391ea81ac2eSAlexander Aring 	return 0;
392ea81ac2eSAlexander Aring 
393ea81ac2eSAlexander Aring err_reg:
394ea81ac2eSAlexander Aring 	if (!net_eq(net, &init_net))
395ea81ac2eSAlexander Aring 		kfree(table);
396ea81ac2eSAlexander Aring err_alloc:
397ea81ac2eSAlexander Aring 	return -ENOMEM;
398ea81ac2eSAlexander Aring }
399ea81ac2eSAlexander Aring 
400ea81ac2eSAlexander Aring static void __net_exit lowpan_frags_ns_sysctl_unregister(struct net *net)
401ea81ac2eSAlexander Aring {
402ea81ac2eSAlexander Aring 	struct ctl_table *table;
403ea81ac2eSAlexander Aring 	struct netns_ieee802154_lowpan *ieee802154_lowpan =
404ea81ac2eSAlexander Aring 		net_ieee802154_lowpan(net);
405ea81ac2eSAlexander Aring 
406ea81ac2eSAlexander Aring 	table = ieee802154_lowpan->sysctl.frags_hdr->ctl_table_arg;
407ea81ac2eSAlexander Aring 	unregister_net_sysctl_table(ieee802154_lowpan->sysctl.frags_hdr);
408ea81ac2eSAlexander Aring 	if (!net_eq(net, &init_net))
409ea81ac2eSAlexander Aring 		kfree(table);
410ea81ac2eSAlexander Aring }
411ea81ac2eSAlexander Aring 
412ea81ac2eSAlexander Aring static struct ctl_table_header *lowpan_ctl_header;
413ea81ac2eSAlexander Aring 
414ea81ac2eSAlexander Aring static int __init lowpan_frags_sysctl_register(void)
415ea81ac2eSAlexander Aring {
416ea81ac2eSAlexander Aring 	lowpan_ctl_header = register_net_sysctl(&init_net,
417ea81ac2eSAlexander Aring 						"net/ieee802154/6lowpan",
418ea81ac2eSAlexander Aring 						lowpan_frags_ctl_table);
419ea81ac2eSAlexander Aring 	return lowpan_ctl_header == NULL ? -ENOMEM : 0;
420ea81ac2eSAlexander Aring }
421ea81ac2eSAlexander Aring 
422ea81ac2eSAlexander Aring static void lowpan_frags_sysctl_unregister(void)
423ea81ac2eSAlexander Aring {
424ea81ac2eSAlexander Aring 	unregister_net_sysctl_table(lowpan_ctl_header);
425ea81ac2eSAlexander Aring }
426ea81ac2eSAlexander Aring #else
427ea81ac2eSAlexander Aring static inline int lowpan_frags_ns_sysctl_register(struct net *net)
428ea81ac2eSAlexander Aring {
429ea81ac2eSAlexander Aring 	return 0;
430ea81ac2eSAlexander Aring }
431ea81ac2eSAlexander Aring 
432ea81ac2eSAlexander Aring static inline void lowpan_frags_ns_sysctl_unregister(struct net *net)
433ea81ac2eSAlexander Aring {
434ea81ac2eSAlexander Aring }
435ea81ac2eSAlexander Aring 
436ea81ac2eSAlexander Aring static inline int __init lowpan_frags_sysctl_register(void)
437ea81ac2eSAlexander Aring {
438ea81ac2eSAlexander Aring 	return 0;
439ea81ac2eSAlexander Aring }
440ea81ac2eSAlexander Aring 
441ea81ac2eSAlexander Aring static inline void lowpan_frags_sysctl_unregister(void)
442ea81ac2eSAlexander Aring {
443ea81ac2eSAlexander Aring }
444ea81ac2eSAlexander Aring #endif
445ea81ac2eSAlexander Aring 
446ea81ac2eSAlexander Aring static int __net_init lowpan_frags_init_net(struct net *net)
447ea81ac2eSAlexander Aring {
448ea81ac2eSAlexander Aring 	struct netns_ieee802154_lowpan *ieee802154_lowpan =
449ea81ac2eSAlexander Aring 		net_ieee802154_lowpan(net);
450787bea77SEric Dumazet 	int res;
451ea81ac2eSAlexander Aring 
452ea81ac2eSAlexander Aring 	ieee802154_lowpan->frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
453ea81ac2eSAlexander Aring 	ieee802154_lowpan->frags.low_thresh = IPV6_FRAG_LOW_THRESH;
454ea81ac2eSAlexander Aring 	ieee802154_lowpan->frags.timeout = IPV6_FRAG_TIMEOUT;
455093ba729SEric Dumazet 	ieee802154_lowpan->frags.f = &lowpan_frags;
456ea81ac2eSAlexander Aring 
457787bea77SEric Dumazet 	res = inet_frags_init_net(&ieee802154_lowpan->frags);
458787bea77SEric Dumazet 	if (res < 0)
459787bea77SEric Dumazet 		return res;
460787bea77SEric Dumazet 	res = lowpan_frags_ns_sysctl_register(net);
461787bea77SEric Dumazet 	if (res < 0)
462093ba729SEric Dumazet 		inet_frags_exit_net(&ieee802154_lowpan->frags);
463787bea77SEric Dumazet 	return res;
464ea81ac2eSAlexander Aring }
465ea81ac2eSAlexander Aring 
466ea81ac2eSAlexander Aring static void __net_exit lowpan_frags_exit_net(struct net *net)
467ea81ac2eSAlexander Aring {
468ea81ac2eSAlexander Aring 	struct netns_ieee802154_lowpan *ieee802154_lowpan =
469ea81ac2eSAlexander Aring 		net_ieee802154_lowpan(net);
470ea81ac2eSAlexander Aring 
471ea81ac2eSAlexander Aring 	lowpan_frags_ns_sysctl_unregister(net);
472093ba729SEric Dumazet 	inet_frags_exit_net(&ieee802154_lowpan->frags);
473ea81ac2eSAlexander Aring }
474ea81ac2eSAlexander Aring 
475ea81ac2eSAlexander Aring static struct pernet_operations lowpan_frags_ops = {
476ea81ac2eSAlexander Aring 	.init = lowpan_frags_init_net,
477ea81ac2eSAlexander Aring 	.exit = lowpan_frags_exit_net,
478ea81ac2eSAlexander Aring };
479ea81ac2eSAlexander Aring 
480648700f7SEric Dumazet static u32 lowpan_key_hashfn(const void *data, u32 len, u32 seed)
481648700f7SEric Dumazet {
482648700f7SEric Dumazet 	return jhash2(data,
483648700f7SEric Dumazet 		      sizeof(struct frag_lowpan_compare_key) / sizeof(u32), seed);
484648700f7SEric Dumazet }
485648700f7SEric Dumazet 
486648700f7SEric Dumazet static u32 lowpan_obj_hashfn(const void *data, u32 len, u32 seed)
487648700f7SEric Dumazet {
488648700f7SEric Dumazet 	const struct inet_frag_queue *fq = data;
489648700f7SEric Dumazet 
490648700f7SEric Dumazet 	return jhash2((const u32 *)&fq->key,
491648700f7SEric Dumazet 		      sizeof(struct frag_lowpan_compare_key) / sizeof(u32), seed);
492648700f7SEric Dumazet }
493648700f7SEric Dumazet 
494648700f7SEric Dumazet static int lowpan_obj_cmpfn(struct rhashtable_compare_arg *arg, const void *ptr)
495648700f7SEric Dumazet {
496648700f7SEric Dumazet 	const struct frag_lowpan_compare_key *key = arg->key;
497648700f7SEric Dumazet 	const struct inet_frag_queue *fq = ptr;
498648700f7SEric Dumazet 
499648700f7SEric Dumazet 	return !!memcmp(&fq->key, key, sizeof(*key));
500648700f7SEric Dumazet }
501648700f7SEric Dumazet 
502648700f7SEric Dumazet static const struct rhashtable_params lowpan_rhash_params = {
503648700f7SEric Dumazet 	.head_offset		= offsetof(struct inet_frag_queue, node),
504648700f7SEric Dumazet 	.hashfn			= lowpan_key_hashfn,
505648700f7SEric Dumazet 	.obj_hashfn		= lowpan_obj_hashfn,
506648700f7SEric Dumazet 	.obj_cmpfn		= lowpan_obj_cmpfn,
507648700f7SEric Dumazet 	.automatic_shrinking	= true,
508648700f7SEric Dumazet };
509648700f7SEric Dumazet 
510ea81ac2eSAlexander Aring int __init lowpan_net_frag_init(void)
511ea81ac2eSAlexander Aring {
512ea81ac2eSAlexander Aring 	int ret;
513ea81ac2eSAlexander Aring 
514ea81ac2eSAlexander Aring 	lowpan_frags.constructor = lowpan_frag_init;
515ea81ac2eSAlexander Aring 	lowpan_frags.destructor = NULL;
516ea81ac2eSAlexander Aring 	lowpan_frags.qsize = sizeof(struct frag_queue);
517ea81ac2eSAlexander Aring 	lowpan_frags.frag_expire = lowpan_frag_expire;
518ea81ac2eSAlexander Aring 	lowpan_frags.frags_cache_name = lowpan_frags_cache_name;
519648700f7SEric Dumazet 	lowpan_frags.rhash_params = lowpan_rhash_params;
520ea81ac2eSAlexander Aring 	ret = inet_frags_init(&lowpan_frags);
521ea81ac2eSAlexander Aring 	if (ret)
522807f1844SEric Dumazet 		goto out;
523ea81ac2eSAlexander Aring 
524807f1844SEric Dumazet 	ret = lowpan_frags_sysctl_register();
525807f1844SEric Dumazet 	if (ret)
526807f1844SEric Dumazet 		goto err_sysctl;
527807f1844SEric Dumazet 
528807f1844SEric Dumazet 	ret = register_pernet_subsys(&lowpan_frags_ops);
529807f1844SEric Dumazet 	if (ret)
530807f1844SEric Dumazet 		goto err_pernet;
531807f1844SEric Dumazet out:
532ea81ac2eSAlexander Aring 	return ret;
533ea81ac2eSAlexander Aring err_pernet:
534ea81ac2eSAlexander Aring 	lowpan_frags_sysctl_unregister();
535807f1844SEric Dumazet err_sysctl:
536807f1844SEric Dumazet 	inet_frags_fini(&lowpan_frags);
537ea81ac2eSAlexander Aring 	return ret;
538ea81ac2eSAlexander Aring }
539ea81ac2eSAlexander Aring 
540ea81ac2eSAlexander Aring void lowpan_net_frag_exit(void)
541ea81ac2eSAlexander Aring {
542ea81ac2eSAlexander Aring 	inet_frags_fini(&lowpan_frags);
543ea81ac2eSAlexander Aring 	lowpan_frags_sysctl_unregister();
544ea81ac2eSAlexander Aring 	unregister_pernet_subsys(&lowpan_frags_ops);
545ea81ac2eSAlexander Aring }
546