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
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	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
141static 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 = &regs->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;
166err:
167	regs->verdict.code = NFT_BREAK;
168}
169
170static void *
171nft_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
190static 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 = &regs->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
227err:
228	if (priv->flags & NFT_EXTHDR_F_PRESENT)
229		*dest = 0;
230	else
231		regs->verdict.code = NFT_BREAK;
232}
233
234static 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				&regs->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(&regs->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;
310err:
311	regs->verdict.code = NFT_BREAK;
312}
313
314static 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;
363err:
364	regs->verdict.code = NFT_BREAK;
365	return;
366drop:
367	/* can't remove, no choice but to drop */
368	regs->verdict.code = NF_DROP;
369}
370
371static 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 = &regs->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);
405err:
406	if (priv->flags & NFT_EXTHDR_F_PRESENT)
407		nft_reg_store8(dest, false);
408	else
409		regs->verdict.code = NFT_BREAK;
410}
411
412static 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 = &regs->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
484err:
485	*dest = 0;
486}
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(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
639static 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
655static 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
669nla_put_failure:
670	return -1;
671}
672
673static 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
684static 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
695static 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
703static 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
730static 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
739static 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
748static 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
757static 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
766static 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
775static 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
784static 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
793static const struct nft_expr_ops *
794nft_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
836struct 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