2 * Copyright (c) 2014 Arturo Borrero Gonzalez <arturo@debian.org>
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.
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>
20 enum nft_registers sreg_proto_min
:8;
21 enum nft_registers sreg_proto_max
:8;
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
},
31 static int nft_redir_validate(const struct nft_ctx
*ctx
,
32 const struct nft_expr
*expr
,
33 const struct nft_data
**data
)
37 err
= nft_chain_validate_dependency(ctx
->chain
, NFT_CHAIN_T_NAT
);
41 return nft_chain_validate_hooks(ctx
->chain
,
42 (1 << NF_INET_PRE_ROUTING
) |
43 (1 << NF_INET_LOCAL_OUT
));
46 static int nft_redir_init(const struct nft_ctx
*ctx
,
47 const struct nft_expr
*expr
,
48 const struct nlattr
* const tb
[])
50 struct nft_redir
*priv
= nft_expr_priv(expr
);
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
]);
59 err
= nft_validate_register_load(priv
->sreg_proto_min
, plen
);
63 if (tb
[NFTA_REDIR_REG_PROTO_MAX
]) {
64 priv
->sreg_proto_max
=
65 nft_parse_register(tb
[NFTA_REDIR_REG_PROTO_MAX
]);
67 err
= nft_validate_register_load(priv
->sreg_proto_max
,
72 priv
->sreg_proto_max
= priv
->sreg_proto_min
;
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
)
82 return nf_ct_netns_get(ctx
->net
, ctx
->family
);
85 static int nft_redir_dump(struct sk_buff
*skb
, const struct nft_expr
*expr
)
87 const struct nft_redir
*priv
= nft_expr_priv(expr
);
89 if (priv
->sreg_proto_min
) {
90 if (nft_dump_register(skb
, NFTA_REDIR_REG_PROTO_MIN
,
91 priv
->sreg_proto_min
))
93 if (nft_dump_register(skb
, NFTA_REDIR_REG_PROTO_MAX
,
94 priv
->sreg_proto_max
))
98 if (priv
->flags
!= 0 &&
99 nla_put_be32(skb
, NFTA_REDIR_FLAGS
, htonl(priv
->flags
)))
100 goto nla_put_failure
;
108 static void nft_redir_ipv4_eval(const struct nft_expr
*expr
,
109 struct nft_regs
*regs
,
110 const struct nft_pktinfo
*pkt
)
112 struct nft_redir
*priv
= nft_expr_priv(expr
);
113 struct nf_nat_ipv4_multi_range_compat mr
;
115 memset(&mr
, 0, sizeof(mr
));
116 if (priv
->sreg_proto_min
) {
117 mr
.range
[0].min
.all
= (__force __be16
)nft_reg_load16(
118 ®s
->data
[priv
->sreg_proto_min
]);
119 mr
.range
[0].max
.all
= (__force __be16
)nft_reg_load16(
120 ®s
->data
[priv
->sreg_proto_max
]);
121 mr
.range
[0].flags
|= NF_NAT_RANGE_PROTO_SPECIFIED
;
124 mr
.range
[0].flags
|= priv
->flags
;
126 regs
->verdict
.code
= nf_nat_redirect_ipv4(pkt
->skb
, &mr
, nft_hook(pkt
));
130 nft_redir_ipv4_destroy(const struct nft_ctx
*ctx
, const struct nft_expr
*expr
)
132 nf_ct_netns_put(ctx
->net
, NFPROTO_IPV4
);
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
,
146 static struct nft_expr_type nft_redir_ipv4_type __read_mostly
= {
147 .family
= NFPROTO_IPV4
,
149 .ops
= &nft_redir_ipv4_ops
,
150 .policy
= nft_redir_policy
,
151 .maxattr
= NFTA_REDIR_MAX
,
152 .owner
= THIS_MODULE
,
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
)
160 struct nft_redir
*priv
= nft_expr_priv(expr
);
161 struct nf_nat_range2 range
;
163 memset(&range
, 0, sizeof(range
));
164 if (priv
->sreg_proto_min
) {
165 range
.min_proto
.all
= (__force __be16
)nft_reg_load16(
166 ®s
->data
[priv
->sreg_proto_min
]);
167 range
.max_proto
.all
= (__force __be16
)nft_reg_load16(
168 ®s
->data
[priv
->sreg_proto_max
]);
169 range
.flags
|= NF_NAT_RANGE_PROTO_SPECIFIED
;
172 range
.flags
|= priv
->flags
;
175 nf_nat_redirect_ipv6(pkt
->skb
, &range
, nft_hook(pkt
));
179 nft_redir_ipv6_destroy(const struct nft_ctx
*ctx
, const struct nft_expr
*expr
)
181 nf_ct_netns_put(ctx
->net
, NFPROTO_IPV6
);
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
,
195 static struct nft_expr_type nft_redir_ipv6_type __read_mostly
= {
196 .family
= NFPROTO_IPV6
,
198 .ops
= &nft_redir_ipv6_ops
,
199 .policy
= nft_redir_policy
,
200 .maxattr
= NFTA_REDIR_MAX
,
201 .owner
= THIS_MODULE
,
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
)
210 switch (nft_pf(pkt
)) {
212 return nft_redir_ipv4_eval(expr
, regs
, pkt
);
214 return nft_redir_ipv6_eval(expr
, regs
, pkt
);
221 nft_redir_inet_destroy(const struct nft_ctx
*ctx
, const struct nft_expr
*expr
)
223 nf_ct_netns_put(ctx
->net
, NFPROTO_INET
);
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
,
237 static struct nft_expr_type nft_redir_inet_type __read_mostly
= {
238 .family
= NFPROTO_INET
,
240 .ops
= &nft_redir_inet_ops
,
241 .policy
= nft_redir_policy
,
242 .maxattr
= NFTA_MASQ_MAX
,
243 .owner
= THIS_MODULE
,
246 static int __init
nft_redir_module_init_inet(void)
248 return nft_register_expr(&nft_redir_inet_type
);
251 static inline int nft_redir_module_init_inet(void) { return 0; }
254 static int __init
nft_redir_module_init(void)
256 int ret
= nft_register_expr(&nft_redir_ipv4_type
);
261 #ifdef CONFIG_NF_TABLES_IPV6
262 ret
= nft_register_expr(&nft_redir_ipv6_type
);
264 nft_unregister_expr(&nft_redir_ipv4_type
);
269 ret
= nft_redir_module_init_inet();
271 nft_unregister_expr(&nft_redir_ipv4_type
);
272 #ifdef CONFIG_NF_TABLES_IPV6
273 nft_unregister_expr(&nft_redir_ipv6_type
);
281 static void __exit
nft_redir_module_exit(void)
283 nft_unregister_expr(&nft_redir_ipv4_type
);
284 #ifdef CONFIG_NF_TABLES_IPV6
285 nft_unregister_expr(&nft_redir_ipv6_type
);
287 #ifdef CONFIG_NF_TABLES_INET
288 nft_unregister_expr(&nft_redir_inet_type
);
292 module_init(nft_redir_module_init
);
293 module_exit(nft_redir_module_exit
);
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");