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

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

diff -u --recursive --new-file v2.0.29/linux/net/ipv4/ip_masq.c linux/net/ipv4/ip_masq.c
@@ -12,9 +12,13 @@
  *	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
  *
  *	
  */
@@ -37,6 +41,7 @@
 #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 */
 
@@ -44,11 +49,54 @@
  *	Implement IP packet masquerading
  */
 
-static const char *strProt[] = {"UDP","TCP"};
+static const char *strProt[] = {"UDP","TCP","ICMP"};
 
-static __inline__ const char * masq_proto_name(unsigned proto)
+/*
+ * masq_proto_num returns 0 for UDP, 1 for TCP, 2 for ICMP
+ */
+
+static int masq_proto_num(unsigned proto)
+{
+   switch (proto)
+   {
+      case IPPROTO_UDP:  return (0); break;
+      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)
 {
-        return strProt[proto==IPPROTO_TCP];
+        return strProt[masq_proto_num(proto)];
 }
 
 /*
@@ -69,9 +117,10 @@
  *	
  */
 
-int ip_masq_free_ports[2] = {
+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, 	/* TCP */
+        PORT_MASQ_END - PORT_MASQ_BEGIN		/* ICMP */
 };
 
 static struct symbol_table ip_masq_syms = {
@@ -103,12 +152,108 @@
 
 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 */
+
 /*
  *	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);
@@ -219,7 +364,7 @@
  *	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.
+ *	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
@@ -236,12 +381,33 @@
 
         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) &&
-                    (s_port==ms->dport || ms->flags & IP_MASQ_F_NO_DPORT) &&
-                    (d_addr==ms->maddr && d_port==ms->mport))
+ 		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 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;
 }
 
@@ -274,6 +440,12 @@
  *	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 *
@@ -282,14 +454,48 @@
         unsigned hash;
         struct ip_masq *ms;
 
+
         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 )
+                    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;
 }
 
@@ -315,41 +521,90 @@
 
 static void masq_expire(unsigned long data)
 {
-	struct ip_masq *ms = (struct ip_masq *)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("Masqueraded %s %lX:%X expired\n",
-			masq_proto_name(ms->protocol),
-			ntohl(ms->saddr),ntohs(ms->sport));
+		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[ms->protocol==IPPROTO_TCP]++;
-                ip_masq_unbind_app(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(struct device *dev, int proto, __u32 saddr, __u16 sport, __u32 daddr, __u16 dport, unsigned mflags)
+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[proto==IPPROTO_TCP];
+        free_ports_p = &ip_masq_free_ports[masq_proto_num(proto)];
 
         if (*free_ports_p == 0) {
                 if (++n_fails < 5)
@@ -375,6 +630,7 @@
         ms->dport	   = dport;
         ms->flags	   = mflags;
         ms->app_data	   = NULL;
+	ms->control	   = NULL;
 
         if (proto == IPPROTO_UDP)
                 ms->flags |= IP_MASQ_F_NO_DADDR;
@@ -389,8 +645,11 @@
 		/*
                  *	Try the next available port number
                  */
-                
-		ms->mport = htons(masq_port++);
+                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);
@@ -413,7 +672,8 @@
                         
                         restore_flags(flags);
                         
-                        ip_masq_bind_app(ms);
+                        if (proto != IPPROTO_ICMP)
+                              ip_masq_bind_app(ms);
                         n_fails = 0;
                         return ms;
                 }
@@ -426,6 +686,11 @@
         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) );
+}
+
 /*
  * 	Set masq expiration (deletion) and adds timer,
  *	if timeout==0 cancel expiration.
@@ -467,6 +732,8 @@
 	 * 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));
 	if (iph->protocol!=IPPROTO_UDP && iph->protocol!=IPPROTO_TCP)
 		return -1;
 
@@ -483,19 +750,59 @@
 #endif
 
         ms = ip_masq_out_get(iph);
-        if (ms!=NULL)
+	if (ms!=NULL) {
                 ip_masq_set_expire(ms,0);
 
+		/*
+		 *      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)
 	{
-                ms = ip_masq_new(dev, iph->protocol,
-                                 iph->saddr, portptr[0],
-                                 iph->daddr, portptr[1],
-                                 0);
+#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;
  	}
@@ -530,7 +837,7 @@
  	 *	Adjust packet accordingly to protocol
  	 */
  	
- 	if (iph->protocol==IPPROTO_UDP)
+ 	if (masq_proto_num(iph->protocol)==0)
  	{
                 timeout = ip_masq_expire->udp_timeout;
  		recalc_check((struct udphdr *)portptr,iph->saddr,iph->daddr,size);
@@ -596,12 +903,63 @@
 	struct ip_masq	*ms;
 	unsigned short   len   = ntohs(iph->tot_len) - (iph->ihl * 4);
 
-#ifdef DEBUG_CONFIG_IP_MASQUERADE
- 	printk("Incoming forward ICMP (%d) %lX -> %lX\n",
-	        icmph->type,
+#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 */
+		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
@@ -618,6 +976,57 @@
 	/* 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;
@@ -628,9 +1037,6 @@
 	 * (but reversed relative to outer IP header!)
 	 */
 	pptr = (__u16 *)&(((char *)ciph)[ciph->ihl*4]);
-	if (ntohs(pptr[1]) < PORT_MASQ_BEGIN ||
- 	    ntohs(pptr[1]) > PORT_MASQ_END)
- 		return 0;
 
 	/* Ensure the checksum is correct */
 	if (ip_compute_csum((unsigned char *) icmph, len)) 
@@ -642,13 +1048,17 @@
 	}
 
 #ifdef DEBUG_CONFIG_IP_MASQUERADE
- 	printk("Handling forward ICMP for %lX:%X -> %lX:%X\n",
+	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_in_get() does */
-	ms = ip_masq_in_get_2(ciph->protocol, ciph->saddr, pptr[0], ciph->daddr, pptr[1]);
+	/* 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;
@@ -670,7 +1080,7 @@
 	icmph->checksum = ip_compute_csum((unsigned char *) icmph, len);
 
 #ifdef DEBUG_CONFIG_IP_MASQUERADE
- 	printk("Rewrote forward ICMP to %lX:%X -> %lX:%X\n",
+	printk("Rewrote forward ICMP to %lX:%X -> %lX:%X\n",
 	       ntohl(ciph->saddr), ntohs(pptr[0]),
 	       ntohl(ciph->daddr), ntohs(pptr[1]));
 #endif
@@ -695,22 +1105,124 @@
 	struct ip_masq	*ms;
 	unsigned short   len   = ntohs(iph->tot_len) - (iph->ihl * 4);
 
-#ifdef DEBUG_CONFIG_IP_MASQUERADE
- 	printk("Incoming reverse ICMP (%d) %lX -> %lX\n",
-	        icmph->type,
+#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
 
-	if ((icmph->type != ICMP_DEST_UNREACH) &&
-	    (icmph->type != ICMP_SOURCE_QUENCH) &&
-	    (icmph->type != ICMP_TIME_EXCEEDED))
-		return 0;
+#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;
 
-	/* Now find the contained IP header */
+		ip_masq_set_expire(ms,0);
+
+		/* 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))
+	if ((ciph->protocol != IPPROTO_UDP) && 
+	    (ciph->protocol != IPPROTO_TCP))
 		return 0;
 
 	/* 
@@ -738,7 +1250,11 @@
 #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]);
+	ms = ip_masq_in_get_2(ciph->protocol,
+			      ciph->daddr,
+			      pptr[1],
+			      ciph->saddr,
+			      pptr[0]);
 
 	if (ms == NULL)
 		return 0;
@@ -786,6 +1302,9 @@
  	struct ip_masq	*ms;
 	unsigned short len;
 	unsigned long 	timeout;
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW 
+ 	struct ip_autofw *af;
+#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
 
 	switch (iph->protocol) {
 	case IPPROTO_ICMP:
@@ -794,9 +1313,17 @@
 	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)
+		if ((ntohs(portptr[1]) < PORT_MASQ_BEGIN ||
+		     ntohs(portptr[1]) > PORT_MASQ_END)
+#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 */
+			)
 			return 0;
+
 		/* Check that the checksum is OK */
 		len = ntohs(iph->tot_len) - (iph->ihl * 4);
 		if ((iph->protocol == IPPROTO_UDP) && (portptr[3] == 0))
@@ -836,8 +1363,31 @@
 
         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))) {
+        	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)) ) {
+        	ms = ip_masq_new_enh(dev, iph->protocol,
+        			     af->where, htons(af->hidden),
+        			     iph->saddr, portptr[0],
+        			     0,
+        			     htons(af->visible));
+        }
+#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
+
         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);
 
@@ -848,7 +1398,7 @@
                 if ( ms->flags & IP_MASQ_F_NO_DPORT && ms->protocol == IPPROTO_TCP ) {
                         ms->flags &= ~IP_MASQ_F_NO_DPORT;
                         ms->dport = portptr[0];
-#if DEBUG_CONFIG_IP_MASQUERADE
+#ifdef DEBUG_CONFIG_IP_MASQUERADE
                         printk("ip_fw_demasquerade(): filled dport=%d\n",
                                ntohs(ms->dport));
 #endif
@@ -856,7 +1406,7 @@
                 if (ms->flags & IP_MASQ_F_NO_DADDR && ms->protocol == IPPROTO_TCP)  {
                         ms->flags &= ~IP_MASQ_F_NO_DADDR;
                         ms->daddr = iph->saddr;
-#if DEBUG_CONFIG_IP_MASQUERADE
+#ifdef DEBUG_CONFIG_IP_MASQUERADE
                         printk("ip_fw_demasquerade(): filled daddr=%X\n",
                                ntohs(ms->daddr));
 #endif
@@ -886,7 +1436,7 @@
 		 * timeouts.
 		 * If a TCP RST is seen collapse the tunnel (by using short timeout)!
                  */
-                if (iph->protocol==IPPROTO_UDP)
+                if (masq_proto_num(iph->protocol)==0)
 		{
                         recalc_check((struct udphdr *)portptr,iph->saddr,iph->daddr,len);
 			timeout = ip_masq_expire->udp_timeout;
@@ -933,9 +1483,54 @@
 }
 
 /*
- *	/proc/net entry
+ *	/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 */
+
 static int ip_msqhst_procinfo(char *buffer, char **start, off_t offset,
 			      int length, int unused)
 {
@@ -1007,6 +1602,14 @@
 		0, &proc_net_inode_operations,
 		ip_msqhst_procinfo
 	});
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
+	proc_net_register(&(struct proc_dir_entry) {
+		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	
         ip_masq_app_init();
 

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