Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
96518518 | 2 | /* |
ef1f7df9 | 3 | * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net> |
25443261 | 4 | * Copyright (c) 2016 Pablo Neira Ayuso <pablo@netfilter.org> |
96518518 | 5 | * |
96518518 PM |
6 | * Development of this code funded by Astaro AG (http://www.astaro.com/) |
7 | */ | |
8 | ||
9 | #include <linux/kernel.h> | |
10 | #include <linux/init.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/netlink.h> | |
13 | #include <linux/netfilter.h> | |
14 | #include <linux/netfilter/nf_tables.h> | |
15 | #include <net/netfilter/nf_tables.h> | |
16 | #include <net/netfilter/nf_conntrack.h> | |
48f66c90 | 17 | #include <net/netfilter/nf_conntrack_acct.h> |
96518518 PM |
18 | #include <net/netfilter/nf_conntrack_tuple.h> |
19 | #include <net/netfilter/nf_conntrack_helper.h> | |
c4ede3d3 | 20 | #include <net/netfilter/nf_conntrack_ecache.h> |
d2bf2f34 | 21 | #include <net/netfilter/nf_conntrack_labels.h> |
7e0b2b57 HS |
22 | #include <net/netfilter/nf_conntrack_timeout.h> |
23 | #include <net/netfilter/nf_conntrack_l4proto.h> | |
857b4602 | 24 | #include <net/netfilter/nf_conntrack_expect.h> |
96518518 PM |
25 | |
26 | struct nft_ct { | |
27 | enum nft_ct_keys key:8; | |
28 | enum ip_conntrack_dir dir:8; | |
d46f2cd2 | 29 | union { |
c4ede3d3 KE |
30 | enum nft_registers dreg:8; |
31 | enum nft_registers sreg:8; | |
32 | }; | |
96518518 PM |
33 | }; |
34 | ||
1a64edf5 FW |
35 | struct nft_ct_helper_obj { |
36 | struct nf_conntrack_helper *helper4; | |
37 | struct nf_conntrack_helper *helper6; | |
38 | u8 l4proto; | |
39 | }; | |
40 | ||
edee4f1e FW |
41 | #ifdef CONFIG_NF_CONNTRACK_ZONES |
42 | static DEFINE_PER_CPU(struct nf_conn *, nft_ct_pcpu_template); | |
43 | static unsigned int nft_ct_pcpu_template_refcnt __read_mostly; | |
44 | #endif | |
45 | ||
48f66c90 FW |
46 | static u64 nft_ct_get_eval_counter(const struct nf_conn_counter *c, |
47 | enum nft_ct_keys k, | |
48 | enum ip_conntrack_dir d) | |
49 | { | |
50 | if (d < IP_CT_DIR_MAX) | |
51 | return k == NFT_CT_BYTES ? atomic64_read(&c[d].bytes) : | |
52 | atomic64_read(&c[d].packets); | |
53 | ||
54 | return nft_ct_get_eval_counter(c, k, IP_CT_DIR_ORIGINAL) + | |
55 | nft_ct_get_eval_counter(c, k, IP_CT_DIR_REPLY); | |
56 | } | |
57 | ||
c4ede3d3 | 58 | static void nft_ct_get_eval(const struct nft_expr *expr, |
a55e22e9 | 59 | struct nft_regs *regs, |
c4ede3d3 | 60 | const struct nft_pktinfo *pkt) |
96518518 PM |
61 | { |
62 | const struct nft_ct *priv = nft_expr_priv(expr); | |
49499c3e | 63 | u32 *dest = ®s->data[priv->dreg]; |
96518518 PM |
64 | enum ip_conntrack_info ctinfo; |
65 | const struct nf_conn *ct; | |
66 | const struct nf_conn_help *help; | |
67 | const struct nf_conntrack_tuple *tuple; | |
68 | const struct nf_conntrack_helper *helper; | |
96518518 PM |
69 | unsigned int state; |
70 | ||
71 | ct = nf_ct_get(pkt->skb, &ctinfo); | |
72 | ||
73 | switch (priv->key) { | |
74 | case NFT_CT_STATE: | |
cc41c84b FW |
75 | if (ct) |
76 | state = NF_CT_STATE_BIT(ctinfo); | |
77 | else if (ctinfo == IP_CT_UNTRACKED) | |
96518518 PM |
78 | state = NF_CT_STATE_UNTRACKED_BIT; |
79 | else | |
cc41c84b | 80 | state = NF_CT_STATE_INVALID_BIT; |
fad136ea | 81 | *dest = state; |
96518518 | 82 | return; |
c1f86676 DM |
83 | default: |
84 | break; | |
96518518 PM |
85 | } |
86 | ||
87 | if (ct == NULL) | |
88 | goto err; | |
89 | ||
90 | switch (priv->key) { | |
91 | case NFT_CT_DIRECTION: | |
10596608 | 92 | nft_reg_store8(dest, CTINFO2DIR(ctinfo)); |
96518518 PM |
93 | return; |
94 | case NFT_CT_STATUS: | |
fad136ea | 95 | *dest = ct->status; |
96518518 PM |
96 | return; |
97 | #ifdef CONFIG_NF_CONNTRACK_MARK | |
98 | case NFT_CT_MARK: | |
fad136ea | 99 | *dest = ct->mark; |
96518518 PM |
100 | return; |
101 | #endif | |
102 | #ifdef CONFIG_NF_CONNTRACK_SECMARK | |
103 | case NFT_CT_SECMARK: | |
fad136ea | 104 | *dest = ct->secmark; |
96518518 PM |
105 | return; |
106 | #endif | |
107 | case NFT_CT_EXPIRATION: | |
c8607e02 | 108 | *dest = jiffies_to_msecs(nf_ct_expires(ct)); |
96518518 PM |
109 | return; |
110 | case NFT_CT_HELPER: | |
111 | if (ct->master == NULL) | |
112 | goto err; | |
113 | help = nfct_help(ct->master); | |
114 | if (help == NULL) | |
115 | goto err; | |
116 | helper = rcu_dereference(help->helper); | |
117 | if (helper == NULL) | |
118 | goto err; | |
fad136ea | 119 | strncpy((char *)dest, helper->name, NF_CT_HELPER_NAME_LEN); |
96518518 | 120 | return; |
d2bf2f34 FW |
121 | #ifdef CONFIG_NF_CONNTRACK_LABELS |
122 | case NFT_CT_LABELS: { | |
123 | struct nf_conn_labels *labels = nf_ct_labels_find(ct); | |
d2bf2f34 | 124 | |
23014011 FW |
125 | if (labels) |
126 | memcpy(dest, labels->bits, NF_CT_LABELS_MAX_SIZE); | |
127 | else | |
fad136ea | 128 | memset(dest, 0, NF_CT_LABELS_MAX_SIZE); |
d2bf2f34 FW |
129 | return; |
130 | } | |
efaea94a | 131 | #endif |
48f66c90 FW |
132 | case NFT_CT_BYTES: /* fallthrough */ |
133 | case NFT_CT_PKTS: { | |
134 | const struct nf_conn_acct *acct = nf_conn_acct_find(ct); | |
135 | u64 count = 0; | |
136 | ||
137 | if (acct) | |
138 | count = nft_ct_get_eval_counter(acct->counter, | |
139 | priv->key, priv->dir); | |
140 | memcpy(dest, &count, sizeof(count)); | |
141 | return; | |
142 | } | |
949a3584 LZ |
143 | case NFT_CT_AVGPKT: { |
144 | const struct nf_conn_acct *acct = nf_conn_acct_find(ct); | |
145 | u64 avgcnt = 0, bcnt = 0, pcnt = 0; | |
146 | ||
147 | if (acct) { | |
148 | pcnt = nft_ct_get_eval_counter(acct->counter, | |
149 | NFT_CT_PKTS, priv->dir); | |
150 | bcnt = nft_ct_get_eval_counter(acct->counter, | |
151 | NFT_CT_BYTES, priv->dir); | |
152 | if (pcnt != 0) | |
153 | avgcnt = div64_u64(bcnt, pcnt); | |
154 | } | |
155 | ||
156 | memcpy(dest, &avgcnt, sizeof(avgcnt)); | |
157 | return; | |
158 | } | |
d767ff2c | 159 | case NFT_CT_L3PROTOCOL: |
10596608 | 160 | nft_reg_store8(dest, nf_ct_l3num(ct)); |
d767ff2c LZ |
161 | return; |
162 | case NFT_CT_PROTOCOL: | |
10596608 | 163 | nft_reg_store8(dest, nf_ct_protonum(ct)); |
d767ff2c | 164 | return; |
ab23821f FW |
165 | #ifdef CONFIG_NF_CONNTRACK_ZONES |
166 | case NFT_CT_ZONE: { | |
167 | const struct nf_conntrack_zone *zone = nf_ct_zone(ct); | |
10596608 | 168 | u16 zoneid; |
ab23821f FW |
169 | |
170 | if (priv->dir < IP_CT_DIR_MAX) | |
10596608 | 171 | zoneid = nf_ct_zone_id(zone, priv->dir); |
ab23821f | 172 | else |
10596608 | 173 | zoneid = zone->id; |
ab23821f | 174 | |
10596608 | 175 | nft_reg_store16(dest, zoneid); |
ab23821f FW |
176 | return; |
177 | } | |
178 | #endif | |
3087c3f7 BM |
179 | case NFT_CT_ID: |
180 | if (!nf_ct_is_confirmed(ct)) | |
181 | goto err; | |
182 | *dest = nf_ct_get_id(ct); | |
183 | return; | |
c1f86676 DM |
184 | default: |
185 | break; | |
96518518 PM |
186 | } |
187 | ||
188 | tuple = &ct->tuplehash[priv->dir].tuple; | |
189 | switch (priv->key) { | |
96518518 | 190 | case NFT_CT_SRC: |
fad136ea | 191 | memcpy(dest, tuple->src.u3.all, |
96518518 PM |
192 | nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16); |
193 | return; | |
194 | case NFT_CT_DST: | |
fad136ea | 195 | memcpy(dest, tuple->dst.u3.all, |
96518518 PM |
196 | nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16); |
197 | return; | |
96518518 | 198 | case NFT_CT_PROTO_SRC: |
10596608 | 199 | nft_reg_store16(dest, (__force u16)tuple->src.u.all); |
96518518 PM |
200 | return; |
201 | case NFT_CT_PROTO_DST: | |
10596608 | 202 | nft_reg_store16(dest, (__force u16)tuple->dst.u.all); |
96518518 | 203 | return; |
d719e3f2 PNA |
204 | case NFT_CT_SRC_IP: |
205 | if (nf_ct_l3num(ct) != NFPROTO_IPV4) | |
206 | goto err; | |
207 | *dest = tuple->src.u3.ip; | |
208 | return; | |
209 | case NFT_CT_DST_IP: | |
210 | if (nf_ct_l3num(ct) != NFPROTO_IPV4) | |
211 | goto err; | |
212 | *dest = tuple->dst.u3.ip; | |
213 | return; | |
214 | case NFT_CT_SRC_IP6: | |
215 | if (nf_ct_l3num(ct) != NFPROTO_IPV6) | |
216 | goto err; | |
217 | memcpy(dest, tuple->src.u3.ip6, sizeof(struct in6_addr)); | |
218 | return; | |
219 | case NFT_CT_DST_IP6: | |
220 | if (nf_ct_l3num(ct) != NFPROTO_IPV6) | |
221 | goto err; | |
222 | memcpy(dest, tuple->dst.u3.ip6, sizeof(struct in6_addr)); | |
223 | return; | |
c1f86676 DM |
224 | default: |
225 | break; | |
96518518 PM |
226 | } |
227 | return; | |
228 | err: | |
a55e22e9 | 229 | regs->verdict.code = NFT_BREAK; |
96518518 PM |
230 | } |
231 | ||
edee4f1e FW |
232 | #ifdef CONFIG_NF_CONNTRACK_ZONES |
233 | static void nft_ct_set_zone_eval(const struct nft_expr *expr, | |
234 | struct nft_regs *regs, | |
235 | const struct nft_pktinfo *pkt) | |
236 | { | |
237 | struct nf_conntrack_zone zone = { .dir = NF_CT_DEFAULT_ZONE_DIR }; | |
238 | const struct nft_ct *priv = nft_expr_priv(expr); | |
239 | struct sk_buff *skb = pkt->skb; | |
240 | enum ip_conntrack_info ctinfo; | |
10596608 | 241 | u16 value = nft_reg_load16(®s->data[priv->sreg]); |
edee4f1e FW |
242 | struct nf_conn *ct; |
243 | ||
244 | ct = nf_ct_get(skb, &ctinfo); | |
245 | if (ct) /* already tracked */ | |
246 | return; | |
247 | ||
248 | zone.id = value; | |
249 | ||
250 | switch (priv->dir) { | |
251 | case IP_CT_DIR_ORIGINAL: | |
252 | zone.dir = NF_CT_ZONE_DIR_ORIG; | |
253 | break; | |
254 | case IP_CT_DIR_REPLY: | |
255 | zone.dir = NF_CT_ZONE_DIR_REPL; | |
256 | break; | |
257 | default: | |
258 | break; | |
259 | } | |
260 | ||
261 | ct = this_cpu_read(nft_ct_pcpu_template); | |
262 | ||
263 | if (likely(atomic_read(&ct->ct_general.use) == 1)) { | |
264 | nf_ct_zone_add(ct, &zone); | |
265 | } else { | |
266 | /* previous skb got queued to userspace */ | |
267 | ct = nf_ct_tmpl_alloc(nft_net(pkt), &zone, GFP_ATOMIC); | |
268 | if (!ct) { | |
269 | regs->verdict.code = NF_DROP; | |
270 | return; | |
271 | } | |
272 | } | |
273 | ||
274 | atomic_inc(&ct->ct_general.use); | |
275 | nf_ct_set(skb, ct, IP_CT_NEW); | |
276 | } | |
277 | #endif | |
278 | ||
c4ede3d3 | 279 | static void nft_ct_set_eval(const struct nft_expr *expr, |
a55e22e9 | 280 | struct nft_regs *regs, |
c4ede3d3 KE |
281 | const struct nft_pktinfo *pkt) |
282 | { | |
283 | const struct nft_ct *priv = nft_expr_priv(expr); | |
284 | struct sk_buff *skb = pkt->skb; | |
b473a1f5 | 285 | #if defined(CONFIG_NF_CONNTRACK_MARK) || defined(CONFIG_NF_CONNTRACK_SECMARK) |
49499c3e | 286 | u32 value = regs->data[priv->sreg]; |
847c8e29 | 287 | #endif |
c4ede3d3 KE |
288 | enum ip_conntrack_info ctinfo; |
289 | struct nf_conn *ct; | |
290 | ||
291 | ct = nf_ct_get(skb, &ctinfo); | |
694a0055 | 292 | if (ct == NULL || nf_ct_is_template(ct)) |
c4ede3d3 KE |
293 | return; |
294 | ||
295 | switch (priv->key) { | |
296 | #ifdef CONFIG_NF_CONNTRACK_MARK | |
297 | case NFT_CT_MARK: | |
298 | if (ct->mark != value) { | |
299 | ct->mark = value; | |
300 | nf_conntrack_event_cache(IPCT_MARK, ct); | |
301 | } | |
302 | break; | |
1ad8f48d | 303 | #endif |
b473a1f5 CG |
304 | #ifdef CONFIG_NF_CONNTRACK_SECMARK |
305 | case NFT_CT_SECMARK: | |
306 | if (ct->secmark != value) { | |
307 | ct->secmark = value; | |
308 | nf_conntrack_event_cache(IPCT_SECMARK, ct); | |
309 | } | |
310 | break; | |
311 | #endif | |
1ad8f48d FW |
312 | #ifdef CONFIG_NF_CONNTRACK_LABELS |
313 | case NFT_CT_LABELS: | |
314 | nf_connlabels_replace(ct, | |
315 | ®s->data[priv->sreg], | |
316 | ®s->data[priv->sreg], | |
317 | NF_CT_LABELS_MAX_SIZE / sizeof(u32)); | |
318 | break; | |
694a0055 FW |
319 | #endif |
320 | #ifdef CONFIG_NF_CONNTRACK_EVENTS | |
321 | case NFT_CT_EVENTMASK: { | |
322 | struct nf_conntrack_ecache *e = nf_ct_ecache_find(ct); | |
323 | u32 ctmask = regs->data[priv->sreg]; | |
324 | ||
325 | if (e) { | |
326 | if (e->ctmask != ctmask) | |
327 | e->ctmask = ctmask; | |
328 | break; | |
329 | } | |
330 | ||
331 | if (ctmask && !nf_ct_is_confirmed(ct)) | |
332 | nf_ct_ecache_ext_add(ct, ctmask, 0, GFP_ATOMIC); | |
333 | break; | |
334 | } | |
c4ede3d3 | 335 | #endif |
c1f86676 DM |
336 | default: |
337 | break; | |
c4ede3d3 KE |
338 | } |
339 | } | |
340 | ||
96518518 PM |
341 | static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = { |
342 | [NFTA_CT_DREG] = { .type = NLA_U32 }, | |
343 | [NFTA_CT_KEY] = { .type = NLA_U32 }, | |
344 | [NFTA_CT_DIRECTION] = { .type = NLA_U8 }, | |
c4ede3d3 | 345 | [NFTA_CT_SREG] = { .type = NLA_U32 }, |
96518518 PM |
346 | }; |
347 | ||
edee4f1e FW |
348 | #ifdef CONFIG_NF_CONNTRACK_ZONES |
349 | static void nft_ct_tmpl_put_pcpu(void) | |
350 | { | |
351 | struct nf_conn *ct; | |
352 | int cpu; | |
353 | ||
354 | for_each_possible_cpu(cpu) { | |
355 | ct = per_cpu(nft_ct_pcpu_template, cpu); | |
356 | if (!ct) | |
357 | break; | |
358 | nf_ct_put(ct); | |
359 | per_cpu(nft_ct_pcpu_template, cpu) = NULL; | |
360 | } | |
361 | } | |
362 | ||
363 | static bool nft_ct_tmpl_alloc_pcpu(void) | |
364 | { | |
365 | struct nf_conntrack_zone zone = { .id = 0 }; | |
366 | struct nf_conn *tmp; | |
367 | int cpu; | |
368 | ||
369 | if (nft_ct_pcpu_template_refcnt) | |
370 | return true; | |
371 | ||
372 | for_each_possible_cpu(cpu) { | |
373 | tmp = nf_ct_tmpl_alloc(&init_net, &zone, GFP_KERNEL); | |
374 | if (!tmp) { | |
375 | nft_ct_tmpl_put_pcpu(); | |
376 | return false; | |
377 | } | |
378 | ||
379 | atomic_set(&tmp->ct_general.use, 1); | |
380 | per_cpu(nft_ct_pcpu_template, cpu) = tmp; | |
381 | } | |
382 | ||
383 | return true; | |
384 | } | |
385 | #endif | |
386 | ||
fe92ca45 PM |
387 | static int nft_ct_get_init(const struct nft_ctx *ctx, |
388 | const struct nft_expr *expr, | |
389 | const struct nlattr * const tb[]) | |
96518518 PM |
390 | { |
391 | struct nft_ct *priv = nft_expr_priv(expr); | |
45d9bcda | 392 | unsigned int len; |
fe92ca45 | 393 | int err; |
96518518 | 394 | |
fe92ca45 | 395 | priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY])); |
ab23821f | 396 | priv->dir = IP_CT_DIR_MAX; |
96518518 | 397 | switch (priv->key) { |
96518518 | 398 | case NFT_CT_DIRECTION: |
45d9bcda PM |
399 | if (tb[NFTA_CT_DIRECTION] != NULL) |
400 | return -EINVAL; | |
401 | len = sizeof(u8); | |
402 | break; | |
403 | case NFT_CT_STATE: | |
96518518 PM |
404 | case NFT_CT_STATUS: |
405 | #ifdef CONFIG_NF_CONNTRACK_MARK | |
406 | case NFT_CT_MARK: | |
407 | #endif | |
408 | #ifdef CONFIG_NF_CONNTRACK_SECMARK | |
409 | case NFT_CT_SECMARK: | |
d2bf2f34 | 410 | #endif |
45d9bcda PM |
411 | case NFT_CT_EXPIRATION: |
412 | if (tb[NFTA_CT_DIRECTION] != NULL) | |
413 | return -EINVAL; | |
414 | len = sizeof(u32); | |
415 | break; | |
d2bf2f34 FW |
416 | #ifdef CONFIG_NF_CONNTRACK_LABELS |
417 | case NFT_CT_LABELS: | |
45d9bcda PM |
418 | if (tb[NFTA_CT_DIRECTION] != NULL) |
419 | return -EINVAL; | |
420 | len = NF_CT_LABELS_MAX_SIZE; | |
421 | break; | |
96518518 | 422 | #endif |
96518518 PM |
423 | case NFT_CT_HELPER: |
424 | if (tb[NFTA_CT_DIRECTION] != NULL) | |
425 | return -EINVAL; | |
45d9bcda | 426 | len = NF_CT_HELPER_NAME_LEN; |
96518518 | 427 | break; |
45d9bcda | 428 | |
51292c07 | 429 | case NFT_CT_L3PROTOCOL: |
96518518 | 430 | case NFT_CT_PROTOCOL: |
d767ff2c LZ |
431 | /* For compatibility, do not report error if NFTA_CT_DIRECTION |
432 | * attribute is specified. | |
433 | */ | |
45d9bcda PM |
434 | len = sizeof(u8); |
435 | break; | |
96518518 PM |
436 | case NFT_CT_SRC: |
437 | case NFT_CT_DST: | |
45d9bcda PM |
438 | if (tb[NFTA_CT_DIRECTION] == NULL) |
439 | return -EINVAL; | |
440 | ||
36596dad | 441 | switch (ctx->family) { |
45d9bcda PM |
442 | case NFPROTO_IPV4: |
443 | len = FIELD_SIZEOF(struct nf_conntrack_tuple, | |
444 | src.u3.ip); | |
445 | break; | |
446 | case NFPROTO_IPV6: | |
447 | case NFPROTO_INET: | |
448 | len = FIELD_SIZEOF(struct nf_conntrack_tuple, | |
449 | src.u3.ip6); | |
450 | break; | |
451 | default: | |
452 | return -EAFNOSUPPORT; | |
453 | } | |
454 | break; | |
d719e3f2 PNA |
455 | case NFT_CT_SRC_IP: |
456 | case NFT_CT_DST_IP: | |
457 | if (tb[NFTA_CT_DIRECTION] == NULL) | |
458 | return -EINVAL; | |
459 | ||
460 | len = FIELD_SIZEOF(struct nf_conntrack_tuple, src.u3.ip); | |
461 | break; | |
462 | case NFT_CT_SRC_IP6: | |
463 | case NFT_CT_DST_IP6: | |
464 | if (tb[NFTA_CT_DIRECTION] == NULL) | |
465 | return -EINVAL; | |
466 | ||
467 | len = FIELD_SIZEOF(struct nf_conntrack_tuple, src.u3.ip6); | |
468 | break; | |
96518518 PM |
469 | case NFT_CT_PROTO_SRC: |
470 | case NFT_CT_PROTO_DST: | |
471 | if (tb[NFTA_CT_DIRECTION] == NULL) | |
472 | return -EINVAL; | |
45d9bcda | 473 | len = FIELD_SIZEOF(struct nf_conntrack_tuple, src.u.all); |
96518518 | 474 | break; |
48f66c90 FW |
475 | case NFT_CT_BYTES: |
476 | case NFT_CT_PKTS: | |
949a3584 | 477 | case NFT_CT_AVGPKT: |
48f66c90 FW |
478 | len = sizeof(u64); |
479 | break; | |
ab23821f FW |
480 | #ifdef CONFIG_NF_CONNTRACK_ZONES |
481 | case NFT_CT_ZONE: | |
482 | len = sizeof(u16); | |
483 | break; | |
484 | #endif | |
3087c3f7 BM |
485 | case NFT_CT_ID: |
486 | len = sizeof(u32); | |
487 | break; | |
96518518 PM |
488 | default: |
489 | return -EOPNOTSUPP; | |
490 | } | |
491 | ||
fe92ca45 PM |
492 | if (tb[NFTA_CT_DIRECTION] != NULL) { |
493 | priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]); | |
494 | switch (priv->dir) { | |
495 | case IP_CT_DIR_ORIGINAL: | |
496 | case IP_CT_DIR_REPLY: | |
497 | break; | |
498 | default: | |
499 | return -EINVAL; | |
500 | } | |
501 | } | |
502 | ||
b1c96ed3 | 503 | priv->dreg = nft_parse_register(tb[NFTA_CT_DREG]); |
1ec10212 PM |
504 | err = nft_validate_register_store(ctx, priv->dreg, NULL, |
505 | NFT_DATA_VALUE, len); | |
fe92ca45 PM |
506 | if (err < 0) |
507 | return err; | |
508 | ||
36596dad | 509 | err = nf_ct_netns_get(ctx->net, ctx->family); |
fe92ca45 PM |
510 | if (err < 0) |
511 | return err; | |
512 | ||
949a3584 LZ |
513 | if (priv->key == NFT_CT_BYTES || |
514 | priv->key == NFT_CT_PKTS || | |
515 | priv->key == NFT_CT_AVGPKT) | |
3f8b61b7 LZ |
516 | nf_ct_set_acct(ctx->net, true); |
517 | ||
c4ede3d3 KE |
518 | return 0; |
519 | } | |
520 | ||
5c178d81 FW |
521 | static void __nft_ct_set_destroy(const struct nft_ctx *ctx, struct nft_ct *priv) |
522 | { | |
523 | switch (priv->key) { | |
524 | #ifdef CONFIG_NF_CONNTRACK_LABELS | |
525 | case NFT_CT_LABELS: | |
526 | nf_connlabels_put(ctx->net); | |
527 | break; | |
edee4f1e FW |
528 | #endif |
529 | #ifdef CONFIG_NF_CONNTRACK_ZONES | |
530 | case NFT_CT_ZONE: | |
531 | if (--nft_ct_pcpu_template_refcnt == 0) | |
532 | nft_ct_tmpl_put_pcpu(); | |
5c178d81 FW |
533 | #endif |
534 | default: | |
535 | break; | |
536 | } | |
537 | } | |
538 | ||
fe92ca45 PM |
539 | static int nft_ct_set_init(const struct nft_ctx *ctx, |
540 | const struct nft_expr *expr, | |
541 | const struct nlattr * const tb[]) | |
c4ede3d3 | 542 | { |
fe92ca45 | 543 | struct nft_ct *priv = nft_expr_priv(expr); |
d07db988 | 544 | unsigned int len; |
fe92ca45 PM |
545 | int err; |
546 | ||
edee4f1e | 547 | priv->dir = IP_CT_DIR_MAX; |
fe92ca45 PM |
548 | priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY])); |
549 | switch (priv->key) { | |
e88e514e | 550 | #ifdef CONFIG_NF_CONNTRACK_MARK |
c4ede3d3 | 551 | case NFT_CT_MARK: |
7bfdde70 LZ |
552 | if (tb[NFTA_CT_DIRECTION]) |
553 | return -EINVAL; | |
d07db988 | 554 | len = FIELD_SIZEOF(struct nf_conn, mark); |
c4ede3d3 | 555 | break; |
1ad8f48d FW |
556 | #endif |
557 | #ifdef CONFIG_NF_CONNTRACK_LABELS | |
558 | case NFT_CT_LABELS: | |
559 | if (tb[NFTA_CT_DIRECTION]) | |
560 | return -EINVAL; | |
561 | len = NF_CT_LABELS_MAX_SIZE; | |
562 | err = nf_connlabels_get(ctx->net, (len * BITS_PER_BYTE) - 1); | |
563 | if (err) | |
564 | return err; | |
565 | break; | |
edee4f1e FW |
566 | #endif |
567 | #ifdef CONFIG_NF_CONNTRACK_ZONES | |
568 | case NFT_CT_ZONE: | |
569 | if (!nft_ct_tmpl_alloc_pcpu()) | |
570 | return -ENOMEM; | |
571 | nft_ct_pcpu_template_refcnt++; | |
427345d6 | 572 | len = sizeof(u16); |
edee4f1e | 573 | break; |
694a0055 FW |
574 | #endif |
575 | #ifdef CONFIG_NF_CONNTRACK_EVENTS | |
576 | case NFT_CT_EVENTMASK: | |
577 | if (tb[NFTA_CT_DIRECTION]) | |
578 | return -EINVAL; | |
579 | len = sizeof(u32); | |
580 | break; | |
b473a1f5 CG |
581 | #endif |
582 | #ifdef CONFIG_NF_CONNTRACK_SECMARK | |
583 | case NFT_CT_SECMARK: | |
584 | if (tb[NFTA_CT_DIRECTION]) | |
585 | return -EINVAL; | |
586 | len = sizeof(u32); | |
587 | break; | |
e88e514e | 588 | #endif |
c4ede3d3 KE |
589 | default: |
590 | return -EOPNOTSUPP; | |
591 | } | |
592 | ||
edee4f1e FW |
593 | if (tb[NFTA_CT_DIRECTION]) { |
594 | priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]); | |
595 | switch (priv->dir) { | |
596 | case IP_CT_DIR_ORIGINAL: | |
597 | case IP_CT_DIR_REPLY: | |
598 | break; | |
599 | default: | |
4494dbc6 LZ |
600 | err = -EINVAL; |
601 | goto err1; | |
edee4f1e FW |
602 | } |
603 | } | |
604 | ||
b1c96ed3 | 605 | priv->sreg = nft_parse_register(tb[NFTA_CT_SREG]); |
d07db988 | 606 | err = nft_validate_register_load(priv->sreg, len); |
fe92ca45 | 607 | if (err < 0) |
590025a2 | 608 | goto err1; |
c4ede3d3 | 609 | |
36596dad | 610 | err = nf_ct_netns_get(ctx->net, ctx->family); |
96518518 | 611 | if (err < 0) |
590025a2 | 612 | goto err1; |
96518518 | 613 | |
96518518 | 614 | return 0; |
590025a2 LZ |
615 | |
616 | err1: | |
5c178d81 | 617 | __nft_ct_set_destroy(ctx, priv); |
590025a2 LZ |
618 | return err; |
619 | } | |
620 | ||
621 | static void nft_ct_get_destroy(const struct nft_ctx *ctx, | |
622 | const struct nft_expr *expr) | |
623 | { | |
36596dad | 624 | nf_ct_netns_put(ctx->net, ctx->family); |
96518518 PM |
625 | } |
626 | ||
590025a2 LZ |
627 | static void nft_ct_set_destroy(const struct nft_ctx *ctx, |
628 | const struct nft_expr *expr) | |
96518518 | 629 | { |
1ad8f48d FW |
630 | struct nft_ct *priv = nft_expr_priv(expr); |
631 | ||
5c178d81 | 632 | __nft_ct_set_destroy(ctx, priv); |
36596dad | 633 | nf_ct_netns_put(ctx->net, ctx->family); |
96518518 PM |
634 | } |
635 | ||
c4ede3d3 | 636 | static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr) |
96518518 PM |
637 | { |
638 | const struct nft_ct *priv = nft_expr_priv(expr); | |
639 | ||
b1c96ed3 | 640 | if (nft_dump_register(skb, NFTA_CT_DREG, priv->dreg)) |
96518518 PM |
641 | goto nla_put_failure; |
642 | if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key))) | |
643 | goto nla_put_failure; | |
2a53bfb3 AB |
644 | |
645 | switch (priv->key) { | |
2a53bfb3 AB |
646 | case NFT_CT_SRC: |
647 | case NFT_CT_DST: | |
d719e3f2 PNA |
648 | case NFT_CT_SRC_IP: |
649 | case NFT_CT_DST_IP: | |
650 | case NFT_CT_SRC_IP6: | |
651 | case NFT_CT_DST_IP6: | |
2a53bfb3 AB |
652 | case NFT_CT_PROTO_SRC: |
653 | case NFT_CT_PROTO_DST: | |
654 | if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir)) | |
655 | goto nla_put_failure; | |
48f66c90 FW |
656 | break; |
657 | case NFT_CT_BYTES: | |
658 | case NFT_CT_PKTS: | |
949a3584 | 659 | case NFT_CT_AVGPKT: |
ab23821f | 660 | case NFT_CT_ZONE: |
48f66c90 FW |
661 | if (priv->dir < IP_CT_DIR_MAX && |
662 | nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir)) | |
663 | goto nla_put_failure; | |
664 | break; | |
2a53bfb3 AB |
665 | default: |
666 | break; | |
667 | } | |
668 | ||
96518518 PM |
669 | return 0; |
670 | ||
671 | nla_put_failure: | |
672 | return -1; | |
673 | } | |
674 | ||
c4ede3d3 KE |
675 | static int nft_ct_set_dump(struct sk_buff *skb, const struct nft_expr *expr) |
676 | { | |
677 | const struct nft_ct *priv = nft_expr_priv(expr); | |
678 | ||
b1c96ed3 | 679 | if (nft_dump_register(skb, NFTA_CT_SREG, priv->sreg)) |
c4ede3d3 KE |
680 | goto nla_put_failure; |
681 | if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key))) | |
682 | goto nla_put_failure; | |
edee4f1e FW |
683 | |
684 | switch (priv->key) { | |
685 | case NFT_CT_ZONE: | |
686 | if (priv->dir < IP_CT_DIR_MAX && | |
687 | nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir)) | |
688 | goto nla_put_failure; | |
689 | break; | |
690 | default: | |
691 | break; | |
692 | } | |
693 | ||
c4ede3d3 KE |
694 | return 0; |
695 | ||
696 | nla_put_failure: | |
697 | return -1; | |
698 | } | |
699 | ||
ef1f7df9 | 700 | static struct nft_expr_type nft_ct_type; |
c4ede3d3 | 701 | static const struct nft_expr_ops nft_ct_get_ops = { |
ef1f7df9 | 702 | .type = &nft_ct_type, |
96518518 | 703 | .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), |
c4ede3d3 | 704 | .eval = nft_ct_get_eval, |
fe92ca45 | 705 | .init = nft_ct_get_init, |
590025a2 | 706 | .destroy = nft_ct_get_destroy, |
c4ede3d3 | 707 | .dump = nft_ct_get_dump, |
ef1f7df9 PM |
708 | }; |
709 | ||
c4ede3d3 KE |
710 | static const struct nft_expr_ops nft_ct_set_ops = { |
711 | .type = &nft_ct_type, | |
712 | .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), | |
713 | .eval = nft_ct_set_eval, | |
fe92ca45 | 714 | .init = nft_ct_set_init, |
590025a2 | 715 | .destroy = nft_ct_set_destroy, |
c4ede3d3 KE |
716 | .dump = nft_ct_set_dump, |
717 | }; | |
718 | ||
edee4f1e FW |
719 | #ifdef CONFIG_NF_CONNTRACK_ZONES |
720 | static const struct nft_expr_ops nft_ct_set_zone_ops = { | |
721 | .type = &nft_ct_type, | |
722 | .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), | |
723 | .eval = nft_ct_set_zone_eval, | |
724 | .init = nft_ct_set_init, | |
725 | .destroy = nft_ct_set_destroy, | |
726 | .dump = nft_ct_set_dump, | |
727 | }; | |
728 | #endif | |
729 | ||
c4ede3d3 KE |
730 | static const struct nft_expr_ops * |
731 | nft_ct_select_ops(const struct nft_ctx *ctx, | |
732 | const struct nlattr * const tb[]) | |
733 | { | |
734 | if (tb[NFTA_CT_KEY] == NULL) | |
735 | return ERR_PTR(-EINVAL); | |
736 | ||
737 | if (tb[NFTA_CT_DREG] && tb[NFTA_CT_SREG]) | |
738 | return ERR_PTR(-EINVAL); | |
739 | ||
740 | if (tb[NFTA_CT_DREG]) | |
741 | return &nft_ct_get_ops; | |
742 | ||
edee4f1e FW |
743 | if (tb[NFTA_CT_SREG]) { |
744 | #ifdef CONFIG_NF_CONNTRACK_ZONES | |
745 | if (nla_get_be32(tb[NFTA_CT_KEY]) == htonl(NFT_CT_ZONE)) | |
746 | return &nft_ct_set_zone_ops; | |
747 | #endif | |
c4ede3d3 | 748 | return &nft_ct_set_ops; |
edee4f1e | 749 | } |
c4ede3d3 KE |
750 | |
751 | return ERR_PTR(-EINVAL); | |
752 | } | |
753 | ||
ef1f7df9 PM |
754 | static struct nft_expr_type nft_ct_type __read_mostly = { |
755 | .name = "ct", | |
d4ef3835 | 756 | .select_ops = nft_ct_select_ops, |
96518518 PM |
757 | .policy = nft_ct_policy, |
758 | .maxattr = NFTA_CT_MAX, | |
ef1f7df9 | 759 | .owner = THIS_MODULE, |
96518518 PM |
760 | }; |
761 | ||
25443261 PNA |
762 | static void nft_notrack_eval(const struct nft_expr *expr, |
763 | struct nft_regs *regs, | |
764 | const struct nft_pktinfo *pkt) | |
765 | { | |
766 | struct sk_buff *skb = pkt->skb; | |
767 | enum ip_conntrack_info ctinfo; | |
768 | struct nf_conn *ct; | |
769 | ||
770 | ct = nf_ct_get(pkt->skb, &ctinfo); | |
771 | /* Previously seen (loopback or untracked)? Ignore. */ | |
cc41c84b | 772 | if (ct || ctinfo == IP_CT_UNTRACKED) |
25443261 PNA |
773 | return; |
774 | ||
cc41c84b | 775 | nf_ct_set(skb, ct, IP_CT_UNTRACKED); |
25443261 PNA |
776 | } |
777 | ||
778 | static struct nft_expr_type nft_notrack_type; | |
779 | static const struct nft_expr_ops nft_notrack_ops = { | |
780 | .type = &nft_notrack_type, | |
781 | .size = NFT_EXPR_SIZE(0), | |
782 | .eval = nft_notrack_eval, | |
783 | }; | |
784 | ||
785 | static struct nft_expr_type nft_notrack_type __read_mostly = { | |
786 | .name = "notrack", | |
787 | .ops = &nft_notrack_ops, | |
788 | .owner = THIS_MODULE, | |
789 | }; | |
790 | ||
7e0b2b57 HS |
791 | #ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
792 | static int | |
793 | nft_ct_timeout_parse_policy(void *timeouts, | |
794 | const struct nf_conntrack_l4proto *l4proto, | |
795 | struct net *net, const struct nlattr *attr) | |
796 | { | |
797 | struct nlattr **tb; | |
798 | int ret = 0; | |
799 | ||
7e0b2b57 HS |
800 | tb = kcalloc(l4proto->ctnl_timeout.nlattr_max + 1, sizeof(*tb), |
801 | GFP_KERNEL); | |
802 | ||
803 | if (!tb) | |
804 | return -ENOMEM; | |
805 | ||
8cb08174 JB |
806 | ret = nla_parse_nested_deprecated(tb, |
807 | l4proto->ctnl_timeout.nlattr_max, | |
808 | attr, | |
809 | l4proto->ctnl_timeout.nla_policy, | |
810 | NULL); | |
7e0b2b57 HS |
811 | if (ret < 0) |
812 | goto err; | |
813 | ||
814 | ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, net, timeouts); | |
815 | ||
816 | err: | |
817 | kfree(tb); | |
818 | return ret; | |
819 | } | |
820 | ||
821 | struct nft_ct_timeout_obj { | |
0434ccdc | 822 | struct nf_ct_timeout *timeout; |
7e0b2b57 HS |
823 | u8 l4proto; |
824 | }; | |
825 | ||
826 | static void nft_ct_timeout_obj_eval(struct nft_object *obj, | |
827 | struct nft_regs *regs, | |
828 | const struct nft_pktinfo *pkt) | |
829 | { | |
830 | const struct nft_ct_timeout_obj *priv = nft_obj_data(obj); | |
831 | struct nf_conn *ct = (struct nf_conn *)skb_nfct(pkt->skb); | |
0434ccdc FW |
832 | struct nf_conn_timeout *timeout; |
833 | const unsigned int *values; | |
834 | ||
835 | if (priv->l4proto != pkt->tprot) | |
836 | return; | |
7e0b2b57 | 837 | |
0434ccdc | 838 | if (!ct || nf_ct_is_template(ct) || nf_ct_is_confirmed(ct)) |
7e0b2b57 HS |
839 | return; |
840 | ||
0434ccdc FW |
841 | timeout = nf_ct_timeout_find(ct); |
842 | if (!timeout) { | |
843 | timeout = nf_ct_timeout_ext_add(ct, priv->timeout, GFP_ATOMIC); | |
844 | if (!timeout) { | |
845 | regs->verdict.code = NF_DROP; | |
846 | return; | |
847 | } | |
848 | } | |
849 | ||
850 | rcu_assign_pointer(timeout->timeout, priv->timeout); | |
851 | ||
852 | /* adjust the timeout as per 'new' state. ct is unconfirmed, | |
853 | * so the current timestamp must not be added. | |
854 | */ | |
855 | values = nf_ct_timeout_data(timeout); | |
856 | if (values) | |
857 | nf_ct_refresh(ct, pkt->skb, values[0]); | |
7e0b2b57 HS |
858 | } |
859 | ||
860 | static int nft_ct_timeout_obj_init(const struct nft_ctx *ctx, | |
861 | const struct nlattr * const tb[], | |
862 | struct nft_object *obj) | |
863 | { | |
7e0b2b57 HS |
864 | struct nft_ct_timeout_obj *priv = nft_obj_data(obj); |
865 | const struct nf_conntrack_l4proto *l4proto; | |
7e0b2b57 HS |
866 | struct nf_ct_timeout *timeout; |
867 | int l3num = ctx->family; | |
7e0b2b57 HS |
868 | __u8 l4num; |
869 | int ret; | |
870 | ||
3206c516 | 871 | if (!tb[NFTA_CT_TIMEOUT_L4PROTO] || |
7e0b2b57 HS |
872 | !tb[NFTA_CT_TIMEOUT_DATA]) |
873 | return -EINVAL; | |
874 | ||
3206c516 HS |
875 | if (tb[NFTA_CT_TIMEOUT_L3PROTO]) |
876 | l3num = ntohs(nla_get_be16(tb[NFTA_CT_TIMEOUT_L3PROTO])); | |
877 | ||
7e0b2b57 HS |
878 | l4num = nla_get_u8(tb[NFTA_CT_TIMEOUT_L4PROTO]); |
879 | priv->l4proto = l4num; | |
880 | ||
4a60dc74 | 881 | l4proto = nf_ct_l4proto_find(l4num); |
7e0b2b57 HS |
882 | |
883 | if (l4proto->l4proto != l4num) { | |
884 | ret = -EOPNOTSUPP; | |
885 | goto err_proto_put; | |
886 | } | |
887 | ||
888 | timeout = kzalloc(sizeof(struct nf_ct_timeout) + | |
889 | l4proto->ctnl_timeout.obj_size, GFP_KERNEL); | |
890 | if (timeout == NULL) { | |
891 | ret = -ENOMEM; | |
892 | goto err_proto_put; | |
893 | } | |
894 | ||
895 | ret = nft_ct_timeout_parse_policy(&timeout->data, l4proto, ctx->net, | |
896 | tb[NFTA_CT_TIMEOUT_DATA]); | |
897 | if (ret < 0) | |
898 | goto err_free_timeout; | |
899 | ||
900 | timeout->l3num = l3num; | |
901 | timeout->l4proto = l4proto; | |
7e0b2b57 HS |
902 | |
903 | ret = nf_ct_netns_get(ctx->net, ctx->family); | |
904 | if (ret < 0) | |
0434ccdc | 905 | goto err_free_timeout; |
7e0b2b57 | 906 | |
0434ccdc | 907 | priv->timeout = timeout; |
7e0b2b57 HS |
908 | return 0; |
909 | ||
7e0b2b57 HS |
910 | err_free_timeout: |
911 | kfree(timeout); | |
912 | err_proto_put: | |
7e0b2b57 HS |
913 | return ret; |
914 | } | |
915 | ||
916 | static void nft_ct_timeout_obj_destroy(const struct nft_ctx *ctx, | |
917 | struct nft_object *obj) | |
918 | { | |
919 | struct nft_ct_timeout_obj *priv = nft_obj_data(obj); | |
0434ccdc | 920 | struct nf_ct_timeout *timeout = priv->timeout; |
7e0b2b57 | 921 | |
7e0b2b57 | 922 | nf_ct_untimeout(ctx->net, timeout); |
7e0b2b57 | 923 | nf_ct_netns_put(ctx->net, ctx->family); |
0434ccdc | 924 | kfree(priv->timeout); |
7e0b2b57 HS |
925 | } |
926 | ||
927 | static int nft_ct_timeout_obj_dump(struct sk_buff *skb, | |
928 | struct nft_object *obj, bool reset) | |
929 | { | |
930 | const struct nft_ct_timeout_obj *priv = nft_obj_data(obj); | |
0434ccdc | 931 | const struct nf_ct_timeout *timeout = priv->timeout; |
7e0b2b57 HS |
932 | struct nlattr *nest_params; |
933 | int ret; | |
934 | ||
935 | if (nla_put_u8(skb, NFTA_CT_TIMEOUT_L4PROTO, timeout->l4proto->l4proto) || | |
936 | nla_put_be16(skb, NFTA_CT_TIMEOUT_L3PROTO, htons(timeout->l3num))) | |
937 | return -1; | |
938 | ||
ae0be8de | 939 | nest_params = nla_nest_start(skb, NFTA_CT_TIMEOUT_DATA); |
7e0b2b57 HS |
940 | if (!nest_params) |
941 | return -1; | |
942 | ||
943 | ret = timeout->l4proto->ctnl_timeout.obj_to_nlattr(skb, &timeout->data); | |
944 | if (ret < 0) | |
945 | return -1; | |
946 | nla_nest_end(skb, nest_params); | |
947 | return 0; | |
948 | } | |
949 | ||
950 | static const struct nla_policy nft_ct_timeout_policy[NFTA_CT_TIMEOUT_MAX + 1] = { | |
951 | [NFTA_CT_TIMEOUT_L3PROTO] = {.type = NLA_U16 }, | |
952 | [NFTA_CT_TIMEOUT_L4PROTO] = {.type = NLA_U8 }, | |
953 | [NFTA_CT_TIMEOUT_DATA] = {.type = NLA_NESTED }, | |
954 | }; | |
955 | ||
956 | static struct nft_object_type nft_ct_timeout_obj_type; | |
957 | ||
958 | static const struct nft_object_ops nft_ct_timeout_obj_ops = { | |
959 | .type = &nft_ct_timeout_obj_type, | |
960 | .size = sizeof(struct nft_ct_timeout_obj), | |
961 | .eval = nft_ct_timeout_obj_eval, | |
962 | .init = nft_ct_timeout_obj_init, | |
963 | .destroy = nft_ct_timeout_obj_destroy, | |
964 | .dump = nft_ct_timeout_obj_dump, | |
965 | }; | |
966 | ||
967 | static struct nft_object_type nft_ct_timeout_obj_type __read_mostly = { | |
968 | .type = NFT_OBJECT_CT_TIMEOUT, | |
969 | .ops = &nft_ct_timeout_obj_ops, | |
970 | .maxattr = NFTA_CT_TIMEOUT_MAX, | |
971 | .policy = nft_ct_timeout_policy, | |
972 | .owner = THIS_MODULE, | |
973 | }; | |
974 | #endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ | |
975 | ||
1a64edf5 FW |
976 | static int nft_ct_helper_obj_init(const struct nft_ctx *ctx, |
977 | const struct nlattr * const tb[], | |
978 | struct nft_object *obj) | |
979 | { | |
980 | struct nft_ct_helper_obj *priv = nft_obj_data(obj); | |
981 | struct nf_conntrack_helper *help4, *help6; | |
982 | char name[NF_CT_HELPER_NAME_LEN]; | |
36596dad | 983 | int family = ctx->family; |
f699edb1 | 984 | int err; |
1a64edf5 FW |
985 | |
986 | if (!tb[NFTA_CT_HELPER_NAME] || !tb[NFTA_CT_HELPER_L4PROTO]) | |
987 | return -EINVAL; | |
988 | ||
989 | priv->l4proto = nla_get_u8(tb[NFTA_CT_HELPER_L4PROTO]); | |
990 | if (!priv->l4proto) | |
991 | return -ENOENT; | |
992 | ||
993 | nla_strlcpy(name, tb[NFTA_CT_HELPER_NAME], sizeof(name)); | |
994 | ||
995 | if (tb[NFTA_CT_HELPER_L3PROTO]) | |
996 | family = ntohs(nla_get_be16(tb[NFTA_CT_HELPER_L3PROTO])); | |
997 | ||
998 | help4 = NULL; | |
999 | help6 = NULL; | |
1000 | ||
1001 | switch (family) { | |
1002 | case NFPROTO_IPV4: | |
36596dad | 1003 | if (ctx->family == NFPROTO_IPV6) |
1a64edf5 FW |
1004 | return -EINVAL; |
1005 | ||
1006 | help4 = nf_conntrack_helper_try_module_get(name, family, | |
1007 | priv->l4proto); | |
1008 | break; | |
1009 | case NFPROTO_IPV6: | |
36596dad | 1010 | if (ctx->family == NFPROTO_IPV4) |
1a64edf5 FW |
1011 | return -EINVAL; |
1012 | ||
1013 | help6 = nf_conntrack_helper_try_module_get(name, family, | |
1014 | priv->l4proto); | |
1015 | break; | |
1016 | case NFPROTO_NETDEV: /* fallthrough */ | |
1017 | case NFPROTO_BRIDGE: /* same */ | |
1018 | case NFPROTO_INET: | |
1019 | help4 = nf_conntrack_helper_try_module_get(name, NFPROTO_IPV4, | |
1020 | priv->l4proto); | |
1021 | help6 = nf_conntrack_helper_try_module_get(name, NFPROTO_IPV6, | |
1022 | priv->l4proto); | |
1023 | break; | |
1024 | default: | |
1025 | return -EAFNOSUPPORT; | |
1026 | } | |
1027 | ||
1028 | /* && is intentional; only error if INET found neither ipv4 or ipv6 */ | |
1029 | if (!help4 && !help6) | |
1030 | return -ENOENT; | |
1031 | ||
1032 | priv->helper4 = help4; | |
1033 | priv->helper6 = help6; | |
1034 | ||
f699edb1 PNA |
1035 | err = nf_ct_netns_get(ctx->net, ctx->family); |
1036 | if (err < 0) | |
1037 | goto err_put_helper; | |
1038 | ||
1a64edf5 | 1039 | return 0; |
f699edb1 PNA |
1040 | |
1041 | err_put_helper: | |
1042 | if (priv->helper4) | |
1043 | nf_conntrack_helper_put(priv->helper4); | |
1044 | if (priv->helper6) | |
1045 | nf_conntrack_helper_put(priv->helper6); | |
1046 | return err; | |
1a64edf5 FW |
1047 | } |
1048 | ||
00bfb320 PNA |
1049 | static void nft_ct_helper_obj_destroy(const struct nft_ctx *ctx, |
1050 | struct nft_object *obj) | |
1a64edf5 FW |
1051 | { |
1052 | struct nft_ct_helper_obj *priv = nft_obj_data(obj); | |
1053 | ||
1054 | if (priv->helper4) | |
d91fc59c | 1055 | nf_conntrack_helper_put(priv->helper4); |
1a64edf5 | 1056 | if (priv->helper6) |
d91fc59c | 1057 | nf_conntrack_helper_put(priv->helper6); |
f699edb1 PNA |
1058 | |
1059 | nf_ct_netns_put(ctx->net, ctx->family); | |
1a64edf5 FW |
1060 | } |
1061 | ||
1062 | static void nft_ct_helper_obj_eval(struct nft_object *obj, | |
1063 | struct nft_regs *regs, | |
1064 | const struct nft_pktinfo *pkt) | |
1065 | { | |
1066 | const struct nft_ct_helper_obj *priv = nft_obj_data(obj); | |
1067 | struct nf_conn *ct = (struct nf_conn *)skb_nfct(pkt->skb); | |
1068 | struct nf_conntrack_helper *to_assign = NULL; | |
1069 | struct nf_conn_help *help; | |
1070 | ||
1071 | if (!ct || | |
1072 | nf_ct_is_confirmed(ct) || | |
1073 | nf_ct_is_template(ct) || | |
1074 | priv->l4proto != nf_ct_protonum(ct)) | |
1075 | return; | |
1076 | ||
1077 | switch (nf_ct_l3num(ct)) { | |
1078 | case NFPROTO_IPV4: | |
1079 | to_assign = priv->helper4; | |
1080 | break; | |
1081 | case NFPROTO_IPV6: | |
1082 | to_assign = priv->helper6; | |
1083 | break; | |
1084 | default: | |
1085 | WARN_ON_ONCE(1); | |
1086 | return; | |
1087 | } | |
1088 | ||
1089 | if (!to_assign) | |
1090 | return; | |
1091 | ||
1092 | if (test_bit(IPS_HELPER_BIT, &ct->status)) | |
1093 | return; | |
1094 | ||
440534d3 | 1095 | help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); |
1a64edf5 FW |
1096 | if (help) { |
1097 | rcu_assign_pointer(help->helper, to_assign); | |
1098 | set_bit(IPS_HELPER_BIT, &ct->status); | |
1099 | } | |
1100 | } | |
1101 | ||
1102 | static int nft_ct_helper_obj_dump(struct sk_buff *skb, | |
1103 | struct nft_object *obj, bool reset) | |
1104 | { | |
1105 | const struct nft_ct_helper_obj *priv = nft_obj_data(obj); | |
b7153458 | 1106 | const struct nf_conntrack_helper *helper; |
1a64edf5 FW |
1107 | u16 family; |
1108 | ||
b7153458 TY |
1109 | if (priv->helper4 && priv->helper6) { |
1110 | family = NFPROTO_INET; | |
1111 | helper = priv->helper4; | |
1112 | } else if (priv->helper6) { | |
1113 | family = NFPROTO_IPV6; | |
1114 | helper = priv->helper6; | |
1115 | } else { | |
1116 | family = NFPROTO_IPV4; | |
1117 | helper = priv->helper4; | |
1118 | } | |
1119 | ||
1a64edf5 FW |
1120 | if (nla_put_string(skb, NFTA_CT_HELPER_NAME, helper->name)) |
1121 | return -1; | |
1122 | ||
1123 | if (nla_put_u8(skb, NFTA_CT_HELPER_L4PROTO, priv->l4proto)) | |
1124 | return -1; | |
1125 | ||
1a64edf5 FW |
1126 | if (nla_put_be16(skb, NFTA_CT_HELPER_L3PROTO, htons(family))) |
1127 | return -1; | |
1128 | ||
1129 | return 0; | |
1130 | } | |
1131 | ||
1132 | static const struct nla_policy nft_ct_helper_policy[NFTA_CT_HELPER_MAX + 1] = { | |
1133 | [NFTA_CT_HELPER_NAME] = { .type = NLA_STRING, | |
1134 | .len = NF_CT_HELPER_NAME_LEN - 1 }, | |
1135 | [NFTA_CT_HELPER_L3PROTO] = { .type = NLA_U16 }, | |
1136 | [NFTA_CT_HELPER_L4PROTO] = { .type = NLA_U8 }, | |
1137 | }; | |
1138 | ||
dfc46034 PBG |
1139 | static struct nft_object_type nft_ct_helper_obj_type; |
1140 | static const struct nft_object_ops nft_ct_helper_obj_ops = { | |
1141 | .type = &nft_ct_helper_obj_type, | |
1a64edf5 | 1142 | .size = sizeof(struct nft_ct_helper_obj), |
1a64edf5 FW |
1143 | .eval = nft_ct_helper_obj_eval, |
1144 | .init = nft_ct_helper_obj_init, | |
1145 | .destroy = nft_ct_helper_obj_destroy, | |
1146 | .dump = nft_ct_helper_obj_dump, | |
dfc46034 PBG |
1147 | }; |
1148 | ||
1149 | static struct nft_object_type nft_ct_helper_obj_type __read_mostly = { | |
1150 | .type = NFT_OBJECT_CT_HELPER, | |
1151 | .ops = &nft_ct_helper_obj_ops, | |
1152 | .maxattr = NFTA_CT_HELPER_MAX, | |
1153 | .policy = nft_ct_helper_policy, | |
1a64edf5 FW |
1154 | .owner = THIS_MODULE, |
1155 | }; | |
1156 | ||
857b4602 SV |
1157 | struct nft_ct_expect_obj { |
1158 | u16 l3num; | |
1159 | __be16 dport; | |
1160 | u8 l4proto; | |
1161 | u8 size; | |
1162 | u32 timeout; | |
1163 | }; | |
1164 | ||
1165 | static int nft_ct_expect_obj_init(const struct nft_ctx *ctx, | |
1166 | const struct nlattr * const tb[], | |
1167 | struct nft_object *obj) | |
1168 | { | |
1169 | struct nft_ct_expect_obj *priv = nft_obj_data(obj); | |
1170 | ||
1171 | if (!tb[NFTA_CT_EXPECT_L4PROTO] || | |
1172 | !tb[NFTA_CT_EXPECT_DPORT] || | |
1173 | !tb[NFTA_CT_EXPECT_TIMEOUT] || | |
1174 | !tb[NFTA_CT_EXPECT_SIZE]) | |
1175 | return -EINVAL; | |
1176 | ||
1177 | priv->l3num = ctx->family; | |
1178 | if (tb[NFTA_CT_EXPECT_L3PROTO]) | |
1179 | priv->l3num = ntohs(nla_get_be16(tb[NFTA_CT_EXPECT_L3PROTO])); | |
1180 | ||
1181 | priv->l4proto = nla_get_u8(tb[NFTA_CT_EXPECT_L4PROTO]); | |
1182 | priv->dport = nla_get_be16(tb[NFTA_CT_EXPECT_DPORT]); | |
1183 | priv->timeout = nla_get_u32(tb[NFTA_CT_EXPECT_TIMEOUT]); | |
1184 | priv->size = nla_get_u8(tb[NFTA_CT_EXPECT_SIZE]); | |
1185 | ||
1186 | return nf_ct_netns_get(ctx->net, ctx->family); | |
1187 | } | |
1188 | ||
1189 | static void nft_ct_expect_obj_destroy(const struct nft_ctx *ctx, | |
1190 | struct nft_object *obj) | |
1191 | { | |
1192 | nf_ct_netns_put(ctx->net, ctx->family); | |
1193 | } | |
1194 | ||
1195 | static int nft_ct_expect_obj_dump(struct sk_buff *skb, | |
1196 | struct nft_object *obj, bool reset) | |
1197 | { | |
1198 | const struct nft_ct_expect_obj *priv = nft_obj_data(obj); | |
1199 | ||
1200 | if (nla_put_be16(skb, NFTA_CT_EXPECT_L3PROTO, htons(priv->l3num)) || | |
1201 | nla_put_u8(skb, NFTA_CT_EXPECT_L4PROTO, priv->l4proto) || | |
1202 | nla_put_be16(skb, NFTA_CT_EXPECT_DPORT, priv->dport) || | |
1203 | nla_put_u32(skb, NFTA_CT_EXPECT_TIMEOUT, priv->timeout) || | |
1204 | nla_put_u8(skb, NFTA_CT_EXPECT_SIZE, priv->size)) | |
1205 | return -1; | |
1206 | ||
1207 | return 0; | |
1208 | } | |
1209 | ||
1210 | static void nft_ct_expect_obj_eval(struct nft_object *obj, | |
1211 | struct nft_regs *regs, | |
1212 | const struct nft_pktinfo *pkt) | |
1213 | { | |
1214 | const struct nft_ct_expect_obj *priv = nft_obj_data(obj); | |
1215 | struct nf_conntrack_expect *exp; | |
1216 | enum ip_conntrack_info ctinfo; | |
1217 | struct nf_conn_help *help; | |
1218 | enum ip_conntrack_dir dir; | |
1219 | u16 l3num = priv->l3num; | |
1220 | struct nf_conn *ct; | |
1221 | ||
1222 | ct = nf_ct_get(pkt->skb, &ctinfo); | |
1223 | if (!ct || ctinfo == IP_CT_UNTRACKED) { | |
1224 | regs->verdict.code = NFT_BREAK; | |
1225 | return; | |
1226 | } | |
1227 | dir = CTINFO2DIR(ctinfo); | |
1228 | ||
1229 | help = nfct_help(ct); | |
1230 | if (!help) | |
1231 | help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); | |
2f0513d4 SV |
1232 | if (!help) { |
1233 | regs->verdict.code = NF_DROP; | |
1234 | return; | |
1235 | } | |
857b4602 SV |
1236 | |
1237 | if (help->expecting[NF_CT_EXPECT_CLASS_DEFAULT] >= priv->size) { | |
1238 | regs->verdict.code = NFT_BREAK; | |
1239 | return; | |
1240 | } | |
1241 | if (l3num == NFPROTO_INET) | |
1242 | l3num = nf_ct_l3num(ct); | |
1243 | ||
1244 | exp = nf_ct_expect_alloc(ct); | |
1245 | if (exp == NULL) { | |
1246 | regs->verdict.code = NF_DROP; | |
1247 | return; | |
1248 | } | |
1249 | nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, l3num, | |
1250 | &ct->tuplehash[!dir].tuple.src.u3, | |
1251 | &ct->tuplehash[!dir].tuple.dst.u3, | |
1252 | priv->l4proto, NULL, &priv->dport); | |
1253 | exp->timeout.expires = jiffies + priv->timeout * HZ; | |
1254 | ||
3c00fb0b | 1255 | if (nf_ct_expect_related(exp, 0) != 0) |
857b4602 SV |
1256 | regs->verdict.code = NF_DROP; |
1257 | } | |
1258 | ||
1259 | static const struct nla_policy nft_ct_expect_policy[NFTA_CT_EXPECT_MAX + 1] = { | |
1260 | [NFTA_CT_EXPECT_L3PROTO] = { .type = NLA_U16 }, | |
1261 | [NFTA_CT_EXPECT_L4PROTO] = { .type = NLA_U8 }, | |
1262 | [NFTA_CT_EXPECT_DPORT] = { .type = NLA_U16 }, | |
1263 | [NFTA_CT_EXPECT_TIMEOUT] = { .type = NLA_U32 }, | |
1264 | [NFTA_CT_EXPECT_SIZE] = { .type = NLA_U8 }, | |
1265 | }; | |
1266 | ||
1267 | static struct nft_object_type nft_ct_expect_obj_type; | |
1268 | ||
1269 | static const struct nft_object_ops nft_ct_expect_obj_ops = { | |
1270 | .type = &nft_ct_expect_obj_type, | |
1271 | .size = sizeof(struct nft_ct_expect_obj), | |
1272 | .eval = nft_ct_expect_obj_eval, | |
1273 | .init = nft_ct_expect_obj_init, | |
1274 | .destroy = nft_ct_expect_obj_destroy, | |
1275 | .dump = nft_ct_expect_obj_dump, | |
1276 | }; | |
1277 | ||
1278 | static struct nft_object_type nft_ct_expect_obj_type __read_mostly = { | |
1279 | .type = NFT_OBJECT_CT_EXPECT, | |
1280 | .ops = &nft_ct_expect_obj_ops, | |
1281 | .maxattr = NFTA_CT_EXPECT_MAX, | |
1282 | .policy = nft_ct_expect_policy, | |
1283 | .owner = THIS_MODULE, | |
1284 | }; | |
1285 | ||
96518518 PM |
1286 | static int __init nft_ct_module_init(void) |
1287 | { | |
25443261 PNA |
1288 | int err; |
1289 | ||
adff6c65 FW |
1290 | BUILD_BUG_ON(NF_CT_LABELS_MAX_SIZE > NFT_REG_SIZE); |
1291 | ||
25443261 PNA |
1292 | err = nft_register_expr(&nft_ct_type); |
1293 | if (err < 0) | |
1294 | return err; | |
1295 | ||
1296 | err = nft_register_expr(&nft_notrack_type); | |
1297 | if (err < 0) | |
1298 | goto err1; | |
1299 | ||
dfc46034 | 1300 | err = nft_register_obj(&nft_ct_helper_obj_type); |
1a64edf5 FW |
1301 | if (err < 0) |
1302 | goto err2; | |
857b4602 SV |
1303 | |
1304 | err = nft_register_obj(&nft_ct_expect_obj_type); | |
1305 | if (err < 0) | |
1306 | goto err3; | |
7e0b2b57 HS |
1307 | #ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
1308 | err = nft_register_obj(&nft_ct_timeout_obj_type); | |
1309 | if (err < 0) | |
857b4602 | 1310 | goto err4; |
7e0b2b57 | 1311 | #endif |
25443261 | 1312 | return 0; |
1a64edf5 | 1313 | |
7e0b2b57 | 1314 | #ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
857b4602 SV |
1315 | err4: |
1316 | nft_unregister_obj(&nft_ct_expect_obj_type); | |
1317 | #endif | |
7e0b2b57 HS |
1318 | err3: |
1319 | nft_unregister_obj(&nft_ct_helper_obj_type); | |
1a64edf5 FW |
1320 | err2: |
1321 | nft_unregister_expr(&nft_notrack_type); | |
25443261 PNA |
1322 | err1: |
1323 | nft_unregister_expr(&nft_ct_type); | |
1324 | return err; | |
96518518 PM |
1325 | } |
1326 | ||
1327 | static void __exit nft_ct_module_exit(void) | |
1328 | { | |
7e0b2b57 HS |
1329 | #ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
1330 | nft_unregister_obj(&nft_ct_timeout_obj_type); | |
1331 | #endif | |
857b4602 | 1332 | nft_unregister_obj(&nft_ct_expect_obj_type); |
dfc46034 | 1333 | nft_unregister_obj(&nft_ct_helper_obj_type); |
25443261 | 1334 | nft_unregister_expr(&nft_notrack_type); |
ef1f7df9 | 1335 | nft_unregister_expr(&nft_ct_type); |
96518518 PM |
1336 | } |
1337 | ||
1338 | module_init(nft_ct_module_init); | |
1339 | module_exit(nft_ct_module_exit); | |
1340 | ||
1341 | MODULE_LICENSE("GPL"); | |
1342 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | |
1343 | MODULE_ALIAS_NFT_EXPR("ct"); | |
25443261 | 1344 | MODULE_ALIAS_NFT_EXPR("notrack"); |
1a64edf5 | 1345 | MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_CT_HELPER); |
7e0b2b57 | 1346 | MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_CT_TIMEOUT); |
857b4602 | 1347 | MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_CT_EXPECT); |