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