xref: /linux/net/netfilter/nft_socket.c (revision 52338415)
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>
8 #include <net/tcp.h>
9 
10 struct nft_socket {
11 	enum nft_socket_keys		key:8;
12 	union {
13 		enum nft_registers	dreg:8;
14 	};
15 };
16 
17 static void nft_socket_eval(const struct nft_expr *expr,
18 			    struct nft_regs *regs,
19 			    const struct nft_pktinfo *pkt)
20 {
21 	const struct nft_socket *priv = nft_expr_priv(expr);
22 	struct sk_buff *skb = pkt->skb;
23 	struct sock *sk = skb->sk;
24 	u32 *dest = &regs->data[priv->dreg];
25 
26 	if (sk && !net_eq(nft_net(pkt), sock_net(sk)))
27 		sk = NULL;
28 
29 	if (!sk)
30 		switch(nft_pf(pkt)) {
31 		case NFPROTO_IPV4:
32 			sk = nf_sk_lookup_slow_v4(nft_net(pkt), skb, nft_in(pkt));
33 			break;
34 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
35 		case NFPROTO_IPV6:
36 			sk = nf_sk_lookup_slow_v6(nft_net(pkt), skb, nft_in(pkt));
37 			break;
38 #endif
39 		default:
40 			WARN_ON_ONCE(1);
41 			regs->verdict.code = NFT_BREAK;
42 			return;
43 		}
44 
45 	if (!sk) {
46 		regs->verdict.code = NFT_BREAK;
47 		return;
48 	}
49 
50 	switch(priv->key) {
51 	case NFT_SOCKET_TRANSPARENT:
52 		nft_reg_store8(dest, inet_sk_transparent(sk));
53 		break;
54 	case NFT_SOCKET_MARK:
55 		if (sk_fullsock(sk)) {
56 			*dest = sk->sk_mark;
57 		} else {
58 			regs->verdict.code = NFT_BREAK;
59 			return;
60 		}
61 		break;
62 	default:
63 		WARN_ON(1);
64 		regs->verdict.code = NFT_BREAK;
65 	}
66 
67 	if (sk != skb->sk)
68 		sock_gen_put(sk);
69 }
70 
71 static const struct nla_policy nft_socket_policy[NFTA_SOCKET_MAX + 1] = {
72 	[NFTA_SOCKET_KEY]		= { .type = NLA_U32 },
73 	[NFTA_SOCKET_DREG]		= { .type = NLA_U32 },
74 };
75 
76 static int nft_socket_init(const struct nft_ctx *ctx,
77 			   const struct nft_expr *expr,
78 			   const struct nlattr * const tb[])
79 {
80 	struct nft_socket *priv = nft_expr_priv(expr);
81 	unsigned int len;
82 
83 	if (!tb[NFTA_SOCKET_DREG] || !tb[NFTA_SOCKET_KEY])
84 		return -EINVAL;
85 
86 	switch(ctx->family) {
87 	case NFPROTO_IPV4:
88 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
89 	case NFPROTO_IPV6:
90 #endif
91 	case NFPROTO_INET:
92 		break;
93 	default:
94 		return -EOPNOTSUPP;
95 	}
96 
97 	priv->key = ntohl(nla_get_u32(tb[NFTA_SOCKET_KEY]));
98 	switch(priv->key) {
99 	case NFT_SOCKET_TRANSPARENT:
100 		len = sizeof(u8);
101 		break;
102 	case NFT_SOCKET_MARK:
103 		len = sizeof(u32);
104 		break;
105 	default:
106 		return -EOPNOTSUPP;
107 	}
108 
109 	priv->dreg = nft_parse_register(tb[NFTA_SOCKET_DREG]);
110 	return nft_validate_register_store(ctx, priv->dreg, NULL,
111 					   NFT_DATA_VALUE, len);
112 }
113 
114 static int nft_socket_dump(struct sk_buff *skb,
115 			   const struct nft_expr *expr)
116 {
117 	const struct nft_socket *priv = nft_expr_priv(expr);
118 
119 	if (nla_put_u32(skb, NFTA_SOCKET_KEY, htonl(priv->key)))
120 		return -1;
121 	if (nft_dump_register(skb, NFTA_SOCKET_DREG, priv->dreg))
122 		return -1;
123 	return 0;
124 }
125 
126 static struct nft_expr_type nft_socket_type;
127 static const struct nft_expr_ops nft_socket_ops = {
128 	.type		= &nft_socket_type,
129 	.size		= NFT_EXPR_SIZE(sizeof(struct nft_socket)),
130 	.eval		= nft_socket_eval,
131 	.init		= nft_socket_init,
132 	.dump		= nft_socket_dump,
133 };
134 
135 static struct nft_expr_type nft_socket_type __read_mostly = {
136 	.name		= "socket",
137 	.ops		= &nft_socket_ops,
138 	.policy		= nft_socket_policy,
139 	.maxattr	= NFTA_SOCKET_MAX,
140 	.owner		= THIS_MODULE,
141 };
142 
143 static int __init nft_socket_module_init(void)
144 {
145 	return nft_register_expr(&nft_socket_type);
146 }
147 
148 static void __exit nft_socket_module_exit(void)
149 {
150 	nft_unregister_expr(&nft_socket_type);
151 }
152 
153 module_init(nft_socket_module_init);
154 module_exit(nft_socket_module_exit);
155 
156 MODULE_LICENSE("GPL");
157 MODULE_AUTHOR("Máté Eckl");
158 MODULE_DESCRIPTION("nf_tables socket match module");
159 MODULE_ALIAS_NFT_EXPR("socket");
160