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 #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> 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_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) 26 #define XT_SOCKET_HAVE_IPV6 1 27 #include <linux/netfilter_ipv6/ip6_tables.h> 28 #include <net/netfilter/ipv6/nf_defrag_ipv6.h> 29 #endif 30 31 #include <linux/netfilter/xt_socket.h> 32 33 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) 34 #define XT_SOCKET_HAVE_CONNTRACK 1 35 #include <net/netfilter/nf_conntrack.h> 36 #endif 37 38 static void 39 xt_socket_put_sk(struct sock *sk) 40 { 41 if (sk->sk_state == TCP_TIME_WAIT) 42 inet_twsk_put(inet_twsk(sk)); 43 else 44 sock_put(sk); 45 } 46 47 static int 48 extract_icmp4_fields(const struct sk_buff *skb, 49 u8 *protocol, 50 __be32 *raddr, 51 __be32 *laddr, 52 __be16 *rport, 53 __be16 *lport) 54 { 55 unsigned int outside_hdrlen = ip_hdrlen(skb); 56 struct iphdr *inside_iph, _inside_iph; 57 struct icmphdr *icmph, _icmph; 58 __be16 *ports, _ports[2]; 59 60 icmph = skb_header_pointer(skb, outside_hdrlen, 61 sizeof(_icmph), &_icmph); 62 if (icmph == NULL) 63 return 1; 64 65 switch (icmph->type) { 66 case ICMP_DEST_UNREACH: 67 case ICMP_SOURCE_QUENCH: 68 case ICMP_REDIRECT: 69 case ICMP_TIME_EXCEEDED: 70 case ICMP_PARAMETERPROB: 71 break; 72 default: 73 return 1; 74 } 75 76 inside_iph = skb_header_pointer(skb, outside_hdrlen + 77 sizeof(struct icmphdr), 78 sizeof(_inside_iph), &_inside_iph); 79 if (inside_iph == NULL) 80 return 1; 81 82 if (inside_iph->protocol != IPPROTO_TCP && 83 inside_iph->protocol != IPPROTO_UDP) 84 return 1; 85 86 ports = skb_header_pointer(skb, outside_hdrlen + 87 sizeof(struct icmphdr) + 88 (inside_iph->ihl << 2), 89 sizeof(_ports), &_ports); 90 if (ports == NULL) 91 return 1; 92 93 /* the inside IP packet is the one quoted from our side, thus 94 * its saddr is the local address */ 95 *protocol = inside_iph->protocol; 96 *laddr = inside_iph->saddr; 97 *lport = ports[0]; 98 *raddr = inside_iph->daddr; 99 *rport = ports[1]; 100 101 return 0; 102 } 103 104 static bool 105 socket_match(const struct sk_buff *skb, struct xt_action_param *par, 106 const struct xt_socket_mtinfo1 *info) 107 { 108 const struct iphdr *iph = ip_hdr(skb); 109 struct udphdr _hdr, *hp = NULL; 110 struct sock *sk; 111 __be32 daddr, saddr; 112 __be16 dport, sport; 113 u8 protocol; 114 #ifdef XT_SOCKET_HAVE_CONNTRACK 115 struct nf_conn const *ct; 116 enum ip_conntrack_info ctinfo; 117 #endif 118 119 if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_TCP) { 120 hp = skb_header_pointer(skb, ip_hdrlen(skb), 121 sizeof(_hdr), &_hdr); 122 if (hp == NULL) 123 return false; 124 125 protocol = iph->protocol; 126 saddr = iph->saddr; 127 sport = hp->source; 128 daddr = iph->daddr; 129 dport = hp->dest; 130 131 } else if (iph->protocol == IPPROTO_ICMP) { 132 if (extract_icmp4_fields(skb, &protocol, &saddr, &daddr, 133 &sport, &dport)) 134 return false; 135 } else { 136 return false; 137 } 138 139 #ifdef XT_SOCKET_HAVE_CONNTRACK 140 /* Do the lookup with the original socket address in case this is a 141 * reply packet of an established SNAT-ted connection. */ 142 143 ct = nf_ct_get(skb, &ctinfo); 144 if (ct && !nf_ct_is_untracked(ct) && 145 ((iph->protocol != IPPROTO_ICMP && 146 ctinfo == IP_CT_IS_REPLY + IP_CT_ESTABLISHED) || 147 (iph->protocol == IPPROTO_ICMP && 148 ctinfo == IP_CT_IS_REPLY + IP_CT_RELATED)) && 149 (ct->status & IPS_SRC_NAT_DONE)) { 150 151 daddr = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; 152 dport = (iph->protocol == IPPROTO_TCP) ? 153 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.tcp.port : 154 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port; 155 } 156 #endif 157 158 sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol, 159 saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY); 160 if (sk != NULL) { 161 bool wildcard; 162 bool transparent = true; 163 164 /* Ignore sockets listening on INADDR_ANY */ 165 wildcard = (sk->sk_state != TCP_TIME_WAIT && 166 inet_sk(sk)->inet_rcv_saddr == 0); 167 168 /* Ignore non-transparent sockets, 169 if XT_SOCKET_TRANSPARENT is used */ 170 if (info && info->flags & XT_SOCKET_TRANSPARENT) 171 transparent = ((sk->sk_state != TCP_TIME_WAIT && 172 inet_sk(sk)->transparent) || 173 (sk->sk_state == TCP_TIME_WAIT && 174 inet_twsk(sk)->tw_transparent)); 175 176 xt_socket_put_sk(sk); 177 178 if (wildcard || !transparent) 179 sk = NULL; 180 } 181 182 pr_debug("proto %hhu %pI4:%hu -> %pI4:%hu (orig %pI4:%hu) sock %p\n", 183 protocol, &saddr, ntohs(sport), 184 &daddr, ntohs(dport), 185 &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); 186 187 return (sk != NULL); 188 } 189 190 static bool 191 socket_mt4_v0(const struct sk_buff *skb, struct xt_action_param *par) 192 { 193 return socket_match(skb, par, NULL); 194 } 195 196 static bool 197 socket_mt4_v1(const struct sk_buff *skb, struct xt_action_param *par) 198 { 199 return socket_match(skb, par, par->matchinfo); 200 } 201 202 #ifdef XT_SOCKET_HAVE_IPV6 203 204 static int 205 extract_icmp6_fields(const struct sk_buff *skb, 206 unsigned int outside_hdrlen, 207 int *protocol, 208 struct in6_addr **raddr, 209 struct in6_addr **laddr, 210 __be16 *rport, 211 __be16 *lport) 212 { 213 struct ipv6hdr *inside_iph, _inside_iph; 214 struct icmp6hdr *icmph, _icmph; 215 __be16 *ports, _ports[2]; 216 u8 inside_nexthdr; 217 int inside_hdrlen; 218 219 icmph = skb_header_pointer(skb, outside_hdrlen, 220 sizeof(_icmph), &_icmph); 221 if (icmph == NULL) 222 return 1; 223 224 if (icmph->icmp6_type & ICMPV6_INFOMSG_MASK) 225 return 1; 226 227 inside_iph = skb_header_pointer(skb, outside_hdrlen + sizeof(_icmph), sizeof(_inside_iph), &_inside_iph); 228 if (inside_iph == NULL) 229 return 1; 230 inside_nexthdr = inside_iph->nexthdr; 231 232 inside_hdrlen = ipv6_skip_exthdr(skb, outside_hdrlen + sizeof(_icmph) + sizeof(_inside_iph), &inside_nexthdr); 233 if (inside_hdrlen < 0) 234 return 1; /* hjm: Packet has no/incomplete transport layer headers. */ 235 236 if (inside_nexthdr != IPPROTO_TCP && 237 inside_nexthdr != IPPROTO_UDP) 238 return 1; 239 240 ports = skb_header_pointer(skb, inside_hdrlen, 241 sizeof(_ports), &_ports); 242 if (ports == NULL) 243 return 1; 244 245 /* the inside IP packet is the one quoted from our side, thus 246 * its saddr is the local address */ 247 *protocol = inside_nexthdr; 248 *laddr = &inside_iph->saddr; 249 *lport = ports[0]; 250 *raddr = &inside_iph->daddr; 251 *rport = ports[1]; 252 253 return 0; 254 } 255 256 static bool 257 socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) 258 { 259 struct ipv6hdr *iph = ipv6_hdr(skb); 260 struct udphdr _hdr, *hp = NULL; 261 struct sock *sk; 262 struct in6_addr *daddr, *saddr; 263 __be16 dport, sport; 264 int thoff, tproto; 265 const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo; 266 267 tproto = ipv6_find_hdr(skb, &thoff, -1, NULL); 268 if (tproto < 0) { 269 pr_debug("unable to find transport header in IPv6 packet, dropping\n"); 270 return NF_DROP; 271 } 272 273 if (tproto == IPPROTO_UDP || tproto == IPPROTO_TCP) { 274 hp = skb_header_pointer(skb, thoff, 275 sizeof(_hdr), &_hdr); 276 if (hp == NULL) 277 return false; 278 279 saddr = &iph->saddr; 280 sport = hp->source; 281 daddr = &iph->daddr; 282 dport = hp->dest; 283 284 } else if (tproto == IPPROTO_ICMPV6) { 285 if (extract_icmp6_fields(skb, thoff, &tproto, &saddr, &daddr, 286 &sport, &dport)) 287 return false; 288 } else { 289 return false; 290 } 291 292 sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, 293 saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY); 294 if (sk != NULL) { 295 bool wildcard; 296 bool transparent = true; 297 298 /* Ignore sockets listening on INADDR_ANY */ 299 wildcard = (sk->sk_state != TCP_TIME_WAIT && 300 ipv6_addr_any(&inet6_sk(sk)->rcv_saddr)); 301 302 /* Ignore non-transparent sockets, 303 if XT_SOCKET_TRANSPARENT is used */ 304 if (info && info->flags & XT_SOCKET_TRANSPARENT) 305 transparent = ((sk->sk_state != TCP_TIME_WAIT && 306 inet_sk(sk)->transparent) || 307 (sk->sk_state == TCP_TIME_WAIT && 308 inet_twsk(sk)->tw_transparent)); 309 310 xt_socket_put_sk(sk); 311 312 if (wildcard || !transparent) 313 sk = NULL; 314 } 315 316 pr_debug("proto %hhd %pI6:%hu -> %pI6:%hu " 317 "(orig %pI6:%hu) sock %p\n", 318 tproto, saddr, ntohs(sport), 319 daddr, ntohs(dport), 320 &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); 321 322 return (sk != NULL); 323 } 324 #endif 325 326 static struct xt_match socket_mt_reg[] __read_mostly = { 327 { 328 .name = "socket", 329 .revision = 0, 330 .family = NFPROTO_IPV4, 331 .match = socket_mt4_v0, 332 .hooks = (1 << NF_INET_PRE_ROUTING) | 333 (1 << NF_INET_LOCAL_IN), 334 .me = THIS_MODULE, 335 }, 336 { 337 .name = "socket", 338 .revision = 1, 339 .family = NFPROTO_IPV4, 340 .match = socket_mt4_v1, 341 .matchsize = sizeof(struct xt_socket_mtinfo1), 342 .hooks = (1 << NF_INET_PRE_ROUTING) | 343 (1 << NF_INET_LOCAL_IN), 344 .me = THIS_MODULE, 345 }, 346 #ifdef XT_SOCKET_HAVE_IPV6 347 { 348 .name = "socket", 349 .revision = 1, 350 .family = NFPROTO_IPV6, 351 .match = socket_mt6_v1, 352 .matchsize = sizeof(struct xt_socket_mtinfo1), 353 .hooks = (1 << NF_INET_PRE_ROUTING) | 354 (1 << NF_INET_LOCAL_IN), 355 .me = THIS_MODULE, 356 }, 357 #endif 358 }; 359 360 static int __init socket_mt_init(void) 361 { 362 nf_defrag_ipv4_enable(); 363 #ifdef XT_SOCKET_HAVE_IPV6 364 nf_defrag_ipv6_enable(); 365 #endif 366 367 return xt_register_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg)); 368 } 369 370 static void __exit socket_mt_exit(void) 371 { 372 xt_unregister_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg)); 373 } 374 375 module_init(socket_mt_init); 376 module_exit(socket_mt_exit); 377 378 MODULE_LICENSE("GPL"); 379 MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler"); 380 MODULE_DESCRIPTION("x_tables socket match module"); 381 MODULE_ALIAS("ipt_socket"); 382 MODULE_ALIAS("ip6t_socket"); 383