1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2014 Arturo Borrero Gonzalez <arturo@debian.org>
6 #include <linux/kernel.h>
7 #include <linux/init.h>
8 #include <linux/module.h>
9 #include <linux/netlink.h>
10 #include <linux/netfilter.h>
11 #include <linux/netfilter/nf_tables.h>
12 #include <net/netfilter/nf_nat.h>
13 #include <net/netfilter/nf_nat_redirect.h>
14 #include <net/netfilter/nf_tables.h>
17 enum nft_registers sreg_proto_min
:8;
18 enum nft_registers sreg_proto_max
:8;
22 static const struct nla_policy nft_redir_policy
[NFTA_REDIR_MAX
+ 1] = {
23 [NFTA_REDIR_REG_PROTO_MIN
] = { .type
= NLA_U32
},
24 [NFTA_REDIR_REG_PROTO_MAX
] = { .type
= NLA_U32
},
25 [NFTA_REDIR_FLAGS
] = { .type
= NLA_U32
},
28 static int nft_redir_validate(const struct nft_ctx
*ctx
,
29 const struct nft_expr
*expr
,
30 const struct nft_data
**data
)
34 err
= nft_chain_validate_dependency(ctx
->chain
, NFT_CHAIN_T_NAT
);
38 return nft_chain_validate_hooks(ctx
->chain
,
39 (1 << NF_INET_PRE_ROUTING
) |
40 (1 << NF_INET_LOCAL_OUT
));
43 static int nft_redir_init(const struct nft_ctx
*ctx
,
44 const struct nft_expr
*expr
,
45 const struct nlattr
* const tb
[])
47 struct nft_redir
*priv
= nft_expr_priv(expr
);
51 plen
= sizeof_field(struct nf_nat_range
, min_addr
.all
);
52 if (tb
[NFTA_REDIR_REG_PROTO_MIN
]) {
53 priv
->sreg_proto_min
=
54 nft_parse_register(tb
[NFTA_REDIR_REG_PROTO_MIN
]);
56 err
= nft_validate_register_load(priv
->sreg_proto_min
, plen
);
60 if (tb
[NFTA_REDIR_REG_PROTO_MAX
]) {
61 priv
->sreg_proto_max
=
62 nft_parse_register(tb
[NFTA_REDIR_REG_PROTO_MAX
]);
64 err
= nft_validate_register_load(priv
->sreg_proto_max
,
69 priv
->sreg_proto_max
= priv
->sreg_proto_min
;
73 if (tb
[NFTA_REDIR_FLAGS
]) {
74 priv
->flags
= ntohl(nla_get_be32(tb
[NFTA_REDIR_FLAGS
]));
75 if (priv
->flags
& ~NF_NAT_RANGE_MASK
)
79 return nf_ct_netns_get(ctx
->net
, ctx
->family
);
82 static int nft_redir_dump(struct sk_buff
*skb
, const struct nft_expr
*expr
)
84 const struct nft_redir
*priv
= nft_expr_priv(expr
);
86 if (priv
->sreg_proto_min
) {
87 if (nft_dump_register(skb
, NFTA_REDIR_REG_PROTO_MIN
,
88 priv
->sreg_proto_min
))
90 if (nft_dump_register(skb
, NFTA_REDIR_REG_PROTO_MAX
,
91 priv
->sreg_proto_max
))
95 if (priv
->flags
!= 0 &&
96 nla_put_be32(skb
, NFTA_REDIR_FLAGS
, htonl(priv
->flags
)))
105 static void nft_redir_ipv4_eval(const struct nft_expr
*expr
,
106 struct nft_regs
*regs
,
107 const struct nft_pktinfo
*pkt
)
109 struct nft_redir
*priv
= nft_expr_priv(expr
);
110 struct nf_nat_ipv4_multi_range_compat mr
;
112 memset(&mr
, 0, sizeof(mr
));
113 if (priv
->sreg_proto_min
) {
114 mr
.range
[0].min
.all
= (__force __be16
)nft_reg_load16(
115 ®s
->data
[priv
->sreg_proto_min
]);
116 mr
.range
[0].max
.all
= (__force __be16
)nft_reg_load16(
117 ®s
->data
[priv
->sreg_proto_max
]);
118 mr
.range
[0].flags
|= NF_NAT_RANGE_PROTO_SPECIFIED
;
121 mr
.range
[0].flags
|= priv
->flags
;
123 regs
->verdict
.code
= nf_nat_redirect_ipv4(pkt
->skb
, &mr
, nft_hook(pkt
));
127 nft_redir_ipv4_destroy(const struct nft_ctx
*ctx
, const struct nft_expr
*expr
)
129 nf_ct_netns_put(ctx
->net
, NFPROTO_IPV4
);
132 static struct nft_expr_type nft_redir_ipv4_type
;
133 static const struct nft_expr_ops nft_redir_ipv4_ops
= {
134 .type
= &nft_redir_ipv4_type
,
135 .size
= NFT_EXPR_SIZE(sizeof(struct nft_redir
)),
136 .eval
= nft_redir_ipv4_eval
,
137 .init
= nft_redir_init
,
138 .destroy
= nft_redir_ipv4_destroy
,
139 .dump
= nft_redir_dump
,
140 .validate
= nft_redir_validate
,
143 static struct nft_expr_type nft_redir_ipv4_type __read_mostly
= {
144 .family
= NFPROTO_IPV4
,
146 .ops
= &nft_redir_ipv4_ops
,
147 .policy
= nft_redir_policy
,
148 .maxattr
= NFTA_REDIR_MAX
,
149 .owner
= THIS_MODULE
,
152 #ifdef CONFIG_NF_TABLES_IPV6
153 static void nft_redir_ipv6_eval(const struct nft_expr
*expr
,
154 struct nft_regs
*regs
,
155 const struct nft_pktinfo
*pkt
)
157 struct nft_redir
*priv
= nft_expr_priv(expr
);
158 struct nf_nat_range2 range
;
160 memset(&range
, 0, sizeof(range
));
161 if (priv
->sreg_proto_min
) {
162 range
.min_proto
.all
= (__force __be16
)nft_reg_load16(
163 ®s
->data
[priv
->sreg_proto_min
]);
164 range
.max_proto
.all
= (__force __be16
)nft_reg_load16(
165 ®s
->data
[priv
->sreg_proto_max
]);
166 range
.flags
|= NF_NAT_RANGE_PROTO_SPECIFIED
;
169 range
.flags
|= priv
->flags
;
172 nf_nat_redirect_ipv6(pkt
->skb
, &range
, nft_hook(pkt
));
176 nft_redir_ipv6_destroy(const struct nft_ctx
*ctx
, const struct nft_expr
*expr
)
178 nf_ct_netns_put(ctx
->net
, NFPROTO_IPV6
);
181 static struct nft_expr_type nft_redir_ipv6_type
;
182 static const struct nft_expr_ops nft_redir_ipv6_ops
= {
183 .type
= &nft_redir_ipv6_type
,
184 .size
= NFT_EXPR_SIZE(sizeof(struct nft_redir
)),
185 .eval
= nft_redir_ipv6_eval
,
186 .init
= nft_redir_init
,
187 .destroy
= nft_redir_ipv6_destroy
,
188 .dump
= nft_redir_dump
,
189 .validate
= nft_redir_validate
,
192 static struct nft_expr_type nft_redir_ipv6_type __read_mostly
= {
193 .family
= NFPROTO_IPV6
,
195 .ops
= &nft_redir_ipv6_ops
,
196 .policy
= nft_redir_policy
,
197 .maxattr
= NFTA_REDIR_MAX
,
198 .owner
= THIS_MODULE
,
202 #ifdef CONFIG_NF_TABLES_INET
203 static void nft_redir_inet_eval(const struct nft_expr
*expr
,
204 struct nft_regs
*regs
,
205 const struct nft_pktinfo
*pkt
)
207 switch (nft_pf(pkt
)) {
209 return nft_redir_ipv4_eval(expr
, regs
, pkt
);
211 return nft_redir_ipv6_eval(expr
, regs
, pkt
);
218 nft_redir_inet_destroy(const struct nft_ctx
*ctx
, const struct nft_expr
*expr
)
220 nf_ct_netns_put(ctx
->net
, NFPROTO_INET
);
223 static struct nft_expr_type nft_redir_inet_type
;
224 static const struct nft_expr_ops nft_redir_inet_ops
= {
225 .type
= &nft_redir_inet_type
,
226 .size
= NFT_EXPR_SIZE(sizeof(struct nft_redir
)),
227 .eval
= nft_redir_inet_eval
,
228 .init
= nft_redir_init
,
229 .destroy
= nft_redir_inet_destroy
,
230 .dump
= nft_redir_dump
,
231 .validate
= nft_redir_validate
,
234 static struct nft_expr_type nft_redir_inet_type __read_mostly
= {
235 .family
= NFPROTO_INET
,
237 .ops
= &nft_redir_inet_ops
,
238 .policy
= nft_redir_policy
,
239 .maxattr
= NFTA_MASQ_MAX
,
240 .owner
= THIS_MODULE
,
243 static int __init
nft_redir_module_init_inet(void)
245 return nft_register_expr(&nft_redir_inet_type
);
248 static inline int nft_redir_module_init_inet(void) { return 0; }
251 static int __init
nft_redir_module_init(void)
253 int ret
= nft_register_expr(&nft_redir_ipv4_type
);
258 #ifdef CONFIG_NF_TABLES_IPV6
259 ret
= nft_register_expr(&nft_redir_ipv6_type
);
261 nft_unregister_expr(&nft_redir_ipv4_type
);
266 ret
= nft_redir_module_init_inet();
268 nft_unregister_expr(&nft_redir_ipv4_type
);
269 #ifdef CONFIG_NF_TABLES_IPV6
270 nft_unregister_expr(&nft_redir_ipv6_type
);
278 static void __exit
nft_redir_module_exit(void)
280 nft_unregister_expr(&nft_redir_ipv4_type
);
281 #ifdef CONFIG_NF_TABLES_IPV6
282 nft_unregister_expr(&nft_redir_ipv6_type
);
284 #ifdef CONFIG_NF_TABLES_INET
285 nft_unregister_expr(&nft_redir_inet_type
);
289 module_init(nft_redir_module_init
);
290 module_exit(nft_redir_module_exit
);
292 MODULE_LICENSE("GPL");
293 MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo@debian.org>");
294 MODULE_ALIAS_NFT_EXPR("redir");