at master 20 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> 4 * 5 * Development of this code funded by Astaro AG (http://www.astaro.com/) 6 */ 7 8#include <linux/unaligned.h> 9#include <linux/kernel.h> 10#include <linux/netlink.h> 11#include <linux/netfilter.h> 12#include <linux/netfilter/nf_tables.h> 13#include <linux/dccp.h> 14#include <linux/sctp.h> 15#include <net/netfilter/nf_tables_core.h> 16#include <net/netfilter/nf_tables.h> 17#include <net/tcp.h> 18 19struct nft_exthdr { 20 u8 type; 21 u8 offset; 22 u8 len; 23 u8 op; 24 u8 dreg; 25 u8 sreg; 26 u8 flags; 27}; 28 29static unsigned int optlen(const u8 *opt, unsigned int offset) 30{ 31 /* Beware zero-length options: make finite progress */ 32 if (opt[offset] <= TCPOPT_NOP || opt[offset + 1] == 0) 33 return 1; 34 else 35 return opt[offset + 1]; 36} 37 38static 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); 44} 45 46static void nft_exthdr_ipv6_eval(const struct nft_expr *expr, 47 struct nft_regs *regs, 48 const struct nft_pktinfo *pkt) 49{ 50 struct nft_exthdr *priv = nft_expr_priv(expr); 51 u32 *dest = &regs->data[priv->dreg]; 52 unsigned int offset = 0; 53 int err; 54 55 if (pkt->skb->protocol != htons(ETH_P_IPV6)) 56 goto err; 57 58 err = ipv6_find_hdr(pkt->skb, &offset, priv->type, NULL, NULL); 59 if (priv->flags & NFT_EXTHDR_F_PRESENT) { 60 nft_reg_store8(dest, err >= 0); 61 return; 62 } else if (err < 0) { 63 goto err; 64 } 65 offset += priv->offset; 66 67 if (nft_skb_copy_to_reg(pkt->skb, offset, dest, priv->len) < 0) 68 goto err; 69 return; 70err: 71 regs->verdict.code = NFT_BREAK; 72} 73 74/* find the offset to specified option. 75 * 76 * If target header is found, its offset is set in *offset and return option 77 * number. Otherwise, return negative error. 78 * 79 * If the first fragment doesn't contain the End of Options it is considered 80 * invalid. 81 */ 82static int ipv4_find_option(struct net *net, struct sk_buff *skb, 83 unsigned int *offset, int target) 84{ 85 unsigned char optbuf[sizeof(struct ip_options) + 40]; 86 struct ip_options *opt = (struct ip_options *)optbuf; 87 struct iphdr *iph, _iph; 88 bool found = false; 89 __be32 info; 90 int optlen; 91 92 iph = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); 93 if (!iph) 94 return -EBADMSG; 95 96 optlen = iph->ihl * 4 - (int)sizeof(struct iphdr); 97 if (optlen <= 0) 98 return -ENOENT; 99 100 memset(opt, 0, sizeof(struct ip_options)); 101 /* Copy the options since __ip_options_compile() modifies 102 * the options. 103 */ 104 if (skb_copy_bits(skb, sizeof(struct iphdr), opt->__data, optlen)) 105 return -EBADMSG; 106 opt->optlen = optlen; 107 108 if (__ip_options_compile(net, opt, NULL, &info)) 109 return -EBADMSG; 110 111 switch (target) { 112 case IPOPT_SSRR: 113 case IPOPT_LSRR: 114 if (!opt->srr) 115 break; 116 found = target == IPOPT_SSRR ? opt->is_strictroute : 117 !opt->is_strictroute; 118 if (found) 119 *offset = opt->srr; 120 break; 121 case IPOPT_RR: 122 if (!opt->rr) 123 break; 124 *offset = opt->rr; 125 found = true; 126 break; 127 case IPOPT_RA: 128 if (!opt->router_alert) 129 break; 130 *offset = opt->router_alert; 131 found = true; 132 break; 133 default: 134 return -EOPNOTSUPP; 135 } 136 return found ? target : -ENOENT; 137} 138 139static void nft_exthdr_ipv4_eval(const struct nft_expr *expr, 140 struct nft_regs *regs, 141 const struct nft_pktinfo *pkt) 142{ 143 struct nft_exthdr *priv = nft_expr_priv(expr); 144 u32 *dest = &regs->data[priv->dreg]; 145 struct sk_buff *skb = pkt->skb; 146 unsigned int offset; 147 int err; 148 149 if (skb->protocol != htons(ETH_P_IP)) 150 goto err; 151 152 err = ipv4_find_option(nft_net(pkt), skb, &offset, priv->type); 153 if (priv->flags & NFT_EXTHDR_F_PRESENT) { 154 nft_reg_store8(dest, err >= 0); 155 return; 156 } else if (err < 0) { 157 goto err; 158 } 159 offset += priv->offset; 160 161 if (nft_skb_copy_to_reg(pkt->skb, offset, dest, priv->len) < 0) 162 goto err; 163 return; 164err: 165 regs->verdict.code = NFT_BREAK; 166} 167 168static void * 169nft_tcp_header_pointer(const struct nft_pktinfo *pkt, 170 unsigned int len, void *buffer, unsigned int *tcphdr_len) 171{ 172 struct tcphdr *tcph; 173 174 if (pkt->tprot != IPPROTO_TCP || pkt->fragoff) 175 return NULL; 176 177 tcph = skb_header_pointer(pkt->skb, nft_thoff(pkt), sizeof(*tcph), buffer); 178 if (!tcph) 179 return NULL; 180 181 *tcphdr_len = __tcp_hdrlen(tcph); 182 if (*tcphdr_len < sizeof(*tcph) || *tcphdr_len > len) 183 return NULL; 184 185 return skb_header_pointer(pkt->skb, nft_thoff(pkt), *tcphdr_len, buffer); 186} 187 188static void nft_exthdr_tcp_eval(const struct nft_expr *expr, 189 struct nft_regs *regs, 190 const struct nft_pktinfo *pkt) 191{ 192 u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE]; 193 struct nft_exthdr *priv = nft_expr_priv(expr); 194 unsigned int i, optl, tcphdr_len, offset; 195 u32 *dest = &regs->data[priv->dreg]; 196 struct tcphdr *tcph; 197 u8 *opt; 198 199 tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len); 200 if (!tcph) 201 goto err; 202 203 opt = (u8 *)tcph; 204 for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) { 205 optl = optlen(opt, i); 206 207 if (priv->type != opt[i]) 208 continue; 209 210 if (i + optl > tcphdr_len || priv->len + priv->offset > optl) 211 goto err; 212 213 offset = i + priv->offset; 214 if (priv->flags & NFT_EXTHDR_F_PRESENT) { 215 nft_reg_store8(dest, 1); 216 } else { 217 if (priv->len % NFT_REG32_SIZE) 218 dest[priv->len / NFT_REG32_SIZE] = 0; 219 memcpy(dest, opt + offset, priv->len); 220 } 221 222 return; 223 } 224 225err: 226 if (priv->flags & NFT_EXTHDR_F_PRESENT) 227 *dest = 0; 228 else 229 regs->verdict.code = NFT_BREAK; 230} 231 232static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr, 233 struct nft_regs *regs, 234 const struct nft_pktinfo *pkt) 235{ 236 u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE]; 237 struct nft_exthdr *priv = nft_expr_priv(expr); 238 unsigned int i, optl, tcphdr_len, offset; 239 struct tcphdr *tcph; 240 u8 *opt; 241 242 tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len); 243 if (!tcph) 244 goto err; 245 246 if (skb_ensure_writable(pkt->skb, nft_thoff(pkt) + tcphdr_len)) 247 goto err; 248 249 tcph = (struct tcphdr *)(pkt->skb->data + nft_thoff(pkt)); 250 opt = (u8 *)tcph; 251 252 for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) { 253 union { 254 __be16 v16; 255 __be32 v32; 256 } old, new; 257 258 optl = optlen(opt, i); 259 260 if (priv->type != opt[i]) 261 continue; 262 263 if (i + optl > tcphdr_len || priv->len + priv->offset > optl) 264 goto err; 265 266 offset = i + priv->offset; 267 268 switch (priv->len) { 269 case 2: 270 old.v16 = (__force __be16)get_unaligned((u16 *)(opt + offset)); 271 new.v16 = (__force __be16)nft_reg_load16( 272 &regs->data[priv->sreg]); 273 274 switch (priv->type) { 275 case TCPOPT_MSS: 276 /* increase can cause connection to stall */ 277 if (ntohs(old.v16) <= ntohs(new.v16)) 278 return; 279 break; 280 } 281 282 if (old.v16 == new.v16) 283 return; 284 285 put_unaligned(new.v16, (__be16*)(opt + offset)); 286 inet_proto_csum_replace2(&tcph->check, pkt->skb, 287 old.v16, new.v16, false); 288 break; 289 case 4: 290 new.v32 = nft_reg_load_be32(&regs->data[priv->sreg]); 291 old.v32 = (__force __be32)get_unaligned((u32 *)(opt + offset)); 292 293 if (old.v32 == new.v32) 294 return; 295 296 put_unaligned(new.v32, (__be32*)(opt + offset)); 297 inet_proto_csum_replace4(&tcph->check, pkt->skb, 298 old.v32, new.v32, false); 299 break; 300 default: 301 WARN_ON_ONCE(1); 302 break; 303 } 304 305 return; 306 } 307 return; 308err: 309 regs->verdict.code = NFT_BREAK; 310} 311 312static void nft_exthdr_tcp_strip_eval(const struct nft_expr *expr, 313 struct nft_regs *regs, 314 const struct nft_pktinfo *pkt) 315{ 316 u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE]; 317 struct nft_exthdr *priv = nft_expr_priv(expr); 318 unsigned int i, tcphdr_len, optl; 319 struct tcphdr *tcph; 320 u8 *opt; 321 322 tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len); 323 if (!tcph) 324 goto err; 325 326 if (skb_ensure_writable(pkt->skb, nft_thoff(pkt) + tcphdr_len)) 327 goto drop; 328 329 tcph = (struct tcphdr *)(pkt->skb->data + nft_thoff(pkt)); 330 opt = (u8 *)tcph; 331 332 for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) { 333 unsigned int j; 334 335 optl = optlen(opt, i); 336 if (priv->type != opt[i]) 337 continue; 338 339 if (i + optl > tcphdr_len) 340 goto drop; 341 342 for (j = 0; j < optl; ++j) { 343 u16 n = TCPOPT_NOP; 344 u16 o = opt[i+j]; 345 346 if ((i + j) % 2 == 0) { 347 o <<= 8; 348 n <<= 8; 349 } 350 inet_proto_csum_replace2(&tcph->check, pkt->skb, htons(o), 351 htons(n), false); 352 } 353 memset(opt + i, TCPOPT_NOP, optl); 354 return; 355 } 356 357 /* option not found, continue. This allows to do multiple 358 * option removals per rule. 359 */ 360 return; 361err: 362 regs->verdict.code = NFT_BREAK; 363 return; 364drop: 365 /* can't remove, no choice but to drop */ 366 regs->verdict.code = NF_DROP; 367} 368 369static void nft_exthdr_sctp_eval(const struct nft_expr *expr, 370 struct nft_regs *regs, 371 const struct nft_pktinfo *pkt) 372{ 373 unsigned int offset = nft_thoff(pkt) + sizeof(struct sctphdr); 374 struct nft_exthdr *priv = nft_expr_priv(expr); 375 u32 *dest = &regs->data[priv->dreg]; 376 const struct sctp_chunkhdr *sch; 377 struct sctp_chunkhdr _sch; 378 379 if (pkt->tprot != IPPROTO_SCTP) 380 goto err; 381 382 do { 383 sch = skb_header_pointer(pkt->skb, offset, sizeof(_sch), &_sch); 384 if (!sch || !sch->length) 385 break; 386 387 if (sch->type == priv->type) { 388 if (priv->flags & NFT_EXTHDR_F_PRESENT) { 389 nft_reg_store8(dest, true); 390 return; 391 } 392 if (priv->offset + priv->len > ntohs(sch->length) || 393 offset + ntohs(sch->length) > pkt->skb->len) 394 break; 395 396 if (nft_skb_copy_to_reg(pkt->skb, offset + priv->offset, 397 dest, priv->len) < 0) 398 break; 399 return; 400 } 401 offset += SCTP_PAD4(ntohs(sch->length)); 402 } while (offset < pkt->skb->len); 403err: 404 if (priv->flags & NFT_EXTHDR_F_PRESENT) 405 nft_reg_store8(dest, false); 406 else 407 regs->verdict.code = NFT_BREAK; 408} 409 410#ifdef CONFIG_NFT_EXTHDR_DCCP 411static void nft_exthdr_dccp_eval(const struct nft_expr *expr, 412 struct nft_regs *regs, 413 const struct nft_pktinfo *pkt) 414{ 415 struct nft_exthdr *priv = nft_expr_priv(expr); 416 unsigned int thoff, dataoff, optoff, optlen, i; 417 u32 *dest = &regs->data[priv->dreg]; 418 const struct dccp_hdr *dh; 419 struct dccp_hdr _dh; 420 421 if (pkt->tprot != IPPROTO_DCCP || pkt->fragoff) 422 goto err; 423 424 thoff = nft_thoff(pkt); 425 426 dh = skb_header_pointer(pkt->skb, thoff, sizeof(_dh), &_dh); 427 if (!dh) 428 goto err; 429 430 dataoff = dh->dccph_doff * sizeof(u32); 431 optoff = __dccp_hdr_len(dh); 432 if (dataoff <= optoff) 433 goto err; 434 435 optlen = dataoff - optoff; 436 437 for (i = 0; i < optlen; ) { 438 /* Options 0 (DCCPO_PADDING) - 31 (DCCPO_MAX_RESERVED) are 1B in 439 * the length; the remaining options are at least 2B long. In 440 * all cases, the first byte contains the option type. In 441 * multi-byte options, the second byte contains the option 442 * length, which must be at least two: 1 for the type plus 1 for 443 * the length plus 0-253 for any following option data. We 444 * aren't interested in the option data, only the type and the 445 * length, so we don't need to read more than two bytes at a 446 * time. 447 */ 448 unsigned int buflen = optlen - i; 449 u8 buf[2], *bufp; 450 u8 type, len; 451 452 if (buflen > sizeof(buf)) 453 buflen = sizeof(buf); 454 455 bufp = skb_header_pointer(pkt->skb, thoff + optoff + i, buflen, 456 &buf); 457 if (!bufp) 458 goto err; 459 460 type = bufp[0]; 461 462 if (type == priv->type) { 463 nft_reg_store8(dest, 1); 464 return; 465 } 466 467 if (type <= DCCPO_MAX_RESERVED) { 468 i++; 469 continue; 470 } 471 472 if (buflen < 2) 473 goto err; 474 475 len = bufp[1]; 476 477 if (len < 2) 478 goto err; 479 480 i += len; 481 } 482 483err: 484 *dest = 0; 485} 486#endif 487 488static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = { 489 [NFTA_EXTHDR_DREG] = { .type = NLA_U32 }, 490 [NFTA_EXTHDR_TYPE] = { .type = NLA_U8 }, 491 [NFTA_EXTHDR_OFFSET] = { .type = NLA_U32 }, 492 [NFTA_EXTHDR_LEN] = NLA_POLICY_MAX(NLA_BE32, 255), 493 [NFTA_EXTHDR_FLAGS] = { .type = NLA_U32 }, 494 [NFTA_EXTHDR_OP] = NLA_POLICY_MAX(NLA_BE32, 255), 495 [NFTA_EXTHDR_SREG] = { .type = NLA_U32 }, 496}; 497 498static int nft_exthdr_init(const struct nft_ctx *ctx, 499 const struct nft_expr *expr, 500 const struct nlattr * const tb[]) 501{ 502 struct nft_exthdr *priv = nft_expr_priv(expr); 503 u32 offset, len, flags = 0, op = NFT_EXTHDR_OP_IPV6; 504 int err; 505 506 if (!tb[NFTA_EXTHDR_DREG] || 507 !tb[NFTA_EXTHDR_TYPE] || 508 !tb[NFTA_EXTHDR_OFFSET] || 509 !tb[NFTA_EXTHDR_LEN]) 510 return -EINVAL; 511 512 err = nft_parse_u32_check(tb[NFTA_EXTHDR_OFFSET], U8_MAX, &offset); 513 if (err < 0) 514 return err; 515 516 err = nft_parse_u32_check(tb[NFTA_EXTHDR_LEN], U8_MAX, &len); 517 if (err < 0) 518 return err; 519 520 if (tb[NFTA_EXTHDR_FLAGS]) { 521 err = nft_parse_u32_check(tb[NFTA_EXTHDR_FLAGS], U8_MAX, &flags); 522 if (err < 0) 523 return err; 524 525 if (flags & ~NFT_EXTHDR_F_PRESENT) 526 return -EINVAL; 527 } 528 529 if (tb[NFTA_EXTHDR_OP]) { 530 err = nft_parse_u32_check(tb[NFTA_EXTHDR_OP], U8_MAX, &op); 531 if (err < 0) 532 return err; 533 } 534 535 priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]); 536 priv->offset = offset; 537 priv->len = len; 538 priv->flags = flags; 539 priv->op = op; 540 541 return nft_parse_register_store(ctx, tb[NFTA_EXTHDR_DREG], 542 &priv->dreg, NULL, NFT_DATA_VALUE, 543 priv->len); 544} 545 546static int nft_exthdr_tcp_set_init(const struct nft_ctx *ctx, 547 const struct nft_expr *expr, 548 const struct nlattr * const tb[]) 549{ 550 struct nft_exthdr *priv = nft_expr_priv(expr); 551 u32 offset, len, flags = 0, op = NFT_EXTHDR_OP_IPV6; 552 int err; 553 554 if (!tb[NFTA_EXTHDR_SREG] || 555 !tb[NFTA_EXTHDR_TYPE] || 556 !tb[NFTA_EXTHDR_OFFSET] || 557 !tb[NFTA_EXTHDR_LEN]) 558 return -EINVAL; 559 560 if (tb[NFTA_EXTHDR_DREG] || tb[NFTA_EXTHDR_FLAGS]) 561 return -EINVAL; 562 563 err = nft_parse_u32_check(tb[NFTA_EXTHDR_OFFSET], U8_MAX, &offset); 564 if (err < 0) 565 return err; 566 567 err = nft_parse_u32_check(tb[NFTA_EXTHDR_LEN], U8_MAX, &len); 568 if (err < 0) 569 return err; 570 571 if (offset < 2) 572 return -EOPNOTSUPP; 573 574 switch (len) { 575 case 2: break; 576 case 4: break; 577 default: 578 return -EOPNOTSUPP; 579 } 580 581 err = nft_parse_u32_check(tb[NFTA_EXTHDR_OP], U8_MAX, &op); 582 if (err < 0) 583 return err; 584 585 priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]); 586 priv->offset = offset; 587 priv->len = len; 588 priv->flags = flags; 589 priv->op = op; 590 591 return nft_parse_register_load(ctx, tb[NFTA_EXTHDR_SREG], &priv->sreg, 592 priv->len); 593} 594 595static int nft_exthdr_tcp_strip_init(const struct nft_ctx *ctx, 596 const struct nft_expr *expr, 597 const struct nlattr * const tb[]) 598{ 599 struct nft_exthdr *priv = nft_expr_priv(expr); 600 601 if (tb[NFTA_EXTHDR_SREG] || 602 tb[NFTA_EXTHDR_DREG] || 603 tb[NFTA_EXTHDR_FLAGS] || 604 tb[NFTA_EXTHDR_OFFSET] || 605 tb[NFTA_EXTHDR_LEN]) 606 return -EINVAL; 607 608 if (!tb[NFTA_EXTHDR_TYPE]) 609 return -EINVAL; 610 611 priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]); 612 priv->op = NFT_EXTHDR_OP_TCPOPT; 613 614 return 0; 615} 616 617static int nft_exthdr_ipv4_init(const struct nft_ctx *ctx, 618 const struct nft_expr *expr, 619 const struct nlattr * const tb[]) 620{ 621 struct nft_exthdr *priv = nft_expr_priv(expr); 622 int err = nft_exthdr_init(ctx, expr, tb); 623 624 if (err < 0) 625 return err; 626 627 switch (priv->type) { 628 case IPOPT_SSRR: 629 case IPOPT_LSRR: 630 case IPOPT_RR: 631 case IPOPT_RA: 632 break; 633 default: 634 return -EOPNOTSUPP; 635 } 636 return 0; 637} 638 639#ifdef CONFIG_NFT_EXTHDR_DCCP 640static int nft_exthdr_dccp_init(const struct nft_ctx *ctx, 641 const struct nft_expr *expr, 642 const struct nlattr * const tb[]) 643{ 644 struct nft_exthdr *priv = nft_expr_priv(expr); 645 int err = nft_exthdr_init(ctx, expr, tb); 646 647 if (err < 0) 648 return err; 649 650 if (!(priv->flags & NFT_EXTHDR_F_PRESENT)) 651 return -EOPNOTSUPP; 652 653 return 0; 654} 655#endif 656 657static int nft_exthdr_dump_common(struct sk_buff *skb, const struct nft_exthdr *priv) 658{ 659 if (nla_put_u8(skb, NFTA_EXTHDR_TYPE, priv->type)) 660 goto nla_put_failure; 661 if (nla_put_be32(skb, NFTA_EXTHDR_OFFSET, htonl(priv->offset))) 662 goto nla_put_failure; 663 if (nla_put_be32(skb, NFTA_EXTHDR_LEN, htonl(priv->len))) 664 goto nla_put_failure; 665 if (nla_put_be32(skb, NFTA_EXTHDR_FLAGS, htonl(priv->flags))) 666 goto nla_put_failure; 667 if (nla_put_be32(skb, NFTA_EXTHDR_OP, htonl(priv->op))) 668 goto nla_put_failure; 669 return 0; 670 671nla_put_failure: 672 return -1; 673} 674 675static int nft_exthdr_dump(struct sk_buff *skb, 676 const struct nft_expr *expr, bool reset) 677{ 678 const struct nft_exthdr *priv = nft_expr_priv(expr); 679 680 if (nft_dump_register(skb, NFTA_EXTHDR_DREG, priv->dreg)) 681 return -1; 682 683 return nft_exthdr_dump_common(skb, priv); 684} 685 686static int nft_exthdr_dump_set(struct sk_buff *skb, 687 const struct nft_expr *expr, bool reset) 688{ 689 const struct nft_exthdr *priv = nft_expr_priv(expr); 690 691 if (nft_dump_register(skb, NFTA_EXTHDR_SREG, priv->sreg)) 692 return -1; 693 694 return nft_exthdr_dump_common(skb, priv); 695} 696 697static int nft_exthdr_dump_strip(struct sk_buff *skb, 698 const struct nft_expr *expr, bool reset) 699{ 700 const struct nft_exthdr *priv = nft_expr_priv(expr); 701 702 return nft_exthdr_dump_common(skb, priv); 703} 704 705static bool nft_exthdr_reduce(struct nft_regs_track *track, 706 const struct nft_expr *expr) 707{ 708 const struct nft_exthdr *priv = nft_expr_priv(expr); 709 const struct nft_exthdr *exthdr; 710 711 if (!nft_reg_track_cmp(track, expr, priv->dreg)) { 712 nft_reg_track_update(track, expr, priv->dreg, priv->len); 713 return false; 714 } 715 716 exthdr = nft_expr_priv(track->regs[priv->dreg].selector); 717 if (priv->type != exthdr->type || 718 priv->op != exthdr->op || 719 priv->flags != exthdr->flags || 720 priv->offset != exthdr->offset || 721 priv->len != exthdr->len) { 722 nft_reg_track_update(track, expr, priv->dreg, priv->len); 723 return false; 724 } 725 726 if (!track->regs[priv->dreg].bitwise) 727 return true; 728 729 return nft_expr_reduce_bitwise(track, expr); 730} 731 732static const struct nft_expr_ops nft_exthdr_ipv6_ops = { 733 .type = &nft_exthdr_type, 734 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), 735 .eval = nft_exthdr_ipv6_eval, 736 .init = nft_exthdr_init, 737 .dump = nft_exthdr_dump, 738 .reduce = nft_exthdr_reduce, 739}; 740 741static const struct nft_expr_ops nft_exthdr_ipv4_ops = { 742 .type = &nft_exthdr_type, 743 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), 744 .eval = nft_exthdr_ipv4_eval, 745 .init = nft_exthdr_ipv4_init, 746 .dump = nft_exthdr_dump, 747 .reduce = nft_exthdr_reduce, 748}; 749 750static const struct nft_expr_ops nft_exthdr_tcp_ops = { 751 .type = &nft_exthdr_type, 752 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), 753 .eval = nft_exthdr_tcp_eval, 754 .init = nft_exthdr_init, 755 .dump = nft_exthdr_dump, 756 .reduce = nft_exthdr_reduce, 757}; 758 759static const struct nft_expr_ops nft_exthdr_tcp_set_ops = { 760 .type = &nft_exthdr_type, 761 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), 762 .eval = nft_exthdr_tcp_set_eval, 763 .init = nft_exthdr_tcp_set_init, 764 .dump = nft_exthdr_dump_set, 765 .reduce = NFT_REDUCE_READONLY, 766}; 767 768static const struct nft_expr_ops nft_exthdr_tcp_strip_ops = { 769 .type = &nft_exthdr_type, 770 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), 771 .eval = nft_exthdr_tcp_strip_eval, 772 .init = nft_exthdr_tcp_strip_init, 773 .dump = nft_exthdr_dump_strip, 774 .reduce = NFT_REDUCE_READONLY, 775}; 776 777static const struct nft_expr_ops nft_exthdr_sctp_ops = { 778 .type = &nft_exthdr_type, 779 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), 780 .eval = nft_exthdr_sctp_eval, 781 .init = nft_exthdr_init, 782 .dump = nft_exthdr_dump, 783 .reduce = nft_exthdr_reduce, 784}; 785 786#ifdef CONFIG_NFT_EXTHDR_DCCP 787static const struct nft_expr_ops nft_exthdr_dccp_ops = { 788 .type = &nft_exthdr_type, 789 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), 790 .eval = nft_exthdr_dccp_eval, 791 .init = nft_exthdr_dccp_init, 792 .dump = nft_exthdr_dump, 793 .reduce = nft_exthdr_reduce, 794}; 795#endif 796 797static const struct nft_expr_ops * 798nft_exthdr_select_ops(const struct nft_ctx *ctx, 799 const struct nlattr * const tb[]) 800{ 801 u32 op; 802 803 if (!tb[NFTA_EXTHDR_OP]) 804 return &nft_exthdr_ipv6_ops; 805 806 if (tb[NFTA_EXTHDR_SREG] && tb[NFTA_EXTHDR_DREG]) 807 return ERR_PTR(-EOPNOTSUPP); 808 809 op = ntohl(nla_get_be32(tb[NFTA_EXTHDR_OP])); 810 switch (op) { 811 case NFT_EXTHDR_OP_TCPOPT: 812 if (tb[NFTA_EXTHDR_SREG]) 813 return &nft_exthdr_tcp_set_ops; 814 if (tb[NFTA_EXTHDR_DREG]) 815 return &nft_exthdr_tcp_ops; 816 return &nft_exthdr_tcp_strip_ops; 817 case NFT_EXTHDR_OP_IPV6: 818 if (tb[NFTA_EXTHDR_DREG]) 819 return &nft_exthdr_ipv6_ops; 820 break; 821 case NFT_EXTHDR_OP_IPV4: 822 if (ctx->family != NFPROTO_IPV6) { 823 if (tb[NFTA_EXTHDR_DREG]) 824 return &nft_exthdr_ipv4_ops; 825 } 826 break; 827 case NFT_EXTHDR_OP_SCTP: 828 if (tb[NFTA_EXTHDR_DREG]) 829 return &nft_exthdr_sctp_ops; 830 break; 831#ifdef CONFIG_NFT_EXTHDR_DCCP 832 case NFT_EXTHDR_OP_DCCP: 833 if (tb[NFTA_EXTHDR_DREG]) 834 return &nft_exthdr_dccp_ops; 835 break; 836#endif 837 } 838 839 return ERR_PTR(-EOPNOTSUPP); 840} 841 842struct nft_expr_type nft_exthdr_type __read_mostly = { 843 .name = "exthdr", 844 .select_ops = nft_exthdr_select_ops, 845 .policy = nft_exthdr_policy, 846 .maxattr = NFTA_EXTHDR_MAX, 847 .owner = THIS_MODULE, 848};