]>
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 { |
345023b0 | 30 | u8 dreg; |
4f16d25c | 31 | u8 sreg; |
c4ede3d3 | 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 | ||
345023b0 PNA |
501 | err = nft_parse_register_store(ctx, tb[NFTA_CT_DREG], &priv->dreg, NULL, |
502 | NFT_DATA_VALUE, len); | |
fe92ca45 PM |
503 | if (err < 0) |
504 | return err; | |
505 | ||
36596dad | 506 | err = nf_ct_netns_get(ctx->net, ctx->family); |
fe92ca45 PM |
507 | if (err < 0) |
508 | return err; | |
509 | ||
949a3584 LZ |
510 | if (priv->key == NFT_CT_BYTES || |
511 | priv->key == NFT_CT_PKTS || | |
512 | priv->key == NFT_CT_AVGPKT) | |
3f8b61b7 LZ |
513 | nf_ct_set_acct(ctx->net, true); |
514 | ||
c4ede3d3 KE |
515 | return 0; |
516 | } | |
517 | ||
5c178d81 FW |
518 | static void __nft_ct_set_destroy(const struct nft_ctx *ctx, struct nft_ct *priv) |
519 | { | |
520 | switch (priv->key) { | |
521 | #ifdef CONFIG_NF_CONNTRACK_LABELS | |
522 | case NFT_CT_LABELS: | |
523 | nf_connlabels_put(ctx->net); | |
524 | break; | |
edee4f1e FW |
525 | #endif |
526 | #ifdef CONFIG_NF_CONNTRACK_ZONES | |
527 | case NFT_CT_ZONE: | |
528 | if (--nft_ct_pcpu_template_refcnt == 0) | |
529 | nft_ct_tmpl_put_pcpu(); | |
5c178d81 FW |
530 | #endif |
531 | default: | |
532 | break; | |
533 | } | |
534 | } | |
535 | ||
fe92ca45 PM |
536 | static int nft_ct_set_init(const struct nft_ctx *ctx, |
537 | const struct nft_expr *expr, | |
538 | const struct nlattr * const tb[]) | |
c4ede3d3 | 539 | { |
fe92ca45 | 540 | struct nft_ct *priv = nft_expr_priv(expr); |
d07db988 | 541 | unsigned int len; |
fe92ca45 PM |
542 | int err; |
543 | ||
edee4f1e | 544 | priv->dir = IP_CT_DIR_MAX; |
fe92ca45 PM |
545 | priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY])); |
546 | switch (priv->key) { | |
e88e514e | 547 | #ifdef CONFIG_NF_CONNTRACK_MARK |
c4ede3d3 | 548 | case NFT_CT_MARK: |
7bfdde70 LZ |
549 | if (tb[NFTA_CT_DIRECTION]) |
550 | return -EINVAL; | |
c593642c | 551 | len = sizeof_field(struct nf_conn, mark); |
c4ede3d3 | 552 | break; |
1ad8f48d FW |
553 | #endif |
554 | #ifdef CONFIG_NF_CONNTRACK_LABELS | |
555 | case NFT_CT_LABELS: | |
556 | if (tb[NFTA_CT_DIRECTION]) | |
557 | return -EINVAL; | |
558 | len = NF_CT_LABELS_MAX_SIZE; | |
559 | err = nf_connlabels_get(ctx->net, (len * BITS_PER_BYTE) - 1); | |
560 | if (err) | |
561 | return err; | |
562 | break; | |
edee4f1e FW |
563 | #endif |
564 | #ifdef CONFIG_NF_CONNTRACK_ZONES | |
565 | case NFT_CT_ZONE: | |
566 | if (!nft_ct_tmpl_alloc_pcpu()) | |
567 | return -ENOMEM; | |
568 | nft_ct_pcpu_template_refcnt++; | |
427345d6 | 569 | len = sizeof(u16); |
edee4f1e | 570 | break; |
694a0055 FW |
571 | #endif |
572 | #ifdef CONFIG_NF_CONNTRACK_EVENTS | |
573 | case NFT_CT_EVENTMASK: | |
574 | if (tb[NFTA_CT_DIRECTION]) | |
575 | return -EINVAL; | |
576 | len = sizeof(u32); | |
577 | break; | |
b473a1f5 CG |
578 | #endif |
579 | #ifdef CONFIG_NF_CONNTRACK_SECMARK | |
580 | case NFT_CT_SECMARK: | |
581 | if (tb[NFTA_CT_DIRECTION]) | |
582 | return -EINVAL; | |
583 | len = sizeof(u32); | |
584 | break; | |
e88e514e | 585 | #endif |
c4ede3d3 KE |
586 | default: |
587 | return -EOPNOTSUPP; | |
588 | } | |
589 | ||
edee4f1e FW |
590 | if (tb[NFTA_CT_DIRECTION]) { |
591 | priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]); | |
592 | switch (priv->dir) { | |
593 | case IP_CT_DIR_ORIGINAL: | |
594 | case IP_CT_DIR_REPLY: | |
595 | break; | |
596 | default: | |
4494dbc6 LZ |
597 | err = -EINVAL; |
598 | goto err1; | |
edee4f1e FW |
599 | } |
600 | } | |
601 | ||
4f16d25c | 602 | err = nft_parse_register_load(tb[NFTA_CT_SREG], &priv->sreg, len); |
fe92ca45 | 603 | if (err < 0) |
590025a2 | 604 | goto err1; |
c4ede3d3 | 605 | |
36596dad | 606 | err = nf_ct_netns_get(ctx->net, ctx->family); |
96518518 | 607 | if (err < 0) |
590025a2 | 608 | goto err1; |
96518518 | 609 | |
96518518 | 610 | return 0; |
590025a2 LZ |
611 | |
612 | err1: | |
5c178d81 | 613 | __nft_ct_set_destroy(ctx, priv); |
590025a2 LZ |
614 | return err; |
615 | } | |
616 | ||
617 | static void nft_ct_get_destroy(const struct nft_ctx *ctx, | |
618 | const struct nft_expr *expr) | |
619 | { | |
36596dad | 620 | nf_ct_netns_put(ctx->net, ctx->family); |
96518518 PM |
621 | } |
622 | ||
590025a2 LZ |
623 | static void nft_ct_set_destroy(const struct nft_ctx *ctx, |
624 | const struct nft_expr *expr) | |
96518518 | 625 | { |
1ad8f48d FW |
626 | struct nft_ct *priv = nft_expr_priv(expr); |
627 | ||
5c178d81 | 628 | __nft_ct_set_destroy(ctx, priv); |
36596dad | 629 | nf_ct_netns_put(ctx->net, ctx->family); |
96518518 PM |
630 | } |
631 | ||
c4ede3d3 | 632 | static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr) |
96518518 PM |
633 | { |
634 | const struct nft_ct *priv = nft_expr_priv(expr); | |
635 | ||
b1c96ed3 | 636 | if (nft_dump_register(skb, NFTA_CT_DREG, priv->dreg)) |
96518518 PM |
637 | goto nla_put_failure; |
638 | if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key))) | |
639 | goto nla_put_failure; | |
2a53bfb3 AB |
640 | |
641 | switch (priv->key) { | |
2a53bfb3 AB |
642 | case NFT_CT_SRC: |
643 | case NFT_CT_DST: | |
d719e3f2 PNA |
644 | case NFT_CT_SRC_IP: |
645 | case NFT_CT_DST_IP: | |
646 | case NFT_CT_SRC_IP6: | |
647 | case NFT_CT_DST_IP6: | |
2a53bfb3 AB |
648 | case NFT_CT_PROTO_SRC: |
649 | case NFT_CT_PROTO_DST: | |
650 | if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir)) | |
651 | goto nla_put_failure; | |
48f66c90 FW |
652 | break; |
653 | case NFT_CT_BYTES: | |
654 | case NFT_CT_PKTS: | |
949a3584 | 655 | case NFT_CT_AVGPKT: |
ab23821f | 656 | case NFT_CT_ZONE: |
48f66c90 FW |
657 | if (priv->dir < IP_CT_DIR_MAX && |
658 | nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir)) | |
659 | goto nla_put_failure; | |
660 | break; | |
2a53bfb3 AB |
661 | default: |
662 | break; | |
663 | } | |
664 | ||
96518518 PM |
665 | return 0; |
666 | ||
667 | nla_put_failure: | |
668 | return -1; | |
669 | } | |
670 | ||
c4ede3d3 KE |
671 | static int nft_ct_set_dump(struct sk_buff *skb, const struct nft_expr *expr) |
672 | { | |
673 | const struct nft_ct *priv = nft_expr_priv(expr); | |
674 | ||
b1c96ed3 | 675 | if (nft_dump_register(skb, NFTA_CT_SREG, priv->sreg)) |
c4ede3d3 KE |
676 | goto nla_put_failure; |
677 | if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key))) | |
678 | goto nla_put_failure; | |
edee4f1e FW |
679 | |
680 | switch (priv->key) { | |
681 | case NFT_CT_ZONE: | |
682 | if (priv->dir < IP_CT_DIR_MAX && | |
683 | nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir)) | |
684 | goto nla_put_failure; | |
685 | break; | |
686 | default: | |
687 | break; | |
688 | } | |
689 | ||
c4ede3d3 KE |
690 | return 0; |
691 | ||
692 | nla_put_failure: | |
693 | return -1; | |
694 | } | |
695 | ||
ef1f7df9 | 696 | static struct nft_expr_type nft_ct_type; |
c4ede3d3 | 697 | static const struct nft_expr_ops nft_ct_get_ops = { |
ef1f7df9 | 698 | .type = &nft_ct_type, |
96518518 | 699 | .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), |
c4ede3d3 | 700 | .eval = nft_ct_get_eval, |
fe92ca45 | 701 | .init = nft_ct_get_init, |
590025a2 | 702 | .destroy = nft_ct_get_destroy, |
c4ede3d3 | 703 | .dump = nft_ct_get_dump, |
ef1f7df9 PM |
704 | }; |
705 | ||
c4ede3d3 KE |
706 | static const struct nft_expr_ops nft_ct_set_ops = { |
707 | .type = &nft_ct_type, | |
708 | .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), | |
709 | .eval = nft_ct_set_eval, | |
fe92ca45 | 710 | .init = nft_ct_set_init, |
590025a2 | 711 | .destroy = nft_ct_set_destroy, |
c4ede3d3 KE |
712 | .dump = nft_ct_set_dump, |
713 | }; | |
714 | ||
edee4f1e FW |
715 | #ifdef CONFIG_NF_CONNTRACK_ZONES |
716 | static const struct nft_expr_ops nft_ct_set_zone_ops = { | |
717 | .type = &nft_ct_type, | |
718 | .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), | |
719 | .eval = nft_ct_set_zone_eval, | |
720 | .init = nft_ct_set_init, | |
721 | .destroy = nft_ct_set_destroy, | |
722 | .dump = nft_ct_set_dump, | |
723 | }; | |
724 | #endif | |
725 | ||
c4ede3d3 KE |
726 | static const struct nft_expr_ops * |
727 | nft_ct_select_ops(const struct nft_ctx *ctx, | |
728 | const struct nlattr * const tb[]) | |
729 | { | |
730 | if (tb[NFTA_CT_KEY] == NULL) | |
731 | return ERR_PTR(-EINVAL); | |
732 | ||
733 | if (tb[NFTA_CT_DREG] && tb[NFTA_CT_SREG]) | |
734 | return ERR_PTR(-EINVAL); | |
735 | ||
736 | if (tb[NFTA_CT_DREG]) | |
737 | return &nft_ct_get_ops; | |
738 | ||
edee4f1e FW |
739 | if (tb[NFTA_CT_SREG]) { |
740 | #ifdef CONFIG_NF_CONNTRACK_ZONES | |
741 | if (nla_get_be32(tb[NFTA_CT_KEY]) == htonl(NFT_CT_ZONE)) | |
742 | return &nft_ct_set_zone_ops; | |
743 | #endif | |
c4ede3d3 | 744 | return &nft_ct_set_ops; |
edee4f1e | 745 | } |
c4ede3d3 KE |
746 | |
747 | return ERR_PTR(-EINVAL); | |
748 | } | |
749 | ||
ef1f7df9 PM |
750 | static struct nft_expr_type nft_ct_type __read_mostly = { |
751 | .name = "ct", | |
d4ef3835 | 752 | .select_ops = nft_ct_select_ops, |
96518518 PM |
753 | .policy = nft_ct_policy, |
754 | .maxattr = NFTA_CT_MAX, | |
ef1f7df9 | 755 | .owner = THIS_MODULE, |
96518518 PM |
756 | }; |
757 | ||
25443261 PNA |
758 | static void nft_notrack_eval(const struct nft_expr *expr, |
759 | struct nft_regs *regs, | |
760 | const struct nft_pktinfo *pkt) | |
761 | { | |
762 | struct sk_buff *skb = pkt->skb; | |
763 | enum ip_conntrack_info ctinfo; | |
764 | struct nf_conn *ct; | |
765 | ||
766 | ct = nf_ct_get(pkt->skb, &ctinfo); | |
767 | /* Previously seen (loopback or untracked)? Ignore. */ | |
cc41c84b | 768 | if (ct || ctinfo == IP_CT_UNTRACKED) |
25443261 PNA |
769 | return; |
770 | ||
cc41c84b | 771 | nf_ct_set(skb, ct, IP_CT_UNTRACKED); |
25443261 PNA |
772 | } |
773 | ||
774 | static struct nft_expr_type nft_notrack_type; | |
775 | static const struct nft_expr_ops nft_notrack_ops = { | |
776 | .type = &nft_notrack_type, | |
777 | .size = NFT_EXPR_SIZE(0), | |
778 | .eval = nft_notrack_eval, | |
779 | }; | |
780 | ||
781 | static struct nft_expr_type nft_notrack_type __read_mostly = { | |
782 | .name = "notrack", | |
783 | .ops = &nft_notrack_ops, | |
784 | .owner = THIS_MODULE, | |
785 | }; | |
786 | ||
7e0b2b57 HS |
787 | #ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
788 | static int | |
789 | nft_ct_timeout_parse_policy(void *timeouts, | |
790 | const struct nf_conntrack_l4proto *l4proto, | |
791 | struct net *net, const struct nlattr *attr) | |
792 | { | |
793 | struct nlattr **tb; | |
794 | int ret = 0; | |
795 | ||
7e0b2b57 HS |
796 | tb = kcalloc(l4proto->ctnl_timeout.nlattr_max + 1, sizeof(*tb), |
797 | GFP_KERNEL); | |
798 | ||
799 | if (!tb) | |
800 | return -ENOMEM; | |
801 | ||
8cb08174 JB |
802 | ret = nla_parse_nested_deprecated(tb, |
803 | l4proto->ctnl_timeout.nlattr_max, | |
804 | attr, | |
805 | l4proto->ctnl_timeout.nla_policy, | |
806 | NULL); | |
7e0b2b57 HS |
807 | if (ret < 0) |
808 | goto err; | |
809 | ||
810 | ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, net, timeouts); | |
811 | ||
812 | err: | |
813 | kfree(tb); | |
814 | return ret; | |
815 | } | |
816 | ||
817 | struct nft_ct_timeout_obj { | |
0434ccdc | 818 | struct nf_ct_timeout *timeout; |
7e0b2b57 HS |
819 | u8 l4proto; |
820 | }; | |
821 | ||
822 | static void nft_ct_timeout_obj_eval(struct nft_object *obj, | |
823 | struct nft_regs *regs, | |
824 | const struct nft_pktinfo *pkt) | |
825 | { | |
826 | const struct nft_ct_timeout_obj *priv = nft_obj_data(obj); | |
827 | struct nf_conn *ct = (struct nf_conn *)skb_nfct(pkt->skb); | |
0434ccdc FW |
828 | struct nf_conn_timeout *timeout; |
829 | const unsigned int *values; | |
830 | ||
831 | if (priv->l4proto != pkt->tprot) | |
832 | return; | |
7e0b2b57 | 833 | |
0434ccdc | 834 | if (!ct || nf_ct_is_template(ct) || nf_ct_is_confirmed(ct)) |
7e0b2b57 HS |
835 | return; |
836 | ||
0434ccdc FW |
837 | timeout = nf_ct_timeout_find(ct); |
838 | if (!timeout) { | |
839 | timeout = nf_ct_timeout_ext_add(ct, priv->timeout, GFP_ATOMIC); | |
840 | if (!timeout) { | |
841 | regs->verdict.code = NF_DROP; | |
842 | return; | |
843 | } | |
844 | } | |
845 | ||
846 | rcu_assign_pointer(timeout->timeout, priv->timeout); | |
847 | ||
848 | /* adjust the timeout as per 'new' state. ct is unconfirmed, | |
849 | * so the current timestamp must not be added. | |
850 | */ | |
851 | values = nf_ct_timeout_data(timeout); | |
852 | if (values) | |
853 | nf_ct_refresh(ct, pkt->skb, values[0]); | |
7e0b2b57 HS |
854 | } |
855 | ||
856 | static int nft_ct_timeout_obj_init(const struct nft_ctx *ctx, | |
857 | const struct nlattr * const tb[], | |
858 | struct nft_object *obj) | |
859 | { | |
7e0b2b57 HS |
860 | struct nft_ct_timeout_obj *priv = nft_obj_data(obj); |
861 | const struct nf_conntrack_l4proto *l4proto; | |
7e0b2b57 HS |
862 | struct nf_ct_timeout *timeout; |
863 | int l3num = ctx->family; | |
7e0b2b57 HS |
864 | __u8 l4num; |
865 | int ret; | |
866 | ||
3206c516 | 867 | if (!tb[NFTA_CT_TIMEOUT_L4PROTO] || |
7e0b2b57 HS |
868 | !tb[NFTA_CT_TIMEOUT_DATA]) |
869 | return -EINVAL; | |
870 | ||
3206c516 HS |
871 | if (tb[NFTA_CT_TIMEOUT_L3PROTO]) |
872 | l3num = ntohs(nla_get_be16(tb[NFTA_CT_TIMEOUT_L3PROTO])); | |
873 | ||
7e0b2b57 HS |
874 | l4num = nla_get_u8(tb[NFTA_CT_TIMEOUT_L4PROTO]); |
875 | priv->l4proto = l4num; | |
876 | ||
4a60dc74 | 877 | l4proto = nf_ct_l4proto_find(l4num); |
7e0b2b57 HS |
878 | |
879 | if (l4proto->l4proto != l4num) { | |
880 | ret = -EOPNOTSUPP; | |
881 | goto err_proto_put; | |
882 | } | |
883 | ||
884 | timeout = kzalloc(sizeof(struct nf_ct_timeout) + | |
885 | l4proto->ctnl_timeout.obj_size, GFP_KERNEL); | |
886 | if (timeout == NULL) { | |
887 | ret = -ENOMEM; | |
888 | goto err_proto_put; | |
889 | } | |
890 | ||
891 | ret = nft_ct_timeout_parse_policy(&timeout->data, l4proto, ctx->net, | |
892 | tb[NFTA_CT_TIMEOUT_DATA]); | |
893 | if (ret < 0) | |
894 | goto err_free_timeout; | |
895 | ||
896 | timeout->l3num = l3num; | |
897 | timeout->l4proto = l4proto; | |
7e0b2b57 HS |
898 | |
899 | ret = nf_ct_netns_get(ctx->net, ctx->family); | |
900 | if (ret < 0) | |
0434ccdc | 901 | goto err_free_timeout; |
7e0b2b57 | 902 | |
0434ccdc | 903 | priv->timeout = timeout; |
7e0b2b57 HS |
904 | return 0; |
905 | ||
7e0b2b57 HS |
906 | err_free_timeout: |
907 | kfree(timeout); | |
908 | err_proto_put: | |
7e0b2b57 HS |
909 | return ret; |
910 | } | |
911 | ||
912 | static void nft_ct_timeout_obj_destroy(const struct nft_ctx *ctx, | |
913 | struct nft_object *obj) | |
914 | { | |
915 | struct nft_ct_timeout_obj *priv = nft_obj_data(obj); | |
0434ccdc | 916 | struct nf_ct_timeout *timeout = priv->timeout; |
7e0b2b57 | 917 | |
7e0b2b57 | 918 | nf_ct_untimeout(ctx->net, timeout); |
7e0b2b57 | 919 | nf_ct_netns_put(ctx->net, ctx->family); |
0434ccdc | 920 | kfree(priv->timeout); |
7e0b2b57 HS |
921 | } |
922 | ||
923 | static int nft_ct_timeout_obj_dump(struct sk_buff *skb, | |
924 | struct nft_object *obj, bool reset) | |
925 | { | |
926 | const struct nft_ct_timeout_obj *priv = nft_obj_data(obj); | |
0434ccdc | 927 | const struct nf_ct_timeout *timeout = priv->timeout; |
7e0b2b57 HS |
928 | struct nlattr *nest_params; |
929 | int ret; | |
930 | ||
931 | if (nla_put_u8(skb, NFTA_CT_TIMEOUT_L4PROTO, timeout->l4proto->l4proto) || | |
932 | nla_put_be16(skb, NFTA_CT_TIMEOUT_L3PROTO, htons(timeout->l3num))) | |
933 | return -1; | |
934 | ||
ae0be8de | 935 | nest_params = nla_nest_start(skb, NFTA_CT_TIMEOUT_DATA); |
7e0b2b57 HS |
936 | if (!nest_params) |
937 | return -1; | |
938 | ||
939 | ret = timeout->l4proto->ctnl_timeout.obj_to_nlattr(skb, &timeout->data); | |
940 | if (ret < 0) | |
941 | return -1; | |
942 | nla_nest_end(skb, nest_params); | |
943 | return 0; | |
944 | } | |
945 | ||
946 | static const struct nla_policy nft_ct_timeout_policy[NFTA_CT_TIMEOUT_MAX + 1] = { | |
947 | [NFTA_CT_TIMEOUT_L3PROTO] = {.type = NLA_U16 }, | |
948 | [NFTA_CT_TIMEOUT_L4PROTO] = {.type = NLA_U8 }, | |
949 | [NFTA_CT_TIMEOUT_DATA] = {.type = NLA_NESTED }, | |
950 | }; | |
951 | ||
952 | static struct nft_object_type nft_ct_timeout_obj_type; | |
953 | ||
954 | static const struct nft_object_ops nft_ct_timeout_obj_ops = { | |
955 | .type = &nft_ct_timeout_obj_type, | |
956 | .size = sizeof(struct nft_ct_timeout_obj), | |
957 | .eval = nft_ct_timeout_obj_eval, | |
958 | .init = nft_ct_timeout_obj_init, | |
959 | .destroy = nft_ct_timeout_obj_destroy, | |
960 | .dump = nft_ct_timeout_obj_dump, | |
961 | }; | |
962 | ||
963 | static struct nft_object_type nft_ct_timeout_obj_type __read_mostly = { | |
964 | .type = NFT_OBJECT_CT_TIMEOUT, | |
965 | .ops = &nft_ct_timeout_obj_ops, | |
966 | .maxattr = NFTA_CT_TIMEOUT_MAX, | |
967 | .policy = nft_ct_timeout_policy, | |
968 | .owner = THIS_MODULE, | |
969 | }; | |
970 | #endif /* CONFIG_NF_CONNTRACK_TIMEOUT */ | |
971 | ||
1a64edf5 FW |
972 | static int nft_ct_helper_obj_init(const struct nft_ctx *ctx, |
973 | const struct nlattr * const tb[], | |
974 | struct nft_object *obj) | |
975 | { | |
976 | struct nft_ct_helper_obj *priv = nft_obj_data(obj); | |
977 | struct nf_conntrack_helper *help4, *help6; | |
978 | char name[NF_CT_HELPER_NAME_LEN]; | |
36596dad | 979 | int family = ctx->family; |
f699edb1 | 980 | int err; |
1a64edf5 FW |
981 | |
982 | if (!tb[NFTA_CT_HELPER_NAME] || !tb[NFTA_CT_HELPER_L4PROTO]) | |
983 | return -EINVAL; | |
984 | ||
985 | priv->l4proto = nla_get_u8(tb[NFTA_CT_HELPER_L4PROTO]); | |
986 | if (!priv->l4proto) | |
987 | return -ENOENT; | |
988 | ||
872f6903 | 989 | nla_strscpy(name, tb[NFTA_CT_HELPER_NAME], sizeof(name)); |
1a64edf5 FW |
990 | |
991 | if (tb[NFTA_CT_HELPER_L3PROTO]) | |
992 | family = ntohs(nla_get_be16(tb[NFTA_CT_HELPER_L3PROTO])); | |
993 | ||
994 | help4 = NULL; | |
995 | help6 = NULL; | |
996 | ||
997 | switch (family) { | |
998 | case NFPROTO_IPV4: | |
36596dad | 999 | if (ctx->family == NFPROTO_IPV6) |
1a64edf5 FW |
1000 | return -EINVAL; |
1001 | ||
1002 | help4 = nf_conntrack_helper_try_module_get(name, family, | |
1003 | priv->l4proto); | |
1004 | break; | |
1005 | case NFPROTO_IPV6: | |
36596dad | 1006 | if (ctx->family == NFPROTO_IPV4) |
1a64edf5 FW |
1007 | return -EINVAL; |
1008 | ||
1009 | help6 = nf_conntrack_helper_try_module_get(name, family, | |
1010 | priv->l4proto); | |
1011 | break; | |
954d8297 GS |
1012 | case NFPROTO_NETDEV: |
1013 | case NFPROTO_BRIDGE: | |
1a64edf5 FW |
1014 | case NFPROTO_INET: |
1015 | help4 = nf_conntrack_helper_try_module_get(name, NFPROTO_IPV4, | |
1016 | priv->l4proto); | |
1017 | help6 = nf_conntrack_helper_try_module_get(name, NFPROTO_IPV6, | |
1018 | priv->l4proto); | |
1019 | break; | |
1020 | default: | |
1021 | return -EAFNOSUPPORT; | |
1022 | } | |
1023 | ||
1024 | /* && is intentional; only error if INET found neither ipv4 or ipv6 */ | |
1025 | if (!help4 && !help6) | |
1026 | return -ENOENT; | |
1027 | ||
1028 | priv->helper4 = help4; | |
1029 | priv->helper6 = help6; | |
1030 | ||
f699edb1 PNA |
1031 | err = nf_ct_netns_get(ctx->net, ctx->family); |
1032 | if (err < 0) | |
1033 | goto err_put_helper; | |
1034 | ||
1a64edf5 | 1035 | return 0; |
f699edb1 PNA |
1036 | |
1037 | err_put_helper: | |
1038 | if (priv->helper4) | |
1039 | nf_conntrack_helper_put(priv->helper4); | |
1040 | if (priv->helper6) | |
1041 | nf_conntrack_helper_put(priv->helper6); | |
1042 | return err; | |
1a64edf5 FW |
1043 | } |
1044 | ||
00bfb320 PNA |
1045 | static void nft_ct_helper_obj_destroy(const struct nft_ctx *ctx, |
1046 | struct nft_object *obj) | |
1a64edf5 FW |
1047 | { |
1048 | struct nft_ct_helper_obj *priv = nft_obj_data(obj); | |
1049 | ||
1050 | if (priv->helper4) | |
d91fc59c | 1051 | nf_conntrack_helper_put(priv->helper4); |
1a64edf5 | 1052 | if (priv->helper6) |
d91fc59c | 1053 | nf_conntrack_helper_put(priv->helper6); |
f699edb1 PNA |
1054 | |
1055 | nf_ct_netns_put(ctx->net, ctx->family); | |
1a64edf5 FW |
1056 | } |
1057 | ||
1058 | static void nft_ct_helper_obj_eval(struct nft_object *obj, | |
1059 | struct nft_regs *regs, | |
1060 | const struct nft_pktinfo *pkt) | |
1061 | { | |
1062 | const struct nft_ct_helper_obj *priv = nft_obj_data(obj); | |
1063 | struct nf_conn *ct = (struct nf_conn *)skb_nfct(pkt->skb); | |
1064 | struct nf_conntrack_helper *to_assign = NULL; | |
1065 | struct nf_conn_help *help; | |
1066 | ||
1067 | if (!ct || | |
1068 | nf_ct_is_confirmed(ct) || | |
1069 | nf_ct_is_template(ct) || | |
1070 | priv->l4proto != nf_ct_protonum(ct)) | |
1071 | return; | |
1072 | ||
1073 | switch (nf_ct_l3num(ct)) { | |
1074 | case NFPROTO_IPV4: | |
1075 | to_assign = priv->helper4; | |
1076 | break; | |
1077 | case NFPROTO_IPV6: | |
1078 | to_assign = priv->helper6; | |
1079 | break; | |
1080 | default: | |
1081 | WARN_ON_ONCE(1); | |
1082 | return; | |
1083 | } | |
1084 | ||
1085 | if (!to_assign) | |
1086 | return; | |
1087 | ||
1088 | if (test_bit(IPS_HELPER_BIT, &ct->status)) | |
1089 | return; | |
1090 | ||
440534d3 | 1091 | help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); |
1a64edf5 FW |
1092 | if (help) { |
1093 | rcu_assign_pointer(help->helper, to_assign); | |
1094 | set_bit(IPS_HELPER_BIT, &ct->status); | |
1095 | } | |
1096 | } | |
1097 | ||
1098 | static int nft_ct_helper_obj_dump(struct sk_buff *skb, | |
1099 | struct nft_object *obj, bool reset) | |
1100 | { | |
1101 | const struct nft_ct_helper_obj *priv = nft_obj_data(obj); | |
b7153458 | 1102 | const struct nf_conntrack_helper *helper; |
1a64edf5 FW |
1103 | u16 family; |
1104 | ||
b7153458 TY |
1105 | if (priv->helper4 && priv->helper6) { |
1106 | family = NFPROTO_INET; | |
1107 | helper = priv->helper4; | |
1108 | } else if (priv->helper6) { | |
1109 | family = NFPROTO_IPV6; | |
1110 | helper = priv->helper6; | |
1111 | } else { | |
1112 | family = NFPROTO_IPV4; | |
1113 | helper = priv->helper4; | |
1114 | } | |
1115 | ||
1a64edf5 FW |
1116 | if (nla_put_string(skb, NFTA_CT_HELPER_NAME, helper->name)) |
1117 | return -1; | |
1118 | ||
1119 | if (nla_put_u8(skb, NFTA_CT_HELPER_L4PROTO, priv->l4proto)) | |
1120 | return -1; | |
1121 | ||
1a64edf5 FW |
1122 | if (nla_put_be16(skb, NFTA_CT_HELPER_L3PROTO, htons(family))) |
1123 | return -1; | |
1124 | ||
1125 | return 0; | |
1126 | } | |
1127 | ||
1128 | static const struct nla_policy nft_ct_helper_policy[NFTA_CT_HELPER_MAX + 1] = { | |
1129 | [NFTA_CT_HELPER_NAME] = { .type = NLA_STRING, | |
1130 | .len = NF_CT_HELPER_NAME_LEN - 1 }, | |
1131 | [NFTA_CT_HELPER_L3PROTO] = { .type = NLA_U16 }, | |
1132 | [NFTA_CT_HELPER_L4PROTO] = { .type = NLA_U8 }, | |
1133 | }; | |
1134 | ||
dfc46034 PBG |
1135 | static struct nft_object_type nft_ct_helper_obj_type; |
1136 | static const struct nft_object_ops nft_ct_helper_obj_ops = { | |
1137 | .type = &nft_ct_helper_obj_type, | |
1a64edf5 | 1138 | .size = sizeof(struct nft_ct_helper_obj), |
1a64edf5 FW |
1139 | .eval = nft_ct_helper_obj_eval, |
1140 | .init = nft_ct_helper_obj_init, | |
1141 | .destroy = nft_ct_helper_obj_destroy, | |
1142 | .dump = nft_ct_helper_obj_dump, | |
dfc46034 PBG |
1143 | }; |
1144 | ||
1145 | static struct nft_object_type nft_ct_helper_obj_type __read_mostly = { | |
1146 | .type = NFT_OBJECT_CT_HELPER, | |
1147 | .ops = &nft_ct_helper_obj_ops, | |
1148 | .maxattr = NFTA_CT_HELPER_MAX, | |
1149 | .policy = nft_ct_helper_policy, | |
1a64edf5 FW |
1150 | .owner = THIS_MODULE, |
1151 | }; | |
1152 | ||
857b4602 SV |
1153 | struct nft_ct_expect_obj { |
1154 | u16 l3num; | |
1155 | __be16 dport; | |
1156 | u8 l4proto; | |
1157 | u8 size; | |
1158 | u32 timeout; | |
1159 | }; | |
1160 | ||
1161 | static int nft_ct_expect_obj_init(const struct nft_ctx *ctx, | |
1162 | const struct nlattr * const tb[], | |
1163 | struct nft_object *obj) | |
1164 | { | |
1165 | struct nft_ct_expect_obj *priv = nft_obj_data(obj); | |
1166 | ||
1167 | if (!tb[NFTA_CT_EXPECT_L4PROTO] || | |
1168 | !tb[NFTA_CT_EXPECT_DPORT] || | |
1169 | !tb[NFTA_CT_EXPECT_TIMEOUT] || | |
1170 | !tb[NFTA_CT_EXPECT_SIZE]) | |
1171 | return -EINVAL; | |
1172 | ||
1173 | priv->l3num = ctx->family; | |
1174 | if (tb[NFTA_CT_EXPECT_L3PROTO]) | |
1175 | priv->l3num = ntohs(nla_get_be16(tb[NFTA_CT_EXPECT_L3PROTO])); | |
1176 | ||
1177 | priv->l4proto = nla_get_u8(tb[NFTA_CT_EXPECT_L4PROTO]); | |
1178 | priv->dport = nla_get_be16(tb[NFTA_CT_EXPECT_DPORT]); | |
1179 | priv->timeout = nla_get_u32(tb[NFTA_CT_EXPECT_TIMEOUT]); | |
1180 | priv->size = nla_get_u8(tb[NFTA_CT_EXPECT_SIZE]); | |
1181 | ||
1182 | return nf_ct_netns_get(ctx->net, ctx->family); | |
1183 | } | |
1184 | ||
1185 | static void nft_ct_expect_obj_destroy(const struct nft_ctx *ctx, | |
1186 | struct nft_object *obj) | |
1187 | { | |
1188 | nf_ct_netns_put(ctx->net, ctx->family); | |
1189 | } | |
1190 | ||
1191 | static int nft_ct_expect_obj_dump(struct sk_buff *skb, | |
1192 | struct nft_object *obj, bool reset) | |
1193 | { | |
1194 | const struct nft_ct_expect_obj *priv = nft_obj_data(obj); | |
1195 | ||
1196 | if (nla_put_be16(skb, NFTA_CT_EXPECT_L3PROTO, htons(priv->l3num)) || | |
1197 | nla_put_u8(skb, NFTA_CT_EXPECT_L4PROTO, priv->l4proto) || | |
1198 | nla_put_be16(skb, NFTA_CT_EXPECT_DPORT, priv->dport) || | |
1199 | nla_put_u32(skb, NFTA_CT_EXPECT_TIMEOUT, priv->timeout) || | |
1200 | nla_put_u8(skb, NFTA_CT_EXPECT_SIZE, priv->size)) | |
1201 | return -1; | |
1202 | ||
1203 | return 0; | |
1204 | } | |
1205 | ||
1206 | static void nft_ct_expect_obj_eval(struct nft_object *obj, | |
1207 | struct nft_regs *regs, | |
1208 | const struct nft_pktinfo *pkt) | |
1209 | { | |
1210 | const struct nft_ct_expect_obj *priv = nft_obj_data(obj); | |
1211 | struct nf_conntrack_expect *exp; | |
1212 | enum ip_conntrack_info ctinfo; | |
1213 | struct nf_conn_help *help; | |
1214 | enum ip_conntrack_dir dir; | |
1215 | u16 l3num = priv->l3num; | |
1216 | struct nf_conn *ct; | |
1217 | ||
1218 | ct = nf_ct_get(pkt->skb, &ctinfo); | |
1219 | if (!ct || ctinfo == IP_CT_UNTRACKED) { | |
1220 | regs->verdict.code = NFT_BREAK; | |
1221 | return; | |
1222 | } | |
1223 | dir = CTINFO2DIR(ctinfo); | |
1224 | ||
1225 | help = nfct_help(ct); | |
1226 | if (!help) | |
1227 | help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); | |
2f0513d4 SV |
1228 | if (!help) { |
1229 | regs->verdict.code = NF_DROP; | |
1230 | return; | |
1231 | } | |
857b4602 SV |
1232 | |
1233 | if (help->expecting[NF_CT_EXPECT_CLASS_DEFAULT] >= priv->size) { | |
1234 | regs->verdict.code = NFT_BREAK; | |
1235 | return; | |
1236 | } | |
1237 | if (l3num == NFPROTO_INET) | |
1238 | l3num = nf_ct_l3num(ct); | |
1239 | ||
1240 | exp = nf_ct_expect_alloc(ct); | |
1241 | if (exp == NULL) { | |
1242 | regs->verdict.code = NF_DROP; | |
1243 | return; | |
1244 | } | |
1245 | nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, l3num, | |
1246 | &ct->tuplehash[!dir].tuple.src.u3, | |
1247 | &ct->tuplehash[!dir].tuple.dst.u3, | |
1248 | priv->l4proto, NULL, &priv->dport); | |
1249 | exp->timeout.expires = jiffies + priv->timeout * HZ; | |
1250 | ||
3c00fb0b | 1251 | if (nf_ct_expect_related(exp, 0) != 0) |
857b4602 SV |
1252 | regs->verdict.code = NF_DROP; |
1253 | } | |
1254 | ||
1255 | static const struct nla_policy nft_ct_expect_policy[NFTA_CT_EXPECT_MAX + 1] = { | |
1256 | [NFTA_CT_EXPECT_L3PROTO] = { .type = NLA_U16 }, | |
1257 | [NFTA_CT_EXPECT_L4PROTO] = { .type = NLA_U8 }, | |
1258 | [NFTA_CT_EXPECT_DPORT] = { .type = NLA_U16 }, | |
1259 | [NFTA_CT_EXPECT_TIMEOUT] = { .type = NLA_U32 }, | |
1260 | [NFTA_CT_EXPECT_SIZE] = { .type = NLA_U8 }, | |
1261 | }; | |
1262 | ||
1263 | static struct nft_object_type nft_ct_expect_obj_type; | |
1264 | ||
1265 | static const struct nft_object_ops nft_ct_expect_obj_ops = { | |
1266 | .type = &nft_ct_expect_obj_type, | |
1267 | .size = sizeof(struct nft_ct_expect_obj), | |
1268 | .eval = nft_ct_expect_obj_eval, | |
1269 | .init = nft_ct_expect_obj_init, | |
1270 | .destroy = nft_ct_expect_obj_destroy, | |
1271 | .dump = nft_ct_expect_obj_dump, | |
1272 | }; | |
1273 | ||
1274 | static struct nft_object_type nft_ct_expect_obj_type __read_mostly = { | |
1275 | .type = NFT_OBJECT_CT_EXPECT, | |
1276 | .ops = &nft_ct_expect_obj_ops, | |
1277 | .maxattr = NFTA_CT_EXPECT_MAX, | |
1278 | .policy = nft_ct_expect_policy, | |
1279 | .owner = THIS_MODULE, | |
1280 | }; | |
1281 | ||
96518518 PM |
1282 | static int __init nft_ct_module_init(void) |
1283 | { | |
25443261 PNA |
1284 | int err; |
1285 | ||
adff6c65 FW |
1286 | BUILD_BUG_ON(NF_CT_LABELS_MAX_SIZE > NFT_REG_SIZE); |
1287 | ||
25443261 PNA |
1288 | err = nft_register_expr(&nft_ct_type); |
1289 | if (err < 0) | |
1290 | return err; | |
1291 | ||
1292 | err = nft_register_expr(&nft_notrack_type); | |
1293 | if (err < 0) | |
1294 | goto err1; | |
1295 | ||
dfc46034 | 1296 | err = nft_register_obj(&nft_ct_helper_obj_type); |
1a64edf5 FW |
1297 | if (err < 0) |
1298 | goto err2; | |
857b4602 SV |
1299 | |
1300 | err = nft_register_obj(&nft_ct_expect_obj_type); | |
1301 | if (err < 0) | |
1302 | goto err3; | |
7e0b2b57 HS |
1303 | #ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
1304 | err = nft_register_obj(&nft_ct_timeout_obj_type); | |
1305 | if (err < 0) | |
857b4602 | 1306 | goto err4; |
7e0b2b57 | 1307 | #endif |
25443261 | 1308 | return 0; |
1a64edf5 | 1309 | |
7e0b2b57 | 1310 | #ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
857b4602 SV |
1311 | err4: |
1312 | nft_unregister_obj(&nft_ct_expect_obj_type); | |
1313 | #endif | |
7e0b2b57 HS |
1314 | err3: |
1315 | nft_unregister_obj(&nft_ct_helper_obj_type); | |
1a64edf5 FW |
1316 | err2: |
1317 | nft_unregister_expr(&nft_notrack_type); | |
25443261 PNA |
1318 | err1: |
1319 | nft_unregister_expr(&nft_ct_type); | |
1320 | return err; | |
96518518 PM |
1321 | } |
1322 | ||
1323 | static void __exit nft_ct_module_exit(void) | |
1324 | { | |
7e0b2b57 HS |
1325 | #ifdef CONFIG_NF_CONNTRACK_TIMEOUT |
1326 | nft_unregister_obj(&nft_ct_timeout_obj_type); | |
1327 | #endif | |
857b4602 | 1328 | nft_unregister_obj(&nft_ct_expect_obj_type); |
dfc46034 | 1329 | nft_unregister_obj(&nft_ct_helper_obj_type); |
25443261 | 1330 | nft_unregister_expr(&nft_notrack_type); |
ef1f7df9 | 1331 | nft_unregister_expr(&nft_ct_type); |
96518518 PM |
1332 | } |
1333 | ||
1334 | module_init(nft_ct_module_init); | |
1335 | module_exit(nft_ct_module_exit); | |
1336 | ||
1337 | MODULE_LICENSE("GPL"); | |
1338 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | |
1339 | MODULE_ALIAS_NFT_EXPR("ct"); | |
25443261 | 1340 | MODULE_ALIAS_NFT_EXPR("notrack"); |
1a64edf5 | 1341 | MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_CT_HELPER); |
7e0b2b57 | 1342 | MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_CT_TIMEOUT); |
857b4602 | 1343 | MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_CT_EXPECT); |
4cacc395 | 1344 | MODULE_DESCRIPTION("Netfilter nf_tables conntrack module"); |