]>
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> |
bd2bbdb4 FW |
4 | * Copyright (c) 2014 Intel Corporation |
5 | * Author: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com> | |
96518518 | 6 | * |
96518518 PM |
7 | * Development of this code funded by Astaro AG (http://www.astaro.com/) |
8 | */ | |
9 | ||
10 | #include <linux/kernel.h> | |
96518518 PM |
11 | #include <linux/netlink.h> |
12 | #include <linux/netfilter.h> | |
13 | #include <linux/netfilter/nf_tables.h> | |
e2a093ff AR |
14 | #include <linux/in.h> |
15 | #include <linux/ip.h> | |
16 | #include <linux/ipv6.h> | |
afc5be30 | 17 | #include <linux/smp.h> |
e639f7ab | 18 | #include <linux/static_key.h> |
96518518 PM |
19 | #include <net/dst.h> |
20 | #include <net/sock.h> | |
21 | #include <net/tcp_states.h> /* for TCP_TIME_WAIT */ | |
22 | #include <net/netfilter/nf_tables.h> | |
e639f7ab | 23 | #include <net/netfilter/nf_tables_core.h> |
30e103fe | 24 | #include <net/netfilter/nft_meta.h> |
c9626a2c | 25 | #include <net/netfilter/nf_tables_offload.h> |
96518518 | 26 | |
b4aae759 FW |
27 | #include <uapi/linux/netfilter_bridge.h> /* NF_BR_PRE_ROUTING */ |
28 | ||
63d10e12 AJ |
29 | #define NFT_META_SECS_PER_MINUTE 60 |
30 | #define NFT_META_SECS_PER_HOUR 3600 | |
31 | #define NFT_META_SECS_PER_DAY 86400 | |
32 | #define NFT_META_DAYS_PER_WEEK 7 | |
33 | ||
b07edbe1 FW |
34 | static DEFINE_PER_CPU(struct rnd_state, nft_prandom_state); |
35 | ||
db8f6f5c | 36 | static u8 nft_meta_weekday(void) |
63d10e12 | 37 | { |
db8f6f5c | 38 | time64_t secs = ktime_get_real_seconds(); |
63d10e12 AJ |
39 | unsigned int dse; |
40 | u8 wday; | |
41 | ||
42 | secs -= NFT_META_SECS_PER_MINUTE * sys_tz.tz_minuteswest; | |
6408c40c | 43 | dse = div_u64(secs, NFT_META_SECS_PER_DAY); |
63d10e12 AJ |
44 | wday = (4 + dse) % NFT_META_DAYS_PER_WEEK; |
45 | ||
46 | return wday; | |
47 | } | |
48 | ||
6408c40c | 49 | static u32 nft_meta_hour(time64_t secs) |
63d10e12 AJ |
50 | { |
51 | struct tm tm; | |
52 | ||
53 | time64_to_tm(secs, 0, &tm); | |
54 | ||
55 | return tm.tm_hour * NFT_META_SECS_PER_HOUR | |
56 | + tm.tm_min * NFT_META_SECS_PER_MINUTE | |
57 | + tm.tm_sec; | |
58 | } | |
59 | ||
db8f6f5c FW |
60 | static noinline_for_stack void |
61 | nft_meta_get_eval_time(enum nft_meta_keys key, | |
62 | u32 *dest) | |
63 | { | |
64 | switch (key) { | |
65 | case NFT_META_TIME_NS: | |
66 | nft_reg_store64(dest, ktime_get_real_ns()); | |
67 | break; | |
68 | case NFT_META_TIME_DAY: | |
69 | nft_reg_store8(dest, nft_meta_weekday()); | |
70 | break; | |
71 | case NFT_META_TIME_HOUR: | |
72 | *dest = nft_meta_hour(ktime_get_real_seconds()); | |
73 | break; | |
74 | default: | |
75 | break; | |
76 | } | |
77 | } | |
78 | ||
4a54594a FW |
79 | static noinline bool |
80 | nft_meta_get_eval_pkttype_lo(const struct nft_pktinfo *pkt, | |
81 | u32 *dest) | |
82 | { | |
83 | const struct sk_buff *skb = pkt->skb; | |
84 | ||
85 | switch (nft_pf(pkt)) { | |
86 | case NFPROTO_IPV4: | |
87 | if (ipv4_is_multicast(ip_hdr(skb)->daddr)) | |
88 | nft_reg_store8(dest, PACKET_MULTICAST); | |
89 | else | |
90 | nft_reg_store8(dest, PACKET_BROADCAST); | |
91 | break; | |
92 | case NFPROTO_IPV6: | |
93 | nft_reg_store8(dest, PACKET_MULTICAST); | |
94 | break; | |
95 | case NFPROTO_NETDEV: | |
96 | switch (skb->protocol) { | |
97 | case htons(ETH_P_IP): { | |
98 | int noff = skb_network_offset(skb); | |
99 | struct iphdr *iph, _iph; | |
100 | ||
101 | iph = skb_header_pointer(skb, noff, | |
102 | sizeof(_iph), &_iph); | |
103 | if (!iph) | |
104 | return false; | |
105 | ||
106 | if (ipv4_is_multicast(iph->daddr)) | |
107 | nft_reg_store8(dest, PACKET_MULTICAST); | |
108 | else | |
109 | nft_reg_store8(dest, PACKET_BROADCAST); | |
110 | ||
111 | break; | |
112 | } | |
113 | case htons(ETH_P_IPV6): | |
114 | nft_reg_store8(dest, PACKET_MULTICAST); | |
115 | break; | |
116 | default: | |
117 | WARN_ON_ONCE(1); | |
118 | return false; | |
119 | } | |
120 | break; | |
121 | default: | |
122 | WARN_ON_ONCE(1); | |
123 | return false; | |
124 | } | |
125 | ||
126 | return true; | |
127 | } | |
128 | ||
222440b4 FW |
129 | void nft_meta_get_eval(const struct nft_expr *expr, |
130 | struct nft_regs *regs, | |
131 | const struct nft_pktinfo *pkt) | |
96518518 PM |
132 | { |
133 | const struct nft_meta *priv = nft_expr_priv(expr); | |
134 | const struct sk_buff *skb = pkt->skb; | |
0e5a1c7e | 135 | const struct net_device *in = nft_in(pkt), *out = nft_out(pkt); |
3aed8225 | 136 | struct sock *sk; |
49499c3e | 137 | u32 *dest = ®s->data[priv->dreg]; |
96518518 PM |
138 | |
139 | switch (priv->key) { | |
140 | case NFT_META_LEN: | |
fad136ea | 141 | *dest = skb->len; |
96518518 PM |
142 | break; |
143 | case NFT_META_PROTOCOL: | |
10596608 | 144 | nft_reg_store16(dest, (__force u16)skb->protocol); |
96518518 | 145 | break; |
124edfa9 | 146 | case NFT_META_NFPROTO: |
10596608 | 147 | nft_reg_store8(dest, nft_pf(pkt)); |
124edfa9 | 148 | break; |
4566bf27 | 149 | case NFT_META_L4PROTO: |
beac5afa PNA |
150 | if (!pkt->tprot_set) |
151 | goto err; | |
10596608 | 152 | nft_reg_store8(dest, pkt->tprot); |
4566bf27 | 153 | break; |
96518518 | 154 | case NFT_META_PRIORITY: |
fad136ea | 155 | *dest = skb->priority; |
96518518 PM |
156 | break; |
157 | case NFT_META_MARK: | |
fad136ea | 158 | *dest = skb->mark; |
96518518 PM |
159 | break; |
160 | case NFT_META_IIF: | |
cb81572e | 161 | *dest = in ? in->ifindex : 0; |
96518518 PM |
162 | break; |
163 | case NFT_META_OIF: | |
cb81572e | 164 | *dest = out ? out->ifindex : 0; |
96518518 PM |
165 | break; |
166 | case NFT_META_IIFNAME: | |
cb81572e | 167 | strncpy((char *)dest, in ? in->name : "", IFNAMSIZ); |
96518518 PM |
168 | break; |
169 | case NFT_META_OIFNAME: | |
cb81572e | 170 | strncpy((char *)dest, out ? out->name : "", IFNAMSIZ); |
96518518 PM |
171 | break; |
172 | case NFT_META_IIFTYPE: | |
173 | if (in == NULL) | |
174 | goto err; | |
10596608 | 175 | nft_reg_store16(dest, in->type); |
96518518 PM |
176 | break; |
177 | case NFT_META_OIFTYPE: | |
178 | if (out == NULL) | |
179 | goto err; | |
10596608 | 180 | nft_reg_store16(dest, out->type); |
96518518 PM |
181 | break; |
182 | case NFT_META_SKUID: | |
3aed8225 | 183 | sk = skb_to_full_sk(skb); |
f5646501 FL |
184 | if (!sk || !sk_fullsock(sk) || |
185 | !net_eq(nft_net(pkt), sock_net(sk))) | |
96518518 PM |
186 | goto err; |
187 | ||
3aed8225 ED |
188 | read_lock_bh(&sk->sk_callback_lock); |
189 | if (sk->sk_socket == NULL || | |
190 | sk->sk_socket->file == NULL) { | |
191 | read_unlock_bh(&sk->sk_callback_lock); | |
96518518 PM |
192 | goto err; |
193 | } | |
194 | ||
fad136ea | 195 | *dest = from_kuid_munged(&init_user_ns, |
3aed8225 ED |
196 | sk->sk_socket->file->f_cred->fsuid); |
197 | read_unlock_bh(&sk->sk_callback_lock); | |
96518518 PM |
198 | break; |
199 | case NFT_META_SKGID: | |
3aed8225 | 200 | sk = skb_to_full_sk(skb); |
f5646501 FL |
201 | if (!sk || !sk_fullsock(sk) || |
202 | !net_eq(nft_net(pkt), sock_net(sk))) | |
96518518 PM |
203 | goto err; |
204 | ||
3aed8225 ED |
205 | read_lock_bh(&sk->sk_callback_lock); |
206 | if (sk->sk_socket == NULL || | |
207 | sk->sk_socket->file == NULL) { | |
208 | read_unlock_bh(&sk->sk_callback_lock); | |
96518518 PM |
209 | goto err; |
210 | } | |
fad136ea | 211 | *dest = from_kgid_munged(&init_user_ns, |
3aed8225 ED |
212 | sk->sk_socket->file->f_cred->fsgid); |
213 | read_unlock_bh(&sk->sk_callback_lock); | |
96518518 | 214 | break; |
06efbd6d | 215 | #ifdef CONFIG_IP_ROUTE_CLASSID |
96518518 PM |
216 | case NFT_META_RTCLASSID: { |
217 | const struct dst_entry *dst = skb_dst(skb); | |
218 | ||
219 | if (dst == NULL) | |
220 | goto err; | |
fad136ea | 221 | *dest = dst->tclassid; |
96518518 PM |
222 | break; |
223 | } | |
224 | #endif | |
225 | #ifdef CONFIG_NETWORK_SECMARK | |
226 | case NFT_META_SECMARK: | |
fad136ea | 227 | *dest = skb->secmark; |
96518518 PM |
228 | break; |
229 | #endif | |
e2a093ff AR |
230 | case NFT_META_PKTTYPE: |
231 | if (skb->pkt_type != PACKET_LOOPBACK) { | |
10596608 | 232 | nft_reg_store8(dest, skb->pkt_type); |
e2a093ff AR |
233 | break; |
234 | } | |
235 | ||
4a54594a | 236 | if (!nft_meta_get_eval_pkttype_lo(pkt, dest)) |
e2a093ff | 237 | goto err; |
e2a093ff | 238 | break; |
afc5be30 | 239 | case NFT_META_CPU: |
fad136ea | 240 | *dest = raw_smp_processor_id(); |
afc5be30 | 241 | break; |
3045d760 AR |
242 | case NFT_META_IIFGROUP: |
243 | if (in == NULL) | |
244 | goto err; | |
fad136ea | 245 | *dest = in->group; |
3045d760 AR |
246 | break; |
247 | case NFT_META_OIFGROUP: | |
248 | if (out == NULL) | |
249 | goto err; | |
fad136ea | 250 | *dest = out->group; |
3045d760 | 251 | break; |
e181a543 | 252 | #ifdef CONFIG_CGROUP_NET_CLASSID |
ce674173 | 253 | case NFT_META_CGROUP: |
3aed8225 | 254 | sk = skb_to_full_sk(skb); |
f5646501 FL |
255 | if (!sk || !sk_fullsock(sk) || |
256 | !net_eq(nft_net(pkt), sock_net(sk))) | |
c5035c77 | 257 | goto err; |
2a56a1fe | 258 | *dest = sock_cgroup_classid(&sk->sk_cgrp_data); |
ce674173 | 259 | break; |
e181a543 | 260 | #endif |
b07edbe1 FW |
261 | case NFT_META_PRANDOM: { |
262 | struct rnd_state *state = this_cpu_ptr(&nft_prandom_state); | |
263 | *dest = prandom_u32_state(state); | |
264 | break; | |
265 | } | |
f6931f5f FW |
266 | #ifdef CONFIG_XFRM |
267 | case NFT_META_SECPATH: | |
7af8f4ca | 268 | nft_reg_store8(dest, secpath_exists(skb)); |
f6931f5f FW |
269 | break; |
270 | #endif | |
0fb4d219 | 271 | case NFT_META_IIFKIND: |
272 | if (in == NULL || in->rtnl_link_ops == NULL) | |
273 | goto err; | |
274 | strncpy((char *)dest, in->rtnl_link_ops->kind, IFNAMSIZ); | |
275 | break; | |
276 | case NFT_META_OIFKIND: | |
277 | if (out == NULL || out->rtnl_link_ops == NULL) | |
278 | goto err; | |
279 | strncpy((char *)dest, out->rtnl_link_ops->kind, IFNAMSIZ); | |
280 | break; | |
63d10e12 | 281 | case NFT_META_TIME_NS: |
63d10e12 | 282 | case NFT_META_TIME_DAY: |
63d10e12 | 283 | case NFT_META_TIME_HOUR: |
db8f6f5c | 284 | nft_meta_get_eval_time(priv->key, dest); |
63d10e12 | 285 | break; |
96518518 PM |
286 | default: |
287 | WARN_ON(1); | |
288 | goto err; | |
289 | } | |
290 | return; | |
291 | ||
292 | err: | |
a55e22e9 | 293 | regs->verdict.code = NFT_BREAK; |
96518518 | 294 | } |
30e103fe | 295 | EXPORT_SYMBOL_GPL(nft_meta_get_eval); |
96518518 | 296 | |
30e103fe | 297 | void nft_meta_set_eval(const struct nft_expr *expr, |
298 | struct nft_regs *regs, | |
299 | const struct nft_pktinfo *pkt) | |
e035b77a ABG |
300 | { |
301 | const struct nft_meta *meta = nft_expr_priv(expr); | |
302 | struct sk_buff *skb = pkt->skb; | |
10596608 LZ |
303 | u32 *sreg = ®s->data[meta->sreg]; |
304 | u32 value = *sreg; | |
97a0549b | 305 | u8 value8; |
e035b77a ABG |
306 | |
307 | switch (meta->key) { | |
308 | case NFT_META_MARK: | |
309 | skb->mark = value; | |
310 | break; | |
311 | case NFT_META_PRIORITY: | |
312 | skb->priority = value; | |
313 | break; | |
b4aae759 | 314 | case NFT_META_PKTTYPE: |
97a0549b | 315 | value8 = nft_reg_load8(sreg); |
10596608 | 316 | |
97a0549b TY |
317 | if (skb->pkt_type != value8 && |
318 | skb_pkt_type_ok(value8) && | |
10596608 | 319 | skb_pkt_type_ok(skb->pkt_type)) |
97a0549b | 320 | skb->pkt_type = value8; |
b4aae759 | 321 | break; |
e035b77a | 322 | case NFT_META_NFTRACE: |
97a0549b TY |
323 | value8 = nft_reg_load8(sreg); |
324 | ||
325 | skb->nf_trace = !!value8; | |
e035b77a | 326 | break; |
b473a1f5 CG |
327 | #ifdef CONFIG_NETWORK_SECMARK |
328 | case NFT_META_SECMARK: | |
329 | skb->secmark = value; | |
330 | break; | |
331 | #endif | |
e035b77a ABG |
332 | default: |
333 | WARN_ON(1); | |
334 | } | |
335 | } | |
30e103fe | 336 | EXPORT_SYMBOL_GPL(nft_meta_set_eval); |
e035b77a | 337 | |
30e103fe | 338 | const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = { |
96518518 PM |
339 | [NFTA_META_DREG] = { .type = NLA_U32 }, |
340 | [NFTA_META_KEY] = { .type = NLA_U32 }, | |
e035b77a | 341 | [NFTA_META_SREG] = { .type = NLA_U32 }, |
96518518 | 342 | }; |
30e103fe | 343 | EXPORT_SYMBOL_GPL(nft_meta_policy); |
96518518 | 344 | |
30e103fe | 345 | int nft_meta_get_init(const struct nft_ctx *ctx, |
346 | const struct nft_expr *expr, | |
347 | const struct nlattr * const tb[]) | |
96518518 | 348 | { |
d2caa696 | 349 | struct nft_meta *priv = nft_expr_priv(expr); |
45d9bcda | 350 | unsigned int len; |
96518518 | 351 | |
d2caa696 PM |
352 | priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); |
353 | switch (priv->key) { | |
96518518 | 354 | case NFT_META_PROTOCOL: |
45d9bcda PM |
355 | case NFT_META_IIFTYPE: |
356 | case NFT_META_OIFTYPE: | |
357 | len = sizeof(u16); | |
358 | break; | |
124edfa9 | 359 | case NFT_META_NFPROTO: |
4566bf27 | 360 | case NFT_META_L4PROTO: |
45d9bcda | 361 | case NFT_META_LEN: |
96518518 PM |
362 | case NFT_META_PRIORITY: |
363 | case NFT_META_MARK: | |
364 | case NFT_META_IIF: | |
365 | case NFT_META_OIF: | |
96518518 PM |
366 | case NFT_META_SKUID: |
367 | case NFT_META_SKGID: | |
06efbd6d | 368 | #ifdef CONFIG_IP_ROUTE_CLASSID |
96518518 PM |
369 | case NFT_META_RTCLASSID: |
370 | #endif | |
371 | #ifdef CONFIG_NETWORK_SECMARK | |
372 | case NFT_META_SECMARK: | |
373 | #endif | |
e2a093ff | 374 | case NFT_META_PKTTYPE: |
afc5be30 | 375 | case NFT_META_CPU: |
3045d760 AR |
376 | case NFT_META_IIFGROUP: |
377 | case NFT_META_OIFGROUP: | |
e181a543 | 378 | #ifdef CONFIG_CGROUP_NET_CLASSID |
ce674173 | 379 | case NFT_META_CGROUP: |
e181a543 | 380 | #endif |
45d9bcda PM |
381 | len = sizeof(u32); |
382 | break; | |
383 | case NFT_META_IIFNAME: | |
384 | case NFT_META_OIFNAME: | |
0fb4d219 | 385 | case NFT_META_IIFKIND: |
386 | case NFT_META_OIFKIND: | |
45d9bcda | 387 | len = IFNAMSIZ; |
d2caa696 | 388 | break; |
b07edbe1 FW |
389 | case NFT_META_PRANDOM: |
390 | prandom_init_once(&nft_prandom_state); | |
391 | len = sizeof(u32); | |
392 | break; | |
f6931f5f FW |
393 | #ifdef CONFIG_XFRM |
394 | case NFT_META_SECPATH: | |
395 | len = sizeof(u8); | |
396 | break; | |
397 | #endif | |
63d10e12 AJ |
398 | case NFT_META_TIME_NS: |
399 | len = sizeof(u64); | |
400 | break; | |
401 | case NFT_META_TIME_DAY: | |
402 | len = sizeof(u8); | |
403 | break; | |
404 | case NFT_META_TIME_HOUR: | |
405 | len = sizeof(u32); | |
406 | break; | |
96518518 PM |
407 | default: |
408 | return -EOPNOTSUPP; | |
409 | } | |
410 | ||
b1c96ed3 | 411 | priv->dreg = nft_parse_register(tb[NFTA_META_DREG]); |
27e6d201 PM |
412 | return nft_validate_register_store(ctx, priv->dreg, NULL, |
413 | NFT_DATA_VALUE, len); | |
e035b77a | 414 | } |
30e103fe | 415 | EXPORT_SYMBOL_GPL(nft_meta_get_init); |
e035b77a | 416 | |
f6931f5f FW |
417 | static int nft_meta_get_validate(const struct nft_ctx *ctx, |
418 | const struct nft_expr *expr, | |
419 | const struct nft_data **data) | |
420 | { | |
421 | #ifdef CONFIG_XFRM | |
422 | const struct nft_meta *priv = nft_expr_priv(expr); | |
423 | unsigned int hooks; | |
424 | ||
425 | if (priv->key != NFT_META_SECPATH) | |
426 | return 0; | |
427 | ||
36596dad | 428 | switch (ctx->family) { |
f6931f5f FW |
429 | case NFPROTO_NETDEV: |
430 | hooks = 1 << NF_NETDEV_INGRESS; | |
431 | break; | |
432 | case NFPROTO_IPV4: | |
433 | case NFPROTO_IPV6: | |
434 | case NFPROTO_INET: | |
435 | hooks = (1 << NF_INET_PRE_ROUTING) | | |
436 | (1 << NF_INET_LOCAL_IN) | | |
437 | (1 << NF_INET_FORWARD); | |
438 | break; | |
439 | default: | |
440 | return -EOPNOTSUPP; | |
441 | } | |
442 | ||
443 | return nft_chain_validate_hooks(ctx->chain, hooks); | |
444 | #else | |
445 | return 0; | |
446 | #endif | |
447 | } | |
448 | ||
30e103fe | 449 | int nft_meta_set_validate(const struct nft_ctx *ctx, |
450 | const struct nft_expr *expr, | |
451 | const struct nft_data **data) | |
b4aae759 | 452 | { |
960fa72f | 453 | struct nft_meta *priv = nft_expr_priv(expr); |
b4aae759 FW |
454 | unsigned int hooks; |
455 | ||
960fa72f LZ |
456 | if (priv->key != NFT_META_PKTTYPE) |
457 | return 0; | |
458 | ||
36596dad | 459 | switch (ctx->family) { |
b4aae759 FW |
460 | case NFPROTO_BRIDGE: |
461 | hooks = 1 << NF_BR_PRE_ROUTING; | |
462 | break; | |
463 | case NFPROTO_NETDEV: | |
464 | hooks = 1 << NF_NETDEV_INGRESS; | |
465 | break; | |
96d9f2a7 LZ |
466 | case NFPROTO_IPV4: |
467 | case NFPROTO_IPV6: | |
468 | case NFPROTO_INET: | |
469 | hooks = 1 << NF_INET_PRE_ROUTING; | |
470 | break; | |
b4aae759 FW |
471 | default: |
472 | return -EOPNOTSUPP; | |
473 | } | |
474 | ||
475 | return nft_chain_validate_hooks(ctx->chain, hooks); | |
476 | } | |
30e103fe | 477 | EXPORT_SYMBOL_GPL(nft_meta_set_validate); |
b4aae759 | 478 | |
30e103fe | 479 | int nft_meta_set_init(const struct nft_ctx *ctx, |
480 | const struct nft_expr *expr, | |
481 | const struct nlattr * const tb[]) | |
e035b77a ABG |
482 | { |
483 | struct nft_meta *priv = nft_expr_priv(expr); | |
d07db988 | 484 | unsigned int len; |
e035b77a ABG |
485 | int err; |
486 | ||
487 | priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); | |
d2caa696 PM |
488 | switch (priv->key) { |
489 | case NFT_META_MARK: | |
490 | case NFT_META_PRIORITY: | |
b473a1f5 CG |
491 | #ifdef CONFIG_NETWORK_SECMARK |
492 | case NFT_META_SECMARK: | |
493 | #endif | |
d07db988 PM |
494 | len = sizeof(u32); |
495 | break; | |
d2caa696 | 496 | case NFT_META_NFTRACE: |
d07db988 | 497 | len = sizeof(u8); |
d2caa696 | 498 | break; |
b4aae759 | 499 | case NFT_META_PKTTYPE: |
b4aae759 FW |
500 | len = sizeof(u8); |
501 | break; | |
d2caa696 PM |
502 | default: |
503 | return -EOPNOTSUPP; | |
e035b77a ABG |
504 | } |
505 | ||
b1c96ed3 | 506 | priv->sreg = nft_parse_register(tb[NFTA_META_SREG]); |
d07db988 | 507 | err = nft_validate_register_load(priv->sreg, len); |
b38895c5 PNA |
508 | if (err < 0) |
509 | return err; | |
e035b77a | 510 | |
e639f7ab FW |
511 | if (priv->key == NFT_META_NFTRACE) |
512 | static_branch_inc(&nft_trace_enabled); | |
513 | ||
e035b77a | 514 | return 0; |
96518518 | 515 | } |
30e103fe | 516 | EXPORT_SYMBOL_GPL(nft_meta_set_init); |
96518518 | 517 | |
30e103fe | 518 | int nft_meta_get_dump(struct sk_buff *skb, |
519 | const struct nft_expr *expr) | |
96518518 PM |
520 | { |
521 | const struct nft_meta *priv = nft_expr_priv(expr); | |
522 | ||
e035b77a ABG |
523 | if (nla_put_be32(skb, NFTA_META_KEY, htonl(priv->key))) |
524 | goto nla_put_failure; | |
b1c96ed3 | 525 | if (nft_dump_register(skb, NFTA_META_DREG, priv->dreg)) |
96518518 | 526 | goto nla_put_failure; |
e035b77a ABG |
527 | return 0; |
528 | ||
529 | nla_put_failure: | |
530 | return -1; | |
531 | } | |
30e103fe | 532 | EXPORT_SYMBOL_GPL(nft_meta_get_dump); |
e035b77a | 533 | |
30e103fe | 534 | int nft_meta_set_dump(struct sk_buff *skb, const struct nft_expr *expr) |
e035b77a ABG |
535 | { |
536 | const struct nft_meta *priv = nft_expr_priv(expr); | |
537 | ||
96518518 PM |
538 | if (nla_put_be32(skb, NFTA_META_KEY, htonl(priv->key))) |
539 | goto nla_put_failure; | |
b1c96ed3 | 540 | if (nft_dump_register(skb, NFTA_META_SREG, priv->sreg)) |
e035b77a ABG |
541 | goto nla_put_failure; |
542 | ||
96518518 PM |
543 | return 0; |
544 | ||
545 | nla_put_failure: | |
546 | return -1; | |
547 | } | |
30e103fe | 548 | EXPORT_SYMBOL_GPL(nft_meta_set_dump); |
96518518 | 549 | |
30e103fe | 550 | void nft_meta_set_destroy(const struct nft_ctx *ctx, |
551 | const struct nft_expr *expr) | |
e639f7ab FW |
552 | { |
553 | const struct nft_meta *priv = nft_expr_priv(expr); | |
554 | ||
555 | if (priv->key == NFT_META_NFTRACE) | |
556 | static_branch_dec(&nft_trace_enabled); | |
557 | } | |
30e103fe | 558 | EXPORT_SYMBOL_GPL(nft_meta_set_destroy); |
e639f7ab | 559 | |
c9626a2c PNA |
560 | static int nft_meta_get_offload(struct nft_offload_ctx *ctx, |
561 | struct nft_flow_rule *flow, | |
562 | const struct nft_expr *expr) | |
563 | { | |
564 | const struct nft_meta *priv = nft_expr_priv(expr); | |
565 | struct nft_offload_reg *reg = &ctx->regs[priv->dreg]; | |
566 | ||
567 | switch (priv->key) { | |
568 | case NFT_META_PROTOCOL: | |
569 | NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_BASIC, basic, n_proto, | |
570 | sizeof(__u16), reg); | |
571 | nft_offload_set_dependency(ctx, NFT_OFFLOAD_DEP_NETWORK); | |
572 | break; | |
573 | case NFT_META_L4PROTO: | |
574 | NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_BASIC, basic, ip_proto, | |
575 | sizeof(__u8), reg); | |
576 | nft_offload_set_dependency(ctx, NFT_OFFLOAD_DEP_TRANSPORT); | |
577 | break; | |
25da5eb3 PNA |
578 | case NFT_META_IIF: |
579 | NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_META, meta, | |
580 | ingress_ifindex, sizeof(__u32), reg); | |
581 | break; | |
8819efc9 PNA |
582 | case NFT_META_IIFTYPE: |
583 | NFT_OFFLOAD_MATCH(FLOW_DISSECTOR_KEY_META, meta, | |
584 | ingress_iftype, sizeof(__u16), reg); | |
585 | break; | |
c9626a2c PNA |
586 | default: |
587 | return -EOPNOTSUPP; | |
588 | } | |
589 | ||
590 | return 0; | |
591 | } | |
592 | ||
e035b77a | 593 | static const struct nft_expr_ops nft_meta_get_ops = { |
ef1f7df9 | 594 | .type = &nft_meta_type, |
96518518 | 595 | .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), |
e035b77a | 596 | .eval = nft_meta_get_eval, |
d2caa696 | 597 | .init = nft_meta_get_init, |
e035b77a | 598 | .dump = nft_meta_get_dump, |
f6931f5f | 599 | .validate = nft_meta_get_validate, |
c9626a2c | 600 | .offload = nft_meta_get_offload, |
ef1f7df9 PM |
601 | }; |
602 | ||
e035b77a ABG |
603 | static const struct nft_expr_ops nft_meta_set_ops = { |
604 | .type = &nft_meta_type, | |
605 | .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), | |
606 | .eval = nft_meta_set_eval, | |
d2caa696 | 607 | .init = nft_meta_set_init, |
e639f7ab | 608 | .destroy = nft_meta_set_destroy, |
e035b77a | 609 | .dump = nft_meta_set_dump, |
960fa72f | 610 | .validate = nft_meta_set_validate, |
e035b77a ABG |
611 | }; |
612 | ||
613 | static const struct nft_expr_ops * | |
614 | nft_meta_select_ops(const struct nft_ctx *ctx, | |
615 | const struct nlattr * const tb[]) | |
616 | { | |
617 | if (tb[NFTA_META_KEY] == NULL) | |
618 | return ERR_PTR(-EINVAL); | |
619 | ||
620 | if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG]) | |
621 | return ERR_PTR(-EINVAL); | |
622 | ||
dfee0e99 | 623 | #if IS_ENABLED(CONFIG_NF_TABLES_BRIDGE) && IS_MODULE(CONFIG_NFT_BRIDGE_META) |
0ef1efd1 PNA |
624 | if (ctx->family == NFPROTO_BRIDGE) |
625 | return ERR_PTR(-EAGAIN); | |
626 | #endif | |
e035b77a ABG |
627 | if (tb[NFTA_META_DREG]) |
628 | return &nft_meta_get_ops; | |
629 | ||
630 | if (tb[NFTA_META_SREG]) | |
631 | return &nft_meta_set_ops; | |
632 | ||
633 | return ERR_PTR(-EINVAL); | |
634 | } | |
635 | ||
8a22543c | 636 | struct nft_expr_type nft_meta_type __read_mostly = { |
ef1f7df9 | 637 | .name = "meta", |
d4ef3835 | 638 | .select_ops = nft_meta_select_ops, |
96518518 PM |
639 | .policy = nft_meta_policy, |
640 | .maxattr = NFTA_META_MAX, | |
ef1f7df9 | 641 | .owner = THIS_MODULE, |
96518518 | 642 | }; |
fb961945 CG |
643 | |
644 | #ifdef CONFIG_NETWORK_SECMARK | |
645 | struct nft_secmark { | |
646 | u32 secid; | |
647 | char *ctx; | |
648 | }; | |
649 | ||
650 | static const struct nla_policy nft_secmark_policy[NFTA_SECMARK_MAX + 1] = { | |
651 | [NFTA_SECMARK_CTX] = { .type = NLA_STRING, .len = NFT_SECMARK_CTX_MAXLEN }, | |
652 | }; | |
653 | ||
654 | static int nft_secmark_compute_secid(struct nft_secmark *priv) | |
655 | { | |
656 | u32 tmp_secid = 0; | |
657 | int err; | |
658 | ||
659 | err = security_secctx_to_secid(priv->ctx, strlen(priv->ctx), &tmp_secid); | |
660 | if (err) | |
661 | return err; | |
662 | ||
663 | if (!tmp_secid) | |
664 | return -ENOENT; | |
665 | ||
666 | err = security_secmark_relabel_packet(tmp_secid); | |
667 | if (err) | |
668 | return err; | |
669 | ||
670 | priv->secid = tmp_secid; | |
671 | return 0; | |
672 | } | |
673 | ||
674 | static void nft_secmark_obj_eval(struct nft_object *obj, struct nft_regs *regs, | |
675 | const struct nft_pktinfo *pkt) | |
676 | { | |
677 | const struct nft_secmark *priv = nft_obj_data(obj); | |
678 | struct sk_buff *skb = pkt->skb; | |
679 | ||
680 | skb->secmark = priv->secid; | |
681 | } | |
682 | ||
683 | static int nft_secmark_obj_init(const struct nft_ctx *ctx, | |
684 | const struct nlattr * const tb[], | |
685 | struct nft_object *obj) | |
686 | { | |
687 | struct nft_secmark *priv = nft_obj_data(obj); | |
688 | int err; | |
689 | ||
690 | if (tb[NFTA_SECMARK_CTX] == NULL) | |
691 | return -EINVAL; | |
692 | ||
693 | priv->ctx = nla_strdup(tb[NFTA_SECMARK_CTX], GFP_KERNEL); | |
694 | if (!priv->ctx) | |
695 | return -ENOMEM; | |
696 | ||
697 | err = nft_secmark_compute_secid(priv); | |
698 | if (err) { | |
699 | kfree(priv->ctx); | |
700 | return err; | |
701 | } | |
702 | ||
703 | security_secmark_refcount_inc(); | |
704 | ||
705 | return 0; | |
706 | } | |
707 | ||
708 | static int nft_secmark_obj_dump(struct sk_buff *skb, struct nft_object *obj, | |
709 | bool reset) | |
710 | { | |
711 | struct nft_secmark *priv = nft_obj_data(obj); | |
712 | int err; | |
713 | ||
714 | if (nla_put_string(skb, NFTA_SECMARK_CTX, priv->ctx)) | |
715 | return -1; | |
716 | ||
717 | if (reset) { | |
718 | err = nft_secmark_compute_secid(priv); | |
719 | if (err) | |
720 | return err; | |
721 | } | |
722 | ||
723 | return 0; | |
724 | } | |
725 | ||
726 | static void nft_secmark_obj_destroy(const struct nft_ctx *ctx, struct nft_object *obj) | |
727 | { | |
728 | struct nft_secmark *priv = nft_obj_data(obj); | |
729 | ||
730 | security_secmark_refcount_dec(); | |
731 | ||
732 | kfree(priv->ctx); | |
733 | } | |
734 | ||
735 | static const struct nft_object_ops nft_secmark_obj_ops = { | |
736 | .type = &nft_secmark_obj_type, | |
737 | .size = sizeof(struct nft_secmark), | |
738 | .init = nft_secmark_obj_init, | |
739 | .eval = nft_secmark_obj_eval, | |
740 | .dump = nft_secmark_obj_dump, | |
741 | .destroy = nft_secmark_obj_destroy, | |
742 | }; | |
743 | struct nft_object_type nft_secmark_obj_type __read_mostly = { | |
744 | .type = NFT_OBJECT_SECMARK, | |
745 | .ops = &nft_secmark_obj_ops, | |
746 | .maxattr = NFTA_SECMARK_MAX, | |
747 | .policy = nft_secmark_policy, | |
748 | .owner = THIS_MODULE, | |
749 | }; | |
750 | #endif /* CONFIG_NETWORK_SECMARK */ |