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