Commit | Line | Data |
---|---|---|
136cdc71 KK |
1 | /* |
2 | * Transparent proxy support for Linux/iptables | |
3 | * | |
4 | * Copyright (C) 2007-2008 BalaBit IT Ltd. | |
5 | * Author: Krisztian Kovacs | |
6 | * | |
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. | |
10 | * | |
11 | */ | |
12 | ||
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> | |
17 | #include <net/tcp.h> | |
18 | #include <net/udp.h> | |
19 | #include <net/icmp.h> | |
20 | #include <net/sock.h> | |
21 | #include <net/inet_sock.h> | |
22 | #include <net/netfilter/nf_tproxy_core.h> | |
23 | #include <net/netfilter/ipv4/nf_defrag_ipv4.h> | |
24 | ||
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> | |
28 | #endif | |
29 | ||
30 | static int | |
31 | extract_icmp_fields(const struct sk_buff *skb, | |
32 | u8 *protocol, | |
33 | __be32 *raddr, | |
34 | __be32 *laddr, | |
35 | __be16 *rport, | |
36 | __be16 *lport) | |
37 | { | |
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]; | |
42 | ||
43 | icmph = skb_header_pointer(skb, outside_hdrlen, | |
44 | sizeof(_icmph), &_icmph); | |
45 | if (icmph == NULL) | |
46 | return 1; | |
47 | ||
48 | switch (icmph->type) { | |
49 | case ICMP_DEST_UNREACH: | |
50 | case ICMP_SOURCE_QUENCH: | |
51 | case ICMP_REDIRECT: | |
52 | case ICMP_TIME_EXCEEDED: | |
53 | case ICMP_PARAMETERPROB: | |
54 | break; | |
55 | default: | |
56 | return 1; | |
57 | } | |
58 | ||
59 | inside_iph = skb_header_pointer(skb, outside_hdrlen + | |
60 | sizeof(struct icmphdr), | |
61 | sizeof(_inside_iph), &_inside_iph); | |
62 | if (inside_iph == NULL) | |
63 | return 1; | |
64 | ||
65 | if (inside_iph->protocol != IPPROTO_TCP && | |
66 | inside_iph->protocol != IPPROTO_UDP) | |
67 | return 1; | |
68 | ||
69 | ports = skb_header_pointer(skb, outside_hdrlen + | |
70 | sizeof(struct icmphdr) + | |
71 | (inside_iph->ihl << 2), | |
72 | sizeof(_ports), &_ports); | |
73 | if (ports == NULL) | |
74 | return 1; | |
75 | ||
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; | |
80 | *lport = ports[0]; | |
81 | *raddr = inside_iph->daddr; | |
82 | *rport = ports[1]; | |
83 | ||
84 | return 0; | |
85 | } | |
86 | ||
87 | ||
88 | static bool | |
f7108a20 | 89 | socket_mt(const struct sk_buff *skb, const struct xt_match_param *par) |
136cdc71 KK |
90 | { |
91 | const struct iphdr *iph = ip_hdr(skb); | |
92 | struct udphdr _hdr, *hp = NULL; | |
93 | struct sock *sk; | |
94 | __be32 daddr, saddr; | |
95 | __be16 dport, sport; | |
96 | u8 protocol; | |
97 | #ifdef XT_SOCKET_HAVE_CONNTRACK | |
98 | struct nf_conn const *ct; | |
99 | enum ip_conntrack_info ctinfo; | |
100 | #endif | |
101 | ||
102 | if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_TCP) { | |
103 | hp = skb_header_pointer(skb, ip_hdrlen(skb), | |
104 | sizeof(_hdr), &_hdr); | |
105 | if (hp == NULL) | |
106 | return false; | |
107 | ||
108 | protocol = iph->protocol; | |
109 | saddr = iph->saddr; | |
110 | sport = hp->source; | |
111 | daddr = iph->daddr; | |
112 | dport = hp->dest; | |
113 | ||
114 | } else if (iph->protocol == IPPROTO_ICMP) { | |
115 | if (extract_icmp_fields(skb, &protocol, &saddr, &daddr, | |
116 | &sport, &dport)) | |
117 | return false; | |
118 | } else { | |
119 | return false; | |
120 | } | |
121 | ||
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. */ | |
125 | ||
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)) { | |
133 | ||
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; | |
138 | } | |
139 | #endif | |
140 | ||
141 | sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol, | |
f7108a20 | 142 | saddr, daddr, sport, dport, par->in, false); |
136cdc71 | 143 | if (sk != NULL) { |
c49b9f29 | 144 | bool wildcard = (sk->sk_state != TCP_TIME_WAIT && inet_sk(sk)->rcv_saddr == 0); |
136cdc71 KK |
145 | |
146 | nf_tproxy_put_sock(sk); | |
147 | if (wildcard) | |
148 | sk = NULL; | |
149 | } | |
150 | ||
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); | |
156 | ||
157 | return (sk != NULL); | |
158 | } | |
159 | ||
160 | static struct xt_match socket_mt_reg __read_mostly = { | |
161 | .name = "socket", | |
162 | .family = AF_INET, | |
163 | .match = socket_mt, | |
164 | .hooks = 1 << NF_INET_PRE_ROUTING, | |
165 | .me = THIS_MODULE, | |
166 | }; | |
167 | ||
168 | static int __init socket_mt_init(void) | |
169 | { | |
170 | nf_defrag_ipv4_enable(); | |
171 | return xt_register_match(&socket_mt_reg); | |
172 | } | |
173 | ||
174 | static void __exit socket_mt_exit(void) | |
175 | { | |
176 | xt_unregister_match(&socket_mt_reg); | |
177 | } | |
178 | ||
179 | module_init(socket_mt_init); | |
180 | module_exit(socket_mt_exit); | |
181 | ||
182 | MODULE_LICENSE("GPL"); | |
183 | MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler"); | |
184 | MODULE_DESCRIPTION("x_tables socket match module"); | |
185 | MODULE_ALIAS("ipt_socket"); |