]> git.neil.brown.name Git - history.git/commitdiff
Linux 2.0.37pre12 2.0.37pre12
authorAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:12:16 +0000 (15:12 -0500)
committerAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:12:16 +0000 (15:12 -0500)
I finally (I hope) nailed the threading crash some people saw - it was
flock(). Also backed out the problematic mmap entry.

You need binutils 2.8 or 2.9 to build this kernel tree. 2.7 is not good
enough

o       mmap race fix wasnt - removed                   (Alan Cox)
o       Further 20 day alpha floppy fix                 (Paul Slootman)
o       PPTP/IPSEC masq update                          (John Hardin)
o       Fix 240 day select bug()                        (Ville Herva)
o       Fix threaded flock/close bug                    (Alan Cox)

12 files changed:
CREDITS
arch/i386/kernel/sys_i386.c
drivers/block/floppy.c
drivers/net/eql.c
fs/locks.c
fs/open.c
fs/select.c
include/linux/in.h
include/net/ip_masq.h
mm/mmap.c
net/ipv4/Config.in
net/ipv4/ip_masq.c

diff --git a/CREDITS b/CREDITS
index 7c1a7d16410aac460cd2dca9377d0e055d3e7944..d9d016f362ce57da27c588aefdc4a3dd7812ed81 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -557,6 +557,14 @@ S: 25360 Georgia Tech Station
 S: Atlanta, Georgia 30332
 S: USA
 
+N: John Hardin
+E: jhardin@wolfenet.com
+W: http://www.wolfenet.com/~jhardin/
+D: IPSec and PPTP masquerade
+S: 17014 Broadway ave.
+S: Snohomish, WA 98296-8031
+S: USA
+
 N: Angelo Haritsis
 E: ah@doc.ic.ac.uk
 D: kernel patches (serial, watchdog)
index ef82f4f20a4ee331f29871cafa48dfbfbcf3f4b0..0368d81b40fa86c4253cf1d74fff04e3f11bb80d 100644 (file)
@@ -48,23 +48,19 @@ asmlinkage int old_mmap(unsigned long *buffer)
        int error;
        unsigned long flags;
        struct file * file = NULL;
-       unsigned long b[6];
 
        error = verify_area(VERIFY_READ, buffer, 6*sizeof(long));
        if (error)
                return error;
-       memcpy_fromfs(&b[0], buffer, 6*sizeof(long));
-       flags = b[3];
+       flags = get_user(buffer+3);
        if (!(flags & MAP_ANONYMOUS)) {
-               unsigned long fd = b[4];
+               unsigned long fd = get_user(buffer+4);
                if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
                        return -EBADF;
        }
        flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-       down(&current->mm->mmap_sem);
-       error = do_mmap(file, b[0], b[1], b[2], flags, b[5]);
-       up(&current->mm->mmap_sem);
-       return error;
+       return do_mmap(file, get_user(buffer), get_user(buffer+1),
+                      get_user(buffer+2), flags, get_user(buffer+5));
 }
 
 extern asmlinkage int sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
index a83c544b70e2e49018babb3203e1177d0c25baba..331abc5c25d0e7e964643e37c533d60640f88e34 100644 (file)
@@ -601,7 +601,7 @@ static void reschedule_timeout(int drive, const char *message, int marg)
                drive = current_drive;
        del_timer(&fd_timeout);
        if (drive < 0 || drive > N_DRIVE) {
-               fd_timeout.expires = jiffies + 20*HZ;
+               fd_timeout.expires = jiffies + 20UL*HZ;
                drive=0;
        } else
                fd_timeout.expires = jiffies + UDP->timeout;
@@ -969,7 +969,7 @@ static void main_command_interrupt(void)
 }
 
 /* waits for a delay (spinup or select) to pass */
-static int wait_for_completion(int delay, timeout_fn function)
+static int wait_for_completion(unsigned long delay, timeout_fn function)
 {
        if (FDCS->reset){
                reset_fdc(); /* do the reset during sleep to win time
@@ -1318,7 +1318,7 @@ static int fdc_dtr(void)
         * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies)
         */
        FDCS->dtr = raw_cmd->rate & 3;
-       return(wait_for_completion(jiffies+2*HZ/100,
+       return(wait_for_completion(jiffies+2UL*HZ/100,
                                   (timeout_fn) floppy_ready));
 } /* fdc_dtr */
 
@@ -2903,7 +2903,7 @@ static int user_reset_fdc(int drive, int arg, int interruptible)
  * Misc Ioctl's and support
  * ========================
  */
-static int fd_copyout(void *param, const void *address, int size)
+static int fd_copyout(void *param, const void *address, unsigned long size)
 {
        int ret;
 
@@ -2912,7 +2912,7 @@ static int fd_copyout(void *param, const void *address, int size)
        return 0;
 }
 
-static int fd_copyin(void *param, void *address, int size)
+static int fd_copyin(void *param, void *address, unsigned long size)
 {
        int ret;
 
index d587dad80378eb8adabb4a12fd534caaa11ffae5..4f130eea2998feed870b1b770b97b87a5fff02e0 100644 (file)
@@ -389,8 +389,8 @@ static int eql_slave_xmit(struct sk_buff *skb, struct device *dev)
 
        eql_schedule_slaves (eql->queue);
   
-       slave_dev = eql_best_slave_dev (eql->queue);
        slave = eql_best_slave (eql->queue); 
+       slave_dev = slave ? slave->dev : 0;
 
        if ( slave_dev != 0 )
        {
index f16b8a4d2da1471ab9b1cfe44de7bb4b18b39729..92b443a35cc5310acba4e7747cd2d358ff78e1b9 100644 (file)
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/stat.h>
+#include <linux/file.h>
 #include <linux/fcntl.h>
 
 #include <asm/segment.h>
@@ -255,17 +256,25 @@ asmlinkage int sys_flock(unsigned int fd, unsigned int cmd)
 {
        struct file_lock file_lock;
        struct file *filp;
-
-       if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd]))
-               return (-EBADF);
+       int err = -EINVAL;
+       
+       filp = fget(fd);
+       if(filp==NULL)
+               return -EBADF;
+       
 
        if (!flock_make_lock(filp, &file_lock, cmd))
-               return (-EINVAL);
-       
-       if ((file_lock.fl_type != F_UNLCK) && !(filp->f_mode & 3))
-               return (-EBADF);
+               goto out;
 
-       return (flock_lock_file(filp, &file_lock, (cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1));
+       if ((file_lock.fl_type != F_UNLCK) && !(filp->f_mode & 3))
+       {
+               err = -EBADF;
+               goto out;
+       }
+       err=flock_lock_file(filp, &file_lock, (cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1);
+out:
+       fput(filp, filp->f_inode);
+       return err;     
 }
 
 /* Report the first existing lock that would conflict with l.
@@ -278,19 +287,27 @@ int fcntl_getlk(unsigned int fd, struct flock *l)
        struct file *filp;
        struct file_lock *fl,file_lock;
 
-       if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd]))
-               return (-EBADF);
        error = verify_area(VERIFY_WRITE, l, sizeof(*l));
        if (error)
                return (error);
 
+       filp = fget(fd);
+       if(filp==NULL)
+               return -EBADF;
+
        memcpy_fromfs(&flock, l, sizeof(flock));
        if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK))
-               return (-EINVAL);
+       {
+               error = -EINVAL;
+               goto out;
+       }
 
        if (!filp->f_inode || !posix_make_lock(filp, &file_lock, &flock))
-               return (-EINVAL);
-
+       {
+               error = -EINVAL;
+               goto out;
+       }
+       
        flock.l_type = F_UNLCK;
        for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) {
                if (!(fl->fl_flags & FL_POSIX))
@@ -307,7 +324,9 @@ int fcntl_getlk(unsigned int fd, struct flock *l)
        }
 
        memcpy_tofs(l, &flock, sizeof(flock));
-       return (0);
+out:
+       fput(filp, filp->f_inode);
+       return error;
 }
 
 /* Apply the lock described by l to an open file descriptor.
@@ -326,19 +345,20 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
        /* Get arguments and validate them ...
         */
 
-       if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd]))
-               return (-EBADF);
-       
        error = verify_area(VERIFY_READ, l, sizeof(*l));
        if (error)
                return (error);
-       
-       if (!(inode = filp->f_inode))
-               return (-EINVAL);
-       
+               
+       filp = fget(fd);
+       if(filp==NULL)
+               return -EBADF;
+
+       inode = filp->f_inode;
+               
        /*
         * This might block, so we do it before checking the inode.
         */
+
        memcpy_fromfs(&flock, l, sizeof(flock));
 
        /* Don't allow mandatory locks on files that may be memory mapped
@@ -350,22 +370,34 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
                struct vm_area_struct *vma = inode->i_mmap;
                do {
                        if (vma->vm_flags & VM_MAYSHARE)
-                               return (-EAGAIN);
+                       {
+                               error = -EAGAIN;
+                               goto out;
+                       }
                        vma = vma->vm_next_share;
                } while (vma != inode->i_mmap);
        }
 
        if (!posix_make_lock(filp, &file_lock, &flock))
-               return (-EINVAL);
+       {
+               error = -EINVAL;
+               goto out;
+       }
        
        switch (flock.l_type) {
        case F_RDLCK:
                if (!(filp->f_mode & 1))
-                       return (-EBADF);
+               {
+                       error = -EBADF;
+                       goto out;
+               }
                break;
        case F_WRLCK:
                if (!(filp->f_mode & 2))
-                       return (-EBADF);
+               {
+                       error = -EBADF;
+                       goto out;
+               }
                break;
        case F_UNLCK:
                break;
@@ -384,13 +416,19 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
 }
 #endif
                if (!(filp->f_mode & 3))
-                       return (-EBADF);
+               {
+                       error = -EBADF;
+                       goto out;
+               }
                break;
        default:
                return (-EINVAL);
        }
        
-       return (posix_lock_file(filp, &file_lock, cmd == F_SETLKW));
+       error = posix_lock_file(filp, &file_lock, cmd == F_SETLKW);
+out:
+       fput(filp, filp->f_inode);
+       return error;
 }
 
 /* This function is called when the file is closed.
index 8516020660da65461fbd3ded0119eb3e7f381dbc..ae633d7033d38b1ae3b0e163808ec838111382a2 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -68,11 +68,10 @@ int do_truncate(struct inode *inode, unsigned long length)
        int error;
        struct iattr newattrs;
 
-
         /* Not pretty: "inode->i_size" shouldn't really be "off_t". But it is. */
         if ((off_t) length < 0)
                return -EINVAL;
-
+                                 
        down(&inode->i_sem);
        newattrs.ia_size = length;
        newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
index ccdbc65644916146574ad1ce5722587aebb22cc2..c53086de1d46ef61aeed72f38e776f6fe8045028 100644 (file)
@@ -347,10 +347,8 @@ asmlinkage int sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct
                (fd_set *) &res_out,
                (fd_set *) &res_ex,
                (fd_set *) &locked);
-       timeout = current->timeout - jiffies - 1;
+       timeout = current->timeout?current->timeout - jiffies - 1:0;
        current->timeout = 0;
-       if ((long) timeout < 0)
-               timeout = 0;
        if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
                put_user(timeout/HZ, &tvp->tv_sec);
                timeout %= HZ;
index b2a44f590f21dc93916121435c83af8a45451724..8a42144e9d889e9afa7fddf1cdca8ce8987b9159 100644 (file)
@@ -31,6 +31,9 @@ enum {
   IPPROTO_PUP = 12,            /* PUP protocol                         */
   IPPROTO_UDP = 17,            /* User Datagram Protocol               */
   IPPROTO_IDP = 22,            /* XNS IDP protocol                     */
+  IPPROTO_GRE = 47,            /* GRE Encapsulation used by PPTP et al */
+  IPPROTO_ESP = 50,            /* ESP protocol for IPSec               */
+  IPPROTO_AH = 51,             /* AH protocol for IPSec                */
 
   IPPROTO_RAW = 255,           /* Raw IP packets                       */
   IPPROTO_MAX
index 0ba79c34ff3e1937b1279a396fc72fe13138653b..a06e75f19be94f39738a564407e5e062b5b2e09d 100644 (file)
@@ -85,12 +85,11 @@ struct ip_masq {
        unsigned  flags;                /* status flags */
        struct ip_masq  *control;       /* Corresponding control connection */
 #ifdef CONFIG_IP_MASQUERADE_IPSEC
-        struct ip_masq  *d_link;       /* hashed link ptr */
+       struct ip_masq  *d_link;        /* hashed link ptr */
        __u32           ospi, ispi;     /* outbound and inbound SPI keys for IPSEC */
-                                        /* also the cookie for ISAKMP masq (maybe) */
+                                       /* also the icookie for ISAKMP masquerade  */
        short           ocnt;           /* counter of inits sent - limit blocking  */
-        short           blocking;       /* if we're blocking another host */
-
+       short           blocking;       /* if we're blocking another host          */
 #endif /* CONFIG_IP_MASQUERADE_IPSEC */
 };
 
index 5ce0e69761522db5df97a1d9bc867716fed5b8c9..83a9f52e99b0fb8caf963c4756c7c942df11f17c 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -69,21 +69,20 @@ asmlinkage unsigned long sys_brk(unsigned long brk)
        unsigned long newbrk, oldbrk;
        struct mm_struct *mm = current->mm;
 
-       down(&mm->mmap_sem);
-
        if (brk < mm->end_code)
-               goto out;
+               return mm->brk;
        newbrk = PAGE_ALIGN(brk);
        oldbrk = PAGE_ALIGN(mm->brk);
        if (oldbrk == newbrk)
-               goto set_brk;
+               return mm->brk = brk;
 
        /*
         * Always allow shrinking brk
         */
        if (brk <= mm->brk) {
+               mm->brk = brk;
                do_munmap(newbrk, oldbrk-newbrk);
-               goto set_brk;
+               return brk;
        }
        /*
         * Check against rlimit and stack..
@@ -92,19 +91,19 @@ asmlinkage unsigned long sys_brk(unsigned long brk)
        if (rlim >= RLIM_INFINITY)
                rlim = ~0;
        if (brk - mm->end_code > rlim)
-               goto out;
+               return mm->brk;
 
        /*
         * Check against existing mmap mappings.
         */
        if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE))
-               goto out;
+               return mm->brk;
 
        /*
         * Check if we have enough memory..
         */
        if (!vm_enough_memory((newbrk-oldbrk) >> PAGE_SHIFT))
-               goto out;
+               return mm->brk;
 
        /*
         * Ok, looks good - let it rip.
@@ -112,12 +111,8 @@ asmlinkage unsigned long sys_brk(unsigned long brk)
        if(do_mmap(NULL, oldbrk, newbrk-oldbrk,
                PROT_READ|PROT_WRITE|PROT_EXEC,
                   MAP_FIXED|MAP_PRIVATE, 0) != oldbrk)
-               goto out;
-set_brk:
-       mm->brk = brk;
-out:
-       up(&mm->mmap_sem);
-       return mm->brk;
+               return mm->brk;
+       return mm->brk = brk;
 }
 
 /*
@@ -789,12 +784,7 @@ static void unmap_fixup(struct vm_area_struct *area,
 
 asmlinkage int sys_munmap(unsigned long addr, size_t len)
 {
-       int ret;
-
-       down(&current->mm->mmap_sem);
-       ret = do_munmap(addr, len);
-       up(&current->mm->mmap_sem);
-       return ret;
+       return do_munmap(addr, len);
 }
 
 /*
@@ -806,7 +796,7 @@ asmlinkage int sys_munmap(unsigned long addr, size_t len)
 int do_munmap(unsigned long addr, size_t len)
 {
        struct vm_area_struct *mpnt, *prev, *next, **npp, *free;
-       
+
        if ((addr & ~PAGE_MASK) || addr > MAX_USER_ADDR || len > MAX_USER_ADDR-addr)
                return -EINVAL;
 
@@ -864,6 +854,7 @@ int do_munmap(unsigned long addr, size_t len)
        } while (free);
 
        /* we could zap the page tables here too.. */
+
        return 0;
 }
 
@@ -989,9 +980,10 @@ void merge_segments (struct mm_struct * mm, unsigned long start_addr, unsigned l
 {
        struct vm_area_struct *prev, *mpnt, *next;
 
+       down(&mm->mmap_sem);
        mpnt = find_vma(mm, start_addr);
        if (!mpnt)
-               return;
+               goto no_vma;
 
        avl_neighbours(mpnt, mm->mmap_avl, &prev, &next);
        /* we have  prev->vm_next == mpnt && mpnt->vm_next = next */
@@ -1051,4 +1043,6 @@ void merge_segments (struct mm_struct * mm, unsigned long start_addr, unsigned l
                kfree_s(mpnt, sizeof(*mpnt));
                mpnt = prev;
        }
+no_vma:
+       up(&mm->mmap_sem);
 }
index 1c2aec1b39c3125a2db32dc6c8d30e83db6fa7d3..b795005aaf8311a5f1c90a69cd1791642154fd89 100644 (file)
@@ -16,21 +16,23 @@ if [ "$CONFIG_FIREWALL" = "y" ]; then
         comment 'Protocol-specific masquerading support will be built as modules.'
         if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then      
           bool 'IP: ipautofw masquerading (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_IPAUTOFW
-          bool 'IP: MS PPTP client masq support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_PPTP
+          bool 'IP: MS PPTP masq support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_PPTP
           if [ "$CONFIG_IP_MASQUERADE_PPTP" = "y" ]; then      
+            bool 'IP: MS PPTP Call ID masq support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT
             bool 'IP: MS PPTP masq debugging' DEBUG_IP_MASQUERADE_PPTP
             if [ "$DEBUG_IP_MASQUERADE_PPTP" = "y" ]; then     
               bool 'IP: MS PPTP masq verbose debugging' DEBUG_IP_MASQUERADE_PPTP_VERBOSE
             fi
-         fi
-         bool 'IP: IPSEC ESP & ISAKMP masq support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_IPSEC
-         if [ "$CONFIG_IP_MASQUERADE_IPSEC" = "y" ]; then      
-           int 'IP: IPSEC masq table lifetime (minutes)' CONFIG_IP_MASQUERADE_IPSEC_EXPIRE 30
-           bool 'IP: IPSEC masq debugging' DEBUG_IP_MASQUERADE_IPSEC
-           if [ "$DEBUG_IP_MASQUERADE_IPSEC" = "y" ]; then     
-             bool 'IP: IPSEC masq verbose debugging' DEBUG_IP_MASQUERADE_IPSEC_VERBOSE
-           fi
-         fi
+          fi
+          bool 'IP: IPSEC ESP & ISAKMP masq support (EXPERIMENTAL)' CONFIG_IP_MASQUERADE_IPSEC
+          if [ "$CONFIG_IP_MASQUERADE_IPSEC" = "y" ]; then     
+            int 'IP: IPSEC masq table lifetime (minutes)' CONFIG_IP_MASQUERADE_IPSEC_EXPIRE 30
+            bool 'IP: Disable inbound ESP destination guessing' CONFIG_IP_MASQUERADE_IPSEC_NOGUESS
+            bool 'IP: IPSEC masq debugging' DEBUG_IP_MASQUERADE_IPSEC
+            if [ "$DEBUG_IP_MASQUERADE_IPSEC" = "y" ]; then    
+              bool 'IP: IPSEC masq verbose debugging' DEBUG_IP_MASQUERADE_IPSEC_VERBOSE
+            fi
+          fi
         fi
         bool 'IP: ICMP masquerading' CONFIG_IP_MASQUERADE_ICMP
       fi
index 8b3aacd5beb0cc0103a17100037351a386034097..bf71ed06790937a58dc63e7adcdeac7304876c99 100644 (file)
@@ -46,8 +46,6 @@
 
 #define IP_MASQ_TAB_SIZE 256    /* must be power of 2 */
 
-#define IPPROTO_GRE    47      /* GRE Encapsulation used by PPTP */
-
 #ifdef CONFIG_IP_MASQUERADE_PPTP
 /*
  * This is clumsier than it otherwise might be (i.e. the
@@ -60,6 +58,9 @@
  * 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";
@@ -125,9 +126,6 @@ struct pptp_gre_header {
 #endif /* CONFIG_IP_MASQUERADE_PPTP */
 
 
-#define IPPROTO_ESP    50      /* ESP protocol for IPSEC */
-#define IPPROTO_AH     51      /* AH protocol for IPSEC */
-
 #ifdef CONFIG_IP_MASQUERADE_IPSEC
 /*
  * The above comments about PPTP apply here, too. This should all be a module.
@@ -1262,8 +1260,6 @@ static void recalc_check(struct udphdr *uh, __u32 saddr,
 /*
  *      Masquerade of GRE connections
  *      to support a PPTP VPN client or server.
- *      John Hardin <jhardin@wolfenet.com> gets all blame...
- *      See also http://www.wolfenet.com/~jhardin/ip_masq_pptp.html
  */
 
 /*
@@ -1679,8 +1675,10 @@ void ip_masq_pptp(struct sk_buff *skb, struct ip_masq *ms, struct device *dev)
         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\n", ntohs(pptph->packetLength),
+        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 &&
@@ -1735,7 +1733,7 @@ void ip_masq_pptp(struct sk_buff *skb, struct ip_masq *ms, struct device *dev)
                                 }
                         break;
                         case PPTP_OUT_CALL_REPLY:
-                                if (iph->saddr == ms->daddr)    /* inbound only */
+                                if (iph->saddr == ms->daddr)    /* inbound (masqueraded client) */
                                 {
 #ifdef DEBUG_IP_MASQUERADE_PPTP
                                         printk(KERN_DEBUG "ip_masq_pptp(): ");
@@ -1799,7 +1797,7 @@ void ip_masq_pptp(struct sk_buff *skb, struct ip_masq *ms, struct device *dev)
                                 }
                         break;
                         case PPTP_IN_CALL_REPLY:
-                                if (iph->saddr == ms->daddr)    /* inbound only */
+                                if (iph->saddr == ms->daddr)    /* inbound (masqueraded client) */
                                 {
 #ifdef DEBUG_IP_MASQUERADE_PPTP
                                         printk(KERN_DEBUG "ip_masq_pptp(): ");
@@ -1948,6 +1946,11 @@ static struct symbol_table pptp_masq_syms = {
  * 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)
@@ -2132,10 +2135,23 @@ 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;
+        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;
@@ -2150,14 +2166,15 @@ int ip_fw_demasq_esp(struct sk_buff **skb_p, struct device *dev)
                 /* illegal SPI - discard */
                 printk(KERN_INFO "ip_fw_demasq_esp(): ");
                 printk("zero SPI from %s discarded\n", in_ntoa(iph->saddr));
-                return 0;
+                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 0;
+                return -1;
         }
 
        /*
@@ -2203,6 +2220,133 @@ int ip_fw_demasq_esp(struct sk_buff **skb_p, struct device *dev)
                 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));
@@ -2240,38 +2384,14 @@ int ip_fw_masquerade(struct sk_buff **skb_ptr, struct device *dev)
 
         if (iph->protocol==IPPROTO_ICMP) 
             return (ip_fw_masq_icmp(skb_ptr,dev));
-        if (iph->protocol==IPPROTO_GRE) 
 #ifdef CONFIG_IP_MASQUERADE_PPTP
+        if (iph->protocol==IPPROTO_GRE) 
             return (ip_fw_masq_gre(skb_ptr,dev));
-#else
-        {
-                /* received the traffic but can't handle it - let user know. */
-                printk(KERN_INFO "MASQ: %s %s ", "GRE", in_ntoa(iph->saddr));
-                printk("-> %s: ", in_ntoa(iph->daddr));
-                printk("%s Masq not enabled - reconfigure kernel\n", "PPTP");
-                return -1;
-        }
 #endif /* CONFIG_IP_MASQUERADE_PPTP */
-        if (iph->protocol==IPPROTO_ESP) 
 #ifdef CONFIG_IP_MASQUERADE_IPSEC
+        if (iph->protocol==IPPROTO_ESP) 
             return (ip_fw_masq_esp(skb_ptr,dev));
-#else
-        {
-                /* received the traffic but can't handle it - let user know. */
-                printk(KERN_INFO "MASQ: %s %s ", "ESP", in_ntoa(iph->saddr));
-                printk("-> %s: ", in_ntoa(iph->daddr));
-                printk("%s Masq not enabled - reconfigure kernel\n", "IPSEC/ISAKMP");
-                return -1;
-        }
 #endif /* CONFIG_IP_MASQUERADE_IPSEC */
-        if (iph->protocol==IPPROTO_AH) 
-        {
-                /* received the traffic but can't handle it - let user know. */
-                printk(KERN_INFO "MASQ: %s %s ", "AH", in_ntoa(iph->saddr));
-                printk("-> %s: ", in_ntoa(iph->daddr));
-                printk("protocol cannot be masqueraded.\n");
-                return -1;
-        }
        if (iph->protocol!=IPPROTO_UDP && iph->protocol!=IPPROTO_TCP)
                return -1;
 
@@ -2421,6 +2541,13 @@ int ip_fw_masquerade(struct sk_buff **skb_ptr, struct device *dev)
        
        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);
        }
@@ -2935,15 +3062,15 @@ int ip_fw_demasquerade(struct sk_buff **skb_p, struct device *dev)
                portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]);
                if ((ntohs(portptr[1]) < PORT_MASQ_BEGIN ||
                     ntohs(portptr[1]) > PORT_MASQ_END)
+#ifdef CONFIG_IP_MASQUERADE_IPSEC
+                    && ((iph->protocol != IPPROTO_UDP) || (ntohs(portptr[0]) != UDP_PORT_ISAKMP) || (ntohs(portptr[1]) != UDP_PORT_ISAKMP))
+#endif /* CONFIG_IP_MASQUERADE_IPSEC */
 #ifdef CONFIG_IP_MASQUERADE_IPAUTOFW 
                    && !ip_autofw_check_range(iph->saddr, portptr[1], 
                                              iph->protocol, 0)
                    && !ip_autofw_check_direct(portptr[1], iph->protocol)
                    && !ip_autofw_check_port(portptr[1], iph->protocol)
 #endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
-#ifdef CONFIG_IP_MASQUERADE_IPSEC
-                    && ((iph->protocol != IPPROTO_UDP) || (ntohs(portptr[0]) != UDP_PORT_ISAKMP) || (ntohs(portptr[1]) != UDP_PORT_ISAKMP))
-#endif /* CONFIG_IP_MASQUERADE_IPSEC */
                        )
                        return 0;
 
@@ -3088,6 +3215,14 @@ int ip_fw_demasquerade(struct sk_buff **skb_p, struct device *dev)
                 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