Merge tag 'pci-v4.18-fixes-2' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaa...
[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_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");