]> asedeno.scripts.mit.edu Git - linux.git/blob - net/netfilter/nft_redir.c
Merge tag 'led-fixes-for-5.2-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux.git] / net / netfilter / nft_redir.c
1 /*
2  * Copyright (c) 2014 Arturo Borrero Gonzalez <arturo@debian.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8
9 #include <linux/kernel.h>
10 #include <linux/init.h>
11 #include <linux/module.h>
12 #include <linux/netlink.h>
13 #include <linux/netfilter.h>
14 #include <linux/netfilter/nf_tables.h>
15 #include <net/netfilter/nf_nat.h>
16 #include <net/netfilter/nf_nat_redirect.h>
17 #include <net/netfilter/nf_tables.h>
18
19 struct nft_redir {
20         enum nft_registers      sreg_proto_min:8;
21         enum nft_registers      sreg_proto_max:8;
22         u16                     flags;
23 };
24
25 static const struct nla_policy nft_redir_policy[NFTA_REDIR_MAX + 1] = {
26         [NFTA_REDIR_REG_PROTO_MIN]      = { .type = NLA_U32 },
27         [NFTA_REDIR_REG_PROTO_MAX]      = { .type = NLA_U32 },
28         [NFTA_REDIR_FLAGS]              = { .type = NLA_U32 },
29 };
30
31 static int nft_redir_validate(const struct nft_ctx *ctx,
32                               const struct nft_expr *expr,
33                               const struct nft_data **data)
34 {
35         int err;
36
37         err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
38         if (err < 0)
39                 return err;
40
41         return nft_chain_validate_hooks(ctx->chain,
42                                         (1 << NF_INET_PRE_ROUTING) |
43                                         (1 << NF_INET_LOCAL_OUT));
44 }
45
46 static int nft_redir_init(const struct nft_ctx *ctx,
47                           const struct nft_expr *expr,
48                           const struct nlattr * const tb[])
49 {
50         struct nft_redir *priv = nft_expr_priv(expr);
51         unsigned int plen;
52         int err;
53
54         plen = FIELD_SIZEOF(struct nf_nat_range, min_addr.all);
55         if (tb[NFTA_REDIR_REG_PROTO_MIN]) {
56                 priv->sreg_proto_min =
57                         nft_parse_register(tb[NFTA_REDIR_REG_PROTO_MIN]);
58
59                 err = nft_validate_register_load(priv->sreg_proto_min, plen);
60                 if (err < 0)
61                         return err;
62
63                 if (tb[NFTA_REDIR_REG_PROTO_MAX]) {
64                         priv->sreg_proto_max =
65                                 nft_parse_register(tb[NFTA_REDIR_REG_PROTO_MAX]);
66
67                         err = nft_validate_register_load(priv->sreg_proto_max,
68                                                          plen);
69                         if (err < 0)
70                                 return err;
71                 } else {
72                         priv->sreg_proto_max = priv->sreg_proto_min;
73                 }
74         }
75
76         if (tb[NFTA_REDIR_FLAGS]) {
77                 priv->flags = ntohl(nla_get_be32(tb[NFTA_REDIR_FLAGS]));
78                 if (priv->flags & ~NF_NAT_RANGE_MASK)
79                         return -EINVAL;
80         }
81
82         return nf_ct_netns_get(ctx->net, ctx->family);
83 }
84
85 static int nft_redir_dump(struct sk_buff *skb, const struct nft_expr *expr)
86 {
87         const struct nft_redir *priv = nft_expr_priv(expr);
88
89         if (priv->sreg_proto_min) {
90                 if (nft_dump_register(skb, NFTA_REDIR_REG_PROTO_MIN,
91                                       priv->sreg_proto_min))
92                         goto nla_put_failure;
93                 if (nft_dump_register(skb, NFTA_REDIR_REG_PROTO_MAX,
94                                       priv->sreg_proto_max))
95                         goto nla_put_failure;
96         }
97
98         if (priv->flags != 0 &&
99             nla_put_be32(skb, NFTA_REDIR_FLAGS, htonl(priv->flags)))
100                         goto nla_put_failure;
101
102         return 0;
103
104 nla_put_failure:
105         return -1;
106 }
107
108 static void nft_redir_ipv4_eval(const struct nft_expr *expr,
109                                 struct nft_regs *regs,
110                                 const struct nft_pktinfo *pkt)
111 {
112         struct nft_redir *priv = nft_expr_priv(expr);
113         struct nf_nat_ipv4_multi_range_compat mr;
114
115         memset(&mr, 0, sizeof(mr));
116         if (priv->sreg_proto_min) {
117                 mr.range[0].min.all = (__force __be16)nft_reg_load16(
118                         &regs->data[priv->sreg_proto_min]);
119                 mr.range[0].max.all = (__force __be16)nft_reg_load16(
120                         &regs->data[priv->sreg_proto_max]);
121                 mr.range[0].flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
122         }
123
124         mr.range[0].flags |= priv->flags;
125
126         regs->verdict.code = nf_nat_redirect_ipv4(pkt->skb, &mr, nft_hook(pkt));
127 }
128
129 static void
130 nft_redir_ipv4_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
131 {
132         nf_ct_netns_put(ctx->net, NFPROTO_IPV4);
133 }
134
135 static struct nft_expr_type nft_redir_ipv4_type;
136 static const struct nft_expr_ops nft_redir_ipv4_ops = {
137         .type           = &nft_redir_ipv4_type,
138         .size           = NFT_EXPR_SIZE(sizeof(struct nft_redir)),
139         .eval           = nft_redir_ipv4_eval,
140         .init           = nft_redir_init,
141         .destroy        = nft_redir_ipv4_destroy,
142         .dump           = nft_redir_dump,
143         .validate       = nft_redir_validate,
144 };
145
146 static struct nft_expr_type nft_redir_ipv4_type __read_mostly = {
147         .family         = NFPROTO_IPV4,
148         .name           = "redir",
149         .ops            = &nft_redir_ipv4_ops,
150         .policy         = nft_redir_policy,
151         .maxattr        = NFTA_REDIR_MAX,
152         .owner          = THIS_MODULE,
153 };
154
155 #ifdef CONFIG_NF_TABLES_IPV6
156 static void nft_redir_ipv6_eval(const struct nft_expr *expr,
157                                 struct nft_regs *regs,
158                                 const struct nft_pktinfo *pkt)
159 {
160         struct nft_redir *priv = nft_expr_priv(expr);
161         struct nf_nat_range2 range;
162
163         memset(&range, 0, sizeof(range));
164         if (priv->sreg_proto_min) {
165                 range.min_proto.all = (__force __be16)nft_reg_load16(
166                         &regs->data[priv->sreg_proto_min]);
167                 range.max_proto.all = (__force __be16)nft_reg_load16(
168                         &regs->data[priv->sreg_proto_max]);
169                 range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
170         }
171
172         range.flags |= priv->flags;
173
174         regs->verdict.code =
175                 nf_nat_redirect_ipv6(pkt->skb, &range, nft_hook(pkt));
176 }
177
178 static void
179 nft_redir_ipv6_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
180 {
181         nf_ct_netns_put(ctx->net, NFPROTO_IPV6);
182 }
183
184 static struct nft_expr_type nft_redir_ipv6_type;
185 static const struct nft_expr_ops nft_redir_ipv6_ops = {
186         .type           = &nft_redir_ipv6_type,
187         .size           = NFT_EXPR_SIZE(sizeof(struct nft_redir)),
188         .eval           = nft_redir_ipv6_eval,
189         .init           = nft_redir_init,
190         .destroy        = nft_redir_ipv6_destroy,
191         .dump           = nft_redir_dump,
192         .validate       = nft_redir_validate,
193 };
194
195 static struct nft_expr_type nft_redir_ipv6_type __read_mostly = {
196         .family         = NFPROTO_IPV6,
197         .name           = "redir",
198         .ops            = &nft_redir_ipv6_ops,
199         .policy         = nft_redir_policy,
200         .maxattr        = NFTA_REDIR_MAX,
201         .owner          = THIS_MODULE,
202 };
203 #endif
204
205 #ifdef CONFIG_NF_TABLES_INET
206 static void nft_redir_inet_eval(const struct nft_expr *expr,
207                                 struct nft_regs *regs,
208                                 const struct nft_pktinfo *pkt)
209 {
210         switch (nft_pf(pkt)) {
211         case NFPROTO_IPV4:
212                 return nft_redir_ipv4_eval(expr, regs, pkt);
213         case NFPROTO_IPV6:
214                 return nft_redir_ipv6_eval(expr, regs, pkt);
215         }
216
217         WARN_ON_ONCE(1);
218 }
219
220 static void
221 nft_redir_inet_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
222 {
223         nf_ct_netns_put(ctx->net, NFPROTO_INET);
224 }
225
226 static struct nft_expr_type nft_redir_inet_type;
227 static const struct nft_expr_ops nft_redir_inet_ops = {
228         .type           = &nft_redir_inet_type,
229         .size           = NFT_EXPR_SIZE(sizeof(struct nft_redir)),
230         .eval           = nft_redir_inet_eval,
231         .init           = nft_redir_init,
232         .destroy        = nft_redir_inet_destroy,
233         .dump           = nft_redir_dump,
234         .validate       = nft_redir_validate,
235 };
236
237 static struct nft_expr_type nft_redir_inet_type __read_mostly = {
238         .family         = NFPROTO_INET,
239         .name           = "redir",
240         .ops            = &nft_redir_inet_ops,
241         .policy         = nft_redir_policy,
242         .maxattr        = NFTA_MASQ_MAX,
243         .owner          = THIS_MODULE,
244 };
245
246 static int __init nft_redir_module_init_inet(void)
247 {
248         return nft_register_expr(&nft_redir_inet_type);
249 }
250 #else
251 static inline int nft_redir_module_init_inet(void) { return 0; }
252 #endif
253
254 static int __init nft_redir_module_init(void)
255 {
256         int ret = nft_register_expr(&nft_redir_ipv4_type);
257
258         if (ret)
259                 return ret;
260
261 #ifdef CONFIG_NF_TABLES_IPV6
262         ret = nft_register_expr(&nft_redir_ipv6_type);
263         if (ret) {
264                 nft_unregister_expr(&nft_redir_ipv4_type);
265                 return ret;
266         }
267 #endif
268
269         ret = nft_redir_module_init_inet();
270         if (ret < 0) {
271                 nft_unregister_expr(&nft_redir_ipv4_type);
272 #ifdef CONFIG_NF_TABLES_IPV6
273                 nft_unregister_expr(&nft_redir_ipv6_type);
274 #endif
275                 return ret;
276         }
277
278         return ret;
279 }
280
281 static void __exit nft_redir_module_exit(void)
282 {
283         nft_unregister_expr(&nft_redir_ipv4_type);
284 #ifdef CONFIG_NF_TABLES_IPV6
285         nft_unregister_expr(&nft_redir_ipv6_type);
286 #endif
287 #ifdef CONFIG_NF_TABLES_INET
288         nft_unregister_expr(&nft_redir_inet_type);
289 #endif
290 }
291
292 module_init(nft_redir_module_init);
293 module_exit(nft_redir_module_exit);
294
295 MODULE_LICENSE("GPL");
296 MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo@debian.org>");
297 MODULE_ALIAS_NFT_AF_EXPR(AF_INET, "redir");
298 MODULE_ALIAS_NFT_AF_EXPR(AF_INET6, "redir");