Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
0004  *
0005  * Development of this code funded by Astaro AG (http://www.astaro.com/)
0006  */
0007 
0008 #include <asm/unaligned.h>
0009 #include <linux/kernel.h>
0010 #include <linux/netlink.h>
0011 #include <linux/netfilter.h>
0012 #include <linux/netfilter/nf_tables.h>
0013 #include <linux/sctp.h>
0014 #include <net/netfilter/nf_tables_core.h>
0015 #include <net/netfilter/nf_tables.h>
0016 #include <net/sctp/sctp.h>
0017 #include <net/tcp.h>
0018 
0019 struct nft_exthdr {
0020     u8          type;
0021     u8          offset;
0022     u8          len;
0023     u8          op;
0024     u8          dreg;
0025     u8          sreg;
0026     u8          flags;
0027 };
0028 
0029 static unsigned int optlen(const u8 *opt, unsigned int offset)
0030 {
0031     /* Beware zero-length options: make finite progress */
0032     if (opt[offset] <= TCPOPT_NOP || opt[offset + 1] == 0)
0033         return 1;
0034     else
0035         return opt[offset + 1];
0036 }
0037 
0038 static void nft_exthdr_ipv6_eval(const struct nft_expr *expr,
0039                  struct nft_regs *regs,
0040                  const struct nft_pktinfo *pkt)
0041 {
0042     struct nft_exthdr *priv = nft_expr_priv(expr);
0043     u32 *dest = &regs->data[priv->dreg];
0044     unsigned int offset = 0;
0045     int err;
0046 
0047     if (pkt->skb->protocol != htons(ETH_P_IPV6))
0048         goto err;
0049 
0050     err = ipv6_find_hdr(pkt->skb, &offset, priv->type, NULL, NULL);
0051     if (priv->flags & NFT_EXTHDR_F_PRESENT) {
0052         nft_reg_store8(dest, err >= 0);
0053         return;
0054     } else if (err < 0) {
0055         goto err;
0056     }
0057     offset += priv->offset;
0058 
0059     dest[priv->len / NFT_REG32_SIZE] = 0;
0060     if (skb_copy_bits(pkt->skb, offset, dest, priv->len) < 0)
0061         goto err;
0062     return;
0063 err:
0064     regs->verdict.code = NFT_BREAK;
0065 }
0066 
0067 /* find the offset to specified option.
0068  *
0069  * If target header is found, its offset is set in *offset and return option
0070  * number. Otherwise, return negative error.
0071  *
0072  * If the first fragment doesn't contain the End of Options it is considered
0073  * invalid.
0074  */
0075 static int ipv4_find_option(struct net *net, struct sk_buff *skb,
0076                 unsigned int *offset, int target)
0077 {
0078     unsigned char optbuf[sizeof(struct ip_options) + 40];
0079     struct ip_options *opt = (struct ip_options *)optbuf;
0080     struct iphdr *iph, _iph;
0081     unsigned int start;
0082     bool found = false;
0083     __be32 info;
0084     int optlen;
0085 
0086     iph = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
0087     if (!iph)
0088         return -EBADMSG;
0089     start = sizeof(struct iphdr);
0090 
0091     optlen = iph->ihl * 4 - (int)sizeof(struct iphdr);
0092     if (optlen <= 0)
0093         return -ENOENT;
0094 
0095     memset(opt, 0, sizeof(struct ip_options));
0096     /* Copy the options since __ip_options_compile() modifies
0097      * the options.
0098      */
0099     if (skb_copy_bits(skb, start, opt->__data, optlen))
0100         return -EBADMSG;
0101     opt->optlen = optlen;
0102 
0103     if (__ip_options_compile(net, opt, NULL, &info))
0104         return -EBADMSG;
0105 
0106     switch (target) {
0107     case IPOPT_SSRR:
0108     case IPOPT_LSRR:
0109         if (!opt->srr)
0110             break;
0111         found = target == IPOPT_SSRR ? opt->is_strictroute :
0112                            !opt->is_strictroute;
0113         if (found)
0114             *offset = opt->srr + start;
0115         break;
0116     case IPOPT_RR:
0117         if (!opt->rr)
0118             break;
0119         *offset = opt->rr + start;
0120         found = true;
0121         break;
0122     case IPOPT_RA:
0123         if (!opt->router_alert)
0124             break;
0125         *offset = opt->router_alert + start;
0126         found = true;
0127         break;
0128     default:
0129         return -EOPNOTSUPP;
0130     }
0131     return found ? target : -ENOENT;
0132 }
0133 
0134 static void nft_exthdr_ipv4_eval(const struct nft_expr *expr,
0135                  struct nft_regs *regs,
0136                  const struct nft_pktinfo *pkt)
0137 {
0138     struct nft_exthdr *priv = nft_expr_priv(expr);
0139     u32 *dest = &regs->data[priv->dreg];
0140     struct sk_buff *skb = pkt->skb;
0141     unsigned int offset;
0142     int err;
0143 
0144     if (skb->protocol != htons(ETH_P_IP))
0145         goto err;
0146 
0147     err = ipv4_find_option(nft_net(pkt), skb, &offset, priv->type);
0148     if (priv->flags & NFT_EXTHDR_F_PRESENT) {
0149         nft_reg_store8(dest, err >= 0);
0150         return;
0151     } else if (err < 0) {
0152         goto err;
0153     }
0154     offset += priv->offset;
0155 
0156     dest[priv->len / NFT_REG32_SIZE] = 0;
0157     if (skb_copy_bits(pkt->skb, offset, dest, priv->len) < 0)
0158         goto err;
0159     return;
0160 err:
0161     regs->verdict.code = NFT_BREAK;
0162 }
0163 
0164 static void *
0165 nft_tcp_header_pointer(const struct nft_pktinfo *pkt,
0166                unsigned int len, void *buffer, unsigned int *tcphdr_len)
0167 {
0168     struct tcphdr *tcph;
0169 
0170     if (pkt->tprot != IPPROTO_TCP || pkt->fragoff)
0171         return NULL;
0172 
0173     tcph = skb_header_pointer(pkt->skb, nft_thoff(pkt), sizeof(*tcph), buffer);
0174     if (!tcph)
0175         return NULL;
0176 
0177     *tcphdr_len = __tcp_hdrlen(tcph);
0178     if (*tcphdr_len < sizeof(*tcph) || *tcphdr_len > len)
0179         return NULL;
0180 
0181     return skb_header_pointer(pkt->skb, nft_thoff(pkt), *tcphdr_len, buffer);
0182 }
0183 
0184 static void nft_exthdr_tcp_eval(const struct nft_expr *expr,
0185                 struct nft_regs *regs,
0186                 const struct nft_pktinfo *pkt)
0187 {
0188     u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE];
0189     struct nft_exthdr *priv = nft_expr_priv(expr);
0190     unsigned int i, optl, tcphdr_len, offset;
0191     u32 *dest = &regs->data[priv->dreg];
0192     struct tcphdr *tcph;
0193     u8 *opt;
0194 
0195     tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len);
0196     if (!tcph)
0197         goto err;
0198 
0199     opt = (u8 *)tcph;
0200     for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) {
0201         optl = optlen(opt, i);
0202 
0203         if (priv->type != opt[i])
0204             continue;
0205 
0206         if (i + optl > tcphdr_len || priv->len + priv->offset > optl)
0207             goto err;
0208 
0209         offset = i + priv->offset;
0210         if (priv->flags & NFT_EXTHDR_F_PRESENT) {
0211             *dest = 1;
0212         } else {
0213             dest[priv->len / NFT_REG32_SIZE] = 0;
0214             memcpy(dest, opt + offset, priv->len);
0215         }
0216 
0217         return;
0218     }
0219 
0220 err:
0221     if (priv->flags & NFT_EXTHDR_F_PRESENT)
0222         *dest = 0;
0223     else
0224         regs->verdict.code = NFT_BREAK;
0225 }
0226 
0227 static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr,
0228                     struct nft_regs *regs,
0229                     const struct nft_pktinfo *pkt)
0230 {
0231     u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE];
0232     struct nft_exthdr *priv = nft_expr_priv(expr);
0233     unsigned int i, optl, tcphdr_len, offset;
0234     struct tcphdr *tcph;
0235     u8 *opt;
0236 
0237     tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len);
0238     if (!tcph)
0239         goto err;
0240 
0241     opt = (u8 *)tcph;
0242     for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) {
0243         union {
0244             __be16 v16;
0245             __be32 v32;
0246         } old, new;
0247 
0248         optl = optlen(opt, i);
0249 
0250         if (priv->type != opt[i])
0251             continue;
0252 
0253         if (i + optl > tcphdr_len || priv->len + priv->offset > optl)
0254             goto err;
0255 
0256         if (skb_ensure_writable(pkt->skb,
0257                     nft_thoff(pkt) + i + priv->len))
0258             goto err;
0259 
0260         tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff,
0261                           &tcphdr_len);
0262         if (!tcph)
0263             goto err;
0264 
0265         offset = i + priv->offset;
0266 
0267         switch (priv->len) {
0268         case 2:
0269             old.v16 = (__force __be16)get_unaligned((u16 *)(opt + offset));
0270             new.v16 = (__force __be16)nft_reg_load16(
0271                 &regs->data[priv->sreg]);
0272 
0273             switch (priv->type) {
0274             case TCPOPT_MSS:
0275                 /* increase can cause connection to stall */
0276                 if (ntohs(old.v16) <= ntohs(new.v16))
0277                     return;
0278             break;
0279             }
0280 
0281             if (old.v16 == new.v16)
0282                 return;
0283 
0284             put_unaligned(new.v16, (__be16*)(opt + offset));
0285             inet_proto_csum_replace2(&tcph->check, pkt->skb,
0286                          old.v16, new.v16, false);
0287             break;
0288         case 4:
0289             new.v32 = nft_reg_load_be32(&regs->data[priv->sreg]);
0290             old.v32 = (__force __be32)get_unaligned((u32 *)(opt + offset));
0291 
0292             if (old.v32 == new.v32)
0293                 return;
0294 
0295             put_unaligned(new.v32, (__be32*)(opt + offset));
0296             inet_proto_csum_replace4(&tcph->check, pkt->skb,
0297                          old.v32, new.v32, false);
0298             break;
0299         default:
0300             WARN_ON_ONCE(1);
0301             break;
0302         }
0303 
0304         return;
0305     }
0306     return;
0307 err:
0308     regs->verdict.code = NFT_BREAK;
0309 }
0310 
0311 static void nft_exthdr_tcp_strip_eval(const struct nft_expr *expr,
0312                       struct nft_regs *regs,
0313                       const struct nft_pktinfo *pkt)
0314 {
0315     u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE];
0316     struct nft_exthdr *priv = nft_expr_priv(expr);
0317     unsigned int i, tcphdr_len, optl;
0318     struct tcphdr *tcph;
0319     u8 *opt;
0320 
0321     tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len);
0322     if (!tcph)
0323         goto err;
0324 
0325     if (skb_ensure_writable(pkt->skb, nft_thoff(pkt) + tcphdr_len))
0326         goto drop;
0327 
0328     opt = (u8 *)nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len);
0329     if (!opt)
0330         goto err;
0331     for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) {
0332         unsigned int j;
0333 
0334         optl = optlen(opt, i);
0335         if (priv->type != opt[i])
0336             continue;
0337 
0338         if (i + optl > tcphdr_len)
0339             goto drop;
0340 
0341         for (j = 0; j < optl; ++j) {
0342             u16 n = TCPOPT_NOP;
0343             u16 o = opt[i+j];
0344 
0345             if ((i + j) % 2 == 0) {
0346                 o <<= 8;
0347                 n <<= 8;
0348             }
0349             inet_proto_csum_replace2(&tcph->check, pkt->skb, htons(o),
0350                          htons(n), false);
0351         }
0352         memset(opt + i, TCPOPT_NOP, optl);
0353         return;
0354     }
0355 
0356     /* option not found, continue. This allows to do multiple
0357      * option removals per rule.
0358      */
0359     return;
0360 err:
0361     regs->verdict.code = NFT_BREAK;
0362     return;
0363 drop:
0364     /* can't remove, no choice but to drop */
0365     regs->verdict.code = NF_DROP;
0366 }
0367 
0368 static void nft_exthdr_sctp_eval(const struct nft_expr *expr,
0369                  struct nft_regs *regs,
0370                  const struct nft_pktinfo *pkt)
0371 {
0372     unsigned int offset = nft_thoff(pkt) + sizeof(struct sctphdr);
0373     struct nft_exthdr *priv = nft_expr_priv(expr);
0374     u32 *dest = &regs->data[priv->dreg];
0375     const struct sctp_chunkhdr *sch;
0376     struct sctp_chunkhdr _sch;
0377 
0378     if (pkt->tprot != IPPROTO_SCTP)
0379         goto err;
0380 
0381     do {
0382         sch = skb_header_pointer(pkt->skb, offset, sizeof(_sch), &_sch);
0383         if (!sch || !sch->length)
0384             break;
0385 
0386         if (sch->type == priv->type) {
0387             if (priv->flags & NFT_EXTHDR_F_PRESENT) {
0388                 nft_reg_store8(dest, true);
0389                 return;
0390             }
0391             if (priv->offset + priv->len > ntohs(sch->length) ||
0392                 offset + ntohs(sch->length) > pkt->skb->len)
0393                 break;
0394 
0395             dest[priv->len / NFT_REG32_SIZE] = 0;
0396             if (skb_copy_bits(pkt->skb, offset + priv->offset,
0397                       dest, priv->len) < 0)
0398                 break;
0399             return;
0400         }
0401         offset += SCTP_PAD4(ntohs(sch->length));
0402     } while (offset < pkt->skb->len);
0403 err:
0404     if (priv->flags & NFT_EXTHDR_F_PRESENT)
0405         nft_reg_store8(dest, false);
0406     else
0407         regs->verdict.code = NFT_BREAK;
0408 }
0409 
0410 static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = {
0411     [NFTA_EXTHDR_DREG]      = { .type = NLA_U32 },
0412     [NFTA_EXTHDR_TYPE]      = { .type = NLA_U8 },
0413     [NFTA_EXTHDR_OFFSET]        = { .type = NLA_U32 },
0414     [NFTA_EXTHDR_LEN]       = { .type = NLA_U32 },
0415     [NFTA_EXTHDR_FLAGS]     = { .type = NLA_U32 },
0416     [NFTA_EXTHDR_OP]        = { .type = NLA_U32 },
0417     [NFTA_EXTHDR_SREG]      = { .type = NLA_U32 },
0418 };
0419 
0420 static int nft_exthdr_init(const struct nft_ctx *ctx,
0421                const struct nft_expr *expr,
0422                const struct nlattr * const tb[])
0423 {
0424     struct nft_exthdr *priv = nft_expr_priv(expr);
0425     u32 offset, len, flags = 0, op = NFT_EXTHDR_OP_IPV6;
0426     int err;
0427 
0428     if (!tb[NFTA_EXTHDR_DREG] ||
0429         !tb[NFTA_EXTHDR_TYPE] ||
0430         !tb[NFTA_EXTHDR_OFFSET] ||
0431         !tb[NFTA_EXTHDR_LEN])
0432         return -EINVAL;
0433 
0434     err = nft_parse_u32_check(tb[NFTA_EXTHDR_OFFSET], U8_MAX, &offset);
0435     if (err < 0)
0436         return err;
0437 
0438     err = nft_parse_u32_check(tb[NFTA_EXTHDR_LEN], U8_MAX, &len);
0439     if (err < 0)
0440         return err;
0441 
0442     if (tb[NFTA_EXTHDR_FLAGS]) {
0443         err = nft_parse_u32_check(tb[NFTA_EXTHDR_FLAGS], U8_MAX, &flags);
0444         if (err < 0)
0445             return err;
0446 
0447         if (flags & ~NFT_EXTHDR_F_PRESENT)
0448             return -EINVAL;
0449     }
0450 
0451     if (tb[NFTA_EXTHDR_OP]) {
0452         err = nft_parse_u32_check(tb[NFTA_EXTHDR_OP], U8_MAX, &op);
0453         if (err < 0)
0454             return err;
0455     }
0456 
0457     priv->type   = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
0458     priv->offset = offset;
0459     priv->len    = len;
0460     priv->flags  = flags;
0461     priv->op     = op;
0462 
0463     return nft_parse_register_store(ctx, tb[NFTA_EXTHDR_DREG],
0464                     &priv->dreg, NULL, NFT_DATA_VALUE,
0465                     priv->len);
0466 }
0467 
0468 static int nft_exthdr_tcp_set_init(const struct nft_ctx *ctx,
0469                    const struct nft_expr *expr,
0470                    const struct nlattr * const tb[])
0471 {
0472     struct nft_exthdr *priv = nft_expr_priv(expr);
0473     u32 offset, len, flags = 0, op = NFT_EXTHDR_OP_IPV6;
0474     int err;
0475 
0476     if (!tb[NFTA_EXTHDR_SREG] ||
0477         !tb[NFTA_EXTHDR_TYPE] ||
0478         !tb[NFTA_EXTHDR_OFFSET] ||
0479         !tb[NFTA_EXTHDR_LEN])
0480         return -EINVAL;
0481 
0482     if (tb[NFTA_EXTHDR_DREG] || tb[NFTA_EXTHDR_FLAGS])
0483         return -EINVAL;
0484 
0485     err = nft_parse_u32_check(tb[NFTA_EXTHDR_OFFSET], U8_MAX, &offset);
0486     if (err < 0)
0487         return err;
0488 
0489     err = nft_parse_u32_check(tb[NFTA_EXTHDR_LEN], U8_MAX, &len);
0490     if (err < 0)
0491         return err;
0492 
0493     if (offset < 2)
0494         return -EOPNOTSUPP;
0495 
0496     switch (len) {
0497     case 2: break;
0498     case 4: break;
0499     default:
0500         return -EOPNOTSUPP;
0501     }
0502 
0503     err = nft_parse_u32_check(tb[NFTA_EXTHDR_OP], U8_MAX, &op);
0504     if (err < 0)
0505         return err;
0506 
0507     priv->type   = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
0508     priv->offset = offset;
0509     priv->len    = len;
0510     priv->flags  = flags;
0511     priv->op     = op;
0512 
0513     return nft_parse_register_load(tb[NFTA_EXTHDR_SREG], &priv->sreg,
0514                        priv->len);
0515 }
0516 
0517 static int nft_exthdr_tcp_strip_init(const struct nft_ctx *ctx,
0518                      const struct nft_expr *expr,
0519                      const struct nlattr * const tb[])
0520 {
0521     struct nft_exthdr *priv = nft_expr_priv(expr);
0522 
0523     if (tb[NFTA_EXTHDR_SREG] ||
0524         tb[NFTA_EXTHDR_DREG] ||
0525         tb[NFTA_EXTHDR_FLAGS] ||
0526         tb[NFTA_EXTHDR_OFFSET] ||
0527         tb[NFTA_EXTHDR_LEN])
0528         return -EINVAL;
0529 
0530     if (!tb[NFTA_EXTHDR_TYPE])
0531         return -EINVAL;
0532 
0533     priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
0534     priv->op = NFT_EXTHDR_OP_TCPOPT;
0535 
0536     return 0;
0537 }
0538 
0539 static int nft_exthdr_ipv4_init(const struct nft_ctx *ctx,
0540                 const struct nft_expr *expr,
0541                 const struct nlattr * const tb[])
0542 {
0543     struct nft_exthdr *priv = nft_expr_priv(expr);
0544     int err = nft_exthdr_init(ctx, expr, tb);
0545 
0546     if (err < 0)
0547         return err;
0548 
0549     switch (priv->type) {
0550     case IPOPT_SSRR:
0551     case IPOPT_LSRR:
0552     case IPOPT_RR:
0553     case IPOPT_RA:
0554         break;
0555     default:
0556         return -EOPNOTSUPP;
0557     }
0558     return 0;
0559 }
0560 
0561 static int nft_exthdr_dump_common(struct sk_buff *skb, const struct nft_exthdr *priv)
0562 {
0563     if (nla_put_u8(skb, NFTA_EXTHDR_TYPE, priv->type))
0564         goto nla_put_failure;
0565     if (nla_put_be32(skb, NFTA_EXTHDR_OFFSET, htonl(priv->offset)))
0566         goto nla_put_failure;
0567     if (nla_put_be32(skb, NFTA_EXTHDR_LEN, htonl(priv->len)))
0568         goto nla_put_failure;
0569     if (nla_put_be32(skb, NFTA_EXTHDR_FLAGS, htonl(priv->flags)))
0570         goto nla_put_failure;
0571     if (nla_put_be32(skb, NFTA_EXTHDR_OP, htonl(priv->op)))
0572         goto nla_put_failure;
0573     return 0;
0574 
0575 nla_put_failure:
0576     return -1;
0577 }
0578 
0579 static int nft_exthdr_dump(struct sk_buff *skb, const struct nft_expr *expr)
0580 {
0581     const struct nft_exthdr *priv = nft_expr_priv(expr);
0582 
0583     if (nft_dump_register(skb, NFTA_EXTHDR_DREG, priv->dreg))
0584         return -1;
0585 
0586     return nft_exthdr_dump_common(skb, priv);
0587 }
0588 
0589 static int nft_exthdr_dump_set(struct sk_buff *skb, const struct nft_expr *expr)
0590 {
0591     const struct nft_exthdr *priv = nft_expr_priv(expr);
0592 
0593     if (nft_dump_register(skb, NFTA_EXTHDR_SREG, priv->sreg))
0594         return -1;
0595 
0596     return nft_exthdr_dump_common(skb, priv);
0597 }
0598 
0599 static int nft_exthdr_dump_strip(struct sk_buff *skb, const struct nft_expr *expr)
0600 {
0601     const struct nft_exthdr *priv = nft_expr_priv(expr);
0602 
0603     return nft_exthdr_dump_common(skb, priv);
0604 }
0605 
0606 static bool nft_exthdr_reduce(struct nft_regs_track *track,
0607                    const struct nft_expr *expr)
0608 {
0609     const struct nft_exthdr *priv = nft_expr_priv(expr);
0610     const struct nft_exthdr *exthdr;
0611 
0612     if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
0613         nft_reg_track_update(track, expr, priv->dreg, priv->len);
0614         return false;
0615     }
0616 
0617     exthdr = nft_expr_priv(track->regs[priv->dreg].selector);
0618     if (priv->type != exthdr->type ||
0619         priv->op != exthdr->op ||
0620         priv->flags != exthdr->flags ||
0621         priv->offset != exthdr->offset ||
0622         priv->len != exthdr->len) {
0623         nft_reg_track_update(track, expr, priv->dreg, priv->len);
0624         return false;
0625     }
0626 
0627     if (!track->regs[priv->dreg].bitwise)
0628         return true;
0629 
0630     return nft_expr_reduce_bitwise(track, expr);
0631 }
0632 
0633 static const struct nft_expr_ops nft_exthdr_ipv6_ops = {
0634     .type       = &nft_exthdr_type,
0635     .size       = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
0636     .eval       = nft_exthdr_ipv6_eval,
0637     .init       = nft_exthdr_init,
0638     .dump       = nft_exthdr_dump,
0639     .reduce     = nft_exthdr_reduce,
0640 };
0641 
0642 static const struct nft_expr_ops nft_exthdr_ipv4_ops = {
0643     .type       = &nft_exthdr_type,
0644     .size       = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
0645     .eval       = nft_exthdr_ipv4_eval,
0646     .init       = nft_exthdr_ipv4_init,
0647     .dump       = nft_exthdr_dump,
0648     .reduce     = nft_exthdr_reduce,
0649 };
0650 
0651 static const struct nft_expr_ops nft_exthdr_tcp_ops = {
0652     .type       = &nft_exthdr_type,
0653     .size       = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
0654     .eval       = nft_exthdr_tcp_eval,
0655     .init       = nft_exthdr_init,
0656     .dump       = nft_exthdr_dump,
0657     .reduce     = nft_exthdr_reduce,
0658 };
0659 
0660 static const struct nft_expr_ops nft_exthdr_tcp_set_ops = {
0661     .type       = &nft_exthdr_type,
0662     .size       = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
0663     .eval       = nft_exthdr_tcp_set_eval,
0664     .init       = nft_exthdr_tcp_set_init,
0665     .dump       = nft_exthdr_dump_set,
0666     .reduce     = NFT_REDUCE_READONLY,
0667 };
0668 
0669 static const struct nft_expr_ops nft_exthdr_tcp_strip_ops = {
0670     .type       = &nft_exthdr_type,
0671     .size       = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
0672     .eval       = nft_exthdr_tcp_strip_eval,
0673     .init       = nft_exthdr_tcp_strip_init,
0674     .dump       = nft_exthdr_dump_strip,
0675     .reduce     = NFT_REDUCE_READONLY,
0676 };
0677 
0678 static const struct nft_expr_ops nft_exthdr_sctp_ops = {
0679     .type       = &nft_exthdr_type,
0680     .size       = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
0681     .eval       = nft_exthdr_sctp_eval,
0682     .init       = nft_exthdr_init,
0683     .dump       = nft_exthdr_dump,
0684     .reduce     = nft_exthdr_reduce,
0685 };
0686 
0687 static const struct nft_expr_ops *
0688 nft_exthdr_select_ops(const struct nft_ctx *ctx,
0689               const struct nlattr * const tb[])
0690 {
0691     u32 op;
0692 
0693     if (!tb[NFTA_EXTHDR_OP])
0694         return &nft_exthdr_ipv6_ops;
0695 
0696     if (tb[NFTA_EXTHDR_SREG] && tb[NFTA_EXTHDR_DREG])
0697         return ERR_PTR(-EOPNOTSUPP);
0698 
0699     op = ntohl(nla_get_be32(tb[NFTA_EXTHDR_OP]));
0700     switch (op) {
0701     case NFT_EXTHDR_OP_TCPOPT:
0702         if (tb[NFTA_EXTHDR_SREG])
0703             return &nft_exthdr_tcp_set_ops;
0704         if (tb[NFTA_EXTHDR_DREG])
0705             return &nft_exthdr_tcp_ops;
0706         return &nft_exthdr_tcp_strip_ops;
0707     case NFT_EXTHDR_OP_IPV6:
0708         if (tb[NFTA_EXTHDR_DREG])
0709             return &nft_exthdr_ipv6_ops;
0710         break;
0711     case NFT_EXTHDR_OP_IPV4:
0712         if (ctx->family != NFPROTO_IPV6) {
0713             if (tb[NFTA_EXTHDR_DREG])
0714                 return &nft_exthdr_ipv4_ops;
0715         }
0716         break;
0717     case NFT_EXTHDR_OP_SCTP:
0718         if (tb[NFTA_EXTHDR_DREG])
0719             return &nft_exthdr_sctp_ops;
0720         break;
0721     }
0722 
0723     return ERR_PTR(-EOPNOTSUPP);
0724 }
0725 
0726 struct nft_expr_type nft_exthdr_type __read_mostly = {
0727     .name       = "exthdr",
0728     .select_ops = nft_exthdr_select_ops,
0729     .policy     = nft_exthdr_policy,
0730     .maxattr    = NFTA_EXTHDR_MAX,
0731     .owner      = THIS_MODULE,
0732 };