diff options
Diffstat (limited to 'target/linux/generic-2.6')
24 files changed, 20224 insertions, 0 deletions
diff --git a/target/linux/generic-2.6/files/init b/target/linux/generic-2.6/files/init new file mode 100755 index 0000000..1fa6a7f --- /dev/null +++ b/target/linux/generic-2.6/files/init @@ -0,0 +1,82 @@ +#!/bin/sh +# Copyright (C) 2006 OpenWrt.org + +export PATH=/bin:/sbin:/usr/bin:/usr/sbin +mount none /proc -t proc +mount none /dev -t devfs +mount non /dev/pts -t devpts + +# the root mount point can come from the kernel command line +# or it can be overridden in the preinit.arch file +[ -f /etc/preinit.arch ] && . /etc/preinit.arch + +# if we have no root parameter, just go to running from ramfs +[ -z $rootfs ] && { + export NOMOUNT="No Root" + exec /sbin/init +} + +#if we have a failsafe boot selected, dont bother +#trying to find or wait for a root mount point +[ -z "$FAILSAFE" ] || { + exec /bin/busybox init +} + +# Load the modules we have in initramfs, this should +# make the media accessible, but, it may take some time +. /etc/functions.sh +load_modules /etc/modules /etc/modules.d/* + +#wait 10 seconds for the disc to show up +#usb stick typically takes 4 to 6 seconds +#till it's readable +#it's quite possible the disc never shows up +#if we netbooted this kernel +COUNTER=0 +while [ $COUNTER -lt 10 ]; do + sleep 1 + [ -e $rootfs ] && let COUNTER=10; + let COUNTER=COUNTER+1 +done +[ -e $rootfs ] || { + export FAILSAFE="NoDisc" + exec /bin/busybox init +} + +# now we'll try mount it, again with a timeout +# This will fail if the inserted stick is formatted +# in a manner we dont understand +COUNTER=0 +while [ $COUNTER -lt 10 ]; do + sleep 1 + mount $rootfs /mnt + [ $? -eq "0" ] && let COUNTER=100; + let COUNTER=COUNTER+1 +done +[ $? -ne "0" ] && { + export FAILSAFE="MountFail" + exec /bin/busybox init +} + +#It mounted, lets look for a postinit file, again, give it time +#I've seen this take 6 seconds to actually complete +COUNTER=0 +while [ $COUNTER -lt 10 ]; do + sleep 1 + [ -e /mnt/etc/banner ] && let COUNTER=10; + let COUNTER=COUNTER+1 +done +[ -e /mnt/etc/banner ] || { + export FAILSAFE="No Openwrt FS" + exec /bin/busybox init +} + +unset rootfs + +mount -o move /proc /mnt/proc +mount -o move /dev /mnt/dev +mount -o move /dev/pts /mnt/dev/pts +mount -o move /tmp /mnt/tmp +mount -o move /sys /mnt/sys +mount none /tmp -t tmpfs +exec switch_root -c /dev/console /mnt /sbin/init diff --git a/target/linux/generic-2.6/patches/000-reenable_devfs.patch b/target/linux/generic-2.6/patches/000-reenable_devfs.patch new file mode 100644 index 0000000..7871c4c --- /dev/null +++ b/target/linux/generic-2.6/patches/000-reenable_devfs.patch @@ -0,0 +1,718 @@ +diff -ur linux-2.6.15-rc5/drivers/mtd/mtd_blkdevs.c linux-2.6.15-rc5-openwrt/drivers/mtd/mtd_blkdevs.c +--- linux-2.6.15-rc5/drivers/mtd/mtd_blkdevs.c 2005-12-04 06:10:42.000000000 +0100 ++++ linux-2.6.15-rc5-openwrt/drivers/mtd/mtd_blkdevs.c 2005-12-15 07:53:20.000000000 +0100 +@@ -21,6 +21,9 @@ + #include <linux/init.h> + #include <asm/semaphore.h> + #include <asm/uaccess.h> ++#ifdef CONFIG_DEVFS_FS ++#include <linux/devfs_fs_kernel.h> ++#endif + + static LIST_HEAD(blktrans_majors); + +@@ -302,6 +305,11 @@ + snprintf(gd->disk_name, sizeof(gd->disk_name), + "%s%d", tr->name, new->devnum); + ++#ifdef CONFIG_DEVFS_FS ++ snprintf(gd->devfs_name, sizeof(gd->devfs_name), ++ "%s/%c", tr->name, (tr->part_bits?'a':'0') + new->devnum); ++#endif ++ + /* 2.5 has capacity in units of 512 bytes while still + having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */ + set_capacity(gd, (new->size * new->blksize) >> 9); +@@ -418,6 +426,10 @@ + return ret; + } + ++#ifdef CONFIG_DEVFS_FS ++ devfs_mk_dir(tr->name); ++#endif ++ + INIT_LIST_HEAD(&tr->devs); + list_add(&tr->list, &blktrans_majors); + +@@ -450,6 +462,10 @@ + tr->remove_dev(dev); + } + ++#ifdef CONFIG_DEVFS_FS ++ devfs_remove(tr->name); ++#endif ++ + blk_cleanup_queue(tr->blkcore_priv->rq); + unregister_blkdev(tr->major, tr->name); + +diff -ur linux-2.6.15-rc5/drivers/mtd/mtdchar.c linux-2.6.15-rc5-openwrt/drivers/mtd/mtdchar.c +--- linux-2.6.15-rc5/drivers/mtd/mtdchar.c 2005-12-04 06:10:42.000000000 +0100 ++++ linux-2.6.15-rc5-openwrt/drivers/mtd/mtdchar.c 2005-12-15 07:49:15.000000000 +0100 +@@ -19,19 +18,33 @@ + + #include <asm/uaccess.h> + ++#ifdef CONFIG_DEVFS_FS ++#include <linux/devfs_fs_kernel.h> ++#else ++#include <linux/device.h> ++ + static struct class *mtd_class; ++#endif + + static void mtd_notify_add(struct mtd_info* mtd) + { + if (!mtd) + return; + ++#ifdef CONFIG_DEVFS_FS ++ devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2), ++ S_IFCHR | S_IRUGO | S_IWUGO, "mtd/%d", mtd->index); ++ ++ devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), ++ S_IFCHR | S_IRUGO, "mtd/%dro", mtd->index); ++#else + class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2), + NULL, "mtd%d", mtd->index); + + class_device_create(mtd_class, NULL, + MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), + NULL, "mtd%dro", mtd->index); ++#endif + } + + static void mtd_notify_remove(struct mtd_info* mtd) +@@ -39,8 +52,13 @@ + if (!mtd) + return; + ++#ifdef CONFIG_DEVFS_FS ++ devfs_remove("mtd/%d", mtd->index); ++ devfs_remove("mtd/%dro", mtd->index); ++#else + class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2)); + class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1)); ++#endif + } + + static struct mtd_notifier notifier = { +@@ -48,6 +66,22 @@ + .remove = mtd_notify_remove, + }; + ++#ifdef CONFIG_DEVFS_FS ++ static inline void mtdchar_devfs_init(void) ++ { ++ devfs_mk_dir("mtd"); ++ register_mtd_user(¬ifier); ++ } ++ static inline void mtdchar_devfs_exit(void) ++ { ++ unregister_mtd_user(¬ifier); ++ devfs_remove("mtd"); ++ } ++ #else /* !DEVFS */ ++ #define mtdchar_devfs_init() do { } while(0) ++ #define mtdchar_devfs_exit() do { } while(0) ++#endif ++ + /* + * We use file->private_data to store a pointer to the MTDdevice. + * Since alighment is at least 32 bits, we have 2 bits free for OTP +@@ -643,6 +677,9 @@ + return -EAGAIN; + } + ++#ifdef CONFIG_DEVFS_FS ++ mtdchar_devfs_init(); ++#else + mtd_class = class_create(THIS_MODULE, "mtd"); + + if (IS_ERR(mtd_class)) { +@@ -652,13 +689,19 @@ + } + + register_mtd_user(¬ifier); ++#endif + return 0; + } + + static void __exit cleanup_mtdchar(void) + { ++ ++#ifdef CONFIG_DEVFS_FS ++ mtdchar_devfs_exit(); ++#else + unregister_mtd_user(¬ifier); + class_destroy(mtd_class); ++#endif + unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); + } + +diff -ur linux-2.6.15-rc5/fs/Kconfig linux-2.6.15-rc5-openwrt/fs/Kconfig +--- linux-2.6.15-rc5/fs/Kconfig 2005-12-04 06:10:42.000000000 +0100 ++++ linux-2.6.15-rc5-openwrt/fs/Kconfig 2005-12-15 07:44:01.000000000 +0100 +@@ -772,6 +772,56 @@ + help + Exports the dump image of crashed kernel in ELF format. + ++config DEVFS_FS ++ bool "/dev file system support (OBSOLETE)" ++ depends on EXPERIMENTAL ++ help ++ This is support for devfs, a virtual file system (like /proc) which ++ provides the file system interface to device drivers, normally found ++ in /dev. Devfs does not depend on major and minor number ++ allocations. Device drivers register entries in /dev which then ++ appear automatically, which means that the system administrator does ++ not have to create character and block special device files in the ++ /dev directory using the mknod command (or MAKEDEV script) anymore. ++ ++ This is work in progress. If you want to use this, you *must* read ++ the material in <file:Documentation/filesystems/devfs/>, especially ++ the file README there. ++ ++ Note that devfs no longer manages /dev/pts! If you are using UNIX98 ++ ptys, you will also need to mount the /dev/pts filesystem (devpts). ++ ++ Note that devfs has been obsoleted by udev, ++ <http://www.kernel.org/pub/linux/utils/kernel/hotplug/>. ++ It has been stripped down to a bare minimum and is only provided for ++ legacy installations that use its naming scheme which is ++ unfortunately different from the names normal Linux installations ++ use. ++ ++ If unsure, say N. ++ ++config DEVFS_MOUNT ++ bool "Automatically mount at boot" ++ depends on DEVFS_FS ++ help ++ This option appears if you have CONFIG_DEVFS_FS enabled. Setting ++ this to 'Y' will make the kernel automatically mount devfs onto /dev ++ when the system is booted, before the init thread is started. ++ You can override this with the "devfs=nomount" boot option. ++ ++ If unsure, say N. ++ ++config DEVFS_DEBUG ++ bool "Debug devfs" ++ depends on DEVFS_FS ++ help ++ If you say Y here, then the /dev file system code will generate ++ debugging messages. See the file ++ <file:Documentation/filesystems/devfs/boot-options> for more ++ details. ++ ++ If unsure, say N. ++ + config SYSFS + bool "sysfs file system support" if EMBEDDED + default y +diff -ur linux-2.6.17/drivers/ieee1394/dv1394.c linux-2.6.17-devfs/drivers/ieee1394/dv1394.c +--- linux-2.6.17/drivers/ieee1394/dv1394.c 2006-06-17 18:49:35.000000000 -0700 ++++ linux-2.6.17-devfs/drivers/ieee1394/dv1394.c 2006-08-25 11:06:18.000000000 -0700 +@@ -73,7 +73,7 @@ + - fix all XXX showstoppers + - disable IR/IT DMA interrupts on shutdown + - flush pci writes to the card by issuing a read +- - character device dispatching ++ - devfs and character device dispatching (* needs testing with Linux 2.2.x) + - switch over to the new kernel DMA API (pci_map_*()) (* needs testing on platforms with IOMMU!) + - keep all video_cards in a list (for open() via chardev), set file->private_data = video + - dv1394_poll should indicate POLLIN when receiving buffers are available +@@ -1096,6 +1096,7 @@ + + init.api_version = DV1394_API_VERSION; + init.n_frames = DV1394_MAX_FRAMES / 4; ++ /* the following are now set via devfs */ + init.channel = video->channel; + init.format = video->pal_or_ntsc; + init.cip_n = video->cip_n; +@@ -1790,6 +1791,8 @@ + { + struct video_card *video = NULL; + ++ /* if the device was opened through devfs, then file->private_data ++ has already been set to video by devfs */ + if (file->private_data) { + video = (struct video_card*) file->private_data; + +@@ -2208,7 +2211,7 @@ + video = kzalloc(sizeof(*video), GFP_KERNEL); + if (!video) { + printk(KERN_ERR "dv1394: cannot allocate video_card\n"); +- return -1; ++ goto err; + } + + video->ohci = ohci; +@@ -2263,14 +2266,37 @@ + list_add_tail(&video->list, &dv1394_cards); + spin_unlock_irqrestore(&dv1394_cards_lock, flags); + ++ if (devfs_mk_cdev(MKDEV(IEEE1394_MAJOR, ++ IEEE1394_MINOR_BLOCK_DV1394*16 + video->id), ++ S_IFCHR|S_IRUGO|S_IWUGO, ++ "ieee1394/dv/host%d/%s/%s", ++ (video->id>>2), ++ (video->pal_or_ntsc == DV1394_NTSC ? "NTSC" : "PAL"), ++ (video->mode == MODE_RECEIVE ? "in" : "out")) < 0) ++ goto err_free; ++ + debug_printk("dv1394: dv1394_init() OK on ID %d\n", video->id); ++ + return 0; ++ ++ err_free: ++ kfree(video); ++ err: ++ return -1; + } + + static void dv1394_un_init(struct video_card *video) + { ++ char buf[32]; ++ + /* obviously nobody has the driver open at this point */ + do_dv1394_shutdown(video, 1); ++ snprintf(buf, sizeof(buf), "dv/host%d/%s/%s", (video->id >> 2), ++ (video->pal_or_ntsc == DV1394_NTSC ? "NTSC" : "PAL"), ++ (video->mode == MODE_RECEIVE ? "in" : "out") ++ ); ++ ++ devfs_remove("ieee1394/%s", buf); + kfree(video); + } + +@@ -2307,6 +2333,9 @@ + + class_device_destroy(hpsb_protocol_class, + MKDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_DV1394 * 16 + (id<<2))); ++ devfs_remove("ieee1394/dv/host%d/NTSC", id); ++ devfs_remove("ieee1394/dv/host%d/PAL", id); ++ devfs_remove("ieee1394/dv/host%d", id); + } + + static void dv1394_add_host (struct hpsb_host *host) +@@ -2323,6 +2352,9 @@ + class_device_create(hpsb_protocol_class, NULL, MKDEV( + IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_DV1394 * 16 + (id<<2)), + NULL, "dv1394-%d", id); ++ devfs_mk_dir("ieee1394/dv/host%d", id); ++ devfs_mk_dir("ieee1394/dv/host%d/NTSC", id); ++ devfs_mk_dir("ieee1394/dv/host%d/PAL", id); + + dv1394_init(ohci, DV1394_NTSC, MODE_RECEIVE); + dv1394_init(ohci, DV1394_NTSC, MODE_TRANSMIT); +@@ -2579,8 +2611,10 @@ + static void __exit dv1394_exit_module(void) + { + hpsb_unregister_protocol(&dv1394_driver); ++ + hpsb_unregister_highlevel(&dv1394_highlevel); + cdev_del(&dv1394_cdev); ++ devfs_remove("ieee1394/dv"); + } + + static int __init dv1394_init_module(void) +@@ -2596,12 +2630,15 @@ + return ret; + } + ++ devfs_mk_dir("ieee1394/dv"); ++ + hpsb_register_highlevel(&dv1394_highlevel); + + ret = hpsb_register_protocol(&dv1394_driver); + if (ret) { + printk(KERN_ERR "dv1394: failed to register protocol\n"); + hpsb_unregister_highlevel(&dv1394_highlevel); ++ devfs_remove("ieee1394/dv"); + cdev_del(&dv1394_cdev); + return ret; + } +diff -ur linux-2.6.17/drivers/ieee1394/ieee1394_core.c linux-2.6.17-devfs/drivers/ieee1394/ieee1394_core.c +--- linux-2.6.17/drivers/ieee1394/ieee1394_core.c 2006-06-17 18:49:35.000000000 -0700 ++++ linux-2.6.17-devfs/drivers/ieee1394/ieee1394_core.c 2006-08-25 11:06:18.000000000 -0700 +@@ -1078,10 +1078,17 @@ + goto exit_release_kernel_thread; + } + ++ /* actually this is a non-fatal error */ ++ ret = devfs_mk_dir("ieee1394"); ++ if (ret < 0) { ++ HPSB_ERR("unable to make devfs dir for device major %d!\n", IEEE1394_MAJOR); ++ goto release_chrdev; ++ } ++ + ret = bus_register(&ieee1394_bus_type); + if (ret < 0) { + HPSB_INFO("bus register failed"); +- goto release_chrdev; ++ goto release_devfs; + } + + for (i = 0; fw_bus_attrs[i]; i++) { +@@ -1092,7 +1099,7 @@ + fw_bus_attrs[i--]); + } + bus_unregister(&ieee1394_bus_type); +- goto release_chrdev; ++ goto release_devfs; + } + } + +@@ -1145,6 +1152,8 @@ + for (i = 0; fw_bus_attrs[i]; i++) + bus_remove_file(&ieee1394_bus_type, fw_bus_attrs[i]); + bus_unregister(&ieee1394_bus_type); ++release_devfs: ++ devfs_remove("ieee1394"); + release_chrdev: + unregister_chrdev_region(IEEE1394_CORE_DEV, 256); + exit_release_kernel_thread: +@@ -1182,6 +1191,7 @@ + hpsb_cleanup_config_roms(); + + unregister_chrdev_region(IEEE1394_CORE_DEV, 256); ++ devfs_remove("ieee1394"); + } + + module_init(ieee1394_init); +diff -ur linux-2.6.17/drivers/ieee1394/ieee1394_core.h linux-2.6.17-devfs/drivers/ieee1394/ieee1394_core.h +--- linux-2.6.17/drivers/ieee1394/ieee1394_core.h 2006-06-17 18:49:35.000000000 -0700 ++++ linux-2.6.17-devfs/drivers/ieee1394/ieee1394_core.h 2006-08-25 11:06:18.000000000 -0700 +@@ -3,6 +3,7 @@ + #define _IEEE1394_CORE_H + + #include <linux/slab.h> ++#include <linux/devfs_fs_kernel.h> + #include <asm/atomic.h> + #include <asm/semaphore.h> + #include "hosts.h" +diff -ur linux-2.6.17/drivers/ieee1394/raw1394.c linux-2.6.17-devfs/drivers/ieee1394/raw1394.c +--- linux-2.6.17/drivers/ieee1394/raw1394.c 2006-06-17 18:49:35.000000000 -0700 ++++ linux-2.6.17-devfs/drivers/ieee1394/raw1394.c 2006-08-25 11:06:18.000000000 -0700 +@@ -41,6 +41,7 @@ + #include <linux/cdev.h> + #include <asm/uaccess.h> + #include <asm/atomic.h> ++#include <linux/devfs_fs_kernel.h> + #include <linux/compat.h> + + #include "csr1212.h" +@@ -2998,6 +2999,9 @@ + goto out_unreg; + } + ++ devfs_mk_cdev(MKDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_RAW1394 * 16), ++ S_IFCHR | S_IRUSR | S_IWUSR, RAW1394_DEVICE_NAME); ++ + cdev_init(&raw1394_cdev, &raw1394_fops); + raw1394_cdev.owner = THIS_MODULE; + kobject_set_name(&raw1394_cdev.kobj, RAW1394_DEVICE_NAME); +@@ -3019,6 +3023,7 @@ + goto out; + + out_dev: ++ devfs_remove(RAW1394_DEVICE_NAME); + class_device_destroy(hpsb_protocol_class, + MKDEV(IEEE1394_MAJOR, + IEEE1394_MINOR_BLOCK_RAW1394 * 16)); +@@ -3034,6 +3039,7 @@ + MKDEV(IEEE1394_MAJOR, + IEEE1394_MINOR_BLOCK_RAW1394 * 16)); + cdev_del(&raw1394_cdev); ++ devfs_remove(RAW1394_DEVICE_NAME); + hpsb_unregister_highlevel(&raw1394_highlevel); + hpsb_unregister_protocol(&raw1394_driver); + } +diff -ur linux-2.6.17/drivers/ieee1394/video1394.c linux-2.6.17-devfs/drivers/ieee1394/video1394.c +--- linux-2.6.17/drivers/ieee1394/video1394.c 2006-06-17 18:49:35.000000000 -0700 ++++ linux-2.6.17-devfs/drivers/ieee1394/video1394.c 2006-08-25 11:06:18.000000000 -0700 +@@ -42,6 +42,7 @@ + #include <linux/poll.h> + #include <linux/smp_lock.h> + #include <linux/delay.h> ++#include <linux/devfs_fs_kernel.h> + #include <linux/bitops.h> + #include <linux/types.h> + #include <linux/vmalloc.h> +@@ -1321,6 +1322,9 @@ + class_device_create(hpsb_protocol_class, NULL, MKDEV( + IEEE1394_MAJOR, minor), + NULL, "%s-%d", VIDEO1394_DRIVER_NAME, ohci->host->id); ++ devfs_mk_cdev(MKDEV(IEEE1394_MAJOR, minor), ++ S_IFCHR | S_IRUSR | S_IWUSR, ++ "%s/%d", VIDEO1394_DRIVER_NAME, ohci->host->id); + } + + +@@ -1328,9 +1332,12 @@ + { + struct ti_ohci *ohci = hpsb_get_hostinfo(&video1394_highlevel, host); + +- if (ohci) ++ if (ohci) { + class_device_destroy(hpsb_protocol_class, MKDEV(IEEE1394_MAJOR, + IEEE1394_MINOR_BLOCK_VIDEO1394 * 16 + ohci->host->id)); ++ devfs_remove("%s/%d", VIDEO1394_DRIVER_NAME, ohci->host->id); ++ } ++ + return; + } + +@@ -1471,8 +1478,12 @@ + static void __exit video1394_exit_module (void) + { + hpsb_unregister_protocol(&video1394_driver); ++ + hpsb_unregister_highlevel(&video1394_highlevel); ++ ++ devfs_remove(VIDEO1394_DRIVER_NAME); + cdev_del(&video1394_cdev); ++ + PRINT_G(KERN_INFO, "Removed " VIDEO1394_DRIVER_NAME " module"); + } + +@@ -1489,12 +1500,15 @@ + return ret; + } + ++ devfs_mk_dir(VIDEO1394_DRIVER_NAME); ++ + hpsb_register_highlevel(&video1394_highlevel); + + ret = hpsb_register_protocol(&video1394_driver); + if (ret) { + PRINT_G(KERN_ERR, "video1394: failed to register protocol"); + hpsb_unregister_highlevel(&video1394_highlevel); ++ devfs_remove(VIDEO1394_DRIVER_NAME); + cdev_del(&video1394_cdev); + return ret; + } +diff -ur linux-2.6.17/drivers/scsi/osst.c linux-2.6.17-devfs/drivers/scsi/osst.c +--- linux-2.6.17/drivers/scsi/osst.c 2006-06-17 18:49:35.000000000 -0700 ++++ linux-2.6.17-devfs/drivers/scsi/osst.c 2006-08-25 11:06:18.000000000 -0700 +@@ -48,6 +48,7 @@ + #include <linux/vmalloc.h> + #include <linux/blkdev.h> + #include <linux/moduleparam.h> ++#include <linux/devfs_fs_kernel.h> + #include <linux/delay.h> + #include <linux/jiffies.h> + #include <asm/uaccess.h> +@@ -5721,7 +5722,7 @@ + struct st_partstat * STps; + struct osst_buffer * buffer; + struct gendisk * drive; +- int i, dev_num; ++ int i, mode, dev_num; + + if (SDp->type != TYPE_TAPE || !osst_supports(SDp)) + return -ENODEV; +@@ -5857,6 +5858,18 @@ + snprintf(name, 8, "%s%s", "n", tape_name(tpnt)); + osst_sysfs_add(MKDEV(OSST_MAJOR, dev_num + 128), dev, tpnt, name); + } ++ for (mode = 0; mode < ST_NBR_MODES; ++mode) { ++ /* Rewind entry */ ++ devfs_mk_cdev(MKDEV(OSST_MAJOR, dev_num + (mode << 5)), ++ S_IFCHR | S_IRUGO | S_IWUGO, ++ "%s/ot%s", SDp->devfs_name, osst_formats[mode]); ++ ++ /* No-rewind entry */ ++ devfs_mk_cdev(MKDEV(OSST_MAJOR, dev_num + (mode << 5) + 128), ++ S_IFCHR | S_IRUGO | S_IWUGO, ++ "%s/ot%sn", SDp->devfs_name, osst_formats[mode]); ++ } ++ drive->number = devfs_register_tape(SDp->devfs_name); + + sdev_printk(KERN_INFO, SDp, + "osst :I: Attached OnStream %.5s tape as %s\n", +@@ -5873,7 +5886,7 @@ + { + struct scsi_device * SDp = to_scsi_device(dev); + struct osst_tape * tpnt; +- int i; ++ int i, mode; + + if ((SDp->type != TYPE_TAPE) || (osst_nr_dev <= 0)) + return 0; +@@ -5884,6 +5897,11 @@ + osst_sysfs_destroy(MKDEV(OSST_MAJOR, i)); + osst_sysfs_destroy(MKDEV(OSST_MAJOR, i+128)); + tpnt->device = NULL; ++ for (mode = 0; mode < ST_NBR_MODES; ++mode) { ++ devfs_remove("%s/ot%s", SDp->devfs_name, osst_formats[mode]); ++ devfs_remove("%s/ot%sn", SDp->devfs_name, osst_formats[mode]); ++ } ++ devfs_unregister_tape(tpnt->drive->number); + put_disk(tpnt->drive); + os_scsi_tapes[i] = NULL; + osst_nr_dev--; +diff -ur linux-2.6.17/drivers/scsi/scsi.c linux-2.6.17-devfs/drivers/scsi/scsi.c +--- linux-2.6.17/drivers/scsi/scsi.c 2006-06-17 18:49:35.000000000 -0700 ++++ linux-2.6.17-devfs/drivers/scsi/scsi.c 2006-08-25 11:07:42.000000000 -0700 +@@ -48,6 +48,7 @@ + #include <linux/delay.h> + #include <linux/init.h> + #include <linux/completion.h> ++#include <linux/devfs_fs_kernel.h> + #include <linux/unistd.h> + #include <linux/spinlock.h> + #include <linux/kmod.h> +@@ -1247,6 +1248,7 @@ + for_each_possible_cpu(i) + INIT_LIST_HEAD(&per_cpu(scsi_done_q, i)); + ++ devfs_mk_dir("scsi"); + printk(KERN_NOTICE "SCSI subsystem initialized\n"); + return 0; + +@@ -1271,6 +1273,7 @@ + scsi_exit_sysctl(); + scsi_exit_hosts(); + scsi_exit_devinfo(); ++ devfs_remove("scsi"); + scsi_exit_procfs(); + scsi_exit_queue(); + } +diff -ur linux-2.6.17/drivers/scsi/scsi_scan.c linux-2.6.17-devfs/drivers/scsi/scsi_scan.c +--- linux-2.6.17/drivers/scsi/scsi_scan.c 2006-06-17 18:49:35.000000000 -0700 ++++ linux-2.6.17-devfs/drivers/scsi/scsi_scan.c 2006-08-25 11:06:18.000000000 -0700 +@@ -716,8 +716,12 @@ + if (inq_result[7] & 0x10) + sdev->sdtr = 1; + ++ sprintf(sdev->devfs_name, "scsi/host%d/bus%d/target%d/lun%d", ++ sdev->host->host_no, sdev->channel, ++ sdev->id, sdev->lun); ++ + /* +- * End sysfs code. ++ * End driverfs/devfs code. + */ + + if ((sdev->scsi_level >= SCSI_2) && (inq_result[7] & 2) && +diff -ur linux-2.6.17/drivers/scsi/sd.c linux-2.6.17-devfs/drivers/scsi/sd.c +--- linux-2.6.17/drivers/scsi/sd.c 2006-06-17 18:49:35.000000000 -0700 ++++ linux-2.6.17-devfs/drivers/scsi/sd.c 2006-08-25 11:06:18.000000000 -0700 +@@ -1683,6 +1683,8 @@ + 'a' + m1, 'a' + m2, 'a' + m3); + } + ++ strcpy(gd->devfs_name, sdp->devfs_name); ++ + gd->private_data = &sdkp->driver; + gd->queue = sdkp->device->request_queue; + +diff -ur linux-2.6.17/drivers/scsi/sg.c linux-2.6.17-devfs/drivers/scsi/sg.c +--- linux-2.6.17/drivers/scsi/sg.c 2006-06-17 18:49:35.000000000 -0700 ++++ linux-2.6.17-devfs/drivers/scsi/sg.c 2006-08-25 11:06:18.000000000 -0700 +@@ -44,6 +44,7 @@ + #include <linux/poll.h> + #include <linux/smp_lock.h> + #include <linux/moduleparam.h> ++#include <linux/devfs_fs_kernel.h> + #include <linux/cdev.h> + #include <linux/seq_file.h> + #include <linux/blkdev.h> +@@ -1427,10 +1428,14 @@ + k = error; + sdp = sg_dev_arr[k]; + ++ devfs_mk_cdev(MKDEV(SCSI_GENERIC_MAJOR, k), ++ S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, ++ "%s/generic", scsidp->devfs_name); + error = cdev_add(cdev, MKDEV(SCSI_GENERIC_MAJOR, k), 1); +- if (error) ++ if (error) { ++ devfs_remove("%s/generic", scsidp->devfs_name); + goto out; +- ++ } + sdp->cdev = cdev; + if (sg_sysfs_valid) { + struct class_device * sg_class_member; +@@ -1520,6 +1525,7 @@ + class_device_destroy(sg_sysfs_class, MKDEV(SCSI_GENERIC_MAJOR, k)); + cdev_del(sdp->cdev); + sdp->cdev = NULL; ++ devfs_remove("%s/generic", scsidp->devfs_name); + put_disk(sdp->disk); + sdp->disk = NULL; + if (NULL == sdp->headfp) +diff -ur linux-2.6.17/drivers/scsi/sr.c linux-2.6.17-devfs/drivers/scsi/sr.c +--- linux-2.6.17/drivers/scsi/sr.c 2006-06-17 18:49:35.000000000 -0700 ++++ linux-2.6.17-devfs/drivers/scsi/sr.c 2006-08-25 11:06:18.000000000 -0700 +@@ -592,6 +592,8 @@ + get_capabilities(cd); + sr_vendor_init(cd); + ++ snprintf(disk->devfs_name, sizeof(disk->devfs_name), ++ "%s/cd", sdev->devfs_name); + disk->driverfs_dev = &sdev->sdev_gendev; + set_capacity(disk, cd->capacity); + disk->private_data = &cd->driver; +diff -ur linux-2.6.17/drivers/scsi/st.c linux-2.6.17-devfs/drivers/scsi/st.c +--- linux-2.6.17/drivers/scsi/st.c 2006-06-17 18:49:35.000000000 -0700 ++++ linux-2.6.17-devfs/drivers/scsi/st.c 2006-08-25 11:06:18.000000000 -0700 +@@ -35,6 +35,7 @@ + #include <linux/spinlock.h> + #include <linux/blkdev.h> + #include <linux/moduleparam.h> ++#include <linux/devfs_fs_kernel.h> + #include <linux/cdev.h> + #include <linux/delay.h> + #include <linux/mutex.h> +@@ -4053,8 +4054,23 @@ + do_create_class_files(tpnt, dev_num, mode); + } + ++ for (mode = 0; mode < ST_NBR_MODES; ++mode) { ++ /* Make sure that the minor numbers corresponding to the four ++ first modes always get the same names */ ++ i = mode << (4 - ST_NBR_MODE_BITS); ++ /* Rewind entry */ ++ devfs_mk_cdev(MKDEV(SCSI_TAPE_MAJOR, TAPE_MINOR(dev_num, mode, 0)), ++ S_IFCHR | S_IRUGO | S_IWUGO, ++ "%s/mt%s", SDp->devfs_name, st_formats[i]); ++ /* No-rewind entry */ ++ devfs_mk_cdev(MKDEV(SCSI_TAPE_MAJOR, TAPE_MINOR(dev_num, mode, 1)), ++ S_IFCHR | S_IRUGO | S_IWUGO, ++ "%s/mt%sn", SDp->devfs_name, st_formats[i]); ++ } ++ disk->number = devfs_register_tape(SDp->devfs_name); ++ + sdev_printk(KERN_WARNING, SDp, + "Attached scsi tape %s\n", tape_name(tpnt)); + printk(KERN_WARNING "%s: try direct i/o: %s (alignment %d B)\n", + tape_name(tpnt), tpnt->try_dio ? "yes" : "no", + queue_dma_alignment(SDp->request_queue) + 1); +@@ -4106,9 +4122,13 @@ + scsi_tapes[i] = NULL; + st_nr_dev--; + write_unlock(&st_dev_arr_lock); ++ devfs_unregister_tape(tpnt->disk->number); + sysfs_remove_link(&tpnt->device->sdev_gendev.kobj, + "tape"); + for (mode = 0; mode < ST_NBR_MODES; ++mode) { ++ j = mode << (4 - ST_NBR_MODE_BITS); ++ devfs_remove("%s/mt%s", SDp->devfs_name, st_formats[j]); ++ devfs_remove("%s/mt%sn", SDp->devfs_name, st_formats[j]); + for (j=0; j < 2; j++) { + class_device_destroy(st_sysfs_class, + MKDEV(SCSI_TAPE_MAJOR, +diff -ur linux-2.6.17/include/scsi/scsi_device.h linux-2.6.17-devfs/include/scsi/scsi_device.h +--- linux-2.6.17/include/scsi/scsi_device.h 2006-06-17 18:49:35.000000000 -0700 ++++ linux-2.6.17-devfs/include/scsi/scsi_device.h 2006-08-25 11:06:18.000000000 -0700 +@@ -74,6 +74,7 @@ + unsigned sector_size; /* size in bytes */ + + void *hostdata; /* available to low-level driver */ ++ char devfs_name[256]; /* devfs junk */ + char type; + char scsi_level; + char inq_periph_qual; /* PQ from INQUIRY data */ diff --git a/target/linux/generic-2.6/patches/001-squashfs.patch b/target/linux/generic-2.6/patches/001-squashfs.patch new file mode 100644 index 0000000..f88dbba --- /dev/null +++ b/target/linux/generic-2.6/patches/001-squashfs.patch @@ -0,0 +1,4173 @@ +diff --new-file -urp linux-2.6.15/fs/Kconfig linux-2.6.15-squashfs3.0/fs/Kconfig +--- linux-2.6.15/fs/Kconfig 2006-03-01 22:37:27.000000000 +0000 ++++ linux-2.6.15-squashfs3.0/fs/Kconfig 2006-03-07 21:12:37.000000000 +0000 +@@ -1151,6 +1151,71 @@ config CRAMFS + + If unsure, say N. + ++config SQUASHFS ++ tristate "SquashFS 3.0 - Squashed file system support" ++ select ZLIB_INFLATE ++ help ++ Saying Y here includes support for SquashFS 3.0 (a Compressed Read-Only File ++ System). Squashfs is a highly compressed read-only filesystem for Linux. ++ It uses zlib compression to compress both files, inodes and directories. ++ Inodes in the system are very small and all blocks are packed to minimise ++ data overhead. Block sizes greater than 4K are supported up to a maximum of 64K. ++ SquashFS 3.0 supports 64 bit filesystems and files (larger than 4GB), full ++ uid/gid information, hard links and timestamps. ++ ++ Squashfs is intended for general read-only filesystem use, for archival ++ use (i.e. in cases where a .tar.gz file may be used), and in embedded ++ systems where low overhead is needed. Further information and filesystem tools ++ are available from http://squashfs.sourceforge.net. ++ ++ If you want to compile this as a module ( = code which can be ++ inserted in and removed from the running kernel whenever you want), ++ say M here and read <file:Documentation/modules.txt>. The module ++ will be called squashfs. Note that the root file system (the one ++ containing the directory /) cannot be compiled as a module. ++ ++ If unsure, say N. ++ ++config SQUASHFS_EMBEDDED ++ ++ bool "Additional options for memory-constrained systems" ++ depends on SQUASHFS ++ default n ++ help ++ Saying Y here allows you to specify cache sizes and how Squashfs ++ allocates memory. This is only intended for memory constrained ++ systems. ++ ++ If unsure, say N. ++ ++config SQUASHFS_FRAGMENT_CACHE_SIZE ++ int "Number of fragments cached" if SQUASHFS_EMBEDDED ++ depends on SQUASHFS ++ default "3" ++ help ++ By default SquashFS caches the last 3 fragments read from ++ the filesystem. Increasing this amount may mean SquashFS ++ has to re-read fragments less often from disk, at the expense ++ of extra system memory. Decreasing this amount will mean ++ SquashFS uses less memory at the expense of extra reads from disk. ++ ++ Note there must be at least one cached fragment. Anything ++ much more than three will probably not make much difference. ++ ++config SQUASHFS_VMALLOC ++ bool "Use Vmalloc rather than Kmalloc" if SQUASHFS_EMBEDDED ++ depends on SQUASHFS ++ default n ++ help ++ By default SquashFS uses kmalloc to obtain fragment cache memory. ++ Kmalloc memory is the standard kernel allocator, but it can fail ++ on memory constrained systems. Because of the way Vmalloc works, ++ Vmalloc can succeed when kmalloc fails. Specifying this option ++ will make SquashFS always use Vmalloc to allocate the ++ fragment cache memory. ++ ++ If unsure, say N. ++ + config VXFS_FS + tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)" + help +diff --new-file -urp linux-2.6.15/fs/Makefile linux-2.6.15-squashfs3.0/fs/Makefile +--- linux-2.6.15/fs/Makefile 2006-03-01 22:37:27.000000000 +0000 ++++ linux-2.6.15-squashfs3.0/fs/Makefile 2006-03-07 21:12:37.000000000 +0000 +@@ -55,6 +55,7 @@ obj-$(CONFIG_EXT3_FS) += ext3/ # Before + obj-$(CONFIG_JBD) += jbd/ + obj-$(CONFIG_EXT2_FS) += ext2/ + obj-$(CONFIG_CRAMFS) += cramfs/ ++obj-$(CONFIG_SQUASHFS) += squashfs/ + obj-$(CONFIG_RAMFS) += ramfs/ + obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ + obj-$(CONFIG_CODA_FS) += coda/ +diff --new-file -urp linux-2.6.15/fs/squashfs/inode.c linux-2.6.15-squashfs3.0/fs/squashfs/inode.c +--- linux-2.6.15/fs/squashfs/inode.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.15-squashfs3.0/fs/squashfs/inode.c 2006-03-07 21:12:37.000000000 +0000 +@@ -0,0 +1,2127 @@ ++/* ++ * Squashfs - a compressed read only filesystem for Linux ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher <phillip@lougher.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * inode.c ++ */ ++ ++#include <linux/types.h> ++#include <linux/squashfs_fs.h> ++#include <linux/module.h> ++#include <linux/errno.h> ++#include <linux/slab.h> ++#include <linux/fs.h> ++#include <linux/smp_lock.h> ++#include <linux/slab.h> ++#include <linux/squashfs_fs_sb.h> ++#include <linux/squashfs_fs_i.h> ++#include <linux/buffer_head.h> ++#include <linux/vfs.h> ++#include <linux/init.h> ++#include <linux/dcache.h> ++#include <linux/wait.h> ++#include <linux/zlib.h> ++#include <linux/blkdev.h> ++#include <linux/vmalloc.h> ++#include <asm/uaccess.h> ++#include <asm/semaphore.h> ++ ++#include "squashfs.h" ++ ++static void squashfs_put_super(struct super_block *); ++static int squashfs_statfs(struct super_block *, struct kstatfs *); ++static int squashfs_symlink_readpage(struct file *file, struct page *page); ++static int squashfs_readpage(struct file *file, struct page *page); ++static int squashfs_readpage4K(struct file *file, struct page *page); ++static int squashfs_readdir(struct file *, void *, filldir_t); ++static struct inode *squashfs_alloc_inode(struct super_block *sb); ++static void squashfs_destroy_inode(struct inode *inode); ++static int init_inodecache(void); ++static void destroy_inodecache(void); ++static struct dentry *squashfs_lookup(struct inode *, struct dentry *, ++ struct nameidata *); ++static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode); ++static long long read_blocklist(struct inode *inode, int index, ++ int readahead_blks, char *block_list, ++ unsigned short **block_p, unsigned int *bsize); ++static struct super_block *squashfs_get_sb(struct file_system_type *, int, ++ const char *, void *); ++ ++ ++static z_stream stream; ++ ++static struct file_system_type squashfs_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "squashfs", ++ .get_sb = squashfs_get_sb, ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV ++}; ++ ++static unsigned char squashfs_filetype_table[] = { ++ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK ++}; ++ ++static struct super_operations squashfs_ops = { ++ .alloc_inode = squashfs_alloc_inode, ++ .destroy_inode = squashfs_destroy_inode, ++ .statfs = squashfs_statfs, ++ .put_super = squashfs_put_super, ++}; ++ ++SQSH_EXTERN struct address_space_operations squashfs_symlink_aops = { ++ .readpage = squashfs_symlink_readpage ++}; ++ ++SQSH_EXTERN struct address_space_operations squashfs_aops = { ++ .readpage = squashfs_readpage ++}; ++ ++SQSH_EXTERN struct address_space_operations squashfs_aops_4K = { ++ .readpage = squashfs_readpage4K ++}; ++ ++static struct file_operations squashfs_dir_ops = { ++ .read = generic_read_dir, ++ .readdir = squashfs_readdir ++}; ++ ++SQSH_EXTERN struct inode_operations squashfs_dir_inode_ops = { ++ .lookup = squashfs_lookup ++}; ++ ++ ++static struct buffer_head *get_block_length(struct super_block *s, ++ int *cur_index, int *offset, int *c_byte) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ unsigned short temp; ++ struct buffer_head *bh; ++ ++ if (!(bh = sb_bread(s, *cur_index))) ++ goto out; ++ ++ if (msblk->devblksize - *offset == 1) { ++ if (msblk->swap) ++ ((unsigned char *) &temp)[1] = *((unsigned char *) ++ (bh->b_data + *offset)); ++ else ++ ((unsigned char *) &temp)[0] = *((unsigned char *) ++ (bh->b_data + *offset)); ++ brelse(bh); ++ if (!(bh = sb_bread(s, ++(*cur_index)))) ++ goto out; ++ if (msblk->swap) ++ ((unsigned char *) &temp)[0] = *((unsigned char *) ++ bh->b_data); ++ else ++ ((unsigned char *) &temp)[1] = *((unsigned char *) ++ bh->b_data); ++ *c_byte = temp; ++ *offset = 1; ++ } else { ++ if (msblk->swap) { ++ ((unsigned char *) &temp)[1] = *((unsigned char *) ++ (bh->b_data + *offset)); ++ ((unsigned char *) &temp)[0] = *((unsigned char *) ++ (bh->b_data + *offset + 1)); ++ } else { ++ ((unsigned char *) &temp)[0] = *((unsigned char *) ++ (bh->b_data + *offset)); ++ ((unsigned char *) &temp)[1] = *((unsigned char *) ++ (bh->b_data + *offset + 1)); ++ } ++ *c_byte = temp; ++ *offset += 2; ++ } ++ ++ if (SQUASHFS_CHECK_DATA(msblk->sblk.flags)) { ++ if (*offset == msblk->devblksize) { ++ brelse(bh); ++ if (!(bh = sb_bread(s, ++(*cur_index)))) ++ goto out; ++ *offset = 0; ++ } ++ if (*((unsigned char *) (bh->b_data + *offset)) != ++ SQUASHFS_MARKER_BYTE) { ++ ERROR("Metadata block marker corrupt @ %x\n", ++ *cur_index); ++ brelse(bh); ++ goto out; ++ } ++ (*offset)++; ++ } ++ return bh; ++ ++out: ++ return NULL; ++} ++ ++ ++SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer, ++ long long index, unsigned int length, ++ long long *next_index) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >> ++ msblk->devblksize_log2) + 2]; ++ unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1); ++ unsigned int cur_index = index >> msblk->devblksize_log2; ++ int bytes, avail_bytes, b = 0, k; ++ char *c_buffer; ++ unsigned int compressed; ++ unsigned int c_byte = length; ++ ++ if (c_byte) { ++ bytes = msblk->devblksize - offset; ++ compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte); ++ c_buffer = compressed ? msblk->read_data : buffer; ++ c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte); ++ ++ TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed ++ ? "" : "un", (unsigned int) c_byte); ++ ++ if (!(bh[0] = sb_getblk(s, cur_index))) ++ goto block_release; ++ ++ for (b = 1; bytes < c_byte; b++) { ++ if (!(bh[b] = sb_getblk(s, ++cur_index))) ++ goto block_release; ++ bytes += msblk->devblksize; ++ } ++ ll_rw_block(READ, b, bh); ++ } else { ++ if (!(bh[0] = get_block_length(s, &cur_index, &offset, ++ &c_byte))) ++ goto read_failure; ++ ++ bytes = msblk->devblksize - offset; ++ compressed = SQUASHFS_COMPRESSED(c_byte); ++ c_buffer = compressed ? msblk->read_data : buffer; ++ c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); ++ ++ TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed ++ ? "" : "un", (unsigned int) c_byte); ++ ++ for (b = 1; bytes < c_byte; b++) { ++ if (!(bh[b] = sb_getblk(s, ++cur_index))) ++ goto block_release; ++ bytes += msblk->devblksize; ++ } ++ ll_rw_block(READ, b - 1, bh + 1); ++ } ++ ++ if (compressed) ++ down(&msblk->read_data_mutex); ++ ++ for (bytes = 0, k = 0; k < b; k++) { ++ avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ? ++ msblk->devblksize - offset : ++ c_byte - bytes; ++ wait_on_buffer(bh[k]); ++ if (!buffer_uptodate(bh[k])) ++ goto block_release; ++ memcpy(c_buffer + bytes, bh[k]->b_data + offset, avail_bytes); ++ bytes += avail_bytes; ++ offset = 0; ++ brelse(bh[k]); ++ } ++ ++ /* ++ * uncompress block ++ */ ++ if (compressed) { ++ int zlib_err; ++ ++ stream.next_in = c_buffer; ++ stream.avail_in = c_byte; ++ stream.next_out = buffer; ++ stream.avail_out = msblk->read_size; ++ ++ if (((zlib_err = zlib_inflateInit(&stream)) != Z_OK) || ++ ((zlib_err = zlib_inflate(&stream, Z_FINISH)) ++ != Z_STREAM_END) || ((zlib_err = ++ zlib_inflateEnd(&stream)) != Z_OK)) { ++ ERROR("zlib_fs returned unexpected result 0x%x\n", ++ zlib_err); ++ bytes = 0; ++ } else ++ bytes = stream.total_out; ++ ++ up(&msblk->read_data_mutex); ++ } ++ ++ if (next_index) ++ *next_index = index + c_byte + (length ? 0 : ++ (SQUASHFS_CHECK_DATA(msblk->sblk.flags) ++ ? 3 : 2)); ++ return bytes; ++ ++block_release: ++ while (--b >= 0) ++ brelse(bh[b]); ++ ++read_failure: ++ ERROR("sb_bread failed reading block 0x%x\n", cur_index); ++ return 0; ++} ++ ++ ++SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, char *buffer, ++ long long block, unsigned int offset, ++ int length, long long *next_block, ++ unsigned int *next_offset) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ int n, i, bytes, return_length = length; ++ long long next_index; ++ ++ TRACE("Entered squashfs_get_cached_block [%llx:%x]\n", block, offset); ++ ++ while ( 1 ) { ++ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) ++ if (msblk->block_cache[i].block == block) ++ break; ++ ++ down(&msblk->block_cache_mutex); ++ ++ if (i == SQUASHFS_CACHED_BLKS) { ++ /* read inode header block */ ++ for (i = msblk->next_cache, n = SQUASHFS_CACHED_BLKS; ++ n ; n --, i = (i + 1) % ++ SQUASHFS_CACHED_BLKS) ++ if (msblk->block_cache[i].block != ++ SQUASHFS_USED_BLK) ++ break; ++ ++ if (n == 0) { ++ wait_queue_t wait; ++ ++ init_waitqueue_entry(&wait, current); ++ add_wait_queue(&msblk->waitq, &wait); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ up(&msblk->block_cache_mutex); ++ schedule(); ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&msblk->waitq, &wait); ++ continue; ++ } ++ msblk->next_cache = (i + 1) % SQUASHFS_CACHED_BLKS; ++ ++ if (msblk->block_cache[i].block == ++ SQUASHFS_INVALID_BLK) { ++ if (!(msblk->block_cache[i].data = ++ kmalloc(SQUASHFS_METADATA_SIZE, ++ GFP_KERNEL))) { ++ ERROR("Failed to allocate cache" ++ "block\n"); ++ up(&msblk->block_cache_mutex); ++ goto out; ++ } ++ } ++ ++ msblk->block_cache[i].block = SQUASHFS_USED_BLK; ++ up(&msblk->block_cache_mutex); ++ ++ if (!(msblk->block_cache[i].length = ++ squashfs_read_data(s, ++ msblk->block_cache[i].data, ++ block, 0, &next_index))) { ++ ERROR("Unable to read cache block [%llx:%x]\n", ++ block, offset); ++ goto out; ++ } ++ ++ down(&msblk->block_cache_mutex); ++ wake_up(&msblk->waitq); ++ msblk->block_cache[i].block = block; ++ msblk->block_cache[i].next_index = next_index; ++ TRACE("Read cache block [%llx:%x]\n", block, offset); ++ } ++ ++ if (msblk->block_cache[i].block != block) { ++ up(&msblk->block_cache_mutex); ++ continue; ++ } ++ ++ if ((bytes = msblk->block_cache[i].length - offset) >= length) { ++ if (buffer) ++ memcpy(buffer, msblk->block_cache[i].data + ++ offset, length); ++ if (msblk->block_cache[i].length - offset == length) { ++ *next_block = msblk->block_cache[i].next_index; ++ *next_offset = 0; ++ } else { ++ *next_block = block; ++ *next_offset = offset + length; ++ } ++ up(&msblk->block_cache_mutex); ++ goto finish; ++ } else { ++ if (buffer) { ++ memcpy(buffer, msblk->block_cache[i].data + ++ offset, bytes); ++ buffer += bytes; ++ } ++ block = msblk->block_cache[i].next_index; ++ up(&msblk->block_cache_mutex); ++ length -= bytes; ++ offset = 0; ++ } ++ } ++ ++finish: ++ return return_length; ++out: ++ return 0; ++} ++ ++ ++static int get_fragment_location(struct super_block *s, unsigned int fragment, ++ long long *fragment_start_block, ++ unsigned int *fragment_size) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ long long start_block = ++ msblk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)]; ++ int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment); ++ struct squashfs_fragment_entry fragment_entry; ++ ++ if (msblk->swap) { ++ struct squashfs_fragment_entry sfragment_entry; ++ ++ if (!squashfs_get_cached_block(s, (char *) &sfragment_entry, ++ start_block, offset, ++ sizeof(sfragment_entry), &start_block, ++ &offset)) ++ goto out; ++ SQUASHFS_SWAP_FRAGMENT_ENTRY(&fragment_entry, &sfragment_entry); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) &fragment_entry, ++ start_block, offset, ++ sizeof(fragment_entry), &start_block, ++ &offset)) ++ goto out; ++ ++ *fragment_start_block = fragment_entry.start_block; ++ *fragment_size = fragment_entry.size; ++ ++ return 1; ++ ++out: ++ return 0; ++} ++ ++ ++SQSH_EXTERN void release_cached_fragment(struct squashfs_sb_info *msblk, struct ++ squashfs_fragment_cache *fragment) ++{ ++ down(&msblk->fragment_mutex); ++ fragment->locked --; ++ wake_up(&msblk->fragment_wait_queue); ++ up(&msblk->fragment_mutex); ++} ++ ++ ++SQSH_EXTERN struct squashfs_fragment_cache *get_cached_fragment(struct super_block ++ *s, long long start_block, ++ int length) ++{ ++ int i, n; ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ ++ while ( 1 ) { ++ down(&msblk->fragment_mutex); ++ ++ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS && ++ msblk->fragment[i].block != start_block; i++); ++ ++ if (i == SQUASHFS_CACHED_FRAGMENTS) { ++ for (i = msblk->next_fragment, n = ++ SQUASHFS_CACHED_FRAGMENTS; n && ++ msblk->fragment[i].locked; n--, i = (i + 1) % ++ SQUASHFS_CACHED_FRAGMENTS); ++ ++ if (n == 0) { ++ wait_queue_t wait; ++ ++ init_waitqueue_entry(&wait, current); ++ add_wait_queue(&msblk->fragment_wait_queue, ++ &wait); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ up(&msblk->fragment_mutex); ++ schedule(); ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&msblk->fragment_wait_queue, ++ &wait); ++ continue; ++ } ++ msblk->next_fragment = (msblk->next_fragment + 1) % ++ SQUASHFS_CACHED_FRAGMENTS; ++ ++ if (msblk->fragment[i].data == NULL) ++ if (!(msblk->fragment[i].data = SQUASHFS_ALLOC ++ (SQUASHFS_FILE_MAX_SIZE))) { ++ ERROR("Failed to allocate fragment " ++ "cache block\n"); ++ up(&msblk->fragment_mutex); ++ goto out; ++ } ++ ++ msblk->fragment[i].block = SQUASHFS_INVALID_BLK; ++ msblk->fragment[i].locked = 1; ++ up(&msblk->fragment_mutex); ++ ++ if (!(msblk->fragment[i].length = squashfs_read_data(s, ++ msblk->fragment[i].data, ++ start_block, length, NULL))) { ++ ERROR("Unable to read fragment cache block " ++ "[%llx]\n", start_block); ++ msblk->fragment[i].locked = 0; ++ goto out; ++ } ++ ++ msblk->fragment[i].block = start_block; ++ TRACE("New fragment %d, start block %lld, locked %d\n", ++ i, msblk->fragment[i].block, ++ msblk->fragment[i].locked); ++ break; ++ } ++ ++ msblk->fragment[i].locked++; ++ up(&msblk->fragment_mutex); ++ TRACE("Got fragment %d, start block %lld, locked %d\n", i, ++ msblk->fragment[i].block, ++ msblk->fragment[i].locked); ++ break; ++ } ++ ++ return &msblk->fragment[i]; ++ ++out: ++ return NULL; ++} ++ ++ ++static struct inode *squashfs_new_inode(struct super_block *s, ++ struct squashfs_base_inode_header *inodeb) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct inode *i = new_inode(s); ++ ++ if (i) { ++ i->i_ino = inodeb->inode_number; ++ i->i_mtime.tv_sec = inodeb->mtime; ++ i->i_atime.tv_sec = inodeb->mtime; ++ i->i_ctime.tv_sec = inodeb->mtime; ++ i->i_uid = msblk->uid[inodeb->uid]; ++ i->i_mode = inodeb->mode; ++ i->i_size = 0; ++ if (inodeb->guid == SQUASHFS_GUIDS) ++ i->i_gid = i->i_uid; ++ else ++ i->i_gid = msblk->guid[inodeb->guid]; ++ } ++ ++ return i; ++} ++ ++ ++static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode) ++{ ++ struct inode *i; ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long block = SQUASHFS_INODE_BLK(inode) + ++ sblk->inode_table_start; ++ unsigned int offset = SQUASHFS_INODE_OFFSET(inode); ++ long long next_block; ++ unsigned int next_offset; ++ union squashfs_inode_header id, sid; ++ struct squashfs_base_inode_header *inodeb = &id.base, ++ *sinodeb = &sid.base; ++ ++ TRACE("Entered squashfs_iget\n"); ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) sinodeb, block, ++ offset, sizeof(*sinodeb), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_BASE_INODE_HEADER(inodeb, sinodeb, ++ sizeof(*sinodeb)); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) inodeb, block, ++ offset, sizeof(*inodeb), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ switch(inodeb->inode_type) { ++ case SQUASHFS_FILE_TYPE: { ++ unsigned int frag_size; ++ long long frag_blk; ++ struct squashfs_reg_inode_header *inodep = &id.reg; ++ struct squashfs_reg_inode_header *sinodep = &sid.reg; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_REG_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ frag_blk = SQUASHFS_INVALID_BLK; ++ if (inodep->fragment != SQUASHFS_INVALID_FRAG && ++ !get_fragment_location(s, ++ inodep->fragment, &frag_blk, &frag_size)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = 1; ++ i->i_size = inodep->file_size; ++ i->i_fop = &generic_ro_fops; ++ i->i_mode |= S_IFREG; ++ i->i_blocks = ((i->i_size - 1) >> 9) + 1; ++ i->i_blksize = PAGE_CACHE_SIZE; ++ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; ++ SQUASHFS_I(i)->u.s1.fragment_size = frag_size; ++ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->u.s1.block_list_start = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ if (sblk->block_size > 4096) ++ i->i_data.a_ops = &squashfs_aops; ++ else ++ i->i_data.a_ops = &squashfs_aops_4K; ++ ++ TRACE("File inode %x:%x, start_block %llx, " ++ "block_list_start %llx, offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, next_block, ++ next_offset); ++ break; ++ } ++ case SQUASHFS_LREG_TYPE: { ++ unsigned int frag_size; ++ long long frag_blk; ++ struct squashfs_lreg_inode_header *inodep = &id.lreg; ++ struct squashfs_lreg_inode_header *sinodep = &sid.lreg; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_LREG_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ frag_blk = SQUASHFS_INVALID_BLK; ++ if (inodep->fragment != SQUASHFS_INVALID_FRAG && ++ !get_fragment_location(s, ++ inodep->fragment, &frag_blk, &frag_size)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_size = inodep->file_size; ++ i->i_fop = &generic_ro_fops; ++ i->i_mode |= S_IFREG; ++ i->i_blocks = ((i->i_size - 1) >> 9) + 1; ++ i->i_blksize = PAGE_CACHE_SIZE; ++ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; ++ SQUASHFS_I(i)->u.s1.fragment_size = frag_size; ++ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->u.s1.block_list_start = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ if (sblk->block_size > 4096) ++ i->i_data.a_ops = &squashfs_aops; ++ else ++ i->i_data.a_ops = &squashfs_aops_4K; ++ ++ TRACE("File inode %x:%x, start_block %llx, " ++ "block_list_start %llx, offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, next_block, ++ next_offset); ++ break; ++ } ++ case SQUASHFS_DIR_TYPE: { ++ struct squashfs_dir_inode_header *inodep = &id.dir; ++ struct squashfs_dir_inode_header *sinodep = &sid.dir; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_DIR_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_size = inodep->file_size; ++ i->i_op = &squashfs_dir_inode_ops; ++ i->i_fop = &squashfs_dir_ops; ++ i->i_mode |= S_IFDIR; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->offset = inodep->offset; ++ SQUASHFS_I(i)->u.s2.directory_index_count = 0; ++ SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode; ++ ++ TRACE("Directory inode %x:%x, start_block %x, offset " ++ "%x\n", SQUASHFS_INODE_BLK(inode), ++ offset, inodep->start_block, ++ inodep->offset); ++ break; ++ } ++ case SQUASHFS_LDIR_TYPE: { ++ struct squashfs_ldir_inode_header *inodep = &id.ldir; ++ struct squashfs_ldir_inode_header *sinodep = &sid.ldir; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_LDIR_INODE_HEADER(inodep, ++ sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_size = inodep->file_size; ++ i->i_op = &squashfs_dir_inode_ops; ++ i->i_fop = &squashfs_dir_ops; ++ i->i_mode |= S_IFDIR; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->offset = inodep->offset; ++ SQUASHFS_I(i)->u.s2.directory_index_start = next_block; ++ SQUASHFS_I(i)->u.s2.directory_index_offset = ++ next_offset; ++ SQUASHFS_I(i)->u.s2.directory_index_count = ++ inodep->i_count; ++ SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode; ++ ++ TRACE("Long directory inode %x:%x, start_block %x, " ++ "offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, inodep->offset); ++ break; ++ } ++ case SQUASHFS_SYMLINK_TYPE: { ++ struct squashfs_symlink_inode_header *inodep = ++ &id.symlink; ++ struct squashfs_symlink_inode_header *sinodep = ++ &sid.symlink; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep, ++ sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_size = inodep->symlink_size; ++ i->i_op = &page_symlink_inode_operations; ++ i->i_data.a_ops = &squashfs_symlink_aops; ++ i->i_mode |= S_IFLNK; ++ SQUASHFS_I(i)->start_block = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ ++ TRACE("Symbolic link inode %x:%x, start_block %llx, " ++ "offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ next_block, next_offset); ++ break; ++ } ++ case SQUASHFS_BLKDEV_TYPE: ++ case SQUASHFS_CHRDEV_TYPE: { ++ struct squashfs_dev_inode_header *inodep = &id.dev; ++ struct squashfs_dev_inode_header *sinodep = &sid.dev; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if ((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_mode |= (inodeb->inode_type == ++ SQUASHFS_CHRDEV_TYPE) ? S_IFCHR : ++ S_IFBLK; ++ init_special_inode(i, i->i_mode, ++ old_decode_dev(inodep->rdev)); ++ ++ TRACE("Device inode %x:%x, rdev %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->rdev); ++ break; ++ } ++ case SQUASHFS_FIFO_TYPE: ++ case SQUASHFS_SOCKET_TYPE: { ++ struct squashfs_ipc_inode_header *inodep = &id.ipc; ++ struct squashfs_ipc_inode_header *sinodep = &sid.ipc; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_IPC_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if ((i = squashfs_new_inode(s, inodeb)) == NULL) ++ goto failed_read1; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE) ++ ? S_IFIFO : S_IFSOCK; ++ init_special_inode(i, i->i_mode, 0); ++ break; ++ } ++ default: ++ ERROR("Unknown inode type %d in squashfs_iget!\n", ++ inodeb->inode_type); ++ goto failed_read1; ++ } ++ ++ insert_inode_hash(i); ++ return i; ++ ++failed_read: ++ ERROR("Unable to read inode [%llx:%x]\n", block, offset); ++ ++failed_read1: ++ return NULL; ++} ++ ++ ++static int read_fragment_index_table(struct super_block *s) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ /* Allocate fragment index table */ ++ if (!(msblk->fragment_index = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES ++ (sblk->fragments), GFP_KERNEL))) { ++ ERROR("Failed to allocate uid/gid table\n"); ++ return 0; ++ } ++ ++ if (SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments) && ++ !squashfs_read_data(s, (char *) ++ msblk->fragment_index, ++ sblk->fragment_table_start, ++ SQUASHFS_FRAGMENT_INDEX_BYTES ++ (sblk->fragments) | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++ ERROR("unable to read fragment index table\n"); ++ return 0; ++ } ++ ++ if (msblk->swap) { ++ int i; ++ long long fragment; ++ ++ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments); ++ i++) { ++ SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment), ++ &msblk->fragment_index[i], 1); ++ msblk->fragment_index[i] = fragment; ++ } ++ } ++ ++ return 1; ++} ++ ++ ++static int supported_squashfs_filesystem(struct squashfs_sb_info *msblk, int silent) ++{ ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ msblk->iget = squashfs_iget; ++ msblk->read_blocklist = read_blocklist; ++ msblk->read_fragment_index_table = read_fragment_index_table; ++ ++ if (sblk->s_major == 1) { ++ if (!squashfs_1_0_supported(msblk)) { ++ SERROR("Major/Minor mismatch, Squashfs 1.0 filesystems " ++ "are unsupported\n"); ++ SERROR("Please recompile with " ++ "Squashfs 1.0 support enabled\n"); ++ return 0; ++ } ++ } else if (sblk->s_major == 2) { ++ if (!squashfs_2_0_supported(msblk)) { ++ SERROR("Major/Minor mismatch, Squashfs 2.0 filesystems " ++ "are unsupported\n"); ++ SERROR("Please recompile with " ++ "Squashfs 2.0 support enabled\n"); ++ return 0; ++ } ++ } else if(sblk->s_major != SQUASHFS_MAJOR || sblk->s_minor > ++ SQUASHFS_MINOR) { ++ SERROR("Major/Minor mismatch, trying to mount newer %d.%d " ++ "filesystem\n", sblk->s_major, sblk->s_minor); ++ SERROR("Please update your kernel\n"); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++ ++static int squashfs_fill_super(struct super_block *s, void *data, int silent) ++{ ++ struct squashfs_sb_info *msblk; ++ struct squashfs_super_block *sblk; ++ int i; ++ char b[BDEVNAME_SIZE]; ++ struct inode *root; ++ ++ TRACE("Entered squashfs_read_superblock\n"); ++ ++ if (!(s->s_fs_info = kmalloc(sizeof(struct squashfs_sb_info), ++ GFP_KERNEL))) { ++ ERROR("Failed to allocate superblock\n"); ++ goto failure; ++ } ++ memset(s->s_fs_info, 0, sizeof(struct squashfs_sb_info)); ++ msblk = s->s_fs_info; ++ sblk = &msblk->sblk; ++ ++ msblk->devblksize = sb_min_blocksize(s, BLOCK_SIZE); ++ msblk->devblksize_log2 = ffz(~msblk->devblksize); ++ ++ init_MUTEX(&msblk->read_data_mutex); ++ init_MUTEX(&msblk->read_page_mutex); ++ init_MUTEX(&msblk->block_cache_mutex); ++ init_MUTEX(&msblk->fragment_mutex); ++ init_MUTEX(&msblk->meta_index_mutex); ++ ++ init_waitqueue_head(&msblk->waitq); ++ init_waitqueue_head(&msblk->fragment_wait_queue); ++ ++ if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START, ++ sizeof(struct squashfs_super_block) | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++ SERROR("unable to read superblock\n"); ++ goto failed_mount; ++ } ++ ++ /* Check it is a SQUASHFS superblock */ ++ msblk->swap = 0; ++ if ((s->s_magic = sblk->s_magic) != SQUASHFS_MAGIC) { ++ if (sblk->s_magic == SQUASHFS_MAGIC_SWAP) { ++ struct squashfs_super_block ssblk; ++ ++ WARNING("Mounting a different endian SQUASHFS " ++ "filesystem on %s\n", bdevname(s->s_bdev, b)); ++ ++ SQUASHFS_SWAP_SUPER_BLOCK(&ssblk, sblk); ++ memcpy(sblk, &ssblk, sizeof(struct squashfs_super_block)); ++ msblk->swap = 1; ++ } else { ++ SERROR("Can't find a SQUASHFS superblock on %s\n", ++ bdevname(s->s_bdev, b)); ++ goto failed_mount; ++ } ++ } ++ ++ /* Check the MAJOR & MINOR versions */ ++ if(!supported_squashfs_filesystem(msblk, silent)) ++ goto failed_mount; ++ ++ TRACE("Found valid superblock on %s\n", bdevname(s->s_bdev, b)); ++ TRACE("Inodes are %scompressed\n", ++ SQUASHFS_UNCOMPRESSED_INODES ++ (sblk->flags) ? "un" : ""); ++ TRACE("Data is %scompressed\n", ++ SQUASHFS_UNCOMPRESSED_DATA(sblk->flags) ++ ? "un" : ""); ++ TRACE("Check data is %s present in the filesystem\n", ++ SQUASHFS_CHECK_DATA(sblk->flags) ? ++ "" : "not"); ++ TRACE("Filesystem size %lld bytes\n", sblk->bytes_used); ++ TRACE("Block size %d\n", sblk->block_size); ++ TRACE("Number of inodes %d\n", sblk->inodes); ++ if (sblk->s_major > 1) ++ TRACE("Number of fragments %d\n", sblk->fragments); ++ TRACE("Number of uids %d\n", sblk->no_uids); ++ TRACE("Number of gids %d\n", sblk->no_guids); ++ TRACE("sblk->inode_table_start %llx\n", sblk->inode_table_start); ++ TRACE("sblk->directory_table_start %llx\n", sblk->directory_table_start); ++ if (sblk->s_major > 1) ++ TRACE("sblk->fragment_table_start %llx\n", ++ sblk->fragment_table_start); ++ TRACE("sblk->uid_start %llx\n", sblk->uid_start); ++ ++ s->s_flags |= MS_RDONLY; ++ s->s_op = &squashfs_ops; ++ ++ /* Init inode_table block pointer array */ ++ if (!(msblk->block_cache = kmalloc(sizeof(struct squashfs_cache) * ++ SQUASHFS_CACHED_BLKS, GFP_KERNEL))) { ++ ERROR("Failed to allocate block cache\n"); ++ goto failed_mount; ++ } ++ ++ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) ++ msblk->block_cache[i].block = SQUASHFS_INVALID_BLK; ++ ++ msblk->next_cache = 0; ++ ++ /* Allocate read_data block */ ++ msblk->read_size = (sblk->block_size < SQUASHFS_METADATA_SIZE) ? ++ SQUASHFS_METADATA_SIZE : ++ sblk->block_size; ++ ++ if (!(msblk->read_data = kmalloc(msblk->read_size, GFP_KERNEL))) { ++ ERROR("Failed to allocate read_data block\n"); ++ goto failed_mount; ++ } ++ ++ /* Allocate read_page block */ ++ if (!(msblk->read_page = kmalloc(sblk->block_size, GFP_KERNEL))) { ++ ERROR("Failed to allocate read_page block\n"); ++ goto failed_mount; ++ } ++ ++ /* Allocate uid and gid tables */ ++ if (!(msblk->uid = kmalloc((sblk->no_uids + sblk->no_guids) * ++ sizeof(unsigned int), GFP_KERNEL))) { ++ ERROR("Failed to allocate uid/gid table\n"); ++ goto failed_mount; ++ } ++ msblk->guid = msblk->uid + sblk->no_uids; ++ ++ if (msblk->swap) { ++ unsigned int suid[sblk->no_uids + sblk->no_guids]; ++ ++ if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start, ++ ((sblk->no_uids + sblk->no_guids) * ++ sizeof(unsigned int)) | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++ ERROR("unable to read uid/gid table\n"); ++ goto failed_mount; ++ } ++ ++ SQUASHFS_SWAP_DATA(msblk->uid, suid, (sblk->no_uids + ++ sblk->no_guids), (sizeof(unsigned int) * 8)); ++ } else ++ if (!squashfs_read_data(s, (char *) msblk->uid, sblk->uid_start, ++ ((sblk->no_uids + sblk->no_guids) * ++ sizeof(unsigned int)) | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++ ERROR("unable to read uid/gid table\n"); ++ goto failed_mount; ++ } ++ ++ ++ if (sblk->s_major == 1 && squashfs_1_0_supported(msblk)) ++ goto allocate_root; ++ ++ if (!(msblk->fragment = kmalloc(sizeof(struct squashfs_fragment_cache) * ++ SQUASHFS_CACHED_FRAGMENTS, GFP_KERNEL))) { ++ ERROR("Failed to allocate fragment block cache\n"); ++ goto failed_mount; ++ } ++ ++ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) { ++ msblk->fragment[i].locked = 0; ++ msblk->fragment[i].block = SQUASHFS_INVALID_BLK; ++ msblk->fragment[i].data = NULL; ++ } ++ ++ msblk->next_fragment = 0; ++ ++ /* Allocate fragment index table */ ++ if (msblk->read_fragment_index_table(s) == 0) ++ goto failed_mount; ++ ++allocate_root: ++ if ((root = (msblk->iget)(s, sblk->root_inode)) == NULL) ++ goto failed_mount; ++ ++ if ((s->s_root = d_alloc_root(root)) == NULL) { ++ ERROR("Root inode create failed\n"); ++ iput(root); ++ goto failed_mount; ++ } ++ ++ TRACE("Leaving squashfs_read_super\n"); ++ return 0; ++ ++failed_mount: ++ kfree(msblk->fragment_index); ++ kfree(msblk->fragment); ++ kfree(msblk->uid); ++ kfree(msblk->read_page); ++ kfree(msblk->read_data); ++ kfree(msblk->block_cache); ++ kfree(msblk->fragment_index_2); ++ kfree(s->s_fs_info); ++ s->s_fs_info = NULL; ++ return -EINVAL; ++ ++failure: ++ return -ENOMEM; ++} ++ ++ ++static int squashfs_statfs(struct super_block *s, struct kstatfs *buf) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ TRACE("Entered squashfs_statfs\n"); ++ ++ buf->f_type = SQUASHFS_MAGIC; ++ buf->f_bsize = sblk->block_size; ++ buf->f_blocks = ((sblk->bytes_used - 1) >> sblk->block_log) + 1; ++ buf->f_bfree = buf->f_bavail = 0; ++ buf->f_files = sblk->inodes; ++ buf->f_ffree = 0; ++ buf->f_namelen = SQUASHFS_NAME_LEN; ++ ++ return 0; ++} ++ ++ ++static int squashfs_symlink_readpage(struct file *file, struct page *page) ++{ ++ struct inode *inode = page->mapping->host; ++ int index = page->index << PAGE_CACHE_SHIFT, length, bytes; ++ long long block = SQUASHFS_I(inode)->start_block; ++ int offset = SQUASHFS_I(inode)->offset; ++ void *pageaddr = kmap(page); ++ ++ TRACE("Entered squashfs_symlink_readpage, page index %ld, start block " ++ "%llx, offset %x\n", page->index, ++ SQUASHFS_I(inode)->start_block, ++ SQUASHFS_I(inode)->offset); ++ ++ for (length = 0; length < index; length += bytes) { ++ if (!(bytes = squashfs_get_cached_block(inode->i_sb, NULL, ++ block, offset, PAGE_CACHE_SIZE, &block, ++ &offset))) { ++ ERROR("Unable to read symbolic link [%llx:%x]\n", block, ++ offset); ++ goto skip_read; ++ } ++ } ++ ++ if (length != index) { ++ ERROR("(squashfs_symlink_readpage) length != index\n"); ++ bytes = 0; ++ goto skip_read; ++ } ++ ++ bytes = (i_size_read(inode) - length) > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE : ++ i_size_read(inode) - length; ++ ++ if (!(bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block, ++ offset, bytes, &block, &offset))) ++ ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset); ++ ++skip_read: ++ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); ++ kunmap(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ ++ return 0; ++} ++ ++ ++struct meta_index *locate_meta_index(struct inode *inode, int index, int offset) ++{ ++ struct meta_index *meta = NULL; ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ int i; ++ ++ down(&msblk->meta_index_mutex); ++ ++ TRACE("locate_meta_index: index %d, offset %d\n", index, offset); ++ ++ if(msblk->meta_index == NULL) ++ goto not_allocated; ++ ++ for (i = 0; i < SQUASHFS_META_NUMBER; i ++) ++ if (msblk->meta_index[i].inode_number == inode->i_ino && ++ msblk->meta_index[i].offset >= offset && ++ msblk->meta_index[i].offset <= index && ++ msblk->meta_index[i].locked == 0) { ++ TRACE("locate_meta_index: entry %d, offset %d\n", i, ++ msblk->meta_index[i].offset); ++ meta = &msblk->meta_index[i]; ++ offset = meta->offset; ++ } ++ ++ if (meta) ++ meta->locked = 1; ++ ++not_allocated: ++ up(&msblk->meta_index_mutex); ++ ++ return meta; ++} ++ ++ ++struct meta_index *empty_meta_index(struct inode *inode, int offset, int skip) ++{ ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ struct meta_index *meta = NULL; ++ int i; ++ ++ down(&msblk->meta_index_mutex); ++ ++ TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip); ++ ++ if(msblk->meta_index == NULL) { ++ if (!(msblk->meta_index = kmalloc(sizeof(struct meta_index) * ++ SQUASHFS_META_NUMBER, GFP_KERNEL))) { ++ ERROR("Failed to allocate meta_index\n"); ++ goto failed; ++ } ++ for(i = 0; i < SQUASHFS_META_NUMBER; i++) { ++ msblk->meta_index[i].inode_number = 0; ++ msblk->meta_index[i].locked = 0; ++ } ++ msblk->next_meta_index = 0; ++ } ++ ++ for(i = SQUASHFS_META_NUMBER; i && ++ msblk->meta_index[msblk->next_meta_index].locked; i --) ++ msblk->next_meta_index = (msblk->next_meta_index + 1) % ++ SQUASHFS_META_NUMBER; ++ ++ if(i == 0) { ++ TRACE("empty_meta_index: failed!\n"); ++ goto failed; ++ } ++ ++ TRACE("empty_meta_index: returned meta entry %d, %p\n", ++ msblk->next_meta_index, ++ &msblk->meta_index[msblk->next_meta_index]); ++ ++ meta = &msblk->meta_index[msblk->next_meta_index]; ++ msblk->next_meta_index = (msblk->next_meta_index + 1) % ++ SQUASHFS_META_NUMBER; ++ ++ meta->inode_number = inode->i_ino; ++ meta->offset = offset; ++ meta->skip = skip; ++ meta->entries = 0; ++ meta->locked = 1; ++ ++failed: ++ up(&msblk->meta_index_mutex); ++ return meta; ++} ++ ++ ++void release_meta_index(struct inode *inode, struct meta_index *meta) ++{ ++ meta->locked = 0; ++} ++ ++ ++static int read_block_index(struct super_block *s, int blocks, char *block_list, ++ long long *start_block, int *offset) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ unsigned int *block_listp; ++ int block = 0; ++ ++ if (msblk->swap) { ++ char sblock_list[blocks << 2]; ++ ++ if (!squashfs_get_cached_block(s, sblock_list, *start_block, ++ *offset, blocks << 2, start_block, offset)) { ++ ERROR("Unable to read block list [%llx:%x]\n", ++ *start_block, *offset); ++ goto failure; ++ } ++ SQUASHFS_SWAP_INTS(((unsigned int *)block_list), ++ ((unsigned int *)sblock_list), blocks); ++ } else ++ if (!squashfs_get_cached_block(s, block_list, *start_block, ++ *offset, blocks << 2, start_block, offset)) { ++ ERROR("Unable to read block list [%llx:%x]\n", ++ *start_block, *offset); ++ goto failure; ++ } ++ ++ for (block_listp = (unsigned int *) block_list; blocks; ++ block_listp++, blocks --) ++ block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp); ++ ++ return block; ++ ++failure: ++ return -1; ++} ++ ++ ++#define SIZE 256 ++ ++static inline int calculate_skip(int blocks) { ++ int skip = (blocks - 1) / ((SQUASHFS_SLOTS * SQUASHFS_META_ENTRIES + 1) * SQUASHFS_META_INDEXES); ++ return skip >= 7 ? 7 : skip + 1; ++} ++ ++ ++static int get_meta_index(struct inode *inode, int index, ++ long long *index_block, int *index_offset, ++ long long *data_block, char *block_list) ++{ ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int skip = calculate_skip(i_size_read(inode) >> sblk->block_log); ++ int offset = 0; ++ struct meta_index *meta; ++ struct meta_entry *meta_entry; ++ long long cur_index_block = SQUASHFS_I(inode)->u.s1.block_list_start; ++ int cur_offset = SQUASHFS_I(inode)->offset; ++ long long cur_data_block = SQUASHFS_I(inode)->start_block; ++ int i; ++ ++ index /= SQUASHFS_META_INDEXES * skip; ++ ++ while ( offset < index ) { ++ meta = locate_meta_index(inode, index, offset + 1); ++ ++ if (meta == NULL) { ++ if ((meta = empty_meta_index(inode, offset + 1, ++ skip)) == NULL) ++ goto all_done; ++ } else { ++ offset = index < meta->offset + meta->entries ? index : ++ meta->offset + meta->entries - 1; ++ meta_entry = &meta->meta_entry[offset - meta->offset]; ++ cur_index_block = meta_entry->index_block + sblk->inode_table_start; ++ cur_offset = meta_entry->offset; ++ cur_data_block = meta_entry->data_block; ++ TRACE("get_meta_index: offset %d, meta->offset %d, " ++ "meta->entries %d\n", offset, meta->offset, ++ meta->entries); ++ TRACE("get_meta_index: index_block 0x%llx, offset 0x%x" ++ " data_block 0x%llx\n", cur_index_block, ++ cur_offset, cur_data_block); ++ } ++ ++ for (i = meta->offset + meta->entries; i <= index && ++ i < meta->offset + SQUASHFS_META_ENTRIES; i++) { ++ int blocks = skip * SQUASHFS_META_INDEXES; ++ ++ while (blocks) { ++ int block = blocks > (SIZE >> 2) ? (SIZE >> 2) : ++ blocks; ++ int res = read_block_index(inode->i_sb, block, ++ block_list, &cur_index_block, ++ &cur_offset); ++ ++ if (res == -1) ++ goto failed; ++ ++ cur_data_block += res; ++ blocks -= block; ++ } ++ ++ meta_entry = &meta->meta_entry[i - meta->offset]; ++ meta_entry->index_block = cur_index_block - sblk->inode_table_start; ++ meta_entry->offset = cur_offset; ++ meta_entry->data_block = cur_data_block; ++ meta->entries ++; ++ offset ++; ++ } ++ ++ TRACE("get_meta_index: meta->offset %d, meta->entries %d\n", ++ meta->offset, meta->entries); ++ ++ release_meta_index(inode, meta); ++ } ++ ++all_done: ++ *index_block = cur_index_block; ++ *index_offset = cur_offset; ++ *data_block = cur_data_block; ++ ++ return offset * SQUASHFS_META_INDEXES * skip; ++ ++failed: ++ release_meta_index(inode, meta); ++ return -1; ++} ++ ++ ++static long long read_blocklist(struct inode *inode, int index, ++ int readahead_blks, char *block_list, ++ unsigned short **block_p, unsigned int *bsize) ++{ ++ long long block_ptr; ++ int offset; ++ long long block; ++ int res = get_meta_index(inode, index, &block_ptr, &offset, &block, ++ block_list); ++ ++ TRACE("read_blocklist: res %d, index %d, block_ptr 0x%llx, offset" ++ " 0x%x, block 0x%llx\n", res, index, block_ptr, offset, ++ block); ++ ++ if(res == -1) ++ goto failure; ++ ++ index -= res; ++ ++ while ( index ) { ++ int blocks = index > (SIZE >> 2) ? (SIZE >> 2) : index; ++ int res = read_block_index(inode->i_sb, blocks, block_list, ++ &block_ptr, &offset); ++ if (res == -1) ++ goto failure; ++ block += res; ++ index -= blocks; ++ } ++ ++ if (read_block_index(inode->i_sb, 1, block_list, ++ &block_ptr, &offset) == -1) ++ goto failure; ++ *bsize = *((unsigned int *) block_list); ++ ++ return block; ++ ++failure: ++ return 0; ++} ++ ++ ++static int squashfs_readpage(struct file *file, struct page *page) ++{ ++ struct inode *inode = page->mapping->host; ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ unsigned char block_list[SIZE]; ++ long long block; ++ unsigned int bsize, i = 0, bytes = 0, byte_offset = 0; ++ int index = page->index >> (sblk->block_log - PAGE_CACHE_SHIFT); ++ void *pageaddr; ++ struct squashfs_fragment_cache *fragment = NULL; ++ char *data_ptr = msblk->read_page; ++ ++ int mask = (1 << (sblk->block_log - PAGE_CACHE_SHIFT)) - 1; ++ int start_index = page->index & ~mask; ++ int end_index = start_index | mask; ++ ++ TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", ++ page->index, ++ SQUASHFS_I(inode)->start_block); ++ ++ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> ++ PAGE_CACHE_SHIFT)) ++ goto skip_read; ++ ++ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK ++ || index < (i_size_read(inode) >> ++ sblk->block_log)) { ++ if ((block = (msblk->read_blocklist)(inode, index, 1, ++ block_list, NULL, &bsize)) == 0) ++ goto skip_read; ++ ++ down(&msblk->read_page_mutex); ++ ++ if (!(bytes = squashfs_read_data(inode->i_sb, msblk->read_page, ++ block, bsize, NULL))) { ++ ERROR("Unable to read page, block %llx, size %x\n", block, ++ bsize); ++ up(&msblk->read_page_mutex); ++ goto skip_read; ++ } ++ } else { ++ if ((fragment = get_cached_fragment(inode->i_sb, ++ SQUASHFS_I(inode)-> ++ u.s1.fragment_start_block, ++ SQUASHFS_I(inode)->u.s1.fragment_size)) ++ == NULL) { ++ ERROR("Unable to read page, block %llx, size %x\n", ++ SQUASHFS_I(inode)-> ++ u.s1.fragment_start_block, ++ (int) SQUASHFS_I(inode)-> ++ u.s1.fragment_size); ++ goto skip_read; ++ } ++ bytes = SQUASHFS_I(inode)->u.s1.fragment_offset + ++ (i_size_read(inode) & (sblk->block_size ++ - 1)); ++ byte_offset = SQUASHFS_I(inode)->u.s1.fragment_offset; ++ data_ptr = fragment->data; ++ } ++ ++ for (i = start_index; i <= end_index && byte_offset < bytes; ++ i++, byte_offset += PAGE_CACHE_SIZE) { ++ struct page *push_page; ++ int available_bytes = (bytes - byte_offset) > PAGE_CACHE_SIZE ? ++ PAGE_CACHE_SIZE : bytes - byte_offset; ++ ++ TRACE("bytes %d, i %d, byte_offset %d, available_bytes %d\n", ++ bytes, i, byte_offset, available_bytes); ++ ++ if (i == page->index) { ++ pageaddr = kmap_atomic(page, KM_USER0); ++ memcpy(pageaddr, data_ptr + byte_offset, ++ available_bytes); ++ memset(pageaddr + available_bytes, 0, ++ PAGE_CACHE_SIZE - available_bytes); ++ kunmap_atomic(pageaddr, KM_USER0); ++ flush_dcache_page(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ } else if ((push_page = ++ grab_cache_page_nowait(page->mapping, i))) { ++ pageaddr = kmap_atomic(push_page, KM_USER0); ++ ++ memcpy(pageaddr, data_ptr + byte_offset, ++ available_bytes); ++ memset(pageaddr + available_bytes, 0, ++ PAGE_CACHE_SIZE - available_bytes); ++ kunmap_atomic(pageaddr, KM_USER0); ++ flush_dcache_page(push_page); ++ SetPageUptodate(push_page); ++ unlock_page(push_page); ++ page_cache_release(push_page); ++ } ++ } ++ ++ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK ++ || index < (i_size_read(inode) >> ++ sblk->block_log)) ++ up(&msblk->read_page_mutex); ++ else ++ release_cached_fragment(msblk, fragment); ++ ++ return 0; ++ ++skip_read: ++ pageaddr = kmap_atomic(page, KM_USER0); ++ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); ++ kunmap_atomic(pageaddr, KM_USER0); ++ flush_dcache_page(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ ++ return 0; ++} ++ ++ ++static int squashfs_readpage4K(struct file *file, struct page *page) ++{ ++ struct inode *inode = page->mapping->host; ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ unsigned char block_list[SIZE]; ++ long long block; ++ unsigned int bsize, bytes = 0; ++ void *pageaddr; ++ ++ TRACE("Entered squashfs_readpage4K, page index %lx, start block %llx\n", ++ page->index, ++ SQUASHFS_I(inode)->start_block); ++ ++ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> ++ PAGE_CACHE_SHIFT)) { ++ pageaddr = kmap_atomic(page, KM_USER0); ++ goto skip_read; ++ } ++ ++ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK ++ || page->index < (i_size_read(inode) >> ++ sblk->block_log)) { ++ block = (msblk->read_blocklist)(inode, page->index, 1, ++ block_list, NULL, &bsize); ++ ++ down(&msblk->read_page_mutex); ++ bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block, ++ bsize, NULL); ++ pageaddr = kmap_atomic(page, KM_USER0); ++ if (bytes) ++ memcpy(pageaddr, msblk->read_page, bytes); ++ else ++ ERROR("Unable to read page, block %llx, size %x\n", ++ block, bsize); ++ up(&msblk->read_page_mutex); ++ } else { ++ struct squashfs_fragment_cache *fragment = ++ get_cached_fragment(inode->i_sb, ++ SQUASHFS_I(inode)-> ++ u.s1.fragment_start_block, ++ SQUASHFS_I(inode)-> u.s1.fragment_size); ++ pageaddr = kmap_atomic(page, KM_USER0); ++ if (fragment) { ++ bytes = i_size_read(inode) & (sblk->block_size - 1); ++ memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)-> ++ u.s1.fragment_offset, bytes); ++ release_cached_fragment(msblk, fragment); ++ } else ++ ERROR("Unable to read page, block %llx, size %x\n", ++ SQUASHFS_I(inode)-> ++ u.s1.fragment_start_block, (int) ++ SQUASHFS_I(inode)-> u.s1.fragment_size); ++ } ++ ++skip_read: ++ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); ++ kunmap_atomic(pageaddr, KM_USER0); ++ flush_dcache_page(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ ++ return 0; ++} ++ ++ ++static int get_dir_index_using_offset(struct super_block *s, long long ++ *next_block, unsigned int *next_offset, ++ long long index_start, ++ unsigned int index_offset, int i_count, ++ long long f_pos) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int i, length = 0; ++ struct squashfs_dir_index index; ++ ++ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n", ++ i_count, (unsigned int) f_pos); ++ ++ f_pos =- 3; ++ if (f_pos == 0) ++ goto finish; ++ ++ for (i = 0; i < i_count; i++) { ++ if (msblk->swap) { ++ struct squashfs_dir_index sindex; ++ squashfs_get_cached_block(s, (char *) &sindex, ++ index_start, index_offset, ++ sizeof(sindex), &index_start, ++ &index_offset); ++ SQUASHFS_SWAP_DIR_INDEX(&index, &sindex); ++ } else ++ squashfs_get_cached_block(s, (char *) &index, ++ index_start, index_offset, ++ sizeof(index), &index_start, ++ &index_offset); ++ ++ if (index.index > f_pos) ++ break; ++ ++ squashfs_get_cached_block(s, NULL, index_start, index_offset, ++ index.size + 1, &index_start, ++ &index_offset); ++ ++ length = index.index; ++ *next_block = index.start_block + sblk->directory_table_start; ++ } ++ ++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++ ++finish: ++ return length + 3; ++} ++ ++ ++static int get_dir_index_using_name(struct super_block *s, long long ++ *next_block, unsigned int *next_offset, ++ long long index_start, ++ unsigned int index_offset, int i_count, ++ const char *name, int size) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int i, length = 0; ++ char buffer[sizeof(struct squashfs_dir_index) + SQUASHFS_NAME_LEN + 1]; ++ struct squashfs_dir_index *index = (struct squashfs_dir_index *) buffer; ++ char str[SQUASHFS_NAME_LEN + 1]; ++ ++ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count); ++ ++ strncpy(str, name, size); ++ str[size] = '\0'; ++ ++ for (i = 0; i < i_count; i++) { ++ if (msblk->swap) { ++ struct squashfs_dir_index sindex; ++ squashfs_get_cached_block(s, (char *) &sindex, ++ index_start, index_offset, ++ sizeof(sindex), &index_start, ++ &index_offset); ++ SQUASHFS_SWAP_DIR_INDEX(index, &sindex); ++ } else ++ squashfs_get_cached_block(s, (char *) index, ++ index_start, index_offset, ++ sizeof(struct squashfs_dir_index), ++ &index_start, &index_offset); ++ ++ squashfs_get_cached_block(s, index->name, index_start, ++ index_offset, index->size + 1, ++ &index_start, &index_offset); ++ ++ index->name[index->size + 1] = '\0'; ++ ++ if (strcmp(index->name, str) > 0) ++ break; ++ ++ length = index->index; ++ *next_block = index->start_block + sblk->directory_table_start; ++ } ++ ++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++ return length + 3; ++} ++ ++ ++static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir) ++{ ++ struct inode *i = file->f_dentry->d_inode; ++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long next_block = SQUASHFS_I(i)->start_block + ++ sblk->directory_table_start; ++ int next_offset = SQUASHFS_I(i)->offset, length = 0, dirs_read = 0, ++ dir_count; ++ struct squashfs_dir_header dirh; ++ char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1]; ++ struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer; ++ ++ TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset); ++ ++ while(file->f_pos < 3) { ++ char *name; ++ int size, i_ino; ++ ++ if(file->f_pos == 0) { ++ name = "."; ++ size = 1; ++ i_ino = i->i_ino; ++ } else { ++ name = ".."; ++ size = 2; ++ i_ino = SQUASHFS_I(i)->u.s2.parent_inode; ++ } ++ TRACE("Calling filldir(%x, %s, %d, %d, %d, %d)\n", ++ (unsigned int) dirent, name, size, (int) ++ file->f_pos, i_ino, ++ squashfs_filetype_table[1]); ++ ++ if (filldir(dirent, name, size, ++ file->f_pos, i_ino, ++ squashfs_filetype_table[1]) < 0) { ++ TRACE("Filldir returned less than 0\n"); ++ goto finish; ++ } ++ file->f_pos += size; ++ dirs_read++; ++ } ++ ++ length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_start, ++ SQUASHFS_I(i)->u.s2.directory_index_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_count, ++ file->f_pos); ++ ++ while (length < i_size_read(i)) { ++ /* read directory header */ ++ if (msblk->swap) { ++ struct squashfs_dir_header sdirh; ++ ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, ++ next_block, next_offset, sizeof(sdirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdirh); ++ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, ++ next_block, next_offset, sizeof(dirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(dirh); ++ } ++ ++ dir_count = dirh.count + 1; ++ while (dir_count--) { ++ if (msblk->swap) { ++ struct squashfs_dir_entry sdire; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ &sdire, next_block, next_offset, ++ sizeof(sdire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdire); ++ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ dire, next_block, next_offset, ++ sizeof(*dire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(*dire); ++ } ++ ++ if (!squashfs_get_cached_block(i->i_sb, dire->name, ++ next_block, next_offset, ++ dire->size + 1, &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += dire->size + 1; ++ ++ if (file->f_pos >= length) ++ continue; ++ ++ dire->name[dire->size + 1] = '\0'; ++ ++ TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d, %d)\n", ++ (unsigned int) dirent, dire->name, ++ dire->size + 1, (int) file->f_pos, ++ dirh.start_block, dire->offset, ++ dirh.inode_number + dire->inode_number, ++ squashfs_filetype_table[dire->type]); ++ ++ if (filldir(dirent, dire->name, dire->size + 1, ++ file->f_pos, ++ dirh.inode_number + dire->inode_number, ++ squashfs_filetype_table[dire->type]) ++ < 0) { ++ TRACE("Filldir returned less than 0\n"); ++ goto finish; ++ } ++ file->f_pos = length; ++ dirs_read++; ++ } ++ } ++ ++finish: ++ return dirs_read; ++ ++failed_read: ++ ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++ next_offset); ++ return 0; ++} ++ ++ ++static struct dentry *squashfs_lookup(struct inode *i, struct dentry *dentry, ++ struct nameidata *nd) ++{ ++ const unsigned char *name = dentry->d_name.name; ++ int len = dentry->d_name.len; ++ struct inode *inode = NULL; ++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long next_block = SQUASHFS_I(i)->start_block + ++ sblk->directory_table_start; ++ int next_offset = SQUASHFS_I(i)->offset, length = 0, ++ dir_count; ++ struct squashfs_dir_header dirh; ++ char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN]; ++ struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer; ++ ++ TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset); ++ ++ if (len > SQUASHFS_NAME_LEN) ++ goto exit_loop; ++ ++ length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_start, ++ SQUASHFS_I(i)->u.s2.directory_index_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_count, name, ++ len); ++ ++ while (length < i_size_read(i)) { ++ /* read directory header */ ++ if (msblk->swap) { ++ struct squashfs_dir_header sdirh; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, ++ next_block, next_offset, sizeof(sdirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdirh); ++ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, ++ next_block, next_offset, sizeof(dirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(dirh); ++ } ++ ++ dir_count = dirh.count + 1; ++ while (dir_count--) { ++ if (msblk->swap) { ++ struct squashfs_dir_entry sdire; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ &sdire, next_block,next_offset, ++ sizeof(sdire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdire); ++ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ dire, next_block,next_offset, ++ sizeof(*dire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(*dire); ++ } ++ ++ if (!squashfs_get_cached_block(i->i_sb, dire->name, ++ next_block, next_offset, dire->size + 1, ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += dire->size + 1; ++ ++ if (name[0] < dire->name[0]) ++ goto exit_loop; ++ ++ if ((len == dire->size + 1) && !strncmp(name, ++ dire->name, len)) { ++ squashfs_inode_t ino = ++ SQUASHFS_MKINODE(dirh.start_block, ++ dire->offset); ++ ++ TRACE("calling squashfs_iget for directory " ++ "entry %s, inode %x:%x, %d\n", name, ++ dirh.start_block, dire->offset, ++ dirh.inode_number + dire->inode_number); ++ ++ inode = (msblk->iget)(i->i_sb, ino); ++ ++ goto exit_loop; ++ } ++ } ++ } ++ ++exit_loop: ++ d_add(dentry, inode); ++ return ERR_PTR(0); ++ ++failed_read: ++ ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++ next_offset); ++ goto exit_loop; ++} ++ ++ ++static void squashfs_put_super(struct super_block *s) ++{ ++ int i; ++ ++ if (s->s_fs_info) { ++ struct squashfs_sb_info *sbi = s->s_fs_info; ++ if (sbi->block_cache) ++ for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) ++ if (sbi->block_cache[i].block != ++ SQUASHFS_INVALID_BLK) ++ kfree(sbi->block_cache[i].data); ++ if (sbi->fragment) ++ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) ++ SQUASHFS_FREE(sbi->fragment[i].data); ++ kfree(sbi->fragment); ++ kfree(sbi->block_cache); ++ kfree(sbi->read_data); ++ kfree(sbi->read_page); ++ kfree(sbi->uid); ++ kfree(sbi->fragment_index); ++ kfree(sbi->fragment_index_2); ++ kfree(sbi->meta_index); ++ kfree(s->s_fs_info); ++ s->s_fs_info = NULL; ++ } ++} ++ ++ ++static struct super_block *squashfs_get_sb(struct file_system_type *fs_type, ++ int flags, const char *dev_name, void *data) ++{ ++ return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super); ++} ++ ++ ++static int __init init_squashfs_fs(void) ++{ ++ int err = init_inodecache(); ++ if (err) ++ goto out; ++ ++ printk(KERN_INFO "squashfs: version 3.0 (2006/03/15) " ++ "Phillip Lougher\n"); ++ ++ if (!(stream.workspace = vmalloc(zlib_inflate_workspacesize()))) { ++ ERROR("Failed to allocate zlib workspace\n"); ++ destroy_inodecache(); ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ if ((err = register_filesystem(&squashfs_fs_type))) { ++ vfree(stream.workspace); ++ destroy_inodecache(); ++ } ++ ++out: ++ return err; ++} ++ ++ ++static void __exit exit_squashfs_fs(void) ++{ ++ vfree(stream.workspace); ++ unregister_filesystem(&squashfs_fs_type); ++ destroy_inodecache(); ++} ++ ++ ++static kmem_cache_t * squashfs_inode_cachep; ++ ++ ++static struct inode *squashfs_alloc_inode(struct super_block *sb) ++{ ++ struct squashfs_inode_info *ei; ++ ei = kmem_cache_alloc(squashfs_inode_cachep, SLAB_KERNEL); ++ if (!ei) ++ return NULL; ++ return &ei->vfs_inode; ++} ++ ++ ++static void squashfs_destroy_inode(struct inode *inode) ++{ ++ kmem_cache_free(squashfs_inode_cachep, SQUASHFS_I(inode)); ++} ++ ++ ++static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) ++{ ++ struct squashfs_inode_info *ei = foo; ++ ++ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == ++ SLAB_CTOR_CONSTRUCTOR) ++ inode_init_once(&ei->vfs_inode); ++} ++ ++ ++static int __init init_inodecache(void) ++{ ++ squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache", ++ sizeof(struct squashfs_inode_info), ++ 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, ++ init_once, NULL); ++ if (squashfs_inode_cachep == NULL) ++ return -ENOMEM; ++ return 0; ++} ++ ++ ++static void destroy_inodecache(void) ++{ ++ if (kmem_cache_destroy(squashfs_inode_cachep)) ++ printk(KERN_INFO "squashfs_inode_cache: not all structures " ++ "were freed\n"); ++} ++ ++ ++module_init(init_squashfs_fs); ++module_exit(exit_squashfs_fs); ++MODULE_DESCRIPTION("squashfs, a compressed read-only filesystem"); ++MODULE_AUTHOR("Phillip Lougher <phillip@lougher.org.uk>"); ++MODULE_LICENSE("GPL"); +diff --new-file -urp linux-2.6.15/fs/squashfs/Makefile linux-2.6.15-squashfs3.0/fs/squashfs/Makefile +--- linux-2.6.15/fs/squashfs/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.15-squashfs3.0/fs/squashfs/Makefile 2006-03-07 21:12:37.000000000 +0000 +@@ -0,0 +1,7 @@ ++# ++# Makefile for the linux squashfs routines. ++# ++ ++obj-$(CONFIG_SQUASHFS) += squashfs.o ++squashfs-y += inode.o ++squashfs-y += squashfs2_0.o +diff --new-file -urp linux-2.6.15/fs/squashfs/squashfs2_0.c linux-2.6.15-squashfs3.0/fs/squashfs/squashfs2_0.c +--- linux-2.6.15/fs/squashfs/squashfs2_0.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.15-squashfs3.0/fs/squashfs/squashfs2_0.c 2006-03-07 21:12:37.000000000 +0000 +@@ -0,0 +1,758 @@ ++/* ++ * Squashfs - a compressed read only filesystem for Linux ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher <phillip@lougher.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs2_0.c ++ */ ++ ++#include <linux/types.h> ++#include <linux/squashfs_fs.h> ++#include <linux/module.h> ++#include <linux/errno.h> ++#include <linux/slab.h> ++#include <linux/fs.h> ++#include <linux/smp_lock.h> ++#include <linux/slab.h> ++#include <linux/squashfs_fs_sb.h> ++#include <linux/squashfs_fs_i.h> ++#include <linux/buffer_head.h> ++#include <linux/vfs.h> ++#include <linux/init.h> ++#include <linux/dcache.h> ++#include <linux/wait.h> ++#include <linux/zlib.h> ++#include <linux/blkdev.h> ++#include <linux/vmalloc.h> ++#include <asm/uaccess.h> ++#include <asm/semaphore.h> ++ ++#include "squashfs.h" ++static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir); ++static struct dentry *squashfs_lookup_2(struct inode *, struct dentry *, ++ struct nameidata *); ++ ++static struct file_operations squashfs_dir_ops_2 = { ++ .read = generic_read_dir, ++ .readdir = squashfs_readdir_2 ++}; ++ ++static struct inode_operations squashfs_dir_inode_ops_2 = { ++ .lookup = squashfs_lookup_2 ++}; ++ ++static unsigned char squashfs_filetype_table[] = { ++ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK ++}; ++ ++static int read_fragment_index_table_2(struct super_block *s) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ if (!(msblk->fragment_index_2 = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES_2 ++ (sblk->fragments), GFP_KERNEL))) { ++ ERROR("Failed to allocate uid/gid table\n"); ++ return 0; ++ } ++ ++ if (SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments) && ++ !squashfs_read_data(s, (char *) ++ msblk->fragment_index_2, ++ sblk->fragment_table_start, ++ SQUASHFS_FRAGMENT_INDEX_BYTES_2 ++ (sblk->fragments) | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) { ++ ERROR("unable to read fragment index table\n"); ++ return 0; ++ } ++ ++ if (msblk->swap) { ++ int i; ++ unsigned int fragment; ++ ++ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES_2(sblk->fragments); ++ i++) { ++ SQUASHFS_SWAP_FRAGMENT_INDEXES_2((&fragment), ++ &msblk->fragment_index_2[i], 1); ++ msblk->fragment_index_2[i] = fragment; ++ } ++ } ++ ++ return 1; ++} ++ ++ ++static int get_fragment_location_2(struct super_block *s, unsigned int fragment, ++ long long *fragment_start_block, ++ unsigned int *fragment_size) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ long long start_block = ++ msblk->fragment_index_2[SQUASHFS_FRAGMENT_INDEX_2(fragment)]; ++ int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET_2(fragment); ++ struct squashfs_fragment_entry_2 fragment_entry; ++ ++ if (msblk->swap) { ++ struct squashfs_fragment_entry_2 sfragment_entry; ++ ++ if (!squashfs_get_cached_block(s, (char *) &sfragment_entry, ++ start_block, offset, ++ sizeof(sfragment_entry), &start_block, ++ &offset)) ++ goto out; ++ SQUASHFS_SWAP_FRAGMENT_ENTRY_2(&fragment_entry, &sfragment_entry); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) &fragment_entry, ++ start_block, offset, ++ sizeof(fragment_entry), &start_block, ++ &offset)) ++ goto out; ++ ++ *fragment_start_block = fragment_entry.start_block; ++ *fragment_size = fragment_entry.size; ++ ++ return 1; ++ ++out: ++ return 0; ++} ++ ++ ++static struct inode *squashfs_new_inode(struct super_block *s, ++ struct squashfs_base_inode_header_2 *inodeb, unsigned int ino) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ struct inode *i = new_inode(s); ++ ++ if (i) { ++ i->i_ino = ino; ++ i->i_mtime.tv_sec = sblk->mkfs_time; ++ i->i_atime.tv_sec = sblk->mkfs_time; ++ i->i_ctime.tv_sec = sblk->mkfs_time; ++ i->i_uid = msblk->uid[inodeb->uid]; ++ i->i_mode = inodeb->mode; ++ i->i_nlink = 1; ++ i->i_size = 0; ++ if (inodeb->guid == SQUASHFS_GUIDS) ++ i->i_gid = i->i_uid; ++ else ++ i->i_gid = msblk->guid[inodeb->guid]; ++ } ++ ++ return i; ++} ++ ++ ++static struct inode *squashfs_iget_2(struct super_block *s, squashfs_inode_t inode) ++{ ++ struct inode *i; ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ unsigned int block = SQUASHFS_INODE_BLK(inode) + ++ sblk->inode_table_start; ++ unsigned int offset = SQUASHFS_INODE_OFFSET(inode); ++ unsigned int ino = SQUASHFS_MK_VFS_INODE(block ++ - sblk->inode_table_start, offset); ++ long long next_block; ++ unsigned int next_offset; ++ union squashfs_inode_header_2 id, sid; ++ struct squashfs_base_inode_header_2 *inodeb = &id.base, ++ *sinodeb = &sid.base; ++ ++ TRACE("Entered squashfs_iget\n"); ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) sinodeb, block, ++ offset, sizeof(*sinodeb), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_BASE_INODE_HEADER_2(inodeb, sinodeb, ++ sizeof(*sinodeb)); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) inodeb, block, ++ offset, sizeof(*inodeb), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ switch(inodeb->inode_type) { ++ case SQUASHFS_FILE_TYPE: { ++ struct squashfs_reg_inode_header_2 *inodep = &id.reg; ++ struct squashfs_reg_inode_header_2 *sinodep = &sid.reg; ++ long long frag_blk; ++ unsigned int frag_size; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_REG_INODE_HEADER_2(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ frag_blk = SQUASHFS_INVALID_BLK; ++ if (inodep->fragment != SQUASHFS_INVALID_FRAG && ++ !get_fragment_location_2(s, ++ inodep->fragment, &frag_blk, &frag_size)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++ goto failed_read1; ++ ++ i->i_size = inodep->file_size; ++ i->i_fop = &generic_ro_fops; ++ i->i_mode |= S_IFREG; ++ i->i_mtime.tv_sec = inodep->mtime; ++ i->i_atime.tv_sec = inodep->mtime; ++ i->i_ctime.tv_sec = inodep->mtime; ++ i->i_blocks = ((i->i_size - 1) >> 9) + 1; ++ i->i_blksize = PAGE_CACHE_SIZE; ++ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; ++ SQUASHFS_I(i)->u.s1.fragment_size = frag_size; ++ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->u.s1.block_list_start = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ if (sblk->block_size > 4096) ++ i->i_data.a_ops = &squashfs_aops; ++ else ++ i->i_data.a_ops = &squashfs_aops_4K; ++ ++ TRACE("File inode %x:%x, start_block %x, " ++ "block_list_start %llx, offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, next_block, ++ next_offset); ++ break; ++ } ++ case SQUASHFS_DIR_TYPE: { ++ struct squashfs_dir_inode_header_2 *inodep = &id.dir; ++ struct squashfs_dir_inode_header_2 *sinodep = &sid.dir; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_DIR_INODE_HEADER_2(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++ goto failed_read1; ++ ++ i->i_size = inodep->file_size; ++ i->i_op = &squashfs_dir_inode_ops_2; ++ i->i_fop = &squashfs_dir_ops_2; ++ i->i_mode |= S_IFDIR; ++ i->i_mtime.tv_sec = inodep->mtime; ++ i->i_atime.tv_sec = inodep->mtime; ++ i->i_ctime.tv_sec = inodep->mtime; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->offset = inodep->offset; ++ SQUASHFS_I(i)->u.s2.directory_index_count = 0; ++ SQUASHFS_I(i)->u.s2.parent_inode = 0; ++ ++ TRACE("Directory inode %x:%x, start_block %x, offset " ++ "%x\n", SQUASHFS_INODE_BLK(inode), ++ offset, inodep->start_block, ++ inodep->offset); ++ break; ++ } ++ case SQUASHFS_LDIR_TYPE: { ++ struct squashfs_ldir_inode_header_2 *inodep = &id.ldir; ++ struct squashfs_ldir_inode_header_2 *sinodep = &sid.ldir; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_LDIR_INODE_HEADER_2(inodep, ++ sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++ goto failed_read1; ++ ++ i->i_size = inodep->file_size; ++ i->i_op = &squashfs_dir_inode_ops_2; ++ i->i_fop = &squashfs_dir_ops_2; ++ i->i_mode |= S_IFDIR; ++ i->i_mtime.tv_sec = inodep->mtime; ++ i->i_atime.tv_sec = inodep->mtime; ++ i->i_ctime.tv_sec = inodep->mtime; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->offset = inodep->offset; ++ SQUASHFS_I(i)->u.s2.directory_index_start = next_block; ++ SQUASHFS_I(i)->u.s2.directory_index_offset = ++ next_offset; ++ SQUASHFS_I(i)->u.s2.directory_index_count = ++ inodep->i_count; ++ SQUASHFS_I(i)->u.s2.parent_inode = 0; ++ ++ TRACE("Long directory inode %x:%x, start_block %x, " ++ "offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, inodep->offset); ++ break; ++ } ++ case SQUASHFS_SYMLINK_TYPE: { ++ struct squashfs_symlink_inode_header_2 *inodep = ++ &id.symlink; ++ struct squashfs_symlink_inode_header_2 *sinodep = ++ &sid.symlink; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep, ++ sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++ goto failed_read1; ++ ++ i->i_size = inodep->symlink_size; ++ i->i_op = &page_symlink_inode_operations; ++ i->i_data.a_ops = &squashfs_symlink_aops; ++ i->i_mode |= S_IFLNK; ++ SQUASHFS_I(i)->start_block = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ ++ TRACE("Symbolic link inode %x:%x, start_block %llx, " ++ "offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ next_block, next_offset); ++ break; ++ } ++ case SQUASHFS_BLKDEV_TYPE: ++ case SQUASHFS_CHRDEV_TYPE: { ++ struct squashfs_dev_inode_header_2 *inodep = &id.dev; ++ struct squashfs_dev_inode_header_2 *sinodep = &sid.dev; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++ goto failed_read1; ++ ++ i->i_mode |= (inodeb->inode_type == ++ SQUASHFS_CHRDEV_TYPE) ? S_IFCHR : ++ S_IFBLK; ++ init_special_inode(i, i->i_mode, ++ old_decode_dev(inodep->rdev)); ++ ++ TRACE("Device inode %x:%x, rdev %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->rdev); ++ break; ++ } ++ case SQUASHFS_FIFO_TYPE: ++ case SQUASHFS_SOCKET_TYPE: { ++ if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL) ++ goto failed_read1; ++ ++ i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE) ++ ? S_IFIFO : S_IFSOCK; ++ init_special_inode(i, i->i_mode, 0); ++ break; ++ } ++ default: ++ ERROR("Unknown inode type %d in squashfs_iget!\n", ++ inodeb->inode_type); ++ goto failed_read1; ++ } ++ ++ insert_inode_hash(i); ++ return i; ++ ++failed_read: ++ ERROR("Unable to read inode [%x:%x]\n", block, offset); ++ ++failed_read1: ++ return NULL; ++} ++ ++ ++static int get_dir_index_using_offset(struct super_block *s, long long ++ *next_block, unsigned int *next_offset, ++ long long index_start, ++ unsigned int index_offset, int i_count, ++ long long f_pos) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int i, length = 0; ++ struct squashfs_dir_index_2 index; ++ ++ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n", ++ i_count, (unsigned int) f_pos); ++ ++ if (f_pos == 0) ++ goto finish; ++ ++ for (i = 0; i < i_count; i++) { ++ if (msblk->swap) { ++ struct squashfs_dir_index_2 sindex; ++ squashfs_get_cached_block(s, (char *) &sindex, ++ index_start, index_offset, ++ sizeof(sindex), &index_start, ++ &index_offset); ++ SQUASHFS_SWAP_DIR_INDEX_2(&index, &sindex); ++ } else ++ squashfs_get_cached_block(s, (char *) &index, ++ index_start, index_offset, ++ sizeof(index), &index_start, ++ &index_offset); ++ ++ if (index.index > f_pos) ++ break; ++ ++ squashfs_get_cached_block(s, NULL, index_start, index_offset, ++ index.size + 1, &index_start, ++ &index_offset); ++ ++ length = index.index; ++ *next_block = index.start_block + sblk->directory_table_start; ++ } ++ ++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++ ++finish: ++ return length; ++} ++ ++ ++static int get_dir_index_using_name(struct super_block *s, long long ++ *next_block, unsigned int *next_offset, ++ long long index_start, ++ unsigned int index_offset, int i_count, ++ const char *name, int size) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int i, length = 0; ++ char buffer[sizeof(struct squashfs_dir_index_2) + SQUASHFS_NAME_LEN + 1]; ++ struct squashfs_dir_index_2 *index = (struct squashfs_dir_index_2 *) buffer; ++ char str[SQUASHFS_NAME_LEN + 1]; ++ ++ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count); ++ ++ strncpy(str, name, size); ++ str[size] = '\0'; ++ ++ for (i = 0; i < i_count; i++) { ++ if (msblk->swap) { ++ struct squashfs_dir_index_2 sindex; ++ squashfs_get_cached_block(s, (char *) &sindex, ++ index_start, index_offset, ++ sizeof(sindex), &index_start, ++ &index_offset); ++ SQUASHFS_SWAP_DIR_INDEX_2(index, &sindex); ++ } else ++ squashfs_get_cached_block(s, (char *) index, ++ index_start, index_offset, ++ sizeof(struct squashfs_dir_index_2), ++ &index_start, &index_offset); ++ ++ squashfs_get_cached_block(s, index->name, index_start, ++ index_offset, index->size + 1, ++ &index_start, &index_offset); ++ ++ index->name[index->size + 1] = '\0'; ++ ++ if (strcmp(index->name, str) > 0) ++ break; ++ ++ length = index->index; ++ *next_block = index->start_block + sblk->directory_table_start; ++ } ++ ++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++ return length; ++} ++ ++ ++static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir) ++{ ++ struct inode *i = file->f_dentry->d_inode; ++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long next_block = SQUASHFS_I(i)->start_block + ++ sblk->directory_table_start; ++ int next_offset = SQUASHFS_I(i)->offset, length = 0, dirs_read = 0, ++ dir_count; ++ struct squashfs_dir_header_2 dirh; ++ char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1]; ++ struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer; ++ ++ TRACE("Entered squashfs_readdir_2 [%llx:%x]\n", next_block, next_offset); ++ ++ length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_start, ++ SQUASHFS_I(i)->u.s2.directory_index_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_count, ++ file->f_pos); ++ ++ while (length < i_size_read(i)) { ++ /* read directory header */ ++ if (msblk->swap) { ++ struct squashfs_dir_header_2 sdirh; ++ ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, ++ next_block, next_offset, sizeof(sdirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdirh); ++ SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, ++ next_block, next_offset, sizeof(dirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(dirh); ++ } ++ ++ dir_count = dirh.count + 1; ++ while (dir_count--) { ++ if (msblk->swap) { ++ struct squashfs_dir_entry_2 sdire; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ &sdire, next_block, next_offset, ++ sizeof(sdire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdire); ++ SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ dire, next_block, next_offset, ++ sizeof(*dire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(*dire); ++ } ++ ++ if (!squashfs_get_cached_block(i->i_sb, dire->name, ++ next_block, next_offset, ++ dire->size + 1, &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += dire->size + 1; ++ ++ if (file->f_pos >= length) ++ continue; ++ ++ dire->name[dire->size + 1] = '\0'; ++ ++ TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n", ++ (unsigned int) dirent, dire->name, ++ dire->size + 1, (int) file->f_pos, ++ dirh.start_block, dire->offset, ++ squashfs_filetype_table[dire->type]); ++ ++ if (filldir(dirent, dire->name, dire->size + 1, ++ file->f_pos, SQUASHFS_MK_VFS_INODE( ++ dirh.start_block, dire->offset), ++ squashfs_filetype_table[dire->type]) ++ < 0) { ++ TRACE("Filldir returned less than 0\n"); ++ goto finish; ++ } ++ file->f_pos = length; ++ dirs_read++; ++ } ++ } ++ ++finish: ++ return dirs_read; ++ ++failed_read: ++ ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++ next_offset); ++ return 0; ++} ++ ++ ++static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry, ++ struct nameidata *nd) ++{ ++ const unsigned char *name = dentry->d_name.name; ++ int len = dentry->d_name.len; ++ struct inode *inode = NULL; ++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long next_block = SQUASHFS_I(i)->start_block + ++ sblk->directory_table_start; ++ int next_offset = SQUASHFS_I(i)->offset, length = 0, ++ dir_count; ++ struct squashfs_dir_header_2 dirh; ++ char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN]; ++ struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer; ++ int sorted = sblk->s_major == 2 && sblk->s_minor >= 1; ++ ++ TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset); ++ ++ if (len > SQUASHFS_NAME_LEN) ++ goto exit_loop; ++ ++ length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_start, ++ SQUASHFS_I(i)->u.s2.directory_index_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_count, name, ++ len); ++ ++ while (length < i_size_read(i)) { ++ /* read directory header */ ++ if (msblk->swap) { ++ struct squashfs_dir_header_2 sdirh; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, ++ next_block, next_offset, sizeof(sdirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdirh); ++ SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, ++ next_block, next_offset, sizeof(dirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(dirh); ++ } ++ ++ dir_count = dirh.count + 1; ++ while (dir_count--) { ++ if (msblk->swap) { ++ struct squashfs_dir_entry_2 sdire; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ &sdire, next_block,next_offset, ++ sizeof(sdire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdire); ++ SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ dire, next_block,next_offset, ++ sizeof(*dire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(*dire); ++ } ++ ++ if (!squashfs_get_cached_block(i->i_sb, dire->name, ++ next_block, next_offset, dire->size + 1, ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += dire->size + 1; ++ ++ if (sorted && name[0] < dire->name[0]) ++ goto exit_loop; ++ ++ if ((len == dire->size + 1) && !strncmp(name, ++ dire->name, len)) { ++ squashfs_inode_t ino = ++ SQUASHFS_MKINODE(dirh.start_block, ++ dire->offset); ++ ++ TRACE("calling squashfs_iget for directory " ++ "entry %s, inode %x:%x, %lld\n", name, ++ dirh.start_block, dire->offset, ino); ++ ++ inode = (msblk->iget)(i->i_sb, ino); ++ ++ goto exit_loop; ++ } ++ } ++ } ++ ++exit_loop: ++ d_add(dentry, inode); ++ return ERR_PTR(0); ++ ++failed_read: ++ ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++ next_offset); ++ goto exit_loop; ++} ++ ++ ++int squashfs_2_0_supported(struct squashfs_sb_info *msblk) ++{ ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ msblk->iget = squashfs_iget_2; ++ msblk->read_fragment_index_table = read_fragment_index_table_2; ++ ++ sblk->bytes_used = sblk->bytes_used_2; ++ sblk->uid_start = sblk->uid_start_2; ++ sblk->guid_start = sblk->guid_start_2; ++ sblk->inode_table_start = sblk->inode_table_start_2; ++ sblk->directory_table_start = sblk->directory_table_start_2; ++ sblk->fragment_table_start = sblk->fragment_table_start_2; ++ ++ return 1; ++} +diff --new-file -urp linux-2.6.15/fs/squashfs/squashfs.h linux-2.6.15-squashfs3.0/fs/squashfs/squashfs.h +--- linux-2.6.15/fs/squashfs/squashfs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.15-squashfs3.0/fs/squashfs/squashfs.h 2006-03-07 21:12:37.000000000 +0000 +@@ -0,0 +1,86 @@ ++/* ++ * Squashfs - a compressed read only filesystem for Linux ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher <phillip@lougher.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs.h ++ */ ++ ++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++#undef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++#endif ++ ++#ifdef SQUASHFS_TRACE ++#define TRACE(s, args...) printk(KERN_NOTICE "SQUASHFS: "s, ## args) ++#else ++#define TRACE(s, args...) {} ++#endif ++ ++#define ERROR(s, args...) printk(KERN_ERR "SQUASHFS error: "s, ## args) ++ ++#define SERROR(s, args...) do { \ ++ if (!silent) \ ++ printk(KERN_ERR "SQUASHFS error: "s, ## args);\ ++ } while(0) ++ ++#define WARNING(s, args...) printk(KERN_WARNING "SQUASHFS: "s, ## args) ++ ++static inline struct squashfs_inode_info *SQUASHFS_I(struct inode *inode) ++{ ++ return list_entry(inode, struct squashfs_inode_info, vfs_inode); ++} ++ ++#if defined(CONFIG_SQUASHFS_1_0_COMPATIBILITY ) || defined(CONFIG_SQUASHFS_2_0_COMPATIBILITY) ++#define SQSH_EXTERN ++extern unsigned int squashfs_read_data(struct super_block *s, char *buffer, ++ long long index, unsigned int length, ++ long long *next_index); ++extern int squashfs_get_cached_block(struct super_block *s, char *buffer, ++ long long block, unsigned int offset, ++ int length, long long *next_block, ++ unsigned int *next_offset); ++extern void release_cached_fragment(struct squashfs_sb_info *msblk, struct ++ squashfs_fragment_cache *fragment); ++extern struct squashfs_fragment_cache *get_cached_fragment(struct super_block ++ *s, long long start_block, ++ int length); ++extern struct address_space_operations squashfs_symlink_aops; ++extern struct address_space_operations squashfs_aops; ++extern struct address_space_operations squashfs_aops_4K; ++extern struct inode_operations squashfs_dir_inode_ops; ++#else ++#define SQSH_EXTERN static ++#endif ++ ++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++extern int squashfs_1_0_supported(struct squashfs_sb_info *msblk); ++#else ++static inline int squashfs_1_0_supported(struct squashfs_sb_info *msblk) ++{ ++ return 0; ++} ++#endif ++ ++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++extern int squashfs_2_0_supported(struct squashfs_sb_info *msblk); ++#else ++static inline int squashfs_2_0_supported(struct squashfs_sb_info *msblk) ++{ ++ return 0; ++} ++#endif +diff --new-file -urp linux-2.6.15/include/linux/squashfs_fs.h linux-2.6.15-squashfs3.0/include/linux/squashfs_fs.h +--- linux-2.6.15/include/linux/squashfs_fs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.15-squashfs3.0/include/linux/squashfs_fs.h 2006-03-07 21:12:37.000000000 +0000 +@@ -0,0 +1,911 @@ ++#ifndef SQUASHFS_FS ++#define SQUASHFS_FS ++ ++/* ++ * Squashfs ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher <phillip@lougher.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs_fs.h ++ */ ++ ++#ifndef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++#define CONFIG_SQUASHFS_2_0_COMPATIBILITY ++#endif ++ ++#ifdef CONFIG_SQUASHFS_VMALLOC ++#define SQUASHFS_ALLOC(a) vmalloc(a) ++#define SQUASHFS_FREE(a) vfree(a) ++#else ++#define SQUASHFS_ALLOC(a) kmalloc(a, GFP_KERNEL) ++#define SQUASHFS_FREE(a) kfree(a) ++#endif ++#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE ++#define SQUASHFS_MAJOR 3 ++#define SQUASHFS_MINOR 0 ++#define SQUASHFS_MAGIC 0x73717368 ++#define SQUASHFS_MAGIC_SWAP 0x68737173 ++#define SQUASHFS_START 0 ++ ++/* size of metadata (inode and directory) blocks */ ++#define SQUASHFS_METADATA_SIZE 8192 ++#define SQUASHFS_METADATA_LOG 13 ++ ++/* default size of data blocks */ ++#define SQUASHFS_FILE_SIZE 65536 ++#define SQUASHFS_FILE_LOG 16 ++ ++#define SQUASHFS_FILE_MAX_SIZE 65536 ++ ++/* Max number of uids and gids */ ++#define SQUASHFS_UIDS 256 ++#define SQUASHFS_GUIDS 255 ++ ++/* Max length of filename (not 255) */ ++#define SQUASHFS_NAME_LEN 256 ++ ++#define SQUASHFS_INVALID ((long long) 0xffffffffffff) ++#define SQUASHFS_INVALID_FRAG ((unsigned int) 0xffffffff) ++#define SQUASHFS_INVALID_BLK ((long long) -1) ++#define SQUASHFS_USED_BLK ((long long) -2) ++ ++/* Filesystem flags */ ++#define SQUASHFS_NOI 0 ++#define SQUASHFS_NOD 1 ++#define SQUASHFS_CHECK 2 ++#define SQUASHFS_NOF 3 ++#define SQUASHFS_NO_FRAG 4 ++#define SQUASHFS_ALWAYS_FRAG 5 ++#define SQUASHFS_DUPLICATE 6 ++ ++#define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1) ++ ++#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_NOI) ++ ++#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_NOD) ++ ++#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_NOF) ++ ++#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_NO_FRAG) ++ ++#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_ALWAYS_FRAG) ++ ++#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_DUPLICATE) ++ ++#define SQUASHFS_CHECK_DATA(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_CHECK) ++ ++#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \ ++ duplicate_checking) (noi | (nod << 1) | (check_data << 2) \ ++ | (nof << 3) | (no_frag << 4) | (always_frag << 5) | \ ++ (duplicate_checking << 6)) ++ ++/* Max number of types and file types */ ++#define SQUASHFS_DIR_TYPE 1 ++#define SQUASHFS_FILE_TYPE 2 ++#define SQUASHFS_SYMLINK_TYPE 3 ++#define SQUASHFS_BLKDEV_TYPE 4 ++#define SQUASHFS_CHRDEV_TYPE 5 ++#define SQUASHFS_FIFO_TYPE 6 ++#define SQUASHFS_SOCKET_TYPE 7 ++#define SQUASHFS_LDIR_TYPE 8 ++#define SQUASHFS_LREG_TYPE 9 ++ ++/* 1.0 filesystem type definitions */ ++#define SQUASHFS_TYPES 5 ++#define SQUASHFS_IPC_TYPE 0 ++ ++/* Flag whether block is compressed or uncompressed, bit is set if block is ++ * uncompressed */ ++#define SQUASHFS_COMPRESSED_BIT (1 << 15) ++ ++#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \ ++ (B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT) ++ ++#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT)) ++ ++#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24) ++ ++#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) (((B) & \ ++ ~SQUASHFS_COMPRESSED_BIT_BLOCK) ? (B) & \ ++ ~SQUASHFS_COMPRESSED_BIT_BLOCK : SQUASHFS_COMPRESSED_BIT_BLOCK) ++ ++#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK)) ++ ++/* ++ * Inode number ops. Inodes consist of a compressed block number, and an ++ * uncompressed offset within that block ++ */ ++#define SQUASHFS_INODE_BLK(a) ((unsigned int) ((a) >> 16)) ++ ++#define SQUASHFS_INODE_OFFSET(a) ((unsigned int) ((a) & 0xffff)) ++ ++#define SQUASHFS_MKINODE(A, B) ((squashfs_inode_t)(((squashfs_inode_t) (A)\ ++ << 16) + (B))) ++ ++/* Compute 32 bit VFS inode number from squashfs inode number */ ++#define SQUASHFS_MK_VFS_INODE(a, b) ((unsigned int) (((a) << 8) + \ ++ ((b) >> 2) + 1)) ++/* XXX */ ++ ++/* Translate between VFS mode and squashfs mode */ ++#define SQUASHFS_MODE(a) ((a) & 0xfff) ++ ++/* fragment and fragment table defines */ ++#define SQUASHFS_FRAGMENT_BYTES(A) (A * sizeof(struct squashfs_fragment_entry)) ++ ++#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \ ++ SQUASHFS_METADATA_SIZE - 1) / \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\ ++ sizeof(long long)) ++ ++/* cached data constants for filesystem */ ++#define SQUASHFS_CACHED_BLKS 8 ++ ++#define SQUASHFS_MAX_FILE_SIZE_LOG 64 ++ ++#define SQUASHFS_MAX_FILE_SIZE ((long long) 1 << \ ++ (SQUASHFS_MAX_FILE_SIZE_LOG - 2)) ++ ++#define SQUASHFS_MARKER_BYTE 0xff ++ ++/* meta index cache */ ++#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int)) ++#define SQUASHFS_META_ENTRIES 31 ++#define SQUASHFS_META_NUMBER 8 ++#define SQUASHFS_SLOTS 4 ++ ++struct meta_entry { ++ long long data_block; ++ unsigned int index_block; ++ unsigned short offset; ++ unsigned short pad; ++}; ++ ++struct meta_index { ++ unsigned int inode_number; ++ unsigned int offset; ++ unsigned short entries; ++ unsigned short skip; ++ unsigned short locked; ++ unsigned short pad; ++ struct meta_entry meta_entry[SQUASHFS_META_ENTRIES]; ++}; ++ ++ ++/* ++ * definitions for structures on disk ++ */ ++ ++typedef long long squashfs_block_t; ++typedef long long squashfs_inode_t; ++ ++struct squashfs_super_block { ++ unsigned int s_magic; ++ unsigned int inodes; ++ unsigned int bytes_used_2; ++ unsigned int uid_start_2; ++ unsigned int guid_start_2; ++ unsigned int inode_table_start_2; ++ unsigned int directory_table_start_2; ++ unsigned int s_major:16; ++ unsigned int s_minor:16; ++ unsigned int block_size_1:16; ++ unsigned int block_log:16; ++ unsigned int flags:8; ++ unsigned int no_uids:8; ++ unsigned int no_guids:8; ++ unsigned int mkfs_time /* time of filesystem creation */; ++ squashfs_inode_t root_inode; ++ unsigned int block_size; ++ unsigned int fragments; ++ unsigned int fragment_table_start_2; ++ long long bytes_used; ++ long long uid_start; ++ long long guid_start; ++ long long inode_table_start; ++ long long directory_table_start; ++ long long fragment_table_start; ++ long long unused; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_index { ++ unsigned int index; ++ unsigned int start_block; ++ unsigned char size; ++ unsigned char name[0]; ++} __attribute__ ((packed)); ++ ++#define SQUASHFS_BASE_INODE_HEADER \ ++ unsigned int inode_type:4; \ ++ unsigned int mode:12; \ ++ unsigned int uid:8; \ ++ unsigned int guid:8; \ ++ unsigned int mtime; \ ++ unsigned int inode_number; ++ ++struct squashfs_base_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++} __attribute__ ((packed)); ++ ++struct squashfs_ipc_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++} __attribute__ ((packed)); ++ ++struct squashfs_dev_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ unsigned short rdev; ++} __attribute__ ((packed)); ++ ++struct squashfs_symlink_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ unsigned short symlink_size; ++ char symlink[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_reg_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ squashfs_block_t start_block; ++ unsigned int fragment; ++ unsigned int offset; ++ unsigned int file_size; ++ unsigned short block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_lreg_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ squashfs_block_t start_block; ++ unsigned int fragment; ++ unsigned int offset; ++ long long file_size; ++ unsigned short block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ unsigned int file_size:19; ++ unsigned int offset:13; ++ unsigned int start_block; ++ unsigned int parent_inode; ++} __attribute__ ((packed)); ++ ++struct squashfs_ldir_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ unsigned int file_size:27; ++ unsigned int offset:13; ++ unsigned int start_block; ++ unsigned int i_count:16; ++ unsigned int parent_inode; ++ struct squashfs_dir_index index[0]; ++} __attribute__ ((packed)); ++ ++union squashfs_inode_header { ++ struct squashfs_base_inode_header base; ++ struct squashfs_dev_inode_header dev; ++ struct squashfs_symlink_inode_header symlink; ++ struct squashfs_reg_inode_header reg; ++ struct squashfs_lreg_inode_header lreg; ++ struct squashfs_dir_inode_header dir; ++ struct squashfs_ldir_inode_header ldir; ++ struct squashfs_ipc_inode_header ipc; ++}; ++ ++struct squashfs_dir_entry { ++ unsigned int offset:13; ++ unsigned int type:3; ++ unsigned int size:8; ++ int inode_number:16; ++ char name[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_header { ++ unsigned int count:8; ++ unsigned int start_block; ++ unsigned int inode_number; ++} __attribute__ ((packed)); ++ ++struct squashfs_fragment_entry { ++ long long start_block; ++ unsigned int size; ++ unsigned int unused; ++} __attribute__ ((packed)); ++ ++extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen); ++extern int squashfs_uncompress_init(void); ++extern int squashfs_uncompress_exit(void); ++ ++/* ++ * macros to convert each packed bitfield structure from little endian to big ++ * endian and vice versa. These are needed when creating or using a filesystem ++ * on a machine with different byte ordering to the target architecture. ++ * ++ */ ++ ++#define SQUASHFS_SWAP_START \ ++ int bits;\ ++ int b_pos;\ ++ unsigned long long val;\ ++ unsigned char *s;\ ++ unsigned char *d; ++ ++#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block));\ ++ SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\ ++ SQUASHFS_SWAP((s)->inodes, d, 32, 32);\ ++ SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\ ++ SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\ ++ SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\ ++ SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\ ++ SQUASHFS_SWAP((s)->s_major, d, 224, 16);\ ++ SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\ ++ SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\ ++ SQUASHFS_SWAP((s)->block_log, d, 272, 16);\ ++ SQUASHFS_SWAP((s)->flags, d, 288, 8);\ ++ SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\ ++ SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\ ++ SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\ ++ SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\ ++ SQUASHFS_SWAP((s)->block_size, d, 408, 32);\ ++ SQUASHFS_SWAP((s)->fragments, d, 440, 32);\ ++ SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\ ++ SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\ ++ SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\ ++ SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\ ++ SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\ ++ SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\ ++ SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\ ++ SQUASHFS_SWAP((s)->unused, d, 888, 64);\ ++} ++ ++#define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\ ++ SQUASHFS_MEMSET(s, d, n);\ ++ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ ++ SQUASHFS_SWAP((s)->mode, d, 4, 12);\ ++ SQUASHFS_SWAP((s)->uid, d, 16, 8);\ ++ SQUASHFS_SWAP((s)->guid, d, 24, 8);\ ++ SQUASHFS_SWAP((s)->mtime, d, 32, 32);\ ++ SQUASHFS_SWAP((s)->inode_number, d, 64, 32); ++ ++#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, n) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\ ++} ++ ++#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_ipc_inode_header))\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_dev_inode_header)); \ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->rdev, d, 128, 16);\ ++} ++ ++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_symlink_inode_header));\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\ ++} ++ ++#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_reg_inode_header));\ ++ SQUASHFS_SWAP((s)->start_block, d, 96, 64);\ ++ SQUASHFS_SWAP((s)->fragment, d, 160, 32);\ ++ SQUASHFS_SWAP((s)->offset, d, 192, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 224, 32);\ ++} ++ ++#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_lreg_inode_header));\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 128, 64);\ ++ SQUASHFS_SWAP((s)->fragment, d, 192, 32);\ ++ SQUASHFS_SWAP((s)->offset, d, 224, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 256, 64);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_dir_inode_header));\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 128, 19);\ ++ SQUASHFS_SWAP((s)->offset, d, 147, 13);\ ++ SQUASHFS_SWAP((s)->start_block, d, 160, 32);\ ++ SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\ ++} ++ ++#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_ldir_inode_header));\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 128, 27);\ ++ SQUASHFS_SWAP((s)->offset, d, 155, 13);\ ++ SQUASHFS_SWAP((s)->start_block, d, 168, 32);\ ++ SQUASHFS_SWAP((s)->i_count, d, 200, 16);\ ++ SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INDEX(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index));\ ++ SQUASHFS_SWAP((s)->index, d, 0, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 32, 32);\ ++ SQUASHFS_SWAP((s)->size, d, 64, 8);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header));\ ++ SQUASHFS_SWAP((s)->count, d, 0, 8);\ ++ SQUASHFS_SWAP((s)->start_block, d, 8, 32);\ ++ SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_ENTRY(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry));\ ++ SQUASHFS_SWAP((s)->offset, d, 0, 13);\ ++ SQUASHFS_SWAP((s)->type, d, 13, 3);\ ++ SQUASHFS_SWAP((s)->size, d, 16, 8);\ ++ SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry));\ ++ SQUASHFS_SWAP((s)->start_block, d, 0, 64);\ ++ SQUASHFS_SWAP((s)->size, d, 64, 32);\ ++} ++ ++#define SQUASHFS_SWAP_SHORTS(s, d, n) {\ ++ int entry;\ ++ int bit_position;\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, n * 2);\ ++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++ 16)\ ++ SQUASHFS_SWAP(s[entry], d, bit_position, 16);\ ++} ++ ++#define SQUASHFS_SWAP_INTS(s, d, n) {\ ++ int entry;\ ++ int bit_position;\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, n * 4);\ ++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++ 32)\ ++ SQUASHFS_SWAP(s[entry], d, bit_position, 32);\ ++} ++ ++#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) {\ ++ int entry;\ ++ int bit_position;\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, n * 8);\ ++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++ 64)\ ++ SQUASHFS_SWAP(s[entry], d, bit_position, 64);\ ++} ++ ++#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\ ++ int entry;\ ++ int bit_position;\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, n * bits / 8);\ ++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++ bits)\ ++ SQUASHFS_SWAP(s[entry], d, bit_position, bits);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n) ++ ++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++ ++struct squashfs_base_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++} __attribute__ ((packed)); ++ ++struct squashfs_ipc_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned int type:4; ++ unsigned int offset:4; ++} __attribute__ ((packed)); ++ ++struct squashfs_dev_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned short rdev; ++} __attribute__ ((packed)); ++ ++struct squashfs_symlink_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned short symlink_size; ++ char symlink[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_reg_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned int mtime; ++ unsigned int start_block; ++ unsigned int file_size:32; ++ unsigned short block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned int file_size:19; ++ unsigned int offset:13; ++ unsigned int mtime; ++ unsigned int start_block:24; ++} __attribute__ ((packed)); ++ ++#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \ ++ SQUASHFS_MEMSET(s, d, n);\ ++ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ ++ SQUASHFS_SWAP((s)->mode, d, 4, 12);\ ++ SQUASHFS_SWAP((s)->uid, d, 16, 4);\ ++ SQUASHFS_SWAP((s)->guid, d, 20, 4); ++ ++#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\ ++} ++ ++#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_ipc_inode_header_1));\ ++ SQUASHFS_SWAP((s)->type, d, 24, 4);\ ++ SQUASHFS_SWAP((s)->offset, d, 28, 4);\ ++} ++ ++#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_dev_inode_header_1));\ ++ SQUASHFS_SWAP((s)->rdev, d, 24, 16);\ ++} ++ ++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_symlink_inode_header_1));\ ++ SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\ ++} ++ ++#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_reg_inode_header_1));\ ++ SQUASHFS_SWAP((s)->mtime, d, 24, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 56, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 88, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_dir_inode_header_1));\ ++ SQUASHFS_SWAP((s)->file_size, d, 24, 19);\ ++ SQUASHFS_SWAP((s)->offset, d, 43, 13);\ ++ SQUASHFS_SWAP((s)->mtime, d, 56, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 88, 24);\ ++} ++ ++#endif ++ ++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++ ++struct squashfs_dir_index_2 { ++ unsigned int index:27; ++ unsigned int start_block:29; ++ unsigned char size; ++ unsigned char name[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_base_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++} __attribute__ ((packed)); ++ ++struct squashfs_ipc_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++} __attribute__ ((packed)); ++ ++struct squashfs_dev_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned short rdev; ++} __attribute__ ((packed)); ++ ++struct squashfs_symlink_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned short symlink_size; ++ char symlink[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_reg_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned int mtime; ++ unsigned int start_block; ++ unsigned int fragment; ++ unsigned int offset; ++ unsigned int file_size:32; ++ unsigned short block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned int file_size:19; ++ unsigned int offset:13; ++ unsigned int mtime; ++ unsigned int start_block:24; ++} __attribute__ ((packed)); ++ ++struct squashfs_ldir_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned int file_size:27; ++ unsigned int offset:13; ++ unsigned int mtime; ++ unsigned int start_block:24; ++ unsigned int i_count:16; ++ struct squashfs_dir_index_2 index[0]; ++} __attribute__ ((packed)); ++ ++union squashfs_inode_header_2 { ++ struct squashfs_base_inode_header_2 base; ++ struct squashfs_dev_inode_header_2 dev; ++ struct squashfs_symlink_inode_header_2 symlink; ++ struct squashfs_reg_inode_header_2 reg; ++ struct squashfs_dir_inode_header_2 dir; ++ struct squashfs_ldir_inode_header_2 ldir; ++ struct squashfs_ipc_inode_header_2 ipc; ++}; ++ ++struct squashfs_dir_header_2 { ++ unsigned int count:8; ++ unsigned int start_block:24; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_entry_2 { ++ unsigned int offset:13; ++ unsigned int type:3; ++ unsigned int size:8; ++ char name[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_fragment_entry_2 { ++ unsigned int start_block; ++ unsigned int size; ++} __attribute__ ((packed)); ++ ++#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\ ++ SQUASHFS_MEMSET(s, d, n);\ ++ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ ++ SQUASHFS_SWAP((s)->mode, d, 4, 12);\ ++ SQUASHFS_SWAP((s)->uid, d, 16, 8);\ ++ SQUASHFS_SWAP((s)->guid, d, 24, 8);\ ++ ++#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\ ++} ++ ++#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \ ++ SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2)) ++ ++#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_dev_inode_header_2)); \ ++ SQUASHFS_SWAP((s)->rdev, d, 32, 16);\ ++} ++ ++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_symlink_inode_header_2));\ ++ SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\ ++} ++ ++#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_reg_inode_header_2));\ ++ SQUASHFS_SWAP((s)->mtime, d, 32, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 64, 32);\ ++ SQUASHFS_SWAP((s)->fragment, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->offset, d, 128, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 160, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_dir_inode_header_2));\ ++ SQUASHFS_SWAP((s)->file_size, d, 32, 19);\ ++ SQUASHFS_SWAP((s)->offset, d, 51, 13);\ ++ SQUASHFS_SWAP((s)->mtime, d, 64, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 96, 24);\ ++} ++ ++#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_ldir_inode_header_2));\ ++ SQUASHFS_SWAP((s)->file_size, d, 32, 27);\ ++ SQUASHFS_SWAP((s)->offset, d, 59, 13);\ ++ SQUASHFS_SWAP((s)->mtime, d, 72, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 104, 24);\ ++ SQUASHFS_SWAP((s)->i_count, d, 128, 16);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\ ++ SQUASHFS_SWAP((s)->index, d, 0, 27);\ ++ SQUASHFS_SWAP((s)->start_block, d, 27, 29);\ ++ SQUASHFS_SWAP((s)->size, d, 56, 8);\ ++} ++#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\ ++ SQUASHFS_SWAP((s)->count, d, 0, 8);\ ++ SQUASHFS_SWAP((s)->start_block, d, 8, 24);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\ ++ SQUASHFS_SWAP((s)->offset, d, 0, 13);\ ++ SQUASHFS_SWAP((s)->type, d, 13, 3);\ ++ SQUASHFS_SWAP((s)->size, d, 16, 8);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\ ++ SQUASHFS_SWAP((s)->start_block, d, 0, 32);\ ++ SQUASHFS_SWAP((s)->size, d, 32, 32);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS(s, d, n) ++ ++/* fragment and fragment table defines */ ++#define SQUASHFS_FRAGMENT_BYTES_2(A) (A * sizeof(struct squashfs_fragment_entry_2)) ++ ++#define SQUASHFS_FRAGMENT_INDEX_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) / \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) % \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEXES_2(A) ((SQUASHFS_FRAGMENT_BYTES_2(A) + \ ++ SQUASHFS_METADATA_SIZE - 1) / \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A) (SQUASHFS_FRAGMENT_INDEXES_2(A) *\ ++ sizeof(int)) ++ ++#endif ++ ++#ifdef __KERNEL__ ++ ++/* ++ * macros used to swap each structure entry, taking into account ++ * bitfields and different bitfield placing conventions on differing ++ * architectures ++ */ ++ ++#include <asm/byteorder.h> ++ ++#ifdef __BIG_ENDIAN ++ /* convert from little endian to big endian */ ++#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \ ++ tbits, b_pos) ++#else ++ /* convert from big endian to little endian */ ++#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \ ++ tbits, 64 - tbits - b_pos) ++#endif ++ ++#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\ ++ b_pos = pos % 8;\ ++ val = 0;\ ++ s = (unsigned char *)p + (pos / 8);\ ++ d = ((unsigned char *) &val) + 7;\ ++ for(bits = 0; bits < (tbits + b_pos); bits += 8) \ ++ *d-- = *s++;\ ++ value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\ ++} ++ ++#define SQUASHFS_MEMSET(s, d, n) memset(s, 0, n); ++ ++#endif ++#endif +diff --new-file -urp linux-2.6.15/include/linux/squashfs_fs_i.h linux-2.6.15-squashfs3.0/include/linux/squashfs_fs_i.h +--- linux-2.6.15/include/linux/squashfs_fs_i.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.15-squashfs3.0/include/linux/squashfs_fs_i.h 2006-03-07 21:12:37.000000000 +0000 +@@ -0,0 +1,45 @@ ++#ifndef SQUASHFS_FS_I ++#define SQUASHFS_FS_I ++/* ++ * Squashfs ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher <phillip@lougher.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs_fs_i.h ++ */ ++ ++struct squashfs_inode_info { ++ long long start_block; ++ unsigned int offset; ++ union { ++ struct { ++ long long fragment_start_block; ++ unsigned int fragment_size; ++ unsigned int fragment_offset; ++ long long block_list_start; ++ } s1; ++ struct { ++ long long directory_index_start; ++ unsigned int directory_index_offset; ++ unsigned int directory_index_count; ++ unsigned int parent_inode; ++ } s2; ++ } u; ++ struct inode vfs_inode; ++}; ++#endif +diff --new-file -urp linux-2.6.15/include/linux/squashfs_fs_sb.h linux-2.6.15-squashfs3.0/include/linux/squashfs_fs_sb.h +--- linux-2.6.15/include/linux/squashfs_fs_sb.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.15-squashfs3.0/include/linux/squashfs_fs_sb.h 2006-03-07 21:12:37.000000000 +0000 +@@ -0,0 +1,74 @@ ++#ifndef SQUASHFS_FS_SB ++#define SQUASHFS_FS_SB ++/* ++ * Squashfs ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006 ++ * Phillip Lougher <phillip@lougher.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs_fs_sb.h ++ */ ++ ++#include <linux/squashfs_fs.h> ++ ++struct squashfs_cache { ++ long long block; ++ int length; ++ long long next_index; ++ char *data; ++}; ++ ++struct squashfs_fragment_cache { ++ long long block; ++ int length; ++ unsigned int locked; ++ char *data; ++}; ++ ++struct squashfs_sb_info { ++ struct squashfs_super_block sblk; ++ int devblksize; ++ int devblksize_log2; ++ int swap; ++ struct squashfs_cache *block_cache; ++ struct squashfs_fragment_cache *fragment; ++ int next_cache; ++ int next_fragment; ++ int next_meta_index; ++ unsigned int *uid; ++ unsigned int *guid; ++ long long *fragment_index; ++ unsigned int *fragment_index_2; ++ unsigned int read_size; ++ char *read_data; ++ char *read_page; ++ struct semaphore read_data_mutex; ++ struct semaphore read_page_mutex; ++ struct semaphore block_cache_mutex; ++ struct semaphore fragment_mutex; ++ struct semaphore meta_index_mutex; ++ wait_queue_head_t waitq; ++ wait_queue_head_t fragment_wait_queue; ++ struct meta_index *meta_index; ++ struct inode *(*iget)(struct super_block *s, squashfs_inode_t \ ++ inode); ++ long long (*read_blocklist)(struct inode *inode, int \ ++ index, int readahead_blks, char *block_list, \ ++ unsigned short **block_p, unsigned int *bsize); ++ int (*read_fragment_index_table)(struct super_block *s); ++}; ++#endif +diff --new-file -urp linux-2.6.15/init/do_mounts_rd.c linux-2.6.15-squashfs3.0/init/do_mounts_rd.c +--- linux-2.6.15/init/do_mounts_rd.c 2006-03-01 22:37:27.000000000 +0000 ++++ linux-2.6.15-squashfs3.0/init/do_mounts_rd.c 2006-03-07 21:12:37.000000000 +0000 +@@ -5,6 +5,7 @@ + #include <linux/ext2_fs.h> + #include <linux/romfs_fs.h> + #include <linux/cramfs_fs.h> ++#include <linux/squashfs_fs.h> + #include <linux/initrd.h> + #include <linux/string.h> + +@@ -39,6 +40,7 @@ static int __init crd_load(int in_fd, in + * numbers could not be found. + * + * We currently check for the following magic numbers: ++ * squashfs + * minix + * ext2 + * romfs +@@ -53,6 +55,7 @@ identify_ramdisk_image(int fd, int start + struct ext2_super_block *ext2sb; + struct romfs_super_block *romfsb; + struct cramfs_super *cramfsb; ++ struct squashfs_super_block *squashfsb; + int nblocks = -1; + unsigned char *buf; + +@@ -64,6 +67,7 @@ identify_ramdisk_image(int fd, int start + ext2sb = (struct ext2_super_block *) buf; + romfsb = (struct romfs_super_block *) buf; + cramfsb = (struct cramfs_super *) buf; ++ squashfsb = (struct squashfs_super_block *) buf; + memset(buf, 0xe5, size); + + /* +@@ -101,6 +105,15 @@ identify_ramdisk_image(int fd, int start + goto done; + } + ++ /* squashfs is at block zero too */ ++ if (squashfsb->s_magic == SQUASHFS_MAGIC) { ++ printk(KERN_NOTICE ++ "RAMDISK: squashfs filesystem found at block %d\n", ++ start_block); ++ nblocks = (squashfsb->bytes_used+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS; ++ goto done; ++ } ++ + /* + * Read block 1 to test for minix and ext2 superblock + */ diff --git a/target/linux/generic-2.6/patches/002-squashfs_lzma.patch b/target/linux/generic-2.6/patches/002-squashfs_lzma.patch new file mode 100644 index 0000000..a166e84 --- /dev/null +++ b/target/linux/generic-2.6/patches/002-squashfs_lzma.patch @@ -0,0 +1,886 @@ +diff -Nur linux-2.6.16/fs/squashfs/inode.c linux-2.6.16-owrt/fs/squashfs/inode.c +--- linux-2.6.16/fs/squashfs/inode.c 2006-03-21 10:55:59.000000000 +0100 ++++ linux-2.6.16-owrt/fs/squashfs/inode.c 2006-03-21 12:24:37.000000000 +0100 +@@ -4,6 +4,9 @@ + * Copyright (c) 2002, 2003, 2004, 2005, 2006 + * Phillip Lougher <phillip@lougher.org.uk> + * ++ * LZMA decompressor support added by Oleg I. Vdovikin ++ * Copyright (c) 2005 Oleg I.Vdovikin <oleg@cs.msu.su> ++ * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, +@@ -21,6 +24,7 @@ + * inode.c + */ + ++#define SQUASHFS_LZMA + #include <linux/types.h> + #include <linux/squashfs_fs.h> + #include <linux/module.h> +@@ -44,6 +48,19 @@ + + #include "squashfs.h" + ++#ifdef SQUASHFS_LZMA ++#include "LzmaDecode.h" ++ ++/* default LZMA settings, should be in sync with mksquashfs */ ++#define LZMA_LC 3 ++#define LZMA_LP 0 ++#define LZMA_PB 2 ++ ++#define LZMA_WORKSPACE_SIZE ((LZMA_BASE_SIZE + \ ++ (LZMA_LIT_SIZE << (LZMA_LC + LZMA_LP))) * sizeof(CProb)) ++ ++#endif ++ + static void squashfs_put_super(struct super_block *); + static int squashfs_statfs(struct super_block *, struct kstatfs *); + static int squashfs_symlink_readpage(struct file *file, struct page *page); +@@ -64,7 +81,11 @@ + const char *, void *); + + ++#ifdef SQUASHFS_LZMA ++static unsigned char lzma_workspace[LZMA_WORKSPACE_SIZE]; ++#else + static z_stream stream; ++#endif + + static struct file_system_type squashfs_fs_type = { + .owner = THIS_MODULE, +@@ -249,6 +270,15 @@ + if (compressed) { + int zlib_err; + ++#ifdef SQUASHFS_LZMA ++ if ((zlib_err = LzmaDecode(lzma_workspace, ++ LZMA_WORKSPACE_SIZE, LZMA_LC, LZMA_LP, LZMA_PB, ++ c_buffer, c_byte, buffer, msblk->read_size, &bytes)) != LZMA_RESULT_OK) ++ { ++ ERROR("lzma returned unexpected result 0x%x\n", zlib_err); ++ bytes = 0; ++ } ++#else + stream.next_in = c_buffer; + stream.avail_in = c_byte; + stream.next_out = buffer; +@@ -263,6 +293,7 @@ + bytes = 0; + } else + bytes = stream.total_out; ++#endif + + up(&msblk->read_data_mutex); + } +@@ -2046,15 +2077,19 @@ + printk(KERN_INFO "squashfs: version 3.0 (2006/03/15) " + "Phillip Lougher\n"); + ++#ifndef SQUASHFS_LZMA + if (!(stream.workspace = vmalloc(zlib_inflate_workspacesize()))) { + ERROR("Failed to allocate zlib workspace\n"); + destroy_inodecache(); + err = -ENOMEM; + goto out; + } ++#endif + + if ((err = register_filesystem(&squashfs_fs_type))) { ++#ifndef SQUASHFS_LZMA + vfree(stream.workspace); ++#endif + destroy_inodecache(); + } + +@@ -2065,7 +2100,9 @@ + + static void __exit exit_squashfs_fs(void) + { ++#ifndef SQUASHFS_LZMA + vfree(stream.workspace); ++#endif + unregister_filesystem(&squashfs_fs_type); + destroy_inodecache(); + } +diff -Nur linux-2.6.16/fs/squashfs/LzmaDecode.c linux-2.6.16-owrt/fs/squashfs/LzmaDecode.c +--- linux-2.6.16/fs/squashfs/LzmaDecode.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/fs/squashfs/LzmaDecode.c 2006-03-21 10:56:57.000000000 +0100 +@@ -0,0 +1,663 @@ ++/* ++ LzmaDecode.c ++ LZMA Decoder ++ ++ LZMA SDK 4.05 Copyright (c) 1999-2004 Igor Pavlov (2004-08-25) ++ http://www.7-zip.org/ ++ ++ LZMA SDK is licensed under two licenses: ++ 1) GNU Lesser General Public License (GNU LGPL) ++ 2) Common Public License (CPL) ++ It means that you can select one of these two licenses and ++ follow rules of that license. ++ ++ SPECIAL EXCEPTION: ++ Igor Pavlov, as the author of this code, expressly permits you to ++ statically or dynamically link your code (or bind by name) to the ++ interfaces of this file without subjecting your linked code to the ++ terms of the CPL or GNU LGPL. Any modifications or additions ++ to this file, however, are subject to the LGPL or CPL terms. ++*/ ++ ++#include "LzmaDecode.h" ++ ++#ifndef Byte ++#define Byte unsigned char ++#endif ++ ++#define kNumTopBits 24 ++#define kTopValue ((UInt32)1 << kNumTopBits) ++ ++#define kNumBitModelTotalBits 11 ++#define kBitModelTotal (1 << kNumBitModelTotalBits) ++#define kNumMoveBits 5 ++ ++typedef struct _CRangeDecoder ++{ ++ Byte *Buffer; ++ Byte *BufferLim; ++ UInt32 Range; ++ UInt32 Code; ++ #ifdef _LZMA_IN_CB ++ ILzmaInCallback *InCallback; ++ int Result; ++ #endif ++ int ExtraBytes; ++} CRangeDecoder; ++ ++Byte RangeDecoderReadByte(CRangeDecoder *rd) ++{ ++ if (rd->Buffer == rd->BufferLim) ++ { ++ #ifdef _LZMA_IN_CB ++ UInt32 size; ++ rd->Result = rd->InCallback->Read(rd->InCallback, &rd->Buffer, &size); ++ rd->BufferLim = rd->Buffer + size; ++ if (size == 0) ++ #endif ++ { ++ rd->ExtraBytes = 1; ++ return 0xFF; ++ } ++ } ++ return (*rd->Buffer++); ++} ++ ++/* #define ReadByte (*rd->Buffer++) */ ++#define ReadByte (RangeDecoderReadByte(rd)) ++ ++void RangeDecoderInit(CRangeDecoder *rd, ++ #ifdef _LZMA_IN_CB ++ ILzmaInCallback *inCallback ++ #else ++ Byte *stream, UInt32 bufferSize ++ #endif ++ ) ++{ ++ int i; ++ #ifdef _LZMA_IN_CB ++ rd->InCallback = inCallback; ++ rd->Buffer = rd->BufferLim = 0; ++ #else ++ rd->Buffer = stream; ++ rd->BufferLim = stream + bufferSize; ++ #endif ++ rd->ExtraBytes = 0; ++ rd->Code = 0; ++ rd->Range = (0xFFFFFFFF); ++ for(i = 0; i < 5; i++) ++ rd->Code = (rd->Code << 8) | ReadByte; ++} ++ ++#define RC_INIT_VAR UInt32 range = rd->Range; UInt32 code = rd->Code; ++#define RC_FLUSH_VAR rd->Range = range; rd->Code = code; ++#define RC_NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | ReadByte; } ++ ++UInt32 RangeDecoderDecodeDirectBits(CRangeDecoder *rd, int numTotalBits) ++{ ++ RC_INIT_VAR ++ UInt32 result = 0; ++ int i; ++ for (i = numTotalBits; i > 0; i--) ++ { ++ /* UInt32 t; */ ++ range >>= 1; ++ ++ result <<= 1; ++ if (code >= range) ++ { ++ code -= range; ++ result |= 1; ++ } ++ /* ++ t = (code - range) >> 31; ++ t &= 1; ++ code -= range & (t - 1); ++ result = (result + result) | (1 - t); ++ */ ++ RC_NORMALIZE ++ } ++ RC_FLUSH_VAR ++ return result; ++} ++ ++int RangeDecoderBitDecode(CProb *prob, CRangeDecoder *rd) ++{ ++ UInt32 bound = (rd->Range >> kNumBitModelTotalBits) * *prob; ++ if (rd->Code < bound) ++ { ++ rd->Range = bound; ++ *prob += (kBitModelTotal - *prob) >> kNumMoveBits; ++ if (rd->Range < kTopValue) ++ { ++ rd->Code = (rd->Code << 8) | ReadByte; ++ rd->Range <<= 8; ++ } ++ return 0; ++ } ++ else ++ { ++ rd->Range -= bound; ++ rd->Code -= bound; ++ *prob -= (*prob) >> kNumMoveBits; ++ if (rd->Range < kTopValue) ++ { ++ rd->Code = (rd->Code << 8) | ReadByte; ++ rd->Range <<= 8; ++ } ++ return 1; ++ } ++} ++ ++#define RC_GET_BIT2(prob, mi, A0, A1) \ ++ UInt32 bound = (range >> kNumBitModelTotalBits) * *prob; \ ++ if (code < bound) \ ++ { A0; range = bound; *prob += (kBitModelTotal - *prob) >> kNumMoveBits; mi <<= 1; } \ ++ else \ ++ { A1; range -= bound; code -= bound; *prob -= (*prob) >> kNumMoveBits; mi = (mi + mi) + 1; } \ ++ RC_NORMALIZE ++ ++#define RC_GET_BIT(prob, mi) RC_GET_BIT2(prob, mi, ; , ;) ++ ++int RangeDecoderBitTreeDecode(CProb *probs, int numLevels, CRangeDecoder *rd) ++{ ++ int mi = 1; ++ int i; ++ #ifdef _LZMA_LOC_OPT ++ RC_INIT_VAR ++ #endif ++ for(i = numLevels; i > 0; i--) ++ { ++ #ifdef _LZMA_LOC_OPT ++ CProb *prob = probs + mi; ++ RC_GET_BIT(prob, mi) ++ #else ++ mi = (mi + mi) + RangeDecoderBitDecode(probs + mi, rd); ++ #endif ++ } ++ #ifdef _LZMA_LOC_OPT ++ RC_FLUSH_VAR ++ #endif ++ return mi - (1 << numLevels); ++} ++ ++int RangeDecoderReverseBitTreeDecode(CProb *probs, int numLevels, CRangeDecoder *rd) ++{ ++ int mi = 1; ++ int i; ++ int symbol = 0; ++ #ifdef _LZMA_LOC_OPT ++ RC_INIT_VAR ++ #endif ++ for(i = 0; i < numLevels; i++) ++ { ++ #ifdef _LZMA_LOC_OPT ++ CProb *prob = probs + mi; ++ RC_GET_BIT2(prob, mi, ; , symbol |= (1 << i)) ++ #else ++ int bit = RangeDecoderBitDecode(probs + mi, rd); ++ mi = mi + mi + bit; ++ symbol |= (bit << i); ++ #endif ++ } ++ #ifdef _LZMA_LOC_OPT ++ RC_FLUSH_VAR ++ #endif ++ return symbol; ++} ++ ++Byte LzmaLiteralDecode(CProb *probs, CRangeDecoder *rd) ++{ ++ int symbol = 1; ++ #ifdef _LZMA_LOC_OPT ++ RC_INIT_VAR ++ #endif ++ do ++ { ++ #ifdef _LZMA_LOC_OPT ++ CProb *prob = probs + symbol; ++ RC_GET_BIT(prob, symbol) ++ #else ++ symbol = (symbol + symbol) | RangeDecoderBitDecode(probs + symbol, rd); ++ #endif ++ } ++ while (symbol < 0x100); ++ #ifdef _LZMA_LOC_OPT ++ RC_FLUSH_VAR ++ #endif ++ return symbol; ++} ++ ++Byte LzmaLiteralDecodeMatch(CProb *probs, CRangeDecoder *rd, Byte matchByte) ++{ ++ int symbol = 1; ++ #ifdef _LZMA_LOC_OPT ++ RC_INIT_VAR ++ #endif ++ do ++ { ++ int bit; ++ int matchBit = (matchByte >> 7) & 1; ++ matchByte <<= 1; ++ #ifdef _LZMA_LOC_OPT ++ { ++ CProb *prob = probs + ((1 + matchBit) << 8) + symbol; ++ RC_GET_BIT2(prob, symbol, bit = 0, bit = 1) ++ } ++ #else ++ bit = RangeDecoderBitDecode(probs + ((1 + matchBit) << 8) + symbol, rd); ++ symbol = (symbol << 1) | bit; ++ #endif ++ if (matchBit != bit) ++ { ++ while (symbol < 0x100) ++ { ++ #ifdef _LZMA_LOC_OPT ++ CProb *prob = probs + symbol; ++ RC_GET_BIT(prob, symbol) ++ #else ++ symbol = (symbol + symbol) | RangeDecoderBitDecode(probs + symbol, rd); ++ #endif ++ } ++ break; ++ } ++ } ++ while (symbol < 0x100); ++ #ifdef _LZMA_LOC_OPT ++ RC_FLUSH_VAR ++ #endif ++ return symbol; ++} ++ ++#define kNumPosBitsMax 4 ++#define kNumPosStatesMax (1 << kNumPosBitsMax) ++ ++#define kLenNumLowBits 3 ++#define kLenNumLowSymbols (1 << kLenNumLowBits) ++#define kLenNumMidBits 3 ++#define kLenNumMidSymbols (1 << kLenNumMidBits) ++#define kLenNumHighBits 8 ++#define kLenNumHighSymbols (1 << kLenNumHighBits) ++ ++#define LenChoice 0 ++#define LenChoice2 (LenChoice + 1) ++#define LenLow (LenChoice2 + 1) ++#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) ++#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) ++#define kNumLenProbs (LenHigh + kLenNumHighSymbols) ++ ++int LzmaLenDecode(CProb *p, CRangeDecoder *rd, int posState) ++{ ++ if(RangeDecoderBitDecode(p + LenChoice, rd) == 0) ++ return RangeDecoderBitTreeDecode(p + LenLow + ++ (posState << kLenNumLowBits), kLenNumLowBits, rd); ++ if(RangeDecoderBitDecode(p + LenChoice2, rd) == 0) ++ return kLenNumLowSymbols + RangeDecoderBitTreeDecode(p + LenMid + ++ (posState << kLenNumMidBits), kLenNumMidBits, rd); ++ return kLenNumLowSymbols + kLenNumMidSymbols + ++ RangeDecoderBitTreeDecode(p + LenHigh, kLenNumHighBits, rd); ++} ++ ++#define kNumStates 12 ++ ++#define kStartPosModelIndex 4 ++#define kEndPosModelIndex 14 ++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) ++ ++#define kNumPosSlotBits 6 ++#define kNumLenToPosStates 4 ++ ++#define kNumAlignBits 4 ++#define kAlignTableSize (1 << kNumAlignBits) ++ ++#define kMatchMinLen 2 ++ ++#define IsMatch 0 ++#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) ++#define IsRepG0 (IsRep + kNumStates) ++#define IsRepG1 (IsRepG0 + kNumStates) ++#define IsRepG2 (IsRepG1 + kNumStates) ++#define IsRep0Long (IsRepG2 + kNumStates) ++#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) ++#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) ++#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) ++#define LenCoder (Align + kAlignTableSize) ++#define RepLenCoder (LenCoder + kNumLenProbs) ++#define Literal (RepLenCoder + kNumLenProbs) ++ ++#if Literal != LZMA_BASE_SIZE ++StopCompilingDueBUG ++#endif ++ ++#ifdef _LZMA_OUT_READ ++ ++typedef struct _LzmaVarState ++{ ++ CRangeDecoder RangeDecoder; ++ Byte *Dictionary; ++ UInt32 DictionarySize; ++ UInt32 DictionaryPos; ++ UInt32 GlobalPos; ++ UInt32 Reps[4]; ++ int lc; ++ int lp; ++ int pb; ++ int State; ++ int PreviousIsMatch; ++ int RemainLen; ++} LzmaVarState; ++ ++int LzmaDecoderInit( ++ unsigned char *buffer, UInt32 bufferSize, ++ int lc, int lp, int pb, ++ unsigned char *dictionary, UInt32 dictionarySize, ++ #ifdef _LZMA_IN_CB ++ ILzmaInCallback *inCallback ++ #else ++ unsigned char *inStream, UInt32 inSize ++ #endif ++ ) ++{ ++ LzmaVarState *vs = (LzmaVarState *)buffer; ++ CProb *p = (CProb *)(buffer + sizeof(LzmaVarState)); ++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + lp)); ++ UInt32 i; ++ if (bufferSize < numProbs * sizeof(CProb) + sizeof(LzmaVarState)) ++ return LZMA_RESULT_NOT_ENOUGH_MEM; ++ vs->Dictionary = dictionary; ++ vs->DictionarySize = dictionarySize; ++ vs->DictionaryPos = 0; ++ vs->GlobalPos = 0; ++ vs->Reps[0] = vs->Reps[1] = vs->Reps[2] = vs->Reps[3] = 1; ++ vs->lc = lc; ++ vs->lp = lp; ++ vs->pb = pb; ++ vs->State = 0; ++ vs->PreviousIsMatch = 0; ++ vs->RemainLen = 0; ++ dictionary[dictionarySize - 1] = 0; ++ for (i = 0; i < numProbs; i++) ++ p[i] = kBitModelTotal >> 1; ++ RangeDecoderInit(&vs->RangeDecoder, ++ #ifdef _LZMA_IN_CB ++ inCallback ++ #else ++ inStream, inSize ++ #endif ++ ); ++ return LZMA_RESULT_OK; ++} ++ ++int LzmaDecode(unsigned char *buffer, ++ unsigned char *outStream, UInt32 outSize, ++ UInt32 *outSizeProcessed) ++{ ++ LzmaVarState *vs = (LzmaVarState *)buffer; ++ CProb *p = (CProb *)(buffer + sizeof(LzmaVarState)); ++ CRangeDecoder rd = vs->RangeDecoder; ++ int state = vs->State; ++ int previousIsMatch = vs->PreviousIsMatch; ++ Byte previousByte; ++ UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3]; ++ UInt32 nowPos = 0; ++ UInt32 posStateMask = (1 << (vs->pb)) - 1; ++ UInt32 literalPosMask = (1 << (vs->lp)) - 1; ++ int lc = vs->lc; ++ int len = vs->RemainLen; ++ UInt32 globalPos = vs->GlobalPos; ++ ++ Byte *dictionary = vs->Dictionary; ++ UInt32 dictionarySize = vs->DictionarySize; ++ UInt32 dictionaryPos = vs->DictionaryPos; ++ ++ if (len == -1) ++ { ++ *outSizeProcessed = 0; ++ return LZMA_RESULT_OK; ++ } ++ ++ while(len > 0 && nowPos < outSize) ++ { ++ UInt32 pos = dictionaryPos - rep0; ++ if (pos >= dictionarySize) ++ pos += dictionarySize; ++ outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos]; ++ if (++dictionaryPos == dictionarySize) ++ dictionaryPos = 0; ++ len--; ++ } ++ if (dictionaryPos == 0) ++ previousByte = dictionary[dictionarySize - 1]; ++ else ++ previousByte = dictionary[dictionaryPos - 1]; ++#else ++ ++int LzmaDecode( ++ Byte *buffer, UInt32 bufferSize, ++ int lc, int lp, int pb, ++ #ifdef _LZMA_IN_CB ++ ILzmaInCallback *inCallback, ++ #else ++ unsigned char *inStream, UInt32 inSize, ++ #endif ++ unsigned char *outStream, UInt32 outSize, ++ UInt32 *outSizeProcessed) ++{ ++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + lp)); ++ CProb *p = (CProb *)buffer; ++ CRangeDecoder rd; ++ UInt32 i; ++ int state = 0; ++ int previousIsMatch = 0; ++ Byte previousByte = 0; ++ UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1; ++ UInt32 nowPos = 0; ++ UInt32 posStateMask = (1 << pb) - 1; ++ UInt32 literalPosMask = (1 << lp) - 1; ++ int len = 0; ++ if (bufferSize < numProbs * sizeof(CProb)) ++ return LZMA_RESULT_NOT_ENOUGH_MEM; ++ for (i = 0; i < numProbs; i++) ++ p[i] = kBitModelTotal >> 1; ++ RangeDecoderInit(&rd, ++ #ifdef _LZMA_IN_CB ++ inCallback ++ #else ++ inStream, inSize ++ #endif ++ ); ++#endif ++ ++ *outSizeProcessed = 0; ++ while(nowPos < outSize) ++ { ++ int posState = (int)( ++ (nowPos ++ #ifdef _LZMA_OUT_READ ++ + globalPos ++ #endif ++ ) ++ & posStateMask); ++ #ifdef _LZMA_IN_CB ++ if (rd.Result != LZMA_RESULT_OK) ++ return rd.Result; ++ #endif ++ if (rd.ExtraBytes != 0) ++ return LZMA_RESULT_DATA_ERROR; ++ if (RangeDecoderBitDecode(p + IsMatch + (state << kNumPosBitsMax) + posState, &rd) == 0) ++ { ++ CProb *probs = p + Literal + (LZMA_LIT_SIZE * ++ ((( ++ (nowPos ++ #ifdef _LZMA_OUT_READ ++ + globalPos ++ #endif ++ ) ++ & literalPosMask) << lc) + (previousByte >> (8 - lc)))); ++ ++ if (state < 4) state = 0; ++ else if (state < 10) state -= 3; ++ else state -= 6; ++ if (previousIsMatch) ++ { ++ Byte matchByte; ++ #ifdef _LZMA_OUT_READ ++ UInt32 pos = dictionaryPos - rep0; ++ if (pos >= dictionarySize) ++ pos += dictionarySize; ++ matchByte = dictionary[pos]; ++ #else ++ matchByte = outStream[nowPos - rep0]; ++ #endif ++ previousByte = LzmaLiteralDecodeMatch(probs, &rd, matchByte); ++ previousIsMatch = 0; ++ } ++ else ++ previousByte = LzmaLiteralDecode(probs, &rd); ++ outStream[nowPos++] = previousByte; ++ #ifdef _LZMA_OUT_READ ++ dictionary[dictionaryPos] = previousByte; ++ if (++dictionaryPos == dictionarySize) ++ dictionaryPos = 0; ++ #endif ++ } ++ else ++ { ++ previousIsMatch = 1; ++ if (RangeDecoderBitDecode(p + IsRep + state, &rd) == 1) ++ { ++ if (RangeDecoderBitDecode(p + IsRepG0 + state, &rd) == 0) ++ { ++ if (RangeDecoderBitDecode(p + IsRep0Long + (state << kNumPosBitsMax) + posState, &rd) == 0) ++ { ++ #ifdef _LZMA_OUT_READ ++ UInt32 pos; ++ #endif ++ if ( ++ (nowPos ++ #ifdef _LZMA_OUT_READ ++ + globalPos ++ #endif ++ ) ++ == 0) ++ return LZMA_RESULT_DATA_ERROR; ++ state = state < 7 ? 9 : 11; ++ #ifdef _LZMA_OUT_READ ++ pos = dictionaryPos - rep0; ++ if (pos >= dictionarySize) ++ pos += dictionarySize; ++ previousByte = dictionary[pos]; ++ dictionary[dictionaryPos] = previousByte; ++ if (++dictionaryPos == dictionarySize) ++ dictionaryPos = 0; ++ #else ++ previousByte = outStream[nowPos - rep0]; ++ #endif ++ outStream[nowPos++] = previousByte; ++ continue; ++ } ++ } ++ else ++ { ++ UInt32 distance; ++ if(RangeDecoderBitDecode(p + IsRepG1 + state, &rd) == 0) ++ distance = rep1; ++ else ++ { ++ if(RangeDecoderBitDecode(p + IsRepG2 + state, &rd) == 0) ++ distance = rep2; ++ else ++ { ++ distance = rep3; ++ rep3 = rep2; ++ } ++ rep2 = rep1; ++ } ++ rep1 = rep0; ++ rep0 = distance; ++ } ++ len = LzmaLenDecode(p + RepLenCoder, &rd, posState); ++ state = state < 7 ? 8 : 11; ++ } ++ else ++ { ++ int posSlot; ++ rep3 = rep2; ++ rep2 = rep1; ++ rep1 = rep0; ++ state = state < 7 ? 7 : 10; ++ len = LzmaLenDecode(p + LenCoder, &rd, posState); ++ posSlot = RangeDecoderBitTreeDecode(p + PosSlot + ++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << ++ kNumPosSlotBits), kNumPosSlotBits, &rd); ++ if (posSlot >= kStartPosModelIndex) ++ { ++ int numDirectBits = ((posSlot >> 1) - 1); ++ rep0 = ((2 | ((UInt32)posSlot & 1)) << numDirectBits); ++ if (posSlot < kEndPosModelIndex) ++ { ++ rep0 += RangeDecoderReverseBitTreeDecode( ++ p + SpecPos + rep0 - posSlot - 1, numDirectBits, &rd); ++ } ++ else ++ { ++ rep0 += RangeDecoderDecodeDirectBits(&rd, ++ numDirectBits - kNumAlignBits) << kNumAlignBits; ++ rep0 += RangeDecoderReverseBitTreeDecode(p + Align, kNumAlignBits, &rd); ++ } ++ } ++ else ++ rep0 = posSlot; ++ rep0++; ++ } ++ if (rep0 == (UInt32)(0)) ++ { ++ /* it's for stream version */ ++ len = -1; ++ break; ++ } ++ if (rep0 > nowPos ++ #ifdef _LZMA_OUT_READ ++ + globalPos ++ #endif ++ ) ++ { ++ return LZMA_RESULT_DATA_ERROR; ++ } ++ len += kMatchMinLen; ++ do ++ { ++ #ifdef _LZMA_OUT_READ ++ UInt32 pos = dictionaryPos - rep0; ++ if (pos >= dictionarySize) ++ pos += dictionarySize; ++ previousByte = dictionary[pos]; ++ dictionary[dictionaryPos] = previousByte; ++ if (++dictionaryPos == dictionarySize) ++ dictionaryPos = 0; ++ #else ++ previousByte = outStream[nowPos - rep0]; ++ #endif ++ outStream[nowPos++] = previousByte; ++ len--; ++ } ++ while(len > 0 && nowPos < outSize); ++ } ++ } ++ ++ #ifdef _LZMA_OUT_READ ++ vs->RangeDecoder = rd; ++ vs->DictionaryPos = dictionaryPos; ++ vs->GlobalPos = globalPos + nowPos; ++ vs->Reps[0] = rep0; ++ vs->Reps[1] = rep1; ++ vs->Reps[2] = rep2; ++ vs->Reps[3] = rep3; ++ vs->State = state; ++ vs->PreviousIsMatch = previousIsMatch; ++ vs->RemainLen = len; ++ #endif ++ ++ *outSizeProcessed = nowPos; ++ return LZMA_RESULT_OK; ++} +diff -Nur linux-2.6.16/fs/squashfs/LzmaDecode.h linux-2.6.16-owrt/fs/squashfs/LzmaDecode.h +--- linux-2.6.16/fs/squashfs/LzmaDecode.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/fs/squashfs/LzmaDecode.h 2006-03-21 10:56:57.000000000 +0100 +@@ -0,0 +1,100 @@ ++/* ++ LzmaDecode.h ++ LZMA Decoder interface ++ ++ LZMA SDK 4.05 Copyright (c) 1999-2004 Igor Pavlov (2004-08-25) ++ http://www.7-zip.org/ ++ ++ LZMA SDK is licensed under two licenses: ++ 1) GNU Lesser General Public License (GNU LGPL) ++ 2) Common Public License (CPL) ++ It means that you can select one of these two licenses and ++ follow rules of that license. ++ ++ SPECIAL EXCEPTION: ++ Igor Pavlov, as the author of this code, expressly permits you to ++ statically or dynamically link your code (or bind by name) to the ++ interfaces of this file without subjecting your linked code to the ++ terms of the CPL or GNU LGPL. Any modifications or additions ++ to this file, however, are subject to the LGPL or CPL terms. ++*/ ++ ++#ifndef __LZMADECODE_H ++#define __LZMADECODE_H ++ ++/* #define _LZMA_IN_CB */ ++/* Use callback for input data */ ++ ++/* #define _LZMA_OUT_READ */ ++/* Use read function for output data */ ++ ++/* #define _LZMA_PROB32 */ ++/* It can increase speed on some 32-bit CPUs, ++ but memory usage will be doubled in that case */ ++ ++/* #define _LZMA_LOC_OPT */ ++/* Enable local speed optimizations inside code */ ++ ++#ifndef UInt32 ++#ifdef _LZMA_UINT32_IS_ULONG ++#define UInt32 unsigned long ++#else ++#define UInt32 unsigned int ++#endif ++#endif ++ ++#ifdef _LZMA_PROB32 ++#define CProb UInt32 ++#else ++#define CProb unsigned short ++#endif ++ ++#define LZMA_RESULT_OK 0 ++#define LZMA_RESULT_DATA_ERROR 1 ++#define LZMA_RESULT_NOT_ENOUGH_MEM 2 ++ ++#ifdef _LZMA_IN_CB ++typedef struct _ILzmaInCallback ++{ ++ int (*Read)(void *object, unsigned char **buffer, UInt32 *bufferSize); ++} ILzmaInCallback; ++#endif ++ ++#define LZMA_BASE_SIZE 1846 ++#define LZMA_LIT_SIZE 768 ++ ++/* ++bufferSize = (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << (lc + lp)))* sizeof(CProb) ++bufferSize += 100 in case of _LZMA_OUT_READ ++by default CProb is unsigned short, ++but if specify _LZMA_PROB_32, CProb will be UInt32(unsigned int) ++*/ ++ ++#ifdef _LZMA_OUT_READ ++int LzmaDecoderInit( ++ unsigned char *buffer, UInt32 bufferSize, ++ int lc, int lp, int pb, ++ unsigned char *dictionary, UInt32 dictionarySize, ++ #ifdef _LZMA_IN_CB ++ ILzmaInCallback *inCallback ++ #else ++ unsigned char *inStream, UInt32 inSize ++ #endif ++); ++#endif ++ ++int LzmaDecode( ++ unsigned char *buffer, ++ #ifndef _LZMA_OUT_READ ++ UInt32 bufferSize, ++ int lc, int lp, int pb, ++ #ifdef _LZMA_IN_CB ++ ILzmaInCallback *inCallback, ++ #else ++ unsigned char *inStream, UInt32 inSize, ++ #endif ++ #endif ++ unsigned char *outStream, UInt32 outSize, ++ UInt32 *outSizeProcessed); ++ ++#endif +diff -Nur linux-2.6.16/fs/squashfs/Makefile linux-2.6.16-owrt/fs/squashfs/Makefile +--- linux-2.6.16/fs/squashfs/Makefile 2006-03-21 10:55:59.000000000 +0100 ++++ linux-2.6.16-owrt/fs/squashfs/Makefile 2006-03-21 10:57:08.000000000 +0100 +@@ -5,3 +5,4 @@ + obj-$(CONFIG_SQUASHFS) += squashfs.o + squashfs-y += inode.o + squashfs-y += squashfs2_0.o ++squashfs-y += LzmaDecode.o diff --git a/target/linux/generic-2.6/patches/004-extra_optimization.patch b/target/linux/generic-2.6/patches/004-extra_optimization.patch new file mode 100644 index 0000000..805d2f0 --- /dev/null +++ b/target/linux/generic-2.6/patches/004-extra_optimization.patch @@ -0,0 +1,12 @@ +--- linux-2.6.12.5/Makefile.old 2005-10-23 22:56:29.017270000 +0200 ++++ linux-2.6.12.5/Makefile 2005-10-23 22:57:23.226138500 +0200 +@@ -533,6 +533,9 @@ + NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include) + CHECKFLAGS += $(NOSTDINC_FLAGS) + ++# improve gcc optimization ++CFLAGS += $(call cc-option,-funit-at-a-time,) ++ + # warn about C99 declaration after statement + CFLAGS += $(call cc-option,-Wdeclaration-after-statement,) + diff --git a/target/linux/generic-2.6/patches/005-gcc4_fix.patch b/target/linux/generic-2.6/patches/005-gcc4_fix.patch new file mode 100644 index 0000000..99489b9 --- /dev/null +++ b/target/linux/generic-2.6/patches/005-gcc4_fix.patch @@ -0,0 +1,221 @@ +diff -ruN linux-2.6.15.1/include/asm-ppc/libgcc.h linux-2.6.15.1-openwrt/include/asm-ppc/libgcc.h +--- linux-2.6.15.1/include/asm-ppc/libgcc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.15.1-openwrt/include/asm-ppc/libgcc.h 2006-02-01 15:47:53.000000000 +0100 +@@ -0,0 +1,8 @@ ++#ifndef __ASM_LIBGCC_H ++#define __ASM_LIBGCC_H ++ ++#undef ARCH_NEEDS_ashldi3 ++#undef ARCH_NEEDS_ashrdi3 ++#undef ARCH_NEEDS_lshrdi3 ++ ++#endif /* __ASM_LIBGCC_H */ +diff -ruN linux-2.6.15.1/include/asm-i386/libgcc.h linux-2.6.15.1-openwrt/include/asm-i386/libgcc.h +--- linux-2.6.15.1/include/asm-i386/libgcc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.15.1-openwrt/include/asm-i386/libgcc.h 2006-02-01 15:47:53.000000000 +0100 +@@ -0,0 +1,8 @@ ++#ifndef __ASM_LIBGCC_H ++#define __ASM_LIBGCC_H ++ ++#undef ARCH_NEEDS_ashldi3 ++#undef ARCH_NEEDS_ashrdi3 ++#undef ARCH_NEEDS_lshrdi3 ++ ++#endif /* __ASM_LIBGCC_H */ +diff -ruN linux-2.6.15.1/include/asm-um/libgcc.h linux-2.6.15.1-openwrt/include/asm-i386/libgcc.h +--- linux-2.6.15.1/include/asm-um/libgcc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.15.1-openwrt/include/asm-um/libgcc.h 2006-02-01 15:47:53.000000000 +0100 +@@ -0,0 +1,8 @@ ++#ifndef __ASM_LIBGCC_H ++#define __ASM_LIBGCC_H ++ ++#undef ARCH_NEEDS_ashldi3 ++#undef ARCH_NEEDS_ashrdi3 ++#undef ARCH_NEEDS_lshrdi3 ++ ++#endif /* __ASM_LIBGCC_H */ +diff -Nur linux-2.6.15.1/include/asm-mips/libgcc.h linux-2.6.15.1-openwrt/include/asm-mips/libgcc.h +--- linux-2.6.15.1/include/asm-mips/libgcc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.15.1-openwrt/include/asm-mips/libgcc.h 2006-01-20 10:32:28.000000000 +0100 +@@ -0,0 +1,8 @@ ++#ifndef __ASM_LIBGCC_H ++#define __ASM_LIBGCC_H ++ ++#define ARCH_NEEDS_ashldi3 ++#define ARCH_NEEDS_ashrdi3 ++#define ARCH_NEEDS_lshrdi3 ++ ++#endif /* __ASM_LIBGCC_H */ +diff -Nur linux-2.6.15.1/include/linux/libgcc.h linux-2.6.15.1-openwrt/include/linux/libgcc.h +--- linux-2.6.15.1/include/linux/libgcc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.15.1-openwrt/include/linux/libgcc.h 2006-01-20 10:33:38.000000000 +0100 +@@ -0,0 +1,32 @@ ++#ifndef __LINUX_LIBGCC_H ++#define __LINUX_LIBGCC_H ++ ++#include <asm/byteorder.h> ++#include <asm/libgcc.h> ++ ++typedef long long DWtype; ++typedef int Wtype; ++typedef unsigned int UWtype; ++typedef int word_type __attribute__ ((mode (__word__))); ++ ++#define BITS_PER_UNIT 8 ++ ++#ifdef __BIG_ENDIAN ++struct DWstruct { ++ Wtype high, low; ++}; ++#elif defined(__LITTLE_ENDIAN) ++struct DWstruct { ++ Wtype low, high; ++}; ++#else ++#error I feel sick. ++#endif ++ ++typedef union ++{ ++ struct DWstruct s; ++ DWtype ll; ++} DWunion; ++ ++#endif /* __LINUX_LIBGCC_H */ +diff -Nur linux-2.6.15.1/lib/ashldi3.c linux-2.6.15.1-openwrt/lib/ashldi3.c +--- linux-2.6.15.1/lib/ashldi3.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.15.1-openwrt/lib/ashldi3.c 2006-01-20 10:38:41.000000000 +0100 +@@ -0,0 +1,32 @@ ++#include <linux/libgcc.h> ++#include <linux/module.h> ++ ++#ifdef ARCH_NEEDS_ashldi3 ++ ++DWtype __ashldi3(DWtype u, word_type b) ++{ ++ DWunion uu, w; ++ word_type bm; ++ ++ if (b == 0) ++ return u; ++ ++ uu.ll = u; ++ bm = (sizeof(Wtype) * BITS_PER_UNIT) - b; ++ ++ if (bm <= 0) { ++ w.s.low = 0; ++ w.s.high = (UWtype) uu.s.low << -bm; ++ } else { ++ const UWtype carries = (UWtype) uu.s.low >> bm; ++ ++ w.s.low = (UWtype) uu.s.low << b; ++ w.s.high = ((UWtype) uu.s.high << b) | carries; ++ } ++ ++ return w.ll; ++} ++ ++EXPORT_SYMBOL(__ashldi3); ++ ++#endif /* ARCH_NEEDS_ashldi3 */ +diff -Nur linux-2.6.15.1/lib/ashrdi3.c linux-2.6.15.1-openwrt/lib/ashrdi3.c +--- linux-2.6.15.1/lib/ashrdi3.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.15.1-openwrt/lib/ashrdi3.c 2006-01-20 10:39:29.000000000 +0100 +@@ -0,0 +1,36 @@ ++#include <linux/libgcc.h> ++#include <linux/module.h> ++ ++/* Unless shift functions are defined with full ANSI prototypes, ++ parameter b will be promoted to int if word_type is smaller than an int. */ ++#ifdef ARCH_NEEDS_ashrdi3 ++ ++DWtype __ashrdi3(DWtype u, word_type b) ++{ ++ DWunion uu, w; ++ word_type bm; ++ ++ if (b == 0) ++ return u; ++ ++ uu.ll = u; ++ bm = (sizeof(Wtype) * BITS_PER_UNIT) - b; ++ ++ if (bm <= 0) { ++ /* w.s.high = 1..1 or 0..0 */ ++ w.s.high = ++ uu.s.high >> (sizeof(Wtype) * BITS_PER_UNIT - 1); ++ w.s.low = uu.s.high >> -bm; ++ } else { ++ const UWtype carries = (UWtype) uu.s.high << bm; ++ ++ w.s.high = uu.s.high >> b; ++ w.s.low = ((UWtype) uu.s.low >> b) | carries; ++ } ++ ++ return w.ll; ++} ++ ++EXPORT_SYMBOL(__ashrdi3); ++ ++#endif /* ARCH_NEEDS_ashrdi3 */ +diff -Nur linux-2.6.15.1/lib/lshrdi3.c linux-2.6.15.1-openwrt/lib/lshrdi3.c +--- linux-2.6.15.1/lib/lshrdi3.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.15.1-openwrt/lib/lshrdi3.c 2006-01-20 10:40:10.000000000 +0100 +@@ -0,0 +1,34 @@ ++#include <linux/libgcc.h> ++#include <linux/module.h> ++ ++/* Unless shift functions are defined with full ANSI prototypes, ++ parameter b will be promoted to int if word_type is smaller than an int. */ ++#ifdef ARCH_NEEDS_lshrdi3 ++ ++DWtype __lshrdi3(DWtype u, word_type b) ++{ ++ DWunion uu, w; ++ word_type bm; ++ ++ if (b == 0) ++ return u; ++ ++ uu.ll = u; ++ bm = (sizeof(Wtype) * BITS_PER_UNIT) - b; ++ ++ if (bm <= 0) { ++ w.s.high = 0; ++ w.s.low = (UWtype) uu.s.high >> -bm; ++ } else { ++ const UWtype carries = (UWtype) uu.s.high << bm; ++ ++ w.s.high = (UWtype) uu.s.high >> b; ++ w.s.low = ((UWtype) uu.s.low >> b) | carries; ++ } ++ ++ return w.ll; ++} ++ ++EXPORT_SYMBOL(__lshrdi3); ++ ++#endif /* ARCH_NEEDS_lshrdi3 */ +diff -Nur linux-2.6.15.1/lib/Makefile linux-2.6.15.1-openwrt/lib/Makefile +--- linux-2.6.15.1/lib/Makefile 2006-01-15 07:16:02.000000000 +0100 ++++ linux-2.6.15.1-openwrt/lib/Makefile 2006-01-20 10:34:19.000000000 +0100 +@@ -8,6 +8,7 @@ + sha1.o + + lib-y += kobject.o kref.o kobject_uevent.o klist.o ++obj-y += ashldi3.o ashrdi3.o lshrdi3.o + + obj-y += sort.o parser.o halfmd4.o + +diff -Nur linux-2.6.15.1/include/asm-arm/libgcc.h linux-2.6.15.1-openwrt/include/asm-arm/libgcc.h +--- linux-2.6.15.1/include/asm-arm/libgcc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.15.1-openwrt/include/asm-arm/libgcc.h 2006-04-12 23:01:18.000000000 +0200 +@@ -0,0 +1,8 @@ ++#ifndef __ASM_LIBGCC_H ++#define __ASM_LIBGCC_H ++ ++#undef ARCH_NEEDS_ashldi3 ++#undef ARCH_NEEDS_ashrdi3 ++#undef ARCH_NEEDS_lshrdi3 ++ ++#endif /* __ASM_LIBGCC_H */ diff --git a/target/linux/generic-2.6/patches/006-gcc4_inline_fix.patch b/target/linux/generic-2.6/patches/006-gcc4_inline_fix.patch new file mode 100644 index 0000000..e95663e --- /dev/null +++ b/target/linux/generic-2.6/patches/006-gcc4_inline_fix.patch @@ -0,0 +1,11 @@ +--- linux-2.6.15.1.old/include/asm-mips/system.h 2006-01-28 15:02:54.481032280 +0100 ++++ linux-2.6.15.1.dev/include/asm-mips/system.h 2006-01-28 14:47:51.634285848 +0100 +@@ -273,7 +273,7 @@ + if something tries to do an invalid xchg(). */ + extern void __xchg_called_with_bad_pointer(void); + +-static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size) ++static __always_inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size) + { + switch (size) { + case 4: diff --git a/target/linux/generic-2.6/patches/010-disable_old_squashfs_compatibility.patch b/target/linux/generic-2.6/patches/010-disable_old_squashfs_compatibility.patch new file mode 100644 index 0000000..1b8b61a --- /dev/null +++ b/target/linux/generic-2.6/patches/010-disable_old_squashfs_compatibility.patch @@ -0,0 +1,22 @@ +diff -Nur linux-2.6.16/fs/squashfs/Makefile linux-2.6.16-owrt/fs/squashfs/Makefile +--- linux-2.6.16/fs/squashfs/Makefile 2006-03-21 13:50:31.000000000 +0100 ++++ linux-2.6.16-owrt/fs/squashfs/Makefile 2006-03-21 13:51:09.000000000 +0100 +@@ -4,5 +4,4 @@ + + obj-$(CONFIG_SQUASHFS) += squashfs.o + squashfs-y += inode.o +-squashfs-y += squashfs2_0.o + squashfs-y += LzmaDecode.o +diff -Nur linux-2.6.16/fs/squashfs/squashfs.h linux-2.6.16-owrt/fs/squashfs/squashfs.h +--- linux-2.6.16/fs/squashfs/squashfs.h 2006-03-21 10:55:59.000000000 +0100 ++++ linux-2.6.16-owrt/fs/squashfs/squashfs.h 2006-03-21 13:50:58.000000000 +0100 +@@ -24,6 +24,9 @@ + #ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY + #undef CONFIG_SQUASHFS_1_0_COMPATIBILITY + #endif ++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++#undef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++#endif + + #ifdef SQUASHFS_TRACE + #define TRACE(s, args...) printk(KERN_NOTICE "SQUASHFS: "s, ## args) diff --git a/target/linux/generic-2.6/patches/011-mips_boot.patch b/target/linux/generic-2.6/patches/011-mips_boot.patch new file mode 100644 index 0000000..611c70d --- /dev/null +++ b/target/linux/generic-2.6/patches/011-mips_boot.patch @@ -0,0 +1,15 @@ +diff -Nur linux-cvs/arch/mips/kernel/head.S linux-aruba/arch/mips/kernel/head.S +--- linux-cvs/arch/mips/kernel/head.S 2004-12-23 00:21:39.000000000 -0800 ++++ linux-aruba/arch/mips/kernel/head.S 2005-10-20 09:16:08.000000000 -0700 +@@ -122,6 +122,10 @@ + #endif + .endm + ++ ++ j kernel_entry ++ nop ++ + /* + * Reserved space for exception handlers. + * Necessary for machines which link their kernels at KSEG0. + diff --git a/target/linux/generic-2.6/patches/100-netfilter_layer7_2.1nbd.patch b/target/linux/generic-2.6/patches/100-netfilter_layer7_2.1nbd.patch new file mode 100644 index 0000000..ec3929b --- /dev/null +++ b/target/linux/generic-2.6/patches/100-netfilter_layer7_2.1nbd.patch @@ -0,0 +1,2071 @@ +diff -Nur linux-2.6.17/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.6.17-owrt/include/linux/netfilter_ipv4/ip_conntrack.h +--- linux-2.6.17/include/linux/netfilter_ipv4/ip_conntrack.h 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-owrt/include/linux/netfilter_ipv4/ip_conntrack.h 2006-06-18 12:29:04.000000000 +0200 +@@ -124,6 +124,15 @@ + /* Traversed often, so hopefully in different cacheline to top */ + /* These are my tuples; original and reply */ + struct ip_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX]; ++ ++#if defined(CONFIG_IP_NF_MATCH_LAYER7) || defined(CONFIG_IP_NF_MATCH_LAYER7_MODULE) ++ struct { ++ char * app_proto; /* e.g. "http". NULL before decision. "unknown" after decision if no match */ ++ char * app_data; /* application layer data so far. NULL after match decision */ ++ unsigned int app_data_len; ++ } layer7; ++#endif ++ + }; + + struct ip_conntrack_expect +diff -Nur linux-2.6.17/include/linux/netfilter_ipv4/ipt_layer7.h linux-2.6.17-owrt/include/linux/netfilter_ipv4/ipt_layer7.h +--- linux-2.6.17/include/linux/netfilter_ipv4/ipt_layer7.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.17-owrt/include/linux/netfilter_ipv4/ipt_layer7.h 2006-06-18 12:29:04.000000000 +0200 +@@ -0,0 +1,27 @@ ++/* ++ By Matthew Strait <quadong@users.sf.net>, Dec 2003. ++ http://l7-filter.sf.net ++ ++ This program is free software; you can redistribute it and/or ++ modify it under the terms of the GNU General Public License ++ as published by the Free Software Foundation; either version ++ 2 of the License, or (at your option) any later version. ++ http://www.gnu.org/licenses/gpl.txt ++*/ ++ ++#ifndef _IPT_LAYER7_H ++#define _IPT_LAYER7_H ++ ++#define MAX_PATTERN_LEN 8192 ++#define MAX_PROTOCOL_LEN 256 ++ ++typedef char *(*proc_ipt_search) (char *, char, char *); ++ ++struct ipt_layer7_info { ++ char protocol[MAX_PROTOCOL_LEN]; ++ char invert:1; ++ char pattern[MAX_PATTERN_LEN]; ++ char pkt; ++}; ++ ++#endif /* _IPT_LAYER7_H */ +diff -Nur linux-2.6.17/net/ipv4/netfilter/ip_conntrack_core.c linux-2.6.17-owrt/net/ipv4/netfilter/ip_conntrack_core.c +--- linux-2.6.17/net/ipv4/netfilter/ip_conntrack_core.c 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-owrt/net/ipv4/netfilter/ip_conntrack_core.c 2006-06-18 12:29:04.000000000 +0200 +@@ -339,6 +339,13 @@ + * too. */ + ip_ct_remove_expectations(ct); + ++ #if defined(CONFIG_IP_NF_MATCH_LAYER7) || defined(CONFIG_IP_NF_MATCH_LAYER7_MODULE) ++ if(ct->layer7.app_proto) ++ kfree(ct->layer7.app_proto); ++ if(ct->layer7.app_data) ++ kfree(ct->layer7.app_data); ++ #endif ++ + /* We overload first tuple to link into unconfirmed list. */ + if (!is_confirmed(ct)) { + BUG_ON(list_empty(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list)); +diff -Nur linux-2.6.17/net/ipv4/netfilter/ip_conntrack_standalone.c linux-2.6.17-owrt/net/ipv4/netfilter/ip_conntrack_standalone.c +--- linux-2.6.17/net/ipv4/netfilter/ip_conntrack_standalone.c 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-owrt/net/ipv4/netfilter/ip_conntrack_standalone.c 2006-06-18 12:29:04.000000000 +0200 +@@ -189,6 +189,12 @@ + return -ENOSPC; + #endif + ++#if defined(CONFIG_IP_NF_MATCH_LAYER7) || defined(CONFIG_IP_NF_MATCH_LAYER7_MODULE) ++ if(conntrack->layer7.app_proto) ++ if (seq_printf(s, "l7proto=%s ",conntrack->layer7.app_proto)) ++ return 1; ++#endif ++ + if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use))) + return -ENOSPC; + +diff -Nur linux-2.6.17/net/ipv4/netfilter/ipt_layer7.c linux-2.6.17-owrt/net/ipv4/netfilter/ipt_layer7.c +--- linux-2.6.17/net/ipv4/netfilter/ipt_layer7.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.17-owrt/net/ipv4/netfilter/ipt_layer7.c 2006-06-18 12:29:04.000000000 +0200 +@@ -0,0 +1,592 @@ ++/* ++ Kernel module to match application layer (OSI layer 7) ++ data in connections. ++ ++ http://l7-filter.sf.net ++ ++ By Matthew Strait and Ethan Sommer, 2003-2005. ++ ++ This program is free software; you can redistribute it and/or ++ modify it under the terms of the GNU General Public License ++ as published by the Free Software Foundation; either version ++ 2 of the License, or (at your option) any later version. ++ http://www.gnu.org/licenses/gpl.txt ++ ++ Based on ipt_string.c (C) 2000 Emmanuel Roger <winfield@freegates.be> ++ and cls_layer7.c (C) 2003 Matthew Strait, Ethan Sommer, Justin Levandoski ++*/ ++ ++#include <linux/module.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter_ipv4/ip_conntrack.h> ++#include <linux/proc_fs.h> ++#include <linux/ctype.h> ++#include <net/ip.h> ++#include <net/tcp.h> ++#include <linux/spinlock.h> ++ ++#include "regexp/regexp.c" ++ ++#include <linux/netfilter_ipv4/ipt_layer7.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++ ++MODULE_AUTHOR("Matthew Strait <quadong@users.sf.net>, Ethan Sommer <sommere@users.sf.net>"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("iptables application layer match module"); ++MODULE_VERSION("2.0"); ++ ++static int maxdatalen = 2048; // this is the default ++module_param(maxdatalen, int, 0444); ++MODULE_PARM_DESC(maxdatalen, "maximum bytes of data looked at by l7-filter"); ++ ++#ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG ++ #define DPRINTK(format,args...) printk(format,##args) ++#else ++ #define DPRINTK(format,args...) ++#endif ++ ++#define TOTAL_PACKETS master_conntrack->counters[IP_CT_DIR_ORIGINAL].packets + \ ++ master_conntrack->counters[IP_CT_DIR_REPLY].packets ++ ++/* Number of packets whose data we look at. ++This can be modified through /proc/net/layer7_numpackets */ ++static int num_packets = 10; ++ ++static struct pattern_cache { ++ char * regex_string; ++ regexp * pattern; ++ struct pattern_cache * next; ++} * first_pattern_cache = NULL; ++ ++/* I'm new to locking. Here are my assumptions: ++ ++- No one will write to /proc/net/layer7_numpackets over and over very fast; ++ if they did, nothing awful would happen. ++ ++- This code will never be processing the same packet twice at the same time, ++ because iptables rules are traversed in order. ++ ++- It doesn't matter if two packets from different connections are in here at ++ the same time, because they don't share any data. ++ ++- It _does_ matter if two packets from the same connection are here at the same ++ time. In this case, we have to protect the conntracks and the list of ++ compiled patterns. ++*/ ++DEFINE_RWLOCK(ct_lock); ++DEFINE_SPINLOCK(list_lock); ++ ++#ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG ++/* Converts an unfriendly string into a friendly one by ++replacing unprintables with periods and all whitespace with " ". */ ++static char * friendly_print(unsigned char * s) ++{ ++ char * f = kmalloc(strlen(s) + 1, GFP_ATOMIC); ++ int i; ++ ++ if(!f) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in friendly_print, bailing.\n"); ++ return NULL; ++ } ++ ++ for(i = 0; i < strlen(s); i++){ ++ if(isprint(s[i]) && s[i] < 128) f[i] = s[i]; ++ else if(isspace(s[i])) f[i] = ' '; ++ else f[i] = '.'; ++ } ++ f[i] = '\0'; ++ return f; ++} ++ ++static char dec2hex(int i) ++{ ++ switch (i) { ++ case 0 ... 9: ++ return (char)(i + '0'); ++ break; ++ case 10 ... 15: ++ return (char)(i - 10 + 'a'); ++ break; ++ default: ++ if (net_ratelimit()) ++ printk("Problem in dec2hex\n"); ++ return '\0'; ++ } ++} ++ ++static char * hex_print(unsigned char * s) ++{ ++ char * g = kmalloc(strlen(s)*3 + 1, GFP_ATOMIC); ++ int i; ++ ++ if(!g) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in hex_print, bailing.\n"); ++ return NULL; ++ } ++ ++ for(i = 0; i < strlen(s); i++) { ++ g[i*3 ] = dec2hex(s[i]/16); ++ g[i*3 + 1] = dec2hex(s[i]%16); ++ g[i*3 + 2] = ' '; ++ } ++ g[i*3] = '\0'; ++ ++ return g; ++} ++#endif // DEBUG ++ ++/* Use instead of regcomp. As we expect to be seeing the same regexps over and ++over again, it make sense to cache the results. */ ++static regexp * compile_and_cache(char * regex_string, char * protocol) ++{ ++ struct pattern_cache * node = first_pattern_cache; ++ struct pattern_cache * last_pattern_cache = first_pattern_cache; ++ struct pattern_cache * tmp; ++ unsigned int len; ++ ++ while (node != NULL) { ++ if (!strcmp(node->regex_string, regex_string)) ++ return node->pattern; ++ ++ last_pattern_cache = node;/* points at the last non-NULL node */ ++ node = node->next; ++ } ++ ++ /* If we reach the end of the list, then we have not yet cached ++ the pattern for this regex. Let's do that now. ++ Be paranoid about running out of memory to avoid list corruption. */ ++ tmp = kmalloc(sizeof(struct pattern_cache), GFP_ATOMIC); ++ ++ if(!tmp) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in compile_and_cache, bailing.\n"); ++ return NULL; ++ } ++ ++ tmp->regex_string = kmalloc(strlen(regex_string) + 1, GFP_ATOMIC); ++ tmp->pattern = kmalloc(sizeof(struct regexp), GFP_ATOMIC); ++ tmp->next = NULL; ++ ++ if(!tmp->regex_string || !tmp->pattern) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in compile_and_cache, bailing.\n"); ++ kfree(tmp->regex_string); ++ kfree(tmp->pattern); ++ kfree(tmp); ++ return NULL; ++ } ++ ++ /* Ok. The new node is all ready now. */ ++ node = tmp; ++ ++ if(first_pattern_cache == NULL) /* list is empty */ ++ first_pattern_cache = node; /* make node the beginning */ ++ else ++ last_pattern_cache->next = node; /* attach node to the end */ ++ ++ /* copy the string and compile the regex */ ++ len = strlen(regex_string); ++ DPRINTK("About to compile this: \"%s\"\n", regex_string); ++ node->pattern = regcomp(regex_string, &len); ++ if ( !node->pattern ) { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: Error compiling regexp \"%s\" (%s)\n", regex_string, protocol); ++ /* pattern is now cached as NULL, so we won't try again. */ ++ } ++ ++ strcpy(node->regex_string, regex_string); ++ return node->pattern; ++} ++ ++static int can_handle(const struct sk_buff *skb) ++{ ++ if(!skb->nh.iph) /* not IP */ ++ return 0; ++ if(skb->nh.iph->protocol != IPPROTO_TCP && ++ skb->nh.iph->protocol != IPPROTO_UDP && ++ skb->nh.iph->protocol != IPPROTO_ICMP) ++ return 0; ++ return 1; ++} ++ ++/* Returns offset the into the skb->data that the application data starts */ ++static int app_data_offset(const struct sk_buff *skb) ++{ ++ /* In case we are ported somewhere (ebtables?) where skb->nh.iph ++ isn't set, this can be gotten from 4*(skb->data[0] & 0x0f) as well. */ ++ int ip_hl = 4*skb->nh.iph->ihl; ++ ++ if( skb->nh.iph->protocol == IPPROTO_TCP ) { ++ /* 12 == offset into TCP header for the header length field. ++ Can't get this with skb->h.th->doff because the tcphdr ++ struct doesn't get set when routing (this is confirmed to be ++ true in Netfilter as well as QoS.) */ ++ int tcp_hl = 4*(skb->data[ip_hl + 12] >> 4); ++ ++ return ip_hl + tcp_hl; ++ } else if( skb->nh.iph->protocol == IPPROTO_UDP ) { ++ return ip_hl + 8; /* UDP header is always 8 bytes */ ++ } else if( skb->nh.iph->protocol == IPPROTO_ICMP ) { ++ return ip_hl + 8; /* ICMP header is 8 bytes */ ++ } else { ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: tried to handle unknown protocol!\n"); ++ return ip_hl + 8; /* something reasonable */ ++ } ++} ++ ++/* handles whether there's a match when we aren't appending data anymore */ ++static int match_no_append(struct ip_conntrack * conntrack, struct ip_conntrack * master_conntrack, ++ enum ip_conntrack_info ctinfo, enum ip_conntrack_info master_ctinfo, ++ struct ipt_layer7_info * info) ++{ ++ /* If we're in here, throw the app data away */ ++ write_lock(&ct_lock); ++ if(master_conntrack->layer7.app_data != NULL) { ++ ++ #ifdef CONFIG_IP_NF_MATCH_LAYER7_DEBUG ++ if(!master_conntrack->layer7.app_proto) { ++ char * f = friendly_print(master_conntrack->layer7.app_data); ++ char * g = hex_print(master_conntrack->layer7.app_data); ++ DPRINTK("\nl7-filter gave up after %d bytes (%llu packets):\n%s\n", ++ strlen(f), ++ TOTAL_PACKETS, f); ++ kfree(f); ++ DPRINTK("In hex: %s\n", g); ++ kfree(g); ++ } ++ #endif ++ ++ kfree(master_conntrack->layer7.app_data); ++ master_conntrack->layer7.app_data = NULL; /* don't free again */ ++ } ++ write_unlock(&ct_lock); ++ ++ if(master_conntrack->layer7.app_proto){ ++ /* Here child connections set their .app_proto (for /proc/net/ip_conntrack) */ ++ write_lock(&ct_lock); ++ if(!conntrack->layer7.app_proto) { ++ conntrack->layer7.app_proto = kmalloc(strlen(master_conntrack->layer7.app_proto)+1, GFP_ATOMIC); ++ if(!conntrack->layer7.app_proto){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in match_no_append, bailing.\n"); ++ write_unlock(&ct_lock); ++ return 1; ++ } ++ strcpy(conntrack->layer7.app_proto, master_conntrack->layer7.app_proto); ++ } ++ write_unlock(&ct_lock); ++ ++ return (!strcmp(master_conntrack->layer7.app_proto, info->protocol)); ++ } ++ else { ++ /* If not classified, set to "unknown" to distinguish from ++ connections that are still being tested. */ ++ write_lock(&ct_lock); ++ master_conntrack->layer7.app_proto = kmalloc(strlen("unknown")+1, GFP_ATOMIC); ++ if(!master_conntrack->layer7.app_proto){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in match_no_append, bailing.\n"); ++ write_unlock(&ct_lock); ++ return 1; ++ } ++ strcpy(master_conntrack->layer7.app_proto, "unknown"); ++ write_unlock(&ct_lock); ++ return 0; ++ } ++} ++ ++static int add_datastr(char *target, int offset, char *app_data, int len) ++{ ++ int length = 0, i; ++ ++ /* Strip nulls. Make everything lower case (our regex lib doesn't ++ do case insensitivity). Add it to the end of the current data. */ ++ for(i = 0; i < maxdatalen-offset-1 && i < len; i++) { ++ if(app_data[i] != '\0') { ++ target[length+offset] = ++ /* the kernel version of tolower mungs 'upper ascii' */ ++ isascii(app_data[i])? tolower(app_data[i]) : app_data[i]; ++ length++; ++ } ++ } ++ target[length+offset] = '\0'; ++ ++ return length; ++} ++ ++/* add the new app data to the conntrack. Return number of bytes added. */ ++static int add_data(struct ip_conntrack * master_conntrack, ++ char * app_data, int appdatalen) ++{ ++ int length; ++ ++ length = add_datastr(master_conntrack->layer7.app_data, master_conntrack->layer7.app_data_len, app_data, appdatalen); ++ master_conntrack->layer7.app_data_len += length; ++ ++ return length; ++} ++ ++/* Returns true on match and false otherwise. */ ++static int match(/* const */struct sk_buff *skb, const struct net_device *in, ++ const struct net_device *out, const void *matchinfo, ++ int offset, int *hotdrop) ++{ ++ struct ipt_layer7_info * info = (struct ipt_layer7_info *)matchinfo; ++ enum ip_conntrack_info master_ctinfo, ctinfo; ++ struct ip_conntrack *master_conntrack, *conntrack; ++ unsigned char *app_data, *tmp_data; ++ unsigned int pattern_result, appdatalen; ++ regexp * comppattern; ++ ++ if(!can_handle(skb)){ ++ DPRINTK("layer7: This is some protocol I can't handle.\n"); ++ return info->invert; ++ } ++ ++ /* Treat parent & all its children together as one connection, except ++ for the purpose of setting conntrack->layer7.app_proto in the actual ++ connection. This makes /proc/net/ip_conntrack more satisfying. */ ++ if(!(conntrack = ip_conntrack_get((struct sk_buff *)skb, &ctinfo)) || ++ !(master_conntrack = ip_conntrack_get((struct sk_buff *)skb, &master_ctinfo))) { ++ //DPRINTK("layer7: packet is not from a known connection, giving up.\n"); ++ return info->invert; ++ } ++ ++ /* Try to get a master conntrack (and its master etc) for FTP, etc. */ ++ while (master_ct(master_conntrack) != NULL) ++ master_conntrack = master_ct(master_conntrack); ++ ++ /* if we've classified it or seen too many packets */ ++ if(!info->pkt && (TOTAL_PACKETS > num_packets || ++ master_conntrack->layer7.app_proto)) { ++ ++ pattern_result = match_no_append(conntrack, master_conntrack, ctinfo, master_ctinfo, info); ++ ++ /* skb->cb[0] == seen. Avoid doing things twice if there are two l7 ++ rules. I'm not sure that using cb for this purpose is correct, although ++ it says "put your private variables there". But it doesn't look like it ++ is being used for anything else in the skbs that make it here. How can ++ I write to cb without making the compiler angry? */ ++ skb->cb[0] = 1; /* marking it seen here is probably irrelevant, but consistant */ ++ ++ return (pattern_result ^ info->invert); ++ } ++ ++ if(skb_is_nonlinear(skb)){ ++ if(skb_linearize(skb, GFP_ATOMIC) != 0){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: failed to linearize packet, bailing.\n"); ++ return info->invert; ++ } ++ } ++ ++ /* now that the skb is linearized, it's safe to set these. */ ++ app_data = skb->data + app_data_offset(skb); ++ appdatalen = skb->tail - app_data; ++ ++ spin_lock_bh(&list_lock); ++ /* the return value gets checked later, when we're ready to use it */ ++ comppattern = compile_and_cache(info->pattern, info->protocol); ++ spin_unlock_bh(&list_lock); ++ ++ if (info->pkt) { ++ tmp_data = kmalloc(maxdatalen, GFP_ATOMIC); ++ if(!tmp_data){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in match, bailing.\n"); ++ return info->invert; ++ } ++ ++ tmp_data[0] = '\0'; ++ add_datastr(tmp_data, 0, app_data, appdatalen); ++ pattern_result = ((comppattern && regexec(comppattern, tmp_data)) ? 1 : 0); ++ kfree(tmp_data); ++ tmp_data = NULL; ++ ++ return (pattern_result ^ info->invert); ++ } ++ ++ /* On the first packet of a connection, allocate space for app data */ ++ write_lock(&ct_lock); ++ if(TOTAL_PACKETS == 1 && !skb->cb[0] && !master_conntrack->layer7.app_data) { ++ master_conntrack->layer7.app_data = kmalloc(maxdatalen, GFP_ATOMIC); ++ if(!master_conntrack->layer7.app_data){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in match, bailing.\n"); ++ write_unlock(&ct_lock); ++ return info->invert; ++ } ++ ++ master_conntrack->layer7.app_data[0] = '\0'; ++ } ++ write_unlock(&ct_lock); ++ ++ /* Can be here, but unallocated, if numpackets is increased near ++ the beginning of a connection */ ++ if(master_conntrack->layer7.app_data == NULL) ++ return (info->invert); /* unmatched */ ++ ++ if(!skb->cb[0]){ ++ int newbytes; ++ write_lock(&ct_lock); ++ newbytes = add_data(master_conntrack, app_data, appdatalen); ++ write_unlock(&ct_lock); ++ ++ if(newbytes == 0) { /* didn't add any data */ ++ skb->cb[0] = 1; ++ /* Didn't match before, not going to match now */ ++ return info->invert; ++ } ++ } ++ ++ /* If looking for "unknown", then never match. "Unknown" means that ++ we've given up; we're still trying with these packets. */ ++ if(!strcmp(info->protocol, "unknown")) { ++ pattern_result = 0; ++ /* If the regexp failed to compile, don't bother running it */ ++ } else if(comppattern && regexec(comppattern, master_conntrack->layer7.app_data)) { ++ DPRINTK("layer7: matched %s\n", info->protocol); ++ pattern_result = 1; ++ } else pattern_result = 0; ++ ++ if(pattern_result) { ++ write_lock(&ct_lock); ++ master_conntrack->layer7.app_proto = kmalloc(strlen(info->protocol)+1, GFP_ATOMIC); ++ if(!master_conntrack->layer7.app_proto){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory in match, bailing.\n"); ++ write_unlock(&ct_lock); ++ return (pattern_result ^ info->invert); ++ } ++ strcpy(master_conntrack->layer7.app_proto, info->protocol); ++ write_unlock(&ct_lock); ++ } ++ ++ /* mark the packet seen */ ++ skb->cb[0] = 1; ++ ++ return (pattern_result ^ info->invert); ++} ++ ++static int checkentry(const char *tablename, const struct ipt_ip *ip, ++ void *matchinfo, unsigned int matchsize, unsigned int hook_mask) ++{ ++ if (matchsize != IPT_ALIGN(sizeof(struct ipt_layer7_info))) ++ return 0; ++ return 1; ++} ++ ++static struct ipt_match layer7_match = { ++ .name = "layer7", ++ .match = &match, ++ .checkentry = &checkentry, ++ .me = THIS_MODULE ++}; ++ ++/* taken from drivers/video/modedb.c */ ++static int my_atoi(const char *s) ++{ ++ int val = 0; ++ ++ for (;; s++) { ++ switch (*s) { ++ case '0'...'9': ++ val = 10*val+(*s-'0'); ++ break; ++ default: ++ return val; ++ } ++ } ++} ++ ++/* write out num_packets to userland. */ ++static int layer7_read_proc(char* page, char ** start, off_t off, int count, ++ int* eof, void * data) ++{ ++ if(num_packets > 99 && net_ratelimit()) ++ printk(KERN_ERR "layer7: NOT REACHED. num_packets too big\n"); ++ ++ page[0] = num_packets/10 + '0'; ++ page[1] = num_packets%10 + '0'; ++ page[2] = '\n'; ++ page[3] = '\0'; ++ ++ *eof=1; ++ ++ return 3; ++} ++ ++/* Read in num_packets from userland */ ++static int layer7_write_proc(struct file* file, const char* buffer, ++ unsigned long count, void *data) ++{ ++ char * foo = kmalloc(count, GFP_ATOMIC); ++ ++ if(!foo){ ++ if (net_ratelimit()) ++ printk(KERN_ERR "layer7: out of memory, bailing. num_packets unchanged.\n"); ++ return count; ++ } ++ ++ if(copy_from_user(foo, buffer, count)) { ++ return -EFAULT; ++ } ++ ++ ++ num_packets = my_atoi(foo); ++ kfree (foo); ++ ++ /* This has an arbitrary limit to make the math easier. I'm lazy. ++ But anyway, 99 is a LOT! If you want more, you're doing it wrong! */ ++ if(num_packets > 99) { ++ printk(KERN_WARNING "layer7: num_packets can't be > 99.\n"); ++ num_packets = 99; ++ } else if(num_packets < 1) { ++ printk(KERN_WARNING "layer7: num_packets can't be < 1.\n"); ++ num_packets = 1; ++ } ++ ++ return count; ++} ++ ++/* register the proc file */ ++static void layer7_init_proc(void) ++{ ++ struct proc_dir_entry* entry; ++ entry = create_proc_entry("layer7_numpackets", 0644, proc_net); ++ entry->read_proc = layer7_read_proc; ++ entry->write_proc = layer7_write_proc; ++} ++ ++static void layer7_cleanup_proc(void) ++{ ++ remove_proc_entry("layer7_numpackets", proc_net); ++} ++ ++static int __init init(void) ++{ ++ layer7_init_proc(); ++ if(maxdatalen < 1) { ++ printk(KERN_WARNING "layer7: maxdatalen can't be < 1, using 1\n"); ++ maxdatalen = 1; ++ } ++ /* This is not a hard limit. It's just here to prevent people from ++ bringing their slow machines to a grinding halt. */ ++ else if(maxdatalen > 65536) { ++ printk(KERN_WARNING "layer7: maxdatalen can't be > 65536, using 65536\n"); ++ maxdatalen = 65536; ++ } ++ return ipt_register_match(&layer7_match); ++} ++ ++static void __exit fini(void) ++{ ++ layer7_cleanup_proc(); ++ ipt_unregister_match(&layer7_match); ++} ++ ++module_init(init); ++module_exit(fini); +diff -Nur linux-2.6.17/net/ipv4/netfilter/Kconfig linux-2.6.17-owrt/net/ipv4/netfilter/Kconfig +--- linux-2.6.17/net/ipv4/netfilter/Kconfig 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-owrt/net/ipv4/netfilter/Kconfig 2006-06-18 12:32:58.000000000 +0200 +@@ -314,6 +314,24 @@ + destination IP' or `500pps from any given source IP' with a single + IPtables rule. + ++config IP_NF_MATCH_LAYER7 ++ tristate "Layer 7 match support (EXPERIMENTAL)" ++ depends on IP_NF_IPTABLES && IP_NF_CT_ACCT && IP_NF_CONNTRACK && EXPERIMENTAL ++ help ++ Say Y if you want to be able to classify connections (and their ++ packets) based on regular expression matching of their application ++ layer data. This is one way to classify applications such as ++ peer-to-peer filesharing systems that do not always use the same ++ port. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_MATCH_LAYER7_DEBUG ++ bool "Layer 7 debugging output" ++ depends on IP_NF_MATCH_LAYER7 ++ help ++ Say Y to get lots of debugging output. ++ + # `filter', generic and specific targets + config IP_NF_FILTER + tristate "Packet filtering" +diff -Nur linux-2.6.17/net/ipv4/netfilter/Makefile linux-2.6.17-owrt/net/ipv4/netfilter/Makefile +--- linux-2.6.17/net/ipv4/netfilter/Makefile 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-owrt/net/ipv4/netfilter/Makefile 2006-06-18 12:29:04.000000000 +0200 +@@ -62,6 +62,8 @@ + obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o + obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o + ++obj-$(CONFIG_IP_NF_MATCH_LAYER7) += ipt_layer7.o ++ + # targets + obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o + obj-$(CONFIG_IP_NF_TARGET_TOS) += ipt_TOS.o +diff -Nur linux-2.6.17/net/ipv4/netfilter/regexp/regexp.c linux-2.6.17-owrt/net/ipv4/netfilter/regexp/regexp.c +--- linux-2.6.17/net/ipv4/netfilter/regexp/regexp.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.17-owrt/net/ipv4/netfilter/regexp/regexp.c 2006-06-18 12:29:04.000000000 +0200 +@@ -0,0 +1,1195 @@ ++/* ++ * regcomp and regexec -- regsub and regerror are elsewhere ++ * @(#)regexp.c 1.3 of 18 April 87 ++ * ++ * Copyright (c) 1986 by University of Toronto. ++ * Written by Henry Spencer. Not derived from licensed software. ++ * ++ * Permission is granted to anyone to use this software for any ++ * purpose on any computer system, and to redistribute it freely, ++ * subject to the following restrictions: ++ * ++ * 1. The author is not responsible for the consequences of use of ++ * this software, no matter how awful, even if they arise ++ * from defects in it. ++ * ++ * 2. The origin of this software must not be misrepresented, either ++ * by explicit claim or by omission. ++ * ++ * 3. Altered versions must be plainly marked as such, and must not ++ * be misrepresented as being the original software. ++ * ++ * Beware that some of this code is subtly aware of the way operator ++ * precedence is structured in regular expressions. Serious changes in ++ * regular-expression syntax might require a total rethink. ++ * ++ * This code was modified by Ethan Sommer to work within the kernel ++ * (it now uses kmalloc etc..) ++ * ++ * Modified slightly by Matthew Strait to use more modern C. ++ */ ++ ++#include "regexp.h" ++#include "regmagic.h" ++ ++/* added by ethan and matt. Lets it work in both kernel and user space. ++(So iptables can use it, for instance.) Yea, it goes both ways... */ ++#if __KERNEL__ ++ #define malloc(foo) kmalloc(foo,GFP_ATOMIC) ++#else ++ #define printk(format,args...) printf(format,##args) ++#endif ++ ++void regerror(char * s) ++{ ++ printk("<3>Regexp: %s\n", s); ++ /* NOTREACHED */ ++} ++ ++/* ++ * The "internal use only" fields in regexp.h are present to pass info from ++ * compile to execute that permits the execute phase to run lots faster on ++ * simple cases. They are: ++ * ++ * regstart char that must begin a match; '\0' if none obvious ++ * reganch is the match anchored (at beginning-of-line only)? ++ * regmust string (pointer into program) that match must include, or NULL ++ * regmlen length of regmust string ++ * ++ * Regstart and reganch permit very fast decisions on suitable starting points ++ * for a match, cutting down the work a lot. Regmust permits fast rejection ++ * of lines that cannot possibly match. The regmust tests are costly enough ++ * that regcomp() supplies a regmust only if the r.e. contains something ++ * potentially expensive (at present, the only such thing detected is * or + ++ * at the start of the r.e., which can involve a lot of backup). Regmlen is ++ * supplied because the test in regexec() needs it and regcomp() is computing ++ * it anyway. ++ */ ++ ++/* ++ * Structure for regexp "program". This is essentially a linear encoding ++ * of a nondeterministic finite-state machine (aka syntax charts or ++ * "railroad normal form" in parsing technology). Each node is an opcode ++ * plus a "next" pointer, possibly plus an operand. "Next" pointers of ++ * all nodes except BRANCH implement concatenation; a "next" pointer with ++ * a BRANCH on both ends of it is connecting two alternatives. (Here we ++ * have one of the subtle syntax dependencies: an individual BRANCH (as ++ * opposed to a collection of them) is never concatenated with anything ++ * because of operator precedence.) The operand of some types of node is ++ * a literal string; for others, it is a node leading into a sub-FSM. In ++ * particular, the operand of a BRANCH node is the first node of the branch. ++ * (NB this is *not* a tree structure: the tail of the branch connects ++ * to the thing following the set of BRANCHes.) The opcodes are: ++ */ ++ ++/* definition number opnd? meaning */ ++#define END 0 /* no End of program. */ ++#define BOL 1 /* no Match "" at beginning of line. */ ++#define EOL 2 /* no Match "" at end of line. */ ++#define ANY 3 /* no Match any one character. */ ++#define ANYOF 4 /* str Match any character in this string. */ ++#define ANYBUT 5 /* str Match any character not in this string. */ ++#define BRANCH 6 /* node Match this alternative, or the next... */ ++#define BACK 7 /* no Match "", "next" ptr points backward. */ ++#define EXACTLY 8 /* str Match this string. */ ++#define NOTHING 9 /* no Match empty string. */ ++#define STAR 10 /* node Match this (simple) thing 0 or more times. */ ++#define PLUS 11 /* node Match this (simple) thing 1 or more times. */ ++#define OPEN 20 /* no Mark this point in input as start of #n. */ ++ /* OPEN+1 is number 1, etc. */ ++#define CLOSE 30 /* no Analogous to OPEN. */ ++ ++/* ++ * Opcode notes: ++ * ++ * BRANCH The set of branches constituting a single choice are hooked ++ * together with their "next" pointers, since precedence prevents ++ * anything being concatenated to any individual branch. The ++ * "next" pointer of the last BRANCH in a choice points to the ++ * thing following the whole choice. This is also where the ++ * final "next" pointer of each individual branch points; each ++ * branch starts with the operand node of a BRANCH node. ++ * ++ * BACK Normal "next" pointers all implicitly point forward; BACK ++ * exists to make loop structures possible. ++ * ++ * STAR,PLUS '?', and complex '*' and '+', are implemented as circular ++ * BRANCH structures using BACK. Simple cases (one character ++ * per match) are implemented with STAR and PLUS for speed ++ * and to minimize recursive plunges. ++ * ++ * OPEN,CLOSE ...are numbered at compile time. ++ */ ++ ++/* ++ * A node is one char of opcode followed by two chars of "next" pointer. ++ * "Next" pointers are stored as two 8-bit pieces, high order first. The ++ * value is a positive offset from the opcode of the node containing it. ++ * An operand, if any, simply follows the node. (Note that much of the ++ * code generation knows about this implicit relationship.) ++ * ++ * Using two bytes for the "next" pointer is vast overkill for most things, ++ * but allows patterns to get big without disasters. ++ */ ++#define OP(p) (*(p)) ++#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) ++#define OPERAND(p) ((p) + 3) ++ ++/* ++ * See regmagic.h for one further detail of program structure. ++ */ ++ ++ ++/* ++ * Utility definitions. ++ */ ++#ifndef CHARBITS ++#define UCHARAT(p) ((int)*(unsigned char *)(p)) ++#else ++#define UCHARAT(p) ((int)*(p)&CHARBITS) ++#endif ++ ++#define FAIL(m) { regerror(m); return(NULL); } ++#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') ++#define META "^$.[()|?+*\\" ++ ++/* ++ * Flags to be passed up and down. ++ */ ++#define HASWIDTH 01 /* Known never to match null string. */ ++#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ ++#define SPSTART 04 /* Starts with * or +. */ ++#define WORST 0 /* Worst case. */ ++ ++/* ++ * Global work variables for regcomp(). ++ */ ++static char *regparse; /* Input-scan pointer. */ ++static int regnpar; /* () count. */ ++static char regdummy; ++static char *regcode; /* Code-emit pointer; ®dummy = don't. */ ++static long regsize; /* Code size. */ ++ ++/* ++ * Forward declarations for regcomp()'s friends. ++ */ ++#ifndef STATIC ++#define STATIC static ++#endif ++STATIC char *reg(int paren,int *flagp); ++STATIC char *regbranch(int *flagp); ++STATIC char *regpiece(int *flagp); ++STATIC char *regatom(int *flagp); ++STATIC char *regnode(char op); ++STATIC char *regnext(char *p); ++STATIC void regc(char b); ++STATIC void reginsert(char op, char *opnd); ++STATIC void regtail(char *p, char *val); ++STATIC void regoptail(char *p, char *val); ++ ++ ++__kernel_size_t my_strcspn(const char *s1,const char *s2) ++{ ++ char *scan1; ++ char *scan2; ++ int count; ++ ++ count = 0; ++ for (scan1 = (char *)s1; *scan1 != '\0'; scan1++) { ++ for (scan2 = (char *)s2; *scan2 != '\0';) /* ++ moved down. */ ++ if (*scan1 == *scan2++) ++ return(count); ++ count++; ++ } ++ return(count); ++} ++ ++/* ++ - regcomp - compile a regular expression into internal code ++ * ++ * We can't allocate space until we know how big the compiled form will be, ++ * but we can't compile it (and thus know how big it is) until we've got a ++ * place to put the code. So we cheat: we compile it twice, once with code ++ * generation turned off and size counting turned on, and once "for real". ++ * This also means that we don't allocate space until we are sure that the ++ * thing really will compile successfully, and we never have to move the ++ * code and thus invalidate pointers into it. (Note that it has to be in ++ * one piece because free() must be able to free it all.) ++ * ++ * Beware that the optimization-preparation code in here knows about some ++ * of the structure of the compiled regexp. ++ */ ++regexp * ++regcomp(char *exp,int *patternsize) ++{ ++ register regexp *r; ++ register char *scan; ++ register char *longest; ++ register int len; ++ int flags; ++ /* commented out by ethan ++ extern char *malloc(); ++ */ ++ ++ if (exp == NULL) ++ FAIL("NULL argument"); ++ ++ /* First pass: determine size, legality. */ ++ regparse = exp; ++ regnpar = 1; ++ regsize = 0L; ++ regcode = ®dummy; ++ regc(MAGIC); ++ if (reg(0, &flags) == NULL) ++ return(NULL); ++ ++ /* Small enough for pointer-storage convention? */ ++ if (regsize >= 32767L) /* Probably could be 65535L. */ ++ FAIL("regexp too big"); ++ ++ /* Allocate space. */ ++ *patternsize=sizeof(regexp) + (unsigned)regsize; ++ r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize); ++ if (r == NULL) ++ FAIL("out of space"); ++ ++ /* Second pass: emit code. */ ++ regparse = exp; ++ regnpar = 1; ++ regcode = r->program; ++ regc(MAGIC); ++ if (reg(0, &flags) == NULL) ++ return(NULL); ++ ++ /* Dig out information for optimizations. */ ++ r->regstart = '\0'; /* Worst-case defaults. */ ++ r->reganch = 0; ++ r->regmust = NULL; ++ r->regmlen = 0; ++ scan = r->program+1; /* First BRANCH. */ ++ if (OP(regnext(scan)) == END) { /* Only one top-level choice. */ ++ scan = OPERAND(scan); ++ ++ /* Starting-point info. */ ++ if (OP(scan) == EXACTLY) ++ r->regstart = *OPERAND(scan); ++ else if (OP(scan) == BOL) ++ r->reganch++; ++ ++ /* ++ * If there's something expensive in the r.e., find the ++ * longest literal string that must appear and make it the ++ * regmust. Resolve ties in favor of later strings, since ++ * the regstart check works with the beginning of the r.e. ++ * and avoiding duplication strengthens checking. Not a ++ * strong reason, but sufficient in the absence of others. ++ */ ++ if (flags&SPSTART) { ++ longest = NULL; ++ len = 0; ++ for (; scan != NULL; scan = regnext(scan)) ++ if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { ++ longest = OPERAND(scan); ++ len = strlen(OPERAND(scan)); ++ } ++ r->regmust = longest; ++ r->regmlen = len; ++ } ++ } ++ ++ return(r); ++} ++ ++/* ++ - reg - regular expression, i.e. main body or parenthesized thing ++ * ++ * Caller must absorb opening parenthesis. ++ * ++ * Combining parenthesis handling with the base level of regular expression ++ * is a trifle forced, but the need to tie the tails of the branches to what ++ * follows makes it hard to avoid. ++ */ ++static char * ++reg(int paren, int *flagp /* Parenthesized? */ ) ++{ ++ register char *ret; ++ register char *br; ++ register char *ender; ++ register int parno = 0; /* 0 makes gcc happy */ ++ int flags; ++ ++ *flagp = HASWIDTH; /* Tentatively. */ ++ ++ /* Make an OPEN node, if parenthesized. */ ++ if (paren) { ++ if (regnpar >= NSUBEXP) ++ FAIL("too many ()"); ++ parno = regnpar; ++ regnpar++; ++ ret = regnode(OPEN+parno); ++ } else ++ ret = NULL; ++ ++ /* Pick up the branches, linking them together. */ ++ br = regbranch(&flags); ++ if (br == NULL) ++ return(NULL); ++ if (ret != NULL) ++ regtail(ret, br); /* OPEN -> first. */ ++ else ++ ret = br; ++ if (!(flags&HASWIDTH)) ++ *flagp &= ~HASWIDTH; ++ *flagp |= flags&SPSTART; ++ while (*regparse == '|') { ++ regparse++; ++ br = regbranch(&flags); ++ if (br == NULL) ++ return(NULL); ++ regtail(ret, br); /* BRANCH -> BRANCH. */ ++ if (!(flags&HASWIDTH)) ++ *flagp &= ~HASWIDTH; ++ *flagp |= flags&SPSTART; ++ } ++ ++ /* Make a closing node, and hook it on the end. */ ++ ender = regnode((paren) ? CLOSE+parno : END); ++ regtail(ret, ender); ++ ++ /* Hook the tails of the branches to the closing node. */ ++ for (br = ret; br != NULL; br = regnext(br)) ++ regoptail(br, ender); ++ ++ /* Check for proper termination. */ ++ if (paren && *regparse++ != ')') { ++ FAIL("unmatched ()"); ++ } else if (!paren && *regparse != '\0') { ++ if (*regparse == ')') { ++ FAIL("unmatched ()"); ++ } else ++ FAIL("junk on end"); /* "Can't happen". */ ++ /* NOTREACHED */ ++ } ++ ++ return(ret); ++} ++ ++/* ++ - regbranch - one alternative of an | operator ++ * ++ * Implements the concatenation operator. ++ */ ++static char * ++regbranch(int *flagp) ++{ ++ register char *ret; ++ register char *chain; ++ register char *latest; ++ int flags; ++ ++ *flagp = WORST; /* Tentatively. */ ++ ++ ret = regnode(BRANCH); ++ chain = NULL; ++ while (*regparse != '\0' && *regparse != '|' && *regparse != ')') { ++ latest = regpiece(&flags); ++ if (latest == NULL) ++ return(NULL); ++ *flagp |= flags&HASWIDTH; ++ if (chain == NULL) /* First piece. */ ++ *flagp |= flags&SPSTART; ++ else ++ regtail(chain, latest); ++ chain = latest; ++ } ++ if (chain == NULL) /* Loop ran zero times. */ ++ (void) regnode(NOTHING); ++ ++ return(ret); ++} ++ ++/* ++ - regpiece - something followed by possible [*+?] ++ * ++ * Note that the branching code sequences used for ? and the general cases ++ * of * and + are somewhat optimized: they use the same NOTHING node as ++ * both the endmarker for their branch list and the body of the last branch. ++ * It might seem that this node could be dispensed with entirely, but the ++ * endmarker role is not redundant. ++ */ ++static char * ++regpiece(int *flagp) ++{ ++ register char *ret; ++ register char op; ++ register char *next; ++ int flags; ++ ++ ret = regatom(&flags); ++ if (ret == NULL) ++ return(NULL); ++ ++ op = *regparse; ++ if (!ISMULT(op)) { ++ *flagp = flags; ++ return(ret); ++ } ++ ++ if (!(flags&HASWIDTH) && op != '?') ++ FAIL("*+ operand could be empty"); ++ *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); ++ ++ if (op == '*' && (flags&SIMPLE)) ++ reginsert(STAR, ret); ++ else if (op == '*') { ++ /* Emit x* as (x&|), where & means "self". */ ++ reginsert(BRANCH, ret); /* Either x */ ++ regoptail(ret, regnode(BACK)); /* and loop */ ++ regoptail(ret, ret); /* back */ ++ regtail(ret, regnode(BRANCH)); /* or */ ++ regtail(ret, regnode(NOTHING)); /* null. */ ++ } else if (op == '+' && (flags&SIMPLE)) ++ reginsert(PLUS, ret); ++ else if (op == '+') { ++ /* Emit x+ as x(&|), where & means "self". */ ++ next = regnode(BRANCH); /* Either */ ++ regtail(ret, next); ++ regtail(regnode(BACK), ret); /* loop back */ ++ regtail(next, regnode(BRANCH)); /* or */ ++ regtail(ret, regnode(NOTHING)); /* null. */ ++ } else if (op == '?') { ++ /* Emit x? as (x|) */ ++ reginsert(BRANCH, ret); /* Either x */ ++ regtail(ret, regnode(BRANCH)); /* or */ ++ next = regnode(NOTHING); /* null. */ ++ regtail(ret, next); ++ regoptail(ret, next); ++ } ++ regparse++; ++ if (ISMULT(*regparse)) ++ FAIL("nested *?+"); ++ ++ return(ret); ++} ++ ++/* ++ - regatom - the lowest level ++ * ++ * Optimization: gobbles an entire sequence of ordinary characters so that ++ * it can turn them into a single node, which is smaller to store and ++ * faster to run. Backslashed characters are exceptions, each becoming a ++ * separate node; the code is simpler that way and it's not worth fixing. ++ */ ++static char * ++regatom(int *flagp) ++{ ++ register char *ret; ++ int flags; ++ ++ *flagp = WORST; /* Tentatively. */ ++ ++ switch (*regparse++) { ++ case '^': ++ ret = regnode(BOL); ++ break; ++ case '$': ++ ret = regnode(EOL); ++ break; ++ case '.': ++ ret = regnode(ANY); ++ *flagp |= HASWIDTH|SIMPLE; ++ break; ++ case '[': { ++ register int class; ++ register int classend; ++ ++ if (*regparse == '^') { /* Complement of range. */ ++ ret = regnode(ANYBUT); ++ regparse++; ++ } else ++ ret = regnode(ANYOF); ++ if (*regparse == ']' || *regparse == '-') ++ regc(*regparse++); ++ while (*regparse != '\0' && *regparse != ']') { ++ if (*regparse == '-') { ++ regparse++; ++ if (*regparse == ']' || *regparse == '\0') ++ regc('-'); ++ else { ++ class = UCHARAT(regparse-2)+1; ++ classend = UCHARAT(regparse); ++ if (class > classend+1) ++ FAIL("invalid [] range"); ++ for (; class <= classend; class++) ++ regc(class); ++ regparse++; ++ } ++ } else ++ regc(*regparse++); ++ } ++ regc('\0'); ++ if (*regparse != ']') ++ FAIL("unmatched []"); ++ regparse++; ++ *flagp |= HASWIDTH|SIMPLE; ++ } ++ break; ++ case '(': ++ ret = reg(1, &flags); ++ if (ret == NULL) ++ return(NULL); ++ *flagp |= flags&(HASWIDTH|SPSTART); ++ break; ++ case '\0': ++ case '|': ++ case ')': ++ FAIL("internal urp"); /* Supposed to be caught earlier. */ ++ break; ++ case '?': ++ case '+': ++ case '*': ++ FAIL("?+* follows nothing"); ++ break; ++ case '\\': ++ if (*regparse == '\0') ++ FAIL("trailing \\"); ++ ret = regnode(EXACTLY); ++ regc(*regparse++); ++ regc('\0'); ++ *flagp |= HASWIDTH|SIMPLE; ++ break; ++ default: { ++ register int len; ++ register char ender; ++ ++ regparse--; ++ len = my_strcspn((const char *)regparse, (const char *)META); ++ if (len <= 0) ++ FAIL("internal disaster"); ++ ender = *(regparse+len); ++ if (len > 1 && ISMULT(ender)) ++ len--; /* Back off clear of ?+* operand. */ ++ *flagp |= HASWIDTH; ++ if (len == 1) ++ *flagp |= SIMPLE; ++ ret = regnode(EXACTLY); ++ while (len > 0) { ++ regc(*regparse++); ++ len--; ++ } ++ regc('\0'); ++ } ++ break; ++ } ++ ++ return(ret); ++} ++ ++/* ++ - regnode - emit a node ++ */ ++static char * /* Location. */ ++regnode(char op) ++{ ++ register char *ret; ++ register char *ptr; ++ ++ ret = regcode; ++ if (ret == ®dummy) { ++ regsize += 3; ++ return(ret); ++ } ++ ++ ptr = ret; ++ *ptr++ = op; ++ *ptr++ = '\0'; /* Null "next" pointer. */ ++ *ptr++ = '\0'; ++ regcode = ptr; ++ ++ return(ret); ++} ++ ++/* ++ - regc - emit (if appropriate) a byte of code ++ */ ++static void ++regc(char b) ++{ ++ if (regcode != ®dummy) ++ *regcode++ = b; ++ else ++ regsize++; ++} ++ ++/* ++ - reginsert - insert an operator in front of already-emitted operand ++ * ++ * Means relocating the operand. ++ */ ++static void ++reginsert(char op, char* opnd) ++{ ++ register char *src; ++ register char *dst; ++ register char *place; ++ ++ if (regcode == ®dummy) { ++ regsize += 3; ++ return; ++ } ++ ++ src = regcode; ++ regcode += 3; ++ dst = regcode; ++ while (src > opnd) ++ *--dst = *--src; ++ ++ place = opnd; /* Op node, where operand used to be. */ ++ *place++ = op; ++ *place++ = '\0'; ++ *place++ = '\0'; ++} ++ ++/* ++ - regtail - set the next-pointer at the end of a node chain ++ */ ++static void ++regtail(char *p, char *val) ++{ ++ register char *scan; ++ register char *temp; ++ register int offset; ++ ++ if (p == ®dummy) ++ return; ++ ++ /* Find last node. */ ++ scan = p; ++ for (;;) { ++ temp = regnext(scan); ++ if (temp == NULL) ++ break; ++ scan = temp; ++ } ++ ++ if (OP(scan) == BACK) ++ offset = scan - val; ++ else ++ offset = val - scan; ++ *(scan+1) = (offset>>8)&0377; ++ *(scan+2) = offset&0377; ++} ++ ++/* ++ - regoptail - regtail on operand of first argument; nop if operandless ++ */ ++static void ++regoptail(char *p, char *val) ++{ ++ /* "Operandless" and "op != BRANCH" are synonymous in practice. */ ++ if (p == NULL || p == ®dummy || OP(p) != BRANCH) ++ return; ++ regtail(OPERAND(p), val); ++} ++ ++/* ++ * regexec and friends ++ */ ++ ++/* ++ * Global work variables for regexec(). ++ */ ++static char *reginput; /* String-input pointer. */ ++static char *regbol; /* Beginning of input, for ^ check. */ ++static char **regstartp; /* Pointer to startp array. */ ++static char **regendp; /* Ditto for endp. */ ++ ++/* ++ * Forwards. ++ */ ++STATIC int regtry(regexp *prog, char *string); ++STATIC int regmatch(char *prog); ++STATIC int regrepeat(char *p); ++ ++#ifdef DEBUG ++int regnarrate = 0; ++void regdump(); ++STATIC char *regprop(char *op); ++#endif ++ ++/* ++ - regexec - match a regexp against a string ++ */ ++int ++regexec(regexp *prog, char *string) ++{ ++ register char *s; ++ ++ /* Be paranoid... */ ++ if (prog == NULL || string == NULL) { ++ printk("<3>Regexp: NULL parameter\n"); ++ return(0); ++ } ++ ++ /* Check validity of program. */ ++ if (UCHARAT(prog->program) != MAGIC) { ++ printk("<3>Regexp: corrupted program\n"); ++ return(0); ++ } ++ ++ /* If there is a "must appear" string, look for it. */ ++ if (prog->regmust != NULL) { ++ s = string; ++ while ((s = strchr(s, prog->regmust[0])) != NULL) { ++ if (strncmp(s, prog->regmust, prog->regmlen) == 0) ++ break; /* Found it. */ ++ s++; ++ } ++ if (s == NULL) /* Not present. */ ++ return(0); ++ } ++ ++ /* Mark beginning of line for ^ . */ ++ regbol = string; ++ ++ /* Simplest case: anchored match need be tried only once. */ ++ if (prog->reganch) ++ return(regtry(prog, string)); ++ ++ /* Messy cases: unanchored match. */ ++ s = string; ++ if (prog->regstart != '\0') ++ /* We know what char it must start with. */ ++ while ((s = strchr(s, prog->regstart)) != NULL) { ++ if (regtry(prog, s)) ++ return(1); ++ s++; ++ } ++ else ++ /* We don't -- general case. */ ++ do { ++ if (regtry(prog, s)) ++ return(1); ++ } while (*s++ != '\0'); ++ ++ /* Failure. */ ++ return(0); ++} ++ ++/* ++ - regtry - try match at specific point ++ */ ++static int /* 0 failure, 1 success */ ++regtry(regexp *prog, char *string) ++{ ++ register int i; ++ register char **sp; ++ register char **ep; ++ ++ reginput = string; ++ regstartp = prog->startp; ++ regendp = prog->endp; ++ ++ sp = prog->startp; ++ ep = prog->endp; ++ for (i = NSUBEXP; i > 0; i--) { ++ *sp++ = NULL; ++ *ep++ = NULL; ++ } ++ if (regmatch(prog->program + 1)) { ++ prog->startp[0] = string; ++ prog->endp[0] = reginput; ++ return(1); ++ } else ++ return(0); ++} ++ ++/* ++ - regmatch - main matching routine ++ * ++ * Conceptually the strategy is simple: check to see whether the current ++ * node matches, call self recursively to see whether the rest matches, ++ * and then act accordingly. In practice we make some effort to avoid ++ * recursion, in particular by going through "ordinary" nodes (that don't ++ * need to know whether the rest of the match failed) by a loop instead of ++ * by recursion. ++ */ ++static int /* 0 failure, 1 success */ ++regmatch(char *prog) ++{ ++ register char *scan = prog; /* Current node. */ ++ char *next; /* Next node. */ ++ ++#ifdef DEBUG ++ if (scan != NULL && regnarrate) ++ fprintf(stderr, "%s(\n", regprop(scan)); ++#endif ++ while (scan != NULL) { ++#ifdef DEBUG ++ if (regnarrate) ++ fprintf(stderr, "%s...\n", regprop(scan)); ++#endif ++ next = regnext(scan); ++ ++ switch (OP(scan)) { ++ case BOL: ++ if (reginput != regbol) ++ return(0); ++ break; ++ case EOL: ++ if (*reginput != '\0') ++ return(0); ++ break; ++ case ANY: ++ if (*reginput == '\0') ++ return(0); ++ reginput++; ++ break; ++ case EXACTLY: { ++ register int len; ++ register char *opnd; ++ ++ opnd = OPERAND(scan); ++ /* Inline the first character, for speed. */ ++ if (*opnd != *reginput) ++ return(0); ++ len = strlen(opnd); ++ if (len > 1 && strncmp(opnd, reginput, len) != 0) ++ return(0); ++ reginput += len; ++ } ++ break; ++ case ANYOF: ++ if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL) ++ return(0); ++ reginput++; ++ break; ++ case ANYBUT: ++ if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL) ++ return(0); ++ reginput++; ++ break; ++ case NOTHING: ++ case BACK: ++ break; ++ case OPEN+1: ++ case OPEN+2: ++ case OPEN+3: ++ case OPEN+4: ++ case OPEN+5: ++ case OPEN+6: ++ case OPEN+7: ++ case OPEN+8: ++ case OPEN+9: { ++ register int no; ++ register char *save; ++ ++ no = OP(scan) - OPEN; ++ save = reginput; ++ ++ if (regmatch(next)) { ++ /* ++ * Don't set startp if some later ++ * invocation of the same parentheses ++ * already has. ++ */ ++ if (regstartp[no] == NULL) ++ regstartp[no] = save; ++ return(1); ++ } else ++ return(0); ++ } ++ break; ++ case CLOSE+1: ++ case CLOSE+2: ++ case CLOSE+3: ++ case CLOSE+4: ++ case CLOSE+5: ++ case CLOSE+6: ++ case CLOSE+7: ++ case CLOSE+8: ++ case CLOSE+9: ++ { ++ register int no; ++ register char *save; ++ ++ no = OP(scan) - CLOSE; ++ save = reginput; ++ ++ if (regmatch(next)) { ++ /* ++ * Don't set endp if some later ++ * invocation of the same parentheses ++ * already has. ++ */ ++ if (regendp[no] == NULL) ++ regendp[no] = save; ++ return(1); ++ } else ++ return(0); ++ } ++ break; ++ case BRANCH: { ++ register char *save; ++ ++ if (OP(next) != BRANCH) /* No choice. */ ++ next = OPERAND(scan); /* Avoid recursion. */ ++ else { ++ do { ++ save = reginput; ++ if (regmatch(OPERAND(scan))) ++ return(1); ++ reginput = save; ++ scan = regnext(scan); ++ } while (scan != NULL && OP(scan) == BRANCH); ++ return(0); ++ /* NOTREACHED */ ++ } ++ } ++ break; ++ case STAR: ++ case PLUS: { ++ register char nextch; ++ register int no; ++ register char *save; ++ register int min; ++ ++ /* ++ * Lookahead to avoid useless match attempts ++ * when we know what character comes next. ++ */ ++ nextch = '\0'; ++ if (OP(next) == EXACTLY) ++ nextch = *OPERAND(next); ++ min = (OP(scan) == STAR) ? 0 : 1; ++ save = reginput; ++ no = regrepeat(OPERAND(scan)); ++ while (no >= min) { ++ /* If it could work, try it. */ ++ if (nextch == '\0' || *reginput == nextch) ++ if (regmatch(next)) ++ return(1); ++ /* Couldn't or didn't -- back up. */ ++ no--; ++ reginput = save + no; ++ } ++ return(0); ++ } ++ break; ++ case END: ++ return(1); /* Success! */ ++ break; ++ default: ++ printk("<3>Regexp: memory corruption\n"); ++ return(0); ++ break; ++ } ++ ++ scan = next; ++ } ++ ++ /* ++ * We get here only if there's trouble -- normally "case END" is ++ * the terminating point. ++ */ ++ printk("<3>Regexp: corrupted pointers\n"); ++ return(0); ++} ++ ++/* ++ - regrepeat - repeatedly match something simple, report how many ++ */ ++static int ++regrepeat(char *p) ++{ ++ register int count = 0; ++ register char *scan; ++ register char *opnd; ++ ++ scan = reginput; ++ opnd = OPERAND(p); ++ switch (OP(p)) { ++ case ANY: ++ count = strlen(scan); ++ scan += count; ++ break; ++ case EXACTLY: ++ while (*opnd == *scan) { ++ count++; ++ scan++; ++ } ++ break; ++ case ANYOF: ++ while (*scan != '\0' && strchr(opnd, *scan) != NULL) { ++ count++; ++ scan++; ++ } ++ break; ++ case ANYBUT: ++ while (*scan != '\0' && strchr(opnd, *scan) == NULL) { ++ count++; ++ scan++; ++ } ++ break; ++ default: /* Oh dear. Called inappropriately. */ ++ printk("<3>Regexp: internal foulup\n"); ++ count = 0; /* Best compromise. */ ++ break; ++ } ++ reginput = scan; ++ ++ return(count); ++} ++ ++/* ++ - regnext - dig the "next" pointer out of a node ++ */ ++static char* ++regnext(char *p) ++{ ++ register int offset; ++ ++ if (p == ®dummy) ++ return(NULL); ++ ++ offset = NEXT(p); ++ if (offset == 0) ++ return(NULL); ++ ++ if (OP(p) == BACK) ++ return(p-offset); ++ else ++ return(p+offset); ++} ++ ++#ifdef DEBUG ++ ++STATIC char *regprop(); ++ ++/* ++ - regdump - dump a regexp onto stdout in vaguely comprehensible form ++ */ ++void ++regdump(regexp *r) ++{ ++ register char *s; ++ register char op = EXACTLY; /* Arbitrary non-END op. */ ++ register char *next; ++ /* extern char *strchr(); */ ++ ++ ++ s = r->program + 1; ++ while (op != END) { /* While that wasn't END last time... */ ++ op = OP(s); ++ printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ ++ next = regnext(s); ++ if (next == NULL) /* Next ptr. */ ++ printf("(0)"); ++ else ++ printf("(%d)", (s-r->program)+(next-s)); ++ s += 3; ++ if (op == ANYOF || op == ANYBUT || op == EXACTLY) { ++ /* Literal string, where present. */ ++ while (*s != '\0') { ++ putchar(*s); ++ s++; ++ } ++ s++; ++ } ++ putchar('\n'); ++ } ++ ++ /* Header fields of interest. */ ++ if (r->regstart != '\0') ++ printf("start `%c' ", r->regstart); ++ if (r->reganch) ++ printf("anchored "); ++ if (r->regmust != NULL) ++ printf("must have \"%s\"", r->regmust); ++ printf("\n"); ++} ++ ++/* ++ - regprop - printable representation of opcode ++ */ ++static char * ++regprop(char *op) ++{ ++#define BUFLEN 50 ++ register char *p; ++ static char buf[BUFLEN]; ++ ++ strcpy(buf, ":"); ++ ++ switch (OP(op)) { ++ case BOL: ++ p = "BOL"; ++ break; ++ case EOL: ++ p = "EOL"; ++ break; ++ case ANY: ++ p = "ANY"; ++ break; ++ case ANYOF: ++ p = "ANYOF"; ++ break; ++ case ANYBUT: ++ p = "ANYBUT"; ++ break; ++ case BRANCH: ++ p = "BRANCH"; ++ break; ++ case EXACTLY: ++ p = "EXACTLY"; ++ break; ++ case NOTHING: ++ p = "NOTHING"; ++ break; ++ case BACK: ++ p = "BACK"; ++ break; ++ case END: ++ p = "END"; ++ break; ++ case OPEN+1: ++ case OPEN+2: ++ case OPEN+3: ++ case OPEN+4: ++ case OPEN+5: ++ case OPEN+6: ++ case OPEN+7: ++ case OPEN+8: ++ case OPEN+9: ++ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "OPEN%d", OP(op)-OPEN); ++ p = NULL; ++ break; ++ case CLOSE+1: ++ case CLOSE+2: ++ case CLOSE+3: ++ case CLOSE+4: ++ case CLOSE+5: ++ case CLOSE+6: ++ case CLOSE+7: ++ case CLOSE+8: ++ case CLOSE+9: ++ snprintf(buf+strlen(buf),BUFLEN-strlen(buf), "CLOSE%d", OP(op)-CLOSE); ++ p = NULL; ++ break; ++ case STAR: ++ p = "STAR"; ++ break; ++ case PLUS: ++ p = "PLUS"; ++ break; ++ default: ++ printk("<3>Regexp: corrupted opcode\n"); ++ break; ++ } ++ if (p != NULL) ++ strncat(buf, p, BUFLEN-strlen(buf)); ++ return(buf); ++} ++#endif ++ ++ +diff -Nur linux-2.6.17/net/ipv4/netfilter/regexp/regexp.h linux-2.6.17-owrt/net/ipv4/netfilter/regexp/regexp.h +--- linux-2.6.17/net/ipv4/netfilter/regexp/regexp.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.17-owrt/net/ipv4/netfilter/regexp/regexp.h 2006-06-18 12:29:04.000000000 +0200 +@@ -0,0 +1,41 @@ ++/* ++ * Definitions etc. for regexp(3) routines. ++ * ++ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], ++ * not the System V one. ++ */ ++ ++#ifndef REGEXP_H ++#define REGEXP_H ++ ++ ++/* ++http://www.opensource.apple.com/darwinsource/10.3/expect-1/expect/expect.h , ++which contains a version of this library, says: ++ ++ * ++ * NSUBEXP must be at least 10, and no greater than 117 or the parser ++ * will not work properly. ++ * ++ ++However, it looks rather like this library is limited to 10. If you think ++otherwise, let us know. ++*/ ++ ++#define NSUBEXP 10 ++typedef struct regexp { ++ char *startp[NSUBEXP]; ++ char *endp[NSUBEXP]; ++ char regstart; /* Internal use only. */ ++ char reganch; /* Internal use only. */ ++ char *regmust; /* Internal use only. */ ++ int regmlen; /* Internal use only. */ ++ char program[1]; /* Unwarranted chumminess with compiler. */ ++} regexp; ++ ++regexp * regcomp(char *exp, int *patternsize); ++int regexec(regexp *prog, char *string); ++void regsub(regexp *prog, char *source, char *dest); ++void regerror(char *s); ++ ++#endif +diff -Nur linux-2.6.17/net/ipv4/netfilter/regexp/regmagic.h linux-2.6.17-owrt/net/ipv4/netfilter/regexp/regmagic.h +--- linux-2.6.17/net/ipv4/netfilter/regexp/regmagic.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.17-owrt/net/ipv4/netfilter/regexp/regmagic.h 2006-06-18 12:29:04.000000000 +0200 +@@ -0,0 +1,5 @@ ++/* ++ * The first byte of the regexp internal "program" is actually this magic ++ * number; the start node begins in the second byte. ++ */ ++#define MAGIC 0234 +diff -Nur linux-2.6.17/net/ipv4/netfilter/regexp/regsub.c linux-2.6.17-owrt/net/ipv4/netfilter/regexp/regsub.c +--- linux-2.6.17/net/ipv4/netfilter/regexp/regsub.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.17-owrt/net/ipv4/netfilter/regexp/regsub.c 2006-06-18 12:29:04.000000000 +0200 +@@ -0,0 +1,95 @@ ++/* ++ * regsub ++ * @(#)regsub.c 1.3 of 2 April 86 ++ * ++ * Copyright (c) 1986 by University of Toronto. ++ * Written by Henry Spencer. Not derived from licensed software. ++ * ++ * Permission is granted to anyone to use this software for any ++ * purpose on any computer system, and to redistribute it freely, ++ * subject to the following restrictions: ++ * ++ * 1. The author is not responsible for the consequences of use of ++ * this software, no matter how awful, even if they arise ++ * from defects in it. ++ * ++ * 2. The origin of this software must not be misrepresented, either ++ * by explicit claim or by omission. ++ * ++ * 3. Altered versions must be plainly marked as such, and must not ++ * be misrepresented as being the original software. ++ * ++ * ++ * This code was modified by Ethan Sommer to work within the kernel ++ * (it now uses kmalloc etc..) ++ * ++ */ ++#include "regexp.h" ++#include "regmagic.h" ++#include <linux/string.h> ++ ++ ++#ifndef CHARBITS ++#define UCHARAT(p) ((int)*(unsigned char *)(p)) ++#else ++#define UCHARAT(p) ((int)*(p)&CHARBITS) ++#endif ++ ++#if 0 ++//void regerror(char * s) ++//{ ++// printk("regexp(3): %s", s); ++// /* NOTREACHED */ ++//} ++#endif ++ ++/* ++ - regsub - perform substitutions after a regexp match ++ */ ++void ++regsub(regexp * prog, char * source, char * dest) ++{ ++ register char *src; ++ register char *dst; ++ register char c; ++ register int no; ++ register int len; ++ ++ /* Not necessary and gcc doesn't like it -MLS */ ++ /*extern char *strncpy();*/ ++ ++ if (prog == NULL || source == NULL || dest == NULL) { ++ regerror("NULL parm to regsub"); ++ return; ++ } ++ if (UCHARAT(prog->program) != MAGIC) { ++ regerror("damaged regexp fed to regsub"); ++ return; ++ } ++ ++ src = source; ++ dst = dest; ++ while ((c = *src++) != '\0') { ++ if (c == '&') ++ no = 0; ++ else if (c == '\\' && '0' <= *src && *src <= '9') ++ no = *src++ - '0'; ++ else ++ no = -1; ++ ++ if (no < 0) { /* Ordinary character. */ ++ if (c == '\\' && (*src == '\\' || *src == '&')) ++ c = *src++; ++ *dst++ = c; ++ } else if (prog->startp[no] != NULL && prog->endp[no] != NULL) { ++ len = prog->endp[no] - prog->startp[no]; ++ (void) strncpy(dst, prog->startp[no], len); ++ dst += len; ++ if (len != 0 && *(dst-1) == '\0') { /* strncpy hit NUL. */ ++ regerror("damaged match string"); ++ return; ++ } ++ } ++ } ++ *dst++ = '\0'; ++} diff --git a/target/linux/generic-2.6/patches/101-ipp2p_0.8.1rc1.patch b/target/linux/generic-2.6/patches/101-ipp2p_0.8.1rc1.patch new file mode 100644 index 0000000..9841db9 --- /dev/null +++ b/target/linux/generic-2.6/patches/101-ipp2p_0.8.1rc1.patch @@ -0,0 +1,935 @@ +diff -Nur linux-2.6.17/include/linux/netfilter_ipv4/ipt_ipp2p.h linux-2.6.17-owrt/include/linux/netfilter_ipv4/ipt_ipp2p.h +--- linux-2.6.17/include/linux/netfilter_ipv4/ipt_ipp2p.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.17-owrt/include/linux/netfilter_ipv4/ipt_ipp2p.h 2006-06-18 12:37:14.000000000 +0200 +@@ -0,0 +1,31 @@ ++#ifndef __IPT_IPP2P_H ++#define __IPT_IPP2P_H ++#define IPP2P_VERSION "0.8.1_rc1" ++ ++struct ipt_p2p_info { ++ int cmd; ++ int debug; ++}; ++ ++#endif //__IPT_IPP2P_H ++ ++#define SHORT_HAND_IPP2P 1 /* --ipp2p switch*/ ++//#define SHORT_HAND_DATA 4 /* --ipp2p-data switch*/ ++#define SHORT_HAND_NONE 5 /* no short hand*/ ++ ++#define IPP2P_EDK (1 << 1) ++#define IPP2P_DATA_KAZAA (1 << 2) ++#define IPP2P_DATA_EDK (1 << 3) ++#define IPP2P_DATA_DC (1 << 4) ++#define IPP2P_DC (1 << 5) ++#define IPP2P_DATA_GNU (1 << 6) ++#define IPP2P_GNU (1 << 7) ++#define IPP2P_KAZAA (1 << 8) ++#define IPP2P_BIT (1 << 9) ++#define IPP2P_APPLE (1 << 10) ++#define IPP2P_SOUL (1 << 11) ++#define IPP2P_WINMX (1 << 12) ++#define IPP2P_ARES (1 << 13) ++#define IPP2P_MUTE (1 << 14) ++#define IPP2P_WASTE (1 << 15) ++#define IPP2P_XDCC (1 << 16) +diff -Nur linux-2.6.17/net/ipv4/netfilter/ipt_ipp2p.c linux-2.6.17-owrt/net/ipv4/netfilter/ipt_ipp2p.c +--- linux-2.6.17/net/ipv4/netfilter/ipt_ipp2p.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.17-owrt/net/ipv4/netfilter/ipt_ipp2p.c 2006-06-18 12:37:14.000000000 +0200 +@@ -0,0 +1,868 @@ ++#if defined(MODVERSIONS) ++#include <linux/modversions.h> ++#endif ++#include <linux/module.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/version.h> ++#include <linux/netfilter_ipv4/ipt_ipp2p.h> ++#include <net/tcp.h> ++#include <net/udp.h> ++ ++#define get_u8(X,O) (*(__u8 *)(X + O)) ++#define get_u16(X,O) (*(__u16 *)(X + O)) ++#define get_u32(X,O) (*(__u32 *)(X + O)) ++ ++MODULE_AUTHOR("Eicke Friedrich/Klaus Degner <ipp2p@ipp2p.org>"); ++MODULE_DESCRIPTION("An extension to iptables to identify P2P traffic."); ++MODULE_LICENSE("GPL"); ++ ++ ++/*Search for UDP eDonkey/eMule/Kad commands*/ ++int ++udp_search_edk (unsigned char *haystack, int packet_len) ++{ ++ unsigned char *t = haystack; ++ t += 8; ++ ++ switch (t[0]) { ++ case 0xe3: ++ { /*edonkey*/ ++ switch (t[1]) ++ { ++ /* client -> server status request */ ++ case 0x96: ++ if (packet_len == 14) return ((IPP2P_EDK * 100) + 50); ++ break; ++ /* server -> client status request */ ++ case 0x97: if (packet_len == 42) return ((IPP2P_EDK * 100) + 51); ++ break; ++ /* server description request */ ++ /* e3 2a ff f0 .. | size == 6 */ ++ case 0xa2: if ( (packet_len == 14) && ( get_u16(t,2) == __constant_htons(0xfff0) ) ) return ((IPP2P_EDK * 100) + 52); ++ break; ++ /* server description response */ ++ /* e3 a3 ff f0 .. | size > 40 && size < 200 */ ++ //case 0xa3: return ((IPP2P_EDK * 100) + 53); ++ // break; ++ case 0x9a: if (packet_len==26) return ((IPP2P_EDK * 100) + 54); ++ break; ++ ++ case 0x92: if (packet_len==18) return ((IPP2P_EDK * 100) + 55); ++ break; ++ } ++ break; ++ } ++ case 0xe4: ++ { ++ switch (t[1]) ++ { ++ /* e4 20 .. | size == 43 */ ++ case 0x20: if ((packet_len == 43) && (t[2] != 0x00) && (t[34] != 0x00)) return ((IPP2P_EDK * 100) + 60); ++ break; ++ /* e4 00 .. 00 | size == 35 ? */ ++ case 0x00: if ((packet_len == 35) && (t[26] == 0x00)) return ((IPP2P_EDK * 100) + 61); ++ break; ++ /* e4 10 .. 00 | size == 35 ? */ ++ case 0x10: if ((packet_len == 35) && (t[26] == 0x00)) return ((IPP2P_EDK * 100) + 62); ++ break; ++ /* e4 18 .. 00 | size == 35 ? */ ++ case 0x18: if ((packet_len == 35) && (t[26] == 0x00)) return ((IPP2P_EDK * 100) + 63); ++ break; ++ /* e4 52 .. | size = 44 */ ++ case 0x52: if (packet_len == 44 ) return ((IPP2P_EDK * 100) + 64); ++ break; ++ /* e4 58 .. | size == 6 */ ++ case 0x58: if (packet_len == 14 ) return ((IPP2P_EDK * 100) + 65); ++ break; ++ /* e4 59 .. | size == 2 */ ++ case 0x59: if (packet_len == 10 )return ((IPP2P_EDK * 100) + 66); ++ break; ++ /* e4 28 .. | packet_len == 52,77,102,127... */ ++ case 0x28: if (((packet_len-52) % 25) == 0) return ((IPP2P_EDK * 100) + 67); ++ break; ++ /* e4 50 xx xx | size == 4 */ ++ case 0x50: if (packet_len == 12) return ((IPP2P_EDK * 100) + 68); ++ break; ++ /* e4 40 xx xx | size == 48 */ ++ case 0x40: if (packet_len == 56) return ((IPP2P_EDK * 100) + 69); ++ break; ++ } ++ break; ++ } ++ } /* end of switch (t[0]) */ ++ return 0; ++}/*udp_search_edk*/ ++ ++ ++/*Search for UDP Gnutella commands*/ ++int ++udp_search_gnu (unsigned char *haystack, int packet_len) ++{ ++ unsigned char *t = haystack; ++ t += 8; ++ ++ if (memcmp(t, "GND", 3) == 0) return ((IPP2P_GNU * 100) + 51); ++ if (memcmp(t, "GNUTELLA ", 9) == 0) return ((IPP2P_GNU * 100) + 52); ++ return 0; ++}/*udp_search_gnu*/ ++ ++ ++/*Search for UDP KaZaA commands*/ ++int ++udp_search_kazaa (unsigned char *haystack, int packet_len) ++{ ++ unsigned char *t = haystack; ++ ++ if (t[packet_len-1] == 0x00){ ++ t += (packet_len - 6); ++ if (memcmp(t, "KaZaA", 5) == 0) return (IPP2P_KAZAA * 100 +50); ++ } ++ ++ return 0; ++}/*udp_search_kazaa*/ ++ ++/*Search for UDP DirectConnect commands*/ ++int ++udp_search_directconnect (unsigned char *haystack, int packet_len) ++{ ++ unsigned char *t = haystack; ++ if ((*(t + 8) == 0x24) && (*(t + packet_len - 1) == 0x7c)) { ++ t+=8; ++ if (memcmp(t, "SR ", 3) == 0) return ((IPP2P_DC * 100) + 60); ++ if (memcmp(t, "Ping ", 5) == 0) return ((IPP2P_DC * 100) + 61); ++ } ++ return 0; ++}/*udp_search_directconnect*/ ++ ++ ++ ++/*Search for UDP BitTorrent commands*/ ++int ++udp_search_bit (unsigned char *haystack, int packet_len) ++{ ++ switch(packet_len) ++ { ++ case 24: ++ /* ^ 00 00 04 17 27 10 19 80 */ ++ if ((ntohl(get_u32(haystack, 8)) == 0x00000417) && (ntohl(get_u32(haystack, 12)) == 0x27101980)) ++ return (IPP2P_BIT * 100 + 50); ++ break; ++ case 44: ++ if (get_u32(haystack, 16) == __constant_htonl(0x00000400) && get_u32(haystack, 36) == __constant_htonl(0x00000104)) ++ return (IPP2P_BIT * 100 + 51); ++ if (get_u32(haystack, 16) == __constant_htonl(0x00000400)) ++ return (IPP2P_BIT * 100 + 61); ++ break; ++ case 65: ++ if (get_u32(haystack, 16) == __constant_htonl(0x00000404) && get_u32(haystack, 36) == __constant_htonl(0x00000104)) ++ return (IPP2P_BIT * 100 + 52); ++ if (get_u32(haystack, 16) == __constant_htonl(0x00000404)) ++ return (IPP2P_BIT * 100 + 62); ++ break; ++ case 67: ++ if (get_u32(haystack, 16) == __constant_htonl(0x00000406) && get_u32(haystack, 36) == __constant_htonl(0x00000104)) ++ return (IPP2P_BIT * 100 + 53); ++ if (get_u32(haystack, 16) == __constant_htonl(0x00000406)) ++ return (IPP2P_BIT * 100 + 63); ++ break; ++ case 211: ++ if (get_u32(haystack, 8) == __constant_htonl(0x00000405)) ++ return (IPP2P_BIT * 100 + 54); ++ break; ++ case 29: ++ if ((get_u32(haystack, 8) == __constant_htonl(0x00000401))) ++ return (IPP2P_BIT * 100 + 55); ++ break; ++ case 52: ++ if (get_u32(haystack,8) == __constant_htonl(0x00000827) && ++ get_u32(haystack,12) == __constant_htonl(0x37502950)) ++ return (IPP2P_BIT * 100 + 80); ++ break; ++ default: ++ /* this packet does not have a constant size */ ++ if (packet_len >= 40 && get_u32(haystack, 16) == __constant_htonl(0x00000402) && get_u32(haystack, 36) == __constant_htonl(0x00000104)) ++ return (IPP2P_BIT * 100 + 56); ++ break; ++ } ++ ++ /* some extra-bitcomet rules: ++ * "d1:" [a|r] "d2:id20:" ++ */ ++ if (packet_len > 30 && get_u8(haystack, 8) == 'd' && get_u8(haystack, 9) == '1' && get_u8(haystack, 10) == ':' ) ++ { ++ if (get_u8(haystack, 11) == 'a' || get_u8(haystack, 11) == 'r') ++ { ++ if (memcmp(haystack+12,"d2:id20:",8)==0) ++ return (IPP2P_BIT * 100 + 57); ++ } ++ } ++ ++#if 0 ++ /* bitlord rules */ ++ /* packetlen must be bigger than 40 */ ++ /* first 4 bytes are zero */ ++ if (packet_len > 40 && get_u32(haystack, 8) == 0x00000000) ++ { ++ /* first rule: 00 00 00 00 01 00 00 xx xx xx xx 00 00 00 00*/ ++ if (get_u32(haystack, 12) == 0x00000000 && ++ get_u32(haystack, 16) == 0x00010000 && ++ get_u32(haystack, 24) == 0x00000000 ) ++ return (IPP2P_BIT * 100 + 71); ++ ++ /* 00 01 00 00 0d 00 00 xx xx xx xx 00 00 00 00*/ ++ if (get_u32(haystack, 12) == 0x00000001 && ++ get_u32(haystack, 16) == 0x000d0000 && ++ get_u32(haystack, 24) == 0x00000000 ) ++ return (IPP2P_BIT * 100 + 71); ++ ++ ++ } ++#endif ++ ++ return 0; ++}/*udp_search_bit*/ ++ ++ ++ ++/*Search for Ares commands*/ ++//#define IPP2P_DEBUG_ARES ++int ++search_ares (const unsigned char *payload, const u16 plen) ++//int search_ares (unsigned char *haystack, int packet_len, int head_len) ++{ ++// const unsigned char *t = haystack + head_len; ++ ++ /* all ares packets start with */ ++ if (payload[1] == 0 && (plen - payload[0]) == 3) ++ { ++ switch (payload[2]) ++ { ++ case 0x5a: ++ /* ares connect */ ++ if ( plen == 6 && payload[5] == 0x05 ) return ((IPP2P_ARES * 100) + 1); ++ break; ++ case 0x09: ++ /* ares search, min 3 chars --> 14 bytes ++ * lets define a search can be up to 30 chars --> max 34 bytes ++ */ ++ if ( plen >= 14 && plen <= 34 ) return ((IPP2P_ARES * 100) + 1); ++ break; ++#ifdef IPP2P_DEBUG_ARES ++ default: ++ printk(KERN_DEBUG "Unknown Ares command %x recognized, len: %u \n", (unsigned int) payload[2],plen); ++#endif /* IPP2P_DEBUG_ARES */ ++ } ++ } ++ ++#if 0 ++ /* found connect packet: 03 00 5a 04 03 05 */ ++ /* new version ares 1.8: 03 00 5a xx xx 05 */ ++ if ((plen) == 6){ /* possible connect command*/ ++ if ((payload[0] == 0x03) && (payload[1] == 0x00) && (payload[2] == 0x5a) && (payload[5] == 0x05)) ++ return ((IPP2P_ARES * 100) + 1); ++ } ++ if ((plen) == 60){ /* possible download command*/ ++ if ((payload[59] == 0x0a) && (payload[58] == 0x0a)){ ++ if (memcmp(t, "PUSH SHA1:", 10) == 0) /* found download command */ ++ return ((IPP2P_ARES * 100) + 2); ++ } ++ } ++#endif ++ ++ return 0; ++} /*search_ares*/ ++ ++/*Search for SoulSeek commands*/ ++int ++search_soul (const unsigned char *payload, const u16 plen) ++{ ++//#define IPP2P_DEBUG_SOUL ++ /* match: xx xx xx xx | xx = sizeof(payload) - 4 */ ++ if (get_u32(payload, 0) == (plen - 4)){ ++ const __u32 m=get_u32(payload, 4); ++ /* match 00 yy yy 00, yy can be everything */ ++ if ( get_u8(payload, 4) == 0x00 && get_u8(payload, 7) == 0x00 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "0: Soulseek command 0x%x recognized\n",get_u32(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 1); ++ } ++ ++ /* next match: 01 yy 00 00 | yy can be everything */ ++ if ( get_u8(payload, 4) == 0x01 && get_u16(payload, 6) == 0x0000 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "1: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 2); ++ } ++ ++ /* other soulseek commandos are: 1-5,7,9,13-18,22,23,26,28,35-37,40-46,50,51,60,62-69,91,92,1001 */ ++ /* try to do this in an intelligent way */ ++ /* get all small commandos */ ++ switch(m) ++ { ++ case 7: ++ case 9: ++ case 22: ++ case 23: ++ case 26: ++ case 28: ++ case 50: ++ case 51: ++ case 60: ++ case 91: ++ case 92: ++ case 1001: ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "2: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 3); ++ } ++ ++ if (m > 0 && m < 6 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "3: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 4); ++ } ++ if (m > 12 && m < 19 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "4: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 5); ++ } ++ ++ if (m > 34 && m < 38 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "5: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 6); ++ } ++ ++ if (m > 39 && m < 47 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "6: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 7); ++ } ++ ++ if (m > 61 && m < 70 ) ++ { ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "7: Soulseek command 0x%x recognized\n",get_u16(payload, 4)); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 8); ++ } ++ ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "unknown SOULSEEK command: 0x%x, first 16 bit: 0x%x, first 8 bit: 0x%x ,soulseek ???\n",get_u32(payload, 4),get_u16(payload, 4) >> 16,get_u8(payload, 4) >> 24); ++#endif /* IPP2P_DEBUG_SOUL */ ++ } ++ ++ /* match 14 00 00 00 01 yy 00 00 00 STRING(YY) 01 00 00 00 00 46|50 00 00 00 00 */ ++ /* without size at the beginning !!! */ ++ if ( get_u32(payload, 0) == 0x14 && get_u8(payload, 4) == 0x01 ) ++ { ++ __u32 y=get_u32(payload, 5); ++ /* we need 19 chars + string */ ++ if ( (y + 19) <= (plen) ) ++ { ++ const unsigned char *w=payload+9+y; ++ if (get_u32(w, 0) == 0x01 && ( get_u16(w, 4) == 0x4600 || get_u16(w, 4) == 0x5000) && get_u32(w, 6) == 0x00); ++#ifdef IPP2P_DEBUG_SOUL ++ printk(KERN_DEBUG "Soulssek special client command recognized\n"); ++#endif /* IPP2P_DEBUG_SOUL */ ++ return ((IPP2P_SOUL * 100) + 9); ++ } ++ } ++ return 0; ++} ++ ++ ++/*Search for WinMX commands*/ ++int ++search_winmx (const unsigned char *payload, const u16 plen) ++{ ++//#define IPP2P_DEBUG_WINMX ++ if (((plen) == 4) && (memcmp(payload, "SEND", 4) == 0)) return ((IPP2P_WINMX * 100) + 1); ++ if (((plen) == 3) && (memcmp(payload, "GET", 3) == 0)) return ((IPP2P_WINMX * 100) + 2); ++ //if (packet_len < (head_len + 10)) return 0; ++ if (plen < 10) return 0; ++ ++ if ((memcmp(payload, "SEND", 4) == 0) || (memcmp(payload, "GET", 3) == 0)){ ++ u16 c=4; ++ const u16 end=plen-2; ++ u8 count=0; ++ while (c < end) ++ { ++ if (payload[c]== 0x20 && payload[c+1] == 0x22) ++ { ++ c++; ++ count++; ++ if (count>=2) return ((IPP2P_WINMX * 100) + 3); ++ } ++ c++; ++ } ++ } ++ ++ if ( plen == 149 && payload[0] == '8' ) ++ { ++#ifdef IPP2P_DEBUG_WINMX ++ printk(KERN_INFO "maybe WinMX\n"); ++#endif ++ if (get_u32(payload,17) == 0 && get_u32(payload,21) == 0 && get_u32(payload,25) == 0 && ++// get_u32(payload,33) == __constant_htonl(0x71182b1a) && get_u32(payload,37) == __constant_htonl(0x05050000) && ++// get_u32(payload,133) == __constant_htonl(0x31097edf) && get_u32(payload,145) == __constant_htonl(0xdcb8f792)) ++ get_u16(payload,39) == 0 && get_u16(payload,135) == __constant_htons(0x7edf) && get_u16(payload,147) == __constant_htons(0xf792)) ++ ++ { ++#ifdef IPP2P_DEBUG_WINMX ++ printk(KERN_INFO "got WinMX\n"); ++#endif ++ return ((IPP2P_WINMX * 100) + 4); ++ } ++ } ++ return 0; ++} /*search_winmx*/ ++ ++ ++/*Search for appleJuice commands*/ ++int ++search_apple (const unsigned char *payload, const u16 plen) ++{ ++ if ( (plen > 7) && (payload[6] == 0x0d) && (payload[7] == 0x0a) && (memcmp(payload, "ajprot", 6) == 0)) return (IPP2P_APPLE * 100); ++ ++ return 0; ++} ++ ++ ++/*Search for BitTorrent commands*/ ++int ++search_bittorrent (const unsigned char *payload, const u16 plen) ++{ ++ if (plen > 20) ++ { ++ /* test for match 0x13+"BitTorrent protocol" */ ++ if (payload[0] == 0x13) ++ { ++ if (memcmp(payload+1, "BitTorrent protocol", 19) == 0) return (IPP2P_BIT * 100); ++ } ++ ++ /* get tracker commandos, all starts with GET / ++ * then it can follow: scrape| announce ++ * and then ?hash_info= ++ */ ++ if (memcmp(payload,"GET /",5) == 0) ++ { ++ /* message scrape */ ++ if ( memcmp(payload+5,"scrape?info_hash=",17)==0 ) return (IPP2P_BIT * 100 + 1); ++ /* message announce */ ++ if ( memcmp(payload+5,"announce?info_hash=",19)==0 ) return (IPP2P_BIT * 100 + 2); ++ } ++ } ++ else ++ { ++ /* bitcomet encryptes the first packet, so we have to detect another ++ * one later in the flow */ ++ /* first try failed, too many missdetections */ ++ //if ( size == 5 && get_u32(t,0) == __constant_htonl(1) && t[4] < 3) return (IPP2P_BIT * 100 + 3); ++ ++ /* second try: block request packets */ ++ if ( plen == 17 && get_u32(payload,0) == __constant_htonl(0x0d) && payload[4] == 0x06 && get_u32(payload,13) == __constant_htonl(0x4000) ) return (IPP2P_BIT * 100 + 3); ++ } ++ ++ return 0; ++} ++ ++ ++ ++/*check for Kazaa get command*/ ++int ++search_kazaa (const unsigned char *payload, const u16 plen) ++ ++{ ++ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a) && memcmp(payload, "GET /.hash=", 11) == 0) ++ return (IPP2P_DATA_KAZAA * 100); ++ ++ return 0; ++} ++ ++ ++/*check for gnutella get command*/ ++int ++search_gnu (const unsigned char *payload, const u16 plen) ++{ ++ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a)) ++ { ++ if (memcmp(payload, "GET /get/", 9) == 0) return ((IPP2P_DATA_GNU * 100) + 1); ++ if (memcmp(payload, "GET /uri-res/", 13) == 0) return ((IPP2P_DATA_GNU * 100) + 2); ++ } ++ return 0; ++} ++ ++ ++/*check for gnutella get commands and other typical data*/ ++int ++search_all_gnu (const unsigned char *payload, const u16 plen) ++{ ++ ++ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a)) ++ { ++ ++ if (memcmp(payload, "GNUTELLA CONNECT/", 17) == 0) return ((IPP2P_GNU * 100) + 1); ++ if (memcmp(payload, "GNUTELLA/", 9) == 0) return ((IPP2P_GNU * 100) + 2); ++ ++ ++ if ((memcmp(payload, "GET /get/", 9) == 0) || (memcmp(payload, "GET /uri-res/", 13) == 0)) ++ { ++ u16 c=8; ++ const u16 end=plen-22; ++ while (c < end) { ++ if ( payload[c] == 0x0a && payload[c+1] == 0x0d && ((memcmp(&payload[c+2], "X-Gnutella-", 11) == 0) || (memcmp(&payload[c+2], "X-Queue:", 8) == 0))) ++ return ((IPP2P_GNU * 100) + 3); ++ c++; ++ } ++ } ++ } ++ return 0; ++} ++ ++ ++/*check for KaZaA download commands and other typical data*/ ++int ++search_all_kazaa (const unsigned char *payload, const u16 plen) ++{ ++ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a)) ++ { ++ ++ if (memcmp(payload, "GIVE ", 5) == 0) return ((IPP2P_KAZAA * 100) + 1); ++ ++ if (memcmp(payload, "GET /", 5) == 0) { ++ u16 c = 8; ++ const u16 end=plen-22; ++ while (c < end) { ++ if ( payload[c] == 0x0a && payload[c+1] == 0x0d && ((memcmp(&payload[c+2], "X-Kazaa-Username: ", 18) == 0) || (memcmp(&payload[c+2], "User-Agent: PeerEnabler/", 24) == 0))) ++ return ((IPP2P_KAZAA * 100) + 2); ++ c++; ++ } ++ } ++ } ++ return 0; ++} ++ ++/*fast check for edonkey file segment transfer command*/ ++int ++search_edk (const unsigned char *payload, const u16 plen) ++{ ++ if (payload[0] != 0xe3) ++ return 0; ++ else { ++ if (payload[5] == 0x47) ++ return (IPP2P_DATA_EDK * 100); ++ else ++ return 0; ++ } ++} ++ ++ ++ ++/*intensive but slower search for some edonkey packets including size-check*/ ++int ++search_all_edk (const unsigned char *payload, const u16 plen) ++{ ++ if (payload[0] != 0xe3) ++ return 0; ++ else { ++ //t += head_len; ++ const u16 cmd = get_u16(payload, 1); ++ if (cmd == (plen - 5)) { ++ switch (payload[5]) { ++ case 0x01: return ((IPP2P_EDK * 100) + 1); /*Client: hello or Server:hello*/ ++ case 0x4c: return ((IPP2P_EDK * 100) + 9); /*Client: Hello-Answer*/ ++ } ++ } ++ return 0; ++ } ++} ++ ++ ++/*fast check for Direct Connect send command*/ ++int ++search_dc (const unsigned char *payload, const u16 plen) ++{ ++ ++ if (payload[0] != 0x24 ) ++ return 0; ++ else { ++ if (memcmp(&payload[1], "Send|", 5) == 0) ++ return (IPP2P_DATA_DC * 100); ++ else ++ return 0; ++ } ++ ++} ++ ++ ++/*intensive but slower check for all direct connect packets*/ ++int ++search_all_dc (const unsigned char *payload, const u16 plen) ++{ ++// unsigned char *t = haystack; ++ ++ if (payload[0] == 0x24 && payload[plen-1] == 0x7c) ++ { ++ const unsigned char *t=&payload[1]; ++ /* Client-Hub-Protocol */ ++ if (memcmp(t, "Lock ", 5) == 0) return ((IPP2P_DC * 100) + 1); ++ /* Client-Client-Protocol, some are already recognized by client-hub (like lock) */ ++ if (memcmp(t, "MyNick ", 7) == 0) return ((IPP2P_DC * 100) + 38); ++ } ++ return 0; ++} ++ ++/*check for mute*/ ++int ++search_mute (const unsigned char *payload, const u16 plen) ++{ ++ if ( plen == 209 || plen == 345 || plen == 473 || plen == 609 || plen == 1121 ) ++ { ++ //printk(KERN_DEBUG "size hit: %u",size); ++ if (memcmp(payload,"PublicKey: ",11) == 0 ) ++ { ++ return ((IPP2P_MUTE * 100) + 0); ++ ++/* if (memcmp(t+size-14,"\x0aEndPublicKey\x0a",14) == 0) ++ { ++ printk(KERN_DEBUG "end pubic key hit: %u",size); ++ ++ }*/ ++ } ++ } ++ return 0; ++} ++ ++ ++/* check for xdcc */ ++int ++search_xdcc (const unsigned char *payload, const u16 plen) ++{ ++ /* search in small packets only */ ++ if (plen > 20 && plen < 200 && payload[plen-1] == 0x0a && payload[plen-2] == 0x0d && memcmp(payload,"PRIVMSG ",8) == 0) ++ { ++ ++ u16 x=10; ++ const u16 end=plen - 13; ++ ++ /* is seems to be a irc private massage, chedck for xdcc command */ ++ while (x < end) ++ { ++ if (payload[x] == ':') ++ { ++ if ( memcmp(&payload[x+1],"xdcc send #",11) == 0 ) ++ return ((IPP2P_XDCC * 100) + 0); ++ } ++ x++; ++ } ++ } ++ return 0; ++} ++ ++/* search for waste */ ++int search_waste(const unsigned char *payload, const u16 plen) ++{ ++ if ( plen >= 8 && memcmp(payload,"GET.sha1:",9) == 0) ++ return ((IPP2P_WASTE * 100) + 0); ++ ++ return 0; ++} ++ ++ ++static struct { ++ int command; ++ __u8 short_hand; /*for fucntions included in short hands*/ ++ int packet_len; ++ int (*function_name) (const unsigned char *, const u16); ++} matchlist[] = { ++ {IPP2P_EDK,SHORT_HAND_IPP2P,20, &search_all_edk}, ++// {IPP2P_DATA_KAZAA,SHORT_HAND_DATA,200, &search_kazaa}, ++// {IPP2P_DATA_EDK,SHORT_HAND_DATA,60, &search_edk}, ++// {IPP2P_DATA_DC,SHORT_HAND_DATA,26, &search_dc}, ++ {IPP2P_DC,SHORT_HAND_IPP2P,5, search_all_dc}, ++// {IPP2P_DATA_GNU,SHORT_HAND_DATA,40, &search_gnu}, ++ {IPP2P_GNU,SHORT_HAND_IPP2P,5, &search_all_gnu}, ++ {IPP2P_KAZAA,SHORT_HAND_IPP2P,5, &search_all_kazaa}, ++ {IPP2P_BIT,SHORT_HAND_IPP2P,20, &search_bittorrent}, ++ {IPP2P_APPLE,SHORT_HAND_IPP2P,5, &search_apple}, ++ {IPP2P_SOUL,SHORT_HAND_IPP2P,5, &search_soul}, ++ {IPP2P_WINMX,SHORT_HAND_IPP2P,2, &search_winmx}, ++ {IPP2P_ARES,SHORT_HAND_IPP2P,5, &search_ares}, ++ {IPP2P_MUTE,SHORT_HAND_NONE,200, &search_mute}, ++ {IPP2P_WASTE,SHORT_HAND_NONE,5, &search_waste}, ++ {IPP2P_XDCC,SHORT_HAND_NONE,5, &search_xdcc}, ++ {0,0,0,NULL} ++}; ++ ++ ++static struct { ++ int command; ++ __u8 short_hand; /*for fucntions included in short hands*/ ++ int packet_len; ++ int (*function_name) (unsigned char *, int); ++} udp_list[] = { ++ {IPP2P_KAZAA,SHORT_HAND_IPP2P,14, &udp_search_kazaa}, ++ {IPP2P_BIT,SHORT_HAND_IPP2P,23, &udp_search_bit}, ++ {IPP2P_GNU,SHORT_HAND_IPP2P,11, &udp_search_gnu}, ++ {IPP2P_EDK,SHORT_HAND_IPP2P,9, &udp_search_edk}, ++ {IPP2P_DC,SHORT_HAND_IPP2P,12, &udp_search_directconnect}, ++ {0,0,0,NULL} ++}; ++ ++ ++static int ++match(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *matchinfo, ++ int offset, ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ const void *hdr, ++ u_int16_t datalen, ++#endif ++ ++ int *hotdrop) ++{ ++ const struct ipt_p2p_info *info = matchinfo; ++ unsigned char *haystack; ++ struct iphdr *ip = skb->nh.iph; ++ int p2p_result = 0, i = 0; ++// int head_len; ++ int hlen = ntohs(ip->tot_len)-(ip->ihl*4); /*hlen = packet-data length*/ ++ ++ /*must not be a fragment*/ ++ if (offset) { ++ if (info->debug) printk("IPP2P.match: offset found %i \n",offset); ++ return 0; ++ } ++ ++ /*make sure that skb is linear*/ ++ if(skb_is_nonlinear(skb)){ ++ if (info->debug) printk("IPP2P.match: nonlinear skb found\n"); ++ return 0; ++ } ++ ++ ++ haystack=(char *)ip+(ip->ihl*4); /*haystack = packet data*/ ++ ++ switch (ip->protocol){ ++ case IPPROTO_TCP: /*what to do with a TCP packet*/ ++ { ++ struct tcphdr *tcph = (void *) ip + ip->ihl * 4; ++ ++ if (tcph->fin) return 0; /*if FIN bit is set bail out*/ ++ if (tcph->syn) return 0; /*if SYN bit is set bail out*/ ++ if (tcph->rst) return 0; /*if RST bit is set bail out*/ ++ ++ haystack += tcph->doff * 4; /*get TCP-Header-Size*/ ++ hlen -= tcph->doff * 4; ++ while (matchlist[i].command) { ++ if ((((info->cmd & matchlist[i].command) == matchlist[i].command) || ++ ((info->cmd & matchlist[i].short_hand) == matchlist[i].short_hand)) && ++ (hlen > matchlist[i].packet_len)) { ++ p2p_result = matchlist[i].function_name(haystack, hlen); ++ if (p2p_result) ++ { ++ if (info->debug) printk("IPP2P.debug:TCP-match: %i from: %u.%u.%u.%u:%i to: %u.%u.%u.%u:%i Length: %i\n", ++ p2p_result, NIPQUAD(ip->saddr),ntohs(tcph->source), NIPQUAD(ip->daddr),ntohs(tcph->dest),hlen); ++ return p2p_result; ++ } ++ } ++ i++; ++ } ++ return p2p_result; ++ } ++ ++ case IPPROTO_UDP: /*what to do with an UDP packet*/ ++ { ++ struct udphdr *udph = (void *) ip + ip->ihl * 4; ++ ++ while (udp_list[i].command){ ++ if ((((info->cmd & udp_list[i].command) == udp_list[i].command) || ++ ((info->cmd & udp_list[i].short_hand) == udp_list[i].short_hand)) && ++ (hlen > udp_list[i].packet_len)) { ++ p2p_result = udp_list[i].function_name(haystack, hlen); ++ if (p2p_result){ ++ if (info->debug) printk("IPP2P.debug:UDP-match: %i from: %u.%u.%u.%u:%i to: %u.%u.%u.%u:%i Length: %i\n", ++ p2p_result, NIPQUAD(ip->saddr),ntohs(udph->source), NIPQUAD(ip->daddr),ntohs(udph->dest),hlen); ++ return p2p_result; ++ } ++ } ++ i++; ++ } ++ return p2p_result; ++ } ++ ++ default: return 0; ++ } ++} ++ ++ ++ ++static int ++checkentry(const char *tablename, ++ const struct ipt_ip *ip, ++ void *matchinfo, ++ unsigned int matchsize, ++ unsigned int hook_mask) ++{ ++ /* Must specify -p tcp */ ++/* if (ip->proto != IPPROTO_TCP || (ip->invflags & IPT_INV_PROTO)) { ++ * printk("ipp2p: Only works on TCP packets, use -p tcp\n"); ++ * return 0; ++ * }*/ ++ return 1; ++} ++ ++ ++ ++ ++static struct ipt_match ipp2p_match = { ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++ { NULL, NULL }, ++ "ipp2p", ++ &match, ++ &checkentry, ++ NULL, ++ THIS_MODULE ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++ .name = "ipp2p", ++ .match = &match, ++ .checkentry = &checkentry, ++ .me = THIS_MODULE, ++#endif ++}; ++ ++ ++static int __init init(void) ++{ ++ printk(KERN_INFO "IPP2P v%s loading\n", IPP2P_VERSION); ++ return ipt_register_match(&ipp2p_match); ++} ++ ++static void __exit fini(void) ++{ ++ ipt_unregister_match(&ipp2p_match); ++ printk(KERN_INFO "IPP2P v%s unloaded\n", IPP2P_VERSION); ++} ++ ++module_init(init); ++module_exit(fini); ++ ++ +diff -Nur linux-2.6.17/net/ipv4/netfilter/Kconfig linux-2.6.17-owrt/net/ipv4/netfilter/Kconfig +--- linux-2.6.17/net/ipv4/netfilter/Kconfig 2006-06-18 12:36:55.000000000 +0200 ++++ linux-2.6.17-owrt/net/ipv4/netfilter/Kconfig 2006-06-18 12:37:14.000000000 +0200 +@@ -222,6 +222,12 @@ + + To compile it as a module, choose M here. If unsure, say N. + ++config IP_NF_MATCH_IPP2P ++ tristate "IPP2P" ++ depends on IP_NF_IPTABLES ++ help ++ Module for matching traffic of various Peer-to-Peer applications ++ + config IP_NF_MATCH_TOS + tristate "TOS match support" + depends on IP_NF_IPTABLES +diff -Nur linux-2.6.17/net/ipv4/netfilter/Makefile linux-2.6.17-owrt/net/ipv4/netfilter/Makefile +--- linux-2.6.17/net/ipv4/netfilter/Makefile 2006-06-18 12:36:55.000000000 +0200 ++++ linux-2.6.17-owrt/net/ipv4/netfilter/Makefile 2006-06-18 12:38:11.000000000 +0200 +@@ -61,7 +61,7 @@ + obj-$(CONFIG_IP_NF_MATCH_AH) += ipt_ah.o + obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o + obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o +- ++obj-$(CONFIG_IP_NF_MATCH_IPP2P) += ipt_ipp2p.o + obj-$(CONFIG_IP_NF_MATCH_LAYER7) += ipt_layer7.o + + # targets diff --git a/target/linux/generic-2.6/patches/102-openswan-2.4.0.kernel-2.6-natt.patch b/target/linux/generic-2.6/patches/102-openswan-2.4.0.kernel-2.6-natt.patch new file mode 100644 index 0000000..6d6939b --- /dev/null +++ b/target/linux/generic-2.6/patches/102-openswan-2.4.0.kernel-2.6-natt.patch @@ -0,0 +1,171 @@ +diff -Nur linux-2.6.16/include/net/xfrmudp.h linux-2.6.16-owrt/include/net/xfrmudp.h +--- linux-2.6.16/include/net/xfrmudp.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/include/net/xfrmudp.h 2006-03-22 21:39:54.000000000 +0100 +@@ -0,0 +1,10 @@ ++/* ++ * pointer to function for type that xfrm4_input wants, to permit ++ * decoupling of XFRM from udp.c ++ */ ++#define HAVE_XFRM4_UDP_REGISTER ++ ++typedef int (*xfrm4_rcv_encap_t)(struct sk_buff *skb, __u16 encap_type); ++extern int udp4_register_esp_rcvencap(xfrm4_rcv_encap_t func ++ , xfrm4_rcv_encap_t *oldfunc); ++extern int udp4_unregister_esp_rcvencap(xfrm4_rcv_encap_t func); +diff -Nur linux-2.6.16/net/ipv4/Kconfig linux-2.6.16-owrt/net/ipv4/Kconfig +--- linux-2.6.16/net/ipv4/Kconfig 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-owrt/net/ipv4/Kconfig 2006-03-22 21:49:04.000000000 +0100 +@@ -271,6 +271,12 @@ + Network), but can be distributed all over the Internet. If you want + to do that, say Y here and to "IP multicast routing" below. + ++config IPSEC_NAT_TRAVERSAL ++ bool "IPSEC NAT-Traversal (KLIPS compatible)" ++ depends on INET ++ ---help--- ++ Includes support for RFC3947/RFC3948 NAT-Traversal of ESP over UDP. ++ + config IP_MROUTE + bool "IP: multicast routing" + depends on IP_MULTICAST +diff -Nur linux-2.6.16/net/ipv4/udp.c linux-2.6.16-owrt/net/ipv4/udp.c +--- linux-2.6.16/net/ipv4/udp.c 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-owrt/net/ipv4/udp.c 2006-03-22 21:39:54.000000000 +0100 +@@ -109,11 +109,14 @@ + #include <net/inet_common.h> + #include <net/checksum.h> + #include <net/xfrm.h> ++#include <net/xfrmudp.h> + + /* + * Snmp MIB for the UDP layer + */ + ++static xfrm4_rcv_encap_t xfrm4_rcv_encap_func; ++ + DEFINE_SNMP_STAT(struct udp_mib, udp_statistics) __read_mostly; + + struct hlist_head udp_hash[UDP_HTABLE_SIZE]; +@@ -882,6 +885,42 @@ + sk_common_release(sk); + } + ++#if defined(CONFIG_XFRM) || defined(CONFIG_IPSEC_NAT_TRAVERSAL) ++ ++/* if XFRM isn't a module, then register it directly. */ ++#if 0 && !defined(CONFIG_XFRM_MODULE) && !defined(CONFIG_IPSEC_NAT_TRAVERSAL) ++static xfrm4_rcv_encap_t xfrm4_rcv_encap_func = xfrm4_rcv_encap; ++#else ++static xfrm4_rcv_encap_t xfrm4_rcv_encap_func = NULL; ++#endif ++ ++int udp4_register_esp_rcvencap(xfrm4_rcv_encap_t func ++ , xfrm4_rcv_encap_t *oldfunc) ++{ ++ if(oldfunc != NULL) { ++ *oldfunc = xfrm4_rcv_encap_func; ++ } ++ ++#if 0 ++ if(xfrm4_rcv_encap_func != NULL) ++ return -1; ++#endif ++ ++ xfrm4_rcv_encap_func = func; ++ return 0; ++} ++ ++int udp4_unregister_esp_rcvencap(xfrm4_rcv_encap_t func) ++{ ++ if(xfrm4_rcv_encap_func != func) ++ return -1; ++ ++ xfrm4_rcv_encap_func = NULL; ++ return 0; ++} ++#endif /* CONFIG_XFRM_MODULE || CONFIG_IPSEC_NAT_TRAVERSAL */ ++ ++ + /* return: + * 1 if the the UDP system should process it + * 0 if we should drop this packet +@@ -889,9 +928,9 @@ + */ + static int udp_encap_rcv(struct sock * sk, struct sk_buff *skb) + { +-#ifndef CONFIG_XFRM ++#if !defined(CONFIG_XFRM) && !defined(CONFIG_IPSEC_NAT_TRAVERSAL) + return 1; +-#else ++#else /* either CONFIG_XFRM or CONFIG_IPSEC_NAT_TRAVERSAL */ + struct udp_sock *up = udp_sk(sk); + struct udphdr *uh = skb->h.uh; + struct iphdr *iph; +@@ -903,11 +942,11 @@ + + /* if we're overly short, let UDP handle it */ + if (udpdata > skb->tail) +- return 1; ++ return 2; + + /* if this is not encapsulated socket, then just return now */ + if (!encap_type) +- return 1; ++ return 3; + + len = skb->tail - udpdata; + +@@ -922,7 +961,7 @@ + len = sizeof(struct udphdr); + } else + /* Must be an IKE packet.. pass it through */ +- return 1; ++ return 4; + break; + case UDP_ENCAP_ESPINUDP_NON_IKE: + /* Check if this is a keepalive packet. If so, eat it. */ +@@ -935,7 +974,7 @@ + len = sizeof(struct udphdr) + 2 * sizeof(u32); + } else + /* Must be an IKE packet.. pass it through */ +- return 1; ++ return 5; + break; + } + +@@ -946,6 +985,8 @@ + */ + if (skb_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) + return 0; ++ if (skb_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) ++ return 0; + + /* Now we can update and verify the packet length... */ + iph = skb->nh.iph; +@@ -1010,9 +1051,13 @@ + return 0; + } + if (ret < 0) { +- /* process the ESP packet */ +- ret = xfrm4_rcv_encap(skb, up->encap_type); +- UDP_INC_STATS_BH(UDP_MIB_INDATAGRAMS); ++ if(xfrm4_rcv_encap_func != NULL) { ++ ret = (*xfrm4_rcv_encap_func)(skb, up->encap_type); ++ UDP_INC_STATS_BH(UDP_MIB_INDATAGRAMS); ++ } else { ++ UDP_INC_STATS_BH(UDP_MIB_INERRORS); ++ ret = 1; ++ } + return -ret; + } + /* FALLTHROUGH -- it's a UDP Packet */ +@@ -1559,3 +1604,9 @@ + EXPORT_SYMBOL(udp_proc_register); + EXPORT_SYMBOL(udp_proc_unregister); + #endif ++ ++#if defined(CONFIG_IPSEC_NAT_TRAVERSAL) ++EXPORT_SYMBOL(udp4_register_esp_rcvencap); ++EXPORT_SYMBOL(udp4_unregister_esp_rcvencap); ++#endif ++ diff --git a/target/linux/generic-2.6/patches/103-netfilter-ipset.patch b/target/linux/generic-2.6/patches/103-netfilter-ipset.patch new file mode 100644 index 0000000..fb75c8b --- /dev/null +++ b/target/linux/generic-2.6/patches/103-netfilter-ipset.patch @@ -0,0 +1,5739 @@ +diff -Nur linux-2.6.16/include/linux/netfilter_ipv4/ip_set.h linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set.h +--- linux-2.6.16/include/linux/netfilter_ipv4/ip_set.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set.h 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,489 @@ ++#ifndef _IP_SET_H ++#define _IP_SET_H ++ ++/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> ++ * Patrick Schaaf <bof@bof.de> ++ * Martin Josefsson <gandalf@wlug.westbo.se> ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * A sockopt of such quality has hardly ever been seen before on the open ++ * market! This little beauty, hardly ever used: above 64, so it's ++ * traditionally used for firewalling, not touched (even once!) by the ++ * 2.0, 2.2 and 2.4 kernels! ++ * ++ * Comes with its own certificate of authenticity, valid anywhere in the ++ * Free world! ++ * ++ * Rusty, 19.4.2000 ++ */ ++#define SO_IP_SET 83 ++ ++/* ++ * Heavily modify by Joakim Axelsson 08.03.2002 ++ * - Made it more modulebased ++ * ++ * Additional heavy modifications by Jozsef Kadlecsik 22.02.2004 ++ * - bindings added ++ * - in order to "deal with" backward compatibility, renamed to ipset ++ */ ++ ++/* ++ * Used so that the kernel module and ipset-binary can match their versions ++ */ ++#define IP_SET_PROTOCOL_VERSION 2 ++ ++#define IP_SET_MAXNAMELEN 32 /* set names and set typenames */ ++ ++/* Lets work with our own typedef for representing an IP address. ++ * We hope to make the code more portable, possibly to IPv6... ++ * ++ * The representation works in HOST byte order, because most set types ++ * will perform arithmetic operations and compare operations. ++ * ++ * For now the type is an uint32_t. ++ * ++ * Make sure to ONLY use the functions when translating and parsing ++ * in order to keep the host byte order and make it more portable: ++ * parse_ip() ++ * parse_mask() ++ * parse_ipandmask() ++ * ip_tostring() ++ * (Joakim: where are they???) ++ */ ++ ++typedef uint32_t ip_set_ip_t; ++ ++/* Sets are identified by an id in kernel space. Tweak with ip_set_id_t ++ * and IP_SET_INVALID_ID if you want to increase the max number of sets. ++ */ ++typedef uint16_t ip_set_id_t; ++ ++#define IP_SET_INVALID_ID 65535 ++ ++/* How deep we follow bindings */ ++#define IP_SET_MAX_BINDINGS 6 ++ ++/* ++ * Option flags for kernel operations (ipt_set_info) ++ */ ++#define IPSET_SRC 0x01 /* Source match/add */ ++#define IPSET_DST 0x02 /* Destination match/add */ ++#define IPSET_MATCH_INV 0x04 /* Inverse matching */ ++ ++/* ++ * Set types (flavours) ++ */ ++#define IPSET_TYPE_IP 0 /* IP address type of set */ ++#define IPSET_TYPE_PORT 1 /* Port type of set */ ++ ++/* Reserved keywords */ ++#define IPSET_TOKEN_DEFAULT ":default:" ++#define IPSET_TOKEN_ALL ":all:" ++ ++/* SO_IP_SET operation constants, and their request struct types. ++ * ++ * Operation ids: ++ * 0-99: commands with version checking ++ * 100-199: add/del/test/bind/unbind ++ * 200-299: list, save, restore ++ */ ++ ++/* Single shot operations: ++ * version, create, destroy, flush, rename and swap ++ * ++ * Sets are identified by name. ++ */ ++ ++#define IP_SET_REQ_STD \ ++ unsigned op; \ ++ unsigned version; \ ++ char name[IP_SET_MAXNAMELEN] ++ ++#define IP_SET_OP_CREATE 0x00000001 /* Create a new (empty) set */ ++struct ip_set_req_create { ++ IP_SET_REQ_STD; ++ char typename[IP_SET_MAXNAMELEN]; ++}; ++ ++#define IP_SET_OP_DESTROY 0x00000002 /* Remove a (empty) set */ ++struct ip_set_req_std { ++ IP_SET_REQ_STD; ++}; ++ ++#define IP_SET_OP_FLUSH 0x00000003 /* Remove all IPs in a set */ ++/* Uses ip_set_req_std */ ++ ++#define IP_SET_OP_RENAME 0x00000004 /* Rename a set */ ++/* Uses ip_set_req_create */ ++ ++#define IP_SET_OP_SWAP 0x00000005 /* Swap two sets */ ++/* Uses ip_set_req_create */ ++ ++union ip_set_name_index { ++ char name[IP_SET_MAXNAMELEN]; ++ ip_set_id_t index; ++}; ++ ++#define IP_SET_OP_GET_BYNAME 0x00000006 /* Get set index by name */ ++struct ip_set_req_get_set { ++ unsigned op; ++ unsigned version; ++ union ip_set_name_index set; ++}; ++ ++#define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */ ++/* Uses ip_set_req_get_set */ ++ ++#define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */ ++struct ip_set_req_version { ++ unsigned op; ++ unsigned version; ++}; ++ ++/* Double shots operations: ++ * add, del, test, bind and unbind. ++ * ++ * First we query the kernel to get the index and type of the target set, ++ * then issue the command. Validity of IP is checked in kernel in order ++ * to minimalize sockopt operations. ++ */ ++ ++/* Get minimal set data for add/del/test/bind/unbind IP */ ++#define IP_SET_OP_ADT_GET 0x00000010 /* Get set and type */ ++struct ip_set_req_adt_get { ++ unsigned op; ++ unsigned version; ++ union ip_set_name_index set; ++ char typename[IP_SET_MAXNAMELEN]; ++}; ++ ++#define IP_SET_REQ_BYINDEX \ ++ unsigned op; \ ++ ip_set_id_t index; ++ ++struct ip_set_req_adt { ++ IP_SET_REQ_BYINDEX; ++}; ++ ++#define IP_SET_OP_ADD_IP 0x00000101 /* Add an IP to a set */ ++/* Uses ip_set_req_adt, with type specific addage */ ++ ++#define IP_SET_OP_DEL_IP 0x00000102 /* Remove an IP from a set */ ++/* Uses ip_set_req_adt, with type specific addage */ ++ ++#define IP_SET_OP_TEST_IP 0x00000103 /* Test an IP in a set */ ++/* Uses ip_set_req_adt, with type specific addage */ ++ ++#define IP_SET_OP_BIND_SET 0x00000104 /* Bind an IP to a set */ ++/* Uses ip_set_req_bind, with type specific addage */ ++struct ip_set_req_bind { ++ IP_SET_REQ_BYINDEX; ++ char binding[IP_SET_MAXNAMELEN]; ++}; ++ ++#define IP_SET_OP_UNBIND_SET 0x00000105 /* Unbind an IP from a set */ ++/* Uses ip_set_req_bind, with type speficic addage ++ * index = 0 means unbinding for all sets */ ++ ++#define IP_SET_OP_TEST_BIND_SET 0x00000106 /* Test binding an IP to a set */ ++/* Uses ip_set_req_bind, with type specific addage */ ++ ++/* Multiple shots operations: list, save, restore. ++ * ++ * - check kernel version and query the max number of sets ++ * - get the basic information on all sets ++ * and size required for the next step ++ * - get actual set data: header, data, bindings ++ */ ++ ++/* Get max_sets and the index of a queried set ++ */ ++#define IP_SET_OP_MAX_SETS 0x00000020 ++struct ip_set_req_max_sets { ++ unsigned op; ++ unsigned version; ++ ip_set_id_t max_sets; /* max_sets */ ++ ip_set_id_t sets; /* real number of sets */ ++ union ip_set_name_index set; /* index of set if name used */ ++}; ++ ++/* Get the id and name of the sets plus size for next step */ ++#define IP_SET_OP_LIST_SIZE 0x00000201 ++#define IP_SET_OP_SAVE_SIZE 0x00000202 ++struct ip_set_req_setnames { ++ unsigned op; ++ ip_set_id_t index; /* set to list/save */ ++ size_t size; /* size to get setdata/bindings */ ++ /* followed by sets number of struct ip_set_name_list */ ++}; ++ ++struct ip_set_name_list { ++ char name[IP_SET_MAXNAMELEN]; ++ char typename[IP_SET_MAXNAMELEN]; ++ ip_set_id_t index; ++ ip_set_id_t id; ++}; ++ ++/* The actual list operation */ ++#define IP_SET_OP_LIST 0x00000203 ++struct ip_set_req_list { ++ IP_SET_REQ_BYINDEX; ++ /* sets number of struct ip_set_list in reply */ ++}; ++ ++struct ip_set_list { ++ ip_set_id_t index; ++ ip_set_id_t binding; ++ u_int32_t ref; ++ size_t header_size; /* Set header data of header_size */ ++ size_t members_size; /* Set members data of members_size */ ++ size_t bindings_size; /* Set bindings data of bindings_size */ ++}; ++ ++struct ip_set_hash_list { ++ ip_set_ip_t ip; ++ ip_set_id_t binding; ++}; ++ ++/* The save operation */ ++#define IP_SET_OP_SAVE 0x00000204 ++/* Uses ip_set_req_list, in the reply replaced by ++ * sets number of struct ip_set_save plus a marker ++ * ip_set_save followed by ip_set_hash_save structures. ++ */ ++struct ip_set_save { ++ ip_set_id_t index; ++ ip_set_id_t binding; ++ size_t header_size; /* Set header data of header_size */ ++ size_t members_size; /* Set members data of members_size */ ++}; ++ ++/* At restoring, ip == 0 means default binding for the given set: */ ++struct ip_set_hash_save { ++ ip_set_ip_t ip; ++ ip_set_id_t id; ++ ip_set_id_t binding; ++}; ++ ++/* The restore operation */ ++#define IP_SET_OP_RESTORE 0x00000205 ++/* Uses ip_set_req_setnames followed by ip_set_restore structures ++ * plus a marker ip_set_restore, followed by ip_set_hash_save ++ * structures. ++ */ ++struct ip_set_restore { ++ char name[IP_SET_MAXNAMELEN]; ++ char typename[IP_SET_MAXNAMELEN]; ++ ip_set_id_t index; ++ size_t header_size; /* Create data of header_size */ ++ size_t members_size; /* Set members data of members_size */ ++}; ++ ++static inline int bitmap_bytes(ip_set_ip_t a, ip_set_ip_t b) ++{ ++ return 4 * ((((b - a + 8) / 8) + 3) / 4); ++} ++ ++#ifdef __KERNEL__ ++ ++#define ip_set_printk(format, args...) \ ++ do { \ ++ printk("%s: %s: ", __FILE__, __FUNCTION__); \ ++ printk(format "\n" , ## args); \ ++ } while (0) ++ ++#if defined(IP_SET_DEBUG) ++#define DP(format, args...) \ ++ do { \ ++ printk("%s: %s (DBG): ", __FILE__, __FUNCTION__);\ ++ printk(format "\n" , ## args); \ ++ } while (0) ++#define IP_SET_ASSERT(x) \ ++ do { \ ++ if (!(x)) \ ++ printk("IP_SET_ASSERT: %s:%i(%s)\n", \ ++ __FILE__, __LINE__, __FUNCTION__); \ ++ } while (0) ++#else ++#define DP(format, args...) ++#define IP_SET_ASSERT(x) ++#endif ++ ++struct ip_set; ++ ++/* ++ * The ip_set_type definition - one per set type, e.g. "ipmap". ++ * ++ * Each individual set has a pointer, set->type, going to one ++ * of these structures. Function pointers inside the structure implement ++ * the real behaviour of the sets. ++ * ++ * If not mentioned differently, the implementation behind the function ++ * pointers of a set_type, is expected to return 0 if ok, and a negative ++ * errno (e.g. -EINVAL) on error. ++ */ ++struct ip_set_type { ++ struct list_head list; /* next in list of set types */ ++ ++ /* test for IP in set (kernel: iptables -m set src|dst) ++ * return 0 if not in set, 1 if in set. ++ */ ++ int (*testip_kernel) (struct ip_set *set, ++ const struct sk_buff * skb, ++ u_int32_t flags, ++ ip_set_ip_t *ip); ++ ++ /* test for IP in set (userspace: ipset -T set IP) ++ * return 0 if not in set, 1 if in set. ++ */ ++ int (*testip) (struct ip_set *set, ++ const void *data, size_t size, ++ ip_set_ip_t *ip); ++ ++ /* ++ * Size of the data structure passed by when ++ * adding/deletin/testing an entry. ++ */ ++ size_t reqsize; ++ ++ /* Add IP into set (userspace: ipset -A set IP) ++ * Return -EEXIST if the address is already in the set, ++ * and -ERANGE if the address lies outside the set bounds. ++ * If the address was not already in the set, 0 is returned. ++ */ ++ int (*addip) (struct ip_set *set, ++ const void *data, size_t size, ++ ip_set_ip_t *ip); ++ ++ /* Add IP into set (kernel: iptables ... -j SET set src|dst) ++ * Return -EEXIST if the address is already in the set, ++ * and -ERANGE if the address lies outside the set bounds. ++ * If the address was not already in the set, 0 is returned. ++ */ ++ int (*addip_kernel) (struct ip_set *set, ++ const struct sk_buff * skb, ++ u_int32_t flags, ++ ip_set_ip_t *ip); ++ ++ /* remove IP from set (userspace: ipset -D set --entry x) ++ * Return -EEXIST if the address is NOT in the set, ++ * and -ERANGE if the address lies outside the set bounds. ++ * If the address really was in the set, 0 is returned. ++ */ ++ int (*delip) (struct ip_set *set, ++ const void *data, size_t size, ++ ip_set_ip_t *ip); ++ ++ /* remove IP from set (kernel: iptables ... -j SET --entry x) ++ * Return -EEXIST if the address is NOT in the set, ++ * and -ERANGE if the address lies outside the set bounds. ++ * If the address really was in the set, 0 is returned. ++ */ ++ int (*delip_kernel) (struct ip_set *set, ++ const struct sk_buff * skb, ++ u_int32_t flags, ++ ip_set_ip_t *ip); ++ ++ /* new set creation - allocated type specific items ++ */ ++ int (*create) (struct ip_set *set, ++ const void *data, size_t size); ++ ++ /* retry the operation after successfully tweaking the set ++ */ ++ int (*retry) (struct ip_set *set); ++ ++ /* set destruction - free type specific items ++ * There is no return value. ++ * Can be called only when child sets are destroyed. ++ */ ++ void (*destroy) (struct ip_set *set); ++ ++ /* set flushing - reset all bits in the set, or something similar. ++ * There is no return value. ++ */ ++ void (*flush) (struct ip_set *set); ++ ++ /* Listing: size needed for header ++ */ ++ size_t header_size; ++ ++ /* Listing: Get the header ++ * ++ * Fill in the information in "data". ++ * This function is always run after list_header_size() under a ++ * writelock on the set. Therefor is the length of "data" always ++ * correct. ++ */ ++ void (*list_header) (const struct ip_set *set, ++ void *data); ++ ++ /* Listing: Get the size for the set members ++ */ ++ int (*list_members_size) (const struct ip_set *set); ++ ++ /* Listing: Get the set members ++ * ++ * Fill in the information in "data". ++ * This function is always run after list_member_size() under a ++ * writelock on the set. Therefor is the length of "data" always ++ * correct. ++ */ ++ void (*list_members) (const struct ip_set *set, ++ void *data); ++ ++ char typename[IP_SET_MAXNAMELEN]; ++ char typecode; ++ int protocol_version; ++ ++ /* Set this to THIS_MODULE if you are a module, otherwise NULL */ ++ struct module *me; ++}; ++ ++extern int ip_set_register_set_type(struct ip_set_type *set_type); ++extern void ip_set_unregister_set_type(struct ip_set_type *set_type); ++ ++/* A generic ipset */ ++struct ip_set { ++ char name[IP_SET_MAXNAMELEN]; /* the name of the set */ ++ rwlock_t lock; /* lock for concurrency control */ ++ ip_set_id_t id; /* set id for swapping */ ++ ip_set_id_t binding; /* default binding for the set */ ++ atomic_t ref; /* in kernel and in hash references */ ++ struct ip_set_type *type; /* the set types */ ++ void *data; /* pooltype specific data */ ++}; ++ ++/* Structure to bind set elements to sets */ ++struct ip_set_hash { ++ struct list_head list; /* list of clashing entries in hash */ ++ ip_set_ip_t ip; /* ip from set */ ++ ip_set_id_t id; /* set id */ ++ ip_set_id_t binding; /* set we bind the element to */ ++}; ++ ++/* register and unregister set references */ ++extern ip_set_id_t ip_set_get_byname(const char name[IP_SET_MAXNAMELEN]); ++extern ip_set_id_t ip_set_get_byindex(ip_set_id_t id); ++extern void ip_set_put(ip_set_id_t id); ++ ++/* API for iptables set match, and SET target */ ++extern void ip_set_addip_kernel(ip_set_id_t id, ++ const struct sk_buff *skb, ++ const u_int32_t *flags); ++extern void ip_set_delip_kernel(ip_set_id_t id, ++ const struct sk_buff *skb, ++ const u_int32_t *flags); ++extern int ip_set_testip_kernel(ip_set_id_t id, ++ const struct sk_buff *skb, ++ const u_int32_t *flags); ++ ++#endif /* __KERNEL__ */ ++ ++#endif /*_IP_SET_H*/ +diff -Nur linux-2.6.16/include/linux/netfilter_ipv4/ip_set_iphash.h linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set_iphash.h +--- linux-2.6.16/include/linux/netfilter_ipv4/ip_set_iphash.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set_iphash.h 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,30 @@ ++#ifndef __IP_SET_IPHASH_H ++#define __IP_SET_IPHASH_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#define SETTYPE_NAME "iphash" ++#define MAX_RANGE 0x0000FFFF ++ ++struct ip_set_iphash { ++ ip_set_ip_t *members; /* the iphash proper */ ++ uint32_t initval; /* initval for jhash_1word */ ++ uint32_t prime; /* prime for double hashing */ ++ uint32_t hashsize; /* hash size */ ++ uint16_t probes; /* max number of probes */ ++ uint16_t resize; /* resize factor in percent */ ++ ip_set_ip_t netmask; /* netmask */ ++}; ++ ++struct ip_set_req_iphash_create { ++ uint32_t hashsize; ++ uint16_t probes; ++ uint16_t resize; ++ ip_set_ip_t netmask; ++}; ++ ++struct ip_set_req_iphash { ++ ip_set_ip_t ip; ++}; ++ ++#endif /* __IP_SET_IPHASH_H */ +diff -Nur linux-2.6.16/include/linux/netfilter_ipv4/ip_set_ipmap.h linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set_ipmap.h +--- linux-2.6.16/include/linux/netfilter_ipv4/ip_set_ipmap.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set_ipmap.h 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,56 @@ ++#ifndef __IP_SET_IPMAP_H ++#define __IP_SET_IPMAP_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#define SETTYPE_NAME "ipmap" ++#define MAX_RANGE 0x0000FFFF ++ ++struct ip_set_ipmap { ++ void *members; /* the ipmap proper */ ++ ip_set_ip_t first_ip; /* host byte order, included in range */ ++ ip_set_ip_t last_ip; /* host byte order, included in range */ ++ ip_set_ip_t netmask; /* subnet netmask */ ++ ip_set_ip_t sizeid; /* size of set in IPs */ ++ u_int16_t hosts; /* number of hosts in a subnet */ ++}; ++ ++struct ip_set_req_ipmap_create { ++ ip_set_ip_t from; ++ ip_set_ip_t to; ++ ip_set_ip_t netmask; ++}; ++ ++struct ip_set_req_ipmap { ++ ip_set_ip_t ip; ++}; ++ ++unsigned int ++mask_to_bits(ip_set_ip_t mask) ++{ ++ unsigned int bits = 32; ++ ip_set_ip_t maskaddr; ++ ++ if (mask == 0xFFFFFFFF) ++ return bits; ++ ++ maskaddr = 0xFFFFFFFE; ++ while (--bits >= 0 && maskaddr != mask) ++ maskaddr <<= 1; ++ ++ return bits; ++} ++ ++ip_set_ip_t ++range_to_mask(ip_set_ip_t from, ip_set_ip_t to, unsigned int *bits) ++{ ++ ip_set_ip_t mask = 0xFFFFFFFE; ++ ++ *bits = 32; ++ while (--(*bits) >= 0 && mask && (to & mask) != from) ++ mask <<= 1; ++ ++ return mask; ++} ++ ++#endif /* __IP_SET_IPMAP_H */ +diff -Nur linux-2.6.16/include/linux/netfilter_ipv4/ip_set_iptree.h linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set_iptree.h +--- linux-2.6.16/include/linux/netfilter_ipv4/ip_set_iptree.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set_iptree.h 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,39 @@ ++#ifndef __IP_SET_IPTREE_H ++#define __IP_SET_IPTREE_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#define SETTYPE_NAME "iptree" ++#define MAX_RANGE 0x0000FFFF ++ ++struct ip_set_iptreed { ++ unsigned long expires[255]; /* x.x.x.ADDR */ ++}; ++ ++struct ip_set_iptreec { ++ struct ip_set_iptreed *tree[255]; /* x.x.ADDR.* */ ++}; ++ ++struct ip_set_iptreeb { ++ struct ip_set_iptreec *tree[255]; /* x.ADDR.*.* */ ++}; ++ ++struct ip_set_iptree { ++ unsigned int timeout; ++ unsigned int gc_interval; ++#ifdef __KERNEL__ ++ struct timer_list gc; ++ struct ip_set_iptreeb *tree[255]; /* ADDR.*.*.* */ ++#endif ++}; ++ ++struct ip_set_req_iptree_create { ++ unsigned int timeout; ++}; ++ ++struct ip_set_req_iptree { ++ ip_set_ip_t ip; ++ unsigned int timeout; ++}; ++ ++#endif /* __IP_SET_IPTREE_H */ +diff -Nur linux-2.6.16/include/linux/netfilter_ipv4/ip_set_jhash.h linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set_jhash.h +--- linux-2.6.16/include/linux/netfilter_ipv4/ip_set_jhash.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set_jhash.h 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,148 @@ ++#ifndef _LINUX_IPSET_JHASH_H ++#define _LINUX_IPSET_JHASH_H ++ ++/* This is a copy of linux/jhash.h but the types u32/u8 are changed ++ * to __u32/__u8 so that the header file can be included into ++ * userspace code as well. Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) ++ */ ++ ++/* jhash.h: Jenkins hash support. ++ * ++ * Copyright (C) 1996 Bob Jenkins (bob_jenkins@burtleburtle.net) ++ * ++ * http://burtleburtle.net/bob/hash/ ++ * ++ * These are the credits from Bob's sources: ++ * ++ * lookup2.c, by Bob Jenkins, December 1996, Public Domain. ++ * hash(), hash2(), hash3, and mix() are externally useful functions. ++ * Routines to test the hash are included if SELF_TEST is defined. ++ * You can use this free for any purpose. It has no warranty. ++ * ++ * Copyright (C) 2003 David S. Miller (davem@redhat.com) ++ * ++ * I've modified Bob's hash to be useful in the Linux kernel, and ++ * any bugs present are surely my fault. -DaveM ++ */ ++ ++/* NOTE: Arguments are modified. */ ++#define __jhash_mix(a, b, c) \ ++{ \ ++ a -= b; a -= c; a ^= (c>>13); \ ++ b -= c; b -= a; b ^= (a<<8); \ ++ c -= a; c -= b; c ^= (b>>13); \ ++ a -= b; a -= c; a ^= (c>>12); \ ++ b -= c; b -= a; b ^= (a<<16); \ ++ c -= a; c -= b; c ^= (b>>5); \ ++ a -= b; a -= c; a ^= (c>>3); \ ++ b -= c; b -= a; b ^= (a<<10); \ ++ c -= a; c -= b; c ^= (b>>15); \ ++} ++ ++/* The golden ration: an arbitrary value */ ++#define JHASH_GOLDEN_RATIO 0x9e3779b9 ++ ++/* The most generic version, hashes an arbitrary sequence ++ * of bytes. No alignment or length assumptions are made about ++ * the input key. ++ */ ++static inline __u32 jhash(void *key, __u32 length, __u32 initval) ++{ ++ __u32 a, b, c, len; ++ __u8 *k = key; ++ ++ len = length; ++ a = b = JHASH_GOLDEN_RATIO; ++ c = initval; ++ ++ while (len >= 12) { ++ a += (k[0] +((__u32)k[1]<<8) +((__u32)k[2]<<16) +((__u32)k[3]<<24)); ++ b += (k[4] +((__u32)k[5]<<8) +((__u32)k[6]<<16) +((__u32)k[7]<<24)); ++ c += (k[8] +((__u32)k[9]<<8) +((__u32)k[10]<<16)+((__u32)k[11]<<24)); ++ ++ __jhash_mix(a,b,c); ++ ++ k += 12; ++ len -= 12; ++ } ++ ++ c += length; ++ switch (len) { ++ case 11: c += ((__u32)k[10]<<24); ++ case 10: c += ((__u32)k[9]<<16); ++ case 9 : c += ((__u32)k[8]<<8); ++ case 8 : b += ((__u32)k[7]<<24); ++ case 7 : b += ((__u32)k[6]<<16); ++ case 6 : b += ((__u32)k[5]<<8); ++ case 5 : b += k[4]; ++ case 4 : a += ((__u32)k[3]<<24); ++ case 3 : a += ((__u32)k[2]<<16); ++ case 2 : a += ((__u32)k[1]<<8); ++ case 1 : a += k[0]; ++ }; ++ ++ __jhash_mix(a,b,c); ++ ++ return c; ++} ++ ++/* A special optimized version that handles 1 or more of __u32s. ++ * The length parameter here is the number of __u32s in the key. ++ */ ++static inline __u32 jhash2(__u32 *k, __u32 length, __u32 initval) ++{ ++ __u32 a, b, c, len; ++ ++ a = b = JHASH_GOLDEN_RATIO; ++ c = initval; ++ len = length; ++ ++ while (len >= 3) { ++ a += k[0]; ++ b += k[1]; ++ c += k[2]; ++ __jhash_mix(a, b, c); ++ k += 3; len -= 3; ++ } ++ ++ c += length * 4; ++ ++ switch (len) { ++ case 2 : b += k[1]; ++ case 1 : a += k[0]; ++ }; ++ ++ __jhash_mix(a,b,c); ++ ++ return c; ++} ++ ++ ++/* A special ultra-optimized versions that knows they are hashing exactly ++ * 3, 2 or 1 word(s). ++ * ++ * NOTE: In partilar the "c += length; __jhash_mix(a,b,c);" normally ++ * done at the end is not done here. ++ */ ++static inline __u32 jhash_3words(__u32 a, __u32 b, __u32 c, __u32 initval) ++{ ++ a += JHASH_GOLDEN_RATIO; ++ b += JHASH_GOLDEN_RATIO; ++ c += initval; ++ ++ __jhash_mix(a, b, c); ++ ++ return c; ++} ++ ++static inline __u32 jhash_2words(__u32 a, __u32 b, __u32 initval) ++{ ++ return jhash_3words(a, b, 0, initval); ++} ++ ++static inline __u32 jhash_1word(__u32 a, __u32 initval) ++{ ++ return jhash_3words(a, 0, 0, initval); ++} ++ ++#endif /* _LINUX_IPSET_JHASH_H */ +diff -Nur linux-2.6.16/include/linux/netfilter_ipv4/ip_set_macipmap.h linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set_macipmap.h +--- linux-2.6.16/include/linux/netfilter_ipv4/ip_set_macipmap.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set_macipmap.h 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,38 @@ ++#ifndef __IP_SET_MACIPMAP_H ++#define __IP_SET_MACIPMAP_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#define SETTYPE_NAME "macipmap" ++#define MAX_RANGE 0x0000FFFF ++ ++/* general flags */ ++#define IPSET_MACIP_MATCHUNSET 1 ++ ++/* per ip flags */ ++#define IPSET_MACIP_ISSET 1 ++ ++struct ip_set_macipmap { ++ void *members; /* the macipmap proper */ ++ ip_set_ip_t first_ip; /* host byte order, included in range */ ++ ip_set_ip_t last_ip; /* host byte order, included in range */ ++ u_int32_t flags; ++}; ++ ++struct ip_set_req_macipmap_create { ++ ip_set_ip_t from; ++ ip_set_ip_t to; ++ u_int32_t flags; ++}; ++ ++struct ip_set_req_macipmap { ++ ip_set_ip_t ip; ++ unsigned char ethernet[ETH_ALEN]; ++}; ++ ++struct ip_set_macip { ++ unsigned short flags; ++ unsigned char ethernet[ETH_ALEN]; ++}; ++ ++#endif /* __IP_SET_MACIPMAP_H */ +diff -Nur linux-2.6.16/include/linux/netfilter_ipv4/ip_set_malloc.h linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set_malloc.h +--- linux-2.6.16/include/linux/netfilter_ipv4/ip_set_malloc.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set_malloc.h 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,42 @@ ++#ifndef _IP_SET_MALLOC_H ++#define _IP_SET_MALLOC_H ++ ++#ifdef __KERNEL__ ++ ++/* Memory allocation and deallocation */ ++static size_t max_malloc_size = 0; ++ ++static inline void init_max_malloc_size(void) ++{ ++#define CACHE(x) max_malloc_size = x; ++#include <linux/kmalloc_sizes.h> ++#undef CACHE ++} ++ ++static inline void * ip_set_malloc_atomic(size_t bytes) ++{ ++ if (bytes > max_malloc_size) ++ return __vmalloc(bytes, GFP_ATOMIC, PAGE_KERNEL); ++ else ++ return kmalloc(bytes, GFP_ATOMIC); ++} ++ ++static inline void * ip_set_malloc(size_t bytes) ++{ ++ if (bytes > max_malloc_size) ++ return vmalloc(bytes); ++ else ++ return kmalloc(bytes, GFP_KERNEL); ++} ++ ++static inline void ip_set_free(void * data, size_t bytes) ++{ ++ if (bytes > max_malloc_size) ++ vfree(data); ++ else ++ kfree(data); ++} ++ ++#endif /* __KERNEL__ */ ++ ++#endif /*_IP_SET_MALLOC_H*/ +diff -Nur linux-2.6.16/include/linux/netfilter_ipv4/ip_set_nethash.h linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set_nethash.h +--- linux-2.6.16/include/linux/netfilter_ipv4/ip_set_nethash.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set_nethash.h 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,55 @@ ++#ifndef __IP_SET_NETHASH_H ++#define __IP_SET_NETHASH_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#define SETTYPE_NAME "nethash" ++#define MAX_RANGE 0x0000FFFF ++ ++struct ip_set_nethash { ++ ip_set_ip_t *members; /* the nethash proper */ ++ uint32_t initval; /* initval for jhash_1word */ ++ uint32_t prime; /* prime for double hashing */ ++ uint32_t hashsize; /* hash size */ ++ uint16_t probes; /* max number of probes */ ++ uint16_t resize; /* resize factor in percent */ ++ unsigned char cidr[30]; /* CIDR sizes */ ++}; ++ ++struct ip_set_req_nethash_create { ++ uint32_t hashsize; ++ uint16_t probes; ++ uint16_t resize; ++}; ++ ++struct ip_set_req_nethash { ++ ip_set_ip_t ip; ++ unsigned char cidr; ++}; ++ ++static unsigned char shifts[] = {255, 253, 249, 241, 225, 193, 129, 1}; ++ ++static inline ip_set_ip_t ++pack(ip_set_ip_t ip, unsigned char cidr) ++{ ++ ip_set_ip_t addr, *paddr = &addr; ++ unsigned char n, t, *a; ++ ++ addr = htonl(ip & (0xFFFFFFFF << (32 - (cidr)))); ++#ifdef __KERNEL__ ++ DP("ip:%u.%u.%u.%u/%u", NIPQUAD(addr), cidr); ++#endif ++ n = cidr / 8; ++ t = cidr % 8; ++ a = &((unsigned char *)paddr)[n]; ++ *a = *a /(1 << (8 - t)) + shifts[t]; ++#ifdef __KERNEL__ ++ DP("n: %u, t: %u, a: %u", n, t, *a); ++ DP("ip:%u.%u.%u.%u/%u, %u.%u.%u.%u", ++ HIPQUAD(ip), cidr, NIPQUAD(addr)); ++#endif ++ ++ return ntohl(addr); ++} ++ ++#endif /* __IP_SET_NETHASH_H */ +diff -Nur linux-2.6.16/include/linux/netfilter_ipv4/ip_set_portmap.h linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set_portmap.h +--- linux-2.6.16/include/linux/netfilter_ipv4/ip_set_portmap.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set_portmap.h 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,25 @@ ++#ifndef __IP_SET_PORTMAP_H ++#define __IP_SET_PORTMAP_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++#define SETTYPE_NAME "portmap" ++#define MAX_RANGE 0x0000FFFF ++#define INVALID_PORT (MAX_RANGE + 1) ++ ++struct ip_set_portmap { ++ void *members; /* the portmap proper */ ++ ip_set_ip_t first_port; /* host byte order, included in range */ ++ ip_set_ip_t last_port; /* host byte order, included in range */ ++}; ++ ++struct ip_set_req_portmap_create { ++ ip_set_ip_t from; ++ ip_set_ip_t to; ++}; ++ ++struct ip_set_req_portmap { ++ ip_set_ip_t port; ++}; ++ ++#endif /* __IP_SET_PORTMAP_H */ +diff -Nur linux-2.6.16/include/linux/netfilter_ipv4/ip_set_prime.h linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set_prime.h +--- linux-2.6.16/include/linux/netfilter_ipv4/ip_set_prime.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/include/linux/netfilter_ipv4/ip_set_prime.h 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,34 @@ ++#ifndef __IP_SET_PRIME_H ++#define __IP_SET_PRIME_H ++ ++static inline unsigned make_prime_bound(unsigned nr) ++{ ++ unsigned long long nr64 = nr; ++ unsigned long long x = 1; ++ nr = 1; ++ while (x <= nr64) { x <<= 2; nr <<= 1; } ++ return nr; ++} ++ ++static inline int make_prime_check(unsigned nr) ++{ ++ unsigned x = 3; ++ unsigned b = make_prime_bound(nr); ++ while (x <= b) { ++ if (0 == (nr % x)) return 0; ++ x += 2; ++ } ++ return 1; ++} ++ ++static unsigned make_prime(unsigned nr) ++{ ++ if (0 == (nr & 1)) nr--; ++ while (nr > 1) { ++ if (make_prime_check(nr)) return nr; ++ nr -= 2; ++ } ++ return 2; ++} ++ ++#endif /* __IP_SET_PRIME_H */ +diff -Nur linux-2.6.16/include/linux/netfilter_ipv4/ipt_set.h linux-2.6.16-owrt/include/linux/netfilter_ipv4/ipt_set.h +--- linux-2.6.16/include/linux/netfilter_ipv4/ipt_set.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/include/linux/netfilter_ipv4/ipt_set.h 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,21 @@ ++#ifndef _IPT_SET_H ++#define _IPT_SET_H ++ ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++struct ipt_set_info { ++ ip_set_id_t index; ++ u_int32_t flags[IP_SET_MAX_BINDINGS + 1]; ++}; ++ ++/* match info */ ++struct ipt_set_info_match { ++ struct ipt_set_info match_set; ++}; ++ ++struct ipt_set_info_target { ++ struct ipt_set_info add_set; ++ struct ipt_set_info del_set; ++}; ++ ++#endif /*_IPT_SET_H*/ +diff -Nur linux-2.6.16/net/ipv4/netfilter/ip_set.c linux-2.6.16-owrt/net/ipv4/netfilter/ip_set.c +--- linux-2.6.16/net/ipv4/netfilter/ip_set.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/net/ipv4/netfilter/ip_set.c 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,1989 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> ++ * Patrick Schaaf <bof@bof.de> ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module for IP set management */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/kmod.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/random.h> ++#include <linux/jhash.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <asm/semaphore.h> ++#include <linux/spinlock.h> ++#include <linux/vmalloc.h> ++ ++#define ASSERT_READ_LOCK(x) /* dont use that */ ++#define ASSERT_WRITE_LOCK(x) ++#include <linux/netfilter_ipv4/listhelp.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++ ++static struct list_head set_type_list; /* all registered sets */ ++static struct ip_set **ip_set_list; /* all individual sets */ ++static DEFINE_RWLOCK(ip_set_lock); /* protects the lists and the hash */ ++static DECLARE_MUTEX(ip_set_app_mutex); /* serializes user access */ ++static ip_set_id_t ip_set_max = CONFIG_IP_NF_SET_MAX; ++static ip_set_id_t ip_set_bindings_hash_size = CONFIG_IP_NF_SET_HASHSIZE; ++static struct list_head *ip_set_hash; /* hash of bindings */ ++static unsigned int ip_set_hash_random; /* random seed */ ++ ++/* ++ * Sets are identified either by the index in ip_set_list or by id. ++ * The id never changes and is used to find a key in the hash. ++ * The index may change by swapping and used at all other places ++ * (set/SET netfilter modules, binding value, etc.) ++ * ++ * Userspace requests are serialized by ip_set_mutex and sets can ++ * be deleted only from userspace. Therefore ip_set_list locking ++ * must obey the following rules: ++ * ++ * - kernel requests: read and write locking mandatory ++ * - user requests: read locking optional, write locking mandatory ++ */ ++ ++static inline void ++__ip_set_get(ip_set_id_t index) ++{ ++ atomic_inc(&ip_set_list[index]->ref); ++} ++ ++static inline void ++__ip_set_put(ip_set_id_t index) ++{ ++ atomic_dec(&ip_set_list[index]->ref); ++} ++ ++/* ++ * Binding routines ++ */ ++ ++static inline int ++ip_hash_cmp(const struct ip_set_hash *set_hash, ++ ip_set_id_t id, ip_set_ip_t ip) ++{ ++ return set_hash->id == id && set_hash->ip == ip; ++} ++ ++static ip_set_id_t ++ip_set_find_in_hash(ip_set_id_t id, ip_set_ip_t ip) ++{ ++ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) ++ % ip_set_bindings_hash_size; ++ struct ip_set_hash *set_hash; ++ ++ ASSERT_READ_LOCK(&ip_set_lock); ++ IP_SET_ASSERT(ip_set_list[id]); ++ DP("set: %s, ip: %u.%u.%u.%u", ip_set_list[id]->name, HIPQUAD(ip)); ++ ++ set_hash = LIST_FIND(&ip_set_hash[key], ip_hash_cmp, ++ struct ip_set_hash *, id, ip); ++ ++ DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, ++ HIPQUAD(ip), ++ set_hash != NULL ? ip_set_list[set_hash->binding]->name : ""); ++ ++ return (set_hash != NULL ? set_hash->binding : IP_SET_INVALID_ID); ++} ++ ++static inline void ++__set_hash_del(struct ip_set_hash *set_hash) ++{ ++ ASSERT_WRITE_LOCK(&ip_set_lock); ++ IP_SET_ASSERT(ip_set_list[set_hash->binding]); ++ ++ __ip_set_put(set_hash->binding); ++ list_del(&set_hash->list); ++ kfree(set_hash); ++} ++ ++static int ++ip_set_hash_del(ip_set_id_t id, ip_set_ip_t ip) ++{ ++ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) ++ % ip_set_bindings_hash_size; ++ struct ip_set_hash *set_hash; ++ ++ IP_SET_ASSERT(ip_set_list[id]); ++ DP("set: %s, ip: %u.%u.%u.%u", ip_set_list[id]->name, HIPQUAD(ip)); ++ write_lock_bh(&ip_set_lock); ++ set_hash = LIST_FIND(&ip_set_hash[key], ip_hash_cmp, ++ struct ip_set_hash *, id, ip); ++ DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, ++ HIPQUAD(ip), ++ set_hash != NULL ? ip_set_list[set_hash->binding]->name : ""); ++ ++ if (set_hash != NULL) ++ __set_hash_del(set_hash); ++ write_unlock_bh(&ip_set_lock); ++ return 0; ++} ++ ++static int ++ip_set_hash_add(ip_set_id_t id, ip_set_ip_t ip, ip_set_id_t binding) ++{ ++ u_int32_t key = jhash_2words(id, ip, ip_set_hash_random) ++ % ip_set_bindings_hash_size; ++ struct ip_set_hash *set_hash; ++ int ret = 0; ++ ++ IP_SET_ASSERT(ip_set_list[id]); ++ IP_SET_ASSERT(ip_set_list[binding]); ++ DP("set: %s, ip: %u.%u.%u.%u, binding: %s", ip_set_list[id]->name, ++ HIPQUAD(ip), ip_set_list[binding]->name); ++ write_lock_bh(&ip_set_lock); ++ set_hash = LIST_FIND(&ip_set_hash[key], ip_hash_cmp, ++ struct ip_set_hash *, id, ip); ++ if (!set_hash) { ++ set_hash = kmalloc(sizeof(struct ip_set_hash), GFP_KERNEL); ++ if (!set_hash) { ++ ret = -ENOMEM; ++ goto unlock; ++ } ++ INIT_LIST_HEAD(&set_hash->list); ++ set_hash->id = id; ++ set_hash->ip = ip; ++ list_add(&ip_set_hash[key], &set_hash->list); ++ } else { ++ IP_SET_ASSERT(ip_set_list[set_hash->binding]); ++ DP("overwrite binding: %s", ++ ip_set_list[set_hash->binding]->name); ++ __ip_set_put(set_hash->binding); ++ } ++ set_hash->binding = binding; ++ __ip_set_get(set_hash->binding); ++ unlock: ++ write_unlock_bh(&ip_set_lock); ++ return ret; ++} ++ ++#define FOREACH_HASH_DO(fn, args...) \ ++({ \ ++ ip_set_id_t __key; \ ++ struct ip_set_hash *__set_hash; \ ++ \ ++ for (__key = 0; __key < ip_set_bindings_hash_size; __key++) { \ ++ list_for_each_entry(__set_hash, &ip_set_hash[__key], list) \ ++ fn(__set_hash , ## args); \ ++ } \ ++}) ++ ++#define FOREACH_HASH_RW_DO(fn, args...) \ ++({ \ ++ ip_set_id_t __key; \ ++ struct ip_set_hash *__set_hash, *__n; \ ++ \ ++ ASSERT_WRITE_LOCK(&ip_set_lock); \ ++ for (__key = 0; __key < ip_set_bindings_hash_size; __key++) { \ ++ list_for_each_entry_safe(__set_hash, __n, &ip_set_hash[__key], list)\ ++ fn(__set_hash , ## args); \ ++ } \ ++}) ++ ++/* Add, del and test set entries from kernel */ ++ ++#define follow_bindings(index, set, ip) \ ++((index = ip_set_find_in_hash((set)->id, ip)) != IP_SET_INVALID_ID \ ++ || (index = (set)->binding) != IP_SET_INVALID_ID) ++ ++int ++ip_set_testip_kernel(ip_set_id_t index, ++ const struct sk_buff *skb, ++ const u_int32_t *flags) ++{ ++ struct ip_set *set; ++ ip_set_ip_t ip; ++ int res, i = 0; ++ ++ IP_SET_ASSERT(flags[i]); ++ read_lock_bh(&ip_set_lock); ++ do { ++ set = ip_set_list[index]; ++ IP_SET_ASSERT(set); ++ DP("set %s, index %u", set->name, index); ++ read_lock_bh(&set->lock); ++ res = set->type->testip_kernel(set, skb, flags[i], &ip); ++ read_unlock_bh(&set->lock); ++ } while (res > 0 ++ && flags[++i] ++ && follow_bindings(index, set, ip)); ++ read_unlock_bh(&ip_set_lock); ++ ++ return res; ++} ++ ++void ++ip_set_addip_kernel(ip_set_id_t index, ++ const struct sk_buff *skb, ++ const u_int32_t *flags) ++{ ++ struct ip_set *set; ++ ip_set_ip_t ip; ++ int res, i= 0; ++ ++ IP_SET_ASSERT(flags[i]); ++ retry: ++ read_lock_bh(&ip_set_lock); ++ do { ++ set = ip_set_list[index]; ++ IP_SET_ASSERT(set); ++ DP("set %s, index %u", set->name, index); ++ write_lock_bh(&set->lock); ++ res = set->type->addip_kernel(set, skb, flags[i], &ip); ++ write_unlock_bh(&set->lock); ++ } while ((res == 0 || res == -EEXIST) ++ && flags[++i] ++ && follow_bindings(index, set, ip)); ++ read_unlock_bh(&ip_set_lock); ++ ++ if (res == -EAGAIN ++ && set->type->retry ++ && (res = set->type->retry(set)) == 0) ++ goto retry; ++} ++ ++void ++ip_set_delip_kernel(ip_set_id_t index, ++ const struct sk_buff *skb, ++ const u_int32_t *flags) ++{ ++ struct ip_set *set; ++ ip_set_ip_t ip; ++ int res, i = 0; ++ ++ IP_SET_ASSERT(flags[i]); ++ read_lock_bh(&ip_set_lock); ++ do { ++ set = ip_set_list[index]; ++ IP_SET_ASSERT(set); ++ DP("set %s, index %u", set->name, index); ++ write_lock_bh(&set->lock); ++ res = set->type->delip_kernel(set, skb, flags[i], &ip); ++ write_unlock_bh(&set->lock); ++ } while ((res == 0 || res == -EEXIST) ++ && flags[++i] ++ && follow_bindings(index, set, ip)); ++ read_unlock_bh(&ip_set_lock); ++} ++ ++/* Register and deregister settype */ ++ ++static inline int ++set_type_equal(const struct ip_set_type *set_type, const char *str2) ++{ ++ return !strncmp(set_type->typename, str2, IP_SET_MAXNAMELEN - 1); ++} ++ ++static inline struct ip_set_type * ++find_set_type(const char *name) ++{ ++ return LIST_FIND(&set_type_list, ++ set_type_equal, ++ struct ip_set_type *, ++ name); ++} ++ ++int ++ip_set_register_set_type(struct ip_set_type *set_type) ++{ ++ int ret = 0; ++ ++ if (set_type->protocol_version != IP_SET_PROTOCOL_VERSION) { ++ ip_set_printk("'%s' uses wrong protocol version %u (want %u)", ++ set_type->typename, ++ set_type->protocol_version, ++ IP_SET_PROTOCOL_VERSION); ++ return -EINVAL; ++ } ++ ++ write_lock_bh(&ip_set_lock); ++ if (find_set_type(set_type->typename)) { ++ /* Duplicate! */ ++ ip_set_printk("'%s' already registered!", ++ set_type->typename); ++ ret = -EINVAL; ++ goto unlock; ++ } ++ if (!try_module_get(THIS_MODULE)) { ++ ret = -EFAULT; ++ goto unlock; ++ } ++ list_append(&set_type_list, set_type); ++ DP("'%s' registered.", set_type->typename); ++ unlock: ++ write_unlock_bh(&ip_set_lock); ++ return ret; ++} ++ ++void ++ip_set_unregister_set_type(struct ip_set_type *set_type) ++{ ++ write_lock_bh(&ip_set_lock); ++ if (!find_set_type(set_type->typename)) { ++ ip_set_printk("'%s' not registered?", ++ set_type->typename); ++ goto unlock; ++ } ++ LIST_DELETE(&set_type_list, set_type); ++ module_put(THIS_MODULE); ++ DP("'%s' unregistered.", set_type->typename); ++ unlock: ++ write_unlock_bh(&ip_set_lock); ++ ++} ++ ++/* ++ * Userspace routines ++ */ ++ ++/* ++ * Find set by name, reference it once. The reference makes sure the ++ * thing pointed to, does not go away under our feet. Drop the reference ++ * later, using ip_set_put(). ++ */ ++ip_set_id_t ++ip_set_get_byname(const char *name) ++{ ++ ip_set_id_t i, index = IP_SET_INVALID_ID; ++ ++ down(&ip_set_app_mutex); ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && strcmp(ip_set_list[i]->name, name) == 0) { ++ __ip_set_get(i); ++ index = i; ++ break; ++ } ++ } ++ up(&ip_set_app_mutex); ++ return index; ++} ++ ++/* ++ * Find set by index, reference it once. The reference makes sure the ++ * thing pointed to, does not go away under our feet. Drop the reference ++ * later, using ip_set_put(). ++ */ ++ip_set_id_t ++ip_set_get_byindex(ip_set_id_t index) ++{ ++ down(&ip_set_app_mutex); ++ ++ if (index >= ip_set_max) ++ return IP_SET_INVALID_ID; ++ ++ if (ip_set_list[index]) ++ __ip_set_get(index); ++ else ++ index = IP_SET_INVALID_ID; ++ ++ up(&ip_set_app_mutex); ++ return index; ++} ++ ++/* ++ * If the given set pointer points to a valid set, decrement ++ * reference count by 1. The caller shall not assume the index ++ * to be valid, after calling this function. ++ */ ++void ip_set_put(ip_set_id_t index) ++{ ++ down(&ip_set_app_mutex); ++ if (ip_set_list[index]) ++ __ip_set_put(index); ++ up(&ip_set_app_mutex); ++} ++ ++/* Find a set by name or index */ ++static ip_set_id_t ++ip_set_find_byname(const char *name) ++{ ++ ip_set_id_t i, index = IP_SET_INVALID_ID; ++ ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && strcmp(ip_set_list[i]->name, name) == 0) { ++ index = i; ++ break; ++ } ++ } ++ return index; ++} ++ ++static ip_set_id_t ++ip_set_find_byindex(ip_set_id_t index) ++{ ++ if (index >= ip_set_max || ip_set_list[index] == NULL) ++ index = IP_SET_INVALID_ID; ++ ++ return index; ++} ++ ++/* ++ * Add, del, test, bind and unbind ++ */ ++ ++static inline int ++__ip_set_testip(struct ip_set *set, ++ const void *data, ++ size_t size, ++ ip_set_ip_t *ip) ++{ ++ int res; ++ ++ read_lock_bh(&set->lock); ++ res = set->type->testip(set, data, size, ip); ++ read_unlock_bh(&set->lock); ++ ++ return res; ++} ++ ++static int ++__ip_set_addip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ do { ++ write_lock_bh(&set->lock); ++ res = set->type->addip(set, data, size, &ip); ++ write_unlock_bh(&set->lock); ++ } while (res == -EAGAIN ++ && set->type->retry ++ && (res = set->type->retry(set)) == 0); ++ ++ return res; ++} ++ ++static int ++ip_set_addip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ ++ return __ip_set_addip(index, ++ data + sizeof(struct ip_set_req_adt), ++ size - sizeof(struct ip_set_req_adt)); ++} ++ ++static int ++ip_set_delip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ write_lock_bh(&set->lock); ++ res = set->type->delip(set, ++ data + sizeof(struct ip_set_req_adt), ++ size - sizeof(struct ip_set_req_adt), ++ &ip); ++ write_unlock_bh(&set->lock); ++ ++ return res; ++} ++ ++static int ++ip_set_testip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ res = __ip_set_testip(set, ++ data + sizeof(struct ip_set_req_adt), ++ size - sizeof(struct ip_set_req_adt), ++ &ip); ++ ++ return (res > 0 ? -EEXIST : res); ++} ++ ++static int ++ip_set_bindip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ struct ip_set_req_bind *req_bind; ++ ip_set_id_t binding; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ if (size < sizeof(struct ip_set_req_bind)) ++ return -EINVAL; ++ ++ req_bind = (struct ip_set_req_bind *) data; ++ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { ++ /* Default binding of a set */ ++ char *binding_name; ++ ++ if (size != sizeof(struct ip_set_req_bind) + IP_SET_MAXNAMELEN) ++ return -EINVAL; ++ ++ binding_name = (char *)(data + sizeof(struct ip_set_req_bind)); ++ binding_name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ binding = ip_set_find_byname(binding_name); ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ write_lock_bh(&ip_set_lock); ++ /* Sets as binding values are referenced */ ++ if (set->binding != IP_SET_INVALID_ID) ++ __ip_set_put(set->binding); ++ set->binding = binding; ++ __ip_set_get(set->binding); ++ write_unlock_bh(&ip_set_lock); ++ ++ return 0; ++ } ++ binding = ip_set_find_byname(req_bind->binding); ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ res = __ip_set_testip(set, ++ data + sizeof(struct ip_set_req_bind), ++ size - sizeof(struct ip_set_req_bind), ++ &ip); ++ DP("set %s, ip: %u.%u.%u.%u, binding %s", ++ set->name, HIPQUAD(ip), ip_set_list[binding]->name); ++ ++ if (res >= 0) ++ res = ip_set_hash_add(set->id, ip, binding); ++ ++ return res; ++} ++ ++#define FOREACH_SET_DO(fn, args...) \ ++({ \ ++ ip_set_id_t __i; \ ++ struct ip_set *__set; \ ++ \ ++ for (__i = 0; __i < ip_set_max; __i++) { \ ++ __set = ip_set_list[__i]; \ ++ if (__set != NULL) \ ++ fn(__set , ##args); \ ++ } \ ++}) ++ ++static inline void ++__set_hash_del_byid(struct ip_set_hash *set_hash, ip_set_id_t id) ++{ ++ if (set_hash->id == id) ++ __set_hash_del(set_hash); ++} ++ ++static inline void ++__unbind_default(struct ip_set *set) ++{ ++ if (set->binding != IP_SET_INVALID_ID) { ++ /* Sets as binding values are referenced */ ++ __ip_set_put(set->binding); ++ set->binding = IP_SET_INVALID_ID; ++ } ++} ++ ++static int ++ip_set_unbindip(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set; ++ struct ip_set_req_bind *req_bind; ++ ip_set_ip_t ip; ++ int res; ++ ++ DP(""); ++ if (size < sizeof(struct ip_set_req_bind)) ++ return -EINVAL; ++ ++ req_bind = (struct ip_set_req_bind *) data; ++ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ DP("%u %s", index, req_bind->binding); ++ if (index == IP_SET_INVALID_ID) { ++ /* unbind :all: */ ++ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { ++ /* Default binding of sets */ ++ write_lock_bh(&ip_set_lock); ++ FOREACH_SET_DO(__unbind_default); ++ write_unlock_bh(&ip_set_lock); ++ return 0; ++ } else if (strcmp(req_bind->binding, IPSET_TOKEN_ALL) == 0) { ++ /* Flush all bindings of all sets*/ ++ write_lock_bh(&ip_set_lock); ++ FOREACH_HASH_RW_DO(__set_hash_del); ++ write_unlock_bh(&ip_set_lock); ++ return 0; ++ } ++ DP("unreachable reached!"); ++ return -EINVAL; ++ } ++ ++ set = ip_set_list[index]; ++ IP_SET_ASSERT(set); ++ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { ++ /* Default binding of set */ ++ ip_set_id_t binding = ip_set_find_byindex(set->binding); ++ ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ write_lock_bh(&ip_set_lock); ++ /* Sets in hash values are referenced */ ++ __ip_set_put(set->binding); ++ set->binding = IP_SET_INVALID_ID; ++ write_unlock_bh(&ip_set_lock); ++ ++ return 0; ++ } else if (strcmp(req_bind->binding, IPSET_TOKEN_ALL) == 0) { ++ /* Flush all bindings */ ++ ++ write_lock_bh(&ip_set_lock); ++ FOREACH_HASH_RW_DO(__set_hash_del_byid, set->id); ++ write_unlock_bh(&ip_set_lock); ++ return 0; ++ } ++ ++ res = __ip_set_testip(set, ++ data + sizeof(struct ip_set_req_bind), ++ size - sizeof(struct ip_set_req_bind), ++ &ip); ++ ++ DP("set %s, ip: %u.%u.%u.%u", set->name, HIPQUAD(ip)); ++ if (res >= 0) ++ res = ip_set_hash_del(set->id, ip); ++ ++ return res; ++} ++ ++static int ++ip_set_testbind(ip_set_id_t index, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ struct ip_set_req_bind *req_bind; ++ ip_set_id_t binding; ++ ip_set_ip_t ip; ++ int res; ++ ++ IP_SET_ASSERT(set); ++ if (size < sizeof(struct ip_set_req_bind)) ++ return -EINVAL; ++ ++ req_bind = (struct ip_set_req_bind *) data; ++ req_bind->binding[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ if (strcmp(req_bind->binding, IPSET_TOKEN_DEFAULT) == 0) { ++ /* Default binding of set */ ++ char *binding_name; ++ ++ if (size != sizeof(struct ip_set_req_bind) + IP_SET_MAXNAMELEN) ++ return -EINVAL; ++ ++ binding_name = (char *)(data + sizeof(struct ip_set_req_bind)); ++ binding_name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ binding = ip_set_find_byname(binding_name); ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ res = (set->binding == binding) ? -EEXIST : 0; ++ ++ return res; ++ } ++ binding = ip_set_find_byname(req_bind->binding); ++ if (binding == IP_SET_INVALID_ID) ++ return -ENOENT; ++ ++ ++ res = __ip_set_testip(set, ++ data + sizeof(struct ip_set_req_bind), ++ size - sizeof(struct ip_set_req_bind), ++ &ip); ++ DP("set %s, ip: %u.%u.%u.%u, binding %s", ++ set->name, HIPQUAD(ip), ip_set_list[binding]->name); ++ ++ if (res >= 0) ++ res = (ip_set_find_in_hash(set->id, ip) == binding) ++ ? -EEXIST : 0; ++ ++ return res; ++} ++ ++static struct ip_set_type * ++find_set_type_rlock(const char *typename) ++{ ++ struct ip_set_type *type; ++ ++ read_lock_bh(&ip_set_lock); ++ type = find_set_type(typename); ++ if (type == NULL) ++ read_unlock_bh(&ip_set_lock); ++ ++ return type; ++} ++ ++static int ++find_free_id(const char *name, ++ ip_set_id_t *index, ++ ip_set_id_t *id) ++{ ++ ip_set_id_t i; ++ ++ *id = IP_SET_INVALID_ID; ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] == NULL) { ++ if (*id == IP_SET_INVALID_ID) ++ *id = *index = i; ++ } else if (strcmp(name, ip_set_list[i]->name) == 0) ++ /* Name clash */ ++ return -EEXIST; ++ } ++ if (*id == IP_SET_INVALID_ID) ++ /* No free slot remained */ ++ return -ERANGE; ++ /* Check that index is usable as id (swapping) */ ++ check: ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && ip_set_list[i]->id == *id) { ++ *id = i; ++ goto check; ++ } ++ } ++ return 0; ++} ++ ++/* ++ * Create a set ++ */ ++static int ++ip_set_create(const char *name, ++ const char *typename, ++ ip_set_id_t restore, ++ const void *data, ++ size_t size) ++{ ++ struct ip_set *set; ++ ip_set_id_t index, id; ++ int res = 0; ++ ++ DP("setname: %s, typename: %s, id: %u", name, typename, restore); ++ /* ++ * First, and without any locks, allocate and initialize ++ * a normal base set structure. ++ */ ++ set = kmalloc(sizeof(struct ip_set), GFP_KERNEL); ++ if (!set) ++ return -ENOMEM; ++ set->lock = RW_LOCK_UNLOCKED; ++ strncpy(set->name, name, IP_SET_MAXNAMELEN); ++ set->binding = IP_SET_INVALID_ID; ++ atomic_set(&set->ref, 0); ++ ++ /* ++ * Next, take the &ip_set_lock, check that we know the type, ++ * and take a reference on the type, to make sure it ++ * stays available while constructing our new set. ++ * ++ * After referencing the type, we drop the &ip_set_lock, ++ * and let the new set construction run without locks. ++ */ ++ set->type = find_set_type_rlock(typename); ++ if (set->type == NULL) { ++ /* Try loading the module */ ++ char modulename[IP_SET_MAXNAMELEN + strlen("ip_set_") + 1]; ++ strcpy(modulename, "ip_set_"); ++ strcat(modulename, typename); ++ DP("try to load %s", modulename); ++ request_module(modulename); ++ set->type = find_set_type_rlock(typename); ++ } ++ if (set->type == NULL) { ++ ip_set_printk("no set type '%s', set '%s' not created", ++ typename, name); ++ res = -ENOENT; ++ goto out; ++ } ++ if (!try_module_get(set->type->me)) { ++ read_unlock_bh(&ip_set_lock); ++ res = -EFAULT; ++ goto out; ++ } ++ read_unlock_bh(&ip_set_lock); ++ ++ /* ++ * Without holding any locks, create private part. ++ */ ++ res = set->type->create(set, data, size); ++ if (res != 0) ++ goto put_out; ++ ++ /* BTW, res==0 here. */ ++ ++ /* ++ * Here, we have a valid, constructed set. &ip_set_lock again, ++ * find free id/index and check that it is not already in ++ * ip_set_list. ++ */ ++ write_lock_bh(&ip_set_lock); ++ if ((res = find_free_id(set->name, &index, &id)) != 0) { ++ DP("no free id!"); ++ goto cleanup; ++ } ++ ++ /* Make sure restore gets the same index */ ++ if (restore != IP_SET_INVALID_ID && index != restore) { ++ DP("Can't restore, sets are screwed up"); ++ res = -ERANGE; ++ goto cleanup; ++ } ++ ++ /* ++ * Finally! Add our shiny new set to the list, and be done. ++ */ ++ DP("create: '%s' created with index %u, id %u!", set->name, index, id); ++ set->id = id; ++ ip_set_list[index] = set; ++ write_unlock_bh(&ip_set_lock); ++ return res; ++ ++ cleanup: ++ write_unlock_bh(&ip_set_lock); ++ set->type->destroy(set); ++ put_out: ++ module_put(set->type->me); ++ out: ++ kfree(set); ++ return res; ++} ++ ++/* ++ * Destroy a given existing set ++ */ ++static void ++ip_set_destroy_set(ip_set_id_t index) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ++ IP_SET_ASSERT(set); ++ DP("set: %s", set->name); ++ write_lock_bh(&ip_set_lock); ++ FOREACH_HASH_RW_DO(__set_hash_del_byid, set->id); ++ if (set->binding != IP_SET_INVALID_ID) ++ __ip_set_put(set->binding); ++ ip_set_list[index] = NULL; ++ write_unlock_bh(&ip_set_lock); ++ ++ /* Must call it without holding any lock */ ++ set->type->destroy(set); ++ module_put(set->type->me); ++ kfree(set); ++} ++ ++/* ++ * Destroy a set - or all sets ++ * Sets must not be referenced/used. ++ */ ++static int ++ip_set_destroy(ip_set_id_t index) ++{ ++ ip_set_id_t i; ++ ++ /* ref modification always protected by the mutex */ ++ if (index != IP_SET_INVALID_ID) { ++ if (atomic_read(&ip_set_list[index]->ref)) ++ return -EBUSY; ++ ip_set_destroy_set(index); ++ } else { ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && (atomic_read(&ip_set_list[i]->ref))) ++ return -EBUSY; ++ } ++ ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL) ++ ip_set_destroy_set(i); ++ } ++ } ++ return 0; ++} ++ ++static void ++ip_set_flush_set(struct ip_set *set) ++{ ++ DP("set: %s %u", set->name, set->id); ++ ++ write_lock_bh(&set->lock); ++ set->type->flush(set); ++ write_unlock_bh(&set->lock); ++} ++ ++/* ++ * Flush data in a set - or in all sets ++ */ ++static int ++ip_set_flush(ip_set_id_t index) ++{ ++ if (index != IP_SET_INVALID_ID) { ++ IP_SET_ASSERT(ip_set_list[index]); ++ ip_set_flush_set(ip_set_list[index]); ++ } else ++ FOREACH_SET_DO(ip_set_flush_set); ++ ++ return 0; ++} ++ ++/* Rename a set */ ++static int ++ip_set_rename(ip_set_id_t index, const char *name) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ ip_set_id_t i; ++ int res = 0; ++ ++ DP("set: %s to %s", set->name, name); ++ write_lock_bh(&ip_set_lock); ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL ++ && strncmp(ip_set_list[i]->name, ++ name, ++ IP_SET_MAXNAMELEN - 1) == 0) { ++ res = -EEXIST; ++ goto unlock; ++ } ++ } ++ strncpy(set->name, name, IP_SET_MAXNAMELEN); ++ unlock: ++ write_unlock_bh(&ip_set_lock); ++ return res; ++} ++ ++/* ++ * Swap two sets so that name/index points to the other. ++ * References are also swapped. ++ */ ++static int ++ip_set_swap(ip_set_id_t from_index, ip_set_id_t to_index) ++{ ++ struct ip_set *from = ip_set_list[from_index]; ++ struct ip_set *to = ip_set_list[to_index]; ++ char from_name[IP_SET_MAXNAMELEN]; ++ u_int32_t from_ref; ++ ++ DP("set: %s to %s", from->name, to->name); ++ /* Type can't be changed. Artifical restriction. */ ++ if (from->type->typecode != to->type->typecode) ++ return -ENOEXEC; ++ ++ /* No magic here: ref munging protected by the mutex */ ++ write_lock_bh(&ip_set_lock); ++ strncpy(from_name, from->name, IP_SET_MAXNAMELEN); ++ from_ref = atomic_read(&from->ref); ++ ++ strncpy(from->name, to->name, IP_SET_MAXNAMELEN); ++ atomic_set(&from->ref, atomic_read(&to->ref)); ++ strncpy(to->name, from_name, IP_SET_MAXNAMELEN); ++ atomic_set(&to->ref, from_ref); ++ ++ ip_set_list[from_index] = to; ++ ip_set_list[to_index] = from; ++ ++ write_unlock_bh(&ip_set_lock); ++ return 0; ++} ++ ++/* ++ * List set data ++ */ ++ ++static inline void ++__set_hash_bindings_size_list(struct ip_set_hash *set_hash, ++ ip_set_id_t id, size_t *size) ++{ ++ if (set_hash->id == id) ++ *size += sizeof(struct ip_set_hash_list); ++} ++ ++static inline void ++__set_hash_bindings_size_save(struct ip_set_hash *set_hash, ++ ip_set_id_t id, size_t *size) ++{ ++ if (set_hash->id == id) ++ *size += sizeof(struct ip_set_hash_save); ++} ++ ++static inline void ++__set_hash_bindings(struct ip_set_hash *set_hash, ++ ip_set_id_t id, void *data, int *used) ++{ ++ if (set_hash->id == id) { ++ struct ip_set_hash_list *hash_list = ++ (struct ip_set_hash_list *)(data + *used); ++ ++ hash_list->ip = set_hash->ip; ++ hash_list->binding = set_hash->binding; ++ *used += sizeof(struct ip_set_hash_list); ++ } ++} ++ ++static int ip_set_list_set(ip_set_id_t index, ++ void *data, ++ int *used, ++ int len) ++{ ++ struct ip_set *set = ip_set_list[index]; ++ struct ip_set_list *set_list; ++ ++ /* Pointer to our header */ ++ set_list = (struct ip_set_list *) (data + *used); ++ ++ DP("set: %s, used: %d %p %p", set->name, *used, data, data + *used); ++ ++ /* Get and ensure header size */ ++ if (*used + sizeof(struct ip_set_list) > len) ++ goto not_enough_mem; ++ *used += sizeof(struct ip_set_list); ++ ++ read_lock_bh(&set->lock); ++ /* Get and ensure set specific header size */ ++ set_list->header_size = set->type->header_size; ++ if (*used + set_list->header_size > len) ++ goto unlock_set; ++ ++ /* Fill in the header */ ++ set_list->index = index; ++ set_list->binding = set->binding; ++ set_list->ref = atomic_read(&set->ref); ++ ++ /* Fill in set spefific header data */ ++ set->type->list_header(set, data + *used); ++ *used += set_list->header_size; ++ ++ /* Get and ensure set specific members size */ ++ set_list->members_size = set->type->list_members_size(set); ++ if (*used + set_list->members_size > len) ++ goto unlock_set; ++ ++ /* Fill in set spefific members data */ ++ set->type->list_members(set, data + *used); ++ *used += set_list->members_size; ++ read_unlock_bh(&set->lock); ++ ++ /* Bindings */ ++ ++ /* Get and ensure set specific bindings size */ ++ set_list->bindings_size = 0; ++ FOREACH_HASH_DO(__set_hash_bindings_size_list, ++ set->id, &set_list->bindings_size); ++ if (*used + set_list->bindings_size > len) ++ goto not_enough_mem; ++ ++ /* Fill in set spefific bindings data */ ++ FOREACH_HASH_DO(__set_hash_bindings, set->id, data, used); ++ ++ return 0; ++ ++ unlock_set: ++ read_unlock_bh(&set->lock); ++ not_enough_mem: ++ DP("not enough mem, try again"); ++ return -EAGAIN; ++} ++ ++/* ++ * Save sets ++ */ ++static int ip_set_save_set(ip_set_id_t index, ++ void *data, ++ int *used, ++ int len) ++{ ++ struct ip_set *set; ++ struct ip_set_save *set_save; ++ ++ /* Pointer to our header */ ++ set_save = (struct ip_set_save *) (data + *used); ++ ++ /* Get and ensure header size */ ++ if (*used + sizeof(struct ip_set_save) > len) ++ goto not_enough_mem; ++ *used += sizeof(struct ip_set_save); ++ ++ set = ip_set_list[index]; ++ DP("set: %s, used: %u(%u) %p %p", set->name, *used, len, ++ data, data + *used); ++ ++ read_lock_bh(&set->lock); ++ /* Get and ensure set specific header size */ ++ set_save->header_size = set->type->header_size; ++ if (*used + set_save->header_size > len) ++ goto unlock_set; ++ ++ /* Fill in the header */ ++ set_save->index = index; ++ set_save->binding = set->binding; ++ ++ /* Fill in set spefific header data */ ++ set->type->list_header(set, data + *used); ++ *used += set_save->header_size; ++ ++ DP("set header filled: %s, used: %u %p %p", set->name, *used, ++ data, data + *used); ++ /* Get and ensure set specific members size */ ++ set_save->members_size = set->type->list_members_size(set); ++ if (*used + set_save->members_size > len) ++ goto unlock_set; ++ ++ /* Fill in set spefific members data */ ++ set->type->list_members(set, data + *used); ++ *used += set_save->members_size; ++ read_unlock_bh(&set->lock); ++ DP("set members filled: %s, used: %u %p %p", set->name, *used, ++ data, data + *used); ++ return 0; ++ ++ unlock_set: ++ read_unlock_bh(&set->lock); ++ not_enough_mem: ++ DP("not enough mem, try again"); ++ return -EAGAIN; ++} ++ ++static inline void ++__set_hash_save_bindings(struct ip_set_hash *set_hash, ++ ip_set_id_t id, ++ void *data, ++ int *used, ++ int len, ++ int *res) ++{ ++ if (*res == 0 ++ && (id == IP_SET_INVALID_ID || set_hash->id == id)) { ++ struct ip_set_hash_save *hash_save = ++ (struct ip_set_hash_save *)(data + *used); ++ /* Ensure bindings size */ ++ if (*used + sizeof(struct ip_set_hash_save) > len) { ++ *res = -ENOMEM; ++ return; ++ } ++ hash_save->id = set_hash->id; ++ hash_save->ip = set_hash->ip; ++ hash_save->binding = set_hash->binding; ++ *used += sizeof(struct ip_set_hash_save); ++ } ++} ++ ++static int ip_set_save_bindings(ip_set_id_t index, ++ void *data, ++ int *used, ++ int len) ++{ ++ int res = 0; ++ struct ip_set_save *set_save; ++ ++ DP("used %u, len %u", *used, len); ++ /* Get and ensure header size */ ++ if (*used + sizeof(struct ip_set_save) > len) ++ return -ENOMEM; ++ ++ /* Marker */ ++ set_save = (struct ip_set_save *) (data + *used); ++ set_save->index = IP_SET_INVALID_ID; ++ *used += sizeof(struct ip_set_save); ++ ++ DP("marker added used %u, len %u", *used, len); ++ /* Fill in bindings data */ ++ if (index != IP_SET_INVALID_ID) ++ /* Sets are identified by id in hash */ ++ index = ip_set_list[index]->id; ++ FOREACH_HASH_DO(__set_hash_save_bindings, index, data, used, len, &res); ++ ++ return res; ++} ++ ++/* ++ * Restore sets ++ */ ++static int ip_set_restore(void *data, ++ int len) ++{ ++ int res = 0; ++ int line = 0, used = 0, members_size; ++ struct ip_set *set; ++ struct ip_set_hash_save *hash_save; ++ struct ip_set_restore *set_restore; ++ ip_set_id_t index; ++ ++ /* Loop to restore sets */ ++ while (1) { ++ line++; ++ ++ DP("%u %u %u", used, sizeof(struct ip_set_restore), len); ++ /* Get and ensure header size */ ++ if (used + sizeof(struct ip_set_restore) > len) ++ return line; ++ set_restore = (struct ip_set_restore *) (data + used); ++ used += sizeof(struct ip_set_restore); ++ ++ /* Ensure data size */ ++ if (used ++ + set_restore->header_size ++ + set_restore->members_size > len) ++ return line; ++ ++ /* Check marker */ ++ if (set_restore->index == IP_SET_INVALID_ID) { ++ line--; ++ goto bindings; ++ } ++ ++ /* Try to create the set */ ++ DP("restore %s %s", set_restore->name, set_restore->typename); ++ res = ip_set_create(set_restore->name, ++ set_restore->typename, ++ set_restore->index, ++ data + used, ++ set_restore->header_size); ++ ++ if (res != 0) ++ return line; ++ used += set_restore->header_size; ++ ++ index = ip_set_find_byindex(set_restore->index); ++ DP("index %u, restore_index %u", index, set_restore->index); ++ if (index != set_restore->index) ++ return line; ++ /* Try to restore members data */ ++ set = ip_set_list[index]; ++ members_size = 0; ++ DP("members_size %u reqsize %u", ++ set_restore->members_size, set->type->reqsize); ++ while (members_size + set->type->reqsize <= ++ set_restore->members_size) { ++ line++; ++ DP("members: %u, line %u", members_size, line); ++ res = __ip_set_addip(index, ++ data + used + members_size, ++ set->type->reqsize); ++ if (!(res == 0 || res == -EEXIST)) ++ return line; ++ members_size += set->type->reqsize; ++ } ++ ++ DP("members_size %u %u", ++ set_restore->members_size, members_size); ++ if (members_size != set_restore->members_size) ++ return line++; ++ used += set_restore->members_size; ++ } ++ ++ bindings: ++ /* Loop to restore bindings */ ++ while (used < len) { ++ line++; ++ ++ DP("restore binding, line %u", line); ++ /* Get and ensure size */ ++ if (used + sizeof(struct ip_set_hash_save) > len) ++ return line; ++ hash_save = (struct ip_set_hash_save *) (data + used); ++ used += sizeof(struct ip_set_hash_save); ++ ++ /* hash_save->id is used to store the index */ ++ index = ip_set_find_byindex(hash_save->id); ++ DP("restore binding index %u, id %u, %u -> %u", ++ index, hash_save->id, hash_save->ip, hash_save->binding); ++ if (index != hash_save->id) ++ return line; ++ ++ set = ip_set_list[hash_save->id]; ++ /* Null valued IP means default binding */ ++ if (hash_save->ip) ++ res = ip_set_hash_add(set->id, ++ hash_save->ip, ++ hash_save->binding); ++ else { ++ IP_SET_ASSERT(set->binding == IP_SET_INVALID_ID); ++ write_lock_bh(&ip_set_lock); ++ set->binding = hash_save->binding; ++ __ip_set_get(set->binding); ++ write_unlock_bh(&ip_set_lock); ++ DP("default binding: %u", set->binding); ++ } ++ if (res != 0) ++ return line; ++ } ++ if (used != len) ++ return line; ++ ++ return 0; ++} ++ ++static int ++ip_set_sockfn_set(struct sock *sk, int optval, void *user, unsigned int len) ++{ ++ void *data; ++ int res = 0; /* Assume OK */ ++ unsigned *op; ++ struct ip_set_req_adt *req_adt; ++ ip_set_id_t index = IP_SET_INVALID_ID; ++ int (*adtfn)(ip_set_id_t index, ++ const void *data, size_t size); ++ struct fn_table { ++ int (*fn)(ip_set_id_t index, ++ const void *data, size_t size); ++ } adtfn_table[] = ++ { { ip_set_addip }, { ip_set_delip }, { ip_set_testip}, ++ { ip_set_bindip}, { ip_set_unbindip }, { ip_set_testbind }, ++ }; ++ ++ DP("optval=%d, user=%p, len=%d", optval, user, len); ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ if (optval != SO_IP_SET) ++ return -EBADF; ++ if (len <= sizeof(unsigned)) { ++ ip_set_printk("short userdata (want >%zu, got %u)", ++ sizeof(unsigned), len); ++ return -EINVAL; ++ } ++ data = vmalloc(len); ++ if (!data) { ++ DP("out of mem for %u bytes", len); ++ return -ENOMEM; ++ } ++ if (copy_from_user(data, user, len) != 0) { ++ res = -EFAULT; ++ goto done; ++ } ++ if (down_interruptible(&ip_set_app_mutex)) { ++ res = -EINTR; ++ goto done; ++ } ++ ++ op = (unsigned *)data; ++ DP("op=%x", *op); ++ ++ if (*op < IP_SET_OP_VERSION) { ++ /* Check the version at the beginning of operations */ ++ struct ip_set_req_version *req_version = ++ (struct ip_set_req_version *) data; ++ if (req_version->version != IP_SET_PROTOCOL_VERSION) { ++ res = -EPROTO; ++ goto done; ++ } ++ } ++ ++ switch (*op) { ++ case IP_SET_OP_CREATE:{ ++ struct ip_set_req_create *req_create ++ = (struct ip_set_req_create *) data; ++ ++ if (len <= sizeof(struct ip_set_req_create)) { ++ ip_set_printk("short CREATE data (want >%zu, got %u)", ++ sizeof(struct ip_set_req_create), len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_create->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ req_create->typename[IP_SET_MAXNAMELEN - 1] = '\0'; ++ res = ip_set_create(req_create->name, ++ req_create->typename, ++ IP_SET_INVALID_ID, ++ data + sizeof(struct ip_set_req_create), ++ len - sizeof(struct ip_set_req_create)); ++ goto done; ++ } ++ case IP_SET_OP_DESTROY:{ ++ struct ip_set_req_std *req_destroy ++ = (struct ip_set_req_std *) data; ++ ++ if (len != sizeof(struct ip_set_req_std)) { ++ ip_set_printk("invalid DESTROY data (want %zu, got %u)", ++ sizeof(struct ip_set_req_std), len); ++ res = -EINVAL; ++ goto done; ++ } ++ if (strcmp(req_destroy->name, IPSET_TOKEN_ALL) == 0) { ++ /* Destroy all sets */ ++ index = IP_SET_INVALID_ID; ++ } else { ++ req_destroy->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byname(req_destroy->name); ++ ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ } ++ ++ res = ip_set_destroy(index); ++ goto done; ++ } ++ case IP_SET_OP_FLUSH:{ ++ struct ip_set_req_std *req_flush = ++ (struct ip_set_req_std *) data; ++ ++ if (len != sizeof(struct ip_set_req_std)) { ++ ip_set_printk("invalid FLUSH data (want %zu, got %u)", ++ sizeof(struct ip_set_req_std), len); ++ res = -EINVAL; ++ goto done; ++ } ++ if (strcmp(req_flush->name, IPSET_TOKEN_ALL) == 0) { ++ /* Flush all sets */ ++ index = IP_SET_INVALID_ID; ++ } else { ++ req_flush->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byname(req_flush->name); ++ ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ } ++ res = ip_set_flush(index); ++ goto done; ++ } ++ case IP_SET_OP_RENAME:{ ++ struct ip_set_req_create *req_rename ++ = (struct ip_set_req_create *) data; ++ ++ if (len != sizeof(struct ip_set_req_create)) { ++ ip_set_printk("invalid RENAME data (want %zu, got %u)", ++ sizeof(struct ip_set_req_create), len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ req_rename->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ req_rename->typename[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ index = ip_set_find_byname(req_rename->name); ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ res = ip_set_rename(index, req_rename->typename); ++ goto done; ++ } ++ case IP_SET_OP_SWAP:{ ++ struct ip_set_req_create *req_swap ++ = (struct ip_set_req_create *) data; ++ ip_set_id_t to_index; ++ ++ if (len != sizeof(struct ip_set_req_create)) { ++ ip_set_printk("invalid SWAP data (want %zu, got %u)", ++ sizeof(struct ip_set_req_create), len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ req_swap->name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ req_swap->typename[IP_SET_MAXNAMELEN - 1] = '\0'; ++ ++ index = ip_set_find_byname(req_swap->name); ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ to_index = ip_set_find_byname(req_swap->typename); ++ if (to_index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ res = ip_set_swap(index, to_index); ++ goto done; ++ } ++ default: ++ break; /* Set identified by id */ ++ } ++ ++ /* There we may have add/del/test/bind/unbind/test_bind operations */ ++ if (*op < IP_SET_OP_ADD_IP || *op > IP_SET_OP_TEST_BIND_SET) { ++ res = -EBADMSG; ++ goto done; ++ } ++ adtfn = adtfn_table[*op - IP_SET_OP_ADD_IP].fn; ++ ++ if (len < sizeof(struct ip_set_req_adt)) { ++ ip_set_printk("short data in adt request (want >=%zu, got %u)", ++ sizeof(struct ip_set_req_adt), len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_adt = (struct ip_set_req_adt *) data; ++ ++ /* -U :all: :all:|:default: uses IP_SET_INVALID_ID */ ++ if (!(*op == IP_SET_OP_UNBIND_SET ++ && req_adt->index == IP_SET_INVALID_ID)) { ++ index = ip_set_find_byindex(req_adt->index); ++ if (index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ } ++ res = adtfn(index, data, len); ++ ++ done: ++ up(&ip_set_app_mutex); ++ vfree(data); ++ if (res > 0) ++ res = 0; ++ DP("final result %d", res); ++ return res; ++} ++ ++static int ++ip_set_sockfn_get(struct sock *sk, int optval, void *user, int *len) ++{ ++ int res = 0; ++ unsigned *op; ++ ip_set_id_t index = IP_SET_INVALID_ID; ++ void *data; ++ int copylen = *len; ++ ++ DP("optval=%d, user=%p, len=%d", optval, user, *len); ++ if (!capable(CAP_NET_ADMIN)) ++ return -EPERM; ++ if (optval != SO_IP_SET) ++ return -EBADF; ++ if (*len < sizeof(unsigned)) { ++ ip_set_printk("short userdata (want >=%zu, got %d)", ++ sizeof(unsigned), *len); ++ return -EINVAL; ++ } ++ data = vmalloc(*len); ++ if (!data) { ++ DP("out of mem for %d bytes", *len); ++ return -ENOMEM; ++ } ++ if (copy_from_user(data, user, *len) != 0) { ++ res = -EFAULT; ++ goto done; ++ } ++ if (down_interruptible(&ip_set_app_mutex)) { ++ res = -EINTR; ++ goto done; ++ } ++ ++ op = (unsigned *) data; ++ DP("op=%x", *op); ++ ++ if (*op < IP_SET_OP_VERSION) { ++ /* Check the version at the beginning of operations */ ++ struct ip_set_req_version *req_version = ++ (struct ip_set_req_version *) data; ++ if (req_version->version != IP_SET_PROTOCOL_VERSION) { ++ res = -EPROTO; ++ goto done; ++ } ++ } ++ ++ switch (*op) { ++ case IP_SET_OP_VERSION: { ++ struct ip_set_req_version *req_version = ++ (struct ip_set_req_version *) data; ++ ++ if (*len != sizeof(struct ip_set_req_version)) { ++ ip_set_printk("invalid VERSION (want %zu, got %d)", ++ sizeof(struct ip_set_req_version), ++ *len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ req_version->version = IP_SET_PROTOCOL_VERSION; ++ res = copy_to_user(user, req_version, ++ sizeof(struct ip_set_req_version)); ++ goto done; ++ } ++ case IP_SET_OP_GET_BYNAME: { ++ struct ip_set_req_get_set *req_get ++ = (struct ip_set_req_get_set *) data; ++ ++ if (*len != sizeof(struct ip_set_req_get_set)) { ++ ip_set_printk("invalid GET_BYNAME (want %zu, got %d)", ++ sizeof(struct ip_set_req_get_set), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byname(req_get->set.name); ++ req_get->set.index = index; ++ goto copy; ++ } ++ case IP_SET_OP_GET_BYINDEX: { ++ struct ip_set_req_get_set *req_get ++ = (struct ip_set_req_get_set *) data; ++ ++ if (*len != sizeof(struct ip_set_req_get_set)) { ++ ip_set_printk("invalid GET_BYINDEX (want %zu, got %d)", ++ sizeof(struct ip_set_req_get_set), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byindex(req_get->set.index); ++ strncpy(req_get->set.name, ++ index == IP_SET_INVALID_ID ? "" ++ : ip_set_list[index]->name, IP_SET_MAXNAMELEN); ++ goto copy; ++ } ++ case IP_SET_OP_ADT_GET: { ++ struct ip_set_req_adt_get *req_get ++ = (struct ip_set_req_adt_get *) data; ++ ++ if (*len != sizeof(struct ip_set_req_adt_get)) { ++ ip_set_printk("invalid ADT_GET (want %zu, got %d)", ++ sizeof(struct ip_set_req_adt_get), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ req_get->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ index = ip_set_find_byname(req_get->set.name); ++ if (index != IP_SET_INVALID_ID) { ++ req_get->set.index = index; ++ strncpy(req_get->typename, ++ ip_set_list[index]->type->typename, ++ IP_SET_MAXNAMELEN - 1); ++ } else { ++ res = -ENOENT; ++ goto done; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_MAX_SETS: { ++ struct ip_set_req_max_sets *req_max_sets ++ = (struct ip_set_req_max_sets *) data; ++ ip_set_id_t i; ++ ++ if (*len != sizeof(struct ip_set_req_max_sets)) { ++ ip_set_printk("invalid MAX_SETS (want %zu, got %d)", ++ sizeof(struct ip_set_req_max_sets), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ if (strcmp(req_max_sets->set.name, IPSET_TOKEN_ALL) == 0) { ++ req_max_sets->set.index = IP_SET_INVALID_ID; ++ } else { ++ req_max_sets->set.name[IP_SET_MAXNAMELEN - 1] = '\0'; ++ req_max_sets->set.index = ++ ip_set_find_byname(req_max_sets->set.name); ++ if (req_max_sets->set.index == IP_SET_INVALID_ID) { ++ res = -ENOENT; ++ goto done; ++ } ++ } ++ req_max_sets->max_sets = ip_set_max; ++ req_max_sets->sets = 0; ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] != NULL) ++ req_max_sets->sets++; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_LIST_SIZE: ++ case IP_SET_OP_SAVE_SIZE: { ++ struct ip_set_req_setnames *req_setnames ++ = (struct ip_set_req_setnames *) data; ++ struct ip_set_name_list *name_list; ++ struct ip_set *set; ++ ip_set_id_t i; ++ int used; ++ ++ if (*len < sizeof(struct ip_set_req_setnames)) { ++ ip_set_printk("short LIST_SIZE (want >=%zu, got %d)", ++ sizeof(struct ip_set_req_setnames), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ ++ req_setnames->size = 0; ++ used = sizeof(struct ip_set_req_setnames); ++ for (i = 0; i < ip_set_max; i++) { ++ if (ip_set_list[i] == NULL) ++ continue; ++ name_list = (struct ip_set_name_list *) ++ (data + used); ++ used += sizeof(struct ip_set_name_list); ++ if (used > copylen) { ++ res = -EAGAIN; ++ goto done; ++ } ++ set = ip_set_list[i]; ++ /* Fill in index, name, etc. */ ++ name_list->index = i; ++ name_list->id = set->id; ++ strncpy(name_list->name, ++ set->name, ++ IP_SET_MAXNAMELEN - 1); ++ strncpy(name_list->typename, ++ set->type->typename, ++ IP_SET_MAXNAMELEN - 1); ++ DP("filled %s of type %s, index %u\n", ++ name_list->name, name_list->typename, ++ name_list->index); ++ if (!(req_setnames->index == IP_SET_INVALID_ID ++ || req_setnames->index == i)) ++ continue; ++ /* Update size */ ++ switch (*op) { ++ case IP_SET_OP_LIST_SIZE: { ++ req_setnames->size += sizeof(struct ip_set_list) ++ + set->type->header_size ++ + set->type->list_members_size(set); ++ FOREACH_HASH_DO(__set_hash_bindings_size_list, ++ i, &req_setnames->size); ++ break; ++ } ++ case IP_SET_OP_SAVE_SIZE: { ++ req_setnames->size += sizeof(struct ip_set_save) ++ + set->type->header_size ++ + set->type->list_members_size(set); ++ FOREACH_HASH_DO(__set_hash_bindings_size_save, ++ i, &req_setnames->size); ++ break; ++ } ++ default: ++ break; ++ } ++ } ++ if (copylen != used) { ++ res = -EAGAIN; ++ goto done; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_LIST: { ++ struct ip_set_req_list *req_list ++ = (struct ip_set_req_list *) data; ++ ip_set_id_t i; ++ int used; ++ ++ if (*len < sizeof(struct ip_set_req_list)) { ++ ip_set_printk("short LIST (want >=%zu, got %d)", ++ sizeof(struct ip_set_req_list), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ index = req_list->index; ++ if (index != IP_SET_INVALID_ID ++ && ip_set_find_byindex(index) != index) { ++ res = -ENOENT; ++ goto done; ++ } ++ used = 0; ++ if (index == IP_SET_INVALID_ID) { ++ /* List all sets */ ++ for (i = 0; i < ip_set_max && res == 0; i++) { ++ if (ip_set_list[i] != NULL) ++ res = ip_set_list_set(i, data, &used, *len); ++ } ++ } else { ++ /* List an individual set */ ++ res = ip_set_list_set(index, data, &used, *len); ++ } ++ if (res != 0) ++ goto done; ++ else if (copylen != used) { ++ res = -EAGAIN; ++ goto done; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_SAVE: { ++ struct ip_set_req_list *req_save ++ = (struct ip_set_req_list *) data; ++ ip_set_id_t i; ++ int used; ++ ++ if (*len < sizeof(struct ip_set_req_list)) { ++ ip_set_printk("short SAVE (want >=%zu, got %d)", ++ sizeof(struct ip_set_req_list), *len); ++ res = -EINVAL; ++ goto done; ++ } ++ index = req_save->index; ++ if (index != IP_SET_INVALID_ID ++ && ip_set_find_byindex(index) != index) { ++ res = -ENOENT; ++ goto done; ++ } ++ used = 0; ++ if (index == IP_SET_INVALID_ID) { ++ /* Save all sets */ ++ for (i = 0; i < ip_set_max && res == 0; i++) { ++ if (ip_set_list[i] != NULL) ++ res = ip_set_save_set(i, data, &used, *len); ++ } ++ } else { ++ /* Save an individual set */ ++ res = ip_set_save_set(index, data, &used, *len); ++ } ++ if (res == 0) ++ res = ip_set_save_bindings(index, data, &used, *len); ++ ++ if (res != 0) ++ goto done; ++ else if (copylen != used) { ++ res = -EAGAIN; ++ goto done; ++ } ++ goto copy; ++ } ++ case IP_SET_OP_RESTORE: { ++ struct ip_set_req_setnames *req_restore ++ = (struct ip_set_req_setnames *) data; ++ int line; ++ ++ if (*len < sizeof(struct ip_set_req_setnames) ++ || *len != req_restore->size) { ++ ip_set_printk("invalid RESTORE (want =%zu, got %d)", ++ req_restore->size, *len); ++ res = -EINVAL; ++ goto done; ++ } ++ line = ip_set_restore(data + sizeof(struct ip_set_req_setnames), ++ req_restore->size - sizeof(struct ip_set_req_setnames)); ++ DP("ip_set_restore: %u", line); ++ if (line != 0) { ++ res = -EAGAIN; ++ req_restore->size = line; ++ copylen = sizeof(struct ip_set_req_setnames); ++ goto copy; ++ } ++ goto done; ++ } ++ default: ++ res = -EBADMSG; ++ goto done; ++ } /* end of switch(op) */ ++ ++ copy: ++ DP("set %s, copylen %u", index != IP_SET_INVALID_ID ++ && ip_set_list[index] ++ ? ip_set_list[index]->name ++ : ":all:", copylen); ++ if (res == 0) ++ res = copy_to_user(user, data, copylen); ++ else ++ copy_to_user(user, data, copylen); ++ ++ done: ++ up(&ip_set_app_mutex); ++ vfree(data); ++ if (res > 0) ++ res = 0; ++ DP("final result %d", res); ++ return res; ++} ++ ++static struct nf_sockopt_ops so_set = { ++ .pf = PF_INET, ++ .set_optmin = SO_IP_SET, ++ .set_optmax = SO_IP_SET + 1, ++ .set = &ip_set_sockfn_set, ++ .get_optmin = SO_IP_SET, ++ .get_optmax = SO_IP_SET + 1, ++ .get = &ip_set_sockfn_get, ++ .use = 0 ++}; ++ ++static int max_sets, hash_size; ++module_param(max_sets, int, 0600); ++MODULE_PARM_DESC(max_sets, "maximal number of sets"); ++module_param(hash_size, int, 0600); ++MODULE_PARM_DESC(hash_size, "hash size for bindings"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); ++MODULE_DESCRIPTION("module implementing core IP set support"); ++ ++static int __init init(void) ++{ ++ int res; ++ ip_set_id_t i; ++ ++ get_random_bytes(&ip_set_hash_random, 4); ++ if (max_sets) ++ ip_set_max = max_sets; ++ ip_set_list = vmalloc(sizeof(struct ip_set *) * ip_set_max); ++ if (!ip_set_list) { ++ printk(KERN_ERR "Unable to create ip_set_list\n"); ++ return -ENOMEM; ++ } ++ memset(ip_set_list, 0, sizeof(struct ip_set *) * ip_set_max); ++ if (hash_size) ++ ip_set_bindings_hash_size = hash_size; ++ ip_set_hash = vmalloc(sizeof(struct list_head) * ip_set_bindings_hash_size); ++ if (!ip_set_hash) { ++ printk(KERN_ERR "Unable to create ip_set_hash\n"); ++ vfree(ip_set_list); ++ return -ENOMEM; ++ } ++ for (i = 0; i < ip_set_bindings_hash_size; i++) ++ INIT_LIST_HEAD(&ip_set_hash[i]); ++ ++ INIT_LIST_HEAD(&set_type_list); ++ ++ res = nf_register_sockopt(&so_set); ++ if (res != 0) { ++ ip_set_printk("SO_SET registry failed: %d", res); ++ vfree(ip_set_list); ++ vfree(ip_set_hash); ++ return res; ++ } ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ /* There can't be any existing set or binding */ ++ nf_unregister_sockopt(&so_set); ++ vfree(ip_set_list); ++ vfree(ip_set_hash); ++ DP("these are the famous last words"); ++} ++ ++EXPORT_SYMBOL(ip_set_register_set_type); ++EXPORT_SYMBOL(ip_set_unregister_set_type); ++ ++EXPORT_SYMBOL(ip_set_get_byname); ++EXPORT_SYMBOL(ip_set_get_byindex); ++EXPORT_SYMBOL(ip_set_put); ++ ++EXPORT_SYMBOL(ip_set_addip_kernel); ++EXPORT_SYMBOL(ip_set_delip_kernel); ++EXPORT_SYMBOL(ip_set_testip_kernel); ++ ++module_init(init); ++module_exit(fini); +diff -Nur linux-2.6.16/net/ipv4/netfilter/ip_set_iphash.c linux-2.6.16-owrt/net/ipv4/netfilter/ip_set_iphash.c +--- linux-2.6.16/net/ipv4/netfilter/ip_set_iphash.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/net/ipv4/netfilter/ip_set_iphash.c 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,379 @@ ++/* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing an ip hash set */ ++ ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <linux/spinlock.h> ++#include <linux/vmalloc.h> ++#include <linux/random.h> ++ ++#include <net/ip.h> ++ ++#include <linux/netfilter_ipv4/ip_set_malloc.h> ++#include <linux/netfilter_ipv4/ip_set_iphash.h> ++#include <linux/netfilter_ipv4/ip_set_jhash.h> ++#include <linux/netfilter_ipv4/ip_set_prime.h> ++ ++static inline __u32 ++jhash_ip(const struct ip_set_iphash *map, ip_set_ip_t ip) ++{ ++ return jhash_1word(ip, map->initval); ++} ++ ++static inline __u32 ++randhash_ip(const struct ip_set_iphash *map, ip_set_ip_t ip) ++{ ++ return (1 + ip % map->prime); ++} ++ ++static inline __u32 ++hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ __u32 jhash, randhash, id; ++ u_int16_t i; ++ ++ *hash_ip = ip & map->netmask; ++ jhash = jhash_ip(map, *hash_ip); ++ randhash = randhash_ip(map, *hash_ip); ++ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u, %u.%u.%u.%u", ++ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip), HIPQUAD(map->netmask)); ++ ++ for (i = 0; i < map->probes; i++) { ++ id = (jhash + i * randhash) % map->hashsize; ++ DP("hash key: %u", id); ++ if (map->members[id] == *hash_ip) ++ return id; ++ /* No shortcut at testing - there can be deleted ++ * entries. */ ++ } ++ return UINT_MAX; ++} ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ return (hash_id(set, ip, hash_ip) != UINT_MAX); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iphash *req = ++ (struct ip_set_req_iphash *) data; ++ ++ if (size != sizeof(struct ip_set_req_iphash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iphash), ++ size); ++ return -EINVAL; ++ } ++ return __testip(set, req->ip, hash_ip); ++} ++ ++static int ++testip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ return __testip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++} ++ ++static inline int ++__addip(struct ip_set_iphash *map, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ __u32 jhash, randhash, probe; ++ u_int16_t i; ++ ++ *hash_ip = ip & map->netmask; ++ jhash = jhash_ip(map, *hash_ip); ++ randhash = randhash_ip(map, *hash_ip); ++ ++ for (i = 0; i < map->probes; i++) { ++ probe = (jhash + i * randhash) % map->hashsize; ++ if (map->members[probe] == *hash_ip) ++ return -EEXIST; ++ if (!map->members[probe]) { ++ map->members[probe] = *hash_ip; ++ return 0; ++ } ++ } ++ /* Trigger rehashing */ ++ return -EAGAIN; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iphash *req = ++ (struct ip_set_req_iphash *) data; ++ ++ if (size != sizeof(struct ip_set_req_iphash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iphash), ++ size); ++ return -EINVAL; ++ } ++ return __addip((struct ip_set_iphash *) set->data, req->ip, hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ return __addip((struct ip_set_iphash *) set->data, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++} ++ ++static int retry(struct ip_set *set) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ ip_set_ip_t hash_ip, *members; ++ u_int32_t i, hashsize; ++ unsigned newbytes; ++ int res; ++ struct ip_set_iphash tmp = { ++ .hashsize = map->hashsize, ++ .probes = map->probes, ++ .resize = map->resize, ++ .netmask = map->netmask, ++ }; ++ ++ if (map->resize == 0) ++ return -ERANGE; ++ ++ again: ++ res = 0; ++ ++ /* Calculate new parameters */ ++ get_random_bytes(&tmp.initval, 4); ++ hashsize = tmp.hashsize + (tmp.hashsize * map->resize)/100; ++ if (hashsize == tmp.hashsize) ++ hashsize++; ++ tmp.prime = make_prime(hashsize); ++ ++ ip_set_printk("rehashing of set %s triggered: " ++ "hashsize grows from %u to %u", ++ set->name, tmp.hashsize, hashsize); ++ tmp.hashsize = hashsize; ++ ++ newbytes = hashsize * sizeof(ip_set_ip_t); ++ tmp.members = ip_set_malloc_atomic(newbytes); ++ if (!tmp.members) { ++ DP("out of memory for %d bytes", newbytes); ++ return -ENOMEM; ++ } ++ memset(tmp.members, 0, newbytes); ++ ++ write_lock_bh(&set->lock); ++ map = (struct ip_set_iphash *) set->data; /* Play safe */ ++ for (i = 0; i < map->hashsize && res == 0; i++) { ++ if (map->members[i]) ++ res = __addip(&tmp, map->members[i], &hash_ip); ++ } ++ if (res) { ++ /* Failure, try again */ ++ write_unlock_bh(&set->lock); ++ ip_set_free(tmp.members, newbytes); ++ goto again; ++ } ++ ++ /* Success at resizing! */ ++ members = map->members; ++ hashsize = map->hashsize; ++ ++ map->initval = tmp.initval; ++ map->prime = tmp.prime; ++ map->hashsize = tmp.hashsize; ++ map->members = tmp.members; ++ write_unlock_bh(&set->lock); ++ ++ ip_set_free(members, hashsize * sizeof(ip_set_ip_t)); ++ ++ return 0; ++} ++ ++static inline int ++__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ ip_set_ip_t id = hash_id(set, ip, hash_ip); ++ ++ if (id == UINT_MAX) ++ return -EEXIST; ++ ++ map->members[id] = 0; ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iphash *req = ++ (struct ip_set_req_iphash *) data; ++ ++ if (size != sizeof(struct ip_set_req_iphash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iphash), ++ size); ++ return -EINVAL; ++ } ++ return __delip(set, req->ip, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ return __delip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ unsigned newbytes; ++ struct ip_set_req_iphash_create *req = ++ (struct ip_set_req_iphash_create *) data; ++ struct ip_set_iphash *map; ++ ++ if (size != sizeof(struct ip_set_req_iphash_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iphash_create), ++ size); ++ return -EINVAL; ++ } ++ ++ if (req->hashsize < 1) { ++ ip_set_printk("hashsize too small"); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_iphash), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_iphash)); ++ return -ENOMEM; ++ } ++ get_random_bytes(&map->initval, 4); ++ map->prime = make_prime(req->hashsize); ++ map->hashsize = req->hashsize; ++ map->probes = req->probes; ++ map->resize = req->resize; ++ map->netmask = req->netmask; ++ newbytes = map->hashsize * sizeof(ip_set_ip_t); ++ map->members = ip_set_malloc(newbytes); ++ if (!map->members) { ++ DP("out of memory for %d bytes", newbytes); ++ kfree(map); ++ return -ENOMEM; ++ } ++ memset(map->members, 0, newbytes); ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ ++ ip_set_free(map->members, map->hashsize * sizeof(ip_set_ip_t)); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ memset(map->members, 0, map->hashsize * sizeof(ip_set_ip_t)); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ struct ip_set_req_iphash_create *header = ++ (struct ip_set_req_iphash_create *) data; ++ ++ header->hashsize = map->hashsize; ++ header->probes = map->probes; ++ header->resize = map->resize; ++ header->netmask = map->netmask; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ ++ return (map->hashsize * sizeof(ip_set_ip_t)); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iphash *map = (struct ip_set_iphash *) set->data; ++ int bytes = map->hashsize * sizeof(ip_set_ip_t); ++ ++ memcpy(data, map->members, bytes); ++} ++ ++static struct ip_set_type ip_set_iphash = { ++ .typename = SETTYPE_NAME, ++ .typecode = IPSET_TYPE_IP, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_iphash), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .retry = &retry, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_iphash_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); ++MODULE_DESCRIPTION("iphash type of IP sets"); ++ ++static int __init init(void) ++{ ++ init_max_malloc_size(); ++ return ip_set_register_set_type(&ip_set_iphash); ++} ++ ++static void __exit fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_iphash); ++} ++ ++module_init(init); ++module_exit(fini); +diff -Nur linux-2.6.16/net/ipv4/netfilter/ip_set_ipmap.c linux-2.6.16-owrt/net/ipv4/netfilter/ip_set_ipmap.c +--- linux-2.6.16/net/ipv4/netfilter/ip_set_ipmap.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/net/ipv4/netfilter/ip_set_ipmap.c 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,313 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> ++ * Patrick Schaaf <bof@bof.de> ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing an IP set type: the single bitmap type */ ++ ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <linux/spinlock.h> ++ ++#include <linux/netfilter_ipv4/ip_set_ipmap.h> ++ ++static inline ip_set_ip_t ++ip_to_id(const struct ip_set_ipmap *map, ip_set_ip_t ip) ++{ ++ return (ip - map->first_ip)/map->hosts; ++} ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ *hash_ip = ip & map->netmask; ++ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", ++ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ return !!test_bit(ip_to_id(map, *hash_ip), map->members); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_ipmap *req = ++ (struct ip_set_req_ipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_ipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipmap), ++ size); ++ return -EINVAL; ++ } ++ return __testip(set, req->ip, hash_ip); ++} ++ ++static int ++testip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ u_int32_t flags, ++ ip_set_ip_t *hash_ip) ++{ ++ int res; ++ ++ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", ++ flags & IPSET_SRC ? "SRC" : "DST", ++ NIPQUAD(skb->nh.iph->saddr), ++ NIPQUAD(skb->nh.iph->daddr)); ++ ++ res = __testip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++ return (res < 0 ? 0 : res); ++} ++ ++static inline int ++__addip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ *hash_ip = ip & map->netmask; ++ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ if (test_and_set_bit(ip_to_id(map, *hash_ip), map->members)) ++ return -EEXIST; ++ ++ return 0; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_ipmap *req = ++ (struct ip_set_req_ipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_ipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipmap), ++ size); ++ return -EINVAL; ++ } ++ DP("%u.%u.%u.%u", HIPQUAD(req->ip)); ++ return __addip(set, req->ip, hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ return __addip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++} ++ ++static inline int ++__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ ++ *hash_ip = ip & map->netmask; ++ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ if (!test_and_clear_bit(ip_to_id(map, *hash_ip), map->members)) ++ return -EEXIST; ++ ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_ipmap *req = ++ (struct ip_set_req_ipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_ipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipmap), ++ size); ++ return -EINVAL; ++ } ++ return __delip(set, req->ip, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ return __delip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ int newbytes; ++ struct ip_set_req_ipmap_create *req = ++ (struct ip_set_req_ipmap_create *) data; ++ struct ip_set_ipmap *map; ++ ++ if (size != sizeof(struct ip_set_req_ipmap_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_ipmap_create), ++ size); ++ return -EINVAL; ++ } ++ ++ DP("from %u.%u.%u.%u to %u.%u.%u.%u", ++ HIPQUAD(req->from), HIPQUAD(req->to)); ++ ++ if (req->from > req->to) { ++ DP("bad ip range"); ++ return -ENOEXEC; ++ } ++ ++ if (req->to - req->from > MAX_RANGE) { ++ ip_set_printk("range too big (max %d addresses)", ++ MAX_RANGE); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_ipmap), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_ipmap)); ++ return -ENOMEM; ++ } ++ map->first_ip = req->from; ++ map->last_ip = req->to; ++ map->netmask = req->netmask; ++ ++ if (req->netmask == 0xFFFFFFFF) { ++ map->hosts = 1; ++ map->sizeid = map->last_ip - map->first_ip + 1; ++ } else { ++ unsigned int mask_bits, netmask_bits; ++ ip_set_ip_t mask; ++ ++ map->first_ip &= map->netmask; /* Should we better bark? */ ++ ++ mask = range_to_mask(map->first_ip, map->last_ip, &mask_bits); ++ netmask_bits = mask_to_bits(map->netmask); ++ ++ if (!mask || netmask_bits <= mask_bits) ++ return -ENOEXEC; ++ ++ map->hosts = 2 << (32 - netmask_bits - 1); ++ map->sizeid = 2 << (netmask_bits - mask_bits - 1); ++ } ++ newbytes = bitmap_bytes(0, map->sizeid - 1); ++ map->members = kmalloc(newbytes, GFP_KERNEL); ++ if (!map->members) { ++ DP("out of memory for %d bytes", newbytes); ++ kfree(map); ++ return -ENOMEM; ++ } ++ memset(map->members, 0, newbytes); ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ kfree(map->members); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ memset(map->members, 0, bitmap_bytes(0, map->sizeid - 1)); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ struct ip_set_req_ipmap_create *header = ++ (struct ip_set_req_ipmap_create *) data; ++ ++ header->from = map->first_ip; ++ header->to = map->last_ip; ++ header->netmask = map->netmask; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ ++ return bitmap_bytes(0, map->sizeid - 1); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_ipmap *map = (struct ip_set_ipmap *) set->data; ++ int bytes = bitmap_bytes(0, map->sizeid - 1); ++ ++ memcpy(data, map->members, bytes); ++} ++ ++static struct ip_set_type ip_set_ipmap = { ++ .typename = SETTYPE_NAME, ++ .typecode = IPSET_TYPE_IP, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_ipmap), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_ipmap_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); ++MODULE_DESCRIPTION("ipmap type of IP sets"); ++ ++static int __init init(void) ++{ ++ return ip_set_register_set_type(&ip_set_ipmap); ++} ++ ++static void __exit fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_ipmap); ++} ++ ++module_init(init); ++module_exit(fini); +diff -Nur linux-2.6.16/net/ipv4/netfilter/ip_set_iptree.c linux-2.6.16-owrt/net/ipv4/netfilter/ip_set_iptree.c +--- linux-2.6.16/net/ipv4/netfilter/ip_set_iptree.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/net/ipv4/netfilter/ip_set_iptree.c 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,510 @@ ++/* Copyright (C) 2005 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing an IP set type: the iptree type */ ++ ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <linux/spinlock.h> ++ ++#include <linux/netfilter_ipv4/ip_set_iptree.h> ++ ++/* Garbage collection interval in seconds: */ ++#define IPTREE_GC_TIME 5*60 ++/* Sleep so many milliseconds before trying again ++ * to delete the gc timer at destroying a set */ ++#define IPTREE_DESTROY_SLEEP 100 ++ ++static kmem_cache_t *branch_cachep; ++static kmem_cache_t *leaf_cachep; ++ ++#define ABCD(a,b,c,d,addrp) do { \ ++ a = ((unsigned char *)addrp)[3]; \ ++ b = ((unsigned char *)addrp)[2]; \ ++ c = ((unsigned char *)addrp)[1]; \ ++ d = ((unsigned char *)addrp)[0]; \ ++} while (0) ++ ++#define TESTIP_WALK(map, elem, branch) do { \ ++ if ((map)->tree[elem]) { \ ++ branch = (map)->tree[elem]; \ ++ } else \ ++ return 0; \ ++} while (0) ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned char a,b,c,d; ++ ++ *hash_ip = ip; ++ ABCD(a, b, c, d, hash_ip); ++ DP("%u %u %u %u timeout %u", a, b, c, d, map->timeout); ++ TESTIP_WALK(map, a, btree); ++ TESTIP_WALK(btree, b, ctree); ++ TESTIP_WALK(ctree, c, dtree); ++ DP("%lu %lu", dtree->expires[d], jiffies); ++ return !!(map->timeout ? (time_after(dtree->expires[d], jiffies)) ++ : dtree->expires[d]); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iptree *req = ++ (struct ip_set_req_iptree *) data; ++ ++ if (size != sizeof(struct ip_set_req_iptree)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iptree), ++ size); ++ return -EINVAL; ++ } ++ return __testip(set, req->ip, hash_ip); ++} ++ ++static int ++testip_kernel(struct ip_set *set, ++ const struct sk_buff *skb, ++ u_int32_t flags, ++ ip_set_ip_t *hash_ip) ++{ ++ int res; ++ ++ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", ++ flags & IPSET_SRC ? "SRC" : "DST", ++ NIPQUAD(skb->nh.iph->saddr), ++ NIPQUAD(skb->nh.iph->daddr)); ++ ++ res = __testip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++ return (res < 0 ? 0 : res); ++} ++ ++#define ADDIP_WALK(map, elem, branch, type, cachep) do { \ ++ if ((map)->tree[elem]) { \ ++ DP("found %u", elem); \ ++ branch = (map)->tree[elem]; \ ++ } else { \ ++ branch = (type *) \ ++ kmem_cache_alloc(cachep, GFP_KERNEL); \ ++ if (branch == NULL) \ ++ return -ENOMEM; \ ++ memset(branch, 0, sizeof(*branch)); \ ++ (map)->tree[elem] = branch; \ ++ DP("alloc %u", elem); \ ++ } \ ++} while (0) ++ ++static inline int ++__addip(struct ip_set *set, ip_set_ip_t ip, unsigned int timeout, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned char a,b,c,d; ++ int ret = 0; ++ ++ *hash_ip = ip; ++ ABCD(a, b, c, d, hash_ip); ++ DP("%u %u %u %u timeout %u", a, b, c, d, timeout); ++ ADDIP_WALK(map, a, btree, struct ip_set_iptreeb, branch_cachep); ++ ADDIP_WALK(btree, b, ctree, struct ip_set_iptreec, branch_cachep); ++ ADDIP_WALK(ctree, c, dtree, struct ip_set_iptreed, leaf_cachep); ++ if (dtree->expires[d] ++ && (!map->timeout || time_after(dtree->expires[d], jiffies))) ++ ret = -EEXIST; ++ dtree->expires[d] = map->timeout ? (timeout * HZ + jiffies) : 1; ++ DP("%u %lu", d, dtree->expires[d]); ++ return ret; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_req_iptree *req = ++ (struct ip_set_req_iptree *) data; ++ ++ if (size != sizeof(struct ip_set_req_iptree)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iptree), ++ size); ++ return -EINVAL; ++ } ++ DP("%u.%u.%u.%u %u", HIPQUAD(req->ip), req->timeout); ++ return __addip(set, req->ip, ++ req->timeout ? req->timeout : map->timeout, ++ hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ ++ return __addip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ map->timeout, ++ hash_ip); ++} ++ ++#define DELIP_WALK(map, elem, branch) do { \ ++ if ((map)->tree[elem]) { \ ++ branch = (map)->tree[elem]; \ ++ } else \ ++ return -EEXIST; \ ++} while (0) ++ ++static inline int ++__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned char a,b,c,d; ++ ++ *hash_ip = ip; ++ ABCD(a, b, c, d, hash_ip); ++ DELIP_WALK(map, a, btree); ++ DELIP_WALK(btree, b, ctree); ++ DELIP_WALK(ctree, c, dtree); ++ ++ if (dtree->expires[d]) { ++ dtree->expires[d] = 0; ++ return 0; ++ } ++ return -EEXIST; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_iptree *req = ++ (struct ip_set_req_iptree *) data; ++ ++ if (size != sizeof(struct ip_set_req_iptree)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iptree), ++ size); ++ return -EINVAL; ++ } ++ return __delip(set, req->ip, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ return __delip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++} ++ ++#define LOOP_WALK_BEGIN(map, i, branch) \ ++ for (i = 0; i < 255; i++) { \ ++ if (!(map)->tree[i]) \ ++ continue; \ ++ branch = (map)->tree[i] ++ ++#define LOOP_WALK_END } ++ ++static void ip_tree_gc(unsigned long ul_set) ++{ ++ struct ip_set *set = (void *) ul_set; ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned char a,b,c,d; ++ unsigned char i,j,k; ++ ++ i = j = k = 0; ++ DP("gc: %s", set->name); ++ write_lock_bh(&set->lock); ++ LOOP_WALK_BEGIN(map, a, btree); ++ LOOP_WALK_BEGIN(btree, b, ctree); ++ LOOP_WALK_BEGIN(ctree, c, dtree); ++ for (d = 0; d < 255; d++) { ++ if (dtree->expires[d]) { ++ DP("gc: %u %u %u %u: expires %lu jiffies %lu", ++ a, b, c, d, ++ dtree->expires[d], jiffies); ++ if (map->timeout ++ && time_before(dtree->expires[d], jiffies)) ++ dtree->expires[d] = 0; ++ else ++ k = 1; ++ } ++ } ++ if (k == 0) { ++ DP("gc: %s: leaf %u %u %u empty", ++ set->name, a, b, c); ++ kmem_cache_free(leaf_cachep, dtree); ++ ctree->tree[c] = NULL; ++ } else { ++ DP("gc: %s: leaf %u %u %u not empty", ++ set->name, a, b, c); ++ j = 1; ++ k = 0; ++ } ++ LOOP_WALK_END; ++ if (j == 0) { ++ DP("gc: %s: branch %u %u empty", ++ set->name, a, b); ++ kmem_cache_free(branch_cachep, ctree); ++ btree->tree[b] = NULL; ++ } else { ++ DP("gc: %s: branch %u %u not empty", ++ set->name, a, b); ++ i = 1; ++ j = k = 0; ++ } ++ LOOP_WALK_END; ++ if (i == 0) { ++ DP("gc: %s: branch %u empty", ++ set->name, a); ++ kmem_cache_free(branch_cachep, btree); ++ map->tree[a] = NULL; ++ } else { ++ DP("gc: %s: branch %u not empty", ++ set->name, a); ++ i = j = k = 0; ++ } ++ LOOP_WALK_END; ++ write_unlock_bh(&set->lock); ++ ++ map->gc.expires = jiffies + map->gc_interval * HZ; ++ add_timer(&map->gc); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ struct ip_set_req_iptree_create *req = ++ (struct ip_set_req_iptree_create *) data; ++ struct ip_set_iptree *map; ++ ++ if (size != sizeof(struct ip_set_req_iptree_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_iptree_create), ++ size); ++ return -EINVAL; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_iptree), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_iptree)); ++ return -ENOMEM; ++ } ++ memset(map, 0, sizeof(*map)); ++ map->timeout = req->timeout; ++ set->data = map; ++ ++ /* If there is no timeout for the entries, ++ * we still have to call gc because delete ++ * do not clean up empty branches */ ++ map->gc_interval = IPTREE_GC_TIME; ++ init_timer(&map->gc); ++ map->gc.data = (unsigned long) set; ++ map->gc.function = ip_tree_gc; ++ map->gc.expires = jiffies + map->gc_interval * HZ; ++ add_timer(&map->gc); ++ ++ return 0; ++} ++ ++static void __flush(struct ip_set_iptree *map) ++{ ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned int a,b,c; ++ ++ LOOP_WALK_BEGIN(map, a, btree); ++ LOOP_WALK_BEGIN(btree, b, ctree); ++ LOOP_WALK_BEGIN(ctree, c, dtree); ++ kmem_cache_free(leaf_cachep, dtree); ++ LOOP_WALK_END; ++ kmem_cache_free(branch_cachep, ctree); ++ LOOP_WALK_END; ++ kmem_cache_free(branch_cachep, btree); ++ LOOP_WALK_END; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ ++ while (!del_timer(&map->gc)) ++ msleep(IPTREE_DESTROY_SLEEP); ++ __flush(map); ++ kfree(map); ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ unsigned int timeout = map->timeout; ++ ++ __flush(map); ++ memset(map, 0, sizeof(*map)); ++ map->timeout = timeout; ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_req_iptree_create *header = ++ (struct ip_set_req_iptree_create *) data; ++ ++ header->timeout = map->timeout; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned char a,b,c,d; ++ unsigned int count = 0; ++ ++ LOOP_WALK_BEGIN(map, a, btree); ++ LOOP_WALK_BEGIN(btree, b, ctree); ++ LOOP_WALK_BEGIN(ctree, c, dtree); ++ for (d = 0; d < 255; d++) { ++ if (dtree->expires[d] ++ && (!map->timeout || time_after(dtree->expires[d], jiffies))) ++ count++; ++ } ++ LOOP_WALK_END; ++ LOOP_WALK_END; ++ LOOP_WALK_END; ++ ++ DP("members %u", count); ++ return (count * sizeof(struct ip_set_req_iptree)); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_iptree *map = (struct ip_set_iptree *) set->data; ++ struct ip_set_iptreeb *btree; ++ struct ip_set_iptreec *ctree; ++ struct ip_set_iptreed *dtree; ++ unsigned char a,b,c,d; ++ size_t offset = 0; ++ struct ip_set_req_iptree *entry; ++ ++ LOOP_WALK_BEGIN(map, a, btree); ++ LOOP_WALK_BEGIN(btree, b, ctree); ++ LOOP_WALK_BEGIN(ctree, c, dtree); ++ for (d = 0; d < 255; d++) { ++ if (dtree->expires[d] ++ && (!map->timeout || time_after(dtree->expires[d], jiffies))) { ++ entry = (struct ip_set_req_iptree *)(data + offset); ++ entry->ip = ((a << 24) | (b << 16) | (c << 8) | d); ++ entry->timeout = !map->timeout ? 0 ++ : (dtree->expires[d] - jiffies)/HZ; ++ offset += sizeof(struct ip_set_req_iptree); ++ } ++ } ++ LOOP_WALK_END; ++ LOOP_WALK_END; ++ LOOP_WALK_END; ++} ++ ++static struct ip_set_type ip_set_iptree = { ++ .typename = SETTYPE_NAME, ++ .typecode = IPSET_TYPE_IP, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_iptree), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_iptree_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); ++MODULE_DESCRIPTION("iptree type of IP sets"); ++ ++static int __init init(void) ++{ ++ int ret; ++ ++ branch_cachep = kmem_cache_create("ip_set_iptreeb", ++ sizeof(struct ip_set_iptreeb), ++ 0, 0, NULL, NULL); ++ if (!branch_cachep) { ++ printk(KERN_ERR "Unable to create ip_set_iptreeb slab cache\n"); ++ ret = -ENOMEM; ++ goto out; ++ } ++ leaf_cachep = kmem_cache_create("ip_set_iptreed", ++ sizeof(struct ip_set_iptreed), ++ 0, 0, NULL, NULL); ++ if (!leaf_cachep) { ++ printk(KERN_ERR "Unable to create ip_set_iptreed slab cache\n"); ++ ret = -ENOMEM; ++ goto free_branch; ++ } ++ ret = ip_set_register_set_type(&ip_set_iptree); ++ if (ret == 0) ++ goto out; ++ ++ kmem_cache_destroy(leaf_cachep); ++ free_branch: ++ kmem_cache_destroy(branch_cachep); ++ out: ++ return ret; ++} ++ ++static void __exit fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_iptree); ++ kmem_cache_destroy(leaf_cachep); ++ kmem_cache_destroy(branch_cachep); ++} ++ ++module_init(init); ++module_exit(fini); +diff -Nur linux-2.6.16/net/ipv4/netfilter/ip_set_macipmap.c linux-2.6.16-owrt/net/ipv4/netfilter/ip_set_macipmap.c +--- linux-2.6.16/net/ipv4/netfilter/ip_set_macipmap.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/net/ipv4/netfilter/ip_set_macipmap.c 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,338 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> ++ * Patrick Schaaf <bof@bof.de> ++ * Martin Josefsson <gandalf@wlug.westbo.se> ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing an IP set type: the macipmap type */ ++ ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <linux/spinlock.h> ++#include <linux/if_ether.h> ++#include <linux/vmalloc.h> ++ ++#include <linux/netfilter_ipv4/ip_set_malloc.h> ++#include <linux/netfilter_ipv4/ip_set_macipmap.h> ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_macipmap *map = (struct ip_set_macipmap *) set->data; ++ struct ip_set_macip *table = (struct ip_set_macip *) map->members; ++ struct ip_set_req_macipmap *req = (struct ip_set_req_macipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_macipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_macipmap), ++ size); ++ return -EINVAL; ++ } ++ ++ if (req->ip < map->first_ip || req->ip > map->last_ip) ++ return -ERANGE; ++ ++ *hash_ip = req->ip; ++ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", ++ set->name, HIPQUAD(req->ip), HIPQUAD(*hash_ip)); ++ if (test_bit(IPSET_MACIP_ISSET, ++ (void *) &table[req->ip - map->first_ip].flags)) { ++ return (memcmp(req->ethernet, ++ &table[req->ip - map->first_ip].ethernet, ++ ETH_ALEN) == 0); ++ } else { ++ return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0); ++ } ++} ++ ++static int ++testip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ struct ip_set_macip *table = ++ (struct ip_set_macip *) map->members; ++ ip_set_ip_t ip; ++ ++ ip = ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr); ++ DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", ++ flags & IPSET_SRC ? "SRC" : "DST", ++ NIPQUAD(skb->nh.iph->saddr), ++ NIPQUAD(skb->nh.iph->daddr)); ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return 0; ++ ++ *hash_ip = ip; ++ DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", ++ set->name, HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ if (test_bit(IPSET_MACIP_ISSET, ++ (void *) &table[ip - map->first_ip].flags)) { ++ /* Is mac pointer valid? ++ * If so, compare... */ ++ return (skb->mac.raw >= skb->head ++ && (skb->mac.raw + ETH_HLEN) <= skb->data ++ && (memcmp(eth_hdr(skb)->h_source, ++ &table[ip - map->first_ip].ethernet, ++ ETH_ALEN) == 0)); ++ } else { ++ return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0); ++ } ++} ++ ++/* returns 0 on success */ ++static inline int ++__addip(struct ip_set *set, ++ ip_set_ip_t ip, unsigned char *ethernet, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ struct ip_set_macip *table = ++ (struct ip_set_macip *) map->members; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ if (test_and_set_bit(IPSET_MACIP_ISSET, ++ (void *) &table[ip - map->first_ip].flags)) ++ return -EEXIST; ++ ++ *hash_ip = ip; ++ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ memcpy(&table[ip - map->first_ip].ethernet, ethernet, ETH_ALEN); ++ return 0; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_macipmap *req = ++ (struct ip_set_req_macipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_macipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_macipmap), ++ size); ++ return -EINVAL; ++ } ++ return __addip(set, req->ip, req->ethernet, hash_ip); ++} ++ ++static int ++addip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ ip_set_ip_t ip; ++ ++ ip = ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr); ++ ++ if (!(skb->mac.raw >= skb->head ++ && (skb->mac.raw + ETH_HLEN) <= skb->data)) ++ return -EINVAL; ++ ++ return __addip(set, ip, eth_hdr(skb)->h_source, hash_ip); ++} ++ ++static inline int ++__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ struct ip_set_macip *table = ++ (struct ip_set_macip *) map->members; ++ ++ if (ip < map->first_ip || ip > map->last_ip) ++ return -ERANGE; ++ if (!test_and_clear_bit(IPSET_MACIP_ISSET, ++ (void *)&table[ip - map->first_ip].flags)) ++ return -EEXIST; ++ ++ *hash_ip = ip; ++ DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_macipmap *req = ++ (struct ip_set_req_macipmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_macipmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_macipmap), ++ size); ++ return -EINVAL; ++ } ++ return __delip(set, req->ip, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ return __delip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++} ++ ++static inline size_t members_size(ip_set_id_t from, ip_set_id_t to) ++{ ++ return (size_t)((to - from + 1) * sizeof(struct ip_set_macip)); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ int newbytes; ++ struct ip_set_req_macipmap_create *req = ++ (struct ip_set_req_macipmap_create *) data; ++ struct ip_set_macipmap *map; ++ ++ if (size != sizeof(struct ip_set_req_macipmap_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_macipmap_create), ++ size); ++ return -EINVAL; ++ } ++ ++ DP("from %u.%u.%u.%u to %u.%u.%u.%u", ++ HIPQUAD(req->from), HIPQUAD(req->to)); ++ ++ if (req->from > req->to) { ++ DP("bad ip range"); ++ return -ENOEXEC; ++ } ++ ++ if (req->to - req->from > MAX_RANGE) { ++ ip_set_printk("range too big (max %d addresses)", ++ MAX_RANGE); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_macipmap), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_macipmap)); ++ return -ENOMEM; ++ } ++ map->flags = req->flags; ++ map->first_ip = req->from; ++ map->last_ip = req->to; ++ newbytes = members_size(map->first_ip, map->last_ip); ++ map->members = ip_set_malloc(newbytes); ++ if (!map->members) { ++ DP("out of memory for %d bytes", newbytes); ++ kfree(map); ++ return -ENOMEM; ++ } ++ memset(map->members, 0, newbytes); ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ ++ ip_set_free(map->members, members_size(map->first_ip, map->last_ip)); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ memset(map->members, 0, members_size(map->first_ip, map->last_ip)); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ struct ip_set_req_macipmap_create *header = ++ (struct ip_set_req_macipmap_create *) data; ++ ++ DP("list_header %x %x %u", map->first_ip, map->last_ip, ++ map->flags); ++ ++ header->from = map->first_ip; ++ header->to = map->last_ip; ++ header->flags = map->flags; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ ++ return members_size(map->first_ip, map->last_ip); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_macipmap *map = ++ (struct ip_set_macipmap *) set->data; ++ ++ int bytes = members_size(map->first_ip, map->last_ip); ++ ++ memcpy(data, map->members, bytes); ++} ++ ++static struct ip_set_type ip_set_macipmap = { ++ .typename = SETTYPE_NAME, ++ .typecode = IPSET_TYPE_IP, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_macipmap), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_macipmap_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); ++MODULE_DESCRIPTION("macipmap type of IP sets"); ++ ++static int __init init(void) ++{ ++ init_max_malloc_size(); ++ return ip_set_register_set_type(&ip_set_macipmap); ++} ++ ++static void __exit fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_macipmap); ++} ++ ++module_init(init); ++module_exit(fini); +diff -Nur linux-2.6.16/net/ipv4/netfilter/ip_set_nethash.c linux-2.6.16-owrt/net/ipv4/netfilter/ip_set_nethash.c +--- linux-2.6.16/net/ipv4/netfilter/ip_set_nethash.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/net/ipv4/netfilter/ip_set_nethash.c 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,449 @@ ++/* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing a cidr nethash set */ ++ ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <linux/spinlock.h> ++#include <linux/vmalloc.h> ++#include <linux/random.h> ++ ++#include <net/ip.h> ++ ++#include <linux/netfilter_ipv4/ip_set_malloc.h> ++#include <linux/netfilter_ipv4/ip_set_nethash.h> ++#include <linux/netfilter_ipv4/ip_set_jhash.h> ++#include <linux/netfilter_ipv4/ip_set_prime.h> ++ ++static inline __u32 ++jhash_ip(const struct ip_set_nethash *map, ip_set_ip_t ip) ++{ ++ return jhash_1word(ip, map->initval); ++} ++ ++static inline __u32 ++randhash_ip(const struct ip_set_nethash *map, ip_set_ip_t ip) ++{ ++ return (1 + ip % map->prime); ++} ++ ++static inline __u32 ++hash_id_cidr(struct ip_set_nethash *map, ++ ip_set_ip_t ip, ++ unsigned char cidr, ++ ip_set_ip_t *hash_ip) ++{ ++ __u32 jhash, randhash, id; ++ u_int16_t i; ++ ++ *hash_ip = pack(ip, cidr); ++ jhash = jhash_ip(map, *hash_ip); ++ randhash = randhash_ip(map, *hash_ip); ++ ++ for (i = 0; i < map->probes; i++) { ++ id = (jhash + i * randhash) % map->hashsize; ++ DP("hash key: %u", id); ++ if (map->members[id] == *hash_ip) ++ return id; ++ } ++ return UINT_MAX; ++} ++ ++static inline __u32 ++hash_id(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ __u32 id = UINT_MAX; ++ int i; ++ ++ for (i = 0; i < 30 && map->cidr[i]; i++) { ++ id = hash_id_cidr(map, ip, map->cidr[i], hash_ip); ++ if (id != UINT_MAX) ++ break; ++ } ++ return id; ++} ++ ++static inline int ++__testip_cidr(struct ip_set *set, ip_set_ip_t ip, unsigned char cidr, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ ++ return (hash_id_cidr(map, ip, cidr, hash_ip) != UINT_MAX); ++} ++ ++static inline int ++__testip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) ++{ ++ return (hash_id(set, ip, hash_ip) != UINT_MAX); ++} ++ ++static int ++testip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_nethash *req = ++ (struct ip_set_req_nethash *) data; ++ ++ if (size != sizeof(struct ip_set_req_nethash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_nethash), ++ size); ++ return -EINVAL; ++ } ++ return (req->cidr == 32 ? __testip(set, req->ip, hash_ip) ++ : __testip_cidr(set, req->ip, req->cidr, hash_ip)); ++} ++ ++static int ++testip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ return __testip(set, ++ ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr), ++ hash_ip); ++} ++ ++static inline int ++__addip_base(struct ip_set_nethash *map, ip_set_ip_t ip) ++{ ++ __u32 jhash, randhash, probe; ++ u_int16_t i; ++ ++ jhash = jhash_ip(map, ip); ++ randhash = randhash_ip(map, ip); ++ ++ for (i = 0; i < map->probes; i++) { ++ probe = (jhash + i * randhash) % map->hashsize; ++ if (map->members[probe] == ip) ++ return -EEXIST; ++ if (!map->members[probe]) { ++ map->members[probe] = ip; ++ return 0; ++ } ++ } ++ /* Trigger rehashing */ ++ return -EAGAIN; ++} ++ ++static inline int ++__addip(struct ip_set_nethash *map, ip_set_ip_t ip, unsigned char cidr, ++ ip_set_ip_t *hash_ip) ++{ ++ *hash_ip = pack(ip, cidr); ++ DP("%u.%u.%u.%u/%u, %u.%u.%u.%u", HIPQUAD(ip), cidr, HIPQUAD(*hash_ip)); ++ ++ return __addip_base(map, *hash_ip); ++} ++ ++static void ++update_cidr_sizes(struct ip_set_nethash *map, unsigned char cidr) ++{ ++ unsigned char next; ++ int i; ++ ++ for (i = 0; i < 30 && map->cidr[i]; i++) { ++ if (map->cidr[i] == cidr) { ++ return; ++ } else if (map->cidr[i] < cidr) { ++ next = map->cidr[i]; ++ map->cidr[i] = cidr; ++ cidr = next; ++ } ++ } ++ if (i < 30) ++ map->cidr[i] = cidr; ++} ++ ++static int ++addip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_nethash *req = ++ (struct ip_set_req_nethash *) data; ++ int ret; ++ ++ if (size != sizeof(struct ip_set_req_nethash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_nethash), ++ size); ++ return -EINVAL; ++ } ++ ret = __addip((struct ip_set_nethash *) set->data, ++ req->ip, req->cidr, hash_ip); ++ ++ if (ret == 0) ++ update_cidr_sizes((struct ip_set_nethash *) set->data, ++ req->cidr); ++ ++ return ret; ++} ++ ++static int ++addip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ int ret = -ERANGE; ++ ip_set_ip_t ip = ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr); ++ ++ if (map->cidr[0]) ++ ret = __addip(map, ip, map->cidr[0], hash_ip); ++ ++ return ret; ++} ++ ++static int retry(struct ip_set *set) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ ip_set_ip_t *members; ++ u_int32_t i, hashsize; ++ unsigned newbytes; ++ int res; ++ struct ip_set_nethash tmp = { ++ .hashsize = map->hashsize, ++ .probes = map->probes, ++ .resize = map->resize ++ }; ++ ++ if (map->resize == 0) ++ return -ERANGE; ++ ++ memcpy(tmp.cidr, map->cidr, 30 * sizeof(unsigned char)); ++ again: ++ res = 0; ++ ++ /* Calculate new parameters */ ++ get_random_bytes(&tmp.initval, 4); ++ hashsize = tmp.hashsize + (tmp.hashsize * map->resize)/100; ++ if (hashsize == tmp.hashsize) ++ hashsize++; ++ tmp.prime = make_prime(hashsize); ++ ++ ip_set_printk("rehashing of set %s triggered: " ++ "hashsize grows from %u to %u", ++ set->name, tmp.hashsize, hashsize); ++ tmp.hashsize = hashsize; ++ ++ newbytes = hashsize * sizeof(ip_set_ip_t); ++ tmp.members = ip_set_malloc_atomic(newbytes); ++ if (!tmp.members) { ++ DP("out of memory for %d bytes", newbytes); ++ return -ENOMEM; ++ } ++ memset(tmp.members, 0, newbytes); ++ ++ write_lock_bh(&set->lock); ++ map = (struct ip_set_nethash *) set->data; /* Play safe */ ++ for (i = 0; i < map->hashsize && res == 0; i++) { ++ if (map->members[i]) ++ res = __addip_base(&tmp, map->members[i]); ++ } ++ if (res) { ++ /* Failure, try again */ ++ write_unlock_bh(&set->lock); ++ ip_set_free(tmp.members, newbytes); ++ goto again; ++ } ++ ++ /* Success at resizing! */ ++ members = map->members; ++ hashsize = map->hashsize; ++ ++ map->initval = tmp.initval; ++ map->prime = tmp.prime; ++ map->hashsize = tmp.hashsize; ++ map->members = tmp.members; ++ write_unlock_bh(&set->lock); ++ ++ ip_set_free(members, hashsize * sizeof(ip_set_ip_t)); ++ ++ return 0; ++} ++ ++static inline int ++__delip(struct ip_set_nethash *map, ip_set_ip_t ip, unsigned char cidr, ++ ip_set_ip_t *hash_ip) ++{ ++ ip_set_ip_t id = hash_id_cidr(map, ip, cidr, hash_ip); ++ ++ if (id == UINT_MAX) ++ return -EEXIST; ++ ++ map->members[id] = 0; ++ return 0; ++} ++ ++static int ++delip(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_req_nethash *req = ++ (struct ip_set_req_nethash *) data; ++ ++ if (size != sizeof(struct ip_set_req_nethash)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_nethash), ++ size); ++ return -EINVAL; ++ } ++ /* TODO: no garbage collection in map->cidr */ ++ return __delip((struct ip_set_nethash *) set->data, ++ req->ip, req->cidr, hash_ip); ++} ++ ++static int ++delip_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_ip) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ int ret = -ERANGE; ++ ip_set_ip_t ip = ntohl(flags & IPSET_SRC ? skb->nh.iph->saddr ++ : skb->nh.iph->daddr); ++ ++ if (map->cidr[0]) ++ ret = __delip(map, ip, map->cidr[0], hash_ip); ++ ++ return ret; ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ unsigned newbytes; ++ struct ip_set_req_nethash_create *req = ++ (struct ip_set_req_nethash_create *) data; ++ struct ip_set_nethash *map; ++ ++ if (size != sizeof(struct ip_set_req_nethash_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_nethash_create), ++ size); ++ return -EINVAL; ++ } ++ ++ if (req->hashsize < 1) { ++ ip_set_printk("hashsize too small"); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_nethash), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_nethash)); ++ return -ENOMEM; ++ } ++ get_random_bytes(&map->initval, 4); ++ map->prime = make_prime(req->hashsize); ++ map->hashsize = req->hashsize; ++ map->probes = req->probes; ++ map->resize = req->resize; ++ memset(map->cidr, 0, 30 * sizeof(unsigned char)); ++ newbytes = map->hashsize * sizeof(ip_set_ip_t); ++ map->members = ip_set_malloc(newbytes); ++ if (!map->members) { ++ DP("out of memory for %d bytes", newbytes); ++ kfree(map); ++ return -ENOMEM; ++ } ++ memset(map->members, 0, newbytes); ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ ++ ip_set_free(map->members, map->hashsize * sizeof(ip_set_ip_t)); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ memset(map->members, 0, map->hashsize * sizeof(ip_set_ip_t)); ++ memset(map->cidr, 0, 30 * sizeof(unsigned char)); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ struct ip_set_req_nethash_create *header = ++ (struct ip_set_req_nethash_create *) data; ++ ++ header->hashsize = map->hashsize; ++ header->probes = map->probes; ++ header->resize = map->resize; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ ++ return (map->hashsize * sizeof(ip_set_ip_t)); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_nethash *map = (struct ip_set_nethash *) set->data; ++ int bytes = map->hashsize * sizeof(ip_set_ip_t); ++ ++ memcpy(data, map->members, bytes); ++} ++ ++static struct ip_set_type ip_set_nethash = { ++ .typename = SETTYPE_NAME, ++ .typecode = IPSET_TYPE_IP, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_nethash), ++ .addip = &addip, ++ .addip_kernel = &addip_kernel, ++ .retry = &retry, ++ .delip = &delip, ++ .delip_kernel = &delip_kernel, ++ .testip = &testip, ++ .testip_kernel = &testip_kernel, ++ .header_size = sizeof(struct ip_set_req_nethash_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); ++MODULE_DESCRIPTION("nethash type of IP sets"); ++ ++static int __init init(void) ++{ ++ return ip_set_register_set_type(&ip_set_nethash); ++} ++ ++static void __exit fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_nethash); ++} ++ ++module_init(init); ++module_exit(fini); +diff -Nur linux-2.6.16/net/ipv4/netfilter/ip_set_portmap.c linux-2.6.16-owrt/net/ipv4/netfilter/ip_set_portmap.c +--- linux-2.6.16/net/ipv4/netfilter/ip_set_portmap.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/net/ipv4/netfilter/ip_set_portmap.c 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,325 @@ ++/* Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module implementing a port set type as a bitmap */ ++ ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/tcp.h> ++#include <linux/udp.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/errno.h> ++#include <asm/uaccess.h> ++#include <asm/bitops.h> ++#include <linux/spinlock.h> ++ ++#include <net/ip.h> ++ ++#include <linux/netfilter_ipv4/ip_set_portmap.h> ++ ++/* We must handle non-linear skbs */ ++static inline ip_set_ip_t ++get_port(const struct sk_buff *skb, u_int32_t flags) ++{ ++ struct iphdr *iph = skb->nh.iph; ++ u_int16_t offset = ntohs(iph->frag_off) & IP_OFFSET; ++ ++ switch (iph->protocol) { ++ case IPPROTO_TCP: { ++ struct tcphdr tcph; ++ ++ /* See comments at tcp_match in ip_tables.c */ ++ if (offset) ++ return INVALID_PORT; ++ ++ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) < 0) ++ /* No choice either */ ++ return INVALID_PORT; ++ ++ return ntohs(flags & IPSET_SRC ? ++ tcph.source : tcph.dest); ++ } ++ case IPPROTO_UDP: { ++ struct udphdr udph; ++ ++ if (offset) ++ return INVALID_PORT; ++ ++ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &udph, sizeof(udph)) < 0) ++ /* No choice either */ ++ return INVALID_PORT; ++ ++ return ntohs(flags & IPSET_SRC ? ++ udph.source : udph.dest); ++ } ++ default: ++ return INVALID_PORT; ++ } ++} ++ ++static inline int ++__testport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ if (port < map->first_port || port > map->last_port) ++ return -ERANGE; ++ ++ *hash_port = port; ++ DP("set: %s, port:%u, %u", set->name, port, *hash_port); ++ return !!test_bit(port - map->first_port, map->members); ++} ++ ++static int ++testport(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_port) ++{ ++ struct ip_set_req_portmap *req = ++ (struct ip_set_req_portmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_portmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_portmap), ++ size); ++ return -EINVAL; ++ } ++ return __testport(set, req->port, hash_port); ++} ++ ++static int ++testport_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_port) ++{ ++ int res; ++ ip_set_ip_t port = get_port(skb, flags); ++ ++ DP("flag %s port %u", flags & IPSET_SRC ? "SRC" : "DST", port); ++ if (port == INVALID_PORT) ++ return 0; ++ ++ res = __testport(set, port, hash_port); ++ ++ return (res < 0 ? 0 : res); ++} ++ ++static inline int ++__addport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ if (port < map->first_port || port > map->last_port) ++ return -ERANGE; ++ if (test_and_set_bit(port - map->first_port, map->members)) ++ return -EEXIST; ++ ++ *hash_port = port; ++ DP("port %u", port); ++ return 0; ++} ++ ++static int ++addport(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_port) ++{ ++ struct ip_set_req_portmap *req = ++ (struct ip_set_req_portmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_portmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_portmap), ++ size); ++ return -EINVAL; ++ } ++ return __addport(set, req->port, hash_port); ++} ++ ++static int ++addport_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_port) ++{ ++ ip_set_ip_t port = get_port(skb, flags); ++ ++ if (port == INVALID_PORT) ++ return -EINVAL; ++ ++ return __addport(set, port, hash_port); ++} ++ ++static inline int ++__delport(struct ip_set *set, ip_set_ip_t port, ip_set_ip_t *hash_port) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ if (port < map->first_port || port > map->last_port) ++ return -ERANGE; ++ if (!test_and_clear_bit(port - map->first_port, map->members)) ++ return -EEXIST; ++ ++ *hash_port = port; ++ DP("port %u", port); ++ return 0; ++} ++ ++static int ++delport(struct ip_set *set, const void *data, size_t size, ++ ip_set_ip_t *hash_port) ++{ ++ struct ip_set_req_portmap *req = ++ (struct ip_set_req_portmap *) data; ++ ++ if (size != sizeof(struct ip_set_req_portmap)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_portmap), ++ size); ++ return -EINVAL; ++ } ++ return __delport(set, req->port, hash_port); ++} ++ ++static int ++delport_kernel(struct ip_set *set, const struct sk_buff *skb, ++ u_int32_t flags, ip_set_ip_t *hash_port) ++{ ++ ip_set_ip_t port = get_port(skb, flags); ++ ++ if (port == INVALID_PORT) ++ return -EINVAL; ++ ++ return __delport(set, port, hash_port); ++} ++ ++static int create(struct ip_set *set, const void *data, size_t size) ++{ ++ int newbytes; ++ struct ip_set_req_portmap_create *req = ++ (struct ip_set_req_portmap_create *) data; ++ struct ip_set_portmap *map; ++ ++ if (size != sizeof(struct ip_set_req_portmap_create)) { ++ ip_set_printk("data length wrong (want %zu, have %zu)", ++ sizeof(struct ip_set_req_portmap_create), ++ size); ++ return -EINVAL; ++ } ++ ++ DP("from %u to %u", req->from, req->to); ++ ++ if (req->from > req->to) { ++ DP("bad port range"); ++ return -ENOEXEC; ++ } ++ ++ if (req->to - req->from > MAX_RANGE) { ++ ip_set_printk("range too big (max %d ports)", ++ MAX_RANGE); ++ return -ENOEXEC; ++ } ++ ++ map = kmalloc(sizeof(struct ip_set_portmap), GFP_KERNEL); ++ if (!map) { ++ DP("out of memory for %d bytes", ++ sizeof(struct ip_set_portmap)); ++ return -ENOMEM; ++ } ++ map->first_port = req->from; ++ map->last_port = req->to; ++ newbytes = bitmap_bytes(req->from, req->to); ++ map->members = kmalloc(newbytes, GFP_KERNEL); ++ if (!map->members) { ++ DP("out of memory for %d bytes", newbytes); ++ kfree(map); ++ return -ENOMEM; ++ } ++ memset(map->members, 0, newbytes); ++ ++ set->data = map; ++ return 0; ++} ++ ++static void destroy(struct ip_set *set) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ kfree(map->members); ++ kfree(map); ++ ++ set->data = NULL; ++} ++ ++static void flush(struct ip_set *set) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ memset(map->members, 0, bitmap_bytes(map->first_port, map->last_port)); ++} ++ ++static void list_header(const struct ip_set *set, void *data) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ struct ip_set_req_portmap_create *header = ++ (struct ip_set_req_portmap_create *) data; ++ ++ DP("list_header %u %u", map->first_port, map->last_port); ++ ++ header->from = map->first_port; ++ header->to = map->last_port; ++} ++ ++static int list_members_size(const struct ip_set *set) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ ++ return bitmap_bytes(map->first_port, map->last_port); ++} ++ ++static void list_members(const struct ip_set *set, void *data) ++{ ++ struct ip_set_portmap *map = (struct ip_set_portmap *) set->data; ++ int bytes = bitmap_bytes(map->first_port, map->last_port); ++ ++ memcpy(data, map->members, bytes); ++} ++ ++static struct ip_set_type ip_set_portmap = { ++ .typename = SETTYPE_NAME, ++ .typecode = IPSET_TYPE_PORT, ++ .protocol_version = IP_SET_PROTOCOL_VERSION, ++ .create = &create, ++ .destroy = &destroy, ++ .flush = &flush, ++ .reqsize = sizeof(struct ip_set_req_portmap), ++ .addip = &addport, ++ .addip_kernel = &addport_kernel, ++ .delip = &delport, ++ .delip_kernel = &delport_kernel, ++ .testip = &testport, ++ .testip_kernel = &testport_kernel, ++ .header_size = sizeof(struct ip_set_req_portmap_create), ++ .list_header = &list_header, ++ .list_members_size = &list_members_size, ++ .list_members = &list_members, ++ .me = THIS_MODULE, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); ++MODULE_DESCRIPTION("portmap type of IP sets"); ++ ++static int __init init(void) ++{ ++ return ip_set_register_set_type(&ip_set_portmap); ++} ++ ++static void __exit fini(void) ++{ ++ /* FIXME: possible race with ip_set_create() */ ++ ip_set_unregister_set_type(&ip_set_portmap); ++} ++ ++module_init(init); ++module_exit(fini); +diff -Nur linux-2.6.16/net/ipv4/netfilter/ipt_set.c linux-2.6.16-owrt/net/ipv4/netfilter/ipt_set.c +--- linux-2.6.16/net/ipv4/netfilter/ipt_set.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/net/ipv4/netfilter/ipt_set.c 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,112 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> ++ * Patrick Schaaf <bof@bof.de> ++ * Martin Josefsson <gandalf@wlug.westbo.se> ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* Kernel module to match an IP set. */ ++ ++#include <linux/module.h> ++#include <linux/ip.h> ++#include <linux/skbuff.h> ++ ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_set.h> ++#include <linux/netfilter_ipv4/ipt_set.h> ++ ++static inline int ++match_set(const struct ipt_set_info *info, ++ const struct sk_buff *skb, ++ int inv) ++{ ++ if (ip_set_testip_kernel(info->index, skb, info->flags)) ++ inv = !inv; ++ return inv; ++} ++ ++static int ++match(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *matchinfo, ++ int offset, ++ int *hotdrop) ++{ ++ const struct ipt_set_info_match *info = matchinfo; ++ ++ return match_set(&info->match_set, ++ skb, ++ info->match_set.flags[0] & IPSET_MATCH_INV); ++} ++ ++static int ++checkentry(const char *tablename, ++ const struct ipt_ip *ip, ++ void *matchinfo, ++ unsigned int matchsize, ++ unsigned int hook_mask) ++{ ++ struct ipt_set_info_match *info = ++ (struct ipt_set_info_match *) matchinfo; ++ ip_set_id_t index; ++ ++ if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) { ++ ip_set_printk("invalid matchsize %d", matchsize); ++ return 0; ++ } ++ ++ index = ip_set_get_byindex(info->match_set.index); ++ ++ if (index == IP_SET_INVALID_ID) { ++ ip_set_printk("Cannot find set indentified by id %u to match", ++ info->match_set.index); ++ return 0; /* error */ ++ } ++ if (info->match_set.flags[IP_SET_MAX_BINDINGS] != 0) { ++ ip_set_printk("That's nasty!"); ++ return 0; /* error */ ++ } ++ ++ return 1; ++} ++ ++static void destroy(void *matchinfo, unsigned int matchsize) ++{ ++ struct ipt_set_info_match *info = matchinfo; ++ ++ if (matchsize != IPT_ALIGN(sizeof(struct ipt_set_info_match))) { ++ ip_set_printk("invalid matchsize %d", matchsize); ++ return; ++ } ++ ++ ip_set_put(info->match_set.index); ++} ++ ++static struct ipt_match set_match = { ++ .name = "set", ++ .match = &match, ++ .checkentry = &checkentry, ++ .destroy = &destroy, ++ .me = THIS_MODULE ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); ++MODULE_DESCRIPTION("iptables IP set match module"); ++ ++static int __init init(void) ++{ ++ return ipt_register_match(&set_match); ++} ++ ++static void __exit fini(void) ++{ ++ ipt_unregister_match(&set_match); ++} ++ ++module_init(init); ++module_exit(fini); +diff -Nur linux-2.6.16/net/ipv4/netfilter/ipt_SET.c linux-2.6.16-owrt/net/ipv4/netfilter/ipt_SET.c +--- linux-2.6.16/net/ipv4/netfilter/ipt_SET.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/net/ipv4/netfilter/ipt_SET.c 2006-03-20 12:53:59.000000000 +0100 +@@ -0,0 +1,128 @@ ++/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> ++ * Patrick Schaaf <bof@bof.de> ++ * Martin Josefsson <gandalf@wlug.westbo.se> ++ * Copyright (C) 2003-2004 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ipt_SET.c - netfilter target to manipulate IP sets */ ++ ++#include <linux/types.h> ++#include <linux/ip.h> ++#include <linux/timer.h> ++#include <linux/module.h> ++#include <linux/netfilter.h> ++#include <linux/netdevice.h> ++#include <linux/if.h> ++#include <linux/inetdevice.h> ++#include <net/protocol.h> ++#include <net/checksum.h> ++#include <linux/netfilter_ipv4.h> ++#include <linux/netfilter_ipv4/ip_nat_rule.h> ++#include <linux/netfilter_ipv4/ipt_set.h> ++ ++static unsigned int ++target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++ const void *targinfo, ++ void *userinfo) ++{ ++ const struct ipt_set_info_target *info = targinfo; ++ ++ if (info->add_set.index != IP_SET_INVALID_ID) ++ ip_set_addip_kernel(info->add_set.index, ++ *pskb, ++ info->add_set.flags); ++ if (info->del_set.index != IP_SET_INVALID_ID) ++ ip_set_delip_kernel(info->del_set.index, ++ *pskb, ++ info->del_set.flags); ++ ++ return IPT_CONTINUE; ++} ++ ++static int ++checkentry(const char *tablename, ++ const struct ipt_entry *e, ++ void *targinfo, ++ unsigned int targinfosize, unsigned int hook_mask) ++{ ++ struct ipt_set_info_target *info = ++ (struct ipt_set_info_target *) targinfo; ++ ip_set_id_t index; ++ ++ if (targinfosize != IPT_ALIGN(sizeof(*info))) { ++ DP("bad target info size %u", targinfosize); ++ return 0; ++ } ++ ++ if (info->add_set.index != IP_SET_INVALID_ID) { ++ index = ip_set_get_byindex(info->add_set.index); ++ if (index == IP_SET_INVALID_ID) { ++ ip_set_printk("cannot find add_set index %u as target", ++ info->add_set.index); ++ return 0; /* error */ ++ } ++ } ++ ++ if (info->del_set.index != IP_SET_INVALID_ID) { ++ index = ip_set_get_byindex(info->del_set.index); ++ if (index == IP_SET_INVALID_ID) { ++ ip_set_printk("cannot find del_set index %u as target", ++ info->del_set.index); ++ return 0; /* error */ ++ } ++ } ++ if (info->add_set.flags[IP_SET_MAX_BINDINGS] != 0 ++ || info->del_set.flags[IP_SET_MAX_BINDINGS] != 0) { ++ ip_set_printk("That's nasty!"); ++ return 0; /* error */ ++ } ++ ++ return 1; ++} ++ ++static void destroy(void *targetinfo, unsigned int targetsize) ++{ ++ struct ipt_set_info_target *info = targetinfo; ++ ++ if (targetsize != IPT_ALIGN(sizeof(struct ipt_set_info_target))) { ++ ip_set_printk("invalid targetsize %d", targetsize); ++ return; ++ } ++ ++ if (info->add_set.index != IP_SET_INVALID_ID) ++ ip_set_put(info->add_set.index); ++ if (info->del_set.index != IP_SET_INVALID_ID) ++ ip_set_put(info->del_set.index); ++} ++ ++static struct ipt_target SET_target = { ++ .name = "SET", ++ .target = target, ++ .checkentry = checkentry, ++ .destroy = destroy, ++ .me = THIS_MODULE ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); ++MODULE_DESCRIPTION("iptables IP set target module"); ++ ++static int __init init(void) ++{ ++ return ipt_register_target(&SET_target); ++} ++ ++static void __exit fini(void) ++{ ++ ipt_unregister_target(&SET_target); ++} ++ ++module_init(init); ++module_exit(fini); +diff -Nur linux-2.6.16/net/ipv4/netfilter/Kconfig linux-2.6.16-owrt/net/ipv4/netfilter/Kconfig +--- linux-2.6.16/net/ipv4/netfilter/Kconfig 2006-03-20 12:52:42.000000000 +0100 ++++ linux-2.6.16-owrt/net/ipv4/netfilter/Kconfig 2006-03-20 12:53:59.000000000 +0100 +@@ -623,5 +623,106 @@ + Allows altering the ARP packet payload: source and destination + hardware and network addresses. + ++config IP_NF_SET ++ tristate "IP set support" ++ depends on INET && NETFILTER ++ help ++ This option adds IP set support to the kernel. ++ In order to define and use sets, you need the userspace utility ++ ipset(8). ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_MAX ++ int "Maximum number of IP sets" ++ default 256 ++ range 2 65534 ++ depends on IP_NF_SET ++ help ++ You can define here default value of the maximum number ++ of IP sets for the kernel. ++ ++ The value can be overriden by the 'max_sets' module ++ parameter of the 'ip_set' module. ++ ++config IP_NF_SET_HASHSIZE ++ int "Hash size for bindings of IP sets" ++ default 1024 ++ depends on IP_NF_SET ++ help ++ You can define here default value of the hash size for ++ bindings of IP sets. ++ ++ The value can be overriden by the 'hash_size' module ++ parameter of the 'ip_set' module. ++ ++config IP_NF_SET_IPMAP ++ tristate "ipmap set support" ++ depends on IP_NF_SET ++ help ++ This option adds the ipmap set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_MACIPMAP ++ tristate "macipmap set support" ++ depends on IP_NF_SET ++ help ++ This option adds the macipmap set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_PORTMAP ++ tristate "portmap set support" ++ depends on IP_NF_SET ++ help ++ This option adds the portmap set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_IPHASH ++ tristate "iphash set support" ++ depends on IP_NF_SET ++ help ++ This option adds the iphash set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_NETHASH ++ tristate "nethash set support" ++ depends on IP_NF_SET ++ help ++ This option adds the nethash set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_SET_IPTREE ++ tristate "iptree set support" ++ depends on IP_NF_SET ++ help ++ This option adds the iptree set type support. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_MATCH_SET ++ tristate "set match support" ++ depends on IP_NF_SET ++ help ++ Set matching matches against given IP sets. ++ You need the ipset utility to create and set up the sets. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++config IP_NF_TARGET_SET ++ tristate "SET target support" ++ depends on IP_NF_SET ++ help ++ The SET target makes possible to add/delete entries ++ in IP sets. ++ You need the ipset utility to create and set up the sets. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ ++ + endmenu + +diff -Nur linux-2.6.16/net/ipv4/netfilter/Makefile linux-2.6.16-owrt/net/ipv4/netfilter/Makefile +--- linux-2.6.16/net/ipv4/netfilter/Makefile 2006-03-20 12:52:42.000000000 +0100 ++++ linux-2.6.16-owrt/net/ipv4/netfilter/Makefile 2006-03-20 12:56:30.000000000 +0100 +@@ -47,6 +47,7 @@ + + # matches + obj-$(CONFIG_IP_NF_MATCH_HASHLIMIT) += ipt_hashlimit.o ++obj-$(CONFIG_IP_NF_MATCH_SET) += ipt_set.o + obj-$(CONFIG_IP_NF_MATCH_IPRANGE) += ipt_iprange.o + obj-$(CONFIG_IP_NF_MATCH_MULTIPORT) += ipt_multiport.o + obj-$(CONFIG_IP_NF_MATCH_OWNER) += ipt_owner.o +@@ -74,6 +75,17 @@ + obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o + obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o + obj-$(CONFIG_IP_NF_TARGET_TCPMSS) += ipt_TCPMSS.o ++obj-$(CONFIG_IP_NF_TARGET_SET) += ipt_SET.o ++ ++# sets ++obj-$(CONFIG_IP_NF_SET) += ip_set.o ++obj-$(CONFIG_IP_NF_SET_IPMAP) += ip_set_ipmap.o ++obj-$(CONFIG_IP_NF_SET_PORTMAP) += ip_set_portmap.o ++obj-$(CONFIG_IP_NF_SET_MACIPMAP) += ip_set_macipmap.o ++obj-$(CONFIG_IP_NF_SET_IPHASH) += ip_set_iphash.o ++obj-$(CONFIG_IP_NF_SET_NETHASH) += ip_set_nethash.o ++obj-$(CONFIG_IP_NF_SET_IPTREE) += ip_set_iptree.o ++ + obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o + obj-$(CONFIG_IP_NF_TARGET_TTL) += ipt_TTL.o + diff --git a/target/linux/generic-2.6/patches/105-netfilter_time.patch b/target/linux/generic-2.6/patches/105-netfilter_time.patch new file mode 100644 index 0000000..34257ee --- /dev/null +++ b/target/linux/generic-2.6/patches/105-netfilter_time.patch @@ -0,0 +1,241 @@ +diff -urN linux-2.6.15-rc6.orig/net/ipv4/netfilter/ipt_time.c linux-2.6.15-rc6/net/ipv4/netfilter/ipt_time.c +--- linux-2.6.15-rc6.orig/net/ipv4/netfilter/ipt_time.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.15-rc6/net/ipv4/netfilter/ipt_time.c 2006-01-07 13:02:59.000000000 +0100 +@@ -0,0 +1,178 @@ ++/* ++ This is a module which is used for time matching ++ It is using some modified code from dietlibc (localtime() function) ++ that you can find at http://www.fefe.de/dietlibc/ ++ This file is distributed under the terms of the GNU General Public ++ License (GPL). Copies of the GPL can be obtained from: ftp://prep.ai.mit.edu/pub/gnu/GPL ++ 2001-05-04 Fabrice MARIE <fabrice@netfilter.org> : initial development. ++ 2001-21-05 Fabrice MARIE <fabrice@netfilter.org> : bug fix in the match code, ++ thanks to "Zeng Yu" <zengy@capitel.com.cn> for bug report. ++ 2001-26-09 Fabrice MARIE <fabrice@netfilter.org> : force the match to be in LOCAL_IN or PRE_ROUTING only. ++ 2001-30-11 Fabrice : added the possibility to use the match in FORWARD/OUTPUT with a little hack, ++ added Nguyen Dang Phuoc Dong <dongnd@tlnet.com.vn> patch to support timezones. ++ 2004-05-02 Fabrice : added support for date matching, from an idea of Fabien COELHO. ++*/ ++ ++#include <linux/module.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ipt_time.h> ++#include <linux/time.h> ++ ++MODULE_AUTHOR("Fabrice MARIE <fabrice@netfilter.org>"); ++MODULE_DESCRIPTION("Match arrival timestamp/date"); ++MODULE_LICENSE("GPL"); ++ ++struct tm ++{ ++ int tm_sec; /* Seconds. [0-60] (1 leap second) */ ++ int tm_min; /* Minutes. [0-59] */ ++ int tm_hour; /* Hours. [0-23] */ ++ int tm_mday; /* Day. [1-31] */ ++ int tm_mon; /* Month. [0-11] */ ++ int tm_year; /* Year - 1900. */ ++ int tm_wday; /* Day of week. [0-6] */ ++ int tm_yday; /* Days in year.[0-365] */ ++ int tm_isdst; /* DST. [-1/0/1]*/ ++ ++ long int tm_gmtoff; /* we don't care, we count from GMT */ ++ const char *tm_zone; /* we don't care, we count from GMT */ ++}; ++ ++void ++localtime(const u32 time, struct tm *r); ++ ++static int ++match(const struct sk_buff *skb, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *matchinfo, ++ int offset, ++ int *hotdrop) ++{ ++ const struct ipt_time_info *info = matchinfo; /* match info for rule */ ++ struct tm currenttime; /* time human readable */ ++ u_int8_t days_of_week[7] = {64, 32, 16, 8, 4, 2, 1}; ++ u_int16_t packet_time; ++ ++ /* We might not have a timestamp, get one */ ++ if (skb->tstamp.off_sec == 0) ++ __net_timestamp((struct sk_buff *)skb); ++ ++ /* First we make sure we are in the date start-stop boundaries */ ++ if ((skb->tstamp.off_sec < info->date_start) || (skb->tstamp.off_sec > info->date_stop)) ++ return 0; /* We are outside the date boundaries */ ++ ++ /* Transform the timestamp of the packet, in a human readable form */ ++ localtime(skb->tstamp.off_sec, ¤ttime); ++ ++ /* check if we match this timestamp, we start by the days... */ ++ if ((days_of_week[currenttime.tm_wday] & info->days_match) != days_of_week[currenttime.tm_wday]) ++ return 0; /* the day doesn't match */ ++ ++ /* ... check the time now */ ++ packet_time = (currenttime.tm_hour * 60) + currenttime.tm_min; ++ if ((packet_time < info->time_start) || (packet_time > info->time_stop)) ++ return 0; ++ ++ /* here we match ! */ ++ return 1; ++} ++ ++static int ++checkentry(const char *tablename, ++ const struct ipt_ip *ip, ++ void *matchinfo, ++ unsigned int matchsize, ++ unsigned int hook_mask) ++{ ++ struct ipt_time_info *info = matchinfo; /* match info for rule */ ++ ++ /* First, check that we are in the correct hooks */ ++ if (hook_mask ++ & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT))) ++ { ++ printk("ipt_time: error, only valid for PRE_ROUTING, LOCAL_IN, FORWARD and OUTPUT)\n"); ++ return 0; ++ } ++ ++ /* Check the size */ ++ if (matchsize != IPT_ALIGN(sizeof(struct ipt_time_info))) ++ return 0; ++ /* Now check the coherence of the data ... */ ++ if ((info->time_start > 1439) || /* 23*60+59 = 1439*/ ++ (info->time_stop > 1439)) ++ { ++ printk(KERN_WARNING "ipt_time: invalid argument\n"); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static struct ipt_match time_match = { ++ .name = "time", ++ .match = &match, ++ .checkentry = &checkentry, ++ .me = THIS_MODULE ++}; ++ ++static int __init init(void) ++{ ++ printk("ipt_time loading\n"); ++ return ipt_register_match(&time_match); ++} ++ ++static void __exit fini(void) ++{ ++ ipt_unregister_match(&time_match); ++ printk("ipt_time unloaded\n"); ++} ++ ++module_init(init); ++module_exit(fini); ++ ++ ++/* The part below is borowed and modified from dietlibc */ ++ ++/* seconds per day */ ++#define SPD 24*60*60 ++ ++void ++localtime(const u32 time, struct tm *r) { ++ u32 i, timep; ++ extern struct timezone sys_tz; ++ const unsigned int __spm[12] = ++ { 0, ++ (31), ++ (31+28), ++ (31+28+31), ++ (31+28+31+30), ++ (31+28+31+30+31), ++ (31+28+31+30+31+30), ++ (31+28+31+30+31+30+31), ++ (31+28+31+30+31+30+31+31), ++ (31+28+31+30+31+30+31+31+30), ++ (31+28+31+30+31+30+31+31+30+31), ++ (31+28+31+30+31+30+31+31+30+31+30), ++ }; ++ register u32 work; ++ ++ timep = time - (sys_tz.tz_minuteswest * 60); ++ work=timep%(SPD); ++ r->tm_sec=work%60; work/=60; ++ r->tm_min=work%60; r->tm_hour=work/60; ++ work=timep/(SPD); ++ r->tm_wday=(4+work)%7; ++ for (i=1970; ; ++i) { ++ register time_t k= (!(i%4) && ((i%100) || !(i%400)))?366:365; ++ if (work>k) ++ work-=k; ++ else ++ break; ++ } ++ r->tm_year=i-1900; ++ for (i=11; i && __spm[i]>work; --i) ; ++ r->tm_mon=i; ++ r->tm_mday=work-__spm[i]+1; ++} +diff -urN linux-2.6.15-rc6.orig/net/ipv4/netfilter/Kconfig linux-2.6.15-rc6/net/ipv4/netfilter/Kconfig +--- linux-2.6.15-rc6.orig/net/ipv4/netfilter/Kconfig 2006-01-07 13:00:21.000000000 +0100 ++++ linux-2.6.15-rc6/net/ipv4/netfilter/Kconfig 2006-01-07 13:05:07.000000000 +0100 +@@ -278,6 +278,22 @@ + + To compile it as a module, choose M here. If unsure, say N. + ++ ++config IP_NF_MATCH_TIME ++ tristate 'TIME match support' ++ depends on IP_NF_IPTABLES ++ help ++ This option adds a `time' match, which allows you ++ to match based on the packet arrival time/date ++ (arrival time/date at the machine which netfilter is running on) or ++ departure time/date (for locally generated packets). ++ ++ If you say Y here, try iptables -m time --help for more information. ++ If you want to compile it as a module, say M here and read ++ ++ Documentation/modules.txt. If unsure, say `N'. ++ ++ + config IP_NF_MATCH_RECENT + tristate "recent match support" + depends on IP_NF_IPTABLES +diff -urN linux-2.6.15-rc6.orig/net/ipv4/netfilter/Makefile linux-2.6.15-rc6/net/ipv4/netfilter/Makefile +--- linux-2.6.15-rc6.orig/net/ipv4/netfilter/Makefile 2006-01-07 13:00:21.000000000 +0100 ++++ linux-2.6.15-rc6/net/ipv4/netfilter/Makefile 2006-01-07 13:03:50.000000000 +0100 +@@ -58,6 +58,7 @@ + obj-$(CONFIG_IP_NF_MATCH_MULTIPORT) += ipt_multiport.o + obj-$(CONFIG_IP_NF_MATCH_OWNER) += ipt_owner.o + obj-$(CONFIG_IP_NF_MATCH_TOS) += ipt_tos.o ++obj-$(CONFIG_IP_NF_MATCH_TIME) += ipt_time.o + obj-$(CONFIG_IP_NF_MATCH_RECENT) += ipt_recent.o + obj-$(CONFIG_IP_NF_MATCH_ECN) += ipt_ecn.o + obj-$(CONFIG_IP_NF_MATCH_DSCP) += ipt_dscp.o +diff -urN linux-2.6.15-rc6.orig/include/linux/netfilter_ipv4/ipt_time.h linux-2.6.15-rc6/include/linux/netfilter_ipv4/ipt_time.h +--- linux-2.6.15-rc6.orig/include/linux/netfilter_ipv4/ipt_time.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.15-rc6/include/linux/netfilter_ipv4/ipt_time.h 2006-01-07 13:02:14.000000000 +0100 +@@ -0,0 +1,18 @@ ++#ifndef __ipt_time_h_included__ ++#define __ipt_time_h_included__ ++ ++ ++struct ipt_time_info { ++ u_int8_t days_match; /* 1 bit per day. -SMTWTFS */ ++ u_int16_t time_start; /* 0 < time_start < 23*60+59 = 1439 */ ++ u_int16_t time_stop; /* 0:0 < time_stat < 23:59 */ ++ ++ /* FIXME: Keep this one for userspace iptables binary compability: */ ++ u_int8_t kerneltime; /* ignore skb time (and use kerneltime) or not. */ ++ ++ time_t date_start; ++ time_t date_stop; ++}; ++ ++ ++#endif /* __ipt_time_h_included__ */ diff --git a/target/linux/generic-2.6/patches/106-netfilter_imq.patch b/target/linux/generic-2.6/patches/106-netfilter_imq.patch new file mode 100644 index 0000000..8114db6 --- /dev/null +++ b/target/linux/generic-2.6/patches/106-netfilter_imq.patch @@ -0,0 +1,885 @@ +diff -Nur linux-2.6.16/drivers/net/imq.c linux-2.6.16-owrt/drivers/net/imq.c +--- linux-2.6.16/drivers/net/imq.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/drivers/net/imq.c 2006-03-20 12:59:23.000000000 +0100 +@@ -0,0 +1,403 @@ ++/* ++ * Pseudo-driver for the intermediate queue device. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ * Authors: Patrick McHardy, <kaber@trash.net> ++ * ++ * The first version was written by Martin Devera, <devik@cdi.cz> ++ * ++ * Credits: Jan Rafaj <imq2t@cedric.vabo.cz> ++ * - Update patch to 2.4.21 ++ * Sebastian Strollo <sstrollo@nortelnetworks.com> ++ * - Fix "Dead-loop on netdevice imq"-issue ++ * Marcel Sebek <sebek64@post.cz> ++ * - Update to 2.6.2-rc1 ++ * ++ * After some time of inactivity there is a group taking care ++ * of IMQ again: http://www.linuximq.net ++ * ++ * ++ * 2004/06/30 - New version of IMQ patch to kernels <=2.6.7 including ++ * the following changes: ++ * ++ * - Correction of ipv6 support "+"s issue (Hasso Tepper) ++ * - Correction of imq_init_devs() issue that resulted in ++ * kernel OOPS unloading IMQ as module (Norbert Buchmuller) ++ * - Addition of functionality to choose number of IMQ devices ++ * during kernel config (Andre Correa) ++ * - Addition of functionality to choose how IMQ hooks on ++ * PRE and POSTROUTING (after or before NAT) (Andre Correa) ++ * - Cosmetic corrections (Norbert Buchmuller) (Andre Correa) ++ * ++ * ++ * 2005/12/16 - IMQ versions between 2.6.7 and 2.6.13 were ++ * released with almost no problems. 2.6.14-x was released ++ * with some important changes: nfcache was removed; After ++ * some weeks of trouble we figured out that some IMQ fields ++ * in skb were missing in skbuff.c - skb_clone and copy_skb_header. ++ * These functions are correctly patched by this new patch version. ++ * ++ * Thanks for all who helped to figure out all the problems with ++ * 2.6.14.x: Patrick McHardy, Rune Kock, VeNoMouS, Max CtRiX, ++ * Kevin Shanahan, Richard Lucassen, Valery Dachev (hopefully ++ * I didn't forget anybody). I apologize again for my lack of time. ++ * ++ * More info at: http://www.linuximq.net/ (Andre Correa) ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/moduleparam.h> ++#include <linux/skbuff.h> ++#include <linux/netdevice.h> ++#include <linux/rtnetlink.h> ++#include <linux/if_arp.h> ++#include <linux/netfilter.h> ++#include <linux/netfilter_ipv4.h> ++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) ++ #include <linux/netfilter_ipv6.h> ++#endif ++#include <linux/imq.h> ++#include <net/pkt_sched.h> ++ ++static nf_hookfn imq_nf_hook; ++ ++static struct nf_hook_ops imq_ingress_ipv4 = { ++ .hook = imq_nf_hook, ++ .owner = THIS_MODULE, ++ .pf = PF_INET, ++ .hooknum = NF_IP_PRE_ROUTING, ++#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB) ++ .priority = NF_IP_PRI_MANGLE + 1 ++#else ++ .priority = NF_IP_PRI_NAT_DST + 1 ++#endif ++}; ++ ++static struct nf_hook_ops imq_egress_ipv4 = { ++ .hook = imq_nf_hook, ++ .owner = THIS_MODULE, ++ .pf = PF_INET, ++ .hooknum = NF_IP_POST_ROUTING, ++#if defined(CONFIG_IMQ_BEHAVIOR_AA) || defined(CONFIG_IMQ_BEHAVIOR_BA) ++ .priority = NF_IP_PRI_LAST ++#else ++ .priority = NF_IP_PRI_NAT_SRC - 1 ++#endif ++}; ++ ++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) ++static struct nf_hook_ops imq_ingress_ipv6 = { ++ .hook = imq_nf_hook, ++ .owner = THIS_MODULE, ++ .pf = PF_INET6, ++ .hooknum = NF_IP6_PRE_ROUTING, ++#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB) ++ .priority = NF_IP6_PRI_MANGLE + 1 ++#else ++ .priority = NF_IP6_PRI_NAT_DST + 1 ++#endif ++}; ++ ++static struct nf_hook_ops imq_egress_ipv6 = { ++ .hook = imq_nf_hook, ++ .owner = THIS_MODULE, ++ .pf = PF_INET6, ++ .hooknum = NF_IP6_POST_ROUTING, ++#if defined(CONFIG_IMQ_BEHAVIOR_AA) || defined(CONFIG_IMQ_BEHAVIOR_BA) ++ .priority = NF_IP6_PRI_LAST ++#else ++ .priority = NF_IP6_PRI_NAT_SRC - 1 ++#endif ++}; ++#endif ++ ++#if defined(CONFIG_IMQ_NUM_DEVS) ++static unsigned int numdevs = CONFIG_IMQ_NUM_DEVS; ++#else ++static unsigned int numdevs = 2; ++#endif ++ ++static struct net_device *imq_devs; ++ ++static struct net_device_stats *imq_get_stats(struct net_device *dev) ++{ ++ return (struct net_device_stats *)dev->priv; ++} ++ ++/* called for packets kfree'd in qdiscs at places other than enqueue */ ++static void imq_skb_destructor(struct sk_buff *skb) ++{ ++ struct nf_info *info = skb->nf_info; ++ ++ if (info) { ++ if (info->indev) ++ dev_put(info->indev); ++ if (info->outdev) ++ dev_put(info->outdev); ++ kfree(info); ++ } ++} ++ ++static int imq_dev_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct net_device_stats *stats = (struct net_device_stats*) dev->priv; ++ ++ stats->tx_bytes += skb->len; ++ stats->tx_packets++; ++ ++ skb->imq_flags = 0; ++ skb->destructor = NULL; ++ ++ dev->trans_start = jiffies; ++ nf_reinject(skb, skb->nf_info, NF_ACCEPT); ++ return 0; ++} ++ ++static int imq_nf_queue(struct sk_buff *skb, struct nf_info *info, unsigned queue_num, void *data) ++{ ++ struct net_device *dev; ++ struct net_device_stats *stats; ++ struct sk_buff *skb2 = NULL; ++ struct Qdisc *q; ++ unsigned int index = skb->imq_flags&IMQ_F_IFMASK; ++ int ret = -1; ++ ++ if (index > numdevs) ++ return -1; ++ ++ dev = imq_devs + index; ++ if (!(dev->flags & IFF_UP)) { ++ skb->imq_flags = 0; ++ nf_reinject(skb, info, NF_ACCEPT); ++ return 0; ++ } ++ dev->last_rx = jiffies; ++ ++ if (skb->destructor) { ++ skb2 = skb; ++ skb = skb_clone(skb, GFP_ATOMIC); ++ if (!skb) ++ return -1; ++ } ++ skb->nf_info = info; ++ ++ stats = (struct net_device_stats *)dev->priv; ++ stats->rx_bytes+= skb->len; ++ stats->rx_packets++; ++ ++ spin_lock_bh(&dev->queue_lock); ++ q = dev->qdisc; ++ if (q->enqueue) { ++ q->enqueue(skb_get(skb), q); ++ if (skb_shared(skb)) { ++ skb->destructor = imq_skb_destructor; ++ kfree_skb(skb); ++ ret = 0; ++ } ++ } ++ if (spin_is_locked(&dev->xmit_lock)) ++ netif_schedule(dev); ++ else ++ ++ while (!netif_queue_stopped(dev) && ++ qdisc_restart(dev)<0) ++ /* NOTHING */; ++ ++ spin_unlock_bh(&dev->queue_lock); ++ ++ if (skb2) ++ kfree_skb(ret ? skb : skb2); ++ ++ return ret; ++} ++ ++static struct nf_queue_handler nfqh = { ++ .name = "imq", ++ .outfn = imq_nf_queue, ++}; ++ ++static unsigned int imq_nf_hook(unsigned int hook, struct sk_buff **pskb, ++ const struct net_device *indev, ++ const struct net_device *outdev, ++ int (*okfn)(struct sk_buff *)) ++{ ++ if ((*pskb)->imq_flags & IMQ_F_ENQUEUE) ++ return NF_QUEUE; ++ ++ return NF_ACCEPT; ++} ++ ++ ++static int __init imq_init_hooks(void) ++{ ++ int err; ++ ++ err = nf_register_queue_handler(PF_INET, &nfqh); ++ if (err > 0) ++ goto err1; ++ if ((err = nf_register_hook(&imq_ingress_ipv4))) ++ goto err2; ++ if ((err = nf_register_hook(&imq_egress_ipv4))) ++ goto err3; ++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) ++ if ((err = nf_register_queue_handler(PF_INET6, &nfqh))) ++ goto err4; ++ if ((err = nf_register_hook(&imq_ingress_ipv6))) ++ goto err5; ++ if ((err = nf_register_hook(&imq_egress_ipv6))) ++ goto err6; ++#endif ++ ++ return 0; ++ ++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) ++err6: ++ nf_unregister_hook(&imq_ingress_ipv6); ++err5: ++ nf_unregister_queue_handler(PF_INET6); ++err4: ++ nf_unregister_hook(&imq_egress_ipv6); ++#endif ++err3: ++ nf_unregister_hook(&imq_ingress_ipv4); ++err2: ++ nf_unregister_queue_handler(PF_INET); ++err1: ++ return err; ++} ++ ++static void __exit imq_unhook(void) ++{ ++ nf_unregister_hook(&imq_ingress_ipv4); ++ nf_unregister_hook(&imq_egress_ipv4); ++ nf_unregister_queue_handler(PF_INET); ++#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) ++ nf_unregister_hook(&imq_ingress_ipv6); ++ nf_unregister_hook(&imq_egress_ipv6); ++ nf_unregister_queue_handler(PF_INET6); ++#endif ++} ++ ++static int __init imq_dev_init(struct net_device *dev) ++{ ++ dev->hard_start_xmit = imq_dev_xmit; ++ dev->type = ARPHRD_VOID; ++ dev->mtu = 1500; ++ dev->tx_queue_len = 30; ++ dev->flags = IFF_NOARP; ++ dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL); ++ if (dev->priv == NULL) ++ return -ENOMEM; ++ memset(dev->priv, 0, sizeof(struct net_device_stats)); ++ dev->get_stats = imq_get_stats; ++ ++ return 0; ++} ++ ++static void imq_dev_uninit(struct net_device *dev) ++{ ++ kfree(dev->priv); ++} ++ ++static int __init imq_init_devs(void) ++{ ++ struct net_device *dev; ++ int i,j; ++ j = numdevs; ++ ++ if (!numdevs || numdevs > IMQ_MAX_DEVS) { ++ printk(KERN_ERR "IMQ: numdevs has to be betweed 1 and %u\n", ++ IMQ_MAX_DEVS); ++ return -EINVAL; ++ } ++ ++ imq_devs = kmalloc(sizeof(struct net_device) * numdevs, GFP_KERNEL); ++ if (!imq_devs) ++ return -ENOMEM; ++ memset(imq_devs, 0, sizeof(struct net_device) * numdevs); ++ ++ /* we start counting at zero */ ++ numdevs--; ++ ++ for (i = 0, dev = imq_devs; i <= numdevs; i++, dev++) { ++ SET_MODULE_OWNER(dev); ++ strcpy(dev->name, "imq%d"); ++ dev->init = imq_dev_init; ++ dev->uninit = imq_dev_uninit; ++ ++ if (register_netdev(dev) < 0) ++ goto err_register; ++ } ++ printk(KERN_INFO "IMQ starting with %u devices...\n", j); ++ return 0; ++ ++err_register: ++ for (; i; i--) ++ unregister_netdev(--dev); ++ kfree(imq_devs); ++ return -EIO; ++} ++ ++static void imq_cleanup_devs(void) ++{ ++ int i; ++ struct net_device *dev = imq_devs; ++ ++ for (i = 0; i <= numdevs; i++) ++ unregister_netdev(dev++); ++ ++ kfree(imq_devs); ++} ++ ++static int __init imq_init_module(void) ++{ ++ int err; ++ ++ if ((err = imq_init_devs())) { ++ printk(KERN_ERR "IMQ: Error trying imq_init_devs()\n"); ++ return err; ++ } ++ if ((err = imq_init_hooks())) { ++ printk(KERN_ERR "IMQ: Error trying imq_init_hooks()\n"); ++ imq_cleanup_devs(); ++ return err; ++ } ++ ++ printk(KERN_INFO "IMQ driver loaded successfully.\n"); ++ ++#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB) ++ printk(KERN_INFO "\tHooking IMQ before NAT on PREROUTING.\n"); ++#else ++ printk(KERN_INFO "\tHooking IMQ after NAT on PREROUTING.\n"); ++#endif ++#if defined(CONFIG_IMQ_BEHAVIOR_AB) || defined(CONFIG_IMQ_BEHAVIOR_BB) ++ printk(KERN_INFO "\tHooking IMQ before NAT on POSTROUTING.\n"); ++#else ++ printk(KERN_INFO "\tHooking IMQ after NAT on POSTROUTING.\n"); ++#endif ++ ++ return 0; ++} ++ ++static void __exit imq_cleanup_module(void) ++{ ++ imq_unhook(); ++ imq_cleanup_devs(); ++ printk(KERN_INFO "IMQ driver unloaded successfully.\n"); ++} ++ ++ ++module_init(imq_init_module); ++module_exit(imq_cleanup_module); ++ ++module_param(numdevs, int, 0); ++MODULE_PARM_DESC(numdevs, "number of IMQ devices (how many imq* devices will be created)"); ++MODULE_AUTHOR("http://www.linuximq.net"); ++MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See http://www.linuximq.net/ for more information."); ++MODULE_LICENSE("GPL"); +diff -Nur linux-2.6.16/drivers/net/Kconfig linux-2.6.16-owrt/drivers/net/Kconfig +--- linux-2.6.16/drivers/net/Kconfig 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-owrt/drivers/net/Kconfig 2006-03-20 12:59:23.000000000 +0100 +@@ -93,6 +93,129 @@ + To compile this driver as a module, choose M here: the module + will be called eql. If unsure, say N. + ++config IMQ ++ tristate "IMQ (intermediate queueing device) support" ++ depends on NETDEVICES && NETFILTER ++ ---help--- ++ The IMQ device(s) is used as placeholder for QoS queueing disciplines. ++ Every packet entering/leaving the IP stack can be directed through ++ the IMQ device where it's enqueued/dequeued to the attached qdisc. ++ This allows you to treat network devices as classes and distribute ++ bandwidth among them. Iptables is used to specify through which IMQ ++ device, if any, packets travel. ++ ++ More information at: http://www.linuximq.net/ ++ ++ To compile this driver as a module, choose M here: the module ++ will be called imq. If unsure, say N. ++ ++choice ++ prompt "IMQ behavior (PRE/POSTROUTING)" ++ depends on IMQ ++ default IMQ_BEHAVIOR_BA ++ help ++ ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ IMQ can work in any of the following ways: ++ ++ PREROUTING | POSTROUTING ++ -----------------|------------------- ++ #1 After NAT | After NAT ++ #2 After NAT | Before NAT ++ #3 Before NAT | After NAT ++ #4 Before NAT | Before NAT ++ ++ The default behavior is to hook before NAT on PREROUTING ++ and after NAT on POSTROUTING (#3). ++ ++ This settings are specially usefull when trying to use IMQ ++ to shape NATed clients. ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++config IMQ_BEHAVIOR_AA ++ bool "IMQ AA" ++ help ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ Choosing this option will make IMQ hook like this: ++ ++ PREROUTING: After NAT ++ POSTROUTING: After NAT ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++config IMQ_BEHAVIOR_AB ++ bool "IMQ AB" ++ help ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ Choosing this option will make IMQ hook like this: ++ ++ PREROUTING: After NAT ++ POSTROUTING: Before NAT ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++config IMQ_BEHAVIOR_BA ++ bool "IMQ BA" ++ help ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ Choosing this option will make IMQ hook like this: ++ ++ PREROUTING: Before NAT ++ POSTROUTING: After NAT ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++config IMQ_BEHAVIOR_BB ++ bool "IMQ BB" ++ help ++ This settings defines how IMQ behaves in respect to its ++ hooking in PREROUTING and POSTROUTING. ++ ++ Choosing this option will make IMQ hook like this: ++ ++ PREROUTING: Before NAT ++ POSTROUTING: Before NAT ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ ++endchoice ++ ++config IMQ_NUM_DEVS ++ ++ int "Number of IMQ devices" ++ range 2 8 ++ depends on IMQ ++ default "2" ++ help ++ ++ This settings defines how many IMQ devices will be ++ created. ++ ++ The default value is 2. ++ ++ More information can be found at: www.linuximq.net ++ ++ If not sure leave the default settings alone. ++ + config TUN + tristate "Universal TUN/TAP device driver support" + select CRC32 +diff -Nur linux-2.6.16/drivers/net/Makefile linux-2.6.16-owrt/drivers/net/Makefile +--- linux-2.6.16/drivers/net/Makefile 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-owrt/drivers/net/Makefile 2006-03-20 13:00:13.000000000 +0100 +@@ -125,6 +125,7 @@ + endif + + obj-$(CONFIG_DUMMY) += dummy.o ++obj-$(CONFIG_IMQ) += imq.o + obj-$(CONFIG_IFB) += ifb.o + obj-$(CONFIG_DE600) += de600.o + obj-$(CONFIG_DE620) += de620.o +diff -Nur linux-2.6.16/include/linux/imq.h linux-2.6.16-owrt/include/linux/imq.h +--- linux-2.6.16/include/linux/imq.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/include/linux/imq.h 2006-03-20 12:59:23.000000000 +0100 +@@ -0,0 +1,9 @@ ++#ifndef _IMQ_H ++#define _IMQ_H ++ ++#define IMQ_MAX_DEVS 16 ++ ++#define IMQ_F_IFMASK 0x7f ++#define IMQ_F_ENQUEUE 0x80 ++ ++#endif /* _IMQ_H */ +diff -Nur linux-2.6.16/include/linux/netfilter_ipv4/ipt_IMQ.h linux-2.6.16-owrt/include/linux/netfilter_ipv4/ipt_IMQ.h +--- linux-2.6.16/include/linux/netfilter_ipv4/ipt_IMQ.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/include/linux/netfilter_ipv4/ipt_IMQ.h 2006-03-20 12:59:23.000000000 +0100 +@@ -0,0 +1,8 @@ ++#ifndef _IPT_IMQ_H ++#define _IPT_IMQ_H ++ ++struct ipt_imq_info { ++ unsigned int todev; /* target imq device */ ++}; ++ ++#endif /* _IPT_IMQ_H */ +diff -Nur linux-2.6.16/include/linux/netfilter_ipv6/ip6t_IMQ.h linux-2.6.16-owrt/include/linux/netfilter_ipv6/ip6t_IMQ.h +--- linux-2.6.16/include/linux/netfilter_ipv6/ip6t_IMQ.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/include/linux/netfilter_ipv6/ip6t_IMQ.h 2006-03-20 12:59:23.000000000 +0100 +@@ -0,0 +1,8 @@ ++#ifndef _IP6T_IMQ_H ++#define _IP6T_IMQ_H ++ ++struct ip6t_imq_info { ++ unsigned int todev; /* target imq device */ ++}; ++ ++#endif /* _IP6T_IMQ_H */ +diff -Nur linux-2.6.16/include/linux/skbuff.h linux-2.6.16-owrt/include/linux/skbuff.h +--- linux-2.6.16/include/linux/skbuff.h 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-owrt/include/linux/skbuff.h 2006-03-20 12:59:23.000000000 +0100 +@@ -275,6 +275,10 @@ + #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) + struct sk_buff *nfct_reasm; + #endif ++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) ++ unsigned char imq_flags; ++ struct nf_info *nf_info; ++#endif + #ifdef CONFIG_BRIDGE_NETFILTER + struct nf_bridge_info *nf_bridge; + #endif +diff -Nur linux-2.6.16/net/core/skbuff.c linux-2.6.16-owrt/net/core/skbuff.c +--- linux-2.6.16/net/core/skbuff.c 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-owrt/net/core/skbuff.c 2006-03-20 12:59:23.000000000 +0100 +@@ -425,6 +425,10 @@ + C(nfct_reasm); + nf_conntrack_get_reasm(skb->nfct_reasm); + #endif ++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) ++ C(imq_flags); ++ C(nf_info); ++#endif /*CONFIG_IMQ*/ + #ifdef CONFIG_BRIDGE_NETFILTER + C(nf_bridge); + nf_bridge_get(skb->nf_bridge); +@@ -489,6 +493,10 @@ + #if defined(CONFIG_IP_VS) || defined(CONFIG_IP_VS_MODULE) + new->ipvs_property = old->ipvs_property; + #endif ++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) ++ new->imq_flags = old->imq_flags; ++ new->nf_info = old->nf_info; ++#endif /*CONFIG_IMQ*/ + #ifdef CONFIG_BRIDGE_NETFILTER + new->nf_bridge = old->nf_bridge; + nf_bridge_get(old->nf_bridge); +diff -Nur linux-2.6.16/net/ipv4/netfilter/ipt_IMQ.c linux-2.6.16-owrt/net/ipv4/netfilter/ipt_IMQ.c +--- linux-2.6.16/net/ipv4/netfilter/ipt_IMQ.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/net/ipv4/netfilter/ipt_IMQ.c 2006-03-20 12:59:23.000000000 +0100 +@@ -0,0 +1,80 @@ ++/* ++ * This target marks packets to be enqueued to an imq device ++ */ ++#include <linux/module.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ipt_IMQ.h> ++#include <linux/imq.h> ++ ++static unsigned int imq_target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++ const void *targinfo, ++ void *userdata) ++{ ++ struct ipt_imq_info *mr = (struct ipt_imq_info*)targinfo; ++ ++ (*pskb)->imq_flags = mr->todev | IMQ_F_ENQUEUE; ++ ++ return IPT_CONTINUE; ++} ++ ++static int imq_checkentry(const char *tablename, ++ const struct ipt_entry *e, ++ void *targinfo, ++ unsigned int targinfosize, ++ unsigned int hook_mask) ++{ ++ struct ipt_imq_info *mr; ++ ++ if (targinfosize != IPT_ALIGN(sizeof(struct ipt_imq_info))) { ++ printk(KERN_WARNING "IMQ: invalid targinfosize\n"); ++ return 0; ++ } ++ mr = (struct ipt_imq_info*)targinfo; ++ ++ if (strcmp(tablename, "mangle") != 0) { ++ printk(KERN_WARNING ++ "IMQ: IMQ can only be called from \"mangle\" table, not \"%s\"\n", ++ tablename); ++ return 0; ++ } ++ ++ if (mr->todev > IMQ_MAX_DEVS) { ++ printk(KERN_WARNING ++ "IMQ: invalid device specified, highest is %u\n", ++ IMQ_MAX_DEVS); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static struct ipt_target ipt_imq_reg = { ++ .name = "IMQ", ++ .target = imq_target, ++ .checkentry = imq_checkentry, ++ .me = THIS_MODULE ++}; ++ ++static int __init init(void) ++{ ++ if (ipt_register_target(&ipt_imq_reg)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ ipt_unregister_target(&ipt_imq_reg); ++} ++ ++module_init(init); ++module_exit(fini); ++ ++MODULE_AUTHOR("http://www.linuximq.net"); ++MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See http://www.linuximq.net/ for more information."); ++MODULE_LICENSE("GPL"); +diff -Nur linux-2.6.16/net/ipv4/netfilter/Kconfig linux-2.6.16-owrt/net/ipv4/netfilter/Kconfig +--- linux-2.6.16/net/ipv4/netfilter/Kconfig 2006-03-20 12:58:53.000000000 +0100 ++++ linux-2.6.16-owrt/net/ipv4/netfilter/Kconfig 2006-03-20 12:59:23.000000000 +0100 +@@ -351,6 +351,17 @@ + + To compile it as a module, choose M here. If unsure, say N. + ++config IP_NF_TARGET_IMQ ++ tristate "IMQ target support" ++ depends on IP_NF_MANGLE ++ help ++ This option adds a `IMQ' target which is used to specify if and ++ to which IMQ device packets should get enqueued/dequeued. ++ ++ For more information visit: http://www.linuximq.net/ ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ + config IP_NF_TARGET_LOG + tristate "LOG target support" + depends on IP_NF_IPTABLES +diff -Nur linux-2.6.16/net/ipv4/netfilter/Makefile linux-2.6.16-owrt/net/ipv4/netfilter/Makefile +--- linux-2.6.16/net/ipv4/netfilter/Makefile 2006-03-20 12:58:53.000000000 +0100 ++++ linux-2.6.16-owrt/net/ipv4/netfilter/Makefile 2006-03-20 13:01:12.000000000 +0100 +@@ -67,6 +67,7 @@ + obj-$(CONFIG_IP_NF_TARGET_TOS) += ipt_TOS.o + obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o + obj-$(CONFIG_IP_NF_TARGET_DSCP) += ipt_DSCP.o ++obj-$(CONFIG_IP_NF_TARGET_IMQ) += ipt_IMQ.o + obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o + obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o + obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o +diff -Nur linux-2.6.16/net/ipv6/netfilter/ip6t_IMQ.c linux-2.6.16-owrt/net/ipv6/netfilter/ip6t_IMQ.c +--- linux-2.6.16/net/ipv6/netfilter/ip6t_IMQ.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16-owrt/net/ipv6/netfilter/ip6t_IMQ.c 2006-03-20 12:59:23.000000000 +0100 +@@ -0,0 +1,80 @@ ++/* ++ * This target marks packets to be enqueued to an imq device ++ */ ++#include <linux/module.h> ++#include <linux/skbuff.h> ++#include <linux/netfilter_ipv6/ip6_tables.h> ++#include <linux/netfilter_ipv6/ip6t_IMQ.h> ++#include <linux/imq.h> ++ ++static unsigned int imq_target(struct sk_buff **pskb, ++ unsigned int hooknum, ++ const struct net_device *in, ++ const struct net_device *out, ++ const void *targinfo, ++ void *userdata) ++{ ++ struct ip6t_imq_info *mr = (struct ip6t_imq_info*)targinfo; ++ ++ (*pskb)->imq_flags = mr->todev | IMQ_F_ENQUEUE; ++ ++ return IP6T_CONTINUE; ++} ++ ++static int imq_checkentry(const char *tablename, ++ const struct ip6t_entry *e, ++ void *targinfo, ++ unsigned int targinfosize, ++ unsigned int hook_mask) ++{ ++ struct ip6t_imq_info *mr; ++ ++ if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_imq_info))) { ++ printk(KERN_WARNING "IMQ: invalid targinfosize\n"); ++ return 0; ++ } ++ mr = (struct ip6t_imq_info*)targinfo; ++ ++ if (strcmp(tablename, "mangle") != 0) { ++ printk(KERN_WARNING ++ "IMQ: IMQ can only be called from \"mangle\" table, not \"%s\"\n", ++ tablename); ++ return 0; ++ } ++ ++ if (mr->todev > IMQ_MAX_DEVS) { ++ printk(KERN_WARNING ++ "IMQ: invalid device specified, highest is %u\n", ++ IMQ_MAX_DEVS); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static struct ip6t_target ip6t_imq_reg = { ++ .name = "IMQ", ++ .target = imq_target, ++ .checkentry = imq_checkentry, ++ .me = THIS_MODULE ++}; ++ ++static int __init init(void) ++{ ++ if (ip6t_register_target(&ip6t_imq_reg)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static void __exit fini(void) ++{ ++ ip6t_unregister_target(&ip6t_imq_reg); ++} ++ ++module_init(init); ++module_exit(fini); ++ ++MODULE_AUTHOR("http://www.linuximq.net"); ++MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See http://www.linuximq.net/ for more information."); ++MODULE_LICENSE("GPL"); +diff -Nur linux-2.6.16/net/ipv6/netfilter/Kconfig linux-2.6.16-owrt/net/ipv6/netfilter/Kconfig +--- linux-2.6.16/net/ipv6/netfilter/Kconfig 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-owrt/net/ipv6/netfilter/Kconfig 2006-03-20 12:59:23.000000000 +0100 +@@ -154,6 +154,15 @@ + + To compile it as a module, choose M here. If unsure, say N. + ++config IP6_NF_TARGET_IMQ ++ tristate "IMQ target support" ++ depends on IP6_NF_MANGLE ++ help ++ This option adds a `IMQ' target which is used to specify if and ++ to which imq device packets should get enqueued/dequeued. ++ ++ To compile it as a module, choose M here. If unsure, say N. ++ + config IP6_NF_TARGET_LOG + tristate "LOG target support" + depends on IP6_NF_FILTER +diff -Nur linux-2.6.16/net/ipv6/netfilter/Makefile linux-2.6.16-owrt/net/ipv6/netfilter/Makefile +--- linux-2.6.16/net/ipv6/netfilter/Makefile 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-owrt/net/ipv6/netfilter/Makefile 2006-03-20 13:02:14.000000000 +0100 +@@ -4,6 +4,7 @@ + + # Link order matters here. + obj-$(CONFIG_IP6_NF_IPTABLES) += ip6_tables.o ++obj-$(CONFIG_IP6_NF_TARGET_IMQ) += ip6t_IMQ.o + obj-$(CONFIG_IP6_NF_MATCH_RT) += ip6t_rt.o + obj-$(CONFIG_IP6_NF_MATCH_OPTS) += ip6t_hbh.o ip6t_dst.o + obj-$(CONFIG_IP6_NF_MATCH_IPV6HEADER) += ip6t_ipv6header.o +diff -Nur linux-2.6.16/net/sched/sch_generic.c linux-2.6.16-owrt/net/sched/sch_generic.c +--- linux-2.6.16/net/sched/sch_generic.c 2006-03-20 06:53:29.000000000 +0100 ++++ linux-2.6.16-owrt/net/sched/sch_generic.c 2006-03-20 12:59:23.000000000 +0100 +@@ -29,6 +29,9 @@ + #include <linux/netdevice.h> + #include <linux/skbuff.h> + #include <linux/rtnetlink.h> ++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) ++#include <linux/imq.h> ++#endif + #include <linux/init.h> + #include <linux/rcupdate.h> + #include <linux/list.h> +@@ -136,7 +139,13 @@ + + if (!netif_queue_stopped(dev)) { + int ret; +- if (netdev_nit) ++ ++ if (netdev_nit ++#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE) ++ && !(skb->imq_flags & IMQ_F_ENQUEUE) ++#endif ++ ) ++ + dev_queue_xmit_nit(skb, dev); + + ret = dev->hard_start_xmit(skb, dev); diff --git a/target/linux/generic-2.6/patches/107-netfilter_nat_sip.patch b/target/linux/generic-2.6/patches/107-netfilter_nat_sip.patch new file mode 100644 index 0000000..6640118 --- /dev/null +++ b/target/linux/generic-2.6/patches/107-netfilter_nat_sip.patch @@ -0,0 +1,830 @@ +diff -urN linux-2.6.16.4/net/ipv4/netfilter/ip_conntrack_sip.c linux-2.6.16.4.new/net/ipv4/netfilter/ip_conntrack_sip.c +--- linux-2.6.16.4/net/ipv4/netfilter/ip_conntrack_sip.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16.4.new/net/ipv4/netfilter/ip_conntrack_sip.c 2006-04-12 17:31:44.000000000 +0200 +@@ -0,0 +1,414 @@ ++/* SIP extension for IP connection tracking. ++ * ++ * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar> ++ * based on RR's ip_conntrack_ftp.c and other modules. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/netfilter.h> ++#include <linux/ip.h> ++#include <linux/ctype.h> ++#include <linux/in.h> ++#include <linux/udp.h> ++#include <net/checksum.h> ++#include <net/udp.h> ++ ++#include <linux/netfilter_ipv4/ip_conntrack_helper.h> ++#include <linux/netfilter_ipv4/ip_conntrack_sip.h> ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>"); ++MODULE_DESCRIPTION("SIP connection tracking helper"); ++ ++static DEFINE_SPINLOCK(sipbf_lock); ++ ++ ++#define MAX_PORTS 8 ++static int ports[MAX_PORTS]; ++static int ports_c; ++module_param_array(ports, int, &ports_c, 0400); ++MODULE_PARM_DESC(ports, " port numbers of sip servers"); ++ ++static unsigned int sip_timeout = SIP_TIMEOUT; ++ ++module_param(sip_timeout, int, 0600); ++MODULE_PARM_DESC(sip_timeout, "timeout for the master sip session"); ++ ++unsigned int (*ip_nat_sip_hook)(struct sk_buff **pskb, ++ enum ip_conntrack_info ctinfo, ++ struct ip_conntrack *ct, ++ const char **dptr); ++EXPORT_SYMBOL_GPL(ip_nat_sip_hook); ++ ++unsigned int (*ip_nat_sdp_hook)(struct sk_buff **pskb, ++ enum ip_conntrack_info ctinfo, ++ struct ip_conntrack_expect *exp, ++ const char *dptr); ++EXPORT_SYMBOL_GPL(ip_nat_sdp_hook); ++ ++int ct_sip_get_info(const char *dptr, size_t dlen, ++ unsigned int *matchoff, ++ unsigned int *matchlen, ++ struct sip_header_nfo *hnfo); ++EXPORT_SYMBOL(ct_sip_get_info); ++ ++#if 0 ++#define DEBUGP printk ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++static int digits_len(const char *dptr, const char *limit, int *shift); ++static int epaddr_len(const char *dptr, const char *limit, int *shift); ++static int skp_digits_len(const char *dptr, const char *limit, int *shift); ++static int skp_epaddr_len(const char *dptr, const char *limit, int *shift); ++ ++struct sip_header_nfo ct_sip_hdrs[] = { ++ { /* Via header */ ++ "Via:", sizeof("Via:") - 1, ++ "\r\nv:", sizeof("\r\nv:") - 1, /* rfc3261 "\r\n" */ ++ "UDP ", sizeof("UDP ") - 1, ++ epaddr_len ++ }, ++ { /* Contact header */ ++ "Contact:", sizeof("Contact:") - 1, ++ "\r\nm:", sizeof("\r\nm:") - 1, ++ "sip:", sizeof("sip:") - 1, ++ skp_epaddr_len ++ }, ++ { /* Content length header */ ++ "Content-Length:", sizeof("Content-Length:") - 1, ++ "\r\nl:", sizeof("\r\nl:") - 1, ++ ":", sizeof(":") - 1, ++ skp_digits_len ++ }, ++ { /* SDP media info */ ++ "\nm=", sizeof("\nm=") - 1, ++ "\rm=", sizeof("\rm=") - 1, ++ "audio ", sizeof("audio ") - 1, ++ digits_len ++ }, ++ { /* SDP owner address*/ ++ "\no=", sizeof("\no=") - 1, ++ "\ro=", sizeof("\ro=") - 1, ++ "IN IP4 ", sizeof("IN IP4 ") - 1, ++ epaddr_len ++ }, ++ { /* SDP connection info */ ++ "\nc=", sizeof("\nc=") - 1, ++ "\rc=", sizeof("\rc=") - 1, ++ "IN IP4 ", sizeof("IN IP4 ") - 1, ++ epaddr_len ++ }, ++ { /* Requests headers */ ++ "sip:", sizeof("sip:") - 1, ++ "sip:", sizeof("sip:") - 1, /* yes, i know.. ;) */ ++ "@", sizeof("@") - 1, ++ epaddr_len ++ }, ++ { /* SDP version header */ ++ "\nv=", sizeof("\nv=") - 1, ++ "\rv=", sizeof("\rv=") - 1, ++ "=", sizeof("=") - 1, ++ digits_len ++ } ++}; ++EXPORT_SYMBOL(ct_sip_hdrs); ++ ++ ++static int digits_len(const char *dptr, const char *limit, int *shift) ++{ ++ int len = 0; ++ while (dptr <= limit && isdigit(*dptr)) { ++ dptr++; ++ len++; ++ } ++ return len; ++} ++ ++/* get digits lenght, skiping blank spaces. */ ++static int skp_digits_len(const char *dptr, const char *limit, int *shift) ++{ ++ for (; dptr <= limit && *dptr == ' '; dptr++) ++ (*shift)++; ++ ++ return digits_len(dptr, limit, shift); ++} ++ ++/* Simple ipaddr parser.. */ ++static int parse_ipaddr(const char *cp, const char **endp, ++ uint32_t *ipaddr, const char *limit) ++{ ++ unsigned long int val; ++ int i, digit = 0; ++ ++ for (i = 0, *ipaddr = 0; cp <= limit && i < 4; i++) { ++ digit = 0; ++ if (!isdigit(*cp)) ++ break; ++ ++ val = simple_strtoul(cp, (char **)&cp, 10); ++ if (val > 0xFF) ++ return -1; ++ ++ ((uint8_t *)ipaddr)[i] = val; ++ digit = 1; ++ ++ if (*cp != '.') ++ break; ++ cp++; ++ } ++ if (!digit) ++ return -1; ++ ++ if (endp) ++ *endp = cp; ++ ++ return 0; ++} ++ ++/* skip ip address. returns it lenght. */ ++static int epaddr_len(const char *dptr, const char *limit, int *shift) ++{ ++ const char *aux = dptr; ++ uint32_t ip; ++ ++ if (parse_ipaddr(dptr, &dptr, &ip, limit) < 0) { ++ DEBUGP("ip: %s parse failed.!\n", dptr); ++ return 0; ++ } ++ ++ /* Port number */ ++ if (*dptr == ':') { ++ dptr++; ++ dptr += digits_len(dptr, limit, shift); ++ } ++ return dptr - aux; ++} ++ ++/* get address lenght, skiping user info. */ ++static int skp_epaddr_len(const char *dptr, const char *limit, int *shift) ++{ ++ for (; dptr <= limit && *dptr != '@'; dptr++) ++ (*shift)++; ++ ++ if (*dptr == '@') { ++ dptr++; ++ (*shift)++; ++ return epaddr_len(dptr, limit, shift); ++ } ++ return 0; ++} ++ ++/* Returns 0 if not found, -1 error parsing. */ ++int ct_sip_get_info(const char *dptr, size_t dlen, ++ unsigned int *matchoff, ++ unsigned int *matchlen, ++ struct sip_header_nfo *hnfo) ++{ ++ const char *limit, *aux, *k = dptr; ++ int shift = 0; ++ ++ limit = dptr + (dlen - hnfo->lnlen); ++ ++ while (dptr <= limit) { ++ if ((strncmp(dptr, hnfo->lname, hnfo->lnlen) != 0) && ++ (strncmp(dptr, hnfo->sname, hnfo->snlen) != 0)) ++ { ++ dptr++; ++ continue; ++ } ++ aux = ct_sip_search(hnfo->ln_str, dptr, hnfo->ln_strlen, ++ ct_sip_lnlen(dptr, limit)); ++ if (!aux) { ++ DEBUGP("'%s' not found in '%s'.\n", hnfo->ln_str, hnfo->lname); ++ return -1; ++ } ++ aux += hnfo->ln_strlen; ++ ++ *matchlen = hnfo->match_len(aux, limit, &shift); ++ if (!*matchlen) ++ return -1; ++ ++ *matchoff = (aux - k) + shift; ++ ++ DEBUGP("%s match succeeded! - len: %u\n", hnfo->lname, *matchlen); ++ return 1; ++ } ++ DEBUGP("%s header not found.\n", hnfo->lname); ++ return 0; ++} ++ ++static int set_expected_rtp(struct sk_buff **pskb, ++ struct ip_conntrack *ct, ++ enum ip_conntrack_info ctinfo, ++ uint32_t ipaddr, uint16_t port, ++ const char *dptr) ++{ ++ struct ip_conntrack_expect *exp; ++ int ret; ++ ++ exp = ip_conntrack_expect_alloc(ct); ++ if (exp == NULL) ++ return NF_DROP; ++ ++ exp->tuple = ((struct ip_conntrack_tuple) ++ { { ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip, { 0 } }, ++ { ipaddr, { .udp = { htons(port) } }, IPPROTO_UDP }}); ++ ++ exp->mask = ((struct ip_conntrack_tuple) ++ { { 0xFFFFFFFF, { 0 } }, ++ { 0xFFFFFFFF, { .udp = { 0xFFFF } }, 0xFF }}); ++ ++ exp->expectfn = NULL; ++ ++ if (ip_nat_sdp_hook) ++ ret = ip_nat_sdp_hook(pskb, ctinfo, exp, dptr); ++ else { ++ if (ip_conntrack_expect_related(exp) != 0) ++ ret = NF_DROP; ++ else ++ ret = NF_ACCEPT; ++ } ++ ip_conntrack_expect_put(exp); ++ ++ return ret; ++} ++ ++static int sip_help(struct sk_buff **pskb, ++ struct ip_conntrack *ct, ++ enum ip_conntrack_info ctinfo) ++{ ++ unsigned int dataoff, datalen; ++ const char *dptr; ++ int ret = NF_ACCEPT; ++ int matchoff, matchlen; ++ uint32_t ipaddr; ++ uint16_t port; ++ ++ /* No Data ? */ ++ dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); ++ if (dataoff >= (*pskb)->len) { ++ DEBUGP("skb->len = %u\n", (*pskb)->len); ++ return NF_ACCEPT; ++ } ++ ++ ip_ct_refresh(ct, *pskb, sip_timeout * HZ); ++ ++ spin_lock_bh(&sipbf_lock); ++ ++ if ((dataoff + (*pskb)->len - dataoff) <= skb_headlen(*pskb)) ++ dptr = (*pskb)->data + dataoff; ++ else { ++ DEBUGP("Copy of skbuff not supported yet.\n"); ++ goto out; ++ } ++ ++ if (ip_nat_sip_hook) { ++ if (!ip_nat_sip_hook(pskb, ctinfo, ct, &dptr)) { ++ ret = NF_DROP; ++ goto out; ++ } ++ } ++ ++ if ((ctinfo) >= IP_CT_IS_REPLY) ++ goto out; ++ ++ /* After this point NAT, could have mangled skb, so ++ we need to recalculate payload lenght. */ ++ datalen = (*pskb)->len - dataoff; ++ ++ if (datalen < (sizeof("SIP/2.0 200") - 1)) ++ goto out; ++ ++ /* RTP info only in some SDP pkts */ ++ if (memcmp(dptr, "INVITE", sizeof("INVITE") - 1) != 0 && ++ memcmp(dptr, "SIP/2.0 200", sizeof("SIP/2.0 200") - 1) != 0) { ++ goto out; ++ } ++ /* Get ip and port address from SDP packet. */ ++ if (ct_sip_get_info(dptr, datalen, &matchoff, &matchlen, ++ &ct_sip_hdrs[POS_CONECTION]) > 0) { ++ ++ /* We'll drop only if there are parse problems. */ ++ if (parse_ipaddr(dptr + matchoff, NULL, &ipaddr, ++ dptr + datalen) < 0) { ++ ret = NF_DROP; ++ goto out; ++ } ++ if (ct_sip_get_info(dptr, datalen, &matchoff, &matchlen, ++ &ct_sip_hdrs[POS_MEDIA]) > 0) { ++ ++ port = simple_strtoul(dptr + matchoff, NULL, 10); ++ if (port < 1024) { ++ ret = NF_DROP; ++ goto out; ++ } ++ ret = set_expected_rtp(pskb, ct, ctinfo, ++ ipaddr, port, dptr); ++ } ++ } ++out: spin_unlock_bh(&sipbf_lock); ++ return ret; ++} ++ ++static struct ip_conntrack_helper sip[MAX_PORTS]; ++static char sip_names[MAX_PORTS][10]; ++ ++static void fini(void) ++{ ++ int i = 0; ++ for (; i < ports_c; i++) { ++ DEBUGP("unregistering helper for port %d\n", ports[i]); ++ ip_conntrack_helper_unregister(&sip[i]); ++ } ++} ++ ++static int __init init(void) ++{ ++ int i, ret; ++ char *tmpname; ++ ++ if (ports_c == 0) ++ ports[ports_c++] = SIP_PORT; ++ ++ for (i = 0; i < ports_c; i++) { ++ /* Create helper structure */ ++ memset(&sip[i], 0, sizeof(struct ip_conntrack_helper)); ++ ++ sip[i].tuple.dst.protonum = IPPROTO_UDP; ++ sip[i].tuple.src.u.udp.port = htons(ports[i]); ++ sip[i].mask.src.u.udp.port = 0xFFFF; ++ sip[i].mask.dst.protonum = 0xFF; ++ sip[i].max_expected = 1; ++ sip[i].timeout = 3 * 60; /* 3 minutes */ ++ sip[i].me = THIS_MODULE; ++ sip[i].help = sip_help; ++ ++ tmpname = &sip_names[i][0]; ++ if (ports[i] == SIP_PORT) ++ sprintf(tmpname, "sip"); ++ else ++ sprintf(tmpname, "sip-%d", i); ++ sip[i].name = tmpname; ++ ++ DEBUGP("port #%d: %d\n", i, ports[i]); ++ ++ ret=ip_conntrack_helper_register(&sip[i]); ++ if (ret) { ++ printk("ERROR registering helper for port %d\n", ++ ports[i]); ++ fini(); ++ return(ret); ++ } ++ } ++ return(0); ++} ++ ++module_init(init); ++module_exit(fini); +diff -urN linux-2.6.16.4/net/ipv4/netfilter/ip_nat_sip.c linux-2.6.16.4.new/net/ipv4/netfilter/ip_nat_sip.c +--- linux-2.6.16.4/net/ipv4/netfilter/ip_nat_sip.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16.4.new/net/ipv4/netfilter/ip_nat_sip.c 2006-04-12 17:31:53.000000000 +0200 +@@ -0,0 +1,249 @@ ++/* SIP extension for UDP NAT alteration. ++ * ++ * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar> ++ * based on RR's ip_nat_ftp.c and other modules. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/module.h> ++#include <linux/netfilter_ipv4.h> ++#include <linux/ip.h> ++#include <linux/udp.h> ++#include <net/udp.h> ++ ++#include <linux/netfilter_ipv4/ip_nat.h> ++#include <linux/netfilter_ipv4/ip_nat_helper.h> ++#include <linux/netfilter_ipv4/ip_conntrack_helper.h> ++#include <linux/netfilter_ipv4/ip_conntrack_sip.h> ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>"); ++MODULE_DESCRIPTION("SIP NAT helper"); ++ ++#if 0 ++#define DEBUGP printk ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++extern struct sip_header_nfo ct_sip_hdrs[]; ++ ++static unsigned int mangle_sip_packet(struct sk_buff **pskb, ++ enum ip_conntrack_info ctinfo, ++ struct ip_conntrack *ct, ++ const char **dptr, size_t dlen, ++ char *buffer, int bufflen, ++ struct sip_header_nfo *hnfo) ++{ ++ unsigned int matchlen, matchoff; ++ ++ if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, hnfo) <= 0) ++ return 0; ++ ++ if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo, ++ matchoff, matchlen, buffer, bufflen)) { ++ return 0; ++ } ++ /* We need to reload this. Thanks Patrick. */ ++ *dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); ++ return 1; ++} ++ ++static unsigned int ip_nat_sip(struct sk_buff **pskb, ++ enum ip_conntrack_info ctinfo, ++ struct ip_conntrack *ct, ++ const char **dptr) ++{ ++ char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; ++ unsigned int bufflen, dataoff; ++ uint32_t ip; ++ uint16_t port; ++ ++ dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); ++ ++ if ((ctinfo) >= IP_CT_IS_REPLY) { ++ ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; ++ port = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port; ++ } else { ++ ip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; ++ port = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port; ++ } ++ bufflen = sprintf(buffer, "%u.%u.%u.%u:%u", NIPQUAD(ip), ntohs(port)); ++ ++ /* short packet ? */ ++ if (((*pskb)->len - dataoff) < (sizeof("SIP/2.0") - 1)) ++ return 0; ++ ++ /* Basic rules: requests and responses. */ ++ if (memcmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) == 0) { ++ ++ if ((ctinfo) < IP_CT_IS_REPLY) { ++ mangle_sip_packet(pskb, ctinfo, ct, dptr, ++ (*pskb)->len - dataoff, buffer, bufflen, ++ &ct_sip_hdrs[POS_CONTACT]); ++ return 1; ++ } ++ ++ if (!mangle_sip_packet(pskb, ctinfo, ct, dptr, (*pskb)->len - dataoff, ++ buffer, bufflen, &ct_sip_hdrs[POS_VIA])) { ++ return 0; ++ } ++ ++ /* This search should ignore case, but later.. */ ++ const char *aux = ct_sip_search("CSeq:", *dptr, sizeof("CSeq:") - 1, ++ (*pskb)->len - dataoff); ++ if (!aux) ++ return 0; ++ ++ if (!ct_sip_search("REGISTER", aux, sizeof("REGISTER"), ++ ct_sip_lnlen(aux, *dptr + (*pskb)->len - dataoff))) { ++ return 1; ++ } ++ return mangle_sip_packet(pskb, ctinfo, ct, dptr, (*pskb)->len - dataoff, ++ buffer, bufflen, &ct_sip_hdrs[POS_CONTACT]); ++ } ++ if ((ctinfo) < IP_CT_IS_REPLY) { ++ if (!mangle_sip_packet(pskb, ctinfo, ct, dptr, (*pskb)->len - dataoff, ++ buffer, bufflen, &ct_sip_hdrs[POS_VIA])) { ++ return 0; ++ } ++ ++ /* Mangle Contact if exists only. - watch udp_nat_mangle()! */ ++ mangle_sip_packet(pskb, ctinfo, ct, dptr, (*pskb)->len - dataoff, ++ buffer, bufflen, &ct_sip_hdrs[POS_CONTACT]); ++ return 1; ++ } ++ /* This mangle requests headers. */ ++ return mangle_sip_packet(pskb, ctinfo, ct, dptr, ++ ct_sip_lnlen(*dptr, *dptr + (*pskb)->len - dataoff), ++ buffer, bufflen, &ct_sip_hdrs[POS_REQ_HEADER]); ++} ++ ++static int mangle_content_len(struct sk_buff **pskb, ++ enum ip_conntrack_info ctinfo, ++ struct ip_conntrack *ct, ++ const char *dptr) ++{ ++ unsigned int dataoff, matchoff, matchlen; ++ char buffer[sizeof("65536")]; ++ int bufflen; ++ ++ dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); ++ ++ /* Get actual SDP lenght */ ++ if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff, ++ &matchlen, &ct_sip_hdrs[POS_SDP_HEADER]) > 0) { ++ ++ /* since ct_sip_get_info() give us a pointer passing 'v=' ++ we need to add 2 bytes in this count. */ ++ int c_len = (*pskb)->len - dataoff - matchoff + 2; ++ ++ /* Now, update SDP lenght */ ++ if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff, ++ &matchlen, &ct_sip_hdrs[POS_CONTENT]) > 0) { ++ ++ bufflen = sprintf(buffer, "%u", c_len); ++ ++ return ip_nat_mangle_udp_packet(pskb, ct, ctinfo, matchoff, ++ matchlen, buffer, bufflen); ++ } ++ } ++ return 0; ++} ++ ++static unsigned int mangle_sdp(struct sk_buff **pskb, ++ enum ip_conntrack_info ctinfo, ++ struct ip_conntrack *ct, ++ uint32_t newip, uint16_t port, ++ const char *dptr) ++{ ++ char buffer[sizeof("nnn.nnn.nnn.nnn")]; ++ unsigned int dataoff, bufflen; ++ ++ dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); ++ ++ /* Mangle owner and contact info. */ ++ bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip)); ++ if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, ++ buffer, bufflen, &ct_sip_hdrs[POS_OWNER])) { ++ return 0; ++ } ++ ++ if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, ++ buffer, bufflen, &ct_sip_hdrs[POS_CONECTION])) { ++ return 0; ++ } ++ ++ /* Mangle media port. */ ++ bufflen = sprintf(buffer, "%u", port); ++ if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, ++ buffer, bufflen, &ct_sip_hdrs[POS_MEDIA])) { ++ return 0; ++ } ++ ++ return mangle_content_len(pskb, ctinfo, ct, dptr); ++} ++ ++/* So, this packet has hit the connection tracking matching code. ++ Mangle it, and change the expectation to match the new version. */ ++static unsigned int ip_nat_sdp(struct sk_buff **pskb, ++ enum ip_conntrack_info ctinfo, ++ struct ip_conntrack_expect *exp, ++ const char *dptr) ++{ ++ struct ip_conntrack *ct = exp->master; ++ uint32_t newip; ++ uint16_t port; ++ ++ DEBUGP("ip_nat_sdp():\n"); ++ ++ /* Connection will come from reply */ ++ newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; ++ ++ exp->tuple.dst.ip = newip; ++ exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port; ++ exp->dir = IP_CT_DIR_REPLY; ++ ++ /* When you see the packet, we need to NAT it the same as the ++ this one. */ ++ exp->expectfn = ip_nat_follow_master; ++ ++ /* Try to get same port: if not, try to change it. */ ++ for (port = ntohs(exp->saved_proto.udp.port); port != 0; port++) { ++ exp->tuple.dst.u.udp.port = htons(port); ++ if (ip_conntrack_expect_related(exp) == 0) ++ break; ++ } ++ ++ if (port == 0) ++ return NF_DROP; ++ ++ if (!mangle_sdp(pskb, ctinfo, ct, newip, port, dptr)) { ++ ip_conntrack_unexpect_related(exp); ++ return NF_DROP; ++ } ++ return NF_ACCEPT; ++} ++ ++static void __exit fini(void) ++{ ++ ip_nat_sip_hook = NULL; ++ ip_nat_sdp_hook = NULL; ++ /* Make sure noone calls it, meanwhile. */ ++ synchronize_net(); ++} ++ ++static int __init init(void) ++{ ++ BUG_ON(ip_nat_sip_hook); ++ BUG_ON(ip_nat_sdp_hook); ++ ip_nat_sip_hook = ip_nat_sip; ++ ip_nat_sdp_hook = ip_nat_sdp; ++ return 0; ++} ++ ++module_init(init); ++module_exit(fini); +diff -urN linux-2.6.16.4/net/ipv4/netfilter/Kconfig linux-2.6.16.4.new/net/ipv4/netfilter/Kconfig +--- linux-2.6.16.4/net/ipv4/netfilter/Kconfig 2006-04-12 17:29:19.000000000 +0200 ++++ linux-2.6.16.4.new/net/ipv4/netfilter/Kconfig 2006-04-12 17:32:53.000000000 +0200 +@@ -168,6 +168,19 @@ + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + ++config IP_NF_SIP ++ tristate 'SIP support' ++ depends on IP_NF_CONNTRACK ++ help ++ SIP is an application-layer control protocol that can establish, ++ modify, and terminate multimedia sessions (conferences) such as ++ Internet telephony calls. With the ip_conntrack_sip and ++ the ip_nat_sip modules you can support the protocol on a connection ++ tracking/NATing firewall. ++ ++ If you want to compile it as a module, say 'M' here and read ++ Documentation/modules.txt. If unsure, say 'N'. ++ + config IP_NF_QUEUE + tristate "IP Userspace queueing via NETLINK (OBSOLETE)" + help +@@ -545,6 +558,12 @@ + default IP_NF_NAT if IP_NF_PPTP=y + default m if IP_NF_PPTP=m + ++config IP_NF_NAT_SIP ++ tristate ++ depends on IP_NF_CONNTRACK!=n && IP_NF_NAT!=n ++ default IP_NF_NAT if IP_NF_SIP=y ++ default m if IP_NF_SIP=m ++ + # mangle + specific targets + config IP_NF_MANGLE + tristate "Packet mangling" +diff -urN linux-2.6.16.4/net/ipv4/netfilter/Makefile linux-2.6.16.4.new/net/ipv4/netfilter/Makefile +--- linux-2.6.16.4/net/ipv4/netfilter/Makefile 2006-04-12 17:29:19.000000000 +0200 ++++ linux-2.6.16.4.new/net/ipv4/netfilter/Makefile 2006-04-12 17:33:39.000000000 +0200 +@@ -28,6 +28,7 @@ + obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o + obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o + obj-$(CONFIG_IP_NF_NETBIOS_NS) += ip_conntrack_netbios_ns.o ++obj-$(CONFIG_IP_NF_SIP) += ip_conntrack_sip.o + + # NAT helpers + obj-$(CONFIG_IP_NF_NAT_PPTP) += ip_nat_pptp.o +@@ -35,6 +36,7 @@ + obj-$(CONFIG_IP_NF_NAT_TFTP) += ip_nat_tftp.o + obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o + obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o ++obj-$(CONFIG_IP_NF_NAT_SIP) += ip_nat_sip.o + + # generic IP tables + obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o +diff -urN linux-2.6.16.4/include/linux/netfilter_ipv4/ip_conntrack.h linux-2.6.16.4.new/include/linux/netfilter_ipv4/ip_conntrack.h +--- linux-2.6.16.4/include/linux/netfilter_ipv4/ip_conntrack.h 2006-04-12 17:29:19.000000000 +0200 ++++ linux-2.6.16.4.new/include/linux/netfilter_ipv4/ip_conntrack.h 2006-04-12 17:30:38.000000000 +0200 +@@ -33,6 +33,7 @@ + #include <linux/netfilter_ipv4/ip_conntrack_amanda.h> + #include <linux/netfilter_ipv4/ip_conntrack_ftp.h> + #include <linux/netfilter_ipv4/ip_conntrack_irc.h> ++#include <linux/netfilter_ipv4/ip_conntrack_sip.h> + + /* per conntrack: application helper private data */ + union ip_conntrack_help { +@@ -40,6 +41,7 @@ + struct ip_ct_pptp_master ct_pptp_info; + struct ip_ct_ftp_master ct_ftp_info; + struct ip_ct_irc_master ct_irc_info; ++ struct ip_ct_sip_master ct_sip_info; + }; + + #ifdef CONFIG_IP_NF_NAT_NEEDED +diff -urN linux-2.6.16.4/include/linux/netfilter_ipv4/ip_conntrack_sip.h linux-2.6.16.4.new/include/linux/netfilter_ipv4/ip_conntrack_sip.h +--- linux-2.6.16.4/include/linux/netfilter_ipv4/ip_conntrack_sip.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16.4.new/include/linux/netfilter_ipv4/ip_conntrack_sip.h 2006-04-12 17:31:12.000000000 +0200 +@@ -0,0 +1,81 @@ ++#ifndef __IP_CONNTRACK_SIP_H__ ++#define __IP_CONNTRACK_SIP_H__ ++/* SIP tracking. */ ++ ++#ifdef __KERNEL__ ++ ++#define SIP_PORT 5060 ++#define SIP_TIMEOUT 3600 ++ ++#define POS_VIA 0 ++#define POS_CONTACT 1 ++#define POS_CONTENT 2 ++#define POS_MEDIA 3 ++#define POS_OWNER 4 ++#define POS_CONECTION 5 ++#define POS_REQ_HEADER 6 ++#define POS_SDP_HEADER 7 ++ ++struct ip_ct_sip_master { ++}; ++ ++struct sip_header_nfo { ++ const char *lname; ++ size_t lnlen; ++ const char *sname; ++ size_t snlen; ++ const char *ln_str; ++ size_t ln_strlen; ++ int (*match_len)(const char *, const char *, int *); ++ ++}; ++ ++extern unsigned int (*ip_nat_sip_hook)(struct sk_buff **pskb, ++ enum ip_conntrack_info ctinfo, ++ struct ip_conntrack *ct, ++ const char **dptr); ++ ++/* For NAT to hook in when on expect. */ ++extern unsigned int (*ip_nat_sdp_hook)(struct sk_buff **pskb, ++ enum ip_conntrack_info ctinfo, ++ struct ip_conntrack_expect *exp, ++ const char *dptr); ++ ++extern int ct_sip_get_info(const char *dptr, size_t dlen, ++ unsigned int *matchoff, ++ unsigned int *matchlen, ++ struct sip_header_nfo *hnfo); ++ ++/* get line lenght until first CR or LF seen. */ ++static __inline__ int ct_sip_lnlen(const char *line, const char *limit) ++{ ++ const char *k = line; ++ ++ while ((line <= limit) && (*line == '\r' || *line == '\n')) ++ line++; ++ ++ while (line <= limit) { ++ if (*line == '\r' || *line == '\n') ++ break; ++ line++; ++ } ++ return line - k; ++} ++ ++/* Linear string search, case sensitive. */ ++static __inline__ ++const char *ct_sip_search(const char *needle, const char *haystack, ++ size_t needle_len, size_t haystack_len) ++{ ++ const char *limit = haystack + (haystack_len - needle_len); ++ ++ while (haystack <= limit) { ++ if (memcmp(haystack, needle, needle_len) == 0) ++ return haystack; ++ haystack++; ++ } ++ return NULL; ++} ++#endif /* __KERNEL__ */ ++ ++#endif /* __IP_CONNTRACK_SIP_H__ */ diff --git a/target/linux/generic-2.6/patches/108-netfilter_route.patch b/target/linux/generic-2.6/patches/108-netfilter_route.patch new file mode 100644 index 0000000..571d6a3 --- /dev/null +++ b/target/linux/generic-2.6/patches/108-netfilter_route.patch @@ -0,0 +1,914 @@ +diff -u'rNF^function' linux-2.6.16.7/include/linux/netfilter_ipv4/ipt_ROUTE.h linux-2.6.16.7-ROUTE/include/linux/netfilter_ipv4/ipt_ROUTE.h +--- linux-2.6.16.7/include/linux/netfilter_ipv4/ipt_ROUTE.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16.7-ROUTE/include/linux/netfilter_ipv4/ipt_ROUTE.h 2006-06-14 16:40:49.000000000 +0200 +@@ -0,0 +1,23 @@ ++/* Header file for iptables ipt_ROUTE target ++ * ++ * (C) 2002 by Cédric de Launois <delaunois@info.ucl.ac.be> ++ * ++ * This software is distributed under GNU GPL v2, 1991 ++ */ ++#ifndef _IPT_ROUTE_H_target ++#define _IPT_ROUTE_H_target ++ ++#define IPT_ROUTE_IFNAMSIZ 16 ++ ++struct ipt_route_target_info { ++ char oif[IPT_ROUTE_IFNAMSIZ]; /* Output Interface Name */ ++ char iif[IPT_ROUTE_IFNAMSIZ]; /* Input Interface Name */ ++ u_int32_t gw; /* IP address of gateway */ ++ u_int8_t flags; ++}; ++ ++/* Values for "flags" field */ ++#define IPT_ROUTE_CONTINUE 0x01 ++#define IPT_ROUTE_TEE 0x02 ++ ++#endif /*_IPT_ROUTE_H_target*/ +diff -u'rNF^function' linux-2.6.16.7/include/linux/netfilter_ipv6/ip6t_ROUTE.h linux-2.6.16.7-ROUTE/include/linux/netfilter_ipv6/ip6t_ROUTE.h +--- linux-2.6.16.7/include/linux/netfilter_ipv6/ip6t_ROUTE.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16.7-ROUTE/include/linux/netfilter_ipv6/ip6t_ROUTE.h 2006-06-14 16:41:08.000000000 +0200 +@@ -0,0 +1,23 @@ ++/* Header file for iptables ip6t_ROUTE target ++ * ++ * (C) 2003 by Cédric de Launois <delaunois@info.ucl.ac.be> ++ * ++ * This software is distributed under GNU GPL v2, 1991 ++ */ ++#ifndef _IPT_ROUTE_H_target ++#define _IPT_ROUTE_H_target ++ ++#define IP6T_ROUTE_IFNAMSIZ 16 ++ ++struct ip6t_route_target_info { ++ char oif[IP6T_ROUTE_IFNAMSIZ]; /* Output Interface Name */ ++ char iif[IP6T_ROUTE_IFNAMSIZ]; /* Input Interface Name */ ++ u_int32_t gw[4]; /* IPv6 address of gateway */ ++ u_int8_t flags; ++}; ++ ++/* Values for "flags" field */ ++#define IP6T_ROUTE_CONTINUE 0x01 ++#define IP6T_ROUTE_TEE 0x02 ++ ++#endif /*_IP6T_ROUTE_H_target*/ +diff -u'rNF^function' linux-2.6.16.7/net/ipv4/netfilter/Kconfig linux-2.6.16.7-ROUTE/net/ipv4/netfilter/Kconfig +--- linux-2.6.16.7/net/ipv4/netfilter/Kconfig 2006-06-14 16:05:44.000000000 +0200 ++++ linux-2.6.16.7-ROUTE/net/ipv4/netfilter/Kconfig 2006-06-14 16:46:40.000000000 +0200 +@@ -491,6 +491,23 @@ + + To compile it as a module, choose M here. If unsure, say N. + ++config IP_NF_TARGET_ROUTE ++ tristate 'ROUTE target support' ++ depends on IP_NF_MANGLE ++ help ++ This option adds a `ROUTE' target, which enables you to setup unusual ++ routes. For example, the ROUTE lets you route a received packet through ++ an interface or towards a host, even if the regular destination of the ++ packet is the router itself. The ROUTE target is also able to change the ++ incoming interface of a packet. ++ ++ The target can be or not a final target. It has to be used inside the ++ mangle table. ++ ++ If you want to compile it as a module, say M here and read ++ Documentation/modules.txt. The module will be called ipt_ROUTE.o. ++ If unsure, say `N'. ++ + config IP_NF_TARGET_NETMAP + tristate "NETMAP target support" + depends on IP_NF_NAT +diff -u'rNF^function' linux-2.6.16.7/net/ipv4/netfilter/Makefile linux-2.6.16.7-ROUTE/net/ipv4/netfilter/Makefile +--- linux-2.6.16.7/net/ipv4/netfilter/Makefile 2006-06-14 16:05:44.000000000 +0200 ++++ linux-2.6.16.7-ROUTE/net/ipv4/netfilter/Makefile 2006-06-14 16:44:02.000000000 +0200 +@@ -74,6 +74,7 @@ + obj-$(CONFIG_IP_NF_TARGET_IMQ) += ipt_IMQ.o + obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o + obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o ++obj-$(CONFIG_IP_NF_TARGET_ROUTE) += ipt_ROUTE.o + obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o + obj-$(CONFIG_IP_NF_TARGET_SAME) += ipt_SAME.o + obj-$(CONFIG_IP_NF_NAT_SNMP_BASIC) += ip_nat_snmp_basic.o +diff -u'rNF^function' linux-2.6.16.7/net/ipv4/netfilter/ipt_ROUTE.c linux-2.6.16.7-ROUTE/net/ipv4/netfilter/ipt_ROUTE.c +--- linux-2.6.16.7/net/ipv4/netfilter/ipt_ROUTE.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16.7-ROUTE/net/ipv4/netfilter/ipt_ROUTE.c 2006-06-14 16:42:23.000000000 +0200 +@@ -0,0 +1,461 @@ ++/* ++ * This implements the ROUTE target, which enables you to setup unusual ++ * routes not supported by the standard kernel routing table. ++ * ++ * Copyright (C) 2002 Cedric de Launois <delaunois@info.ucl.ac.be> ++ * ++ * v 1.11 2004/11/23 ++ * ++ * This software is distributed under GNU GPL v2, 1991 ++ */ ++ ++#include <linux/module.h> ++#include <linux/skbuff.h> ++#include <linux/ip.h> ++#include <linux/netfilter_ipv4/ip_tables.h> ++#include <linux/netfilter_ipv4/ip_conntrack.h> ++#include <linux/netfilter_ipv4/ipt_ROUTE.h> ++#include <linux/netdevice.h> ++#include <linux/route.h> ++#include <linux/if_arp.h> ++#include <net/ip.h> ++#include <net/route.h> ++#include <net/icmp.h> ++#include <net/checksum.h> ++ ++#if 0 ++#define DEBUGP printk ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Cedric de Launois <delaunois@info.ucl.ac.be>"); ++MODULE_DESCRIPTION("iptables ROUTE target module"); ++ ++/* Try to route the packet according to the routing keys specified in ++ * route_info. Keys are : ++ * - ifindex : ++ * 0 if no oif preferred, ++ * otherwise set to the index of the desired oif ++ * - route_info->gw : ++ * 0 if no gateway specified, ++ * otherwise set to the next host to which the pkt must be routed ++ * If success, skb->dev is the output device to which the packet must ++ * be sent and skb->dst is not NULL ++ * ++ * RETURN: -1 if an error occured ++ * 1 if the packet was succesfully routed to the ++ * destination desired ++ * 0 if the kernel routing table could not route the packet ++ * according to the keys specified ++ */ ++static int route(struct sk_buff *skb, ++ unsigned int ifindex, ++ const struct ipt_route_target_info *route_info) ++{ ++ int err; ++ struct rtable *rt; ++ struct iphdr *iph = skb->nh.iph; ++ struct flowi fl = { ++ .oif = ifindex, ++ .nl_u = { ++ .ip4_u = { ++ .daddr = iph->daddr, ++ .saddr = 0, ++ .tos = RT_TOS(iph->tos), ++ .scope = RT_SCOPE_UNIVERSE, ++ } ++ } ++ }; ++ ++ /* The destination address may be overloaded by the target */ ++ if (route_info->gw) ++ fl.fl4_dst = route_info->gw; ++ ++ /* Trying to route the packet using the standard routing table. */ ++ if ((err = ip_route_output_key(&rt, &fl))) { ++ if (net_ratelimit()) ++ DEBUGP("ipt_ROUTE: couldn't route pkt (err: %i)",err); ++ return -1; ++ } ++ ++ /* Drop old route. */ ++ dst_release(skb->dst); ++ skb->dst = NULL; ++ ++ /* Success if no oif specified or if the oif correspond to the ++ * one desired */ ++ if (!ifindex || rt->u.dst.dev->ifindex == ifindex) { ++ skb->dst = &rt->u.dst; ++ skb->dev = skb->dst->dev; ++ skb->protocol = htons(ETH_P_IP); ++ return 1; ++ } ++ ++ /* The interface selected by the routing table is not the one ++ * specified by the user. This may happen because the dst address ++ * is one of our own addresses. ++ */ ++ if (net_ratelimit()) ++ DEBUGP("ipt_ROUTE: failed to route as desired gw=%u.%u.%u.%u oif=%i (got oif=%i)\n", ++ NIPQUAD(route_info->gw), ifindex, rt->u.dst.dev->ifindex); ++ ++ return 0; ++} ++ ++ ++/* Stolen from ip_finish_output2 ++ * PRE : skb->dev is set to the device we are leaving by ++ * skb->dst is not NULL ++ * POST: the packet is sent with the link layer header pushed ++ * the packet is destroyed ++ */ ++static void ip_direct_send(struct sk_buff *skb) ++{ ++ struct dst_entry *dst = skb->dst; ++ struct hh_cache *hh = dst->hh; ++ struct net_device *dev = dst->dev; ++ int hh_len = LL_RESERVED_SPACE(dev); ++ ++ /* Be paranoid, rather than too clever. */ ++ if (unlikely(skb_headroom(skb) < hh_len && dev->hard_header)) { ++ struct sk_buff *skb2; ++ ++ skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev)); ++ if (skb2 == NULL) { ++ kfree_skb(skb); ++ return; ++ } ++ if (skb->sk) ++ skb_set_owner_w(skb2, skb->sk); ++ kfree_skb(skb); ++ skb = skb2; ++ } ++ ++ if (hh) { ++ int hh_alen; ++ ++ read_lock_bh(&hh->hh_lock); ++ hh_alen = HH_DATA_ALIGN(hh->hh_len); ++ memcpy(skb->data - hh_alen, hh->hh_data, hh_alen); ++ read_unlock_bh(&hh->hh_lock); ++ skb_push(skb, hh->hh_len); ++ hh->hh_output(skb); ++ } else if (dst->neighbour) ++ dst->neighbour->output(skb); ++ else { ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ipt_ROUTE: no hdr & no neighbour cache!\n"); ++ kfree_skb(skb); ++ } ++} ++ ++ ++/* PRE : skb->dev is set to the device we are leaving by ++ * POST: - the packet is directly sent to the skb->dev device, without ++ * pushing the link layer header. ++ * - the packet is destroyed ++ */ ++static inline int dev_direct_send(struct sk_buff *skb) ++{ ++ return dev_queue_xmit(skb); ++} ++ ++ ++static unsigned int route_oif(const struct ipt_route_target_info *route_info, ++ struct sk_buff *skb) ++{ ++ unsigned int ifindex = 0; ++ struct net_device *dev_out = NULL; ++ ++ /* The user set the interface name to use. ++ * Getting the current interface index. ++ */ ++ if ((dev_out = dev_get_by_name(route_info->oif))) { ++ ifindex = dev_out->ifindex; ++ } else { ++ /* Unknown interface name : packet dropped */ ++ if (net_ratelimit()) ++ DEBUGP("ipt_ROUTE: oif interface %s not found\n", route_info->oif); ++ return NF_DROP; ++ } ++ ++ /* Trying the standard way of routing packets */ ++ switch (route(skb, ifindex, route_info)) { ++ case 1: ++ dev_put(dev_out); ++ if (route_info->flags & IPT_ROUTE_CONTINUE) ++ return IPT_CONTINUE; ++ ++ ip_direct_send(skb); ++ return NF_STOLEN; ++ ++ case 0: ++ /* Failed to send to oif. Trying the hard way */ ++ if (route_info->flags & IPT_ROUTE_CONTINUE) ++ return NF_DROP; ++ ++ if (net_ratelimit()) ++ DEBUGP("ipt_ROUTE: forcing the use of %i\n", ++ ifindex); ++ ++ /* We have to force the use of an interface. ++ * This interface must be a tunnel interface since ++ * otherwise we can't guess the hw address for ++ * the packet. For a tunnel interface, no hw address ++ * is needed. ++ */ ++ if ((dev_out->type != ARPHRD_TUNNEL) ++ && (dev_out->type != ARPHRD_IPGRE)) { ++ if (net_ratelimit()) ++ DEBUGP("ipt_ROUTE: can't guess the hw addr !\n"); ++ dev_put(dev_out); ++ return NF_DROP; ++ } ++ ++ /* Send the packet. This will also free skb ++ * Do not go through the POST_ROUTING hook because ++ * skb->dst is not set and because it will probably ++ * get confused by the destination IP address. ++ */ ++ skb->dev = dev_out; ++ dev_direct_send(skb); ++ dev_put(dev_out); ++ return NF_STOLEN; ++ ++ default: ++ /* Unexpected error */ ++ dev_put(dev_out); ++ return NF_DROP; ++ } ++} ++ ++ ++static unsigned int route_iif(const struct ipt_route_target_info *route_info, ++ struct sk_buff *skb) ++{ ++ struct net_device *dev_in = NULL; ++ ++ /* Getting the current interface index. */ ++ if (!(dev_in = dev_get_by_name(route_info->iif))) { ++ if (net_ratelimit()) ++ DEBUGP("ipt_ROUTE: iif interface %s not found\n", route_info->iif); ++ return NF_DROP; ++ } ++ ++ skb->dev = dev_in; ++ dst_release(skb->dst); ++ skb->dst = NULL; ++ ++ netif_rx(skb); ++ dev_put(dev_in); ++ return NF_STOLEN; ++} ++ ++ ++static unsigned int route_gw(const struct ipt_route_target_info *route_info, ++ struct sk_buff *skb) ++{ ++ if (route(skb, 0, route_info)!=1) ++ return NF_DROP; ++ ++ if (route_info->flags & IPT_ROUTE_CONTINUE) ++ return IPT_CONTINUE; ++ ++ ip_direct_send(skb); ++ return NF_STOLEN; ++} ++ ++ ++/* To detect and deter routed packet loopback when using the --tee option, ++ * we take a page out of the raw.patch book: on the copied skb, we set up ++ * a fake ->nfct entry, pointing to the local &route_tee_track. We skip ++ * routing packets when we see they already have that ->nfct. ++ */ ++ ++static struct ip_conntrack route_tee_track; ++ ++static unsigned int ipt_route_target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++ const void *targinfo, ++ void *userinfo) ++{ ++ const struct ipt_route_target_info *route_info = targinfo; ++ struct sk_buff *skb = *pskb; ++ unsigned int res; ++ ++ if (skb->nfct == &route_tee_track.ct_general) { ++ /* Loopback - a packet we already routed, is to be ++ * routed another time. Avoid that, now. ++ */ ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ipt_ROUTE: loopback - DROP!\n"); ++ return NF_DROP; ++ } ++ ++ /* If we are at PREROUTING or INPUT hook ++ * the TTL isn't decreased by the IP stack ++ */ ++ if (hooknum == NF_IP_PRE_ROUTING || ++ hooknum == NF_IP_LOCAL_IN) { ++ ++ struct iphdr *iph = skb->nh.iph; ++ ++ if (iph->ttl <= 1) { ++ struct rtable *rt; ++ struct flowi fl = { ++ .oif = 0, ++ .nl_u = { ++ .ip4_u = { ++ .daddr = iph->daddr, ++ .saddr = iph->saddr, ++ .tos = RT_TOS(iph->tos), ++ .scope = ((iph->tos & RTO_ONLINK) ? ++ RT_SCOPE_LINK : ++ RT_SCOPE_UNIVERSE) ++ } ++ } ++ }; ++ ++ if (ip_route_output_key(&rt, &fl)) { ++ return NF_DROP; ++ } ++ ++ if (skb->dev == rt->u.dst.dev) { ++ /* Drop old route. */ ++ dst_release(skb->dst); ++ skb->dst = &rt->u.dst; ++ ++ /* this will traverse normal stack, and ++ * thus call conntrack on the icmp packet */ ++ icmp_send(skb, ICMP_TIME_EXCEEDED, ++ ICMP_EXC_TTL, 0); ++ } ++ ++ return NF_DROP; ++ } ++ ++ /* ++ * If we are at INPUT the checksum must be recalculated since ++ * the length could change as the result of a defragmentation. ++ */ ++ if(hooknum == NF_IP_LOCAL_IN) { ++ iph->ttl = iph->ttl - 1; ++ iph->check = 0; ++ iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); ++ } else { ++ ip_decrease_ttl(iph); ++ } ++ } ++ ++ if ((route_info->flags & IPT_ROUTE_TEE)) { ++ /* ++ * Copy the *pskb, and route the copy. Will later return ++ * IPT_CONTINUE for the original skb, which should continue ++ * on its way as if nothing happened. The copy should be ++ * independantly delivered to the ROUTE --gw. ++ */ ++ skb = skb_copy(*pskb, GFP_ATOMIC); ++ if (!skb) { ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ipt_ROUTE: copy failed!\n"); ++ return IPT_CONTINUE; ++ } ++ } ++ ++ /* Tell conntrack to forget this packet since it may get confused ++ * when a packet is leaving with dst address == our address. ++ * Good idea ? Dunno. Need advice. ++ * ++ * NEW: mark the skb with our &route_tee_track, so we avoid looping ++ * on any already routed packet. ++ */ ++ if (!(route_info->flags & IPT_ROUTE_CONTINUE)) { ++ nf_conntrack_put(skb->nfct); ++ skb->nfct = &route_tee_track.ct_general; ++ skb->nfctinfo = IP_CT_NEW; ++ nf_conntrack_get(skb->nfct); ++ } ++ ++ if (route_info->oif[0] != '\0') { ++ res = route_oif(route_info, skb); ++ } else if (route_info->iif[0] != '\0') { ++ res = route_iif(route_info, skb); ++ } else if (route_info->gw) { ++ res = route_gw(route_info, skb); ++ } else { ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ipt_ROUTE: no parameter !\n"); ++ res = IPT_CONTINUE; ++ } ++ ++ if ((route_info->flags & IPT_ROUTE_TEE)) ++ res = IPT_CONTINUE; ++ ++ return res; ++} ++ ++ ++static int ipt_route_checkentry(const char *tablename, ++ const void *e, ++ void *targinfo, ++ unsigned int targinfosize, ++ unsigned int hook_mask) ++{ ++ if (strcmp(tablename, "mangle") != 0) { ++ printk("ipt_ROUTE: bad table `%s', use the `mangle' table.\n", ++ tablename); ++ return 0; ++ } ++ ++ if (hook_mask & ~( (1 << NF_IP_PRE_ROUTING) ++ | (1 << NF_IP_LOCAL_IN) ++ | (1 << NF_IP_FORWARD) ++ | (1 << NF_IP_LOCAL_OUT) ++ | (1 << NF_IP_POST_ROUTING))) { ++ printk("ipt_ROUTE: bad hook\n"); ++ return 0; ++ } ++ ++ if (targinfosize != IPT_ALIGN(sizeof(struct ipt_route_target_info))) { ++ printk(KERN_WARNING "ipt_ROUTE: targinfosize %u != %Zu\n", ++ targinfosize, ++ IPT_ALIGN(sizeof(struct ipt_route_target_info))); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++ ++static struct ipt_target ipt_route_reg = { ++ .name = "ROUTE", ++ .target = ipt_route_target, ++ .checkentry = ipt_route_checkentry, ++ .me = THIS_MODULE, ++}; ++ ++static int __init init(void) ++{ ++ /* Set up fake conntrack (stolen from raw.patch): ++ - to never be deleted, not in any hashes */ ++ atomic_set(&route_tee_track.ct_general.use, 1); ++ /* - and look it like as a confirmed connection */ ++ set_bit(IPS_CONFIRMED_BIT, &route_tee_track.status); ++ /* Initialize fake conntrack so that NAT will skip it */ ++ route_tee_track.status |= IPS_NAT_DONE_MASK; ++ ++ return ipt_register_target(&ipt_route_reg); ++} ++ ++ ++static void __exit fini(void) ++{ ++ ipt_unregister_target(&ipt_route_reg); ++} ++ ++module_init(init); ++module_exit(fini); +diff -u'rNF^function' linux-2.6.16.7/net/ipv6/ipv6_syms.c linux-2.6.16.7-ROUTE/net/ipv6/ipv6_syms.c +--- linux-2.6.16.7/net/ipv6/ipv6_syms.c 2006-04-17 23:53:25.000000000 +0200 ++++ linux-2.6.16.7-ROUTE/net/ipv6/ipv6_syms.c 2006-06-14 17:02:32.000000000 +0200 +@@ -12,6 +12,7 @@ + EXPORT_SYMBOL(icmpv6_statistics); + EXPORT_SYMBOL(icmpv6_err_convert); + EXPORT_SYMBOL(ndisc_mc_map); ++EXPORT_SYMBOL(nd_tbl); + EXPORT_SYMBOL(register_inet6addr_notifier); + EXPORT_SYMBOL(unregister_inet6addr_notifier); + EXPORT_SYMBOL(ip6_route_output); +diff -u'rNF^function' linux-2.6.16.7/net/ipv6/netfilter/Kconfig linux-2.6.16.7-ROUTE/net/ipv6/netfilter/Kconfig +--- linux-2.6.16.7/net/ipv6/netfilter/Kconfig 2006-06-14 16:05:44.000000000 +0200 ++++ linux-2.6.16.7-ROUTE/net/ipv6/netfilter/Kconfig 2006-06-14 16:45:45.000000000 +0200 +@@ -182,6 +182,19 @@ + + To compile it as a module, choose M here. If unsure, say N. + ++config IP6_NF_TARGET_ROUTE ++ tristate ' ROUTE target support' ++ depends on IP6_NF_MANGLE ++ help ++ This option adds a `ROUTE' target, which enables you to setup unusual ++ routes. The ROUTE target is also able to change the incoming interface ++ of a packet. ++ ++ The target can be or not a final target. It has to be used inside the ++ mangle table. ++ ++ Not working as a module. ++ + config IP6_NF_MANGLE + tristate "Packet mangling" + depends on IP6_NF_IPTABLES +diff -u'rNF^function' linux-2.6.16.7/net/ipv6/netfilter/Makefile linux-2.6.16.7-ROUTE/net/ipv6/netfilter/Makefile +--- linux-2.6.16.7/net/ipv6/netfilter/Makefile 2006-06-14 16:05:44.000000000 +0200 ++++ linux-2.6.16.7-ROUTE/net/ipv6/netfilter/Makefile 2006-06-14 16:45:00.000000000 +0200 +@@ -22,6 +22,7 @@ + obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o + obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o + obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o ++obj-$(CONFIG_IP6_NF_TARGET_ROUTE) += ip6t_ROUTE.o + + # objects for l3 independent conntrack + nf_conntrack_ipv6-objs := nf_conntrack_l3proto_ipv6.o nf_conntrack_proto_icmpv6.o nf_conntrack_reasm.o +diff -u'rNF^function' linux-2.6.16.7/net/ipv6/netfilter/ip6t_ROUTE.c linux-2.6.16.7-ROUTE/net/ipv6/netfilter/ip6t_ROUTE.c +--- linux-2.6.16.7/net/ipv6/netfilter/ip6t_ROUTE.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.16.7-ROUTE/net/ipv6/netfilter/ip6t_ROUTE.c 2006-06-14 16:42:44.000000000 +0200 +@@ -0,0 +1,308 @@ ++/* ++ * This implements the ROUTE v6 target, which enables you to setup unusual ++ * routes not supported by the standard kernel routing table. ++ * ++ * Copyright (C) 2003 Cedric de Launois <delaunois@info.ucl.ac.be> ++ * ++ * v 1.1 2004/11/23 ++ * ++ * This software is distributed under GNU GPL v2, 1991 ++ */ ++ ++#include <linux/module.h> ++#include <linux/skbuff.h> ++#include <linux/ipv6.h> ++#include <linux/netfilter_ipv6/ip6_tables.h> ++#include <linux/netfilter_ipv6/ip6t_ROUTE.h> ++#include <linux/netdevice.h> ++#include <net/ipv6.h> ++#include <net/ndisc.h> ++#include <net/ip6_route.h> ++#include <linux/icmpv6.h> ++ ++#if 1 ++#define DEBUGP printk ++#else ++#define DEBUGP(format, args...) ++#endif ++ ++#define NIP6(addr) \ ++ ntohs((addr).s6_addr16[0]), \ ++ ntohs((addr).s6_addr16[1]), \ ++ ntohs((addr).s6_addr16[2]), \ ++ ntohs((addr).s6_addr16[3]), \ ++ ntohs((addr).s6_addr16[4]), \ ++ ntohs((addr).s6_addr16[5]), \ ++ ntohs((addr).s6_addr16[6]), \ ++ ntohs((addr).s6_addr16[7]) ++ ++/* Route the packet according to the routing keys specified in ++ * route_info. Keys are : ++ * - ifindex : ++ * 0 if no oif preferred, ++ * otherwise set to the index of the desired oif ++ * - route_info->gw : ++ * 0 if no gateway specified, ++ * otherwise set to the next host to which the pkt must be routed ++ * If success, skb->dev is the output device to which the packet must ++ * be sent and skb->dst is not NULL ++ * ++ * RETURN: 1 if the packet was succesfully routed to the ++ * destination desired ++ * 0 if the kernel routing table could not route the packet ++ * according to the keys specified ++ */ ++static int ++route6(struct sk_buff *skb, ++ unsigned int ifindex, ++ const struct ip6t_route_target_info *route_info) ++{ ++ struct rt6_info *rt = NULL; ++ struct ipv6hdr *ipv6h = skb->nh.ipv6h; ++ struct in6_addr *gw = (struct in6_addr*)&route_info->gw; ++ ++ DEBUGP("ip6t_ROUTE: called with: "); ++ DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(ipv6h->daddr)); ++ DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(*gw)); ++ DEBUGP("OUT=%s\n", route_info->oif); ++ ++ if (ipv6_addr_any(gw)) ++ rt = rt6_lookup(&ipv6h->daddr, &ipv6h->saddr, ifindex, 1); ++ else ++ rt = rt6_lookup(gw, &ipv6h->saddr, ifindex, 1); ++ ++ if (!rt) ++ goto no_route; ++ ++ DEBUGP("ip6t_ROUTE: routing gives: "); ++ DEBUGP("DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_dst.addr)); ++ DEBUGP("GATEWAY=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", NIP6(rt->rt6i_gateway)); ++ DEBUGP("OUT=%s\n", rt->rt6i_dev->name); ++ ++ if (ifindex && rt->rt6i_dev->ifindex!=ifindex) ++ goto wrong_route; ++ ++ if (!rt->rt6i_nexthop) { ++ DEBUGP("ip6t_ROUTE: discovering neighbour\n"); ++ rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_dst.addr); ++ } ++ ++ /* Drop old route. */ ++ dst_release(skb->dst); ++ skb->dst = &rt->u.dst; ++ skb->dev = rt->rt6i_dev; ++ return 1; ++ ++ wrong_route: ++ dst_release(&rt->u.dst); ++ no_route: ++ if (!net_ratelimit()) ++ return 0; ++ ++ printk("ip6t_ROUTE: no explicit route found "); ++ if (ifindex) ++ printk("via interface %s ", route_info->oif); ++ if (!ipv6_addr_any(gw)) ++ printk("via gateway %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", NIP6(*gw)); ++ printk("\n"); ++ return 0; ++} ++ ++ ++/* Stolen from ip6_output_finish ++ * PRE : skb->dev is set to the device we are leaving by ++ * skb->dst is not NULL ++ * POST: the packet is sent with the link layer header pushed ++ * the packet is destroyed ++ */ ++static void ip_direct_send(struct sk_buff *skb) ++{ ++ struct dst_entry *dst = skb->dst; ++ struct hh_cache *hh = dst->hh; ++ ++ if (hh) { ++ read_lock_bh(&hh->hh_lock); ++ memcpy(skb->data - 16, hh->hh_data, 16); ++ read_unlock_bh(&hh->hh_lock); ++ skb_push(skb, hh->hh_len); ++ hh->hh_output(skb); ++ } else if (dst->neighbour) ++ dst->neighbour->output(skb); ++ else { ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ip6t_ROUTE: no hdr & no neighbour cache!\n"); ++ kfree_skb(skb); ++ } ++} ++ ++ ++static unsigned int ++route6_oif(const struct ip6t_route_target_info *route_info, ++ struct sk_buff *skb) ++{ ++ unsigned int ifindex = 0; ++ struct net_device *dev_out = NULL; ++ ++ /* The user set the interface name to use. ++ * Getting the current interface index. ++ */ ++ if ((dev_out = dev_get_by_name(route_info->oif))) { ++ ifindex = dev_out->ifindex; ++ } else { ++ /* Unknown interface name : packet dropped */ ++ if (net_ratelimit()) ++ DEBUGP("ip6t_ROUTE: oif interface %s not found\n", route_info->oif); ++ ++ if (route_info->flags & IP6T_ROUTE_CONTINUE) ++ return IP6T_CONTINUE; ++ else ++ return NF_DROP; ++ } ++ ++ /* Trying the standard way of routing packets */ ++ if (route6(skb, ifindex, route_info)) { ++ dev_put(dev_out); ++ if (route_info->flags & IP6T_ROUTE_CONTINUE) ++ return IP6T_CONTINUE; ++ ++ ip_direct_send(skb); ++ return NF_STOLEN; ++ } else ++ return NF_DROP; ++} ++ ++ ++static unsigned int ++route6_gw(const struct ip6t_route_target_info *route_info, ++ struct sk_buff *skb) ++{ ++ if (route6(skb, 0, route_info)) { ++ if (route_info->flags & IP6T_ROUTE_CONTINUE) ++ return IP6T_CONTINUE; ++ ++ ip_direct_send(skb); ++ return NF_STOLEN; ++ } else ++ return NF_DROP; ++} ++ ++ ++static unsigned int ++ip6t_route_target(struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ unsigned int hooknum, ++ const void *targinfo, ++ void *userinfo) ++{ ++ const struct ip6t_route_target_info *route_info = targinfo; ++ struct sk_buff *skb = *pskb; ++ struct in6_addr *gw = (struct in6_addr*)&route_info->gw; ++ unsigned int res; ++ ++ if (route_info->flags & IP6T_ROUTE_CONTINUE) ++ goto do_it; ++ ++ /* If we are at PREROUTING or INPUT hook ++ * the TTL isn't decreased by the IP stack ++ */ ++ if (hooknum == NF_IP6_PRE_ROUTING || ++ hooknum == NF_IP6_LOCAL_IN) { ++ ++ struct ipv6hdr *ipv6h = skb->nh.ipv6h; ++ ++ if (ipv6h->hop_limit <= 1) { ++ /* Force OUTPUT device used as source address */ ++ skb->dev = skb->dst->dev; ++ ++ icmpv6_send(skb, ICMPV6_TIME_EXCEED, ++ ICMPV6_EXC_HOPLIMIT, 0, skb->dev); ++ ++ return NF_DROP; ++ } ++ ++ ipv6h->hop_limit--; ++ } ++ ++ if ((route_info->flags & IP6T_ROUTE_TEE)) { ++ /* ++ * Copy the *pskb, and route the copy. Will later return ++ * IP6T_CONTINUE for the original skb, which should continue ++ * on its way as if nothing happened. The copy should be ++ * independantly delivered to the ROUTE --gw. ++ */ ++ skb = skb_copy(*pskb, GFP_ATOMIC); ++ if (!skb) { ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ip6t_ROUTE: copy failed!\n"); ++ return IP6T_CONTINUE; ++ } ++ } ++ ++do_it: ++ if (route_info->oif[0]) { ++ res = route6_oif(route_info, skb); ++ } else if (!ipv6_addr_any(gw)) { ++ res = route6_gw(route_info, skb); ++ } else { ++ if (net_ratelimit()) ++ DEBUGP(KERN_DEBUG "ip6t_ROUTE: no parameter !\n"); ++ res = IP6T_CONTINUE; ++ } ++ ++ if ((route_info->flags & IP6T_ROUTE_TEE)) ++ res = IP6T_CONTINUE; ++ ++ return res; ++} ++ ++ ++static int ++ip6t_route_checkentry(const char *tablename, ++ const struct ip6t_entry *e, ++ void *targinfo, ++ unsigned int targinfosize, ++ unsigned int hook_mask) ++{ ++ if (strcmp(tablename, "mangle") != 0) { ++ printk("ip6t_ROUTE: can only be called from \"mangle\" table.\n"); ++ return 0; ++ } ++ ++ if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_route_target_info))) { ++ printk(KERN_WARNING "ip6t_ROUTE: targinfosize %u != %Zu\n", ++ targinfosize, ++ IP6T_ALIGN(sizeof(struct ip6t_route_target_info))); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++ ++static struct ip6t_target ip6t_route_reg = { ++ .name = "ROUTE", ++ .target = ip6t_route_target, ++ .checkentry = ip6t_route_checkentry, ++ .me = THIS_MODULE ++}; ++ ++ ++static int __init init(void) ++{ ++ printk(KERN_DEBUG "registering ipv6 ROUTE target\n"); ++ if (ip6t_register_target(&ip6t_route_reg)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++ ++static void __exit fini(void) ++{ ++ ip6t_unregister_target(&ip6t_route_reg); ++} ++ ++module_init(init); ++module_exit(fini); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic-2.6/patches/200-sched_esfq.patch b/target/linux/generic-2.6/patches/200-sched_esfq.patch new file mode 100644 index 0000000..8790bad --- /dev/null +++ b/target/linux/generic-2.6/patches/200-sched_esfq.patch @@ -0,0 +1,730 @@ +diff -Naur linux-2.6.15.1.orig/include/linux/pkt_sched.h linux-2.6.15.1/include/linux/pkt_sched.h +--- linux-2.6.15.1.orig/include/linux/pkt_sched.h 2006-01-14 22:16:02.000000000 -0800 ++++ linux-2.6.15.1/include/linux/pkt_sched.h 2006-01-30 16:02:32.000000000 -0800 +@@ -146,8 +146,35 @@ + * + * The only reason for this is efficiency, it is possible + * to change these parameters in compile time. ++ * ++ * If you need to play with these values use esfq instead. + */ + ++/* ESFQ section */ ++ ++enum ++{ ++ /* traditional */ ++ TCA_SFQ_HASH_CLASSIC, ++ TCA_SFQ_HASH_DST, ++ TCA_SFQ_HASH_SRC, ++ TCA_SFQ_HASH_FWMARK, ++ /* direct */ ++ TCA_SFQ_HASH_DSTDIR, ++ TCA_SFQ_HASH_SRCDIR, ++ TCA_SFQ_HASH_FWMARKDIR, ++}; ++ ++struct tc_esfq_qopt ++{ ++ unsigned quantum; /* Bytes per round allocated to flow */ ++ int perturb_period; /* Period of hash perturbation */ ++ __u32 limit; /* Maximal packets in queue */ ++ unsigned divisor; /* Hash divisor */ ++ unsigned flows; /* Maximal number of flows */ ++ unsigned hash_kind; /* Hash function to use for flow identification */ ++}; ++ + /* RED section */ + + enum +diff -Naur linux-2.6.15.1.orig/net/sched/Kconfig linux-2.6.15.1/net/sched/Kconfig +--- linux-2.6.15.1.orig/net/sched/Kconfig 2006-01-14 22:16:02.000000000 -0800 ++++ linux-2.6.15.1/net/sched/Kconfig 2006-01-30 16:02:32.000000000 -0800 +@@ -185,6 +185,28 @@ + To compile this code as a module, choose M here: the + module will be called sch_sfq. + ++config NET_SCH_ESFQ ++ tristate "ESFQ queue" ++ depends on NET_SCHED ++ ---help--- ++ Say Y here if you want to use the Enhanced Stochastic Fairness ++ Queueing (ESFQ) packet scheduling algorithm for some of your network ++ devices or as a leaf discipline for a classful qdisc such as HTB or ++ CBQ (see the top of <file:net/sched/sch_esfq.c> for details and ++ references to the SFQ algorithm). ++ ++ This is an enchanced SFQ version which allows you to control some ++ hardcoded values in the SFQ scheduler: queue depth, hash table size, ++ and queues limit. ++ ++ ESFQ also adds control to the hash function used to identify packet ++ flows. The original SFQ hashes by individual flow (TCP session or UDP ++ stream); ESFQ can hash by src or dst IP as well, which can be more ++ fair to users in some networking situations. ++ ++ To compile this code as a module, choose M here: the ++ module will be called sch_esfq. ++ + config NET_SCH_TEQL + tristate "True Link Equalizer (TEQL)" + ---help--- +diff -Naur linux-2.6.15.1.orig/net/sched/Makefile linux-2.6.15.1/net/sched/Makefile +--- linux-2.6.15.1.orig/net/sched/Makefile 2006-01-14 22:16:02.000000000 -0800 ++++ linux-2.6.15.1/net/sched/Makefile 2006-01-30 16:02:32.000000000 -0800 +@@ -23,6 +23,7 @@ + obj-$(CONFIG_NET_SCH_INGRESS) += sch_ingress.o + obj-$(CONFIG_NET_SCH_DSMARK) += sch_dsmark.o + obj-$(CONFIG_NET_SCH_SFQ) += sch_sfq.o ++obj-$(CONFIG_NET_SCH_ESFQ) += sch_esfq.o + obj-$(CONFIG_NET_SCH_TBF) += sch_tbf.o + obj-$(CONFIG_NET_SCH_TEQL) += sch_teql.o + obj-$(CONFIG_NET_SCH_PRIO) += sch_prio.o +diff -Naur linux-2.6.15.1.orig/net/sched/sch_esfq.c linux-2.6.15.1/net/sched/sch_esfq.c +--- linux-2.6.15.1.orig/net/sched/sch_esfq.c 1969-12-31 16:00:00.000000000 -0800 ++++ linux-2.6.15.1/net/sched/sch_esfq.c 2006-01-30 16:12:29.000000000 -0800 +@@ -0,0 +1,644 @@ ++/* ++ * net/sched/sch_esfq.c Extended Stochastic Fairness Queueing discipline. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ * ++ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> ++ * ++ * Changes: Alexander Atanasov, <alex@ssi.bg> ++ * Added dynamic depth,limit,divisor,hash_kind options. ++ * Added dst and src hashes. ++ * ++ * Alexander Clouter, <alex@digriz.org.uk> ++ * Ported ESFQ to Linux 2.6. ++ * ++ * Corey Hickey, <bugfood-c@fatooh.org> ++ * Maintenance of the Linux 2.6 port. ++ * Added fwmark hash (thanks to Robert Kurjata) ++ * Added direct hashing for src, dst, and fwmark. ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <asm/uaccess.h> ++#include <asm/system.h> ++#include <linux/bitops.h> ++#include <linux/types.h> ++#include <linux/kernel.h> ++#include <linux/jiffies.h> ++#include <linux/string.h> ++#include <linux/mm.h> ++#include <linux/socket.h> ++#include <linux/sockios.h> ++#include <linux/in.h> ++#include <linux/errno.h> ++#include <linux/interrupt.h> ++#include <linux/if_ether.h> ++#include <linux/inet.h> ++#include <linux/netdevice.h> ++#include <linux/etherdevice.h> ++#include <linux/notifier.h> ++#include <linux/init.h> ++#include <net/ip.h> ++#include <linux/ipv6.h> ++#include <net/route.h> ++#include <linux/skbuff.h> ++#include <net/sock.h> ++#include <net/pkt_sched.h> ++ ++ ++/* Stochastic Fairness Queuing algorithm. ++ For more comments look at sch_sfq.c. ++ The difference is that you can change limit, depth, ++ hash table size and choose 7 hash types. ++ ++ classic: same as in sch_sfq.c ++ dst: destination IP address ++ src: source IP address ++ fwmark: netfilter mark value ++ dst_direct: ++ src_direct: ++ fwmark_direct: direct hashing of the above sources ++ ++ TODO: ++ make sfq_change work. ++*/ ++ ++ ++/* This type should contain at least SFQ_DEPTH*2 values */ ++typedef unsigned int esfq_index; ++ ++struct esfq_head ++{ ++ esfq_index next; ++ esfq_index prev; ++}; ++ ++struct esfq_sched_data ++{ ++/* Parameters */ ++ int perturb_period; ++ unsigned quantum; /* Allotment per round: MUST BE >= MTU */ ++ int limit; ++ unsigned depth; ++ unsigned hash_divisor; ++ unsigned hash_kind; ++/* Variables */ ++ struct timer_list perturb_timer; ++ int perturbation; ++ esfq_index tail; /* Index of current slot in round */ ++ esfq_index max_depth; /* Maximal depth */ ++ ++ esfq_index *ht; /* Hash table */ ++ esfq_index *next; /* Active slots link */ ++ short *allot; /* Current allotment per slot */ ++ unsigned short *hash; /* Hash value indexed by slots */ ++ struct sk_buff_head *qs; /* Slot queue */ ++ struct esfq_head *dep; /* Linked list of slots, indexed by depth */ ++ unsigned dyn_min; /* For dynamic divisor adjustment; minimum value seen */ ++ unsigned dyn_max; /* maximum value seen */ ++ unsigned dyn_range; /* saved range */ ++}; ++ ++static __inline__ unsigned esfq_hash_u32(struct esfq_sched_data *q,u32 h) ++{ ++ int pert = q->perturbation; ++ ++ if (pert) ++ h = (h<<pert) ^ (h>>(0x1F - pert)); ++ ++ h = ntohl(h) * 2654435761UL; ++ return h & (q->hash_divisor-1); ++} ++ ++/* Hash input values directly into the "nearest" slot, taking into account the ++ * range of input values seen. This is most useful when the hash table is at ++ * least as large as the range of possible values. */ ++static __inline__ unsigned esfq_hash_direct(struct esfq_sched_data *q, u32 h) ++{ ++ /* adjust minimum and maximum */ ++ if (h < q->dyn_min || h > q->dyn_max) { ++ q->dyn_min = h < q->dyn_min ? h : q->dyn_min; ++ q->dyn_max = h > q->dyn_max ? h : q->dyn_max; ++ ++ /* find new range */ ++ if ((q->dyn_range = q->dyn_max - q->dyn_min) >= q->hash_divisor) ++ printk(KERN_WARNING "ESFQ: (direct hash) Input range %u is larger than hash " ++ "table. See ESFQ README for details.\n", q->dyn_range); ++ } ++ ++ /* hash input values into slot numbers */ ++ if (q->dyn_min == q->dyn_max) ++ return 0; /* only one value seen; avoid division by 0 */ ++ else ++ return (h - q->dyn_min) * (q->hash_divisor - 1) / q->dyn_range; ++} ++ ++static __inline__ unsigned esfq_fold_hash_classic(struct esfq_sched_data *q, u32 h, u32 h1) ++{ ++ int pert = q->perturbation; ++ ++ /* Have we any rotation primitives? If not, WHY? */ ++ h ^= (h1<<pert) ^ (h1>>(0x1F - pert)); ++ h ^= h>>10; ++ return h & (q->hash_divisor-1); ++} ++ ++static unsigned esfq_hash(struct esfq_sched_data *q, struct sk_buff *skb) ++{ ++ u32 h, h2; ++ u32 hs; ++ u32 nfm; ++ ++ switch (skb->protocol) { ++ case __constant_htons(ETH_P_IP): ++ { ++ struct iphdr *iph = skb->nh.iph; ++ h = iph->daddr; ++ hs = iph->saddr; ++ nfm = skb->nfmark; ++ h2 = hs^iph->protocol; ++ if (!(iph->frag_off&htons(IP_MF|IP_OFFSET)) && ++ (iph->protocol == IPPROTO_TCP || ++ iph->protocol == IPPROTO_UDP || ++ iph->protocol == IPPROTO_SCTP || ++ iph->protocol == IPPROTO_DCCP || ++ iph->protocol == IPPROTO_ESP)) ++ h2 ^= *(((u32*)iph) + iph->ihl); ++ break; ++ } ++ case __constant_htons(ETH_P_IPV6): ++ { ++ struct ipv6hdr *iph = skb->nh.ipv6h; ++ h = iph->daddr.s6_addr32[3]; ++ hs = iph->saddr.s6_addr32[3]; ++ nfm = skb->nfmark; ++ h2 = hs^iph->nexthdr; ++ if (iph->nexthdr == IPPROTO_TCP || ++ iph->nexthdr == IPPROTO_UDP || ++ iph->nexthdr == IPPROTO_SCTP || ++ iph->nexthdr == IPPROTO_DCCP || ++ iph->nexthdr == IPPROTO_ESP) ++ h2 ^= *(u32*)&iph[1]; ++ break; ++ } ++ default: ++ h = (u32)(unsigned long)skb->dst; ++ hs = (u32)(unsigned long)skb->sk; ++ nfm = skb->nfmark; ++ h2 = hs^skb->protocol; ++ } ++ switch(q->hash_kind) ++ { ++ case TCA_SFQ_HASH_CLASSIC: ++ return esfq_fold_hash_classic(q, h, h2); ++ case TCA_SFQ_HASH_DST: ++ return esfq_hash_u32(q,h); ++ case TCA_SFQ_HASH_DSTDIR: ++ return esfq_hash_direct(q, ntohl(h)); ++ case TCA_SFQ_HASH_SRC: ++ return esfq_hash_u32(q,hs); ++ case TCA_SFQ_HASH_SRCDIR: ++ return esfq_hash_direct(q, ntohl(hs)); ++#ifdef CONFIG_NETFILTER ++ case TCA_SFQ_HASH_FWMARK: ++ return esfq_hash_u32(q,nfm); ++ case TCA_SFQ_HASH_FWMARKDIR: ++ return esfq_hash_direct(q,nfm); ++#endif ++ default: ++ if (net_ratelimit()) ++ printk(KERN_WARNING "ESFQ: Unknown hash method. Falling back to classic.\n"); ++ } ++ return esfq_fold_hash_classic(q, h, h2); ++} ++ ++static inline void esfq_link(struct esfq_sched_data *q, esfq_index x) ++{ ++ esfq_index p, n; ++ int d = q->qs[x].qlen + q->depth; ++ ++ p = d; ++ n = q->dep[d].next; ++ q->dep[x].next = n; ++ q->dep[x].prev = p; ++ q->dep[p].next = q->dep[n].prev = x; ++} ++ ++static inline void esfq_dec(struct esfq_sched_data *q, esfq_index x) ++{ ++ esfq_index p, n; ++ ++ n = q->dep[x].next; ++ p = q->dep[x].prev; ++ q->dep[p].next = n; ++ q->dep[n].prev = p; ++ ++ if (n == p && q->max_depth == q->qs[x].qlen + 1) ++ q->max_depth--; ++ ++ esfq_link(q, x); ++} ++ ++static inline void esfq_inc(struct esfq_sched_data *q, esfq_index x) ++{ ++ esfq_index p, n; ++ int d; ++ ++ n = q->dep[x].next; ++ p = q->dep[x].prev; ++ q->dep[p].next = n; ++ q->dep[n].prev = p; ++ d = q->qs[x].qlen; ++ if (q->max_depth < d) ++ q->max_depth = d; ++ ++ esfq_link(q, x); ++} ++ ++static unsigned int esfq_drop(struct Qdisc *sch) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ esfq_index d = q->max_depth; ++ struct sk_buff *skb; ++ unsigned int len; ++ ++ /* Queue is full! Find the longest slot and ++ drop a packet from it */ ++ ++ if (d > 1) { ++ esfq_index x = q->dep[d+q->depth].next; ++ skb = q->qs[x].prev; ++ len = skb->len; ++ __skb_unlink(skb, &q->qs[x]); ++ kfree_skb(skb); ++ esfq_dec(q, x); ++ sch->q.qlen--; ++ sch->qstats.drops++; ++ return len; ++ } ++ ++ if (d == 1) { ++ /* It is difficult to believe, but ALL THE SLOTS HAVE LENGTH 1. */ ++ d = q->next[q->tail]; ++ q->next[q->tail] = q->next[d]; ++ q->allot[q->next[d]] += q->quantum; ++ skb = q->qs[d].prev; ++ len = skb->len; ++ __skb_unlink(skb, &q->qs[d]); ++ kfree_skb(skb); ++ esfq_dec(q, d); ++ sch->q.qlen--; ++ q->ht[q->hash[d]] = q->depth; ++ sch->qstats.drops++; ++ return len; ++ } ++ ++ return 0; ++} ++ ++static int ++esfq_enqueue(struct sk_buff *skb, struct Qdisc* sch) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ unsigned hash = esfq_hash(q, skb); ++ unsigned depth = q->depth; ++ esfq_index x; ++ ++ x = q->ht[hash]; ++ if (x == depth) { ++ q->ht[hash] = x = q->dep[depth].next; ++ q->hash[x] = hash; ++ } ++ __skb_queue_tail(&q->qs[x], skb); ++ esfq_inc(q, x); ++ if (q->qs[x].qlen == 1) { /* The flow is new */ ++ if (q->tail == depth) { /* It is the first flow */ ++ q->tail = x; ++ q->next[x] = x; ++ q->allot[x] = q->quantum; ++ } else { ++ q->next[x] = q->next[q->tail]; ++ q->next[q->tail] = x; ++ q->tail = x; ++ } ++ } ++ if (++sch->q.qlen < q->limit-1) { ++ sch->bstats.bytes += skb->len; ++ sch->bstats.packets++; ++ return 0; ++ } ++ ++ esfq_drop(sch); ++ return NET_XMIT_CN; ++} ++ ++static int ++esfq_requeue(struct sk_buff *skb, struct Qdisc* sch) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ unsigned hash = esfq_hash(q, skb); ++ unsigned depth = q->depth; ++ esfq_index x; ++ ++ x = q->ht[hash]; ++ if (x == depth) { ++ q->ht[hash] = x = q->dep[depth].next; ++ q->hash[x] = hash; ++ } ++ __skb_queue_head(&q->qs[x], skb); ++ esfq_inc(q, x); ++ if (q->qs[x].qlen == 1) { /* The flow is new */ ++ if (q->tail == depth) { /* It is the first flow */ ++ q->tail = x; ++ q->next[x] = x; ++ q->allot[x] = q->quantum; ++ } else { ++ q->next[x] = q->next[q->tail]; ++ q->next[q->tail] = x; ++ q->tail = x; ++ } ++ } ++ if (++sch->q.qlen < q->limit - 1) { ++ sch->qstats.requeues++; ++ return 0; ++ } ++ ++ sch->qstats.drops++; ++ esfq_drop(sch); ++ return NET_XMIT_CN; ++} ++ ++ ++ ++ ++static struct sk_buff * ++esfq_dequeue(struct Qdisc* sch) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ struct sk_buff *skb; ++ unsigned depth = q->depth; ++ esfq_index a, old_a; ++ ++ /* No active slots */ ++ if (q->tail == depth) ++ return NULL; ++ ++ a = old_a = q->next[q->tail]; ++ ++ /* Grab packet */ ++ skb = __skb_dequeue(&q->qs[a]); ++ esfq_dec(q, a); ++ sch->q.qlen--; ++ ++ /* Is the slot empty? */ ++ if (q->qs[a].qlen == 0) { ++ q->ht[q->hash[a]] = depth; ++ a = q->next[a]; ++ if (a == old_a) { ++ q->tail = depth; ++ return skb; ++ } ++ q->next[q->tail] = a; ++ q->allot[a] += q->quantum; ++ } else if ((q->allot[a] -= skb->len) <= 0) { ++ q->tail = a; ++ a = q->next[a]; ++ q->allot[a] += q->quantum; ++ } ++ ++ return skb; ++} ++ ++static void ++esfq_reset(struct Qdisc* sch) ++{ ++ struct sk_buff *skb; ++ ++ while ((skb = esfq_dequeue(sch)) != NULL) ++ kfree_skb(skb); ++} ++ ++static void esfq_perturbation(unsigned long arg) ++{ ++ struct Qdisc *sch = (struct Qdisc*)arg; ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ ++ q->perturbation = net_random()&0x1F; ++ ++ if (q->perturb_period) { ++ q->perturb_timer.expires = jiffies + q->perturb_period; ++ add_timer(&q->perturb_timer); ++ } ++} ++ ++static int esfq_change(struct Qdisc *sch, struct rtattr *opt) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ struct tc_esfq_qopt *ctl = RTA_DATA(opt); ++ int old_perturb = q->perturb_period; ++ ++ if (opt->rta_len < RTA_LENGTH(sizeof(*ctl))) ++ return -EINVAL; ++ ++ sch_tree_lock(sch); ++ q->quantum = ctl->quantum ? : psched_mtu(sch->dev); ++ q->perturb_period = ctl->perturb_period*HZ; ++// q->hash_divisor = ctl->divisor; ++// q->tail = q->limit = q->depth = ctl->flows; ++ ++ if (ctl->limit) ++ q->limit = min_t(u32, ctl->limit, q->depth); ++ ++ if (ctl->hash_kind) { ++ q->hash_kind = ctl->hash_kind; ++ if (q->hash_kind != TCA_SFQ_HASH_CLASSIC) ++ q->perturb_period = 0; ++ } ++ ++ // is sch_tree_lock enough to do this ? ++ while (sch->q.qlen >= q->limit-1) ++ esfq_drop(sch); ++ ++ if (old_perturb) ++ del_timer(&q->perturb_timer); ++ if (q->perturb_period) { ++ q->perturb_timer.expires = jiffies + q->perturb_period; ++ add_timer(&q->perturb_timer); ++ } else { ++ q->perturbation = 0; ++ } ++ sch_tree_unlock(sch); ++ return 0; ++} ++ ++static int esfq_init(struct Qdisc *sch, struct rtattr *opt) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ struct tc_esfq_qopt *ctl; ++ esfq_index p = ~0UL/2; ++ int i; ++ ++ if (opt && opt->rta_len < RTA_LENGTH(sizeof(*ctl))) ++ return -EINVAL; ++ ++ init_timer(&q->perturb_timer); ++ q->perturb_timer.data = (unsigned long)sch; ++ q->perturb_timer.function = esfq_perturbation; ++ q->perturbation = 0; ++ q->hash_kind = TCA_SFQ_HASH_CLASSIC; ++ q->max_depth = 0; ++ q->dyn_min = ~0U; /* maximum value for this type */ ++ q->dyn_max = 0; /* dyn_min/dyn_max will be set properly upon first packet */ ++ if (opt == NULL) { ++ q->quantum = psched_mtu(sch->dev); ++ q->perturb_period = 0; ++ q->hash_divisor = 1024; ++ q->tail = q->limit = q->depth = 128; ++ ++ } else { ++ ctl = RTA_DATA(opt); ++ q->quantum = ctl->quantum ? : psched_mtu(sch->dev); ++ q->perturb_period = ctl->perturb_period*HZ; ++ q->hash_divisor = ctl->divisor ? : 1024; ++ q->tail = q->limit = q->depth = ctl->flows ? : 128; ++ ++ if ( q->depth > p - 1 ) ++ return -EINVAL; ++ ++ if (ctl->limit) ++ q->limit = min_t(u32, ctl->limit, q->depth); ++ ++ if (ctl->hash_kind) { ++ q->hash_kind = ctl->hash_kind; ++ } ++ ++ if (q->perturb_period) { ++ q->perturb_timer.expires = jiffies + q->perturb_period; ++ add_timer(&q->perturb_timer); ++ } ++ } ++ ++ q->ht = kmalloc(q->hash_divisor*sizeof(esfq_index), GFP_KERNEL); ++ if (!q->ht) ++ goto err_case; ++ ++ q->dep = kmalloc((1+q->depth*2)*sizeof(struct esfq_head), GFP_KERNEL); ++ if (!q->dep) ++ goto err_case; ++ q->next = kmalloc(q->depth*sizeof(esfq_index), GFP_KERNEL); ++ if (!q->next) ++ goto err_case; ++ ++ q->allot = kmalloc(q->depth*sizeof(short), GFP_KERNEL); ++ if (!q->allot) ++ goto err_case; ++ q->hash = kmalloc(q->depth*sizeof(unsigned short), GFP_KERNEL); ++ if (!q->hash) ++ goto err_case; ++ q->qs = kmalloc(q->depth*sizeof(struct sk_buff_head), GFP_KERNEL); ++ if (!q->qs) ++ goto err_case; ++ ++ for (i=0; i< q->hash_divisor; i++) ++ q->ht[i] = q->depth; ++ for (i=0; i<q->depth; i++) { ++ skb_queue_head_init(&q->qs[i]); ++ q->dep[i+q->depth].next = i+q->depth; ++ q->dep[i+q->depth].prev = i+q->depth; ++ } ++ ++ for (i=0; i<q->depth; i++) ++ esfq_link(q, i); ++ return 0; ++err_case: ++ del_timer(&q->perturb_timer); ++ if (q->ht) ++ kfree(q->ht); ++ if (q->dep) ++ kfree(q->dep); ++ if (q->next) ++ kfree(q->next); ++ if (q->allot) ++ kfree(q->allot); ++ if (q->hash) ++ kfree(q->hash); ++ if (q->qs) ++ kfree(q->qs); ++ return -ENOBUFS; ++} ++ ++static void esfq_destroy(struct Qdisc *sch) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ del_timer(&q->perturb_timer); ++ if(q->ht) ++ kfree(q->ht); ++ if(q->dep) ++ kfree(q->dep); ++ if(q->next) ++ kfree(q->next); ++ if(q->allot) ++ kfree(q->allot); ++ if(q->hash) ++ kfree(q->hash); ++ if(q->qs) ++ kfree(q->qs); ++} ++ ++static int esfq_dump(struct Qdisc *sch, struct sk_buff *skb) ++{ ++ struct esfq_sched_data *q = qdisc_priv(sch); ++ unsigned char *b = skb->tail; ++ struct tc_esfq_qopt opt; ++ ++ opt.quantum = q->quantum; ++ opt.perturb_period = q->perturb_period/HZ; ++ ++ opt.limit = q->limit; ++ opt.divisor = q->hash_divisor; ++ opt.flows = q->depth; ++ opt.hash_kind = q->hash_kind; ++ ++ RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); ++ ++ return skb->len; ++ ++rtattr_failure: ++ skb_trim(skb, b - skb->data); ++ return -1; ++} ++ ++static struct Qdisc_ops esfq_qdisc_ops = ++{ ++ .next = NULL, ++ .cl_ops = NULL, ++ .id = "esfq", ++ .priv_size = sizeof(struct esfq_sched_data), ++ .enqueue = esfq_enqueue, ++ .dequeue = esfq_dequeue, ++ .requeue = esfq_requeue, ++ .drop = esfq_drop, ++ .init = esfq_init, ++ .reset = esfq_reset, ++ .destroy = esfq_destroy, ++ .change = NULL, /* esfq_change - needs more work */ ++ .dump = esfq_dump, ++ .owner = THIS_MODULE, ++}; ++ ++static int __init esfq_module_init(void) ++{ ++ return register_qdisc(&esfq_qdisc_ops); ++} ++static void __exit esfq_module_exit(void) ++{ ++ unregister_qdisc(&esfq_qdisc_ops); ++} ++module_init(esfq_module_init) ++module_exit(esfq_module_exit) ++MODULE_LICENSE("GPL"); diff --git a/target/linux/generic-2.6/patches/201-multiple_default_gateways.patch b/target/linux/generic-2.6/patches/201-multiple_default_gateways.patch new file mode 100644 index 0000000..3be34d7 --- /dev/null +++ b/target/linux/generic-2.6/patches/201-multiple_default_gateways.patch @@ -0,0 +1,1238 @@ +diff -Nur linux-2.6.17/include/linux/netfilter_ipv4/ip_nat.h linux-2.6.17-owrt/include/linux/netfilter_ipv4/ip_nat.h +--- linux-2.6.17/include/linux/netfilter_ipv4/ip_nat.h 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-owrt/include/linux/netfilter_ipv4/ip_nat.h 2006-06-18 16:53:21.000000000 +0200 +@@ -63,6 +63,13 @@ + + struct ip_conntrack; + ++/* Call input routing for SNAT-ed traffic */ ++extern unsigned int ip_nat_route_input(unsigned int hooknum, ++ struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)); ++ + /* Set up the info structure to map into this range. */ + extern unsigned int ip_nat_setup_info(struct ip_conntrack *conntrack, + const struct ip_nat_range *range, +diff -Nur linux-2.6.17/include/linux/rtnetlink.h linux-2.6.17-owrt/include/linux/rtnetlink.h +--- linux-2.6.17/include/linux/rtnetlink.h 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-owrt/include/linux/rtnetlink.h 2006-06-18 16:53:21.000000000 +0200 +@@ -293,6 +293,8 @@ + #define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */ + #define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */ + #define RTNH_F_ONLINK 4 /* Gateway is forced on link */ ++#define RTNH_F_SUSPECT 8 /* We don't know the real state */ ++#define RTNH_F_BADSTATE (RTNH_F_DEAD | RTNH_F_SUSPECT) + + /* Macros to handle hexthops */ + +diff -Nur linux-2.6.17/include/net/flow.h linux-2.6.17-owrt/include/net/flow.h +--- linux-2.6.17/include/net/flow.h 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-owrt/include/net/flow.h 2006-06-18 16:53:21.000000000 +0200 +@@ -19,6 +19,8 @@ + __u32 daddr; + __u32 saddr; + __u32 fwmark; ++ __u32 lsrc; ++ __u32 gw; + __u8 tos; + __u8 scope; + } ip4_u; +@@ -46,6 +48,8 @@ + #define fl4_dst nl_u.ip4_u.daddr + #define fl4_src nl_u.ip4_u.saddr + #define fl4_fwmark nl_u.ip4_u.fwmark ++#define fl4_lsrc nl_u.ip4_u.lsrc ++#define fl4_gw nl_u.ip4_u.gw + #define fl4_tos nl_u.ip4_u.tos + #define fl4_scope nl_u.ip4_u.scope + +diff -Nur linux-2.6.17/include/net/ip_fib.h linux-2.6.17-owrt/include/net/ip_fib.h +--- linux-2.6.17/include/net/ip_fib.h 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-owrt/include/net/ip_fib.h 2006-06-18 16:53:21.000000000 +0200 +@@ -195,7 +195,8 @@ + + static inline void fib_select_default(const struct flowi *flp, struct fib_result *res) + { +- if (FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) ++ if ((FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) || ++ FIB_RES_NH(*res).nh_scope == RT_SCOPE_HOST) + ip_fib_main_table->tb_select_default(ip_fib_main_table, flp, res); + } + +@@ -207,6 +208,7 @@ + extern int fib_lookup(const struct flowi *flp, struct fib_result *res); + extern struct fib_table *__fib_new_table(int id); + extern void fib_rule_put(struct fib_rule *r); ++extern int fib_result_table(struct fib_result *res); + + static inline struct fib_table *fib_get_table(int id) + { +@@ -302,4 +304,6 @@ + extern void fib_proc_exit(void); + #endif + ++extern rwlock_t fib_nhflags_lock; ++ + #endif /* _NET_FIB_H */ +diff -Nur linux-2.6.17/include/net/route.h linux-2.6.17-owrt/include/net/route.h +--- linux-2.6.17/include/net/route.h 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-owrt/include/net/route.h 2006-06-18 16:53:21.000000000 +0200 +@@ -117,6 +117,7 @@ + extern int ip_route_output_key(struct rtable **, struct flowi *flp); + extern int ip_route_output_flow(struct rtable **rp, struct flowi *flp, struct sock *sk, int flags); + extern int ip_route_input(struct sk_buff*, u32 dst, u32 src, u8 tos, struct net_device *devin); ++extern int ip_route_input_lookup(struct sk_buff*, u32 dst, u32 src, u8 tos, struct net_device *devin, u32 lsrc); + extern unsigned short ip_rt_frag_needed(struct iphdr *iph, unsigned short new_mtu); + extern void ip_rt_send_redirect(struct sk_buff *skb); + +diff -Nur linux-2.6.17/net/ipv4/fib_frontend.c linux-2.6.17-owrt/net/ipv4/fib_frontend.c +--- linux-2.6.17/net/ipv4/fib_frontend.c 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-owrt/net/ipv4/fib_frontend.c 2006-06-18 16:53:21.000000000 +0200 +@@ -56,6 +56,8 @@ + struct fib_table *ip_fib_local_table; + struct fib_table *ip_fib_main_table; + ++#define FIB_RES_TABLE(r) (RT_TABLE_MAIN) ++ + #else + + #define RT_TABLE_MIN 1 +@@ -73,6 +75,7 @@ + return tb; + } + ++#define FIB_RES_TABLE(r) (fib_result_table(r)) + + #endif /* CONFIG_IP_MULTIPLE_TABLES */ + +@@ -170,6 +173,9 @@ + .tos = tos } }, + .iif = oif }; + struct fib_result res; ++ int table; ++ unsigned char prefixlen; ++ unsigned char scope; + int no_addr, rpf; + int ret; + +@@ -191,31 +197,35 @@ + goto e_inval_res; + *spec_dst = FIB_RES_PREFSRC(res); + fib_combine_itag(itag, &res); +-#ifdef CONFIG_IP_ROUTE_MULTIPATH +- if (FIB_RES_DEV(res) == dev || res.fi->fib_nhs > 1) +-#else + if (FIB_RES_DEV(res) == dev) +-#endif + { + ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST; + fib_res_put(&res); + return ret; + } ++ table = FIB_RES_TABLE(&res); ++ prefixlen = res.prefixlen; ++ scope = res.scope; + fib_res_put(&res); + if (no_addr) + goto last_resort; +- if (rpf) +- goto e_inval; + fl.oif = dev->ifindex; + + ret = 0; + if (fib_lookup(&fl, &res) == 0) { +- if (res.type == RTN_UNICAST) { ++ if (res.type == RTN_UNICAST && ++ ((table == FIB_RES_TABLE(&res) && ++ res.prefixlen >= prefixlen && res.scope >= scope) || ++ !rpf)) { + *spec_dst = FIB_RES_PREFSRC(res); + ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST; ++ fib_res_put(&res); ++ return ret; + } + fib_res_put(&res); + } ++ if (rpf) ++ goto e_inval; + return ret; + + last_resort: +@@ -589,9 +599,7 @@ + switch (event) { + case NETDEV_UP: + fib_add_ifaddr(ifa); +-#ifdef CONFIG_IP_ROUTE_MULTIPATH + fib_sync_up(ifa->ifa_dev->dev); +-#endif + rt_cache_flush(-1); + break; + case NETDEV_DOWN: +@@ -627,9 +635,7 @@ + for_ifa(in_dev) { + fib_add_ifaddr(ifa); + } endfor_ifa(in_dev); +-#ifdef CONFIG_IP_ROUTE_MULTIPATH + fib_sync_up(dev); +-#endif + rt_cache_flush(-1); + break; + case NETDEV_DOWN: +diff -Nur linux-2.6.17/net/ipv4/fib_hash.c linux-2.6.17-owrt/net/ipv4/fib_hash.c +--- linux-2.6.17/net/ipv4/fib_hash.c 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-owrt/net/ipv4/fib_hash.c 2006-06-18 16:53:21.000000000 +0200 +@@ -277,30 +277,38 @@ + return err; + } + +-static int fn_hash_last_dflt=-1; +- + static void + fn_hash_select_default(struct fib_table *tb, const struct flowi *flp, struct fib_result *res) + { +- int order, last_idx; ++ int order, last_idx, last_dflt, last_nhsel; ++ struct fib_alias *first_fa = NULL; ++ struct hlist_head *head; + struct hlist_node *node; + struct fib_node *f; + struct fib_info *fi = NULL; + struct fib_info *last_resort; + struct fn_hash *t = (struct fn_hash*)tb->tb_data; +- struct fn_zone *fz = t->fn_zones[0]; ++ struct fn_zone *fz = t->fn_zones[res->prefixlen]; ++ u32 k; + + if (fz == NULL) + return; + ++ k = fz_key(flp->fl4_dst, fz); ++ last_dflt = -2; ++ last_nhsel = 0; + last_idx = -1; + last_resort = NULL; + order = -1; + + read_lock(&fib_hash_lock); +- hlist_for_each_entry(f, node, &fz->fz_hash[0], fn_hash) { ++ head = &fz->fz_hash[fn_hash(k, fz)]; ++ hlist_for_each_entry(f, node, head, fn_hash) { + struct fib_alias *fa; + ++ if (f->fn_key != k) ++ continue; ++ + list_for_each_entry(fa, &f->fn_alias, fa_list) { + struct fib_info *next_fi = fa->fa_info; + +@@ -308,41 +316,52 @@ + fa->fa_type != RTN_UNICAST) + continue; + ++ if (fa->fa_tos && ++ fa->fa_tos != flp->fl4_tos) ++ continue; + if (next_fi->fib_priority > res->fi->fib_priority) + break; +- if (!next_fi->fib_nh[0].nh_gw || +- next_fi->fib_nh[0].nh_scope != RT_SCOPE_LINK) +- continue; + fa->fa_state |= FA_S_ACCESSED; + +- if (fi == NULL) { +- if (next_fi != res->fi) +- break; +- } else if (!fib_detect_death(fi, order, &last_resort, +- &last_idx, &fn_hash_last_dflt)) { ++ if (!first_fa) { ++ last_dflt = fa->fa_last_dflt; ++ first_fa = fa; ++ } ++ if (fi && !fib_detect_death(fi, order, &last_resort, ++ &last_idx, &last_dflt, &last_nhsel, flp)) { + if (res->fi) + fib_info_put(res->fi); + res->fi = fi; + atomic_inc(&fi->fib_clntref); +- fn_hash_last_dflt = order; ++ first_fa->fa_last_dflt = order; + goto out; + } + fi = next_fi; + order++; + } ++ break; + } + + if (order <= 0 || fi == NULL) { +- fn_hash_last_dflt = -1; ++ if (fi && fi->fib_nhs > 1 && ++ fib_detect_death(fi, order, &last_resort, &last_idx, ++ &last_dflt, &last_nhsel, flp) && ++ last_resort == fi) { ++ read_lock_bh(&fib_nhflags_lock); ++ fi->fib_nh[last_nhsel].nh_flags &= ~RTNH_F_SUSPECT; ++ read_unlock_bh(&fib_nhflags_lock); ++ } ++ if (first_fa) first_fa->fa_last_dflt = -1; + goto out; + } + +- if (!fib_detect_death(fi, order, &last_resort, &last_idx, &fn_hash_last_dflt)) { ++ if (!fib_detect_death(fi, order, &last_resort, &last_idx, ++ &last_dflt, &last_nhsel, flp)) { + if (res->fi) + fib_info_put(res->fi); + res->fi = fi; + atomic_inc(&fi->fib_clntref); +- fn_hash_last_dflt = order; ++ first_fa->fa_last_dflt = order; + goto out; + } + +@@ -352,8 +371,11 @@ + res->fi = last_resort; + if (last_resort) + atomic_inc(&last_resort->fib_clntref); ++ read_lock_bh(&fib_nhflags_lock); ++ last_resort->fib_nh[last_nhsel].nh_flags &= ~RTNH_F_SUSPECT; ++ read_unlock_bh(&fib_nhflags_lock); ++ first_fa->fa_last_dflt = last_idx; + } +- fn_hash_last_dflt = last_idx; + out: + read_unlock(&fib_hash_lock); + } +@@ -452,6 +474,7 @@ + write_lock_bh(&fib_hash_lock); + fi_drop = fa->fa_info; + fa->fa_info = fi; ++ fa->fa_last_dflt = -1; + fa->fa_type = type; + fa->fa_scope = r->rtm_scope; + state = fa->fa_state; +@@ -511,6 +534,7 @@ + new_fa->fa_type = type; + new_fa->fa_scope = r->rtm_scope; + new_fa->fa_state = 0; ++ new_fa->fa_last_dflt = -1; + + /* + * Insert new entry to the list. +diff -Nur linux-2.6.17/net/ipv4/fib_lookup.h linux-2.6.17-owrt/net/ipv4/fib_lookup.h +--- linux-2.6.17/net/ipv4/fib_lookup.h 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-owrt/net/ipv4/fib_lookup.h 2006-06-18 16:53:21.000000000 +0200 +@@ -9,6 +9,7 @@ + struct list_head fa_list; + struct rcu_head rcu; + struct fib_info *fa_info; ++ int fa_last_dflt; + u8 fa_tos; + u8 fa_type; + u8 fa_scope; +@@ -40,6 +41,7 @@ + u8 tos, u32 prio); + extern int fib_detect_death(struct fib_info *fi, int order, + struct fib_info **last_resort, +- int *last_idx, int *dflt); ++ int *last_idx, int *dflt, int *last_nhsel, ++ const struct flowi *flp); + + #endif /* _FIB_LOOKUP_H */ +diff -Nur linux-2.6.17/net/ipv4/fib_rules.c linux-2.6.17-owrt/net/ipv4/fib_rules.c +--- linux-2.6.17/net/ipv4/fib_rules.c 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-owrt/net/ipv4/fib_rules.c 2006-06-18 16:53:21.000000000 +0200 +@@ -296,6 +296,11 @@ + } + } + ++int fib_result_table(struct fib_result *res) ++{ ++ return res->r->r_table; ++} ++ + int fib_lookup(const struct flowi *flp, struct fib_result *res) + { + int err; +@@ -361,7 +366,8 @@ + void fib_select_default(const struct flowi *flp, struct fib_result *res) + { + if (res->r && res->r->r_action == RTN_UNICAST && +- FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) { ++ ((FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) || ++ FIB_RES_NH(*res).nh_scope == RT_SCOPE_HOST)) { + struct fib_table *tb; + if ((tb = fib_get_table(res->r->r_table)) != NULL) + tb->tb_select_default(tb, flp, res); +diff -Nur linux-2.6.17/net/ipv4/fib_semantics.c linux-2.6.17-owrt/net/ipv4/fib_semantics.c +--- linux-2.6.17/net/ipv4/fib_semantics.c 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-owrt/net/ipv4/fib_semantics.c 2006-06-18 16:53:21.000000000 +0200 +@@ -55,6 +55,7 @@ + static struct hlist_head *fib_info_laddrhash; + static unsigned int fib_hash_size; + static unsigned int fib_info_cnt; ++rwlock_t fib_nhflags_lock = RW_LOCK_UNLOCKED; + + #define DEVINDEX_HASHBITS 8 + #define DEVINDEX_HASHSIZE (1U << DEVINDEX_HASHBITS) +@@ -190,7 +191,7 @@ + #ifdef CONFIG_NET_CLS_ROUTE + nh->nh_tclassid != onh->nh_tclassid || + #endif +- ((nh->nh_flags^onh->nh_flags)&~RTNH_F_DEAD)) ++ ((nh->nh_flags^onh->nh_flags)&~RTNH_F_BADSTATE)) + return -1; + onh++; + } endfor_nexthops(fi); +@@ -227,7 +228,7 @@ + nfi->fib_priority == fi->fib_priority && + memcmp(nfi->fib_metrics, fi->fib_metrics, + sizeof(fi->fib_metrics)) == 0 && +- ((nfi->fib_flags^fi->fib_flags)&~RTNH_F_DEAD) == 0 && ++ ((nfi->fib_flags^fi->fib_flags)&~RTNH_F_BADSTATE) == 0 && + (nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0)) + return fi; + } +@@ -319,26 +320,70 @@ + } + + int fib_detect_death(struct fib_info *fi, int order, +- struct fib_info **last_resort, int *last_idx, int *dflt) ++ struct fib_info **last_resort, int *last_idx, int *dflt, ++ int *last_nhsel, const struct flowi *flp) + { + struct neighbour *n; +- int state = NUD_NONE; ++ int nhsel; ++ int state; ++ struct fib_nh * nh; ++ u32 dst; ++ int flag, dead = 1; ++ ++ /* change_nexthops(fi) { */ ++ for (nhsel = 0, nh = fi->fib_nh; nhsel < fi->fib_nhs; nh++, nhsel++) { ++ if (flp->oif && flp->oif != nh->nh_oif) ++ continue; ++ if (flp->fl4_gw && flp->fl4_gw != nh->nh_gw && nh->nh_gw && ++ nh->nh_scope == RT_SCOPE_LINK) ++ continue; ++ if (nh->nh_flags & RTNH_F_DEAD) ++ continue; + +- n = neigh_lookup(&arp_tbl, &fi->fib_nh[0].nh_gw, fi->fib_dev); +- if (n) { +- state = n->nud_state; +- neigh_release(n); +- } +- if (state==NUD_REACHABLE) +- return 0; +- if ((state&NUD_VALID) && order != *dflt) +- return 0; +- if ((state&NUD_VALID) || +- (*last_idx<0 && order > *dflt)) { +- *last_resort = fi; +- *last_idx = order; ++ flag = 0; ++ if (nh->nh_dev->flags & IFF_NOARP) { ++ dead = 0; ++ goto setfl; ++ } ++ ++ dst = nh->nh_gw; ++ if (!nh->nh_gw || nh->nh_scope != RT_SCOPE_LINK) ++ dst = flp->fl4_dst; ++ ++ state = NUD_NONE; ++ n = neigh_lookup(&arp_tbl, &dst, nh->nh_dev); ++ if (n) { ++ state = n->nud_state; ++ neigh_release(n); ++ } ++ if (state==NUD_REACHABLE || ++ ((state&NUD_VALID) && order != *dflt)) { ++ dead = 0; ++ goto setfl; ++ } ++ if (!(state&NUD_VALID)) ++ flag = 1; ++ if (!dead) ++ goto setfl; ++ if ((state&NUD_VALID) || ++ (*last_idx<0 && order >= *dflt)) { ++ *last_resort = fi; ++ *last_idx = order; ++ *last_nhsel = nhsel; ++ } ++ ++ setfl: ++ ++ read_lock_bh(&fib_nhflags_lock); ++ if (flag) ++ nh->nh_flags |= RTNH_F_SUSPECT; ++ else ++ nh->nh_flags &= ~RTNH_F_SUSPECT; ++ read_unlock_bh(&fib_nhflags_lock); + } +- return 1; ++ /* } endfor_nexthops(fi) */ ++ ++ return dead; + } + + #ifdef CONFIG_IP_ROUTE_MULTIPATH +@@ -509,8 +554,11 @@ + return -EINVAL; + if ((dev = __dev_get_by_index(nh->nh_oif)) == NULL) + return -ENODEV; +- if (!(dev->flags&IFF_UP)) +- return -ENETDOWN; ++ if (!(dev->flags&IFF_UP)) { ++ if (fi->fib_protocol != RTPROT_STATIC) ++ return -ENETDOWN; ++ nh->nh_flags |= RTNH_F_DEAD; ++ } + nh->nh_dev = dev; + dev_hold(dev); + nh->nh_scope = RT_SCOPE_LINK; +@@ -525,24 +573,48 @@ + /* It is not necessary, but requires a bit of thinking */ + if (fl.fl4_scope < RT_SCOPE_LINK) + fl.fl4_scope = RT_SCOPE_LINK; +- if ((err = fib_lookup(&fl, &res)) != 0) +- return err; ++ err = fib_lookup(&fl, &res); + } +- err = -EINVAL; +- if (res.type != RTN_UNICAST && res.type != RTN_LOCAL) +- goto out; +- nh->nh_scope = res.scope; +- nh->nh_oif = FIB_RES_OIF(res); +- if ((nh->nh_dev = FIB_RES_DEV(res)) == NULL) +- goto out; +- dev_hold(nh->nh_dev); +- err = -ENETDOWN; +- if (!(nh->nh_dev->flags & IFF_UP)) +- goto out; +- err = 0; ++ if (err) { ++ struct in_device *in_dev; ++ ++ if (err != -ENETUNREACH || ++ fi->fib_protocol != RTPROT_STATIC) ++ return err; ++ ++ in_dev = inetdev_by_index(nh->nh_oif); ++ if (in_dev == NULL || ++ in_dev->dev->flags & IFF_UP) { ++ if (in_dev) ++ in_dev_put(in_dev); ++ return err; ++ } ++ nh->nh_flags |= RTNH_F_DEAD; ++ nh->nh_scope = RT_SCOPE_LINK; ++ nh->nh_dev = in_dev->dev; ++ dev_hold(nh->nh_dev); ++ in_dev_put(in_dev); ++ } else { ++ err = -EINVAL; ++ if (res.type != RTN_UNICAST && res.type != RTN_LOCAL) ++ goto out; ++ nh->nh_scope = res.scope; ++ nh->nh_oif = FIB_RES_OIF(res); ++ if ((nh->nh_dev = FIB_RES_DEV(res)) == NULL) ++ goto out; ++ dev_hold(nh->nh_dev); ++ if (!(nh->nh_dev->flags & IFF_UP)) { ++ if (fi->fib_protocol != RTPROT_STATIC) { ++ err = -ENETDOWN; ++ goto out; ++ } ++ nh->nh_flags |= RTNH_F_DEAD; ++ } ++ err = 0; + out: +- fib_res_put(&res); +- return err; ++ fib_res_put(&res); ++ return err; ++ } + } else { + struct in_device *in_dev; + +@@ -553,8 +625,11 @@ + if (in_dev == NULL) + return -ENODEV; + if (!(in_dev->dev->flags&IFF_UP)) { +- in_dev_put(in_dev); +- return -ENETDOWN; ++ if (fi->fib_protocol != RTPROT_STATIC) { ++ in_dev_put(in_dev); ++ return -ENETDOWN; ++ } ++ nh->nh_flags |= RTNH_F_DEAD; + } + nh->nh_dev = in_dev->dev; + dev_hold(nh->nh_dev); +@@ -892,8 +967,12 @@ + for_nexthops(fi) { + if (nh->nh_flags&RTNH_F_DEAD) + continue; +- if (!flp->oif || flp->oif == nh->nh_oif) +- break; ++ if (flp->oif && flp->oif != nh->nh_oif) ++ continue; ++ if (flp->fl4_gw && flp->fl4_gw != nh->nh_gw && ++ nh->nh_gw && nh->nh_scope == RT_SCOPE_LINK) ++ continue; ++ break; + } + #ifdef CONFIG_IP_ROUTE_MULTIPATH + if (nhsel < fi->fib_nhs) { +@@ -1199,18 +1278,29 @@ + prev_fi = fi; + dead = 0; + change_nexthops(fi) { +- if (nh->nh_flags&RTNH_F_DEAD) +- dead++; +- else if (nh->nh_dev == dev && +- nh->nh_scope != scope) { +- nh->nh_flags |= RTNH_F_DEAD; ++ if (nh->nh_flags&RTNH_F_DEAD) { ++ if (fi->fib_protocol!=RTPROT_STATIC || ++ nh->nh_dev == NULL || ++ __in_dev_get_rtnl(nh->nh_dev) == NULL || ++ nh->nh_dev->flags&IFF_UP) ++ dead++; ++ } else if (nh->nh_dev == dev && ++ nh->nh_scope != scope) { ++ write_lock_bh(&fib_nhflags_lock); + #ifdef CONFIG_IP_ROUTE_MULTIPATH +- spin_lock_bh(&fib_multipath_lock); ++ spin_lock(&fib_multipath_lock); ++ nh->nh_flags |= RTNH_F_DEAD; + fi->fib_power -= nh->nh_power; + nh->nh_power = 0; +- spin_unlock_bh(&fib_multipath_lock); ++ spin_unlock(&fib_multipath_lock); ++#else ++ nh->nh_flags |= RTNH_F_DEAD; + #endif +- dead++; ++ write_unlock_bh(&fib_nhflags_lock); ++ if (fi->fib_protocol!=RTPROT_STATIC || ++ force || ++ __in_dev_get_rtnl(dev) == NULL) ++ dead++; + } + #ifdef CONFIG_IP_ROUTE_MULTIPATH + if (force > 1 && nh->nh_dev == dev) { +@@ -1229,11 +1319,8 @@ + return ret; + } + +-#ifdef CONFIG_IP_ROUTE_MULTIPATH +- + /* +- Dead device goes up. We wake up dead nexthops. +- It takes sense only on multipath routes. ++ Dead device goes up or new address is added. We wake up dead nexthops. + */ + + int fib_sync_up(struct net_device *dev) +@@ -1243,8 +1330,10 @@ + struct hlist_head *head; + struct hlist_node *node; + struct fib_nh *nh; +- int ret; ++ struct fib_result res; ++ int ret, rep; + ++repeat: + if (!(dev->flags&IFF_UP)) + return 0; + +@@ -1252,6 +1341,7 @@ + hash = fib_devindex_hashfn(dev->ifindex); + head = &fib_info_devhash[hash]; + ret = 0; ++ rep = 0; + + hlist_for_each_entry(nh, node, head, nh_hash) { + struct fib_info *fi = nh->nh_parent; +@@ -1264,19 +1354,37 @@ + prev_fi = fi; + alive = 0; + change_nexthops(fi) { +- if (!(nh->nh_flags&RTNH_F_DEAD)) { +- alive++; ++ if (!(nh->nh_flags&RTNH_F_DEAD)) + continue; +- } + if (nh->nh_dev == NULL || !(nh->nh_dev->flags&IFF_UP)) + continue; + if (nh->nh_dev != dev || !__in_dev_get_rtnl(dev)) + continue; ++ if (nh->nh_gw && fi->fib_protocol == RTPROT_STATIC) { ++ struct flowi fl = { ++ .nl_u = { .ip4_u = ++ { .daddr = nh->nh_gw, ++ .scope = nh->nh_scope } }, ++ .oif = nh->nh_oif, ++ }; ++ if (fib_lookup(&fl, &res) != 0) ++ continue; ++ if (res.type != RTN_UNICAST && ++ res.type != RTN_LOCAL) { ++ fib_res_put(&res); ++ continue; ++ } ++ nh->nh_scope = res.scope; ++ fib_res_put(&res); ++ rep = 1; ++ } + alive++; ++#ifdef CONFIG_IP_ROUTE_MULTIPATH + spin_lock_bh(&fib_multipath_lock); + nh->nh_power = 0; + nh->nh_flags &= ~RTNH_F_DEAD; + spin_unlock_bh(&fib_multipath_lock); ++#endif + } endfor_nexthops(fi) + + if (alive > 0) { +@@ -1284,10 +1392,14 @@ + ret++; + } + } ++ if (rep) ++ goto repeat; + + return ret; + } + ++#ifdef CONFIG_IP_ROUTE_MULTIPATH ++ + /* + The algorithm is suboptimal, but it provides really + fair weighted route distribution. +@@ -1296,24 +1408,45 @@ + void fib_select_multipath(const struct flowi *flp, struct fib_result *res) + { + struct fib_info *fi = res->fi; +- int w; ++ int w, alive; + + spin_lock_bh(&fib_multipath_lock); ++ if (flp->oif) { ++ int sel = -1; ++ w = -1; ++ change_nexthops(fi) { ++ if (flp->oif != nh->nh_oif) ++ continue; ++ if (flp->fl4_gw && flp->fl4_gw != nh->nh_gw && ++ nh->nh_gw && nh->nh_scope == RT_SCOPE_LINK) ++ continue; ++ if (!(nh->nh_flags&RTNH_F_BADSTATE)) { ++ if (nh->nh_power > w) { ++ w = nh->nh_power; ++ sel = nhsel; ++ } ++ } ++ } endfor_nexthops(fi); ++ if (sel >= 0) { ++ spin_unlock_bh(&fib_multipath_lock); ++ res->nh_sel = sel; ++ return; ++ } ++ goto last_resort; ++ } ++ ++repeat: + if (fi->fib_power <= 0) { + int power = 0; + change_nexthops(fi) { +- if (!(nh->nh_flags&RTNH_F_DEAD)) { ++ if (!(nh->nh_flags&RTNH_F_BADSTATE)) { + power += nh->nh_weight; + nh->nh_power = nh->nh_weight; + } + } endfor_nexthops(fi); + fi->fib_power = power; +- if (power <= 0) { +- spin_unlock_bh(&fib_multipath_lock); +- /* Race condition: route has just become dead. */ +- res->nh_sel = 0; +- return; +- } ++ if (power <= 0) ++ goto last_resort; + } + + +@@ -1323,20 +1456,40 @@ + + w = jiffies % fi->fib_power; + ++ alive = 0; + change_nexthops(fi) { +- if (!(nh->nh_flags&RTNH_F_DEAD) && nh->nh_power) { ++ if (!(nh->nh_flags&RTNH_F_BADSTATE) && nh->nh_power) { + if ((w -= nh->nh_power) <= 0) { + nh->nh_power--; + fi->fib_power--; +- res->nh_sel = nhsel; + spin_unlock_bh(&fib_multipath_lock); ++ res->nh_sel = nhsel; + return; + } ++ alive = 1; ++ } ++ } endfor_nexthops(fi); ++ if (alive) { ++ fi->fib_power = 0; ++ goto repeat; ++ } ++ ++last_resort: ++ ++ for_nexthops(fi) { ++ if (!(nh->nh_flags&RTNH_F_DEAD)) { ++ if (flp->oif && flp->oif != nh->nh_oif) ++ continue; ++ if (flp->fl4_gw && flp->fl4_gw != nh->nh_gw && ++ nh->nh_gw && nh->nh_scope == RT_SCOPE_LINK) ++ continue; ++ spin_unlock_bh(&fib_multipath_lock); ++ res->nh_sel = nhsel; ++ return; + } + } endfor_nexthops(fi); + + /* Race condition: route has just become dead. */ +- res->nh_sel = 0; + spin_unlock_bh(&fib_multipath_lock); + } + #endif +diff -Nur linux-2.6.17/net/ipv4/netfilter/ip_nat_core.c linux-2.6.17-owrt/net/ipv4/netfilter/ip_nat_core.c +--- linux-2.6.17/net/ipv4/netfilter/ip_nat_core.c 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-owrt/net/ipv4/netfilter/ip_nat_core.c 2006-06-18 16:53:21.000000000 +0200 +@@ -589,6 +589,53 @@ + EXPORT_SYMBOL_GPL(ip_nat_port_range_to_nfattr); + #endif + ++unsigned int ++ip_nat_route_input(unsigned int hooknum, ++ struct sk_buff **pskb, ++ const struct net_device *in, ++ const struct net_device *out, ++ int (*okfn)(struct sk_buff *)) ++{ ++ struct sk_buff *skb = *pskb; ++ struct iphdr *iph; ++ struct ip_conntrack *conn; ++ enum ip_conntrack_info ctinfo; ++ enum ip_conntrack_dir dir; ++ unsigned long statusbit; ++ u32 saddr; ++ ++ if (!(conn = ip_conntrack_get(skb, &ctinfo))) ++ return NF_ACCEPT; ++ ++ if (!(conn->status & IPS_NAT_DONE_MASK)) ++ return NF_ACCEPT; ++ dir = CTINFO2DIR(ctinfo); ++ statusbit = IPS_SRC_NAT; ++ if (dir == IP_CT_DIR_REPLY) ++ statusbit ^= IPS_NAT_MASK; ++ if (!(conn->status & statusbit)) ++ return NF_ACCEPT; ++ ++ if (skb->dst) ++ return NF_ACCEPT; ++ ++ if (skb->len < sizeof(struct iphdr)) ++ return NF_ACCEPT; ++ ++ /* use daddr in other direction as masquerade address (lsrc) */ ++ iph = skb->nh.iph; ++ saddr = conn->tuplehash[!dir].tuple.dst.ip; ++ if (saddr == iph->saddr) ++ return NF_ACCEPT; ++ ++ if (ip_route_input_lookup(skb, iph->daddr, iph->saddr, iph->tos, ++ skb->dev, saddr)) ++ return NF_DROP; ++ ++ return NF_ACCEPT; ++} ++EXPORT_SYMBOL_GPL(ip_nat_route_input); ++ + static int __init ip_nat_init(void) + { + size_t i; +diff -Nur linux-2.6.17/net/ipv4/netfilter/ip_nat_standalone.c linux-2.6.17-owrt/net/ipv4/netfilter/ip_nat_standalone.c +--- linux-2.6.17/net/ipv4/netfilter/ip_nat_standalone.c 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-owrt/net/ipv4/netfilter/ip_nat_standalone.c 2006-06-18 17:12:03.000000000 +0200 +@@ -334,6 +334,14 @@ + .hooknum = NF_IP_LOCAL_OUT, + .priority = NF_IP_PRI_NAT_DST, + }, ++ /* Before routing, route before mangling */ ++ { ++ .hook = ip_nat_route_input, ++ .owner = THIS_MODULE, ++ .pf = PF_INET, ++ .hooknum = NF_IP_PRE_ROUTING, ++ .priority = NF_IP_PRI_LAST-1, ++ }, + /* After packet filtering, change source */ + { + .hook = ip_nat_fn, +diff -Nur linux-2.6.17/net/ipv4/netfilter/ipt_MASQUERADE.c linux-2.6.17-owrt/net/ipv4/netfilter/ipt_MASQUERADE.c +--- linux-2.6.17/net/ipv4/netfilter/ipt_MASQUERADE.c 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-owrt/net/ipv4/netfilter/ipt_MASQUERADE.c 2006-06-18 16:53:21.000000000 +0200 +@@ -88,13 +88,31 @@ + return NF_ACCEPT; + + mr = targinfo; +- rt = (struct rtable *)(*pskb)->dst; +- newsrc = inet_select_addr(out, rt->rt_gateway, RT_SCOPE_UNIVERSE); +- if (!newsrc) { +- printk("MASQUERADE: %s ate my IP address\n", out->name); +- return NF_DROP; ++ ++ { ++ struct flowi fl = { .nl_u = { .ip4_u = ++ { .daddr = (*pskb)->nh.iph->daddr, ++ .tos = (RT_TOS((*pskb)->nh.iph->tos) | ++ RTO_CONN), ++ .gw = ((struct rtable *) (*pskb)->dst)->rt_gateway, ++#ifdef CONFIG_IP_ROUTE_FWMARK ++ .fwmark = (*pskb)->nfmark ++#endif ++ } }, ++ .oif = out->ifindex }; ++ if (ip_route_output_key(&rt, &fl) != 0) { ++ /* Funky routing can do this. */ ++ if (net_ratelimit()) ++ printk("MASQUERADE:" ++ " No route: Rusty's brain broke!\n"); ++ return NF_DROP; ++ } + } + ++ newsrc = rt->rt_src; ++ DEBUGP("newsrc = %u.%u.%u.%u\n", NIPQUAD(newsrc)); ++ ip_rt_put(rt); ++ + write_lock_bh(&masq_lock); + ct->nat.masq_index = out->ifindex; + write_unlock_bh(&masq_lock); +diff -Nur linux-2.6.17/net/ipv4/route.c linux-2.6.17-owrt/net/ipv4/route.c +--- linux-2.6.17/net/ipv4/route.c 2006-06-18 03:49:35.000000000 +0200 ++++ linux-2.6.17-owrt/net/ipv4/route.c 2006-06-18 16:53:21.000000000 +0200 +@@ -1195,6 +1195,7 @@ + + /* Gateway is different ... */ + rt->rt_gateway = new_gw; ++ if (rt->fl.fl4_gw) rt->fl.fl4_gw = new_gw; + + /* Redirect received -> path was valid */ + dst_confirm(&rth->u.dst); +@@ -1626,6 +1627,7 @@ + rth->fl.fl4_fwmark= skb->nfmark; + #endif + rth->fl.fl4_src = saddr; ++ rth->fl.fl4_lsrc = 0; + rth->rt_src = saddr; + #ifdef CONFIG_NET_CLS_ROUTE + rth->u.dst.tclassid = itag; +@@ -1636,6 +1638,7 @@ + dev_hold(rth->u.dst.dev); + rth->idev = in_dev_get(rth->u.dst.dev); + rth->fl.oif = 0; ++ rth->fl.fl4_gw = 0; + rth->rt_gateway = daddr; + rth->rt_spec_dst= spec_dst; + rth->rt_type = RTN_MULTICAST; +@@ -1700,7 +1703,7 @@ + struct fib_result* res, + struct in_device *in_dev, + u32 daddr, u32 saddr, u32 tos, +- struct rtable **result) ++ u32 lsrc, struct rtable **result) + { + + struct rtable *rth; +@@ -1733,6 +1736,7 @@ + flags |= RTCF_DIRECTSRC; + + if (out_dev == in_dev && err && !(flags & (RTCF_NAT | RTCF_MASQ)) && ++ !lsrc && + (IN_DEV_SHARED_MEDIA(out_dev) || + inet_addr_onlink(out_dev, saddr, FIB_RES_GW(*res)))) + flags |= RTCF_DOREDIRECT; +@@ -1772,6 +1776,7 @@ + #endif + rth->fl.fl4_src = saddr; + rth->rt_src = saddr; ++ rth->fl.fl4_lsrc = lsrc; + rth->rt_gateway = daddr; + rth->rt_iif = + rth->fl.iif = in_dev->dev->ifindex; +@@ -1779,6 +1784,7 @@ + dev_hold(rth->u.dst.dev); + rth->idev = in_dev_get(rth->u.dst.dev); + rth->fl.oif = 0; ++ rth->fl.fl4_gw = 0; + rth->rt_spec_dst= spec_dst; + + rth->u.dst.input = ip_forward; +@@ -1800,19 +1806,20 @@ + struct fib_result* res, + const struct flowi *fl, + struct in_device *in_dev, +- u32 daddr, u32 saddr, u32 tos) ++ u32 daddr, u32 saddr, u32 tos, u32 lsrc) + { + struct rtable* rth = NULL; + int err; + unsigned hash; + ++ fib_select_default(fl, res); + #ifdef CONFIG_IP_ROUTE_MULTIPATH +- if (res->fi && res->fi->fib_nhs > 1 && fl->oif == 0) ++ if (res->fi && res->fi->fib_nhs > 1) + fib_select_multipath(fl, res); + #endif + + /* create a routing cache entry */ +- err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos, &rth); ++ err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos, lsrc, &rth); + if (err) + return err; + +@@ -1825,7 +1832,7 @@ + struct fib_result* res, + const struct flowi *fl, + struct in_device *in_dev, +- u32 daddr, u32 saddr, u32 tos) ++ u32 daddr, u32 saddr, u32 tos, u32 lsrc) + { + #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED + struct rtable* rth = NULL, *rtres; +@@ -1841,7 +1848,7 @@ + /* distinguish between multipath and singlepath */ + if (hopcount < 2) + return ip_mkroute_input_def(skb, res, fl, in_dev, daddr, +- saddr, tos); ++ saddr, tos, 0); + + /* add all alternatives to the routing cache */ + for (hop = 0; hop < hopcount; hop++) { +@@ -1853,7 +1860,7 @@ + + /* create a routing cache entry */ + err = __mkroute_input(skb, res, in_dev, daddr, saddr, tos, +- &rth); ++ 0, &rth); + if (err) + return err; + +@@ -1873,7 +1880,7 @@ + skb->dst = &rtres->u.dst; + return err; + #else /* CONFIG_IP_ROUTE_MULTIPATH_CACHED */ +- return ip_mkroute_input_def(skb, res, fl, in_dev, daddr, saddr, tos); ++ return ip_mkroute_input_def(skb, res, fl, in_dev, daddr, saddr, tos, lsrc); + #endif /* CONFIG_IP_ROUTE_MULTIPATH_CACHED */ + } + +@@ -1889,20 +1896,20 @@ + */ + + static int ip_route_input_slow(struct sk_buff *skb, u32 daddr, u32 saddr, +- u8 tos, struct net_device *dev) ++ u8 tos, struct net_device *dev, u32 lsrc) + { + struct fib_result res; + struct in_device *in_dev = in_dev_get(dev); + struct flowi fl = { .nl_u = { .ip4_u = + { .daddr = daddr, +- .saddr = saddr, ++ .saddr = lsrc? : saddr, + .tos = tos, + .scope = RT_SCOPE_UNIVERSE, + #ifdef CONFIG_IP_ROUTE_FWMARK + .fwmark = skb->nfmark + #endif + } }, +- .iif = dev->ifindex }; ++ .iif = lsrc? loopback_dev.ifindex : dev->ifindex }; + unsigned flags = 0; + u32 itag = 0; + struct rtable * rth; +@@ -1935,6 +1942,12 @@ + if (BADCLASS(daddr) || ZERONET(daddr) || LOOPBACK(daddr)) + goto martian_destination; + ++ if (lsrc) { ++ if (MULTICAST(lsrc) || BADCLASS(lsrc) || ++ ZERONET(lsrc) || LOOPBACK(lsrc)) ++ goto e_inval; ++ } ++ + /* + * Now we are ready to route packet. + */ +@@ -1944,6 +1957,10 @@ + goto no_route; + } + free_res = 1; ++ if (lsrc && res.type != RTN_UNICAST && res.type != RTN_NAT) ++ goto e_inval; ++ fl.iif = dev->ifindex; ++ fl.fl4_src = saddr; + + RT_CACHE_STAT_INC(in_slow_tot); + +@@ -1968,7 +1985,7 @@ + if (res.type != RTN_UNICAST) + goto martian_destination; + +- err = ip_mkroute_input(skb, &res, &fl, in_dev, daddr, saddr, tos); ++ err = ip_mkroute_input(skb, &res, &fl, in_dev, daddr, saddr, tos, lsrc); + if (err == -ENOBUFS) + goto e_nobufs; + if (err == -EINVAL) +@@ -1983,6 +2000,8 @@ + brd_input: + if (skb->protocol != htons(ETH_P_IP)) + goto e_inval; ++ if (lsrc) ++ goto e_inval; + + if (ZERONET(saddr)) + spec_dst = inet_select_addr(dev, 0, RT_SCOPE_LINK); +@@ -2025,6 +2044,7 @@ + rth->u.dst.dev = &loopback_dev; + dev_hold(rth->u.dst.dev); + rth->idev = in_dev_get(rth->u.dst.dev); ++ rth->fl.fl4_gw = 0; + rth->rt_gateway = daddr; + rth->rt_spec_dst= spec_dst; + rth->u.dst.input= ip_local_deliver; +@@ -2074,8 +2094,9 @@ + goto e_inval; + } + +-int ip_route_input(struct sk_buff *skb, u32 daddr, u32 saddr, +- u8 tos, struct net_device *dev) ++static inline int ++ip_route_input_cached(struct sk_buff *skb, u32 daddr, u32 saddr, ++ u8 tos, struct net_device *dev, u32 lsrc) + { + struct rtable * rth; + unsigned hash; +@@ -2090,6 +2111,7 @@ + if (rth->fl.fl4_dst == daddr && + rth->fl.fl4_src == saddr && + rth->fl.iif == iif && ++ rth->fl.fl4_lsrc == lsrc && + rth->fl.oif == 0 && + #ifdef CONFIG_IP_ROUTE_FWMARK + rth->fl.fl4_fwmark == skb->nfmark && +@@ -2138,7 +2160,19 @@ + rcu_read_unlock(); + return -EINVAL; + } +- return ip_route_input_slow(skb, daddr, saddr, tos, dev); ++ return ip_route_input_slow(skb, daddr, saddr, tos, dev, lsrc); ++} ++ ++int ip_route_input(struct sk_buff *skb, u32 daddr, u32 saddr, ++ u8 tos, struct net_device *dev) ++{ ++ return ip_route_input_cached(skb, daddr, saddr, tos, dev, 0); ++} ++ ++int ip_route_input_lookup(struct sk_buff *skb, u32 daddr, u32 saddr, ++ u8 tos, struct net_device *dev, u32 lsrc) ++{ ++ return ip_route_input_cached(skb, daddr, saddr, tos, dev, lsrc); + } + + static inline int __mkroute_output(struct rtable **result, +@@ -2217,6 +2251,7 @@ + rth->fl.fl4_tos = tos; + rth->fl.fl4_src = oldflp->fl4_src; + rth->fl.oif = oldflp->oif; ++ rth->fl.fl4_gw = oldflp->fl4_gw; + #ifdef CONFIG_IP_ROUTE_FWMARK + rth->fl.fl4_fwmark= oldflp->fl4_fwmark; + #endif +@@ -2361,6 +2396,7 @@ + struct flowi fl = { .nl_u = { .ip4_u = + { .daddr = oldflp->fl4_dst, + .saddr = oldflp->fl4_src, ++ .gw = oldflp->fl4_gw, + .tos = tos & IPTOS_RT_MASK, + .scope = ((tos & RTO_ONLINK) ? + RT_SCOPE_LINK : +@@ -2466,6 +2502,7 @@ + dev_out = &loopback_dev; + dev_hold(dev_out); + fl.oif = loopback_dev.ifindex; ++ fl.fl4_gw = 0; + res.type = RTN_LOCAL; + flags |= RTCF_LOCAL; + goto make_route; +@@ -2473,7 +2510,7 @@ + + if (fib_lookup(&fl, &res)) { + res.fi = NULL; +- if (oldflp->oif) { ++ if (oldflp->oif && dev_out->flags & IFF_UP) { + /* Apparently, routing tables are wrong. Assume, + that the destination is on link. + +@@ -2513,6 +2550,7 @@ + dev_out = &loopback_dev; + dev_hold(dev_out); + fl.oif = dev_out->ifindex; ++ fl.fl4_gw = 0; + if (res.fi) + fib_info_put(res.fi); + res.fi = NULL; +@@ -2520,13 +2558,12 @@ + goto make_route; + } + ++ if (res.type == RTN_UNICAST) ++ fib_select_default(&fl, &res); + #ifdef CONFIG_IP_ROUTE_MULTIPATH +- if (res.fi->fib_nhs > 1 && fl.oif == 0) ++ if (res.fi->fib_nhs > 1) + fib_select_multipath(&fl, &res); +- else + #endif +- if (!res.prefixlen && res.type == RTN_UNICAST && !fl.oif) +- fib_select_default(&fl, &res); + + if (!fl.fl4_src) + fl.fl4_src = FIB_RES_PREFSRC(res); +@@ -2563,6 +2600,7 @@ + rth->fl.fl4_src == flp->fl4_src && + rth->fl.iif == 0 && + rth->fl.oif == flp->oif && ++ rth->fl.fl4_gw == flp->fl4_gw && + #ifdef CONFIG_IP_ROUTE_FWMARK + rth->fl.fl4_fwmark == flp->fl4_fwmark && + #endif +@@ -3199,3 +3237,4 @@ + EXPORT_SYMBOL(__ip_select_ident); + EXPORT_SYMBOL(ip_route_input); + EXPORT_SYMBOL(ip_route_output_key); ++EXPORT_SYMBOL(ip_route_input_lookup); diff --git a/target/linux/generic-2.6/patches/202-mips-freestanding.patch b/target/linux/generic-2.6/patches/202-mips-freestanding.patch new file mode 100644 index 0000000..0441261 --- /dev/null +++ b/target/linux/generic-2.6/patches/202-mips-freestanding.patch @@ -0,0 +1,12 @@ +--- linux-2.6.17/arch/mips/Makefile 2006-06-19 15:12:09.000000000 -0700 ++++ linux-2.6.17/arch/mips/Makefile 2006-06-19 15:11:59.000000000 -0700 +@@ -602,6 +602,9 @@ + core-$(CONFIG_TOSHIBA_RBTX4938) += arch/mips/tx4938/common/ + load-$(CONFIG_TOSHIBA_RBTX4938) += 0xffffffff80100000 + ++# temporary until string.h is fixed ++cflags-y += -ffreestanding ++ + cflags-y += -Iinclude/asm-mips/mach-generic + drivers-$(CONFIG_PCI) += arch/mips/pci/ + diff --git a/target/linux/generic-2.6/patches/203-fix_initrd_duplication.patch b/target/linux/generic-2.6/patches/203-fix_initrd_duplication.patch new file mode 100644 index 0000000..4cef893 --- /dev/null +++ b/target/linux/generic-2.6/patches/203-fix_initrd_duplication.patch @@ -0,0 +1,13 @@ +diff -Nurb linux-2.6.17/usr/Makefile linux-2.6.17/usr/Makefile +--- linux-2.6.17/usr/Makefile 2006-06-20 11:51:27.000000000 -0700 ++++ linux-2.6.17/usr/Makefile 2006-06-20 11:51:34.000000000 -0700 +@@ -21,8 +21,7 @@ + $(CONFIG_INITRAMFS_SOURCE),-d) + ramfs-args := \ + $(if $(CONFIG_INITRAMFS_ROOT_UID), -u $(CONFIG_INITRAMFS_ROOT_UID)) \ +- $(if $(CONFIG_INITRAMFS_ROOT_GID), -g $(CONFIG_INITRAMFS_ROOT_GID)) \ +- $(ramfs-input) ++ $(if $(CONFIG_INITRAMFS_ROOT_GID), -g $(CONFIG_INITRAMFS_ROOT_GID)) + + # .initramfs_data.cpio.gz.d is used to identify all files included + # in initramfs and to detect if any files are added/removed. diff --git a/target/linux/generic-2.6/patches/204-jffs2_eofdetect.patch b/target/linux/generic-2.6/patches/204-jffs2_eofdetect.patch new file mode 100644 index 0000000..9f248c9 --- /dev/null +++ b/target/linux/generic-2.6/patches/204-jffs2_eofdetect.patch @@ -0,0 +1,58 @@ +diff -urN linux.old/fs/jffs2/build.c linux.dev/fs/jffs2/build.c +--- linux.old/fs/jffs2/build.c 2006-06-18 03:49:35.000000000 +0200 ++++ linux.dev/fs/jffs2/build.c 2006-06-23 21:46:48.000000000 +0200 +@@ -107,6 +107,17 @@ + dbg_fsbuild("scanned flash completely\n"); + jffs2_dbg_dump_block_lists_nolock(c); + ++ if (c->flags & (1 << 7)) { ++ printk("%s(): unlocking the mtd device... ", __func__); ++ if (c->mtd->unlock) ++ c->mtd->unlock(c->mtd, 0, c->mtd->size); ++ printk("done.\n"); ++ ++ printk("%s(): erasing all blocks after the end marker... ", __func__); ++ jffs2_erase_pending_blocks(c, -1); ++ printk("done.\n"); ++ } ++ + dbg_fsbuild("pass 1 starting\n"); + c->flags |= JFFS2_SB_FLAG_BUILDING; + /* Now scan the directory tree, increasing nlink according to every dirent found. */ +diff -urN linux.old/fs/jffs2/scan.c linux.dev/fs/jffs2/scan.c +--- linux.old/fs/jffs2/scan.c 2006-06-23 21:39:38.000000000 +0200 ++++ linux.dev/fs/jffs2/scan.c 2006-06-23 21:42:18.000000000 +0200 +@@ -119,9 +119,12 @@ + + /* reset summary info for next eraseblock scan */ + jffs2_sum_reset_collected(s); +- +- ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), +- buf_size, s); ++ ++ if (c->flags & (1 << 7)) ++ ret = BLK_STATE_ALLFF; ++ else ++ ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), ++ buf_size, s); + + if (ret < 0) + goto out; +@@ -389,6 +392,17 @@ + return err; + } + ++ if ((buf[0] == 0xde) && ++ (buf[1] == 0xad) && ++ (buf[2] == 0xc0) && ++ (buf[3] == 0xde)) { ++ /* end of filesystem. erase everything after this point */ ++ printk("%s(): End of filesystem marker found at 0x%x\n", __func__, jeb->offset); ++ c->flags |= (1 << 7); ++ ++ return BLK_STATE_ALLFF; ++ } ++ + /* We temporarily use 'ofs' as a pointer into the buffer/jeb */ + ofs = 0; + diff --git a/target/linux/generic-2.6/patches/205-block2mtd_fix.patch b/target/linux/generic-2.6/patches/205-block2mtd_fix.patch new file mode 100644 index 0000000..9aaffc5 --- /dev/null +++ b/target/linux/generic-2.6/patches/205-block2mtd_fix.patch @@ -0,0 +1,235 @@ +diff -urN linux.old/drivers/mtd/devices/block2mtd.c linux.dev/drivers/mtd/devices/block2mtd.c +--- linux.old/drivers/mtd/devices/block2mtd.c 2006-07-29 19:53:54.000000000 +0200 ++++ linux.dev/drivers/mtd/devices/block2mtd.c 2006-07-29 19:47:03.000000000 +0200 +@@ -4,11 +4,10 @@ + * block2mtd.c - create an mtd from a block device + * + * Copyright (C) 2001,2002 Simon Evans <spse@secret.org.uk> +- * Copyright (C) 2004,2005 Jörn Engel <joern@wh.fh-wedel.de> ++ * Copyright (C) 2004-2006 Jörn Engel <joern@wh.fh-wedel.de> + * + * Licence: GPL + */ +-#include <linux/config.h> + #include <linux/module.h> + #include <linux/fs.h> + #include <linux/blkdev.h> +@@ -19,6 +18,7 @@ #include <linux/init.h> + #include <linux/mtd/mtd.h> + #include <linux/buffer_head.h> + #include <linux/mutex.h> ++#include <linux/mount.h> + + #define VERSION "$Revision: 1.30 $" + +@@ -62,10 +61,8 @@ + read_lock_irq(&mapping->tree_lock); + for (i = 0; i < PAGE_READAHEAD; i++) { + pagei = index + i; +- if (pagei > end_index) { +- INFO("Overrun end of disk in cache readahead\n"); ++ if (pagei > end_index) + break; +- } + page = radix_tree_lookup(&mapping->page_tree, pagei); + if (page && (!i)) + break; +@@ -237,6 +237,8 @@ static int _block2mtd_write(struct block + } + return 0; + } ++ ++ + static int block2mtd_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) + { +@@ -300,6 +302,19 @@ static struct block2mtd_dev *add_device( + + /* Get a handle on the device */ + bdev = open_bdev_excl(devname, O_RDWR, NULL); ++#ifndef MODULE ++ if (IS_ERR(bdev)) { ++ ++ /* We might not have rootfs mounted at this point. Try ++ to resolve the device name by other means. */ ++ ++ dev_t dev = name_to_dev_t(devname); ++ if (dev != 0) { ++ bdev = open_by_devnum(dev, FMODE_WRITE | FMODE_READ); ++ } ++ } ++#endif ++ + if (IS_ERR(bdev)) { + ERROR("error: cannot open device %s", devname); + goto devinit_err; +@@ -331,7 +347,6 @@ static struct block2mtd_dev *add_device( + dev->mtd.writev = default_mtd_writev; + dev->mtd.sync = block2mtd_sync; + dev->mtd.read = block2mtd_read; +- dev->mtd.readv = default_mtd_readv; + dev->mtd.priv = dev; + dev->mtd.owner = THIS_MODULE; + +@@ -351,6 +366,12 @@ devinit_err: + } + + ++/* This function works similar to reguler strtoul. In addition, it ++ * allows some suffixes for a more human-readable number format: ++ * ki, Ki, kiB, KiB - multiply result with 1024 ++ * Mi, MiB - multiply result with 1024^2 ++ * Gi, GiB - multiply result with 1024^3 ++ */ + static int ustrtoul(const char *cp, char **endp, unsigned int base) + { + unsigned long result = simple_strtoul(cp, endp, base); +@@ -359,11 +380,16 @@ static int ustrtoul(const char *cp, char + result *= 1024; + case 'M': + result *= 1024; ++ case 'K': + case 'k': + result *= 1024; + /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */ +- if ((*endp)[1] == 'i') +- (*endp) += 2; ++ if ((*endp)[1] == 'i') { ++ if ((*endp)[2] == 'B') ++ (*endp) += 3; ++ else ++ (*endp) += 2; ++ } + } + return result; + } +@@ -383,26 +409,6 @@ static int parse_num(size_t *num, const + } + + +-static int parse_name(char **pname, const char *token, size_t limit) +-{ +- size_t len; +- char *name; +- +- len = strlen(token) + 1; +- if (len > limit) +- return -ENOSPC; +- +- name = kmalloc(len, GFP_KERNEL); +- if (!name) +- return -ENOMEM; +- +- strcpy(name, token); +- +- *pname = name; +- return 0; +-} +- +- + static inline void kill_final_newline(char *str) + { + char *newline = strrchr(str, '\n'); +@@ -416,9 +422,16 @@ #define parse_err(fmt, args...) do { \ + return 0; \ + } while (0) + +-static int block2mtd_setup(const char *val, struct kernel_param *kp) ++#ifndef MODULE ++static int block2mtd_init_called = 0; ++static __initdata char block2mtd_paramline[80 + 12]; /* 80 for device, 12 for erase size */ ++#endif ++ ++ ++static int block2mtd_setup2(const char *val) + { +- char buf[80+12], *str=buf; /* 80 for device, 12 for erase size */ ++ char buf[80 + 12]; /* 80 for device, 12 for erase size */ ++ char *str = buf; + char *token[2]; + char *name; + size_t erase_size = PAGE_SIZE; +@@ -430,7 +443,7 @@ static int block2mtd_setup(const char *v + strcpy(str, val); + kill_final_newline(str); + +- for (i=0; i<2; i++) ++ for (i = 0; i < 2; i++) + token[i] = strsep(&str, ","); + + if (str) +@@ -439,18 +452,16 @@ static int block2mtd_setup(const char *v + if (!token[0]) + parse_err("no argument"); + +- ret = parse_name(&name, token[0], 80); +- if (ret == -ENOMEM) +- parse_err("out of memory"); +- if (ret == -ENOSPC) +- parse_err("name too long"); +- if (ret) +- return 0; ++ name = token[0]; ++ if (strlen(name) + 1 > 80) ++ parse_err("device name too long"); + + if (token[1]) { + ret = parse_num(&erase_size, token[1]); +- if (ret) ++ if (ret) { ++ kfree(name); + parse_err("illegal erase size"); ++ } + } + + add_device(name, erase_size); +@@ -459,13 +470,48 @@ static int block2mtd_setup(const char *v + } + + ++static int block2mtd_setup(const char *val, struct kernel_param *kp) ++{ ++#ifdef MODULE ++ return block2mtd_setup2(val); ++#else ++ /* If more parameters are later passed in via ++ /sys/module/block2mtd/parameters/block2mtd ++ and block2mtd_init() has already been called, ++ we can parse the argument now. */ ++ ++ if (block2mtd_init_called) ++ return block2mtd_setup2(val); ++ ++ /* During early boot stage, we only save the parameters ++ here. We must parse them later: if the param passed ++ from kernel boot command line, block2mtd_setup() is ++ called so early that it is not possible to resolve ++ the device (even kmalloc() fails). Deter that work to ++ block2mtd_setup2(). */ ++ ++ strlcpy(block2mtd_paramline, val, sizeof(block2mtd_paramline)); ++ ++ return 0; ++#endif ++} ++ ++ + module_param_call(block2mtd, block2mtd_setup, NULL, NULL, 0200); + MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=<dev>[,<erasesize>]\""); + + static int __init block2mtd_init(void) + { ++ int ret = 0; + INFO("version " VERSION); +- return 0; ++ ++#ifndef MODULE ++ if (strlen(block2mtd_paramline)) ++ ret = block2mtd_setup2(block2mtd_paramline); ++ block2mtd_init_called = 1; ++#endif ++ ++ return ret; + } + + diff --git a/target/linux/generic-2.6/patches/206-pppoe_mtu_fix.patch b/target/linux/generic-2.6/patches/206-pppoe_mtu_fix.patch new file mode 100644 index 0000000..1ad8220 --- /dev/null +++ b/target/linux/generic-2.6/patches/206-pppoe_mtu_fix.patch @@ -0,0 +1,12 @@ +--- linux-2.6.17/drivers/net/pppoe.c.old 2006-10-10 13:47:56.000000000 +0200 ++++ linux-2.6.17/drivers/net/pppoe.c 2006-10-10 13:52:02.000000000 +0200 +@@ -600,6 +600,9 @@ + po->chan.hdrlen = (sizeof(struct pppoe_hdr) + + dev->hard_header_len); + ++ if (po->chan.mtu > dev->mtu - sizeof(struct pppoe_hdr)) ++ po->chan.mtu = dev->mtu - sizeof(struct pppoe_hdr); ++ + po->chan.private = sk; + po->chan.ops = &pppoe_chan_ops; + |