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 89 socket_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 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"); 186