1 /* SPDX-License-Identifier: GPL-2.0 */
2 #include <linux/module.h>
3 #include <linux/netfilter/nf_tables.h>
4 #include <net/netfilter/nf_tables.h>
5 #include <net/netfilter/nf_tables_core.h>
6 #include <net/netfilter/nf_socket.h>
7 #include <net/inet_sock.h>
11 enum nft_socket_keys key
:8;
12 u8 level
; /* cgroupv2 level to extract */
13 u8 level_user
; /* cgroupv2 level provided by userspace */
20 static void nft_socket_wildcard(const struct nft_pktinfo
*pkt
,
21 struct nft_regs
*regs
, struct sock
*sk
,
24 switch (nft_pf(pkt
)) {
26 nft_reg_store8(dest
, inet_sk(sk
)->inet_rcv_saddr
== 0);
28 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
30 nft_reg_store8(dest
, ipv6_addr_any(&sk
->sk_v6_rcv_saddr
));
34 regs
->verdict
.code
= NFT_BREAK
;
39 #ifdef CONFIG_SOCK_CGROUP_DATA
41 nft_sock_get_eval_cgroupv2(u32
*dest
, struct sock
*sk
, const struct nft_pktinfo
*pkt
, u32 level
)
49 cgrp
= cgroup_ancestor(sock_cgroup_ptr(&sk
->sk_cgrp_data
), level
);
53 cgid
= cgroup_id(cgrp
);
54 memcpy(dest
, &cgid
, sizeof(u64
));
58 /* process context only, uses current->nsproxy. */
59 static noinline
int nft_socket_cgroup_subtree_level(void)
61 struct cgroup
*cgrp
= cgroup_get_from_path("/");
71 if (WARN_ON_ONCE(level
> 255))
74 if (WARN_ON_ONCE(level
< 0))
81 static struct sock
*nft_socket_do_lookup(const struct nft_pktinfo
*pkt
)
83 const struct net_device
*indev
= nft_in(pkt
);
84 const struct sk_buff
*skb
= pkt
->skb
;
85 struct sock
*sk
= NULL
;
90 switch (nft_pf(pkt
)) {
92 sk
= nf_sk_lookup_slow_v4(nft_net(pkt
), skb
, indev
);
94 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
96 sk
= nf_sk_lookup_slow_v6(nft_net(pkt
), skb
, indev
);
107 static void nft_socket_eval(const struct nft_expr
*expr
,
108 struct nft_regs
*regs
,
109 const struct nft_pktinfo
*pkt
)
111 const struct nft_socket
*priv
= nft_expr_priv(expr
);
112 struct sk_buff
*skb
= pkt
->skb
;
113 struct sock
*sk
= skb
->sk
;
114 u32
*dest
= ®s
->data
[priv
->dreg
];
116 if (sk
&& !net_eq(nft_net(pkt
), sock_net(sk
)))
120 sk
= nft_socket_do_lookup(pkt
);
123 regs
->verdict
.code
= NFT_BREAK
;
128 case NFT_SOCKET_TRANSPARENT
:
129 nft_reg_store8(dest
, inet_sk_transparent(sk
));
131 case NFT_SOCKET_MARK
:
132 if (sk_fullsock(sk
)) {
133 *dest
= READ_ONCE(sk
->sk_mark
);
135 regs
->verdict
.code
= NFT_BREAK
;
139 case NFT_SOCKET_WILDCARD
:
140 if (!sk_fullsock(sk
)) {
141 regs
->verdict
.code
= NFT_BREAK
;
144 nft_socket_wildcard(pkt
, regs
, sk
, dest
);
146 #ifdef CONFIG_SOCK_CGROUP_DATA
147 case NFT_SOCKET_CGROUPV2
:
148 if (!nft_sock_get_eval_cgroupv2(dest
, sk
, pkt
, priv
->level
)) {
149 regs
->verdict
.code
= NFT_BREAK
;
156 regs
->verdict
.code
= NFT_BREAK
;
164 static const struct nla_policy nft_socket_policy
[NFTA_SOCKET_MAX
+ 1] = {
165 [NFTA_SOCKET_KEY
] = NLA_POLICY_MAX(NLA_BE32
, 255),
166 [NFTA_SOCKET_DREG
] = { .type
= NLA_U32
},
167 [NFTA_SOCKET_LEVEL
] = NLA_POLICY_MAX(NLA_BE32
, 255),
170 static int nft_socket_init(const struct nft_ctx
*ctx
,
171 const struct nft_expr
*expr
,
172 const struct nlattr
* const tb
[])
174 struct nft_socket
*priv
= nft_expr_priv(expr
);
177 if (!tb
[NFTA_SOCKET_DREG
] || !tb
[NFTA_SOCKET_KEY
])
180 switch(ctx
->family
) {
182 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
191 priv
->key
= ntohl(nla_get_be32(tb
[NFTA_SOCKET_KEY
]));
193 case NFT_SOCKET_TRANSPARENT
:
194 case NFT_SOCKET_WILDCARD
:
197 case NFT_SOCKET_MARK
:
200 #ifdef CONFIG_SOCK_CGROUP_DATA
201 case NFT_SOCKET_CGROUPV2
: {
205 if (!tb
[NFTA_SOCKET_LEVEL
])
208 level
= ntohl(nla_get_be32(tb
[NFTA_SOCKET_LEVEL
]));
212 err
= nft_socket_cgroup_subtree_level();
216 priv
->level_user
= level
;
219 /* Implies a giant cgroup tree */
220 if (WARN_ON_ONCE(level
> 255))
233 return nft_parse_register_store(ctx
, tb
[NFTA_SOCKET_DREG
], &priv
->dreg
,
234 NULL
, NFT_DATA_VALUE
, len
);
237 static int nft_socket_dump(struct sk_buff
*skb
,
238 const struct nft_expr
*expr
, bool reset
)
240 const struct nft_socket
*priv
= nft_expr_priv(expr
);
242 if (nla_put_be32(skb
, NFTA_SOCKET_KEY
, htonl(priv
->key
)))
244 if (nft_dump_register(skb
, NFTA_SOCKET_DREG
, priv
->dreg
))
246 if (priv
->key
== NFT_SOCKET_CGROUPV2
&&
247 nla_put_be32(skb
, NFTA_SOCKET_LEVEL
, htonl(priv
->level_user
)))
252 static bool nft_socket_reduce(struct nft_regs_track
*track
,
253 const struct nft_expr
*expr
)
255 const struct nft_socket
*priv
= nft_expr_priv(expr
);
256 const struct nft_socket
*socket
;
258 if (!nft_reg_track_cmp(track
, expr
, priv
->dreg
)) {
259 nft_reg_track_update(track
, expr
, priv
->dreg
, priv
->len
);
263 socket
= nft_expr_priv(track
->regs
[priv
->dreg
].selector
);
264 if (priv
->key
!= socket
->key
||
265 priv
->dreg
!= socket
->dreg
||
266 priv
->level
!= socket
->level
) {
267 nft_reg_track_update(track
, expr
, priv
->dreg
, priv
->len
);
271 if (!track
->regs
[priv
->dreg
].bitwise
)
274 return nft_expr_reduce_bitwise(track
, expr
);
277 static int nft_socket_validate(const struct nft_ctx
*ctx
,
278 const struct nft_expr
*expr
)
280 if (ctx
->family
!= NFPROTO_IPV4
&&
281 ctx
->family
!= NFPROTO_IPV6
&&
282 ctx
->family
!= NFPROTO_INET
)
285 return nft_chain_validate_hooks(ctx
->chain
,
286 (1 << NF_INET_PRE_ROUTING
) |
287 (1 << NF_INET_LOCAL_IN
) |
288 (1 << NF_INET_LOCAL_OUT
));
291 static struct nft_expr_type nft_socket_type
;
292 static const struct nft_expr_ops nft_socket_ops
= {
293 .type
= &nft_socket_type
,
294 .size
= NFT_EXPR_SIZE(sizeof(struct nft_socket
)),
295 .eval
= nft_socket_eval
,
296 .init
= nft_socket_init
,
297 .dump
= nft_socket_dump
,
298 .validate
= nft_socket_validate
,
299 .reduce
= nft_socket_reduce
,
302 static struct nft_expr_type nft_socket_type __read_mostly
= {
304 .ops
= &nft_socket_ops
,
305 .policy
= nft_socket_policy
,
306 .maxattr
= NFTA_SOCKET_MAX
,
307 .owner
= THIS_MODULE
,
310 static int __init
nft_socket_module_init(void)
312 return nft_register_expr(&nft_socket_type
);
315 static void __exit
nft_socket_module_exit(void)
317 nft_unregister_expr(&nft_socket_type
);
320 module_init(nft_socket_module_init
);
321 module_exit(nft_socket_module_exit
);
323 MODULE_LICENSE("GPL");
324 MODULE_AUTHOR("Máté Eckl");
325 MODULE_DESCRIPTION("nf_tables socket match module");
326 MODULE_ALIAS_NFT_EXPR("socket");