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