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