diff options
author | Máté Eckl <ecklm94@gmail.com> | 2018-05-28 03:15:33 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2018-06-01 03:46:15 -0400 |
commit | 554ced0a6e2946562c20d9fffdbaf2aa7da36b1b (patch) | |
tree | 183337776f85d8e10d2a23b7ddc49a59cc0502c7 | |
parent | 7849958b51aa392e3592b6b8181db0baad979b0b (diff) |
netfilter: nf_tables: add support for native socket matching
Now it can only match the transparent flag of an ip/ipv6 socket.
Signed-off-by: Máté Eckl <ecklm94@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r-- | include/uapi/linux/netfilter/nf_tables.h | 25 | ||||
-rw-r--r-- | net/netfilter/Kconfig | 9 | ||||
-rw-r--r-- | net/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/netfilter/nft_socket.c | 143 |
4 files changed, 178 insertions, 0 deletions
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 9c71f024f9cc..3d46c82a5ebd 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h | |||
@@ -905,6 +905,31 @@ enum nft_rt_attributes { | |||
905 | #define NFTA_RT_MAX (__NFTA_RT_MAX - 1) | 905 | #define NFTA_RT_MAX (__NFTA_RT_MAX - 1) |
906 | 906 | ||
907 | /** | 907 | /** |
908 | * enum nft_socket_attributes - nf_tables socket expression netlink attributes | ||
909 | * | ||
910 | * @NFTA_SOCKET_KEY: socket key to match | ||
911 | * @NFTA_SOCKET_DREG: destination register | ||
912 | */ | ||
913 | enum nft_socket_attributes { | ||
914 | NFTA_SOCKET_UNSPEC, | ||
915 | NFTA_SOCKET_KEY, | ||
916 | NFTA_SOCKET_DREG, | ||
917 | __NFTA_SOCKET_MAX | ||
918 | }; | ||
919 | #define NFTA_SOCKET_MAX (__NFTA_SOCKET_MAX - 1) | ||
920 | |||
921 | /* | ||
922 | * enum nft_socket_keys - nf_tables socket expression keys | ||
923 | * | ||
924 | * @NFT_SOCKET_TRANSPARENT: Value of the IP(V6)_TRANSPARENT socket option_ | ||
925 | */ | ||
926 | enum nft_socket_keys { | ||
927 | NFT_SOCKET_TRANSPARENT, | ||
928 | __NFT_SOCKET_MAX | ||
929 | }; | ||
930 | #define NFT_SOCKET_MAX (__NFT_SOCKET_MAX - 1) | ||
931 | |||
932 | /** | ||
908 | * enum nft_ct_keys - nf_tables ct expression keys | 933 | * enum nft_ct_keys - nf_tables ct expression keys |
909 | * | 934 | * |
910 | * @NFT_CT_STATE: conntrack state (bitmask of enum ip_conntrack_info) | 935 | * @NFT_CT_STATE: conntrack state (bitmask of enum ip_conntrack_info) |
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 3ec8886850b2..276e1e32f44e 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig | |||
@@ -613,6 +613,15 @@ config NFT_FIB_INET | |||
613 | The lookup will be delegated to the IPv4 or IPv6 FIB depending | 613 | The lookup will be delegated to the IPv4 or IPv6 FIB depending |
614 | on the protocol of the packet. | 614 | on the protocol of the packet. |
615 | 615 | ||
616 | config NFT_SOCKET | ||
617 | tristate "Netfilter nf_tables socket match support" | ||
618 | depends on IPV6 || IPV6=n | ||
619 | select NF_SOCKET_IPV4 | ||
620 | select NF_SOCKET_IPV6 if IPV6 | ||
621 | help | ||
622 | This option allows matching for the presence or absence of a | ||
623 | corresponding socket and its attributes. | ||
624 | |||
616 | if NF_TABLES_NETDEV | 625 | if NF_TABLES_NETDEV |
617 | 626 | ||
618 | config NF_DUP_NETDEV | 627 | config NF_DUP_NETDEV |
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 9b3434360d49..eec169555731 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
@@ -102,6 +102,7 @@ obj-$(CONFIG_NFT_FIB) += nft_fib.o | |||
102 | obj-$(CONFIG_NFT_FIB_INET) += nft_fib_inet.o | 102 | obj-$(CONFIG_NFT_FIB_INET) += nft_fib_inet.o |
103 | obj-$(CONFIG_NFT_FIB_NETDEV) += nft_fib_netdev.o | 103 | obj-$(CONFIG_NFT_FIB_NETDEV) += nft_fib_netdev.o |
104 | obj-$(CONFIG_NF_OSF) += nf_osf.o | 104 | obj-$(CONFIG_NF_OSF) += nf_osf.o |
105 | obj-$(CONFIG_NFT_SOCKET) += nft_socket.o | ||
105 | 106 | ||
106 | # nf_tables netdev | 107 | # nf_tables netdev |
107 | obj-$(CONFIG_NFT_DUP_NETDEV) += nft_dup_netdev.o | 108 | obj-$(CONFIG_NFT_DUP_NETDEV) += nft_dup_netdev.o |
diff --git a/net/netfilter/nft_socket.c b/net/netfilter/nft_socket.c new file mode 100644 index 000000000000..d86337068ecb --- /dev/null +++ b/net/netfilter/nft_socket.c | |||
@@ -0,0 +1,143 @@ | |||
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 | |||
9 | struct nft_socket { | ||
10 | enum nft_socket_keys key:8; | ||
11 | union { | ||
12 | enum nft_registers dreg:8; | ||
13 | }; | ||
14 | }; | ||
15 | |||
16 | static void nft_socket_eval(const struct nft_expr *expr, | ||
17 | struct nft_regs *regs, | ||
18 | const struct nft_pktinfo *pkt) | ||
19 | { | ||
20 | const struct nft_socket *priv = nft_expr_priv(expr); | ||
21 | struct sk_buff *skb = pkt->skb; | ||
22 | struct sock *sk = skb->sk; | ||
23 | u32 *dest = ®s->data[priv->dreg]; | ||
24 | |||
25 | if (!sk) | ||
26 | switch(nft_pf(pkt)) { | ||
27 | case NFPROTO_IPV4: | ||
28 | sk = nf_sk_lookup_slow_v4(nft_net(pkt), skb, nft_in(pkt)); | ||
29 | break; | ||
30 | #if IS_ENABLED(CONFIG_NF_SOCKET_IPV6) | ||
31 | case NFPROTO_IPV6: | ||
32 | sk = nf_sk_lookup_slow_v6(nft_net(pkt), skb, nft_in(pkt)); | ||
33 | break; | ||
34 | #endif | ||
35 | default: | ||
36 | WARN_ON_ONCE(1); | ||
37 | regs->verdict.code = NFT_BREAK; | ||
38 | return; | ||
39 | } | ||
40 | |||
41 | if(!sk) { | ||
42 | nft_reg_store8(dest, 0); | ||
43 | return; | ||
44 | } | ||
45 | |||
46 | /* So that subsequent socket matching not to require other lookups. */ | ||
47 | skb->sk = sk; | ||
48 | |||
49 | switch(priv->key) { | ||
50 | case NFT_SOCKET_TRANSPARENT: | ||
51 | nft_reg_store8(dest, nf_sk_is_transparent(sk)); | ||
52 | break; | ||
53 | default: | ||
54 | WARN_ON(1); | ||
55 | regs->verdict.code = NFT_BREAK; | ||
56 | } | ||
57 | } | ||
58 | |||
59 | static const struct nla_policy nft_socket_policy[NFTA_SOCKET_MAX + 1] = { | ||
60 | [NFTA_SOCKET_KEY] = { .type = NLA_U32 }, | ||
61 | [NFTA_SOCKET_DREG] = { .type = NLA_U32 }, | ||
62 | }; | ||
63 | |||
64 | static int nft_socket_init(const struct nft_ctx *ctx, | ||
65 | const struct nft_expr *expr, | ||
66 | const struct nlattr * const tb[]) | ||
67 | { | ||
68 | struct nft_socket *priv = nft_expr_priv(expr); | ||
69 | unsigned int len; | ||
70 | |||
71 | if (!tb[NFTA_SOCKET_DREG] || !tb[NFTA_SOCKET_KEY]) | ||
72 | return -EINVAL; | ||
73 | |||
74 | switch(ctx->family) { | ||
75 | case NFPROTO_IPV4: | ||
76 | #if IS_ENABLED(CONFIG_NF_SOCKET_IPV6) | ||
77 | case NFPROTO_IPV6: | ||
78 | #endif | ||
79 | case NFPROTO_INET: | ||
80 | break; | ||
81 | default: | ||
82 | return -EOPNOTSUPP; | ||
83 | } | ||
84 | |||
85 | priv->key = ntohl(nla_get_u32(tb[NFTA_SOCKET_KEY])); | ||
86 | switch(priv->key) { | ||
87 | case NFT_SOCKET_TRANSPARENT: | ||
88 | len = sizeof(u8); | ||
89 | break; | ||
90 | default: | ||
91 | return -EOPNOTSUPP; | ||
92 | } | ||
93 | |||
94 | priv->dreg = nft_parse_register(tb[NFTA_SOCKET_DREG]); | ||
95 | return nft_validate_register_store(ctx, priv->dreg, NULL, | ||
96 | NFT_DATA_VALUE, len); | ||
97 | } | ||
98 | |||
99 | static int nft_socket_dump(struct sk_buff *skb, | ||
100 | const struct nft_expr *expr) | ||
101 | { | ||
102 | const struct nft_socket *priv = nft_expr_priv(expr); | ||
103 | |||
104 | if (nla_put_u32(skb, NFTA_SOCKET_KEY, htonl(priv->key))) | ||
105 | return -1; | ||
106 | if (nft_dump_register(skb, NFTA_SOCKET_DREG, priv->dreg)) | ||
107 | return -1; | ||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | static struct nft_expr_type nft_socket_type; | ||
112 | static const struct nft_expr_ops nft_socket_ops = { | ||
113 | .type = &nft_socket_type, | ||
114 | .size = NFT_EXPR_SIZE(sizeof(struct nft_socket)), | ||
115 | .eval = nft_socket_eval, | ||
116 | .init = nft_socket_init, | ||
117 | .dump = nft_socket_dump, | ||
118 | }; | ||
119 | |||
120 | static struct nft_expr_type nft_socket_type __read_mostly = { | ||
121 | .name = "socket", | ||
122 | .ops = &nft_socket_ops, | ||
123 | .policy = nft_socket_policy, | ||
124 | .maxattr = NFTA_SOCKET_MAX, | ||
125 | .owner = THIS_MODULE, | ||
126 | }; | ||
127 | |||
128 | static int __init nft_socket_module_init(void) | ||
129 | { | ||
130 | return nft_register_expr(&nft_socket_type); | ||
131 | } | ||
132 | |||
133 | static void __exit nft_socket_module_exit(void) | ||
134 | { | ||
135 | nft_unregister_expr(&nft_socket_type); | ||
136 | } | ||
137 | |||
138 | module_init(nft_socket_module_init); | ||
139 | module_exit(nft_socket_module_exit); | ||
140 | |||
141 | MODULE_LICENSE("GPL"); | ||
142 | MODULE_AUTHOR("Máté Eckl"); | ||
143 | MODULE_DESCRIPTION("nf_tables socket match module"); | ||