What is it ?IPFW2+IPv6+dummynet is patch originally produced by Luigi Rizzo and later maintained by Mariano Tortoriello. (tortomari AT email.it) This patch enables dummynet support for IPv6 via IPFW2. IPFW2 implements superset of old IPv6 firewall (ip6fw) functionality. I needed the functionality of the patch primarily in RELENG_4_10 so I have fixed several small bugs in patch from Mariano, added IPFW2 support to IPv6 forwarding code, added IPv6 packet logging code (from ip6fw) code and made it all compile. Note: I am not the original author of the patch, I've just added bits of code and tweaked it for my purposes. The credits belong to L.Rizzo and his students. DownloadYou will need patch for /sbin/ipfw and kernel binaries:
Changelog
How to installInstallation process comprises of getting/patching the sources, compiling/installing new kernel and sbin/ipfw binary:
How to make release based on one of the patchesThe patches above contain both userland (/sbin/ipfw) and kernel modifications. For easier install, you can make a release from them. You will need to set couple of variables first. For building release based on FreeBSD-5.4-release, you can use the following: bname=5.4-RELEASE-i2i6d reltag=RELENG_5_4_0_RELEASE TMP_DIR=/tmp/FreeBSD--release CVS_ROOT=:pserver:anoncvs@anoncvs.at.FreeBSD.org:/home/ncvs patch=src-ipfw2-ipv6-dummynet-5.4.patch patchscript=ipfw2-script.sh The CVS server can be arbitrary public CVS from the list of FreeBSD CVS servers. The script ipfw2-script.sh set in patchscript should contain the following: #!/bin/sh # for building release with IPFW2 # presume we are in CHROOTDIR for release building now # (according to /usr/src/release/Makefile) # this script is meant to be run as LOCAL_SCRIPT echo "IPFW2=1" >> etc/make.conf After setting the variables and populating /usr/obj according to FreeBSD release building document (by executing make world in /usr/src) you can execute following script:
cd /usr/src/release
if ! make release "BUILDNAME=$bname" "RELEASETAG=$reltag" \
CHROOTDIR=$TMP_DIR CVSROOT=$CVS_ROOT NODOC=YES NOPORTS=YES \
"LOCAL_PATCHES=$patch" "LOCAL_SCRIPT=$patchscript" > $TMP_DIR.log 2>&1; then
echo "Release making failed, see $TMP_DIR.log" >&2
exit 1
fi
When the script completes its work, you will find release files in TMP_DIR/R/ftp. Known bugs/TODO
Function call flowIPv6 forwarding with dummynet
Unlike IPv4 forwarding function call flow, KAME IPv6 forwarding call flow
passes packet from ip6_input() routine to ip6_forward(),
which in turn passes the mbuf chain to nd6_output(), not
ip6_output().
The difference is that ip6_forward() does not call ip6_output() routine , but uses nd6.c:nd6_output() function which first check various ND related conditions and after it calls interface processing function ((*ifp->if_output)(ifp, m, (struct sockaddr *)dst, rt)) summary: pfil hooks need to be in ip6_input(), ip6_output() and also in ip6_forward() function. Consider following scenario:
fec0:2::2/32 fec0:2::1/32 fec0:1::1/32 fec0:1::2/32
|-----------| |----------| |---------|
| notebook |--- cross cable -- | router |------ hub -------| pc |
|-----------| 100baseTX fxp1|----------|fxp0 10baseT/UTP |---------|
full-duplex
with following rules configured on host router:
$ipfw=/sbin/ipfw ## outgoing queue $ipfw pipe 1400 config bw 64Kbit/s queue 16KBytes ## incoming queue $ipfw pipe 1401 config bw 64Kbit/s queue 16KBytes # ND $ipfw add 01000 allow ipv6 from any to ff02::/32 # via fxp1 $ipfw add 01001 allow ipv6 from fe80::/32 to fe80::/32 # via fxp1 ipv6addr="fec0:2::2/128" # queue only on fxp1 $ipfw add 01500 pipe 1400 log ipv6 from any to $ipv6addr out $ipfw add 01501 allow log ipv6 from any to $ipv6addr out $ipfw add 01600 pipe 1401 log ipv6 from $ipv6addr to any in $ipfw add 01601 allow log ipv6 from $ipv6addr to any in # default deny on fxp1 $ipfw add 65420 deny log all from any to any via fxp1 # allow the rest $ipfw add 65421 allow log ip from any to any Now, when host (pc) behind one interface sends packet to another host (notebook) behind second interface of the router, the function flow for packet sent from pc to notebook goes like this:
kernel
|---------------------------------------------------------------------
| |
| 1/HZ /<--- ip_dn_io_ptr() |
------------- /-- <---. ^ |
<--| interface |<----- ip6_output()--<----/ \ | |
| logic | ip_fw_chk_ptr() | dummynet | ip6_forward() |
------------- \ sched. | ^ |
| \ / | |
| '________/ ip_fw_chk_ptr() |
| ip6_input() |-------|
| ^ | ifc |
| (ip6intr())<--| logic |<--
| ---------
| |
---------------------------------------------------------------------|
The function flow in more detail:
The reply packet coming from notebook to pc
has similar function call flow. (rules 01600
and 01601 are matched this time)
Aug 10 10:07:57 ipfw2 /kernel: ipfw: 1500 Pipe 1400 IPV6-ICMP:128.0 [fec0:0001::0002] [fec0:0002::0002] out via fxp1 Aug 10 10:07:57 ipfw2 /kernel: ipfw: 1501 Accept IPV6-ICMP:128.0 [fec0:0001::0002] [fec0:0002::0002] out via fxp1 Aug 10 10:07:57 ipfw2 /kernel: ipfw: 1600 Pipe 1401 IPV6-ICMP:129.0 [fec0:0002::0002] [fec0:0001::0002] in via fxp1 Aug 10 10:07:57 ipfw2 /kernel: ipfw: 1601 Accept IPV6-ICMP:129.0 [fec0:0002::0002] [fec0:0001::0002] in via fxp1 Aug 10 10:07:57 ipfw2 /kernel: ipfw: 65421 Accept IPV6-ICMP:129.0 [fec0:0002::0002] [fec0:0001::0002] out via fxp0 |