|
10 | 10 | #include <linux/netlink.h>
|
11 | 11 | #include <linux/netfilter.h>
|
12 | 12 | #include <linux/netfilter/nf_tables.h>
|
| 13 | +#include <linux/dccp.h> |
13 | 14 | #include <linux/sctp.h>
|
14 | 15 | #include <net/netfilter/nf_tables_core.h>
|
15 | 16 | #include <net/netfilter/nf_tables.h>
|
@@ -406,6 +407,82 @@ static void nft_exthdr_sctp_eval(const struct nft_expr *expr,
|
406 | 407 | regs->verdict.code = NFT_BREAK;
|
407 | 408 | }
|
408 | 409 |
|
| 410 | +static void nft_exthdr_dccp_eval(const struct nft_expr *expr, |
| 411 | + struct nft_regs *regs, |
| 412 | + const struct nft_pktinfo *pkt) |
| 413 | +{ |
| 414 | + struct nft_exthdr *priv = nft_expr_priv(expr); |
| 415 | + unsigned int thoff, dataoff, optoff, optlen, i; |
| 416 | + u32 *dest = ®s->data[priv->dreg]; |
| 417 | + const struct dccp_hdr *dh; |
| 418 | + struct dccp_hdr _dh; |
| 419 | + |
| 420 | + if (pkt->tprot != IPPROTO_DCCP || pkt->fragoff) |
| 421 | + goto err; |
| 422 | + |
| 423 | + thoff = nft_thoff(pkt); |
| 424 | + |
| 425 | + dh = skb_header_pointer(pkt->skb, thoff, sizeof(_dh), &_dh); |
| 426 | + if (!dh) |
| 427 | + goto err; |
| 428 | + |
| 429 | + dataoff = dh->dccph_doff * sizeof(u32); |
| 430 | + optoff = __dccp_hdr_len(dh); |
| 431 | + if (dataoff <= optoff) |
| 432 | + goto err; |
| 433 | + |
| 434 | + optlen = dataoff - optoff; |
| 435 | + |
| 436 | + for (i = 0; i < optlen; ) { |
| 437 | + /* Options 0 (DCCPO_PADDING) - 31 (DCCPO_MAX_RESERVED) are 1B in |
| 438 | + * the length; the remaining options are at least 2B long. In |
| 439 | + * all cases, the first byte contains the option type. In |
| 440 | + * multi-byte options, the second byte contains the option |
| 441 | + * length, which must be at least two: 1 for the type plus 1 for |
| 442 | + * the length plus 0-253 for any following option data. We |
| 443 | + * aren't interested in the option data, only the type and the |
| 444 | + * length, so we don't need to read more than two bytes at a |
| 445 | + * time. |
| 446 | + */ |
| 447 | + unsigned int buflen = optlen - i; |
| 448 | + u8 buf[2], *bufp; |
| 449 | + u8 type, len; |
| 450 | + |
| 451 | + if (buflen > sizeof(buf)) |
| 452 | + buflen = sizeof(buf); |
| 453 | + |
| 454 | + bufp = skb_header_pointer(pkt->skb, thoff + optoff + i, buflen, |
| 455 | + &buf); |
| 456 | + if (!bufp) |
| 457 | + goto err; |
| 458 | + |
| 459 | + type = bufp[0]; |
| 460 | + |
| 461 | + if (type == priv->type) { |
| 462 | + *dest = 1; |
| 463 | + return; |
| 464 | + } |
| 465 | + |
| 466 | + if (type <= DCCPO_MAX_RESERVED) { |
| 467 | + i++; |
| 468 | + continue; |
| 469 | + } |
| 470 | + |
| 471 | + if (buflen < 2) |
| 472 | + goto err; |
| 473 | + |
| 474 | + len = bufp[1]; |
| 475 | + |
| 476 | + if (len < 2) |
| 477 | + goto err; |
| 478 | + |
| 479 | + i += len; |
| 480 | + } |
| 481 | + |
| 482 | +err: |
| 483 | + *dest = 0; |
| 484 | +} |
| 485 | + |
409 | 486 | static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = {
|
410 | 487 | [NFTA_EXTHDR_DREG] = { .type = NLA_U32 },
|
411 | 488 | [NFTA_EXTHDR_TYPE] = { .type = NLA_U8 },
|
@@ -557,6 +634,22 @@ static int nft_exthdr_ipv4_init(const struct nft_ctx *ctx,
|
557 | 634 | return 0;
|
558 | 635 | }
|
559 | 636 |
|
| 637 | +static int nft_exthdr_dccp_init(const struct nft_ctx *ctx, |
| 638 | + const struct nft_expr *expr, |
| 639 | + const struct nlattr * const tb[]) |
| 640 | +{ |
| 641 | + struct nft_exthdr *priv = nft_expr_priv(expr); |
| 642 | + int err = nft_exthdr_init(ctx, expr, tb); |
| 643 | + |
| 644 | + if (err < 0) |
| 645 | + return err; |
| 646 | + |
| 647 | + if (!(priv->flags & NFT_EXTHDR_F_PRESENT)) |
| 648 | + return -EOPNOTSUPP; |
| 649 | + |
| 650 | + return 0; |
| 651 | +} |
| 652 | + |
560 | 653 | static int nft_exthdr_dump_common(struct sk_buff *skb, const struct nft_exthdr *priv)
|
561 | 654 | {
|
562 | 655 | if (nla_put_u8(skb, NFTA_EXTHDR_TYPE, priv->type))
|
@@ -686,6 +779,15 @@ static const struct nft_expr_ops nft_exthdr_sctp_ops = {
|
686 | 779 | .reduce = nft_exthdr_reduce,
|
687 | 780 | };
|
688 | 781 |
|
| 782 | +static const struct nft_expr_ops nft_exthdr_dccp_ops = { |
| 783 | + .type = &nft_exthdr_type, |
| 784 | + .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), |
| 785 | + .eval = nft_exthdr_dccp_eval, |
| 786 | + .init = nft_exthdr_dccp_init, |
| 787 | + .dump = nft_exthdr_dump, |
| 788 | + .reduce = nft_exthdr_reduce, |
| 789 | +}; |
| 790 | + |
689 | 791 | static const struct nft_expr_ops *
|
690 | 792 | nft_exthdr_select_ops(const struct nft_ctx *ctx,
|
691 | 793 | const struct nlattr * const tb[])
|
@@ -720,6 +822,10 @@ nft_exthdr_select_ops(const struct nft_ctx *ctx,
|
720 | 822 | if (tb[NFTA_EXTHDR_DREG])
|
721 | 823 | return &nft_exthdr_sctp_ops;
|
722 | 824 | break;
|
| 825 | + case NFT_EXTHDR_OP_DCCP: |
| 826 | + if (tb[NFTA_EXTHDR_DREG]) |
| 827 | + return &nft_exthdr_dccp_ops; |
| 828 | + break; |
723 | 829 | }
|
724 | 830 |
|
725 | 831 | return ERR_PTR(-EOPNOTSUPP);
|
|
0 commit comments