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.
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 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
26 #define XT_SOCKET_HAVE_CONNTRACK 1
27 #include <net/netfilter/nf_conntrack.h>
31 extract_icmp_fields(const struct sk_buff
*skb
,
38 unsigned int outside_hdrlen
= ip_hdrlen(skb
);
39 struct iphdr
*inside_iph
, _inside_iph
;
40 struct icmphdr
*icmph
, _icmph
;
41 __be16
*ports
, _ports
[2];
43 icmph
= skb_header_pointer(skb
, outside_hdrlen
,
44 sizeof(_icmph
), &_icmph
);
48 switch (icmph
->type
) {
49 case ICMP_DEST_UNREACH
:
50 case ICMP_SOURCE_QUENCH
:
52 case ICMP_TIME_EXCEEDED
:
53 case ICMP_PARAMETERPROB
:
59 inside_iph
= skb_header_pointer(skb
, outside_hdrlen
+
60 sizeof(struct icmphdr
),
61 sizeof(_inside_iph
), &_inside_iph
);
62 if (inside_iph
== NULL
)
65 if (inside_iph
->protocol
!= IPPROTO_TCP
&&
66 inside_iph
->protocol
!= IPPROTO_UDP
)
69 ports
= skb_header_pointer(skb
, outside_hdrlen
+
70 sizeof(struct icmphdr
) +
71 (inside_iph
->ihl
<< 2),
72 sizeof(_ports
), &_ports
);
76 /* the inside IP packet is the one quoted from our side, thus
77 * its saddr is the local address */
78 *protocol
= inside_iph
->protocol
;
79 *laddr
= inside_iph
->saddr
;
81 *raddr
= inside_iph
->daddr
;
89 socket_mt(const struct sk_buff
*skb
, const struct xt_match_param
*par
)
91 const struct iphdr
*iph
= ip_hdr(skb
);
92 struct udphdr _hdr
, *hp
= NULL
;
97 #ifdef XT_SOCKET_HAVE_CONNTRACK
98 struct nf_conn
const *ct
;
99 enum ip_conntrack_info ctinfo
;
102 if (iph
->protocol
== IPPROTO_UDP
|| iph
->protocol
== IPPROTO_TCP
) {
103 hp
= skb_header_pointer(skb
, ip_hdrlen(skb
),
104 sizeof(_hdr
), &_hdr
);
108 protocol
= iph
->protocol
;
114 } else if (iph
->protocol
== IPPROTO_ICMP
) {
115 if (extract_icmp_fields(skb
, &protocol
, &saddr
, &daddr
,
122 #ifdef XT_SOCKET_HAVE_CONNTRACK
123 /* Do the lookup with the original socket address in case this is a
124 * reply packet of an established SNAT-ted connection. */
126 ct
= nf_ct_get(skb
, &ctinfo
);
127 if (ct
&& (ct
!= &nf_conntrack_untracked
) &&
128 ((iph
->protocol
!= IPPROTO_ICMP
&&
129 ctinfo
== IP_CT_IS_REPLY
+ IP_CT_ESTABLISHED
) ||
130 (iph
->protocol
== IPPROTO_ICMP
&&
131 ctinfo
== IP_CT_IS_REPLY
+ IP_CT_RELATED
)) &&
132 (ct
->status
& IPS_SRC_NAT_DONE
)) {
134 daddr
= ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
.src
.u3
.ip
;
135 dport
= (iph
->protocol
== IPPROTO_TCP
) ?
136 ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
.src
.u
.tcp
.port
:
137 ct
->tuplehash
[IP_CT_DIR_ORIGINAL
].tuple
.src
.u
.udp
.port
;
141 sk
= nf_tproxy_get_sock_v4(dev_net(skb
->dev
), protocol
,
142 saddr
, daddr
, sport
, dport
, par
->in
, false);
144 bool wildcard
= (sk
->sk_state
!= TCP_TIME_WAIT
&& inet_sk(sk
)->rcv_saddr
== 0);
146 nf_tproxy_put_sock(sk
);
151 pr_debug("socket match: proto %u %08x:%u -> %08x:%u "
152 "(orig %08x:%u) sock %p\n",
153 protocol
, ntohl(saddr
), ntohs(sport
),
154 ntohl(daddr
), ntohs(dport
),
155 ntohl(iph
->daddr
), hp
? ntohs(hp
->dest
) : 0, sk
);
160 static struct xt_match socket_mt_reg __read_mostly
= {
164 .hooks
= 1 << NF_INET_PRE_ROUTING
,
168 static int __init
socket_mt_init(void)
170 nf_defrag_ipv4_enable();
171 return xt_register_match(&socket_mt_reg
);
174 static void __exit
socket_mt_exit(void)
176 xt_unregister_match(&socket_mt_reg
);
179 module_init(socket_mt_init
);
180 module_exit(socket_mt_exit
);
182 MODULE_LICENSE("GPL");
183 MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler");
184 MODULE_DESCRIPTION("x_tables socket match module");
185 MODULE_ALIAS("ipt_socket");