Blame view

net/netfilter/nft_exthdr.c 12.7 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
96518518c   Patrick McHardy   netfilter: add nf...
2
3
4
  /*
   * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
   *
96518518c   Patrick McHardy   netfilter: add nf...
5
6
   * Development of this code funded by Astaro AG (http://www.astaro.com/)
   */
99d1712bc   Florian Westphal   netfilter: exthdr...
7
  #include <asm/unaligned.h>
96518518c   Patrick McHardy   netfilter: add nf...
8
  #include <linux/kernel.h>
96518518c   Patrick McHardy   netfilter: add nf...
9
10
11
  #include <linux/netlink.h>
  #include <linux/netfilter.h>
  #include <linux/netfilter/nf_tables.h>
d0103158c   Florian Westphal   netfilter: nf_tab...
12
  #include <net/netfilter/nf_tables_core.h>
96518518c   Patrick McHardy   netfilter: add nf...
13
  #include <net/netfilter/nf_tables.h>
935b7f643   Manuel Messner   netfilter: nft_ex...
14
  #include <net/tcp.h>
96518518c   Patrick McHardy   netfilter: add nf...
15
16
17
18
19
  
  struct nft_exthdr {
  	u8			type;
  	u8			offset;
  	u8			len;
935b7f643   Manuel Messner   netfilter: nft_ex...
20
  	u8			op;
96518518c   Patrick McHardy   netfilter: add nf...
21
  	enum nft_registers	dreg:8;
99d1712bc   Florian Westphal   netfilter: exthdr...
22
  	enum nft_registers	sreg:8;
c078ca3b0   Phil Sutter   netfilter: nft_ex...
23
  	u8			flags;
96518518c   Patrick McHardy   netfilter: add nf...
24
  };
935b7f643   Manuel Messner   netfilter: nft_ex...
25
26
27
28
29
30
31
32
33
34
35
36
  static unsigned int optlen(const u8 *opt, unsigned int offset)
  {
  	/* Beware zero-length options: make finite progress */
  	if (opt[offset] <= TCPOPT_NOP || opt[offset + 1] == 0)
  		return 1;
  	else
  		return opt[offset + 1];
  }
  
  static void nft_exthdr_ipv6_eval(const struct nft_expr *expr,
  				 struct nft_regs *regs,
  				 const struct nft_pktinfo *pkt)
96518518c   Patrick McHardy   netfilter: add nf...
37
38
  {
  	struct nft_exthdr *priv = nft_expr_priv(expr);
49499c3e6   Patrick McHardy   netfilter: nf_tab...
39
  	u32 *dest = &regs->data[priv->dreg];
540436c80   Daniel Borkmann   netfilter: nft_ex...
40
  	unsigned int offset = 0;
96518518c   Patrick McHardy   netfilter: add nf...
41
42
43
  	int err;
  
  	err = ipv6_find_hdr(pkt->skb, &offset, priv->type, NULL, NULL);
c078ca3b0   Phil Sutter   netfilter: nft_ex...
44
  	if (priv->flags & NFT_EXTHDR_F_PRESENT) {
b42833667   Stephen Suryaputra   netfilter: nf_tab...
45
  		nft_reg_store8(dest, err >= 0);
c078ca3b0   Phil Sutter   netfilter: nft_ex...
46
47
  		return;
  	} else if (err < 0) {
96518518c   Patrick McHardy   netfilter: add nf...
48
  		goto err;
c078ca3b0   Phil Sutter   netfilter: nft_ex...
49
  	}
96518518c   Patrick McHardy   netfilter: add nf...
50
  	offset += priv->offset;
49499c3e6   Patrick McHardy   netfilter: nf_tab...
51
  	dest[priv->len / NFT_REG32_SIZE] = 0;
fad136ea0   Patrick McHardy   netfilter: nf_tab...
52
  	if (skb_copy_bits(pkt->skb, offset, dest, priv->len) < 0)
96518518c   Patrick McHardy   netfilter: add nf...
53
54
55
  		goto err;
  	return;
  err:
a55e22e92   Patrick McHardy   netfilter: nf_tab...
56
  	regs->verdict.code = NFT_BREAK;
96518518c   Patrick McHardy   netfilter: add nf...
57
  }
dbb5281a1   Stephen Suryaputra   netfilter: nf_tab...
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
  /* find the offset to specified option.
   *
   * If target header is found, its offset is set in *offset and return option
   * number. Otherwise, return negative error.
   *
   * If the first fragment doesn't contain the End of Options it is considered
   * invalid.
   */
  static int ipv4_find_option(struct net *net, struct sk_buff *skb,
  			    unsigned int *offset, int target)
  {
  	unsigned char optbuf[sizeof(struct ip_options) + 40];
  	struct ip_options *opt = (struct ip_options *)optbuf;
  	struct iphdr *iph, _iph;
  	unsigned int start;
  	bool found = false;
  	__be32 info;
  	int optlen;
  
  	iph = skb_header_pointer(skb, 0, sizeof(_iph), &_iph);
  	if (!iph)
  		return -EBADMSG;
  	start = sizeof(struct iphdr);
  
  	optlen = iph->ihl * 4 - (int)sizeof(struct iphdr);
  	if (optlen <= 0)
  		return -ENOENT;
  
  	memset(opt, 0, sizeof(struct ip_options));
  	/* Copy the options since __ip_options_compile() modifies
  	 * the options.
  	 */
  	if (skb_copy_bits(skb, start, opt->__data, optlen))
  		return -EBADMSG;
  	opt->optlen = optlen;
  
  	if (__ip_options_compile(net, opt, NULL, &info))
  		return -EBADMSG;
  
  	switch (target) {
  	case IPOPT_SSRR:
  	case IPOPT_LSRR:
  		if (!opt->srr)
  			break;
  		found = target == IPOPT_SSRR ? opt->is_strictroute :
  					       !opt->is_strictroute;
  		if (found)
  			*offset = opt->srr + start;
  		break;
  	case IPOPT_RR:
  		if (!opt->rr)
  			break;
  		*offset = opt->rr + start;
  		found = true;
  		break;
  	case IPOPT_RA:
  		if (!opt->router_alert)
  			break;
  		*offset = opt->router_alert + start;
  		found = true;
  		break;
  	default:
  		return -EOPNOTSUPP;
  	}
  	return found ? target : -ENOENT;
  }
  
  static void nft_exthdr_ipv4_eval(const struct nft_expr *expr,
  				 struct nft_regs *regs,
  				 const struct nft_pktinfo *pkt)
  {
  	struct nft_exthdr *priv = nft_expr_priv(expr);
  	u32 *dest = &regs->data[priv->dreg];
  	struct sk_buff *skb = pkt->skb;
  	unsigned int offset;
  	int err;
  
  	if (skb->protocol != htons(ETH_P_IP))
  		goto err;
  
  	err = ipv4_find_option(nft_net(pkt), skb, &offset, priv->type);
  	if (priv->flags & NFT_EXTHDR_F_PRESENT) {
b42833667   Stephen Suryaputra   netfilter: nf_tab...
140
  		nft_reg_store8(dest, err >= 0);
dbb5281a1   Stephen Suryaputra   netfilter: nf_tab...
141
142
143
144
145
146
147
148
149
150
151
152
153
  		return;
  	} else if (err < 0) {
  		goto err;
  	}
  	offset += priv->offset;
  
  	dest[priv->len / NFT_REG32_SIZE] = 0;
  	if (skb_copy_bits(pkt->skb, offset, dest, priv->len) < 0)
  		goto err;
  	return;
  err:
  	regs->verdict.code = NFT_BREAK;
  }
a18177008   Florian Westphal   netfilter: exthdr...
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
  static void *
  nft_tcp_header_pointer(const struct nft_pktinfo *pkt,
  		       unsigned int len, void *buffer, unsigned int *tcphdr_len)
  {
  	struct tcphdr *tcph;
  
  	if (!pkt->tprot_set || pkt->tprot != IPPROTO_TCP)
  		return NULL;
  
  	tcph = skb_header_pointer(pkt->skb, pkt->xt.thoff, sizeof(*tcph), buffer);
  	if (!tcph)
  		return NULL;
  
  	*tcphdr_len = __tcp_hdrlen(tcph);
  	if (*tcphdr_len < sizeof(*tcph) || *tcphdr_len > len)
  		return NULL;
  
  	return skb_header_pointer(pkt->skb, pkt->xt.thoff, *tcphdr_len, buffer);
  }
935b7f643   Manuel Messner   netfilter: nft_ex...
173
174
175
176
177
178
179
180
181
182
  static void nft_exthdr_tcp_eval(const struct nft_expr *expr,
  				struct nft_regs *regs,
  				const struct nft_pktinfo *pkt)
  {
  	u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE];
  	struct nft_exthdr *priv = nft_expr_priv(expr);
  	unsigned int i, optl, tcphdr_len, offset;
  	u32 *dest = &regs->data[priv->dreg];
  	struct tcphdr *tcph;
  	u8 *opt;
a18177008   Florian Westphal   netfilter: exthdr...
183
  	tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len);
935b7f643   Manuel Messner   netfilter: nft_ex...
184
185
186
187
188
189
190
191
192
193
194
195
196
197
  	if (!tcph)
  		goto err;
  
  	opt = (u8 *)tcph;
  	for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) {
  		optl = optlen(opt, i);
  
  		if (priv->type != opt[i])
  			continue;
  
  		if (i + optl > tcphdr_len || priv->len + priv->offset > optl)
  			goto err;
  
  		offset = i + priv->offset;
3c1fece88   Phil Sutter   netfilter: nft_ex...
198
199
200
201
202
203
  		if (priv->flags & NFT_EXTHDR_F_PRESENT) {
  			*dest = 1;
  		} else {
  			dest[priv->len / NFT_REG32_SIZE] = 0;
  			memcpy(dest, opt + offset, priv->len);
  		}
935b7f643   Manuel Messner   netfilter: nft_ex...
204
205
206
207
208
  
  		return;
  	}
  
  err:
3c1fece88   Phil Sutter   netfilter: nft_ex...
209
210
211
212
  	if (priv->flags & NFT_EXTHDR_F_PRESENT)
  		*dest = 0;
  	else
  		regs->verdict.code = NFT_BREAK;
935b7f643   Manuel Messner   netfilter: nft_ex...
213
  }
99d1712bc   Florian Westphal   netfilter: exthdr...
214
215
216
217
218
219
220
221
222
  static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr,
  				    struct nft_regs *regs,
  				    const struct nft_pktinfo *pkt)
  {
  	u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE];
  	struct nft_exthdr *priv = nft_expr_priv(expr);
  	unsigned int i, optl, tcphdr_len, offset;
  	struct tcphdr *tcph;
  	u8 *opt;
99d1712bc   Florian Westphal   netfilter: exthdr...
223
224
225
226
227
228
229
230
  
  	tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len);
  	if (!tcph)
  		return;
  
  	opt = (u8 *)tcph;
  	for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) {
  		union {
99d1712bc   Florian Westphal   netfilter: exthdr...
231
232
233
234
235
236
237
238
239
240
241
  			__be16 v16;
  			__be32 v32;
  		} old, new;
  
  		optl = optlen(opt, i);
  
  		if (priv->type != opt[i])
  			continue;
  
  		if (i + optl > tcphdr_len || priv->len + priv->offset > optl)
  			return;
7418ee4c8   Florian Westphal   netfilter: nf_tab...
242
243
  		if (skb_ensure_writable(pkt->skb,
  					pkt->xt.thoff + i + priv->len))
99d1712bc   Florian Westphal   netfilter: exthdr...
244
245
246
247
248
249
  			return;
  
  		tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff,
  					      &tcphdr_len);
  		if (!tcph)
  			return;
99d1712bc   Florian Westphal   netfilter: exthdr...
250
251
252
253
254
  		offset = i + priv->offset;
  
  		switch (priv->len) {
  		case 2:
  			old.v16 = get_unaligned((u16 *)(opt + offset));
2e34328b3   Sergey Marinkevich   netfilter: nft_ex...
255
256
  			new.v16 = (__force __be16)nft_reg_load16(
  				&regs->data[priv->sreg]);
99d1712bc   Florian Westphal   netfilter: exthdr...
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
  
  			switch (priv->type) {
  			case TCPOPT_MSS:
  				/* increase can cause connection to stall */
  				if (ntohs(old.v16) <= ntohs(new.v16))
  					return;
  			break;
  			}
  
  			if (old.v16 == new.v16)
  				return;
  
  			put_unaligned(new.v16, (u16*)(opt + offset));
  			inet_proto_csum_replace2(&tcph->check, pkt->skb,
  						 old.v16, new.v16, false);
  			break;
  		case 4:
2e34328b3   Sergey Marinkevich   netfilter: nft_ex...
274
  			new.v32 = regs->data[priv->sreg];
99d1712bc   Florian Westphal   netfilter: exthdr...
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
  			old.v32 = get_unaligned((u32 *)(opt + offset));
  
  			if (old.v32 == new.v32)
  				return;
  
  			put_unaligned(new.v32, (u32*)(opt + offset));
  			inet_proto_csum_replace4(&tcph->check, pkt->skb,
  						 old.v32, new.v32, false);
  			break;
  		default:
  			WARN_ON_ONCE(1);
  			break;
  		}
  
  		return;
  	}
  }
96518518c   Patrick McHardy   netfilter: add nf...
292
293
294
295
296
  static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = {
  	[NFTA_EXTHDR_DREG]		= { .type = NLA_U32 },
  	[NFTA_EXTHDR_TYPE]		= { .type = NLA_U8 },
  	[NFTA_EXTHDR_OFFSET]		= { .type = NLA_U32 },
  	[NFTA_EXTHDR_LEN]		= { .type = NLA_U32 },
c078ca3b0   Phil Sutter   netfilter: nft_ex...
297
  	[NFTA_EXTHDR_FLAGS]		= { .type = NLA_U32 },
f5b5702ac   Florian Westphal   netfilter: exthdr...
298
299
  	[NFTA_EXTHDR_OP]		= { .type = NLA_U32 },
  	[NFTA_EXTHDR_SREG]		= { .type = NLA_U32 },
96518518c   Patrick McHardy   netfilter: add nf...
300
301
302
303
304
305
306
  };
  
  static int nft_exthdr_init(const struct nft_ctx *ctx,
  			   const struct nft_expr *expr,
  			   const struct nlattr * const tb[])
  {
  	struct nft_exthdr *priv = nft_expr_priv(expr);
935b7f643   Manuel Messner   netfilter: nft_ex...
307
  	u32 offset, len, flags = 0, op = NFT_EXTHDR_OP_IPV6;
21a9e0f15   Dan Carpenter   netfilter: nft_ex...
308
  	int err;
96518518c   Patrick McHardy   netfilter: add nf...
309

935b7f643   Manuel Messner   netfilter: nft_ex...
310
311
312
313
  	if (!tb[NFTA_EXTHDR_DREG] ||
  	    !tb[NFTA_EXTHDR_TYPE] ||
  	    !tb[NFTA_EXTHDR_OFFSET] ||
  	    !tb[NFTA_EXTHDR_LEN])
96518518c   Patrick McHardy   netfilter: add nf...
314
  		return -EINVAL;
36b701fae   Laura Garcia Liebana   netfilter: nf_tab...
315
316
317
  	err = nft_parse_u32_check(tb[NFTA_EXTHDR_OFFSET], U8_MAX, &offset);
  	if (err < 0)
  		return err;
4da449ae1   Laura Garcia Liebana   netfilter: nft_ex...
318

36b701fae   Laura Garcia Liebana   netfilter: nf_tab...
319
320
321
  	err = nft_parse_u32_check(tb[NFTA_EXTHDR_LEN], U8_MAX, &len);
  	if (err < 0)
  		return err;
4da449ae1   Laura Garcia Liebana   netfilter: nft_ex...
322

c078ca3b0   Phil Sutter   netfilter: nft_ex...
323
324
325
326
327
328
329
330
  	if (tb[NFTA_EXTHDR_FLAGS]) {
  		err = nft_parse_u32_check(tb[NFTA_EXTHDR_FLAGS], U8_MAX, &flags);
  		if (err < 0)
  			return err;
  
  		if (flags & ~NFT_EXTHDR_F_PRESENT)
  			return -EINVAL;
  	}
935b7f643   Manuel Messner   netfilter: nft_ex...
331
332
333
334
335
  	if (tb[NFTA_EXTHDR_OP]) {
  		err = nft_parse_u32_check(tb[NFTA_EXTHDR_OP], U8_MAX, &op);
  		if (err < 0)
  			return err;
  	}
96518518c   Patrick McHardy   netfilter: add nf...
336
  	priv->type   = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
4da449ae1   Laura Garcia Liebana   netfilter: nft_ex...
337
338
  	priv->offset = offset;
  	priv->len    = len;
b1c96ed37   Patrick McHardy   netfilter: nf_tab...
339
  	priv->dreg   = nft_parse_register(tb[NFTA_EXTHDR_DREG]);
c078ca3b0   Phil Sutter   netfilter: nft_ex...
340
  	priv->flags  = flags;
935b7f643   Manuel Messner   netfilter: nft_ex...
341
  	priv->op     = op;
96518518c   Patrick McHardy   netfilter: add nf...
342

1ec10212f   Patrick McHardy   netfilter: nf_tab...
343
344
  	return nft_validate_register_store(ctx, priv->dreg, NULL,
  					   NFT_DATA_VALUE, priv->len);
96518518c   Patrick McHardy   netfilter: add nf...
345
  }
99d1712bc   Florian Westphal   netfilter: exthdr...
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
  static int nft_exthdr_tcp_set_init(const struct nft_ctx *ctx,
  				   const struct nft_expr *expr,
  				   const struct nlattr * const tb[])
  {
  	struct nft_exthdr *priv = nft_expr_priv(expr);
  	u32 offset, len, flags = 0, op = NFT_EXTHDR_OP_IPV6;
  	int err;
  
  	if (!tb[NFTA_EXTHDR_SREG] ||
  	    !tb[NFTA_EXTHDR_TYPE] ||
  	    !tb[NFTA_EXTHDR_OFFSET] ||
  	    !tb[NFTA_EXTHDR_LEN])
  		return -EINVAL;
  
  	if (tb[NFTA_EXTHDR_DREG] || tb[NFTA_EXTHDR_FLAGS])
  		return -EINVAL;
  
  	err = nft_parse_u32_check(tb[NFTA_EXTHDR_OFFSET], U8_MAX, &offset);
  	if (err < 0)
  		return err;
  
  	err = nft_parse_u32_check(tb[NFTA_EXTHDR_LEN], U8_MAX, &len);
  	if (err < 0)
  		return err;
  
  	if (offset < 2)
  		return -EOPNOTSUPP;
  
  	switch (len) {
  	case 2: break;
  	case 4: break;
  	default:
  		return -EOPNOTSUPP;
  	}
  
  	err = nft_parse_u32_check(tb[NFTA_EXTHDR_OP], U8_MAX, &op);
  	if (err < 0)
  		return err;
  
  	priv->type   = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
  	priv->offset = offset;
  	priv->len    = len;
  	priv->sreg   = nft_parse_register(tb[NFTA_EXTHDR_SREG]);
  	priv->flags  = flags;
  	priv->op     = op;
  
  	return nft_validate_register_load(priv->sreg, priv->len);
  }
dbb5281a1   Stephen Suryaputra   netfilter: nf_tab...
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
  static int nft_exthdr_ipv4_init(const struct nft_ctx *ctx,
  				const struct nft_expr *expr,
  				const struct nlattr * const tb[])
  {
  	struct nft_exthdr *priv = nft_expr_priv(expr);
  	int err = nft_exthdr_init(ctx, expr, tb);
  
  	if (err < 0)
  		return err;
  
  	switch (priv->type) {
  	case IPOPT_SSRR:
  	case IPOPT_LSRR:
  	case IPOPT_RR:
  	case IPOPT_RA:
  		break;
  	default:
  		return -EOPNOTSUPP;
  	}
  	return 0;
  }
5e7d695a4   Florian Westphal   netfilter: exthdr...
415
  static int nft_exthdr_dump_common(struct sk_buff *skb, const struct nft_exthdr *priv)
96518518c   Patrick McHardy   netfilter: add nf...
416
  {
96518518c   Patrick McHardy   netfilter: add nf...
417
418
419
420
421
422
  	if (nla_put_u8(skb, NFTA_EXTHDR_TYPE, priv->type))
  		goto nla_put_failure;
  	if (nla_put_be32(skb, NFTA_EXTHDR_OFFSET, htonl(priv->offset)))
  		goto nla_put_failure;
  	if (nla_put_be32(skb, NFTA_EXTHDR_LEN, htonl(priv->len)))
  		goto nla_put_failure;
c078ca3b0   Phil Sutter   netfilter: nft_ex...
423
424
  	if (nla_put_be32(skb, NFTA_EXTHDR_FLAGS, htonl(priv->flags)))
  		goto nla_put_failure;
935b7f643   Manuel Messner   netfilter: nft_ex...
425
426
  	if (nla_put_be32(skb, NFTA_EXTHDR_OP, htonl(priv->op)))
  		goto nla_put_failure;
96518518c   Patrick McHardy   netfilter: add nf...
427
428
429
430
431
  	return 0;
  
  nla_put_failure:
  	return -1;
  }
5e7d695a4   Florian Westphal   netfilter: exthdr...
432
433
434
435
436
437
438
439
440
  static int nft_exthdr_dump(struct sk_buff *skb, const struct nft_expr *expr)
  {
  	const struct nft_exthdr *priv = nft_expr_priv(expr);
  
  	if (nft_dump_register(skb, NFTA_EXTHDR_DREG, priv->dreg))
  		return -1;
  
  	return nft_exthdr_dump_common(skb, priv);
  }
99d1712bc   Florian Westphal   netfilter: exthdr...
441
442
443
444
445
446
447
448
449
  static int nft_exthdr_dump_set(struct sk_buff *skb, const struct nft_expr *expr)
  {
  	const struct nft_exthdr *priv = nft_expr_priv(expr);
  
  	if (nft_dump_register(skb, NFTA_EXTHDR_SREG, priv->sreg))
  		return -1;
  
  	return nft_exthdr_dump_common(skb, priv);
  }
935b7f643   Manuel Messner   netfilter: nft_ex...
450
451
452
453
454
455
456
  static const struct nft_expr_ops nft_exthdr_ipv6_ops = {
  	.type		= &nft_exthdr_type,
  	.size		= NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
  	.eval		= nft_exthdr_ipv6_eval,
  	.init		= nft_exthdr_init,
  	.dump		= nft_exthdr_dump,
  };
dbb5281a1   Stephen Suryaputra   netfilter: nf_tab...
457
458
459
460
461
462
463
  static const struct nft_expr_ops nft_exthdr_ipv4_ops = {
  	.type		= &nft_exthdr_type,
  	.size		= NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
  	.eval		= nft_exthdr_ipv4_eval,
  	.init		= nft_exthdr_ipv4_init,
  	.dump		= nft_exthdr_dump,
  };
935b7f643   Manuel Messner   netfilter: nft_ex...
464
  static const struct nft_expr_ops nft_exthdr_tcp_ops = {
ef1f7df91   Patrick McHardy   netfilter: nf_tab...
465
  	.type		= &nft_exthdr_type,
96518518c   Patrick McHardy   netfilter: add nf...
466
  	.size		= NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
935b7f643   Manuel Messner   netfilter: nft_ex...
467
  	.eval		= nft_exthdr_tcp_eval,
96518518c   Patrick McHardy   netfilter: add nf...
468
469
  	.init		= nft_exthdr_init,
  	.dump		= nft_exthdr_dump,
ef1f7df91   Patrick McHardy   netfilter: nf_tab...
470
  };
99d1712bc   Florian Westphal   netfilter: exthdr...
471
472
473
474
475
476
477
  static const struct nft_expr_ops nft_exthdr_tcp_set_ops = {
  	.type		= &nft_exthdr_type,
  	.size		= NFT_EXPR_SIZE(sizeof(struct nft_exthdr)),
  	.eval		= nft_exthdr_tcp_set_eval,
  	.init		= nft_exthdr_tcp_set_init,
  	.dump		= nft_exthdr_dump_set,
  };
935b7f643   Manuel Messner   netfilter: nft_ex...
478
479
480
481
482
483
484
485
  static const struct nft_expr_ops *
  nft_exthdr_select_ops(const struct nft_ctx *ctx,
  		      const struct nlattr * const tb[])
  {
  	u32 op;
  
  	if (!tb[NFTA_EXTHDR_OP])
  		return &nft_exthdr_ipv6_ops;
99d1712bc   Florian Westphal   netfilter: exthdr...
486
487
  	if (tb[NFTA_EXTHDR_SREG] && tb[NFTA_EXTHDR_DREG])
  		return ERR_PTR(-EOPNOTSUPP);
5fd02ebe6   Florian Westphal   netfilter: fix a ...
488
  	op = ntohl(nla_get_be32(tb[NFTA_EXTHDR_OP]));
935b7f643   Manuel Messner   netfilter: nft_ex...
489
490
  	switch (op) {
  	case NFT_EXTHDR_OP_TCPOPT:
99d1712bc   Florian Westphal   netfilter: exthdr...
491
492
493
494
495
  		if (tb[NFTA_EXTHDR_SREG])
  			return &nft_exthdr_tcp_set_ops;
  		if (tb[NFTA_EXTHDR_DREG])
  			return &nft_exthdr_tcp_ops;
  		break;
935b7f643   Manuel Messner   netfilter: nft_ex...
496
  	case NFT_EXTHDR_OP_IPV6:
99d1712bc   Florian Westphal   netfilter: exthdr...
497
498
499
  		if (tb[NFTA_EXTHDR_DREG])
  			return &nft_exthdr_ipv6_ops;
  		break;
dbb5281a1   Stephen Suryaputra   netfilter: nf_tab...
500
501
502
503
504
505
  	case NFT_EXTHDR_OP_IPV4:
  		if (ctx->family != NFPROTO_IPV6) {
  			if (tb[NFTA_EXTHDR_DREG])
  				return &nft_exthdr_ipv4_ops;
  		}
  		break;
935b7f643   Manuel Messner   netfilter: nft_ex...
506
507
508
509
  	}
  
  	return ERR_PTR(-EOPNOTSUPP);
  }
d0103158c   Florian Westphal   netfilter: nf_tab...
510
  struct nft_expr_type nft_exthdr_type __read_mostly = {
ef1f7df91   Patrick McHardy   netfilter: nf_tab...
511
  	.name		= "exthdr",
d4ef38354   Arushi Singhal   netfilter: Remove...
512
  	.select_ops	= nft_exthdr_select_ops,
96518518c   Patrick McHardy   netfilter: add nf...
513
514
  	.policy		= nft_exthdr_policy,
  	.maxattr	= NFTA_EXTHDR_MAX,
ef1f7df91   Patrick McHardy   netfilter: nf_tab...
515
  	.owner		= THIS_MODULE,
96518518c   Patrick McHardy   netfilter: add nf...
516
  };