at v2.6.30 185 lines 4.7 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 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 30static int 31extract_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 88static bool 89socket_mt(const struct sk_buff *skb, const struct xt_match_param *par) 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, 142 saddr, daddr, sport, dport, par->in, false); 143 if (sk != NULL) { 144 bool wildcard = (sk->sk_state != TCP_TIME_WAIT && inet_sk(sk)->rcv_saddr == 0); 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 160static 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 168static int __init socket_mt_init(void) 169{ 170 nf_defrag_ipv4_enable(); 171 return xt_register_match(&socket_mt_reg); 172} 173 174static void __exit socket_mt_exit(void) 175{ 176 xt_unregister_match(&socket_mt_reg); 177} 178 179module_init(socket_mt_init); 180module_exit(socket_mt_exit); 181 182MODULE_LICENSE("GPL"); 183MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler"); 184MODULE_DESCRIPTION("x_tables socket match module"); 185MODULE_ALIAS("ipt_socket");