1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
5 * Development of this code funded by Astaro AG (http://www.astaro.com/)
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/sctp/sctp.h>
29 static unsigned int optlen(const u8 *opt, unsigned int offset)
31 /* Beware zero-length options: make finite progress */
32 if (opt[offset] <= TCPOPT_NOP || opt[offset + 1] == 0)
35 return opt[offset + 1];
38 static void nft_exthdr_ipv6_eval(const struct nft_expr *expr,
39 struct nft_regs *regs,
40 const struct nft_pktinfo *pkt)
42 struct nft_exthdr *priv = nft_expr_priv(expr);
43 u32 *dest = ®s->data[priv->dreg];
44 unsigned int offset = 0;
47 err = ipv6_find_hdr(pkt->skb, &offset, priv->type, NULL, NULL);
48 if (priv->flags & NFT_EXTHDR_F_PRESENT) {
49 nft_reg_store8(dest, err >= 0);
54 offset += priv->offset;
56 dest[priv->len / NFT_REG32_SIZE] = 0;
57 if (skb_copy_bits(pkt->skb, offset, dest, priv->len) < 0)
61 regs->verdict.code = NFT_BREAK;
64 /* find the offset to specified option.
66 * If target header is found, its offset is set in *offset and return option
67 * number. Otherwise, return negative error.
69 * If the first fragment doesn't contain the End of Options it is considered
72 static int ipv4_find_option(struct net *net, struct sk_buff *skb,
73 unsigned int *offset, int target)
75 unsigned char optbuf[sizeof(struct ip_options) + 40];
76 struct ip_options *opt = (struct ip_options *)optbuf;
77 struct iphdr *iph, _iph;
83 iph = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
86 start = sizeof(struct iphdr);
88 optlen = iph->ihl * 4 - (int)sizeof(struct iphdr);
92 memset(opt, 0, sizeof(struct ip_options));
93 /* Copy the options since __ip_options_compile() modifies
96 if (skb_copy_bits(skb, start, opt->__data, optlen))
100 if (__ip_options_compile(net, opt, NULL, &info))
108 found = target == IPOPT_SSRR ? opt->is_strictroute :
109 !opt->is_strictroute;
111 *offset = opt->srr + start;
116 *offset = opt->rr + start;
120 if (!opt->router_alert)
122 *offset = opt->router_alert + start;
128 return found ? target : -ENOENT;
131 static void nft_exthdr_ipv4_eval(const struct nft_expr *expr,
132 struct nft_regs *regs,
133 const struct nft_pktinfo *pkt)
135 struct nft_exthdr *priv = nft_expr_priv(expr);
136 u32 *dest = ®s->data[priv->dreg];
137 struct sk_buff *skb = pkt->skb;
141 if (skb->protocol != htons(ETH_P_IP))
144 err = ipv4_find_option(nft_net(pkt), skb, &offset, priv->type);
145 if (priv->flags & NFT_EXTHDR_F_PRESENT) {
146 nft_reg_store8(dest, err >= 0);
148 } else if (err < 0) {
151 offset += priv->offset;
153 dest[priv->len / NFT_REG32_SIZE] = 0;
154 if (skb_copy_bits(pkt->skb, offset, dest, priv->len) < 0)
158 regs->verdict.code = NFT_BREAK;
162 nft_tcp_header_pointer(const struct nft_pktinfo *pkt,
163 unsigned int len, void *buffer, unsigned int *tcphdr_len)
167 if (!pkt->tprot_set || pkt->tprot != IPPROTO_TCP)
170 tcph = skb_header_pointer(pkt->skb, nft_thoff(pkt), sizeof(*tcph), buffer);
174 *tcphdr_len = __tcp_hdrlen(tcph);
175 if (*tcphdr_len < sizeof(*tcph) || *tcphdr_len > len)
178 return skb_header_pointer(pkt->skb, nft_thoff(pkt), *tcphdr_len, buffer);
181 static void nft_exthdr_tcp_eval(const struct nft_expr *expr,
182 struct nft_regs *regs,
183 const struct nft_pktinfo *pkt)
185 u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE];
186 struct nft_exthdr *priv = nft_expr_priv(expr);
187 unsigned int i, optl, tcphdr_len, offset;
188 u32 *dest = ®s->data[priv->dreg];
192 tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len);
197 for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) {
198 optl = optlen(opt, i);
200 if (priv->type != opt[i])
203 if (i + optl > tcphdr_len || priv->len + priv->offset > optl)
206 offset = i + priv->offset;
207 if (priv->flags & NFT_EXTHDR_F_PRESENT) {
210 dest[priv->len / NFT_REG32_SIZE] = 0;
211 memcpy(dest, opt + offset, priv->len);
218 if (priv->flags & NFT_EXTHDR_F_PRESENT)
221 regs->verdict.code = NFT_BREAK;
224 static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr,
225 struct nft_regs *regs,
226 const struct nft_pktinfo *pkt)
228 u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE];
229 struct nft_exthdr *priv = nft_expr_priv(expr);
230 unsigned int i, optl, tcphdr_len, offset;
234 tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len);
239 for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) {
245 optl = optlen(opt, i);
247 if (priv->type != opt[i])
250 if (i + optl > tcphdr_len || priv->len + priv->offset > optl)
253 if (skb_ensure_writable(pkt->skb,
254 nft_thoff(pkt) + i + priv->len))
257 tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff,
262 offset = i + priv->offset;
266 old.v16 = get_unaligned((u16 *)(opt + offset));
267 new.v16 = (__force __be16)nft_reg_load16(
268 ®s->data[priv->sreg]);
270 switch (priv->type) {
272 /* increase can cause connection to stall */
273 if (ntohs(old.v16) <= ntohs(new.v16))
278 if (old.v16 == new.v16)
281 put_unaligned(new.v16, (u16*)(opt + offset));
282 inet_proto_csum_replace2(&tcph->check, pkt->skb,
283 old.v16, new.v16, false);
286 new.v32 = regs->data[priv->sreg];
287 old.v32 = get_unaligned((u32 *)(opt + offset));
289 if (old.v32 == new.v32)
292 put_unaligned(new.v32, (u32*)(opt + offset));
293 inet_proto_csum_replace4(&tcph->check, pkt->skb,
294 old.v32, new.v32, false);
305 static void nft_exthdr_sctp_eval(const struct nft_expr *expr,
306 struct nft_regs *regs,
307 const struct nft_pktinfo *pkt)
309 unsigned int offset = nft_thoff(pkt) + sizeof(struct sctphdr);
310 struct nft_exthdr *priv = nft_expr_priv(expr);
311 u32 *dest = ®s->data[priv->dreg];
312 const struct sctp_chunkhdr *sch;
313 struct sctp_chunkhdr _sch;
316 sch = skb_header_pointer(pkt->skb, offset, sizeof(_sch), &_sch);
317 if (!sch || !sch->length)
320 if (sch->type == priv->type) {
321 if (priv->flags & NFT_EXTHDR_F_PRESENT) {
322 nft_reg_store8(dest, true);
325 if (priv->offset + priv->len > ntohs(sch->length) ||
326 offset + ntohs(sch->length) > pkt->skb->len)
329 dest[priv->len / NFT_REG32_SIZE] = 0;
330 if (skb_copy_bits(pkt->skb, offset + priv->offset,
331 dest, priv->len) < 0)
335 offset += SCTP_PAD4(ntohs(sch->length));
336 } while (offset < pkt->skb->len);
338 if (priv->flags & NFT_EXTHDR_F_PRESENT)
339 nft_reg_store8(dest, false);
341 regs->verdict.code = NFT_BREAK;
344 static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = {
345 [NFTA_EXTHDR_DREG] = { .type = NLA_U32 },
346 [NFTA_EXTHDR_TYPE] = { .type = NLA_U8 },
347 [NFTA_EXTHDR_OFFSET] = { .type = NLA_U32 },
348 [NFTA_EXTHDR_LEN] = { .type = NLA_U32 },
349 [NFTA_EXTHDR_FLAGS] = { .type = NLA_U32 },
350 [NFTA_EXTHDR_OP] = { .type = NLA_U32 },
351 [NFTA_EXTHDR_SREG] = { .type = NLA_U32 },
354 static int nft_exthdr_init(const struct nft_ctx *ctx,
355 const struct nft_expr *expr,
356 const struct nlattr * const tb[])
358 struct nft_exthdr *priv = nft_expr_priv(expr);
359 u32 offset, len, flags = 0, op = NFT_EXTHDR_OP_IPV6;
362 if (!tb[NFTA_EXTHDR_DREG] ||
363 !tb[NFTA_EXTHDR_TYPE] ||
364 !tb[NFTA_EXTHDR_OFFSET] ||
365 !tb[NFTA_EXTHDR_LEN])
368 err = nft_parse_u32_check(tb[NFTA_EXTHDR_OFFSET], U8_MAX, &offset);
372 err = nft_parse_u32_check(tb[NFTA_EXTHDR_LEN], U8_MAX, &len);
376 if (tb[NFTA_EXTHDR_FLAGS]) {
377 err = nft_parse_u32_check(tb[NFTA_EXTHDR_FLAGS], U8_MAX, &flags);
381 if (flags & ~NFT_EXTHDR_F_PRESENT)
385 if (tb[NFTA_EXTHDR_OP]) {
386 err = nft_parse_u32_check(tb[NFTA_EXTHDR_OP], U8_MAX, &op);
391 priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
392 priv->offset = offset;
397 return nft_parse_register_store(ctx, tb[NFTA_EXTHDR_DREG],
398 &priv->dreg, NULL, NFT_DATA_VALUE,
402 static int nft_exthdr_tcp_set_init(const struct nft_ctx *ctx,
403 const struct nft_expr *expr,
404 const struct nlattr * const tb[])
406 struct nft_exthdr *priv = nft_expr_priv(expr);
407 u32 offset, len, flags = 0, op = NFT_EXTHDR_OP_IPV6;
410 if (!tb[NFTA_EXTHDR_SREG] ||
411 !tb[NFTA_EXTHDR_TYPE] ||
412 !tb[NFTA_EXTHDR_OFFSET] ||
413 !tb[NFTA_EXTHDR_LEN])
416 if (tb[NFTA_EXTHDR_DREG] || tb[NFTA_EXTHDR_FLAGS])
419 err = nft_parse_u32_check(tb[NFTA_EXTHDR_OFFSET], U8_MAX, &offset);
423 err = nft_parse_u32_check(tb[NFTA_EXTHDR_LEN], U8_MAX, &len);
437 err = nft_parse_u32_check(tb[NFTA_EXTHDR_OP], U8_MAX, &op);
441 priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
442 priv->offset = offset;
447 return nft_parse_register_load(tb[NFTA_EXTHDR_SREG], &priv->sreg,
451 static int nft_exthdr_ipv4_init(const struct nft_ctx *ctx,
452 const struct nft_expr *expr,
453 const struct nlattr * const tb[])
455 struct nft_exthdr *priv = nft_expr_priv(expr);
456 int err = nft_exthdr_init(ctx, expr, tb);
461 switch (priv->type) {
473 static int nft_exthdr_dump_common(struct sk_buff *skb, const struct nft_exthdr *priv)
475 if (nla_put_u8(skb, NFTA_EXTHDR_TYPE, priv->type))
476 goto nla_put_failure;
477 if (nla_put_be32(skb, NFTA_EXTHDR_OFFSET, htonl(priv->offset)))
478 goto nla_put_failure;
479 if (nla_put_be32(skb, NFTA_EXTHDR_LEN, htonl(priv->len)))
480 goto nla_put_failure;
481 if (nla_put_be32(skb, NFTA_EXTHDR_FLAGS, htonl(priv->flags)))
482 goto nla_put_failure;
483 if (nla_put_be32(skb, NFTA_EXTHDR_OP, htonl(priv->op)))
484 goto nla_put_failure;
491 static int nft_exthdr_dump(struct sk_buff *skb, const struct nft_expr *expr)
493 const struct nft_exthdr *priv = nft_expr_priv(expr);
495 if (nft_dump_register(skb, NFTA_EXTHDR_DREG, priv->dreg))
498 return nft_exthdr_dump_common(skb, priv);
501 static int nft_exthdr_dump_set(struct sk_buff *skb, const struct nft_expr *expr)
503 const struct nft_exthdr *priv = nft_expr_priv(expr);
505 if (nft_dump_register(skb, NFTA_EXTHDR_SREG, priv->sreg))
508 return nft_exthdr_dump_common(skb, priv);
511 static const struct nft_expr_ops nft_exthdr_ipv6_ops = {
512 .type = &nft_exthdr_type,
513 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
514 .eval = nft_exthdr_ipv6_eval,
515 .init = nft_exthdr_init,
516 .dump = nft_exthdr_dump,
519 static const struct nft_expr_ops nft_exthdr_ipv4_ops = {
520 .type = &nft_exthdr_type,
521 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
522 .eval = nft_exthdr_ipv4_eval,
523 .init = nft_exthdr_ipv4_init,
524 .dump = nft_exthdr_dump,
527 static const struct nft_expr_ops nft_exthdr_tcp_ops = {
528 .type = &nft_exthdr_type,
529 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
530 .eval = nft_exthdr_tcp_eval,
531 .init = nft_exthdr_init,
532 .dump = nft_exthdr_dump,
535 static const struct nft_expr_ops nft_exthdr_tcp_set_ops = {
536 .type = &nft_exthdr_type,
537 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
538 .eval = nft_exthdr_tcp_set_eval,
539 .init = nft_exthdr_tcp_set_init,
540 .dump = nft_exthdr_dump_set,
543 static const struct nft_expr_ops nft_exthdr_sctp_ops = {
544 .type = &nft_exthdr_type,
545 .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
546 .eval = nft_exthdr_sctp_eval,
547 .init = nft_exthdr_init,
548 .dump = nft_exthdr_dump,
551 static const struct nft_expr_ops *
552 nft_exthdr_select_ops(const struct nft_ctx *ctx,
553 const struct nlattr * const tb[])
557 if (!tb[NFTA_EXTHDR_OP])
558 return &nft_exthdr_ipv6_ops;
560 if (tb[NFTA_EXTHDR_SREG] && tb[NFTA_EXTHDR_DREG])
561 return ERR_PTR(-EOPNOTSUPP);
563 op = ntohl(nla_get_be32(tb[NFTA_EXTHDR_OP]));
565 case NFT_EXTHDR_OP_TCPOPT:
566 if (tb[NFTA_EXTHDR_SREG])
567 return &nft_exthdr_tcp_set_ops;
568 if (tb[NFTA_EXTHDR_DREG])
569 return &nft_exthdr_tcp_ops;
571 case NFT_EXTHDR_OP_IPV6:
572 if (tb[NFTA_EXTHDR_DREG])
573 return &nft_exthdr_ipv6_ops;
575 case NFT_EXTHDR_OP_IPV4:
576 if (ctx->family != NFPROTO_IPV6) {
577 if (tb[NFTA_EXTHDR_DREG])
578 return &nft_exthdr_ipv4_ops;
581 case NFT_EXTHDR_OP_SCTP:
582 if (tb[NFTA_EXTHDR_DREG])
583 return &nft_exthdr_sctp_ops;
587 return ERR_PTR(-EOPNOTSUPP);
590 struct nft_expr_type nft_exthdr_type __read_mostly = {
592 .select_ops = nft_exthdr_select_ops,
593 .policy = nft_exthdr_policy,
594 .maxattr = NFTA_EXTHDR_MAX,
595 .owner = THIS_MODULE,