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