at v3.3-rc2 9.8 kB view raw
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 IS_ENABLED(CONFIG_IP6_NF_IPTABLES) 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 IS_ENABLED(CONFIG_NF_CONNTRACK) 34#define XT_SOCKET_HAVE_CONNTRACK 1 35#include <net/netfilter/nf_conntrack.h> 36#endif 37 38static void 39xt_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 47static int 48extract_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 104static bool 105socket_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_ESTABLISHED_REPLY) || 147 (iph->protocol == IPPROTO_ICMP && 148 ctinfo == IP_CT_RELATED_REPLY)) && 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 190static bool 191socket_mt4_v0(const struct sk_buff *skb, struct xt_action_param *par) 192{ 193 return socket_match(skb, par, NULL); 194} 195 196static bool 197socket_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 204static int 205extract_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 __be16 inside_fragoff; 218 int inside_hdrlen; 219 220 icmph = skb_header_pointer(skb, outside_hdrlen, 221 sizeof(_icmph), &_icmph); 222 if (icmph == NULL) 223 return 1; 224 225 if (icmph->icmp6_type & ICMPV6_INFOMSG_MASK) 226 return 1; 227 228 inside_iph = skb_header_pointer(skb, outside_hdrlen + sizeof(_icmph), sizeof(_inside_iph), &_inside_iph); 229 if (inside_iph == NULL) 230 return 1; 231 inside_nexthdr = inside_iph->nexthdr; 232 233 inside_hdrlen = ipv6_skip_exthdr(skb, outside_hdrlen + sizeof(_icmph) + sizeof(_inside_iph), 234 &inside_nexthdr, &inside_fragoff); 235 if (inside_hdrlen < 0) 236 return 1; /* hjm: Packet has no/incomplete transport layer headers. */ 237 238 if (inside_nexthdr != IPPROTO_TCP && 239 inside_nexthdr != IPPROTO_UDP) 240 return 1; 241 242 ports = skb_header_pointer(skb, inside_hdrlen, 243 sizeof(_ports), &_ports); 244 if (ports == NULL) 245 return 1; 246 247 /* the inside IP packet is the one quoted from our side, thus 248 * its saddr is the local address */ 249 *protocol = inside_nexthdr; 250 *laddr = &inside_iph->saddr; 251 *lport = ports[0]; 252 *raddr = &inside_iph->daddr; 253 *rport = ports[1]; 254 255 return 0; 256} 257 258static bool 259socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) 260{ 261 struct ipv6hdr *iph = ipv6_hdr(skb); 262 struct udphdr _hdr, *hp = NULL; 263 struct sock *sk; 264 struct in6_addr *daddr, *saddr; 265 __be16 dport, sport; 266 int thoff, tproto; 267 const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo; 268 269 tproto = ipv6_find_hdr(skb, &thoff, -1, NULL); 270 if (tproto < 0) { 271 pr_debug("unable to find transport header in IPv6 packet, dropping\n"); 272 return NF_DROP; 273 } 274 275 if (tproto == IPPROTO_UDP || tproto == IPPROTO_TCP) { 276 hp = skb_header_pointer(skb, thoff, 277 sizeof(_hdr), &_hdr); 278 if (hp == NULL) 279 return false; 280 281 saddr = &iph->saddr; 282 sport = hp->source; 283 daddr = &iph->daddr; 284 dport = hp->dest; 285 286 } else if (tproto == IPPROTO_ICMPV6) { 287 if (extract_icmp6_fields(skb, thoff, &tproto, &saddr, &daddr, 288 &sport, &dport)) 289 return false; 290 } else { 291 return false; 292 } 293 294 sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, 295 saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY); 296 if (sk != NULL) { 297 bool wildcard; 298 bool transparent = true; 299 300 /* Ignore sockets listening on INADDR_ANY */ 301 wildcard = (sk->sk_state != TCP_TIME_WAIT && 302 ipv6_addr_any(&inet6_sk(sk)->rcv_saddr)); 303 304 /* Ignore non-transparent sockets, 305 if XT_SOCKET_TRANSPARENT is used */ 306 if (info && info->flags & XT_SOCKET_TRANSPARENT) 307 transparent = ((sk->sk_state != TCP_TIME_WAIT && 308 inet_sk(sk)->transparent) || 309 (sk->sk_state == TCP_TIME_WAIT && 310 inet_twsk(sk)->tw_transparent)); 311 312 xt_socket_put_sk(sk); 313 314 if (wildcard || !transparent) 315 sk = NULL; 316 } 317 318 pr_debug("proto %hhd %pI6:%hu -> %pI6:%hu " 319 "(orig %pI6:%hu) sock %p\n", 320 tproto, saddr, ntohs(sport), 321 daddr, ntohs(dport), 322 &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); 323 324 return (sk != NULL); 325} 326#endif 327 328static struct xt_match socket_mt_reg[] __read_mostly = { 329 { 330 .name = "socket", 331 .revision = 0, 332 .family = NFPROTO_IPV4, 333 .match = socket_mt4_v0, 334 .hooks = (1 << NF_INET_PRE_ROUTING) | 335 (1 << NF_INET_LOCAL_IN), 336 .me = THIS_MODULE, 337 }, 338 { 339 .name = "socket", 340 .revision = 1, 341 .family = NFPROTO_IPV4, 342 .match = socket_mt4_v1, 343 .matchsize = sizeof(struct xt_socket_mtinfo1), 344 .hooks = (1 << NF_INET_PRE_ROUTING) | 345 (1 << NF_INET_LOCAL_IN), 346 .me = THIS_MODULE, 347 }, 348#ifdef XT_SOCKET_HAVE_IPV6 349 { 350 .name = "socket", 351 .revision = 1, 352 .family = NFPROTO_IPV6, 353 .match = socket_mt6_v1, 354 .matchsize = sizeof(struct xt_socket_mtinfo1), 355 .hooks = (1 << NF_INET_PRE_ROUTING) | 356 (1 << NF_INET_LOCAL_IN), 357 .me = THIS_MODULE, 358 }, 359#endif 360}; 361 362static int __init socket_mt_init(void) 363{ 364 nf_defrag_ipv4_enable(); 365#ifdef XT_SOCKET_HAVE_IPV6 366 nf_defrag_ipv6_enable(); 367#endif 368 369 return xt_register_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg)); 370} 371 372static void __exit socket_mt_exit(void) 373{ 374 xt_unregister_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg)); 375} 376 377module_init(socket_mt_init); 378module_exit(socket_mt_exit); 379 380MODULE_LICENSE("GPL"); 381MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler"); 382MODULE_DESCRIPTION("x_tables socket match module"); 383MODULE_ALIAS("ipt_socket"); 384MODULE_ALIAS("ip6t_socket");