hc
2024-05-10 37f49e37ab4cb5d0bc4c60eb5c6d4dd57db767bb
kernel/net/ieee802154/6lowpan/reassembly.c
....@@ -1,15 +1,10 @@
1
+// SPDX-License-Identifier: GPL-2.0-or-later
12 /* 6LoWPAN fragment reassembly
2
- *
33 *
44 * Authors:
55 * Alexander Aring <aar@pengutronix.de>
66 *
77 * Based on: net/ipv6/reassembly.c
8
- *
9
- * This program is free software; you can redistribute it and/or
10
- * modify it under the terms of the GNU General Public License
11
- * as published by the Free Software Foundation; either version
12
- * 2 of the License, or (at your option) any later version.
138 */
149
1510 #define pr_fmt(fmt) "6LoWPAN: " fmt
....@@ -27,6 +22,7 @@
2722 #include <net/6lowpan.h>
2823 #include <net/ipv6_frag.h>
2924 #include <net/inet_frag.h>
25
+#include <net/ip.h>
3026
3127 #include "6lowpan_i.h"
3228
....@@ -34,8 +30,8 @@
3430
3531 static struct inet_frags lowpan_frags;
3632
37
-static int lowpan_frag_reasm(struct lowpan_frag_queue *fq,
38
- struct sk_buff *prev, struct net_device *ldev);
33
+static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *skb,
34
+ struct sk_buff *prev, struct net_device *ldev);
3935
4036 static void lowpan_frag_init(struct inet_frag_queue *q, const void *a)
4137 {
....@@ -78,7 +74,7 @@
7874 key.src = *src;
7975 key.dst = *dst;
8076
81
- q = inet_frag_find(&ieee802154_lowpan->frags, &key);
77
+ q = inet_frag_find(ieee802154_lowpan->fqdir, &key);
8278 if (!q)
8379 return NULL;
8480
....@@ -88,9 +84,15 @@
8884 static int lowpan_frag_queue(struct lowpan_frag_queue *fq,
8985 struct sk_buff *skb, u8 frag_type)
9086 {
91
- struct sk_buff *prev, *next;
87
+ struct sk_buff *prev_tail;
9288 struct net_device *ldev;
93
- int end, offset;
89
+ int end, offset, err;
90
+
91
+ /* inet_frag_queue_* functions use skb->cb; see struct ipfrag_skb_cb
92
+ * in inet_fragment.c
93
+ */
94
+ BUILD_BUG_ON(sizeof(struct lowpan_802154_cb) > sizeof(struct inet_skb_parm));
95
+ BUILD_BUG_ON(sizeof(struct lowpan_802154_cb) > sizeof(struct inet6_skb_parm));
9496
9597 if (fq->q.flags & INET_FRAG_COMPLETE)
9698 goto err;
....@@ -117,45 +119,22 @@
117119 }
118120 }
119121
120
- /* Find out which fragments are in front and at the back of us
121
- * in the chain of fragments so far. We must know where to put
122
- * this fragment, right?
123
- */
124
- prev = fq->q.fragments_tail;
125
- if (!prev ||
126
- lowpan_802154_cb(prev)->d_offset <
127
- lowpan_802154_cb(skb)->d_offset) {
128
- next = NULL;
129
- goto found;
130
- }
131
- prev = NULL;
132
- for (next = fq->q.fragments; next != NULL; next = next->next) {
133
- if (lowpan_802154_cb(next)->d_offset >=
134
- lowpan_802154_cb(skb)->d_offset)
135
- break; /* bingo! */
136
- prev = next;
137
- }
138
-
139
-found:
140
- /* Insert this fragment in the chain of fragments. */
141
- skb->next = next;
142
- if (!next)
143
- fq->q.fragments_tail = skb;
144
- if (prev)
145
- prev->next = skb;
146
- else
147
- fq->q.fragments = skb;
148
-
149122 ldev = skb->dev;
150123 if (ldev)
151124 skb->dev = NULL;
125
+ barrier();
126
+
127
+ prev_tail = fq->q.fragments_tail;
128
+ err = inet_frag_queue_insert(&fq->q, skb, offset, end);
129
+ if (err)
130
+ goto err;
152131
153132 fq->q.stamp = skb->tstamp;
154133 if (frag_type == LOWPAN_DISPATCH_FRAG1)
155134 fq->q.flags |= INET_FRAG_FIRST_IN;
156135
157136 fq->q.meat += skb->len;
158
- add_frag_mem_limit(fq->q.net, skb->truesize);
137
+ add_frag_mem_limit(fq->q.fqdir, skb->truesize);
159138
160139 if (fq->q.flags == (INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN) &&
161140 fq->q.meat == fq->q.len) {
....@@ -163,10 +142,11 @@
163142 unsigned long orefdst = skb->_skb_refdst;
164143
165144 skb->_skb_refdst = 0UL;
166
- res = lowpan_frag_reasm(fq, prev, ldev);
145
+ res = lowpan_frag_reasm(fq, skb, prev_tail, ldev);
167146 skb->_skb_refdst = orefdst;
168147 return res;
169148 }
149
+ skb_dst_drop(skb);
170150
171151 return -1;
172152 err:
....@@ -175,97 +155,28 @@
175155 }
176156
177157 /* Check if this packet is complete.
178
- * Returns NULL on failure by any reason, and pointer
179
- * to current nexthdr field in reassembled frame.
180158 *
181159 * It is called with locked fq, and caller must check that
182160 * queue is eligible for reassembly i.e. it is not COMPLETE,
183161 * the last and the first frames arrived and all the bits are here.
184162 */
185
-static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *prev,
186
- struct net_device *ldev)
163
+static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *skb,
164
+ struct sk_buff *prev_tail, struct net_device *ldev)
187165 {
188
- struct sk_buff *fp, *head = fq->q.fragments;
189
- int sum_truesize;
166
+ void *reasm_data;
190167
191168 inet_frag_kill(&fq->q);
192169
193
- /* Make the one we just received the head. */
194
- if (prev) {
195
- head = prev->next;
196
- fp = skb_clone(head, GFP_ATOMIC);
197
-
198
- if (!fp)
199
- goto out_oom;
200
-
201
- fp->next = head->next;
202
- if (!fp->next)
203
- fq->q.fragments_tail = fp;
204
- prev->next = fp;
205
-
206
- skb_morph(head, fq->q.fragments);
207
- head->next = fq->q.fragments->next;
208
-
209
- consume_skb(fq->q.fragments);
210
- fq->q.fragments = head;
211
- }
212
-
213
- /* Head of list must not be cloned. */
214
- if (skb_unclone(head, GFP_ATOMIC))
170
+ reasm_data = inet_frag_reasm_prepare(&fq->q, skb, prev_tail);
171
+ if (!reasm_data)
215172 goto out_oom;
173
+ inet_frag_reasm_finish(&fq->q, skb, reasm_data, false);
216174
217
- /* If the first fragment is fragmented itself, we split
218
- * it to two chunks: the first with data and paged part
219
- * and the second, holding only fragments.
220
- */
221
- if (skb_has_frag_list(head)) {
222
- struct sk_buff *clone;
223
- int i, plen = 0;
224
-
225
- clone = alloc_skb(0, GFP_ATOMIC);
226
- if (!clone)
227
- goto out_oom;
228
- clone->next = head->next;
229
- head->next = clone;
230
- skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
231
- skb_frag_list_init(head);
232
- for (i = 0; i < skb_shinfo(head)->nr_frags; i++)
233
- plen += skb_frag_size(&skb_shinfo(head)->frags[i]);
234
- clone->len = head->data_len - plen;
235
- clone->data_len = clone->len;
236
- head->data_len -= clone->len;
237
- head->len -= clone->len;
238
- add_frag_mem_limit(fq->q.net, clone->truesize);
239
- }
240
-
241
- WARN_ON(head == NULL);
242
-
243
- sum_truesize = head->truesize;
244
- for (fp = head->next; fp;) {
245
- bool headstolen;
246
- int delta;
247
- struct sk_buff *next = fp->next;
248
-
249
- sum_truesize += fp->truesize;
250
- if (skb_try_coalesce(head, fp, &headstolen, &delta)) {
251
- kfree_skb_partial(fp, headstolen);
252
- } else {
253
- if (!skb_shinfo(head)->frag_list)
254
- skb_shinfo(head)->frag_list = fp;
255
- head->data_len += fp->len;
256
- head->len += fp->len;
257
- head->truesize += fp->truesize;
258
- }
259
- fp = next;
260
- }
261
- sub_frag_mem_limit(fq->q.net, sum_truesize);
262
-
263
- head->next = NULL;
264
- head->dev = ldev;
265
- head->tstamp = fq->q.stamp;
266
-
267
- fq->q.fragments = NULL;
175
+ skb->dev = ldev;
176
+ skb->tstamp = fq->q.stamp;
177
+ fq->q.rb_fragments = RB_ROOT;
268178 fq->q.fragments_tail = NULL;
179
+ fq->q.last_run_head = NULL;
269180
270181 return 1;
271182 out_oom:
....@@ -284,7 +195,7 @@
284195 net_warn_ratelimited("%s: received unknown dispatch\n",
285196 __func__);
286197
287
- /* fall-through */
198
+ fallthrough;
288199 default:
289200 /* all others failure */
290201 return NET_RX_DROP;
....@@ -410,23 +321,18 @@
410321 static struct ctl_table lowpan_frags_ns_ctl_table[] = {
411322 {
412323 .procname = "6lowpanfrag_high_thresh",
413
- .data = &init_net.ieee802154_lowpan.frags.high_thresh,
414324 .maxlen = sizeof(unsigned long),
415325 .mode = 0644,
416326 .proc_handler = proc_doulongvec_minmax,
417
- .extra1 = &init_net.ieee802154_lowpan.frags.low_thresh
418327 },
419328 {
420329 .procname = "6lowpanfrag_low_thresh",
421
- .data = &init_net.ieee802154_lowpan.frags.low_thresh,
422330 .maxlen = sizeof(unsigned long),
423331 .mode = 0644,
424332 .proc_handler = proc_doulongvec_minmax,
425
- .extra2 = &init_net.ieee802154_lowpan.frags.high_thresh
426333 },
427334 {
428335 .procname = "6lowpanfrag_time",
429
- .data = &init_net.ieee802154_lowpan.frags.timeout,
430336 .maxlen = sizeof(int),
431337 .mode = 0644,
432338 .proc_handler = proc_dointvec_jiffies,
....@@ -461,17 +367,16 @@
461367 if (table == NULL)
462368 goto err_alloc;
463369
464
- table[0].data = &ieee802154_lowpan->frags.high_thresh;
465
- table[0].extra1 = &ieee802154_lowpan->frags.low_thresh;
466
- table[0].extra2 = &init_net.ieee802154_lowpan.frags.high_thresh;
467
- table[1].data = &ieee802154_lowpan->frags.low_thresh;
468
- table[1].extra2 = &ieee802154_lowpan->frags.high_thresh;
469
- table[2].data = &ieee802154_lowpan->frags.timeout;
470
-
471370 /* Don't export sysctls to unprivileged users */
472371 if (net->user_ns != &init_user_ns)
473372 table[0].procname = NULL;
474373 }
374
+
375
+ table[0].data = &ieee802154_lowpan->fqdir->high_thresh;
376
+ table[0].extra1 = &ieee802154_lowpan->fqdir->low_thresh;
377
+ table[1].data = &ieee802154_lowpan->fqdir->low_thresh;
378
+ table[1].extra2 = &ieee802154_lowpan->fqdir->high_thresh;
379
+ table[2].data = &ieee802154_lowpan->fqdir->timeout;
475380
476381 hdr = register_net_sysctl(net, "net/ieee802154/6lowpan", table);
477382 if (hdr == NULL)
....@@ -539,18 +444,27 @@
539444 net_ieee802154_lowpan(net);
540445 int res;
541446
542
- ieee802154_lowpan->frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
543
- ieee802154_lowpan->frags.low_thresh = IPV6_FRAG_LOW_THRESH;
544
- ieee802154_lowpan->frags.timeout = IPV6_FRAG_TIMEOUT;
545
- ieee802154_lowpan->frags.f = &lowpan_frags;
546447
547
- res = inet_frags_init_net(&ieee802154_lowpan->frags);
448
+ res = fqdir_init(&ieee802154_lowpan->fqdir, &lowpan_frags, net);
548449 if (res < 0)
549450 return res;
451
+
452
+ ieee802154_lowpan->fqdir->high_thresh = IPV6_FRAG_HIGH_THRESH;
453
+ ieee802154_lowpan->fqdir->low_thresh = IPV6_FRAG_LOW_THRESH;
454
+ ieee802154_lowpan->fqdir->timeout = IPV6_FRAG_TIMEOUT;
455
+
550456 res = lowpan_frags_ns_sysctl_register(net);
551457 if (res < 0)
552
- inet_frags_exit_net(&ieee802154_lowpan->frags);
458
+ fqdir_exit(ieee802154_lowpan->fqdir);
553459 return res;
460
+}
461
+
462
+static void __net_exit lowpan_frags_pre_exit_net(struct net *net)
463
+{
464
+ struct netns_ieee802154_lowpan *ieee802154_lowpan =
465
+ net_ieee802154_lowpan(net);
466
+
467
+ fqdir_pre_exit(ieee802154_lowpan->fqdir);
554468 }
555469
556470 static void __net_exit lowpan_frags_exit_net(struct net *net)
....@@ -559,12 +473,13 @@
559473 net_ieee802154_lowpan(net);
560474
561475 lowpan_frags_ns_sysctl_unregister(net);
562
- inet_frags_exit_net(&ieee802154_lowpan->frags);
476
+ fqdir_exit(ieee802154_lowpan->fqdir);
563477 }
564478
565479 static struct pernet_operations lowpan_frags_ops = {
566
- .init = lowpan_frags_init_net,
567
- .exit = lowpan_frags_exit_net,
480
+ .init = lowpan_frags_init_net,
481
+ .pre_exit = lowpan_frags_pre_exit_net,
482
+ .exit = lowpan_frags_exit_net,
568483 };
569484
570485 static u32 lowpan_key_hashfn(const void *data, u32 len, u32 seed)