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_tproxy.h>
7 #include <net/inet_sock.h>
9 #include <linux/if_ether.h>
10 #include <net/netfilter/ipv4/nf_defrag_ipv4.h>
11 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
12 #include <net/netfilter/ipv6/nf_defrag_ipv6.h>
16 enum nft_registers sreg_addr
:8;
17 enum nft_registers sreg_port
:8;
21 static void nft_tproxy_eval_v4(const struct nft_expr
*expr
,
22 struct nft_regs
*regs
,
23 const struct nft_pktinfo
*pkt
)
25 const struct nft_tproxy
*priv
= nft_expr_priv(expr
);
26 struct sk_buff
*skb
= pkt
->skb
;
27 const struct iphdr
*iph
= ip_hdr(skb
);
28 struct udphdr _hdr
, *hp
;
33 hp
= skb_header_pointer(skb
, ip_hdrlen(skb
), sizeof(_hdr
), &_hdr
);
35 regs
->verdict
.code
= NFT_BREAK
;
39 /* check if there's an ongoing connection on the packet addresses, this
40 * happens if the redirect already happened and the current packet
41 * belongs to an already established connection
43 sk
= nf_tproxy_get_sock_v4(nft_net(pkt
), skb
, iph
->protocol
,
44 iph
->saddr
, iph
->daddr
,
46 skb
->dev
, NF_TPROXY_LOOKUP_ESTABLISHED
);
49 taddr
= regs
->data
[priv
->sreg_addr
];
50 taddr
= nf_tproxy_laddr4(skb
, taddr
, iph
->daddr
);
53 tport
= regs
->data
[priv
->sreg_port
];
57 /* UDP has no TCP_TIME_WAIT state, so we never enter here */
58 if (sk
&& sk
->sk_state
== TCP_TIME_WAIT
) {
59 /* reopening a TIME_WAIT connection needs special handling */
60 sk
= nf_tproxy_handle_time_wait4(nft_net(pkt
), skb
, taddr
, tport
, sk
);
62 /* no, there's no established connection, check if
63 * there's a listener on the redirected addr/port
65 sk
= nf_tproxy_get_sock_v4(nft_net(pkt
), skb
, iph
->protocol
,
68 skb
->dev
, NF_TPROXY_LOOKUP_LISTENER
);
71 if (sk
&& nf_tproxy_sk_is_transparent(sk
))
72 nf_tproxy_assign_sock(skb
, sk
);
74 regs
->verdict
.code
= NFT_BREAK
;
77 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
78 static void nft_tproxy_eval_v6(const struct nft_expr
*expr
,
79 struct nft_regs
*regs
,
80 const struct nft_pktinfo
*pkt
)
82 const struct nft_tproxy
*priv
= nft_expr_priv(expr
);
83 struct sk_buff
*skb
= pkt
->skb
;
84 const struct ipv6hdr
*iph
= ipv6_hdr(skb
);
85 struct in6_addr taddr
;
86 int thoff
= pkt
->xt
.thoff
;
87 struct udphdr _hdr
, *hp
;
92 memset(&taddr
, 0, sizeof(taddr
));
94 if (!pkt
->tprot_set
) {
95 regs
->verdict
.code
= NFT_BREAK
;
100 hp
= skb_header_pointer(skb
, thoff
, sizeof(_hdr
), &_hdr
);
102 regs
->verdict
.code
= NFT_BREAK
;
106 /* check if there's an ongoing connection on the packet addresses, this
107 * happens if the redirect already happened and the current packet
108 * belongs to an already established connection
110 sk
= nf_tproxy_get_sock_v6(nft_net(pkt
), skb
, thoff
, l4proto
,
111 &iph
->saddr
, &iph
->daddr
,
112 hp
->source
, hp
->dest
,
113 nft_in(pkt
), NF_TPROXY_LOOKUP_ESTABLISHED
);
116 memcpy(&taddr
, ®s
->data
[priv
->sreg_addr
], sizeof(taddr
));
117 taddr
= *nf_tproxy_laddr6(skb
, &taddr
, &iph
->daddr
);
120 tport
= regs
->data
[priv
->sreg_port
];
124 /* UDP has no TCP_TIME_WAIT state, so we never enter here */
125 if (sk
&& sk
->sk_state
== TCP_TIME_WAIT
) {
126 /* reopening a TIME_WAIT connection needs special handling */
127 sk
= nf_tproxy_handle_time_wait6(skb
, l4proto
, thoff
,
133 /* no there's no established connection, check if
134 * there's a listener on the redirected addr/port
136 sk
= nf_tproxy_get_sock_v6(nft_net(pkt
), skb
, thoff
,
137 l4proto
, &iph
->saddr
, &taddr
,
139 nft_in(pkt
), NF_TPROXY_LOOKUP_LISTENER
);
142 /* NOTE: assign_sock consumes our sk reference */
143 if (sk
&& nf_tproxy_sk_is_transparent(sk
))
144 nf_tproxy_assign_sock(skb
, sk
);
146 regs
->verdict
.code
= NFT_BREAK
;
150 static void nft_tproxy_eval(const struct nft_expr
*expr
,
151 struct nft_regs
*regs
,
152 const struct nft_pktinfo
*pkt
)
154 const struct nft_tproxy
*priv
= nft_expr_priv(expr
);
156 switch (nft_pf(pkt
)) {
158 switch (priv
->family
) {
161 nft_tproxy_eval_v4(expr
, regs
, pkt
);
165 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
167 switch (priv
->family
) {
170 nft_tproxy_eval_v6(expr
, regs
, pkt
);
175 regs
->verdict
.code
= NFT_BREAK
;
178 static const struct nla_policy nft_tproxy_policy
[NFTA_TPROXY_MAX
+ 1] = {
179 [NFTA_TPROXY_FAMILY
] = { .type
= NLA_U32
},
180 [NFTA_TPROXY_REG_ADDR
] = { .type
= NLA_U32
},
181 [NFTA_TPROXY_REG_PORT
] = { .type
= NLA_U32
},
184 static int nft_tproxy_init(const struct nft_ctx
*ctx
,
185 const struct nft_expr
*expr
,
186 const struct nlattr
* const tb
[])
188 struct nft_tproxy
*priv
= nft_expr_priv(expr
);
189 unsigned int alen
= 0;
192 if (!tb
[NFTA_TPROXY_FAMILY
] ||
193 (!tb
[NFTA_TPROXY_REG_ADDR
] && !tb
[NFTA_TPROXY_REG_PORT
]))
196 priv
->family
= ntohl(nla_get_be32(tb
[NFTA_TPROXY_FAMILY
]));
198 switch (ctx
->family
) {
200 if (priv
->family
!= NFPROTO_IPV4
)
203 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
205 if (priv
->family
!= NFPROTO_IPV6
)
215 /* Address is specified but the rule family is not set accordingly */
216 if (priv
->family
== NFPROTO_UNSPEC
&& tb
[NFTA_TPROXY_REG_ADDR
])
219 switch (priv
->family
) {
221 alen
= FIELD_SIZEOF(union nf_inet_addr
, in
);
222 err
= nf_defrag_ipv4_enable(ctx
->net
);
226 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
228 alen
= FIELD_SIZEOF(union nf_inet_addr
, in6
);
229 err
= nf_defrag_ipv6_enable(ctx
->net
);
235 /* No address is specified here */
236 err
= nf_defrag_ipv4_enable(ctx
->net
);
239 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
240 err
= nf_defrag_ipv6_enable(ctx
->net
);
249 if (tb
[NFTA_TPROXY_REG_ADDR
]) {
250 priv
->sreg_addr
= nft_parse_register(tb
[NFTA_TPROXY_REG_ADDR
]);
251 err
= nft_validate_register_load(priv
->sreg_addr
, alen
);
256 if (tb
[NFTA_TPROXY_REG_PORT
]) {
257 priv
->sreg_port
= nft_parse_register(tb
[NFTA_TPROXY_REG_PORT
]);
258 err
= nft_validate_register_load(priv
->sreg_port
, sizeof(u16
));
266 static int nft_tproxy_dump(struct sk_buff
*skb
,
267 const struct nft_expr
*expr
)
269 const struct nft_tproxy
*priv
= nft_expr_priv(expr
);
271 if (nla_put_be32(skb
, NFTA_TPROXY_FAMILY
, htonl(priv
->family
)))
274 if (priv
->sreg_addr
&&
275 nft_dump_register(skb
, NFTA_TPROXY_REG_ADDR
, priv
->sreg_addr
))
278 if (priv
->sreg_port
&&
279 nft_dump_register(skb
, NFTA_TPROXY_REG_PORT
, priv
->sreg_port
))
285 static struct nft_expr_type nft_tproxy_type
;
286 static const struct nft_expr_ops nft_tproxy_ops
= {
287 .type
= &nft_tproxy_type
,
288 .size
= NFT_EXPR_SIZE(sizeof(struct nft_tproxy
)),
289 .eval
= nft_tproxy_eval
,
290 .init
= nft_tproxy_init
,
291 .dump
= nft_tproxy_dump
,
294 static struct nft_expr_type nft_tproxy_type __read_mostly
= {
296 .ops
= &nft_tproxy_ops
,
297 .policy
= nft_tproxy_policy
,
298 .maxattr
= NFTA_TPROXY_MAX
,
299 .owner
= THIS_MODULE
,
302 static int __init
nft_tproxy_module_init(void)
304 return nft_register_expr(&nft_tproxy_type
);
307 static void __exit
nft_tproxy_module_exit(void)
309 nft_unregister_expr(&nft_tproxy_type
);
312 module_init(nft_tproxy_module_init
);
313 module_exit(nft_tproxy_module_exit
);
315 MODULE_LICENSE("GPL");
316 MODULE_AUTHOR("Máté Eckl");
317 MODULE_DESCRIPTION("nf_tables tproxy support module");
318 MODULE_ALIAS_NFT_EXPR("tproxy");