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