Sync with the new ipfw3 version.
marta [Thu, 15 Apr 2010 13:26:28 +0000 (13:26 +0000)]
The major enhancement is a completely restructured version of
dummynet, with support for different packet scheduling algorithms
(loadable at runtime), faster queue/pipe lookup, and a much cleaner
internal architecture and kernel/userland ABI which simplifies
future extensions.

104 files changed:
020-mips-hz1000.patch [new file with mode: 0644]
Makefile
Makefile.openwrt
NOTES
README
README.openwrt [deleted file]
binary/README.txt [new file with mode: 0644]
binary/cyg-ipfw.exe [new file with mode: 0644]
binary/cygwin1.dll [new file with mode: 0644]
binary/ipfw.exe [new file with mode: 0644]
binary/ipfw.sys [new file with mode: 0644]
binary/netipfw.inf [new file with mode: 0644]
binary/netipfw_m.inf [new file with mode: 0644]
binary/testme.bat [new file with mode: 0644]
binary/wget.exe [new file with mode: 0644]
dummynet2/Makefile
dummynet2/bsd_compat.c
dummynet2/debug.c [new file with mode: 0644]
dummynet2/dn_heap.c [new file with mode: 0644]
dummynet2/dn_sched_fifo.c [new file with mode: 0644]
dummynet2/dn_sched_prio.c [new file with mode: 0755]
dummynet2/dn_sched_qfq.c [new file with mode: 0644]
dummynet2/dn_sched_rr.c [new file with mode: 0644]
dummynet2/dn_sched_wf2q.c [new file with mode: 0644]
dummynet2/include/net/radix.h
dummynet2/include/netinet/ip.h
dummynet2/include/netinet/ip_dummynet.h
dummynet2/include/netinet/ip_fw.h
dummynet2/include/netinet/ipfw/dn_heap.h [new file with mode: 0644]
dummynet2/include/netinet/ipfw/dn_sched.h [new file with mode: 0644]
dummynet2/include/netinet/ipfw/ip_dn_private.h [new file with mode: 0644]
dummynet2/include/netinet/ipfw/ip_fw_private.h
dummynet2/include/netinet/tcp.h
dummynet2/include/sys/malloc.h
dummynet2/include/sys/mbuf.h
dummynet2/include/sys/queue.h
dummynet2/include/sys/systm.h
dummynet2/include/sys/taskqueue.h
dummynet2/ip_dn_glue.c [new file with mode: 0644]
dummynet2/ip_dn_io.c [new file with mode: 0644]
dummynet2/ip_dummynet.c
dummynet2/ip_fw2.c
dummynet2/ip_fw_dynamic.c
dummynet2/ip_fw_log.c
dummynet2/ip_fw_pfil.c
dummynet2/ip_fw_sockopt.c
dummynet2/ip_fw_table.c
dummynet2/ipfw2_mod.c
dummynet2/md_win.c [new file with mode: 0644]
dummynet2/miniport.c [new file with mode: 0644]
dummynet2/missing.h
dummynet2/netipfw.inf [new file with mode: 0644]
dummynet2/netipfw_m.inf [new file with mode: 0644]
dummynet2/passthru.c [new file with mode: 0644]
dummynet2/passthru.h [new file with mode: 0644]
dummynet2/precomp.h [new file with mode: 0644]
dummynet2/protocol.c [new file with mode: 0644]
dummynet2/radix.c
dummynet2/winmissing.h [new file with mode: 0644]
glue.h
ipfw/Makefile
ipfw/altq.c [deleted file]
ipfw/dummynet.c
ipfw/glue.c
ipfw/include_e/libutil.h [deleted file]
ipfw/include_e/sys/sockio.h [deleted file]
ipfw/ipfw.8 [new file with mode: 0644]
ipfw/ipfw2.c
ipfw/ipfw2.h
ipfw/ipv6.c
ipfw/main.c
ipfw/nat.c [deleted file]
ipfw/rule_test.sh [new file with mode: 0755]
ipfw/ws2_32.def [new file with mode: 0644]
kmod-ipfw3_2.4.35.4-brcm-2.4-1_mipsel.ipk [new file with mode: 0644]
original_passthru/makefile [new file with mode: 0644]
original_passthru/miniport.c [new file with mode: 0644]
original_passthru/netsf.inf [new file with mode: 0644]
original_passthru/netsf_m.inf [new file with mode: 0644]
original_passthru/passthru.c [new file with mode: 0644]
original_passthru/passthru.h [new file with mode: 0644]
original_passthru/passthru.htm [new file with mode: 0644]
original_passthru/passthru.rc [new file with mode: 0644]
original_passthru/precomp.h [new file with mode: 0644]
original_passthru/protocol.c [new file with mode: 0644]
original_passthru/sources [new file with mode: 0644]
planetlab/ipfw.cron
planetlab/planetlab-tags.mk
planetlab/planetlab.mk
planetlab/sample_hook
tcc-0.9.25-bsd.zip [new file with mode: 0644]
tcc_glue.h [new file with mode: 0644]
test/Makefile [new file with mode: 0644]
test/basic_ipfw.sh [new file with mode: 0755]
test/dn_test.h [new file with mode: 0644]
test/main.c [new file with mode: 0644]
test/memory_leak.sh [new file with mode: 0644]
test/mylist.h [new file with mode: 0644]
test/test_dn_heap.c [new file with mode: 0644]
test/test_dn_sched.c [new file with mode: 0644]
win64/mysetenv.sh [new file with mode: 0644]
win64/netipfw.inf [new file with mode: 0644]
win64/netipfw_m.inf [new file with mode: 0644]
win64/sources [new file with mode: 0644]

diff --git a/020-mips-hz1000.patch b/020-mips-hz1000.patch
new file mode 100644 (file)
index 0000000..eb54ca2
--- /dev/null
@@ -0,0 +1,11 @@
+--- include/asm-mips/param_orig.h      2010-02-23 12:45:58.000000000 +0100
++++ include/asm-mips/param.h   2010-02-23 12:00:31.000000000 +0100
+@@ -41,7 +41,7 @@
+    counter is increasing.  This value is independent from the external value
+    and can be changed in order to suit the hardware and application
+    requirements.  */
+-#  define HZ 100
++#  define HZ 1000
+ #  define hz_to_std(a) (a)
+ #endif /* Not a DECstation  */
index 55b8e0b..21530fc 100644 (file)
--- a/Makefile
+++ b/Makefile
 # We assume that $(USRDIR) contains include/ and lib/ used to build userland.
 
 DATE ?= $(shell date +%Y%m%d)
-SNAPSHOT_NAME=ipfw_mod-$(DATE)
+SNAPSHOT_NAME=$(DATE)-ipfw3.tgz
+BINDIST=$(DATE)-dummynet-linux.tgz
+WINDIST=$(DATE)-dummynet-windows.zip
+
+###########################################
+#  windows x86 and x64 specific variables #
+###########################################
+#  DRIVE must be the hard drive letter where DDK is installed
+#  DDKDIR must be the path to the DDK root directory, without drive letter
+#  TARGETOS (x64 only) must be one of the following:
+#  wnet   -> windows server 2003
+#  wlh    -> windows vista and windows server 2008
+#  win7   -> windows 7
+#  future version must be added here
+export DDK
+export DRIVE
+export DDKDIR
+DRIVE = C:
+DDKDIR = /WinDDK/7600.16385.1
+DDK = $(DRIVE)$(DDKDIR)
+
+TARGETOS=win7
 
 _all: all
 
-all clean distclean:
+clean distclean:
        echo target is $(@)
        (cd ipfw && $(MAKE) $(@) )
        (cd dummynet2 && $(MAKE) $(@) )
+       # -- windows x64 only
+       - rm -rf dummynet2-64
+       - rm -rf ipfw-64
+       - rm -rf binary64
+
+all:
+       echo target is $(@)
+       (cd ipfw && $(MAKE) $(@) )
+       (cd dummynet2 && $(MAKE) $(@) )
+       # -- windows only
+       - [ -f ipfw/ipfw.exe ] && cp ipfw/ipfw.exe binary/ipfw.exe
+       - [ -f dummynet2/objchk_wxp_x86/i386/ipfw.sys ] && \
+               cp dummynet2/objchk_wxp_x86/i386/ipfw.sys binary/ipfw.sys
 
 snapshot:
-       (cd ..; tar cvzhf /tmp/$(SNAPSHOT_NAME).tgz --exclude .svn \
+       $(MAKE) distclean
+       (cd ..; tar cvzhf /tmp/$(SNAPSHOT_NAME) --exclude .svn \
                --exclude README.openwrt --exclude tags --exclude NOTES \
-               ipfw_mod )
+               --exclude tcc-0.9.25-bsd \
+               --exclude original_passthru \
+               --exclude ipfw3.diff --exclude add_rules \
+               ipfw3 )
+
+bindist:
+       $(MAKE) clean
+       $(MAKE) all
+       tar cvzf /tmp/$(BINDIST) ipfw/ipfw ipfw/ipfw.8 dummynet2/ipfw_mod.ko
+
+windist:
+       $(MAKE) clean
+       -$(MAKE) all
+       -rm /tmp/$(WINDIST)
+       zip -r /tmp/$(WINDIST) binary -x \*.svn\*
+
+win64: clean
+       (cd dummynet2 && $(MAKE) include_e)
+       cp -r ipfw ipfw-64
+       echo "EXTRA_CFLAGS += -D_X64EMU" >> ipfw-64/Makefile
+       (cd ipfw-64 && $(MAKE) all)
+       cp -r dummynet2 dummynet2-64
+       rm -f dummynet2-64/Makefile
+       cp win64/sources dummynet2-64/sources
+       mkdir dummynet2-64/tmpbuild
+       mkdir binary64
+       win64/mysetenv.sh $(DRIVE) $(DDKDIR) $(TARGETOS)
+       cp binary/cygwin1.dll binary64/cygwin1.dll
+       cp ipfw-64/ipfw.exe binary64/ipfw.exe
+       cp win64/*.inf binary64
+       cp binary/testme.bat binary64/testme.bat
+       cp binary/wget.exe binary64/wget.exe
+       
+planetlab_update:
+       # clean and create a local working directory
+       rm -rf /tmp/pl-tmp
+       mkdir -p /tmp/pl-tmp/pl
+       mkdir -p /tmp/pl-tmp/ol2
+       # get the trunk version of the PlanetLab repository
+       # to specify the sshkey use the .ssh/config file
+       (cd /tmp/pl-tmp/pl; \
+               svn co svn+ssh://svn.planet-lab.org/svn/ipfw/trunk)
+       # get an updated copy of the main ipfw repository
+       (cd /tmp/pl-tmp/ol2; \
+               svn export svn+ssh://onelab2.iet.unipi.it/home/svn/ports-luigi/dummynet-branches/ipfw3)
+       # copy the new version over the old one
+       (cd /tmp/pl-tmp; cp -rP ol2/ipfw3/* pl/trunk)
+       # files cleanup in the old version
+       (cd /tmp/pl-tmp; diff -r ol2/ipfw3 pl/trunk | \
+               grep -v "svn" | awk '{print $$3 $$4}' | \
+               sed 's/:/\//' | xargs rm -rf)
+       # local adjustmens here
+       rm -rf /tmp/pl-tmp/pl/trunk/planetlab/check_planetlab_sync
+       # commit to the remote repo
+       @echo "Please, revise the update with the commands:"
+       @echo "(cd /tmp/pl-tmp/pl/trunk; svn diff)"
+       @echo "(cd /tmp/pl-tmp/pl/trunk; svn status)"
+       @echo "and commit with:"
+       @echo "(cd /tmp/pl-tmp/pl/trunk; svn ci -m 'Update from the mail ipfw repo.')"
 
 install:
index b618a52..437b32a 100644 (file)
@@ -1,14 +1,14 @@
 # Makefile to build the package in openwrt.
-# goes into package/ipfw2/Makefile
+# goes into package/ipfw3/Makefile
 #
 # Edit IPFW_DIR to point to the directory with the sources for ipfw
 
-IPFW_DIR := $(TOPDIR)/../ipfw_mod
+IPFW_DIR := $(TOPDIR)/../ipfw3
 
 include $(TOPDIR)/rules.mk
 include $(INCLUDE_DIR)/kernel.mk
 
-PKG_NAME:=kmod-ipfw2
+PKG_NAME:=kmod-ipfw3
 PKG_RELEASE:=1
 
 # MV is undefined
@@ -17,20 +17,20 @@ MV ?= mv
 include $(INCLUDE_DIR)/package.mk
 
 # Description for the package.
-# The names KernelPackage/ipfw2 must match the arguments to the
-# call $(eval $(call KernelPackage,ipfw2)) used to build it
+# The names KernelPackage/ipfw3 must match the arguments to the
+# call $(eval $(call KernelPackage,ipfw3)) used to build it
 
 
-define KernelPackage/ipfw2
+define KernelPackage/ipfw3
  SUBMENU:=Other modules
  TITLE:= IPFW and dummynet
  # FILES is what makes up the module, both kernel and userland
  # It must be in the KernelPackage section
- FILES := $(PKG_BUILD_DIR)/dummynet/ipfw_mod.o $(PKG_BUILD_DIR)/ipfw/ipfw
+ FILES := $(PKG_BUILD_DIR)/dummynet2/ipfw_mod.o $(PKG_BUILD_DIR)/ipfw/ipfw
  # AUTOLOAD:=$(call AutoLoad,80,ipfw_mod)
 endef
 
-define KernelPackage/ipfw2/description
+define KernelPackage/ipfw3/description
  This package contains the ipfw and dummynet module
 endef
 
@@ -45,7 +45,6 @@ define Build/Prepare
        mkdir -p $(PKG_BUILD_DIR)
        $(CP) -Rp $(IPFW_DIR)/* $(PKG_BUILD_DIR)/
        (cd $(PKG_BUILD_DIR)/ipfw && $(MAKE) include_e )
-       (cd $(PKG_BUILD_DIR)/dummynet && $(MAKE) include_e )
        (cd $(PKG_BUILD_DIR)/dummynet2 && $(MAKE) include_e )
 endef
 
@@ -54,33 +53,28 @@ define Build/Compile
        $(MAKE) -C "$(LINUX_DIR)" \
                CROSS_COMPILE="$(TARGET_CROSS)" \
                ARCH="$(LINUX_KARCH)" \
-               SUBDIRS="$(PKG_BUILD_DIR)/dummynet" \
-               VER=openwrt modules
-       $(MAKE) -C "$(LINUX_DIR)" \
-               CROSS_COMPILE="$(TARGET_CROSS)" \
-               ARCH="$(LINUX_KARCH)" \
                SUBDIRS="$(PKG_BUILD_DIR)/dummynet2" \
                VER=openwrt modules
        # compile the userland part for openwrt
        $(MAKE) -C $(PKG_BUILD_DIR)/ipfw \
                $(TARGET_CONFIGURE_OPTS) \
-               CFLAGS="$(TARGET_CFLAGS) -I./include_e -I./include -include ../glue.h" \
+               CFLAGS="$(TARGET_CFLAGS) -DSYSCTL_NODE -DEMULATE_SYSCTL -I./include_e -I./include -include ../glue.h -DNO_ALTQ" \
                VER=openwrt all
 endef
 
-define Package/ipfw2-userland
+define Package/ipfw3-userland
   SECTION:=utils
   CATEGORY:=Utilities
   TITLE := /sbin/ipfw
   DESCRIPTION := This is the control program for ipfw and dummynet
 endef
 
-define Package/ipfw2-userland/install
+define Package/ipfw3-userland/install
        $(INSTALL_DIR) $(1) /sbin
 endef
 
 # XXX not entirely clear why the install entry for userland works,
-# given that /sbin/ipfw is in KernelPackage/ipfw2
+# given that /sbin/ipfw is in KernelPackage/ipfw3
 
-$(eval $(call Package,ipfw2-userland))
-$(eval $(call KernelPackage,ipfw2))
+$(eval $(call Package,ipfw3-userland))
+$(eval $(call KernelPackage,ipfw3))
diff --git a/NOTES b/NOTES
index b00a8fd..dc3dd95 100644 (file)
--- a/NOTES
+++ b/NOTES
@@ -1,5 +1,5 @@
 #
-# $Id: NOTES 2844 2009-06-22 10:59:35Z luigi $
+# $Id: NOTES 5355 2010-02-18 18:58:43Z luigi $
 #
 
 ---------------------------------------------------------------------
@@ -21,28 +21,20 @@ available in the port platforms, and hooks for the sockopt/packet
 data.
 
 
-TODO (LINUX), 20090622:
+TODO 20100205:
 + use an appropriate identifier instead of LINUX24
 + find the discharging module hook, in order to force a queue flush
-+ use the output interface as a clock for the pipe
 + better matching on interface names (case insensitive etc ?)
 + match by interface address
 + verify path
 + send keepalives
-+ tables support
-+ uid/gid match (through the socket ?)
-+ pullup or data in external buffers
++ pullup of data in external buffers
 + O_TAG
 + O_DIVERT
 + O_TEE
 + O_SETFIB
 + kmem_cache_alloc 
 
-TODO (WINDOWS) 20090622
-+ all of the above, once it works
-# svn
-https://svn.sourceforge.net/svnroot/wipfw
-
 TODO (OpenWRT) 20090622
 + add a module compilation for 2.6
 
@@ -87,11 +79,6 @@ Other porting issues are in ipfw2_mod.c
 
 ------ WINDOWS PORT ------
 
-A port to windows is still in progress.
-This directory contain a port of ipfw and dummynet to Windows.
-
---- BACKGROUND:
-
 We started from the wipfw port available at [WIPFW] , but
 most of the port is done from scratch using the most recent
 version of ipfw+dummynet from HEAD/RELENG_7 as of March 2009
@@ -173,6 +160,7 @@ have to replicate them.
 gcc attributes are also not present.
 
 C99 types are not present, remapped in <sys/cdefs.h>
+Also, we don't have C99 initializers which sometimes gives trouble.
 
 --- USEFUL LINKS:
 
@@ -188,3 +176,27 @@ C99 types are not present, remapped in <sys/cdefs.h>
 
 [CYGWIN]
        http://www.cygwin.com/setup.exe
+Windows Driver Kit
+http://www.microsoft.com/whdc/DevTools/WDK/WDKpkg.mspx
+
+Debug Symbols for WinXP SP3
+http://www.microsoft.com/whdc/devtools/debugging/symbolpkg.mspx#d
+
+DbgView
+http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx
+
+Cygwin
+http://www.cygwin.com/
+(installazione pacchetti di default + categoria devel)
+
+Winrar (il WDK e' distribuito in un file .iso)
+http://www.rarlab.com/download.htm
+
+puttycyg (terminale per cygwin)
+http://code.google.com/p/puttycyg/
+
+Tortoise SVN
+http://tortoisesvn.net/downloads
+
+EditPlus
+http://www.editplus.com/
diff --git a/README b/README
index 0c3b4e8..c65b91a 100644 (file)
--- a/README
+++ b/README
 #
-# $Id: README 4502 2009-12-15 11:10:33Z marta $
+# $Id: README 6070 2010-04-15 11:58:21Z marta $
 #
 
-This directory contains a port of ipfw and dummynet to Linux and OpenWrt
-(including PlanetLab).  A Windows version is in the works but not ready yet.
-Building the code produces:
-
-       a kernel module,        ipfw_mod.ko
-       a userland program,     /sbin/ipfw
-
+This directory contains a port of ipfw and dummynet to Linux/OpenWrt
+(including PlanetLab) and Windows. This version of ipfw and dummynet
+is called "ipfw3" as it is the third major rewrite of the code.
 The source code here comes straight from FreeBSD (roughly the
-version in RELENG_7 and HEAD as of December 2009), plus some glue code
+version in HEAD as of February 2010), plus some glue code
 and headers written from scratch.
 Unless specified otherwise, all the code here is under a BSD license.
 
-Note:
-       - the linux version miss the "one_pass" feature
+Specific build instructions are below, and in general produce
+
+       a kernel module,        ipfw_mod.ko (ipfw.sys on windows)
+       a userland program,     /sbin/ipfw (ipfw.exe on windows)
+
+which you need to install on your system.
+
+CREDITS:
+    Luigi Rizzo (main design and development)
+    Marta Carbone (Linux and Planetlab ports)
+    Riccardo Panicucci (modular scheduler support)
+    Francesco Magno (Windows port)
+    Fabio Checconi (the QFQ scheduler)
+    Funding from Universita` di Pisa (NETOS project),
+       European Commission (ONELAB2 project)
+    
+=========== INSTALL/REMOVE INSTRUCTIONS ========================
+
+FreeBSD, OSX:
+    INSTALL:
+       kldload ipfw.ko ; kldload dummynet.ko 
+    REMOVE:
+       kldunload dummynet.ko; kldunload ipfw.ko
+
+Linux
+    INSTALL:
+       # Do the following as root
+       insmod ./dummynet2/ipfw_mod.ko
+       cp ipfw/ipfw /usr/local/sbin
+    REMOVE:
+       rmmod ipfw_mod.ko
+
+OpenWRT
+    INSTALL:   # use the correct name for your system
+       opkg install  kmod-ipfw3_2.4.35.4-brcm-2.4-1_mipsel.ipk #install
+       ls -l ls -l /lib/modules/2.4.35.4/ipfw*     # check
+       insmod /lib/modules/2.4.35.4/ipfw_mod.o     # load the module
+       /lib/modules/2.4.35.4/ipfw show             # launch the userspace tool
+    REMOVE:
+       rmmod ipfw_mod.o                            # remove the module
+
+Windows:
+    INSTALL THE NDIS DRIVER
+
+       - open the configuration panel for the network card in use
+         (right click on the icon on the SYSTRAY, or go to
+         Control Panel -> Network and select one card)
+
+       - click on Properties->Install->Service->Add
+       - click on 'Driver Disk' and select 'netipfw.inf' in this folder
+       - select 'ipfw+dummynet' which is the only service you should see
+       - click accept on the warnings for the installation of an unknown
+         driver (roughly twice per existing network card)
+
+       Now you are ready to use the emulator. To configure it, open a 'cmd'
+       window and you can use the ipfw command from the command line.
+       Otherwise click on the 'TESTME.bat' which is a batch program that
+       runs various tests.
+
+    REMOVE:
+       - select a network card as above.
+       - click on Properties
+       - select 'ipfw+dummynet'
+       - click on 'Remove'
+
 
 =================== BUILD INSTRUCTIONS ==========================
 
+***** Windows XP ******
+    You can find a pre-built version in the binary/ subdirectory.
+    To build your own version of the package you need:
+       - MSVC DDK available from ...
+           http://www.microsoft.com/whdc/DevTools/WDK/WDKpkg.mspx
+
+       - optionally, DbgView if you want to see diagnostic
+           http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx
+
+       - cygwin, http://www.cygwin.com/
+         with base packages, make, c compiler, possibly an editor
+         and subversion.
+
+    Edit Makefile in the root directory, and set configuration
+    variables to match your current system (hard drive
+    and path where DDK is installed)
+    Open a shell from cygwin, move to this directory, and simply
+    run "make". The output of the build will be in this
+    directory, made of 4 files:
+       ipfw.exe (you also need cygwin.dll)
+       ipfw.sys (an NDIS intermediate filter driver)
+       dummynet.inf and dummynet_m.inf (installer files)
+
+***** Windows crosscompilation for 64 bit using DDK ******
+    Edit root directory's Makefile and set target
+    operating system
+    From the root directory, run 'make win64', this will:
+    - create ipfw-64 and dummynet2-64 subdirs
+    - patch ipfw makefile to support comunication
+    with 64bit module and build it
+    - replace dummynet makefile with proprietary
+    WinDDK one, named 'sources', and build the module
+    - create a binary64 directory containing
+    module and .inf install files, program
+    binary and relative cygwin dll
+    - install the driver from this directory in the
+    usual way.
+
 ***** Linux 2.6.x ******
 
        make KERNELPATH=/path/to/linux USRDIR=/path/to/usr
@@ -28,7 +125,11 @@ Note:
     KERNELPATH=/lib/modules/`uname -r`/build   --- XXX check ?
 
     NOTE: make sure CONFIG_NETFILTER is enabled in the kernel
-    configuration file. You can enable it by doing
+    configuration file. You need the ncurses devel library,
+    that can be installed according your distro with:
+       apt-get install ncurses-dev     # for debian based distro
+       yum -y install ncurses-dev      # for fedora based distro
+    You can enable CONFIG_NETFILTER by doing:
     
        "(cd ${KERNELPATH}; make menuconfig)"
 
@@ -38,8 +139,8 @@ Note:
            Networking options  --->
               [*] Network packet filtering framework (Netfilter)
 
-       If you have not yet compiled your kernel source, you need to
-       prepare the build environment:
+    If you have not yet compiled your kernel source, you need to
+    prepare the build environment:
 
        (cd $(KERNELPATH); make oldconfig; make prepare; make scripts)
 
@@ -67,64 +168,80 @@ Note:
        wget http://downloads.openwrt.org/kamikaze/8.09.1/kamikaze_8.09.1_source.tar.bz2
        tar xvjf kamikaze_8.09.1_source.tar.bz2
 
-    + "cd" to the directory with the OpenWrt sources (the one that
+    + move to the directory with the OpenWrt sources (the one that
       contains Config.in, rules.mk ...)
 
        cd kamikaze_8.09.1
 
+    + Optional: Add support for 1ms resolution.
+
+       By default OpenWRT kernel is compiled with HZ=100; this implies
+        that all timeouts are rounded to 10ms, too coarse for dummynet.
+        The file 020-mips-hz1000.patch contains a kernel patch to build
+       a kernel with HZ=1000 (i.e. 1ms resolution) as in Linux/FreeBSD.
+        To apply this patch, go in the kernel source directory and
+        patch the kernel
+
+               cd build_dir/linux-brcm-2.4/linux-2.4.35.4
+               cat $IPFW3_SOURCES/020-mips-hz1000.patch | patch -p0
+
+       where IPFW3_SOURCES contains the ipfw3 source code.
+       Now, the next kernel recompilation will use the right HZ value
+
     + Optional: to be sure that the tools are working, make a first
-      compilation as follows:
+      build as follows:
 
        - run "make menuconfig" and set the correct target device,
          drivers, and so on;
        - run "make" to do the build
 
-    + Add ipfw2 to the openwrt package, as follows:
+    + Add ipfw3 to the openwrt package, as follows:
 
       - copy the code from this directory to the place used for the build:
 
-               cp -Rp /path_to_ipfw_mod ../ipfw_mod; 
+               cp -Rp /path_to_ipfw3 ../ipfw3; 
 
        If you want, you can fetch a newer version from the web
-       (cd ..; rm -rf ipfw_mod;
-       wget http://info.iet.unipi.it/~luigi/dummynet/ipfw_mod-latest.tgz;\
-       tar xvzf ipfw_mod-latest.tgz)
+       (cd ..; rm -rf ipfw3; \
+       wget http://info.iet.unipi.it/~luigi/dummynet/ipfw3-latest.tgz;\
+       tar xvzf ipfw3-latest.tgz)
 
       - run the following commands:
-       (mkdir package/ipfw2;
-       cp ../ipfw_mod/Makefile.openwrt package/ipfw2/Makefile)
+       (mkdir package/ipfw3; \
+       cp ../ipfw3/Makefile.openwrt package/ipfw3/Makefile)
 
-       to create the package/ipfw2 directory in the OpenWrt source
-       directory, and copy Makefile.openwrt to package/ipfw2/Makefile:
+       to create the package/ipfw3 directory in the OpenWrt source
+       directory, and copy Makefile.openwrt to package/ipfw3/Makefile ;
 
-      - if necessary, edit package/ipfw2/Makefile and set IPFW_DIR to point to
-       the directory ipfw_mod, which contains the ipfw sources
+      - if necessary, edit package/ipfw3/Makefile and set IPFW_DIR to point to
+       the directory ipfw3, which contains the sources;
 
-      - run "make menuconfig" and select ipfw2 as a module <M> in
-           Kernel Modules -> Other modules -> kmod-ipfw2 
+      - run "make menuconfig" and select kmod-ipfw3 as a module <M> in
+           Kernel Modules -> Other modules -> kmod-ipfw3 
 
       - run "make" to build the package, "make V=99" for verbose build.
 
       - to modify the code, assuming you are in directory "kamikaze_8.09.1"
        
-       (cd ../ipfw_mod && vi ...the files you are interested in )
-       rm -rf build_dir/linux-brcm-2.4/kmod-ipfw2
-       make package/ipfw2/compile V=99
+       (cd ../ipfw3 && vi ...the files you are interested in )
+       rm -rf build_dir/linux-brcm-2.4/kmod-ipfw3
+       make package/ipfw3/compile V=99
 
-    The resulting package is located in bin/packages/mipsel/kmod-ipfw2*,
+    The resulting package is located in bin/packages/mipsel/kmod-ipfw3*,
     upload the file and install on the target system, as follows:
 
-    opkg install  kmod-ipfw2_2.4.35.4-brcm-2.4-1_mipsel.ipk #install
+    opkg install  kmod-ipfw3_2.4.35.4-brcm-2.4-1_mipsel.ipk #install
     ls -l ls -l /lib/modules/2.4.35.4/ipfw*     # check
     insmod /lib/modules/2.4.35.4/ipfw_mod.o     # load the module
     /lib/modules/2.4.35.4/ipfw show             # launch the userspace tool
     rmmod ipfw_mod.o                            # remove the module
 
 ***** PLANETLAB BUILD (within a slice) *****
-These instruction can be used by PlanetLab developers to compile the dummynet module
-on a node. To install the module on the node users need root access in root context.
-PlanetLab users that want to use the dummynet package should ask to PlanetLab support
-for nodes with dummynet emulation capabilities.
+These instruction can be used by PlanetLab developers to compile
+the dummynet module on a node. To install the module on the node
+users need root access in root context.  PlanetLab users that want
+to use the dummynet package should ask to PlanetLab support for
+nodes with dummynet emulation capabilities.
 
     Follow the instructions below. You can just cut&paste
 
@@ -153,117 +270,6 @@ for nodes with dummynet emulation capabilities.
     then after you have updated the repository again
        (cd test/XYZ; sudo make ipfwslice ipfwroot)
 
---- other, instructions (to be verified) ---
-
-To build a kernel module for the PlanetLab distribution you need a build system.
-For an up-to-date and detailed information on how to build a local myplc installation,
-a local mirror, a PlanetLab test system see[1]
-
-To create a build system you need to do the following steps:
-
- 1. install CentOS 5, detailed information[2]
-
- 1.A download the image from the main site[3] for example:
-
-       wget http://mi.mirror.garr.it/mirrors/CentOS/5.4/isos/i386/CentOS-5.4-i386-netinstall.iso
-
- 1.B Add the repository
-
-       cat >> /etc/yum.repos.d/dhozac-vserver.repo <<EOF
-       [dhozac-vserver]
-name=Linux-VServer related packages for CentOS $releasever - $basearch
-baseurl=http://rpm.hozac.com/dhozac/centos/$releasever/vserver/$basearch
-gpgkey=http://rpm.hozac.com/conf/keys/RPM-DHOZAC-GPG-KEY
-EOF
-
- 1.C Update, install and config the system
-
-       yum update yum
-       yum install kernel
-       yum install util-vserver{,-core,-lib,-sysv,-build}
-       yum install vim
-       yum install subversion
-       /etc/init.d/vprocunhide start
-       chkconfig vservers-default on
-
- 2. create a vserver
-
- 2.A Checkout the planetlab build
-
-       cd
-       svn co http://svn.planet-lab.org/svn/build/trunk svn-build
-
- 2.B Search for a working RPM distribution in:
-
-       http://build.onelab.eu/onelab/
-       # good distribution ends in .ok, bad in .ko
-       # in this example we used the following:
-       http://build.onelab.eu/onelab/2008.03.02--onelab-f8-linux32/RPMS/
-
- 2.C Creating a vserver
-
-       cd ~/svn-build
-       ./vtest-init-vserver.sh -f f8 -d onelab -p linux32 mybuild \
-         http://build.onelab.eu/onelab/2008.03.02--onelab-f8-linux32/RPMS/ \
-         -- --interface eth0:138.96.255.221 --hostname vnode01.inria.fr &> mybuild.log&
-
- 3. create the build
-
- 3.A Enter on the vserver, and create the build
-
-       vserver mybuild enter
-       cd \
-       svn co http://svn.planet-lab.org/svn/build/trunk build
-
- 4. build
-
- 4.A build[4]
-       cd /build
-
-       # full cleanup
-       make distclean
-
-       # the compilation is composed by several steps,
-       # make help for more information
-       # the first for the onelab compilation will download
-       # the SPEC file from the repository specified in
-       # onelab-tags.mk
-       make stage1=true PLDISTRO=onelab
-
-       # to download and build a module, for example ipfw:
-       make ipfw
-
-       # to do local changes
-       cd /build/CODEBASE
-       rm -rf ipfw
-       # download the ipfw sources and extract it into ./ipfw
-       # by svn
-       svn+ssh://onelab2.iet.unipi.it/home/svn/ports-luigi/dummynet-branches/ipfw_mod ./ipfw
-       # from web
-        wget http://info.iet.unipi.it/~luigi/dummynet/ipfw_mod-latest.tgz
-        tar xvzf ipfw_mod-latest.tgz
-
-       # start the compilation
-       rm -rf SOURCES/ipfw*
-       rm -rf BUILD/ipfw-0.1/
-       rm -rf SRPMS/ipfw*
-       rm -rf RPMS/i386/ipfw*
-       make ipfw
-
- 5. download and install sources into a node
-
- 5.A Copy RPMS into the node and install it:
-       # exit from the root context
-       exit
-       scp  /vserver/mybuild/build/RPMS/i386/ipfw-* root@node.iet.unipi.it:
-       ssh root@node.iet.unipi.it
-       rpm -e ipfw
-       rpm -ivh ./ipfw-0-9...TAB
-       modprobe ipfw_mod
-
-       # the ipfw package should be installed
-       ipfw show
-
 --- References
 [1] https://svn.planet-lab.org/wiki/VserverCentos
 [2] http://wiki.linux-vserver.org/Installation_on_CentOS
diff --git a/README.openwrt b/README.openwrt
deleted file mode 100644 (file)
index dcf8194..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-OpenWrt on ASUSWL-520GC (admin, admin)
-
-Notes:
-   The firmware installed is the version: 2.0.1.1, the ip address
-   is 192.168.1.1, the web gui can be used by admin, admin.
-   After reflashing the board, the old firmware should be available
-   in the cdrom shipped with the router.
-
-OpenWRT compatility:
-1. The 2.4 kernel version has some troubles accessing the flash
-   and this will actually make the board hang after a while. 
-
-2. The 2.6 kernel does not have the same issue, except for the
-   open source b43 wireless driver, that make the board reboot
-   as soon as it is loaded.
-
-For this reason, when configuring the kernel option from the toolchain
-menu, the wireless target profile to choose should be `No WiFi'
-
-Flash the board:
-1. The compiled binary images are under the main tree, ./bin the file
-   to be used is openwrt-brcm47xx-squashfs.trx.
-2. Start the router in diag mode and goes with tftp, as follows:
-       tftp 192.168.1.1
-       tftp> bin
-       tftp> put openwrt-brcm47xx-squashfs.trx
-       Sent 1904640 bytes in 5.4 seconds
-       tftp> 
-3. Wait for 10 minutes, the reboot
diff --git a/binary/README.txt b/binary/README.txt
new file mode 100644 (file)
index 0000000..0212277
--- /dev/null
@@ -0,0 +1,27 @@
+This directory contains the binaries to install and use IPFW and\r
+DUMMYNET on a Windows Machine. The kernel part is an NDIS module,\r
+whereas the user interface is a command line program.\r
+\r
+1. INSTALL THE NDIS DRIVER\r
+\r
+- open the configuration panel for the network card in use\r
+  (either right click on the icon on the SYSTRAY, or go to\r
+  Control Panel -> Network and select one card)\r
+\r
+- click on Properties->Install->Service->Add\r
+- click on 'Driver Disk' and select 'netipfw.inf' in this folder\r
+- select 'ipfw+dummynet' which is the only service you should see\r
+- click accept on the warnings for the installation of an unknown\r
+  driver (roughly twice per existing network card)\r
+\r
+Now you are ready to use the emulator. To configure it, open a 'cmd'\r
+window and you can use the ipfw command from the command line.\r
+Otherwise click on the 'TESTME.bat' which is a batch program that\r
+runs various tests.\r
+\r
+2. UNINSTALL THE DRIVER\r
+\r
+- select a network card as above.\r
+- click on Properties\r
+- select 'ipfw+dummynet'\r
+- click on 'Remove'\r
diff --git a/binary/cyg-ipfw.exe b/binary/cyg-ipfw.exe
new file mode 100644 (file)
index 0000000..71c79e2
Binary files /dev/null and b/binary/cyg-ipfw.exe differ
diff --git a/binary/cygwin1.dll b/binary/cygwin1.dll
new file mode 100644 (file)
index 0000000..ae3bebe
Binary files /dev/null and b/binary/cygwin1.dll differ
diff --git a/binary/ipfw.exe b/binary/ipfw.exe
new file mode 100644 (file)
index 0000000..e756d8b
Binary files /dev/null and b/binary/ipfw.exe differ
diff --git a/binary/ipfw.sys b/binary/ipfw.sys
new file mode 100644 (file)
index 0000000..5683737
Binary files /dev/null and b/binary/ipfw.sys differ
diff --git a/binary/netipfw.inf b/binary/netipfw.inf
new file mode 100644 (file)
index 0000000..af0f716
--- /dev/null
@@ -0,0 +1,79 @@
+; version section\r
+[Version]\r
+Signature  = "$Windows NT$"\r
+Class      = NetService\r
+ClassGUID  = {4D36E974-E325-11CE-BFC1-08002BE10318}\r
+Provider   = %Unipi%\r
+DriverVer  = 26/02/2010,3.0.0.1\r
+\r
+; manufacturer section\r
+[Manufacturer]\r
+%Unipi% = UNIPI,NTx86\r
+\r
+; control flags section\r
+; optional, unused in netipfw.inf inf, used in netipfw_m.inf\r
+[ControlFlags]\r
+\r
+; models section\r
+[UNIPI] ; Win2k\r
+%Desc% = Ipfw.ndi, unipi_ipfw\r
+[UNIPI.NTx86] ;For WinXP and later\r
+%Desc% = Ipfw.ndi, unipi_ipfw\r
+\r
+; ddinstall section\r
+[Ipfw.ndi]\r
+AddReg          = Ipfw.ndi.AddReg, Ipfw.AddReg\r
+Characteristics = 0x4410 ;  NCF_FILTER | NCF_NDIS_PROTOCOL !--Filter Specific--!!\r
+CopyFiles       = Ipfw.Files.Sys\r
+CopyInf         = netipfw_m.inf\r
+\r
+; remove section\r
+[Ipfw.ndi.Remove]\r
+DelFiles = Ipfw.Files.Sys\r
+\r
+;ddinstall.services section\r
+[Ipfw.ndi.Services]\r
+AddService = Ipfw,,Ipfw.AddService\r
+\r
+[Ipfw.AddService]\r
+DisplayName    = %ServiceDesc%\r
+ServiceType    = 1 ;SERVICE_KERNEL_DRIVER\r
+StartType      = 3 ;SERVICE_DEMAND_START\r
+ErrorControl   = 1 ;SERVICE_ERROR_NORMAL\r
+ServiceBinary  = %12%\ipfw.sys\r
+AddReg         = Ipfw.AddService.AddReg\r
+\r
+[Ipfw.AddService.AddReg]\r
+\r
+;file copy related sections\r
+[SourceDisksNames]\r
+1=%DiskDescription%,"",,\r
+\r
+[SourceDisksFiles]\r
+ipfw.sys=1\r
+\r
+[DestinationDirs]\r
+DefaultDestDir = 12\r
+Ipfw.Files.Sys   = 12   ; %windir%\System32\drivers\r
+\r
+; ddinstall->copyfiles points here\r
+[Ipfw.Files.Sys]\r
+ipfw.sys,,,2\r
+\r
+; ddinstall->addreg points here\r
+[Ipfw.ndi.AddReg]\r
+HKR, Ndi,            HelpText,            , %HELP% ; this is displayed at the bottom of the General page of the Connection Properties dialog box\r
+HKR, Ndi,            FilterClass,         , failover\r
+HKR, Ndi,            FilterDeviceInfId,   , unipi_ipfwmp\r
+HKR, Ndi,            Service,             , Ipfw\r
+HKR, Ndi\Interfaces, UpperRange,          , noupper\r
+HKR, Ndi\Interfaces, LowerRange,          , nolower\r
+HKR, Ndi\Interfaces, FilterMediaTypes,    , "ethernet, tokenring, fddi, wan"\r
+\r
+;strings section\r
+[Strings]\r
+Unipi = "Unipi"\r
+DiskDescription = "Ipfw Driver Disk"\r
+Desc = "ipfw+dummynet"\r
+HELP = "This is ipfw and dummynet network emulator, developed by unipi.it"\r
+ServiceDesc = "ipfw service"\r
diff --git a/binary/netipfw_m.inf b/binary/netipfw_m.inf
new file mode 100644 (file)
index 0000000..9174c0d
--- /dev/null
@@ -0,0 +1,54 @@
+; version section\r
+[Version]\r
+Signature  = "$Windows NT$"\r
+Class      = Net\r
+ClassGUID  = {4D36E972-E325-11CE-BFC1-08002BE10318}\r
+Provider   = %Unipi%\r
+DriverVer  = 26/02/2010,3.0.0.1\r
+\r
+; control flags section\r
+; optional, unused in netipfw.inf inf, used in netipfw_m.inf\r
+[ControlFlags]\r
+ExcludeFromSelect = unipi_ipfwmp\r
+\r
+; destinationdirs section, optional\r
+[DestinationDirs]\r
+DefaultDestDir=12\r
+; No files to copy \r
+\r
+; manufacturer section\r
+[Manufacturer]\r
+%Unipi% = UNIPI,NTx86\r
+\r
+; models section\r
+[UNIPI] ; Win2k\r
+%Desc% = IpfwMP.ndi, unipi_ipfwmp\r
+[UNIPI.NTx86] ;For WinXP and later\r
+%Desc% = IpfwMP.ndi, unipi_ipfwmp\r
+\r
+; ddinstall section\r
+[IpfwMP.ndi]\r
+AddReg  = IpfwMP.ndi.AddReg\r
+Characteristics = 0x29 ;NCF_NOT_USER_REMOVABLE | NCF_VIRTUAL | NCF_HIDDEN\r
+\r
+; ddinstall->addreg points here\r
+[IpfwMP.ndi.AddReg]\r
+HKR, Ndi, Service,  0,  IpfwMP\r
+\r
+;ddinstall.services section\r
+[IpfwMP.ndi.Services]\r
+AddService = IpfwMP,0x2, IpfwMP.AddService\r
+\r
+[IpfwMP.AddService]\r
+ServiceType    = 1 ;SERVICE_KERNEL_DRIVER\r
+StartType      = 3 ;SERVICE_DEMAND_START\r
+ErrorControl   = 1 ;SERVICE_ERROR_NORMAL\r
+ServiceBinary  = %12%\ipfw.sys\r
+AddReg         = IpfwMP.AddService.AddReg\r
+\r
+[IpfwMP.AddService.AddReg]\r
+; None\r
+\r
+[Strings]\r
+Unipi = "Unipi"\r
+Desc = "Ipfw Miniport"
\ No newline at end of file
diff --git a/binary/testme.bat b/binary/testme.bat
new file mode 100644 (file)
index 0000000..b35ab87
--- /dev/null
@@ -0,0 +1,79 @@
+@echo on\r
+@set CYGWIN=nodosfilewarning\r
+\r
+@ipfw -q flush\r
+@ipfw -q pipe flush\r
+@echo ######################################################################\r
+@echo ## Setting delay to 100ms for both incoming and outgoing ip packets ##\r
+@echo ## and sending 4 echo request to Google                             ##\r
+@echo ######################################################################\r
+ipfw pipe 3 config delay 100ms mask all\r
+ipfw add pipe 3 ip from any to any\r
+ipfw pipe show\r
+ping -n 4 www.google.it\r
+\r
+@echo ##############################################\r
+@echo ## Raising delay to 300ms and pinging again ##\r
+@echo ##############################################\r
+ipfw pipe 3 config delay 300ms mask all\r
+ipfw pipe show\r
+ping -n 4 www.google.com\r
+\r
+@echo ##################################\r
+@echo ## Shaping bandwidth to 500kbps ##\r
+@echo ##################################\r
+ipfw pipe 3 config bw 500Kbit/s mask all\r
+ipfw pipe show\r
+wget http://info.iet.unipi.it/~luigi/1m\r
+@del 1m\r
+\r
+@echo ###################################\r
+@echo ## Lowering bandwidth to 250kbps ##\r
+@echo ###################################\r
+ipfw pipe 3 config bw 250Kbit/s mask all\r
+ipfw pipe show\r
+wget http://info.iet.unipi.it/~luigi/1m\r
+@del 1m\r
+\r
+@echo ###################################################################\r
+@echo ## Simulating 50 percent packet loss and sending 15 echo request ##\r
+@echo ###################################################################\r
+@ipfw -q flush\r
+@ipfw -q pipe flush\r
+ipfw add prob 0.5 deny proto icmp in\r
+ping -n 15 -w 300 www.google.it\r
+@ipfw -q flush\r
+\r
+@echo ##############################\r
+@echo ## Showing SYSCTL variables ##\r
+@echo ##############################\r
+ipfw sysctl -a\r
+\r
+@echo #############################################\r
+@echo ## Inserting rules to test command parsing ##\r
+@echo #############################################\r
+@echo -- dropping all packets of a specific protocol --\r
+ipfw add deny proto icmp\r
+@echo -- dropping packets of all protocols except a specific one --\r
+ipfw add deny not proto tcp\r
+@echo -- dropping all packets from IP x to IP y --\r
+ipfw add deny src-ip 1.2.3.4 dst-ip 5.6.7.8\r
+@echo -- dropping all ssh outgoing connections --\r
+ipfw add deny out dst-port 22\r
+@echo -- allowing already opened browser connections --\r
+@echo -- but preventing new ones from being opened   --\r
+ipfw add deny out proto tcp dst-port 80 tcpflags syn\r
+@echo -- another way to do the same thing --\r
+ipfw add allow out proto tcp dst-port 80 established\r
+ipfw add deny out proto tcp dst-port 80 setup\r
+@echo -- checking what rules have been inserted --\r
+ipfw -c show\r
+@ipfw -q flush\r
+\r
+@echo #################\r
+@echo ## Cleaning up ##\r
+@echo #################\r
+ipfw -q flush\r
+ipfw -q pipe flush\r
+\r
+pause\r
diff --git a/binary/wget.exe b/binary/wget.exe
new file mode 100644 (file)
index 0000000..f2a11c1
Binary files /dev/null and b/binary/wget.exe differ
index e51ccb2..3d4a42b 100644 (file)
@@ -1,10 +1,9 @@
-#
-# $Id: Makefile 4657 2010-01-04 11:20:53Z marta $
-#
-# gnu Makefile to build linux module for ipfw+dummynet.
+# $Id: Makefile 5858 2010-03-24 16:16:19Z svn_magno $
+# gnu Makefile to build linux/Windows module for ipfw+dummynet.
 #
 # The defaults are set to build without modifications on PlanetLab
 # and possibly 2.6 versions.
+# On Windows, we use gnu-make and MSC
 
 # Some variables need to have specific names, because they are used
 # by the build infrastructure on Linux and OpenWrt. They are:
@@ -27,6 +26,7 @@
 #   VER                linux version we are building for (2.4 2.6 or openwrt)
 #---
 
+UNAME:=$(shell uname)
 $(warning including dummynet/Makefile)
 
 # lets default for 2.6 for planetlab builds
@@ -38,9 +38,14 @@ obj-m := ipfw_mod.o
 
 #-- the list of source files. IPFW_SRCS is our own name.
 # Original ipfw and dummynet sources + FreeBSD stuff,
-IPFW_SRCS := ip_fw2.c ip_dummynet.c ip_fw_pfil.c ip_fw_sockopt.c
+IPFW_SRCS := ip_fw2.c ip_fw_pfil.c ip_fw_sockopt.c
 IPFW_SRCS += ip_fw_dynamic.c ip_fw_table.c ip_fw_log.c
 IPFW_SRCS += radix.c in_cksum.c
+IPFW_SRCS += ip_dummynet.c ip_dn_io.c ip_dn_glue.c
+IPFW_SRCS += dn_heap.c
+IPFW_SRCS += dn_sched_fifo.c dn_sched_wf2q.c
+IPFW_SRCS += dn_sched_rr.c dn_sched_qfq.c
+IPFW_SRCS += dn_sched_prio.c
 # Module glue and functions missing in linux
 IPFW_SRCS += ipfw2_mod.c bsd_compat.c
 
@@ -58,6 +63,124 @@ ipfw-cflags += -include $(M)/missing.h      # headers
 
 $(warning "---- Building dummynet kernel module for Version $(VER)")
 
+ifneq (,$(findstring CYGWIN,$(shell uname)))
+    ISWIN=1
+endif
+ifneq ($(TCC),)
+    ISWIN=1
+endif
+ifeq ($(ISWIN),1)
+    M ?= $(shell pwd)
+    WIN_SRCS += md_win.c
+    WIN_SRCS += miniport.c protocol.c passthru.c debug.c
+    #compiler, linker, target, sources and objects
+    #DDK is exported from the root makefile
+    #DDK = C:/WinDDK/7600.16385.1
+    OBJDIR=objchk_wxp_x86/i386/
+
+    TARGET = ipfw
+
+    CSOURCES = $(IPFW_SRCS) $(WIN_SRCS)
+
+    COBJS := $(CSOURCES:.c=.obj)
+    COBJS := $(addprefix $(OBJDIR),$(COBJS))
+
+    #include paths
+    INCLUDE_PATHS = -Ii386 -Iinclude -Iinclude_e -I.
+    # INCLUDE_PATHS += -I$(OBJDIR)
+    INCLUDE_PATHS += -I$(DDK)/inc/api
+    INCLUDE_PATHS += -I$(DDK)/inc/ddk
+    INCLUDE_PATHS += -I$(DDK)/inc/crt
+
+    # #preprocessor MS defines
+    PREPROC  = -D_X86_=1 -Di386=1 -DSTD_CALL -DCONDITION_HANDLING=1
+    PREPROC += -DNT_UP=0 -DNT_INST=0 -DWIN32=100 -D_NT1X_=100 -DWINNT=1
+    PREPROC += -D_WIN32_WINNT=0x0501 -DWINVER=0x0501 -D_WIN32_IE=0x0603
+    PREPROC += -DWIN32_LEAN_AND_MEAN=1 
+    PREPROC += -D__BUILDMACHINE__=WinDDK -DFPO=0 -D_DLL=1
+    PREPROC += -DNDIS_MINIPORT_DRIVER -DNDIS_WDM=1
+    PREPROC += -DNDIS51_MINIPORT=1 -DNDIS51=1
+    PREPROC += -DMSC_NOOPT -DNTDDI_VERSION=0x05010200
+    PREPROC += -DKMDF_MAJOR_VERSION_STRING=01 -DKMDF_MINOR_VERSION_STRING=009
+    #PREPROC += -DDBG=1 #debug
+    PREPROC += -DNDEBUG #always up, seems no effect, possibly no debug?
+    PREPROC += -DDEVL=1 #always up, seems no effect
+    #macroing module name, WARNING: must match the one in .inf files
+    PREPROC += -DMODULENAME=Ipfw 
+
+    #our defines
+    OUR_PREPROC  = -D_KERNEL -DKERNEL_MODULE -DKLD_MODULE
+    OUR_PREPROC += -D__BSD_VISIBLE -DIPFIREWALL_DEFAULT_TO_ACCEPT
+    OUR_PREPROC += -D__LITTLE_ENDIAN -DSYSCTL_NODE -DEMULATE_SYSCTL
+
+ifeq ($(TCC),)
+    CC = $(DDK)/bin/x86/x86/cl.exe
+    LD = $(DDK)/bin/x86/x86/link.exe
+    # #complier options
+    CFLAGS  = -Fo$(OBJDIR)  -c -FC -Zc:wchar_t-
+    CFLAGS += -Zl -Zp8 -Gy -Gm- -GF -cbstring -Gz -hotpatch -EHs-c-
+    CFLAGS += -W2 # -W3 gives too many conversion errors
+    CFLAGS += -GR- -GF -GS -Zi # XXX do we need this ?
+    CFLAGS += -Fd$(OBJDIR)
+    CFLAGS += -wd4603 -wd4627 -typedil-
+    CFLAGS += -FI $(DDK)/inc/api/warning.h
+    CFLAGS += -FI winmissing.h
+    CFLAGS += -FI missing.h    # headers
+    CFLAGS += -FI ../glue.h    # headers
+
+    #optimization options
+    OPTIMIZE = -Od -Oi -Oy-
+
+    #linker options
+    LDFLAGS  = /MERGE:_PAGE=PAGE /MERGE:_TEXT=.text
+    LDFLAGS += /SECTION:INIT,d /OPT:REF /OPT:ICF
+    LDFLAGS += /IGNORE:4198,4010,4037,4039,4065,4070,4078,4087,4089,4221
+    LDFLAGS += /INCREMENTAL:NO /release /NODEFAULTLIB /WX
+    LDFLAGS += /debug /debugtype:cv,fixup,pdata
+    LDFLAGS += /version:6.1 /osversion:6.1 /functionpadmin:5
+    LDFLAGS += /safeseh /pdbcompress
+    LDFLAGS += /STACK:0x40000,0x1000 /driver /base:0x10000 /align:0x80
+    LDFLAGS += /stub:$(DDK)\\lib\\wxp\\stub512.com
+    LDFLAGS += /subsystem:native,5.01 /entry:GsDriverEntry@8
+    LDFLAGS += /out:$(OBJDIR)/ipfw.sys
+
+    #libraries to build against
+    LIBS  = $(DDK)/lib/wxp/i386/BufferOverflowK.lib
+    LIBS += $(DDK)/lib/wxp/i386/ntoskrnl.lib
+    LIBS += $(DDK)/lib/wxp/i386/hal.lib
+    LIBS += $(DDK)/lib/wxp/i386/wmilib.lib
+    LIBS += $(DDK)/lib/wxp/i386/ndis.lib
+    LIBS += $(DDK)/lib/wxp/i386/sehupd.lib
+else
+    # TCC points to the root of tcc tree
+    CC=$(TCC)/bin/wintcc
+    EXTRA_CFLAGS += -DTCC -I..
+    EXTRA_CFLAGS += -I$(TCC)/include/winapi -I$(TCC)/include
+    EXTRA_CFLAGS += -nostdinc
+
+    CFLAGS += -include winmissing.h -include missing.h -include ../glue.h
+    CFLAGS += -I../../inc/api -I../../inc/ddk -I../../inc/crt
+    CFLAGS += -DRC_INVOKED
+endif
+
+    #empty include directory to be built
+    M ?= $(shell pwd)
+    EDIRS += asm linux
+    EFILES += asm/div64.h
+    EFILES += linux/if.h linux/random.h linux/errno.h
+    EFILES += net/if_types.h net/inet_hashtables.h net/route.h
+
+    #targets
+all: $(TARGET)
+
+$(TARGET): include_e
+       rm -rf objchk_wxp_x86
+       mkdir -p objchk_wxp_x86/i386
+       $(CC) $(INCLUDE_PATHS) $(PREPROC) $(OUR_PREPROC) $(CFLAGS) $(OPTIMIZE) $(CSOURCES)
+       $(LD) $(LDFLAGS) $(COBJS) $(LIBS)
+
+else # !windows
+
 # We have three sections for OpenWrt, Linux 2.4 and Linux 2.6
 
 ifeq ($(VER),openwrt)
@@ -72,7 +195,7 @@ ifeq ($(VER),openwrt)
   xcflags-y += -O1 -DLINUX_24
   xcflags-y += -g
 
-  EXTRA_CFLAGS := $(xcflags-y) $(ipfw-cflags)
+  EXTRA_CFLAGS := $(xcflags-y) $(ipfw-cflags) -DSYSCTL_NODE -DEMULATE_SYSCTL
 
   # we should not export anything
   #export-objs := ipfw2_mod.o
@@ -170,21 +293,23 @@ $(obj-m): $(ipfw_mod-y)
 # so we allow it to be overridden
 M ?= $(shell pwd)
 endif # !openwrt
+endif # !windows
 
 #--- various common targets
 clean:
        -rm -f *.o *.ko Module.symvers *.mod.c
+       -rm -rf objchk_wxp_x86
        -rm -rf include_e
 
 distclean: clean
        -rm -f .*cmd modules.order opt_*
        -rm -rf .tmp_versions include_e
-       -rm -rf .*.o.d
+       -rm -rf .*.o.d _CL_*
 
 # support to create empty dirs and files in include_e/
 # EDIRS is the list of directories, EFILES is the list of files.
 
-EDIRS= altq arpa machine net netinet netinet6 sys
+EDIRS  += altq arpa machine net netinet netinet6 sys
 
 EFILES += opt_inet6.h opt_ipfw.h opt_ipsec.h opt_mpath.h
 EFILES += opt_mbuf_stress_test.h opt_param.h
index 70268bb..21d19b6 100644 (file)
@@ -24,7 +24,7 @@
  */
 
 /*
- * $Id: bsd_compat.c 4665 2010-01-04 12:35:39Z luigi $
+ * $Id: bsd_compat.c 5813 2010-03-22 18:05:13Z svn_magno $
  *
  * kernel variables and functions that are not available in linux.
  */
@@ -32,6 +32,9 @@
 #include <sys/cdefs.h>
 #include <asm/div64.h> /* do_div on 2.4 */
 #include <linux/random.h>      /* get_random_bytes on 2.4 */
+#include <netinet/ip_fw.h>
+#include <netinet/ip_dummynet.h>
+#include <sys/malloc.h>
 
 /*
  * gettimeofday would be in sys/time.h but it is not
@@ -43,7 +46,6 @@ int ticks;            /* kernel ticks counter */
 int hz = 1000;         /* default clock time */
 long tick = 1000;      /* XXX is this 100000/hz ? */
 int bootverbose = 0;
-time_t time_uptime = 0;
 struct timeval boottime;
 
 int     ip_defttl;
@@ -157,6 +159,7 @@ ip_reass(struct mbuf *clone)
 
 /* credentials check */
 #include <netinet/ip_fw.h>
+#ifdef __linux__
 int
 cred_check(void *_insn,  int proto, struct ifnet *oif,
     struct in_addr dst_ip, u_int16_t dst_port, struct in_addr src_ip,
@@ -186,6 +189,7 @@ cred_check(void *_insn,  int proto, struct ifnet *oif,
                match = (u->gid == (uid_t)insn->d[0]);
        return match;
 }
+#endif /* __linux__ */
 
 int
 jailed(struct ucred *cred)
@@ -212,6 +216,7 @@ sooptcopyout(struct sockopt *sopt, const void *buf, size_t len)
 
        if (len < valsize)
                sopt->sopt_valsize = valsize = len;
+       //printf("copyout buf = %p, sopt = %p, soptval = %p, len = %d \n", buf, sopt, sopt->sopt_val, len);
        bcopy(buf, sopt->sopt_val, valsize);
        return 0;
 }
@@ -228,6 +233,7 @@ sooptcopyin(struct sockopt *sopt, void *buf, size_t len, size_t minlen)
                return EINVAL;
        if (valsize > len)
                sopt->sopt_valsize = valsize = len;
+       //printf("copyin buf = %p, sopt = %p, soptval = %p, len = %d \n", buf, sopt, sopt->sopt_val, len);
        bcopy(sopt->sopt_val, buf, valsize);
        return 0;
 }
@@ -235,10 +241,7 @@ sooptcopyin(struct sockopt *sopt, void *buf, size_t len, size_t minlen)
 void
 getmicrouptime(struct timeval *tv)
 {
-#ifdef _WIN32
-#else
        do_gettimeofday(tv);
-#endif
 }
 
 
@@ -271,7 +274,13 @@ int
 random(void)
 {
 #ifdef _WIN32
-       return 0x123456;
+       static unsigned long seed;
+       if (seed == 0) {
+               LARGE_INTEGER tm;
+               KeQuerySystemTime(&tm);
+               seed = tm.LowPart;
+       }
+       return RtlRandomEx(&seed) & 0x7fffffff;
 #else
        int r;
        get_random_bytes(&r, sizeof(r));
@@ -302,6 +311,34 @@ div64(int64_t a, int64_t b)
 #endif
 }
 
+#ifdef __MIPSEL__
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+        char *d = dst;
+        const char *s = src;
+        size_t n = siz;
+        /* Copy as many bytes as will fit */
+        if (n != 0 && --n != 0) {
+                do {
+                        if ((*d++ = *s++) == 0)
+                                break;
+                } while (--n != 0);
+        }
+
+        /* Not enough room in dst, add NUL and traverse rest of src */
+        if (n == 0) {
+                if (siz != 0)
+                        *d = '\0';              /* NUL-terminate dst */
+                while (*s++)
+                        ;
+        }
+
+        return(s - src - 1);    /* count does not include NUL */
+}
+#endif // __MIPSEL__
+
 /*
  * compact version of fnmatch.
  */
@@ -328,43 +365,182 @@ fnmatch(const char *pattern, const char *string, int flags)
        return 1;       /* no match */
 }
 
-#ifdef _WIN32
-/*
- * as good as anywhere, place here the missing calls
+/* support for sysctl emulation.
+ * XXX this is actually MI code that should be enabled also on openwrt
  */
+#ifdef EMULATE_SYSCTL
+static struct sysctltable GST;
 
-void *
-my_alloc(int size)
+int
+kesysctl_emu_get(struct sockopt* sopt)
 {
-       void *_ret = ExAllocatePoolWithTag(0, size, 'wfpi');
-       if (_ret)
-               memset(_ret, 0, size);
-       return _ret;
+       struct dn_id* oid = sopt->sopt_val;
+       struct sysctlhead* entry;
+       int sizeneeded = sizeof(struct dn_id) + GST.totalsize +
+               sizeof(struct sysctlhead);
+       unsigned char* pstring;
+       unsigned char* pdata;
+       int i;
+       
+       if (sopt->sopt_valsize < sizeneeded) {
+               // this is a probe to retrieve the space needed for
+               // a dump of the sysctl table
+               oid->id = sizeneeded;
+               sopt->sopt_valsize = sizeof(struct dn_id);
+               return 0;
+       }
+       
+       entry = (struct sysctlhead*)(oid+1);
+       for( i=0; i<GST.count; i++) {
+               entry->blocklen = GST.entry[i].head.blocklen;
+               entry->namelen = GST.entry[i].head.namelen;
+               entry->flags = GST.entry[i].head.flags;
+               entry->datalen = GST.entry[i].head.datalen;
+               pdata = (unsigned char*)(entry+1);
+               pstring = pdata+GST.entry[i].head.datalen;
+               bcopy(GST.entry[i].data, pdata, GST.entry[i].head.datalen);
+               bcopy(GST.entry[i].name, pstring, GST.entry[i].head.namelen);
+               entry = (struct sysctlhead*)
+                       ((unsigned char*)(entry) + GST.entry[i].head.blocklen);
+       }
+       sopt->sopt_valsize = sizeneeded;
+       return 0;
 }
 
-void
-panic(const char *fmt, ...)
+int
+kesysctl_emu_set(void* p, int l)
+{
+       struct sysctlhead* entry;
+       unsigned char* pdata;
+       unsigned char* pstring;
+       int i = 0;
+       
+       entry = (struct sysctlhead*)(((struct dn_id*)p)+1);
+       pdata = (unsigned char*)(entry+1);
+       pstring = pdata + entry->datalen;
+       
+       for (i=0; i<GST.count; i++) {
+               if (strcmp(GST.entry[i].name, pstring) != 0)
+                       continue;
+               printf("%s: match found! %s\n",__FUNCTION__,pstring);
+               //sanity check on len, not really useful now since
+               //we only accept int32
+               if (entry->datalen != GST.entry[i].head.datalen) {
+                       printf("%s: len mismatch, user %d vs kernel %d\n",
+                               __FUNCTION__, entry->datalen,
+                               GST.entry[i].head.datalen);
+                       return -1;
+               }
+               // check access (at the moment flags handles only the R/W rights
+               //later on will be type + access
+               if( (GST.entry[i].head.flags & 3) == CTLFLAG_RD) {
+                       printf("%s: the entry %s is read only\n",
+                               __FUNCTION__,GST.entry[i].name);
+                       return -1;
+               }
+               bcopy(pdata, GST.entry[i].data, GST.entry[i].head.datalen);
+               return 0;
+       }
+       printf("%s: match not found\n",__FUNCTION__);
+       return 0;
+}
+
+/* convert all _ to . until the first . */
+static void
+underscoretopoint(char* s)
 {
-       printf("%s", fmt);
-       for (;;);
+       for (; *s && *s != '.'; s++)
+               if (*s == '_')
+                       *s = '.';
 }
 
-#include <stdarg.h>
+static int
+formatnames()
+{
+       int i;
+       int size=0;
+       char* name;
+
+       for (i=0; i<GST.count; i++)
+               size += GST.entry[i].head.namelen;
+       GST.namebuffer = malloc(size, 0, 0);
+       if (GST.namebuffer == NULL)
+               return -1;
+       name = GST.namebuffer;
+       for (i=0; i<GST.count; i++) {
+               bcopy(GST.entry[i].name, name, GST.entry[i].head.namelen);
+               underscoretopoint(name);
+               GST.entry[i].name = name;
+               name += GST.entry[i].head.namelen;
+       }
+       return 0;
+}
 
-extern int _vsnprintf(char *buf, int buf_size, char * fmt, va_list ap);
+static void
+dumpGST()
+{
+       int i;
+
+       for (i=0; i<GST.count; i++) {
+               printf("SYSCTL: entry %i\n", i);
+               printf("name %s\n", GST.entry[i].name);
+               printf("namelen %i\n", GST.entry[i].head.namelen);
+               printf("type %i access %i\n",
+                       GST.entry[i].head.flags >> 2,
+                       GST.entry[i].head.flags & 0x00000003);
+               printf("data %i\n", *(int*)(GST.entry[i].data));
+               printf("datalen %i\n", GST.entry[i].head.datalen);
+               printf("blocklen %i\n", GST.entry[i].head.blocklen);
+       }
+}
 
-/*
- * Windows' _snprintf doesn't terminate buffer with zero if size > buf_size
- */
-int
-snprintf(char *buf, int buf_size, char *fmt, ...)
+void sysctl_addgroup_f1();
+void sysctl_addgroup_f2();
+void sysctl_addgroup_f3();
+void sysctl_addgroup_f4();
+
+void
+keinit_GST()
 {
-    va_list ap;
-    va_start(ap, fmt);
-    if (_vsnprintf(buf, buf_size, fmt, ap) < 0)
-        buf[buf_size - 1] = '\0';
-    va_end(ap);
+       int ret;
+
+       sysctl_addgroup_f1();
+       sysctl_addgroup_f2();
+       sysctl_addgroup_f3();
+       sysctl_addgroup_f4();
+       ret = formatnames();
+       if (ret != 0)
+               printf("conversion of names failed for some reason\n");
+       //dumpGST();
+       printf("*** Global Sysctl Table entries = %i, total size = %i ***\n",
+               GST.count, GST.totalsize);
+}
 
-    return 0;
+void
+keexit_GST()
+{
+       if (GST.namebuffer != NULL)
+               free(GST.namebuffer,0);
+       bzero(&GST, sizeof(GST));
 }
-#endif
+
+void
+sysctl_pushback(char* name, int flags, int datalen, void* data)
+{
+       if (GST.count >= GST_HARD_LIMIT) {
+               printf("WARNING: global sysctl table full, this entry will not be added,"
+                               "please recompile the module increasing the table size\n");
+               return;
+       }
+       GST.entry[GST.count].head.namelen = strlen(name)+1; //add space for '\0'
+       GST.entry[GST.count].name = name;
+       GST.entry[GST.count].head.flags = flags;
+       GST.entry[GST.count].data = data;
+       GST.entry[GST.count].head.datalen = datalen;
+       GST.entry[GST.count].head.blocklen =
+               ((sizeof(struct sysctlhead) + GST.entry[GST.count].head.namelen +
+                       GST.entry[GST.count].head.datalen)+3) & ~3;
+       GST.totalsize += GST.entry[GST.count].head.blocklen;
+       GST.count++;
+}
+#endif /* EMULATE_SYSCTL */
diff --git a/dummynet2/debug.c b/dummynet2/debug.c
new file mode 100644 (file)
index 0000000..67a4f23
--- /dev/null
@@ -0,0 +1,67 @@
+#include <ntddk.h>
+
+const char* texify_cmd(int i)
+{
+       if (i==110)
+               return("IP_FW_ADD");
+       if (i==111)
+               return("IP_FW_DEL");
+       if (i==112)
+               return("IP_FW_FLUSH");
+       if (i==113)
+               return("IP_FW_ZERO");
+       if (i==114)
+               return("IP_FW_GET");
+       if (i==115)
+               return("IP_FW_RESETLOG");
+       if (i==116)
+               return("IP_FW_NAT_CFG");
+       if (i==117)
+               return("IP_FW_NAT_DEL");
+       if (i==118)
+               return("IP_FW_NAT_GET_CONFIG");
+       if (i==119)
+               return("IP_FW_NAT_GET_LOG");
+       if (i==120)
+               return("IP_DUMMYNET_CONFIGURE");
+       if (i==121)
+               return("IP_DUMMYNET_DEL");
+       if (i==122)
+               return("IP_DUMMYNET_FLUSH");
+       if (i==124)
+               return("IP_DUMMYNET_GET");
+       if (i==108)
+               return("IP_FW3");
+       if (i==109)
+               return("IP_DUMMYNET3");
+       return ("BOH");
+}
+
+const char* texify_proto(unsigned int p)
+{
+       if (p==1)
+               return("ICMP");
+       if (p==6)
+               return("TCP");
+       if (p==17)
+               return("UDP");
+       return("OTHER");
+}
+
+void hexdump(unsigned char* addr, int len, const char *msg)
+{
+       int i;
+       const  int cicli = len/8;
+       const int resto = len%8;
+       unsigned char d[8];
+
+       DbgPrint("%s at %p len %d\n", msg, addr, len);
+       for (i=0; i<=cicli; i++) {
+               bzero(d, 8);
+               bcopy(addr+i*8, d, i < cicli ? 8 : resto);
+               DbgPrint("%04X %02X %02X %02X %02X %02X %02X %02X %02X\n",
+                       i*8, d[0], d[1], d[2], d[3], d[4],
+                       d[5], d[6], d[7]);
+       }
+       DbgPrint("\n");
+}
diff --git a/dummynet2/dn_heap.c b/dummynet2/dn_heap.c
new file mode 100644 (file)
index 0000000..390ae8d
--- /dev/null
@@ -0,0 +1,550 @@
+/*-
+ * Copyright (c) 1998-2002,2010 Luigi Rizzo, Universita` di Pisa
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Binary heap and hash tables, used in dummynet
+ *
+ * $Id: dn_heap.c 5646 2010-03-08 12:48:30Z luigi $
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#ifdef _KERNEL
+__FBSDID("$FreeBSD: user/luigi/ipfw3-head/sys/netinet/ipfw/dn_heap.c 203279 2010-01-31 12:20:29Z luigi $");
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <netinet/ipfw/dn_heap.h>
+#ifndef log
+#define log(x, arg...)
+#endif
+
+#else /* !_KERNEL */
+
+#include <stdio.h>
+#include <dn_test.h>
+#include <strings.h>
+#include <stdlib.h>
+
+#include  "dn_heap.h"
+#define log(x, arg...) fprintf(stderr, ## arg)
+#define panic(x...)    fprintf(stderr, ## x), exit(1)
+#define MALLOC_DEFINE(a, b, c)
+static void *my_malloc(int s) {        return malloc(s); }
+static void my_free(void *p) { free(p); }
+#define malloc(s, t, w)        my_malloc(s)
+#define free(p, t)     my_free(p)
+#endif /* !_KERNEL */
+
+MALLOC_DEFINE(M_DN_HEAP, "dummynet", "dummynet heap");
+
+/*
+ * Heap management functions.
+ *
+ * In the heap, first node is element 0. Children of i are 2i+1 and 2i+2.
+ * Some macros help finding parent/children so we can optimize them.
+ *
+ * heap_init() is called to expand the heap when needed.
+ * Increment size in blocks of 16 entries.
+ * Returns 1 on error, 0 on success
+ */
+#define HEAP_FATHER(x) ( ( (x) - 1 ) / 2 )
+#define HEAP_LEFT(x) ( (x)+(x) + 1 )
+#define        HEAP_SWAP(a, b, buffer) { buffer = a ; a = b ; b = buffer ; }
+#define HEAP_INCREMENT 15
+
+static int
+heap_resize(struct dn_heap *h, unsigned int new_size)
+{
+       struct dn_heap_entry *p;
+
+       if (h->size >= new_size )       /* have enough room */
+               return 0;
+#if 1  /* round to the next power of 2 */
+       new_size |= new_size >> 1;
+       new_size |= new_size >> 2;
+       new_size |= new_size >> 4;
+       new_size |= new_size >> 8;
+       new_size |= new_size >> 16;
+#else
+       new_size = (new_size + HEAP_INCREMENT ) & ~HEAP_INCREMENT;
+#endif
+       p = malloc(new_size * sizeof(*p), M_DN_HEAP, M_NOWAIT);
+       if (p == NULL) {
+               printf("--- %s, resize %d failed\n", __func__, new_size );
+               return 1; /* error */
+       }
+       if (h->size > 0) {
+               bcopy(h->p, p, h->size * sizeof(*p) );
+               free(h->p, M_DN_HEAP);
+       }
+       h->p = p;
+       h->size = new_size;
+       return 0;
+}
+
+int
+heap_init(struct dn_heap *h, int size, int ofs)
+{
+       if (heap_resize(h, size))
+               return 1;
+       h->elements = 0;
+       h->ofs = ofs;
+       return 0;
+}
+
+/*
+ * Insert element in heap. Normally, p != NULL, we insert p in
+ * a new position and bubble up. If p == NULL, then the element is
+ * already in place, and key is the position where to start the
+ * bubble-up.
+ * Returns 1 on failure (cannot allocate new heap entry)
+ *
+ * If ofs > 0 the position (index, int) of the element in the heap is
+ * also stored in the element itself at the given offset in bytes.
+ */
+#define SET_OFFSET(h, i) do {                                  \
+       if (h->ofs > 0)                                         \
+           *((int32_t *)((char *)(h->p[i].object) + h->ofs)) = i;      \
+       } while (0)
+/*
+ * RESET_OFFSET is used for sanity checks. It sets ofs
+ * to an invalid value.
+ */
+#define RESET_OFFSET(h, i) do {                                        \
+       if (h->ofs > 0)                                         \
+           *((int32_t *)((char *)(h->p[i].object) + h->ofs)) = -16;    \
+       } while (0)
+
+int
+heap_insert(struct dn_heap *h, uint64_t key1, void *p)
+{
+       int son = h->elements;
+
+       //log("%s key %llu p %p\n", __FUNCTION__, key1, p);
+       if (p == NULL) { /* data already there, set starting point */
+               son = key1;
+       } else { /* insert new element at the end, possibly resize */
+               son = h->elements;
+               if (son == h->size) /* need resize... */
+                       // XXX expand by 16 or so
+                       if (heap_resize(h, h->elements+16) )
+                               return 1; /* failure... */
+               h->p[son].object = p;
+               h->p[son].key = key1;
+               h->elements++;
+       }
+       /* make sure that son >= father along the path */
+       while (son > 0) {
+               int father = HEAP_FATHER(son);
+               struct dn_heap_entry tmp;
+
+               if (DN_KEY_LT( h->p[father].key, h->p[son].key ) )
+                       break; /* found right position */
+               /* son smaller than father, swap and repeat */
+               HEAP_SWAP(h->p[son], h->p[father], tmp);
+               SET_OFFSET(h, son);
+               son = father;
+       }
+       SET_OFFSET(h, son);
+       return 0;
+}
+
+/*
+ * remove top element from heap, or obj if obj != NULL
+ */
+void
+heap_extract(struct dn_heap *h, void *obj)
+{
+       int child, father, max = h->elements - 1;
+
+       if (max < 0) {
+               printf("--- %s: empty heap 0x%p\n", __FUNCTION__, h);
+               return;
+       }
+       if (obj == NULL)
+               father = 0; /* default: move up smallest child */
+       else { /* extract specific element, index is at offset */
+               if (h->ofs <= 0)
+                       panic("%s: extract from middle not set on %p\n",
+                               __FUNCTION__, h);
+               father = *((int *)((char *)obj + h->ofs));
+               if (father < 0 || father >= h->elements) {
+                       panic("%s: father %d out of bound 0..%d\n",
+                               __FUNCTION__, father, h->elements);
+               }
+       }
+       /*
+        * below, father is the index of the empty element, which
+        * we replace at each step with the smallest child until we
+        * reach the bottom level.
+        */
+       // XXX why removing RESET_OFFSET increases runtime by 10% ?
+       RESET_OFFSET(h, father);
+       while ( (child = HEAP_LEFT(father)) <= max ) {
+               if (child != max &&
+                   DN_KEY_LT(h->p[child+1].key, h->p[child].key) )
+                       child++; /* take right child, otherwise left */
+               h->p[father] = h->p[child];
+               SET_OFFSET(h, father);
+               father = child;
+       }
+       h->elements--;
+       if (father != max) {
+               /*
+                * Fill hole with last entry and bubble up,
+                * reusing the insert code
+                */
+               h->p[father] = h->p[max];
+               heap_insert(h, father, NULL);
+       }
+}
+
+#if 0
+/*
+ * change object position and update references
+ * XXX this one is never used!
+ */
+static void
+heap_move(struct dn_heap *h, uint64_t new_key, void *object)
+{
+       int temp, i, max = h->elements-1;
+       struct dn_heap_entry *p, buf;
+
+       if (h->ofs <= 0)
+               panic("cannot move items on this heap");
+       p = h->p;       /* shortcut */
+
+       i = *((int *)((char *)object + h->ofs));
+       if (DN_KEY_LT(new_key, p[i].key) ) { /* must move up */
+               p[i].key = new_key;
+               for (; i>0 &&
+                   DN_KEY_LT(new_key, p[(temp = HEAP_FATHER(i))].key);
+                   i = temp ) { /* bubble up */
+                       HEAP_SWAP(p[i], p[temp], buf);
+                       SET_OFFSET(h, i);
+               }
+       } else {                /* must move down */
+               p[i].key = new_key;
+               while ( (temp = HEAP_LEFT(i)) <= max ) {
+                       /* found left child */
+                       if (temp != max &&
+                           DN_KEY_LT(p[temp+1].key, p[temp].key))
+                               temp++; /* select child with min key */
+                       if (DN_KEY_LT(>p[temp].key, new_key)) {
+                               /* go down */
+                               HEAP_SWAP(p[i], p[temp], buf);
+                               SET_OFFSET(h, i);
+                       } else
+                               break;
+                       i = temp;
+               }
+       }
+       SET_OFFSET(h, i);
+}
+#endif /* heap_move, unused */
+
+/*
+ * heapify() will reorganize data inside an array to maintain the
+ * heap property. It is needed when we delete a bunch of entries.
+ */
+static void
+heapify(struct dn_heap *h)
+{
+       int i;
+
+       for (i = 0; i < h->elements; i++ )
+               heap_insert(h, i , NULL);
+}
+
+int
+heap_scan(struct dn_heap *h, int (*fn)(void *, uintptr_t),
+       uintptr_t arg)
+{
+       int i, ret, found;
+
+       for (i = found = 0 ; i < h->elements ;) {
+               ret = fn(h->p[i].object, arg);
+               if (ret & HEAP_SCAN_DEL) {
+                       h->elements-- ;
+                       h->p[i] = h->p[h->elements] ;
+                       found++ ;
+               } else
+                       i++ ;
+               if (ret & HEAP_SCAN_END)
+                       break;
+       }
+       if (found)
+               heapify(h);
+       return found;
+}
+
+/*
+ * cleanup the heap and free data structure
+ */
+void
+heap_free(struct dn_heap *h)
+{
+       if (h->size >0 )
+               free(h->p, M_DN_HEAP);
+       bzero(h, sizeof(*h) );
+}
+
+/*
+ * hash table support.
+ */
+
+struct dn_ht {
+        int buckets;            /* how many buckets, really buckets - 1*/
+        int entries;            /* how many entries */
+        int ofs;               /* offset of link field */
+        uint32_t (*hash)(uintptr_t, int, void *arg);
+        int (*match)(void *_el, uintptr_t key, int, void *);
+        void *(*newh)(uintptr_t, int, void *);
+        void **ht;              /* bucket heads */
+};
+/*
+ * Initialize, allocating bucket pointers inline.
+ * Recycle previous record if possible.
+ * If the 'newh' function is not supplied, we assume that the
+ * key passed to ht_find is the same object to be stored in.
+ */
+struct dn_ht *
+dn_ht_init(struct dn_ht *ht, int buckets, int ofs,
+        uint32_t (*h)(uintptr_t, int, void *),
+        int (*match)(void *, uintptr_t, int, void *),
+       void *(*newh)(uintptr_t, int, void *))
+{
+       int l;
+
+       /*
+        * Notes about rounding bucket size to a power of two.
+        * Given the original bucket size, we compute the nearest lower and
+        * higher power of two, minus 1  (respectively b_min and b_max) because
+        * this value will be used to do an AND with the index returned
+        * by hash function.
+        * To choice between these two values, the original bucket size is
+        * compared with b_min. If the original size is greater than 4/3 b_min,
+        * we round the bucket size to b_max, else to b_min.
+        * This ratio try to round to the nearest power of two, advantaging
+        * the greater size if the different between two power is relatively
+        * big.
+        * Rounding the bucket size to a power of two avoid the use of
+        * module when calculating the correct bucket.
+        * The ht->buckets variable store the bucket size - 1 to simply
+        * do an AND between the index returned by hash function and ht->bucket
+        * instead of a module.
+        */
+       int b_min; /* min buckets */
+       int b_max; /* max buckets */
+       int b_ori; /* original buckets */
+
+       if (h == NULL || match == NULL) {
+               printf("--- missing hash or match function");
+               return NULL;
+       }
+       if (buckets < 1 || buckets > 65536)
+               return NULL;
+
+       b_ori = buckets;
+       /* calculate next power of 2, - 1*/
+       buckets |= buckets >> 1;
+       buckets |= buckets >> 2;
+       buckets |= buckets >> 4;
+       buckets |= buckets >> 8;
+       buckets |= buckets >> 16;
+
+       b_max = buckets; /* Next power */
+       b_min = buckets >> 1; /* Previous power */
+
+       /* Calculate the 'nearest' bucket size */
+       if (b_min * 4000 / 3000 < b_ori)
+               buckets = b_max;
+       else
+               buckets = b_min;
+
+       if (ht) {       /* see if we can reuse */
+               if (buckets <= ht->buckets) {
+                       ht->buckets = buckets;
+               } else {
+                       /* free pointers if not allocated inline */
+                       if (ht->ht != (void *)(ht + 1))
+                               free(ht->ht, M_DN_HEAP);
+                       free(ht, M_DN_HEAP);
+                       ht = NULL;
+               }
+       }
+       if (ht == NULL) {
+               /* Allocate buckets + 1 entries because buckets is use to
+                * do the AND with the index returned by hash function
+                */
+               l = sizeof(*ht) + (buckets + 1) * sizeof(void **);
+               ht = malloc(l, M_DN_HEAP, M_NOWAIT | M_ZERO);
+       }
+       if (ht) {
+               ht->ht = (void **)(ht + 1);
+               ht->buckets = buckets;
+               ht->ofs = ofs;
+               ht->hash = h;
+               ht->match = match;
+               ht->newh = newh;
+       }
+       return ht;
+}
+
+/* dummy callback for dn_ht_free to unlink all */
+static int
+do_del(void *obj, void *arg)
+{
+       return DNHT_SCAN_DEL;
+}
+
+void
+dn_ht_free(struct dn_ht *ht, int flags)
+{
+       if (ht == NULL)
+               return;
+       if (flags & DNHT_REMOVE) {
+               (void)dn_ht_scan(ht, do_del, NULL);
+       } else {
+               if (ht->ht && ht->ht != (void *)(ht + 1))
+                       free(ht->ht, M_DN_HEAP);
+               free(ht, M_DN_HEAP);
+       }
+}
+
+int
+dn_ht_entries(struct dn_ht *ht)
+{
+       return ht ? ht->entries : 0;
+}
+
+/* lookup and optionally create or delete element */
+void *
+dn_ht_find(struct dn_ht *ht, uintptr_t key, int flags, void *arg)
+{
+       int i;
+       void **pp, *p;
+
+       if (ht == NULL) /* easy on an empty hash */
+               return NULL;
+       i = (ht->buckets == 1) ? 0 :
+               (ht->hash(key, flags, arg) & ht->buckets);
+
+       for (pp = &ht->ht[i]; (p = *pp); pp = (void **)((char *)p + ht->ofs)) {
+               if (flags & DNHT_MATCH_PTR) {
+                       if (key == (uintptr_t)p)
+                               break;
+               } else if (ht->match(p, key, flags, arg)) /* found match */
+                       break;
+       }
+       if (p) {
+               if (flags & DNHT_REMOVE) {
+                       /* link in the next element */
+                       *pp = *(void **)((char *)p + ht->ofs);
+                       *(void **)((char *)p + ht->ofs) = NULL;
+                       ht->entries--;
+               }
+       } else if (flags & DNHT_INSERT) {
+               // printf("%s before calling new, bucket %d ofs %d\n",
+               //      __FUNCTION__, i, ht->ofs);
+               p = ht->newh ? ht->newh(key, flags, arg) : (void *)key;
+               // printf("%s newh returns %p\n", __FUNCTION__, p);
+               if (p) {
+                       ht->entries++;
+                       *(void **)((char *)p + ht->ofs) = ht->ht[i];
+                       ht->ht[i] = p;
+               }
+       }
+       return p;
+}
+
+/*
+ * do a scan with the option to delete the object. Extract next before
+ * running the callback because the element may be destroyed there.
+ */
+int
+dn_ht_scan(struct dn_ht *ht, int (*fn)(void *, void *), void *arg)
+{
+       int i, ret, found = 0;
+       void **curp, *cur, *next;
+
+       if (ht == NULL || fn == NULL)
+               return 0;
+       for (i = 0; i <= ht->buckets; i++) {
+               curp = &ht->ht[i];
+               while ( (cur = *curp) != NULL) {
+                       next = *(void **)((char *)cur + ht->ofs);
+                       ret = fn(cur, arg);
+                       if (ret & DNHT_SCAN_DEL) {
+                               found++;
+                               ht->entries--;
+                               *curp = next;
+                       } else {
+                               curp = (void **)((char *)cur + ht->ofs);
+                       }
+                       if (ret & DNHT_SCAN_END)
+                               return found;
+               }
+       }
+       return found;
+}
+
+/*
+ * Similar to dn_ht_scan(), except thah the scan is performed only
+ * in the bucket 'bucket'. The function returns a correct bucket number if
+ * the original is invalid
+ */
+int
+dn_ht_scan_bucket(struct dn_ht *ht, int *bucket, int (*fn)(void *, void *),
+                void *arg)
+{
+       int i, ret, found = 0;
+       void **curp, *cur, *next;
+
+       if (ht == NULL || fn == NULL)
+               return 0;
+       if (*bucket > ht->buckets)
+               *bucket = 0;
+       i = *bucket;
+
+       curp = &ht->ht[i];
+       while ( (cur = *curp) != NULL) {
+               next = *(void **)((char *)cur + ht->ofs);
+               ret = fn(cur, arg);
+               if (ret & DNHT_SCAN_DEL) {
+                       found++;
+                       ht->entries--;
+                       *curp = next;
+               } else {
+                       curp = (void **)((char *)cur + ht->ofs);
+               }
+               if (ret & DNHT_SCAN_END)
+                       return found;
+       }
+       return found;
+}
+
diff --git a/dummynet2/dn_sched_fifo.c b/dummynet2/dn_sched_fifo.c
new file mode 100644 (file)
index 0000000..d7d923e
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id: dn_sched_fifo.c 5621 2010-03-04 16:51:27Z luigi $
+ */
+
+#ifdef _KERNEL
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <net/if.h>    /* IFNAMSIZ */
+#include <netinet/in.h>
+#include <netinet/ip_var.h>            /* ipfw_rule_ref */
+#include <netinet/ip_fw.h>     /* flow_id */
+#include <netinet/ip_dummynet.h>
+#include <netinet/ipfw/dn_heap.h>
+#include <netinet/ipfw/ip_dn_private.h>
+#include <netinet/ipfw/dn_sched.h>
+#else
+#include <dn_test.h>
+#endif
+
+/*
+ * This file implements a FIFO scheduler for a single queue.
+ * The queue is allocated as part of the scheduler instance,
+ * and there is a single flowset is in the template which stores
+ * queue size and policy.
+ * Enqueue and dequeue use the default library functions.
+ */
+static int 
+fifo_enqueue(struct dn_sch_inst *si, struct dn_queue *q, struct mbuf *m)
+{
+       /* XXX if called with q != NULL and m=NULL, this is a
+        * re-enqueue from an existing scheduler, which we should
+        * handle.
+        */
+       return dn_enqueue((struct dn_queue *)(si+1), m, 0);
+}
+
+static struct mbuf *
+fifo_dequeue(struct dn_sch_inst *si)
+{
+       return dn_dequeue((struct dn_queue *)(si + 1));
+}
+
+static int
+fifo_new_sched(struct dn_sch_inst *si)
+{
+       /* This scheduler instance contains the queue */
+       struct dn_queue *q = (struct dn_queue *)(si + 1);
+
+        set_oid(&q->ni.oid, DN_QUEUE, sizeof(*q));
+       q->_si = si;
+       q->fs = si->sched->fs;
+       return 0;
+}
+
+static int
+fifo_free_sched(struct dn_sch_inst *si)
+{
+       struct dn_queue *q = (struct dn_queue *)(si + 1);
+       dn_free_pkts(q->mq.head);
+       bzero(q, sizeof(*q));
+       return 0;
+}
+
+/*
+ * FIFO scheduler descriptor
+ * contains the type of the scheduler, the name, the size of extra
+ * data structures, and function pointers.
+ */
+static struct dn_alg fifo_desc = {
+       _SI( .type = )  DN_SCHED_FIFO,
+       _SI( .name = )  "FIFO",
+       _SI( .flags = ) 0,
+
+       _SI( .schk_datalen = ) 0,
+       _SI( .si_datalen = )  sizeof(struct dn_queue),
+       _SI( .q_datalen = )  0,
+
+       _SI( .enqueue = )  fifo_enqueue,
+       _SI( .dequeue = )  fifo_dequeue,
+       _SI( .config = )  NULL,
+       _SI( .destroy = )  NULL,
+       _SI( .new_sched = )  fifo_new_sched,
+       _SI( .free_sched = )  fifo_free_sched,
+       _SI( .new_fsk = )  NULL,
+       _SI( .free_fsk = )  NULL,
+       _SI( .new_queue = )  NULL,
+       _SI( .free_queue = )  NULL,
+};
+
+DECLARE_DNSCHED_MODULE(dn_fifo, &fifo_desc);
diff --git a/dummynet2/dn_sched_prio.c b/dummynet2/dn_sched_prio.c
new file mode 100755 (executable)
index 0000000..048945f
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id: dn_sched_prio.c 5797 2010-03-21 16:31:08Z luigi $
+ */
+#ifdef _KERNEL
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <net/if.h>    /* IFNAMSIZ */
+#include <netinet/in.h>
+#include <netinet/ip_var.h>            /* ipfw_rule_ref */
+#include <netinet/ip_fw.h>     /* flow_id */
+#include <netinet/ip_dummynet.h>
+#include <netinet/ipfw/dn_heap.h>
+#include <netinet/ipfw/ip_dn_private.h>
+#include <netinet/ipfw/dn_sched.h>
+#else
+#include <dn_test.h>
+#endif
+
+#define DN_SCHED_PRIO  5 //XXX
+
+#if !defined(_KERNEL) || !defined(__linux__)
+#define test_bit(ix, pData)    ((*pData) & (1<<(ix)))
+#define __set_bit(ix, pData)   (*pData) |= (1<<(ix))
+#define __clear_bit(ix, pData) (*pData) &= ~(1<<(ix))
+#endif
+
+#ifdef __MIPSEL__
+#define __clear_bit(ix, pData) (*pData) &= ~(1<<(ix))
+#endif
+
+/* Size of the array of queues pointers. */
+#define BITMAP_T       unsigned long
+#define MAXPRIO                (sizeof(BITMAP_T) * 8)
+
+/*
+ * The scheduler instance contains an array of pointers to queues,
+ * one for each priority, and a bitmap listing backlogged queues.
+ */
+struct prio_si {
+       BITMAP_T bitmap;                        /* array bitmap */
+       struct dn_queue *q_array[MAXPRIO];      /* Array of queues pointers */
+};
+
+/*
+ * If a queue with the same priority is already backlogged, use
+ * that one instead of the queue passed as argument.
+ */
+static int 
+prio_enqueue(struct dn_sch_inst *_si, struct dn_queue *q, struct mbuf *m)
+{
+       struct prio_si *si = (struct prio_si *)(_si + 1);
+       int prio = q->fs->fs.par[0];
+
+       if (test_bit(prio, &si->bitmap) == 0) {
+               /* No queue with this priority, insert */
+               __set_bit(prio, &si->bitmap);
+               si->q_array[prio] = q;
+       } else { /* use the existing queue */
+               q = si->q_array[prio];
+       }
+       if (dn_enqueue(q, m, 0))
+               return 1;
+       return 0;
+}
+
+/*
+ * Packets are dequeued only from the highest priority queue.
+ * The function ffs() return the lowest bit in the bitmap that rapresent
+ * the array index (-1) which contains the pointer to the highest priority
+ * queue.
+ * After the dequeue, if this queue become empty, it is index is removed
+ * from the bitmap.
+ * Scheduler is idle if the bitmap is empty
+ *
+ * NOTE: highest priority is 0, lowest is sched->max_prio_q
+ */
+static struct mbuf *
+prio_dequeue(struct dn_sch_inst *_si)
+{
+       struct prio_si *si = (struct prio_si *)(_si + 1);
+       struct mbuf *m;
+       struct dn_queue *q;
+       int prio;
+
+       if (si->bitmap == 0) /* scheduler idle */
+               return NULL;
+
+       prio = ffs(si->bitmap) - 1;
+
+       /* Take the highest priority queue in the scheduler */
+       q = si->q_array[prio];
+       // assert(q)
+
+       m = dn_dequeue(q);
+       if (q->mq.head == NULL) {
+               /* Queue is now empty, remove from scheduler
+                * and mark it
+                */
+               si->q_array[prio] = NULL;
+               __clear_bit(prio, &si->bitmap);
+       }
+       return m;
+}
+
+static int
+prio_new_sched(struct dn_sch_inst *_si)
+{
+       struct prio_si *si = (struct prio_si *)(_si + 1);
+
+       bzero(si->q_array, sizeof(si->q_array));
+       si->bitmap = 0;
+
+       return 0;
+}
+
+static int
+prio_new_fsk(struct dn_fsk *fs)
+{
+       /* Check if the prioritiy is between 0 and MAXPRIO-1 */
+       ipdn_bound_var(&fs->fs.par[0], 0, 0, MAXPRIO - 1, "PRIO priority");
+       return 0;
+}
+
+static int
+prio_new_queue(struct dn_queue *q)
+{
+       struct prio_si *si = (struct prio_si *)(q->_si + 1);
+       int prio = q->fs->fs.par[0];
+       struct dn_queue *oldq;
+
+       q->ni.oid.subtype = DN_SCHED_PRIO;
+
+       if (q->mq.head == NULL)
+               return 0;
+
+       /* Queue already full, must insert in the scheduler or append
+        * mbufs to existing queue. This partly duplicates prio_enqueue
+        */
+       if (test_bit(prio, &si->bitmap) == 0) {
+               /* No queue with this priority, insert */
+               __set_bit(prio, &si->bitmap);
+               si->q_array[prio] = q;
+       } else if ( (oldq = si->q_array[prio]) != q) {
+               /* must append to the existing queue.
+                * can simply append q->mq.head to q2->...
+                * and add the counters to those of q2
+                */
+               oldq->mq.tail->m_nextpkt = q->mq.head;
+               oldq->mq.tail = q->mq.tail;
+               oldq->ni.length += q->ni.length;
+               q->ni.length = 0;
+               oldq->ni.len_bytes += q->ni.len_bytes;
+               q->ni.len_bytes = 0;
+               q->mq.tail = q->mq.head = NULL;
+       }
+       return 0;
+}
+
+static int
+prio_free_queue(struct dn_queue *q)
+{
+       int prio = q->fs->fs.par[0];
+       struct prio_si *si = (struct prio_si *)(q->_si + 1);
+
+       if (si->q_array[prio] == q) {
+               si->q_array[prio] = NULL;
+               __clear_bit(prio, &si->bitmap);
+       }
+       return 0;
+}
+
+
+static struct dn_alg prio_desc = {
+       _SI( .type = ) DN_SCHED_PRIO,
+       _SI( .name = ) "PRIO",
+       _SI( .flags = ) DN_MULTIQUEUE,
+
+       /* we need extra space in the si and the queue */
+       _SI( .schk_datalen = ) 0,
+       _SI( .si_datalen = ) sizeof(struct prio_si),
+       _SI( .q_datalen = ) 0,
+
+       _SI( .enqueue = ) prio_enqueue,
+       _SI( .dequeue = ) prio_dequeue,
+
+       _SI( .config = )  NULL,
+       _SI( .destroy = )  NULL,
+       _SI( .new_sched = ) prio_new_sched,
+       _SI( .free_sched = ) NULL,
+
+       _SI( .new_fsk = ) prio_new_fsk,
+       _SI( .free_fsk = )  NULL,
+
+       _SI( .new_queue = ) prio_new_queue,
+       _SI( .free_queue = ) prio_free_queue,
+};
+
+
+DECLARE_DNSCHED_MODULE(dn_prio, &prio_desc);
diff --git a/dummynet2/dn_sched_qfq.c b/dummynet2/dn_sched_qfq.c
new file mode 100644 (file)
index 0000000..13bf659
--- /dev/null
@@ -0,0 +1,864 @@
+/*
+ * Copyright (c) 2010 Fabio Checconi, Luigi Rizzo, Paolo Valente
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id: dn_sched_qfq.c 5621 2010-03-04 16:51:27Z luigi $
+ */
+
+#ifdef _KERNEL
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <net/if.h>    /* IFNAMSIZ */
+#include <netinet/in.h>
+#include <netinet/ip_var.h>            /* ipfw_rule_ref */
+#include <netinet/ip_fw.h>     /* flow_id */
+#include <netinet/ip_dummynet.h>
+#include <netinet/ipfw/dn_heap.h>
+#include <netinet/ipfw/ip_dn_private.h>
+#include <netinet/ipfw/dn_sched.h>
+#else
+#include <dn_test.h>
+#endif
+
+#ifdef QFQ_DEBUG
+struct qfq_sched;
+static void dump_sched(struct qfq_sched *q, const char *msg);
+#define        NO(x)   x
+#else
+#define NO(x)
+#endif
+#define DN_SCHED_QFQ   4 // XXX Where?
+typedef        unsigned long   bitmap;
+
+/*
+ * bitmaps ops are critical. Some linux versions have __fls
+ * and the bitmap ops. Some machines have ffs
+ */
+#if defined(_WIN32)
+int fls(unsigned int n)
+{
+       int i = 0;
+       for (i = 0; n > 0; n >>= 1, i++)
+               ;
+       return i;
+}
+#endif
+
+#if !defined(_KERNEL) || defined( __FreeBSD__ ) || defined(_WIN32)
+static inline unsigned long __fls(unsigned long word)
+{
+       return fls(word) - 1;
+}
+#endif
+
+#if !defined(_KERNEL) || !defined(__linux__)
+#ifdef QFQ_DEBUG
+int test_bit(int ix, bitmap *p)
+{
+       if (ix < 0 || ix > 31)
+               D("bad index %d", ix);
+       return *p & (1<<ix);
+}
+void __set_bit(int ix, bitmap *p)
+{
+       if (ix < 0 || ix > 31)
+               D("bad index %d", ix);
+       *p |= (1<<ix);
+}
+void __clear_bit(int ix, bitmap *p)
+{
+       if (ix < 0 || ix > 31)
+               D("bad index %d", ix);
+       *p &= ~(1<<ix);
+}
+#else /* !QFQ_DEBUG */
+/* XXX do we have fast version, or leave it to the compiler ? */
+#define test_bit(ix, pData)    ((*pData) & (1<<(ix)))
+#define __set_bit(ix, pData)   (*pData) |= (1<<(ix))
+#define __clear_bit(ix, pData) (*pData) &= ~(1<<(ix))
+#endif /* !QFQ_DEBUG */
+#endif /* !__linux__ */
+
+#ifdef __MIPSEL__
+#define __clear_bit(ix, pData) (*pData) &= ~(1<<(ix))
+#endif
+
+/*-------------------------------------------*/
+/*
+
+Virtual time computations.
+
+S, F and V are all computed in fixed point arithmetic with
+FRAC_BITS decimal bits.
+
+   QFQ_MAX_INDEX is the maximum index allowed for a group. We need
+       one bit per index.
+   QFQ_MAX_WSHIFT is the maximum power of two supported as a weight.
+   The layout of the bits is as below:
+  
+                   [ MTU_SHIFT ][      FRAC_BITS    ]
+                   [ MAX_INDEX    ][ MIN_SLOT_SHIFT ]
+                                ^.__grp->index = 0
+                                *.__grp->slot_shift
+  
+   where MIN_SLOT_SHIFT is derived by difference from the others.
+
+The max group index corresponds to Lmax/w_min, where
+Lmax=1<<MTU_SHIFT, w_min = 1 .
+From this, and knowing how many groups (MAX_INDEX) we want,
+we can derive the shift corresponding to each group.
+
+Because we often need to compute
+       F = S + len/w_i  and V = V + len/wsum
+instead of storing w_i store the value
+       inv_w = (1<<FRAC_BITS)/w_i
+so we can do F = S + len * inv_w * wsum.
+We use W_TOT in the formulas so we can easily move between
+static and adaptive weight sum.
+
+The per-scheduler-instance data contain all the data structures
+for the scheduler: bitmaps and bucket lists.
+
+ */
+/*
+ * Maximum number of consecutive slots occupied by backlogged classes
+ * inside a group. This is approx lmax/lmin + 5.
+ * XXX check because it poses constraints on MAX_INDEX
+ */
+#define QFQ_MAX_SLOTS  32
+/*
+ * Shifts used for class<->group mapping. Class weights are
+ * in the range [1, QFQ_MAX_WEIGHT], we to map each class i to the
+ * group with the smallest index that can support the L_i / r_i
+ * configured for the class.
+ *
+ * grp->index is the index of the group; and grp->slot_shift
+ * is the shift for the corresponding (scaled) sigma_i.
+ *
+ * When computing the group index, we do (len<<FP_SHIFT)/weight,
+ * then compute an FLS (which is like a log2()), and if the result
+ * is below the MAX_INDEX region we use 0 (which is the same as
+ * using a larger len).
+ */
+#define QFQ_MAX_INDEX          19
+#define QFQ_MAX_WSHIFT         16      /* log2(max_weight) */
+
+#define        QFQ_MAX_WEIGHT          (1<<QFQ_MAX_WSHIFT)
+#define QFQ_MAX_WSUM           (2*QFQ_MAX_WEIGHT)
+//#define IWSUM        (q->i_wsum)
+#define IWSUM  ((1<<FRAC_BITS)/QFQ_MAX_WSUM)
+
+#define FRAC_BITS              30      /* fixed point arithmetic */
+#define ONE_FP                 (1UL << FRAC_BITS)
+
+#define QFQ_MTU_SHIFT          11      /* log2(max_len) */
+#define QFQ_MIN_SLOT_SHIFT     (FRAC_BITS + QFQ_MTU_SHIFT - QFQ_MAX_INDEX)
+
+/*
+ * Possible group states, also indexes for the bitmaps array in
+ * struct qfq_queue. We rely on ER, IR, EB, IB being numbered 0..3
+ */
+enum qfq_state { ER, IR, EB, IB, QFQ_MAX_STATE };
+
+struct qfq_group;
+/*
+ * additional queue info. Some of this info should come from
+ * the flowset, we copy them here for faster processing.
+ * This is an overlay of the struct dn_queue
+ */
+struct qfq_class {
+       struct dn_queue _q;
+       uint64_t S, F;          /* flow timestamps (exact) */
+       struct qfq_class *next; /* Link for the slot list. */
+
+       /* group we belong to. In principle we would need the index,
+        * which is log_2(lmax/weight), but we never reference it
+        * directly, only the group.
+        */
+       struct qfq_group *grp;
+
+       /* these are copied from the flowset. */
+       uint32_t        inv_w;  /* ONE_FP/weight */
+       uint32_t        lmax;   /* Max packet size for this flow. */
+};
+
+/* Group descriptor, see the paper for details.
+ * Basically this contains the bucket lists
+ */
+struct qfq_group {
+       uint64_t S, F;                  /* group timestamps (approx). */
+       unsigned int slot_shift;        /* Slot shift. */
+       unsigned int index;             /* Group index. */
+       unsigned int front;             /* Index of the front slot. */
+       bitmap full_slots;              /* non-empty slots */
+
+       /* Array of lists of active classes. */
+       struct qfq_class *slots[QFQ_MAX_SLOTS];
+};
+
+/* scheduler instance descriptor. */
+struct qfq_sched {
+       uint64_t        V;              /* Precise virtual time. */
+       uint32_t        wsum;           /* weight sum */
+       NO(uint32_t     i_wsum;         /* ONE_FP/w_sum */
+       uint32_t        _queued;        /* debugging */
+       uint32_t        loops;  /* debugging */)
+       bitmap bitmaps[QFQ_MAX_STATE];  /* Group bitmaps. */
+       struct qfq_group groups[QFQ_MAX_INDEX + 1]; /* The groups. */
+};
+
+/*---- support functions ----------------------------*/
+
+/* Generic comparison function, handling wraparound. */
+static inline int qfq_gt(uint64_t a, uint64_t b)
+{
+       return (int64_t)(a - b) > 0;
+}
+
+/* Round a precise timestamp to its slotted value. */
+static inline uint64_t qfq_round_down(uint64_t ts, unsigned int shift)
+{
+       return ts & ~((1ULL << shift) - 1);
+}
+
+/* return the pointer to the group with lowest index in the bitmap */
+static inline struct qfq_group *qfq_ffs(struct qfq_sched *q,
+                                       unsigned long bitmap)
+{
+       int index = ffs(bitmap) - 1; // zero-based
+       return &q->groups[index];
+}
+
+/*
+ * Calculate a flow index, given its weight and maximum packet length.
+ * index = log_2(maxlen/weight) but we need to apply the scaling.
+ * This is used only once at flow creation.
+ */
+static int qfq_calc_index(uint32_t inv_w, unsigned int maxlen)
+{
+       uint64_t slot_size = (uint64_t)maxlen *inv_w;
+       unsigned long size_map;
+       int index = 0;
+
+       size_map = (unsigned long)(slot_size >> QFQ_MIN_SLOT_SHIFT);
+       if (!size_map)
+               goto out;
+
+       index = __fls(size_map) + 1;    // basically a log_2()
+       index -= !(slot_size - (1ULL << (index + QFQ_MIN_SLOT_SHIFT - 1)));
+
+       if (index < 0)
+               index = 0;
+
+out:
+       ND("W = %d, L = %d, I = %d\n", ONE_FP/inv_w, maxlen, index);
+       return index;
+}
+/*---- end support functions ----*/
+
+/*-------- API calls --------------------------------*/
+/*
+ * Validate and copy parameters from flowset.
+ */
+static int
+qfq_new_queue(struct dn_queue *_q)
+{
+       struct qfq_sched *q = (struct qfq_sched *)(_q->_si + 1);
+       struct qfq_class *cl = (struct qfq_class *)_q;
+       int i;
+       uint32_t w;     /* approximated weight */
+
+       /* import parameters from the flowset. They should be correct
+        * already.
+        */
+       w = _q->fs->fs.par[0];
+       cl->lmax = _q->fs->fs.par[1];
+       if (!w || w > QFQ_MAX_WEIGHT) {
+               w = 1;
+               D("rounding weight to 1");
+       }
+       cl->inv_w = ONE_FP/w;
+       w = ONE_FP/cl->inv_w;   
+       if (q->wsum + w > QFQ_MAX_WSUM)
+               return EINVAL;
+
+       i = qfq_calc_index(cl->inv_w, cl->lmax);
+       cl->grp = &q->groups[i];
+       q->wsum += w;
+       // XXX cl->S = q->V; ?
+       // XXX compute q->i_wsum
+       return 0;
+}
+
+/* remove an empty queue */
+static int
+qfq_free_queue(struct dn_queue *_q)
+{
+       struct qfq_sched *q = (struct qfq_sched *)(_q->_si + 1);
+       struct qfq_class *cl = (struct qfq_class *)_q;
+       if (cl->inv_w) {
+               q->wsum -= ONE_FP/cl->inv_w;
+               cl->inv_w = 0; /* reset weight to avoid run twice */
+       }
+       return 0;
+}
+
+/* Calculate a mask to mimic what would be ffs_from(). */
+static inline unsigned long
+mask_from(unsigned long bitmap, int from)
+{
+       return bitmap & ~((1UL << from) - 1);
+}
+
+/*
+ * The state computation relies on ER=0, IR=1, EB=2, IB=3
+ * First compute eligibility comparing grp->S, q->V,
+ * then check if someone is blocking us and possibly add EB
+ */
+static inline unsigned int
+qfq_calc_state(struct qfq_sched *q, struct qfq_group *grp)
+{
+       /* if S > V we are not eligible */
+       unsigned int state = qfq_gt(grp->S, q->V);
+       unsigned long mask = mask_from(q->bitmaps[ER], grp->index);
+       struct qfq_group *next;
+
+       if (mask) {
+               next = qfq_ffs(q, mask);
+               if (qfq_gt(grp->F, next->F))
+                       state |= EB;
+       }
+
+       return state;
+}
+
+/*
+ * In principle
+ *     q->bitmaps[dst] |= q->bitmaps[src] & mask;
+ *     q->bitmaps[src] &= ~mask;
+ * but we should make sure that src != dst
+ */
+static inline void
+qfq_move_groups(struct qfq_sched *q, unsigned long mask, int src, int dst)
+{
+       q->bitmaps[dst] |= q->bitmaps[src] & mask;
+       q->bitmaps[src] &= ~mask;
+}
+
+static inline void
+qfq_unblock_groups(struct qfq_sched *q, int index, uint64_t old_finish)
+{
+       unsigned long mask = mask_from(q->bitmaps[ER], index + 1);
+       struct qfq_group *next;
+
+       if (mask) {
+               next = qfq_ffs(q, mask);
+               if (!qfq_gt(next->F, old_finish))
+                       return;
+       }
+
+       mask = (1UL << index) - 1;
+       qfq_move_groups(q, mask, EB, ER);
+       qfq_move_groups(q, mask, IB, IR);
+}
+
+/*
+ * perhaps
+ *
+       old_V ^= q->V;
+       old_V >>= QFQ_MIN_SLOT_SHIFT;
+       if (old_V) {
+               ...
+       }
+ *
+ */
+static inline void
+qfq_make_eligible(struct qfq_sched *q, uint64_t old_V)
+{
+       unsigned long mask, vslot, old_vslot;
+
+       vslot = q->V >> QFQ_MIN_SLOT_SHIFT;
+       old_vslot = old_V >> QFQ_MIN_SLOT_SHIFT;
+
+       if (vslot != old_vslot) {
+               mask = (2UL << (__fls(vslot ^ old_vslot))) - 1;
+               qfq_move_groups(q, mask, IR, ER);
+               qfq_move_groups(q, mask, IB, EB);
+       }
+}
+
+/*
+ * XXX we should make sure that slot becomes less than 32.
+ * This is guaranteed by the input values.
+ * roundedS is always cl->S rounded on grp->slot_shift bits.
+ */
+static inline void
+qfq_slot_insert(struct qfq_group *grp, struct qfq_class *cl, uint64_t roundedS)
+{
+       uint64_t slot = (roundedS - grp->S) >> grp->slot_shift;
+       unsigned int i = (grp->front + slot) % QFQ_MAX_SLOTS;
+
+       cl->next = grp->slots[i];
+       grp->slots[i] = cl;
+       __set_bit(slot, &grp->full_slots);
+}
+
+/*
+ * remove the entry from the slot
+ */
+static inline void
+qfq_front_slot_remove(struct qfq_group *grp)
+{
+       struct qfq_class **h = &grp->slots[grp->front];
+
+       *h = (*h)->next;
+       if (!*h)
+               __clear_bit(0, &grp->full_slots);
+}
+
+/*
+ * Returns the first full queue in a group. As a side effect,
+ * adjust the bucket list so the first non-empty bucket is at
+ * position 0 in full_slots.
+ */
+static inline struct qfq_class *
+qfq_slot_scan(struct qfq_group *grp)
+{
+       int i;
+
+       ND("grp %d full %x", grp->index, grp->full_slots);
+       if (!grp->full_slots)
+               return NULL;
+
+       i = ffs(grp->full_slots) - 1; // zero-based
+       if (i > 0) {
+               grp->front = (grp->front + i) % QFQ_MAX_SLOTS;
+               grp->full_slots >>= i;
+       }
+
+       return grp->slots[grp->front];
+}
+
+/*
+ * adjust the bucket list. When the start time of a group decreases,
+ * we move the index down (modulo QFQ_MAX_SLOTS) so we don't need to
+ * move the objects. The mask of occupied slots must be shifted
+ * because we use ffs() to find the first non-empty slot.
+ * This covers decreases in the group's start time, but what about
+ * increases of the start time ?
+ * Here too we should make sure that i is less than 32
+ */
+static inline void
+qfq_slot_rotate(struct qfq_sched *q, struct qfq_group *grp, uint64_t roundedS)
+{
+       unsigned int i = (grp->S - roundedS) >> grp->slot_shift;
+
+       grp->full_slots <<= i;
+       grp->front = (grp->front - i) % QFQ_MAX_SLOTS;
+}
+
+
+static inline void
+qfq_update_eligible(struct qfq_sched *q, uint64_t old_V)
+{
+       bitmap ineligible;
+
+       ineligible = q->bitmaps[IR] | q->bitmaps[IB];
+       if (ineligible) {
+               if (!q->bitmaps[ER]) {
+                       struct qfq_group *grp;
+                       grp = qfq_ffs(q, ineligible);
+                       if (qfq_gt(grp->S, q->V))
+                               q->V = grp->S;
+               }
+               qfq_make_eligible(q, old_V);
+       }
+}
+
+/*
+ * Updates the class, returns true if also the group needs to be updated.
+ */
+static inline int
+qfq_update_class(struct qfq_sched *q, struct qfq_group *grp,
+           struct qfq_class *cl)
+{
+
+       cl->S = cl->F;
+       if (cl->_q.mq.head == NULL)  {
+               qfq_front_slot_remove(grp);
+       } else {
+               unsigned int len;
+               uint64_t roundedS;
+
+               len = cl->_q.mq.head->m_pkthdr.len;
+               cl->F = cl->S + (uint64_t)len * cl->inv_w;
+               roundedS = qfq_round_down(cl->S, grp->slot_shift);
+               if (roundedS == grp->S)
+                       return 0;
+
+               qfq_front_slot_remove(grp);
+               qfq_slot_insert(grp, cl, roundedS);
+       }
+       return 1;
+}
+
+static struct mbuf *
+qfq_dequeue(struct dn_sch_inst *si)
+{
+       struct qfq_sched *q = (struct qfq_sched *)(si + 1);
+       struct qfq_group *grp;
+       struct qfq_class *cl;
+       struct mbuf *m;
+       uint64_t old_V;
+
+       NO(q->loops++;)
+       if (!q->bitmaps[ER]) {
+               NO(if (q->queued)
+                       dump_sched(q, "start dequeue");)
+               return NULL;
+       }
+
+       grp = qfq_ffs(q, q->bitmaps[ER]);
+
+       cl = grp->slots[grp->front];
+       /* extract from the first bucket in the bucket list */
+       m = dn_dequeue(&cl->_q);
+
+       if (!m) {
+               D("BUG/* non-workconserving leaf */");
+               return NULL;
+       }
+       NO(q->queued--;)
+       old_V = q->V;
+       q->V += (uint64_t)m->m_pkthdr.len * IWSUM;
+       ND("m is %p F 0x%llx V now 0x%llx", m, cl->F, q->V);
+
+       if (qfq_update_class(q, grp, cl)) {
+               uint64_t old_F = grp->F;
+               cl = qfq_slot_scan(grp);
+               if (!cl) { /* group gone, remove from ER */
+                       __clear_bit(grp->index, &q->bitmaps[ER]);
+                       // grp->S = grp->F + 1; // XXX debugging only
+               } else {
+                       uint64_t roundedS = qfq_round_down(cl->S, grp->slot_shift);
+                       unsigned int s;
+
+                       if (grp->S == roundedS)
+                               goto skip_unblock;
+                       grp->S = roundedS;
+                       grp->F = roundedS + (2ULL << grp->slot_shift);
+                       /* remove from ER and put in the new set */
+                       __clear_bit(grp->index, &q->bitmaps[ER]);
+                       s = qfq_calc_state(q, grp);
+                       __set_bit(grp->index, &q->bitmaps[s]);
+               }
+               /* we need to unblock even if the group has gone away */
+               qfq_unblock_groups(q, grp->index, old_F);
+       }
+
+skip_unblock:
+       qfq_update_eligible(q, old_V);
+       NO(if (!q->bitmaps[ER] && q->queued)
+               dump_sched(q, "end dequeue");)
+
+       return m;
+}
+
+/*
+ * Assign a reasonable start time for a new flow k in group i.
+ * Admissible values for \hat(F) are multiples of \sigma_i
+ * no greater than V+\sigma_i . Larger values mean that
+ * we had a wraparound so we consider the timestamp to be stale.
+ *
+ * If F is not stale and F >= V then we set S = F.
+ * Otherwise we should assign S = V, but this may violate
+ * the ordering in ER. So, if we have groups in ER, set S to
+ * the F_j of the first group j which would be blocking us.
+ * We are guaranteed not to move S backward because
+ * otherwise our group i would still be blocked.
+ */
+static inline void
+qfq_update_start(struct qfq_sched *q, struct qfq_class *cl)
+{
+       unsigned long mask;
+       uint32_t limit, roundedF;
+       int slot_shift = cl->grp->slot_shift;
+
+       roundedF = qfq_round_down(cl->F, slot_shift);
+       limit = qfq_round_down(q->V, slot_shift) + (1UL << slot_shift);
+
+       if (!qfq_gt(cl->F, q->V) || qfq_gt(roundedF, limit)) {
+               /* timestamp was stale */
+               mask = mask_from(q->bitmaps[ER], cl->grp->index);
+               if (mask) {
+                       struct qfq_group *next = qfq_ffs(q, mask);
+                       if (qfq_gt(roundedF, next->F)) {
+                               cl->S = next->F;
+                               return;
+                       }
+               }
+               cl->S = q->V;
+       } else { /* timestamp is not stale */
+               cl->S = cl->F;
+       }
+}
+
+static int
+qfq_enqueue(struct dn_sch_inst *si, struct dn_queue *_q, struct mbuf *m)
+{
+       struct qfq_sched *q = (struct qfq_sched *)(si + 1);
+       struct qfq_group *grp;
+       struct qfq_class *cl = (struct qfq_class *)_q;
+       uint64_t roundedS;
+       int s;
+
+       NO(q->loops++;)
+       DX(4, "len %d flow %p inv_w 0x%x grp %d", m->m_pkthdr.len,
+               _q, cl->inv_w, cl->grp->index);
+       /* XXX verify that the packet obeys the parameters */
+       if (m != _q->mq.head) {
+               if (dn_enqueue(_q, m, 0)) /* packet was dropped */
+                       return 1;
+               NO(q->queued++;)
+               if (m != _q->mq.head)
+                       return 0;
+       }
+       /* If reach this point, queue q was idle */
+       grp = cl->grp;
+       qfq_update_start(q, cl); /* adjust start time */
+       /* compute new finish time and rounded start. */
+       cl->F = cl->S + (uint64_t)(m->m_pkthdr.len) * cl->inv_w;
+       roundedS = qfq_round_down(cl->S, grp->slot_shift);
+
+       /*
+        * insert cl in the correct bucket.
+        * If cl->S >= grp->S we don't need to adjust the
+        * bucket list and simply go to the insertion phase.
+        * Otherwise grp->S is decreasing, we must make room
+        * in the bucket list, and also recompute the group state.
+        * Finally, if there were no flows in this group and nobody
+        * was in ER make sure to adjust V.
+        */
+       if (grp->full_slots) {
+               if (!qfq_gt(grp->S, cl->S))
+                       goto skip_update;
+               /* create a slot for this cl->S */
+               qfq_slot_rotate(q, grp, roundedS);
+               /* group was surely ineligible, remove */
+               __clear_bit(grp->index, &q->bitmaps[IR]);
+               __clear_bit(grp->index, &q->bitmaps[IB]);
+       } else if (!q->bitmaps[ER] && qfq_gt(roundedS, q->V))
+               q->V = roundedS;
+
+       grp->S = roundedS;
+       grp->F = roundedS + (2ULL << grp->slot_shift); // i.e. 2\sigma_i
+       s = qfq_calc_state(q, grp);
+       __set_bit(grp->index, &q->bitmaps[s]);
+       ND("new state %d 0x%x", s, q->bitmaps[s]);
+       ND("S %llx F %llx V %llx", cl->S, cl->F, q->V);
+skip_update:
+       qfq_slot_insert(grp, cl, roundedS);
+
+       return 0;
+}
+
+
+#if 0
+static inline void
+qfq_slot_remove(struct qfq_sched *q, struct qfq_group *grp,
+       struct qfq_class *cl, struct qfq_class **pprev)
+{
+       unsigned int i, offset;
+       uint64_t roundedS;
+
+       roundedS = qfq_round_down(cl->S, grp->slot_shift);
+       offset = (roundedS - grp->S) >> grp->slot_shift;
+       i = (grp->front + offset) % QFQ_MAX_SLOTS;
+
+#ifdef notyet
+       if (!pprev) {
+               pprev = &grp->slots[i];
+               while (*pprev && *pprev != cl)
+                       pprev = &(*pprev)->next;
+       }
+#endif
+
+       *pprev = cl->next;
+       if (!grp->slots[i])
+               __clear_bit(offset, &grp->full_slots);
+}
+
+/*
+ * called to forcibly destroy a queue.
+ * If the queue is not in the front bucket, or if it has
+ * other queues in the front bucket, we can simply remove
+ * the queue with no other side effects.
+ * Otherwise we must propagate the event up.
+ * XXX description to be completed.
+ */
+static void
+qfq_deactivate_class(struct qfq_sched *q, struct qfq_class *cl,
+                                struct qfq_class **pprev)
+{
+       struct qfq_group *grp = &q->groups[cl->index];
+       unsigned long mask;
+       uint64_t roundedS;
+       int s;
+
+       cl->F = cl->S;  // not needed if the class goes away.
+       qfq_slot_remove(q, grp, cl, pprev);
+
+       if (!grp->full_slots) {
+               /* nothing left in the group, remove from all sets.
+                * Do ER last because if we were blocking other groups
+                * we must unblock them.
+                */
+               __clear_bit(grp->index, &q->bitmaps[IR]);
+               __clear_bit(grp->index, &q->bitmaps[EB]);
+               __clear_bit(grp->index, &q->bitmaps[IB]);
+
+               if (test_bit(grp->index, &q->bitmaps[ER]) &&
+                   !(q->bitmaps[ER] & ~((1UL << grp->index) - 1))) {
+                       mask = q->bitmaps[ER] & ((1UL << grp->index) - 1);
+                       if (mask)
+                               mask = ~((1UL << __fls(mask)) - 1);
+                       else
+                               mask = ~0UL;
+                       qfq_move_groups(q, mask, EB, ER);
+                       qfq_move_groups(q, mask, IB, IR);
+               }
+               __clear_bit(grp->index, &q->bitmaps[ER]);
+       } else if (!grp->slots[grp->front]) {
+               cl = qfq_slot_scan(grp);
+               roundedS = qfq_round_down(cl->S, grp->slot_shift);
+               if (grp->S != roundedS) {
+                       __clear_bit(grp->index, &q->bitmaps[ER]);
+                       __clear_bit(grp->index, &q->bitmaps[IR]);
+                       __clear_bit(grp->index, &q->bitmaps[EB]);
+                       __clear_bit(grp->index, &q->bitmaps[IB]);
+                       grp->S = roundedS;
+                       grp->F = roundedS + (2ULL << grp->slot_shift);
+                       s = qfq_calc_state(q, grp);
+                       __set_bit(grp->index, &q->bitmaps[s]);
+               }
+       }
+       qfq_update_eligible(q, q->V);
+}
+#endif
+
+static int
+qfq_new_fsk(struct dn_fsk *f)
+{
+       ipdn_bound_var(&f->fs.par[0], 1, 1, QFQ_MAX_WEIGHT, "qfq weight");
+       ipdn_bound_var(&f->fs.par[1], 1500, 1, 2000, "qfq maxlen");
+       ND("weight %d len %d\n", f->fs.par[0], f->fs.par[1]);
+       return 0;
+}
+
+/*
+ * initialize a new scheduler instance
+ */
+static int
+qfq_new_sched(struct dn_sch_inst *si)
+{
+       struct qfq_sched *q = (struct qfq_sched *)(si + 1);
+       struct qfq_group *grp;
+       int i;
+
+       for (i = 0; i <= QFQ_MAX_INDEX; i++) {
+               grp = &q->groups[i];
+               grp->index = i;
+               grp->slot_shift = QFQ_MTU_SHIFT + FRAC_BITS -
+                                       (QFQ_MAX_INDEX - i);
+       }
+       return 0;
+}
+
+/*
+ * QFQ scheduler descriptor
+ */
+static struct dn_alg qfq_desc = {
+       _SI( .type = ) DN_SCHED_QFQ,
+       _SI( .name = ) "QFQ",
+       _SI( .flags = ) DN_MULTIQUEUE,
+
+       _SI( .schk_datalen = ) 0,
+       _SI( .si_datalen = ) sizeof(struct qfq_sched),
+       _SI( .q_datalen = ) sizeof(struct qfq_class) - sizeof(struct dn_queue),
+
+       _SI( .enqueue = ) qfq_enqueue,
+       _SI( .dequeue = ) qfq_dequeue,
+
+       _SI( .config = )  NULL,
+       _SI( .destroy = )  NULL,
+       _SI( .new_sched = ) qfq_new_sched,
+       _SI( .free_sched = )  NULL,
+       _SI( .new_fsk = ) qfq_new_fsk,
+       _SI( .free_fsk = )  NULL,
+       _SI( .new_queue = ) qfq_new_queue,
+       _SI( .free_queue = ) qfq_free_queue,
+};
+
+DECLARE_DNSCHED_MODULE(dn_qfq, &qfq_desc);
+
+#ifdef QFQ_DEBUG
+static void
+dump_groups(struct qfq_sched *q, uint32_t mask)
+{
+       int i, j;
+
+       for (i = 0; i < QFQ_MAX_INDEX + 1; i++) {
+               struct qfq_group *g = &q->groups[i];
+
+               if (0 == (mask & (1<<i)))
+                       continue;
+               for (j = 0; j < QFQ_MAX_SLOTS; j++) {
+                       if (g->slots[j])
+                               D("    bucket %d %p", j, g->slots[j]);
+               }
+               D("full_slots 0x%x", g->full_slots);
+               D("        %2d S 0x%20llx F 0x%llx %c", i,
+                       g->S, g->F,
+                       mask & (1<<i) ? '1' : '0');
+       }
+}
+
+static void
+dump_sched(struct qfq_sched *q, const char *msg)
+{
+       D("--- in %s: ---", msg);
+       ND("loops %d queued %d V 0x%llx", q->loops, q->queued, q->V);
+       D("    ER 0x%08x", q->bitmaps[ER]);
+       D("    EB 0x%08x", q->bitmaps[EB]);
+       D("    IR 0x%08x", q->bitmaps[IR]);
+       D("    IB 0x%08x", q->bitmaps[IB]);
+       dump_groups(q, 0xffffffff);
+};
+#endif /* QFQ_DEBUG */
diff --git a/dummynet2/dn_sched_rr.c b/dummynet2/dn_sched_rr.c
new file mode 100644 (file)
index 0000000..59c36ac
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id: dn_sched_rr.c 5621 2010-03-04 16:51:27Z luigi $
+ */
+
+#ifdef _KERNEL
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <net/if.h>    /* IFNAMSIZ */
+#include <netinet/in.h>
+#include <netinet/ip_var.h>            /* ipfw_rule_ref */
+#include <netinet/ip_fw.h>     /* flow_id */
+#include <netinet/ip_dummynet.h>
+#include <netinet/ipfw/dn_heap.h>
+#include <netinet/ipfw/ip_dn_private.h>
+#include <netinet/ipfw/dn_sched.h>
+#else
+#include <dn_test.h>
+#endif
+
+#define DN_SCHED_RR    3 // XXX Where?
+
+struct rr_queue {
+       struct dn_queue q;              /* Standard queue */
+       int status;                     /* 1: queue is in the list */
+       int credit;                     /* Number of bytes to transmit */
+       int quantum;                    /* quantum * C */
+       struct rr_queue *qnext;         /* */
+};
+
+/* struct rr_schk contains global config parameters
+ * and is right after dn_schk
+ */
+struct rr_schk {
+       int min_q;              /* Min quantum */
+       int max_q;              /* Max quantum */
+       int q_bytes;            /* Bytes per quantum */
+};
+
+/* per-instance round robin list, right after dn_sch_inst */
+struct rr_si {
+       struct rr_queue *head, *tail;   /* Pointer to current queue */
+};
+
+/* Append a queue to the rr list */
+static inline void
+rr_append(struct rr_queue *q, struct rr_si *si)
+{
+       q->status = 1;          /* mark as in-rr_list */
+       q->credit = q->quantum; /* initialize credit */
+
+       /* append to the tail */
+       if (si->head == NULL)
+               si->head = q;
+       else
+               si->tail->qnext = q;
+       si->tail = q;           /* advance the tail pointer */
+       q->qnext = si->head;    /* make it circular */
+}
+
+/* Remove the head queue from circular list. */
+static inline void
+rr_remove_head(struct rr_si *si)
+{
+       if (si->head == NULL)
+               return; /* empty queue */
+       si->head->status = 0;
+       
+       if (si->head == si->tail) {
+               si->head = si->tail = NULL;
+               return;
+       }
+
+       si->head = si->head->qnext;
+       si->tail->qnext = si->head;
+}
+
+/* Remove a queue from circular list.
+ * XXX see if ti can be merge with remove_queue()
+ */
+static inline void
+remove_queue_q(struct rr_queue *q, struct rr_si *si)
+{
+       struct rr_queue *prev;
+       
+       if (q->status != 1)
+               return;
+       if (q == si->head) {
+               rr_remove_head(si);
+               return;
+       }
+
+       for (prev = si->head; prev; prev = prev->qnext) {
+               if (prev->qnext != q)
+                       continue;
+               prev->qnext = q->qnext;
+               if (q == si->tail)
+                       si->tail = prev;
+               q->status = 0;
+               break;
+       }
+}
+
+
+static inline void
+next_pointer(struct rr_si *si)
+{
+       if (si->head == NULL)
+               return; /* empty queue */
+
+       si->head = si->head->qnext;
+       si->tail = si->tail->qnext;
+}
+
+static int 
+rr_enqueue(struct dn_sch_inst *_si, struct dn_queue *q, struct mbuf *m)
+{
+       struct rr_si *si;
+       struct rr_queue *rrq;
+
+       if (m != q->mq.head) {
+               if (dn_enqueue(q, m, 0)) /* packet was dropped */
+                       return 1;
+               if (m != q->mq.head)
+                       return 0;
+       }
+
+       /* If reach this point, queue q was idle */ 
+       si = (struct rr_si *)(_si + 1);
+       rrq = (struct rr_queue *)q;
+
+       if (rrq->status == 1) /* Queue is already in the queue list */
+               return 0;
+
+       /* Insert the queue in the queue list */
+       rr_append(rrq, si);
+
+       return 0;
+}
+
+static struct mbuf *
+rr_dequeue(struct dn_sch_inst *_si)
+{
+       /* Access scheduler instance private data */
+       struct rr_si *si = (struct rr_si *)(_si + 1);
+       struct rr_queue *rrq;
+       uint64_t len;
+
+       while ( (rrq = si->head) ) {
+               struct mbuf *m = rrq->q.mq.head;
+               if ( m == NULL) {
+                       /* empty queue, remove from list */
+                       rr_remove_head(si);
+                       continue;
+               }
+               len = m->m_pkthdr.len;
+
+               if (len > rrq->credit) {
+                       /* Packet too big */
+                       rrq->credit += rrq->quantum;
+                       /* Try next queue */
+                       next_pointer(si);
+               } else {
+                       rrq->credit -= len;
+                       return dn_dequeue(&rrq->q);
+               }
+       }
+
+       /* no packet to dequeue*/
+       return NULL;
+}
+
+static int
+rr_config(struct dn_schk *_schk)
+{
+       struct rr_schk *schk = (struct rr_schk *)(_schk + 1);
+       ND("called");
+
+       /* use reasonable quantums (64..2k bytes, default 1500) */
+       schk->min_q = 64;
+       schk->max_q = 2048;
+       schk->q_bytes = 1500;   /* quantum */
+
+       return 0;
+}
+
+static int
+rr_new_sched(struct dn_sch_inst *_si)
+{
+       struct rr_si *si = (struct rr_si *)(_si + 1);
+
+       ND("called");
+       si->head = si->tail = NULL;
+
+       return 0;
+}
+
+static int
+rr_free_sched(struct dn_sch_inst *_si)
+{
+       ND("called");
+       /* Nothing to do? */
+       return 0;
+}
+
+static int
+rr_new_fsk(struct dn_fsk *fs)
+{
+       struct rr_schk *schk = (struct rr_schk *)(fs->sched + 1);
+       /* par[0] is the weight, par[1] is the quantum step */
+       ipdn_bound_var(&fs->fs.par[0], 1,
+               1, 65536, "RR weight");
+       ipdn_bound_var(&fs->fs.par[1], schk->q_bytes,
+               schk->min_q, schk->max_q, "RR quantum");
+       return 0;
+}
+
+static int
+rr_new_queue(struct dn_queue *_q)
+{
+       struct rr_queue *q = (struct rr_queue *)_q;
+
+       _q->ni.oid.subtype = DN_SCHED_RR;
+
+       q->quantum = _q->fs->fs.par[0] * _q->fs->fs.par[1];
+       ND("called, q->quantum %d", q->quantum);
+       q->credit = q->quantum;
+       q->status = 0;
+
+       if (_q->mq.head != NULL) {
+               /* Queue NOT empty, insert in the queue list */
+               rr_append(q, (struct rr_si *)(_q->_si + 1));
+       }
+       return 0;
+}
+
+static int
+rr_free_queue(struct dn_queue *_q)
+{
+       struct rr_queue *q = (struct rr_queue *)_q;
+
+       ND("called");
+       if (q->status == 1) {
+               struct rr_si *si = (struct rr_si *)(_q->_si + 1);
+               remove_queue_q(q, si);
+       }
+       return 0;
+}
+
+/*
+ * RR scheduler descriptor
+ * contains the type of the scheduler, the name, the size of the
+ * structures and function pointers.
+ */
+static struct dn_alg rr_desc = {
+       _SI( .type = ) DN_SCHED_RR,
+       _SI( .name = ) "RR",
+       _SI( .flags = ) DN_MULTIQUEUE,
+
+       _SI( .schk_datalen = ) 0,
+       _SI( .si_datalen = ) sizeof(struct rr_si),
+       _SI( .q_datalen = ) sizeof(struct rr_queue) - sizeof(struct dn_queue),
+
+       _SI( .enqueue = ) rr_enqueue,
+       _SI( .dequeue = ) rr_dequeue,
+
+       _SI( .config = ) rr_config,
+       _SI( .destroy = ) NULL,
+       _SI( .new_sched = ) rr_new_sched,
+       _SI( .free_sched = ) rr_free_sched,
+       _SI( .new_fsk = ) rr_new_fsk,
+       _SI( .free_fsk = ) NULL,
+       _SI( .new_queue = ) rr_new_queue,
+       _SI( .free_queue = ) rr_free_queue,
+};
+
+
+DECLARE_DNSCHED_MODULE(dn_rr, &rr_desc);
diff --git a/dummynet2/dn_sched_wf2q.c b/dummynet2/dn_sched_wf2q.c
new file mode 100644 (file)
index 0000000..e221989
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa
+ * Copyright (c) 2000-2002 Luigi Rizzo, Universita` di Pisa
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id: dn_sched_wf2q.c 5621 2010-03-04 16:51:27Z luigi $
+ */
+
+#ifdef _KERNEL
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <net/if.h>    /* IFNAMSIZ */
+#include <netinet/in.h>
+#include <netinet/ip_var.h>            /* ipfw_rule_ref */
+#include <netinet/ip_fw.h>     /* flow_id */
+#include <netinet/ip_dummynet.h>
+#include <netinet/ipfw/dn_heap.h>
+#include <netinet/ipfw/ip_dn_private.h>
+#include <netinet/ipfw/dn_sched.h>
+#else
+#include <dn_test.h>
+#endif
+
+#ifndef MAX64
+#define MAX64(x,y)  (( (int64_t) ( (y)-(x) )) > 0 ) ? (y) : (x)
+#endif
+
+/*
+ * timestamps are computed on 64 bit using fixed point arithmetic.
+ * LMAX_BITS, WMAX_BITS are the max number of bits for the packet len
+ * and sum of weights, respectively. FRAC_BITS is the number of
+ * fractional bits. We want FRAC_BITS >> WMAX_BITS to avoid too large
+ * errors when computing the inverse, FRAC_BITS < 32 so we can do 1/w
+ * using an unsigned 32-bit division, and to avoid wraparounds we need
+ * LMAX_BITS + WMAX_BITS + FRAC_BITS << 64
+ * As an example
+ * FRAC_BITS = 26, LMAX_BITS=14, WMAX_BITS = 19
+ */
+#ifndef FRAC_BITS
+#define FRAC_BITS    28 /* shift for fixed point arithmetic */
+#define        ONE_FP  (1UL << FRAC_BITS)
+#endif
+
+/*
+ * Private information for the scheduler instance:
+ * sch_heap (key is Finish time) returns the next queue to serve
+ * ne_heap (key is Start time) stores not-eligible queues
+ * idle_heap (key=start/finish time) stores idle flows. It must
+ *     support extract-from-middle.
+ * A flow is only in 1 of the three heaps.
+ * XXX todo: use a more efficient data structure, e.g. a tree sorted
+ * by F with min_subtree(S) in each node
+ */
+struct wf2qp_si {
+    struct dn_heap sch_heap;   /* top extract - key Finish  time */
+    struct dn_heap ne_heap;    /* top extract - key Start   time */
+    struct dn_heap idle_heap;  /* random extract - key Start=Finish time */
+    uint64_t V;                        /* virtual time */
+    uint32_t inv_wsum;         /* inverse of sum of weights */
+    uint32_t wsum;             /* sum of weights */
+};
+
+struct wf2qp_queue {
+    struct dn_queue _q;
+    uint64_t S, F;             /* start time, finish time */
+    uint32_t inv_w;            /* ONE_FP / weight */
+    int32_t heap_pos;          /* position (index) of struct in heap */
+};
+
+/*
+ * This file implements a WF2Q+ scheduler as it has been in dummynet
+ * since 2000.
+ * The scheduler supports per-flow queues and has O(log N) complexity.
+ *
+ * WF2Q+ needs to drain entries from the idle heap so that we
+ * can keep the sum of weights up to date. We can do it whenever
+ * we get a chance, or periodically, or following some other
+ * strategy. The function idle_check() drains at most N elements
+ * from the idle heap.
+ */
+static void
+idle_check(struct wf2qp_si *si, int n, int force)
+{
+    struct dn_heap *h = &si->idle_heap;
+    while (n-- > 0 && h->elements > 0 &&
+               (force || DN_KEY_LT(HEAP_TOP(h)->key, si->V))) {
+       struct dn_queue *q = HEAP_TOP(h)->object;
+        struct wf2qp_queue *alg_fq = (struct wf2qp_queue *)q;
+
+        heap_extract(h, NULL);
+        /* XXX to let the flowset delete the queue we should
+        * mark it as 'unused' by the scheduler.
+        */
+        alg_fq->S = alg_fq->F + 1; /* Mark timestamp as invalid. */
+        si->wsum -= q->fs->fs.par[0];  /* adjust sum of weights */
+       if (si->wsum > 0)
+               si->inv_wsum = ONE_FP/si->wsum;
+    }
+}
+
+static int 
+wf2qp_enqueue(struct dn_sch_inst *_si, struct dn_queue *q, struct mbuf *m)
+{
+    struct dn_fsk *fs = q->fs;
+    struct wf2qp_si *si = (struct wf2qp_si *)(_si + 1);
+    struct wf2qp_queue *alg_fq;
+    uint64_t len = m->m_pkthdr.len;
+
+    if (m != q->mq.head) {
+       if (dn_enqueue(q, m, 0)) /* packet was dropped */
+           return 1;
+       if (m != q->mq.head)    /* queue was already busy */
+           return 0;
+    }
+
+    /* If reach this point, queue q was idle */ 
+    alg_fq = (struct wf2qp_queue *)q;
+
+    if (DN_KEY_LT(alg_fq->F, alg_fq->S)) {
+        /* F<S means timestamps are invalid ->brand new queue. */
+        alg_fq->S = si->V;             /* init start time */
+        si->wsum += fs->fs.par[0];     /* add weight of new queue. */
+       si->inv_wsum = ONE_FP/si->wsum;
+    } else { /* if it was idle then it was in the idle heap */
+        heap_extract(&si->idle_heap, q);
+        alg_fq->S = MAX64(alg_fq->F, si->V);   /* compute new S */
+    }
+    alg_fq->F = alg_fq->S + len * alg_fq->inv_w;
+
+    /* if nothing is backlogged, make sure this flow is eligible */
+    if (si->ne_heap.elements == 0 && si->sch_heap.elements == 0)
+        si->V = MAX64(alg_fq->S, si->V);
+
+    /*
+     * Look at eligibility. A flow is not eligibile if S>V (when
+     * this happens, it means that there is some other flow already
+     * scheduled for the same pipe, so the sch_heap cannot be
+     * empty). If the flow is not eligible we just store it in the
+     * ne_heap. Otherwise, we store in the sch_heap.
+     * Note that for all flows in sch_heap (SCH), S_i <= V,
+     * and for all flows in ne_heap (NEH), S_i > V.
+     * So when we need to compute max(V, min(S_i)) forall i in
+     * SCH+NEH, we only need to look into NEH.
+     */
+    if (DN_KEY_LT(si->V, alg_fq->S)) {
+        /* S>V means flow Not eligible. */
+        if (si->sch_heap.elements == 0)
+            D("++ ouch! not eligible but empty scheduler!");
+        heap_insert(&si->ne_heap, alg_fq->S, q);
+    } else {
+        heap_insert(&si->sch_heap, alg_fq->F, q);
+    }
+    return 0;
+}
+
+/* XXX invariant: sch > 0 || V >= min(S in neh) */
+static struct mbuf *
+wf2qp_dequeue(struct dn_sch_inst *_si)
+{
+       /* Access scheduler instance private data */
+       struct wf2qp_si *si = (struct wf2qp_si *)(_si + 1);
+       struct mbuf *m;
+       struct dn_queue *q;
+       struct dn_heap *sch = &si->sch_heap;
+       struct dn_heap *neh = &si->ne_heap;
+       struct wf2qp_queue *alg_fq;
+
+       if (sch->elements == 0 && neh->elements == 0) {
+               /* we have nothing to do. We could kill the idle heap
+                * altogether and reset V
+                */
+               idle_check(si, 0x7fffffff, 1);
+               si->V = 0;
+               si->wsum = 0;   /* should be set already */
+               return NULL;    /* quick return if nothing to do */
+       }
+       idle_check(si, 1, 0);   /* drain something from the idle heap */
+
+       /* make sure at least one element is eligible, bumping V
+        * and moving entries that have become eligible.
+        * We need to repeat the first part twice, before and
+        * after extracting the candidate, or enqueue() will
+        * find the data structure in a wrong state.
+        */
+  m = NULL;
+  for(;;) {
+       /*
+        * Compute V = max(V, min(S_i)). Remember that all elements
+        * in sch have by definition S_i <= V so if sch is not empty,
+        * V is surely the max and we must not update it. Conversely,
+        * if sch is empty we only need to look at neh.
+        * We don't need to move the queues, as it will be done at the
+        * next enqueue
+        */
+       if (sch->elements == 0 && neh->elements > 0) {
+               si->V = MAX64(si->V, HEAP_TOP(neh)->key);
+       }
+       while (neh->elements > 0 &&
+                   DN_KEY_LEQ(HEAP_TOP(neh)->key, si->V)) {
+               q = HEAP_TOP(neh)->object;
+               alg_fq = (struct wf2qp_queue *)q;
+               heap_extract(neh, NULL);
+               heap_insert(sch, alg_fq->F, q);
+       }
+       if (m) /* pkt found in previous iteration */
+               break;
+       /* ok we have at least one eligible pkt */
+       q = HEAP_TOP(sch)->object;
+       alg_fq = (struct wf2qp_queue *)q;
+       m = dn_dequeue(q);
+       heap_extract(sch, NULL); /* Remove queue from heap. */
+       si->V += (uint64_t)(m->m_pkthdr.len) * si->inv_wsum;
+       alg_fq->S = alg_fq->F;  /* Update start time. */
+       if (q->mq.head == 0) {  /* not backlogged any more. */
+               heap_insert(&si->idle_heap, alg_fq->F, q);
+       } else {                        /* Still backlogged. */
+               /* Update F, store in neh or sch */
+               uint64_t len = q->mq.head->m_pkthdr.len;
+               alg_fq->F += len * alg_fq->inv_w;
+               if (DN_KEY_LEQ(alg_fq->S, si->V)) {
+                       heap_insert(sch, alg_fq->F, q);
+               } else {
+                       heap_insert(neh, alg_fq->S, q);
+               }
+       }
+    }
+       return m;
+}
+
+static int
+wf2qp_new_sched(struct dn_sch_inst *_si)
+{
+       struct wf2qp_si *si = (struct wf2qp_si *)(_si + 1);
+       int ofs = offsetof(struct wf2qp_queue, heap_pos);
+
+       /* all heaps support extract from middle */
+       if (heap_init(&si->idle_heap, 16, ofs) ||
+           heap_init(&si->sch_heap, 16, ofs) ||
+           heap_init(&si->ne_heap, 16, ofs)) {
+               heap_free(&si->ne_heap);
+               heap_free(&si->sch_heap);
+               heap_free(&si->idle_heap);
+               return ENOMEM;
+       }
+       return 0;
+}
+
+static int
+wf2qp_free_sched(struct dn_sch_inst *_si)
+{
+       struct wf2qp_si *si = (struct wf2qp_si *)(_si + 1);
+
+       heap_free(&si->sch_heap);
+       heap_free(&si->ne_heap);
+       heap_free(&si->idle_heap);
+
+       return 0;
+}
+
+static int
+wf2qp_new_fsk(struct dn_fsk *fs)
+{
+       ipdn_bound_var(&fs->fs.par[0], 1,
+               1, 100, "WF2Q+ weight");
+       return 0;
+}
+
+static int
+wf2qp_new_queue(struct dn_queue *_q)
+{
+       struct wf2qp_queue *q = (struct wf2qp_queue *)_q;
+
+       _q->ni.oid.subtype = DN_SCHED_WF2QP;
+       q->F = 0;       /* not strictly necessary */
+       q->S = q->F + 1;    /* mark timestamp as invalid. */
+        q->inv_w = ONE_FP / _q->fs->fs.par[0];
+       if (_q->mq.head != NULL) {
+               wf2qp_enqueue(_q->_si, _q, _q->mq.head);
+       }
+       return 0;
+}
+
+/*
+ * Called when the infrastructure removes a queue (e.g. flowset
+ * is reconfigured). Nothing to do if we did not 'own' the queue,
+ * otherwise remove it from the right heap and adjust the sum
+ * of weights.
+ */
+static int
+wf2qp_free_queue(struct dn_queue *q)
+{
+       struct wf2qp_queue *alg_fq = (struct wf2qp_queue *)q;
+       struct wf2qp_si *si = (struct wf2qp_si *)(q->_si + 1);
+    
+       if (alg_fq->S >= alg_fq->F + 1)
+               return 0;       /* nothing to do, not in any heap */
+       si->wsum -= q->fs->fs.par[0];
+       if (si->wsum > 0)
+               si->inv_wsum = ONE_FP/si->wsum;
+
+       /* extract from the heap. XXX TODO we may need to adjust V
+        * to make sure the invariants hold.
+        */
+       if (q->mq.head == NULL) {
+               heap_extract(&si->idle_heap, q);
+       } else if (DN_KEY_LT(si->V, alg_fq->S)) {
+               heap_extract(&si->ne_heap, q);
+       } else {
+               heap_extract(&si->sch_heap, q);
+       }
+       return 0;
+}
+
+/*
+ * WF2Q+ scheduler descriptor
+ * contains the type of the scheduler, the name, the size of the
+ * structures and function pointers.
+ */
+static struct dn_alg wf2qp_desc = {
+       _SI( .type = ) DN_SCHED_WF2QP,
+       _SI( .name = ) "WF2Q+",
+       _SI( .flags = ) DN_MULTIQUEUE,
+
+       /* we need extra space in the si and the queue */
+       _SI( .schk_datalen = ) 0,
+       _SI( .si_datalen = ) sizeof(struct wf2qp_si),
+       _SI( .q_datalen = ) sizeof(struct wf2qp_queue) -
+                               sizeof(struct dn_queue),
+
+       _SI( .enqueue = ) wf2qp_enqueue,
+       _SI( .dequeue = ) wf2qp_dequeue,
+
+       _SI( .config = )  NULL,
+       _SI( .destroy = )  NULL,
+       _SI( .new_sched = ) wf2qp_new_sched,
+       _SI( .free_sched = ) wf2qp_free_sched,
+
+       _SI( .new_fsk = ) wf2qp_new_fsk,
+       _SI( .free_fsk = )  NULL,
+
+       _SI( .new_queue = ) wf2qp_new_queue,
+       _SI( .free_queue = ) wf2qp_free_queue,
+};
+
+
+DECLARE_DNSCHED_MODULE(dn_wf2qp, &wf2qp_desc);
index 45cb7ca..a69b844 100644 (file)
@@ -157,6 +157,7 @@ struct radix_node_head {
 
 void    rn_init(int);
 int     rn_inithead(void **, int);
+int     rn_detachhead(void **);
 int     rn_refines(void *, void *);
 struct radix_node
         *rn_addmask(void *, int, int),
index bdd8cf0..e88551f 100644 (file)
  */
 struct ip {
 #if BYTE_ORDER == LITTLE_ENDIAN
-        u_int   ip_hl:4,                /* header length */
+        u_char  ip_hl:4,                /* header length */
                 ip_v:4;                 /* version */
 #endif
 #if BYTE_ORDER == BIG_ENDIAN
-        u_int   ip_v:4,                 /* version */
+        u_char  ip_v:4,                 /* version */
                 ip_hl:4;                /* header length */
 #endif
         u_char  ip_tos;                 /* type of service */
index f01bfe2..6795d7c 100644 (file)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 1998-2002 Luigi Rizzo, Universita` di Pisa
+ * Copyright (c) 1998-2010 Luigi Rizzo, Universita` di Pisa
  * Portions Copyright (c) 2000 Akamba Corp.
  * All rights reserved
  *
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/netinet/ip_dummynet.h,v 1.40.2.1 2008/04/25 10:26:30 oleg Exp $
+ * $FreeBSD: user/luigi/ipfw3-head/sys/netinet/ip_dummynet.h 203321 2010-01-31 21:39:25Z luigi $
  */
 
 #ifndef _IP_DUMMYNET_H
 #define _IP_DUMMYNET_H
 
 /*
- * Definition of dummynet data structures. In the structures, I decided
- * not to use the macros in <sys/queue.h> in the hope of making the code
- * easier to port to other architectures. The type of lists and queue we
- * use here is pretty simple anyways.
- */
-
-/*
- * We start with a heap, which is used in the scheduler to decide when
- * to transmit packets etc.
+ * Definition of the kernel-userland API for dummynet.
  *
- * The key for the heap is used for two different values:
+ * Setsockopt() and getsockopt() pass a batch of objects, each
+ * of them starting with a "struct dn_id" which should fully identify
+ * the object and its relation with others in the sequence.
+ * The first object in each request should have
+ *      type= DN_CMD_*, id = DN_API_VERSION.
+ * For other objects, type and subtype specify the object, len indicates
+ * the total length including the header, and 'id' identifies the specific
+ * object.
  *
- * 1. timer ticks- max 10K/second, so 32 bits are enough;
- *
- * 2. virtual times. These increase in steps of len/x, where len is the
- *    packet length, and x is either the weight of the flow, or the
- *    sum of all weights.
- *    If we limit to max 1000 flows and a max weight of 100, then
- *    x needs 17 bits. The packet size is 16 bits, so we can easily
- *    overflow if we do not allow errors.
- * So we use a key "dn_key" which is 64 bits. Some macros are used to
- * compare key values and handle wraparounds.
- * MAX64 returns the largest of two key values.
- * MY_M is used as a shift count when doing fixed point arithmetic
- * (a better name would be useful...).
- */
-typedef u_int64_t dn_key ;      /* sorting key */
-#define DN_KEY_LT(a,b)     ((int64_t)((a)-(b)) < 0)
-#define DN_KEY_LEQ(a,b)    ((int64_t)((a)-(b)) <= 0)
-#define DN_KEY_GT(a,b)     ((int64_t)((a)-(b)) > 0)
-#define DN_KEY_GEQ(a,b)    ((int64_t)((a)-(b)) >= 0)
-#define MAX64(x,y)  (( (int64_t) ( (y)-(x) )) > 0 ) ? (y) : (x)
-#define MY_M   16 /* number of left shift to obtain a larger precision */
-
-/*
- * XXX With this scaling, max 1000 flows, max weight 100, 1Gbit/s, the
- * virtual time wraps every 15 days.
+ * Most objects are numbered with an identifier in the range 1..65535.
+ * DN_MAX_ID indicates the first value outside the range.
  */
 
+#define        DN_API_VERSION  12500000
+#define        DN_MAX_ID       0x10000
 
-/*
- * The maximum hash table size for queues.  This value must be a power
- * of 2.
- */
-#define DN_MAX_HASH_SIZE 65536
+struct dn_id {
+       uint16_t        len;    /* total obj len including this header */
+       uint8_t         type;
+       uint8_t         subtype;
+       uint32_t        id;     /* generic id */
+};
 
 /*
- * A heap entry is made of a key and a pointer to the actual
- * object stored in the heap.
- * The heap is an array of dn_heap_entry entries, dynamically allocated.
- * Current size is "size", with "elements" actually in use.
- * The heap normally supports only ordered insert and extract from the top.
- * If we want to extract an object from the middle of the heap, we
- * have to know where the object itself is located in the heap (or we
- * need to scan the whole array). To this purpose, an object has a
- * field (int) which contains the index of the object itself into the
- * heap. When the object is moved, the field must also be updated.
- * The offset of the index in the object is stored in the 'offset'
- * field in the heap descriptor. The assumption is that this offset
- * is non-zero if we want to support extract from the middle.
- */
-struct dn_heap_entry {
-    dn_key key ;       /* sorting key. Topmost element is smallest one */
-    void *object ;     /* object pointer */
-} ;
-
-struct dn_heap {
-    int size ;
-    int elements ;
-    int offset ; /* XXX if > 0 this is the offset of direct ptr to obj */
-    struct dn_heap_entry *p ;  /* really an array of "size" entries */
-} ;
-
-#ifdef _KERNEL
-/*
- * Packets processed by dummynet have an mbuf tag associated with
- * them that carries their dummynet state.  This is used within
- * the dummynet code as well as outside when checking for special
- * processing requirements.
- * Note that the first part is the reinject info and is common to
- * other forms of packet reinjection.
+ * These values are in the type field of struct dn_id.
+ * To preserve the ABI, never rearrange the list or delete
+ * entries with the exception of DN_LAST
  */
-struct dn_pkt_tag {
-       struct ipfw_rule_ref rule;      /* matching rule */
-
-    /* second part, dummynet specific */
-    int dn_dir;                        /* action when packet comes out. */
-                               /* see ip_fw_private.h */
+enum {
+       DN_NONE = 0,
+       DN_LINK = 1,
+       DN_FS,
+       DN_SCH,
+       DN_SCH_I,
+       DN_QUEUE,
+       DN_DELAY_LINE,
+       DN_PROFILE,
+       DN_FLOW,                /* struct dn_flow */
+       DN_TEXT,                /* opaque text is the object */
+
+       DN_CMD_CONFIG = 0x80,   /* objects follow */
+       DN_CMD_DELETE,          /* subtype + list of entries */
+       DN_CMD_GET,             /* subtype + list of entries */
+       DN_CMD_FLUSH,
+       /* for compatibility with FreeBSD 7.2/8 */
+       DN_COMPAT_PIPE,
+       DN_COMPAT_QUEUE,
+       DN_GET_COMPAT,
+
+       /* special commands for emulation of sysctl variables */
+       DN_SYSCTL_GET,
+       DN_SYSCTL_SET,
+
+       DN_LAST,
+};
+enum { /* subtype for schedulers, flowset and the like */
+       DN_SCHED_UNKNOWN = 0,
+       DN_SCHED_FIFO = 1,
+       DN_SCHED_WF2QP = 2,
+       /* others are in individual modules */
+};
 
-    dn_key output_time;                /* when the pkt is due for delivery     */
-    struct ifnet *ifp;         /* interface, for ip_output             */
-    struct _ip6dn_args ip6opt; /* XXX ipv6 options                     */
+enum { /* user flags */
+       DN_HAVE_MASK    = 0x0001,       /* fs or sched has a mask */
+       DN_NOERROR      = 0x0002,       /* do not report errors */
+       DN_QHT_HASH     = 0x0004,       /* qht is a hash table */
+       DN_QSIZE_BYTES  = 0x0008,       /* queue size is in bytes */
+       DN_HAS_PROFILE  = 0x0010,       /* a link has a profile */
+       DN_IS_RED       = 0x0020,
+       DN_IS_GENTLE_RED= 0x0040,
+       DN_PIPE_CMD     = 0x1000,       /* pipe config... */
 };
-#endif /* _KERNEL */
 
 /*
- * Overall structure of dummynet (with WF2Q+):
-
-In dummynet, packets are selected with the firewall rules, and passed
-to two different objects: PIPE or QUEUE.
-
-A QUEUE is just a queue with configurable size and queue management
-policy. It is also associated with a mask (to discriminate among
-different flows), a weight (used to give different shares of the
-bandwidth to different flows) and a "pipe", which essentially
-supplies the transmit clock for all queues associated with that
-pipe.
-
-A PIPE emulates a fixed-bandwidth link, whose bandwidth is
-configurable.  The "clock" for a pipe can come from either an
-internal timer, or from the transmit interrupt of an interface.
-A pipe is also associated with one (or more, if masks are used)
-queue, where all packets for that pipe are stored.
-
-The bandwidth available on the pipe is shared by the queues
-associated with that pipe (only one in case the packet is sent
-to a PIPE) according to the WF2Q+ scheduling algorithm and the
-configured weights.
-
-In general, incoming packets are stored in the appropriate queue,
-which is then placed into one of a few heaps managed by a scheduler
-to decide when the packet should be extracted.
-The scheduler (a function called dummynet()) is run at every timer
-tick, and grabs queues from the head of the heaps when they are
-ready for processing.
-
-There are three data structures definining a pipe and associated queues:
-
- + dn_pipe, which contains the main configuration parameters related
-   to delay and bandwidth;
- + dn_flow_set, which contains WF2Q+ configuration, flow
-   masks, plr and RED configuration;
- + dn_flow_queue, which is the per-flow queue (containing the packets)
-
-Multiple dn_flow_set can be linked to the same pipe, and multiple
-dn_flow_queue can be linked to the same dn_flow_set.
-All data structures are linked in a linear list which is used for
-housekeeping purposes.
-
-During configuration, we create and initialize the dn_flow_set
-and dn_pipe structures (a dn_pipe also contains a dn_flow_set).
-
-At runtime: packets are sent to the appropriate dn_flow_set (either
-WFQ ones, or the one embedded in the dn_pipe for fixed-rate flows),
-which in turn dispatches them to the appropriate dn_flow_queue
-(created dynamically according to the masks).
-
-The transmit clock for fixed rate flows (ready_event()) selects the
-dn_flow_queue to be used to transmit the next packet. For WF2Q,
-wfq_ready_event() extract a pipe which in turn selects the right
-flow using a number of heaps defined into the pipe itself.
-
- *
+ * link template.
  */
+struct dn_link {
+       struct dn_id oid;
+
+       /*
+        * Userland sets bw and delay in bits/s and milliseconds.
+        * The kernel converts this back and forth to bits/tick and ticks.
+        * XXX what about burst ?
+        */
+       int32_t         link_nr;
+       int             bandwidth;      /* bit/s or bits/tick.   */
+       int             delay;          /* ms and ticks */
+       uint64_t        burst;          /* scaled. bits*Hz  XXX */
+};
 
 /*
- * per flow queue. This contains the flow identifier, the queue
- * of packets, counters, and parameters used to support both RED and
- * WF2Q+.
- *
- * A dn_flow_queue is created and initialized whenever a packet for
- * a new flow arrives.
+ * A flowset, which is a template for flows. Contains parameters
+ * from the command line: id, target scheduler, queue sizes, plr,
+ * flow masks, buckets for the flow hash, and possibly scheduler-
+ * specific parameters (weight, quantum and so on).
  */
-struct dn_flow_queue {
-    struct dn_flow_queue *next ;
-    struct ipfw_flow_id id ;
-
-    struct mbuf *head, *tail ; /* queue of packets */
-    u_int len ;
-    u_int len_bytes ;
-
-    /*
-     * When we emulate MAC overheads, or channel unavailability due
-     * to other traffic on a shared medium, we augment the packet at
-     * the head of the queue with an 'extra_bits' field representsing
-     * the additional delay the packet will be subject to:
-     *         extra_bits = bw*unavailable_time.
-     * With large bandwidth and large delays, extra_bits (and also numbytes)
-     * can become very large, so better play safe and use 64 bit
-     */
-    uint64_t numbytes ;                /* credit for transmission (dynamic queues) */
-    int64_t extra_bits;                /* extra bits simulating unavailable channel */
-
-    u_int64_t tot_pkts ;       /* statistics counters  */
-    u_int64_t tot_bytes ;
-    u_int32_t drops ;
-
-    int hash_slot ;            /* debugging/diagnostic */
-
-    /* RED parameters */
-    int avg ;                   /* average queue length est. (scaled) */
-    int count ;                 /* arrivals since last RED drop */
-    int random ;                /* random value (scaled) */
-    dn_key idle_time;          /* start of queue idle time */
-
-    /* WF2Q+ support */
-    struct dn_flow_set *fs ;   /* parent flow set */
-    int heap_pos ;             /* position (index) of struct in heap */
-    dn_key sched_time ;                /* current time when queue enters ready_heap */
-
-    dn_key S,F ;               /* start time, finish time */
-    /*
-     * Setting F < S means the timestamp is invalid. We only need
-     * to test this when the queue is empty.
-     */
-} ;
+struct dn_fs {
+       struct dn_id oid;
+       uint32_t fs_nr; /* the flowset number */
+       uint32_t flags; /* userland flags */
+       int qsize;      /* queue size in slots or bytes */
+       int32_t plr;    /* PLR, pkt loss rate (2^31-1 means 100%) */
+       uint32_t buckets;       /* buckets used for the queue hash table */
+
+       struct ipfw_flow_id flow_mask;
+       uint32_t sched_nr;      /* the scheduler we attach to */
+       /* generic scheduler parameters. Leave them at -1 if unset.
+        * Now we use 0: weight, 1: lmax, 2: priority
+        */
+       int par[4];
+
+       /* RED/GRED parameters.
+        * weight and probabilities are in the range 0..1 represented
+        * in fixed point arithmetic with SCALE_RED decimal bits.
+        */
+#define SCALE_RED              16
+#define SCALE(x)               ( (x) << SCALE_RED )
+#define SCALE_VAL(x)           ( (x) >> SCALE_RED )
+#define SCALE_MUL(x,y)         ( ( (x) * (y) ) >> SCALE_RED )
+       int w_q ;               /* queue weight (scaled) */
+       int max_th ;            /* maximum threshold for queue (scaled) */
+       int min_th ;            /* minimum threshold for queue (scaled) */
+       int max_p ;             /* maximum value for p_b (scaled) */
 
-/*
- * flow_set descriptor. Contains the "template" parameters for the
- * queue configuration, and pointers to the hash table of dn_flow_queue's.
- *
- * The hash table is an array of lists -- we identify the slot by
- * hashing the flow-id, then scan the list looking for a match.
- * The size of the hash table (buckets) is configurable on a per-queue
- * basis.
- *
- * A dn_flow_set is created whenever a new queue or pipe is created (in the
- * latter case, the structure is located inside the struct dn_pipe).
- */
-struct dn_flow_set {
-    SLIST_ENTRY(dn_flow_set)   next;   /* linked list in a hash slot */
-
-    u_short fs_nr ;             /* flow_set number       */
-    u_short flags_fs;
-#define DN_HAVE_FLOW_MASK      0x0001
-#define DN_IS_RED              0x0002
-#define DN_IS_GENTLE_RED       0x0004
-#define DN_QSIZE_IS_BYTES      0x0008  /* queue size is measured in bytes */
-#define DN_NOERROR             0x0010  /* do not report ENOBUFS on drops  */
-#define        DN_HAS_PROFILE          0x0020  /* the pipe has a delay profile. */
-#define DN_IS_PIPE             0x4000
-#define DN_IS_QUEUE            0x8000
-
-    struct dn_pipe *pipe ;     /* pointer to parent pipe */
-    u_short parent_nr ;                /* parent pipe#, 0 if local to a pipe */
-
-    int weight ;               /* WFQ queue weight */
-    int qsize ;                        /* queue size in slots or bytes */
-    int plr ;                  /* pkt loss rate (2^31-1 means 100%) */
-
-    struct ipfw_flow_id flow_mask ;
-
-    /* hash table of queues onto this flow_set */
-    int rq_size ;              /* number of slots */
-    int rq_elements ;          /* active elements */
-    struct dn_flow_queue **rq; /* array of rq_size entries */
-
-    u_int32_t last_expired ;   /* do not expire too frequently */
-    int backlogged ;           /* #active queues for this flowset */
-
-        /* RED parameters */
-#define SCALE_RED               16
-#define SCALE(x)                ( (x) << SCALE_RED )
-#define SCALE_VAL(x)            ( (x) >> SCALE_RED )
-#define SCALE_MUL(x,y)          ( ( (x) * (y) ) >> SCALE_RED )
-    int w_q ;                  /* queue weight (scaled) */
-    int max_th ;               /* maximum threshold for queue (scaled) */
-    int min_th ;               /* minimum threshold for queue (scaled) */
-    int max_p ;                        /* maximum value for p_b (scaled) */
-    u_int c_1 ;                        /* max_p/(max_th-min_th) (scaled) */
-    u_int c_2 ;                        /* max_p*min_th/(max_th-min_th) (scaled) */
-    u_int c_3 ;                        /* for GRED, (1-max_p)/max_th (scaled) */
-    u_int c_4 ;                        /* for GRED, 1 - 2*max_p (scaled) */
-    u_int * w_q_lookup ;       /* lookup table for computing (1-w_q)^t */
-    u_int lookup_depth ;       /* depth of lookup table */
-    int lookup_step ;          /* granularity inside the lookup table */
-    int lookup_weight ;                /* equal to (1-w_q)^t / (1-w_q)^(t+1) */
-    int avg_pkt_size ;         /* medium packet size */
-    int max_pkt_size ;         /* max packet size */
 };
-SLIST_HEAD(dn_flow_set_head, dn_flow_set);
 
 /*
- * Pipe descriptor. Contains global parameters, delay-line queue,
- * and the flow_set used for fixed-rate queues.
- *
- * For WF2Q+ support it also has 3 heaps holding dn_flow_queue:
- *   not_eligible_heap, for queues whose start time is higher
- *     than the virtual time. Sorted by start time.
- *   scheduler_heap, for queues eligible for scheduling. Sorted by
- *     finish time.
- *   idle_heap, all flows that are idle and can be removed. We
- *     do that on each tick so we do not slow down too much
- *     operations during forwarding.
- *
+ * dn_flow collects flow_id and stats for queues and scheduler
+ * instances, and is used to pass these info to userland.
+ * oid.type/oid.subtype describe the object, oid.id is number
+ * of the parent object.
  */
-struct dn_pipe {               /* a pipe */
-    SLIST_ENTRY(dn_pipe)       next;   /* linked list in a hash slot */
-
-    int        pipe_nr ;               /* number       */
-    int bandwidth;             /* really, bytes/tick.  */
-    int        delay ;                 /* really, ticks        */
-
-    struct     mbuf *head, *tail ;     /* packets in delay line */
+struct dn_flow {
+       struct dn_id    oid;
+       struct ipfw_flow_id fid;
+       uint64_t        tot_pkts; /* statistics counters  */
+       uint64_t        tot_bytes;
+       uint32_t        length; /* Queue lenght, in packets */
+       uint32_t        len_bytes; /* Queue lenght, in bytes */
+       uint32_t        drops;
+};
 
-    /* WF2Q+ */
-    struct dn_heap scheduler_heap ; /* top extract - key Finish time*/
-    struct dn_heap not_eligible_heap; /* top extract- key Start time */
-    struct dn_heap idle_heap ; /* random extract - key Start=Finish time */
 
-    dn_key V ;                 /* virtual time */
-    int sum;                   /* sum of weights of all active sessions */
+/*
+ * Scheduler template, mostly indicating the name, number,
+ * sched_mask and buckets.
+ */
+struct dn_sch {
+       struct dn_id    oid;
+       uint32_t        sched_nr; /* N, scheduler number */
+       uint32_t        buckets; /* number of buckets for the instances */
+       uint32_t        flags;  /* have_mask, ... */
+
+       char name[16];  /* null terminated */
+       /* mask to select the appropriate scheduler instance */
+       struct ipfw_flow_id sched_mask; /* M */
+};
 
-    /* Same as in dn_flow_queue, numbytes can become large */
-    int64_t numbytes;          /* bits I can transmit (more or less). */
-    uint64_t burst;            /* burst size, scaled: bits * hz */
 
-    dn_key sched_time ;                /* time pipe was scheduled in ready_heap */
-    dn_key idle_time;          /* start of pipe idle time */
+/* A delay profile is attached to a link.
+ * Note that a profile, as any other object, cannot be longer than 2^16
+ */
+#define        ED_MAX_SAMPLES_NO       1024
+struct dn_profile {
+       struct dn_id    oid;
+       /* fields to simulate a delay profile */
+#define ED_MAX_NAME_LEN         32
+       char            name[ED_MAX_NAME_LEN];
+       int             link_nr;
+       int             loss_level;
+       int             bandwidth;      // XXX use link bandwidth?
+       int             samples_no;     /* actual length of samples[] */
+       int samples[ED_MAX_SAMPLES_NO]; /* may be shorter */
+};
 
-    /*
-     * When the tx clock come from an interface (if_name[0] != '\0'), its name
-     * is stored below, whereas the ifp is filled when the rule is configured.
-     */
-    char if_name[IFNAMSIZ];
-    struct ifnet *ifp ;
-    int ready ; /* set if ifp != NULL and we got a signal from it */
 
-    struct dn_flow_set fs ; /* used with fixed-rate flows */
 
-    /* fields to simulate a delay profile */
+/*
+ * Overall structure of dummynet
 
-#define ED_MAX_NAME_LEN                32
-    char name[ED_MAX_NAME_LEN];
-    int loss_level;
-    int samples_no;
-    int *samples;
-};
+In dummynet, packets are selected with the firewall rules, and passed
+to two different objects: PIPE or QUEUE (bad name).
+
+A QUEUE defines a classifier, which groups packets into flows
+according to a 'mask', puts them into independent queues (one
+per flow) with configurable size and queue management policy,
+and passes flows to a scheduler:
+
+                 (flow_mask|sched_mask)  sched_mask
+        +---------+   weight Wx  +-------------+
+         |         |->-[flow]-->--|             |-+
+    -->--| QUEUE x |   ...        |             | |
+         |         |->-[flow]-->--| SCHEDuler N | |
+        +---------+              |             | |
+            ...                  |             +--[LINK N]-->--
+        +---------+   weight Wy  |             | +--[LINK N]-->--
+         |         |->-[flow]-->--|             | |
+    -->--| QUEUE y |   ...        |             | |
+         |         |->-[flow]-->--|             | |
+        +---------+              +-------------+ |
+                                   +-------------+
+
+Many QUEUE objects can connect to the same scheduler, each
+QUEUE object can have its own set of parameters.
+
+In turn, the SCHEDuler 'forks' multiple instances according
+to a 'sched_mask', each instance manages its own set of queues
+and transmits on a private instance of a configurable LINK.
+
+A PIPE is a simplified version of the above, where there
+is no flow_mask, and each scheduler instance handles a single queue.
+
+The following data structures (visible from userland) describe
+the objects used by dummynet:
+
+ + dn_link, contains the main configuration parameters related
+   to delay and bandwidth;
+ + dn_profile describes a delay profile;
+ + dn_flow describes the flow status (flow id, statistics)
+   
+ + dn_sch describes a scheduler
+ + dn_fs describes a flowset (msk, weight, queue parameters)
 
-/* dn_pipe_max is used to pass pipe configuration from userland onto
- * kernel space and back
+ *
  */
-#define ED_MAX_SAMPLES_NO      1024
-struct dn_pipe_max {
-       struct dn_pipe pipe;
-       int samples[ED_MAX_SAMPLES_NO];
-};
-
-SLIST_HEAD(dn_pipe_head, dn_pipe);
 
 #endif /* _IP_DUMMYNET_H */
index 238601f..d037b45 100644 (file)
@@ -22,7 +22,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: head/sys/netinet/ip_fw.h 200580 2009-12-15 16:15:14Z luigi $
+ * $FreeBSD: user/luigi/ipfw3-head/sys/netinet/ip_fw.h 202072 2010-01-11 10:12:35Z luigi $
  */
 
 #ifndef _IPFW2_H
@@ -461,7 +461,13 @@ typedef struct _ipfw_insn_icmp6 {
  */
 
 struct ip_fw {
+#ifdef _X64EMU
+               int32_t pad1;
+#endif
        struct ip_fw    *x_next;        /* linked list of rules         */
+#ifdef _X64EMU
+               int32_t pad2;
+#endif
        struct ip_fw    *next_rule;     /* ptr to next [skipto] rule    */
        /* 'next_rule' is used to pass up 'set_disable' status          */
 
@@ -487,24 +493,29 @@ struct ip_fw {
 #define RULESIZE(rule)  (sizeof(struct ip_fw) + \
        ((struct ip_fw *)(rule))->cmd_len * 4 - 4)
 
+#if 1 // should be moved to in.h
 /*
  * This structure is used as a flow mask and a flow id for various
  * parts of the code.
+ * addr_type is used in userland and kernel to mark the address type.
+ * fib is used in the kernel to record the fib in use.
+ * _flags is used in the kernel to store tcp flags for dynamic rules.
  */
 struct ipfw_flow_id {
-       u_int32_t       dst_ip;
-       u_int32_t       src_ip;
-       u_int16_t       dst_port;
-       u_int16_t       src_port;
-       u_int8_t        fib;
-       u_int8_t        proto;
-       u_int8_t        flags;  /* protocol-specific flags */
-       uint8_t         addr_type; /* 4 = ipv4, 6 = ipv6, 1=ether ? */
-       struct in6_addr dst_ip6;        /* could also store MAC addr! */
+       uint32_t        dst_ip;
+       uint32_t        src_ip;
+       uint16_t        dst_port;
+       uint16_t        src_port;
+       uint8_t fib;
+       uint8_t proto;
+       uint8_t         _flags; /* protocol-specific flags */
+       uint8_t         addr_type; /* 4=ip4, 6=ip6, 1=ether ? */
+       struct in6_addr dst_ip6;
        struct in6_addr src_ip6;
-       u_int32_t       flow_id6;
-       u_int32_t       frag_id6;
+       uint32_t        flow_id6;
+       uint32_t        extra; /* queue/pipe or frag_id */
 };
+#endif
 
 #define IS_IP6_FLOW_ID(id)     ((id)->addr_type == 6)
 
diff --git a/dummynet2/include/netinet/ipfw/dn_heap.h b/dummynet2/include/netinet/ipfw/dn_heap.h
new file mode 100644 (file)
index 0000000..09b2ac7
--- /dev/null
@@ -0,0 +1,191 @@
+/*-
+ * Copyright (c) 1998-2010 Luigi Rizzo, Universita` di Pisa
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Binary heap and hash tables, header file
+ *
+ * $FreeBSD: head/sys/netinet/ipfw/dn_heap.h 204865 2010-03-08 11:27:08Z luigi $
+ */
+
+#ifndef _IP_DN_HEAP_H
+#define _IP_DN_HEAP_H
+
+#define DN_KEY_LT(a,b)     ((int64_t)((a)-(b)) < 0)
+#define DN_KEY_LEQ(a,b)    ((int64_t)((a)-(b)) <= 0)
+
+/*
+ * This module implements a binary heap supporting random extraction.
+ *
+ * A heap entry contains an uint64_t key and a pointer to object.
+ * DN_KEY_LT(a,b) returns true if key 'a' is smaller than 'b'
+ *
+ * The heap is a struct dn_heap plus a dynamically allocated
+ * array of dn_heap_entry entries. 'size' represents the size of
+ * the array, 'elements' count entries in use. The topmost
+ * element has the smallest key.
+ * The heap supports ordered insert, and extract from the top.
+ * To extract an object from the middle of the heap, we the object
+ * must reserve an 'int32_t' to store the position of the object
+ * in the heap itself, and the location of this field must be
+ * passed as an argument to heap_init() -- use -1 if the feature
+ * is not used.
+ */
+struct dn_heap_entry {
+       uint64_t key;   /* sorting key, smallest comes first */
+       void *object;   /* object pointer */
+};
+
+struct dn_heap {
+       int size;       /* the size of the array */
+       int elements;   /* elements in use */
+       int ofs;        /* offset in the object of heap index */
+       struct dn_heap_entry *p;        /* array of "size" entries */
+};
+
+enum {
+       HEAP_SCAN_DEL = 1,
+       HEAP_SCAN_END = 2,
+};
+
+/*
+ * heap_init() reinitializes the heap setting the size and the offset
+ *     of the index for random extraction (use -1 if not used).
+ *     The 'elements' counter is set to 0.
+ *
+ * SET_HEAP_OFS() indicates where, in the object, is stored the index
+ *     for random extractions from the heap.
+ *
+ * heap_free() frees the memory associated to a heap.
+ *
+ * heap_insert() adds a key-pointer pair to the heap
+ *
+ * HEAP_TOP() returns a pointer to the top element of the heap,
+ *     but makes no checks on its existance (XXX should we change ?)
+ *
+ * heap_extract() removes the entry at the top, returing the pointer.
+ *     (the key should have been read before).
+ *
+ * heap_scan() invokes a callback on each entry of the heap.
+ *     The callback can return a combination of HEAP_SCAN_DEL and
+ *     HEAP_SCAN_END. HEAP_SCAN_DEL means the current element must
+ *     be removed, and HEAP_SCAN_END means to terminate the scan.
+ *     heap_scan() returns the number of elements removed.
+ *     Because the order is not guaranteed, we should use heap_scan()
+ *     only as a last resort mechanism.
+ */
+#define HEAP_TOP(h)    ((h)->p)
+#define SET_HEAP_OFS(h, n)     do { (h)->ofs = n; } while (0)
+int     heap_init(struct dn_heap *h, int size, int ofs);
+int     heap_insert(struct dn_heap *h, uint64_t key1, void *p);
+void    heap_extract(struct dn_heap *h, void *obj);
+void heap_free(struct dn_heap *h);
+int heap_scan(struct dn_heap *, int (*)(void *, uintptr_t), uintptr_t);
+
+/*------------------------------------------------------
+ * This module implements a generic hash table with support for
+ * running callbacks on the entire table. To avoid allocating
+ * memory during hash table operations, objects must reserve
+ * space for a link field. XXX if the heap is moderately full,
+ * an SLIST suffices, and we can tolerate the cost of a hash
+ * computation on each removal.
+ *
+ * dn_ht_init() initializes the table, setting the number of
+ *     buckets, the offset of the link field, the main callbacks.
+ *     Callbacks are:
+ * 
+ *     hash(key, flags, arg) called to return a bucket index.
+ *     match(obj, key, flags, arg) called to determine if key
+ *             matches the current 'obj' in the heap
+ *     newh(key, flags, arg) optional, used to allocate a new
+ *             object during insertions.
+ *
+ * dn_ht_free() frees the heap or unlink elements.
+ *     DNHT_REMOVE unlink elements, 0 frees the heap.
+ *     You need two calls to do both.
+ *
+ * dn_ht_find() is the main lookup function, which can also be
+ *     used to insert or delete elements in the hash table.
+ *     The final 'arg' is passed to all callbacks.
+ *
+ * dn_ht_scan() is used to invoke a callback on all entries of
+ *     the heap, or possibly on just one bucket. The callback
+ *     is invoked with a pointer to the object, and must return
+ *     one of DNHT_SCAN_DEL or DNHT_SCAN_END to request the
+ *     removal of the object from the heap and the end of the
+ *     scan, respectively.
+ *
+ * dn_ht_scan_bucket() is similar to dn_ht_scan(), except that it scans
+ *     only the specific bucket of the table. The bucket is a in-out
+ *     parameter and return a valid bucket number if the original
+ *     is invalid.
+ *
+ * A combination of flags can be used to modify the operation
+ * of the dn_ht_find(), and of the callbacks:
+ *
+ * DNHT_KEY_IS_OBJ     means the key is the object pointer.
+ *     It is usally of interest for the hash and match functions.
+ *
+ * DNHT_MATCH_PTR      during a lookup, match pointers instead
+ *     of calling match(). Normally used when removing specific
+ *     entries. Does not imply KEY_IS_OBJ as the latter _is_ used
+ *     by the match function.
+ *
+ * DNHT_INSERT         insert the element if not found.
+ *     Calls new() to allocates a new object unless
+ *     DNHT_KEY_IS_OBJ is set.
+ *
+ * DNHT_UNIQUE         only insert if object not found.
+ *     XXX should it imply DNHT_INSERT ?
+ *
+ * DNHT_REMOVE         remove objects if we find them.
+ */
+struct dn_ht;  /* should be opaque */
+
+struct dn_ht *dn_ht_init(struct dn_ht *, int buckets, int ofs, 
+        uint32_t (*hash)(uintptr_t, int, void *),
+        int (*match)(void *, uintptr_t, int, void *),
+        void *(*newh)(uintptr_t, int, void *));
+void dn_ht_free(struct dn_ht *, int flags);
+
+void *dn_ht_find(struct dn_ht *, uintptr_t, int, void *);
+int dn_ht_scan(struct dn_ht *, int (*)(void *, void *), void *);
+int dn_ht_scan_bucket(struct dn_ht *, int * , int (*)(void *, void *), void *);
+int dn_ht_entries(struct dn_ht *);
+
+enum {  /* flags values.
+        * first two are returned by the scan callback to indicate
+        * to delete the matching element or to end the scan
+        */
+        DNHT_SCAN_DEL  = 0x0001,
+        DNHT_SCAN_END  = 0x0002,
+        DNHT_KEY_IS_OBJ        = 0x0004,       /* key is the obj pointer */
+        DNHT_MATCH_PTR = 0x0008,       /* match by pointer, not match() */
+        DNHT_INSERT    = 0x0010,       /* insert if not found */
+        DNHT_UNIQUE    = 0x0020,       /* report error if already there */
+        DNHT_REMOVE    = 0x0040,       /* remove on find or dn_ht_free */
+}; 
+
+#endif /* _IP_DN_HEAP_H */
diff --git a/dummynet2/include/netinet/ipfw/dn_sched.h b/dummynet2/include/netinet/ipfw/dn_sched.h
new file mode 100644 (file)
index 0000000..3c75b64
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2010 Riccardo Panicucci, Luigi Rizzo, Universita` di Pisa
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * The API to write a packet scheduling algorithm for dummynet.
+ *
+ * $FreeBSD: head/sys/netinet/ipfw/dn_sched.h 204591 2010-03-02 17:40:48Z luigi $
+ */
+
+#ifndef _DN_SCHED_H
+#define _DN_SCHED_H
+
+#define        DN_MULTIQUEUE   0x01
+/*
+ * Descriptor for a scheduling algorithm.
+ * Contains all function pointers for a given scheduler
+ * This is typically created when a module is loaded, and stored
+ * in a global list of schedulers.
+ */
+struct dn_alg {
+       uint32_t type;           /* the scheduler type */
+       const char *name;   /* scheduler name */
+       uint32_t flags; /* DN_MULTIQUEUE if supports multiple queues */
+
+       /*
+        * The following define the size of 3 optional data structures
+        * that may need to be allocated at runtime, and are appended
+        * to each of the base data structures: scheduler, sched.inst,
+        * and queue. We don't have a per-flowset structure.
+        */
+       /*    + parameters attached to the template, e.g.
+        *      default queue sizes, weights, quantum size, and so on;
+        */
+       size_t schk_datalen;
+
+       /*    + per-instance parameters, such as timestamps,
+        *      containers for queues, etc;
+        */
+       size_t si_datalen;
+
+       size_t q_datalen;       /* per-queue parameters (e.g. S,F) */
+
+       /*
+        * Methods implemented by the scheduler:
+        * enqueue      enqueue packet 'm' on scheduler 's', queue 'q'.
+        *      q is NULL for !MULTIQUEUE.
+        *      Return 0 on success, 1 on drop (packet consumed anyways).
+        *      Note that q should be interpreted only as a hint
+        *      on the flow that the mbuf belongs to: while a
+        *      scheduler will normally enqueue m into q, it is ok
+        *      to leave q alone and put the mbuf elsewhere.
+        *      This function is called in two cases:
+        *       - when a new packet arrives to the scheduler;
+        *       - when a scheduler is reconfigured. In this case the
+        *         call is issued by the new_queue callback, with a 
+        *         non empty queue (q) and m pointing to the first
+        *         mbuf in the queue. For this reason, the function
+        *         should internally check for (m != q->mq.head)
+        *         before calling dn_enqueue().
+        *
+        * dequeue      Called when scheduler instance 's' can
+        *      dequeue a packet. Return NULL if none are available.
+        *      XXX what about non work-conserving ?
+        *
+        * config       called on 'sched X config ...', normally writes
+        *      in the area of size sch_arg
+        *
+        * destroy      called on 'sched delete', frees everything
+        *      in sch_arg (other parts are handled by more specific
+        *      functions)
+        *
+        * new_sched    called when a new instance is created, e.g.
+        *      to create the local queue for !MULTIQUEUE, set V or
+        *      copy parameters for WFQ, and so on.
+        *
+        * free_sched   called when deleting an instance, cleans
+        *      extra data in the per-instance area.
+        *
+        * new_fsk      called when a flowset is linked to a scheduler,
+        *      e.g. to validate parameters such as weights etc.
+        * free_fsk     when a flowset is unlinked from a scheduler.
+        *      (probably unnecessary)
+        *
+        * new_queue    called to set the per-queue parameters,
+        *      e.g. S and F, adjust sum of weights in the parent, etc.
+        *
+        *      The new_queue callback is normally called from when
+        *      creating a new queue. In some cases (such as a
+        *      scheduler change or reconfiguration) it can be called
+        *      with a non empty queue. In this case, the queue
+        *      In case of non empty queue, the new_queue callback could
+        *      need to call the enqueue function. In this case,
+        *      the callback should eventually call enqueue() passing
+        *      as m the first element in the queue.
+        *
+        * free_queue   actions related to a queue removal, e.g. undo
+        *      all the above. If the queue has data in it, also remove
+        *      from the scheduler. This can e.g. happen during a reconfigure.
+        */
+       int (*enqueue)(struct dn_sch_inst *, struct dn_queue *,
+               struct mbuf *);
+       struct mbuf * (*dequeue)(struct dn_sch_inst *);
+
+       int (*config)(struct dn_schk *);
+       int (*destroy)(struct dn_schk*);
+       int (*new_sched)(struct dn_sch_inst *);
+       int (*free_sched)(struct dn_sch_inst *);
+       int (*new_fsk)(struct dn_fsk *f);
+       int (*free_fsk)(struct dn_fsk *f);
+       int (*new_queue)(struct dn_queue *q);
+       int (*free_queue)(struct dn_queue *q);
+
+       /* run-time fields */
+       int ref_count;      /* XXX number of instances in the system */
+       SLIST_ENTRY(dn_alg) next; /* Next scheduler in the list */
+};
+
+/* MSVC does not support initializers so we need this ugly macro */
+#ifdef _WIN32
+#define _SI(fld)
+#else
+#define _SI(fld)       fld
+#endif
+
+/*
+ * Additionally, dummynet exports some functions and macros
+ * to be used by schedulers:
+ */
+
+void dn_free_pkts(struct mbuf *mnext);
+int dn_enqueue(struct dn_queue *q, struct mbuf* m, int drop);
+/* bound a variable between min and max */
+int ipdn_bound_var(int *v, int dflt, int lo, int hi, const char *msg);
+
+/*
+ * Extract the head of a queue, update stats. Must be the very last
+ * thing done on a dequeue as the queue itself may go away.
+ */
+static __inline struct mbuf*
+dn_dequeue(struct dn_queue *q)
+{
+       struct mbuf *m = q->mq.head;
+       if (m == NULL)
+               return NULL;
+       q->mq.head = m->m_nextpkt;
+       q->ni.length--;
+       q->ni.len_bytes -= m->m_pkthdr.len;
+       if (q->_si) {
+               q->_si->ni.length--;
+               q->_si->ni.len_bytes -= m->m_pkthdr.len;
+       }
+       if (q->ni.length == 0) /* queue is now idle */
+               q->q_time = dn_cfg.curr_time;
+       return m;
+}
+
+int dn_sched_modevent(module_t mod, int cmd, void *arg);
+
+#define DECLARE_DNSCHED_MODULE(name, dnsched)                  \
+       static moduledata_t name##_mod = {                      \
+               #name, dn_sched_modevent, dnsched               \
+       };                                                      \
+       DECLARE_MODULE(name, name##_mod,                        \
+               SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);     \
+        MODULE_DEPEND(name, dummynet, 3, 3, 3);
+#endif /* _DN_SCHED_H */
diff --git a/dummynet2/include/netinet/ipfw/ip_dn_private.h b/dummynet2/include/netinet/ipfw/ip_dn_private.h
new file mode 100644 (file)
index 0000000..47cc5e8
--- /dev/null
@@ -0,0 +1,402 @@
+/*-
+ * Copyright (c) 2010 Luigi Rizzo, Riccardo Panicucci, Universita` di Pisa
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * internal dummynet APIs.
+ *
+ * $FreeBSD: head/sys/netinet/ipfw/ip_dn_private.h 204591 2010-03-02 17:40:48Z luigi $
+ */
+
+#ifndef _IP_DN_PRIVATE_H
+#define _IP_DN_PRIVATE_H
+
+/* debugging support
+ * use ND() to remove debugging, D() to print a line,
+ * DX(level, ...) to print above a certain level
+ * If you redefine D() you are expected to redefine all.
+ */
+#ifndef D
+#define ND(fmt, ...) do {} while (0)
+#define D1(fmt, ...) do {} while (0)
+#define D(fmt, ...) printf("%-10s " fmt "\n",      \
+        __FUNCTION__, ## __VA_ARGS__)
+#define DX(lev, fmt, ...) do {              \
+        if (dn_cfg.debug > lev) D(fmt, ## __VA_ARGS__); } while (0)
+#endif
+
+MALLOC_DECLARE(M_DUMMYNET);
+
+#ifndef FREE_PKT
+#define        FREE_PKT(m)     m_freem(m)
+#endif
+
+#ifndef __linux__
+#define div64(a, b)  ((int64_t)(a) / (int64_t)(b))
+#endif
+
+#define DN_LOCK_INIT() do {                            \
+       mtx_init(&dn_cfg.uh_mtx, "dn_uh", NULL, MTX_DEF);       \
+       mtx_init(&dn_cfg.bh_mtx, "dn_bh", NULL, MTX_DEF);       \
+       } while (0)
+#define DN_LOCK_DESTROY() do {                         \
+       mtx_destroy(&dn_cfg.uh_mtx);                    \
+       mtx_destroy(&dn_cfg.bh_mtx);                    \
+       } while (0)
+#if 0 /* not used yet */
+#define DN_UH_RLOCK()          mtx_lock(&dn_cfg.uh_mtx)
+#define DN_UH_RUNLOCK()                mtx_unlock(&dn_cfg.uh_mtx)
+#define DN_UH_WLOCK()          mtx_lock(&dn_cfg.uh_mtx)
+#define DN_UH_WUNLOCK()                mtx_unlock(&dn_cfg.uh_mtx)
+#define DN_UH_LOCK_ASSERT()    mtx_assert(&dn_cfg.uh_mtx, MA_OWNED)
+#endif
+
+#define DN_BH_RLOCK()          mtx_lock(&dn_cfg.uh_mtx)
+#define DN_BH_RUNLOCK()                mtx_unlock(&dn_cfg.uh_mtx)
+#define DN_BH_WLOCK()          mtx_lock(&dn_cfg.uh_mtx)
+#define DN_BH_WUNLOCK()                mtx_unlock(&dn_cfg.uh_mtx)
+#define DN_BH_LOCK_ASSERT()    mtx_assert(&dn_cfg.uh_mtx, MA_OWNED)
+
+SLIST_HEAD(dn_schk_head, dn_schk);
+SLIST_HEAD(dn_sch_inst_head, dn_sch_inst);
+SLIST_HEAD(dn_fsk_head, dn_fsk);
+SLIST_HEAD(dn_queue_head, dn_queue);
+SLIST_HEAD(dn_alg_head, dn_alg);
+
+struct mq {    /* a basic queue of packets*/
+        struct mbuf *head, *tail;
+};
+
+static inline void
+set_oid(struct dn_id *o, int type, int len)
+{
+        o->type = type;
+        o->len = len;
+        o->subtype = 0;
+};
+
+/*
+ * configuration and global data for a dummynet instance
+ *
+ * When a configuration is modified from userland, 'id' is incremented
+ * so we can use the value to check for stale pointers.
+ */
+struct dn_parms {
+       uint32_t        id;             /* configuration version */
+
+       /* defaults (sysctl-accessible) */
+       int     red_lookup_depth;
+       int     red_avg_pkt_size;
+       int     red_max_pkt_size;
+       int     hash_size;
+       int     max_hash_size;
+       long    byte_limit;             /* max queue sizes */
+       long    slot_limit;
+
+       int     io_fast;
+       int     debug;
+
+       /* timekeeping */
+       struct timeval prev_t;          /* last time dummynet_tick ran */
+       struct dn_heap  evheap;         /* scheduled events */
+
+       /* counters of objects -- used for reporting space */
+       int     schk_count;
+       int     si_count;
+       int     fsk_count;
+       int     queue_count;
+
+       /* ticks and other stuff */
+       uint64_t        curr_time;
+       /* flowsets and schedulers are in hash tables, with 'hash_size'
+        * buckets. fshash is looked up at every packet arrival
+        * so better be generous if we expect many entries.
+        */
+       struct dn_ht    *fshash;
+       struct dn_ht    *schedhash;
+       /* list of flowsets without a scheduler -- use sch_chain */
+       struct dn_fsk_head      fsu;    /* list of unlinked flowsets */
+       struct dn_alg_head      schedlist;      /* list of algorithms */
+
+       /* Store the fs/sch to scan when draining. The value is the
+        * bucket number of the hash table. Expire can be disabled
+        * with net.inet.ip.dummynet.expire=0, or it happens every
+        * expire ticks.
+        **/
+       int drain_fs;
+       int drain_sch;
+       uint32_t expire;
+       uint32_t expire_cycle;  /* tick count */
+       
+       /* if the upper half is busy doing something long,
+        * can set the busy flag and we will enqueue packets in
+        * a queue for later processing.
+        */
+       int     busy;
+       struct  mq      pending;
+
+#ifdef _KERNEL
+       /*
+        * This file is normally used in the kernel, unless we do
+        * some userland tests, in which case we do not need a mtx.
+        * uh_mtx arbitrates between system calls and also
+        * protects fshash, schedhash and fsunlinked.
+        * These structures are readonly for the lower half.
+        * bh_mtx protects all other structures which may be
+        * modified upon packet arrivals
+        */
+#if defined( __linux__ ) || defined( _WIN32 )
+       spinlock_t uh_mtx;
+       spinlock_t bh_mtx;
+#else
+       struct mtx uh_mtx;
+       struct mtx bh_mtx;
+#endif
+
+#endif /* _KERNEL */
+};
+
+/*
+ * Delay line, contains all packets on output from a link.
+ * Every scheduler instance has one.
+ */
+struct delay_line {
+       struct dn_id oid;
+       struct dn_sch_inst *si;
+       struct mq mq;
+};
+
+/*
+ * The kernel side of a flowset. It is linked in a hash table
+ * of flowsets, and in a list of children of their parent scheduler.
+ * qht is either the queue or (if HAVE_MASK) a hash table queues.
+ * Note that the mask to use is the (flow_mask|sched_mask), which
+ * changes as we attach/detach schedulers. So we store it here.
+ *
+ * XXX If we want to add scheduler-specific parameters, we need to
+ * put them in external storage because the scheduler may not be
+ * available when the fsk is created.
+ */
+struct dn_fsk { /* kernel side of a flowset */
+       struct dn_fs fs;
+       SLIST_ENTRY(dn_fsk) fsk_next;   /* hash chain for fshash */
+
+       struct ipfw_flow_id fsk_mask;
+
+       /* qht is a hash table of queues, or just a single queue
+        * a bit in fs.flags tells us which one
+        */
+       struct dn_ht    *qht;
+       struct dn_schk *sched;          /* Sched we are linked to */
+       SLIST_ENTRY(dn_fsk) sch_chain;  /* list of fsk attached to sched */
+
+       /* bucket index used by drain routine to drain queues for this
+        * flowset
+        */
+       int drain_bucket;
+       /* Parameter realted to RED / GRED */
+       /* original values are in dn_fs*/
+       int w_q ;               /* queue weight (scaled) */
+       int max_th ;            /* maximum threshold for queue (scaled) */
+       int min_th ;            /* minimum threshold for queue (scaled) */
+       int max_p ;             /* maximum value for p_b (scaled) */
+
+       u_int c_1 ;             /* max_p/(max_th-min_th) (scaled) */
+       u_int c_2 ;             /* max_p*min_th/(max_th-min_th) (scaled) */
+       u_int c_3 ;             /* for GRED, (1-max_p)/max_th (scaled) */
+       u_int c_4 ;             /* for GRED, 1 - 2*max_p (scaled) */
+       u_int * w_q_lookup ;    /* lookup table for computing (1-w_q)^t */
+       u_int lookup_depth ;    /* depth of lookup table */
+       int lookup_step ;       /* granularity inside the lookup table */
+       int lookup_weight ;     /* equal to (1-w_q)^t / (1-w_q)^(t+1) */
+       int avg_pkt_size ;      /* medium packet size */
+       int max_pkt_size ;      /* max packet size */
+};
+
+/*
+ * A queue is created as a child of a flowset unless it belongs to
+ * a !MULTIQUEUE scheduler. It is normally in a hash table in the
+ * flowset. fs always points to the parent flowset.
+ * si normally points to the sch_inst, unless the flowset has been
+ * detached from the scheduler -- in this case si == NULL and we
+ * should not enqueue.
+ */
+struct dn_queue {
+       struct dn_flow ni;      /* oid, flow_id, stats */
+       struct mq mq;   /* packets queue */
+       struct dn_sch_inst *_si;        /* owner scheduler instance */
+       SLIST_ENTRY(dn_queue) q_next; /* hash chain list for qht */
+       struct dn_fsk *fs;              /* parent flowset. */
+
+       /* RED parameters */
+       int avg;                /* average queue length est. (scaled) */
+       int count;              /* arrivals since last RED drop */
+       int random;             /* random value (scaled) */
+       uint64_t q_time;        /* start of queue idle time */
+
+};
+
+/*
+ * The kernel side of a scheduler. Contains the userland config,
+ * a link, pointer to extra config arguments from command line,
+ * kernel flags, and a pointer to the scheduler methods.
+ * It is stored in a hash table, and holds a list of all
+ * flowsets and scheduler instances.
+ * XXX sch must be at the beginning, see schk_hash().
+ */
+struct dn_schk {
+       struct dn_sch sch;
+       struct dn_alg *fp;      /* Pointer to scheduler functions */
+       struct dn_link link;    /* The link, embedded */
+       struct dn_profile *profile; /* delay profile, if any */
+       struct dn_id *cfg;      /* extra config arguments */
+
+       SLIST_ENTRY(dn_schk) schk_next;  /* hash chain for schedhash */
+
+       struct dn_fsk_head fsk_list;  /* all fsk linked to me */
+       struct dn_fsk *fs;      /* Flowset for !MULTIQUEUE */
+
+       /* bucket index used by the drain routine to drain the scheduler
+        * instance for this flowset.
+        */
+       int drain_bucket;
+
+       /* Hash table of all instances (through sch.sched_mask)
+        * or single instance if no mask. Always valid.
+        */
+       struct dn_ht    *siht;
+};
+
+
+/*
+ * Scheduler instance.
+ * Contains variables and all queues relative to a this instance.
+ * This struct is created a runtime.
+ */
+struct dn_sch_inst {
+       struct dn_flow  ni;     /* oid, flowid and stats */
+       SLIST_ENTRY(dn_sch_inst) si_next; /* hash chain for siht */
+       struct delay_line dline;
+       struct dn_schk *sched;  /* the template */
+       int             kflags; /* DN_ACTIVE */
+
+       int64_t credit;         /* bits I can transmit (more or less). */
+       uint64_t sched_time;    /* time link was scheduled in ready_heap */
+       uint64_t idle_time;     /* start of scheduler instance idle time */
+
+       /* q_count is the number of queues that this instance is using.
+        * The counter is incremented or decremented when
+        * a reference from the queue is created or deleted.
+        * It is used to make sure that a scheduler instance can be safely
+        * deleted by the drain routine. See notes below.
+        */
+       int q_count;
+
+};
+
+/*
+ * NOTE about object drain.
+ * The system will automatically (XXX check when) drain queues and
+ * scheduler instances when they are idle.
+ * A queue is idle when it has no packets; an instance is idle when
+ * it is not in the evheap heap, and the corresponding delay line is empty.
+ * A queue can be safely deleted when it is idle because of the scheduler
+ * function xxx_free_queue() will remove any references to it.
+ * An instance can be only deleted when no queues reference it. To be sure
+ * of that, a counter (q_count) stores the number of queues that are pointing
+ * to the instance.
+ *
+ * XXX
+ * Order of scan:
+ * - take all flowset in a bucket for the flowset hash table
+ * - take all queues in a bucket for the flowset
+ * - increment the queue bucket
+ * - scan next flowset bucket
+ * Nothing is done if a bucket contains no entries.
+ *
+ * The same schema is used for sceduler instances
+ */
+
+
+/* kernel-side flags. Linux has DN_DELETE in fcntl.h 
+ */
+enum {
+       /* 1 and 2 are reserved for the SCAN flags */
+       DN_DESTROY      = 0x0004, /* destroy */
+       DN_DELETE_FS    = 0x0008, /* destroy flowset */
+       DN_DETACH       = 0x0010,
+       DN_ACTIVE       = 0x0020, /* object is in evheap */
+       DN_F_DLINE      = 0x0040, /* object is a delay line */
+       DN_F_SCHI       = 0x00C0, /* object is a sched.instance */
+       DN_QHT_IS_Q     = 0x0100, /* in flowset, qht is a single queue */
+};
+
+extern struct dn_parms dn_cfg;
+
+int dummynet_io(struct mbuf **, int , struct ip_fw_args *);
+void dummynet_task(void *context, int pending);
+void dn_reschedule(void);
+
+struct dn_queue *ipdn_q_find(struct dn_fsk *, struct dn_sch_inst *,
+        struct ipfw_flow_id *);
+struct dn_sch_inst *ipdn_si_find(struct dn_schk *, struct ipfw_flow_id *);
+
+/*
+ * copy_range is a template for requests for ranges of pipes/queues/scheds.
+ * The number of ranges is variable and can be derived by o.len.
+ * As a default, we use a small number of entries so that the struct
+ * fits easily on the stack and is sufficient for most common requests.
+ */
+#define DEFAULT_RANGES 5
+struct copy_range {
+        struct dn_id o;
+        uint32_t       r[ 2 * DEFAULT_RANGES ];
+};
+
+struct copy_args {
+       char **start;
+       char *end;
+       int flags;
+       int type;
+       struct copy_range *extra;       /* extra filtering */
+};
+
+struct sockopt;
+int ip_dummynet_compat(struct sockopt *sopt);
+int dummynet_get(struct sockopt *sopt, void **compat);
+int dn_c_copy_q (void *_ni, void *arg);
+int dn_c_copy_pipe(struct dn_schk *s, struct copy_args *a, int nq);
+int dn_c_copy_fs(struct dn_fsk *f, struct copy_args *a, int nq);
+int dn_compat_copy_queue(struct copy_args *a, void *_o);
+int dn_compat_copy_pipe(struct copy_args *a, void *_o);
+int copy_data_helper_compat(void *_o, void *_arg);
+int dn_compat_calc_size(void);
+int do_config(void *p, int l);
+
+/* function to drain idle object */
+void dn_drain_scheduler(void);
+void dn_drain_queue(void);
+
+#endif /* _IP_DN_PRIVATE_H */
index 41ae845..5bf3416 100644 (file)
 
 #ifdef _KERNEL
 
-#define MTAG_IPFW      1148380143      /* IPFW-tagged cookie */
-#define MTAG_IPFW_RULE 1262273568      /* rule reference */
+/*
+ * For platforms that do not have SYSCTL support, we wrap the
+ * SYSCTL_* into a function (one per file) to collect the values
+ * into an array at module initialization. The wrapping macros,
+ * SYSBEGIN() and SYSEND, are empty in the default case.
+ */
+#ifndef SYSBEGIN
+#define SYSBEGIN(x)
+#endif
+#ifndef SYSEND
+#define SYSEND
+#endif
 
 /* Return values from ipfw_chk() */
 enum {
@@ -66,38 +76,6 @@ struct _ip6dn_args {
        struct route_in6 ro_pmtu_or;
 };
 
-/*
- * Reference to an ipfw rule that can be carried outside critical sections.
- * A rule is identified by rulenum:rule_id which is ordered.
- * In version chain_id the rule can be found in slot 'slot', so
- * we don't need a lookup if chain_id == chain->id.
- *
- * On exit from the firewall this structure refers to the rule after
- * the matching one (slot points to the new rule; rulenum:rule_id-1
- * is the matching rule), and additional info (e.g. info often contains
- * the insn argument or tablearg in the low 16 bits, in host format).
- * On entry, the structure is valid if slot>0, and refers to the starting
- * rules. 'info' contains the reason for reinject, e.g. divert port,
- * divert direction, and so on.
- */
-struct ipfw_rule_ref {
-       uint32_t        slot;           /* slot for matching rule       */
-       uint32_t        rulenum;        /* matching rule number         */
-       uint32_t        rule_id;        /* matching rule id             */
-       uint32_t        chain_id;       /* ruleset id                   */
-       uint32_t        info;           /* see below                    */
-};
-
-enum {
-       IPFW_INFO_MASK  = 0x0000ffff,
-       IPFW_INFO_OUT   = 0x00000000,   /* outgoing, just for convenience */
-       IPFW_INFO_IN    = 0x80000000,   /* incoming, overloads dir */
-       IPFW_ONEPASS    = 0x40000000,   /* One-pass, do not reinject */
-       IPFW_IS_MASK    = 0x30000000,   /* which source ? */
-       IPFW_IS_DIVERT  = 0x20000000,
-       IPFW_IS_DUMMYNET =0x10000000,
-       IPFW_IS_PIPE    = 0x08000000,   /* pip1=1, queue = 0 */
-};
 
 /*
  * Arguments for calling ipfw_chk() and dummynet_io(). We put them
@@ -153,11 +131,13 @@ enum {
 };
 
 /* wrapper for freeing a packet, in case we need to do more work */
-#ifdef __linux__
+#ifndef FREE_PKT
+#if defined(__linux__) || defined(_WIN32)
 #define FREE_PKT(m)    netisr_dispatch(-1, m)
 #else
 #define FREE_PKT(m)    m_freem(m)
 #endif
+#endif /* !FREE_PKT */
 
 /*
  * Function definitions.
@@ -283,13 +263,17 @@ int ipfw_ctl(struct sockopt *sopt);
 int ipfw_chk(struct ip_fw_args *args);
 void ipfw_reap_rules(struct ip_fw *head);
 
+/* In ip_fw_pfil */
+int ipfw_check_hook(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
+     struct inpcb *inp);
+
 /* In ip_fw_table.c */
 struct radix_node;
 int ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
     uint32_t *val);
 int ipfw_init_tables(struct ip_fw_chain *ch);
+void ipfw_destroy_tables(struct ip_fw_chain *ch);
 int ipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl);
-void ipfw_flush_tables(struct ip_fw_chain *ch);
 int ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
     uint8_t mlen, uint32_t value);
 int ipfw_dump_table_entry(struct radix_node *rn, void *arg);
@@ -298,10 +282,7 @@ int ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
 int ipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt);
 int ipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl);
 
-/* hooks for divert */
-extern void (*ip_divert_ptr)(struct mbuf *m, int incoming);
-
-/* In ip_fw_nat.c */
+/* In ip_fw_nat.c -- XXX to be moved to ip_var.h */
 
 extern struct cfg_nat *(*lookup_nat_ptr)(struct nat_list *, int);
 
@@ -316,14 +297,5 @@ extern ipfw_nat_cfg_t *ipfw_nat_del_ptr;
 extern ipfw_nat_cfg_t *ipfw_nat_get_cfg_ptr;
 extern ipfw_nat_cfg_t *ipfw_nat_get_log_ptr;
 
-/* netgraph prototypes */
-
-typedef int ng_ipfw_input_t(struct mbuf **, int, struct ip_fw_args *, int);
-extern  ng_ipfw_input_t *ng_ipfw_input_p;
-#define NG_IPFW_LOADED  (ng_ipfw_input_p != NULL)
-
-#define TAGSIZ  (sizeof(struct ng_ipfw_tag) - sizeof(struct m_tag))
-
-
 #endif /* _KERNEL */
 #endif /* _IPFW2_PRIVATE_H */
index 168d971..5af35a7 100644 (file)
@@ -54,11 +54,11 @@ struct tcphdr {
        tcp_seq th_seq;                 /* sequence number */
        tcp_seq th_ack;                 /* acknowledgement number */
 #if BYTE_ORDER == LITTLE_ENDIAN
-       u_int   th_x2:4,                /* (unused) */
+       u_char  th_x2:4,                /* (unused) */
                th_off:4;               /* data offset */
 #endif
 #if BYTE_ORDER == BIG_ENDIAN
-       u_int   th_off:4,               /* data offset */
+       u_char  th_off:4,               /* data offset */
                th_x2:4;                /* (unused) */
 #endif
        u_char  th_flags;
index 285e789..ac16aed 100644 (file)
@@ -45,6 +45,7 @@
  * ExFreePoolWithTag(ptr, tag)
  */
 #define malloc(_size, _type, _flags) my_alloc(_size)
+#define calloc(_size, _type, _flags) my_alloc(_size)
 
 void *my_alloc(int _size);
 /* the 'tag' version does not work without -Gz in the linker */
index 12837bf..a752ebd 100644 (file)
@@ -64,14 +64,24 @@ struct mbuf {
        void *m_data;
        int m_len;      /* length in this mbuf */
        int m_flags;
+#ifdef __linux__
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
        struct nf_info *queue_entry;
 #else
        struct nf_queue_entry *queue_entry;
 #endif
+#else /* _WIN32 */
+       int             direction;      /* could go in rcvif */
+       NDIS_HANDLE     context;        /* replaces queue_entry or skb ?*/
+       PNDIS_PACKET    pkt;
+#endif
        struct sk_buff *m_skb;
        struct {
+#ifdef __linux__
                struct net_device *rcvif;
+#else
+               struct ifnet *rcvif;
+#endif
                int len;        /* total packet len */
                SLIST_HEAD (packet_tags, m_tag) tags;
        } m_pkthdr;
@@ -173,12 +183,11 @@ m_freem(struct mbuf *m)
        free(m, M_IPFW);
 };
 
-/* we cannot pullup */
-//#define m_pullup(__m, __i)   (m)
+/* m_pullup is not supported, there is a macro in missing.h */
 
 #define M_GETFIB(_m)   0
 
-#endif /* !__linux__ */
+#endif /* __linux__ || _WIN32 */
 
 /*
  * Persistent tags stay with an mbuf until the mbuf is reclaimed.  Otherwise
index 8f06f17..3630218 100644 (file)
@@ -144,6 +144,9 @@ struct name {                                                               \
 #define        SLIST_HEAD_INITIALIZER(head)                                    \
        { NULL }
 
+#if defined( _WIN32 ) && defined(SLIST_ENTRY)
+#undef SLIST_ENTRY
+#endif
 #define        SLIST_ENTRY(type)                                               \
 struct {                                                               \
        struct type *sle_next;  /* next element */                      \
index 238a7d3..db8ef7a 100644 (file)
@@ -1,6 +1,9 @@
 #ifndef _SYS_SYSTM_H_
 #define _SYS_SYSTM_H_
 
+#define CALLOUT_ACTIVE          0x0002 /* callout is currently active */
+#define CALLOUT_MPSAFE          0x0008 /* callout handler is mp safe */
+
 #ifndef _WIN32 /* this is the linux version */
 /* callout support, in <sys/callout.h> on FreeBSD */
 /*
@@ -25,49 +28,94 @@ callout_reset(struct callout *co, int ticks, void (*fn)(void *), void *arg)
 #define callout_drain(co)       del_timer(co)
 #define callout_stop(co)        del_timer(co)
 
-#define CALLOUT_ACTIVE          0x0002 /* callout is currently active */
-#define CALLOUT_MPSAFE          0x0008 /* callout handler is mp safe */
-
 #else /* _WIN32 */
+#include <ndis.h>
 
 /* This is the windows part for callout support */
 struct callout {
-       int dummy;
+       KTIMER thetimer;
+       KDPC timerdpc;
+       int dpcinitialized;
+       LARGE_INTEGER duetime;
 };
+
+void dummynet (void*);
+VOID dummynet_dpc(
+    __in struct _KDPC  *Dpc,
+    __in_opt PVOID  DeferredContext,
+    __in_opt PVOID  SystemArgument1,
+    __in_opt PVOID  SystemArgument2
+    );
+
+VOID ipfw_dpc(
+    __in struct _KDPC  *Dpc,
+    __in_opt PVOID  DeferredContext,
+    __in_opt PVOID  SystemArgument1,
+    __in_opt PVOID  SystemArgument2
+    );
+
+/* callout_reset must handle two problems:
+ * - dummynet() scheduler must be run always on the same processor
+ * because do_gettimeofday() is based on cpu performance counter, and
+ * _occasionally_ can leap backward in time if we query another cpu.
+ * typically this won't happen that much, and the cpu will almost always
+ * be the same even without the affinity restriction, but better to be sure.
+ * - ipfw_tick() does not have the granularity requirements of dummynet()
+ * but we need to pass a pointer as argument.
+ *
+ * for these reasons, if we are called for dummynet() timer,
+ * KeInitializeDpc is called only once as it should be, and the thread
+ * is forced on cpu0 (which is always present), while if we're called
+ * for ipfw_tick(), we re-initialize the DPC each time, using
+ * parameter DeferredContext to pass the needed pointer. since this
+ * timer is called only once a sec, this won't hurt that much.
+ */
 static __inline int
-callout_reset(struct callout *co, int ticks, void (*fn)(void *), void *arg)
+callout_reset(struct callout *co, int ticks, void (*fn)(void *), void *arg) 
 {
+       if(fn == &dummynet)
+       {
+               if(co->dpcinitialized == 0)
+               {
+                       KeInitializeDpc(&co->timerdpc, dummynet_dpc, NULL);
+                       KeSetTargetProcessorDpc(&co->timerdpc, 0);
+                       co->dpcinitialized = 1;
+               }
+       }
+       else
+       {
+               KeInitializeDpc(&co->timerdpc, ipfw_dpc, arg);
+       }
+       co->duetime.QuadPart = (-ticks)*10000;
+       KeSetTimer(&co->thetimer, co->duetime, &co->timerdpc);
        return 0;
 }
 
-#define callout_init(co, safe)
-#define callout_drain(co)
-#define callout_stop(co)
-#endif /* !_WIN32 */
+static __inline void
+callout_init(struct callout* co, int safe)
+{
+       printf("%s: initializing timer at %p\n",__FUNCTION__,co);
+       KeInitializeTimer(&co->thetimer);
+}
 
+static __inline int
+callout_drain(struct callout* co)
+{
+       BOOLEAN canceled = KeCancelTimer(&co->thetimer);
+       while (canceled != TRUE)
+       {
+               canceled = KeCancelTimer(&co->thetimer);
+       }
+       printf("%s: stopping timer at %p\n",__FUNCTION__,co);
+       return 0;
+}
 
-#if 0
-/* add out timer to the kernel global timer list */
-NTSTATUS 
-  IoInitializeTimer(
-    IN PDEVICE_OBJECT  DeviceObject,
-    IN PIO_TIMER_ROUTINE  TimerRoutine,
-    IN PVOID  Context
-    );
+static __inline int
+callout_stop(struct callout* co)
+{
+       return callout_drain(co);
+}
 
-/* see differences :
-IoInitializeDpcRequest
-       http://dsrg.mff.cuni.cz/~ceres/sch/osy/text/ch04s01s01.php
-       example http://www.beyondlogic.org/interrupts/winnt_isr_dpc.htm
-KeInitializeDpc  IRQL: Any level
-IoInitializeTimer IRQL: Passive level
-KeInitializeTimer */
-VOID 
-  KeInitializeDpc(
-    IN PRKDPC  Dpc,
-    IN PKDEFERRED_ROUTINE  DeferredRoutine,
-    IN PVOID  DeferredContext
-    );
-#endif /* commented out */ 
+#endif /* _WIN32 */
 
 #endif /* _SYS_SYSTM_H_ */
index f11d286..43efdd5 100644 (file)
@@ -4,6 +4,15 @@
 /*
  * Remap taskqueue to direct calls
  */
+
+#ifdef _WIN32
+struct task {
+       void (*func)(void*, int);
+};
+#define taskqueue_enqueue(tq, ta)      (ta)->func(NULL,1)
+#define TASK_INIT(a,b,c,d) do {                                \
+       (a)->func = (c); } while (0)
+#else
 struct task {
        void (*func)(void);
 };
@@ -11,6 +20,7 @@ struct task {
 #define TASK_INIT(a,b,c,d) do {                                \
        (a)->func = (void (*)(void))c; } while (0)
 
+#endif
 #define taskqueue_create_fast(_a, _b, _c, _d)  NULL
 #define taskqueue_start_threads(_a, _b, _c, _d)
 
diff --git a/dummynet2/ip_dn_glue.c b/dummynet2/ip_dn_glue.c
new file mode 100644 (file)
index 0000000..0df0829
--- /dev/null
@@ -0,0 +1,845 @@
+/*-
+ * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id: ip_dn_glue.c 6031 2010-04-09 15:25:41Z svn_panicucci $
+ *
+ * Binary compatibility support for /sbin/ipfw RELENG_7 and RELENG_8
+ */
+
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/rwlock.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/time.h>
+#include <sys/taskqueue.h>
+#include <net/if.h>    /* IFNAMSIZ, struct ifaddr, ifq head, lock.h mutex.h */
+#include <netinet/in.h>
+#include <netinet/ip_var.h>    /* ip_output(), IP_FORWARDING */
+#include <netinet/ip_fw.h>
+#include <netinet/ipfw/ip_fw_private.h>
+#include <netinet/ipfw/dn_heap.h>
+#include <netinet/ip_dummynet.h>
+#include <netinet/ipfw/ip_dn_private.h>
+#include <netinet/ipfw/dn_sched.h>
+
+/* FREEBSD7.2 ip_dummynet.h r191715*/
+
+struct dn_heap_entry7 {
+       int64_t key;        /* sorting key. Topmost element is smallest one */
+       void *object;      /* object pointer */
+};
+
+struct dn_heap7 {
+       int size;
+       int elements;
+       int offset; /* XXX if > 0 this is the offset of direct ptr to obj */
+       struct dn_heap_entry7 *p;   /* really an array of "size" entries */
+};
+
+/* Common to 7.2 and 8 */
+struct dn_flow_set {
+       SLIST_ENTRY(dn_flow_set)    next;   /* linked list in a hash slot */
+
+       u_short fs_nr ;             /* flow_set number       */
+       u_short flags_fs;
+#define DNOLD_HAVE_FLOW_MASK   0x0001
+#define DNOLD_IS_RED       0x0002
+#define DNOLD_IS_GENTLE_RED    0x0004
+#define DNOLD_QSIZE_IS_BYTES   0x0008  /* queue size is measured in bytes */
+#define DNOLD_NOERROR      0x0010  /* do not report ENOBUFS on drops  */
+#define DNOLD_HAS_PROFILE      0x0020  /* the pipe has a delay profile. */
+#define DNOLD_IS_PIPE      0x4000
+#define DNOLD_IS_QUEUE     0x8000
+
+       struct dn_pipe7 *pipe ;  /* pointer to parent pipe */
+       u_short parent_nr ;     /* parent pipe#, 0 if local to a pipe */
+
+       int weight ;        /* WFQ queue weight */
+       int qsize ;         /* queue size in slots or bytes */
+       int plr ;           /* pkt loss rate (2^31-1 means 100%) */
+
+       struct ipfw_flow_id flow_mask ;
+
+       /* hash table of queues onto this flow_set */
+       int rq_size ;       /* number of slots */
+       int rq_elements ;       /* active elements */
+       struct dn_flow_queue7 **rq;  /* array of rq_size entries */
+
+       u_int32_t last_expired ;    /* do not expire too frequently */
+       int backlogged ;        /* #active queues for this flowset */
+
+        /* RED parameters */
+#define SCALE_RED               16
+#define SCALE(x)                ( (x) << SCALE_RED )
+#define SCALE_VAL(x)            ( (x) >> SCALE_RED )
+#define SCALE_MUL(x,y)          ( ( (x) * (y) ) >> SCALE_RED )
+       int w_q ;           /* queue weight (scaled) */
+       int max_th ;        /* maximum threshold for queue (scaled) */
+       int min_th ;        /* minimum threshold for queue (scaled) */
+       int max_p ;         /* maximum value for p_b (scaled) */
+       u_int c_1 ;         /* max_p/(max_th-min_th) (scaled) */
+       u_int c_2 ;         /* max_p*min_th/(max_th-min_th) (scaled) */
+       u_int c_3 ;         /* for GRED, (1-max_p)/max_th (scaled) */
+       u_int c_4 ;         /* for GRED, 1 - 2*max_p (scaled) */
+       u_int * w_q_lookup ;    /* lookup table for computing (1-w_q)^t */
+       u_int lookup_depth ;    /* depth of lookup table */
+       int lookup_step ;       /* granularity inside the lookup table */
+       int lookup_weight ;     /* equal to (1-w_q)^t / (1-w_q)^(t+1) */
+       int avg_pkt_size ;      /* medium packet size */
+       int max_pkt_size ;      /* max packet size */
+};
+SLIST_HEAD(dn_flow_set_head, dn_flow_set);
+
+#define DN_IS_PIPE             0x4000
+#define DN_IS_QUEUE            0x8000
+struct dn_flow_queue7 {
+       struct dn_flow_queue7 *next ;
+       struct ipfw_flow_id id ;
+
+       struct mbuf *head, *tail ;  /* queue of packets */
+       u_int len ;
+       u_int len_bytes ;
+
+       u_long numbytes;
+
+       u_int64_t tot_pkts ;    /* statistics counters  */
+       u_int64_t tot_bytes ;
+       u_int32_t drops ;
+
+       int hash_slot ;     /* debugging/diagnostic */
+
+       /* RED parameters */
+       int avg ;                   /* average queue length est. (scaled) */
+       int count ;                 /* arrivals since last RED drop */
+       int random ;                /* random value (scaled) */
+       u_int32_t q_time;      /* start of queue idle time */
+
+       /* WF2Q+ support */
+       struct dn_flow_set *fs ;    /* parent flow set */
+       int heap_pos ;      /* position (index) of struct in heap */
+       int64_t sched_time ;     /* current time when queue enters ready_heap */
+
+       int64_t S,F ;        /* start time, finish time */
+};
+
+struct dn_pipe7 {        /* a pipe */
+       SLIST_ENTRY(dn_pipe7)    next;   /* linked list in a hash slot */
+
+       int pipe_nr ;       /* number   */
+       int bandwidth;      /* really, bytes/tick.  */
+       int delay ;         /* really, ticks    */
+
+       struct  mbuf *head, *tail ; /* packets in delay line */
+
+       /* WF2Q+ */
+       struct dn_heap7 scheduler_heap ; /* top extract - key Finish time*/
+       struct dn_heap7 not_eligible_heap; /* top extract- key Start time */
+       struct dn_heap7 idle_heap ; /* random extract - key Start=Finish time */
+
+       int64_t V ;          /* virtual time */
+       int sum;            /* sum of weights of all active sessions */
+
+       int numbytes;
+
+       int64_t sched_time ;     /* time pipe was scheduled in ready_heap */
+
+       /*
+       * When the tx clock come from an interface (if_name[0] != '\0'), its name
+       * is stored below, whereas the ifp is filled when the rule is configured.
+       */
+       char if_name[IFNAMSIZ];
+       struct ifnet *ifp ;
+       int ready ; /* set if ifp != NULL and we got a signal from it */
+
+       struct dn_flow_set fs ; /* used with fixed-rate flows */
+};
+SLIST_HEAD(dn_pipe_head7, dn_pipe7);
+
+
+/* FREEBSD8 ip_dummynet.h r196045 */
+struct dn_flow_queue8 {
+       struct dn_flow_queue8 *next ;
+       struct ipfw_flow_id id ;
+
+       struct mbuf *head, *tail ;  /* queue of packets */
+       u_int len ;
+       u_int len_bytes ;
+
+       uint64_t numbytes ;     /* credit for transmission (dynamic queues) */
+       int64_t extra_bits;     /* extra bits simulating unavailable channel */
+
+       u_int64_t tot_pkts ;    /* statistics counters  */
+       u_int64_t tot_bytes ;
+       u_int32_t drops ;
+
+       int hash_slot ;     /* debugging/diagnostic */
+
+       /* RED parameters */
+       int avg ;                   /* average queue length est. (scaled) */
+       int count ;                 /* arrivals since last RED drop */
+       int random ;                /* random value (scaled) */
+       int64_t idle_time;       /* start of queue idle time */
+
+       /* WF2Q+ support */
+       struct dn_flow_set *fs ;    /* parent flow set */
+       int heap_pos ;      /* position (index) of struct in heap */
+       int64_t sched_time ;     /* current time when queue enters ready_heap */
+
+       int64_t S,F ;        /* start time, finish time */
+};
+
+struct dn_pipe8 {        /* a pipe */
+       SLIST_ENTRY(dn_pipe8)    next;   /* linked list in a hash slot */
+
+       int pipe_nr ;       /* number   */
+       int bandwidth;      /* really, bytes/tick.  */
+       int delay ;         /* really, ticks    */
+
+       struct  mbuf *head, *tail ; /* packets in delay line */
+
+       /* WF2Q+ */
+       struct dn_heap7 scheduler_heap ; /* top extract - key Finish time*/
+       struct dn_heap7 not_eligible_heap; /* top extract- key Start time */
+       struct dn_heap7 idle_heap ; /* random extract - key Start=Finish time */
+
+       int64_t V ;          /* virtual time */
+       int sum;            /* sum of weights of all active sessions */
+
+       /* Same as in dn_flow_queue, numbytes can become large */
+       int64_t numbytes;       /* bits I can transmit (more or less). */
+       uint64_t burst;     /* burst size, scaled: bits * hz */
+
+       int64_t sched_time ;     /* time pipe was scheduled in ready_heap */
+       int64_t idle_time;       /* start of pipe idle time */
+
+       char if_name[IFNAMSIZ];
+       struct ifnet *ifp ;
+       int ready ; /* set if ifp != NULL and we got a signal from it */
+
+       struct dn_flow_set fs ; /* used with fixed-rate flows */
+
+    /* fields to simulate a delay profile */
+#define ED_MAX_NAME_LEN     32
+       char name[ED_MAX_NAME_LEN];
+       int loss_level;
+       int samples_no;
+       int *samples;
+};
+
+#define ED_MAX_SAMPLES_NO   1024
+struct dn_pipe_max8 {
+       struct dn_pipe8 pipe;
+       int samples[ED_MAX_SAMPLES_NO];
+};
+SLIST_HEAD(dn_pipe_head8, dn_pipe8);
+
+/*
+ * Changes from 7.2 to 8:
+ * dn_pipe:
+ *      numbytes from int to int64_t
+ *      add burst (int64_t)
+ *      add idle_time (int64_t)
+ *      add profile
+ *      add struct dn_pipe_max
+ *      add flag DN_HAS_PROFILE
+ *
+ * dn_flow_queue
+ *      numbytes from u_long to int64_t
+ *      add extra_bits (int64_t)
+ *      q_time from u_int32_t to int64_t and name idle_time
+ *
+ * dn_flow_set unchanged
+ *
+ */
+
+/* NOTE:XXX copied from dummynet.c */
+#define O_NEXT(p, len) ((void *)((char *)p + len))
+static void
+oid_fill(struct dn_id *oid, int len, int type, uintptr_t id)
+{
+       oid->len = len;
+       oid->type = type;
+       oid->subtype = 0;
+       oid->id = id;
+}
+/* make room in the buffer and move the pointer forward */
+static void *
+o_next(struct dn_id **o, int len, int type)
+{
+       struct dn_id *ret = *o;
+       oid_fill(ret, len, type, 0);
+       *o = O_NEXT(*o, len);
+       return ret;
+}
+
+
+static size_t pipesize7 = sizeof(struct dn_pipe7);
+static size_t pipesize8 = sizeof(struct dn_pipe8);
+static size_t pipesizemax8 = sizeof(struct dn_pipe_max8);
+
+/* Indicate 'ipfw' version
+ * 1: from FreeBSD 7.2
+ * 0: from FreeBSD 8
+ * -1: unknow (for now is unused)
+ *
+ * It is update when a IP_DUMMYNET_DEL or IP_DUMMYNET_CONFIGURE request arrives
+ * NOTE: if a IP_DUMMYNET_GET arrives and the 'ipfw' version is unknow,
+ *       it is suppose to be the FreeBSD 8 version.
+ */
+static int is7 = 0;
+
+static int
+convertflags2new(int src)
+{
+       int dst = 0;
+
+       if (src & DNOLD_HAVE_FLOW_MASK)
+               dst |= DN_HAVE_MASK;
+       if (src & DNOLD_QSIZE_IS_BYTES)
+               dst |= DN_QSIZE_BYTES;
+       if (src & DNOLD_NOERROR)
+               dst |= DN_NOERROR;
+       if (src & DNOLD_IS_RED)
+               dst |= DN_IS_RED;
+       if (src & DNOLD_IS_GENTLE_RED)
+               dst |= DN_IS_GENTLE_RED;
+       if (src & DNOLD_HAS_PROFILE)
+               dst |= DN_HAS_PROFILE;
+
+       return dst;
+}
+
+static int
+convertflags2old(int src)
+{
+       int dst = 0;
+
+       if (src & DN_HAVE_MASK)
+               dst |= DNOLD_HAVE_FLOW_MASK;
+       if (src & DN_IS_RED)
+               dst |= DNOLD_IS_RED;
+       if (src & DN_IS_GENTLE_RED)
+               dst |= DNOLD_IS_GENTLE_RED;
+       if (src & DN_NOERROR)
+               dst |= DNOLD_NOERROR;
+       if (src & DN_HAS_PROFILE)
+               dst |= DNOLD_HAS_PROFILE;
+       if (src & DN_QSIZE_BYTES)
+               dst |= DNOLD_QSIZE_IS_BYTES;
+
+       return dst;
+}
+
+static int
+dn_compat_del(void *v)
+{
+       struct dn_pipe7 *p = (struct dn_pipe7 *) v;
+       struct dn_pipe8 *p8 = (struct dn_pipe8 *) v;
+       struct {
+               struct dn_id oid;
+               uintptr_t a[1]; /* add more if we want a list */
+       } cmd;
+
+       /* XXX DN_API_VERSION ??? */
+       oid_fill((void *)&cmd, sizeof(cmd), DN_CMD_DELETE, DN_API_VERSION);
+
+       if (is7) {
+               if (p->pipe_nr == 0 && p->fs.fs_nr == 0)
+                       return EINVAL;
+               if (p->pipe_nr != 0 && p->fs.fs_nr != 0)
+                       return EINVAL;
+       } else {
+               if (p8->pipe_nr == 0 && p8->fs.fs_nr == 0)
+                       return EINVAL;
+               if (p8->pipe_nr != 0 && p8->fs.fs_nr != 0)
+                       return EINVAL;
+       }
+
+       if (p->pipe_nr != 0) { /* pipe x delete */
+               cmd.a[0] = p->pipe_nr;
+               cmd.oid.subtype = DN_LINK;
+       } else { /* queue x delete */
+               cmd.oid.subtype = DN_FS;
+               cmd.a[0] = (is7) ? p->fs.fs_nr : p8->fs.fs_nr;
+       }
+
+       return do_config(&cmd, cmd.oid.len);
+}
+
+static int
+dn_compat_config_queue(struct dn_fs *fs, void* v)
+{
+       struct dn_pipe7 *p7 = (struct dn_pipe7 *)v;
+       struct dn_pipe8 *p8 = (struct dn_pipe8 *)v;
+       struct dn_flow_set *f;
+
+       if (is7)
+               f = &p7->fs;
+       else
+               f = &p8->fs;
+
+       fs->fs_nr = f->fs_nr;
+       fs->sched_nr = f->parent_nr;
+       fs->flow_mask = f->flow_mask;
+       fs->buckets = f->rq_size;
+       fs->qsize = f->qsize;
+       fs->plr = f->plr;
+       fs->par[0] = f->weight;
+       fs->flags = convertflags2new(f->flags_fs);
+       if (fs->flags & DN_IS_GENTLE_RED || fs->flags & DN_IS_RED) {
+               fs->w_q = f->w_q;
+               fs->max_th = f->max_th;
+               fs->min_th = f->min_th;
+               fs->max_p = f->max_p;
+       }
+
+       return 0;
+}
+
+static int
+dn_compat_config_pipe(struct dn_sch *sch, struct dn_link *p, 
+                     struct dn_fs *fs, void* v)
+{
+       struct dn_pipe7 *p7 = (struct dn_pipe7 *)v;
+       struct dn_pipe8 *p8 = (struct dn_pipe8 *)v;
+       int i = p7->pipe_nr;
+
+       sch->sched_nr = i;
+       sch->oid.subtype = 0;
+       p->link_nr = i;
+       fs->fs_nr = i + 2*DN_MAX_ID;
+       fs->sched_nr = i + DN_MAX_ID;
+
+       /* Common to 7 and 8 */
+       p->bandwidth = p7->bandwidth;
+       p->delay = p7->delay;
+       if (!is7) {
+               /* FreeBSD 8 has burst  */
+               p->burst = p8->burst;
+       }
+
+       /* fill the fifo flowset */
+       dn_compat_config_queue(fs, v);
+       fs->fs_nr = i + 2*DN_MAX_ID;
+       fs->sched_nr = i + DN_MAX_ID;
+
+       /* Move scheduler related parameter from fs to sch */
+       sch->buckets = fs->buckets; /*XXX*/
+       fs->buckets = 0;
+       if (fs->flags & DN_HAVE_MASK) {
+               sch->flags |= DN_HAVE_MASK;
+               fs->flags &= ~DN_HAVE_MASK;
+               sch->sched_mask = fs->flow_mask;
+               bzero(&fs->flow_mask, sizeof(struct ipfw_flow_id));
+       }
+
+       return 0;
+}
+
+static int
+dn_compat_config_profile(struct dn_profile *pf, struct dn_link *p,
+                        void *v)
+{
+       struct dn_pipe8 *p8 = (struct dn_pipe8 *)v;
+
+       p8->samples = &(((struct dn_pipe_max8 *)p8)->samples[0]);
+       
+       pf->link_nr = p->link_nr;
+       pf->loss_level = p8->loss_level;
+//     pf->bandwidth = p->bandwidth; //XXX bandwidth redundant?
+       pf->samples_no = p8->samples_no;
+       strncpy(pf->name, p8->name,sizeof(pf->name));
+       bcopy(p8->samples, pf->samples, sizeof(pf->samples));
+
+       return 0;
+}
+
+/*
+ * If p->pipe_nr != 0 the command is 'pipe x config', so need to create
+ * the three main struct, else only a flowset is created
+ */
+static int
+dn_compat_configure(void *v)
+{
+       struct dn_id *buf = NULL, *base;
+       struct dn_sch *sch = NULL;
+       struct dn_link *p = NULL;
+       struct dn_fs *fs = NULL;
+       struct dn_profile *pf = NULL;
+       int lmax;
+       int error;
+
+       struct dn_pipe7 *p7 = (struct dn_pipe7 *)v;
+       struct dn_pipe8 *p8 = (struct dn_pipe8 *)v;
+
+       int i; /* number of object to configure */
+
+       lmax = sizeof(struct dn_id);    /* command header */
+       lmax += sizeof(struct dn_sch) + sizeof(struct dn_link) +
+               sizeof(struct dn_fs) + sizeof(struct dn_profile);
+
+       base = buf = malloc(lmax, M_DUMMYNET, M_WAIT|M_ZERO);
+       o_next(&buf, sizeof(struct dn_id), DN_CMD_CONFIG);
+       base->id = DN_API_VERSION;
+
+       /* pipe_nr is the same in p7 and p8 */
+       i = p7->pipe_nr;
+       if (i != 0) { /* pipe config */
+               sch = o_next(&buf, sizeof(*sch), DN_SCH);
+               p = o_next(&buf, sizeof(*p), DN_LINK);
+               fs = o_next(&buf, sizeof(*fs), DN_FS);
+
+               error = dn_compat_config_pipe(sch, p, fs, v);
+               if (error) {
+                       free(buf, M_DUMMYNET);
+                       return error;
+               }
+               if (!is7 && p8->samples_no > 0) {
+                       /* Add profiles*/
+                       pf = o_next(&buf, sizeof(*pf), DN_PROFILE);
+                       error = dn_compat_config_profile(pf, p, v);
+                       if (error) {
+                               free(buf, M_DUMMYNET);
+                               return error;
+                       }
+               }
+       } else { /* queue config */
+               fs = o_next(&buf, sizeof(*fs), DN_FS);
+               error = dn_compat_config_queue(fs, v);
+               if (error) {
+                       free(buf, M_DUMMYNET);
+                       return error;
+               }
+       }
+       error = do_config(base, (char *)buf - (char *)base);
+
+       if (buf)
+               free(buf, M_DUMMYNET);
+       return error;
+}
+
+int
+dn_compat_calc_size(void)
+{
+       int need = 0;
+       /* XXX use FreeBSD 8 struct size */
+       /* NOTE:
+        * - half scheduler:            schk_count/2
+        * - all flowset:               fsk_count
+        * - all flowset queues:        queue_count
+        * - all pipe queue:            si_count
+        */
+       need += dn_cfg.schk_count * sizeof(struct dn_pipe8) / 2;
+       need += dn_cfg.fsk_count * sizeof(struct dn_flow_set);
+       need += dn_cfg.si_count * sizeof(struct dn_flow_queue8);
+       need += dn_cfg.queue_count * sizeof(struct dn_flow_queue8);
+
+       return need;
+}
+
+int
+dn_c_copy_q (void *_ni, void *arg)
+{
+       struct copy_args *a = arg;
+       struct dn_flow_queue7 *fq7 = (struct dn_flow_queue7 *)*a->start;
+       struct dn_flow_queue8 *fq8 = (struct dn_flow_queue8 *)*a->start;
+       struct dn_flow *ni = (struct dn_flow *)_ni;
+       int size = 0;
+
+       /* XXX hash slot not set */
+       /* No difference between 7.2/8 */
+       fq7->len = ni->length;
+       fq7->len_bytes = ni->len_bytes;
+       fq7->id = ni->fid;
+
+       if (is7) {
+               size = sizeof(struct dn_flow_queue7);
+               fq7->tot_pkts = ni->tot_pkts;
+               fq7->tot_bytes = ni->tot_bytes;
+               fq7->drops = ni->drops;
+       } else {
+               size = sizeof(struct dn_flow_queue8);
+               fq8->tot_pkts = ni->tot_pkts;
+               fq8->tot_bytes = ni->tot_bytes;
+               fq8->drops = ni->drops;
+       }
+
+       *a->start += size;
+       return 0;
+}
+
+int
+dn_c_copy_pipe(struct dn_schk *s, struct copy_args *a, int nq)
+{
+       struct dn_link *l = &s->link;
+       struct dn_fsk *f = s->fs;
+
+       struct dn_pipe7 *pipe7 = (struct dn_pipe7 *)*a->start;
+       struct dn_pipe8 *pipe8 = (struct dn_pipe8 *)*a->start;
+       struct dn_flow_set *fs;
+       int size = 0;
+
+       if (is7) {
+               fs = &pipe7->fs;
+               size = sizeof(struct dn_pipe7);
+       } else {
+               fs = &pipe8->fs;
+               size = sizeof(struct dn_pipe8);
+       }
+
+       /* These 4 field are the same in pipe7 and pipe8 */
+       pipe7->next.sle_next = (struct dn_pipe7 *)DN_IS_PIPE;
+       pipe7->bandwidth = l->bandwidth;
+       pipe7->delay = l->delay;
+       pipe7->pipe_nr = l->link_nr - DN_MAX_ID;
+
+       if (!is7) {
+               if (s->profile) {
+                       struct dn_profile *pf = s->profile;
+                       strncpy(pipe8->name, pf->name, sizeof(pf->name));
+                       pipe8->loss_level = pf->loss_level;
+                       pipe8->samples_no = pf->samples_no;
+               }
+               pipe8->burst = div64(l->burst , 8 * hz);
+       }
+
+       fs->flow_mask = s->sch.sched_mask;
+       fs->rq_size = s->sch.buckets ? s->sch.buckets : 1;
+
+       fs->parent_nr = l->link_nr - DN_MAX_ID;
+       fs->qsize = f->fs.qsize;
+       fs->plr = f->fs.plr;
+       fs->w_q = f->fs.w_q;
+       fs->max_th = f->max_th;
+       fs->min_th = f->min_th;
+       fs->max_p = f->fs.max_p;
+       fs->rq_elements = nq;
+
+       fs->flags_fs = convertflags2old(f->fs.flags);
+
+       *a->start += size;
+       return 0;
+}
+
+
+int
+dn_compat_copy_pipe(struct copy_args *a, void *_o)
+{
+       int have = a->end - *a->start;
+       int need = 0;
+       int pipe_size = sizeof(struct dn_pipe8);
+       int queue_size = sizeof(struct dn_flow_queue8);
+       int n_queue = 0; /* number of queues */
+
+       struct dn_schk *s = (struct dn_schk *)_o;
+       /* calculate needed space:
+        * - struct dn_pipe
+        * - if there are instances, dn_queue * n_instances
+        */
+       n_queue = (s->sch.flags & DN_HAVE_MASK ? dn_ht_entries(s->siht) :
+                                               (s->siht ? 1 : 0));
+       need = pipe_size + queue_size * n_queue;
+       if (have < need) {
+               D("have %d < need %d", have, need);
+               return 1;
+       }
+       /* copy pipe */
+       dn_c_copy_pipe(s, a, n_queue);
+
+       /* copy queues */
+       if (s->sch.flags & DN_HAVE_MASK)
+               dn_ht_scan(s->siht, dn_c_copy_q, a);
+       else if (s->siht)
+               dn_c_copy_q(s->siht, a);
+       return 0;
+}
+
+int
+dn_c_copy_fs(struct dn_fsk *f, struct copy_args *a, int nq)
+{
+       struct dn_flow_set *fs = (struct dn_flow_set *)*a->start;
+
+       fs->next.sle_next = (struct dn_flow_set *)DN_IS_QUEUE;
+       fs->fs_nr = f->fs.fs_nr;
+       fs->qsize = f->fs.qsize;
+       fs->plr = f->fs.plr;
+       fs->w_q = f->fs.w_q;
+       fs->max_th = f->max_th;
+       fs->min_th = f->min_th;
+       fs->max_p = f->fs.max_p;
+       fs->flow_mask = f->fs.flow_mask;
+       fs->rq_elements = nq;
+       fs->rq_size = (f->fs.buckets ? f->fs.buckets : 1);
+       fs->parent_nr = f->fs.sched_nr;
+       fs->weight = f->fs.par[0];
+
+       fs->flags_fs = convertflags2old(f->fs.flags);
+       *a->start += sizeof(struct dn_flow_set);
+       return 0;
+}
+
+int
+dn_compat_copy_queue(struct copy_args *a, void *_o)
+{
+       int have = a->end - *a->start;
+       int need = 0;
+       int fs_size = sizeof(struct dn_flow_set);
+       int queue_size = sizeof(struct dn_flow_queue8);
+
+       struct dn_fsk *fs = (struct dn_fsk *)_o;
+       int n_queue = 0; /* number of queues */
+
+       n_queue = (fs->fs.flags & DN_HAVE_MASK ? dn_ht_entries(fs->qht) :
+                                               (fs->qht ? 1 : 0));
+
+       need = fs_size + queue_size * n_queue;
+       if (have < need) {
+               D("have < need");
+               return 1;
+       }
+
+       /* copy flowset */
+       dn_c_copy_fs(fs, a, n_queue);
+
+       /* copy queues */
+       if (fs->fs.flags & DN_HAVE_MASK)
+               dn_ht_scan(fs->qht, dn_c_copy_q, a);
+       else if (fs->qht)
+               dn_c_copy_q(fs->qht, a);
+
+       return 0;
+}
+
+int
+copy_data_helper_compat(void *_o, void *_arg)
+{
+       struct copy_args *a = _arg;
+
+       if (a->type == DN_COMPAT_PIPE) {
+               struct dn_schk *s = _o;
+               if (s->sch.oid.subtype != 1 || s->sch.sched_nr <= DN_MAX_ID) {
+                       return 0;       /* not old type */
+               }
+               /* copy pipe parameters, and if instance exists, copy
+                * other parameters and eventually queues.
+                */
+               if(dn_compat_copy_pipe(a, _o))
+                       return DNHT_SCAN_END;
+       } else if (a->type == DN_COMPAT_QUEUE) {
+               struct dn_fsk *fs = _o;
+               if (fs->fs.fs_nr >= DN_MAX_ID)
+                       return 0;
+               if (dn_compat_copy_queue(a, _o))
+                       return DNHT_SCAN_END;
+       }
+       return 0;
+}
+
+/* Main function to manage old requests */
+int
+ip_dummynet_compat(struct sockopt *sopt)
+{
+       int error=0;
+       void *v = NULL;
+       struct dn_id oid;
+
+       /* Lenght of data, used to found ipfw version... */
+       int len = sopt->sopt_valsize;
+
+       /* len can be 0 if command was dummynet_flush */
+       if (len == pipesize7) {
+               D("setting compatibility with FreeBSD 7.2");
+               is7 = 1;
+       }
+       else if (len == pipesize8 || len == pipesizemax8) {
+               D("setting compatibility with FreeBSD 8");
+               is7 = 0;
+       }
+
+       switch (sopt->sopt_name) {
+       default:
+               printf("dummynet: -- unknown option %d", sopt->sopt_name);
+               error = EINVAL;
+               break;
+
+       case IP_DUMMYNET_FLUSH:
+               oid_fill(&oid, sizeof(oid), DN_CMD_FLUSH, DN_API_VERSION);
+               do_config(&oid, oid.len);
+               break;
+
+       case IP_DUMMYNET_DEL:
+               v = malloc(len, M_TEMP, M_WAITOK);
+               error = sooptcopyin(sopt, v, len, len);
+               if (error)
+                       break;
+               error = dn_compat_del(v);
+               free(v, M_DUMMYNET);
+               break;
+
+       case IP_DUMMYNET_CONFIGURE:
+               v = malloc(len, M_TEMP, M_WAITOK);
+               error = sooptcopyin(sopt, v, len, len);
+               if (error)
+                       break;
+               error = dn_compat_configure(v);
+               free(v, M_DUMMYNET);
+               break;
+
+       case IP_DUMMYNET_GET: {
+               void *buf;
+               int ret;
+               int original_size = sopt->sopt_valsize;
+               int size;
+
+               ret = dummynet_get(sopt, &buf);
+               if (ret)
+                       return 0;//XXX ?
+               size = sopt->sopt_valsize;
+               sopt->sopt_valsize = original_size;
+               D("size=%d, buf=%p", size, buf);
+               ret = sooptcopyout(sopt, buf, size);
+               if (ret)
+                       printf("  %s ERROR sooptcopyout\n", __FUNCTION__);
+               if (buf)
+                       free(buf, M_DUMMYNET);
+           }
+       }
+
+       return error;
+}
+
+
diff --git a/dummynet2/ip_dn_io.c b/dummynet2/ip_dn_io.c
new file mode 100644 (file)
index 0000000..3450466
--- /dev/null
@@ -0,0 +1,807 @@
+/*-
+ * Copyright (c) 2010 Luigi Rizzo, Riccardo Panicucci, Universita` di Pisa
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Dummynet portions related to packet handling.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: user/luigi/ipfw3-head/sys/netinet/ipfw/ip_dn_io.c 203321 2010-01-31 21:39:25Z luigi $");
+
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/priv.h>
+#include <sys/proc.h>
+#include <sys/rwlock.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+#include <net/if.h>    /* IFNAMSIZ, struct ifaddr, ifq head, lock.h mutex.h */
+#include <net/netisr.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>                /* ip_len, ip_off */
+#include <netinet/ip_var.h>    /* ip_output(), IP_FORWARDING */
+#include <netinet/ip_fw.h>
+#include <netinet/ipfw/ip_fw_private.h>
+#include <netinet/ipfw/dn_heap.h>
+#include <netinet/ip_dummynet.h>
+#include <netinet/ipfw/ip_dn_private.h>
+#include <netinet/ipfw/dn_sched.h>
+
+#include <netinet/if_ether.h> /* various ether_* routines */
+
+#include <netinet/ip6.h>       /* for ip6_input, ip6_output prototypes */
+#include <netinet6/ip6_var.h>
+
+/*
+ * We keep a private variable for the simulation time, but we could
+ * probably use an existing one ("softticks" in sys/kern/kern_timeout.c)
+ * instead of dn_cfg.curr_time
+ */
+
+struct dn_parms dn_cfg;
+
+static long tick_last;         /* Last tick duration (usec). */
+static long tick_delta;                /* Last vs standard tick diff (usec). */
+static long tick_delta_sum;    /* Accumulated tick difference (usec).*/
+static long tick_adjustment;   /* Tick adjustments done. */
+static long tick_lost;         /* Lost(coalesced) ticks number. */
+/* Adjusted vs non-adjusted curr_time difference (ticks). */
+static long tick_diff;
+
+static unsigned long   io_pkt;
+static unsigned long   io_pkt_fast;
+static unsigned long   io_pkt_drop;
+
+/*
+ * We use a heap to store entities for which we have pending timer events.
+ * The heap is checked at every tick and all entities with expired events
+ * are extracted.
+ */
+  
+MALLOC_DEFINE(M_DUMMYNET, "dummynet", "dummynet heap");
+
+extern void (*bridge_dn_p)(struct mbuf *, struct ifnet *);
+
+#ifdef SYSCTL_NODE
+
+SYSBEGIN(f4)
+
+SYSCTL_DECL(_net_inet);
+SYSCTL_DECL(_net_inet_ip);
+SYSCTL_NODE(_net_inet_ip, OID_AUTO, dummynet, CTLFLAG_RW, 0, "Dummynet");
+
+/* parameters */
+SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, hash_size,
+    CTLFLAG_RW, &dn_cfg.hash_size, 0, "Default hash table size");
+SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, pipe_slot_limit,
+    CTLFLAG_RW, &dn_cfg.slot_limit, 0,
+    "Upper limit in slots for pipe queue.");
+SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, pipe_byte_limit,
+    CTLFLAG_RW, &dn_cfg.byte_limit, 0,
+    "Upper limit in bytes for pipe queue.");
+SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, io_fast,
+    CTLFLAG_RW, &dn_cfg.io_fast, 0, "Enable fast dummynet io.");
+SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, debug,
+    CTLFLAG_RW, &dn_cfg.debug, 0, "Dummynet debug level");
+SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, expire,
+    CTLFLAG_RW, &dn_cfg.expire, 0, "Expire empty queues/pipes");
+SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, expire_cycle,
+    CTLFLAG_RD, &dn_cfg.expire_cycle, 0, "Expire cycle for queues/pipes");
+
+/* RED parameters */
+SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, red_lookup_depth,
+    CTLFLAG_RD, &dn_cfg.red_lookup_depth, 0, "Depth of RED lookup table");
+SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, red_avg_pkt_size,
+    CTLFLAG_RD, &dn_cfg.red_avg_pkt_size, 0, "RED Medium packet size");
+SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, red_max_pkt_size,
+    CTLFLAG_RD, &dn_cfg.red_max_pkt_size, 0, "RED Max packet size");
+
+/* time adjustment */
+SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_delta,
+    CTLFLAG_RD, &tick_delta, 0, "Last vs standard tick difference (usec).");
+SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_delta_sum,
+    CTLFLAG_RD, &tick_delta_sum, 0, "Accumulated tick difference (usec).");
+SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_adjustment,
+    CTLFLAG_RD, &tick_adjustment, 0, "Tick adjustments done.");
+SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_diff,
+    CTLFLAG_RD, &tick_diff, 0,
+    "Adjusted vs non-adjusted curr_time difference (ticks).");
+SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_lost,
+    CTLFLAG_RD, &tick_lost, 0,
+    "Number of ticks coalesced by dummynet taskqueue.");
+
+/* statistics */
+SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, schk_count,
+    CTLFLAG_RD, &dn_cfg.schk_count, 0, "Number of schedulers");
+SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, si_count,
+    CTLFLAG_RD, &dn_cfg.si_count, 0, "Number of scheduler instances");
+SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, fsk_count,
+    CTLFLAG_RD, &dn_cfg.fsk_count, 0, "Number of flowsets");
+SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, queue_count,
+    CTLFLAG_RD, &dn_cfg.queue_count, 0, "Number of queues");
+SYSCTL_ULONG(_net_inet_ip_dummynet, OID_AUTO, io_pkt,
+    CTLFLAG_RD, &io_pkt, 0,
+    "Number of packets passed to dummynet.");
+SYSCTL_ULONG(_net_inet_ip_dummynet, OID_AUTO, io_pkt_fast,
+    CTLFLAG_RD, &io_pkt_fast, 0,
+    "Number of packets bypassed dummynet scheduler.");
+SYSCTL_ULONG(_net_inet_ip_dummynet, OID_AUTO, io_pkt_drop,
+    CTLFLAG_RD, &io_pkt_drop, 0,
+    "Number of packets dropped by dummynet.");
+
+SYSEND
+
+#endif
+
+static void    dummynet_send(struct mbuf *);
+
+/*
+ * Packets processed by dummynet have an mbuf tag associated with
+ * them that carries their dummynet state.
+ * Outside dummynet, only the 'rule' field is relevant, and it must
+ * be at the beginning of the structure.
+ */
+struct dn_pkt_tag {
+       struct ipfw_rule_ref rule;      /* matching rule        */
+
+       /* second part, dummynet specific */
+       int dn_dir;             /* action when packet comes out.*/
+                               /* see ip_fw_private.h          */
+       uint64_t output_time;   /* when the pkt is due for delivery*/
+       struct ifnet *ifp;      /* interface, for ip_output     */
+       struct _ip6dn_args ip6opt;      /* XXX ipv6 options     */
+};
+
+/*
+ * Return the mbuf tag holding the dummynet state (it should
+ * be the first one on the list).
+ */
+static struct dn_pkt_tag *
+dn_tag_get(struct mbuf *m)
+{
+       struct m_tag *mtag = m_tag_first(m);
+       KASSERT(mtag != NULL &&
+           mtag->m_tag_cookie == MTAG_ABI_COMPAT &&
+           mtag->m_tag_id == PACKET_TAG_DUMMYNET,
+           ("packet on dummynet queue w/o dummynet tag!"));
+       return (struct dn_pkt_tag *)(mtag+1);
+}
+
+static inline void
+mq_append(struct mq *q, struct mbuf *m)
+{
+       if (q->head == NULL)
+               q->head = m;
+       else
+               q->tail->m_nextpkt = m;
+       q->tail = m;
+       m->m_nextpkt = NULL;
+}
+
+/*
+ * Dispose a list of packet. Use a functions so if we need to do
+ * more work, this is a central point to do it.
+ */
+void dn_free_pkts(struct mbuf *mnext)
+{
+        struct mbuf *m;
+    
+        while ((m = mnext) != NULL) {
+                mnext = m->m_nextpkt;
+                FREE_PKT(m);
+        }
+}
+
+static int
+red_drops (struct dn_queue *q, int len)
+{
+       /*
+        * RED algorithm
+        *
+        * RED calculates the average queue size (avg) using a low-pass filter
+        * with an exponential weighted (w_q) moving average:
+        *      avg  <-  (1-w_q) * avg + w_q * q_size
+        * where q_size is the queue length (measured in bytes or * packets).
+        *
+        * If q_size == 0, we compute the idle time for the link, and set
+        *      avg = (1 - w_q)^(idle/s)
+        * where s is the time needed for transmitting a medium-sized packet.
+        *
+        * Now, if avg < min_th the packet is enqueued.
+        * If avg > max_th the packet is dropped. Otherwise, the packet is
+        * dropped with probability P function of avg.
+        */
+
+       struct dn_fsk *fs = q->fs;
+       int64_t p_b = 0;
+
+       /* Queue in bytes or packets? */
+       uint32_t q_size = (fs->fs.flags & DN_QSIZE_BYTES) ?
+           q->ni.len_bytes : q->ni.length;
+
+       /* Average queue size estimation. */
+       if (q_size != 0) {
+               /* Queue is not empty, avg <- avg + (q_size - avg) * w_q */
+               int diff = SCALE(q_size) - q->avg;
+               int64_t v = SCALE_MUL((int64_t)diff, (int64_t)fs->w_q);
+
+               q->avg += (int)v;
+       } else {
+               /*
+                * Queue is empty, find for how long the queue has been
+                * empty and use a lookup table for computing
+                * (1 - * w_q)^(idle_time/s) where s is the time to send a
+                * (small) packet.
+                * XXX check wraps...
+                */
+               if (q->avg) {
+                       u_int t = div64((dn_cfg.curr_time - q->q_time), fs->lookup_step);
+
+                       q->avg = (t < fs->lookup_depth) ?
+                           SCALE_MUL(q->avg, fs->w_q_lookup[t]) : 0;
+               }
+       }
+
+       /* Should i drop? */
+       if (q->avg < fs->min_th) {
+               q->count = -1;
+               return (0);     /* accept packet */
+       }
+       if (q->avg >= fs->max_th) {     /* average queue >=  max threshold */
+               if (fs->fs.flags & DN_IS_GENTLE_RED) {
+                       /*
+                        * According to Gentle-RED, if avg is greater than
+                        * max_th the packet is dropped with a probability
+                        *       p_b = c_3 * avg - c_4
+                        * where c_3 = (1 - max_p) / max_th
+                        *       c_4 = 1 - 2 * max_p
+                        */
+                       p_b = SCALE_MUL((int64_t)fs->c_3, (int64_t)q->avg) -
+                           fs->c_4;
+               } else {
+                       q->count = -1;
+                       return (1);
+               }
+       } else if (q->avg > fs->min_th) {
+               /*
+                * We compute p_b using the linear dropping function
+                *       p_b = c_1 * avg - c_2
+                * where c_1 = max_p / (max_th - min_th)
+                *       c_2 = max_p * min_th / (max_th - min_th)
+                */
+               p_b = SCALE_MUL((int64_t)fs->c_1, (int64_t)q->avg) - fs->c_2;
+       }
+
+       if (fs->fs.flags & DN_QSIZE_BYTES)
+               p_b = div64((p_b * len) , fs->max_pkt_size);
+       if (++q->count == 0)
+               q->random = random() & 0xffff;
+       else {
+               /*
+                * q->count counts packets arrived since last drop, so a greater
+                * value of q->count means a greater packet drop probability.
+                */
+               if (SCALE_MUL(p_b, SCALE((int64_t)q->count)) > q->random) {
+                       q->count = 0;
+                       /* After a drop we calculate a new random value. */
+                       q->random = random() & 0xffff;
+                       return (1);     /* drop */
+               }
+       }
+       /* End of RED algorithm. */
+
+       return (0);     /* accept */
+
+}
+
+/*
+ * Enqueue a packet in q, subject to space and queue management policy
+ * (whose parameters are in q->fs).
+ * Update stats for the queue and the scheduler.
+ * Return 0 on success, 1 on drop. The packet is consumed anyways.
+ */
+int
+dn_enqueue(struct dn_queue *q, struct mbuf* m, int drop)
+{   
+       struct dn_fs *f;
+       struct dn_flow *ni;     /* stats for scheduler instance */
+       uint64_t len;
+
+       if (q->fs == NULL || q->_si == NULL) {
+               printf("%s fs %p si %p, dropping\n",
+                       __FUNCTION__, q->fs, q->_si);
+               FREE_PKT(m);
+               return 1;
+       }
+       f = &(q->fs->fs);
+       ni = &q->_si->ni;
+       len = m->m_pkthdr.len;
+       /* Update statistics, then check reasons to drop pkt. */
+       q->ni.tot_bytes += len;
+       q->ni.tot_pkts++;
+       ni->tot_bytes += len;
+       ni->tot_pkts++;
+       if (drop)
+               goto drop;
+       if (f->plr && random() < f->plr)
+               goto drop;
+       if (f->flags & DN_IS_RED && red_drops(q, m->m_pkthdr.len))
+               goto drop;
+       if (f->flags & DN_QSIZE_BYTES) {
+               if (q->ni.len_bytes > f->qsize)
+                       goto drop;
+       } else if (q->ni.length >= f->qsize) {
+               goto drop;
+       }
+       mq_append(&q->mq, m);
+       q->ni.length++;
+       q->ni.len_bytes += len;
+       ni->length++;
+       ni->len_bytes += len;
+       return 0;
+
+drop:
+       io_pkt_drop++;
+       q->ni.drops++;
+       ni->drops++;
+       FREE_PKT(m);
+       return 1;
+}
+
+/*
+ * Fetch packets from the delay line which are due now. If there are
+ * leftover packets, reinsert the delay line in the heap.
+ * Runs under scheduler lock.
+ */
+static void
+transmit_event(struct mq *q, struct delay_line *dline, uint64_t now)
+{
+       struct mbuf *m;
+       struct dn_pkt_tag *pkt = NULL;
+
+       dline->oid.subtype = 0; /* not in heap */
+       while ((m = dline->mq.head) != NULL) {
+               pkt = dn_tag_get(m);
+               if (!DN_KEY_LEQ(pkt->output_time, now))
+                       break;
+               dline->mq.head = m->m_nextpkt;
+               mq_append(q, m);
+       }
+       if (m != NULL) {
+               dline->oid.subtype = 1; /* in heap */
+               heap_insert(&dn_cfg.evheap, pkt->output_time, dline);
+       }
+}
+
+/*
+ * Convert the additional MAC overheads/delays into an equivalent
+ * number of bits for the given data rate. The samples are
+ * in milliseconds so we need to divide by 1000.
+ */
+static uint64_t
+extra_bits(struct mbuf *m, struct dn_schk *s)
+{
+       int index;
+       uint64_t bits;
+       struct dn_profile *pf = s->profile;
+
+       if (!pf || pf->samples_no == 0)
+               return 0;
+       index  = random() % pf->samples_no;
+       bits = div64((uint64_t)pf->samples[index] * s->link.bandwidth, 1000);
+       if (index >= pf->loss_level) {
+               struct dn_pkt_tag *dt = dn_tag_get(m);
+               if (dt)
+                       dt->dn_dir = DIR_DROP;
+       }
+       return bits;
+}
+
+/*
+ * Send traffic from a scheduler instance due by 'now'.
+ * Return a pointer to the head of the queue.
+ */
+static struct mbuf *
+serve_sched(struct mq *q, struct dn_sch_inst *si, uint64_t now)
+{
+       struct mq def_q;
+       struct dn_schk *s = si->sched;
+       struct mbuf *m = NULL;
+       int delay_line_idle = (si->dline.mq.head == NULL);
+       int done, bw;
+
+       if (q == NULL) {
+               q = &def_q;
+               q->head = NULL;
+       }
+
+       bw = s->link.bandwidth;
+       si->kflags &= ~DN_ACTIVE;
+
+       if (bw > 0)
+               si->credit += (now - si->sched_time) * bw;
+       else
+               si->credit = 0;
+       si->sched_time = now;
+       done = 0;
+       while (si->credit >= 0 && (m = s->fp->dequeue(si)) != NULL) {
+               if (m->m_pkthdr.len < 0) {
+                       /* Received a packet with negative length.
+                        * the scheduler instance will be waken up after
+                        * -m->m_pkthdr.len ticks.
+                        */
+                       si->kflags |= DN_ACTIVE;
+                       heap_insert(&dn_cfg.evheap, now - m->m_pkthdr.len, si);
+
+                       /* Delete the fake packet */
+                       free(m, M_DUMMYNET);
+
+                       /* Dont' touch credit, exit from the function */
+                       return NULL;
+               } else {                /* normal behaviour */
+                       uint64_t len_scaled;
+                       done++;
+                       len_scaled = (bw == 0) ? 0 : hz *
+                               (m->m_pkthdr.len * 8 + extra_bits(m, s));
+                       si->credit -= len_scaled;
+                       /* Move packet in the delay line */
+                       dn_tag_get(m)->output_time += s->link.delay ;
+                       mq_append(&si->dline.mq, m);
+               }
+       }
+       /*
+        * If credit >= 0 the instance is idle, mark time.
+        * Otherwise put back in the heap, and adjust the output
+        * time of the last inserted packet, m, which was too early.
+        */
+       if (si->credit >= 0) {
+               si->idle_time = now;
+       } else {
+               uint64_t t;
+               KASSERT (bw > 0, ("bw=0 and credit<0 ?"));
+               t = div64(bw - 1 - si->credit, bw);
+               if (m)
+                       dn_tag_get(m)->output_time += t;
+               si->kflags |= DN_ACTIVE;
+               heap_insert(&dn_cfg.evheap, now + t, si);
+       }
+       if (delay_line_idle && done)
+               transmit_event(q, &si->dline, now);
+       return q->head;
+}
+
+/*
+ * The timer handler for dummynet. Time is computed in ticks, but
+ * but the code is tolerant to the actual rate at which this is called.
+ * Once complete, the function reschedules itself for the next tick.
+ */
+void
+dummynet_task(void *context, int pending)
+{
+       struct timeval t;
+       struct mq q = { NULL, NULL }; /* queue to accumulate results */
+       
+       DN_BH_WLOCK();
+
+       /* Update number of lost(coalesced) ticks. */
+       tick_lost += pending - 1;
+
+       getmicrouptime(&t);
+       /* Last tick duration (usec). */
+       tick_last = (t.tv_sec - dn_cfg.prev_t.tv_sec) * 1000000 +
+       (t.tv_usec - dn_cfg.prev_t.tv_usec);
+       /* Last tick vs standard tick difference (usec). */
+       tick_delta = (tick_last * hz - 1000000) / hz;
+       /* Accumulated tick difference (usec). */
+       tick_delta_sum += tick_delta;
+
+       dn_cfg.prev_t = t;
+
+       /*
+       * Adjust curr_time if the accumulated tick difference is
+       * greater than the 'standard' tick. Since curr_time should
+       * be monotonically increasing, we do positive adjustments
+       * as required, and throttle curr_time in case of negative
+       * adjustment.
+       */
+       dn_cfg.curr_time++;
+       if (tick_delta_sum - tick >= 0) {
+               int diff = tick_delta_sum / tick;
+
+               dn_cfg.curr_time += diff;
+               tick_diff += diff;
+               tick_delta_sum %= tick;
+               tick_adjustment++;
+       } else if (tick_delta_sum + tick <= 0) {
+               dn_cfg.curr_time--;
+               tick_diff--;
+               tick_delta_sum += tick;
+               tick_adjustment++;
+       }
+
+       /* serve pending events, accumulate in q */
+       for (;;) {
+               struct dn_id *p;    /* generic parameter to handler */
+
+               if (dn_cfg.evheap.elements == 0 ||
+                   DN_KEY_LT(dn_cfg.curr_time, HEAP_TOP(&dn_cfg.evheap)->key))
+                       break;
+               p = HEAP_TOP(&dn_cfg.evheap)->object;
+               heap_extract(&dn_cfg.evheap, NULL);
+
+               if (p->type == DN_SCH_I) {
+                       serve_sched(&q, (struct dn_sch_inst *)p, dn_cfg.curr_time);
+               } else { /* extracted a delay line */
+                       transmit_event(&q, (struct delay_line *)p, dn_cfg.curr_time);
+               }
+       }
+       if (dn_cfg.expire && ++dn_cfg.expire_cycle >= dn_cfg.expire) {
+               dn_cfg.expire_cycle = 0;
+       dn_drain_scheduler();
+       dn_drain_queue();
+       }
+
+       DN_BH_WUNLOCK();
+       dn_reschedule();
+       if (q.head != NULL)
+               dummynet_send(q.head);
+}
+
+/*
+ * forward a chain of packets to the proper destination.
+ * This runs outside the dummynet lock.
+ */
+static void
+dummynet_send(struct mbuf *m)
+{
+       struct mbuf *n;
+
+       for (; m != NULL; m = n) {
+               struct ifnet *ifp = NULL;       /* gcc 3.4.6 complains */
+               struct m_tag *tag;
+               int dst;
+
+               n = m->m_nextpkt;
+               m->m_nextpkt = NULL;
+               tag = m_tag_first(m);
+               if (tag == NULL) { /* should not happen */
+                       dst = DIR_DROP;
+               } else {
+                       struct dn_pkt_tag *pkt = dn_tag_get(m);
+                       /* extract the dummynet info, rename the tag
+                        * to carry reinject info.
+                        */
+                       dst = pkt->dn_dir;
+                       ifp = pkt->ifp;
+                       tag->m_tag_cookie = MTAG_IPFW_RULE;
+                       tag->m_tag_id = 0;
+               }
+
+               switch (dst) {
+               case DIR_OUT:
+                       SET_HOST_IPLEN(mtod(m, struct ip *));
+                       ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL);
+                       break ;
+
+               case DIR_IN :
+                       /* put header in network format for ip_input() */
+                       //SET_NET_IPLEN(mtod(m, struct ip *));
+                       netisr_dispatch(NETISR_IP, m);
+                       break;
+
+#ifdef INET6
+               case DIR_IN | PROTO_IPV6:
+                       netisr_dispatch(NETISR_IPV6, m);
+                       break;
+
+               case DIR_OUT | PROTO_IPV6:
+                       SET_HOST_IPLEN(mtod(m, struct ip *));
+                       ip6_output(m, NULL, NULL, IPV6_FORWARDING, NULL, NULL, NULL);
+                       break;
+#endif
+
+               case DIR_FWD | PROTO_IFB: /* DN_TO_IFB_FWD: */
+                       if (bridge_dn_p != NULL)
+                               ((*bridge_dn_p)(m, ifp));
+                       else
+                               printf("dummynet: if_bridge not loaded\n");
+
+                       break;
+
+               case DIR_IN | PROTO_LAYER2: /* DN_TO_ETH_DEMUX: */
+                       /*
+                        * The Ethernet code assumes the Ethernet header is
+                        * contiguous in the first mbuf header.
+                        * Insure this is true.
+                        */
+                       if (m->m_len < ETHER_HDR_LEN &&
+                           (m = m_pullup(m, ETHER_HDR_LEN)) == NULL) {
+                               printf("dummynet/ether: pullup failed, "
+                                   "dropping packet\n");
+                               break;
+                       }
+                       ether_demux(m->m_pkthdr.rcvif, m);
+                       break;
+
+               case DIR_OUT | PROTO_LAYER2: /* N_TO_ETH_OUT: */
+                       ether_output_frame(ifp, m);
+                       break;
+
+               case DIR_DROP:
+                       /* drop the packet after some time */
+                       FREE_PKT(m);
+                       break;
+
+               default:
+                       printf("dummynet: bad switch %d!\n", dst);
+                       FREE_PKT(m);
+                       break;
+               }
+       }
+}
+
+static inline int
+tag_mbuf(struct mbuf *m, int dir, struct ip_fw_args *fwa)
+{
+       struct dn_pkt_tag *dt;
+       struct m_tag *mtag;
+
+       mtag = m_tag_get(PACKET_TAG_DUMMYNET,
+                   sizeof(*dt), M_NOWAIT | M_ZERO);
+       if (mtag == NULL)
+               return 1;               /* Cannot allocate packet header. */
+       m_tag_prepend(m, mtag);         /* Attach to mbuf chain. */
+       dt = (struct dn_pkt_tag *)(mtag + 1);
+       dt->rule = fwa->rule;
+       dt->rule.info &= IPFW_ONEPASS;  /* only keep this info */
+       dt->dn_dir = dir;
+       dt->ifp = fwa->oif;
+       /* dt->output tame is updated as we move through */
+       dt->output_time = dn_cfg.curr_time;
+       return 0;
+}
+
+
+/*
+ * dummynet hook for packets.
+ * We use the argument to locate the flowset fs and the sched_set sch
+ * associated to it. The we apply flow_mask and sched_mask to
+ * determine the queue and scheduler instances.
+ *
+ * dir         where shall we send the packet after dummynet.
+ * *m0         the mbuf with the packet
+ * ifp         the 'ifp' parameter from the caller.
+ *             NULL in ip_input, destination interface in ip_output,
+ */
+int
+dummynet_io(struct mbuf **m0, int dir, struct ip_fw_args *fwa)
+{
+       struct mbuf *m = *m0;
+       struct dn_fsk *fs = NULL;
+       struct dn_sch_inst *si;
+       struct dn_queue *q = NULL;      /* default */
+
+       int fs_id = (fwa->rule.info & IPFW_INFO_MASK) +
+               ((fwa->rule.info & IPFW_IS_PIPE) ? 2*DN_MAX_ID : 0);
+       DN_BH_WLOCK();
+       io_pkt++;
+       /* we could actually tag outside the lock, but who cares... */
+       if (tag_mbuf(m, dir, fwa))
+               goto dropit;
+       if (dn_cfg.busy) {
+               /* if the upper half is busy doing something expensive,
+                * lets queue the packet and move forward
+                */
+               mq_append(&dn_cfg.pending, m);
+               m = *m0 = NULL; /* consumed */
+               goto done; /* already active, nothing to do */
+       }
+       /* XXX locate_flowset could be optimised with a direct ref. */
+       fs = dn_ht_find(dn_cfg.fshash, fs_id, 0, NULL);
+       if (fs == NULL)
+               goto dropit;    /* This queue/pipe does not exist! */
+       if (fs->sched == NULL)  /* should not happen */
+               goto dropit;
+       /* find scheduler instance, possibly applying sched_mask */
+       si = ipdn_si_find(fs->sched, &(fwa->f_id));
+       if (si == NULL)
+               goto dropit;
+       /*
+        * If the scheduler supports multiple queues, find the right one
+        * (otherwise it will be ignored by enqueue).
+        */
+       if (fs->sched->fp->flags & DN_MULTIQUEUE) {
+               q = ipdn_q_find(fs, si, &(fwa->f_id));
+               if (q == NULL)
+                       goto dropit;
+       }
+       if (fs->sched->fp->enqueue(si, q, m)) {
+               printf("%s dropped by enqueue\n", __FUNCTION__);
+               /* packet was dropped by enqueue() */
+               m = *m0 = NULL;
+               goto dropit;
+       }
+
+       if (si->kflags & DN_ACTIVE) {
+               m = *m0 = NULL; /* consumed */
+               goto done; /* already active, nothing to do */
+       }
+
+       /* compute the initial allowance */
+       {
+           struct dn_link *p = &fs->sched->link;
+           si->credit = dn_cfg.io_fast ? p->bandwidth : 0;
+           if (p->burst) {
+               uint64_t burst = (dn_cfg.curr_time - si->idle_time) * p->bandwidth;
+               if (burst > p->burst)
+                       burst = p->burst;
+               si->credit += burst;
+           }
+       }
+       /* pass through scheduler and delay line */
+       m = serve_sched(NULL, si, dn_cfg.curr_time);
+
+       /* optimization -- pass it back to ipfw for immediate send */
+       /* XXX Don't call dummynet_send() if scheduler return the packet
+        *     just enqueued. This avoid a lock order reversal.
+        *     
+        */
+       if (/*dn_cfg.io_fast &&*/ m == *m0 && (dir & PROTO_LAYER2) == 0 ) {
+               /* fast io, rename the tag * to carry reinject info. */
+               struct m_tag *tag = m_tag_first(m);
+
+               tag->m_tag_cookie = MTAG_IPFW_RULE;
+               tag->m_tag_id = 0;
+               io_pkt_fast++;
+               if (m->m_nextpkt != NULL) {
+                       printf("dummynet: fast io: pkt chain detected!\n");
+                       m->m_nextpkt = NULL;
+               }
+               m = NULL;
+       } else {
+               *m0 = NULL;
+       }
+done:
+       DN_BH_WUNLOCK();
+       if (m)
+               dummynet_send(m);
+       return 0;
+
+dropit:
+       io_pkt_drop++;
+       DN_BH_WUNLOCK();
+       if (m)
+               FREE_PKT(m);
+       *m0 = NULL;
+       return (fs && (fs->fs.flags & DN_NOERROR)) ? 0 : ENOBUFS;
+}
index bb34c04..817e07f 100644 (file)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 1998-2002 Luigi Rizzo, Universita` di Pisa
+ * Copyright (c) 1998-2002,2010 Luigi Rizzo, Universita` di Pisa
  * Portions Copyright (c) 2000 Akamba Corp.
  * All rights reserved
  *
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: head/sys/netinet/ipfw/ip_dummynet.c 200601 2009-12-16 10:48:40Z luigi $");
-
-#define        DUMMYNET_DEBUG
-
-#include "opt_inet6.h"
+__FBSDID("$FreeBSD: user/luigi/ipfw3-head/sys/netinet/ipfw/ip_dummynet.c 203340 2010-02-01 12:06:37Z luigi $");
 
 /*
- * This module implements IP dummynet, a bandwidth limiter/delay emulator
- * used in conjunction with the ipfw package.
- * Description of the data structures used is in ip_dummynet.h
- * Here you mainly find the following blocks of code:
- *  + variable declarations;
- *  + heap management functions;
- *  + scheduler and dummynet functions;
- *  + configuration and initialization.
- *
- * NOTA BENE: critical sections are protected by the "dummynet lock".
- *
- * Most important Changes:
- *
- * 011004: KLDable
- * 010124: Fixed WF2Q behaviour
- * 010122: Fixed spl protection.
- * 000601: WF2Q support
- * 000106: large rewrite, use heaps to handle very many pipes.
- * 980513:     initial release
- *
- * include files marked with XXX are probably not needed
+ * Configuration and internal object management for dummynet.
  */
 
+#include "opt_inet6.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/malloc.h>
@@ -69,2234 +47,2116 @@ __FBSDID("$FreeBSD: head/sys/netinet/ipfw/ip_dummynet.c 200601 2009-12-16 10:48:
 #include <sys/socket.h>
 #include <sys/socketvar.h>
 #include <sys/time.h>
-#include <sys/sysctl.h>
 #include <sys/taskqueue.h>
 #include <net/if.h>    /* IFNAMSIZ, struct ifaddr, ifq head, lock.h mutex.h */
-#include <net/netisr.h>
 #include <netinet/in.h>
-#include <netinet/ip.h>                /* ip_len, ip_off */
+#include <netinet/ip_var.h>    /* ip_output(), IP_FORWARDING */
 #include <netinet/ip_fw.h>
 #include <netinet/ipfw/ip_fw_private.h>
+#include <netinet/ipfw/dn_heap.h>
 #include <netinet/ip_dummynet.h>
-#include <netinet/ip_var.h>    /* ip_output(), IP_FORWARDING */
-
-#include <netinet/if_ether.h> /* various ether_* routines */
-
-#include <netinet/ip6.h>       /* for ip6_input, ip6_output prototypes */
-#include <netinet6/ip6_var.h>
+#include <netinet/ipfw/ip_dn_private.h>
+#include <netinet/ipfw/dn_sched.h>
+
+/* which objects to copy */
+#define DN_C_LINK      0x01
+#define DN_C_SCH       0x02
+#define DN_C_FLOW      0x04
+#define DN_C_FS                0x08
+#define DN_C_QUEUE     0x10
+
+/* we use this argument in case of a schk_new */
+struct schk_new_arg {
+       struct dn_alg *fp;
+       struct dn_sch *sch;
+};
 
-/*
- * We keep a private variable for the simulation time, but we could
- * probably use an existing one ("softticks" in sys/kern/kern_timeout.c)
- */
-static dn_key curr_time = 0 ; /* current simulation time */
+/*---- callout hooks. ----*/
+static struct callout dn_timeout;
+static struct task     dn_task;
+static struct taskqueue        *dn_tq = NULL;
 
-static int dn_hash_size = 64 ; /* default hash size */
+/* dummynet and ipfw_tick can't be static in windows */
+void
+dummynet(void * __unused unused)
+{
 
-/* statistics on number of queue searches and search steps */
-static long searches, search_steps ;
-static int pipe_expire = 1 ;   /* expire queue if empty */
-static int dn_max_ratio = 16 ; /* max queues/buckets ratio */
+       taskqueue_enqueue(dn_tq, &dn_task);
+}
 
-static long pipe_slot_limit = 100; /* Foot shooting limit for pipe queues. */
-static long pipe_byte_limit = 1024 * 1024;
+void
+dn_reschedule(void)
+{
+       callout_reset(&dn_timeout, 1, dummynet, NULL);
+}
+/*----- end of callout hooks -----*/
 
-static int red_lookup_depth = 256;     /* RED - default lookup table depth */
-static int red_avg_pkt_size = 512;      /* RED - default medium packet size */
-static int red_max_pkt_size = 1500;     /* RED - default max packet size */
+/* Return a scheduler descriptor given the type or name. */
+static struct dn_alg *
+find_sched_type(int type, char *name)
+{
+       struct dn_alg *d;
 
-static struct timeval prev_t, t;
-static long tick_last;                 /* Last tick duration (usec). */
-static long tick_delta;                        /* Last vs standard tick diff (usec). */
-static long tick_delta_sum;            /* Accumulated tick difference (usec).*/
-static long tick_adjustment;           /* Tick adjustments done. */
-static long tick_lost;                 /* Lost(coalesced) ticks number. */
-/* Adjusted vs non-adjusted curr_time difference (ticks). */
-static long tick_diff;
+       SLIST_FOREACH(d, &dn_cfg.schedlist, next) {
+               if (d->type == type || (name && !strcmp(d->name, name)))
+                       return d;
+       }
+       return NULL; /* not found */
+}
 
-static int             io_fast;
-static unsigned long   io_pkt;
-static unsigned long   io_pkt_fast;
-static unsigned long   io_pkt_drop;
+int
+ipdn_bound_var(int *v, int dflt, int lo, int hi, const char *msg)
+{
+       int oldv = *v;
+       const char *op = NULL;
+       if (oldv < lo) {
+               *v = dflt;
+               op = "Bump";
+       } else if (oldv > hi) {
+               *v = hi;
+               op = "Clamp";
+       } else
+               return *v;
+       if (op && msg)
+               printf("%s %s to %d (was %d)\n", op, msg, *v, oldv);
+       return *v;
+}
 
+/*---- flow_id mask, hash and compare functions ---*/
 /*
- * Three heaps contain queues and pipes that the scheduler handles:
- *
- * ready_heap contains all dn_flow_queue related to fixed-rate pipes.
- *
- * wfq_ready_heap contains the pipes associated with WF2Q flows
- *
- * extract_heap contains pipes associated with delay lines.
- *
+ * The flow_id includes the 5-tuple, the queue/pipe number
+ * which we store in the extra area in host order,
+ * and for ipv6 also the flow_id6.
+ * XXX see if we want the tos byte (can store in 'flags')
  */
+static struct ipfw_flow_id *
+flow_id_mask(struct ipfw_flow_id *mask, struct ipfw_flow_id *id)
+{
+       int is_v6 = IS_IP6_FLOW_ID(id);
 
-MALLOC_DEFINE(M_DUMMYNET, "dummynet", "dummynet heap");
-
-static struct dn_heap ready_heap, extract_heap, wfq_ready_heap ;
-
-static int     heap_init(struct dn_heap *h, int size);
-static int     heap_insert (struct dn_heap *h, dn_key key1, void *p);
-static void    heap_extract(struct dn_heap *h, void *obj);
-static void    transmit_event(struct dn_pipe *pipe, struct mbuf **head,
-                   struct mbuf **tail);
-static void    ready_event(struct dn_flow_queue *q, struct mbuf **head,
-                   struct mbuf **tail);
-static void    ready_event_wfq(struct dn_pipe *p, struct mbuf **head,
-                   struct mbuf **tail);
-
-#define        HASHSIZE        16
-#define        HASH(num)       ((((num) >> 8) ^ ((num) >> 4) ^ (num)) & 0x0f)
-static struct dn_pipe_head     pipehash[HASHSIZE];     /* all pipes */
-static struct dn_flow_set_head flowsethash[HASHSIZE];  /* all flowsets */
-
-static struct callout dn_timeout;
-
-extern void (*bridge_dn_p)(struct mbuf *, struct ifnet *);
-
-#ifdef SYSCTL_NODE
-SYSCTL_DECL(_net_inet);
-SYSCTL_DECL(_net_inet_ip);
-
-SYSCTL_NODE(_net_inet_ip, OID_AUTO, dummynet, CTLFLAG_RW, 0, "Dummynet");
-SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, hash_size,
-    CTLFLAG_RW, &dn_hash_size, 0, "Default hash table size");
-#if 0  /* curr_time is 64 bit */
-SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, curr_time,
-    CTLFLAG_RD, &curr_time, 0, "Current tick");
-#endif
-SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, ready_heap,
-    CTLFLAG_RD, &ready_heap.size, 0, "Size of ready heap");
-SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, extract_heap,
-    CTLFLAG_RD, &extract_heap.size, 0, "Size of extract heap");
-SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, searches,
-    CTLFLAG_RD, &searches, 0, "Number of queue searches");
-SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, search_steps,
-    CTLFLAG_RD, &search_steps, 0, "Number of queue search steps");
-SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, expire,
-    CTLFLAG_RW, &pipe_expire, 0, "Expire queue if empty");
-SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, max_chain_len,
-    CTLFLAG_RW, &dn_max_ratio, 0,
-    "Max ratio between dynamic queues and buckets");
-SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, red_lookup_depth,
-    CTLFLAG_RD, &red_lookup_depth, 0, "Depth of RED lookup table");
-SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, red_avg_pkt_size,
-    CTLFLAG_RD, &red_avg_pkt_size, 0, "RED Medium packet size");
-SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, red_max_pkt_size,
-    CTLFLAG_RD, &red_max_pkt_size, 0, "RED Max packet size");
-SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_delta,
-    CTLFLAG_RD, &tick_delta, 0, "Last vs standard tick difference (usec).");
-SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_delta_sum,
-    CTLFLAG_RD, &tick_delta_sum, 0, "Accumulated tick difference (usec).");
-SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_adjustment,
-    CTLFLAG_RD, &tick_adjustment, 0, "Tick adjustments done.");
-SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_diff,
-    CTLFLAG_RD, &tick_diff, 0,
-    "Adjusted vs non-adjusted curr_time difference (ticks).");
-SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, tick_lost,
-    CTLFLAG_RD, &tick_lost, 0,
-    "Number of ticks coalesced by dummynet taskqueue.");
-SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, io_fast,
-    CTLFLAG_RW, &io_fast, 0, "Enable fast dummynet io.");
-SYSCTL_ULONG(_net_inet_ip_dummynet, OID_AUTO, io_pkt,
-    CTLFLAG_RD, &io_pkt, 0,
-    "Number of packets passed to dummynet.");
-SYSCTL_ULONG(_net_inet_ip_dummynet, OID_AUTO, io_pkt_fast,
-    CTLFLAG_RD, &io_pkt_fast, 0,
-    "Number of packets bypassed dummynet scheduler.");
-SYSCTL_ULONG(_net_inet_ip_dummynet, OID_AUTO, io_pkt_drop,
-    CTLFLAG_RD, &io_pkt_drop, 0,
-    "Number of packets dropped by dummynet.");
-SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, pipe_slot_limit,
-    CTLFLAG_RW, &pipe_slot_limit, 0, "Upper limit in slots for pipe queue.");
-SYSCTL_LONG(_net_inet_ip_dummynet, OID_AUTO, pipe_byte_limit,
-    CTLFLAG_RW, &pipe_byte_limit, 0, "Upper limit in bytes for pipe queue.");
-#endif
-
-#ifdef DUMMYNET_DEBUG
-int    dummynet_debug = 0;
-#ifdef SYSCTL_NODE
-SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, debug, CTLFLAG_RW, &dummynet_debug,
-           0, "control debugging printfs");
-#endif
-#define        DPRINTF(X)      if (dummynet_debug) printf X
-#else
-#define        DPRINTF(X)
-#endif
-
-static struct task     dn_task;
-static struct taskqueue        *dn_tq = NULL;
-static void dummynet_task(void *, int);
-
-#if defined( __linux__ ) || defined( _WIN32 )
-static DEFINE_SPINLOCK(dummynet_mtx);
-#else
-static struct mtx dummynet_mtx;
-#endif
-#define        DUMMYNET_LOCK_INIT() \
-       mtx_init(&dummynet_mtx, "dummynet", NULL, MTX_DEF)
-#define        DUMMYNET_LOCK_DESTROY() mtx_destroy(&dummynet_mtx)
-#define        DUMMYNET_LOCK()         mtx_lock(&dummynet_mtx)
-#define        DUMMYNET_UNLOCK()       mtx_unlock(&dummynet_mtx)
-#define        DUMMYNET_LOCK_ASSERT()  mtx_assert(&dummynet_mtx, MA_OWNED)
-
-static int     config_pipe(struct dn_pipe *p);
-static int     ip_dn_ctl(struct sockopt *sopt);
-
-static void    dummynet(void *);
-static void    dummynet_flush(void);
-static void    dummynet_send(struct mbuf *);
-void           dummynet_drain(void);
-static int     dummynet_io(struct mbuf **, int , struct ip_fw_args *);
+       id->dst_port &= mask->dst_port;
+       id->src_port &= mask->src_port;
+       id->proto &= mask->proto;
+       id->extra &= mask->extra;
+       if (is_v6) {
+               APPLY_MASK(&id->dst_ip6, &mask->dst_ip6);
+               APPLY_MASK(&id->src_ip6, &mask->src_ip6);
+               id->flow_id6 &= mask->flow_id6;
+       } else {
+               id->dst_ip &= mask->dst_ip;
+               id->src_ip &= mask->src_ip;
+       }
+       return id;
+}
 
-/*
- * Flow queue is idle if:
- *   1) it's empty for at least 1 tick
- *   2) it has invalid timestamp (WF2Q case)
- *   3) parent pipe has no 'exhausted' burst.
- */
-#define QUEUE_IS_IDLE(q) ((q)->head == NULL && (q)->S == (q)->F + 1 && \
-       curr_time > (q)->idle_time + 1 && \
-       ((q)->numbytes + (curr_time - (q)->idle_time - 1) * \
-       (q)->fs->pipe->bandwidth >= (q)->fs->pipe->burst))
+/* computes an OR of two masks, result in dst and also returned */
+static struct ipfw_flow_id *
+flow_id_or(struct ipfw_flow_id *src, struct ipfw_flow_id *dst)
+{
+       int is_v6 = IS_IP6_FLOW_ID(dst);
 
-/*
- * Heap management functions.
- *
- * In the heap, first node is element 0. Children of i are 2i+1 and 2i+2.
- * Some macros help finding parent/children so we can optimize them.
- *
- * heap_init() is called to expand the heap when needed.
- * Increment size in blocks of 16 entries.
- * XXX failure to allocate a new element is a pretty bad failure
- * as we basically stall a whole queue forever!!
- * Returns 1 on error, 0 on success
- */
-#define HEAP_FATHER(x) ( ( (x) - 1 ) / 2 )
-#define HEAP_LEFT(x) ( 2*(x) + 1 )
-#define HEAP_IS_LEFT(x) ( (x) & 1 )
-#define HEAP_RIGHT(x) ( 2*(x) + 2 )
-#define        HEAP_SWAP(a, b, buffer) { buffer = a ; a = b ; b = buffer ; }
-#define HEAP_INCREMENT 15
+       dst->dst_port |= src->dst_port;
+       dst->src_port |= src->src_port;
+       dst->proto |= src->proto;
+       dst->extra |= src->extra;
+       if (is_v6) {
+#define OR_MASK(_d, _s)                          \
+    (_d)->__u6_addr.__u6_addr32[0] |= (_s)->__u6_addr.__u6_addr32[0]; \
+    (_d)->__u6_addr.__u6_addr32[1] |= (_s)->__u6_addr.__u6_addr32[1]; \
+    (_d)->__u6_addr.__u6_addr32[2] |= (_s)->__u6_addr.__u6_addr32[2]; \
+    (_d)->__u6_addr.__u6_addr32[3] |= (_s)->__u6_addr.__u6_addr32[3];
+               OR_MASK(&dst->dst_ip6, &src->dst_ip6);
+               OR_MASK(&dst->src_ip6, &src->src_ip6);
+#undef OR_MASK
+               dst->flow_id6 |= src->flow_id6;
+       } else {
+               dst->dst_ip |= src->dst_ip;
+               dst->src_ip |= src->src_ip;
+       }
+       return dst;
+}
 
 static int
-heap_init(struct dn_heap *h, int new_size)
+nonzero_mask(struct ipfw_flow_id *m)
 {
-    struct dn_heap_entry *p;
+       if (m->dst_port || m->src_port || m->proto || m->extra)
+               return 1;
+       if (IS_IP6_FLOW_ID(m)) {
+               return
+                       m->dst_ip6.__u6_addr.__u6_addr32[0] ||
+                       m->dst_ip6.__u6_addr.__u6_addr32[1] ||
+                       m->dst_ip6.__u6_addr.__u6_addr32[2] ||
+                       m->dst_ip6.__u6_addr.__u6_addr32[3] ||
+                       m->src_ip6.__u6_addr.__u6_addr32[0] ||
+                       m->src_ip6.__u6_addr.__u6_addr32[1] ||
+                       m->src_ip6.__u6_addr.__u6_addr32[2] ||
+                       m->src_ip6.__u6_addr.__u6_addr32[3] ||
+                       m->flow_id6;
+       } else {
+               return m->dst_ip || m->src_ip;
+       }
+}
 
-    if (h->size >= new_size ) {
-       printf("dummynet: %s, Bogus call, have %d want %d\n", __func__,
-               h->size, new_size);
-       return 0 ;
-    }
-    new_size = (new_size + HEAP_INCREMENT ) & ~HEAP_INCREMENT ;
-    p = malloc(new_size * sizeof(*p), M_DUMMYNET, M_NOWAIT);
-    if (p == NULL) {
-       printf("dummynet: %s, resize %d failed\n", __func__, new_size );
-       return 1 ; /* error */
-    }
-    if (h->size > 0) {
-       bcopy(h->p, p, h->size * sizeof(*p) );
-       free(h->p, M_DUMMYNET);
+/* XXX we may want a better hash function */
+static uint32_t
+flow_id_hash(struct ipfw_flow_id *id)
+{
+    uint32_t i;
+
+    if (IS_IP6_FLOW_ID(id)) {
+       uint32_t *d = (uint32_t *)&id->dst_ip6;
+       uint32_t *s = (uint32_t *)&id->src_ip6;
+        i = (d[0]      ) ^ (d[1])       ^
+            (d[2]      ) ^ (d[3])       ^
+            (d[0] >> 15) ^ (d[1] >> 15) ^
+            (d[2] >> 15) ^ (d[3] >> 15) ^
+            (s[0] <<  1) ^ (s[1] <<  1) ^
+            (s[2] <<  1) ^ (s[3] <<  1) ^
+            (s[0] << 16) ^ (s[1] << 16) ^
+            (s[2] << 16) ^ (s[3] << 16) ^
+            (id->dst_port << 1) ^ (id->src_port) ^
+           (id->extra) ^
+            (id->proto ) ^ (id->flow_id6);
+    } else {
+        i = (id->dst_ip)        ^ (id->dst_ip >> 15) ^
+            (id->src_ip << 1)   ^ (id->src_ip >> 16) ^
+           (id->extra) ^
+            (id->dst_port << 1) ^ (id->src_port)     ^ (id->proto);
     }
-    h->p = p ;
-    h->size = new_size ;
-    return 0 ;
+    return i;
 }
 
-/*
- * Insert element in heap. Normally, p != NULL, we insert p in
- * a new position and bubble up. If p == NULL, then the element is
- * already in place, and key is the position where to start the
- * bubble-up.
- * Returns 1 on failure (cannot allocate new heap entry)
- *
- * If offset > 0 the position (index, int) of the element in the heap is
- * also stored in the element itself at the given offset in bytes.
- */
-#define SET_OFFSET(heap, node) \
-    if (heap->offset > 0) \
-           *((int *)((char *)(heap->p[node].object) + heap->offset)) = node ;
-/*
- * RESET_OFFSET is used for sanity checks. It sets offset to an invalid value.
- */
-#define RESET_OFFSET(heap, node) \
-    if (heap->offset > 0) \
-           *((int *)((char *)(heap->p[node].object) + heap->offset)) = -1 ;
+/* Like bcmp, returns 0 if ids match, 1 otherwise. */
 static int
-heap_insert(struct dn_heap *h, dn_key key1, void *p)
-{
-    int son = h->elements ;
-
-    if (p == NULL)     /* data already there, set starting point */
-       son = key1 ;
-    else {             /* insert new element at the end, possibly resize */
-       son = h->elements ;
-       if (son == h->size) /* need resize... */
-           if (heap_init(h, h->elements+1) )
-               return 1 ; /* failure... */
-       h->p[son].object = p ;
-       h->p[son].key = key1 ;
-       h->elements++ ;
-    }
-    while (son > 0) {                          /* bubble up */
-       int father = HEAP_FATHER(son) ;
-       struct dn_heap_entry tmp  ;
-
-       if (DN_KEY_LT( h->p[father].key, h->p[son].key ) )
-           break ; /* found right position */
-       /* son smaller than father, swap and repeat */
-       HEAP_SWAP(h->p[son], h->p[father], tmp) ;
-       SET_OFFSET(h, son);
-       son = father ;
-    }
-    SET_OFFSET(h, son);
-    return 0 ;
+flow_id_cmp(struct ipfw_flow_id *id1, struct ipfw_flow_id *id2)
+{
+       int is_v6 = IS_IP6_FLOW_ID(id1);
+
+       if (!is_v6) {
+           if (IS_IP6_FLOW_ID(id2))
+               return 1; /* different address families */
+
+           return (id1->dst_ip == id2->dst_ip &&
+           id1->src_ip == id2->src_ip &&
+           id1->dst_port == id2->dst_port &&
+           id1->src_port == id2->src_port &&
+           id1->proto == id2->proto &&
+                   id1->extra == id2->extra) ? 0 : 1;
+       }
+       /* the ipv6 case */
+       return (
+           !bcmp(&id1->dst_ip6,&id2->dst_ip6, sizeof(id1->dst_ip6)) &&
+           !bcmp(&id1->src_ip6,&id2->src_ip6, sizeof(id1->src_ip6)) &&
+           id1->dst_port == id2->dst_port &&
+           id1->src_port == id2->src_port &&
+           id1->proto == id2->proto &&
+           id1->extra == id2->extra &&
+           id1->flow_id6 == id2->flow_id6) ? 0 : 1;
 }
+/*--------- end of flow-id mask, hash and compare ---------*/
 
-/*
- * remove top element from heap, or obj if obj != NULL
+/*--- support functions for the qht hashtable ----
+ * Entries are hashed by flow-id
  */
-static void
-heap_extract(struct dn_heap *h, void *obj)
+static uint32_t
+q_hash(uintptr_t key, int flags, void *arg)
 {
-    int child, father, max = h->elements - 1 ;
+       /* compute the hash slot from the flow id */
+       struct ipfw_flow_id *id = (flags & DNHT_KEY_IS_OBJ) ?
+               &((struct dn_queue *)key)->ni.fid :
+               (struct ipfw_flow_id *)key;
 
-    if (max < 0) {
-       printf("dummynet: warning, extract from empty heap 0x%p\n", h);
-       return ;
-    }
-    father = 0 ; /* default: move up smallest child */
-    if (obj != NULL) { /* extract specific element, index is at offset */
-       if (h->offset <= 0)
-           panic("dummynet: heap_extract from middle not supported on this heap!!!\n");
-       father = *((int *)((char *)obj + h->offset)) ;
-       if (father < 0 || father >= h->elements) {
-           printf("dummynet: heap_extract, father %d out of bound 0..%d\n",
-               father, h->elements);
-           panic("dummynet: heap_extract");
+       return flow_id_hash(id);
+}
+
+static int
+q_match(void *obj, uintptr_t key, int flags, void *arg)
+{
+       struct dn_queue *o = (struct dn_queue *)obj;
+       struct ipfw_flow_id *id2;
+
+       if (flags & DNHT_KEY_IS_OBJ) {
+               /* compare pointers */
+               id2 = &((struct dn_queue *)key)->ni.fid;
+       } else {
+               id2 = (struct ipfw_flow_id *)key;
        }
-    }
-    RESET_OFFSET(h, father);
-    child = HEAP_LEFT(father) ;                /* left child */
-    while (child <= max) {             /* valid entry */
-       if (child != max && DN_KEY_LT(h->p[child+1].key, h->p[child].key) )
-           child = child+1 ;           /* take right child, otherwise left */
-       h->p[father] = h->p[child] ;
-       SET_OFFSET(h, father);
-       father = child ;
-       child = HEAP_LEFT(child) ;   /* left child for next loop */
-    }
-    h->elements-- ;
-    if (father != max) {
-       /*
-        * Fill hole with last entry and bubble up, reusing the insert code
-        */
-       h->p[father] = h->p[max] ;
-       heap_insert(h, father, NULL); /* this one cannot fail */
-    }
+       return (0 == flow_id_cmp(&o->ni.fid,  id2));
 }
 
-#if 0
 /*
- * change object position and update references
- * XXX this one is never used!
+ * create a new queue instance for the given 'key'.
  */
-static void
-heap_move(struct dn_heap *h, dn_key new_key, void *object)
-{
-    int temp;
-    int i ;
-    int max = h->elements-1 ;
-    struct dn_heap_entry buf ;
-
-    if (h->offset <= 0)
-       panic("cannot move items on this heap");
-
-    i = *((int *)((char *)object + h->offset));
-    if (DN_KEY_LT(new_key, h->p[i].key) ) { /* must move up */
-       h->p[i].key = new_key ;
-       for (; i>0 && DN_KEY_LT(new_key, h->p[(temp = HEAP_FATHER(i))].key) ;
-                i = temp ) { /* bubble up */
-           HEAP_SWAP(h->p[i], h->p[temp], buf) ;
-           SET_OFFSET(h, i);
-       }
-    } else {           /* must move down */
-       h->p[i].key = new_key ;
-       while ( (temp = HEAP_LEFT(i)) <= max ) { /* found left child */
-           if ((temp != max) && DN_KEY_GT(h->p[temp].key, h->p[temp+1].key))
-               temp++ ; /* select child with min key */
-           if (DN_KEY_GT(new_key, h->p[temp].key)) { /* go down */
-               HEAP_SWAP(h->p[i], h->p[temp], buf) ;
-               SET_OFFSET(h, i);
-           } else
-               break ;
-           i = temp ;
+static void *
+q_new(uintptr_t key, int flags, void *arg)
+{   
+       struct dn_queue *q, *template = arg;
+       struct dn_fsk *fs = template->fs;
+       int size = sizeof(*q) + fs->sched->fp->q_datalen;
+
+       q = malloc(size, M_DUMMYNET, M_NOWAIT | M_ZERO);
+       if (q == NULL) {
+               D("no memory for new queue");
+               return NULL;
        }
-    }
-    SET_OFFSET(h, i);
+
+       set_oid(&q->ni.oid, DN_QUEUE, size);
+       if (fs->fs.flags & DN_QHT_HASH)
+               q->ni.fid = *(struct ipfw_flow_id *)key;
+       q->fs = fs;
+       q->_si = template->_si;
+       q->_si->q_count++;
+
+       if (fs->sched->fp->new_queue)
+               fs->sched->fp->new_queue(q);
+       dn_cfg.queue_count++;
+       return q;
 }
-#endif /* heap_move, unused */
 
 /*
- * heapify() will reorganize data inside an array to maintain the
- * heap property. It is needed when we delete a bunch of entries.
+ * Notify schedulers that a queue is going away.
+ * If (flags & DN_DESTROY), also free the packets.
+ * The version for callbacks is called q_delete_cb().
  */
 static void
-heapify(struct dn_heap *h)
+dn_delete_queue(struct dn_queue *q, int flags)
 {
-    int i ;
+       struct dn_fsk *fs = q->fs;
+
+       // D("fs %p si %p\n", fs, q->_si);
+       /* notify the parent scheduler that the queue is going away */
+       if (fs && fs->sched->fp->free_queue)
+               fs->sched->fp->free_queue(q);
+       q->_si->q_count--;
+       q->_si = NULL;
+       if (flags & DN_DESTROY) {
+               if (q->mq.head)
+                       dn_free_pkts(q->mq.head);
+               bzero(q, sizeof(*q));   // safety
+               free(q, M_DUMMYNET);
+               dn_cfg.queue_count--;
+       }
+}
 
-    for (i = 0 ; i < h->elements ; i++ )
-       heap_insert(h, i , NULL) ;
+static int
+q_delete_cb(void *q, void *arg)
+{
+       int flags = (int)(uintptr_t)arg;
+       dn_delete_queue(q, flags);
+       return (flags & DN_DESTROY) ? DNHT_SCAN_DEL : 0;
 }
 
 /*
- * cleanup the heap and free data structure
+ * calls dn_delete_queue/q_delete_cb on all queues,
+ * which notifies the parent scheduler and possibly drains packets.
+ * flags & DN_DESTROY: drains queues and destroy qht;
  */
 static void
-heap_free(struct dn_heap *h)
+qht_delete(struct dn_fsk *fs, int flags)
 {
-    if (h->size >0 )
-       free(h->p, M_DUMMYNET);
-    bzero(h, sizeof(*h) );
+       ND("fs %d start flags %d qht %p",
+               fs->fs.fs_nr, flags, fs->qht);
+       if (!fs->qht)
+               return;
+       if (fs->fs.flags & DN_QHT_HASH) {
+               dn_ht_scan(fs->qht, q_delete_cb, (void *)(uintptr_t)flags);
+               if (flags & DN_DESTROY) {
+                       dn_ht_free(fs->qht, 0);
+                       fs->qht = NULL;
+               }
+       } else {
+               dn_delete_queue((struct dn_queue *)(fs->qht), flags);
+               if (flags & DN_DESTROY)
+                       fs->qht = NULL;
+       }
 }
 
 /*
- * --- end of heap management functions ---
+ * Find and possibly create the queue for a MULTIQUEUE scheduler.
+ * We never call it for !MULTIQUEUE (the queue is in the sch_inst).
  */
+struct dn_queue *
+ipdn_q_find(struct dn_fsk *fs, struct dn_sch_inst *si,
+       struct ipfw_flow_id *id)
+{
+       struct dn_queue template;
+
+       template._si = si;
+       template.fs = fs;
+
+       if (fs->fs.flags & DN_QHT_HASH) {
+               struct ipfw_flow_id masked_id;
+               if (fs->qht == NULL) {
+                       fs->qht = dn_ht_init(NULL, fs->fs.buckets,
+                               offsetof(struct dn_queue, q_next),
+                               q_hash, q_match, q_new);
+                       if (fs->qht == NULL)
+                               return NULL;
+               }
+               masked_id = *id;
+               flow_id_mask(&fs->fsk_mask, &masked_id);
+               return dn_ht_find(fs->qht, (uintptr_t)&masked_id,
+                       DNHT_INSERT, &template);
+       } else {
+               if (fs->qht == NULL)
+                       fs->qht = q_new(0, 0, &template);
+               return (struct dn_queue *)fs->qht;
+       }
+}
+/*--- end of queue hash table ---*/
 
-/*
- * Dispose a list of packet. Use an inline functions so if we
- * need to free extra state associated to a packet, this is a
- * central point to do it.
+/*--- support functions for the sch_inst hashtable ----
+ *
+ * These are hashed by flow-id
  */
-
-static __inline void dn_free_pkts(struct mbuf *mnext)
+static uint32_t
+si_hash(uintptr_t key, int flags, void *arg)
 {
-       struct mbuf *m;
+       /* compute the hash slot from the flow id */
+       struct ipfw_flow_id *id = (flags & DNHT_KEY_IS_OBJ) ?
+               &((struct dn_sch_inst *)key)->ni.fid :
+               (struct ipfw_flow_id *)key;
 
-       while ((m = mnext) != NULL) {
-               mnext = m->m_nextpkt;
-               FREE_PKT(m);
-       }
+       return flow_id_hash(id);
 }
 
-/*
- * Return the mbuf tag holding the dummynet state.  As an optimization
- * this is assumed to be the first tag on the list.  If this turns out
- * wrong we'll need to search the list.
- */
-static struct dn_pkt_tag *
-dn_tag_get(struct mbuf *m)
+static int
+si_match(void *obj, uintptr_t key, int flags, void *arg)
 {
-    struct m_tag *mtag = m_tag_first(m);
-    KASSERT(mtag != NULL &&
-           mtag->m_tag_cookie == MTAG_ABI_COMPAT &&
-           mtag->m_tag_id == PACKET_TAG_DUMMYNET,
-           ("packet on dummynet queue w/o dummynet tag!"));
-    return (struct dn_pkt_tag *)(mtag+1);
+       struct dn_sch_inst *o = obj;
+       struct ipfw_flow_id *id2;
+
+       id2 = (flags & DNHT_KEY_IS_OBJ) ?
+               &((struct dn_sch_inst *)key)->ni.fid :
+               (struct ipfw_flow_id *)key;
+       return flow_id_cmp(&o->ni.fid,  id2) == 0;
 }
 
 /*
- * Scheduler functions:
- *
- * transmit_event() is called when the delay-line needs to enter
- * the scheduler, either because of existing pkts getting ready,
- * or new packets entering the queue. The event handled is the delivery
- * time of the packet.
- *
- * ready_event() does something similar with fixed-rate queues, and the
- * event handled is the finish time of the head pkt.
- *
- * wfq_ready_event() does something similar with WF2Q queues, and the
- * event handled is the start time of the head pkt.
- *
- * In all cases, we make sure that the data structures are consistent
- * before passing pkts out, because this might trigger recursive
- * invocations of the procedures.
+ * create a new instance for the given 'key'
+ * Allocate memory for instance, delay line and scheduler private data.
  */
-static void
-transmit_event(struct dn_pipe *pipe, struct mbuf **head, struct mbuf **tail)
+static void *
+si_new(uintptr_t key, int flags, void *arg)
 {
-       struct mbuf *m;
-       struct dn_pkt_tag *pkt;
-
-       DUMMYNET_LOCK_ASSERT();
-
-       while ((m = pipe->head) != NULL) {
-               pkt = dn_tag_get(m);
-               if (!DN_KEY_LEQ(pkt->output_time, curr_time))
-                       break;
-
-               pipe->head = m->m_nextpkt;
-               if (*tail != NULL)
-                       (*tail)->m_nextpkt = m;
-               else
-                       *head = m;
-               *tail = m;
+       struct dn_schk *s = arg;
+       struct dn_sch_inst *si;
+       int l = sizeof(*si) + s->fp->si_datalen;
+
+       si = malloc(l, M_DUMMYNET, M_NOWAIT | M_ZERO);
+       if (si == NULL)
+               goto error;
+       /* Set length only for the part passed up to userland. */
+       set_oid(&si->ni.oid, DN_SCH_I, sizeof(struct dn_flow));
+       set_oid(&(si->dline.oid), DN_DELAY_LINE,
+               sizeof(struct delay_line));
+       /* mark si and dline as outside the event queue */
+       si->ni.oid.id = si->dline.oid.id = -1;
+
+       si->sched = s;
+       si->dline.si = si;
+
+       if (s->fp->new_sched && s->fp->new_sched(si)) {
+               D("new_sched error");
+               goto error;
        }
-       if (*tail != NULL)
-               (*tail)->m_nextpkt = NULL;
+       if (s->sch.flags & DN_HAVE_MASK)
+               si->ni.fid = *(struct ipfw_flow_id *)key;
 
-       /* If there are leftover packets, put into the heap for next event. */
-       if ((m = pipe->head) != NULL) {
-               pkt = dn_tag_get(m);
-               /*
-                * XXX Should check errors on heap_insert, by draining the
-                * whole pipe p and hoping in the future we are more successful.
-                */
-               heap_insert(&extract_heap, pkt->output_time, pipe);
+       dn_cfg.si_count++;
+       return si;
+
+error:
+       if (si) {
+               bzero(si, sizeof(*si)); // safety
+               free(si, M_DUMMYNET);
        }
+        return NULL;
 }
 
-#ifndef __linux__
-#define div64(a, b)    ((int64_t)(a) / (int64_t)(b))
-#endif
 /*
- * Compute how many ticks we have to wait before being able to send
- * a packet. This is computed as the "wire time" for the packet
- * (length + extra bits), minus the credit available, scaled to ticks.
- * Check that the result is not be negative (it could be if we have
- * too much leftover credit in q->numbytes).
+ * Callback from siht to delete all scheduler instances. Remove
+ * si and delay line from the system heap, destroy all queues.
+ * We assume that all flowset have been notified and do not
+ * point to us anymore.
  */
-static inline dn_key
-set_ticks(struct mbuf *m, struct dn_flow_queue *q, struct dn_pipe *p)
+static int
+si_destroy(void *_si, void *arg)
 {
-       int64_t ret;
-
-       ret = div64( (m->m_pkthdr.len * 8 + q->extra_bits) * hz
-               - q->numbytes + p->bandwidth - 1 , p->bandwidth);
-       if (ret < 0)
-               ret = 0;
-       return ret;
+       struct dn_sch_inst *si = _si;
+       struct dn_schk *s = si->sched;
+       struct delay_line *dl = &si->dline;
+
+       if (dl->oid.subtype) /* remove delay line from event heap */
+               heap_extract(&dn_cfg.evheap, dl);
+       dn_free_pkts(dl->mq.head);      /* drain delay line */
+       if (si->kflags & DN_ACTIVE) /* remove si from event heap */
+               heap_extract(&dn_cfg.evheap, si);
+       if (s->fp->free_sched)
+               s->fp->free_sched(si);
+       bzero(si, sizeof(*si)); /* safety */
+       free(si, M_DUMMYNET);
+       dn_cf