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>
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
},
26 NLA_POLICY_MASK(NLA_BE32
, NF_NAT_RANGE_MASK
),
29 static int nft_redir_validate(const struct nft_ctx
*ctx
,
30 const struct nft_expr
*expr
)
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_proto
.all
);
52 if (tb
[NFTA_REDIR_REG_PROTO_MIN
]) {
53 err
= nft_parse_register_load(ctx
, tb
[NFTA_REDIR_REG_PROTO_MIN
],
54 &priv
->sreg_proto_min
, plen
);
58 if (tb
[NFTA_REDIR_REG_PROTO_MAX
]) {
59 err
= nft_parse_register_load(ctx
, tb
[NFTA_REDIR_REG_PROTO_MAX
],
60 &priv
->sreg_proto_max
,
65 priv
->sreg_proto_max
= priv
->sreg_proto_min
;
68 priv
->flags
|= NF_NAT_RANGE_PROTO_SPECIFIED
;
71 if (tb
[NFTA_REDIR_FLAGS
])
72 priv
->flags
= ntohl(nla_get_be32(tb
[NFTA_REDIR_FLAGS
]));
74 return nf_ct_netns_get(ctx
->net
, ctx
->family
);
77 static int nft_redir_dump(struct sk_buff
*skb
,
78 const struct nft_expr
*expr
, bool reset
)
80 const struct nft_redir
*priv
= nft_expr_priv(expr
);
82 if (priv
->sreg_proto_min
) {
83 if (nft_dump_register(skb
, NFTA_REDIR_REG_PROTO_MIN
,
84 priv
->sreg_proto_min
))
86 if (nft_dump_register(skb
, NFTA_REDIR_REG_PROTO_MAX
,
87 priv
->sreg_proto_max
))
91 if (priv
->flags
!= 0 &&
92 nla_put_be32(skb
, NFTA_REDIR_FLAGS
, htonl(priv
->flags
)))
101 static void nft_redir_eval(const struct nft_expr
*expr
,
102 struct nft_regs
*regs
,
103 const struct nft_pktinfo
*pkt
)
105 const struct nft_redir
*priv
= nft_expr_priv(expr
);
106 struct nf_nat_range2 range
;
108 memset(&range
, 0, sizeof(range
));
109 range
.flags
= priv
->flags
;
110 if (priv
->sreg_proto_min
) {
111 range
.min_proto
.all
= (__force __be16
)
112 nft_reg_load16(®s
->data
[priv
->sreg_proto_min
]);
113 range
.max_proto
.all
= (__force __be16
)
114 nft_reg_load16(®s
->data
[priv
->sreg_proto_max
]);
117 switch (nft_pf(pkt
)) {
119 regs
->verdict
.code
= nf_nat_redirect_ipv4(pkt
->skb
, &range
,
122 #ifdef CONFIG_NF_TABLES_IPV6
124 regs
->verdict
.code
= nf_nat_redirect_ipv6(pkt
->skb
, &range
,
135 nft_redir_ipv4_destroy(const struct nft_ctx
*ctx
, const struct nft_expr
*expr
)
137 nf_ct_netns_put(ctx
->net
, NFPROTO_IPV4
);
140 static struct nft_expr_type nft_redir_ipv4_type
;
141 static const struct nft_expr_ops nft_redir_ipv4_ops
= {
142 .type
= &nft_redir_ipv4_type
,
143 .size
= NFT_EXPR_SIZE(sizeof(struct nft_redir
)),
144 .eval
= nft_redir_eval
,
145 .init
= nft_redir_init
,
146 .destroy
= nft_redir_ipv4_destroy
,
147 .dump
= nft_redir_dump
,
148 .validate
= nft_redir_validate
,
149 .reduce
= NFT_REDUCE_READONLY
,
152 static struct nft_expr_type nft_redir_ipv4_type __read_mostly
= {
153 .family
= NFPROTO_IPV4
,
155 .ops
= &nft_redir_ipv4_ops
,
156 .policy
= nft_redir_policy
,
157 .maxattr
= NFTA_REDIR_MAX
,
158 .owner
= THIS_MODULE
,
161 #ifdef CONFIG_NF_TABLES_IPV6
163 nft_redir_ipv6_destroy(const struct nft_ctx
*ctx
, const struct nft_expr
*expr
)
165 nf_ct_netns_put(ctx
->net
, NFPROTO_IPV6
);
168 static struct nft_expr_type nft_redir_ipv6_type
;
169 static const struct nft_expr_ops nft_redir_ipv6_ops
= {
170 .type
= &nft_redir_ipv6_type
,
171 .size
= NFT_EXPR_SIZE(sizeof(struct nft_redir
)),
172 .eval
= nft_redir_eval
,
173 .init
= nft_redir_init
,
174 .destroy
= nft_redir_ipv6_destroy
,
175 .dump
= nft_redir_dump
,
176 .validate
= nft_redir_validate
,
177 .reduce
= NFT_REDUCE_READONLY
,
180 static struct nft_expr_type nft_redir_ipv6_type __read_mostly
= {
181 .family
= NFPROTO_IPV6
,
183 .ops
= &nft_redir_ipv6_ops
,
184 .policy
= nft_redir_policy
,
185 .maxattr
= NFTA_REDIR_MAX
,
186 .owner
= THIS_MODULE
,
190 #ifdef CONFIG_NF_TABLES_INET
192 nft_redir_inet_destroy(const struct nft_ctx
*ctx
, const struct nft_expr
*expr
)
194 nf_ct_netns_put(ctx
->net
, NFPROTO_INET
);
197 static struct nft_expr_type nft_redir_inet_type
;
198 static const struct nft_expr_ops nft_redir_inet_ops
= {
199 .type
= &nft_redir_inet_type
,
200 .size
= NFT_EXPR_SIZE(sizeof(struct nft_redir
)),
201 .eval
= nft_redir_eval
,
202 .init
= nft_redir_init
,
203 .destroy
= nft_redir_inet_destroy
,
204 .dump
= nft_redir_dump
,
205 .validate
= nft_redir_validate
,
206 .reduce
= NFT_REDUCE_READONLY
,
209 static struct nft_expr_type nft_redir_inet_type __read_mostly
= {
210 .family
= NFPROTO_INET
,
212 .ops
= &nft_redir_inet_ops
,
213 .policy
= nft_redir_policy
,
214 .maxattr
= NFTA_REDIR_MAX
,
215 .owner
= THIS_MODULE
,
218 static int __init
nft_redir_module_init_inet(void)
220 return nft_register_expr(&nft_redir_inet_type
);
223 static inline int nft_redir_module_init_inet(void) { return 0; }
226 static int __init
nft_redir_module_init(void)
228 int ret
= nft_register_expr(&nft_redir_ipv4_type
);
233 #ifdef CONFIG_NF_TABLES_IPV6
234 ret
= nft_register_expr(&nft_redir_ipv6_type
);
236 nft_unregister_expr(&nft_redir_ipv4_type
);
241 ret
= nft_redir_module_init_inet();
243 nft_unregister_expr(&nft_redir_ipv4_type
);
244 #ifdef CONFIG_NF_TABLES_IPV6
245 nft_unregister_expr(&nft_redir_ipv6_type
);
253 static void __exit
nft_redir_module_exit(void)
255 nft_unregister_expr(&nft_redir_ipv4_type
);
256 #ifdef CONFIG_NF_TABLES_IPV6
257 nft_unregister_expr(&nft_redir_ipv6_type
);
259 #ifdef CONFIG_NF_TABLES_INET
260 nft_unregister_expr(&nft_redir_inet_type
);
264 module_init(nft_redir_module_init
);
265 module_exit(nft_redir_module_exit
);
267 MODULE_LICENSE("GPL");
268 MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo@debian.org>");
269 MODULE_ALIAS_NFT_EXPR("redir");
270 MODULE_DESCRIPTION("Netfilter nftables redirect support");