From 92082108d9cfd92c755ad4ac55306d900605408f Mon Sep 17 00:00:00 2001 From: David Weinehall Date: Fri, 23 Nov 2007 15:12:20 -0500 Subject: [PATCH] Linux 2.0.39pre4 o Large-disk fixes (Andries Brouwer) o Wavelan-driver cleanup & bugfixes (Jean Tourrilhes) o Security-fixes (Solar Designer) o Quota-fixes (Jan Kara) o Fixed GPF using IPsec Masquerade (Rudolf Lippan) o (s)size_t-patch for fs/proc/mem.c (David Weinehall) reverted --- drivers/block/ide.c | 141 +- drivers/net/ppp.c | 7 +- drivers/net/slip.c | 6 +- drivers/net/wavelan.c | 238 ++- drivers/net/wavelan.p.h | 71 +- fs/binfmt_elf.c | 40 +- fs/dquot.c | 56 +- fs/exec.c | 106 +- fs/proc/array.c | 3 +- fs/proc/fd.c | 1 + fs/proc/mem.c | 34 +- fs/proc/root.c | 1 + include/linux/sched.h | 3 + include/net/ip_masq.h | 14 + kernel/exit.c | 3 +- kernel/fork.c | 17 +- net/ipv4/ip_masq.c | 47 +- net/ipv4/ip_masq.c.orig | 3416 ++++++++++++++++++++++++++++++++++++++ net/ipv4/ip_masq_quake.c | 12 + net/ipv4/tcp_input.c | 9 + 20 files changed, 3882 insertions(+), 343 deletions(-) create mode 100644 net/ipv4/ip_masq.c.orig diff --git a/drivers/block/ide.c b/drivers/block/ide.c index f33f9e31c12e..0b4ed6e676d5 100644 --- a/drivers/block/ide.c +++ b/drivers/block/ide.c @@ -606,6 +606,9 @@ void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int t * * Returns: 1 if lba_capacity looks sensible * 0 otherwise + * + * Note: we must not change id->cyls here, otherwise a second call + * of this routine might no longer find lba_capacity ok. */ static int lba_capacity_is_ok (struct hd_driveid *id) { @@ -614,14 +617,15 @@ static int lba_capacity_is_ok (struct hd_driveid *id) unsigned long _10_percent = chs_sects / 10; /* - * very large drives (8GB+) may lie about the number of cylinders - * This is a split test for drives 8 Gig and Bigger only. + * The ATA spec tells large drives to return + * C/H/S = 16383/16/63 independent of their size. + * Some drives can be jumpered to use 15 heads instead of 16. */ - if ((id->lba_capacity >= 16514064) && (id->cyls == 0x3fff) && - (id->heads == 16) && (id->sectors == 63)) { - id->cyls = lba_sects / (16 * 63); /* correct cyls */ + if (id->cyls == 16383 && id->sectors == 63 && + (id->heads == 15 || id->heads == 16) && + id->lba_capacity >= 16383*63*id->heads) return 1; /* lba_capacity is our only option */ - } + /* perform a rough sanity check on lba_sects: within 10% is "okay" */ if ((lba_sects - chs_sects) < _10_percent) return 1; /* lba_capacity is good */ @@ -638,11 +642,13 @@ static int lba_capacity_is_ok (struct hd_driveid *id) /* * current_capacity() returns the capacity (in sectors) of a drive * according to its current geometry/LBA settings. + * + * It also sets select.b.lba. */ static unsigned long current_capacity (ide_drive_t *drive) { struct hd_driveid *id = drive->id; - unsigned long capacity = drive->cyl * drive->head * drive->sect; + unsigned long capacity; if (!drive->present) return 0; @@ -652,22 +658,14 @@ static unsigned long current_capacity (ide_drive_t *drive) #endif /* CONFIG_BLK_DEV_IDEFLOPPY */ if (drive->media != ide_disk) return 0x7fffffff; /* cdrom or tape */ + drive->select.b.lba = 0; /* Determine capacity, and use LBA if the drive properly supports it */ + capacity = drive->cyl * drive->head * drive->sect; if (id != NULL && (id->capability & 2) && lba_capacity_is_ok(id)) { if (id->lba_capacity >= capacity) { - drive->cyl = id->lba_capacity / (drive->head * drive->sect); capacity = id->lba_capacity; drive->select.b.lba = 1; -#if 0 - /* - * This is the correct place to perform this task; - * however, we do this later for reporting. - */ - if (*(int *)&id->cur_capacity0 != id->lba_capacity) { - *(int *)&id->cur_capacity0 = id->lba_capacity; - } -#endif } } return (capacity - drive->sect0); @@ -697,8 +695,9 @@ static void ide_geninit (struct gendisk *gd) idefloppy_setup(drive); #endif /* CONFIG_BLK_DEV_IDEFLOPPY */ drive->part[0].nr_sects = current_capacity(drive); - if (!drive->present || (drive->media != ide_disk && drive->media != ide_floppy) || - !drive->part[0].nr_sects) { + if (!drive->present + || (drive->media != ide_disk && drive->media != ide_floppy) + || !drive->part[0].nr_sects) { drive->part[0].start_sect = -1; /* skip partition check */ } } @@ -2127,7 +2126,8 @@ static int revalidate_disk(kdev_t i_rdev) }; drive->part[0].nr_sects = current_capacity(drive); - if ((drive->media != ide_disk && drive->media != ide_floppy) || !drive->part[0].nr_sects) + if ((drive->media != ide_disk && drive->media != ide_floppy) + || !drive->part[0].nr_sects) drive->part[0].start_sect = -1; resetup_one_dev(HWIF(drive)->gd, drive->select.b.unit); @@ -2565,8 +2565,8 @@ static inline void do_identify (ide_drive_t *drive, byte cmd) drive->sect = drive->bios_sect = id->sectors; } /* Handle logical geometry translation by the drive */ - if ((id->field_valid & 1) && id->cur_cyls && - id->cur_heads && (id->cur_heads <= 16) && id->cur_sectors) { + if ((id->field_valid & 1) && id->cur_cyls && id->cur_heads + && (id->cur_heads <= 16) && id->cur_sectors) { /* * Extract the physical drive geometry for our use. * Note that we purposely do *not* update the bios info. @@ -2593,12 +2593,9 @@ static inline void do_identify (ide_drive_t *drive, byte cmd) /* Use physical geometry if what we have still makes no sense */ if ((!drive->head || drive->head > 16) && id->heads && id->heads <= 16) { - if ((id->lba_capacity > 16514064) || (id->cyls == 0x3fff)) { - id->cyls = ((int)(id->lba_capacity/(id->heads * id->sectors))); - } - drive->cyl = id->cur_cyls = id->cyls; - drive->head = id->cur_heads = id->heads; - drive->sect = id->cur_sectors = id->sectors; + drive->cyl = id->cyls; + drive->head = id->heads; + drive->sect = id->sectors; } /* calculate drive capacity, and select LBA if possible */ @@ -2608,21 +2605,17 @@ static inline void do_identify (ide_drive_t *drive, byte cmd) * if possible, give fdisk access to more of the drive, * by correcting bios_cyls: */ - if ((capacity >= (id->cyls * id->heads * id->sectors)) && - (!drive->forced_geom) && drive->bios_sect && drive->bios_head) { - drive->bios_cyl = (capacity / drive->bios_sect) / drive->bios_head; -#ifdef DEBUG - printk("FDISK Fixing Geometry :: CHS=%d/%d/%d to CHS=%d/%d/%d\n", - drive->id->cur_cyls, - drive->id->cur_heads, - drive->id->cur_sectors, - drive->bios_cyl, - drive->bios_head, - drive->bios_sect); -#endif - drive->id->cur_cyls = drive->bios_cyl; - drive->id->cur_heads = drive->bios_head; - drive->id->cur_sectors = drive->bios_sect; + if (capacity > drive->bios_cyl * drive->bios_head * drive->bios_sect + && !drive->forced_geom && drive->bios_sect && drive->bios_head) { + int cyl = (capacity / drive->bios_sect) / drive->bios_head; + if (cyl <= 65535) + drive->bios_cyl = cyl; + else { + /* OK until 539 GB */ + drive->bios_sect = 63; + drive->bios_head = 255; + drive->bios_cyl = capacity / (63*255); + } } if (!strncmp(id->model, "BMI ", 4) && @@ -2666,18 +2659,6 @@ static inline void do_identify (ide_drive_t *drive, byte cmd) } } printk("\n"); - if (drive->select.b.lba) { - if (*(int *)&id->cur_capacity0 != id->lba_capacity) { -#ifdef DEBUG - printk(" CurSects=%d, LBASects=%d, ", - *(int *)&id->cur_capacity0, id->lba_capacity); -#endif - *(int *)&id->cur_capacity0 = id->lba_capacity; -#ifdef DEBUG - printk( "Fixed CurSects=%d\n", *(int *)&id->cur_capacity0); -#endif - } - } } /* @@ -3380,12 +3361,6 @@ done: * Returns 1 if the geometry translation was successful. */ -/* - * We update the values if the current CHS as determined by the kernel. - * This now allows for the hdparm tool to read the actual settings of the - * being used by the kernel. This removes the need for doing guess - * translations based on the raw values of the drive. - */ int ide_xlate_1024 (kdev_t i_rdev, int xparm, const char *msg) { ide_drive_t *drive; @@ -3397,48 +3372,14 @@ int ide_xlate_1024 (kdev_t i_rdev, int xparm, const char *msg) if (!drive) return 0; - if (drive->forced_geom) { - /* - * Update the current 3D drive values. - */ - drive->id->cur_cyls = drive->bios_cyl; - drive->id->cur_heads = drive->bios_head; - drive->id->cur_sectors = drive->bios_sect; + if (drive->forced_geom) return 0; - } - if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63) { - /* - * Update the current 3D drive values. - */ - drive->id->cur_cyls = drive->bios_cyl; - drive->id->cur_heads = drive->bios_head; - drive->id->cur_sectors = drive->bios_sect; + if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63) return 0; /* we already have a translation */ - } printk("%s ", msg); - if (xparm < 0 && (drive->bios_cyl * drive->bios_head * drive->bios_sect) < (1024 * 16 * 63)) { - /* - * Update the current 3D drive values. - */ - drive->id->cur_cyls = drive->bios_cyl; - drive->id->cur_heads = drive->bios_head; - drive->id->cur_sectors = drive->bios_sect; - return 0; /* small disk: no translation needed */ - } - - if (drive->id) { - drive->cyl = drive->id->cyls; - drive->head = drive->id->heads; - drive->sect = drive->id->sectors; - } - drive->bios_cyl = drive->cyl; - drive->bios_head = drive->head; - drive->bios_sect = drive->sect; - drive->special.b.set_geometry = 1; - tracks = drive->bios_cyl * drive->bios_head * drive->bios_sect / 63; drive->bios_sect = 63; if (xparm > 1) { @@ -3466,12 +3407,6 @@ int ide_xlate_1024 (kdev_t i_rdev, int xparm, const char *msg) } drive->part[0].nr_sects = current_capacity(drive); printk("[%d/%d/%d]", drive->bios_cyl, drive->bios_head, drive->bios_sect); - /* - * Update the current 3D drive values. - */ - drive->id->cur_cyls = drive->bios_cyl; - drive->id->cur_heads = drive->bios_head; - drive->id->cur_sectors = drive->bios_sect; return 1; } diff --git a/drivers/net/ppp.c b/drivers/net/ppp.c index bd2c4788cfa8..00770328c2e8 100644 --- a/drivers/net/ppp.c +++ b/drivers/net/ppp.c @@ -780,11 +780,16 @@ ppp_tty_close (struct tty_struct *tty) static int ppp_tty_open (struct tty_struct *tty) { - struct ppp *ppp = tty2ppp (tty); + struct ppp *ppp; int indx; + + if (!suser()) + return -EPERM; + /* * There should not be an existing table for this slot. */ + ppp = tty2ppp (tty); if (ppp) { if (ppp->flags & SC_DEBUG) printk (KERN_ERR diff --git a/drivers/net/slip.c b/drivers/net/slip.c index 3979df66511f..085b108081f5 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -698,10 +698,14 @@ slip_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int static int slip_open(struct tty_struct *tty) { - struct slip *sl = (struct slip *) tty->disc_data; + struct slip *sl; int err; + if (!suser()) + return -EPERM; + /* First make sure we're not already connected. */ + sl = (struct slip *) tty->disc_data; if (sl && sl->magic == SLIP_MAGIC) { return -EEXIST; } diff --git a/drivers/net/wavelan.c b/drivers/net/wavelan.c index 4e68cab3dcdd..19b1d36e55c6 100644 --- a/drivers/net/wavelan.c +++ b/drivers/net/wavelan.c @@ -324,6 +324,7 @@ psa_crc(u_char * psa, /* The PSA */ return crc_bytes; } /* psa_crc */ +#endif /* SET_PSA_CRC */ /*------------------------------------------------------------------*/ /* @@ -334,6 +335,7 @@ update_psa_checksum(device * dev, u_long ioaddr, u_short hacr) { +#ifdef SET_PSA_CRC psa_t psa; u_short crc; @@ -363,8 +365,8 @@ update_psa_checksum(device * dev, if(crc != 0) printk(KERN_WARNING "%s: update_psa_checksum(): CRC does not agree with PSA data (even after recalculating)\n", dev->name); #endif /* DEBUG_IOCTL_INFO */ -} /* update_psa_checksum */ #endif /* SET_PSA_CRC */ +} /* update_psa_checksum */ /*------------------------------------------------------------------*/ /* @@ -748,23 +750,23 @@ wv_config_complete(device * dev, unsigned short ias_addr; /* Check mc_config command */ - if((status & AC_SFLD_OK) != 0) - printk(KERN_INFO "wv_config_complete(): set_multicast_address failed; status = 0x%x\n", - dev->name, str, status); + if((status & AC_SFLD_OK) != AC_SFLD_OK) + printk(KERN_INFO "%s: wv_config_complete(): set_multicast_address failed; status = 0x%x\n", + dev->name, status); /* check ia-config command */ ias_addr = mcs_addr - sizeof(ac_ias_t); obram_read(ioaddr, acoff(ias_addr, ac_status), (unsigned char *)&status, sizeof(status)); - if((status & AC_SFLD_OK) != 0) - printk(KERN_INFO "wv_config_complete(): set_MAC_address; status = 0x%x\n", - dev->name, str, status); + if((status & AC_SFLD_OK) != AC_SFLD_OK) + printk(KERN_INFO "%s: wv_config_complete(): set_MAC_address failed; status = 0x%x\n", + dev->name, status); /* Check config command */ cfg_addr = ias_addr - sizeof(ac_cfg_t); obram_read(ioaddr, acoff(cfg_addr, ac_status), (unsigned char *)&status, sizeof(status)); - if((status & AC_SFLD_OK) != 0) - printk(KERN_INFO "wv_config_complete(): configure; status = 0x%x\n", - dev->name, str, status); + if((status & AC_SFLD_OK) != AC_SFLD_OK) + printk(KERN_INFO "%s: wv_config_complete(): configure failed; status = 0x%x\n", + dev->name, status); #endif /* DEBUG_CONFIG_ERROR */ ret = 1; /* Ready to be scrapped */ @@ -800,15 +802,15 @@ wv_complete(device * dev, /* Read the first transmit buffer */ obram_read(ioaddr, acoff(lp->tx_first_in_use, ac_status), (unsigned char *)&tx_status, sizeof(tx_status)); + /* If not completed -> exit */ + if((tx_status & AC_SFLD_C) == 0) + break; + /* Hack for reconfiguration... */ if(tx_status == 0xFFFF) if(!wv_config_complete(dev, ioaddr, lp)) break; /* Not completed */ - /* If not completed -> exit */ - if((tx_status & AC_SFLD_C) == 0) - break; - /* We now remove this buffer */ nreaped++; --lp->tx_n_in_use; @@ -841,7 +843,7 @@ if (lp->tx_n_in_use > 0) lp->stats.tx_packets++; ncollisions = tx_status & AC_SFLD_MAXCOL; lp->stats.collisions += ncollisions; -#ifdef DEBUG_INTERRUPT_INFO +#ifdef DEBUG_TX_INFO if(ncollisions > 0) printk(KERN_DEBUG "%s: wv_complete(): tx completed after %d collisions.\n", dev->name, ncollisions); @@ -850,53 +852,49 @@ if (lp->tx_n_in_use > 0) else { lp->stats.tx_errors++; -#ifndef IGNORE_NORMAL_XMIT_ERRS if(tx_status & AC_SFLD_S10) { lp->stats.tx_carrier_errors++; -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO "%s: wv_complete(): tx error: no CS.\n", +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG "%s: wv_complete(): tx error: no CS.\n", dev->name); #endif } -#endif /* IGNORE_NORMAL_XMIT_ERRS */ if(tx_status & AC_SFLD_S9) { lp->stats.tx_carrier_errors++; -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO "%s: wv_complete(): tx error: lost CTS.\n", +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG "%s: wv_complete(): tx error: lost CTS.\n", dev->name); #endif } if(tx_status & AC_SFLD_S8) { lp->stats.tx_fifo_errors++; -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO "%s: wv_complete(): tx error: slow DMA.\n", +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG "%s: wv_complete(): tx error: slow DMA.\n", dev->name); #endif } -#ifndef IGNORE_NORMAL_XMIT_ERRS if(tx_status & AC_SFLD_S6) { lp->stats.tx_heartbeat_errors++; -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO "%s: wv_complete(): tx error: heart beat.\n", +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG "%s: wv_complete(): tx error: heart beat.\n", dev->name); #endif } if(tx_status & AC_SFLD_S5) { lp->stats.tx_aborted_errors++; -#ifdef DEBUG_INTERRUPT_ERROR - printk(KERN_INFO "%s: wv_complete(): tx error: too many collisions.\n", +#ifdef DEBUG_TX_FAIL + printk(KERN_DEBUG "%s: wv_complete(): tx error: too many collisions.\n", dev->name); #endif } -#endif /* IGNORE_NORMAL_XMIT_ERRS */ } -#ifdef DEBUG_INTERRUPT_INFO +#ifdef DEBUG_TX_INFO printk(KERN_DEBUG "%s: wv_complete(): tx completed, tx_status 0x%04x\n", dev->name, tx_status); #endif @@ -1323,21 +1321,21 @@ wv_packet_info(u_char * p, /* Packet to dump */ char * msg1, /* Name of the device */ char * msg2) /* Name of the function */ { -#ifndef DEBUG_PACKET_DUMP + int i; + int maxi; + printk(KERN_DEBUG "%s: %s(): dest %02X:%02X:%02X:%02X:%02X:%02X, length %d\n", msg1, msg2, p[0], p[1], p[2], p[3], p[4], p[5], length); printk(KERN_DEBUG "%s: %s(): src %02X:%02X:%02X:%02X:%02X:%02X, type 0x%02X%02X\n", msg1, msg2, p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13]); -#else /* DEBUG_PACKET_DUMP */ - int i; - int maxi; +#ifdef DEBUG_PACKET_DUMP - printk(KERN_DEBUG "%s: %s(): len=%d, data=\"", msg1, msg2, length); + printk(KERN_DEBUG "data=\""); if((maxi = length) > DEBUG_PACKET_DUMP) maxi = DEBUG_PACKET_DUMP; - for(i = 0; i < maxi; i++) + for(i = 14; i < maxi; i++) if(p[i] >= ' ' && p[i] <= '~') printk(" %c", p[i]); else @@ -1565,7 +1563,9 @@ wavelan_set_multicast_list(device * dev) /*------------------------------------------------------------------*/ /* * This function doesn't exist... + * (Note : it was a nice way to test the reconfigure stuff...) */ +#ifdef SET_MAC_ADDRESS static int wavelan_set_mac_address(device * dev, void * addr) @@ -1580,6 +1580,7 @@ wavelan_set_mac_address(device * dev, return 0; } +#endif /* SET_MAC_ADDRESS */ #ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */ @@ -1940,10 +1941,8 @@ wavelan_ioctl(struct device * dev, /* device on which the ioctl is applied */ /* Disable nwid in the mmc (no filtering) */ mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), MMW_LOOPT_SEL_DIS_NWID); } -#ifdef SET_PSA_CRC /* update the Wavelan checksum */ update_psa_checksum(dev, ioaddr, lp->hacr); -#endif break; case SIOCGIWNWID: @@ -2001,10 +2000,8 @@ wavelan_ioctl(struct device * dev, /* device on which the ioctl is applied */ psa.psa_thr_pre_set = wrq->u.sensitivity & 0x3F; psa_write(ioaddr, lp->hacr, (char *)&psa.psa_thr_pre_set - (char *)&psa, (unsigned char *) &psa.psa_thr_pre_set, 1); -#ifdef SET_PSA_CRC /* update the Wavelan checksum */ update_psa_checksum(dev, ioaddr, lp->hacr); -#endif mmc_out(ioaddr, mmwoff(0, mmw_thr_pre_set), psa.psa_thr_pre_set); break; @@ -2052,10 +2049,8 @@ wavelan_ioctl(struct device * dev, /* device on which the ioctl is applied */ mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0); } -#ifdef SET_PSA_CRC /* update the Wavelan checksum */ update_psa_checksum(dev, ioaddr, lp->hacr); -#endif break; case SIOCGIWENCODE: @@ -2269,10 +2264,8 @@ wavelan_ioctl(struct device * dev, /* device on which the ioctl is applied */ psa.psa_quality_thr = *(wrq->u.name) & 0x0F; psa_write(ioaddr, lp->hacr, (char *)&psa.psa_quality_thr - (char *)&psa, (unsigned char *)&psa.psa_quality_thr, 1); -#ifdef SET_PSA_CRC /* update the Wavelan checksum */ update_psa_checksum(dev, ioaddr, lp->hacr); -#endif mmc_out(ioaddr, mmwoff(0, mmw_quality_thr), psa.psa_quality_thr); break; @@ -2435,7 +2428,7 @@ wv_packet_read(device * dev, #ifdef DEBUG_RX_TRACE printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n", - dev->name, fd_p, sksize); + dev->name, buf_off, sksize); #endif /* Allocate buffer for the data */ @@ -2520,6 +2513,8 @@ wv_receive(device * dev) { u_long ioaddr = dev->base_addr; net_local * lp = (net_local *)dev->priv; + fd_t fd; + rbd_t rbd; int nreaped = 0; #ifdef DEBUG_RX_TRACE @@ -2529,48 +2524,62 @@ wv_receive(device * dev) /* Loop on each received packet */ for(;;) { - fd_t fd; - rbd_t rbd; - ushort pkt_len; - obram_read(ioaddr, lp->rx_head, (unsigned char *) &fd, sizeof(fd)); - /* If the current frame is not complete, we have reach the end... */ + /* Note about the status : + * It start up to be 0 (the value we set). Then, when the RU + * grab the buffer to prepare for reception, it sets the + * FD_STATUS_B flag. When the RU has finished receiving the + * frame, it clears FD_STATUS_B, set FD_STATUS_C to indicate + * completion and set the other flags to indicate the eventual + * errors. FD_STATUS_OK indicates that the reception was OK. + */ + + /* If the current frame is not complete, we have reached the end. */ if((fd.fd_status & FD_STATUS_C) != FD_STATUS_C) - break; /* This is how we exit the loop */ + break; /* This is how we exit the loop. */ nreaped++; - /* Check if frame correctly received */ - if((fd.fd_status & (FD_STATUS_B | FD_STATUS_OK)) != - (FD_STATUS_B | FD_STATUS_OK)) + /* Check whether frame was correctly received. */ + if((fd.fd_status & FD_STATUS_OK) == FD_STATUS_OK) { - /* - * Not sure about this one -- it does not seem - * to be an error so we will keep quiet about it. - */ -#ifndef IGNORE_NORMAL_XMIT_ERRS -#ifdef DEBUG_RX_ERROR - if((fd.fd_status & FD_STATUS_B) != FD_STATUS_B) - printk(KERN_INFO "%s: wv_receive(): frame not consumed by RU.\n", - dev->name); -#endif -#endif /* IGNORE_NORMAL_XMIT_ERRS */ + /* Does the frame contain a pointer to the data? Let's check. */ + if(fd.fd_rbd_offset != I82586NULL) + { + /* Read the receive buffer descriptor */ + obram_read(ioaddr, fd.fd_rbd_offset, + (unsigned char *) &rbd, sizeof(rbd)); #ifdef DEBUG_RX_ERROR - if((fd.fd_status & FD_STATUS_OK) != FD_STATUS_OK) - printk(KERN_INFO "%s: wv_receive(): frame not received successfully.\n", + if((rbd.rbd_status & RBD_STATUS_EOF) != RBD_STATUS_EOF) + printk(KERN_INFO "%s: wv_receive(): missing EOF flag.\n", + dev->name); + + if((rbd.rbd_status & RBD_STATUS_F) != RBD_STATUS_F) + printk(KERN_INFO "%s: wv_receive(): missing F flag.\n", + dev->name); +#endif /* DEBUG_RX_ERROR */ + + /* Read the packet and transmit to Linux */ + wv_packet_read(dev, rbd.rbd_bufl, + rbd.rbd_status & RBD_STATUS_ACNT); + } +#ifdef DEBUG_RX_ERROR + else /* if frame has no data */ + printk(KERN_INFO "%s: wv_receive(): frame has no data.\n", dev->name); #endif } - - /* Were there problems in processing the frame? Let's check. */ - if((fd.fd_status & (FD_STATUS_S6 | FD_STATUS_S7 | FD_STATUS_S8 | - FD_STATUS_S9 | FD_STATUS_S10 | FD_STATUS_S11)) - != 0) + else /* If reception was no successful */ { lp->stats.rx_errors++; +#ifdef DEBUG_RX_INFO + printk(KERN_DEBUG "%s: wv_receive(): frame not received successfully (%X).\n", + dev->name, fd.fd_status); +#endif + #ifdef DEBUG_RX_ERROR if((fd.fd_status & FD_STATUS_S6) != 0) printk(KERN_INFO "%s: wv_receive(): no EOF flag.\n", dev->name); @@ -2579,8 +2588,8 @@ wv_receive(device * dev) if((fd.fd_status & FD_STATUS_S7) != 0) { lp->stats.rx_length_errors++; -#ifdef DEBUG_RX_ERROR - printk(KERN_INFO "%s: wv_receive(): frame too short.\n", +#ifdef DEBUG_RX_FAIL + printk(KERN_DEBUG "%s: wv_receive(): frame too short.\n", dev->name); #endif } @@ -2588,8 +2597,8 @@ wv_receive(device * dev) if((fd.fd_status & FD_STATUS_S8) != 0) { lp->stats.rx_over_errors++; -#ifdef DEBUG_RX_ERROR - printk(KERN_INFO "%s: wv_receive(): rx DMA overrun.\n", +#ifdef DEBUG_RX_FAIL + printk(KERN_DEBUG "%s: wv_receive(): rx DMA overrun.\n", dev->name); #endif } @@ -2597,8 +2606,8 @@ wv_receive(device * dev) if((fd.fd_status & FD_STATUS_S9) != 0) { lp->stats.rx_fifo_errors++; -#ifdef DEBUG_RX_ERROR - printk(KERN_INFO "%s: wv_receive(): ran out of resources.\n", +#ifdef DEBUG_RX_FAIL + printk(KERN_DEBUG "%s: wv_receive(): ran out of resources.\n", dev->name); #endif } @@ -2606,8 +2615,8 @@ wv_receive(device * dev) if((fd.fd_status & FD_STATUS_S10) != 0) { lp->stats.rx_frame_errors++; -#ifdef DEBUG_RX_ERROR - printk(KERN_INFO "%s: wv_receive(): alignment error.\n", +#ifdef DEBUG_RX_FAIL + printk(KERN_DEBUG "%s: wv_receive(): alignment error.\n", dev->name); #endif } @@ -2615,38 +2624,12 @@ wv_receive(device * dev) if((fd.fd_status & FD_STATUS_S11) != 0) { lp->stats.rx_crc_errors++; -#ifdef DEBUG_RX_ERROR - printk(KERN_INFO "%s: wv_receive(): CRC error.\n", dev->name); +#ifdef DEBUG_RX_FAIL + printk(KERN_DEBUG "%s: wv_receive(): CRC error.\n", dev->name); #endif } } - /* Does the frame contain a pointer to the data? Let's check. */ - if(fd.fd_rbd_offset == I82586NULL) -#ifdef DEBUG_RX_ERROR - printk(KERN_INFO "%s: wv_receive(): frame has no data.\n", dev->name); -#endif - else - { - obram_read(ioaddr, fd.fd_rbd_offset, - (unsigned char *) &rbd, sizeof(rbd)); - -#ifdef DEBUG_RX_ERROR - if((rbd.rbd_status & RBD_STATUS_EOF) != RBD_STATUS_EOF) - printk(KERN_INFO "%s: wv_receive(): missing EOF flag.\n", - dev->name); - - if((rbd.rbd_status & RBD_STATUS_F) != RBD_STATUS_F) - printk(KERN_INFO "%s: wv_receive(): missing F flag.\n", - dev->name); -#endif - - pkt_len = rbd.rbd_status & RBD_STATUS_ACNT; - - /* Read the packet and transmit to Linux */ - wv_packet_read(dev, rbd.rbd_bufl, pkt_len); - } /* if frame has data */ - fd.fd_status = 0; obram_write(ioaddr, fdoff(lp->rx_head, fd_status), (unsigned char *) &fd.fd_status, sizeof(fd.fd_status)); @@ -2781,7 +2764,7 @@ if (lp->tx_n_in_use > 0) /* * Data */ - obram_write(ioaddr, buf_addr, buf, clen); + obram_write(ioaddr, buf_addr, buf, length); /* * Overwrite the predecessor NOP link @@ -2967,10 +2950,8 @@ wv_mmc_init(device * dev) (unsigned char *)&psa.psa_quality_thr, 1); psa_write(ioaddr, lp->hacr, (char *)&psa.psa_conf_status - (char *)&psa, (unsigned char *)&psa.psa_conf_status, 1); -#ifdef SET_PSA_CRC /* update the Wavelan checksum */ update_psa_checksum(dev, ioaddr, lp->hacr); -#endif #endif } @@ -3142,7 +3123,7 @@ wv_ru_start(device * dev) if(i <= 0) { -#ifdef DEBUG_CONFIG_ERRORS +#ifdef DEBUG_CONFIG_ERROR printk(KERN_INFO "%s: wavelan_ru_start(): board not accepting command.\n", dev->name); #endif @@ -3246,7 +3227,7 @@ wv_cu_start(device * dev) if(i <= 0) { -#ifdef DEBUG_CONFIG_ERRORS +#ifdef DEBUG_CONFIG_ERROR printk(KERN_INFO "%s: wavelan_cu_start(): board not accepting command.\n", dev->name); #endif @@ -3332,7 +3313,7 @@ wv_82586_start(device * dev) if(i <= 0) { -#ifdef DEBUG_CONFIG_ERRORS +#ifdef DEBUG_CONFIG_ERROR printk(KERN_INFO "%s: wv_82586_start(): iscp_busy timeout.\n", dev->name); #endif @@ -3352,7 +3333,7 @@ wv_82586_start(device * dev) if (i <= 0) { -#ifdef DEBUG_CONFIG_ERRORS +#ifdef DEBUG_CONFIG_ERROR printk(KERN_INFO "%s: wv_82586_start(): status: expected 0x%02x, got 0x%02x.\n", dev->name, SCB_ST_CX | SCB_ST_CNA, scb.scb_status); #endif @@ -3373,7 +3354,7 @@ wv_82586_start(device * dev) obram_read(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb)); if(cb.ac_status & AC_SFLD_FAIL) { -#ifdef DEBUG_CONFIG_ERRORS +#ifdef DEBUG_CONFIG_ERROR printk(KERN_INFO "%s: wv_82586_start(): i82586 Self Test failed.\n", dev->name); #endif @@ -3654,13 +3635,16 @@ wv_hw_reset(device * dev) wv_ints_on(dev); /* Start card functions */ - if((wv_ru_start(dev) < 0) || - (wv_cu_start(dev) < 0)) + if(wv_cu_start(dev) < 0) return -1; - /* Finish configuration */ + /* Setup the controller and parameters */ wv_82586_config(dev); + /* Finish configuration with the receive unit */ + if(wv_ru_start(dev) < 0) + return -1; + #ifdef DEBUG_CONFIG_TRACE printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name); #endif @@ -3968,7 +3952,7 @@ wavelan_open(device * dev) /* Check irq */ if(dev->irq == 0) { -#ifdef DEBUG_CONFIG_ERRORS +#ifdef DEBUG_CONFIG_ERROR printk(KERN_WARNING "%s: wavelan_open(): no IRQ\n", dev->name); #endif return -ENXIO; @@ -3980,7 +3964,7 @@ wavelan_open(device * dev) (request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN", NULL) != 0)) { irq2dev_map[dev->irq] = (device *) NULL; -#ifdef DEBUG_CONFIG_ERRORS +#ifdef DEBUG_CONFIG_ERROR printk(KERN_WARNING "%s: wavelan_open(): invalid IRQ\n", dev->name); #endif return -EAGAIN; @@ -3996,7 +3980,7 @@ wavelan_open(device * dev) { free_irq(dev->irq, NULL); irq2dev_map[dev->irq] = (device *) NULL; -#ifdef DEBUG_CONFIG_ERRORS +#ifdef DEBUG_CONFIG_ERROR printk(KERN_INFO "%s: wavelan_open(): impossible to start the card\n", dev->name); #endif @@ -4094,10 +4078,8 @@ wavelan_config(device * dev) #endif psa_write(ioaddr, HACR_DEFAULT, psaoff(0, psa_int_req_no), &irq_mask, 1); -#ifdef SET_PSA_CRC /* update the Wavelan checksum */ update_psa_checksum(dev, ioaddr, HACR_DEFAULT); -#endif wv_hacr_reset(ioaddr); } } @@ -4151,7 +4133,9 @@ wavelan_config(device * dev) dev->hard_start_xmit = wavelan_packet_xmit; dev->get_stats = wavelan_get_stats; dev->set_multicast_list = &wavelan_set_multicast_list; +#ifdef SET_MAC_ADDRESS dev->set_mac_address = &wavelan_set_mac_address; +#endif /* SET_MAC_ADDRESS */ #ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */ dev->do_ioctl = wavelan_ioctl; @@ -4207,7 +4191,7 @@ wavelan_probe(device * dev) /* Don't probe at all. */ if(base_addr < 0) { -#ifdef DEBUG_CONFIG_ERRORS +#ifdef DEBUG_CONFIG_ERROR printk(KERN_WARNING "%s: wavelan_probe(): invalid base address\n", dev->name); #endif @@ -4290,7 +4274,7 @@ init_module(void) /* If probing is asked */ if(io[0] == 0) { -#ifdef DEBUG_CONFIG_ERRORS +#ifdef DEBUG_CONFIG_ERROR printk(KERN_WARNING "WaveLAN init_module(): doing device probing (bad !)\n"); printk(KERN_WARNING "Specify base addresses while loading module to correct the problem\n"); #endif @@ -4334,7 +4318,7 @@ init_module(void) } /* if there is something at the address */ } /* Loop on all addresses. */ -#ifdef DEBUG_CONFIG_ERRORS +#ifdef DEBUG_CONFIG_ERROR if(wavelan_list == (net_local *) NULL) printk(KERN_WARNING "WaveLAN init_module(): no device found\n"); #endif diff --git a/drivers/net/wavelan.p.h b/drivers/net/wavelan.p.h index 136ebb510238..3ea2b0515475 100644 --- a/drivers/net/wavelan.p.h +++ b/drivers/net/wavelan.p.h @@ -34,11 +34,22 @@ * I try to maintain a web page with the Wireless LAN Howto at : * http://www-uk.hpl.hp.com/people/jt/Linux/Wavelan.html * + * Debugging and options + * --------------------- + * You will find below a set of '#define" allowing a very fine control + * on the driver behaviour and the debug messages printed. + * The main options are : + * o SET_PSA_CRC, to have your card correctly recognised by + * an access point and the Point-to-Point diagnostic tool. + * o USE_PSA_CONFIG, to read configuration from the PSA (EEprom) + * (otherwise we always start afresh with some defaults) + * * wavelan.o is darn too big * ------------------------- * That's true ! There is a very simple way to reduce the driver * object by 33% (yes !). Comment out the following line : * #include + * Other compile options can also reduce the size of it... * * Debugging and options * --------------------- @@ -272,7 +283,17 @@ * - Correct i82586 configuration parameters * - Encryption initialisation bug (Robert McCormack) * - New mac addresses detected in the probe - * - Increase watchdog for busy envirnoments + * - Increase watchdog for busy environments + * + * Changes made for release in 2.0.38 & 2.2.7 : + * ------------------------------------------ + * - Correct the reception logic to better report errors and avoid + * sending bogus packet up the stack + * - Delay RU config to avoid corrupting first received packet + * - Change config completion code (to actually check something) + * - Avoid reading out of bound in skbuf to transmit + * - Rectify a lot of (useless) debugging code + * - Change the way to `#ifdef SET_PSA_CRC' * * Wishes & dreams : * --------------- @@ -311,6 +332,24 @@ #include "i82586.h" #include "wavelan.h" +/************************** DRIVER OPTIONS **************************/ +/* + * `#define' or `#undef' the following constant to change the behaviour + * of the driver... + */ +#undef SET_PSA_CRC /* Calculate and set the CRC on PSA (slower) */ +#define USE_PSA_CONFIG /* Use info from the PSA. */ +#undef STRUCT_CHECK /* Verify padding of structures. */ +#undef EEPROM_IS_PROTECTED /* doesn't seem to be necessary */ +#define MULTICAST_AVOID /* Avoid extra multicast (I'm sceptical). */ +#undef SET_MAC_ADDRESS /* Experimental */ + +#ifdef WIRELESS_EXT /* If wireless extensions exist in the kernel */ +/* Warning: this stuff will slow down the driver. */ +#define WIRELESS_SPY /* Enable spying addresses. */ +#undef HISTOGRAM /* Enable histogram of signal level. */ +#endif + /****************************** DEBUG ******************************/ #undef DEBUG_MODULE_TRACE /* Module insertion/removal */ @@ -320,14 +359,16 @@ #define DEBUG_INTERRUPT_ERROR /* problems */ #undef DEBUG_CONFIG_TRACE /* Trace the config functions */ #undef DEBUG_CONFIG_INFO /* What's going on... */ -#define DEBUG_CONFIG_ERRORS /* Errors on configuration */ +#define DEBUG_CONFIG_ERROR /* Errors on configuration */ #undef DEBUG_TX_TRACE /* Transmission calls */ #undef DEBUG_TX_INFO /* Header of the transmited packet */ -#define DEBUG_TX_ERROR /* unexpected conditions */ +#undef DEBUG_TX_FAIL /* Normal failure conditions */ +#define DEBUG_TX_ERROR /* Unexpected conditions */ #undef DEBUG_RX_TRACE /* Transmission calls */ -#undef DEBUG_RX_INFO /* Header of the transmited packet */ -#define DEBUG_RX_ERROR /* unexpected conditions */ -#undef DEBUG_PACKET_DUMP 16 /* Dump packet on the screen */ +#undef DEBUG_RX_INFO /* Header of the received packet */ +#undef DEBUG_RX_FAIL /* Normal failure conditions */ +#define DEBUG_RX_ERROR /* Unexpected conditions */ +#undef DEBUG_PACKET_DUMP 32 /* Dump packet on the screen */ #undef DEBUG_IOCTL_TRACE /* Misc call by Linux */ #undef DEBUG_IOCTL_INFO /* Various debug info */ #define DEBUG_IOCTL_ERROR /* What's going wrong */ @@ -339,26 +380,10 @@ #undef DEBUG_I82586_SHOW /* Show i82586 status */ #undef DEBUG_DEVICE_SHOW /* Show device parameters */ -/* Options : */ -#define USE_PSA_CONFIG /* Use info from the PSA */ -#define SET_PSA_CRC /* Calculate and set the CRC on PSA */ -#define IGNORE_NORMAL_XMIT_ERRS /* Don't bother with normal conditions */ -#undef STRUCT_CHECK /* Verify padding of structures */ -#undef OLDIES /* Old code (to redo) */ -#undef RECORD_SNR /* To redo */ -#undef EEPROM_IS_PROTECTED /* Doesn't seem to be necessary */ -#define MULTICAST_AVOID /* Avoid extra multicast (I'm sceptical) */ - -#ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */ -/* Warning : these stuff will slow down the driver... */ -#define WIRELESS_SPY /* Enable spying addresses */ -#undef HISTOGRAM /* Enable histogram of sig level... */ -#endif - /************************ CONSTANTS & MACROS ************************/ #ifdef DEBUG_VERSION_SHOW -static const char *version = "wavelan.c : v18 (wireless extensions) 18/2/99\n"; +static const char *version = "wavelan.c : v19 (wireless extensions) 20/4/99\n"; #endif /* Watchdog temporisation */ diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 11c1a91496dd..6d2e034382d2 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -197,28 +197,20 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, interp_elf_ex->e_type != ET_DYN) || !elf_check_arch(interp_elf_ex->e_machine) || (!interpreter_inode->i_op || - !interpreter_inode->i_op->default_file_ops->mmap)) { + !interpreter_inode->i_op->default_file_ops->mmap) || + interp_elf_ex->e_phentsize != sizeof(struct elf_phdr) || + interp_elf_ex->e_phnum < 1 || + interp_elf_ex->e_phnum > PAGE_SIZE / sizeof(struct elf_phdr)) { return ~0UL; } /* Now read in all of the header information */ - if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > PAGE_SIZE) - return ~0UL; - elf_phdata = (struct elf_phdr *) kmalloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, GFP_KERNEL); if (!elf_phdata) return ~0UL; - /* - * If the size of this structure has changed, then punt, since - * we will be doing the wrong thing. - */ - if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr)) { - kfree(elf_phdata); - return ~0UL; - } retval = read_exec(interpreter_inode, interp_elf_ex->e_phoff, (char *) elf_phdata, sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, 1); @@ -306,6 +298,11 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, kfree(elf_phdata); *interp_load_addr = load_addr; + /* + * AUDIT: is everything deallocated properly if this happens + * to be ~0UL? We'd better switch to out-of-band error reporting. + * Also for a.out. + */ return ((unsigned long) interp_elf_ex->e_entry) + load_addr; } @@ -397,7 +394,10 @@ static inline int do_load_elf_binary(struct linux_binprm *bprm, struct pt_regs * elf_ex.e_type != ET_DYN) || (!elf_check_arch(elf_ex.e_machine)) || (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops || - !bprm->inode->i_op->default_file_ops->mmap)) { + !bprm->inode->i_op->default_file_ops->mmap) || + elf_ex.e_phentsize != sizeof(struct elf_phdr) || + elf_ex.e_phnum < 1 || + elf_ex.e_phnum > 0xfff0 / sizeof(struct elf_phdr)) { return -ENOEXEC; } /* Now read in all of the header information */ @@ -434,12 +434,14 @@ static inline int do_load_elf_binary(struct linux_binprm *bprm, struct pt_regs * for (i = 0; i < elf_ex.e_phnum; i++) { if (elf_ppnt->p_type == PT_INTERP) { - if (elf_interpreter != NULL) { + if (elf_interpreter != NULL || + elf_ppnt->p_filesz < 2 || + elf_ppnt->p_filesz > PAGE_SIZE) { iput(interpreter_inode); kfree(elf_phdata); kfree(elf_interpreter); sys_close(elf_exec_fileno); - return -EINVAL; + return -ENOEXEC; } /* This is the program interpreter used for * shared libraries - for now assume that this @@ -456,6 +458,7 @@ static inline int do_load_elf_binary(struct linux_binprm *bprm, struct pt_regs * retval = read_exec(bprm->inode, elf_ppnt->p_offset, elf_interpreter, elf_ppnt->p_filesz, 1); + elf_interpreter[elf_ppnt->p_filesz - 1] = 0; /* If the program interpreter is one of these two, then assume an iBCS2 image. Otherwise assume a native linux image. */ @@ -629,14 +632,17 @@ static inline int do_load_elf_binary(struct linux_binprm *bprm, struct pt_regs * &interp_load_addr); iput(interpreter_inode); - kfree(elf_interpreter); if (elf_entry == ~0UL) { - printk("Unable to load interpreter\n"); + printk("Unable to load interpreter %.128s\n", + elf_interpreter); + kfree(elf_interpreter); kfree(elf_phdata); send_sig(SIGSEGV, current, 0); return 0; } + + kfree(elf_interpreter); } kfree(elf_phdata); diff --git a/fs/dquot.c b/fs/dquot.c index af115eb5a494..b778a3e85180 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -143,14 +143,17 @@ static void grow_dquots(void) return; dqstats.pages_allocated++; cnt = PAGE_SIZE / sizeof(struct dquot); + memset(dquot, 0, PAGE_SIZE); nr_dquots += cnt; nr_free_dquots += cnt; if (!first_dquot) { dquot->dq_next = dquot->dq_prev = first_dquot = dquot++; cnt--; } - for (; cnt; cnt--) + for (; cnt; cnt--) { + init_waitqueue(&dquot->dq_wait); insert_dquot_free(dquot++); + } } /* @@ -219,11 +222,18 @@ static void write_dquot(struct dquot *dquot) short type = dquot->dq_type; struct file *filp = dquot->dq_mnt->mnt_quotas[type]; unsigned short fs; + struct dqblk wrt_dquot; if (!(dquot->dq_flags & DQ_MOD) || (filp == (struct file *)NULL)) return; lock_dquot(dquot); + if (!dquot->dq_mnt) { /* Invalidated dquot? */ + unlock_dquot(dquot); + return; + } down(&dquot->dq_mnt->mnt_sem); + dquot->dq_flags &= ~DQ_MOD; /* Clear the flag unconditionally - we don't want to loop on error */ + memcpy(&wrt_dquot, &dquot->dq_dqb, sizeof(struct dqblk)); /* Copy structure so we can unlock it */ if (filp->f_op->lseek) { if (filp->f_op->lseek(filp->f_inode, filp, dqoff(dquot->dq_id), 0) != dqoff(dquot->dq_id)) { @@ -238,14 +248,13 @@ static void write_dquot(struct dquot *dquot) if(p>=0) filp->f_pos = p; } + unlock_dquot(dquot); /* We have to unlock structure as write might need to update it */ fs = get_fs(); set_fs(KERNEL_DS); - if (filp->f_op->write(filp->f_inode, filp, - (char *)&dquot->dq_dqb, sizeof(struct dqblk)) == sizeof(struct dqblk)) - dquot->dq_flags &= ~DQ_MOD; + if (filp->f_op->write(filp->f_inode, filp, (char *)&wrt_dquot, sizeof(struct dqblk)) != sizeof(struct dqblk)) + printk(KERN_ERR "VFS: write_dquot() failed.\n"); up(&dquot->dq_mnt->mnt_sem); set_fs(fs); - unlock_dquot(dquot); dqstats.writes++; } @@ -286,14 +295,18 @@ int sync_dquots(kdev_t dev, short type) int i; dqstats.syncs++; +restart: for (i = 0; i < nr_dquots * 2; i++, dquot = dquot->dq_next) { if (dev == NODEV || dquot->dq_count == 0 || dquot->dq_dev != dev) continue; if (type != -1 && dquot->dq_type != type) continue; + if (!(dquot->dq_flags & DQ_MOD)) /* It might get modified I know but to restart after each locked dquot... */ + continue; wait_on_dquot(dquot); if (dquot->dq_flags & DQ_MOD) write_dquot(dquot); + goto restart; } return(0); } @@ -306,6 +319,7 @@ void invalidate_dquots(kdev_t dev, short type) struct dquot *dquot, *next; int cnt; +restart: next = first_dquot; for (cnt = nr_dquots ; cnt > 0 ; cnt--) { dquot = next; @@ -316,10 +330,14 @@ void invalidate_dquots(kdev_t dev, short type) printk("VFS: dquot busy on removed device %s\n", kdevname(dev)); continue; } - if (dquot->dq_flags & DQ_MOD) + if (dquot->dq_flags & DQ_MOD) { write_dquot(dquot); + dqstats.drops++; + clear_dquot(dquot); + goto restart; /* As we might block inside of write or clear_dquot */ + } dqstats.drops++; - clear_dquot(dquot); + clear_dquot(dquot); /* Here we can't block - DQ_LOCKED was tested before */ } } @@ -463,30 +481,34 @@ static void dqput(struct dquot *dquot) * checking and doesn't need to be written. It just an empty * dquot that is put back into the freelist. */ - if (dquot->dq_mnt != (struct vfsmount *)NULL) { - dqstats.drops++; - wait_on_dquot(dquot); +repeat: + if (dquot->dq_mnt) { /* We can block inside of wait and so we have to check again */ + if (dquot->dq_flags & DQ_LOCKED) { + __wait_on_dquot(dquot); + goto repeat; + } + if (!dquot->dq_count) { printk("VFS: dqput: trying to free free dquot\n"); printk("VFS: device %s, dquot of %s %d\n", kdevname(dquot->dq_dev), quotatypes[dquot->dq_type], dquot->dq_id); return; } -repeat: if (dquot->dq_count > 1) { dquot->dq_count--; return; } - wake_up(&dquot_wait); if (dquot->dq_flags & DQ_MOD) { write_dquot(dquot); /* we can sleep - so do again */ wait_on_dquot(dquot); goto repeat; } + dqstats.drops++; } if (dquot->dq_count) { dquot->dq_count--; nr_free_dquots++; + wake_up(&dquot_wait); /* Here the dquot is really free */ } return; } @@ -583,6 +605,14 @@ repeat: put_last_free(dquot); insert_dquot_hash(dquot); read_dquot(dquot); + if (!dquot->dq_mnt) { /* Invalidated in the mean time? */ + /* + * As quota was turned off we can just return NODQUOT. I know it's + * not perfect as somebody might turn quota on again but... + */ + dqput(dquot); + return NODQUOT; + } return(dquot); } @@ -959,9 +989,11 @@ int quota_off(kdev_t dev, short type) vfsmnt->mnt_sb->dq_op = (struct dquot_operations *)NULL; reset_dquot_ptrs(dev, cnt); invalidate_dquots(dev, cnt); + down(&vfsmnt->mnt_sem); /* Wait for any pending IO - dquot is not locked on write so we can easily go here during write */ close_fp(vfsmnt->mnt_quotas[cnt]); vfsmnt->mnt_quotas[cnt] = (struct file *)NULL; vfsmnt->mnt_iexp[cnt] = vfsmnt->mnt_bexp[cnt] = (time_t)NULL; + up(&vfsmnt->mnt_sem); } return(0); } diff --git a/fs/exec.c b/fs/exec.c index 1cea5624701d..d8a31a3eb096 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -178,26 +178,51 @@ asmlinkage int sys_uselib(const char * library) /* * count() counts the number of arguments/envelopes - * - * We also do some limited EFAULT checking: this isn't complete, but - * it does cover most cases. I'll have to do this correctly some day.. */ -static int count(char ** argv) +static int count(void *base, int size, int max) { int error, i = 0; - char ** tmp, *p; - - if ((tmp = argv) != NULL) { - error = verify_area(VERIFY_READ, tmp, sizeof(char *)); - if (error) - return error; - while ((p = get_user(tmp++)) != NULL) { - i++; - error = verify_area(VERIFY_READ, p, 1); - if (error) - return error; + void *tmp = base; + unsigned long length = 0, chunk = size, limit; + int grow = 1; + + if (!tmp) return 0; + + limit = PAGE_SIZE - ((unsigned long)tmp & (PAGE_SIZE - 1)); + error = verify_area(VERIFY_READ, tmp, limit); + if (error) limit = 0; + + do { + if (length >= limit) + do { + if (!grow) { + if (chunk <= sizeof(char *)) + return -EFAULT; + chunk >>= 1; + } + error = verify_area(VERIFY_READ, tmp, chunk); + if (error) grow = 0; else { + limit += chunk; + if (grow) chunk <<= 1; + } + } while (error); + + if (size == 1) { + do { + if (!get_user(((char *)tmp)++)) goto out; + if (++i > max) return -E2BIG; + } while (i < limit); + length = i; + } else { + do { + if (!get_user(((char **)tmp)++)) goto out; + if ((length += size) > max) return -E2BIG; + i++; + } while (length < limit); } - } + } while (1); + +out: return i; } @@ -221,12 +246,12 @@ static int count(char ** argv) unsigned long copy_strings(int argc,char ** argv,unsigned long *page, unsigned long p, int from_kmem) { - char *tmp, *tmp1, *pag = NULL; + char *tmp, *pag = NULL; int len, offset = 0; unsigned long old_fs, new_fs; - if (!p) - return 0; /* bullet-proofing */ + if ((long)p <= 0) + return p; /* bullet-proofing */ new_fs = get_ds(); old_fs = get_fs(); if (from_kmem==2) @@ -234,16 +259,16 @@ unsigned long copy_strings(int argc,char ** argv,unsigned long *page, while (argc-- > 0) { if (from_kmem == 1) set_fs(new_fs); - if (!(tmp1 = tmp = get_user(argv+argc))) + if (!(tmp = get_user(argv+argc))) panic("VFS: argc is wrong"); if (from_kmem == 1) set_fs(old_fs); - while (get_user(tmp++)); - len = tmp - tmp1; - if (p < len) { /* this shouldn't happen - 128kB */ + len = count(tmp, 1, p); + if (len < 0 || len >= p) { /* EFAULT or E2BIG */ set_fs(old_fs); - return 0; + return len < 0 ? len : -E2BIG; } + tmp += ++len; while (len) { --p; --tmp; --len; if (--offset < 0) { @@ -253,7 +278,7 @@ unsigned long copy_strings(int argc,char ** argv,unsigned long *page, if (!(pag = (char *) page[p/PAGE_SIZE]) && !(pag = (char *) page[p/PAGE_SIZE] = (unsigned long *) get_free_page(GFP_USER))) - return 0; + return -EFAULT; if (from_kmem==2) set_fs(new_fs); @@ -559,6 +584,27 @@ int prepare_binprm(struct linux_binprm *bprm) if (!suser()) return -EPERM; } + + /* + * Increment the privileged execution counter, so that our + * old children know not to send bad exit_signal's to us. + */ + if (!++current->priv) { + struct task_struct *p; + + /* + * The counter can't really overflow with real-world + * programs (and it has to be the privileged program + * itself that causes the overflow), but we handle + * this case anyway, just for correctness. + */ + for_each_task(p) { + if (p->p_pptr == current) { + p->ppriv = 0; + current->priv = 1; + } + } + } } memset(bprm->buf,0,sizeof(bprm->buf)); @@ -601,6 +647,8 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) bprm->dont_iput = 1; remove_arg_zero(bprm); bprm->p = copy_strings(1, dynloader, bprm->page, bprm->p, 2); + if ((long)bprm->p < 0) + return (long)bprm->p; bprm->argc++; bprm->loader = bprm->p; retval = open_namei(dynloader[0], 0, 0, &bprm->inode, NULL); @@ -673,9 +721,9 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs bprm.loader = 0; bprm.exec = 0; bprm.dont_iput = 0; - if ((bprm.argc = count(argv)) < 0) + if ((bprm.argc = count(argv, sizeof(char *), bprm.p)) < 0) return bprm.argc; - if ((bprm.envc = count(envp)) < 0) + if ((bprm.envc = count(envp, sizeof(char *), bprm.p)) < 0) return bprm.envc; retval = prepare_binprm(&bprm); @@ -685,8 +733,8 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs bprm.exec = bprm.p; bprm.p = copy_strings(bprm.envc,envp,bprm.page,bprm.p,0); bprm.p = copy_strings(bprm.argc,argv,bprm.page,bprm.p,0); - if (!bprm.p) - retval = -E2BIG; + if ((long)bprm.p < 0) + retval = (long)bprm.p; } if(retval>=0) diff --git a/fs/proc/array.c b/fs/proc/array.c index d2e2323867e1..33357d99f641 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -610,6 +610,7 @@ static int get_stat(int pid, char * buffer) vsize += vma->vm_end - vma->vm_start; vma = vma->vm_next; } + if ((current->fsuid == tsk->euid && tsk->dumpable) || suser()) if (tsk->kernel_stack_page) { eip = KSTK_EIP(tsk); esp = KSTK_ESP(tsk); @@ -1026,7 +1027,7 @@ static int process_unauthorized(int type, int pid) case PROC_PID_CMDLINE: return 0; } - if(suser() || current->fsuid == (*p)->euid) + if ((current->fsuid == (*p)->euid && (*p)->dumpable) || suser()) return 0; return 1; } diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 6ddae29c0ad9..243bb7d4f57e 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -86,6 +86,7 @@ static int proc_lookupfd(struct inode * dir, const char * name, int len, } iput(dir); fd = 0; + if (len > 1 && *name == '0') fd = 0xfffff; else while (len-- > 0) { c = *name - '0'; name++; diff --git a/fs/proc/mem.c b/fs/proc/mem.c index 2b9fb9ac6bdc..668220e5c490 100644 --- a/fs/proc/mem.c +++ b/fs/proc/mem.c @@ -17,17 +17,16 @@ /* * mem_write isn't really a good idea right now. It needs - * to check a lot more: if the process we try to write to + * to check a lot more: if the process we try to write to * dies in the middle right now, mem_write will overwrite * kernel memory.. This disables it altogether. */ #define mem_write NULL -static ssize_t check_range(struct mm_struct * mm, unsigned long addr, - size_t count) +static int check_range(struct mm_struct * mm, unsigned long addr, int count) { struct vm_area_struct *vma; - ssize_t retval; + int retval; vma = find_vma(mm, addr); if (!vma) @@ -77,8 +76,7 @@ static struct task_struct * get_task(int pid) return tsk; } -static ssize_t mem_read(struct inode * inode, struct file * file, - char * buf, size_t count) +static int mem_read(struct inode * inode, struct file * file,char * buf, int count) { pgd_t *page_dir; pmd_t *page_middle; @@ -87,7 +85,7 @@ static ssize_t mem_read(struct inode * inode, struct file * file, struct task_struct * tsk; unsigned long addr; char *tmp; - ssize_t scount, i; + int i; if (count < 0) return -EINVAL; @@ -95,11 +93,11 @@ static ssize_t mem_read(struct inode * inode, struct file * file, if (!tsk) return -ESRCH; addr = file->f_pos; - scount = check_range(tsk->mm, addr, count); - if (scount < 0) - return scount; + count = check_range(tsk->mm, addr, count); + if (count < 0) + return count; tmp = buf; - while (scount > 0) { + while (count > 0) { if (current->signal & ~current->blocked) break; page_dir = pgd_offset(tsk->mm,addr); @@ -123,12 +121,12 @@ static ssize_t mem_read(struct inode * inode, struct file * file, break; page = (char *) pte_page(pte) + (addr & ~PAGE_MASK); i = PAGE_SIZE-(addr & ~PAGE_MASK); - if (i > scount) - i = scount; + if (i > count) + i = count; memcpy_tofs(tmp, page, i); addr += i; tmp += i; - scount -= i; + count -= i; } file->f_pos = addr; return tmp-buf; @@ -136,8 +134,7 @@ static ssize_t mem_read(struct inode * inode, struct file * file, #ifndef mem_write -static ssize_t mem_write(struct inode * inode, struct file * file, - char * buf, ssize_t count) +static int mem_write(struct inode * inode, struct file * file,char * buf, int count) { pgd_t *page_dir; pmd_t *page_middle; @@ -146,7 +143,7 @@ static ssize_t mem_write(struct inode * inode, struct file * file, struct task_struct * tsk; unsigned long addr; char *tmp; - ssize_t i; + int i; if (count < 0) return -EINVAL; @@ -198,8 +195,7 @@ static ssize_t mem_write(struct inode * inode, struct file * file, #endif -static off_t mem_lseek(struct inode * inode, struct file * file, - off_t offset, int orig) +static int mem_lseek(struct inode * inode, struct file * file, off_t offset, int orig) { switch (orig) { case 0: diff --git a/fs/proc/root.c b/fs/proc/root.c index 962f428a0a16..ac42854e8947 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -466,6 +466,7 @@ static int proc_root_lookup(struct inode * dir,const char * name, int len, } pid *= 10; pid += c; + if (!pid) break; if (pid & 0xffff0000) { pid = 0; break; diff --git a/include/linux/sched.h b/include/linux/sched.h index 523d4c483c50..4f8eb3c17ce8 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -244,6 +244,8 @@ struct task_struct { struct mm_struct *mm; /* signal handlers */ struct signal_struct *sig; +/* privileged execution counters, for exit_signal permission checking */ + int priv, ppriv; #ifdef __SMP__ int processor; int last_processor; @@ -309,6 +311,7 @@ struct task_struct { /* files */ &init_files, \ /* mm */ &init_mm, \ /* signals */ &init_signals, \ +/* priv */ 0, 0, \ } extern struct mm_struct init_mm; diff --git a/include/net/ip_masq.h b/include/net/ip_masq.h index a06e75f19be9..ddab2da4783f 100644 --- a/include/net/ip_masq.h +++ b/include/net/ip_masq.h @@ -24,6 +24,8 @@ #define PORT_MASQ_BEGIN 61000 #define PORT_MASQ_END (PORT_MASQ_BEGIN+4096) +#define IP_MASQ_TAB_SIZE 256 /* MUST be a power of 2 */ + /* * Default timeouts for masquerade functions The control channels now * expire the same as TCP channels (other than being updated by @@ -113,6 +115,18 @@ extern struct ip_fw_masq *ip_masq_expire; extern int ip_masq_free_ports[3]; +#ifdef CONFIG_IP_MASQUERADE_IPSEC +/* + * Any application support module that changes the destination + * IP or port *must* rehash if IPsec masq is enabled + */ +extern struct ip_masq *ip_masq_m_tab[IP_MASQ_TAB_SIZE]; +extern struct ip_masq *ip_masq_s_tab[IP_MASQ_TAB_SIZE]; +extern struct ip_masq *ip_masq_d_tab[IP_MASQ_TAB_SIZE]; +extern __inline__ int ip_masq_hash(struct ip_masq *ms); +extern __inline__ int ip_masq_unhash(struct ip_masq *ms); +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + /* * ip_masq initializer (registers symbols and /proc/net entries) */ diff --git a/kernel/exit.c b/kernel/exit.c index ae2bb3518857..4b0dbef6b5be 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -118,7 +118,8 @@ int send_sig(unsigned long sig,struct task_struct * p,int priv) void notify_parent(struct task_struct * tsk, int signal) { - send_sig(signal, tsk->p_pptr, 1); + send_sig(signal, tsk->p_pptr, !signal || signal == SIGCHLD || + tsk->p_pptr->priv == tsk->ppriv); wake_up_interruptible(&tsk->p_pptr->wait_chldexit); } diff --git a/kernel/fork.c b/kernel/fork.c index 42254afadefc..fda6cbf43f75 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -228,10 +228,23 @@ static inline int copy_sighand(unsigned long clone_flags, struct task_struct * t int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs) { int nr; - int error = -ENOMEM; + int error = -EINVAL; unsigned long new_stack; struct task_struct *p; +/* + * Disallow unknown clone(2) flags, as well as CLONE_PID, unless we are + * the boot up thread. + * + * Avoid taking any branches in the common case. + */ + if (clone_flags & + (-(signed long)current->pid >> (sizeof(long) * 8 - 1)) & + ~(unsigned long)(CSIGNAL | + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND)) + goto bad_fork; + + error = -ENOMEM; p = (struct task_struct *) kmalloc(sizeof(*p), GFP_KERNEL); if (!p) goto bad_fork; @@ -264,6 +277,8 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs) p->p_cptr = NULL; init_waitqueue(&p->wait_chldexit); p->signal = 0; + p->priv = 0; + p->ppriv = current->priv; p->it_real_value = p->it_virt_value = p->it_prof_value = 0; p->it_real_incr = p->it_virt_incr = p->it_prof_incr = 0; init_timer(&p->real_timer); diff --git a/net/ipv4/ip_masq.c b/net/ipv4/ip_masq.c index bf71ed067909..08175f775c9d 100644 --- a/net/ipv4/ip_masq.c +++ b/net/ipv4/ip_masq.c @@ -1,8 +1,8 @@ /* * - * Masquerading functionality + * Masquerading functionality * - * Copyright (c) 1994 Pauline Middelink + * Copyright (c) 1994 Pauline Middelink * * See ip_fw.c for original log * @@ -20,8 +20,9 @@ * 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 - * + * John D. Hardin : Added PPTP and IPSEC protocols + * Rudolf Lippan : dest hashing bugfix + * */ #include @@ -44,8 +45,6 @@ #include #include -#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 @@ -301,6 +300,10 @@ static struct symbol_table ip_masq_syms = { X(ip_masq_set_expire), X(ip_masq_free_ports), X(ip_masq_expire), +#ifdef CONFIG_IP_MASQUERADE_IPSEC + X(ip_masq_hash), + X(ip_masq_unhash), +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ X(ip_masq_out_get_2), #include }; @@ -445,7 +448,10 @@ ip_masq_hash_key(unsigned proto, __u32 addr, __u16 port) * returns bool success. */ -static __inline__ int +#ifndef CONFIG_IP_MASQUERADE_IPSEC +static +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ +__inline__ int ip_masq_hash(struct ip_masq *ms) { unsigned hash; @@ -495,7 +501,10 @@ ip_masq_hash(struct ip_masq *ms) * returns bool success. */ -static __inline__ int ip_masq_unhash(struct ip_masq *ms) +#ifndef CONFIG_IP_MASQUERADE_IPSEC +static +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ +__inline__ int ip_masq_unhash(struct ip_masq *ms) { unsigned hash; struct ip_masq ** ms_p; @@ -3158,16 +3167,38 @@ int ip_fw_demasquerade(struct sk_buff **skb_p, struct device *dev) */ if ( ms->flags & IP_MASQ_F_NO_DPORT && ms->protocol == IPPROTO_TCP ) { +#ifdef CONFIG_IP_MASQUERADE_IPSEC + unsigned long flags; + save_flags(flags); + cli(); + ip_masq_unhash(ms); +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ ms->flags &= ~IP_MASQ_F_NO_DPORT; ms->dport = portptr[0]; +#ifdef CONFIG_IP_MASQUERADE_IPSEC + ip_masq_hash(ms); + restore_flags(flags); +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + #ifdef DEBUG_CONFIG_IP_MASQUERADE printk("ip_fw_demasquerade(): filled dport=%d\n", ntohs(ms->dport)); #endif } if (ms->flags & IP_MASQ_F_NO_DADDR && ms->protocol == IPPROTO_TCP) { +#ifdef CONFIG_IP_MASQUERADE_IPSEC + unsigned long flags; + save_flags(flags); + cli(); + ip_masq_unhash(ms); +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ ms->flags &= ~IP_MASQ_F_NO_DADDR; ms->daddr = iph->saddr; +#ifdef CONFIG_IP_MASQUERADE_IPSEC + ip_masq_hash(ms); + restore_flags(flags); +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + #ifdef DEBUG_CONFIG_IP_MASQUERADE printk("ip_fw_demasquerade(): filled daddr=%X\n", ntohs(ms->daddr)); diff --git a/net/ipv4/ip_masq.c.orig b/net/ipv4/ip_masq.c.orig new file mode 100644 index 000000000000..bf71ed067909 --- /dev/null +++ b/net/ipv4/ip_masq.c.orig @@ -0,0 +1,3416 @@ +/* + * + * Masquerading functionality + * + * Copyright (c) 1994 Pauline Middelink + * + * See ip_fw.c for original log + * + * Fixes: + * Juan Jose Ciarlante : Modularized application masquerading (see ip_masq_app.c) + * Juan Jose Ciarlante : New struct ip_masq_seq that holds output/input delta seq. + * Juan Jose Ciarlante : Added hashed lookup by proto,maddr,mport and proto,saddr,sport + * Juan Jose Ciarlante : Fixed deadlock if free ports get exhausted + * Juan Jose Ciarlante : Added NO_ADDR status flag. + * Richard Lynch : Added IP Autoforward + * Nigel Metheringham : Added ICMP handling for demasquerade + * Nigel Metheringham : Checksum checking of masqueraded data + * Nigel Metheringham : Better handling of timeouts of TCP conns + * Keith Owens : Keep control channels alive if any related data entries. + * Delian Delchev : Added support for ICMP requests and replys + * Nigel Metheringham : ICMP in ICMP handling, tidy ups, bug fixes, made ICMP optional + * Juan Jose Ciarlante : re-assign maddr if no packet received from outside + * John D. Hardin : Added PPTP and IPSEC protocols + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 gets all blame... + * See also http://www.wolfenet.com/~jhardin/ip_masq_vpn.html + */ + +static const char *strGREProt = "GRE"; + +#ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT +/* + * MULTICLIENT watches the control channel and preloads the + * call ID into the masq table entry, so we want the + * masq table entry to persist until a Call Disconnect + * occurs, otherwise the call IDs will be lost and the link broken. + */ +#define MASQUERADE_EXPIRE_PPTP 15*60*HZ + +/* + * To support multiple clients communicating with the same server, + * we have to sniff the control channel and trap the client's + * call ID, then substitute a unique-to-the-firewall call ID. + * Then on inbound GRE packets we use the bogus call ID to figure + * out which client to route the traffic to, then replace the + * bogus call ID with the client's real call ID, which we've saved. + * For simplicity we'll use masq port as the bogus call ID. + * The actual call ID will be stored in the masq table as + * the source port, and the destination port will always be zero. + * + * NB: PPTP servers can tell whether the client is masqueraded by + * looking for call IDs above 61000. + */ +#define PPTP_CONTROL_PORT 1723 + +#else /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ + +/* non-MULTICLIENT ignores call IDs, so masq table + * entries may expire quickly without causing problems. + */ +#define MASQUERADE_EXPIRE_PPTP 5*60*HZ + +#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ + +/* + * Define this here rather than in /usr/src/linux/include/wherever/whatever.h + * in order to localize my mistakes to one file... + * + * This struct may be architecture-specific because of the bitmaps. + */ +struct pptp_gre_header { + __u8 + recur:3, + is_strict:1, + has_seq:1, + has_key:1, + has_routing:1, + has_cksum:1; + __u8 + version:3, + flags:5; + __u16 + protocol, + payload_len, + call_id; /* peer's call_id for this session */ + +}; + +#endif /* CONFIG_IP_MASQUERADE_PPTP */ + + +#ifdef CONFIG_IP_MASQUERADE_IPSEC +/* + * The above comments about PPTP apply here, too. This should all be a module. + * + * The "port numbers" for masq table purposes will be part of the + * SPI, just to gain a little benefit from the hashing. + */ + +static const char *strESPProt = "ESP"; +static const char *strAHProt = "AH"; + +/* + * ISAKMP uses 500/udp, and the traffic must come from + * 500/udp (i.e. 500/udp <-> 500/udp), so we need to + * check for ISAKMP UDP traffic and avoid changing the + * source port number. In order to associate the data streams + * we may need to sniff the ISAKMP cookies as well. + */ +#define UDP_PORT_ISAKMP 500 /* ISAKMP default UDP port */ + +#if CONFIG_IP_MASQUERADE_IPSEC_EXPIRE > 15 +#define MASQUERADE_EXPIRE_IPSEC CONFIG_IP_MASQUERADE_IPSEC_EXPIRE*60*HZ +#else +#define MASQUERADE_EXPIRE_IPSEC 15*60*HZ +#endif + +/* + * We can't know the inbound SPI until it comes in (the ISAKMP exchange + * is encryptd so we can't sniff it out of that), so we associate inbound + * and outbound traffic by inspection. If somebody sends a new packet to a + * remote server, then block all other new traffic to that server until we + * get a response from that server with a SPI we haven't seen yet. It is + * assumed that this is the correct response - we have no way to verify it, + * as everything else is encrypted. + * + * If there is a collision, the block will last for up to two minutes (or + * whatever MASQUERADE_EXPIRE_IPSEC_INIT is set to), and if the client + * retries during that time the timer will be reset. This could easily lead + * to a Denial of Service, so we limit the number of retries that will + * reset the timer. This means the maximum time the server could be blocked + * is ((IPSEC_INIT_RETRIES + 1) * MASQUERADE_EXPIRE_IPSEC_INIT). + * + * Note: blocking will not affect already-established traffic (i.e. where + * the inbound SPI has been associated with an outbound SPI). + */ +#define MASQUERADE_EXPIRE_IPSEC_INIT 2*60*HZ +#define IPSEC_INIT_RETRIES 5 + +/* + * ...connections that don't get an answer are squelched + * (recognized but ignored) for a short time to prevent DoS. + * SPI values 1-255 are reserved by the IANA and are currently (2/99) + * not assigned. If that should change, this number must also be changed + * to an unused NONZERO value: + */ +#define IPSEC_INIT_SQUELCHED 1 + +struct ip_masq * ip_masq_out_get_ipsec(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 o_spi); +struct ip_masq * ip_masq_in_get_ipsec(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 i_spi); +struct ip_masq * ip_masq_out_get_isakmp(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 cookie); +struct ip_masq * ip_masq_in_get_isakmp(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 cookie); + +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + +/* + * Implement IP packet masquerading + */ + +static const char *strProt[] = {"UDP","TCP","ICMP"}; + +/* + * masq_proto_num returns 0 for UDP, 1 for TCP, 2 for ICMP + * + * No, I am NOT going to add GRE/ESP/AH support to everything that relies on this... + * + */ + +static int masq_proto_num(unsigned proto) +{ + switch (proto) + { + case IPPROTO_UDP: return (0); break; +#ifdef CONFIG_IP_MASQUERADE_PPTP + case IPPROTO_GRE: +#endif /* CONFIG_IP_MASQUERADE_PPTP */ +#ifdef CONFIG_IP_MASQUERADE_IPSEC + case IPPROTO_ESP: +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + case IPPROTO_TCP: return (1); break; + case IPPROTO_ICMP: return (2); break; + default: return (-1); break; + } +} + +#ifdef CONFIG_IP_MASQUERADE_ICMP +/* + * Converts an ICMP reply code into the equivalent request code + */ +static __inline__ const __u8 icmp_type_request(__u8 type) +{ + switch (type) + { + case ICMP_ECHOREPLY: return ICMP_ECHO; break; + case ICMP_TIMESTAMPREPLY: return ICMP_TIMESTAMP; break; + case ICMP_INFO_REPLY: return ICMP_INFO_REQUEST; break; + case ICMP_ADDRESSREPLY: return ICMP_ADDRESS; break; + default: return (255); break; + } +} + +/* + * Helper macros - attempt to make code clearer! + */ + +/* ID used in ICMP lookups */ +#define icmp_id(icmph) ((icmph->un).echo.id) +/* (port) hash value using in ICMP lookups for requests */ +#define icmp_hv_req(icmph) ((__u16)(icmph->code+(__u16)(icmph->type<<8))) +/* (port) hash value using in ICMP lookups for replies */ +#define icmp_hv_rep(icmph) ((__u16)(icmph->code+(__u16)(icmp_type_request(icmph->type)<<8))) +#endif + +static __inline__ const char *masq_proto_name(unsigned proto) +{ + + /* + * I don't want to track down everything that + * relies on masq_proto_num() and make it GRE/ESP/AH-tolerant. + */ +#ifdef CONFIG_IP_MASQUERADE_PPTP + if (proto == IPPROTO_GRE) { + return strGREProt; + } +#endif /* CONFIG_IP_MASQUERADE_PPTP */ +#ifdef CONFIG_IP_MASQUERADE_IPSEC + if (proto == IPPROTO_ESP) { + return strESPProt; + } else if (proto == IPPROTO_AH) { + return strAHProt; + } +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + + return strProt[masq_proto_num(proto)]; +} + +/* + * Last masq_port number in use. + * Will cycle in MASQ_PORT boundaries. + */ +static __u16 masq_port = PORT_MASQ_BEGIN; + +/* + * free ports counters (UDP & TCP) + * + * Their value is _less_ or _equal_ to actual free ports: + * same masq port, diff masq addr (firewall iface address) allocated + * entries are accounted but their actually don't eat a more than 1 port. + * + * Greater values could lower MASQ_EXPIRATION setting as a way to + * manage 'masq_entries resource'. + * + */ + +int ip_masq_free_ports[3] = { + PORT_MASQ_END - PORT_MASQ_BEGIN, /* UDP */ + PORT_MASQ_END - PORT_MASQ_BEGIN, /* TCP */ + PORT_MASQ_END - PORT_MASQ_BEGIN /* ICMP */ +}; + +static struct symbol_table ip_masq_syms = { +#include + X(ip_masq_new), + X(ip_masq_set_expire), + X(ip_masq_free_ports), + X(ip_masq_expire), + X(ip_masq_out_get_2), +#include +}; + +/* + * 2 ip_masq hash tables: for input and output pkts lookups. + */ + +struct ip_masq *ip_masq_m_tab[IP_MASQ_TAB_SIZE]; +struct ip_masq *ip_masq_s_tab[IP_MASQ_TAB_SIZE]; + +#ifdef CONFIG_IP_MASQUERADE_IPSEC + /* + * Add a third hash table for input lookup by remote side + */ +struct ip_masq *ip_masq_d_tab[IP_MASQ_TAB_SIZE]; + +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + +/* + * timeouts + */ + +static struct ip_fw_masq ip_masq_dummy = { + MASQUERADE_EXPIRE_TCP, + MASQUERADE_EXPIRE_TCP_FIN, + MASQUERADE_EXPIRE_UDP +}; + +struct ip_fw_masq *ip_masq_expire = &ip_masq_dummy; + +#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW +/* + * Auto-forwarding table + */ + +struct ip_autofw * ip_autofw_hosts = NULL; + +/* + * Check if a masq entry should be created for a packet + */ + +struct ip_autofw * ip_autofw_check_range (__u32 where, __u16 port, __u16 protocol, int reqact) +{ + struct ip_autofw *af; + af=ip_autofw_hosts; + port=ntohs(port); + while (af) + { + if (af->type==IP_FWD_RANGE && + port>=af->low && + port<=af->high && + protocol==af->protocol && + /* it's ok to create masq entries after the timeout if we're in insecure mode */ + (af->flags & IP_AUTOFW_ACTIVE || !reqact || !(af->flags & IP_AUTOFW_SECURE)) && + (!(af->flags & IP_AUTOFW_SECURE) || af->lastcontact==where || !reqact)) + return(af); + af=af->next; + } + return(NULL); +} + +struct ip_autofw * ip_autofw_check_port (__u16 port, __u16 protocol) +{ + struct ip_autofw *af; + af=ip_autofw_hosts; + port=ntohs(port); + while (af) + { + if (af->type==IP_FWD_PORT && port==af->visible && protocol==af->protocol) + return(af); + af=af->next; + } + return(NULL); +} + +struct ip_autofw * ip_autofw_check_direct (__u16 port, __u16 protocol) +{ + struct ip_autofw *af; + af=ip_autofw_hosts; + port=ntohs(port); + while (af) + { + if (af->type==IP_FWD_DIRECT && af->low<=port && af->high>=port) + return(af); + af=af->next; + } + return(NULL); +} + +void ip_autofw_update_out (__u32 who, __u32 where, __u16 port, __u16 protocol) +{ + struct ip_autofw *af; + af=ip_autofw_hosts; + port=ntohs(port); + while (af) + { + if (af->type==IP_FWD_RANGE && af->ctlport==port && af->ctlproto==protocol) + { + if (af->flags & IP_AUTOFW_USETIME) + { + if (af->timer.expires) + del_timer(&af->timer); + af->timer.expires=jiffies+IP_AUTOFW_EXPIRE; + add_timer(&af->timer); + } + af->flags|=IP_AUTOFW_ACTIVE; + af->lastcontact=where; + af->where=who; + } + af=af->next; + } +} + +void ip_autofw_update_in (__u32 where, __u16 port, __u16 protocol) +{ +/* struct ip_autofw *af; + af=ip_autofw_check_range(where, port,protocol); + if (af) + { + del_timer(&af->timer); + af->timer.expires=jiffies+IP_AUTOFW_EXPIRE; + add_timer(&af->timer); + }*/ +} + +#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ + +/* + * Returns hash value + */ + +static __inline__ unsigned +ip_masq_hash_key(unsigned proto, __u32 addr, __u16 port) +{ + return (proto^ntohl(addr)^ntohs(port)) & (IP_MASQ_TAB_SIZE-1); +} + +/* + * Hashes ip_masq by its proto,addrs,ports. + * should be called with masked interrupts. + * returns bool success. + */ + +static __inline__ int +ip_masq_hash(struct ip_masq *ms) +{ + unsigned hash; + + if (ms->flags & IP_MASQ_F_HASHED) { + printk("ip_masq_hash(): request for already hashed\n"); + return 0; + } + /* + * Hash by proto,m{addr,port} + */ + hash = ip_masq_hash_key(ms->protocol, ms->maddr, ms->mport); + ms->m_link = ip_masq_m_tab[hash]; + ip_masq_m_tab[hash] = ms; + + /* + * Hash by proto,s{addr,port} + */ +#ifdef CONFIG_IP_MASQUERADE_PPTP + if (ms->protocol == IPPROTO_GRE) { + /* Ignore the source port (Call ID) when hashing, as + * outbound packets will not be able to supply it... + */ + hash = ip_masq_hash_key(ms->protocol, ms->saddr, 0); + } else +#endif /* CONFIG_IP_MASQUERADE_PPTP */ + hash = ip_masq_hash_key(ms->protocol, ms->saddr, ms->sport); + ms->s_link = ip_masq_s_tab[hash]; + ip_masq_s_tab[hash] = ms; + +#ifdef CONFIG_IP_MASQUERADE_IPSEC + /* + * Hash by proto,d{addr,port} + */ + hash = ip_masq_hash_key(ms->protocol, ms->daddr, ms->dport); + ms->d_link = ip_masq_d_tab[hash]; + ip_masq_d_tab[hash] = ms; +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + + ms->flags |= IP_MASQ_F_HASHED; + return 1; +} + +/* + * UNhashes ip_masq from ip_masq_[ms]_tables. + * should be called with masked interrupts. + * returns bool success. + */ + +static __inline__ int ip_masq_unhash(struct ip_masq *ms) +{ + unsigned hash; + struct ip_masq ** ms_p; + if (!(ms->flags & IP_MASQ_F_HASHED)) { + printk("ip_masq_unhash(): request for unhash flagged\n"); + return 0; + } + /* + * UNhash by m{addr,port} + */ + hash = ip_masq_hash_key(ms->protocol, ms->maddr, ms->mport); + for (ms_p = &ip_masq_m_tab[hash]; *ms_p ; ms_p = &(*ms_p)->m_link) + if (ms == (*ms_p)) { + *ms_p = ms->m_link; + break; + } + /* + * UNhash by s{addr,port} + */ +#ifdef CONFIG_IP_MASQUERADE_PPTP + if (ms->protocol == IPPROTO_GRE) { + hash = ip_masq_hash_key(ms->protocol, ms->saddr, 0); + } else +#endif /* CONFIG_IP_MASQUERADE_PPTP */ + hash = ip_masq_hash_key(ms->protocol, ms->saddr, ms->sport); + for (ms_p = &ip_masq_s_tab[hash]; *ms_p ; ms_p = &(*ms_p)->s_link) + if (ms == (*ms_p)) { + *ms_p = ms->s_link; + break; + } + +#ifdef CONFIG_IP_MASQUERADE_IPSEC + /* + * UNhash by d{addr,port} + */ + hash = ip_masq_hash_key(ms->protocol, ms->daddr, ms->dport); + for (ms_p = &ip_masq_d_tab[hash]; *ms_p ; ms_p = &(*ms_p)->d_link) + if (ms == (*ms_p)) { + *ms_p = ms->d_link; + break; + } +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + + ms->flags &= ~IP_MASQ_F_HASHED; + return 1; +} + +/* + * Returns ip_masq associated with addresses found in iph. + * called for pkts coming from outside-to-INside the firewall + * + * NB. Cannot check destination address, just for the incoming port. + * reason: archie.doc.ac.uk has 6 interfaces, you send to + * phoenix and get a reply from any other interface(==dst)! + * + * [Only for UDP] - AC + */ + +struct ip_masq * +ip_masq_in_get(struct iphdr *iph) +{ + __u16 *portptr; + int protocol; + __u32 s_addr, d_addr; + __u16 s_port, d_port; +#ifdef CONFIG_IP_MASQUERADE_IPSEC + __u32 cookie; +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + + portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]); + protocol = iph->protocol; + s_addr = iph->saddr; + s_port = portptr[0]; + d_addr = iph->daddr; + d_port = portptr[1]; + +#ifdef CONFIG_IP_MASQUERADE_IPSEC + if (protocol == IPPROTO_UDP && ntohs(s_port) == UDP_PORT_ISAKMP && ntohs(d_port) == UDP_PORT_ISAKMP) { + cookie = *((__u32 *)&portptr[4]); + return ip_masq_in_get_isakmp(protocol, s_addr, s_port, d_addr, d_port, cookie); + } else +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + + return ip_masq_in_get_2(protocol, s_addr, s_port, d_addr, d_port); +} + +/* + * Returns ip_masq associated with supplied parameters, either + * broken out of the ip/tcp headers or directly supplied for those + * pathological protocols with address/port in the data stream + * (ftp, irc). addresses and ports are in network order. + * called for pkts coming from outside-to-INside the firewall. + * + * NB. Cannot check destination address, just for the incoming port. + * reason: archie.doc.ac.uk has 6 interfaces, you send to + * phoenix and get a reply from any other interface(==dst)! + * + * [Only for UDP] - AC + */ + +struct ip_masq * +ip_masq_in_get_2(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port) +{ + unsigned hash; + struct ip_masq *ms; + + hash = ip_masq_hash_key(protocol, d_addr, d_port); + for(ms = ip_masq_m_tab[hash]; ms ; ms = ms->m_link) { + if (protocol==ms->protocol && + ((s_addr==ms->daddr || ms->flags & IP_MASQ_F_NO_DADDR) +#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW + || (ms->dport==htons(1558)) +#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ + ) && + (s_port==ms->dport || ms->flags & IP_MASQ_F_NO_DPORT) && + (d_addr==ms->maddr && d_port==ms->mport)) { +#ifdef DEBUG_IP_MASQUERADE_VERBOSE + printk("MASQ: look/in %d %08X:%04hX->%08X:%04hX OK\n", + protocol, + s_addr, + s_port, + d_addr, + d_port); +#endif + return ms; + } + } + +#ifdef CONFIG_IP_MASQUERADE_PPTP + if (protocol == IPPROTO_GRE) { + for(ms = ip_masq_m_tab[hash]; ms ; ms = ms->m_link) { + if (protocol==ms->protocol && +#ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT + ms->mport == d_port && /* ignore source port */ +#else /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ + ms->mport == 0 && ms->sport == 0 && +#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ + s_addr==ms->daddr && d_addr==ms->maddr) { +#ifdef DEBUG_IP_MASQUERADE_PPTP_VERBOSE + printk(KERN_DEBUG "MASQ: look/in %d %08X:%04hX->%08X:%04hX OK\n", + protocol, + s_addr, + s_port, + d_addr, + d_port); +#endif /* DEBUG_IP_MASQUERADE_PPTP_VERBOSE */ + return ms; + } + } + } +#endif /* CONFIG_IP_MASQUERADE_PPTP */ + + +#ifdef DEBUG_IP_MASQUERADE_VERBOSE + printk("MASQ: look/in %d %08X:%04hX->%08X:%04hX fail\n", + protocol, + s_addr, + s_port, + d_addr, + d_port); +#endif + return NULL; +} + +#ifdef CONFIG_IP_MASQUERADE_IPSEC +struct ip_masq * +ip_masq_in_get_ipsec(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 i_spi) +{ + unsigned hash; + struct ip_masq *ms; + + if (protocol != IPPROTO_ESP) { + return ip_masq_in_get_2(protocol,s_addr,s_port,d_addr,d_port); + } + + /* find an entry for a packet coming in from outside, + * or find whether there's a setup pending + */ + + if (i_spi != 0) { + /* there's a SPI - look for a completed entry */ + hash = ip_masq_hash_key(protocol, s_addr, s_port); + for(ms = ip_masq_d_tab[hash]; ms ; ms = ms->d_link) { + if (protocol==ms->protocol && + s_addr==ms->daddr && + d_addr==ms->maddr && + ms->ispi != 0 && i_spi==ms->ispi) { +#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE + printk(KERN_DEBUG "MASQ: IPSEC look/in %08X->%08X:%08X OK\n", + s_addr, + d_addr, + i_spi); +#endif + return ms; + } + } + } + + /* no joy. look for a pending connection - maybe somebody else's + * if we're checking for a pending setup, the d_addr will be zero + * to avoid having to know the masq IP. + */ + hash = ip_masq_hash_key(protocol, s_addr, 0); + for(ms = ip_masq_d_tab[hash]; ms ; ms = ms->d_link) { + if (protocol==ms->protocol && + s_addr==ms->daddr && + (d_addr==0 || d_addr==ms->maddr) && + ms->ispi==0) { +#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE + printk(KERN_DEBUG "MASQ: IPSEC look/in %08X->%08X:0 OK\n", + s_addr, + d_addr + ); +#endif + return ms; + } + } + +#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE + printk(KERN_DEBUG "MASQ: IPSEC look/in %08X->%08X:%08X fail\n", + s_addr, + d_addr, + i_spi); +#endif + return NULL; +} + +struct ip_masq * +ip_masq_in_get_isakmp(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 cookie) +{ + unsigned hash; + struct ip_masq *ms; + +#ifdef DEBUG_IP_MASQUERADE_IPSEC + printk(KERN_DEBUG "ip_masq_in_get_isakmp(): "); + printk("%s -> ", in_ntoa(s_addr)); + printk("%s cookie %lX\n", in_ntoa(d_addr), ntohl(cookie)); +#endif /* DEBUG_IP_MASQUERADE_IPSEC */ + + if (cookie == 0) { + printk(KERN_INFO "ip_masq_in_get_isakmp(): "); + printk("zero cookie from %s\n", in_ntoa(s_addr)); + } + + hash = ip_masq_hash_key(protocol, d_addr, d_port); + for(ms = ip_masq_m_tab[hash]; ms ; ms = ms->m_link) { + if (protocol==ms->protocol && + cookie==ms->ospi && + ((s_addr==ms->daddr || ms->flags & IP_MASQ_F_NO_DADDR) + ) && + (s_port==ms->dport || ms->flags & IP_MASQ_F_NO_DPORT) && + (d_addr==ms->maddr && d_port==ms->mport)) { +#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE + printk(KERN_DEBUG "MASQ: look/in %d %08X:%04hX->%08X:%04hX %08X OK\n", + protocol, + s_addr, + s_port, + d_addr, + d_port, + cookie); +#endif + return ms; + } + } + +#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE + printk(KERN_DEBUG "MASQ: look/in %d %08X:%04hX->%08X:%04hX %08X fail\n", + protocol, + s_addr, + s_port, + d_addr, + d_port, + cookie); +#endif + return NULL; +} + +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + +/* + * Returns ip_masq associated with addresses found in iph. + * called for pkts coming from inside-to-OUTside the firewall. + */ + +struct ip_masq * +ip_masq_out_get(struct iphdr *iph) +{ + __u16 *portptr; + int protocol; + __u32 s_addr, d_addr; + __u16 s_port, d_port; +#ifdef CONFIG_IP_MASQUERADE_IPSEC + __u32 cookie; +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + + + portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]); + protocol = iph->protocol; + s_addr = iph->saddr; + s_port = portptr[0]; + d_addr = iph->daddr; + d_port = portptr[1]; + +#ifdef CONFIG_IP_MASQUERADE_IPSEC + if (protocol == IPPROTO_UDP && ntohs(s_port) == UDP_PORT_ISAKMP && ntohs(d_port) == UDP_PORT_ISAKMP) { + cookie = *((__u32 *)&portptr[4]); + return ip_masq_out_get_isakmp(protocol, s_addr, s_port, d_addr, d_port, cookie); + } else +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + + return ip_masq_out_get_2(protocol, s_addr, s_port, d_addr, d_port); +} + +/* + * Returns ip_masq associated with supplied parameters, either + * broken out of the ip/tcp headers or directly supplied for those + * pathological protocols with address/port in the data stream + * (ftp, irc). addresses and ports are in network order. + * called for pkts coming from inside-to-OUTside the firewall. + * + * Normally we know the source address and port but for some protocols + * (e.g. ftp PASV) we do not know the source port initially. Alas the + * hash is keyed on source port so if the first lookup fails then try again + * with a zero port, this time only looking at entries marked "no source + * port". + */ + +struct ip_masq * +ip_masq_out_get_2(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port) +{ + unsigned hash; + struct ip_masq *ms; + + +#ifdef CONFIG_IP_MASQUERADE_PPTP +#ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT + if (protocol == IPPROTO_GRE) { + /* + * Call ID is saved in source port number, + * but we have no way of knowing it on the outbound packet... + * we only know the *other side's* Call ID + */ + + hash = ip_masq_hash_key(protocol, s_addr, 0); + for(ms = ip_masq_s_tab[hash]; ms ; ms = ms->s_link) { + if (protocol == ms->protocol && + s_addr == ms->saddr && (s_port == 0 || s_port == ms->sport) && + d_addr == ms->daddr && d_port == ms->dport ) { +#ifdef DEBUG_IP_MASQUERADE_VERBOSE + printk(KERN_DEBUG "MASQ: lk/out2 %d %08X:%04hX->%08X:%04hX OK\n", + protocol, + s_addr, + s_port, + d_addr, + d_port); +#endif /* DEBUG_IP_MASQUERADE_VERBOSE */ + return ms; + } + } + } +#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ +#endif /* CONFIG_IP_MASQUERADE_PPTP */ + + hash = ip_masq_hash_key(protocol, s_addr, s_port); + for(ms = ip_masq_s_tab[hash]; ms ; ms = ms->s_link) { + if (protocol == ms->protocol && + s_addr == ms->saddr && s_port == ms->sport && + d_addr == ms->daddr && d_port == ms->dport ) { +#ifdef DEBUG_IP_MASQUERADE_VERBOSE + printk("MASQ: lk/out1 %d %08X:%04hX->%08X:%04hX OK\n", + protocol, + s_addr, + s_port, + d_addr, + d_port); +#endif + return ms; + } + } + hash = ip_masq_hash_key(protocol, s_addr, 0); + for(ms = ip_masq_s_tab[hash]; ms ; ms = ms->s_link) { + if (ms->flags & IP_MASQ_F_NO_SPORT && + protocol == ms->protocol && + s_addr == ms->saddr && + d_addr == ms->daddr && d_port == ms->dport ) { +#ifdef DEBUG_IP_MASQUERADE_VERBOSE + printk("MASQ: lk/out2 %d %08X:%04hX->%08X:%04hX OK\n", + protocol, + s_addr, + s_port, + d_addr, + d_port); +#endif + return ms; + } + } +#ifdef DEBUG_IP_MASQUERADE_VERBOSE + printk("MASQ: lk/out1 %d %08X:%04hX->%08X:%04hX fail\n", + protocol, + s_addr, + s_port, + d_addr, + d_port); +#endif + return NULL; +} + +#ifdef CONFIG_IP_MASQUERADE_IPSEC +struct ip_masq * +ip_masq_out_get_ipsec(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 o_spi) +{ + unsigned hash; + struct ip_masq *ms; + + if (protocol != IPPROTO_ESP) { + return ip_masq_out_get_2(protocol,s_addr,s_port,d_addr,d_port); + } + + hash = ip_masq_hash_key(protocol, s_addr, s_port); + for(ms = ip_masq_s_tab[hash]; ms ; ms = ms->s_link) { + if (protocol==ms->protocol && + s_addr==ms->saddr && + d_addr==ms->daddr && + o_spi==ms->ospi) { +#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE + printk(KERN_DEBUG "MASQ: IPSEC look/out %08X:%08X->%08X OK\n", + s_addr, + o_spi, + d_addr); +#endif + return ms; + } + } + +#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE + printk(KERN_DEBUG "MASQ: IPSEC look/out %08X:%08X->%08X fail\n", + s_addr, + o_spi, + d_addr); +#endif + return NULL; +} + +struct ip_masq * +ip_masq_out_get_isakmp(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port, __u32 cookie) +{ + unsigned hash; + struct ip_masq *ms; + +#ifdef DEBUG_IP_MASQUERADE_IPSEC + printk(KERN_DEBUG "ip_masq_out_get_isakmp(): "); + printk("%s -> ", in_ntoa(s_addr)); + printk("%s cookie %lX\n", in_ntoa(d_addr), ntohl(cookie)); +#endif /* DEBUG_IP_MASQUERADE_IPSEC */ + + if (cookie == 0) { + printk(KERN_INFO "ip_masq_out_get_isakmp(): "); + printk("zero cookie from %s\n", in_ntoa(s_addr)); + } + + hash = ip_masq_hash_key(protocol, s_addr, s_port); + for(ms = ip_masq_s_tab[hash]; ms ; ms = ms->s_link) { + if (protocol == ms->protocol && + cookie == ms->ospi && + s_addr == ms->saddr && s_port == ms->sport && + d_addr == ms->daddr && d_port == ms->dport ) { +#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE + printk(KERN_DEBUG "MASQ: lk/out1 %d %08X:%04hX->%08X:%04hX %08X OK\n", + protocol, + s_addr, + s_port, + d_addr, + d_port, + cookie); +#endif + return ms; + } + } + +#ifdef DEBUG_IP_MASQUERADE_IPSEC_VERBOSE + printk(KERN_DEBUG "MASQ: lk/out1 %d %08X:%04hX->%08X:%04hX %08X fail\n", + protocol, + s_addr, + s_port, + d_addr, + d_port, + cookie); +#endif + return NULL; +} + +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + +/* + * Returns ip_masq for given proto,m_addr,m_port. + * called by allocation routine to find an unused m_port. + */ + +struct ip_masq * +ip_masq_getbym(int protocol, __u32 m_addr, __u16 m_port) +{ + unsigned hash; + struct ip_masq *ms; + + hash = ip_masq_hash_key(protocol, m_addr, m_port); + for(ms = ip_masq_m_tab[hash]; ms ; ms = ms->m_link) { + if ( protocol==ms->protocol && + (m_addr==ms->maddr && m_port==ms->mport)) + return ms; + } + return NULL; +} + +static void masq_expire(unsigned long data) +{ + struct ip_masq *ms = (struct ip_masq *)data, *ms_data; + unsigned long flags; + + if (ms->flags & IP_MASQ_F_CONTROL) { + /* a control channel is about to expire */ + int idx = 0, reprieve = 0; +#ifdef DEBUG_CONFIG_IP_MASQUERADE + printk("Masquerade control %s %lX:%X about to expire\n", + masq_proto_name(ms->protocol), + ntohl(ms->saddr),ntohs(ms->sport)); +#endif + save_flags(flags); + cli(); + + /* + * If any other masquerade entry claims that the expiring entry + * is its control channel then keep the control entry alive. + * Useful for long running data channels with inactive control + * links which we don't want to lose, e.g. ftp. + * Assumption: loops such as a->b->a or a->a will never occur. + */ + for (idx = 0; idx < IP_MASQ_TAB_SIZE && !reprieve; idx++) { + for (ms_data = ip_masq_m_tab[idx]; ms_data ; ms_data = ms_data->m_link) { + if (ms_data->control == ms) { + reprieve = 1; /* this control connection can live a bit longer */ + ip_masq_set_expire(ms, ip_masq_expire->tcp_timeout); +#ifdef DEBUG_CONFIG_IP_MASQUERADE + printk("Masquerade control %s %lX:%X expiry reprieved\n", + masq_proto_name(ms->protocol), + ntohl(ms->saddr),ntohs(ms->sport)); +#endif + break; + } + } + } + restore_flags(flags); + if (reprieve) + return; + } + +#ifdef DEBUG_CONFIG_IP_MASQUERADE + printk("Masqueraded %s %lX:%X expired\n",masq_proto_name(ms->protocol),ntohl(ms->saddr),ntohs(ms->sport)); +#endif + + save_flags(flags); + cli(); + + if (ip_masq_unhash(ms)) { + ip_masq_free_ports[masq_proto_num(ms->protocol)]++; + if (ms->protocol != IPPROTO_ICMP) + ip_masq_unbind_app(ms); + kfree_s(ms,sizeof(*ms)); + } + + restore_flags(flags); +} + +#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW +void ip_autofw_expire(unsigned long data) +{ + struct ip_autofw * af; + af=(struct ip_autofw *) data; + af->flags&=0xFFFF ^ IP_AUTOFW_ACTIVE; + af->timer.expires=0; + af->lastcontact=0; + if (af->flags & IP_AUTOFW_SECURE) + af->where=0; +} +#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ + +/* + * Create a new masquerade list entry, also allocate an + * unused mport, keeping the portnumber between the + * given boundaries MASQ_BEGIN and MASQ_END. + */ + +struct ip_masq * ip_masq_new_enh(struct device *dev, int proto, __u32 saddr, __u16 sport, __u32 daddr, __u16 dport, unsigned mflags, __u16 matchport) +{ + struct ip_masq *ms, *mst; + int ports_tried, *free_ports_p; + unsigned long flags; + static int n_fails = 0; + + free_ports_p = &ip_masq_free_ports[masq_proto_num(proto)]; + + if (*free_ports_p == 0) { + if (++n_fails < 5) + printk("ip_masq_new(proto=%s): no free ports.\n", + masq_proto_name(proto)); + return NULL; + } + ms = (struct ip_masq *) kmalloc(sizeof(struct ip_masq), GFP_ATOMIC); + if (ms == NULL) { + if (++n_fails < 5) + printk("ip_masq_new(proto=%s): no memory available.\n", + masq_proto_name(proto)); + return NULL; + } + memset(ms, 0, sizeof(*ms)); + init_timer(&ms->timer); + ms->timer.data = (unsigned long)ms; + ms->timer.function = masq_expire; + ms->protocol = proto; + ms->saddr = saddr; + ms->sport = sport; + ms->daddr = daddr; + ms->dport = dport; + ms->flags = mflags; + ms->app_data = NULL; + ms->control = NULL; + + if (proto == IPPROTO_UDP && !matchport) + ms->flags |= IP_MASQ_F_NO_DADDR; + + /* get masq address from rif */ + ms->maddr = dev->pa_addr; + /* + * Setup new entry as not replied yet. + * This flag will allow masq. addr (ms->maddr) + * to follow forwarding interface address. + */ + ms->flags |= IP_MASQ_F_NO_REPLY; + + for (ports_tried = 0; + (*free_ports_p && (ports_tried <= (PORT_MASQ_END - PORT_MASQ_BEGIN))); + ports_tried++){ + +#ifdef CONFIG_IP_MASQUERADE_PPTP +#ifndef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT + /* Ignoring PPTP call IDs. + * Don't needlessly increase the TCP port pointer. + */ + if (proto == IPPROTO_GRE) { + ms->mport = 0; + mst = NULL; + } else { +#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ +#endif /* CONFIG_IP_MASQUERADE_PPTP */ + +#ifdef CONFIG_IP_MASQUERADE_IPSEC + /* ESP masq keys off the SPI, not the port number. + * Don't needlessly increase the TCP port pointer. + */ + if (proto == IPPROTO_ESP) { + ms->mport = 0; + mst = NULL; + } else { + if (proto == IPPROTO_UDP && ntohs(sport) == UDP_PORT_ISAKMP && ntohs(dport) == UDP_PORT_ISAKMP) { + /* the port number cannot be changed */ + ms->mport = htons(UDP_PORT_ISAKMP); + mst = NULL; + } else { +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + + save_flags(flags); + cli(); + + /* + * Try the next available port number + */ + if (!matchport || ports_tried) + ms->mport = htons(masq_port++); + else + ms->mport = matchport; + + if (masq_port==PORT_MASQ_END) masq_port = PORT_MASQ_BEGIN; + + restore_flags(flags); + + /* + * lookup to find out if this port is used. + */ + + mst = ip_masq_getbym(proto, ms->maddr, ms->mport); + +#ifdef CONFIG_IP_MASQUERADE_PPTP +#ifndef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT + } +#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ +#endif /* CONFIG_IP_MASQUERADE_PPTP */ + +#ifdef CONFIG_IP_MASQUERADE_IPSEC + } + } +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + + if (mst == NULL || matchport) { + save_flags(flags); + cli(); + + if (*free_ports_p == 0) { + restore_flags(flags); + break; + } + (*free_ports_p)--; + ip_masq_hash(ms); + + restore_flags(flags); + + if (proto != IPPROTO_ICMP) + ip_masq_bind_app(ms); + n_fails = 0; + return ms; + } + } + + if (++n_fails < 5) + printk("ip_masq_new(proto=%s): could not get free masq entry (free=%d).\n", + masq_proto_name(ms->protocol), *free_ports_p); + kfree_s(ms, sizeof(*ms)); + return NULL; +} + +struct ip_masq * ip_masq_new(struct device *dev, int proto, __u32 saddr, __u16 sport, __u32 daddr, __u16 dport, unsigned mflags) +{ + return (ip_masq_new_enh(dev, proto, saddr, sport, daddr, dport, mflags, 0) ); +} + +/* + * Set masq expiration (deletion) and adds timer, + * if timeout==0 cancel expiration. + * Warning: it does not check/delete previous timer! + */ + +void ip_masq_set_expire(struct ip_masq *ms, unsigned long tout) +{ + /* There Can Be Only One (timer on a masq table entry, that is) */ + del_timer(&ms->timer); + if (tout) { + ms->timer.expires = jiffies+tout; + add_timer(&ms->timer); + } +} + +static void recalc_check(struct udphdr *uh, __u32 saddr, + __u32 daddr, int len) +{ + uh->check=0; + uh->check=csum_tcpudp_magic(saddr,daddr,len, + IPPROTO_UDP, csum_partial((char *)uh,len,0)); + if(uh->check==0) + uh->check=0xFFFF; +} + + +#ifdef CONFIG_IP_MASQUERADE_PPTP +/* + * Masquerade of GRE connections + * to support a PPTP VPN client or server. + */ + +/* + * Handle outbound GRE packets. + * + * This is largely a copy of ip_fw_masquerade() + */ + +int ip_fw_masq_gre(struct sk_buff **skb_p, struct device *dev) +{ + struct sk_buff *skb = *skb_p; + struct iphdr *iph = skb->h.iph; + struct pptp_gre_header *greh; +#ifdef DEBUG_IP_MASQUERADE_PPTP +#ifdef DEBUG_IP_MASQUERADE_PPTP_VERBOSE + __u8 *greraw; +#endif /* DEBUG_IP_MASQUERADE_PPTP_VERBOSE */ +#endif /* DEBUG_IP_MASQUERADE_PPTP */ + struct ip_masq *ms; + unsigned long flags; + + + greh = (struct pptp_gre_header *)&(((char *)iph)[iph->ihl*4]); + +#ifdef DEBUG_IP_MASQUERADE_PPTP + + printk(KERN_DEBUG "ip_fw_masq_gre(): "); + printk("Outbound GRE packet from %s", in_ntoa(iph->saddr)); + printk(" to %s\n", in_ntoa(iph->daddr)); + +#ifdef DEBUG_IP_MASQUERADE_PPTP_VERBOSE + greraw = (__u8 *) greh; + printk(KERN_DEBUG "ip_fw_masq_gre(): "); + printk("GRE raw: %X %X %X %X %X %X %X %X %X %X %X %X.\n", + greraw[0], + greraw[1], + greraw[2], + greraw[3], + greraw[4], + greraw[5], + greraw[6], + greraw[7], + greraw[8], + greraw[9], + greraw[10], + greraw[11]); + printk(KERN_DEBUG "ip_fw_masq_gre(): "); + printk("GRE C: %d R: %d K: %d S: %d s: %d recur: %X.\n", + greh->has_cksum, + greh->has_routing, + greh->has_key, + greh->has_seq, + greh->is_strict, + greh->recur); + printk(KERN_DEBUG "ip_fw_masq_gre(): "); + printk("GRE flags: %X ver: %X.\n", greh->flags, greh->version); + printk(KERN_DEBUG "ip_fw_masq_gre(): "); + printk("GRE proto: %X.\n", ntohs(greh->protocol)); +#endif /* DEBUG_IP_MASQUERADE_PPTP_VERBOSE */ +#endif /* DEBUG_IP_MASQUERADE_PPTP */ + + if (ntohs(greh->protocol) != 0x880B) { +#ifdef DEBUG_IP_MASQUERADE_PPTP + printk(KERN_INFO "ip_fw_masq_gre(): "); + printk("GRE protocol %X not 0x880B (non-PPTP encap?) - discarding.\n", ntohs(greh->protocol)); +#endif /* DEBUG_IP_MASQUERADE_PPTP */ + return -1; + } + + /* + * Look for masq table entry + */ + + ms = ip_masq_out_get_2(IPPROTO_GRE, + iph->saddr, 0, + iph->daddr, 0); + + if (ms!=NULL) { + /* delete the expiration timer */ + ip_masq_set_expire(ms,0); + + /* + * Make sure that the masq IP address is correct + * for dynamic IP... + */ + if ( (ms->maddr != dev->pa_addr) && (sysctl_ip_dynaddr & 3) ) { + printk(KERN_INFO "ip_fw_masq_gre(): "); + printk("change maddr from %s", in_ntoa(ms->maddr)); + printk(" to %s\n", in_ntoa(dev->pa_addr)); + save_flags(flags); + cli(); + ip_masq_unhash(ms); + ms->maddr = dev->pa_addr; + ip_masq_hash(ms); + restore_flags(flags); + } + } else { + /* + * Nope, not found, create a new entry for it, maybe + */ + +#ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT + /* masq table entry has to come from control channel sniffing. + * If we can't find one, it may have expired. + * How can this happen with the control channel active? + */ + printk(KERN_INFO "ip_fw_masq_gre(): "); + printk("Outbound GRE to %s has no masq table entry.\n", + in_ntoa(iph->daddr)); + return -1; +#else /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ + /* call IDs ignored, can create masq table entries on the fly. */ + ms = ip_masq_new(dev, iph->protocol, + iph->saddr, 0, + iph->daddr, 0, + 0); + + if (ms == NULL) { + printk(KERN_NOTICE "ip_fw_masq_gre(): Couldn't create masq table entry.\n"); + return -1; + } +#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ + } + + /* + * Set iph source addr from ip_masq obj. + */ + iph->saddr = ms->maddr; + + /* + * set timeout and check IP header + */ + + ip_masq_set_expire(ms, MASQUERADE_EXPIRE_PPTP); + ip_send_check(iph); + +#ifdef DEBUG_IP_MASQUERADE_PPTP + printk(KERN_DEBUG "MASQ: GRE O-routed from %s over %s\n", + in_ntoa(ms->maddr), dev->name); +#endif /* DEBUG_IP_MASQUERADE_PPTP */ + + return 0; +} + +/* + * Handle inbound GRE packets. + * + */ + +int ip_fw_demasq_gre(struct sk_buff **skb_p, struct device *dev) +{ + struct sk_buff *skb = *skb_p; + struct iphdr *iph = skb->h.iph; + struct pptp_gre_header *greh; +#ifdef DEBUG_IP_MASQUERADE_PPTP +#ifdef DEBUG_IP_MASQUERADE_PPTP_VERBOSE + __u8 *greraw; +#endif /* DEBUG_IP_MASQUERADE_PPTP_VERBOSE */ +#endif /* DEBUG_IP_MASQUERADE_PPTP */ + struct ip_masq *ms; + + + greh = (struct pptp_gre_header *)&(((char *)iph)[iph->ihl*4]); + +#ifdef DEBUG_IP_MASQUERADE_PPTP + + 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 + 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 +}; + +#endif /* CONFIG_IP_MASQUERADE_PPTP */ + + +#ifdef CONFIG_IP_MASQUERADE_IPSEC +/* + * Quick-and-dirty handling of ESP connections + * John Hardin 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 + 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 +}; + +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + + +int ip_fw_masquerade(struct sk_buff **skb_ptr, struct device *dev) +{ + struct sk_buff *skb=*skb_ptr; + struct iphdr *iph = skb->h.iph; + __u16 *portptr; + struct ip_masq *ms; + int size; + unsigned long timeout; + + /* + * We can only masquerade protocols with ports... + * [TODO] + * We may need to consider masq-ing some ICMP related to masq-ed protocols + */ + + if (iph->protocol==IPPROTO_ICMP) + return (ip_fw_masq_icmp(skb_ptr,dev)); +#ifdef CONFIG_IP_MASQUERADE_PPTP + if (iph->protocol==IPPROTO_GRE) + return (ip_fw_masq_gre(skb_ptr,dev)); +#endif /* CONFIG_IP_MASQUERADE_PPTP */ +#ifdef CONFIG_IP_MASQUERADE_IPSEC + if (iph->protocol==IPPROTO_ESP) + return (ip_fw_masq_esp(skb_ptr,dev)); +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + if (iph->protocol!=IPPROTO_UDP && iph->protocol!=IPPROTO_TCP) + return -1; + + /* + * Now hunt the list to see if we have an old entry + */ + + portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]); +#ifdef DEBUG_CONFIG_IP_MASQUERADE + printk("Outgoing %s %lX:%X -> %lX:%X\n", + masq_proto_name(iph->protocol), + ntohl(iph->saddr), ntohs(portptr[0]), + ntohl(iph->daddr), ntohs(portptr[1])); +#endif + + ms = ip_masq_out_get(iph); + + if (ms!=NULL) { + ip_masq_set_expire(ms,0); + + /* + * If sysctl & 3 and either + * no pkt has been received yet + * or + * sysctl & 4 + * in this tunnel ... + * "You are welcome, diald, ipppd, pppd-3.3...". + */ + if ( (sysctl_ip_dynaddr & 3) && (ms->flags & IP_MASQ_F_NO_REPLY || sysctl_ip_dynaddr & 4) && dev->pa_addr != ms->maddr) { + unsigned long flags; + if (sysctl_ip_dynaddr & 2) { + printk(KERN_INFO "ip_fw_masquerade(): change maddr from %s", + in_ntoa(ms->maddr)); + printk(" to %s\n", in_ntoa(dev->pa_addr)); + } + save_flags(flags); + cli(); + ip_masq_unhash(ms); + ms->maddr = dev->pa_addr; + ip_masq_hash(ms); + restore_flags(flags); + } + + /* + * Set sport if not defined yet (e.g. ftp PASV). Because + * masq entries are hashed on sport, unhash with old value + * and hash with new. + */ + + if ( ms->flags & IP_MASQ_F_NO_SPORT && ms->protocol == IPPROTO_TCP ) { + unsigned long flags; + ms->flags &= ~IP_MASQ_F_NO_SPORT; + save_flags(flags); + cli(); + ip_masq_unhash(ms); + ms->sport = portptr[0]; + ip_masq_hash(ms); /* hash on new sport */ + restore_flags(flags); +#ifdef DEBUG_CONFIG_IP_MASQUERADE + printk("ip_fw_masquerade(): filled sport=%d\n", + ntohs(ms->sport)); +#endif + } + } + +#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW + /* update any ipautofw entries .. */ + ip_autofw_update_out(iph->saddr, iph->daddr, portptr[1], + iph->protocol); +#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ + + /* + * Nope, not found, create a new entry for it + */ + if (ms==NULL) + { +#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW + /* if the source port is supposed to match the masq port, then + make it so */ + if (ip_autofw_check_direct(portptr[1],iph->protocol)) + ms = ip_masq_new_enh(dev, iph->protocol, + iph->saddr, portptr[0], + iph->daddr, portptr[1], + 0, + portptr[0]); + else +#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ + ms = ip_masq_new_enh(dev, iph->protocol, + iph->saddr, portptr[0], + iph->daddr, portptr[1], + 0, + 0); + if (ms == NULL) + return -1; + +#ifdef CONFIG_IP_MASQUERADE_IPSEC + if (iph->protocol == IPPROTO_UDP && ntohs(portptr[0]) == UDP_PORT_ISAKMP && ntohs(portptr[1]) == UDP_PORT_ISAKMP) { + /* save the initiator cookie */ + ms->ospi = *((__u32 *)&portptr[4]); + } +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + } + +#ifdef CONFIG_IP_MASQUERADE_PPTP +#ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT + if (iph->protocol == IPPROTO_TCP && ntohs(portptr[1]) == PPTP_CONTROL_PORT) + { + /* + * Packet sent to PPTP control port. Process it. + * May change call ID word in request, but + * packet length will not change. + */ + ip_masq_pptp(skb, ms, dev); + } +#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ +#endif /* CONFIG_IP_MASQUERADE_PPTP */ + + /* + * Change the fragments origin + */ + + size = skb->len - ((unsigned char *)portptr - skb->h.raw); + /* + * Set iph addr and port from ip_masq obj. + */ + iph->saddr = ms->maddr; + portptr[0] = ms->mport; + + /* + * Attempt ip_masq_app call. + * will fix ip_masq and iph seq stuff + */ + if (ip_masq_app_pkt_out(ms, skb_ptr, dev) != 0) + { + /* + * skb has possibly changed, update pointers. + */ + skb = *skb_ptr; + iph = skb->h.iph; + portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]); + size = skb->len - ((unsigned char *)portptr-skb->h.raw); + } + + /* + * Adjust packet accordingly to protocol + */ + + if (masq_proto_num(iph->protocol)==0) + { +#ifdef CONFIG_IP_MASQUERADE_IPSEC + if (iph->protocol == IPPROTO_UDP && ntohs(portptr[0]) == UDP_PORT_ISAKMP && ntohs(portptr[1]) == UDP_PORT_ISAKMP) { + /* ISAKMP timeout should be same as ESP timeout to allow for rekeying */ + timeout = MASQUERADE_EXPIRE_IPSEC; + } else +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + + timeout = ip_masq_expire->udp_timeout; + recalc_check((struct udphdr *)portptr,iph->saddr,iph->daddr,size); + } + else + { + struct tcphdr *th; + th = (struct tcphdr *)portptr; + + /* Set the flags up correctly... */ + if (th->fin) + { + ms->flags |= IP_MASQ_F_SAW_FIN_OUT; + } + + if (th->rst) + { + ms->flags |= IP_MASQ_F_SAW_RST; + } + + /* + * Timeout depends if FIN packet has been seen + * Very short timeout if RST packet seen. + */ + if (ms->flags & IP_MASQ_F_SAW_RST) + { + timeout = 1; + } + else if ((ms->flags & IP_MASQ_F_SAW_FIN) == IP_MASQ_F_SAW_FIN) + { + timeout = ip_masq_expire->tcp_fin_timeout; + } + else timeout = ip_masq_expire->tcp_timeout; + + skb->csum = csum_partial((void *)(th + 1), size - sizeof(*th), 0); + tcp_send_check(th,iph->saddr,iph->daddr,size,skb); + } + ip_masq_set_expire(ms, timeout); + ip_send_check(iph); + + #ifdef DEBUG_CONFIG_IP_MASQUERADE + printk("O-routed from %lX:%X over %s\n",ntohl(ms->maddr),ntohs(ms->mport),dev->name); + #endif + + return 0; + } + + +/* + * Handle ICMP messages in forward direction. + * Find any that might be relevant, check against existing connections, + * forward to masqueraded host if relevant. + * Currently handles error types - unreachable, quench, ttl exceeded + */ + +int ip_fw_masq_icmp(struct sk_buff **skb_p, struct device *dev) +{ + struct sk_buff *skb = *skb_p; + struct iphdr *iph = skb->h.iph; + struct icmphdr *icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2)); + struct iphdr *ciph; /* The ip header contained within the ICMP */ + __u16 *pptr; /* port numbers from TCP/UDP contained header */ + struct ip_masq *ms; + unsigned short len = ntohs(iph->tot_len) - (iph->ihl * 4); + +#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP + printk("Incoming forward ICMP (%d,%d) %lX -> %lX\n", + icmph->type, ntohs(icmp_id(icmph)), + ntohl(iph->saddr), ntohl(iph->daddr)); +#endif + +#ifdef CONFIG_IP_MASQUERADE_ICMP + if ((icmph->type == ICMP_ECHO ) || + (icmph->type == ICMP_TIMESTAMP ) || + (icmph->type == ICMP_INFO_REQUEST ) || + (icmph->type == ICMP_ADDRESS )) { +#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP + printk("MASQ: icmp request rcv %lX->%lX id %d type %d\n", + ntohl(iph->saddr), + ntohl(iph->daddr), + ntohs(icmp_id(icmph)), + icmph->type); +#endif + ms = ip_masq_out_get_2(iph->protocol, + iph->saddr, + icmp_id(icmph), + iph->daddr, + icmp_hv_req(icmph)); + if (ms == NULL) { + ms = ip_masq_new(dev, + iph->protocol, + iph->saddr, + icmp_id(icmph), + iph->daddr, + icmp_hv_req(icmph), + 0); + if (ms == NULL) + return (-1); +#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP + printk("MASQ: Create new icmp entry\n"); +#endif + } + ip_masq_set_expire(ms, 0); + /* Rewrite source address */ + + /* + * If sysctl & 3 and either + * no pkt has been received yet + * or + * sysctl & 4 + * in this tunnel ... + * "You are welcome, diald, ipppd, pppd-3.3...". + */ + if ( (sysctl_ip_dynaddr & 3) && (ms->flags & IP_MASQ_F_NO_REPLY || sysctl_ip_dynaddr & 4) && dev->pa_addr != ms->maddr) { + unsigned long flags; +#ifdef DEBUG_CONFIG_IP_MASQUERADE + printk(KERN_INFO "ip_fw_masq_icmp(): change masq.addr %s", + in_ntoa(ms->maddr)); + printk("-> %s\n", in_ntoa(dev->pa_addr)); +#endif + save_flags(flags); + cli(); + ip_masq_unhash(ms); + ms->maddr = dev->pa_addr; + ip_masq_hash(ms); + restore_flags(flags); + } + + iph->saddr = ms->maddr; + ip_send_check(iph); + /* Rewrite port (id) */ + (icmph->un).echo.id = ms->mport; + icmph->checksum = 0; + icmph->checksum = ip_compute_csum((unsigned char *)icmph, len); + ip_masq_set_expire(ms, MASQUERADE_EXPIRE_ICMP); +#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP + printk("MASQ: icmp request rwt %lX->%lX id %d type %d\n", + ntohl(iph->saddr), + ntohl(iph->daddr), + ntohs(icmp_id(icmph)), + icmph->type); +#endif + return (1); + } +#endif + + /* + * Work through seeing if this is for us. + * These checks are supposed to be in an order that + * means easy things are checked first to speed up + * processing.... however this means that some + * packets will manage to get a long way down this + * stack and then be rejected, but thats life + */ + if ((icmph->type != ICMP_DEST_UNREACH) && + (icmph->type != ICMP_SOURCE_QUENCH) && + (icmph->type != ICMP_TIME_EXCEEDED)) + return 0; + + /* Now find the contained IP header */ + ciph = (struct iphdr *) (icmph + 1); + +#ifdef CONFIG_IP_MASQUERADE_ICMP + if (ciph->protocol == IPPROTO_ICMP) { + /* + * This section handles ICMP errors for ICMP packets + */ + struct icmphdr *cicmph = (struct icmphdr *)((char *)ciph + + (ciph->ihl<<2)); + +#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP + printk("MASQ: fw icmp/icmp rcv %lX->%lX id %d type %d\n", + ntohl(ciph->saddr), + ntohl(ciph->daddr), + ntohs(icmp_id(cicmph)), + cicmph->type); +#endif + ms = ip_masq_out_get_2(ciph->protocol, + ciph->daddr, + icmp_id(cicmph), + ciph->saddr, + icmp_hv_rep(cicmph)); + + if (ms == NULL) + return 0; + + /* Now we do real damage to this packet...! */ + /* First change the source IP address, and recalc checksum */ + iph->saddr = ms->maddr; + ip_send_check(iph); + + /* Now change the *dest* address in the contained IP */ + ciph->daddr = ms->maddr; + ip_send_check(ciph); + + /* Change the ID to the masqed one! */ + (cicmph->un).echo.id = ms->mport; + + /* And finally the ICMP checksum */ + icmph->checksum = 0; + icmph->checksum = ip_compute_csum((unsigned char *) icmph, len); + +#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP + printk("MASQ: fw icmp/icmp rwt %lX->%lX id %d type %d\n", + ntohl(ciph->saddr), + ntohl(ciph->daddr), + ntohs(icmp_id(cicmph)), + cicmph->type); +#endif + return 1; + } +#endif /* CONFIG_IP_MASQUERADE_ICMP */ + + /* We are only interested ICMPs generated from TCP or UDP packets */ + if ((ciph->protocol != IPPROTO_UDP) && (ciph->protocol != IPPROTO_TCP)) + return 0; + + /* + * Find the ports involved - this packet was + * incoming so the ports are right way round + * (but reversed relative to outer IP header!) + */ + pptr = (__u16 *)&(((char *)ciph)[ciph->ihl*4]); + + /* Ensure the checksum is correct */ + if (ip_compute_csum((unsigned char *) icmph, len)) + { + /* Failed checksum! */ + printk(KERN_DEBUG "MASQ: forward ICMP: failed checksum from %s!\n", + in_ntoa(iph->saddr)); + return(-1); + } + +#ifdef DEBUG_CONFIG_IP_MASQUERADE + printk("Handling forward ICMP for %lX:%X -> %lX:%X\n", + ntohl(ciph->saddr), ntohs(pptr[0]), + ntohl(ciph->daddr), ntohs(pptr[1])); +#endif + + /* This is pretty much what ip_masq_out_get() does */ + ms = ip_masq_out_get_2(ciph->protocol, + ciph->daddr, + pptr[1], + ciph->saddr, + pptr[0]); + + if (ms == NULL) + return 0; + + /* Now we do real damage to this packet...! */ + /* First change the source IP address, and recalc checksum */ + iph->saddr = ms->maddr; + ip_send_check(iph); + + /* Now change the *dest* address in the contained IP */ + ciph->daddr = ms->maddr; + ip_send_check(ciph); + + /* the TCP/UDP dest port - cannot redo check */ + pptr[1] = ms->mport; + + /* And finally the ICMP checksum */ + icmph->checksum = 0; + icmph->checksum = ip_compute_csum((unsigned char *) icmph, len); + +#ifdef DEBUG_CONFIG_IP_MASQUERADE + printk("Rewrote forward ICMP to %lX:%X -> %lX:%X\n", + ntohl(ciph->saddr), ntohs(pptr[0]), + ntohl(ciph->daddr), ntohs(pptr[1])); +#endif + + return 1; +} + +/* + * Handle ICMP messages in reverse (demasquerade) direction. + * Find any that might be relevant, check against existing connections, + * forward to masqueraded host if relevant. + * Currently handles error types - unreachable, quench, ttl exceeded + */ + +int ip_fw_demasq_icmp(struct sk_buff **skb_p, struct device *dev) +{ + struct sk_buff *skb = *skb_p; + struct iphdr *iph = skb->h.iph; + struct icmphdr *icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2)); + struct iphdr *ciph; /* The ip header contained within the ICMP */ + __u16 *pptr; /* port numbers from TCP/UDP contained header */ + struct ip_masq *ms; + unsigned short len = ntohs(iph->tot_len) - (iph->ihl * 4); + +#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP + printk("MASQ: icmp in/rev (%d,%d) %lX -> %lX\n", + icmph->type, ntohs(icmp_id(icmph)), + ntohl(iph->saddr), ntohl(iph->daddr)); +#endif + +#ifdef CONFIG_IP_MASQUERADE_ICMP + if ((icmph->type == ICMP_ECHOREPLY) || + (icmph->type == ICMP_TIMESTAMPREPLY) || + (icmph->type == ICMP_INFO_REPLY) || + (icmph->type == ICMP_ADDRESSREPLY)) { +#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP + printk("MASQ: icmp reply rcv %lX->%lX id %d type %d, req %d\n", + ntohl(iph->saddr), + ntohl(iph->daddr), + ntohs(icmp_id(icmph)), + icmph->type, + icmp_type_request(icmph->type)); +#endif + ms = ip_masq_in_get_2(iph->protocol, + iph->saddr, + icmp_hv_rep(icmph), + iph->daddr, + icmp_id(icmph)); + if (ms == NULL) + return 0; + + ip_masq_set_expire(ms,0); + + /* + * got reply, so clear flag + */ + ms->flags &= ~IP_MASQ_F_NO_REPLY; + + /* Reset source address */ + iph->daddr = ms->saddr; + /* Redo IP header checksum */ + ip_send_check(iph); + /* Set ID to fake port number */ + (icmph->un).echo.id = ms->sport; + /* Reset ICMP checksum and set expiry */ + icmph->checksum=0; + icmph->checksum=ip_compute_csum((unsigned char *)icmph,len); + ip_masq_set_expire(ms, MASQUERADE_EXPIRE_ICMP); +#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP + printk("MASQ: icmp reply rwt %lX->%lX id %d type %d\n", + ntohl(iph->saddr), + ntohl(iph->daddr), + ntohs(icmp_id(icmph)), + icmph->type); +#endif + return 1; + } else { +#endif + if ((icmph->type != ICMP_DEST_UNREACH) && + (icmph->type != ICMP_SOURCE_QUENCH) && + (icmph->type != ICMP_TIME_EXCEEDED)) + return 0; +#ifdef CONFIG_IP_MASQUERADE_ICMP + } +#endif + /* + * If we get here we have an ICMP error of one of the above 3 types + * Now find the contained IP header + */ + ciph = (struct iphdr *) (icmph + 1); + +#ifdef CONFIG_IP_MASQUERADE_ICMP + if (ciph->protocol == IPPROTO_ICMP) { + /* + * This section handles ICMP errors for ICMP packets + * + * First get a new ICMP header structure out of the IP packet + */ + struct icmphdr *cicmph = (struct icmphdr *)((char *)ciph + + (ciph->ihl<<2)); + +#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP + printk("MASQ: rv icmp/icmp rcv %lX->%lX id %d type %d\n", + ntohl(ciph->saddr), + ntohl(ciph->daddr), + ntohs(icmp_id(cicmph)), + cicmph->type); +#endif + ms = ip_masq_in_get_2(ciph->protocol, + ciph->daddr, + icmp_hv_req(cicmph), + ciph->saddr, + icmp_id(cicmph)); + + if (ms == NULL) + return 0; + + /* Now we do real damage to this packet...! */ + /* First change the dest IP address, and recalc checksum */ + iph->daddr = ms->saddr; + ip_send_check(iph); + + /* Now change the *source* address in the contained IP */ + ciph->saddr = ms->saddr; + ip_send_check(ciph); + + /* Change the ID to the original one! */ + (cicmph->un).echo.id = ms->sport; + + /* And finally the ICMP checksum */ + icmph->checksum = 0; + icmph->checksum = ip_compute_csum((unsigned char *) icmph, len); + +#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP + printk("MASQ: rv icmp/icmp rwt %lX->%lX id %d type %d\n", + ntohl(ciph->saddr), + ntohl(ciph->daddr), + ntohs(icmp_id(cicmph)), + cicmph->type); +#endif + return 1; + } +#endif /* CONFIG_IP_MASQUERADE_ICMP */ + + /* We are only interested ICMPs generated from TCP or UDP packets */ + if ((ciph->protocol != IPPROTO_UDP) && + (ciph->protocol != IPPROTO_TCP)) + return 0; + + /* + * Find the ports involved - remember this packet was + * *outgoing* so the ports are reversed (and addresses) + */ + pptr = (__u16 *)&(((char *)ciph)[ciph->ihl*4]); + if (ntohs(pptr[0]) < PORT_MASQ_BEGIN || + ntohs(pptr[0]) > PORT_MASQ_END) + return 0; + + /* Ensure the checksum is correct */ + if (ip_compute_csum((unsigned char *) icmph, len)) + { + /* Failed checksum! */ + printk(KERN_DEBUG "MASQ: reverse ICMP: failed checksum from %s!\n", + in_ntoa(iph->saddr)); + return(-1); + } + +#ifdef DEBUG_CONFIG_IP_MASQUERADE + printk("Handling reverse ICMP for %lX:%X -> %lX:%X\n", + ntohl(ciph->saddr), ntohs(pptr[0]), + ntohl(ciph->daddr), ntohs(pptr[1])); +#endif + + /* This is pretty much what ip_masq_in_get() does, except params are wrong way round */ + ms = ip_masq_in_get_2(ciph->protocol, + ciph->daddr, + pptr[1], + ciph->saddr, + pptr[0]); + + if (ms == NULL) + return 0; + + /* Now we do real damage to this packet...! */ + /* First change the dest IP address, and recalc checksum */ + iph->daddr = ms->saddr; + ip_send_check(iph); + + /* Now change the *source* address in the contained IP */ + ciph->saddr = ms->saddr; + ip_send_check(ciph); + + /* the TCP/UDP source port - cannot redo check */ + pptr[0] = ms->sport; + + /* And finally the ICMP checksum */ + icmph->checksum = 0; + icmph->checksum = ip_compute_csum((unsigned char *) icmph, len); + +#ifdef DEBUG_CONFIG_IP_MASQUERADE + printk("Rewrote reverse ICMP to %lX:%X -> %lX:%X\n", + ntohl(ciph->saddr), ntohs(pptr[0]), + ntohl(ciph->daddr), ntohs(pptr[1])); +#endif + + return 1; +} + + + /* + * Check if it's an masqueraded port, look it up, + * and send it on its way... + * + * Better not have many hosts using the designated portrange + * as 'normal' ports, or you'll be spending many time in + * this function. + */ + +int ip_fw_demasquerade(struct sk_buff **skb_p, struct device *dev) +{ + struct sk_buff *skb = *skb_p; + struct iphdr *iph = skb->h.iph; + __u16 *portptr; + struct ip_masq *ms; + unsigned short len; + unsigned long timeout = MASQUERADE_EXPIRE_TCP; +#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW + struct ip_autofw *af; +#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ + + + switch (iph->protocol) { + case IPPROTO_ICMP: + return(ip_fw_demasq_icmp(skb_p, dev)); +#ifdef CONFIG_IP_MASQUERADE_PPTP + case IPPROTO_GRE: + return(ip_fw_demasq_gre(skb_p, dev)); +#endif /* CONFIG_IP_MASQUERADE_PPTP */ +#ifdef CONFIG_IP_MASQUERADE_IPSEC + case IPPROTO_ESP: + return(ip_fw_demasq_esp(skb_p, dev)); +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + case IPPROTO_TCP: + case IPPROTO_UDP: + /* Make sure packet is in the masq range */ + portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]); + if ((ntohs(portptr[1]) < PORT_MASQ_BEGIN || + ntohs(portptr[1]) > PORT_MASQ_END) +#ifdef CONFIG_IP_MASQUERADE_IPSEC + && ((iph->protocol != IPPROTO_UDP) || (ntohs(portptr[0]) != UDP_PORT_ISAKMP) || (ntohs(portptr[1]) != UDP_PORT_ISAKMP)) +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ +#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW + && !ip_autofw_check_range(iph->saddr, portptr[1], + iph->protocol, 0) + && !ip_autofw_check_direct(portptr[1], iph->protocol) + && !ip_autofw_check_port(portptr[1], iph->protocol) +#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ + ) + return 0; + + /* Check that the checksum is OK */ + len = ntohs(iph->tot_len) - (iph->ihl * 4); + if ((iph->protocol == IPPROTO_UDP) && (portptr[3] == 0)) + /* No UDP checksum */ + break; + + switch (skb->ip_summed) + { + case CHECKSUM_NONE: + skb->csum = csum_partial((char *)portptr, len, 0); + case CHECKSUM_HW: + if (csum_tcpudp_magic(iph->saddr, iph->daddr, len, + iph->protocol, skb->csum)) + { + printk(KERN_DEBUG "MASQ: failed TCP/UDP checksum from %s!\n", + in_ntoa(iph->saddr)); + return -1; + } + default: + /* CHECKSUM_UNNECESSARY */ + } + break; + default: + return 0; + } + + +#ifdef DEBUG_CONFIG_IP_MASQUERADE + printk("Incoming %s %lX:%X -> %lX:%X\n", + masq_proto_name(iph->protocol), + ntohl(iph->saddr), ntohs(portptr[0]), + ntohl(iph->daddr), ntohs(portptr[1])); +#endif + /* + * reroute to original host:port if found... + */ + + ms = ip_masq_in_get(iph); + +#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW + + if (ms == NULL && (af=ip_autofw_check_range(iph->saddr, portptr[1], iph->protocol, 0))) + { +#ifdef DEBUG_CONFIG_IP_MASQUERADE + printk("ip_autofw_check_range\n"); +#endif + ms = ip_masq_new_enh(dev, iph->protocol, + af->where, portptr[1], + iph->saddr, portptr[0], + 0, + portptr[1]); + } + if ( ms == NULL && (af=ip_autofw_check_port(portptr[1], iph->protocol)) ) + { +#ifdef DEBUG_CONFIG_IP_MASQUERADE + printk("ip_autofw_check_port\n"); +#endif + ms = ip_masq_new_enh(dev, iph->protocol, + af->where, htons(af->hidden), + iph->saddr, portptr[0], + IP_MASQ_F_AFW_PORT, + htons(af->visible)); + } +#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ + + if (ms != NULL) + { +#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW + ip_autofw_update_in(iph->saddr, portptr[1], iph->protocol); +#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */ + + /* Stop the timer ticking.... */ + ip_masq_set_expire(ms,0); + + /* + * got reply, so clear flag + */ + ms->flags &= ~IP_MASQ_F_NO_REPLY; + + /* + * Set dport if not defined yet. + */ + + if ( ms->flags & IP_MASQ_F_NO_DPORT && ms->protocol == IPPROTO_TCP ) { + ms->flags &= ~IP_MASQ_F_NO_DPORT; + ms->dport = portptr[0]; +#ifdef DEBUG_CONFIG_IP_MASQUERADE + printk("ip_fw_demasquerade(): filled dport=%d\n", + ntohs(ms->dport)); +#endif + } + if (ms->flags & IP_MASQ_F_NO_DADDR && ms->protocol == IPPROTO_TCP) { + ms->flags &= ~IP_MASQ_F_NO_DADDR; + ms->daddr = iph->saddr; +#ifdef DEBUG_CONFIG_IP_MASQUERADE + printk("ip_fw_demasquerade(): filled daddr=%X\n", + ntohs(ms->daddr)); +#endif + } + iph->daddr = ms->saddr; + portptr[1] = ms->sport; + + /* + * Attempt ip_masq_app call. + * will fix ip_masq and iph ack_seq stuff + */ + + if (ip_masq_app_pkt_in(ms, skb_p, dev) != 0) + { + /* + * skb has changed, update pointers. + */ + + skb = *skb_p; + iph = skb->h.iph; + portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]); + len = ntohs(iph->tot_len) - (iph->ihl * 4); + } + +#ifdef CONFIG_IP_MASQUERADE_PPTP +#ifdef CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT + if (iph->protocol == IPPROTO_TCP && ntohs(portptr[0]) == PPTP_CONTROL_PORT) + { + /* + * Packet received from PPTP control port. Process it. + * May change call ID word in request, but + * packet length will not change. + */ + ip_masq_pptp(skb, ms, dev); + } +#endif /* CONFIG_IP_MASQUERADE_PPTP_MULTICLIENT */ +#endif /* CONFIG_IP_MASQUERADE_PPTP */ + + /* + * Yug! adjust UDP/TCP and IP checksums, also update + * timeouts. + * If a TCP RST is seen collapse the tunnel (by using short timeout)! + */ + if (masq_proto_num(iph->protocol)==0) + { + recalc_check((struct udphdr *)portptr,iph->saddr,iph->daddr,len); + +#ifdef CONFIG_IP_MASQUERADE_IPSEC + if (iph->protocol == IPPROTO_UDP && ntohs(portptr[0]) == UDP_PORT_ISAKMP && ntohs(portptr[1]) == UDP_PORT_ISAKMP) { + /* ISAKMP timeout should be same as ESP timeout to allow for rekeying */ + timeout = MASQUERADE_EXPIRE_IPSEC; + } else +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ + + timeout = ip_masq_expire->udp_timeout; + } + else + { + struct tcphdr *th; + if(len>=sizeof(struct tcphdr)) + { + skb->csum = csum_partial((void *)(((struct tcphdr *)portptr) + 1), + len - sizeof(struct tcphdr), 0); + tcp_send_check((struct tcphdr *)portptr,iph->saddr,iph->daddr,len,skb); + + /* Check if TCP FIN or RST */ + th = (struct tcphdr *)portptr; + if (th->fin) + { + ms->flags |= IP_MASQ_F_SAW_FIN_IN; + } + if (th->rst) + { + ms->flags |= IP_MASQ_F_SAW_RST; + } + + /* Now set the timeouts */ + if (ms->flags & IP_MASQ_F_SAW_RST) + { + timeout = 1; + } + else if ((ms->flags & IP_MASQ_F_SAW_FIN) == IP_MASQ_F_SAW_FIN) + { + timeout = ip_masq_expire->tcp_fin_timeout; + } + else timeout = ip_masq_expire->tcp_timeout; + } + } + ip_masq_set_expire(ms, timeout); + ip_send_check(iph); +#ifdef DEBUG_CONFIG_IP_MASQUERADE + printk("I-routed to %lX:%X\n",ntohl(iph->daddr),ntohs(portptr[1])); +#endif + return 1; + } + + /* sorry, all this trouble for a no-hit :) */ + return 0; +} + +/* + * /proc/net entries + */ + + +#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW + +static int ip_autofw_procinfo(char *buffer, char **start, off_t offset, + int length, int unused) +{ + off_t pos=0, begin=0; + struct ip_autofw * af; + int len=0; + + len=sprintf(buffer,"Type Prot Low High Vis Hid Where Last CPto CPrt Timer Flags\n"); + + for(af = ip_autofw_hosts; af ; af = af->next) + { + len+=sprintf(buffer+len,"%4X %4X %04X-%04X/%04X %04X %08lX %08lX %04X %04X %6lu %4X\n", + af->type, + af->protocol, + af->low, + af->high, + af->visible, + af->hidden, + ntohl(af->where), + ntohl(af->lastcontact), + af->ctlproto, + af->ctlport, + (af->timer.expirestimer.expires-jiffies), + af->flags); + + pos=begin+len; + if(posoffset+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) +{ + off_t pos=0, begin; + struct ip_masq *ms; + unsigned long flags; + char temp[129]; + int idx = 0; + int len=0; + + if (offset < 128) + { +#ifdef CONFIG_IP_MASQUERADE_ICMP + sprintf(temp, + "Prc FromIP FPrt ToIP TPrt Masq Init-seq Delta PDelta Expires (free=%d,%d,%d)", + ip_masq_free_ports[0], ip_masq_free_ports[1], ip_masq_free_ports[2]); +#else /* !defined(CONFIG_IP_MASQUERADE_ICMP) */ + sprintf(temp, + "Prc FromIP FPrt ToIP TPrt Masq Init-seq Delta PDelta Expires (free=%d,%d)", + ip_masq_free_ports[0], ip_masq_free_ports[1]); +#endif /* CONFIG_IP_MASQUERADE_ICMP */ + len = sprintf(buffer, "%-127s\n", temp); + } + pos = 128; + save_flags(flags); + cli(); + + for(idx = 0; idx < IP_MASQ_TAB_SIZE; idx++) + for(ms = ip_masq_m_tab[idx]; ms ; ms = ms->m_link) + { + int timer_active; + pos += 128; + if (pos <= offset) + continue; + + timer_active = del_timer(&ms->timer); + if (!timer_active) + ms->timer.expires = jiffies; + sprintf(temp,"%s %08lX:%04X %08lX:%04X %04X %08X %6d %6d %7lu", + masq_proto_name(ms->protocol), + ntohl(ms->saddr), ntohs(ms->sport), + ntohl(ms->daddr), ntohs(ms->dport), + ntohs(ms->mport), + ms->out_seq.init_seq, + ms->out_seq.delta, + ms->out_seq.previous_delta, + ms->timer.expires-jiffies); + if (timer_active) + add_timer(&ms->timer); + len += sprintf(buffer+len, "%-127s\n", temp); + + if(len >= length) + goto done; + } +done: + restore_flags(flags); + begin = len - (pos - offset); + *start = buffer + begin; + len -= begin; + if(len>length) + len = length; + return len; +} + +/* + * Initialize ip masquerading + */ +int ip_masq_init(void) +{ + register_symtab (&ip_masq_syms); +#ifdef CONFIG_IP_MASQUERADE_PPTP + register_symtab (&pptp_masq_syms); +#endif /* CONFIG_IP_MASQUERADE_PPTP */ +#ifdef CONFIG_IP_MASQUERADE_IPSEC + register_symtab (&ipsec_masq_syms); +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ +#ifdef CONFIG_PROC_FS + proc_net_register(&(struct proc_dir_entry) { + PROC_NET_IPMSQHST, 13, "ip_masquerade", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + ip_msqhst_procinfo + }); +#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW + 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(); + + return 0; +} diff --git a/net/ipv4/ip_masq_quake.c b/net/ipv4/ip_masq_quake.c index 942b7290252a..c516aa87096a 100644 --- a/net/ipv4/ip_masq_quake.c +++ b/net/ipv4/ip_masq_quake.c @@ -85,6 +85,9 @@ masq_quake_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms) int masq_quake_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, struct device *dev) { +#ifdef CONFIG_IP_MASQUERADE_IPSEC + unsigned long flags; +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ struct sk_buff *skb; struct iphdr *iph; struct udphdr *uh; @@ -155,7 +158,16 @@ masq_quake_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **sk memcpy(&udp_port, data, 2); +#ifdef CONFIG_IP_MASQUERADE_IPSEC + save_flags(flags); + cli(); + ip_masq_unhash(ms); +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ ms->dport = htons(udp_port); +#ifdef CONFIG_IP_MASQUERADE_IPSEC + ip_masq_hash(ms); + restore_flags(flags); +#endif /* CONFIG_IP_MASQUERADE_IPSEC */ #if DEBUG_CONFIG_IP_MASQ_QUAKE printk("Quake_in: in_rewrote UDP port %d \n", udp_port); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 4418590f2c19..3f3f4b3e8f9c 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1186,6 +1186,15 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, u32 ack, int len) * then we can probably ignore it. */ + if (sk->state == TCP_SYN_RECV) { + /* + * Should be the exact sequence number for the handshake + * to succeed, or sequence prediction gets a bit easier. + * Also, "partially-established" connections are bad for + * the rest of our code. + */ + if (ack != sk->sent_seq) goto uninteresting_ack; + } else if (after(ack, sk->sent_seq) || before(ack, sk->rcv_ack_seq)) goto uninteresting_ack; -- 2.39.5