patch-2.0.37 linux/net/ipv4/ip_masq.c

Next file: linux/net/ipv4/ip_sockglue.c
Previous file: linux/net/ipv4/igmp.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.0.36/linux/net/ipv4/ip_masq.c linux/net/ipv4/ip_masq.c
@@ -20,6 +20,7 @@
  *	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
  *	
  */
 
@@ -45,6 +46,150 @@
 
 #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
  */
@@ -53,6 +198,9 @@
 
 /*
  * 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)
@@ -60,6 +208,12 @@
    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;
@@ -96,6 +250,24 @@
 
 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)];
 }
 
@@ -140,6 +312,14 @@
 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
  */
@@ -284,10 +464,26 @@
         /*
          *	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;
@@ -319,6 +515,11 @@
         /*
          *	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))  {
@@ -326,6 +527,18 @@
                         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;
 }
@@ -348,6 +561,9 @@
         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;
@@ -356,6 +572,13 @@
         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);
 }
 
@@ -400,6 +623,32 @@
                         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,
@@ -411,6 +660,121 @@
         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.
@@ -423,6 +787,10 @@
         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;
@@ -431,6 +799,13 @@
         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);
 }
 
@@ -455,6 +830,35 @@
         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 &&
@@ -499,6 +903,92 @@
         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.
@@ -647,6 +1137,34 @@
         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();
                 
@@ -667,6 +1185,18 @@
                  */
                 
                 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();
@@ -707,11 +1237,11 @@
 
 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);
-        } else {
-                del_timer(&ms->timer);
         }
 }
 
@@ -725,6 +1255,1118 @@
 		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
+
+	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));
+
+#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 :) */
+	printk(KERN_INFO "ip_fw_demasq_gre(): ");
+	printk("Inbound from %s has no masq table entry.\n", in_ntoa(iph->saddr));
+ 	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 :) */
+        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));
+ 	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;
@@ -742,6 +2384,14 @@
 
         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;
 
@@ -758,6 +2408,7 @@
 #endif
 
         ms = ip_masq_out_get(iph);
+
 	if (ms!=NULL) {
                 ip_masq_set_expire(ms,0);
                 
@@ -835,8 +2486,29 @@
                         	         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
  	 */
@@ -869,6 +2541,13 @@
  	
  	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);
  	}
@@ -1360,20 +3039,32 @@
  	__u16	*portptr;
  	struct ip_masq	*ms;
 	unsigned short len;
-	unsigned long 	timeout;
+	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)
@@ -1502,6 +3193,20 @@
                         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.
@@ -1510,6 +3215,14 @@
                 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
@@ -1675,6 +3388,12 @@
 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(&(struct proc_dir_entry) {
 		PROC_NET_IPMSQHST, 13, "ip_masquerade",

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov