1ea81ac2eSAlexander Aring /*	6LoWPAN fragment reassembly
2ea81ac2eSAlexander Aring  *
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  *	This program is free software; you can redistribute it and/or
10ea81ac2eSAlexander Aring  *	modify it under the terms of the GNU General Public License
11ea81ac2eSAlexander Aring  *	as published by the Free Software Foundation; either version
12ea81ac2eSAlexander Aring  *	2 of the License, or (at your option) any later version.
13ea81ac2eSAlexander Aring  */
14ea81ac2eSAlexander Aring 
15ea81ac2eSAlexander Aring #define pr_fmt(fmt) "6LoWPAN: " fmt
16ea81ac2eSAlexander Aring 
17ea81ac2eSAlexander Aring #include <linux/net.h>
18ea81ac2eSAlexander Aring #include <linux/list.h>
19ea81ac2eSAlexander Aring #include <linux/netdevice.h>
20ea81ac2eSAlexander Aring #include <linux/random.h>
21ea81ac2eSAlexander Aring #include <linux/jhash.h>
22ea81ac2eSAlexander Aring #include <linux/skbuff.h>
23ea81ac2eSAlexander Aring #include <linux/slab.h>
24ea81ac2eSAlexander Aring #include <linux/export.h>
25ea81ac2eSAlexander Aring 
26ea81ac2eSAlexander Aring #include <net/ieee802154_netdev.h>
27ea81ac2eSAlexander Aring #include <net/6lowpan.h>
2870b095c8SFlorian Westphal #include <net/ipv6_frag.h>
29ea81ac2eSAlexander Aring #include <net/inet_frag.h>
30254c5dbeSPeter Oskolkov #include <net/ip.h>
31ea81ac2eSAlexander Aring 
328691ee59SAlexander Aring #include "6lowpan_i.h"
33ea81ac2eSAlexander Aring 
34ea81ac2eSAlexander Aring static const char lowpan_frags_cache_name[] = "lowpan-frags";
35ea81ac2eSAlexander Aring 
36ea81ac2eSAlexander Aring static struct inet_frags lowpan_frags;
37ea81ac2eSAlexander Aring 
38254c5dbeSPeter Oskolkov static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *skb,
39f4606583SAlexander Aring 			     struct sk_buff *prev,  struct net_device *ldev);
40ea81ac2eSAlexander Aring 
41ea81ac2eSAlexander Aring static void lowpan_frag_init(struct inet_frag_queue *q, const void *a)
42ea81ac2eSAlexander Aring {
43648700f7SEric Dumazet 	const struct frag_lowpan_compare_key *key = a;
44ea81ac2eSAlexander Aring 
45648700f7SEric Dumazet 	BUILD_BUG_ON(sizeof(*key) > sizeof(q->key));
46648700f7SEric Dumazet 	memcpy(&q->key, key, sizeof(*key));
47ea81ac2eSAlexander Aring }
48ea81ac2eSAlexander Aring 
4978802011SKees Cook static void lowpan_frag_expire(struct timer_list *t)
50ea81ac2eSAlexander Aring {
5178802011SKees Cook 	struct inet_frag_queue *frag = from_timer(frag, t, timer);
52ea81ac2eSAlexander Aring 	struct frag_queue *fq;
53ea81ac2eSAlexander Aring 
5478802011SKees Cook 	fq = container_of(frag, struct frag_queue, q);
55ea81ac2eSAlexander Aring 
56ea81ac2eSAlexander Aring 	spin_lock(&fq->q.lock);
57ea81ac2eSAlexander Aring 
58ea81ac2eSAlexander Aring 	if (fq->q.flags & INET_FRAG_COMPLETE)
59ea81ac2eSAlexander Aring 		goto out;
60ea81ac2eSAlexander Aring 
61093ba729SEric Dumazet 	inet_frag_kill(&fq->q);
62ea81ac2eSAlexander Aring out:
63ea81ac2eSAlexander Aring 	spin_unlock(&fq->q.lock);
64093ba729SEric Dumazet 	inet_frag_put(&fq->q);
65ea81ac2eSAlexander Aring }
66ea81ac2eSAlexander Aring 
67ea81ac2eSAlexander Aring static inline struct lowpan_frag_queue *
6872a5e6bbSAlexander Aring fq_find(struct net *net, const struct lowpan_802154_cb *cb,
69ea81ac2eSAlexander Aring 	const struct ieee802154_addr *src,
70ea81ac2eSAlexander Aring 	const struct ieee802154_addr *dst)
71ea81ac2eSAlexander Aring {
72ea81ac2eSAlexander Aring 	struct netns_ieee802154_lowpan *ieee802154_lowpan =
73ea81ac2eSAlexander Aring 		net_ieee802154_lowpan(net);
74f18fa5deSAlexander Aring 	struct frag_lowpan_compare_key key = {};
75648700f7SEric Dumazet 	struct inet_frag_queue *q;
76ea81ac2eSAlexander Aring 
77f18fa5deSAlexander Aring 	key.tag = cb->d_tag;
78f18fa5deSAlexander Aring 	key.d_size = cb->d_size;
79f18fa5deSAlexander Aring 	key.src = *src;
80f18fa5deSAlexander Aring 	key.dst = *dst;
81f18fa5deSAlexander Aring 
82803fdd99SEric Dumazet 	q = inet_frag_find(&ieee802154_lowpan->fqdir, &key);
832d44ed22SEric Dumazet 	if (!q)
84ea81ac2eSAlexander Aring 		return NULL;
852d44ed22SEric Dumazet 
86ea81ac2eSAlexander Aring 	return container_of(q, struct lowpan_frag_queue, q);
87ea81ac2eSAlexander Aring }
88ea81ac2eSAlexander Aring 
89ea81ac2eSAlexander Aring static int lowpan_frag_queue(struct lowpan_frag_queue *fq,
9072a5e6bbSAlexander Aring 			     struct sk_buff *skb, u8 frag_type)
91ea81ac2eSAlexander Aring {
92254c5dbeSPeter Oskolkov 	struct sk_buff *prev_tail;
93f4606583SAlexander Aring 	struct net_device *ldev;
94254c5dbeSPeter Oskolkov 	int end, offset, err;
95254c5dbeSPeter Oskolkov 
96254c5dbeSPeter Oskolkov 	/* inet_frag_queue_* functions use skb->cb; see struct ipfrag_skb_cb
97254c5dbeSPeter Oskolkov 	 * in inet_fragment.c
98254c5dbeSPeter Oskolkov 	 */
99254c5dbeSPeter Oskolkov 	BUILD_BUG_ON(sizeof(struct lowpan_802154_cb) > sizeof(struct inet_skb_parm));
100254c5dbeSPeter Oskolkov 	BUILD_BUG_ON(sizeof(struct lowpan_802154_cb) > sizeof(struct inet6_skb_parm));
101ea81ac2eSAlexander Aring 
102ea81ac2eSAlexander Aring 	if (fq->q.flags & INET_FRAG_COMPLETE)
103ea81ac2eSAlexander Aring 		goto err;
104ea81ac2eSAlexander Aring 
10572a5e6bbSAlexander Aring 	offset = lowpan_802154_cb(skb)->d_offset << 3;
10672a5e6bbSAlexander Aring 	end = lowpan_802154_cb(skb)->d_size;
107ea81ac2eSAlexander Aring 
108ea81ac2eSAlexander Aring 	/* Is this the final fragment? */
109ea81ac2eSAlexander Aring 	if (offset + skb->len == end) {
110ea81ac2eSAlexander Aring 		/* If we already have some bits beyond end
111ea81ac2eSAlexander Aring 		 * or have different end, the segment is corrupted.
112ea81ac2eSAlexander Aring 		 */
113ea81ac2eSAlexander Aring 		if (end < fq->q.len ||
114ea81ac2eSAlexander Aring 		    ((fq->q.flags & INET_FRAG_LAST_IN) && end != fq->q.len))
115ea81ac2eSAlexander Aring 			goto err;
116ea81ac2eSAlexander Aring 		fq->q.flags |= INET_FRAG_LAST_IN;
117ea81ac2eSAlexander Aring 		fq->q.len = end;
118ea81ac2eSAlexander Aring 	} else {
119ea81ac2eSAlexander Aring 		if (end > fq->q.len) {
120ea81ac2eSAlexander Aring 			/* Some bits beyond end -> corruption. */
121ea81ac2eSAlexander Aring 			if (fq->q.flags & INET_FRAG_LAST_IN)
122ea81ac2eSAlexander Aring 				goto err;
123ea81ac2eSAlexander Aring 			fq->q.len = end;
124ea81ac2eSAlexander Aring 		}
125ea81ac2eSAlexander Aring 	}
126ea81ac2eSAlexander Aring 
127f4606583SAlexander Aring 	ldev = skb->dev;
128f4606583SAlexander Aring 	if (ldev)
129ea81ac2eSAlexander Aring 		skb->dev = NULL;
130254c5dbeSPeter Oskolkov 	barrier();
131254c5dbeSPeter Oskolkov 
132254c5dbeSPeter Oskolkov 	prev_tail = fq->q.fragments_tail;
133254c5dbeSPeter Oskolkov 	err = inet_frag_queue_insert(&fq->q, skb, offset, end);
134254c5dbeSPeter Oskolkov 	if (err)
135254c5dbeSPeter Oskolkov 		goto err;
136ea81ac2eSAlexander Aring 
137ea81ac2eSAlexander Aring 	fq->q.stamp = skb->tstamp;
13872a5e6bbSAlexander Aring 	if (frag_type == LOWPAN_DISPATCH_FRAG1)
139ea81ac2eSAlexander Aring 		fq->q.flags |= INET_FRAG_FIRST_IN;
14072a5e6bbSAlexander Aring 
141ea81ac2eSAlexander Aring 	fq->q.meat += skb->len;
1426ce3b4dcSEric Dumazet 	add_frag_mem_limit(fq->q.fqdir, skb->truesize);
143ea81ac2eSAlexander Aring 
144ea81ac2eSAlexander Aring 	if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
145ea81ac2eSAlexander Aring 	    fq->q.meat == fq->q.len) {
146ea81ac2eSAlexander Aring 		int res;
147ea81ac2eSAlexander Aring 		unsigned long orefdst = skb->_skb_refdst;
148ea81ac2eSAlexander Aring 
149ea81ac2eSAlexander Aring 		skb->_skb_refdst = 0UL;
150254c5dbeSPeter Oskolkov 		res = lowpan_frag_reasm(fq, skb, prev_tail, ldev);
151ea81ac2eSAlexander Aring 		skb->_skb_refdst = orefdst;
152ea81ac2eSAlexander Aring 		return res;
153ea81ac2eSAlexander Aring 	}
154254c5dbeSPeter Oskolkov 	skb_dst_drop(skb);
155ea81ac2eSAlexander Aring 
156ea81ac2eSAlexander Aring 	return -1;
157ea81ac2eSAlexander Aring err:
158ea81ac2eSAlexander Aring 	kfree_skb(skb);
159ea81ac2eSAlexander Aring 	return -1;
160ea81ac2eSAlexander Aring }
161ea81ac2eSAlexander Aring 
162ea81ac2eSAlexander Aring /*	Check if this packet is complete.
163ea81ac2eSAlexander Aring  *
164ea81ac2eSAlexander Aring  *	It is called with locked fq, and caller must check that
165ea81ac2eSAlexander Aring  *	queue is eligible for reassembly i.e. it is not COMPLETE,
166ea81ac2eSAlexander Aring  *	the last and the first frames arrived and all the bits are here.
167ea81ac2eSAlexander Aring  */
168254c5dbeSPeter Oskolkov static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *skb,
169254c5dbeSPeter Oskolkov 			     struct sk_buff *prev_tail, struct net_device *ldev)
170ea81ac2eSAlexander Aring {
171254c5dbeSPeter Oskolkov 	void *reasm_data;
172ea81ac2eSAlexander Aring 
173093ba729SEric Dumazet 	inet_frag_kill(&fq->q);
174ea81ac2eSAlexander Aring 
175254c5dbeSPeter Oskolkov 	reasm_data = inet_frag_reasm_prepare(&fq->q, skb, prev_tail);
176254c5dbeSPeter Oskolkov 	if (!reasm_data)
177ea81ac2eSAlexander Aring 		goto out_oom;
178254c5dbeSPeter Oskolkov 	inet_frag_reasm_finish(&fq->q, skb, reasm_data);
179ea81ac2eSAlexander Aring 
180254c5dbeSPeter Oskolkov 	skb->dev = ldev;
181254c5dbeSPeter Oskolkov 	skb->tstamp = fq->q.stamp;
182254c5dbeSPeter Oskolkov 	fq->q.rb_fragments = RB_ROOT;
183ea81ac2eSAlexander Aring 	fq->q.fragments_tail = NULL;
184254c5dbeSPeter Oskolkov 	fq->q.last_run_head = NULL;
185ea81ac2eSAlexander Aring 
186ea81ac2eSAlexander Aring 	return 1;
187ea81ac2eSAlexander Aring out_oom:
188ea81ac2eSAlexander Aring 	net_dbg_ratelimited("lowpan_frag_reasm: no memory for reassembly\n");
189ea81ac2eSAlexander Aring 	return -1;
190ea81ac2eSAlexander Aring }
191ea81ac2eSAlexander Aring 
19272a5e6bbSAlexander Aring static int lowpan_frag_rx_handlers_result(struct sk_buff *skb,
19372a5e6bbSAlexander Aring 					  lowpan_rx_result res)
19472a5e6bbSAlexander Aring {
19572a5e6bbSAlexander Aring 	switch (res) {
19672a5e6bbSAlexander Aring 	case RX_QUEUED:
19772a5e6bbSAlexander Aring 		return NET_RX_SUCCESS;
19872a5e6bbSAlexander Aring 	case RX_CONTINUE:
19972a5e6bbSAlexander Aring 		/* nobody cared about this packet */
20072a5e6bbSAlexander Aring 		net_warn_ratelimited("%s: received unknown dispatch\n",
20172a5e6bbSAlexander Aring 				     __func__);
20272a5e6bbSAlexander Aring 
20372a5e6bbSAlexander Aring 		/* fall-through */
20472a5e6bbSAlexander Aring 	default:
20572a5e6bbSAlexander Aring 		/* all others failure */
20672a5e6bbSAlexander Aring 		return NET_RX_DROP;
20772a5e6bbSAlexander Aring 	}
20872a5e6bbSAlexander Aring }
20972a5e6bbSAlexander Aring 
21072a5e6bbSAlexander Aring static lowpan_rx_result lowpan_frag_rx_h_iphc(struct sk_buff *skb)
21172a5e6bbSAlexander Aring {
21272a5e6bbSAlexander Aring 	int ret;
21372a5e6bbSAlexander Aring 
21472a5e6bbSAlexander Aring 	if (!lowpan_is_iphc(*skb_network_header(skb)))
21572a5e6bbSAlexander Aring 		return RX_CONTINUE;
21672a5e6bbSAlexander Aring 
21772a5e6bbSAlexander Aring 	ret = lowpan_iphc_decompress(skb);
21872a5e6bbSAlexander Aring 	if (ret < 0)
21972a5e6bbSAlexander Aring 		return RX_DROP;
22072a5e6bbSAlexander Aring 
22172a5e6bbSAlexander Aring 	return RX_QUEUED;
22272a5e6bbSAlexander Aring }
22372a5e6bbSAlexander Aring 
22472a5e6bbSAlexander Aring static int lowpan_invoke_frag_rx_handlers(struct sk_buff *skb)
22572a5e6bbSAlexander Aring {
22672a5e6bbSAlexander Aring 	lowpan_rx_result res;
22772a5e6bbSAlexander Aring 
22872a5e6bbSAlexander Aring #define CALL_RXH(rxh)			\
22972a5e6bbSAlexander Aring 	do {				\
23072a5e6bbSAlexander Aring 		res = rxh(skb);	\
23172a5e6bbSAlexander Aring 		if (res != RX_CONTINUE)	\
23272a5e6bbSAlexander Aring 			goto rxh_next;	\
23372a5e6bbSAlexander Aring 	} while (0)
23472a5e6bbSAlexander Aring 
23572a5e6bbSAlexander Aring 	/* likely at first */
23672a5e6bbSAlexander Aring 	CALL_RXH(lowpan_frag_rx_h_iphc);
23772a5e6bbSAlexander Aring 	CALL_RXH(lowpan_rx_h_ipv6);
23872a5e6bbSAlexander Aring 
23972a5e6bbSAlexander Aring rxh_next:
24072a5e6bbSAlexander Aring 	return lowpan_frag_rx_handlers_result(skb, res);
24172a5e6bbSAlexander Aring #undef CALL_RXH
24272a5e6bbSAlexander Aring }
24372a5e6bbSAlexander Aring 
24472a5e6bbSAlexander Aring #define LOWPAN_FRAG_DGRAM_SIZE_HIGH_MASK	0x07
24572a5e6bbSAlexander Aring #define LOWPAN_FRAG_DGRAM_SIZE_HIGH_SHIFT	8
24672a5e6bbSAlexander Aring 
24772a5e6bbSAlexander Aring static int lowpan_get_cb(struct sk_buff *skb, u8 frag_type,
24872a5e6bbSAlexander Aring 			 struct lowpan_802154_cb *cb)
249ea81ac2eSAlexander Aring {
250ea81ac2eSAlexander Aring 	bool fail;
25172a5e6bbSAlexander Aring 	u8 high = 0, low = 0;
252ea81ac2eSAlexander Aring 	__be16 d_tag = 0;
253ea81ac2eSAlexander Aring 
25472a5e6bbSAlexander Aring 	fail = lowpan_fetch_skb(skb, &high, 1);
255ea81ac2eSAlexander Aring 	fail |= lowpan_fetch_skb(skb, &low, 1);
25672a5e6bbSAlexander Aring 	/* remove the dispatch value and use first three bits as high value
25772a5e6bbSAlexander Aring 	 * for the datagram size
25872a5e6bbSAlexander Aring 	 */
25972a5e6bbSAlexander Aring 	cb->d_size = (high & LOWPAN_FRAG_DGRAM_SIZE_HIGH_MASK) <<
26072a5e6bbSAlexander Aring 		LOWPAN_FRAG_DGRAM_SIZE_HIGH_SHIFT | low;
261ea81ac2eSAlexander Aring 	fail |= lowpan_fetch_skb(skb, &d_tag, 2);
26272a5e6bbSAlexander Aring 	cb->d_tag = ntohs(d_tag);
263ea81ac2eSAlexander Aring 
264ea81ac2eSAlexander Aring 	if (frag_type == LOWPAN_DISPATCH_FRAGN) {
26572a5e6bbSAlexander Aring 		fail |= lowpan_fetch_skb(skb, &cb->d_offset, 1);
266ea81ac2eSAlexander Aring 	} else {
267ea81ac2eSAlexander Aring 		skb_reset_network_header(skb);
26872a5e6bbSAlexander Aring 		cb->d_offset = 0;
26972a5e6bbSAlexander Aring 		/* check if datagram_size has ipv6hdr on FRAG1 */
27072a5e6bbSAlexander Aring 		fail |= cb->d_size < sizeof(struct ipv6hdr);
27172a5e6bbSAlexander Aring 		/* check if we can dereference the dispatch value */
27272a5e6bbSAlexander Aring 		fail |= !skb->len;
273ea81ac2eSAlexander Aring 	}
274ea81ac2eSAlexander Aring 
275ea81ac2eSAlexander Aring 	if (unlikely(fail))
276ea81ac2eSAlexander Aring 		return -EIO;
277ea81ac2eSAlexander Aring 
278ea81ac2eSAlexander Aring 	return 0;
279ea81ac2eSAlexander Aring }
280ea81ac2eSAlexander Aring 
28172a5e6bbSAlexander Aring int lowpan_frag_rcv(struct sk_buff *skb, u8 frag_type)
282ea81ac2eSAlexander Aring {
283ea81ac2eSAlexander Aring 	struct lowpan_frag_queue *fq;
284ea81ac2eSAlexander Aring 	struct net *net = dev_net(skb->dev);
28572a5e6bbSAlexander Aring 	struct lowpan_802154_cb *cb = lowpan_802154_cb(skb);
286f18fa5deSAlexander Aring 	struct ieee802154_hdr hdr = {};
287ea81ac2eSAlexander Aring 	int err;
288ea81ac2eSAlexander Aring 
28972a5e6bbSAlexander Aring 	if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0)
29072a5e6bbSAlexander Aring 		goto err;
291ea81ac2eSAlexander Aring 
29272a5e6bbSAlexander Aring 	err = lowpan_get_cb(skb, frag_type, cb);
293ea81ac2eSAlexander Aring 	if (err < 0)
294ea81ac2eSAlexander Aring 		goto err;
295ea81ac2eSAlexander Aring 
29672a5e6bbSAlexander Aring 	if (frag_type == LOWPAN_DISPATCH_FRAG1) {
29772a5e6bbSAlexander Aring 		err = lowpan_invoke_frag_rx_handlers(skb);
29872a5e6bbSAlexander Aring 		if (err == NET_RX_DROP)
29972a5e6bbSAlexander Aring 			goto err;
30072a5e6bbSAlexander Aring 	}
30172a5e6bbSAlexander Aring 
30272a5e6bbSAlexander Aring 	if (cb->d_size > IPV6_MIN_MTU) {
303ea81ac2eSAlexander Aring 		net_warn_ratelimited("lowpan_frag_rcv: datagram size exceeds MTU\n");
304ea81ac2eSAlexander Aring 		goto err;
305ea81ac2eSAlexander Aring 	}
306ea81ac2eSAlexander Aring 
30772a5e6bbSAlexander Aring 	fq = fq_find(net, cb, &hdr.source, &hdr.dest);
308ea81ac2eSAlexander Aring 	if (fq != NULL) {
309ea81ac2eSAlexander Aring 		int ret;
310ea81ac2eSAlexander Aring 
311ea81ac2eSAlexander Aring 		spin_lock(&fq->q.lock);
312ea81ac2eSAlexander Aring 		ret = lowpan_frag_queue(fq, skb, frag_type);
313ea81ac2eSAlexander Aring 		spin_unlock(&fq->q.lock);
314ea81ac2eSAlexander Aring 
315093ba729SEric Dumazet 		inet_frag_put(&fq->q);
316ea81ac2eSAlexander Aring 		return ret;
317ea81ac2eSAlexander Aring 	}
318ea81ac2eSAlexander Aring 
319ea81ac2eSAlexander Aring err:
320ea81ac2eSAlexander Aring 	kfree_skb(skb);
321ea81ac2eSAlexander Aring 	return -1;
322ea81ac2eSAlexander Aring }
323ea81ac2eSAlexander Aring 
324ea81ac2eSAlexander Aring #ifdef CONFIG_SYSCTL
325ea81ac2eSAlexander Aring 
326ea81ac2eSAlexander Aring static struct ctl_table lowpan_frags_ns_ctl_table[] = {
327ea81ac2eSAlexander Aring 	{
328ea81ac2eSAlexander Aring 		.procname	= "6lowpanfrag_high_thresh",
3293e67f106SEric Dumazet 		.maxlen		= sizeof(unsigned long),
330ea81ac2eSAlexander Aring 		.mode		= 0644,
3313e67f106SEric Dumazet 		.proc_handler	= proc_doulongvec_minmax,
332ea81ac2eSAlexander Aring 	},
333ea81ac2eSAlexander Aring 	{
334ea81ac2eSAlexander Aring 		.procname	= "6lowpanfrag_low_thresh",
3353e67f106SEric Dumazet 		.maxlen		= sizeof(unsigned long),
336ea81ac2eSAlexander Aring 		.mode		= 0644,
3373e67f106SEric Dumazet 		.proc_handler	= proc_doulongvec_minmax,
338ea81ac2eSAlexander Aring 	},
339ea81ac2eSAlexander Aring 	{
340ea81ac2eSAlexander Aring 		.procname	= "6lowpanfrag_time",
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 
375d2dfd435SEric Dumazet 		/* Don't export sysctls to unprivileged users */
376d2dfd435SEric Dumazet 		if (net->user_ns != &init_user_ns)
377d2dfd435SEric Dumazet 			table[0].procname = NULL;
378d2dfd435SEric Dumazet 	}
379d2dfd435SEric Dumazet 
380803fdd99SEric Dumazet 	table[0].data	= &ieee802154_lowpan->fqdir.high_thresh;
381803fdd99SEric Dumazet 	table[0].extra1	= &ieee802154_lowpan->fqdir.low_thresh;
382803fdd99SEric Dumazet 	table[1].data	= &ieee802154_lowpan->fqdir.low_thresh;
383803fdd99SEric Dumazet 	table[1].extra2	= &ieee802154_lowpan->fqdir.high_thresh;
384803fdd99SEric Dumazet 	table[2].data	= &ieee802154_lowpan->fqdir.timeout;
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 
452803fdd99SEric Dumazet 	ieee802154_lowpan->fqdir.high_thresh = IPV6_FRAG_HIGH_THRESH;
453803fdd99SEric Dumazet 	ieee802154_lowpan->fqdir.low_thresh = IPV6_FRAG_LOW_THRESH;
454803fdd99SEric Dumazet 	ieee802154_lowpan->fqdir.timeout = IPV6_FRAG_TIMEOUT;
455803fdd99SEric Dumazet 	ieee802154_lowpan->fqdir.f = &lowpan_frags;
456ea81ac2eSAlexander Aring 
457803fdd99SEric Dumazet 	res = inet_frags_init_net(&ieee802154_lowpan->fqdir);
458787bea77SEric Dumazet 	if (res < 0)
459787bea77SEric Dumazet 		return res;
460787bea77SEric Dumazet 	res = lowpan_frags_ns_sysctl_register(net);
461787bea77SEric Dumazet 	if (res < 0)
462803fdd99SEric Dumazet 		fqdir_exit(&ieee802154_lowpan->fqdir);
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);
472803fdd99SEric Dumazet 	fqdir_exit(&ieee802154_lowpan->fqdir);
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