URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [tags/] [before_ORP/] [uclinux/] [uClinux-2.0.x/] [net/] [ipv4/] [ip_masq.c] - Rev 1765
Compare with Previous | Blame | View Log
/* * * Masquerading functionality * * Copyright (c) 1994 Pauline Middelink * * See ip_fw.c for original log * * Fixes: * Juan Jose Ciarlante : Modularized application masquerading (see ip_masq_app.c) * Juan Jose Ciarlante : New struct ip_masq_seq that holds output/input delta seq. * Juan Jose Ciarlante : Added hashed lookup by proto,maddr,mport and proto,saddr,sport * Juan Jose Ciarlante : Fixed deadlock if free ports get exhausted * Juan Jose Ciarlante : Added NO_ADDR status flag. * Richard Lynch : Added IP Autoforward * Nigel Metheringham : Added ICMP handling for demasquerade * Nigel Metheringham : Checksum checking of masqueraded data * Nigel Metheringham : Better handling of timeouts of TCP conns * Keith Owens : Keep control channels alive if any related data entries. * Delian Delchev : Added support for ICMP requests and replys * Nigel Metheringham : ICMP in ICMP handling, tidy ups, bug fixes, made ICMP optional * Juan Jose Ciarlante : re-assign maddr if no packet received from outside * John D. Hardin : Added PPTP and IPSEC protocols * */ #include <linux/config.h> #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/skbuff.h> #include <asm/system.h> #include <linux/stat.h> #include <linux/proc_fs.h> #include <linux/in.h> #include <linux/ip.h> #include <linux/inet.h> #include <net/protocol.h> #include <net/icmp.h> #include <net/tcp.h> #include <net/udp.h> #include <net/checksum.h> #include <net/ip_masq.h> #include <linux/ip_fw.h> #define IP_MASQ_TAB_SIZE 256 /* must be power of 2 */ #ifdef CONFIG_IP_MASQUERADE_PPTP /* * This is clumsier than it otherwise might be (i.e. the * PPTP control channel sniffer should be a module, and there * should be a separate table for GRE masq entries so that * we're not making all of the hacks to the TCP table code) # but I wanted to keep the code changes localized to one file # if possible. * This should all be modular, and the table routines need to * be somewhat more generic. * * Maybe for 2.0.38 - we'll see. * * John Hardin <jhardin@wolfenet.com> gets all blame... * See also http://www.wolfenet.com/~jhardin/ip_masq_vpn.html */ static const char *strGREProt = "GRE"; #ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT /* * MULTICLIENT watches the control channel and preloads the * call ID into the masq table entry, so we want the * masq table entry to persist until a Call Disconnect * occurs, otherwise the call IDs will be lost and the link broken. */ #define MASQUERADE_EXPIRE_PPTP 15*60*HZ /* * To support multiple clients communicating with the same server, * we have to sniff the control channel and trap the client's * call ID, then substitute a unique-to-the-firewall call ID. * Then on inbound GRE packets we use the bogus call ID to figure * out which client to route the traffic to, then replace the * bogus call ID with the client's real call ID, which we've saved. * For simplicity we'll use masq port as the bogus call ID. * The actual call ID will be stored in the masq table as * the source port, and the destination port will always be zero. * * NB: PPTP servers can tell whether the client is masqueraded by * looking for call IDs above 61000. */ #define PPTP_CONTROL_PORT 1723 #else /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ /* non-MULTICLIENT ignores call IDs, so masq table * entries may expire quickly without causing problems. */ #define MASQUERADE_EXPIRE_PPTP 5*60*HZ #endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ /* * Define this here rather than in /usr/src/linux/include/wherever/whatever.h * in order to localize my mistakes to one file... * * This struct may be architecture-specific because of the bitmaps. */ struct pptp_gre_header { __u8 recur:3, is_strict:1, has_seq:1, has_key:1, has_routing:1, has_cksum:1; __u8 version:3, flags:5; __u16 protocol, payload_len, call_id; /* peer's call_id for this session */ }; #endif /* CONFIG_IP_MASQUERADE_PPTP */ #ifdef CONFIG_IP_MASQUERADE_IPSEC /* * The above comments about PPTP apply here, too. This should all be a module. * * The "port numbers" for masq table purposes will be part of the * SPI, just to gain a little benefit from the hashing. */ static const char *strESPProt = "ESP"; static const char *strAHProt = "AH"; /* * ISAKMP uses 500/udp, and the traffic must come from * 500/udp (i.e. 500/udp <-> 500/udp), so we need to * check for ISAKMP UDP traffic and avoid changing the * source port number. In order to associate the data streams * we may need to sniff the ISAKMP cookies as well. */ #define UDP_PORT_ISAKMP 500 /* ISAKMP default UDP port */ #if CONFIG_IP_MASQUERADE_IPSEC_EXPIRE > 15 #define MASQUERADE_EXPIRE_IPSEC CONFIG_IP_MASQUERADE_IPSEC_EXPIRE*60*HZ #else #define MASQUERADE_EXPIRE_IPSEC 15*60*HZ #endif /* * We can't know the inbound SPI until it comes in (the ISAKMP exchange * is encryptd so we can't sniff it out of that), so we associate inbound * and outbound traffic by inspection. If somebody sends a new packet to a * remote server, then block all other new traffic to that server until we * get a response from that server with a SPI we haven't seen yet. It is * assumed that this is the correct response - we have no way to verify it, * as everything else is encrypted. * * If there is a collision, the block will last for up to two minutes (or * whatever MASQUERADE_EXPIRE_IPSEC_INIT is set to), and if the client * retries during that time the timer will be reset. This could easily lead * to a Denial of Service, so we limit the number of retries that will * reset the timer. This means the maximum time the server could be blocked * is ((IPSEC_INIT_RETRIES + 1) * MASQUERADE_EXPIRE_IPSEC_INIT). * * Note: blocking will not affect already-established traffic (i.e. where * the inbound SPI has been associated with an outbound SPI). */ #define MASQUERADE_EXPIRE_IPSEC_INIT 2*60*HZ #define IPSEC_INIT_RETRIES 5 /* * ...connections that don't get an answer are squelched * (recognized but ignored) for a short time to prevent DoS. * SPI values 1-255 are reserved by the IANA and are currently (2/99) * not assigned. If that should change, this number must also be changed * to an unused NONZERO value: */ #define IPSEC_INIT_SQUELCHED 1 struct ip_masq * ip_masq_out_get_ipsec(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 o_spi); struct ip_masq * ip_masq_in_get_ipsec(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 i_spi); struct ip_masq * ip_masq_out_get_isakmp(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 cookie); struct ip_masq * ip_masq_in_get_isakmp(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 cookie); #endif /* CONFIG_IP_MASQUERADE_IPSEC */ /* * Implement IP packet masquerading */ static const char *strProt[] = {"UDP","TCP","ICMP"}; /* * masq_proto_num returns 0 for UDP, 1 for TCP, 2 for ICMP * * No, I am NOT going to add GRE/ESP/AH support to everything that relies on this... * */ static int masq_proto_num(unsigned proto) { switch (proto) { case IPPROTO_UDP: return (0); break; #ifdef CONFIG_IP_MASQUERADE_PPTP case IPPROTO_GRE: #endif /* CONFIG_IP_MASQUERADE_PPTP */ #ifdef CONFIG_IP_MASQUERADE_IPSEC case IPPROTO_ESP: #endif /* CONFIG_IP_MASQUERADE_IPSEC */ case IPPROTO_TCP: return (1); break; case IPPROTO_ICMP: return (2); break; default: return (-1); break; } } #ifdef CONFIG_IP_MASQUERADE_ICMP /* * Converts an ICMP reply code into the equivalent request code */ static __inline__ const __u8 icmp_type_request(__u8 type) { switch (type) { case ICMP_ECHOREPLY: return ICMP_ECHO; break; case ICMP_TIMESTAMPREPLY: return ICMP_TIMESTAMP; break; case ICMP_INFO_REPLY: return ICMP_INFO_REQUEST; break; case ICMP_ADDRESSREPLY: return ICMP_ADDRESS; break; default: return (255); break; } } /* * Helper macros - attempt to make code clearer! */ /* ID used in ICMP lookups */ #define icmp_id(icmph) ((icmph->un).echo.id) /* (port) hash value using in ICMP lookups for requests */ #define icmp_hv_req(icmph) ((__u16)(icmph->code+(__u16)(icmph->type<<8))) /* (port) hash value using in ICMP lookups for replies */ #define icmp_hv_rep(icmph) ((__u16)(icmph->code+(__u16)(icmp_type_request(icmph->type)<<8))) #endif static __inline__ const char *masq_proto_name(unsigned proto) { /* * I don't want to track down everything that * relies on masq_proto_num() and make it GRE/ESP/AH-tolerant. */ #ifdef CONFIG_IP_MASQUERADE_PPTP if (proto == IPPROTO_GRE) { return strGREProt; } #endif /* CONFIG_IP_MASQUERADE_PPTP */ #ifdef CONFIG_IP_MASQUERADE_IPSEC if (proto == IPPROTO_ESP) { return strESPProt; } else if (proto == IPPROTO_AH) { return strAHProt; } #endif /* CONFIG_IP_MASQUERADE_IPSEC */ return strProt[masq_proto_num(proto)]; } /* * Last masq_port number in use. * Will cycle in MASQ_PORT boundaries. */ static __u16 masq_port = PORT_MASQ_BEGIN; /* * free ports counters (UDP & TCP) * * Their value is _less_ or _equal_ to actual free ports: * same masq port, diff masq addr (firewall iface address) allocated * entries are accounted but their actually don't eat a more than 1 port. * * Greater values could lower MASQ_EXPIRATION setting as a way to * manage 'masq_entries resource'. * */ int ip_masq_free_ports[3] = { PORT_MASQ_END - PORT_MASQ_BEGIN, /* UDP */ PORT_MASQ_END - PORT_MASQ_BEGIN, /* TCP */ PORT_MASQ_END - PORT_MASQ_BEGIN /* ICMP */ }; static struct symbol_table ip_masq_syms = { #include <linux/symtab_begin.h> X(ip_masq_new), X(ip_masq_set_expire), X(ip_masq_free_ports), X(ip_masq_expire), X(ip_masq_out_get_2), #include <linux/symtab_end.h> }; /* * 2 ip_masq hash tables: for input and output pkts lookups. */ struct ip_masq *ip_masq_m_tab[IP_MASQ_TAB_SIZE]; struct ip_masq *ip_masq_s_tab[IP_MASQ_TAB_SIZE]; #ifdef CONFIG_IP_MASQUERADE_IPSEC /* * Add a third hash table for input lookup by remote side */ struct ip_masq *ip_masq_d_tab[IP_MASQ_TAB_SIZE]; #endif /* CONFIG_IP_MASQUERADE_IPSEC */ /* * timeouts */ static struct ip_fw_masq ip_masq_dummy = { MASQUERADE_EXPIRE_TCP, MASQUERADE_EXPIRE_TCP_FIN, MASQUERADE_EXPIRE_UDP }; struct ip_fw_masq *ip_masq_expire = &ip_masq_dummy; #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW /* * Auto-forwarding table */ struct ip_autofw * ip_autofw_hosts = NULL; /* * Check if a masq entry should be created for a packet */ struct ip_autofw * ip_autofw_check_range (__u32 where, __u16 port, __u16 protocol, int reqact) { struct ip_autofw *af; af=ip_autofw_hosts; port=ntohs(port); while (af) { if (af->type==IP_FWD_RANGE && port>=af->low && port<=af->high && protocol==af->protocol && /* it's ok to create masq entries after the timeout if we're in insecure mode */ (af->flags & IP_AUTOFW_ACTIVE || !reqact || !(af->flags & IP_AUTOFW_SECURE)) && (!(af->flags & IP_AUTOFW_SECURE) || af->lastcontact==where || !reqact)) return(af); af=af->next; } return(NULL); } struct ip_autofw * ip_autofw_check_port (__u16 port, __u16 protocol) { struct ip_autofw *af; af=ip_autofw_hosts; port=ntohs(port); while (af) { if (af->type==IP_FWD_PORT && port==af->visible && protocol==af->protocol) return(af); af=af->next; } return(NULL); } struct ip_autofw * ip_autofw_check_direct (__u16 port, __u16 protocol) { struct ip_autofw *af; af=ip_autofw_hosts; port=ntohs(port); while (af) { if (af->type==IP_FWD_DIRECT && af->low<=port && af->high>=port) return(af); af=af->next; } return(NULL); } void ip_autofw_update_out (__u32 who, __u32 where, __u16 port, __u16 protocol) { struct ip_autofw *af; af=ip_autofw_hosts; port=ntohs(port); while (af) { if (af->type==IP_FWD_RANGE && af->ctlport==port && af->ctlproto==protocol) { if (af->flags & IP_AUTOFW_USETIME) { if (af->timer.expires) del_timer(&af->timer); af->timer.expires=jiffies+IP_AUTOFW_EXPIRE; add_timer(&af->timer); } af->flags|=IP_AUTOFW_ACTIVE; af->lastcontact=where; af->where=who; } af=af->next; } } void ip_autofw_update_in (__u32 where, __u16 port, __u16 protocol) { /* struct ip_autofw *af; af=ip_autofw_check_range(where, port,protocol); if (af) { del_timer(&af->timer); af->timer.expires=jiffies+IP_AUTOFW_EXPIRE; add_timer(&af->timer); }*/ } #endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ #ifdef CONFIG_IP_MASQUERADE_IPPORTFW struct ip_portfw *ipportfw_lst[2]; struct ip_portfw *ip_portfw_lookup(__u16 protocol, __u16 lport, __u32 laddr, __u32 *daddr, __u16 *dport) { int prot = (protocol==IPPROTO_TCP); struct ip_portfw *n; unsigned long flags; save_flags(flags); cli(); for (n = ipportfw_lst[prot] ; n; n = n->next) { if (lport == n->lport && laddr == n->laddr) { *daddr = n->raddr; *dport = n->rport; restore_flags(flags); return n; } } restore_flags(flags); return NULL; } int ip_portfw_check(__u16 protocol, __u16 lport, __u32 laddr) { __u16 rport; __u32 raddr; return (ip_portfw_lookup(protocol, lport, laddr, &raddr, &rport) != NULL); } #endif /* CONFIG_IP_MASQUERADE_IPPORTFW */ /* * Returns hash value */ static __inline__ unsigned ip_masq_hash_key(unsigned proto, __u32 addr, __u16 port) { return (proto^ntohl(addr)^ntohs(port)) & (IP_MASQ_TAB_SIZE-1); } /* * Hashes ip_masq by its proto,addrs,ports. * should be called with masked interrupts. * returns bool success. */ static __inline__ int ip_masq_hash(struct ip_masq *ms) { unsigned hash; if (ms->flags & IP_MASQ_F_HASHED) { printk("ip_masq_hash(): request for already hashed\n"); return 0; } /* * Hash by proto,m{addr,port} */ hash = ip_masq_hash_key(ms->protocol, ms->maddr, ms->mport); ms->m_link = ip_masq_m_tab[hash]; ip_masq_m_tab[hash] = ms; /* * Hash by proto,s{addr,port} */ #ifdef CONFIG_IP_MASQUERADE_PPTP if (ms->protocol == IPPROTO_GRE) { /* Ignore the source port (Call ID) when hashing, as * outbound packets will not be able to supply it... */ hash = ip_masq_hash_key(ms->protocol, ms->saddr, 0); } else #endif /* CONFIG_IP_MASQUERADE_PPTP */ hash = ip_masq_hash_key(ms->protocol, ms->saddr, ms->sport); ms->s_link = ip_masq_s_tab[hash]; ip_masq_s_tab[hash] = ms; #ifdef CONFIG_IP_MASQUERADE_IPSEC /* * Hash by proto,d{addr,port} */ hash = ip_masq_hash_key(ms->protocol, ms->daddr, ms->dport); ms->d_link = ip_masq_d_tab[hash]; ip_masq_d_tab[hash] = ms; #endif /* CONFIG_IP_MASQUERADE_IPSEC */ ms->flags |= IP_MASQ_F_HASHED; return 1; } /* * UNhashes ip_masq from ip_masq_[ms]_tables. * should be called with masked interrupts. * returns bool success. */ static __inline__ int ip_masq_unhash(struct ip_masq *ms) { unsigned hash; struct ip_masq ** ms_p; if (!(ms->flags & IP_MASQ_F_HASHED)) { printk("ip_masq_unhash(): request for unhash flagged\n"); return 0; } /* * UNhash by m{addr,port} */ hash = ip_masq_hash_key(ms->protocol, ms->maddr, ms->mport); for (ms_p = &ip_masq_m_tab[hash]; *ms_p ; ms_p = &(*ms_p)->m_link) if (ms == (*ms_p)) { *ms_p = ms->m_link; break; } /* * UNhash by s{addr,port} */ #ifdef CONFIG_IP_MASQUERADE_PPTP if (ms->protocol == IPPROTO_GRE) { hash = ip_masq_hash_key(ms->protocol, ms->saddr, 0); } else #endif /* CONFIG_IP_MASQUERADE_PPTP */ hash = ip_masq_hash_key(ms->protocol, ms->saddr, ms->sport); for (ms_p = &ip_masq_s_tab[hash]; *ms_p ; ms_p = &(*ms_p)->s_link) if (ms == (*ms_p)) { *ms_p = ms->s_link; break; } #ifdef CONFIG_IP_MASQUERADE_IPSEC /* * UNhash by d{addr,port} */ hash = ip_masq_hash_key(ms->protocol, ms->daddr, ms->dport); for (ms_p = &ip_masq_d_tab[hash]; *ms_p ; ms_p = &(*ms_p)->d_link) if (ms == (*ms_p)) { *ms_p = ms->d_link; break; } #endif /* CONFIG_IP_MASQUERADE_IPSEC */ ms->flags &= ~IP_MASQ_F_HASHED; return 1; } /* * Returns ip_masq associated with addresses found in iph. * called for pkts coming from outside-to-INside the firewall * * NB. Cannot check destination address, just for the incoming port. * reason: archie.doc.ac.uk has 6 interfaces, you send to * phoenix and get a reply from any other interface(==dst)! * * [Only for UDP] - AC */ struct ip_masq * ip_masq_in_get(struct iphdr *iph) { __u16 *portptr; int protocol; __u32 s_addr, d_addr; __u16 s_port, d_port; #ifdef CONFIG_IP_MASQUERADE_IPSEC __u32 cookie; #endif /* CONFIG_IP_MASQUERADE_IPSEC */ portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]); protocol = iph->protocol; s_addr = iph->saddr; s_port = portptr[0]; d_addr = iph->daddr; d_port = portptr[1]; #ifdef CONFIG_IP_MASQUERADE_IPSEC if (protocol == IPPROTO_UDP && ntohs(s_port) == UDP_PORT_ISAKMP && ntohs(d_port) == UDP_PORT_ISAKMP) { cookie = *((__u32 *)&portptr[4]); return ip_masq_in_get_isakmp(protocol, s_addr, s_port, d_addr, d_port, cookie); } else #endif /* CONFIG_IP_MASQUERADE_IPSEC */ return ip_masq_in_get_2(protocol, s_addr, s_port, d_addr, d_port); } /* * Returns ip_masq associated with supplied parameters, either * broken out of the ip/tcp headers or directly supplied for those * pathological protocols with address/port in the data stream * (ftp, irc). addresses and ports are in network order. * called for pkts coming from outside-to-INside the firewall. * * NB. Cannot check destination address, just for the incoming port. * reason: archie.doc.ac.uk has 6 interfaces, you send to * phoenix and get a reply from any other interface(==dst)! * * [Only for UDP] - AC */ struct ip_masq * ip_masq_in_get_2(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port) { unsigned hash; struct ip_masq *ms; hash = ip_masq_hash_key(protocol, d_addr, d_port); for(ms = ip_masq_m_tab[hash]; ms ; ms = ms->m_link) { if (protocol==ms->protocol && ((s_addr==ms->daddr || ms->flags & IP_MASQ_F_NO_DADDR) #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW || (ms->dport==htons(1558)) #endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ ) && (s_port==ms->dport || ms->flags & IP_MASQ_F_NO_DPORT) && (d_addr==ms->maddr && d_port==ms->mport)) { #ifdef DEBUG_IP_MASQUERADE_VERBOSE printk("MASQ: look/in %d %08X:%04hX->%08X:%04hX OK\n", protocol, s_addr, s_port, d_addr, d_port); #endif return ms; } } #ifdef CONFIG_IP_MASQUERADE_PPTP if (protocol == IPPROTO_GRE) { for(ms = ip_masq_m_tab[hash]; ms ; ms = ms->m_link) { if (protocol==ms->protocol && #ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT ms->mport == d_port && /* ignore source port */ #else /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ ms->mport == 0 && ms->sport == 0 && #endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ s_addr==ms->daddr && d_addr==ms->maddr) { #ifdef DEBUG_IP_MASQUERADE_PPTP_VERBOSE printk(KERN_DEBUG "MASQ: look/in %d %08X:%04hX->%08X:%04hX OK\n", protocol, s_addr, s_port, d_addr, d_port); #endif /* DEBUG_IP_MASQUERADE_PPTP_VERBOSE */ return ms; } } } #endif /* CONFIG_IP_MASQUERADE_PPTP */ #ifdef DEBUG_IP_MASQUERADE_VERBOSE printk("MASQ: look/in %d %08X:%04hX->%08X:%04hX fail\n", protocol, s_addr, s_port, d_addr, d_port); #endif return NULL; } #ifdef CONFIG_IP_MASQUERADE_IPSEC struct ip_masq * ip_masq_in_get_ipsec(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 i_spi) { unsigned hash; struct ip_masq *ms; if (protocol != IPPROTO_ESP) { return ip_masq_in_get_2(protocol,s_addr,s_port,d_addr,d_port); } /* find an entry for a packet coming in from outside, * or find whether there's a setup pending */ if (i_spi != 0) { /* there's a SPI - look for a completed entry */ hash = ip_masq_hash_key(protocol, s_addr, s_port); for(ms = ip_masq_d_tab[hash]; ms ; ms = ms->d_link) { if (protocol==ms->protocol && s_addr==ms->daddr && d_addr==ms->maddr && ms->ispi != 0 && i_spi==ms->ispi) { #ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE printk(KERN_DEBUG "MASQ: IPSEC look/in %08X->%08X:%08X OK\n", s_addr, d_addr, i_spi); #endif return ms; } } } /* no joy. look for a pending connection - maybe somebody else's * if we're checking for a pending setup, the d_addr will be zero * to avoid having to know the masq IP. */ hash = ip_masq_hash_key(protocol, s_addr, 0); for(ms = ip_masq_d_tab[hash]; ms ; ms = ms->d_link) { if (protocol==ms->protocol && s_addr==ms->daddr && (d_addr==0 || d_addr==ms->maddr) && ms->ispi==0) { #ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE printk(KERN_DEBUG "MASQ: IPSEC look/in %08X->%08X:0 OK\n", s_addr, d_addr ); #endif return ms; } } #ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE printk(KERN_DEBUG "MASQ: IPSEC look/in %08X->%08X:%08X fail\n", s_addr, d_addr, i_spi); #endif return NULL; } struct ip_masq * ip_masq_in_get_isakmp(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 cookie) { unsigned hash; struct ip_masq *ms; #ifdef DEBUG_IP_MASQUERADE_IPSEC printk(KERN_DEBUG "ip_masq_in_get_isakmp(): "); printk("%s -> ", in_ntoa(s_addr)); printk("%s cookie %lX\n", in_ntoa(d_addr), ntohl(cookie)); #endif /* DEBUG_IP_MASQUERADE_IPSEC */ if (cookie == 0) { printk(KERN_INFO "ip_masq_in_get_isakmp(): "); printk("zero cookie from %s\n", in_ntoa(s_addr)); } hash = ip_masq_hash_key(protocol, d_addr, d_port); for(ms = ip_masq_m_tab[hash]; ms ; ms = ms->m_link) { if (protocol==ms->protocol && cookie==ms->ospi && ((s_addr==ms->daddr || ms->flags & IP_MASQ_F_NO_DADDR) ) && (s_port==ms->dport || ms->flags & IP_MASQ_F_NO_DPORT) && (d_addr==ms->maddr && d_port==ms->mport)) { #ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE printk(KERN_DEBUG "MASQ: look/in %d %08X:%04hX->%08X:%04hX %08X OK\n", protocol, s_addr, s_port, d_addr, d_port, cookie); #endif return ms; } } #ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE printk(KERN_DEBUG "MASQ: look/in %d %08X:%04hX->%08X:%04hX %08X fail\n", protocol, s_addr, s_port, d_addr, d_port, cookie); #endif return NULL; } #endif /* CONFIG_IP_MASQUERADE_IPSEC */ /* * Returns ip_masq associated with addresses found in iph. * called for pkts coming from inside-to-OUTside the firewall. */ struct ip_masq * ip_masq_out_get(struct iphdr *iph) { __u16 *portptr; int protocol; __u32 s_addr, d_addr; __u16 s_port, d_port; #ifdef CONFIG_IP_MASQUERADE_IPSEC __u32 cookie; #endif /* CONFIG_IP_MASQUERADE_IPSEC */ portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]); protocol = iph->protocol; s_addr = iph->saddr; s_port = portptr[0]; d_addr = iph->daddr; d_port = portptr[1]; #ifdef CONFIG_IP_MASQUERADE_IPSEC if (protocol == IPPROTO_UDP && ntohs(s_port) == UDP_PORT_ISAKMP && ntohs(d_port) == UDP_PORT_ISAKMP) { cookie = *((__u32 *)&portptr[4]); return ip_masq_out_get_isakmp(protocol, s_addr, s_port, d_addr, d_port, cookie); } else #endif /* CONFIG_IP_MASQUERADE_IPSEC */ return ip_masq_out_get_2(protocol, s_addr, s_port, d_addr, d_port); } /* * Returns ip_masq associated with supplied parameters, either * broken out of the ip/tcp headers or directly supplied for those * pathological protocols with address/port in the data stream * (ftp, irc). addresses and ports are in network order. * called for pkts coming from inside-to-OUTside the firewall. * * Normally we know the source address and port but for some protocols * (e.g. ftp PASV) we do not know the source port initially. Alas the * hash is keyed on source port so if the first lookup fails then try again * with a zero port, this time only looking at entries marked "no source * port". */ struct ip_masq * ip_masq_out_get_2(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port) { unsigned hash; struct ip_masq *ms; #ifdef CONFIG_IP_MASQUERADE_PPTP #ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT if (protocol == IPPROTO_GRE) { /* * Call ID is saved in source port number, * but we have no way of knowing it on the outbound packet... * we only know the *other side's* Call ID */ hash = ip_masq_hash_key(protocol, s_addr, 0); for(ms = ip_masq_s_tab[hash]; ms ; ms = ms->s_link) { if (protocol == ms->protocol && s_addr == ms->saddr && (s_port == 0 || s_port == ms->sport) && d_addr == ms->daddr && d_port == ms->dport ) { #ifdef DEBUG_IP_MASQUERADE_VERBOSE printk(KERN_DEBUG "MASQ: lk/out2 %d %08X:%04hX->%08X:%04hX OK\n", protocol, s_addr, s_port, d_addr, d_port); #endif /* DEBUG_IP_MASQUERADE_VERBOSE */ return ms; } } } #endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ #endif /* CONFIG_IP_MASQUERADE_PPTP */ hash = ip_masq_hash_key(protocol, s_addr, s_port); for(ms = ip_masq_s_tab[hash]; ms ; ms = ms->s_link) { if (protocol == ms->protocol && s_addr == ms->saddr && s_port == ms->sport && d_addr == ms->daddr && d_port == ms->dport ) { #ifdef DEBUG_IP_MASQUERADE_VERBOSE printk("MASQ: lk/out1 %d %08X:%04hX->%08X:%04hX OK\n", protocol, s_addr, s_port, d_addr, d_port); #endif return ms; } } hash = ip_masq_hash_key(protocol, s_addr, 0); for(ms = ip_masq_s_tab[hash]; ms ; ms = ms->s_link) { if (ms->flags & IP_MASQ_F_NO_SPORT && protocol == ms->protocol && s_addr == ms->saddr && d_addr == ms->daddr && d_port == ms->dport ) { #ifdef DEBUG_IP_MASQUERADE_VERBOSE printk("MASQ: lk/out2 %d %08X:%04hX->%08X:%04hX OK\n", protocol, s_addr, s_port, d_addr, d_port); #endif return ms; } } #ifdef DEBUG_IP_MASQUERADE_VERBOSE printk("MASQ: lk/out1 %d %08X:%04hX->%08X:%04hX fail\n", protocol, s_addr, s_port, d_addr, d_port); #endif return NULL; } #ifdef CONFIG_IP_MASQUERADE_IPSEC struct ip_masq * ip_masq_out_get_ipsec(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 o_spi) { unsigned hash; struct ip_masq *ms; if (protocol != IPPROTO_ESP) { return ip_masq_out_get_2(protocol,s_addr,s_port,d_addr,d_port); } hash = ip_masq_hash_key(protocol, s_addr, s_port); for(ms = ip_masq_s_tab[hash]; ms ; ms = ms->s_link) { if (protocol==ms->protocol && s_addr==ms->saddr && d_addr==ms->daddr && o_spi==ms->ospi) { #ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE printk(KERN_DEBUG "MASQ: IPSEC look/out %08X:%08X->%08X OK\n", s_addr, o_spi, d_addr); #endif return ms; } } #ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE printk(KERN_DEBUG "MASQ: IPSEC look/out %08X:%08X->%08X fail\n", s_addr, o_spi, d_addr); #endif return NULL; } struct ip_masq * ip_masq_out_get_isakmp(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 cookie) { unsigned hash; struct ip_masq *ms; #ifdef DEBUG_IP_MASQUERADE_IPSEC printk(KERN_DEBUG "ip_masq_out_get_isakmp(): "); printk("%s -> ", in_ntoa(s_addr)); printk("%s cookie %lX\n", in_ntoa(d_addr), ntohl(cookie)); #endif /* DEBUG_IP_MASQUERADE_IPSEC */ if (cookie == 0) { printk(KERN_INFO "ip_masq_out_get_isakmp(): "); printk("zero cookie from %s\n", in_ntoa(s_addr)); } hash = ip_masq_hash_key(protocol, s_addr, s_port); for(ms = ip_masq_s_tab[hash]; ms ; ms = ms->s_link) { if (protocol == ms->protocol && cookie == ms->ospi && s_addr == ms->saddr && s_port == ms->sport && d_addr == ms->daddr && d_port == ms->dport ) { #ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE printk(KERN_DEBUG "MASQ: lk/out1 %d %08X:%04hX->%08X:%04hX %08X OK\n", protocol, s_addr, s_port, d_addr, d_port, cookie); #endif return ms; } } #ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE printk(KERN_DEBUG "MASQ: lk/out1 %d %08X:%04hX->%08X:%04hX %08X fail\n", protocol, s_addr, s_port, d_addr, d_port, cookie); #endif return NULL; } #endif /* CONFIG_IP_MASQUERADE_IPSEC */ /* * Returns ip_masq for given proto,m_addr,m_port. * called by allocation routine to find an unused m_port. */ struct ip_masq * ip_masq_getbym(int protocol, __u32 m_addr, __u16 m_port) { unsigned hash; struct ip_masq *ms; hash = ip_masq_hash_key(protocol, m_addr, m_port); for(ms = ip_masq_m_tab[hash]; ms ; ms = ms->m_link) { if ( protocol==ms->protocol && (m_addr==ms->maddr && m_port==ms->mport)) return ms; } return NULL; } static void masq_expire(unsigned long data) { struct ip_masq *ms = (struct ip_masq *)data, *ms_data; unsigned long flags; if (ms->flags & IP_MASQ_F_CONTROL) { /* a control channel is about to expire */ int idx = 0, reprieve = 0; #ifdef DEBUG_CONFIG_IP_MASQUERADE printk("Masquerade control %s %lX:%X about to expire\n", masq_proto_name(ms->protocol), ntohl(ms->saddr),ntohs(ms->sport)); #endif save_flags(flags); cli(); /* * If any other masquerade entry claims that the expiring entry * is its control channel then keep the control entry alive. * Useful for long running data channels with inactive control * links which we don't want to lose, e.g. ftp. * Assumption: loops such as a->b->a or a->a will never occur. */ for (idx = 0; idx < IP_MASQ_TAB_SIZE && !reprieve; idx++) { for (ms_data = ip_masq_m_tab[idx]; ms_data ; ms_data = ms_data->m_link) { if (ms_data->control == ms) { reprieve = 1; /* this control connection can live a bit longer */ ip_masq_set_expire(ms, ip_masq_expire->tcp_timeout); #ifdef DEBUG_CONFIG_IP_MASQUERADE printk("Masquerade control %s %lX:%X expiry reprieved\n", masq_proto_name(ms->protocol), ntohl(ms->saddr),ntohs(ms->sport)); #endif break; } } } restore_flags(flags); if (reprieve) return; } #ifdef DEBUG_CONFIG_IP_MASQUERADE printk("Masqueraded %s %lX:%X expired\n",masq_proto_name(ms->protocol),ntohl(ms->saddr),ntohs(ms->sport)); #endif save_flags(flags); cli(); if (ip_masq_unhash(ms)) { ip_masq_free_ports[masq_proto_num(ms->protocol)]++; if (ms->protocol != IPPROTO_ICMP) ip_masq_unbind_app(ms); kfree_s(ms,sizeof(*ms)); } restore_flags(flags); } #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW void ip_autofw_expire(unsigned long data) { struct ip_autofw * af; af=(struct ip_autofw *) data; af->flags&=0xFFFF ^ IP_AUTOFW_ACTIVE; af->timer.expires=0; af->lastcontact=0; if (af->flags & IP_AUTOFW_SECURE) af->where=0; } #endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ /* * Create a new masquerade list entry, also allocate an * unused mport, keeping the portnumber between the * given boundaries MASQ_BEGIN and MASQ_END. */ struct ip_masq * ip_masq_new_enh(struct device *dev, int proto, __u32 saddr, __u16 sport, __u32 daddr, __u16 dport, unsigned mflags, __u16 matchport) { struct ip_masq *ms, *mst; int ports_tried, *free_ports_p; unsigned long flags; static int n_fails = 0; free_ports_p = &ip_masq_free_ports[masq_proto_num(proto)]; if (*free_ports_p == 0) { if (++n_fails < 5) printk("ip_masq_new(proto=%s): no free ports.\n", masq_proto_name(proto)); return NULL; } ms = (struct ip_masq *) kmalloc(sizeof(struct ip_masq), GFP_ATOMIC); if (ms == NULL) { if (++n_fails < 5) printk("ip_masq_new(proto=%s): no memory available.\n", masq_proto_name(proto)); return NULL; } memset(ms, 0, sizeof(*ms)); init_timer(&ms->timer); ms->timer.data = (unsigned long)ms; ms->timer.function = masq_expire; ms->protocol = proto; ms->saddr = saddr; ms->sport = sport; ms->daddr = daddr; ms->dport = dport; ms->flags = mflags; ms->app_data = NULL; ms->control = NULL; if (proto == IPPROTO_UDP && !matchport) ms->flags |= IP_MASQ_F_NO_DADDR; /* get masq address from rif */ ms->maddr = dev->pa_addr; /* * Setup new entry as not replied yet. * This flag will allow masq. addr (ms->maddr) * to follow forwarding interface address. */ ms->flags |= IP_MASQ_F_NO_REPLY; for (ports_tried = 0; (*free_ports_p && (ports_tried <= (PORT_MASQ_END - PORT_MASQ_BEGIN))); ports_tried++){ #ifdef CONFIG_IP_MASQUERADE_PPTP #ifndef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT /* Ignoring PPTP call IDs. * Don't needlessly increase the TCP port pointer. */ if (proto == IPPROTO_GRE) { ms->mport = 0; mst = NULL; } else { #endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ #endif /* CONFIG_IP_MASQUERADE_PPTP */ #ifdef CONFIG_IP_MASQUERADE_IPSEC /* ESP masq keys off the SPI, not the port number. * Don't needlessly increase the TCP port pointer. */ if (proto == IPPROTO_ESP) { ms->mport = 0; mst = NULL; } else { if (proto == IPPROTO_UDP && ntohs(sport) == UDP_PORT_ISAKMP && ntohs(dport) == UDP_PORT_ISAKMP) { /* the port number cannot be changed */ ms->mport = htons(UDP_PORT_ISAKMP); mst = NULL; } else { #endif /* CONFIG_IP_MASQUERADE_IPSEC */ save_flags(flags); cli(); /* * Try the next available port number */ if (!matchport || ports_tried) ms->mport = htons(masq_port++); else ms->mport = matchport; if (masq_port==PORT_MASQ_END) masq_port = PORT_MASQ_BEGIN; restore_flags(flags); /* * lookup to find out if this port is used. */ mst = ip_masq_getbym(proto, ms->maddr, ms->mport); #ifdef CONFIG_IP_MASQUERADE_PPTP #ifndef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT } #endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ #endif /* CONFIG_IP_MASQUERADE_PPTP */ #ifdef CONFIG_IP_MASQUERADE_IPSEC } } #endif /* CONFIG_IP_MASQUERADE_IPSEC */ if (mst == NULL || matchport) { save_flags(flags); cli(); if (*free_ports_p == 0) { restore_flags(flags); break; } (*free_ports_p)--; ip_masq_hash(ms); restore_flags(flags); if (proto != IPPROTO_ICMP) ip_masq_bind_app(ms); n_fails = 0; return ms; } } if (++n_fails < 5) printk("ip_masq_new(proto=%s): could not get free masq entry (free=%d).\n", masq_proto_name(ms->protocol), *free_ports_p); kfree_s(ms, sizeof(*ms)); return NULL; } struct ip_masq * ip_masq_new(struct device *dev, int proto, __u32 saddr, __u16 sport, __u32 daddr, __u16 dport, unsigned mflags) { return (ip_masq_new_enh(dev, proto, saddr, sport, daddr, dport, mflags, 0) ); } #ifdef CONFIG_IP_MASQUERADE_IPPORTFW /* New form of ip_masq creation which creates masqs for Port Forwarding * The routine is sufficiently different to ip_masq_new to require its own function */ struct ip_masq * ip_masq_new_portfw(struct device *dev, int proto, __u32 raddr, __u16 rport, __u32 saddr, __u16 sport, __u32 laddr, __u16 lport) { struct ip_masq *ms; static int n_fails = 0; unsigned long flags; ms = (struct ip_masq *) kmalloc(sizeof(struct ip_masq), GFP_ATOMIC); if (ms == NULL) { if (++n_fails < 5) printk("ip_masq_new_s(proto=%s): no memory available.\n", masq_proto_name(proto)); return NULL; } memset(ms, 0, sizeof(*ms)); init_timer(&ms->timer); ms->timer.data = (unsigned long)ms; ms->timer.function = masq_expire; ms->protocol = proto; ms->saddr = raddr; ms->sport = rport; ms->daddr = saddr; ms->dport = sport; ms->maddr = laddr; ms->mport = lport; ms->flags = 0; ms->app_data = NULL; ms->control = NULL; ip_masq_free_ports[masq_proto_num(proto)]--; save_flags(flags); cli(); ip_masq_hash(ms); restore_flags(flags); return ms; } #endif /* CONFIG_IP_MASQUERADE_IPPORTFW */ /* * Set masq expiration (deletion) and adds timer, * if timeout==0 cancel expiration. * Warning: it does not check/delete previous timer! */ void ip_masq_set_expire(struct ip_masq *ms, unsigned long tout) { /* There Can Be Only One (timer on a masq table entry, that is) */ del_timer(&ms->timer); if (tout) { ms->timer.expires = jiffies+tout; add_timer(&ms->timer); } } static void recalc_check(struct udphdr *uh, __u32 saddr, __u32 daddr, int len) { uh->check=0; uh->check=csum_tcpudp_magic(saddr,daddr,len, IPPROTO_UDP, csum_partial((char *)uh,len,0)); if(uh->check==0) uh->check=0xFFFF; } #ifdef CONFIG_IP_MASQUERADE_PPTP /* * Masquerade of GRE connections * to support a PPTP VPN client or server. */ /* * Handle outbound GRE packets. * * This is largely a copy of ip_fw_masquerade() */ int ip_fw_masq_gre(struct sk_buff **skb_p, struct device *dev) { struct sk_buff *skb = *skb_p; struct iphdr *iph = skb->h.iph; struct pptp_gre_header *greh; #ifdef DEBUG_IP_MASQUERADE_PPTP #ifdef DEBUG_IP_MASQUERADE_PPTP_VERBOSE __u8 *greraw; #endif /* DEBUG_IP_MASQUERADE_PPTP_VERBOSE */ #endif /* DEBUG_IP_MASQUERADE_PPTP */ struct ip_masq *ms; unsigned long flags; greh = (struct pptp_gre_header *)&(((char *)iph)[iph->ihl*4]); #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_DEBUG "ip_fw_masq_gre(): "); printk("Outbound GRE packet from %s", in_ntoa(iph->saddr)); printk(" to %s\n", in_ntoa(iph->daddr)); #ifdef DEBUG_IP_MASQUERADE_PPTP_VERBOSE greraw = (__u8 *) greh; printk(KERN_DEBUG "ip_fw_masq_gre(): "); printk("GRE raw: %X %X %X %X %X %X %X %X %X %X %X %X.\n", greraw[0], greraw[1], greraw[2], greraw[3], greraw[4], greraw[5], greraw[6], greraw[7], greraw[8], greraw[9], greraw[10], greraw[11]); printk(KERN_DEBUG "ip_fw_masq_gre(): "); printk("GRE C: %d R: %d K: %d S: %d s: %d recur: %X.\n", greh->has_cksum, greh->has_routing, greh->has_key, greh->has_seq, greh->is_strict, greh->recur); printk(KERN_DEBUG "ip_fw_masq_gre(): "); printk("GRE flags: %X ver: %X.\n", greh->flags, greh->version); printk(KERN_DEBUG "ip_fw_masq_gre(): "); printk("GRE proto: %X.\n", ntohs(greh->protocol)); #endif /* DEBUG_IP_MASQUERADE_PPTP_VERBOSE */ #endif /* DEBUG_IP_MASQUERADE_PPTP */ if (ntohs(greh->protocol) != 0x880B) { #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_INFO "ip_fw_masq_gre(): "); printk("GRE protocol %X not 0x880B (non-PPTP encap?) - discarding.\n", ntohs(greh->protocol)); #endif /* DEBUG_IP_MASQUERADE_PPTP */ return -1; } /* * Look for masq table entry */ ms = ip_masq_out_get_2(IPPROTO_GRE, iph->saddr, 0, iph->daddr, 0); if (ms!=NULL) { /* delete the expiration timer */ ip_masq_set_expire(ms,0); /* * Make sure that the masq IP address is correct * for dynamic IP... */ if ( (ms->maddr != dev->pa_addr) && (sysctl_ip_dynaddr & 3) ) { printk(KERN_INFO "ip_fw_masq_gre(): "); printk("change maddr from %s", in_ntoa(ms->maddr)); printk(" to %s\n", in_ntoa(dev->pa_addr)); save_flags(flags); cli(); ip_masq_unhash(ms); ms->maddr = dev->pa_addr; ip_masq_hash(ms); restore_flags(flags); } } else { /* * Nope, not found, create a new entry for it, maybe */ #ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT /* masq table entry has to come from control channel sniffing. * If we can't find one, it may have expired. * How can this happen with the control channel active? */ printk(KERN_INFO "ip_fw_masq_gre(): "); printk("Outbound GRE to %s has no masq table entry.\n", in_ntoa(iph->daddr)); return -1; #else /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ /* call IDs ignored, can create masq table entries on the fly. */ ms = ip_masq_new(dev, iph->protocol, iph->saddr, 0, iph->daddr, 0, 0); if (ms == NULL) { printk(KERN_NOTICE "ip_fw_masq_gre(): Couldn't create masq table entry.\n"); return -1; } #endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ } /* * Set iph source addr from ip_masq obj. */ iph->saddr = ms->maddr; /* * set timeout and check IP header */ ip_masq_set_expire(ms, MASQUERADE_EXPIRE_PPTP); ip_send_check(iph); #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_DEBUG "MASQ: GRE O-routed from %s over %s\n", in_ntoa(ms->maddr), dev->name); #endif /* DEBUG_IP_MASQUERADE_PPTP */ return 0; } /* * Handle inbound GRE packets. * */ int ip_fw_demasq_gre(struct sk_buff **skb_p, struct device *dev) { struct sk_buff *skb = *skb_p; struct iphdr *iph = skb->h.iph; struct pptp_gre_header *greh; #ifdef DEBUG_IP_MASQUERADE_PPTP #ifdef DEBUG_IP_MASQUERADE_PPTP_VERBOSE __u8 *greraw; #endif /* DEBUG_IP_MASQUERADE_PPTP_VERBOSE */ #endif /* DEBUG_IP_MASQUERADE_PPTP */ struct ip_masq *ms; greh = (struct pptp_gre_header *)&(((char *)iph)[iph->ihl*4]); #ifdef DEBUG_IP_MASQUERADE_PPTP #if 0 printk(KERN_DEBUG "ip_fw_demasq_gre(): "); printk("Inbound GRE packet from %s", in_ntoa(iph->saddr)); printk(" to %s\n", in_ntoa(iph->daddr)); #endif #ifdef DEBUG_IP_MASQUERADE_PPTP_VERBOSE greraw = (__u8 *) greh; printk(KERN_DEBUG "ip_fw_demasq_gre(): "); printk("GRE raw: %X %X %X %X %X %X %X %X %X %X %X %X.\n", greraw[0], greraw[1], greraw[2], greraw[3], greraw[4], greraw[5], greraw[6], greraw[7], greraw[8], greraw[9], greraw[10], greraw[11]); printk(KERN_DEBUG "ip_fw_demasq_gre(): "); printk("GRE C: %d R: %d K: %d S: %d s: %d recur: %X.\n", greh->has_cksum, greh->has_routing, greh->has_key, greh->has_seq, greh->is_strict, greh->recur); printk(KERN_DEBUG "ip_fw_demasq_gre(): "); printk("GRE flags: %X ver: %X.\n", greh->flags, greh->version); printk(KERN_DEBUG "ip_fw_demasq_gre(): "); printk("GRE proto: %X.\n", ntohs(greh->protocol)); #endif /* DEBUG_IP_MASQUERADE_PPTP_VERBOSE */ #ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT printk(KERN_DEBUG "ip_fw_demasq_gre(): "); printk("PPTP call ID: %X.\n", ntohs(greh->call_id)); #endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ #endif /* DEBUG_IP_MASQUERADE_PPTP */ if (ntohs(greh->protocol) != 0x880B) { #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_INFO "ip_fw_demasq_gre(): "); printk("GRE protocol %X not 0x880B (non-PPTP encap?) - discarding.\n", ntohs(greh->protocol)); #endif /* DEBUG_IP_MASQUERADE_PPTP */ return -1; } /* * Look for a masq table entry and reroute if found */ #ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT ms = ip_masq_getbym(IPPROTO_GRE, iph->daddr, greh->call_id); #else /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ ms = ip_masq_in_get_2(IPPROTO_GRE, iph->saddr, 0, iph->daddr, 0); #endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ if (ms != NULL) { /* delete the expiration timer */ ip_masq_set_expire(ms,0); iph->daddr = ms->saddr; #ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT /* * change peer call ID to original value * (saved in masq table source port) */ greh->call_id = ms->sport; #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_DEBUG "ip_fw_demasq_gre(): "); printk("inbound PPTP from %s call ID now %X\n", in_ntoa(iph->saddr), ntohs(greh->call_id)); #endif /* DEBUG_IP_MASQUERADE_PPTP */ #endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ /* * resum checksums and set timeout */ ip_masq_set_expire(ms, MASQUERADE_EXPIRE_PPTP); ip_send_check(iph); #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_DEBUG "MASQ: GRE I-routed to %s\n", in_ntoa(iph->daddr)); #endif /* DEBUG_IP_MASQUERADE_PPTP */ return 1; } /* sorry, all this trouble for a no-hit :) */ #if 0 printk(KERN_INFO "ip_fw_demasq_gre(): "); printk("Inbound from %s has no masq table entry.\n", in_ntoa(iph->saddr)); #endif return 0; } #ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT /* * Define all of the PPTP control channel message structures. * Sniff the control channel looking for start- and end-call * messages, and masquerade the Call ID as if it was a TCP * port. */ #define PPTP_CONTROL_PACKET 1 #define PPTP_MGMT_PACKET 2 #define PPTP_MAGIC_COOKIE 0x1A2B3C4D struct PptpPacketHeader { __u16 packetLength; __u16 packetType; __u32 magicCookie; }; /* PptpControlMessageType values */ #define PPTP_START_SESSION_REQUEST 1 #define PPTP_START_SESSION_REPLY 2 #define PPTP_STOP_SESSION_REQUEST 3 #define PPTP_STOP_SESSION_REPLY 4 #define PPTP_ECHO_REQUEST 5 #define PPTP_ECHO_REPLY 6 #define PPTP_OUT_CALL_REQUEST 7 #define PPTP_OUT_CALL_REPLY 8 #define PPTP_IN_CALL_REQUEST 9 #define PPTP_IN_CALL_REPLY 10 #define PPTP_CALL_CLEAR_REQUEST 11 #define PPTP_CALL_DISCONNECT_NOTIFY 12 #define PPTP_CALL_ERROR_NOTIFY 13 #define PPTP_WAN_ERROR_NOTIFY 14 #define PPTP_SET_LINK_INFO 15 struct PptpControlHeader { __u16 messageType; __u16 reserved; }; struct PptpOutCallRequest { __u16 callID; __u16 callSerialNumber; __u32 minBPS; __u32 maxBPS; __u32 bearerType; __u32 framingType; __u16 packetWindow; __u16 packetProcDelay; __u16 reserved1; __u16 phoneNumberLength; __u16 reserved2; __u8 phoneNumber[64]; __u8 subAddress[64]; }; struct PptpOutCallReply { __u16 callID; __u16 peersCallID; __u8 resultCode; __u8 generalErrorCode; __u16 causeCode; __u32 connectSpeed; __u16 packetWindow; __u16 packetProcDelay; __u32 physChannelID; }; struct PptpInCallRequest { __u16 callID; __u16 callSerialNumber; __u32 callBearerType; __u32 physChannelID; __u16 dialedNumberLength; __u16 dialingNumberLength; __u8 dialedNumber[64]; __u8 dialingNumber[64]; __u8 subAddress[64]; }; struct PptpInCallReply { __u16 callID; __u16 peersCallID; __u8 resultCode; __u8 generalErrorCode; __u16 packetWindow; __u16 packetProcDelay; __u16 reserved; }; struct PptpCallDisconnectNotify { __u16 callID; __u8 resultCode; __u8 generalErrorCode; __u16 causeCode; __u16 reserved; __u8 callStatistics[128]; }; struct PptpWanErrorNotify { __u16 peersCallID; __u16 reserved; __u32 crcErrors; __u32 framingErrors; __u32 hardwareOverRuns; __u32 bufferOverRuns; __u32 timeoutErrors; __u32 alignmentErrors; }; struct PptpSetLinkInfo { __u16 peersCallID; __u16 reserved; __u32 sendAccm; __u32 recvAccm; }; /* Packet sent to or from PPTP control port. Process it. */ /* Yes, all of this should be in a kernel module. Real Soon Now... */ void ip_masq_pptp(struct sk_buff *skb, struct ip_masq *ms, struct device *dev) { struct iphdr *iph = skb->h.iph; struct PptpPacketHeader *pptph = NULL; struct PptpControlHeader *ctlh = NULL; union { char *req; struct PptpOutCallRequest *ocreq; struct PptpOutCallReply *ocack; struct PptpInCallRequest *icreq; struct PptpInCallReply *icack; struct PptpCallDisconnectNotify *disc; struct PptpWanErrorNotify *wanerr; struct PptpSetLinkInfo *setlink; } pptpReq; struct ip_masq *ms_gre = NULL; /* * The GRE data channel will be treated as the "control channel" * for the purposes of masq because there are keepalives happening * on the control channel, whereas the data channel may be subject * to relatively long periods of inactivity. */ pptph = (struct PptpPacketHeader *)&(((char *)iph)[sizeof(struct iphdr) + sizeof(struct tcphdr)]); #ifdef DEBUG_IP_MASQUERADE_PPTP_VERBOSE printk(KERN_DEBUG "ip_masq_pptp(): "); printk("LEN=%d TY=%d MC=%lX", ntohs(pptph->packetLength), ntohs(pptph->packetType), ntohl(pptph->magicCookie)); printk(" from %s", in_ntoa(iph->saddr)); printk(" to %s\n", in_ntoa(iph->daddr)); #endif /* DEBUG_IP_MASQUERADE_PPTP_VERBOSE */ if (ntohs(pptph->packetType) == PPTP_CONTROL_PACKET && ntohl(pptph->magicCookie) == PPTP_MAGIC_COOKIE) { #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_DEBUG "ip_masq_pptp(): "); printk("PPTP control packet from %s", in_ntoa(iph->saddr)); printk(" to %s\n", in_ntoa(iph->daddr)); #endif /* DEBUG_IP_MASQUERADE_PPTP */ ctlh = (struct PptpControlHeader *)&(((char*)pptph)[sizeof(struct PptpPacketHeader)]); pptpReq.req = &(((char*)ctlh)[sizeof(struct PptpControlHeader)]); #ifdef DEBUG_IP_MASQUERADE_PPTP_VERBOSE printk(KERN_DEBUG "ip_masq_pptp(): "); printk("MTY=%X R0=%X\n", ntohs(ctlh->messageType), ctlh->reserved); #endif /* DEBUG_IP_MASQUERADE_PPTP_VERBOSE */ switch (ntohs(ctlh->messageType)) { case PPTP_OUT_CALL_REQUEST: if (iph->daddr == ms->daddr) /* outbound only */ { #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_DEBUG "ip_masq_pptp(): "); printk("Call request, call ID %X\n", ntohs(pptpReq.ocreq->callID)); #endif /* DEBUG_IP_MASQUERADE_PPTP */ ms_gre = ip_masq_new(dev, IPPROTO_GRE, ms->saddr, pptpReq.ocreq->callID, ms->daddr, 0, 0); if (ms_gre != NULL) { ms->control = ms_gre; ms_gre->flags |= IP_MASQ_F_CONTROL; ip_masq_set_expire(ms_gre, 0); ip_masq_set_expire(ms_gre, 2*60*HZ); pptpReq.ocreq->callID = ms_gre->mport; printk(KERN_INFO "ip_masq_pptp(): "); printk("Req outcall PPTP sess %s", in_ntoa(ms->saddr)); printk(" -> %s", in_ntoa(ms->daddr)); printk(" Call ID %X -> %X.\n", ntohs(ms_gre->sport), ntohs(ms_gre->mport)); #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_DEBUG "ip_masq_pptp(): "); printk("masqed call ID %X\n", ntohs(pptpReq.ocreq->callID)); #endif /* DEBUG_IP_MASQUERADE_PPTP */ } else { printk(KERN_NOTICE "ip_masq_pptp(): "); printk("Couldn't create GRE masq table entry (%s)\n", "OUT_CALL_REQ"); } } break; case PPTP_OUT_CALL_REPLY: if (iph->saddr == ms->daddr) /* inbound (masqueraded client) */ { #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_DEBUG "ip_masq_pptp(): "); printk("Call reply, peer call ID %X\n", ntohs(pptpReq.ocack->peersCallID)); #endif /* DEBUG_IP_MASQUERADE_PPTP */ ms_gre = ip_masq_getbym(IPPROTO_GRE, ms->maddr, pptpReq.ocack->peersCallID); if (ms_gre != NULL) { ip_masq_set_expire(ms_gre, 0); ip_masq_set_expire(ms_gre, 2*60*HZ); pptpReq.ocack->peersCallID = ms_gre->sport; printk(KERN_INFO "ip_masq_pptp(): "); printk("Estab outcall PPTP sess %s", in_ntoa(ms->saddr)); printk(" -> %s", in_ntoa(ms->daddr)); printk(" Call ID %X -> %X.\n", ntohs(ms_gre->sport), ntohs(ms_gre->mport)); #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_DEBUG "ip_masq_pptp(): "); printk("unmasqed call ID %X\n", ntohs(pptpReq.ocack->callID)); #endif /* DEBUG_IP_MASQUERADE_PPTP */ } else { printk(KERN_INFO "ip_masq_pptp(): "); printk("Lost GRE masq table entry (%s)\n", "OUT_CALL_REPLY"); } } break; case PPTP_IN_CALL_REQUEST: if (iph->daddr == ms->daddr) /* outbound only */ { #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_DEBUG "ip_masq_pptp(): "); printk("Call request, call ID %X\n", ntohs(pptpReq.icreq->callID)); #endif /* DEBUG_IP_MASQUERADE_PPTP */ ms_gre = ip_masq_new(dev, IPPROTO_GRE, ms->saddr, pptpReq.icreq->callID, ms->daddr, 0, 0); if (ms_gre != NULL) { ms->control = ms_gre; ms_gre->flags |= IP_MASQ_F_CONTROL; ip_masq_set_expire(ms_gre, 0); ip_masq_set_expire(ms_gre, 2*60*HZ); pptpReq.icreq->callID = ms_gre->mport; printk(KERN_INFO "ip_masq_pptp(): "); printk("Req incall PPTP sess %s", in_ntoa(ms->saddr)); printk(" -> %s", in_ntoa(ms->daddr)); printk(" Call ID %X -> %X.\n", ntohs(ms_gre->sport), ntohs(ms_gre->mport)); #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_DEBUG "ip_masq_pptp(): "); printk("masqed call ID %X\n", ntohs(pptpReq.icreq->callID)); #endif /* DEBUG_IP_MASQUERADE_PPTP */ } else { printk(KERN_NOTICE "ip_masq_pptp(): "); printk("Couldn't create GRE masq table entry (%s)\n", "IN_CALL_REQ"); } } break; case PPTP_IN_CALL_REPLY: if (iph->saddr == ms->daddr) /* inbound (masqueraded client) */ { #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_DEBUG "ip_masq_pptp(): "); printk("Call reply, peer call ID %X\n", ntohs(pptpReq.icack->peersCallID)); #endif /* DEBUG_IP_MASQUERADE_PPTP */ ms_gre = ip_masq_getbym(IPPROTO_GRE, ms->maddr, pptpReq.icack->peersCallID); if (ms_gre != NULL) { ip_masq_set_expire(ms_gre, 0); ip_masq_set_expire(ms_gre, 2*60*HZ); pptpReq.icack->peersCallID = ms_gre->sport; printk(KERN_INFO "ip_masq_pptp(): "); printk("Estab incall PPTP sess %s", in_ntoa(ms->saddr)); printk(" -> %s", in_ntoa(ms->daddr)); printk(" Call ID %X -> %X.\n", ntohs(ms_gre->sport), ntohs(ms_gre->mport)); #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_DEBUG "ip_masq_pptp(): "); printk("unmasqed call ID %X\n", ntohs(pptpReq.icack->callID)); #endif /* DEBUG_IP_MASQUERADE_PPTP */ } else { printk(KERN_INFO "ip_masq_pptp(): "); printk("Lost GRE masq table entry (%s)\n", "IN_CALL_REPLY"); } } break; case PPTP_CALL_DISCONNECT_NOTIFY: if (iph->daddr == ms->daddr) /* outbound only */ { #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_DEBUG "ip_masq_pptp(): "); printk("Disconnect notify, call ID %X\n", ntohs(pptpReq.disc->callID)); #endif /* DEBUG_IP_MASQUERADE_PPTP */ ms_gre = ip_masq_out_get_2(IPPROTO_GRE, iph->saddr, pptpReq.disc->callID, iph->daddr, 0); if (ms_gre != NULL) { /* * expire the data channel * table entry quickly now. */ ip_masq_set_expire(ms_gre, 0); ip_masq_set_expire(ms_gre, 30*HZ); ms->control = NULL; ms_gre->flags &= ~IP_MASQ_F_CONTROL; pptpReq.disc->callID = ms_gre->mport; printk(KERN_INFO "ip_masq_pptp(): "); printk("Disconnect PPTP sess %s", in_ntoa(ms->saddr)); printk(" -> %s", in_ntoa(ms->daddr)); printk(" Call ID %X -> %X.\n", ntohs(ms_gre->sport), ntohs(ms_gre->mport)); #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_DEBUG "ip_masq_pptp(): "); printk("masqed call ID %X\n", ntohs(pptpReq.disc->callID)); #endif /* DEBUG_IP_MASQUERADE_PPTP */ } } break; case PPTP_WAN_ERROR_NOTIFY: if (iph->saddr == ms->daddr) /* inbound only */ { #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_DEBUG "ip_masq_pptp(): "); printk("Error notify, peer call ID %X\n", ntohs(pptpReq.wanerr->peersCallID)); #endif /* DEBUG_IP_MASQUERADE_PPTP */ ms_gre = ip_masq_getbym(IPPROTO_GRE, ms->maddr, pptpReq.wanerr->peersCallID); if (ms_gre != NULL) { pptpReq.wanerr->peersCallID = ms_gre->sport; #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_DEBUG "ip_masq_pptp(): "); printk("unmasqed call ID %X\n", ntohs(pptpReq.wanerr->peersCallID)); #endif /* DEBUG_IP_MASQUERADE_PPTP */ } else { printk(KERN_INFO "ip_masq_pptp(): "); printk("Lost GRE masq table entry (%s)\n", "WAN_ERROR_NOTIFY"); } } break; case PPTP_SET_LINK_INFO: if (iph->saddr == ms->daddr) /* inbound only */ { #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_DEBUG "ip_masq_pptp(): "); printk("Set link info, peer call ID %X\n", ntohs(pptpReq.setlink->peersCallID)); #endif /* DEBUG_IP_MASQUERADE_PPTP */ ms_gre = ip_masq_getbym(IPPROTO_GRE, ms->maddr, pptpReq.setlink->peersCallID); if (ms_gre != NULL) { pptpReq.setlink->peersCallID = ms_gre->sport; #ifdef DEBUG_IP_MASQUERADE_PPTP printk(KERN_DEBUG "ip_masq_pptp(): "); printk("unmasqed call ID %X\n", ntohs(pptpReq.setlink->peersCallID)); #endif /* DEBUG_IP_MASQUERADE_PPTP */ } else { printk(KERN_INFO "ip_masq_pptp(): "); printk("Lost GRE masq table entry (%s)\n", "SET_LINK_INFO"); } } break; } } } #endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ static struct symbol_table pptp_masq_syms = { #include <linux/symtab_begin.h> X(ip_fw_masq_gre), X(ip_fw_demasq_gre), #ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT X(ip_masq_pptp), #endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ #include <linux/symtab_end.h> }; #endif /* CONFIG_IP_MASQUERADE_PPTP */ #ifdef CONFIG_IP_MASQUERADE_IPSEC /* * Quick-and-dirty handling of ESP connections * John Hardin <jhardin@wolfenet.com> gets all blame... */ /* * Handle outbound ESP packets. * * This is largely a copy of ip_fw_masquerade() * * To associate inbound traffic with outbound traffic, we only * allow one session per remote host to be negotiated at a time. * If a packet comes in and there's no masq table entry for it, * then check for other masq table entries for the same server * with the inbound SPI set to zero (i.e. no response yet). If * found, discard the packet. * This will DoS the server for the duration of the connection * attempt, so keep the masq entry's lifetime short until a * response comes in. * If multiple masqueraded hosts are in contention for the same * remote host, enforce round-robin access. This may lead to * misassociation of response traffic if the response is delayed * a great deal, but the masqueraded hosts will clean that up * if it happens. */ int ip_fw_masq_esp(struct sk_buff **skb_p, struct device *dev) { struct sk_buff *skb = *skb_p; struct iphdr *iph = skb->h.iph; struct ip_masq *ms; unsigned long flags; __u32 o_spi; __u16 fake_sport; unsigned long timeout = MASQUERADE_EXPIRE_IPSEC; o_spi = *((__u32 *)&(((char *)iph)[iph->ihl*4])); fake_sport = (__u16) ntohl(o_spi) & 0xffff; #ifdef DEBUG_IP_MASQUERADE_IPSEC printk(KERN_DEBUG "ip_fw_masq_esp(): "); printk("pkt %s", in_ntoa(iph->saddr)); printk(" -> %s SPI %lX (fakeport %X)\n", in_ntoa(iph->daddr), ntohl(o_spi), fake_sport); #endif /* DEBUG_IP_MASQUERADE_IPSEC */ if (o_spi == 0) { /* illegal SPI - discard */ printk(KERN_INFO "ip_fw_masq_esp(): "); printk("zero SPI from %s discarded\n", in_ntoa(iph->saddr)); return -1; } /* * Look for masq table entry */ ms = ip_masq_out_get_ipsec(IPPROTO_ESP, iph->saddr, fake_sport, iph->daddr, 0, o_spi); if (ms!=NULL) { if (ms->ispi == IPSEC_INIT_SQUELCHED) { /* squelched: toss the packet without changing the timer */ #ifdef DEBUG_IP_MASQUERADE_IPSEC printk(KERN_INFO "ip_fw_masq_esp(): "); printk("init %s ", in_ntoa(iph->saddr)); printk("-> %s SPI %lX ", in_ntoa(iph->daddr), ntohl(o_spi)); printk("squelched\n"); #endif /* DEBUG_IP_MASQUERADE_IPSEC */ return -1; } /* delete the expiration timer */ ip_masq_set_expire(ms,0); /* * Make sure that the masq IP address is correct * for dynamic IP... */ if ( (ms->maddr != dev->pa_addr) && (sysctl_ip_dynaddr & 3) ) { printk(KERN_INFO "ip_fw_masq_esp(): "); printk("change maddr from %s", in_ntoa(ms->maddr)); printk(" to %s\n", in_ntoa(dev->pa_addr)); save_flags(flags); cli(); ip_masq_unhash(ms); ms->maddr = dev->pa_addr; ip_masq_hash(ms); restore_flags(flags); } if (ms->ispi == 0) { /* no response yet, keep timeout short */ timeout = MASQUERADE_EXPIRE_IPSEC_INIT; if (ms->blocking) { /* prevent DoS: limit init packet timer resets */ ms->ocnt++; #ifdef DEBUG_IP_MASQUERADE_IPSEC printk(KERN_INFO "ip_fw_masq_esp(): "); printk("init %s ", in_ntoa(iph->saddr)); printk("-> %s SPI %lX ", in_ntoa(iph->daddr), ntohl(o_spi)); printk("retry %d\n", ms->ocnt); #endif /* DEBUG_IP_MASQUERADE_IPSEC */ if (ms->ocnt > IPSEC_INIT_RETRIES) { /* more than IPSEC_INIT_RETRIES tries, give up */ printk(KERN_INFO "ip_fw_masq_esp(): "); printk("init %s ", in_ntoa(iph->saddr)); printk("-> %s SPI %lX ", in_ntoa(iph->daddr), ntohl(o_spi)); printk("no response after %d tries, unblocking & squelching\n", ms->ocnt); /* squelch that source+SPI for a bit */ timeout = 30*HZ; save_flags(flags); cli(); ip_masq_unhash(ms); ms->ispi = IPSEC_INIT_SQUELCHED; ms->dport = IPSEC_INIT_SQUELCHED; ip_masq_hash(ms); restore_flags(flags); ip_masq_set_expire(ms, timeout); /* toss the packet */ return -1; } } } } else { /* * Nope, not found, create a new entry for it, maybe */ /* see if there are any pending inits with the same destination... */ ms = ip_masq_in_get_ipsec(IPPROTO_ESP, iph->daddr, 0, 0, 0, 0); if (ms != NULL) { /* found one with ispi == 0 */ if (ms->saddr != iph->saddr) { /* it's not ours, don't step on their toes */ printk(KERN_INFO "ip_fw_masq_esp(): "); printk("init %s ", in_ntoa(iph->saddr)); printk("-> %s ", in_ntoa(iph->daddr)); printk("temporarily blocked by pending "); printk("%s init\n", in_ntoa(ms->saddr)); /* let it know it has competition */ ms->blocking = 1; /* toss the packet */ return -1; } if (ms->ospi != o_spi) { /* SPIs differ, still waiting for a previous attempt to expire */ printk(KERN_INFO "ip_fw_masq_esp(): "); printk("init %s ", in_ntoa(iph->saddr)); printk("-> %s SPI %lX ", in_ntoa(iph->daddr), ntohl(o_spi)); printk("temporarily blocked by pending "); printk("init w/ SPI %lX\n", ntohl(ms->ospi)); /* let it know it has competition */ ms->blocking = 1; /* toss the packet */ return -1; } } else /* nothing pending, make new entry, pending response */ ms = ip_masq_new(dev, iph->protocol, iph->saddr, fake_sport, iph->daddr, 0, 0); if (ms == NULL) { printk(KERN_NOTICE "ip_fw_masq_esp(): Couldn't create masq table entry.\n"); return -1; } ms->blocking = ms->ocnt = 0; ms->ospi = o_spi; timeout = MASQUERADE_EXPIRE_IPSEC_INIT; /* fairly brief timeout while waiting for a response */ } /* * Set iph source addr from ip_masq obj. */ iph->saddr = ms->maddr; /* * set timeout and check IP header */ ip_masq_set_expire(ms, timeout); ip_send_check(iph); #ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE printk(KERN_DEBUG "MASQ: ESP O-routed from %s over %s\n", in_ntoa(ms->maddr), dev->name); #endif /* DEBUG_IP_MASQUERADE_IPSEC_VERBOSE */ return 0; } /* * Handle inbound ESP packets. * */ int ip_fw_demasq_esp(struct sk_buff **skb_p, struct device *dev) { struct sk_buff *skb = *skb_p; struct iphdr *iph = skb->h.iph; struct ip_masq *ms; unsigned long flags; __u32 i_spi; __u16 fake_sport; #ifndef CONFIG_IP_MASQUERADE_IPSEC_NOGUESS #define ESP_GUESS_SZ 5 /* minimum 3, please */ #define ESP_CAND_MIN_TM 5*60*HZ /* max 10*60*HZ? */ unsigned hash; int i, ii, ncand = 0, nguess = 0; __u16 isakmp; __u32 cand_ip, guess_ip[ESP_GUESS_SZ]; unsigned long cand_tm, guess_tm[ESP_GUESS_SZ]; struct sk_buff *skb_cl; struct iphdr *iph_cl; #endif /* CONFIG_IP_MASQUERADE_IPSEC_NOGUESS */ i_spi = *((__u32 *)&(((char *)iph)[iph->ihl*4])); fake_sport = (__u16) ntohl(i_spi) & 0xffff; #ifdef DEBUG_IP_MASQUERADE_IPSEC printk(KERN_DEBUG "ip_fw_demasq_esp(): "); printk("pkt %s", in_ntoa(iph->saddr)); printk(" -> %s SPI %lX (fakeport %X)\n", in_ntoa(iph->daddr), ntohl(i_spi), fake_sport); #endif /* DEBUG_IP_MASQUERADE_IPSEC */ if (i_spi == 0) { /* illegal SPI - discard */ printk(KERN_INFO "ip_fw_demasq_esp(): "); printk("zero SPI from %s discarded\n", in_ntoa(iph->saddr)); return -1; } if (i_spi == IPSEC_INIT_SQUELCHED) { /* Ack! This shouldn't happen! */ /* IPSEC_INIT_SQUELCHED is chosen to be a reserved value as of 4/99 */ printk(KERN_NOTICE "ip_fw_demasq_esp(): "); printk("SPI from %s is IPSEC_INIT_SQUELCHED - modify ip_masq.c!\n", in_ntoa(iph->saddr)); return -1; } /* * Look for a masq table entry and reroute if found */ ms = ip_masq_in_get_ipsec(IPPROTO_ESP, iph->saddr, fake_sport, iph->daddr, 0, i_spi); if (ms != NULL) { /* delete the expiration timer */ ip_masq_set_expire(ms,0); iph->daddr = ms->saddr; if (ms->ispi == 0) { #ifdef DEBUG_IP_MASQUERADE_IPSEC printk(KERN_INFO "ip_fw_demasq_esp(): "); printk("resp from %s SPI %lX", in_ntoa(iph->saddr), ntohl(i_spi)); printk(" routed to %s (SPI %lX)\n", in_ntoa(ms->saddr), ntohl(ms->ospi)); #endif /* DEBUG_IP_MASQUERADE_IPSEC */ save_flags(flags); cli(); ip_masq_unhash(ms); ms->ispi = i_spi; ms->dport = fake_sport; ip_masq_hash(ms); restore_flags(flags); } /* * resum checksums and set timeout */ ip_masq_set_expire(ms, MASQUERADE_EXPIRE_IPSEC); ip_send_check(iph); #ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE printk(KERN_DEBUG "MASQ: ESP I-routed to %s\n", in_ntoa(iph->daddr)); #endif /* DEBUG_IP_MASQUERADE_IPSEC_VERBOSE */ return 1; } #ifndef CONFIG_IP_MASQUERADE_IPSEC_NOGUESS /* Guess who this packet is likely intended for: * Scan the UDP masq table for local hosts that have communicated via * ISAKMP with the host who sent this packet. * Using an insertion sort with duplicate IP suppression, build a list * of the ESP_GUESS_SZ most recent ISAKMP sessions (determined by * sorting in decreasing order of timeout timer). * Clone the original packet and send it to those hosts, but DON'T make * a masq table entry, as we're only guessing. It is assumed that the correct * host will respond to the traffic and that will create a masq table entry. * To limit the list a bit, don't consider any ISAKMP masq entries with * less than ESP_CAND_MIN_TM time to live. This should be some value less * than the IPSEC table timeout or *all* entries will be ignored... */ #ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE printk(KERN_DEBUG "ip_fw_demasq_esp(): "); printk("guessing from %s SPI %lX\n", in_ntoa(iph->saddr), ntohl(i_spi)); #endif /* DEBUG_IP_MASQUERADE_IPSEC_VERBOSE */ /* zero out the guess table */ for (i = 0;i < ESP_GUESS_SZ; i++) { guess_ip[i] = 0; guess_tm[i] = 0; } /* scan ISAKMP sessions with the source host */ isakmp = htons(UDP_PORT_ISAKMP); hash = ip_masq_hash_key(IPPROTO_UDP, iph->saddr, isakmp); for(ms = ip_masq_d_tab[hash]; ms ; ms = ms->d_link) { if (ms->protocol == IPPROTO_UDP && ms->daddr == iph->saddr && ms->sport == isakmp && ms->dport == isakmp && ms->mport == isakmp && ms->ospi != 0) { /* a candidate... */ ncand++; cand_ip = ms->saddr; cand_tm = ms->timer.expires - jiffies; #ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE printk(KERN_DEBUG "ip_fw_demasq_esp(): "); printk("cand %d: IP %s TM %ld\n", ncand, in_ntoa(cand_ip), cand_tm); #endif /* DEBUG_IP_MASQUERADE_IPSEC_VERBOSE */ if (cand_tm > ESP_CAND_MIN_TM) { /* traffic is recent enough, add to list (maybe) */ for (i = 0; i < ESP_GUESS_SZ; i++) { if (cand_tm > guess_tm[i]) { /* newer */ if (guess_ip[i] != 0 && cand_ip != guess_ip[i]) { /* newer and IP different - insert */ if (i < (ESP_GUESS_SZ - 1)) { /* move entries down the list, * find first entry after this slot * where the IP is 0 (unused) or * IP == candidate (older traffic, same host) * rather than simply going to the end of the list, * for efficiency (don't shift zeros) and * duplicate IP suppression (don't keep older entries * having the same IP) */ for (ii = i + 1; ii < (ESP_GUESS_SZ - 1); ii++) { if (guess_ip[ii] == 0 || guess_ip[ii] == cand_ip) break; } for (ii-- ; ii >= i; ii--) { guess_ip[ii+1] = guess_ip[ii]; guess_tm[ii+1] = guess_tm[ii]; } } } guess_ip[i] = cand_ip; guess_tm[i] = cand_tm; break; } if (cand_ip == guess_ip[i]) { /* fresher entry already there */ break; } } } } } if (guess_ip[0]) { /* had guesses - send */ if (guess_ip[1]) { /* multiple guesses, send a copy to all */ for (i = 0; guess_ip[i] != 0; i++) { nguess++; #ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE printk(KERN_DEBUG "ip_fw_demasq_esp(): "); printk("guess %d: IP %s TM %ld\n", nguess, in_ntoa(guess_ip[i]), guess_tm[i]); #endif /* DEBUG_IP_MASQUERADE_IPSEC_VERBOSE */ /* duplicate and send the skb */ if ((skb_cl = skb_copy(skb, GFP_ATOMIC)) == NULL) { printk(KERN_INFO "ip_fw_demasq_esp(): "); printk("guessing: cannot copy skb\n"); } else { iph_cl = skb_cl->h.iph; iph_cl->daddr = guess_ip[i]; ip_send_check(iph_cl); ip_forward(skb_cl, dev, IPFWD_MASQUERADED, iph_cl->daddr); kfree_skb(skb_cl, FREE_WRITE); } } #ifdef DEBUG_IP_MASQUERADE_IPSEC printk(KERN_INFO "ip_fw_demasq_esp(): "); printk("guessing from %s SPI %lX sent to", in_ntoa(iph->saddr), ntohl(i_spi)); printk(" %d hosts (%d cand)\n", nguess, ncand); #endif /* DEBUG_IP_MASQUERADE_IPSEC */ return -1; /* discard original packet */ } else { /* only one guess, send original packet to that host */ iph->daddr = guess_ip[0]; ip_send_check(iph); #ifdef DEBUG_IP_MASQUERADE_IPSEC printk(KERN_INFO "ip_fw_demasq_esp(): "); printk("guessing from %s SPI %lX sent to", in_ntoa(iph->saddr), ntohl(i_spi)); printk(" %s (%d cand)\n", in_ntoa(guess_ip[0]), ncand); #endif /* DEBUG_IP_MASQUERADE_IPSEC */ return 1; } } #endif /* CONFIG_IP_MASQUERADE_IPSEC_NOGUESS */ /* sorry, all this trouble for a no-hit :) */ #if 0 printk(KERN_INFO "ip_fw_demasq_esp(): "); printk("Inbound from %s SPI %lX has no masq table entry.\n", in_ntoa(iph->saddr), ntohl(i_spi)); #endif return 0; } static struct symbol_table ipsec_masq_syms = { #include <linux/symtab_begin.h> X(ip_masq_out_get_ipsec), X(ip_masq_in_get_ipsec), X(ip_masq_out_get_isakmp), X(ip_masq_in_get_isakmp), X(ip_fw_masq_esp), X(ip_fw_demasq_esp), #include <linux/symtab_end.h> }; #endif /* CONFIG_IP_MASQUERADE_IPSEC */ int ip_fw_masquerade(struct sk_buff **skb_ptr, struct device *dev) { struct sk_buff *skb=*skb_ptr; struct iphdr *iph = skb->h.iph; __u16 *portptr; struct ip_masq *ms; int size; unsigned long timeout; /* * We can only masquerade protocols with ports... * [TODO] * We may need to consider masq-ing some ICMP related to masq-ed protocols */ if (iph->protocol==IPPROTO_ICMP) return (ip_fw_masq_icmp(skb_ptr,dev)); #ifdef CONFIG_IP_MASQUERADE_PPTP if (iph->protocol==IPPROTO_GRE) return (ip_fw_masq_gre(skb_ptr,dev)); #endif /* CONFIG_IP_MASQUERADE_PPTP */ #ifdef CONFIG_IP_MASQUERADE_IPSEC if (iph->protocol==IPPROTO_ESP) return (ip_fw_masq_esp(skb_ptr,dev)); #endif /* CONFIG_IP_MASQUERADE_IPSEC */ if (iph->protocol!=IPPROTO_UDP && iph->protocol!=IPPROTO_TCP) return -1; /* * Now hunt the list to see if we have an old entry */ portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]); #ifdef DEBUG_CONFIG_IP_MASQUERADE printk("Outgoing %s %lX:%X -> %lX:%X\n", masq_proto_name(iph->protocol), ntohl(iph->saddr), ntohs(portptr[0]), ntohl(iph->daddr), ntohs(portptr[1])); #endif ms = ip_masq_out_get(iph); if (ms!=NULL) { ip_masq_set_expire(ms,0); /* * If sysctl & 3 and either * no pkt has been received yet * or * sysctl & 4 * in this tunnel ... * "You are welcome, diald, ipppd, pppd-3.3...". */ if ( (sysctl_ip_dynaddr & 3) && (ms->flags & IP_MASQ_F_NO_REPLY || sysctl_ip_dynaddr & 4) && dev->pa_addr != ms->maddr) { unsigned long flags; if (sysctl_ip_dynaddr & 2) { printk(KERN_INFO "ip_fw_masquerade(): change maddr from %s", in_ntoa(ms->maddr)); printk(" to %s\n", in_ntoa(dev->pa_addr)); } save_flags(flags); cli(); ip_masq_unhash(ms); ms->maddr = dev->pa_addr; ip_masq_hash(ms); restore_flags(flags); } /* * Set sport if not defined yet (e.g. ftp PASV). Because * masq entries are hashed on sport, unhash with old value * and hash with new. */ if ( ms->flags & IP_MASQ_F_NO_SPORT && ms->protocol == IPPROTO_TCP ) { unsigned long flags; ms->flags &= ~IP_MASQ_F_NO_SPORT; save_flags(flags); cli(); ip_masq_unhash(ms); ms->sport = portptr[0]; ip_masq_hash(ms); /* hash on new sport */ restore_flags(flags); #ifdef DEBUG_CONFIG_IP_MASQUERADE printk("ip_fw_masquerade(): filled sport=%d\n", ntohs(ms->sport)); #endif } } #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW /* update any ipautofw entries .. */ ip_autofw_update_out(iph->saddr, iph->daddr, portptr[1], iph->protocol); #endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ /* * Nope, not found, create a new entry for it */ if (ms==NULL) { #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW /* if the source port is supposed to match the masq port, then make it so */ if (ip_autofw_check_direct(portptr[1],iph->protocol)) ms = ip_masq_new_enh(dev, iph->protocol, iph->saddr, portptr[0], iph->daddr, portptr[1], 0, portptr[0]); else #endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ ms = ip_masq_new_enh(dev, iph->protocol, iph->saddr, portptr[0], iph->daddr, portptr[1], 0, 0); if (ms == NULL) return -1; #ifdef CONFIG_IP_MASQUERADE_IPSEC if (iph->protocol == IPPROTO_UDP && ntohs(portptr[0]) == UDP_PORT_ISAKMP && ntohs(portptr[1]) == UDP_PORT_ISAKMP) { /* save the initiator cookie */ ms->ospi = *((__u32 *)&portptr[4]); } #endif /* CONFIG_IP_MASQUERADE_IPSEC */ } #ifdef CONFIG_IP_MASQUERADE_PPTP #ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT if (iph->protocol == IPPROTO_TCP && ntohs(portptr[1]) == PPTP_CONTROL_PORT) { /* * Packet sent to PPTP control port. Process it. * May change call ID word in request, but * packet length will not change. */ ip_masq_pptp(skb, ms, dev); } #endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ #endif /* CONFIG_IP_MASQUERADE_PPTP */ /* * Change the fragments origin */ size = skb->len - ((unsigned char *)portptr - skb->h.raw); /* * Set iph addr and port from ip_masq obj. */ iph->saddr = ms->maddr; portptr[0] = ms->mport; /* * Attempt ip_masq_app call. * will fix ip_masq and iph seq stuff */ if (ip_masq_app_pkt_out(ms, skb_ptr, dev) != 0) { /* * skb has possibly changed, update pointers. */ skb = *skb_ptr; iph = skb->h.iph; portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]); size = skb->len - ((unsigned char *)portptr-skb->h.raw); } /* * Adjust packet accordingly to protocol */ if (masq_proto_num(iph->protocol)==0) { #ifdef CONFIG_IP_MASQUERADE_IPSEC if (iph->protocol == IPPROTO_UDP && ntohs(portptr[0]) == UDP_PORT_ISAKMP && ntohs(portptr[1]) == UDP_PORT_ISAKMP) { /* ISAKMP timeout should be same as ESP timeout to allow for rekeying */ timeout = MASQUERADE_EXPIRE_IPSEC; } else #endif /* CONFIG_IP_MASQUERADE_IPSEC */ timeout = ip_masq_expire->udp_timeout; recalc_check((struct udphdr *)portptr,iph->saddr,iph->daddr,size); } else { struct tcphdr *th; th = (struct tcphdr *)portptr; /* Set the flags up correctly... */ if (th->fin) { ms->flags |= IP_MASQ_F_SAW_FIN_OUT; } if (th->rst) { ms->flags |= IP_MASQ_F_SAW_RST; } /* * Timeout depends if FIN packet has been seen * Very short timeout if RST packet seen. */ if (ms->flags & IP_MASQ_F_SAW_RST) { timeout = 1; } else if ((ms->flags & IP_MASQ_F_SAW_FIN) == IP_MASQ_F_SAW_FIN) { timeout = ip_masq_expire->tcp_fin_timeout; } else timeout = ip_masq_expire->tcp_timeout; skb->csum = csum_partial((void *)(th + 1), size - sizeof(*th), 0); tcp_send_check(th,iph->saddr,iph->daddr,size,skb); } ip_masq_set_expire(ms, timeout); ip_send_check(iph); #ifdef DEBUG_CONFIG_IP_MASQUERADE printk("O-routed from %lX:%X over %s\n",ntohl(ms->maddr),ntohs(ms->mport),dev->name); #endif return 0; } /* * Handle ICMP messages in forward direction. * Find any that might be relevant, check against existing connections, * forward to masqueraded host if relevant. * Currently handles error types - unreachable, quench, ttl exceeded */ int ip_fw_masq_icmp(struct sk_buff **skb_p, struct device *dev) { struct sk_buff *skb = *skb_p; struct iphdr *iph = skb->h.iph; struct icmphdr *icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2)); struct iphdr *ciph; /* The ip header contained within the ICMP */ __u16 *pptr; /* port numbers from TCP/UDP contained header */ struct ip_masq *ms; unsigned short len = ntohs(iph->tot_len) - (iph->ihl * 4); #ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP printk("Incoming forward ICMP (%d,%d) %lX -> %lX\n", icmph->type, ntohs(icmp_id(icmph)), ntohl(iph->saddr), ntohl(iph->daddr)); #endif #ifdef CONFIG_IP_MASQUERADE_ICMP if ((icmph->type == ICMP_ECHO ) || (icmph->type == ICMP_TIMESTAMP ) || (icmph->type == ICMP_INFO_REQUEST ) || (icmph->type == ICMP_ADDRESS )) { #ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP printk("MASQ: icmp request rcv %lX->%lX id %d type %d\n", ntohl(iph->saddr), ntohl(iph->daddr), ntohs(icmp_id(icmph)), icmph->type); #endif ms = ip_masq_out_get_2(iph->protocol, iph->saddr, icmp_id(icmph), iph->daddr, icmp_hv_req(icmph)); if (ms == NULL) { ms = ip_masq_new(dev, iph->protocol, iph->saddr, icmp_id(icmph), iph->daddr, icmp_hv_req(icmph), 0); if (ms == NULL) return (-1); #ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP printk("MASQ: Create new icmp entry\n"); #endif } ip_masq_set_expire(ms, 0); /* Rewrite source address */ /* * If sysctl & 3 and either * no pkt has been received yet * or * sysctl & 4 * in this tunnel ... * "You are welcome, diald, ipppd, pppd-3.3...". */ if ( (sysctl_ip_dynaddr & 3) && (ms->flags & IP_MASQ_F_NO_REPLY || sysctl_ip_dynaddr & 4) && dev->pa_addr != ms->maddr) { unsigned long flags; #ifdef DEBUG_CONFIG_IP_MASQUERADE printk(KERN_INFO "ip_fw_masq_icmp(): change masq.addr %s", in_ntoa(ms->maddr)); printk("-> %s\n", in_ntoa(dev->pa_addr)); #endif save_flags(flags); cli(); ip_masq_unhash(ms); ms->maddr = dev->pa_addr; ip_masq_hash(ms); restore_flags(flags); } iph->saddr = ms->maddr; ip_send_check(iph); /* Rewrite port (id) */ (icmph->un).echo.id = ms->mport; icmph->checksum = 0; icmph->checksum = ip_compute_csum((unsigned char *)icmph, len); ip_masq_set_expire(ms, MASQUERADE_EXPIRE_ICMP); #ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP printk("MASQ: icmp request rwt %lX->%lX id %d type %d\n", ntohl(iph->saddr), ntohl(iph->daddr), ntohs(icmp_id(icmph)), icmph->type); #endif return (1); } #endif /* * Work through seeing if this is for us. * These checks are supposed to be in an order that * means easy things are checked first to speed up * processing.... however this means that some * packets will manage to get a long way down this * stack and then be rejected, but thats life */ if ((icmph->type != ICMP_DEST_UNREACH) && (icmph->type != ICMP_SOURCE_QUENCH) && (icmph->type != ICMP_TIME_EXCEEDED)) return 0; /* Now find the contained IP header */ ciph = (struct iphdr *) (icmph + 1); #ifdef CONFIG_IP_MASQUERADE_ICMP if (ciph->protocol == IPPROTO_ICMP) { /* * This section handles ICMP errors for ICMP packets */ struct icmphdr *cicmph = (struct icmphdr *)((char *)ciph + (ciph->ihl<<2)); #ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP printk("MASQ: fw icmp/icmp rcv %lX->%lX id %d type %d\n", ntohl(ciph->saddr), ntohl(ciph->daddr), ntohs(icmp_id(cicmph)), cicmph->type); #endif ms = ip_masq_out_get_2(ciph->protocol, ciph->daddr, icmp_id(cicmph), ciph->saddr, icmp_hv_rep(cicmph)); if (ms == NULL) return 0; /* Now we do real damage to this packet...! */ /* First change the source IP address, and recalc checksum */ iph->saddr = ms->maddr; ip_send_check(iph); /* Now change the *dest* address in the contained IP */ ciph->daddr = ms->maddr; ip_send_check(ciph); /* Change the ID to the masqed one! */ (cicmph->un).echo.id = ms->mport; /* And finally the ICMP checksum */ icmph->checksum = 0; icmph->checksum = ip_compute_csum((unsigned char *) icmph, len); #ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP printk("MASQ: fw icmp/icmp rwt %lX->%lX id %d type %d\n", ntohl(ciph->saddr), ntohl(ciph->daddr), ntohs(icmp_id(cicmph)), cicmph->type); #endif return 1; } #endif /* CONFIG_IP_MASQUERADE_ICMP */ /* We are only interested ICMPs generated from TCP or UDP packets */ if ((ciph->protocol != IPPROTO_UDP) && (ciph->protocol != IPPROTO_TCP)) return 0; /* * Find the ports involved - this packet was * incoming so the ports are right way round * (but reversed relative to outer IP header!) */ pptr = (__u16 *)&(((char *)ciph)[ciph->ihl*4]); /* Ensure the checksum is correct */ if (ip_compute_csum((unsigned char *) icmph, len)) { /* Failed checksum! */ printk(KERN_DEBUG "MASQ: forward ICMP: failed checksum from %s!\n", in_ntoa(iph->saddr)); return(-1); } #ifdef DEBUG_CONFIG_IP_MASQUERADE printk("Handling forward ICMP for %lX:%X -> %lX:%X\n", ntohl(ciph->saddr), ntohs(pptr[0]), ntohl(ciph->daddr), ntohs(pptr[1])); #endif /* This is pretty much what ip_masq_out_get() does */ ms = ip_masq_out_get_2(ciph->protocol, ciph->daddr, pptr[1], ciph->saddr, pptr[0]); if (ms == NULL) return 0; /* Now we do real damage to this packet...! */ /* First change the source IP address, and recalc checksum */ iph->saddr = ms->maddr; ip_send_check(iph); /* Now change the *dest* address in the contained IP */ ciph->daddr = ms->maddr; ip_send_check(ciph); /* the TCP/UDP dest port - cannot redo check */ pptr[1] = ms->mport; /* And finally the ICMP checksum */ icmph->checksum = 0; icmph->checksum = ip_compute_csum((unsigned char *) icmph, len); #ifdef DEBUG_CONFIG_IP_MASQUERADE printk("Rewrote forward ICMP to %lX:%X -> %lX:%X\n", ntohl(ciph->saddr), ntohs(pptr[0]), ntohl(ciph->daddr), ntohs(pptr[1])); #endif return 1; } /* * Handle ICMP messages in reverse (demasquerade) direction. * Find any that might be relevant, check against existing connections, * forward to masqueraded host if relevant. * Currently handles error types - unreachable, quench, ttl exceeded */ int ip_fw_demasq_icmp(struct sk_buff **skb_p, struct device *dev) { struct sk_buff *skb = *skb_p; struct iphdr *iph = skb->h.iph; struct icmphdr *icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2)); struct iphdr *ciph; /* The ip header contained within the ICMP */ __u16 *pptr; /* port numbers from TCP/UDP contained header */ struct ip_masq *ms; unsigned short len = ntohs(iph->tot_len) - (iph->ihl * 4); #ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP printk("MASQ: icmp in/rev (%d,%d) %lX -> %lX\n", icmph->type, ntohs(icmp_id(icmph)), ntohl(iph->saddr), ntohl(iph->daddr)); #endif #ifdef CONFIG_IP_MASQUERADE_ICMP if ((icmph->type == ICMP_ECHOREPLY) || (icmph->type == ICMP_TIMESTAMPREPLY) || (icmph->type == ICMP_INFO_REPLY) || (icmph->type == ICMP_ADDRESSREPLY)) { #ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP printk("MASQ: icmp reply rcv %lX->%lX id %d type %d, req %d\n", ntohl(iph->saddr), ntohl(iph->daddr), ntohs(icmp_id(icmph)), icmph->type, icmp_type_request(icmph->type)); #endif ms = ip_masq_in_get_2(iph->protocol, iph->saddr, icmp_hv_rep(icmph), iph->daddr, icmp_id(icmph)); if (ms == NULL) return 0; ip_masq_set_expire(ms,0); /* * got reply, so clear flag */ ms->flags &= ~IP_MASQ_F_NO_REPLY; /* Reset source address */ iph->daddr = ms->saddr; /* Redo IP header checksum */ ip_send_check(iph); /* Set ID to fake port number */ (icmph->un).echo.id = ms->sport; /* Reset ICMP checksum and set expiry */ icmph->checksum=0; icmph->checksum=ip_compute_csum((unsigned char *)icmph,len); ip_masq_set_expire(ms, MASQUERADE_EXPIRE_ICMP); #ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP printk("MASQ: icmp reply rwt %lX->%lX id %d type %d\n", ntohl(iph->saddr), ntohl(iph->daddr), ntohs(icmp_id(icmph)), icmph->type); #endif return 1; } else { #endif if ((icmph->type != ICMP_DEST_UNREACH) && (icmph->type != ICMP_SOURCE_QUENCH) && (icmph->type != ICMP_TIME_EXCEEDED)) return 0; #ifdef CONFIG_IP_MASQUERADE_ICMP } #endif /* * If we get here we have an ICMP error of one of the above 3 types * Now find the contained IP header */ ciph = (struct iphdr *) (icmph + 1); #ifdef CONFIG_IP_MASQUERADE_ICMP if (ciph->protocol == IPPROTO_ICMP) { /* * This section handles ICMP errors for ICMP packets * * First get a new ICMP header structure out of the IP packet */ struct icmphdr *cicmph = (struct icmphdr *)((char *)ciph + (ciph->ihl<<2)); #ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP printk("MASQ: rv icmp/icmp rcv %lX->%lX id %d type %d\n", ntohl(ciph->saddr), ntohl(ciph->daddr), ntohs(icmp_id(cicmph)), cicmph->type); #endif ms = ip_masq_in_get_2(ciph->protocol, ciph->daddr, icmp_hv_req(cicmph), ciph->saddr, icmp_id(cicmph)); if (ms == NULL) return 0; /* Now we do real damage to this packet...! */ /* First change the dest IP address, and recalc checksum */ iph->daddr = ms->saddr; ip_send_check(iph); /* Now change the *source* address in the contained IP */ ciph->saddr = ms->saddr; ip_send_check(ciph); /* Change the ID to the original one! */ (cicmph->un).echo.id = ms->sport; /* And finally the ICMP checksum */ icmph->checksum = 0; icmph->checksum = ip_compute_csum((unsigned char *) icmph, len); #ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP printk("MASQ: rv icmp/icmp rwt %lX->%lX id %d type %d\n", ntohl(ciph->saddr), ntohl(ciph->daddr), ntohs(icmp_id(cicmph)), cicmph->type); #endif return 1; } #endif /* CONFIG_IP_MASQUERADE_ICMP */ /* We are only interested ICMPs generated from TCP or UDP packets */ if ((ciph->protocol != IPPROTO_UDP) && (ciph->protocol != IPPROTO_TCP)) return 0; /* * Find the ports involved - remember this packet was * *outgoing* so the ports are reversed (and addresses) */ pptr = (__u16 *)&(((char *)ciph)[ciph->ihl*4]); if (ntohs(pptr[0]) < PORT_MASQ_BEGIN || ntohs(pptr[0]) > PORT_MASQ_END) return 0; /* Ensure the checksum is correct */ if (ip_compute_csum((unsigned char *) icmph, len)) { /* Failed checksum! */ printk(KERN_DEBUG "MASQ: reverse ICMP: failed checksum from %s!\n", in_ntoa(iph->saddr)); return(-1); } #ifdef DEBUG_CONFIG_IP_MASQUERADE printk("Handling reverse ICMP for %lX:%X -> %lX:%X\n", ntohl(ciph->saddr), ntohs(pptr[0]), ntohl(ciph->daddr), ntohs(pptr[1])); #endif /* This is pretty much what ip_masq_in_get() does, except params are wrong way round */ ms = ip_masq_in_get_2(ciph->protocol, ciph->daddr, pptr[1], ciph->saddr, pptr[0]); if (ms == NULL) return 0; /* Now we do real damage to this packet...! */ /* First change the dest IP address, and recalc checksum */ iph->daddr = ms->saddr; ip_send_check(iph); /* Now change the *source* address in the contained IP */ ciph->saddr = ms->saddr; ip_send_check(ciph); /* the TCP/UDP source port - cannot redo check */ pptr[0] = ms->sport; /* And finally the ICMP checksum */ icmph->checksum = 0; icmph->checksum = ip_compute_csum((unsigned char *) icmph, len); #ifdef DEBUG_CONFIG_IP_MASQUERADE printk("Rewrote reverse ICMP to %lX:%X -> %lX:%X\n", ntohl(ciph->saddr), ntohs(pptr[0]), ntohl(ciph->daddr), ntohs(pptr[1])); #endif return 1; } /* * Check if it's an masqueraded port, look it up, * and send it on its way... * * Better not have many hosts using the designated portrange * as 'normal' ports, or you'll be spending many time in * this function. */ int ip_fw_demasquerade(struct sk_buff **skb_p, struct device *dev) { struct sk_buff *skb = *skb_p; struct iphdr *iph = skb->h.iph; __u16 *portptr; struct ip_masq *ms; unsigned short len; unsigned long timeout = MASQUERADE_EXPIRE_TCP; #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW struct ip_autofw *af; #endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ switch (iph->protocol) { case IPPROTO_ICMP: return(ip_fw_demasq_icmp(skb_p, dev)); #ifdef CONFIG_IP_MASQUERADE_PPTP case IPPROTO_GRE: return(ip_fw_demasq_gre(skb_p, dev)); #endif /* CONFIG_IP_MASQUERADE_PPTP */ #ifdef CONFIG_IP_MASQUERADE_IPSEC case IPPROTO_ESP: return(ip_fw_demasq_esp(skb_p, dev)); #endif /* CONFIG_IP_MASQUERADE_IPSEC */ case IPPROTO_TCP: case IPPROTO_UDP: /* Make sure packet is in the masq range */ portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]); if ((ntohs(portptr[1]) < PORT_MASQ_BEGIN || ntohs(portptr[1]) > PORT_MASQ_END) #ifdef CONFIG_IP_MASQUERADE_IPSEC && ((iph->protocol != IPPROTO_UDP) || (ntohs(portptr[0]) != UDP_PORT_ISAKMP) || (ntohs(portptr[1]) != UDP_PORT_ISAKMP)) #endif /* CONFIG_IP_MASQUERADE_IPSEC */ #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW && !ip_autofw_check_range(iph->saddr, portptr[1], iph->protocol, 0) && !ip_autofw_check_direct(portptr[1], iph->protocol) && !ip_autofw_check_port(portptr[1], iph->protocol) #endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ #ifdef CONFIG_IP_MASQUERADE_IPPORTFW && !ip_portfw_check(iph->protocol, portptr[1], iph->daddr) #endif /* CONFIG_IP_MASQUERADE_IPPORTFW */ ) return 0; /* Check that the checksum is OK */ len = ntohs(iph->tot_len) - (iph->ihl * 4); if ((iph->protocol == IPPROTO_UDP) && (portptr[3] == 0)) /* No UDP checksum */ break; switch (skb->ip_summed) { case CHECKSUM_NONE: skb->csum = csum_partial((char *)portptr, len, 0); case CHECKSUM_HW: if (csum_tcpudp_magic(iph->saddr, iph->daddr, len, iph->protocol, skb->csum)) { printk(KERN_DEBUG "MASQ: failed TCP/UDP checksum from %s!\n", in_ntoa(iph->saddr)); return -1; } default: /* CHECKSUM_UNNECESSARY */ } break; default: return 0; } #ifdef DEBUG_CONFIG_IP_MASQUERADE printk("Incoming %s %lX:%X -> %lX:%X\n", masq_proto_name(iph->protocol), ntohl(iph->saddr), ntohs(portptr[0]), ntohl(iph->daddr), ntohs(portptr[1])); #endif /* * reroute to original host:port if found... */ ms = ip_masq_in_get(iph); #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW if (ms == NULL && (af=ip_autofw_check_range(iph->saddr, portptr[1], iph->protocol, 0))) { #ifdef DEBUG_CONFIG_IP_MASQUERADE printk("ip_autofw_check_range\n"); #endif ms = ip_masq_new_enh(dev, iph->protocol, af->where, portptr[1], iph->saddr, portptr[0], 0, portptr[1]); } if ( ms == NULL && (af=ip_autofw_check_port(portptr[1], iph->protocol)) ) { #ifdef DEBUG_CONFIG_IP_MASQUERADE printk("ip_autofw_check_port\n"); #endif ms = ip_masq_new_enh(dev, iph->protocol, af->where, htons(af->hidden), iph->saddr, portptr[0], IP_MASQ_F_AFW_PORT, htons(af->visible)); } #endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ #ifdef CONFIG_IP_MASQUERADE_IPPORTFW /* If no entry exists in the masquerading table, and the port is involved in port forwarding, create a new entry */ { __u32 raddr; __u16 rport; if ((ms == NULL) && (iph->protocol==IPPROTO_TCP || iph->protocol==IPPROTO_UDP) && ip_portfw_lookup(iph->protocol, portptr[1], iph->daddr, &raddr, &rport)) { ms = ip_masq_new_portfw(dev, iph->protocol, raddr, rport, iph->saddr, portptr[0], iph->daddr, portptr[1]); } } #endif /* CONFIG_IP_MASQUERADE_IPPORTFW */ if (ms != NULL) { #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW ip_autofw_update_in(iph->saddr, portptr[1], iph->protocol); #endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ /* Stop the timer ticking.... */ ip_masq_set_expire(ms,0); /* * got reply, so clear flag */ ms->flags &= ~IP_MASQ_F_NO_REPLY; /* * Set dport if not defined yet. */ if ( ms->flags & IP_MASQ_F_NO_DPORT && ms->protocol == IPPROTO_TCP ) { ms->flags &= ~IP_MASQ_F_NO_DPORT; ms->dport = portptr[0]; #ifdef DEBUG_CONFIG_IP_MASQUERADE printk("ip_fw_demasquerade(): filled dport=%d\n", ntohs(ms->dport)); #endif } if (ms->flags & IP_MASQ_F_NO_DADDR && ms->protocol == IPPROTO_TCP) { ms->flags &= ~IP_MASQ_F_NO_DADDR; ms->daddr = iph->saddr; #ifdef DEBUG_CONFIG_IP_MASQUERADE printk("ip_fw_demasquerade(): filled daddr=%X\n", ntohs(ms->daddr)); #endif } iph->daddr = ms->saddr; portptr[1] = ms->sport; /* * Attempt ip_masq_app call. * will fix ip_masq and iph ack_seq stuff */ if (ip_masq_app_pkt_in(ms, skb_p, dev) != 0) { /* * skb has changed, update pointers. */ skb = *skb_p; iph = skb->h.iph; portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]); len = ntohs(iph->tot_len) - (iph->ihl * 4); } #ifdef CONFIG_IP_MASQUERADE_PPTP #ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT if (iph->protocol == IPPROTO_TCP && ntohs(portptr[0]) == PPTP_CONTROL_PORT) { /* * Packet received from PPTP control port. Process it. * May change call ID word in request, but * packet length will not change. */ ip_masq_pptp(skb, ms, dev); } #endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ #endif /* CONFIG_IP_MASQUERADE_PPTP */ /* * Yug! adjust UDP/TCP and IP checksums, also update * timeouts. * If a TCP RST is seen collapse the tunnel (by using short timeout)! */ if (masq_proto_num(iph->protocol)==0) { recalc_check((struct udphdr *)portptr,iph->saddr,iph->daddr,len); #ifdef CONFIG_IP_MASQUERADE_IPSEC if (iph->protocol == IPPROTO_UDP && ntohs(portptr[0]) == UDP_PORT_ISAKMP && ntohs(portptr[1]) == UDP_PORT_ISAKMP) { /* ISAKMP timeout should be same as ESP timeout to allow for rekeying */ timeout = MASQUERADE_EXPIRE_IPSEC; } else #endif /* CONFIG_IP_MASQUERADE_IPSEC */ timeout = ip_masq_expire->udp_timeout; } else { struct tcphdr *th; if(len>=sizeof(struct tcphdr)) { skb->csum = csum_partial((void *)(((struct tcphdr *)portptr) + 1), len - sizeof(struct tcphdr), 0); tcp_send_check((struct tcphdr *)portptr,iph->saddr,iph->daddr,len,skb); /* Check if TCP FIN or RST */ th = (struct tcphdr *)portptr; if (th->fin) { ms->flags |= IP_MASQ_F_SAW_FIN_IN; } if (th->rst) { ms->flags |= IP_MASQ_F_SAW_RST; } /* Now set the timeouts */ if (ms->flags & IP_MASQ_F_SAW_RST) { timeout = 1; } else if ((ms->flags & IP_MASQ_F_SAW_FIN) == IP_MASQ_F_SAW_FIN) { timeout = ip_masq_expire->tcp_fin_timeout; } else timeout = ip_masq_expire->tcp_timeout; } } ip_masq_set_expire(ms, timeout); ip_send_check(iph); #ifdef DEBUG_CONFIG_IP_MASQUERADE printk("I-routed to %lX:%X\n",ntohl(iph->daddr),ntohs(portptr[1])); #endif return 1; } /* sorry, all this trouble for a no-hit :) */ return 0; } /* * /proc/net entries */ #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW static int ip_autofw_procinfo(char *buffer, char **start, off_t offset, int length, int unused) { off_t pos=0, begin=0; struct ip_autofw * af; int len=0; len=sprintf(buffer,"Type Prot Low High Vis Hid Where Last CPto CPrt Timer Flags\n"); for(af = ip_autofw_hosts; af ; af = af->next) { len+=sprintf(buffer+len,"%4X %4X %04X-%04X/%04X %04X %08lX %08lX %04X %04X %6lu %4X\n", af->type, af->protocol, af->low, af->high, af->visible, af->hidden, ntohl(af->where), ntohl(af->lastcontact), af->ctlproto, af->ctlport, (af->timer.expires<jiffies ? 0 : af->timer.expires-jiffies), af->flags); pos=begin+len; if(pos<offset) { len=0; begin=pos; } if(pos>offset+length) break; } *start=buffer+(offset-begin); len-=(offset-begin); if(len>length) len=length; return len; } #endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ #ifdef CONFIG_IP_MASQUERADE_IPPORTFW static int ip_portfw_procinfo(char *buffer, char **start, off_t offset, int length, int unused) { off_t pos=0, begin; struct ip_portfw *pf; unsigned long flags; char temp[65]; int ind, raddr, laddr; int len=0; if (offset < 64) { sprintf(temp, "Prot Local Addr/Port > Remote Addr/Port"); len = sprintf(buffer, "%-63s\n", temp); } pos = 64; save_flags(flags); cli(); for(ind = 0; ind < 2; ind++) { for(pf = ipportfw_lst[ind]; pf ; pf = pf->next) { pos += 64; if (pos <= offset) continue; raddr = ntohl(pf->raddr); laddr = ntohl(pf->laddr); sprintf(temp,"%s %d.%d.%d.%d/%d > %d.%d.%d.%d/%d", strProt[ind], (laddr >> 24) & 255, (laddr >> 16) & 255, (laddr >> 8) & 255, laddr & 255, ntohs(pf->lport), (raddr >> 24) & 255, (raddr >> 16) & 255, (raddr >> 8) & 255, raddr & 255, ntohs(pf->rport) ); len += sprintf(buffer+len, "%-63s\n", temp); if (len >= length) goto done; } } done: restore_flags(flags); begin = len - (pos - offset); *start = buffer + begin; len -= begin; if(len>length) len = length; return len; } #endif /* CONFIG_IP_MASQUERADE_IPPORTFW */ static int ip_msqhst_procinfo(char *buffer, char **start, off_t offset, int length, int unused) { off_t pos=0, begin; struct ip_masq *ms; unsigned long flags; char temp[129]; int idx = 0; int len=0; if (offset < 128) { #ifdef CONFIG_IP_MASQUERADE_ICMP sprintf(temp, "Prc FromIP FPrt ToIP TPrt Masq Init-seq Delta PDelta Expires (free=%d,%d,%d)", ip_masq_free_ports[0], ip_masq_free_ports[1], ip_masq_free_ports[2]); #else /* !defined(CONFIG_IP_MASQUERADE_ICMP) */ sprintf(temp, "Prc FromIP FPrt ToIP TPrt Masq Init-seq Delta PDelta Expires (free=%d,%d)", ip_masq_free_ports[0], ip_masq_free_ports[1]); #endif /* CONFIG_IP_MASQUERADE_ICMP */ len = sprintf(buffer, "%-127s\n", temp); } pos = 128; save_flags(flags); cli(); for(idx = 0; idx < IP_MASQ_TAB_SIZE; idx++) for(ms = ip_masq_m_tab[idx]; ms ; ms = ms->m_link) { int timer_active; pos += 128; if (pos <= offset) continue; timer_active = del_timer(&ms->timer); if (!timer_active) ms->timer.expires = jiffies; sprintf(temp,"%s %08lX:%04X %08lX:%04X %04X %08X %6d %6d %7lu", masq_proto_name(ms->protocol), ntohl(ms->saddr), ntohs(ms->sport), ntohl(ms->daddr), ntohs(ms->dport), ntohs(ms->mport), ms->out_seq.init_seq, ms->out_seq.delta, ms->out_seq.previous_delta, ms->timer.expires-jiffies); if (timer_active) add_timer(&ms->timer); len += sprintf(buffer+len, "%-127s\n", temp); if(len >= length) goto done; } done: restore_flags(flags); begin = len - (pos - offset); *start = buffer + begin; len -= begin; if(len>length) len = length; return len; } #ifdef CONFIG_PROC_FS static struct proc_dir_entry pde1 = { PROC_NET_IPMSQHST, 13, "ip_masquerade", S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, ip_msqhst_procinfo }; #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW static struct proc_dir_entry pde2 = { PROC_NET_IPAUTOFW, 9, "ip_autofw", S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, ip_autofw_procinfo }; #endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ #endif /* CONFIG_PROC_FS */ /* * Initialize ip masquerading */ int ip_masq_init(void) { register_symtab (&ip_masq_syms); #ifdef CONFIG_IP_MASQUERADE_PPTP register_symtab (&pptp_masq_syms); #endif /* CONFIG_IP_MASQUERADE_PPTP */ #ifdef CONFIG_IP_MASQUERADE_IPSEC register_symtab (&ipsec_masq_syms); #endif /* CONFIG_IP_MASQUERADE_IPSEC */ #ifdef CONFIG_PROC_FS proc_net_register(&pde1); #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW proc_net_register(&pde2); #endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ #ifdef CONFIG_IP_MASQUERADE_IPPORTFW proc_net_register(&(struct proc_dir_entry) { PROC_NET_IPPORTFW, 9, "ip_portfw", S_IFREG | S_IRUGO , 1, 0, 0, 0, &proc_net_inode_operations, ip_portfw_procinfo }); #endif /* CONFIG_IP_MASQUERADE_IPPORTFW */ #endif /* CONFIG_PROC_FS */ ip_masq_app_init(); #if !defined(MODULE) { extern int ip_masq_ftp_init(void), ip_masq_irc_init(void); extern int ip_masq_raudio_init(void), ip_cuseeme_irc_init(void); extern int ip_masq_vdolive_init(void), ip_quake_irc_init(void); ip_masq_ftp_init(); ip_masq_irc_init(); ip_masq_raudio_init(); ip_masq_cuseeme_init(); ip_masq_vdolive_init(); ip_masq_quake_init(); } #endif /* MODULE */ return 0; }