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 <asm/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 19 struct 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 29 static 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 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); 44 } 45 46 static 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 = ®s->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; 70 err: 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 */ 82 static 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 unsigned int start; 89 bool found = false; 90 __be32 info; 91 int optlen; 92 93 iph = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); 94 if (!iph) 95 return -EBADMSG; 96 start = sizeof(struct iphdr); 97 98 optlen = iph->ihl * 4 - (int)sizeof(struct iphdr); 99 if (optlen <= 0) 100 return -ENOENT; 101 102 memset(opt, 0, sizeof(struct ip_options)); 103 /* Copy the options since __ip_options_compile() modifies 104 * the options. 105 */ 106 if (skb_copy_bits(skb, start, opt->__data, optlen)) 107 return -EBADMSG; 108 opt->optlen = optlen; 109 110 if (__ip_options_compile(net, opt, NULL, &info)) 111 return -EBADMSG; 112 113 switch (target) { 114 case IPOPT_SSRR: 115 case IPOPT_LSRR: 116 if (!opt->srr) 117 break; 118 found = target == IPOPT_SSRR ? opt->is_strictroute : 119 !opt->is_strictroute; 120 if (found) 121 *offset = opt->srr + start; 122 break; 123 case IPOPT_RR: 124 if (!opt->rr) 125 break; 126 *offset = opt->rr + start; 127 found = true; 128 break; 129 case IPOPT_RA: 130 if (!opt->router_alert) 131 break; 132 *offset = opt->router_alert + start; 133 found = true; 134 break; 135 default: 136 return -EOPNOTSUPP; 137 } 138 return found ? target : -ENOENT; 139 } 140 141 static void nft_exthdr_ipv4_eval(const struct nft_expr *expr, 142 struct nft_regs *regs, 143 const struct nft_pktinfo *pkt) 144 { 145 struct nft_exthdr *priv = nft_expr_priv(expr); 146 u32 *dest = ®s->data[priv->dreg]; 147 struct sk_buff *skb = pkt->skb; 148 unsigned int offset; 149 int err; 150 151 if (skb->protocol != htons(ETH_P_IP)) 152 goto err; 153 154 err = ipv4_find_option(nft_net(pkt), skb, &offset, priv->type); 155 if (priv->flags & NFT_EXTHDR_F_PRESENT) { 156 nft_reg_store8(dest, err >= 0); 157 return; 158 } else if (err < 0) { 159 goto err; 160 } 161 offset += priv->offset; 162 163 if (nft_skb_copy_to_reg(pkt->skb, offset, dest, priv->len) < 0) 164 goto err; 165 return; 166 err: 167 regs->verdict.code = NFT_BREAK; 168 } 169 170 static void * 171 nft_tcp_header_pointer(const struct nft_pktinfo *pkt, 172 unsigned int len, void *buffer, unsigned int *tcphdr_len) 173 { 174 struct tcphdr *tcph; 175 176 if (pkt->tprot != IPPROTO_TCP || pkt->fragoff) 177 return NULL; 178 179 tcph = skb_header_pointer(pkt->skb, nft_thoff(pkt), sizeof(*tcph), buffer); 180 if (!tcph) 181 return NULL; 182 183 *tcphdr_len = __tcp_hdrlen(tcph); 184 if (*tcphdr_len < sizeof(*tcph) || *tcphdr_len > len) 185 return NULL; 186 187 return skb_header_pointer(pkt->skb, nft_thoff(pkt), *tcphdr_len, buffer); 188 } 189 190 static void nft_exthdr_tcp_eval(const struct nft_expr *expr, 191 struct nft_regs *regs, 192 const struct nft_pktinfo *pkt) 193 { 194 u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE]; 195 struct nft_exthdr *priv = nft_expr_priv(expr); 196 unsigned int i, optl, tcphdr_len, offset; 197 u32 *dest = ®s->data[priv->dreg]; 198 struct tcphdr *tcph; 199 u8 *opt; 200 201 tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len); 202 if (!tcph) 203 goto err; 204 205 opt = (u8 *)tcph; 206 for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) { 207 optl = optlen(opt, i); 208 209 if (priv->type != opt[i]) 210 continue; 211 212 if (i + optl > tcphdr_len || priv->len + priv->offset > optl) 213 goto err; 214 215 offset = i + priv->offset; 216 if (priv->flags & NFT_EXTHDR_F_PRESENT) { 217 nft_reg_store8(dest, 1); 218 } else { 219 if (priv->len % NFT_REG32_SIZE) 220 dest[priv->len / NFT_REG32_SIZE] = 0; 221 memcpy(dest, opt + offset, priv->len); 222 } 223 224 return; 225 } 226 227 err: 228 if (priv->flags & NFT_EXTHDR_F_PRESENT) 229 *dest = 0; 230 else 231 regs->verdict.code = NFT_BREAK; 232 } 233 234 static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr, 235 struct nft_regs *regs, 236 const struct nft_pktinfo *pkt) 237 { 238 u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE]; 239 struct nft_exthdr *priv = nft_expr_priv(expr); 240 unsigned int i, optl, tcphdr_len, offset; 241 struct tcphdr *tcph; 242 u8 *opt; 243 244 tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len); 245 if (!tcph) 246 goto err; 247 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)); 252 opt = (u8 *)tcph; 253 254 for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) { 255 union { 256 __be16 v16; 257 __be32 v32; 258 } old, new; 259 260 optl = optlen(opt, i); 261 262 if (priv->type != opt[i]) 263 continue; 264 265 if (i + optl > tcphdr_len || priv->len + priv->offset > optl) 266 goto err; 267 268 offset = i + priv->offset; 269 270 switch (priv->len) { 271 case 2: 272 old.v16 = (__force __be16)get_unaligned((u16 *)(opt + offset)); 273 new.v16 = (__force __be16)nft_reg_load16( 274 ®s->data[priv->sreg]); 275 276 switch (priv->type) { 277 case TCPOPT_MSS: 278 /* increase can cause connection to stall */ 279 if (ntohs(old.v16) <= ntohs(new.v16)) 280 return; 281 break; 282 } 283 284 if (old.v16 == new.v16) 285 return; 286 287 put_unaligned(new.v16, (__be16*)(opt + offset)); 288 inet_proto_csum_replace2(&tcph->check, pkt->skb, 289 old.v16, new.v16, false); 290 break; 291 case 4: 292 new.v32 = nft_reg_load_be32(®s->data[priv->sreg]); 293 old.v32 = (__force __be32)get_unaligned((u32 *)(opt + offset)); 294 295 if (old.v32 == new.v32) 296 return; 297 298 put_unaligned(new.v32, (__be32*)(opt + offset)); 299 inet_proto_csum_replace4(&tcph->check, pkt->skb, 300 old.v32, new.v32, false); 301 break; 302 default: 303 WARN_ON_ONCE(1); 304 break; 305 } 306 307 return; 308 } 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 = ®s->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; 410 } 411 412 static void nft_exthdr_dccp_eval(const struct nft_expr *expr, 413 struct nft_regs *regs, 414 const struct nft_pktinfo *pkt) 415 { 416 struct nft_exthdr *priv = nft_expr_priv(expr); 417 unsigned int thoff, dataoff, optoff, optlen, i; 418 u32 *dest = ®s->data[priv->dreg]; 419 const struct dccp_hdr *dh; 420 struct dccp_hdr _dh; 421 422 if (pkt->tprot != IPPROTO_DCCP || pkt->fragoff) 423 goto err; 424 425 thoff = nft_thoff(pkt); 426 427 dh = skb_header_pointer(pkt->skb, thoff, sizeof(_dh), &_dh); 428 if (!dh) 429 goto err; 430 431 dataoff = dh->dccph_doff * sizeof(u32); 432 optoff = __dccp_hdr_len(dh); 433 if (dataoff <= optoff) 434 goto err; 435 436 optlen = dataoff - optoff; 437 438 for (i = 0; i < optlen; ) { 439 /* Options 0 (DCCPO_PADDING) - 31 (DCCPO_MAX_RESERVED) are 1B in 440 * the length; the remaining options are at least 2B long. In 441 * all cases, the first byte contains the option type. In 442 * multi-byte options, the second byte contains the option 443 * length, which must be at least two: 1 for the type plus 1 for 444 * the length plus 0-253 for any following option data. We 445 * aren't interested in the option data, only the type and the 446 * length, so we don't need to read more than two bytes at a 447 * time. 448 */ 449 unsigned int buflen = optlen - i; 450 u8 buf[2], *bufp; 451 u8 type, len; 452 453 if (buflen > sizeof(buf)) 454 buflen = sizeof(buf); 455 456 bufp = skb_header_pointer(pkt->skb, thoff + optoff + i, buflen, 457 &buf); 458 if (!bufp) 459 goto err; 460 461 type = bufp[0]; 462 463 if (type == priv->type) { 464 nft_reg_store8(dest, 1); 465 return; 466 } 467 468 if (type <= DCCPO_MAX_RESERVED) { 469 i++; 470 continue; 471 } 472 473 if (buflen < 2) 474 goto err; 475 476 len = bufp[1]; 477 478 if (len < 2) 479 goto err; 480 481 i += len; 482 } 483 484 err: 485 *dest = 0; 486 } 487 488 static 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 498 static 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 546 static 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(tb[NFTA_EXTHDR_SREG], &priv->sreg, 592 priv->len); 593 } 594 595 static 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 617 static 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 static int nft_exthdr_dccp_init(const struct nft_ctx *ctx, 640 const struct nft_expr *expr, 641 const struct nlattr * const tb[]) 642 { 643 struct nft_exthdr *priv = nft_expr_priv(expr); 644 int err = nft_exthdr_init(ctx, expr, tb); 645 646 if (err < 0) 647 return err; 648 649 if (!(priv->flags & NFT_EXTHDR_F_PRESENT)) 650 return -EOPNOTSUPP; 651 652 return 0; 653 } 654 655 static int nft_exthdr_dump_common(struct sk_buff *skb, const struct nft_exthdr *priv) 656 { 657 if (nla_put_u8(skb, NFTA_EXTHDR_TYPE, priv->type)) 658 goto nla_put_failure; 659 if (nla_put_be32(skb, NFTA_EXTHDR_OFFSET, htonl(priv->offset))) 660 goto nla_put_failure; 661 if (nla_put_be32(skb, NFTA_EXTHDR_LEN, htonl(priv->len))) 662 goto nla_put_failure; 663 if (nla_put_be32(skb, NFTA_EXTHDR_FLAGS, htonl(priv->flags))) 664 goto nla_put_failure; 665 if (nla_put_be32(skb, NFTA_EXTHDR_OP, htonl(priv->op))) 666 goto nla_put_failure; 667 return 0; 668 669 nla_put_failure: 670 return -1; 671 } 672 673 static int nft_exthdr_dump(struct sk_buff *skb, 674 const struct nft_expr *expr, bool reset) 675 { 676 const struct nft_exthdr *priv = nft_expr_priv(expr); 677 678 if (nft_dump_register(skb, NFTA_EXTHDR_DREG, priv->dreg)) 679 return -1; 680 681 return nft_exthdr_dump_common(skb, priv); 682 } 683 684 static int nft_exthdr_dump_set(struct sk_buff *skb, 685 const struct nft_expr *expr, bool reset) 686 { 687 const struct nft_exthdr *priv = nft_expr_priv(expr); 688 689 if (nft_dump_register(skb, NFTA_EXTHDR_SREG, priv->sreg)) 690 return -1; 691 692 return nft_exthdr_dump_common(skb, priv); 693 } 694 695 static int nft_exthdr_dump_strip(struct sk_buff *skb, 696 const struct nft_expr *expr, bool reset) 697 { 698 const struct nft_exthdr *priv = nft_expr_priv(expr); 699 700 return nft_exthdr_dump_common(skb, priv); 701 } 702 703 static bool nft_exthdr_reduce(struct nft_regs_track *track, 704 const struct nft_expr *expr) 705 { 706 const struct nft_exthdr *priv = nft_expr_priv(expr); 707 const struct nft_exthdr *exthdr; 708 709 if (!nft_reg_track_cmp(track, expr, priv->dreg)) { 710 nft_reg_track_update(track, expr, priv->dreg, priv->len); 711 return false; 712 } 713 714 exthdr = nft_expr_priv(track->regs[priv->dreg].selector); 715 if (priv->type != exthdr->type || 716 priv->op != exthdr->op || 717 priv->flags != exthdr->flags || 718 priv->offset != exthdr->offset || 719 priv->len != exthdr->len) { 720 nft_reg_track_update(track, expr, priv->dreg, priv->len); 721 return false; 722 } 723 724 if (!track->regs[priv->dreg].bitwise) 725 return true; 726 727 return nft_expr_reduce_bitwise(track, expr); 728 } 729 730 static const struct nft_expr_ops nft_exthdr_ipv6_ops = { 731 .type = &nft_exthdr_type, 732 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), 733 .eval = nft_exthdr_ipv6_eval, 734 .init = nft_exthdr_init, 735 .dump = nft_exthdr_dump, 736 .reduce = nft_exthdr_reduce, 737 }; 738 739 static const struct nft_expr_ops nft_exthdr_ipv4_ops = { 740 .type = &nft_exthdr_type, 741 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), 742 .eval = nft_exthdr_ipv4_eval, 743 .init = nft_exthdr_ipv4_init, 744 .dump = nft_exthdr_dump, 745 .reduce = nft_exthdr_reduce, 746 }; 747 748 static const struct nft_expr_ops nft_exthdr_tcp_ops = { 749 .type = &nft_exthdr_type, 750 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), 751 .eval = nft_exthdr_tcp_eval, 752 .init = nft_exthdr_init, 753 .dump = nft_exthdr_dump, 754 .reduce = nft_exthdr_reduce, 755 }; 756 757 static const struct nft_expr_ops nft_exthdr_tcp_set_ops = { 758 .type = &nft_exthdr_type, 759 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), 760 .eval = nft_exthdr_tcp_set_eval, 761 .init = nft_exthdr_tcp_set_init, 762 .dump = nft_exthdr_dump_set, 763 .reduce = NFT_REDUCE_READONLY, 764 }; 765 766 static const struct nft_expr_ops nft_exthdr_tcp_strip_ops = { 767 .type = &nft_exthdr_type, 768 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), 769 .eval = nft_exthdr_tcp_strip_eval, 770 .init = nft_exthdr_tcp_strip_init, 771 .dump = nft_exthdr_dump_strip, 772 .reduce = NFT_REDUCE_READONLY, 773 }; 774 775 static const struct nft_expr_ops nft_exthdr_sctp_ops = { 776 .type = &nft_exthdr_type, 777 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), 778 .eval = nft_exthdr_sctp_eval, 779 .init = nft_exthdr_init, 780 .dump = nft_exthdr_dump, 781 .reduce = nft_exthdr_reduce, 782 }; 783 784 static const struct nft_expr_ops nft_exthdr_dccp_ops = { 785 .type = &nft_exthdr_type, 786 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), 787 .eval = nft_exthdr_dccp_eval, 788 .init = nft_exthdr_dccp_init, 789 .dump = nft_exthdr_dump, 790 .reduce = nft_exthdr_reduce, 791 }; 792 793 static const struct nft_expr_ops * 794 nft_exthdr_select_ops(const struct nft_ctx *ctx, 795 const struct nlattr * const tb[]) 796 { 797 u32 op; 798 799 if (!tb[NFTA_EXTHDR_OP]) 800 return &nft_exthdr_ipv6_ops; 801 802 if (tb[NFTA_EXTHDR_SREG] && tb[NFTA_EXTHDR_DREG]) 803 return ERR_PTR(-EOPNOTSUPP); 804 805 op = ntohl(nla_get_be32(tb[NFTA_EXTHDR_OP])); 806 switch (op) { 807 case NFT_EXTHDR_OP_TCPOPT: 808 if (tb[NFTA_EXTHDR_SREG]) 809 return &nft_exthdr_tcp_set_ops; 810 if (tb[NFTA_EXTHDR_DREG]) 811 return &nft_exthdr_tcp_ops; 812 return &nft_exthdr_tcp_strip_ops; 813 case NFT_EXTHDR_OP_IPV6: 814 if (tb[NFTA_EXTHDR_DREG]) 815 return &nft_exthdr_ipv6_ops; 816 break; 817 case NFT_EXTHDR_OP_IPV4: 818 if (ctx->family != NFPROTO_IPV6) { 819 if (tb[NFTA_EXTHDR_DREG]) 820 return &nft_exthdr_ipv4_ops; 821 } 822 break; 823 case NFT_EXTHDR_OP_SCTP: 824 if (tb[NFTA_EXTHDR_DREG]) 825 return &nft_exthdr_sctp_ops; 826 break; 827 case NFT_EXTHDR_OP_DCCP: 828 if (tb[NFTA_EXTHDR_DREG]) 829 return &nft_exthdr_dccp_ops; 830 break; 831 } 832 833 return ERR_PTR(-EOPNOTSUPP); 834 } 835 836 struct nft_expr_type nft_exthdr_type __read_mostly = { 837 .name = "exthdr", 838 .select_ops = nft_exthdr_select_ops, 839 .policy = nft_exthdr_policy, 840 .maxattr = NFTA_EXTHDR_MAX, 841 .owner = THIS_MODULE, 842 }; 843
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.