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
lowpan_frag_init(struct inet_frag_queue * q,const void * a)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
lowpan_frag_expire(struct timer_list * t)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 *
fq_find(struct net * net,const struct lowpan_802154_cb * cb,const struct ieee802154_addr * src,const struct ieee802154_addr * dst)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
774907abc6SEric Dumazet q = inet_frag_find(ieee802154_lowpan->fqdir, &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
lowpan_frag_queue(struct lowpan_frag_queue * fq,struct sk_buff * skb,u8 frag_type)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;
133335c8cf3SMartin KaFai Lau fq->q.mono_delivery_time = skb->mono_delivery_time;
13472a5e6bbSAlexander Aring if (frag_type == LOWPAN_DISPATCH_FRAG1)
135ea81ac2eSAlexander Aring fq->q.flags |= INET_FRAG_FIRST_IN;
13672a5e6bbSAlexander Aring
137ea81ac2eSAlexander Aring fq->q.meat += skb->len;
1386ce3b4dcSEric Dumazet add_frag_mem_limit(fq->q.fqdir, skb->truesize);
139ea81ac2eSAlexander Aring
140ea81ac2eSAlexander Aring if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
141ea81ac2eSAlexander Aring fq->q.meat == fq->q.len) {
142ea81ac2eSAlexander Aring int res;
143ea81ac2eSAlexander Aring unsigned long orefdst = skb->_skb_refdst;
144ea81ac2eSAlexander Aring
145ea81ac2eSAlexander Aring skb->_skb_refdst = 0UL;
146254c5dbeSPeter Oskolkov res = lowpan_frag_reasm(fq, skb, prev_tail, ldev);
147ea81ac2eSAlexander Aring skb->_skb_refdst = orefdst;
148ea81ac2eSAlexander Aring return res;
149ea81ac2eSAlexander Aring }
150254c5dbeSPeter Oskolkov skb_dst_drop(skb);
151ea81ac2eSAlexander Aring
152ea81ac2eSAlexander Aring return -1;
153ea81ac2eSAlexander Aring err:
154ea81ac2eSAlexander Aring kfree_skb(skb);
155ea81ac2eSAlexander Aring return -1;
156ea81ac2eSAlexander Aring }
157ea81ac2eSAlexander Aring
158ea81ac2eSAlexander Aring /* Check if this packet is complete.
159ea81ac2eSAlexander Aring *
160ea81ac2eSAlexander Aring * It is called with locked fq, and caller must check that
161ea81ac2eSAlexander Aring * queue is eligible for reassembly i.e. it is not COMPLETE,
162ea81ac2eSAlexander Aring * the last and the first frames arrived and all the bits are here.
163ea81ac2eSAlexander Aring */
lowpan_frag_reasm(struct lowpan_frag_queue * fq,struct sk_buff * skb,struct sk_buff * prev_tail,struct net_device * ldev)164254c5dbeSPeter Oskolkov static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *skb,
165254c5dbeSPeter Oskolkov struct sk_buff *prev_tail, struct net_device *ldev)
166ea81ac2eSAlexander Aring {
167254c5dbeSPeter Oskolkov void *reasm_data;
168ea81ac2eSAlexander Aring
169093ba729SEric Dumazet inet_frag_kill(&fq->q);
170ea81ac2eSAlexander Aring
171254c5dbeSPeter Oskolkov reasm_data = inet_frag_reasm_prepare(&fq->q, skb, prev_tail);
172254c5dbeSPeter Oskolkov if (!reasm_data)
173ea81ac2eSAlexander Aring goto out_oom;
174891584f4SGuillaume Nault inet_frag_reasm_finish(&fq->q, skb, reasm_data, false);
175ea81ac2eSAlexander Aring
176254c5dbeSPeter Oskolkov skb->dev = ldev;
177254c5dbeSPeter Oskolkov skb->tstamp = fq->q.stamp;
178254c5dbeSPeter Oskolkov fq->q.rb_fragments = RB_ROOT;
179ea81ac2eSAlexander Aring fq->q.fragments_tail = NULL;
180254c5dbeSPeter Oskolkov fq->q.last_run_head = NULL;
181ea81ac2eSAlexander Aring
182ea81ac2eSAlexander Aring return 1;
183ea81ac2eSAlexander Aring out_oom:
184ea81ac2eSAlexander Aring net_dbg_ratelimited("lowpan_frag_reasm: no memory for reassembly\n");
185ea81ac2eSAlexander Aring return -1;
186ea81ac2eSAlexander Aring }
187ea81ac2eSAlexander Aring
lowpan_frag_rx_handlers_result(struct sk_buff * skb,lowpan_rx_result res)18872a5e6bbSAlexander Aring static int lowpan_frag_rx_handlers_result(struct sk_buff *skb,
18972a5e6bbSAlexander Aring lowpan_rx_result res)
19072a5e6bbSAlexander Aring {
19172a5e6bbSAlexander Aring switch (res) {
19272a5e6bbSAlexander Aring case RX_QUEUED:
19372a5e6bbSAlexander Aring return NET_RX_SUCCESS;
19472a5e6bbSAlexander Aring case RX_CONTINUE:
19572a5e6bbSAlexander Aring /* nobody cared about this packet */
19672a5e6bbSAlexander Aring net_warn_ratelimited("%s: received unknown dispatch\n",
19772a5e6bbSAlexander Aring __func__);
19872a5e6bbSAlexander Aring
199df561f66SGustavo A. R. Silva fallthrough;
20072a5e6bbSAlexander Aring default:
20172a5e6bbSAlexander Aring /* all others failure */
20272a5e6bbSAlexander Aring return NET_RX_DROP;
20372a5e6bbSAlexander Aring }
20472a5e6bbSAlexander Aring }
20572a5e6bbSAlexander Aring
lowpan_frag_rx_h_iphc(struct sk_buff * skb)20672a5e6bbSAlexander Aring static lowpan_rx_result lowpan_frag_rx_h_iphc(struct sk_buff *skb)
20772a5e6bbSAlexander Aring {
20872a5e6bbSAlexander Aring int ret;
20972a5e6bbSAlexander Aring
21072a5e6bbSAlexander Aring if (!lowpan_is_iphc(*skb_network_header(skb)))
21172a5e6bbSAlexander Aring return RX_CONTINUE;
21272a5e6bbSAlexander Aring
21372a5e6bbSAlexander Aring ret = lowpan_iphc_decompress(skb);
21472a5e6bbSAlexander Aring if (ret < 0)
21572a5e6bbSAlexander Aring return RX_DROP;
21672a5e6bbSAlexander Aring
21772a5e6bbSAlexander Aring return RX_QUEUED;
21872a5e6bbSAlexander Aring }
21972a5e6bbSAlexander Aring
lowpan_invoke_frag_rx_handlers(struct sk_buff * skb)22072a5e6bbSAlexander Aring static int lowpan_invoke_frag_rx_handlers(struct sk_buff *skb)
22172a5e6bbSAlexander Aring {
22272a5e6bbSAlexander Aring lowpan_rx_result res;
22372a5e6bbSAlexander Aring
22472a5e6bbSAlexander Aring #define CALL_RXH(rxh) \
22572a5e6bbSAlexander Aring do { \
22672a5e6bbSAlexander Aring res = rxh(skb); \
22772a5e6bbSAlexander Aring if (res != RX_CONTINUE) \
22872a5e6bbSAlexander Aring goto rxh_next; \
22972a5e6bbSAlexander Aring } while (0)
23072a5e6bbSAlexander Aring
23172a5e6bbSAlexander Aring /* likely at first */
23272a5e6bbSAlexander Aring CALL_RXH(lowpan_frag_rx_h_iphc);
23372a5e6bbSAlexander Aring CALL_RXH(lowpan_rx_h_ipv6);
23472a5e6bbSAlexander Aring
23572a5e6bbSAlexander Aring rxh_next:
23672a5e6bbSAlexander Aring return lowpan_frag_rx_handlers_result(skb, res);
23772a5e6bbSAlexander Aring #undef CALL_RXH
23872a5e6bbSAlexander Aring }
23972a5e6bbSAlexander Aring
24072a5e6bbSAlexander Aring #define LOWPAN_FRAG_DGRAM_SIZE_HIGH_MASK 0x07
24172a5e6bbSAlexander Aring #define LOWPAN_FRAG_DGRAM_SIZE_HIGH_SHIFT 8
24272a5e6bbSAlexander Aring
lowpan_get_cb(struct sk_buff * skb,u8 frag_type,struct lowpan_802154_cb * cb)24372a5e6bbSAlexander Aring static int lowpan_get_cb(struct sk_buff *skb, u8 frag_type,
24472a5e6bbSAlexander Aring struct lowpan_802154_cb *cb)
245ea81ac2eSAlexander Aring {
246ea81ac2eSAlexander Aring bool fail;
24772a5e6bbSAlexander Aring u8 high = 0, low = 0;
248ea81ac2eSAlexander Aring __be16 d_tag = 0;
249ea81ac2eSAlexander Aring
25072a5e6bbSAlexander Aring fail = lowpan_fetch_skb(skb, &high, 1);
251ea81ac2eSAlexander Aring fail |= lowpan_fetch_skb(skb, &low, 1);
25272a5e6bbSAlexander Aring /* remove the dispatch value and use first three bits as high value
25372a5e6bbSAlexander Aring * for the datagram size
25472a5e6bbSAlexander Aring */
25572a5e6bbSAlexander Aring cb->d_size = (high & LOWPAN_FRAG_DGRAM_SIZE_HIGH_MASK) <<
25672a5e6bbSAlexander Aring LOWPAN_FRAG_DGRAM_SIZE_HIGH_SHIFT | low;
257ea81ac2eSAlexander Aring fail |= lowpan_fetch_skb(skb, &d_tag, 2);
25872a5e6bbSAlexander Aring cb->d_tag = ntohs(d_tag);
259ea81ac2eSAlexander Aring
260ea81ac2eSAlexander Aring if (frag_type == LOWPAN_DISPATCH_FRAGN) {
26172a5e6bbSAlexander Aring fail |= lowpan_fetch_skb(skb, &cb->d_offset, 1);
262ea81ac2eSAlexander Aring } else {
263ea81ac2eSAlexander Aring skb_reset_network_header(skb);
26472a5e6bbSAlexander Aring cb->d_offset = 0;
26572a5e6bbSAlexander Aring /* check if datagram_size has ipv6hdr on FRAG1 */
26672a5e6bbSAlexander Aring fail |= cb->d_size < sizeof(struct ipv6hdr);
26772a5e6bbSAlexander Aring /* check if we can dereference the dispatch value */
26872a5e6bbSAlexander Aring fail |= !skb->len;
269ea81ac2eSAlexander Aring }
270ea81ac2eSAlexander Aring
271ea81ac2eSAlexander Aring if (unlikely(fail))
272ea81ac2eSAlexander Aring return -EIO;
273ea81ac2eSAlexander Aring
274ea81ac2eSAlexander Aring return 0;
275ea81ac2eSAlexander Aring }
276ea81ac2eSAlexander Aring
lowpan_frag_rcv(struct sk_buff * skb,u8 frag_type)27772a5e6bbSAlexander Aring int lowpan_frag_rcv(struct sk_buff *skb, u8 frag_type)
278ea81ac2eSAlexander Aring {
279ea81ac2eSAlexander Aring struct lowpan_frag_queue *fq;
280ea81ac2eSAlexander Aring struct net *net = dev_net(skb->dev);
28172a5e6bbSAlexander Aring struct lowpan_802154_cb *cb = lowpan_802154_cb(skb);
282f18fa5deSAlexander Aring struct ieee802154_hdr hdr = {};
283ea81ac2eSAlexander Aring int err;
284ea81ac2eSAlexander Aring
28572a5e6bbSAlexander Aring if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0)
28672a5e6bbSAlexander Aring goto err;
287ea81ac2eSAlexander Aring
28872a5e6bbSAlexander Aring err = lowpan_get_cb(skb, frag_type, cb);
289ea81ac2eSAlexander Aring if (err < 0)
290ea81ac2eSAlexander Aring goto err;
291ea81ac2eSAlexander Aring
29272a5e6bbSAlexander Aring if (frag_type == LOWPAN_DISPATCH_FRAG1) {
29372a5e6bbSAlexander Aring err = lowpan_invoke_frag_rx_handlers(skb);
29472a5e6bbSAlexander Aring if (err == NET_RX_DROP)
29572a5e6bbSAlexander Aring goto err;
29672a5e6bbSAlexander Aring }
29772a5e6bbSAlexander Aring
29872a5e6bbSAlexander Aring if (cb->d_size > IPV6_MIN_MTU) {
299ea81ac2eSAlexander Aring net_warn_ratelimited("lowpan_frag_rcv: datagram size exceeds MTU\n");
300ea81ac2eSAlexander Aring goto err;
301ea81ac2eSAlexander Aring }
302ea81ac2eSAlexander Aring
30372a5e6bbSAlexander Aring fq = fq_find(net, cb, &hdr.source, &hdr.dest);
304ea81ac2eSAlexander Aring if (fq != NULL) {
305ea81ac2eSAlexander Aring int ret;
306ea81ac2eSAlexander Aring
307ea81ac2eSAlexander Aring spin_lock(&fq->q.lock);
308ea81ac2eSAlexander Aring ret = lowpan_frag_queue(fq, skb, frag_type);
309ea81ac2eSAlexander Aring spin_unlock(&fq->q.lock);
310ea81ac2eSAlexander Aring
311093ba729SEric Dumazet inet_frag_put(&fq->q);
312ea81ac2eSAlexander Aring return ret;
313ea81ac2eSAlexander Aring }
314ea81ac2eSAlexander Aring
315ea81ac2eSAlexander Aring err:
316ea81ac2eSAlexander Aring kfree_skb(skb);
317ea81ac2eSAlexander Aring return -1;
318ea81ac2eSAlexander Aring }
319ea81ac2eSAlexander Aring
320ea81ac2eSAlexander Aring #ifdef CONFIG_SYSCTL
321ea81ac2eSAlexander Aring
322ea81ac2eSAlexander Aring static struct ctl_table lowpan_frags_ns_ctl_table[] = {
323ea81ac2eSAlexander Aring {
324ea81ac2eSAlexander Aring .procname = "6lowpanfrag_high_thresh",
3253e67f106SEric Dumazet .maxlen = sizeof(unsigned long),
326ea81ac2eSAlexander Aring .mode = 0644,
3273e67f106SEric Dumazet .proc_handler = proc_doulongvec_minmax,
328ea81ac2eSAlexander Aring },
329ea81ac2eSAlexander Aring {
330ea81ac2eSAlexander Aring .procname = "6lowpanfrag_low_thresh",
3313e67f106SEric Dumazet .maxlen = sizeof(unsigned long),
332ea81ac2eSAlexander Aring .mode = 0644,
3333e67f106SEric Dumazet .proc_handler = proc_doulongvec_minmax,
334ea81ac2eSAlexander Aring },
335ea81ac2eSAlexander Aring {
336ea81ac2eSAlexander Aring .procname = "6lowpanfrag_time",
337ea81ac2eSAlexander Aring .maxlen = sizeof(int),
338ea81ac2eSAlexander Aring .mode = 0644,
339ea81ac2eSAlexander Aring .proc_handler = proc_dointvec_jiffies,
340ea81ac2eSAlexander Aring },
341ea81ac2eSAlexander Aring { }
342ea81ac2eSAlexander Aring };
343ea81ac2eSAlexander Aring
344ea81ac2eSAlexander Aring /* secret interval has been deprecated */
345ea81ac2eSAlexander Aring static int lowpan_frags_secret_interval_unused;
346ea81ac2eSAlexander Aring static struct ctl_table lowpan_frags_ctl_table[] = {
347ea81ac2eSAlexander Aring {
348ea81ac2eSAlexander Aring .procname = "6lowpanfrag_secret_interval",
349ea81ac2eSAlexander Aring .data = &lowpan_frags_secret_interval_unused,
350ea81ac2eSAlexander Aring .maxlen = sizeof(int),
351ea81ac2eSAlexander Aring .mode = 0644,
352ea81ac2eSAlexander Aring .proc_handler = proc_dointvec_jiffies,
353ea81ac2eSAlexander Aring },
354ea81ac2eSAlexander Aring { }
355ea81ac2eSAlexander Aring };
356ea81ac2eSAlexander Aring
lowpan_frags_ns_sysctl_register(struct net * net)357ea81ac2eSAlexander Aring static int __net_init lowpan_frags_ns_sysctl_register(struct net *net)
358ea81ac2eSAlexander Aring {
359ea81ac2eSAlexander Aring struct ctl_table *table;
360ea81ac2eSAlexander Aring struct ctl_table_header *hdr;
361ea81ac2eSAlexander Aring struct netns_ieee802154_lowpan *ieee802154_lowpan =
362ea81ac2eSAlexander Aring net_ieee802154_lowpan(net);
363*c899710fSJoel Granados size_t table_size = ARRAY_SIZE(lowpan_frags_ns_ctl_table);
364ea81ac2eSAlexander Aring
365ea81ac2eSAlexander Aring table = lowpan_frags_ns_ctl_table;
366ea81ac2eSAlexander Aring if (!net_eq(net, &init_net)) {
367ea81ac2eSAlexander Aring table = kmemdup(table, sizeof(lowpan_frags_ns_ctl_table),
368ea81ac2eSAlexander Aring GFP_KERNEL);
369ea81ac2eSAlexander Aring if (table == NULL)
370ea81ac2eSAlexander Aring goto err_alloc;
371ea81ac2eSAlexander Aring
372d2dfd435SEric Dumazet /* Don't export sysctls to unprivileged users */
373*c899710fSJoel Granados if (net->user_ns != &init_user_ns) {
374d2dfd435SEric Dumazet table[0].procname = NULL;
375*c899710fSJoel Granados table_size = 0;
376*c899710fSJoel Granados }
377d2dfd435SEric Dumazet }
378d2dfd435SEric Dumazet
3794907abc6SEric Dumazet table[0].data = &ieee802154_lowpan->fqdir->high_thresh;
3804907abc6SEric Dumazet table[0].extra1 = &ieee802154_lowpan->fqdir->low_thresh;
3814907abc6SEric Dumazet table[1].data = &ieee802154_lowpan->fqdir->low_thresh;
3824907abc6SEric Dumazet table[1].extra2 = &ieee802154_lowpan->fqdir->high_thresh;
3834907abc6SEric Dumazet table[2].data = &ieee802154_lowpan->fqdir->timeout;
384ea81ac2eSAlexander Aring
385*c899710fSJoel Granados hdr = register_net_sysctl_sz(net, "net/ieee802154/6lowpan", table,
386*c899710fSJoel Granados table_size);
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
lowpan_frags_ns_sysctl_unregister(struct net * net)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
lowpan_frags_sysctl_register(void)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
lowpan_frags_sysctl_unregister(void)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
lowpan_frags_ns_sysctl_register(struct net * net)427ea81ac2eSAlexander Aring static inline int lowpan_frags_ns_sysctl_register(struct net *net)
428ea81ac2eSAlexander Aring {
429ea81ac2eSAlexander Aring return 0;
430ea81ac2eSAlexander Aring }
431ea81ac2eSAlexander Aring
lowpan_frags_ns_sysctl_unregister(struct net * net)432ea81ac2eSAlexander Aring static inline void lowpan_frags_ns_sysctl_unregister(struct net *net)
433ea81ac2eSAlexander Aring {
434ea81ac2eSAlexander Aring }
435ea81ac2eSAlexander Aring
lowpan_frags_sysctl_register(void)436ea81ac2eSAlexander Aring static inline int __init lowpan_frags_sysctl_register(void)
437ea81ac2eSAlexander Aring {
438ea81ac2eSAlexander Aring return 0;
439ea81ac2eSAlexander Aring }
440ea81ac2eSAlexander Aring
lowpan_frags_sysctl_unregister(void)441ea81ac2eSAlexander Aring static inline void lowpan_frags_sysctl_unregister(void)
442ea81ac2eSAlexander Aring {
443ea81ac2eSAlexander Aring }
444ea81ac2eSAlexander Aring #endif
445ea81ac2eSAlexander Aring
lowpan_frags_init_net(struct net * net)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
453a39aca67SEric Dumazet res = fqdir_init(&ieee802154_lowpan->fqdir, &lowpan_frags, net);
454787bea77SEric Dumazet if (res < 0)
455787bea77SEric Dumazet return res;
4564907abc6SEric Dumazet
4574907abc6SEric Dumazet ieee802154_lowpan->fqdir->high_thresh = IPV6_FRAG_HIGH_THRESH;
4584907abc6SEric Dumazet ieee802154_lowpan->fqdir->low_thresh = IPV6_FRAG_LOW_THRESH;
4594907abc6SEric Dumazet ieee802154_lowpan->fqdir->timeout = IPV6_FRAG_TIMEOUT;
4604907abc6SEric Dumazet
461787bea77SEric Dumazet res = lowpan_frags_ns_sysctl_register(net);
462787bea77SEric Dumazet if (res < 0)
4634907abc6SEric Dumazet fqdir_exit(ieee802154_lowpan->fqdir);
464787bea77SEric Dumazet return res;
465ea81ac2eSAlexander Aring }
466ea81ac2eSAlexander Aring
lowpan_frags_pre_exit_net(struct net * net)467d5dd8879SEric Dumazet static void __net_exit lowpan_frags_pre_exit_net(struct net *net)
468d5dd8879SEric Dumazet {
469d5dd8879SEric Dumazet struct netns_ieee802154_lowpan *ieee802154_lowpan =
470d5dd8879SEric Dumazet net_ieee802154_lowpan(net);
471d5dd8879SEric Dumazet
472d5dd8879SEric Dumazet fqdir_pre_exit(ieee802154_lowpan->fqdir);
473d5dd8879SEric Dumazet }
474d5dd8879SEric Dumazet
lowpan_frags_exit_net(struct net * net)475ea81ac2eSAlexander Aring static void __net_exit lowpan_frags_exit_net(struct net *net)
476ea81ac2eSAlexander Aring {
477ea81ac2eSAlexander Aring struct netns_ieee802154_lowpan *ieee802154_lowpan =
478ea81ac2eSAlexander Aring net_ieee802154_lowpan(net);
479ea81ac2eSAlexander Aring
480ea81ac2eSAlexander Aring lowpan_frags_ns_sysctl_unregister(net);
4814907abc6SEric Dumazet fqdir_exit(ieee802154_lowpan->fqdir);
482ea81ac2eSAlexander Aring }
483ea81ac2eSAlexander Aring
484ea81ac2eSAlexander Aring static struct pernet_operations lowpan_frags_ops = {
485ea81ac2eSAlexander Aring .init = lowpan_frags_init_net,
486d5dd8879SEric Dumazet .pre_exit = lowpan_frags_pre_exit_net,
487ea81ac2eSAlexander Aring .exit = lowpan_frags_exit_net,
488ea81ac2eSAlexander Aring };
489ea81ac2eSAlexander Aring
lowpan_key_hashfn(const void * data,u32 len,u32 seed)490648700f7SEric Dumazet static u32 lowpan_key_hashfn(const void *data, u32 len, u32 seed)
491648700f7SEric Dumazet {
492648700f7SEric Dumazet return jhash2(data,
493648700f7SEric Dumazet sizeof(struct frag_lowpan_compare_key) / sizeof(u32), seed);
494648700f7SEric Dumazet }
495648700f7SEric Dumazet
lowpan_obj_hashfn(const void * data,u32 len,u32 seed)496648700f7SEric Dumazet static u32 lowpan_obj_hashfn(const void *data, u32 len, u32 seed)
497648700f7SEric Dumazet {
498648700f7SEric Dumazet const struct inet_frag_queue *fq = data;
499648700f7SEric Dumazet
500648700f7SEric Dumazet return jhash2((const u32 *)&fq->key,
501648700f7SEric Dumazet sizeof(struct frag_lowpan_compare_key) / sizeof(u32), seed);
502648700f7SEric Dumazet }
503648700f7SEric Dumazet
lowpan_obj_cmpfn(struct rhashtable_compare_arg * arg,const void * ptr)504648700f7SEric Dumazet static int lowpan_obj_cmpfn(struct rhashtable_compare_arg *arg, const void *ptr)
505648700f7SEric Dumazet {
506648700f7SEric Dumazet const struct frag_lowpan_compare_key *key = arg->key;
507648700f7SEric Dumazet const struct inet_frag_queue *fq = ptr;
508648700f7SEric Dumazet
509648700f7SEric Dumazet return !!memcmp(&fq->key, key, sizeof(*key));
510648700f7SEric Dumazet }
511648700f7SEric Dumazet
512648700f7SEric Dumazet static const struct rhashtable_params lowpan_rhash_params = {
513648700f7SEric Dumazet .head_offset = offsetof(struct inet_frag_queue, node),
514648700f7SEric Dumazet .hashfn = lowpan_key_hashfn,
515648700f7SEric Dumazet .obj_hashfn = lowpan_obj_hashfn,
516648700f7SEric Dumazet .obj_cmpfn = lowpan_obj_cmpfn,
517648700f7SEric Dumazet .automatic_shrinking = true,
518648700f7SEric Dumazet };
519648700f7SEric Dumazet
lowpan_net_frag_init(void)520ea81ac2eSAlexander Aring int __init lowpan_net_frag_init(void)
521ea81ac2eSAlexander Aring {
522ea81ac2eSAlexander Aring int ret;
523ea81ac2eSAlexander Aring
524ea81ac2eSAlexander Aring lowpan_frags.constructor = lowpan_frag_init;
525ea81ac2eSAlexander Aring lowpan_frags.destructor = NULL;
526ea81ac2eSAlexander Aring lowpan_frags.qsize = sizeof(struct frag_queue);
527ea81ac2eSAlexander Aring lowpan_frags.frag_expire = lowpan_frag_expire;
528ea81ac2eSAlexander Aring lowpan_frags.frags_cache_name = lowpan_frags_cache_name;
529648700f7SEric Dumazet lowpan_frags.rhash_params = lowpan_rhash_params;
530ea81ac2eSAlexander Aring ret = inet_frags_init(&lowpan_frags);
531ea81ac2eSAlexander Aring if (ret)
532807f1844SEric Dumazet goto out;
533ea81ac2eSAlexander Aring
534807f1844SEric Dumazet ret = lowpan_frags_sysctl_register();
535807f1844SEric Dumazet if (ret)
536807f1844SEric Dumazet goto err_sysctl;
537807f1844SEric Dumazet
538807f1844SEric Dumazet ret = register_pernet_subsys(&lowpan_frags_ops);
539807f1844SEric Dumazet if (ret)
540807f1844SEric Dumazet goto err_pernet;
541807f1844SEric Dumazet out:
542ea81ac2eSAlexander Aring return ret;
543ea81ac2eSAlexander Aring err_pernet:
544ea81ac2eSAlexander Aring lowpan_frags_sysctl_unregister();
545807f1844SEric Dumazet err_sysctl:
546807f1844SEric Dumazet inet_frags_fini(&lowpan_frags);
547ea81ac2eSAlexander Aring return ret;
548ea81ac2eSAlexander Aring }
549ea81ac2eSAlexander Aring
lowpan_net_frag_exit(void)550ea81ac2eSAlexander Aring void lowpan_net_frag_exit(void)
551ea81ac2eSAlexander Aring {
552ea81ac2eSAlexander Aring lowpan_frags_sysctl_unregister();
553ea81ac2eSAlexander Aring unregister_pernet_subsys(&lowpan_frags_ops);
554ae7352d3SEric Dumazet inet_frags_fini(&lowpan_frags);
555ea81ac2eSAlexander Aring }
556