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> | |
b1fd94e7 | 17 | #include <linux/random.h> |
afc5be30 | 18 | #include <linux/smp.h> |
e639f7ab | 19 | #include <linux/static_key.h> |
96518518 | 20 | #include <net/dst.h> |
c14ceb0e | 21 | #include <net/ip.h> |
96518518 PM |
22 | #include <net/sock.h> |
23 | #include <net/tcp_states.h> /* for TCP_TIME_WAIT */ | |
24 | #include <net/netfilter/nf_tables.h> | |
e639f7ab | 25 | #include <net/netfilter/nf_tables_core.h> |
30e103fe | 26 | #include <net/netfilter/nft_meta.h> |
c9626a2c | 27 | #include <net/netfilter/nf_tables_offload.h> |
96518518 | 28 | |
b4aae759 FW |
29 | #include <uapi/linux/netfilter_bridge.h> /* NF_BR_PRE_ROUTING */ |
30 | ||
63d10e12 AJ |
31 | #define NFT_META_SECS_PER_MINUTE 60 |
32 | #define NFT_META_SECS_PER_HOUR 3600 | |
33 | #define NFT_META_SECS_PER_DAY 86400 | |
34 | #define NFT_META_DAYS_PER_WEEK 7 | |
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: | |
c301f098 | 66 | nft_reg_store64((u64 *)dest, ktime_get_real_ns()); |
db8f6f5c FW |
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 | ||
726b44f0 FW |
129 | static noinline bool |
130 | nft_meta_get_eval_skugid(enum nft_meta_keys key, | |
131 | u32 *dest, | |
132 | const struct nft_pktinfo *pkt) | |
133 | { | |
134 | struct sock *sk = skb_to_full_sk(pkt->skb); | |
135 | struct socket *sock; | |
136 | ||
137 | if (!sk || !sk_fullsock(sk) || !net_eq(nft_net(pkt), sock_net(sk))) | |
138 | return false; | |
139 | ||
140 | read_lock_bh(&sk->sk_callback_lock); | |
141 | sock = sk->sk_socket; | |
142 | if (!sock || !sock->file) { | |
143 | read_unlock_bh(&sk->sk_callback_lock); | |
144 | return false; | |
145 | } | |
146 | ||
147 | switch (key) { | |
148 | case NFT_META_SKUID: | |
0c92411b | 149 | *dest = from_kuid_munged(sock_net(sk)->user_ns, |
726b44f0 FW |
150 | sock->file->f_cred->fsuid); |
151 | break; | |
152 | case NFT_META_SKGID: | |
0c92411b | 153 | *dest = from_kgid_munged(sock_net(sk)->user_ns, |
726b44f0 FW |
154 | sock->file->f_cred->fsgid); |
155 | break; | |
156 | default: | |
157 | break; | |
158 | } | |
159 | ||
160 | read_unlock_bh(&sk->sk_callback_lock); | |
161 | return true; | |
162 | } | |
163 | ||
b1327fbc FW |
164 | #ifdef CONFIG_CGROUP_NET_CLASSID |
165 | static noinline bool | |
166 | nft_meta_get_eval_cgroup(u32 *dest, const struct nft_pktinfo *pkt) | |
167 | { | |
168 | struct sock *sk = skb_to_full_sk(pkt->skb); | |
169 | ||
170 | if (!sk || !sk_fullsock(sk) || !net_eq(nft_net(pkt), sock_net(sk))) | |
171 | return false; | |
172 | ||
173 | *dest = sock_cgroup_classid(&sk->sk_cgrp_data); | |
174 | return true; | |
175 | } | |
176 | #endif | |
177 | ||
a4150a1f FW |
178 | static noinline bool nft_meta_get_eval_kind(enum nft_meta_keys key, |
179 | u32 *dest, | |
180 | const struct nft_pktinfo *pkt) | |
181 | { | |
182 | const struct net_device *in = nft_in(pkt), *out = nft_out(pkt); | |
183 | ||
184 | switch (key) { | |
185 | case NFT_META_IIFKIND: | |
186 | if (!in || !in->rtnl_link_ops) | |
187 | return false; | |
ad156c23 | 188 | strscpy_pad((char *)dest, in->rtnl_link_ops->kind, IFNAMSIZ); |
a4150a1f FW |
189 | break; |
190 | case NFT_META_OIFKIND: | |
191 | if (!out || !out->rtnl_link_ops) | |
192 | return false; | |
ad156c23 | 193 | strscpy_pad((char *)dest, out->rtnl_link_ops->kind, IFNAMSIZ); |
a4150a1f FW |
194 | break; |
195 | default: | |
196 | return false; | |
197 | } | |
198 | ||
199 | return true; | |
200 | } | |
201 | ||
8724e819 FW |
202 | static void nft_meta_store_ifindex(u32 *dest, const struct net_device *dev) |
203 | { | |
204 | *dest = dev ? dev->ifindex : 0; | |
205 | } | |
206 | ||
207 | static void nft_meta_store_ifname(u32 *dest, const struct net_device *dev) | |
208 | { | |
ad156c23 | 209 | strscpy_pad((char *)dest, dev ? dev->name : "", IFNAMSIZ); |
8724e819 FW |
210 | } |
211 | ||
212 | static bool nft_meta_store_iftype(u32 *dest, const struct net_device *dev) | |
213 | { | |
214 | if (!dev) | |
215 | return false; | |
216 | ||
217 | nft_reg_store16(dest, dev->type); | |
218 | return true; | |
219 | } | |
220 | ||
221 | static bool nft_meta_store_ifgroup(u32 *dest, const struct net_device *dev) | |
222 | { | |
223 | if (!dev) | |
224 | return false; | |
225 | ||
226 | *dest = dev->group; | |
227 | return true; | |
228 | } | |
229 | ||
230 | static bool nft_meta_get_eval_ifname(enum nft_meta_keys key, u32 *dest, | |
231 | const struct nft_pktinfo *pkt) | |
232 | { | |
233 | switch (key) { | |
234 | case NFT_META_IIFNAME: | |
235 | nft_meta_store_ifname(dest, nft_in(pkt)); | |
236 | break; | |
237 | case NFT_META_OIFNAME: | |
238 | nft_meta_store_ifname(dest, nft_out(pkt)); | |
239 | break; | |
240 | case NFT_META_IIF: | |
241 | nft_meta_store_ifindex(dest, nft_in(pkt)); | |
242 | break; | |
243 | case NFT_META_OIF: | |
244 | nft_meta_store_ifindex(dest, nft_out(pkt)); | |
245 | break; | |
56fa9501 PNA |
246 | case NFT_META_IFTYPE: |
247 | if (!nft_meta_store_iftype(dest, pkt->skb->dev)) | |
248 | return false; | |
249 | break; | |
250 | case __NFT_META_IIFTYPE: | |
8724e819 FW |
251 | if (!nft_meta_store_iftype(dest, nft_in(pkt))) |
252 | return false; | |
253 | break; | |
254 | case NFT_META_OIFTYPE: | |
255 | if (!nft_meta_store_iftype(dest, nft_out(pkt))) | |
256 | return false; | |
257 | break; | |
258 | case NFT_META_IIFGROUP: | |
78470d9d | 259 | if (!nft_meta_store_ifgroup(dest, nft_in(pkt))) |
8724e819 FW |
260 | return false; |
261 | break; | |
262 | case NFT_META_OIFGROUP: | |
263 | if (!nft_meta_store_ifgroup(dest, nft_out(pkt))) | |
264 | return false; | |
265 | break; | |
266 | default: | |
267 | return false; | |
268 | } | |
269 | ||
270 | return true; | |
271 | } | |
272 | ||
01a0fc82 FW |
273 | #ifdef CONFIG_IP_ROUTE_CLASSID |
274 | static noinline bool | |
275 | nft_meta_get_eval_rtclassid(const struct sk_buff *skb, u32 *dest) | |
276 | { | |
277 | const struct dst_entry *dst = skb_dst(skb); | |
278 | ||
279 | if (!dst) | |
280 | return false; | |
281 | ||
282 | *dest = dst->tclassid; | |
283 | return true; | |
284 | } | |
285 | #endif | |
286 | ||
c14ceb0e FW |
287 | static noinline u32 nft_meta_get_eval_sdif(const struct nft_pktinfo *pkt) |
288 | { | |
289 | switch (nft_pf(pkt)) { | |
290 | case NFPROTO_IPV4: | |
291 | return inet_sdif(pkt->skb); | |
292 | case NFPROTO_IPV6: | |
293 | return inet6_sdif(pkt->skb); | |
294 | } | |
295 | ||
296 | return 0; | |
297 | } | |
298 | ||
299 | static noinline void | |
300 | nft_meta_get_eval_sdifname(u32 *dest, const struct nft_pktinfo *pkt) | |
301 | { | |
302 | u32 sdif = nft_meta_get_eval_sdif(pkt); | |
303 | const struct net_device *dev; | |
304 | ||
305 | dev = sdif ? dev_get_by_index_rcu(nft_net(pkt), sdif) : NULL; | |
306 | nft_meta_store_ifname(dest, dev); | |
307 | } | |
308 | ||
222440b4 FW |
309 | void nft_meta_get_eval(const struct nft_expr *expr, |
310 | struct nft_regs *regs, | |
311 | const struct nft_pktinfo *pkt) | |
96518518 PM |
312 | { |
313 | const struct nft_meta *priv = nft_expr_priv(expr); | |
314 | const struct sk_buff *skb = pkt->skb; | |
49499c3e | 315 | u32 *dest = ®s->data[priv->dreg]; |
96518518 PM |
316 | |
317 | switch (priv->key) { | |
318 | case NFT_META_LEN: | |
fad136ea | 319 | *dest = skb->len; |
96518518 PM |
320 | break; |
321 | case NFT_META_PROTOCOL: | |
10596608 | 322 | nft_reg_store16(dest, (__force u16)skb->protocol); |
96518518 | 323 | break; |
124edfa9 | 324 | case NFT_META_NFPROTO: |
10596608 | 325 | nft_reg_store8(dest, nft_pf(pkt)); |
124edfa9 | 326 | break; |
4566bf27 | 327 | case NFT_META_L4PROTO: |
b5bdc6f9 | 328 | if (!(pkt->flags & NFT_PKTINFO_L4PROTO)) |
beac5afa | 329 | goto err; |
10596608 | 330 | nft_reg_store8(dest, pkt->tprot); |
4566bf27 | 331 | break; |
96518518 | 332 | case NFT_META_PRIORITY: |
fad136ea | 333 | *dest = skb->priority; |
96518518 PM |
334 | break; |
335 | case NFT_META_MARK: | |
fad136ea | 336 | *dest = skb->mark; |
96518518 PM |
337 | break; |
338 | case NFT_META_IIF: | |
96518518 | 339 | case NFT_META_OIF: |
96518518 | 340 | case NFT_META_IIFNAME: |
96518518 | 341 | case NFT_META_OIFNAME: |
96518518 | 342 | case NFT_META_IIFTYPE: |
96518518 | 343 | case NFT_META_OIFTYPE: |
8724e819 FW |
344 | case NFT_META_IIFGROUP: |
345 | case NFT_META_OIFGROUP: | |
346 | if (!nft_meta_get_eval_ifname(priv->key, dest, pkt)) | |
96518518 | 347 | goto err; |
96518518 PM |
348 | break; |
349 | case NFT_META_SKUID: | |
96518518 | 350 | case NFT_META_SKGID: |
726b44f0 | 351 | if (!nft_meta_get_eval_skugid(priv->key, dest, pkt)) |
96518518 | 352 | goto err; |
96518518 | 353 | break; |
06efbd6d | 354 | #ifdef CONFIG_IP_ROUTE_CLASSID |
01a0fc82 FW |
355 | case NFT_META_RTCLASSID: |
356 | if (!nft_meta_get_eval_rtclassid(skb, dest)) | |
96518518 | 357 | goto err; |
96518518 | 358 | break; |
96518518 PM |
359 | #endif |
360 | #ifdef CONFIG_NETWORK_SECMARK | |
361 | case NFT_META_SECMARK: | |
fad136ea | 362 | *dest = skb->secmark; |
96518518 PM |
363 | break; |
364 | #endif | |
e2a093ff AR |
365 | case NFT_META_PKTTYPE: |
366 | if (skb->pkt_type != PACKET_LOOPBACK) { | |
10596608 | 367 | nft_reg_store8(dest, skb->pkt_type); |
e2a093ff AR |
368 | break; |
369 | } | |
370 | ||
4a54594a | 371 | if (!nft_meta_get_eval_pkttype_lo(pkt, dest)) |
e2a093ff | 372 | goto err; |
e2a093ff | 373 | break; |
afc5be30 | 374 | case NFT_META_CPU: |
fad136ea | 375 | *dest = raw_smp_processor_id(); |
afc5be30 | 376 | break; |
e181a543 | 377 | #ifdef CONFIG_CGROUP_NET_CLASSID |
ce674173 | 378 | case NFT_META_CGROUP: |
b1327fbc | 379 | if (!nft_meta_get_eval_cgroup(dest, pkt)) |
c5035c77 | 380 | goto err; |
ce674173 | 381 | break; |
e181a543 | 382 | #endif |
6b2faee0 | 383 | case NFT_META_PRANDOM: |
b1fd94e7 | 384 | *dest = get_random_u32(); |
b07edbe1 | 385 | break; |
f6931f5f FW |
386 | #ifdef CONFIG_XFRM |
387 | case NFT_META_SECPATH: | |
7af8f4ca | 388 | nft_reg_store8(dest, secpath_exists(skb)); |
f6931f5f FW |
389 | break; |
390 | #endif | |
0fb4d219 | 391 | case NFT_META_IIFKIND: |
0fb4d219 | 392 | case NFT_META_OIFKIND: |
a4150a1f | 393 | if (!nft_meta_get_eval_kind(priv->key, dest, pkt)) |
0fb4d219 | 394 | goto err; |
0fb4d219 | 395 | break; |
63d10e12 | 396 | case NFT_META_TIME_NS: |
63d10e12 | 397 | case NFT_META_TIME_DAY: |
63d10e12 | 398 | case NFT_META_TIME_HOUR: |
db8f6f5c | 399 | nft_meta_get_eval_time(priv->key, dest); |
63d10e12 | 400 | break; |
c14ceb0e FW |
401 | case NFT_META_SDIF: |
402 | *dest = nft_meta_get_eval_sdif(pkt); | |
403 | break; | |
404 | case NFT_META_SDIFNAME: | |
405 | nft_meta_get_eval_sdifname(dest, pkt); | |
406 | break; | |
96518518 PM |
407 | default: |
408 | WARN_ON(1); | |
409 | goto err; | |
410 | } | |
411 | return; | |
412 | ||
413 | err: | |
a55e22e9 | 414 | regs->verdict.code = NFT_BREAK; |
96518518 | 415 | } |
30e103fe | 416 | EXPORT_SYMBOL_GPL(nft_meta_get_eval); |
96518518 | 417 | |
30e103fe | 418 | void nft_meta_set_eval(const struct nft_expr *expr, |
419 | struct nft_regs *regs, | |
420 | const struct nft_pktinfo *pkt) | |
e035b77a ABG |
421 | { |
422 | const struct nft_meta *meta = nft_expr_priv(expr); | |
423 | struct sk_buff *skb = pkt->skb; | |
10596608 LZ |
424 | u32 *sreg = ®s->data[meta->sreg]; |
425 | u32 value = *sreg; | |
97a0549b | 426 | u8 value8; |
e035b77a ABG |
427 | |
428 | switch (meta->key) { | |
429 | case NFT_META_MARK: | |
430 | skb->mark = value; | |
431 | break; | |
432 | case NFT_META_PRIORITY: | |
433 | skb->priority = value; | |
434 | break; | |
b4aae759 | 435 | case NFT_META_PKTTYPE: |
97a0549b | 436 | value8 = nft_reg_load8(sreg); |
10596608 | 437 | |
97a0549b TY |
438 | if (skb->pkt_type != value8 && |
439 | skb_pkt_type_ok(value8) && | |
10596608 | 440 | skb_pkt_type_ok(skb->pkt_type)) |
97a0549b | 441 | skb->pkt_type = value8; |
b4aae759 | 442 | break; |
e035b77a | 443 | case NFT_META_NFTRACE: |
97a0549b TY |
444 | value8 = nft_reg_load8(sreg); |
445 | ||
446 | skb->nf_trace = !!value8; | |
e035b77a | 447 | break; |
b473a1f5 CG |
448 | #ifdef CONFIG_NETWORK_SECMARK |
449 | case NFT_META_SECMARK: | |
450 | skb->secmark = value; | |
451 | break; | |
452 | #endif | |
e035b77a ABG |
453 | default: |
454 | WARN_ON(1); | |
455 | } | |
456 | } | |
30e103fe | 457 | EXPORT_SYMBOL_GPL(nft_meta_set_eval); |
e035b77a | 458 | |
30e103fe | 459 | const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = { |
96518518 | 460 | [NFTA_META_DREG] = { .type = NLA_U32 }, |
a412dbf4 | 461 | [NFTA_META_KEY] = NLA_POLICY_MAX(NLA_BE32, 255), |
e035b77a | 462 | [NFTA_META_SREG] = { .type = NLA_U32 }, |
96518518 | 463 | }; |
30e103fe | 464 | EXPORT_SYMBOL_GPL(nft_meta_policy); |
96518518 | 465 | |
30e103fe | 466 | int nft_meta_get_init(const struct nft_ctx *ctx, |
467 | const struct nft_expr *expr, | |
468 | const struct nlattr * const tb[]) | |
96518518 | 469 | { |
d2caa696 | 470 | struct nft_meta *priv = nft_expr_priv(expr); |
45d9bcda | 471 | unsigned int len; |
96518518 | 472 | |
d2caa696 PM |
473 | priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); |
474 | switch (priv->key) { | |
96518518 | 475 | case NFT_META_PROTOCOL: |
45d9bcda PM |
476 | case NFT_META_IIFTYPE: |
477 | case NFT_META_OIFTYPE: | |
478 | len = sizeof(u16); | |
479 | break; | |
124edfa9 | 480 | case NFT_META_NFPROTO: |
4566bf27 | 481 | case NFT_META_L4PROTO: |
45d9bcda | 482 | case NFT_META_LEN: |
96518518 PM |
483 | case NFT_META_PRIORITY: |
484 | case NFT_META_MARK: | |
485 | case NFT_META_IIF: | |
486 | case NFT_META_OIF: | |
c14ceb0e | 487 | case NFT_META_SDIF: |
96518518 PM |
488 | case NFT_META_SKUID: |
489 | case NFT_META_SKGID: | |
06efbd6d | 490 | #ifdef CONFIG_IP_ROUTE_CLASSID |
96518518 PM |
491 | case NFT_META_RTCLASSID: |
492 | #endif | |
493 | #ifdef CONFIG_NETWORK_SECMARK | |
494 | case NFT_META_SECMARK: | |
495 | #endif | |
e2a093ff | 496 | case NFT_META_PKTTYPE: |
afc5be30 | 497 | case NFT_META_CPU: |
3045d760 AR |
498 | case NFT_META_IIFGROUP: |
499 | case NFT_META_OIFGROUP: | |
e181a543 | 500 | #ifdef CONFIG_CGROUP_NET_CLASSID |
ce674173 | 501 | case NFT_META_CGROUP: |
e181a543 | 502 | #endif |
45d9bcda PM |
503 | len = sizeof(u32); |
504 | break; | |
505 | case NFT_META_IIFNAME: | |
506 | case NFT_META_OIFNAME: | |
0fb4d219 | 507 | case NFT_META_IIFKIND: |
508 | case NFT_META_OIFKIND: | |
c14ceb0e | 509 | case NFT_META_SDIFNAME: |
45d9bcda | 510 | len = IFNAMSIZ; |
d2caa696 | 511 | break; |
b07edbe1 | 512 | case NFT_META_PRANDOM: |
b07edbe1 FW |
513 | len = sizeof(u32); |
514 | break; | |
f6931f5f FW |
515 | #ifdef CONFIG_XFRM |
516 | case NFT_META_SECPATH: | |
517 | len = sizeof(u8); | |
518 | break; | |
519 | #endif | |
63d10e12 AJ |
520 | case NFT_META_TIME_NS: |
521 | len = sizeof(u64); | |
522 | break; | |
523 | case NFT_META_TIME_DAY: | |
524 | len = sizeof(u8); | |
525 | break; | |
526 | case NFT_META_TIME_HOUR: | |
527 | len = sizeof(u32); | |
528 | break; | |
96518518 PM |
529 | default: |
530 | return -EOPNOTSUPP; | |
531 | } | |
532 | ||
34cc9e52 | 533 | priv->len = len; |
345023b0 PNA |
534 | return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg, |
535 | NULL, NFT_DATA_VALUE, len); | |
e035b77a | 536 | } |
30e103fe | 537 | EXPORT_SYMBOL_GPL(nft_meta_get_init); |
e035b77a | 538 | |
c14ceb0e | 539 | static int nft_meta_get_validate_sdif(const struct nft_ctx *ctx) |
f6931f5f | 540 | { |
f6931f5f FW |
541 | unsigned int hooks; |
542 | ||
c14ceb0e FW |
543 | switch (ctx->family) { |
544 | case NFPROTO_IPV4: | |
545 | case NFPROTO_IPV6: | |
546 | case NFPROTO_INET: | |
547 | hooks = (1 << NF_INET_LOCAL_IN) | | |
548 | (1 << NF_INET_FORWARD); | |
549 | break; | |
550 | default: | |
551 | return -EOPNOTSUPP; | |
552 | } | |
553 | ||
554 | return nft_chain_validate_hooks(ctx->chain, hooks); | |
555 | } | |
556 | ||
557 | static int nft_meta_get_validate_xfrm(const struct nft_ctx *ctx) | |
558 | { | |
559 | #ifdef CONFIG_XFRM | |
560 | unsigned int hooks; | |
f6931f5f | 561 | |
36596dad | 562 | switch (ctx->family) { |
f6931f5f FW |
563 | case NFPROTO_NETDEV: |
564 | hooks = 1 << NF_NETDEV_INGRESS; | |
565 | break; | |
566 | case NFPROTO_IPV4: | |
567 | case NFPROTO_IPV6: | |
568 | case NFPROTO_INET: | |
569 | hooks = (1 << NF_INET_PRE_ROUTING) | | |
570 | (1 << NF_INET_LOCAL_IN) | | |
571 | (1 << NF_INET_FORWARD); | |
572 | break; | |
573 | default: | |
574 | return -EOPNOTSUPP; | |
575 | } | |
576 | ||
577 | return nft_chain_validate_hooks(ctx->chain, hooks); | |
578 | #else | |
579 | return 0; | |
580 | #endif | |
581 | } | |
582 | ||
c14ceb0e | 583 | static int nft_meta_get_validate(const struct nft_ctx *ctx, |
eaf9b2c8 | 584 | const struct nft_expr *expr) |
c14ceb0e FW |
585 | { |
586 | const struct nft_meta *priv = nft_expr_priv(expr); | |
587 | ||
588 | switch (priv->key) { | |
589 | case NFT_META_SECPATH: | |
590 | return nft_meta_get_validate_xfrm(ctx); | |
591 | case NFT_META_SDIF: | |
592 | case NFT_META_SDIFNAME: | |
593 | return nft_meta_get_validate_sdif(ctx); | |
594 | default: | |
595 | break; | |
596 | } | |
597 | ||
598 | return 0; | |
599 | } | |
600 | ||
30e103fe | 601 | int nft_meta_set_validate(const struct nft_ctx *ctx, |
eaf9b2c8 | 602 | const struct nft_expr *expr) |
b4aae759 | 603 | { |
960fa72f | 604 | struct nft_meta *priv = nft_expr_priv(expr); |
b4aae759 FW |
605 | unsigned int hooks; |
606 | ||
960fa72f LZ |
607 | if (priv->key != NFT_META_PKTTYPE) |
608 | return 0; | |
609 | ||
36596dad | 610 | switch (ctx->family) { |
b4aae759 FW |
611 | case NFPROTO_BRIDGE: |
612 | hooks = 1 << NF_BR_PRE_ROUTING; | |
613 | break; | |
614 | case NFPROTO_NETDEV: | |
615 | hooks = 1 << NF_NETDEV_INGRESS; | |
616 | break; | |
96d9f2a7 LZ |
617 | case NFPROTO_IPV4: |
618 | case NFPROTO_IPV6: | |
619 | case NFPROTO_INET: | |
620 | hooks = 1 << NF_INET_PRE_ROUTING; | |
621 | break; | |
b4aae759 FW |
622 | default: |
623 | return -EOPNOTSUPP; | |
624 | } | |
625 | ||
626 | return nft_chain_validate_hooks(ctx->chain, hooks); | |
627 | } | |
30e103fe | 628 | EXPORT_SYMBOL_GPL(nft_meta_set_validate); |
b4aae759 | 629 | |
30e103fe | 630 | int nft_meta_set_init(const struct nft_ctx *ctx, |
631 | const struct nft_expr *expr, | |
632 | const struct nlattr * const tb[]) | |
e035b77a ABG |
633 | { |
634 | struct nft_meta *priv = nft_expr_priv(expr); | |
d07db988 | 635 | unsigned int len; |
e035b77a ABG |
636 | int err; |
637 | ||
638 | priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); | |
d2caa696 PM |
639 | switch (priv->key) { |
640 | case NFT_META_MARK: | |
641 | case NFT_META_PRIORITY: | |
b473a1f5 CG |
642 | #ifdef CONFIG_NETWORK_SECMARK |
643 | case NFT_META_SECMARK: | |
644 | #endif | |
d07db988 PM |
645 | len = sizeof(u32); |
646 | break; | |
d2caa696 | 647 | case NFT_META_NFTRACE: |
d07db988 | 648 | len = sizeof(u8); |
d2caa696 | 649 | break; |
b4aae759 | 650 | case NFT_META_PKTTYPE: |
b4aae759 FW |
651 | len = sizeof(u8); |
652 | break; | |
d2caa696 PM |
653 | default: |
654 | return -EOPNOTSUPP; | |
e035b77a ABG |
655 | } |
656 | ||
34cc9e52 | 657 | priv->len = len; |
7ea0522e | 658 | err = nft_parse_register_load(ctx, tb[NFTA_META_SREG], &priv->sreg, len); |
b38895c5 PNA |
659 | if (err < 0) |
660 | return err; | |
e035b77a | 661 | |
e639f7ab FW |
662 | if (priv->key == NFT_META_NFTRACE) |
663 | static_branch_inc(&nft_trace_enabled); | |
664 | ||
e035b77a | 665 | return 0; |
96518518 | 666 | } |
30e103fe | 667 | EXPORT_SYMBOL_GPL(nft_meta_set_init); |
96518518 | 668 | |
30e103fe | 669 | int nft_meta_get_dump(struct sk_buff *skb, |
7d34aa3e | 670 | const struct nft_expr *expr, bool reset) |
96518518 PM |
671 | { |
672 | const struct nft_meta *priv = nft_expr_priv(expr); | |
673 | ||
e035b77a ABG |
674 | if (nla_put_be32(skb, NFTA_META_KEY, htonl(priv->key))) |
675 | goto nla_put_failure; | |
b1c96ed3 | 676 | if (nft_dump_register(skb, NFTA_META_DREG, priv->dreg)) |
96518518 | 677 | goto nla_put_failure; |
e035b77a ABG |
678 | return 0; |
679 | ||
680 | nla_put_failure: | |
681 | return -1; | |
682 | } | |
30e103fe | 683 | EXPORT_SYMBOL_GPL(nft_meta_get_dump); |
e035b77a | 684 | |
7d34aa3e PS |
685 | int nft_meta_set_dump(struct sk_buff *skb, |
686 | const struct nft_expr *expr, bool reset) | |
e035b77a ABG |
687 | { |
688 | const struct nft_meta *priv = nft_expr_priv(expr); | |
689 | ||
96518518 PM |
690 | if (nla_put_be32(skb, NFTA_META_KEY, htonl(priv->key))) |
691 | goto nla_put_failure; | |
b1c96ed3 | 692 | if (nft_dump_register(skb, NFTA_META_SREG, priv->sreg)) |
e035b77a ABG |
693 | goto nla_put_failure; |
694 | ||
96518518 PM |
695 | return 0; |
696 | ||
697 | nla_put_failure: | |
698 | return -1; | |
699 | } | |
30e103fe | 700 | EXPORT_SYMBOL_GPL(nft_meta_set_dump); |
96518518 | 701 | |
30e103fe | 702 | void nft_meta_set_destroy(const struct nft_ctx *ctx, |
703 | const struct nft_expr *expr) | |
e639f7ab FW |
704 | { |
705 | const struct nft_meta *priv = nft_expr_priv(expr); | |
706 | ||
707 | if (priv->key == NFT_META_NFTRACE) | |
708 | static_branch_dec(&nft_trace_enabled); | |
709 | } | |
30e103fe | 710 | EXPORT_SYMBOL_GPL(nft_meta_set_destroy); |
e639f7ab | 711 | |
c9626a2c PNA |
712 | static int nft_meta_get_offload(struct nft_offload_ctx *ctx, |
713 | struct nft_flow_rule *flow, | |
714 | const struct nft_expr *expr) | |
715 | { | |
716 | const struct nft_meta *priv = nft_expr_priv(expr); | |
717 | struct nft_offload_reg *reg = &ctx->regs[priv->dreg]; | |
718 | ||
719 | switch (priv->key) { | |
720 | case NFT_META_PROTOCOL: | |
a5d45bc0 PNA |
721 | NFT_OFFLOAD_MATCH_EXACT(FLOW_DISSECTOR_KEY_BASIC, basic, n_proto, |
722 | sizeof(__u16), reg); | |
c9626a2c PNA |
723 | nft_offload_set_dependency(ctx, NFT_OFFLOAD_DEP_NETWORK); |
724 | break; | |
725 | case NFT_META_L4PROTO: | |
a5d45bc0 PNA |
726 | NFT_OFFLOAD_MATCH_EXACT(FLOW_DISSECTOR_KEY_BASIC, basic, ip_proto, |
727 | sizeof(__u8), reg); | |
c9626a2c PNA |
728 | nft_offload_set_dependency(ctx, NFT_OFFLOAD_DEP_TRANSPORT); |
729 | break; | |
25da5eb3 | 730 | case NFT_META_IIF: |
a5d45bc0 PNA |
731 | NFT_OFFLOAD_MATCH_EXACT(FLOW_DISSECTOR_KEY_META, meta, |
732 | ingress_ifindex, sizeof(__u32), reg); | |
25da5eb3 | 733 | break; |
8819efc9 | 734 | case NFT_META_IIFTYPE: |
a5d45bc0 PNA |
735 | NFT_OFFLOAD_MATCH_EXACT(FLOW_DISSECTOR_KEY_META, meta, |
736 | ingress_iftype, sizeof(__u16), reg); | |
8819efc9 | 737 | break; |
c9626a2c PNA |
738 | default: |
739 | return -EOPNOTSUPP; | |
740 | } | |
741 | ||
742 | return 0; | |
743 | } | |
744 | ||
aaa7b20b FW |
745 | bool nft_meta_get_reduce(struct nft_regs_track *track, |
746 | const struct nft_expr *expr) | |
9b17afb2 PNA |
747 | { |
748 | const struct nft_meta *priv = nft_expr_priv(expr); | |
749 | const struct nft_meta *meta; | |
750 | ||
34cc9e52 PNA |
751 | if (!nft_reg_track_cmp(track, expr, priv->dreg)) { |
752 | nft_reg_track_update(track, expr, priv->dreg, priv->len); | |
9b17afb2 PNA |
753 | return false; |
754 | } | |
755 | ||
756 | meta = nft_expr_priv(track->regs[priv->dreg].selector); | |
757 | if (priv->key != meta->key || | |
758 | priv->dreg != meta->dreg) { | |
34cc9e52 | 759 | nft_reg_track_update(track, expr, priv->dreg, priv->len); |
9b17afb2 PNA |
760 | return false; |
761 | } | |
762 | ||
763 | if (!track->regs[priv->dreg].bitwise) | |
764 | return true; | |
765 | ||
be5650f8 | 766 | return nft_expr_reduce_bitwise(track, expr); |
9b17afb2 | 767 | } |
aaa7b20b | 768 | EXPORT_SYMBOL_GPL(nft_meta_get_reduce); |
9b17afb2 | 769 | |
e035b77a | 770 | static const struct nft_expr_ops nft_meta_get_ops = { |
ef1f7df9 | 771 | .type = &nft_meta_type, |
96518518 | 772 | .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), |
e035b77a | 773 | .eval = nft_meta_get_eval, |
d2caa696 | 774 | .init = nft_meta_get_init, |
e035b77a | 775 | .dump = nft_meta_get_dump, |
9b17afb2 | 776 | .reduce = nft_meta_get_reduce, |
f6931f5f | 777 | .validate = nft_meta_get_validate, |
c9626a2c | 778 | .offload = nft_meta_get_offload, |
ef1f7df9 PM |
779 | }; |
780 | ||
4a80e026 PNA |
781 | static bool nft_meta_set_reduce(struct nft_regs_track *track, |
782 | const struct nft_expr *expr) | |
783 | { | |
784 | int i; | |
785 | ||
786 | for (i = 0; i < NFT_REG32_NUM; i++) { | |
787 | if (!track->regs[i].selector) | |
788 | continue; | |
789 | ||
790 | if (track->regs[i].selector->ops != &nft_meta_get_ops) | |
791 | continue; | |
792 | ||
34cc9e52 | 793 | __nft_reg_track_cancel(track, i); |
4a80e026 PNA |
794 | } |
795 | ||
796 | return false; | |
797 | } | |
798 | ||
e035b77a ABG |
799 | static const struct nft_expr_ops nft_meta_set_ops = { |
800 | .type = &nft_meta_type, | |
801 | .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), | |
802 | .eval = nft_meta_set_eval, | |
d2caa696 | 803 | .init = nft_meta_set_init, |
e639f7ab | 804 | .destroy = nft_meta_set_destroy, |
e035b77a | 805 | .dump = nft_meta_set_dump, |
4a80e026 | 806 | .reduce = nft_meta_set_reduce, |
960fa72f | 807 | .validate = nft_meta_set_validate, |
e035b77a ABG |
808 | }; |
809 | ||
810 | static const struct nft_expr_ops * | |
811 | nft_meta_select_ops(const struct nft_ctx *ctx, | |
812 | const struct nlattr * const tb[]) | |
813 | { | |
814 | if (tb[NFTA_META_KEY] == NULL) | |
815 | return ERR_PTR(-EINVAL); | |
816 | ||
817 | if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG]) | |
818 | return ERR_PTR(-EINVAL); | |
819 | ||
dfee0e99 | 820 | #if IS_ENABLED(CONFIG_NF_TABLES_BRIDGE) && IS_MODULE(CONFIG_NFT_BRIDGE_META) |
0ef1efd1 PNA |
821 | if (ctx->family == NFPROTO_BRIDGE) |
822 | return ERR_PTR(-EAGAIN); | |
823 | #endif | |
e035b77a ABG |
824 | if (tb[NFTA_META_DREG]) |
825 | return &nft_meta_get_ops; | |
826 | ||
827 | if (tb[NFTA_META_SREG]) | |
828 | return &nft_meta_set_ops; | |
829 | ||
830 | return ERR_PTR(-EINVAL); | |
831 | } | |
832 | ||
a150d122 PNA |
833 | static int nft_meta_inner_init(const struct nft_ctx *ctx, |
834 | const struct nft_expr *expr, | |
835 | const struct nlattr * const tb[]) | |
836 | { | |
837 | struct nft_meta *priv = nft_expr_priv(expr); | |
838 | unsigned int len; | |
839 | ||
c4ab9da8 DO |
840 | if (!tb[NFTA_META_KEY] || !tb[NFTA_META_DREG]) |
841 | return -EINVAL; | |
842 | ||
a150d122 PNA |
843 | priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); |
844 | switch (priv->key) { | |
845 | case NFT_META_PROTOCOL: | |
846 | len = sizeof(u16); | |
847 | break; | |
848 | case NFT_META_L4PROTO: | |
849 | len = sizeof(u32); | |
850 | break; | |
851 | default: | |
852 | return -EOPNOTSUPP; | |
853 | } | |
854 | priv->len = len; | |
855 | ||
856 | return nft_parse_register_store(ctx, tb[NFTA_META_DREG], &priv->dreg, | |
857 | NULL, NFT_DATA_VALUE, len); | |
858 | } | |
859 | ||
860 | void nft_meta_inner_eval(const struct nft_expr *expr, | |
861 | struct nft_regs *regs, | |
862 | const struct nft_pktinfo *pkt, | |
863 | struct nft_inner_tun_ctx *tun_ctx) | |
864 | { | |
865 | const struct nft_meta *priv = nft_expr_priv(expr); | |
866 | u32 *dest = ®s->data[priv->dreg]; | |
867 | ||
868 | switch (priv->key) { | |
869 | case NFT_META_PROTOCOL: | |
870 | nft_reg_store16(dest, (__force u16)tun_ctx->llproto); | |
871 | break; | |
872 | case NFT_META_L4PROTO: | |
873 | if (!(tun_ctx->flags & NFT_PAYLOAD_CTX_INNER_TH)) | |
874 | goto err; | |
875 | ||
876 | nft_reg_store8(dest, tun_ctx->l4proto); | |
877 | break; | |
878 | default: | |
879 | WARN_ON_ONCE(1); | |
880 | goto err; | |
881 | } | |
882 | return; | |
883 | ||
884 | err: | |
885 | regs->verdict.code = NFT_BREAK; | |
886 | } | |
887 | EXPORT_SYMBOL_GPL(nft_meta_inner_eval); | |
888 | ||
889 | static const struct nft_expr_ops nft_meta_inner_ops = { | |
890 | .type = &nft_meta_type, | |
891 | .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), | |
892 | .init = nft_meta_inner_init, | |
893 | .dump = nft_meta_get_dump, | |
894 | /* direct call to nft_meta_inner_eval(). */ | |
895 | }; | |
896 | ||
8a22543c | 897 | struct nft_expr_type nft_meta_type __read_mostly = { |
ef1f7df9 | 898 | .name = "meta", |
d4ef3835 | 899 | .select_ops = nft_meta_select_ops, |
a150d122 | 900 | .inner_ops = &nft_meta_inner_ops, |
96518518 PM |
901 | .policy = nft_meta_policy, |
902 | .maxattr = NFTA_META_MAX, | |
ef1f7df9 | 903 | .owner = THIS_MODULE, |
96518518 | 904 | }; |
fb961945 CG |
905 | |
906 | #ifdef CONFIG_NETWORK_SECMARK | |
907 | struct nft_secmark { | |
908 | u32 secid; | |
909 | char *ctx; | |
910 | }; | |
911 | ||
912 | static const struct nla_policy nft_secmark_policy[NFTA_SECMARK_MAX + 1] = { | |
913 | [NFTA_SECMARK_CTX] = { .type = NLA_STRING, .len = NFT_SECMARK_CTX_MAXLEN }, | |
914 | }; | |
915 | ||
916 | static int nft_secmark_compute_secid(struct nft_secmark *priv) | |
917 | { | |
918 | u32 tmp_secid = 0; | |
919 | int err; | |
920 | ||
921 | err = security_secctx_to_secid(priv->ctx, strlen(priv->ctx), &tmp_secid); | |
922 | if (err) | |
923 | return err; | |
924 | ||
925 | if (!tmp_secid) | |
926 | return -ENOENT; | |
927 | ||
928 | err = security_secmark_relabel_packet(tmp_secid); | |
929 | if (err) | |
930 | return err; | |
931 | ||
932 | priv->secid = tmp_secid; | |
933 | return 0; | |
934 | } | |
935 | ||
936 | static void nft_secmark_obj_eval(struct nft_object *obj, struct nft_regs *regs, | |
937 | const struct nft_pktinfo *pkt) | |
938 | { | |
939 | const struct nft_secmark *priv = nft_obj_data(obj); | |
940 | struct sk_buff *skb = pkt->skb; | |
941 | ||
942 | skb->secmark = priv->secid; | |
943 | } | |
944 | ||
945 | static int nft_secmark_obj_init(const struct nft_ctx *ctx, | |
946 | const struct nlattr * const tb[], | |
947 | struct nft_object *obj) | |
948 | { | |
949 | struct nft_secmark *priv = nft_obj_data(obj); | |
950 | int err; | |
951 | ||
952 | if (tb[NFTA_SECMARK_CTX] == NULL) | |
953 | return -EINVAL; | |
954 | ||
69e687ce | 955 | priv->ctx = nla_strdup(tb[NFTA_SECMARK_CTX], GFP_KERNEL_ACCOUNT); |
fb961945 CG |
956 | if (!priv->ctx) |
957 | return -ENOMEM; | |
958 | ||
959 | err = nft_secmark_compute_secid(priv); | |
960 | if (err) { | |
961 | kfree(priv->ctx); | |
962 | return err; | |
963 | } | |
964 | ||
965 | security_secmark_refcount_inc(); | |
966 | ||
967 | return 0; | |
968 | } | |
969 | ||
970 | static int nft_secmark_obj_dump(struct sk_buff *skb, struct nft_object *obj, | |
971 | bool reset) | |
972 | { | |
973 | struct nft_secmark *priv = nft_obj_data(obj); | |
974 | int err; | |
975 | ||
976 | if (nla_put_string(skb, NFTA_SECMARK_CTX, priv->ctx)) | |
977 | return -1; | |
978 | ||
979 | if (reset) { | |
980 | err = nft_secmark_compute_secid(priv); | |
981 | if (err) | |
982 | return err; | |
983 | } | |
984 | ||
985 | return 0; | |
986 | } | |
987 | ||
988 | static void nft_secmark_obj_destroy(const struct nft_ctx *ctx, struct nft_object *obj) | |
989 | { | |
990 | struct nft_secmark *priv = nft_obj_data(obj); | |
991 | ||
992 | security_secmark_refcount_dec(); | |
993 | ||
994 | kfree(priv->ctx); | |
995 | } | |
996 | ||
997 | static const struct nft_object_ops nft_secmark_obj_ops = { | |
998 | .type = &nft_secmark_obj_type, | |
999 | .size = sizeof(struct nft_secmark), | |
1000 | .init = nft_secmark_obj_init, | |
1001 | .eval = nft_secmark_obj_eval, | |
1002 | .dump = nft_secmark_obj_dump, | |
1003 | .destroy = nft_secmark_obj_destroy, | |
1004 | }; | |
1005 | struct nft_object_type nft_secmark_obj_type __read_mostly = { | |
1006 | .type = NFT_OBJECT_SECMARK, | |
1007 | .ops = &nft_secmark_obj_ops, | |
1008 | .maxattr = NFTA_SECMARK_MAX, | |
1009 | .policy = nft_secmark_policy, | |
1010 | .owner = THIS_MODULE, | |
1011 | }; | |
1012 | #endif /* CONFIG_NETWORK_SECMARK */ |