2 * Transparent proxy support for Linux/iptables
4 * Copyright (C) 2007-2008 BalaBit IT Ltd.
5 * Author: Krisztian Kovacs
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
12 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13 #include <linux/module.h>
14 #include <linux/skbuff.h>
15 #include <linux/netfilter/x_tables.h>
16 #include <linux/netfilter_ipv4/ip_tables.h>
21 #include <net/inet_sock.h>
22 #include <net/netfilter/nf_tproxy_core.h>
23 #include <net/netfilter/ipv4/nf_defrag_ipv4.h>
25 #include <linux/netfilter/xt_socket.h>
27 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
28 #define XT_SOCKET_HAVE_CONNTRACK 1
29 #include <net/netfilter/nf_conntrack.h>
33 extract_icmp_fields(const struct sk_buff
*skb
,
40 unsigned int outside_hdrlen
= ip_hdrlen(skb
);
41 struct iphdr
*inside_iph
, _inside_iph
;
42 struct icmphdr
*icmph
, _icmph
;
43 __be16
*ports
, _ports
[2];
45 icmph
= skb_header_pointer(skb
, outside_hdrlen
,
46 sizeof(_icmph
), &_icmph
);
50 switch (icmph
->type
) {
51 case ICMP_DEST_UNREACH
:
52 case ICMP_SOURCE_QUENCH
:
54 case ICMP_TIME_EXCEEDED
:
55 case ICMP_PARAMETERPROB
:
61 inside_iph
= skb_header_pointer(skb
, outside_hdrlen
+
62 sizeof(struct icmphdr
),
63 sizeof(_inside_iph
), &_inside_iph
);
64 if (inside_iph
== NULL
)
67 if (inside_iph
->protocol
!= IPPROTO_TCP
&&
68 inside_iph
->protocol
!= IPPROTO_UDP
)
71 ports
= skb_header_pointer(skb
, outside_hdrlen
+
72 sizeof(struct icmphdr
) +
73 (inside_iph
->ihl
<< 2),
74 sizeof(_ports
), &_ports
);
78 /* the inside IP packet is the one quoted from our side, thus
79 * its saddr is the local address */
80 *protocol
= inside_iph
->protocol
;
81 *laddr
= inside_iph
->saddr
;
83 *raddr
= inside_iph
->daddr
;
91 socket_match(const struct sk_buff
*skb
, struct xt_action_param
*par
,
92 const struct xt_socket_mtinfo1
*info
)
94 const struct iphdr
*iph
= ip_hdr(skb
);
95 struct udphdr _hdr
, *hp
= NULL
;
100 #ifdef XT_SOCKET_HAVE_CONNTRACK
101 struct nf_conn
const *ct
;
102 enum ip_conntrack_info ctinfo
;
105 if (iph
->protocol
== IPPROTO_UDP
|| iph
->protocol
== IPPROTO_TCP
) {
106 hp
= skb_header_pointer(skb
, ip_hdrlen(skb
),
107 sizeof(_hdr
), &_hdr
);
111 protocol
= iph
->protocol
;
117 } else if (iph
->protocol
== IPPROTO_ICMP
) {
118 if (extract_icmp_fields(skb
, &protocol
, &saddr
, &daddr
,
125 #ifdef XT_SOCKET_HAVE_CONNTRACK
126 /* Do the lookup with the original socket address in case this is a
127 * reply packet of an established SNAT-ted connection. */
129 ct
= nf_ct_get(skb
, &ctinfo
);
130 if (ct
&& !nf_ct_is_untracked(ct
) &&
131 ((iph
->protocol
!= IPPROTO_ICMP
&&
132 ctinfo
== IP_CT_IS_REPLY
+ IP_CT_ESTABLISHED
) ||
133 (iph
->protocol
== IPPROTO_ICMP
&&
134 ctinfo
== IP_CT_IS_REPLY
+ IP_CT_RELATED
)) &&
135 (ct
->status
& IPS_SRC_NAT_DONE
)) {
137 daddr
= ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
.src
.u3
.ip
;
138 dport
= (iph
->protocol
== IPPROTO_TCP
) ?
139 ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
.src
.u
.tcp
.port
:
140 ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
.src
.u
.udp
.port
;
144 sk
= nf_tproxy_get_sock_v4(dev_net(skb
->dev
), protocol
,
145 saddr
, daddr
, sport
, dport
, par
->in
, false);
148 bool transparent
= true;
150 /* Ignore sockets listening on INADDR_ANY */
151 wildcard
= (sk
->sk_state
!= TCP_TIME_WAIT
&&
152 inet_sk(sk
)->inet_rcv_saddr
== 0);
154 /* Ignore non-transparent sockets,
155 if XT_SOCKET_TRANSPARENT is used */
156 if (info
&& info
->flags
& XT_SOCKET_TRANSPARENT
)
157 transparent
= ((sk
->sk_state
!= TCP_TIME_WAIT
&&
158 inet_sk(sk
)->transparent
) ||
159 (sk
->sk_state
== TCP_TIME_WAIT
&&
160 inet_twsk(sk
)->tw_transparent
));
162 nf_tproxy_put_sock(sk
);
164 if (wildcard
|| !transparent
)
168 pr_debug("proto %u %08x:%u -> %08x:%u (orig %08x:%u) sock %p\n",
169 protocol
, ntohl(saddr
), ntohs(sport
),
170 ntohl(daddr
), ntohs(dport
),
171 ntohl(iph
->daddr
), hp
? ntohs(hp
->dest
) : 0, sk
);
177 socket_mt_v0(const struct sk_buff
*skb
, struct xt_action_param
*par
)
179 return socket_match(skb
, par
, NULL
);
183 socket_mt_v1(const struct sk_buff
*skb
, struct xt_action_param
*par
)
185 return socket_match(skb
, par
, par
->matchinfo
);
188 static struct xt_match socket_mt_reg
[] __read_mostly
= {
192 .family
= NFPROTO_IPV4
,
193 .match
= socket_mt_v0
,
194 .hooks
= (1 << NF_INET_PRE_ROUTING
) |
195 (1 << NF_INET_LOCAL_IN
),
201 .family
= NFPROTO_IPV4
,
202 .match
= socket_mt_v1
,
203 .matchsize
= sizeof(struct xt_socket_mtinfo1
),
204 .hooks
= (1 << NF_INET_PRE_ROUTING
) |
205 (1 << NF_INET_LOCAL_IN
),
210 static int __init
socket_mt_init(void)
212 nf_defrag_ipv4_enable();
213 return xt_register_matches(socket_mt_reg
, ARRAY_SIZE(socket_mt_reg
));
216 static void __exit
socket_mt_exit(void)
218 xt_unregister_matches(socket_mt_reg
, ARRAY_SIZE(socket_mt_reg
));
221 module_init(socket_mt_init
);
222 module_exit(socket_mt_exit
);
224 MODULE_LICENSE("GPL");
225 MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler");
226 MODULE_DESCRIPTION("x_tables socket match module");
227 MODULE_ALIAS("ipt_socket");