hc
2024-01-03 2f7c68cb55ecb7331f2381deb497c27155f32faf
kernel/net/netfilter/nft_exthdr.c
....@@ -10,8 +10,10 @@
1010 #include <linux/netlink.h>
1111 #include <linux/netfilter.h>
1212 #include <linux/netfilter/nf_tables.h>
13
+#include <linux/sctp.h>
1314 #include <net/netfilter/nf_tables_core.h>
1415 #include <net/netfilter/nf_tables.h>
16
+#include <net/sctp/sctp.h>
1517 #include <net/tcp.h>
1618
1719 struct nft_exthdr {
....@@ -31,6 +33,14 @@
3133 return 1;
3234 else
3335 return opt[offset + 1];
36
+}
37
+
38
+static int nft_skb_copy_to_reg(const struct sk_buff *skb, int offset, u32 *dest, unsigned int len)
39
+{
40
+ if (len % NFT_REG32_SIZE)
41
+ dest[len / NFT_REG32_SIZE] = 0;
42
+
43
+ return skb_copy_bits(skb, offset, dest, len);
3444 }
3545
3646 static void nft_exthdr_ipv6_eval(const struct nft_expr *expr,
....@@ -54,8 +64,7 @@
5464 }
5565 offset += priv->offset;
5666
57
- dest[priv->len / NFT_REG32_SIZE] = 0;
58
- if (skb_copy_bits(pkt->skb, offset, dest, priv->len) < 0)
67
+ if (nft_skb_copy_to_reg(pkt->skb, offset, dest, priv->len) < 0)
5968 goto err;
6069 return;
6170 err:
....@@ -151,8 +160,7 @@
151160 }
152161 offset += priv->offset;
153162
154
- dest[priv->len / NFT_REG32_SIZE] = 0;
155
- if (skb_copy_bits(pkt->skb, offset, dest, priv->len) < 0)
163
+ if (nft_skb_copy_to_reg(pkt->skb, offset, dest, priv->len) < 0)
156164 goto err;
157165 return;
158166 err:
....@@ -168,7 +176,7 @@
168176 if (!pkt->tprot_set || pkt->tprot != IPPROTO_TCP)
169177 return NULL;
170178
171
- tcph = skb_header_pointer(pkt->skb, pkt->xt.thoff, sizeof(*tcph), buffer);
179
+ tcph = skb_header_pointer(pkt->skb, nft_thoff(pkt), sizeof(*tcph), buffer);
172180 if (!tcph)
173181 return NULL;
174182
....@@ -176,7 +184,7 @@
176184 if (*tcphdr_len < sizeof(*tcph) || *tcphdr_len > len)
177185 return NULL;
178186
179
- return skb_header_pointer(pkt->skb, pkt->xt.thoff, *tcphdr_len, buffer);
187
+ return skb_header_pointer(pkt->skb, nft_thoff(pkt), *tcphdr_len, buffer);
180188 }
181189
182190 static void nft_exthdr_tcp_eval(const struct nft_expr *expr,
....@@ -208,7 +216,8 @@
208216 if (priv->flags & NFT_EXTHDR_F_PRESENT) {
209217 *dest = 1;
210218 } else {
211
- dest[priv->len / NFT_REG32_SIZE] = 0;
219
+ if (priv->len % NFT_REG32_SIZE)
220
+ dest[priv->len / NFT_REG32_SIZE] = 0;
212221 memcpy(dest, opt + offset, priv->len);
213222 }
214223
....@@ -234,9 +243,14 @@
234243
235244 tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len);
236245 if (!tcph)
237
- return;
246
+ goto err;
238247
248
+ if (skb_ensure_writable(pkt->skb, nft_thoff(pkt) + tcphdr_len))
249
+ goto err;
250
+
251
+ tcph = (struct tcphdr *)(pkt->skb->data + nft_thoff(pkt));
239252 opt = (u8 *)tcph;
253
+
240254 for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) {
241255 union {
242256 __be16 v16;
....@@ -249,16 +263,7 @@
249263 continue;
250264
251265 if (i + optl > tcphdr_len || priv->len + priv->offset > optl)
252
- return;
253
-
254
- if (skb_ensure_writable(pkt->skb,
255
- pkt->xt.thoff + i + priv->len))
256
- return;
257
-
258
- tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff,
259
- &tcphdr_len);
260
- if (!tcph)
261
- return;
266
+ goto err;
262267
263268 offset = i + priv->offset;
264269
....@@ -301,6 +306,107 @@
301306
302307 return;
303308 }
309
+ return;
310
+err:
311
+ regs->verdict.code = NFT_BREAK;
312
+}
313
+
314
+static void nft_exthdr_tcp_strip_eval(const struct nft_expr *expr,
315
+ struct nft_regs *regs,
316
+ const struct nft_pktinfo *pkt)
317
+{
318
+ u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE];
319
+ struct nft_exthdr *priv = nft_expr_priv(expr);
320
+ unsigned int i, tcphdr_len, optl;
321
+ struct tcphdr *tcph;
322
+ u8 *opt;
323
+
324
+ tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len);
325
+ if (!tcph)
326
+ goto err;
327
+
328
+ if (skb_ensure_writable(pkt->skb, nft_thoff(pkt) + tcphdr_len))
329
+ goto drop;
330
+
331
+ tcph = (struct tcphdr *)(pkt->skb->data + nft_thoff(pkt));
332
+ opt = (u8 *)tcph;
333
+
334
+ for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) {
335
+ unsigned int j;
336
+
337
+ optl = optlen(opt, i);
338
+ if (priv->type != opt[i])
339
+ continue;
340
+
341
+ if (i + optl > tcphdr_len)
342
+ goto drop;
343
+
344
+ for (j = 0; j < optl; ++j) {
345
+ u16 n = TCPOPT_NOP;
346
+ u16 o = opt[i+j];
347
+
348
+ if ((i + j) % 2 == 0) {
349
+ o <<= 8;
350
+ n <<= 8;
351
+ }
352
+ inet_proto_csum_replace2(&tcph->check, pkt->skb, htons(o),
353
+ htons(n), false);
354
+ }
355
+ memset(opt + i, TCPOPT_NOP, optl);
356
+ return;
357
+ }
358
+
359
+ /* option not found, continue. This allows to do multiple
360
+ * option removals per rule.
361
+ */
362
+ return;
363
+err:
364
+ regs->verdict.code = NFT_BREAK;
365
+ return;
366
+drop:
367
+ /* can't remove, no choice but to drop */
368
+ regs->verdict.code = NF_DROP;
369
+}
370
+
371
+static void nft_exthdr_sctp_eval(const struct nft_expr *expr,
372
+ struct nft_regs *regs,
373
+ const struct nft_pktinfo *pkt)
374
+{
375
+ unsigned int offset = nft_thoff(pkt) + sizeof(struct sctphdr);
376
+ struct nft_exthdr *priv = nft_expr_priv(expr);
377
+ u32 *dest = &regs->data[priv->dreg];
378
+ const struct sctp_chunkhdr *sch;
379
+ struct sctp_chunkhdr _sch;
380
+
381
+ if (pkt->tprot != IPPROTO_SCTP)
382
+ goto err;
383
+
384
+ do {
385
+ sch = skb_header_pointer(pkt->skb, offset, sizeof(_sch), &_sch);
386
+ if (!sch || !sch->length)
387
+ break;
388
+
389
+ if (sch->type == priv->type) {
390
+ if (priv->flags & NFT_EXTHDR_F_PRESENT) {
391
+ nft_reg_store8(dest, true);
392
+ return;
393
+ }
394
+ if (priv->offset + priv->len > ntohs(sch->length) ||
395
+ offset + ntohs(sch->length) > pkt->skb->len)
396
+ break;
397
+
398
+ if (nft_skb_copy_to_reg(pkt->skb, offset + priv->offset,
399
+ dest, priv->len) < 0)
400
+ break;
401
+ return;
402
+ }
403
+ offset += SCTP_PAD4(ntohs(sch->length));
404
+ } while (offset < pkt->skb->len);
405
+err:
406
+ if (priv->flags & NFT_EXTHDR_F_PRESENT)
407
+ nft_reg_store8(dest, false);
408
+ else
409
+ regs->verdict.code = NFT_BREAK;
304410 }
305411
306412 static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = {
....@@ -410,6 +516,28 @@
410516 priv->len);
411517 }
412518
519
+static int nft_exthdr_tcp_strip_init(const struct nft_ctx *ctx,
520
+ const struct nft_expr *expr,
521
+ const struct nlattr * const tb[])
522
+{
523
+ struct nft_exthdr *priv = nft_expr_priv(expr);
524
+
525
+ if (tb[NFTA_EXTHDR_SREG] ||
526
+ tb[NFTA_EXTHDR_DREG] ||
527
+ tb[NFTA_EXTHDR_FLAGS] ||
528
+ tb[NFTA_EXTHDR_OFFSET] ||
529
+ tb[NFTA_EXTHDR_LEN])
530
+ return -EINVAL;
531
+
532
+ if (!tb[NFTA_EXTHDR_TYPE])
533
+ return -EINVAL;
534
+
535
+ priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
536
+ priv->op = NFT_EXTHDR_OP_TCPOPT;
537
+
538
+ return 0;
539
+}
540
+
413541 static int nft_exthdr_ipv4_init(const struct nft_ctx *ctx,
414542 const struct nft_expr *expr,
415543 const struct nlattr * const tb[])
....@@ -470,6 +598,13 @@
470598 return nft_exthdr_dump_common(skb, priv);
471599 }
472600
601
+static int nft_exthdr_dump_strip(struct sk_buff *skb, const struct nft_expr *expr)
602
+{
603
+ const struct nft_exthdr *priv = nft_expr_priv(expr);
604
+
605
+ return nft_exthdr_dump_common(skb, priv);
606
+}
607
+
473608 static const struct nft_expr_ops nft_exthdr_ipv6_ops = {
474609 .type = &nft_exthdr_type,
475610 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
....@@ -502,6 +637,22 @@
502637 .dump = nft_exthdr_dump_set,
503638 };
504639
640
+static const struct nft_expr_ops nft_exthdr_tcp_strip_ops = {
641
+ .type = &nft_exthdr_type,
642
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
643
+ .eval = nft_exthdr_tcp_strip_eval,
644
+ .init = nft_exthdr_tcp_strip_init,
645
+ .dump = nft_exthdr_dump_strip,
646
+};
647
+
648
+static const struct nft_expr_ops nft_exthdr_sctp_ops = {
649
+ .type = &nft_exthdr_type,
650
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
651
+ .eval = nft_exthdr_sctp_eval,
652
+ .init = nft_exthdr_init,
653
+ .dump = nft_exthdr_dump,
654
+};
655
+
505656 static const struct nft_expr_ops *
506657 nft_exthdr_select_ops(const struct nft_ctx *ctx,
507658 const struct nlattr * const tb[])
....@@ -521,7 +672,7 @@
521672 return &nft_exthdr_tcp_set_ops;
522673 if (tb[NFTA_EXTHDR_DREG])
523674 return &nft_exthdr_tcp_ops;
524
- break;
675
+ return &nft_exthdr_tcp_strip_ops;
525676 case NFT_EXTHDR_OP_IPV6:
526677 if (tb[NFTA_EXTHDR_DREG])
527678 return &nft_exthdr_ipv6_ops;
....@@ -532,6 +683,10 @@
532683 return &nft_exthdr_ipv4_ops;
533684 }
534685 break;
686
+ case NFT_EXTHDR_OP_SCTP:
687
+ if (tb[NFTA_EXTHDR_DREG])
688
+ return &nft_exthdr_sctp_ops;
689
+ break;
535690 }
536691
537692 return ERR_PTR(-EOPNOTSUPP);