? sys/arch/hpcmips/conf/build-hpcmips ? sys/arch/i386/conf/SOEKRIS ? sys/arch/i386/conf/STARFRUIT Index: etc/etc.acorn26/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.acorn26/MAKEDEV,v retrieving revision 1.11 diff -u -r1.11 MAKEDEV --- etc/etc.acorn26/MAKEDEV 2003/04/27 13:15:28 1.11 +++ etc/etc.acorn26/MAKEDEV 2003/06/30 04:58:16 @@ -83,6 +83,7 @@ # fd file descriptors (/dev/fd/*) # bpf* Berkeley packet filter devices # ipl IP Filter +# pf PF packet filter # random Random number generator, see rnd(4) # tun* network tunnel driver # cfs* Coda file system device @@ -353,6 +354,12 @@ mknod ipstate c 23 2 mknod ipauth c 23 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 31 0 + chmod 600 pf ;; random) Index: etc/etc.acorn32/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.acorn32/MAKEDEV,v retrieving revision 1.29 diff -u -r1.29 MAKEDEV --- etc/etc.acorn32/MAKEDEV 2003/06/08 20:12:33 1.29 +++ etc/etc.acorn32/MAKEDEV 2003/06/30 04:58:17 @@ -105,6 +105,7 @@ # bpf* packet filter # beep Risc PC speaker # ipl IP Filter +# pf PF packet filter # openfirm OpenFirmware accessor # random Random number generator # lkm loadable kernel modules interface @@ -153,7 +154,7 @@ makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev ttyv0 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 - makedev lpa0 lpt0 tun0 tun1 tun2 ipl + makedev lpa0 lpt0 tun0 tun1 tun2 ipl pf makedev beep lkm qms0 pms0 local makedev usbs isdns makedev mouse-qms0 random openfirm @@ -519,6 +520,12 @@ mknod ipstate c 46 2 mknod ipauth c 46 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 89 0 + chmod 600 pf ;; beep) Index: etc/etc.algor/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.algor/MAKEDEV,v retrieving revision 1.12 diff -u -r1.12 MAKEDEV --- etc/etc.algor/MAKEDEV 2003/05/07 13:41:34 1.12 +++ etc/etc.algor/MAKEDEV 2003/06/30 04:58:17 @@ -106,6 +106,7 @@ # ch* SCSI media changer # fd file descriptors (/dev/fd/*) # ipl IP Filter +# pf PF packet filter # kbd keyboard (provides events, for X11) # lkm loadable kernel modules interface # mouse mouse (provides events, for X11) @@ -156,7 +157,7 @@ makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 makedev tun0 tun1 tun2 tun3 makedev ttyC0 ttyC1 lkm - makedev lpa0 lpt0 audio ipl wd0 wd1 fd0 fd1 + makedev lpa0 lpt0 audio ipl pf wd0 wd1 fd0 fd1 makedev audio0 audio1 audio2 audio3 makedev random satlink0 speaker mlx0 local makedev scsibus0 scsibus1 scsibus2 scsibus3 @@ -647,6 +648,12 @@ mknod ipstate c 35 2 mknod ipauth c 35 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 70 0 + chmod 600 pf ;; satlink*) Index: etc/etc.alpha/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.alpha/MAKEDEV,v retrieving revision 1.111 diff -u -r1.111 MAKEDEV --- etc/etc.alpha/MAKEDEV 2003/06/01 20:25:02 1.111 +++ etc/etc.alpha/MAKEDEV 2003/06/30 04:58:18 @@ -108,6 +108,7 @@ # ch* SCSI media changer # fd file descriptors (/dev/fd/*) # ipl IP Filter +# pf PF packet filter # kbd keyboard (provides events, for X11) # lkm loadable kernel modules interface # mouse mouse (provides events, for X11) @@ -166,7 +167,7 @@ makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 makedev tun0 tun1 tun2 tun3 makedev ttyB0 ttyB1 ttyC0 ttyC1 lkm - makedev lpa0 lpt0 audio ipl wd0 wd1 fd0 fd1 + makedev lpa0 lpt0 audio ipl pf wd0 wd1 fd0 fd1 makedev audio0 audio1 audio2 audio3 makedev random satlink0 speaker mlx0 local makedev scsibus0 scsibus1 scsibus2 scsibus3 @@ -688,6 +689,12 @@ mknod ipstate c 35 2 mknod ipauth c 35 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 78 0 + chmod 600 pf ;; satlink*) Index: etc/etc.amd64/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.amd64/MAKEDEV,v retrieving revision 1.4 diff -u -r1.4 MAKEDEV --- etc/etc.amd64/MAKEDEV 2003/05/15 13:18:05 1.4 +++ etc/etc.amd64/MAKEDEV 2003/06/30 04:58:18 @@ -108,6 +108,7 @@ # bpf* packet filter # cir* Consumer IR # ipl IP Filter +# pf PF packet filter # irframe* IrDA physical frame # random Random number generator, see rnd(4) # speaker PC speaker (XXX - installed) @@ -187,7 +188,7 @@ makedev tty2 tty3 pty0 makedev lpa0 lpa1 lpa2 makedev lpt0 lpt1 lpt2 - makedev tun0 tun1 tun2 tun3 ipl + makedev tun0 tun1 tun2 tun3 ipl pf makedev bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 makedev vnd1 makedev raid1 raid2 raid3 raid4 raid5 raid6 raid7 @@ -710,6 +711,12 @@ mknod ipstate c 44 2 mknod ipauth c 44 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 86 0 + chmod 600 pf ;; speaker) # (XXX - installed) Index: etc/etc.amiga/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.amiga/MAKEDEV,v retrieving revision 1.87 diff -u -r1.87 MAKEDEV --- etc/etc.amiga/MAKEDEV 2003/04/27 13:15:30 1.87 +++ etc/etc.amiga/MAKEDEV 2003/06/30 04:58:19 @@ -110,6 +110,7 @@ # view* generic interface to graphic displays # lkm loadable kernel modules interface # ipl IP Filter +# pf PF packet filter # random Random number generator # bpf* Berkeley Packet Filter # tun* network tunnel driver @@ -161,7 +162,7 @@ makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev vnd0 vnd1 vnd2 vnd3 vnd4 vnd5 vnd6 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 - makedev tun0 tun1 lkm ipl random local + makedev tun0 tun1 lkm ipl pf random local makedev audio0 audio1 audio2 audio3 makedev scsibus0 scsibus1 scsibus2 scsibus3 makedev isdns @@ -589,6 +590,12 @@ mknod ipstate c 40 2 mknod ipauth c 40 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 59 0 + chmod 600 pf ;; audio*) Index: etc/etc.arc/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.arc/MAKEDEV,v retrieving revision 1.29 diff -u -r1.29 MAKEDEV --- etc/etc.arc/MAKEDEV 2003/04/27 13:15:31 1.29 +++ etc/etc.arc/MAKEDEV 2003/06/30 04:58:19 @@ -91,6 +91,7 @@ # ch* SCSI media changer # fd file descriptors (/dev/fd/*) # ipl IP Filter +# pf PF packet filter # kbd keyboard (provides events, for X11) # mouse mouse (provides events, for X11) # pms* PS/2 mouse @@ -136,7 +137,7 @@ makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 makedev tun0 tun1 tun2 tun3 makedev tty00 tty01 tty02 tty03 - makedev wskbd0 wsmouse0 lpt0 ipl wd0 wd1 wd2 wd3 fd0 fd1 + makedev wskbd0 wsmouse0 lpt0 ipl pf wd0 wd1 wd2 wd3 fd0 fd1 makedev ttyC0 pms0 joy0 makedev random local makedev scsibus0 scsibus1 scsibus2 scsibus3 @@ -448,6 +449,12 @@ mknod ipstate c 31 2 mknod ipauth c 31 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 56 0 + chmod 600 pf ;; random) Index: etc/etc.atari/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.atari/MAKEDEV,v retrieving revision 1.67 diff -u -r1.67 MAKEDEV --- etc/etc.atari/MAKEDEV 2003/04/27 13:15:31 1.67 +++ etc/etc.atari/MAKEDEV 2003/06/30 04:58:19 @@ -104,6 +104,7 @@ # ch SCSI changer # grf* Motherboard bitmapped video. # ipl IP Filter +# pf PF packet filter # kbd Atari keyboard. # lkm loadable kernel modules interface # random Random number generator @@ -153,7 +154,7 @@ makedev kbd sd0 sd1 sd2 sd3 sd4 sd5 sd6 sd7 sd8 sd9 vnd0 vnd1 vnd2 makedev vnd3 vnd4 view00 view01 view02 view03 view04 view05 makedev pty0 - makedev vnd5 vnd6 cd0 fd0 fd1 fd2 fd3 bpf0 bpf1 bpf2 bpf3 ipl + makedev vnd5 vnd6 cd0 fd0 fd1 fd2 fd3 bpf0 bpf1 bpf2 bpf3 ipl pf makedev ccd0 ccd1 ccd2 ccd3 makedev cgd0 cgd1 cgd2 cgd3 makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 @@ -558,6 +559,12 @@ mknod ipstate c 36 2 mknod ipauth c 36 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 57 0 + chmod 600 pf ;; random) Index: etc/etc.bebox/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.bebox/MAKEDEV,v retrieving revision 1.41 diff -u -r1.41 MAKEDEV --- etc/etc.bebox/MAKEDEV 2003/04/27 13:15:32 1.41 +++ etc/etc.bebox/MAKEDEV 2003/06/30 04:58:20 @@ -92,6 +92,7 @@ # fd file descriptors # bpf* packet filter # ipl IP Filter +# pf PF packet filter # random Random number generator # speaker PC speaker (XXX - installed) # lkm loadable kernel modules interface @@ -136,7 +137,7 @@ makedev tty0 tty1 pty0 makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev st0 st1 ch0 cd0 cd1 vnd0 vnd1 lpa0 lpa1 lpa2 - makedev lpt0 lpt1 lpt2 ttyv0 tun0 tun1 ipl + makedev lpt0 lpt1 lpt2 ttyv0 tun0 tun1 ipl pf makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 makedev ccd0 ccd1 ccd2 ccd3 md0 ss0 ch0 uk0 uk1 random ttyCY0 makedev cgd0 cgd1 cgd2 cgd3 @@ -485,6 +486,12 @@ mknod ipstate c 44 2 mknod ipauth c 44 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 62 0 + chmod 600 pf ;; speaker) # (XXX - installed) Index: etc/etc.cats/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.cats/MAKEDEV,v retrieving revision 1.25 diff -u -r1.25 MAKEDEV --- etc/etc.cats/MAKEDEV 2003/05/07 13:41:35 1.25 +++ etc/etc.cats/MAKEDEV 2003/06/30 04:58:20 @@ -101,6 +101,7 @@ # fd file descriptors # bpf* packet filter # ipl IP Filter +# pf PF packet filter # random Random number generator # lkm loadable kernel modules interface # tun* network tunnel driver @@ -149,7 +150,7 @@ makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev ttyv0 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 - makedev lpa0 lpt0 tun0 tun1 tun2 ipl + makedev lpa0 lpt0 tun0 tun1 tun2 ipl pf makedev lkm local makedev usbs isdns makedev random @@ -529,6 +530,12 @@ mknod ipstate c 46 2 mknod ipauth c 46 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 106 0 + chmod 600 pf ;; lkm) Index: etc/etc.cesfic/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.cesfic/MAKEDEV,v retrieving revision 1.6 diff -u -r1.6 MAKEDEV --- etc/etc.cesfic/MAKEDEV 2003/04/27 13:15:32 1.6 +++ etc/etc.cesfic/MAKEDEV 2003/06/30 04:58:20 @@ -30,6 +30,7 @@ # random Random number generator, see rnd(4) # systrace syscall tracer # tun* network tunnel driver +# pf PF packet filter # dialin=0 @@ -143,6 +144,12 @@ mknod tty$unit c 10 $(($ounit + $dialin )) mknod dty$unit c 10 $(($ounit + $dialout)) chown uucp tty$unit dty$unit + ;; + +pf) + rm -f pf + mknod pf c 26 0 + chmod 600 pf ;; lkm) Index: etc/etc.cobalt/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.cobalt/MAKEDEV,v retrieving revision 1.27 diff -u -r1.27 MAKEDEV --- etc/etc.cobalt/MAKEDEV 2003/04/27 13:15:33 1.27 +++ etc/etc.cobalt/MAKEDEV 2003/06/30 04:58:20 @@ -85,6 +85,7 @@ # ch* SCSI media changer # fd file descriptors (/dev/fd/*) # ipl IP Filter +# pf PF packet filter # kbd keyboard (provides events, for X11) # mouse mouse (provides events, for X11) # random Random number generator @@ -361,6 +362,12 @@ mknod ipstate c 20 2 mknod ipauth c 20 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 38 0 + chmod 600 pf ;; random) Index: etc/etc.dreamcast/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.dreamcast/MAKEDEV,v retrieving revision 1.37 diff -u -r1.37 MAKEDEV --- etc/etc.dreamcast/MAKEDEV 2003/05/15 13:18:07 1.37 +++ etc/etc.dreamcast/MAKEDEV 2003/06/30 04:58:21 @@ -83,6 +83,7 @@ # fd file descriptors # bpf* packet filter # ipl IP Filter +# pf PF packet filter # cbq Alternate Queueing (ALTQ) # random Random number generator # speaker PC speaker (XXX - installed) @@ -118,7 +119,7 @@ makedev std wscons fd gdrom0 maple makedev scif0 sci0 pty0 makedev vnd0 vnd1 - makedev bpf0 bpf1 bpf2 bpf3 tun0 tun1 ipl + makedev bpf0 bpf1 bpf2 bpf3 tun0 tun1 ipl pf makedev ccd0 ccd1 ccd2 ccd3 md0 random makedev cgd0 cgd1 cgd2 cgd3 makedev lkm audio local @@ -386,6 +387,12 @@ mknod ipstate c 44 2 mknod ipauth c 44 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 65 0 + chmod 600 pf ;; lkm) Index: etc/etc.evbarm/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.evbarm/MAKEDEV,v retrieving revision 1.29 diff -u -r1.29 MAKEDEV --- etc/etc.evbarm/MAKEDEV 2003/05/07 13:41:35 1.29 +++ etc/etc.evbarm/MAKEDEV 2003/06/30 04:58:21 @@ -98,6 +98,7 @@ # fd file descriptors # bpf* packet filter # ipl IP Filter +# pf PF packet filter # random Random number generator # lkm loadable kernel modules interface # tun* network tunnel driver @@ -144,7 +145,7 @@ makedev ccd0 ccd1 uk0 uk1 ss0 makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 - makedev lpa0 lpt0 tun0 tun1 tun2 ipl + makedev lpa0 lpt0 tun0 tun1 tun2 ipl pf makedev lkm local makedev usbs isdns makedev random @@ -501,6 +502,12 @@ mknod ipstate c 46 2 mknod ipauth c 46 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 106 0 + chmod 600 pf ;; lkm) Index: etc/etc.evbmips/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.evbmips/MAKEDEV,v retrieving revision 1.14 diff -u -r1.14 MAKEDEV --- etc/etc.evbmips/MAKEDEV 2003/05/07 13:41:36 1.14 +++ etc/etc.evbmips/MAKEDEV 2003/06/30 04:58:21 @@ -108,6 +108,7 @@ # clockctl clock control for non root users # fd file descriptors (/dev/fd/*) # ipl IP Filter +# pf PF packet filter # kbd keyboard (provides events, for X11) # lkm loadable kernel modules interface # mouse mouse (provides events, for X11) @@ -162,7 +163,7 @@ makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 makedev tun0 tun1 tun2 tun3 makedev tty00 tty01 - makedev wskbd0 wsmouse0 lpa0 lpt0 audio ipl wd0 wd1 fd0 fd1 + makedev wskbd0 wsmouse0 lpa0 lpt0 audio ipl pf wd0 wd1 fd0 fd1 makedev audio0 audio1 audio2 audio3 makedev random satlink0 speaker mlx0 local makedev scsibus0 scsibus1 scsibus2 scsibus3 @@ -672,6 +673,12 @@ mknod ipstate c 35 2 mknod ipauth c 35 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 77 0 + chmod 600 pf ;; satlink*) Index: etc/etc.evbppc/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.evbppc/MAKEDEV,v retrieving revision 1.8 diff -u -r1.8 MAKEDEV --- etc/etc.evbppc/MAKEDEV 2003/05/07 13:41:36 1.8 +++ etc/etc.evbppc/MAKEDEV 2003/06/30 04:58:21 @@ -99,6 +99,7 @@ # fd file descriptors # bpf* packet filter # ipl IP Filter +# pf PF packet filter # random random number generator # lkm loadable kernel modules interface # audio* audio devices @@ -151,7 +152,7 @@ makedev tty00 tty01 pty0 makedev lpa0 lpa1 lpa2 lpt0 lpt1 lpt2 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 - makedev tun0 tun1 ipl usbs ttyCZ0 ttyCY0 audio + makedev tun0 tun1 ipl pf usbs ttyCZ0 ttyCY0 audio makedev music rmidi0 rmidi1 rmidi2 rmidi3 rmidi4 rmidi5 rmidi6 rmidi7 makedev ccd0 ccd1 ccd2 ccd3 makedev lkm local random @@ -586,6 +587,12 @@ mknod ipstate c 21 2 mknod ipauth c 21 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 75 0 + chmod 600 pf ;; lkm) Index: etc/etc.evbsh3/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.evbsh3/MAKEDEV,v retrieving revision 1.27 diff -u -r1.27 MAKEDEV --- etc/etc.evbsh3/MAKEDEV 2003/05/15 13:18:08 1.27 +++ etc/etc.evbsh3/MAKEDEV 2003/06/30 04:58:22 @@ -71,6 +71,7 @@ # fd file descriptors # bpf* packet filter # ipl IP Filter +# pf PF packet filter # cbq Alternate Queueing (ALTQ) # random Random number generator # speaker PC speaker (XXX - installed) @@ -105,7 +106,7 @@ makedev std fd fd0 makedev tty0 tty1 pty0 makedev vnd0 vnd1 - makedev bpf0 bpf1 bpf2 bpf3 tun0 tun1 ipl + makedev bpf0 bpf1 bpf2 bpf3 tun0 tun1 ipl pf makedev ccd0 ccd1 ccd2 ccd3 md0 random makedev cgd0 cgd1 cgd2 cgd3 makedev lkm audio local @@ -294,6 +295,12 @@ mknod ipstate c 44 2 mknod ipauth c 44 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 56 0 + chmod 600 pf ;; lkm) Index: etc/etc.evbsh5/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.evbsh5/MAKEDEV,v retrieving revision 1.9 diff -u -r1.9 MAKEDEV --- etc/etc.evbsh5/MAKEDEV 2003/05/15 13:18:09 1.9 +++ etc/etc.evbsh5/MAKEDEV 2003/06/30 04:58:22 @@ -70,6 +70,7 @@ # fd file descriptors # bpf* packet filter # ipl IP Filter +# pf PF packet filter # cbq Alternate Queueing (ALTQ) # random Random number generator # speaker PC speaker (XXX - installed) @@ -104,7 +105,7 @@ makedev std fd fd0 makedev tty0 tty1 pty0 makedev vnd0 vnd1 - makedev bpf0 bpf1 bpf2 bpf3 tun0 tun1 ipl + makedev bpf0 bpf1 bpf2 bpf3 tun0 tun1 ipl pf makedev ccd0 ccd1 ccd2 ccd3 md0 random makedev lkm audio local makedev clockctl @@ -289,6 +290,12 @@ mknod ipstate c 44 2 mknod ipauth c 44 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 10 0 + chmod 600 pf ;; lkm) Index: etc/etc.hp300/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.hp300/MAKEDEV,v retrieving revision 1.55 diff -u -r1.55 MAKEDEV --- etc/etc.hp300/MAKEDEV 2003/04/27 13:15:35 1.55 +++ etc/etc.hp300/MAKEDEV 2003/06/30 04:58:22 @@ -80,6 +80,7 @@ # tun* network tunnel driver # lkm loadable kernel modules interface # ipl IP Filter +# pf PF packet filter # random Random number generator # scsibus* SCSI busses, see scsi(4), scsictl(8) # systrace syscall tracer @@ -113,7 +114,7 @@ makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev hil grf0 ite0 dca0 dcm0 dcm1 dcm2 dcm3 apci0 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 - makedev tun0 tun1 tun2 tun3 lkm ipl random + makedev tun0 tun1 tun2 tun3 lkm ipl pf random makedev scsibus0 scsibus1 scsibus2 scsibus3 makedev local makedev clockctl @@ -452,6 +453,12 @@ mknod ipstate c 18 2 mknod ipauth c 18 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 46 0 + chmod 600 pf ;; opty) Index: etc/etc.hp700/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.hp700/MAKEDEV,v retrieving revision 1.12 diff -u -r1.12 MAKEDEV --- etc/etc.hp700/MAKEDEV 2003/04/27 13:15:35 1.12 +++ etc/etc.hp700/MAKEDEV 2003/06/30 04:58:22 @@ -63,6 +63,7 @@ # random Random number generator # scsibus* SCSI busses, see scsi(4), scsictl(8) # systrace syscall tracer +# pf PF packet filter PATH=/sbin:/bin:/usr/sbin:/usr/bin umask 77 @@ -249,6 +250,12 @@ rm -f systrace mknod systrace c 34 0 chmod 644 systrace + ;; + +pf) + rm -f pf + mknod pf c 37 0 + chmod 600 pf ;; local) Index: etc/etc.hpcarm/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.hpcarm/MAKEDEV,v retrieving revision 1.17 diff -u -r1.17 MAKEDEV --- etc/etc.hpcarm/MAKEDEV 2003/05/15 19:05:28 1.17 +++ etc/etc.hpcarm/MAKEDEV 2003/06/30 04:58:22 @@ -104,6 +104,7 @@ # bpf* packet filter # beep Risc PC speaker # ipl IP Filter +# pf PF packet filter # openfirm OpenFirmware accessor # random Random number generator # lkm loadable kernel modules interface @@ -151,7 +152,7 @@ makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev ttyv0 ttyS0 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 - makedev lpa0 lpt0 tun0 tun1 tun2 ipl + makedev lpa0 lpt0 tun0 tun1 tun2 ipl pf makedev beep lkm local makedev usbs isdns makedev random openfirm @@ -522,6 +523,12 @@ mknod ipstate c 46 2 mknod ipauth c 46 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 106 0 + chmod 600 pf ;; beep) Index: etc/etc.hpcmips/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.hpcmips/MAKEDEV,v retrieving revision 1.51 diff -u -r1.51 MAKEDEV --- etc/etc.hpcmips/MAKEDEV 2003/05/07 13:41:36 1.51 +++ etc/etc.hpcmips/MAKEDEV 2003/06/30 04:58:23 @@ -96,6 +96,7 @@ # fd file descriptors # bpf* packet filter # ipl IP Filter +# pf PF packet filter # lkm loadable kernel modules interface # random Random number generator # tun* network tunnel driver @@ -143,7 +144,7 @@ makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev st0 st1 ch0 cd0 cd1 vnd0 vnd1 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 - makedev lpt0 lpt1 lpt2 tun0 tun1 ipl lkm + makedev lpt0 lpt1 lpt2 tun0 tun1 ipl pf lkm makedev ccd0 ccd1 ccd2 ccd3 md0 ss0 ch0 uk0 uk1 random makedev cgd0 cgd1 cgd2 cgd3 makedev usbs isdns @@ -568,6 +569,12 @@ mknod ipstate c 27 2 mknod ipauth c 27 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 102 0 + chmod 600 pf ;; lkm) Index: etc/etc.hpcsh/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.hpcsh/MAKEDEV,v retrieving revision 1.27 diff -u -r1.27 MAKEDEV --- etc/etc.hpcsh/MAKEDEV 2003/04/27 13:15:37 1.27 +++ etc/etc.hpcsh/MAKEDEV 2003/06/30 04:58:23 @@ -73,6 +73,7 @@ # fd file descriptors # bpf* packet filter # ipl IP Filter +# pf PF packet filter # random Random number generator # tun* network tunnel driver # scsibus* SCSI busses, see scsi(4), scsictl(8) @@ -415,6 +416,12 @@ mknod ipstate c 23 2 mknod ipauth c 23 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 38 0 + chmod 600 pf ;; random) Index: etc/etc.i386/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.i386/MAKEDEV,v retrieving revision 1.202 diff -u -r1.202 MAKEDEV --- etc/etc.i386/MAKEDEV 2003/05/15 13:18:09 1.202 +++ etc/etc.i386/MAKEDEV 2003/06/30 04:58:24 @@ -108,6 +108,7 @@ # bpf* packet filter # cir* Consumer IR # ipl IP Filter +# pf PF packet filter # irframe* IrDA physical frame # random Random number generator, see rnd(4) # speaker PC speaker (XXX - installed) @@ -196,7 +197,7 @@ makedev tty2 tty3 pty0 makedev lpa0 lpa1 lpa2 makedev lpt0 lpt1 lpt2 - makedev tun0 tun1 tun2 tun3 ipl + makedev tun0 tun1 tun2 tun3 ipl pf makedev bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 makedev vnd1 makedev raid1 raid2 raid3 raid4 raid5 raid6 raid7 @@ -726,6 +727,12 @@ mknod ipstate c 44 2 mknod ipauth c 44 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 108 0 + chmod 600 pf ;; speaker) # (XXX - installed) Index: etc/etc.luna68k/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.luna68k/MAKEDEV,v retrieving revision 1.24 diff -u -r1.24 MAKEDEV --- etc/etc.luna68k/MAKEDEV 2003/04/27 13:15:38 1.24 +++ etc/etc.luna68k/MAKEDEV 2003/06/30 04:58:24 @@ -34,6 +34,7 @@ # ch* SCSI media changer # fd file descriptor device # ipl IP Filter +# pf PF packet filter # lkm loadable kernel modules interface # random Random number generator # scsibus* SCSI busses, see scsi(4), scsictl(8) @@ -272,6 +273,12 @@ mknod ipstate c 23 2 mknod ipauth c 23 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 39 0 + chmod 600 pf ;; random) Index: etc/etc.mac68k/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.mac68k/MAKEDEV,v retrieving revision 1.73 diff -u -r1.73 MAKEDEV --- etc/etc.mac68k/MAKEDEV 2003/04/27 13:15:38 1.73 +++ etc/etc.mac68k/MAKEDEV 2003/06/30 04:58:24 @@ -75,6 +75,7 @@ # bpf* packet filter # lkm loadable kernel modules interface # ipl IP Filter +# pf PF packet filter # random Random number generator # scsibus* SCSI busses, see scsi(4), scsictl(8) # ss* SCSI scanner @@ -118,7 +119,7 @@ makedev ttye0 adb grf0 grf1 grf2 grf3 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 makedev bpf8 bpf9 bpf10 bpf11 random - makedev tun0 tun1 tun2 tun3 asc0 lkm ipl local + makedev tun0 tun1 tun2 tun3 asc0 lkm ipl pf local makedev scsibus0 scsibus1 scsibus2 scsibus3 cfs0 makedev clockctl makedev systrace @@ -394,6 +395,12 @@ mknod ipstate c 35 2 mknod ipauth c 35 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 53 0 + chmod 600 pf ;; asc*) Index: etc/etc.macppc/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.macppc/MAKEDEV,v retrieving revision 1.78 diff -u -r1.78 MAKEDEV --- etc/etc.macppc/MAKEDEV 2003/05/07 13:41:37 1.78 +++ etc/etc.macppc/MAKEDEV 2003/06/30 04:58:25 @@ -97,6 +97,7 @@ # fd file descriptors # bpf* packet filter # ipl IP Filter +# pf PF packet filter # openfirm OpenFirmware queries # random random number generator # lkm loadable kernel modules interface @@ -150,7 +151,7 @@ makedev mlx0 ld0 ld1 ld2 ld3 makedev tty00 tty01 tty10 pty0 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 - makedev tun0 tun1 ipl usbs ttyCZ0 ttyCY0 audio + makedev tun0 tun1 ipl pf usbs ttyCZ0 ttyCY0 audio makedev music rmidi0 rmidi1 rmidi2 rmidi3 rmidi4 rmidi5 rmidi6 rmidi7 makedev ccd0 ccd1 ccd2 ccd3 makedev lkm local random nvram apm adb grf0 openfirm @@ -604,6 +605,12 @@ mknod ipstate c 21 2 mknod ipauth c 21 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 75 0 + chmod 600 pf ;; lkm) Index: etc/etc.mipsco/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.mipsco/MAKEDEV,v retrieving revision 1.23 diff -u -r1.23 MAKEDEV --- etc/etc.mipsco/MAKEDEV 2003/04/27 13:15:39 1.23 +++ etc/etc.mipsco/MAKEDEV 2003/06/30 04:58:25 @@ -70,6 +70,7 @@ # lkm loadable kernel modules interface # tun* network tunnel driver # ipl IP Filter +# pf PF packet filter # random Random number generator # scsibus* SCSI busses, see scsi(4), scsictl(8) # ss* SCSI scanner @@ -104,7 +105,7 @@ makedev tun0 tun1 tun2 tun3 raid0 raid1 raid2 raid3 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 makedev scsibus0 scsibus1 scsibus2 scsibus3 - makedev ipl raid0 raid1 raid2 raid3 + makedev ipl pf raid0 raid1 raid2 raid3 makedev lkm random local makedev clockctl makedev systrace @@ -283,6 +284,12 @@ mknod ipstate c 46 2 mknod ipauth c 46 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 78 0 + chmod 600 pf ;; opty) Index: etc/etc.mmeye/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.mmeye/MAKEDEV,v retrieving revision 1.32 diff -u -r1.32 MAKEDEV --- etc/etc.mmeye/MAKEDEV 2003/04/27 13:15:39 1.32 +++ etc/etc.mmeye/MAKEDEV 2003/06/30 04:58:25 @@ -73,6 +73,7 @@ # fd file descriptors # bpf* packet filter # ipl IP Filter +# pf PF packet filter # random Random number generator # speaker PC speaker (XXX - installed) # lkm loadable kernel modules interface @@ -113,7 +114,7 @@ makedev tty0 tty1 pty0 makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev st0 st1 ch0 cd0 cd1 mcd0 vnd0 vnd1 lpa0 lpa1 lpa2 - makedev lpt0 lpt1 lpt2 bpf0 bpf1 bpf2 bpf3 tun0 tun1 ipl + makedev lpt0 lpt1 lpt2 bpf0 bpf1 bpf2 bpf3 tun0 tun1 ipl pf makedev ccd0 ccd1 ccd2 ccd3 md0 ss0 ch0 uk0 uk1 random makedev cgd0 cgd1 cgd2 cgd3 makedev speaker lkm audio joy0 joy1 local satlink0 @@ -374,6 +375,12 @@ mknod ipstate c 44 2 mknod ipauth c 44 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 56 0 + chmod 600 pf ;; speaker) # (XXX - installed) Index: etc/etc.mvme68k/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.mvme68k/MAKEDEV,v retrieving revision 1.44 diff -u -r1.44 MAKEDEV --- etc/etc.mvme68k/MAKEDEV 2003/04/27 13:15:40 1.44 +++ etc/etc.mvme68k/MAKEDEV 2003/06/30 04:58:25 @@ -76,6 +76,7 @@ # uk* unknown SCSI device # ch* SCSI media changer # ipl IP Filter device +# pf PF packet filter # random Random number generator # systrace syscall tracer # @@ -108,7 +109,7 @@ makedev tun0 tun1 makedev ccd0 ccd1 ccd2 ccd3 md0 ss0 ch0 uk0 uk1 lkm makedev cgd0 cgd1 cgd2 cgd3 - makedev ipl random local + makedev ipl pf random local makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev scsibus0 scsibus1 scsibus2 scsibus3 makedev clockctl @@ -336,6 +337,12 @@ mknod ipstate c 7 2 mknod ipauth c 7 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 43 0 + chmod 600 pf ;; random) Index: etc/etc.mvmeppc/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.mvmeppc/MAKEDEV,v retrieving revision 1.15 diff -u -r1.15 MAKEDEV --- etc/etc.mvmeppc/MAKEDEV 2003/04/27 13:15:40 1.15 +++ etc/etc.mvmeppc/MAKEDEV 2003/06/30 04:58:25 @@ -87,6 +87,7 @@ # fd file descriptors # bpf* packet filter # ipl IP Filter +# pf PF packet filter # random Random number generator # speaker PC speaker (XXX - installed) # lkm loadable kernel modules interface @@ -132,7 +133,7 @@ makedev tty0 tty1 pty0 makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev st0 st1 ch0 cd0 cd1 vnd0 vnd1 - makedev lpt0 lpt1 lpt2 ttyv0 tun0 tun1 ipl + makedev lpt0 lpt1 lpt2 ttyv0 tun0 tun1 ipl pf makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 makedev ccd0 ccd1 ccd2 ccd3 md0 ss0 ch0 uk0 uk1 random makedev cgd0 cgd1 cgd2 cgd3 @@ -470,6 +471,12 @@ mknod ipstate c 44 2 mknod ipauth c 44 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 62 0 + chmod 600 pf ;; speaker) # (XXX - installed) Index: etc/etc.netwinder/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.netwinder/MAKEDEV,v retrieving revision 1.32 diff -u -r1.32 MAKEDEV --- etc/etc.netwinder/MAKEDEV 2003/05/15 19:05:29 1.32 +++ etc/etc.netwinder/MAKEDEV 2003/06/30 04:58:26 @@ -102,6 +102,7 @@ # bpf* packet filter # beep Risc PC speaker # ipl IP Filter +# pf PF packet filter # openfirm OpenFirmware accessor # random Random number generator # lkm loadable kernel modules interface @@ -149,7 +150,7 @@ makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev ttyv0 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 - makedev lpa0 lpt0 tun0 tun1 tun2 ipl + makedev lpa0 lpt0 tun0 tun1 tun2 ipl pf makedev beep lkm local makedev usbs isdns makedev random openfirm @@ -510,6 +511,12 @@ mknod ipstate c 46 2 mknod ipauth c 46 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 106 0 + chmod 600 pf ;; beep) Index: etc/etc.news68k/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.news68k/MAKEDEV,v retrieving revision 1.27 diff -u -r1.27 MAKEDEV --- etc/etc.news68k/MAKEDEV 2003/04/27 13:15:41 1.27 +++ etc/etc.news68k/MAKEDEV 2003/06/30 04:58:26 @@ -67,6 +67,7 @@ # lkm loadable kernel modules interface # tun* network tunnel driver # ipl IP Filter +# pf PF packet filter # random Random number generator # scsibus* SCSI busses, see scsi(4), scsictl(8) # ss* SCSI scanner @@ -102,7 +103,7 @@ makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 makedev scsibus0 scsibus1 scsibus2 scsibus3 - makedev ipl raid0 raid1 raid2 raid3 + makedev ipl pf raid0 raid1 raid2 raid3 makedev lkm random local makedev clockctl makedev systrace @@ -287,6 +288,12 @@ mknod ipstate c 46 2 mknod ipauth c 46 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 80 0 + chmod 600 pf ;; opty) Index: etc/etc.newsmips/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.newsmips/MAKEDEV,v retrieving revision 1.40 diff -u -r1.40 MAKEDEV --- etc/etc.newsmips/MAKEDEV 2003/04/27 13:15:41 1.40 +++ etc/etc.newsmips/MAKEDEV 2003/06/30 04:58:26 @@ -66,6 +66,7 @@ # lkm loadable kernel modules interface # tun* network tunnel driver # ipl IP Filter +# pf PF packet filter # random Random number generator # scsibus* SCSI busses, see scsi(4), scsictl(8) # ss* SCSI scanner @@ -102,7 +103,7 @@ makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 makedev scsibus0 scsibus1 scsibus2 scsibus3 - makedev ipl raid0 raid1 raid2 raid3 + makedev ipl pf raid0 raid1 raid2 raid3 makedev lkm random local makedev clockctl makedev systrace @@ -283,6 +284,12 @@ mknod ipstate c 46 2 mknod ipauth c 46 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 79 0 + chmod 600 pf ;; wscons) Index: etc/etc.next68k/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.next68k/MAKEDEV,v retrieving revision 1.39 diff -u -r1.39 MAKEDEV --- etc/etc.next68k/MAKEDEV 2003/04/27 13:15:41 1.39 +++ etc/etc.next68k/MAKEDEV 2003/06/30 04:58:26 @@ -74,6 +74,7 @@ # bpf* packet filter # lkm loadable kernel modules interface # ipl IP Filter +# pf PF packet filter # random Random number generator # scsibus* SCSI busses, see scsi(4), scsictl(8) # ss* SCSI scanner @@ -111,7 +112,7 @@ makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 makedev bpf8 bpf9 bpf10 bpf11 random - makedev tun0 tun1 tun2 tun3 lkm ipl local + makedev tun0 tun1 tun2 tun3 lkm ipl pf local makedev scsibus0 scsibus1 scsibus2 scsibus3 makedev clockctl makedev systrace @@ -394,6 +395,12 @@ mknod ipstate c 35 2 mknod ipauth c 35 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 50 0 + chmod 600 pf ;; random) Index: etc/etc.ofppc/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.ofppc/MAKEDEV,v retrieving revision 1.10 diff -u -r1.10 MAKEDEV --- etc/etc.ofppc/MAKEDEV 2003/05/07 13:41:38 1.10 +++ etc/etc.ofppc/MAKEDEV 2003/06/30 04:58:27 @@ -97,6 +97,7 @@ # fd file descriptors # bpf* packet filter # ipl IP Filter +# pf PF packet filter # openfirm OpenFirmware queries # random random number generator # lkm loadable kernel modules interface @@ -143,7 +144,7 @@ makedev ofdisk0 ofdisk1 ofdisk2 ofdisk3 makedev random tty00 tty01 tty10 pty0 pty1 pty2 pty3 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 - makedev tun0 tun1 ipl usbs ttyCZ0 ttyCY0 audio openfirm + makedev tun0 tun1 ipl pf usbs ttyCZ0 ttyCY0 audio openfirm makedev music rmidi0 rmidi1 rmidi2 rmidi3 rmidi4 rmidi5 rmidi6 rmidi7 makedev lkm local makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 @@ -560,6 +561,12 @@ mknod ipstate c 21 2 mknod ipauth c 21 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 23 0 + chmod 600 pf ;; lkm) Index: etc/etc.pc532/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.pc532/MAKEDEV,v retrieving revision 1.49 diff -u -r1.49 MAKEDEV --- etc/etc.pc532/MAKEDEV 2003/04/27 13:15:42 1.49 +++ etc/etc.pc532/MAKEDEV 2003/06/30 04:58:27 @@ -68,6 +68,7 @@ # fd file descriptors # bpf* packet filter # ipl IP Filter +# pf PF packet filter # random Random number generator # lkm loadable kernel modules interface # tun* network tunnel driver @@ -104,7 +105,7 @@ makedev tty0 tty1 tty2 tty3 tty4 tty5 tty6 tty7 makedev pty0 vnd0 vnd1 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 - makedev tun0 tun1 tun2 lkm ipl ccd0 ccd1 ccd2 + makedev tun0 tun1 tun2 lkm ipl pf ccd0 ccd1 ccd2 makedev ccd3 md0 ss0 ch0 uk0 uk1 lpt0 lpt1 rtc makedev cgd0 cgd1 cgd2 cgd3 makedev se0 se1 se2 se3 random @@ -328,6 +329,12 @@ mknod ipstate c 28 2 mknod ipauth c 28 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 36 0 + chmod 600 pf ;; lkm) Index: etc/etc.playstation2/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.playstation2/MAKEDEV,v retrieving revision 1.26 diff -u -r1.26 MAKEDEV --- etc/etc.playstation2/MAKEDEV 2003/05/07 13:41:38 1.26 +++ etc/etc.playstation2/MAKEDEV 2003/06/30 04:58:27 @@ -72,6 +72,7 @@ # fd file descriptors # bpf* packet filter # ipl IP Filter +# pf PF packet filter # random Random number generator # tun* network tunnel driver # systrace syscall tracer @@ -108,7 +109,7 @@ makedev vnd0 vnd1 vnd2 vnd3 makedev local makedev random - makedev ipl + makedev ipl pf makedev tun0 tun1 makedev md0 makedev usbs @@ -335,6 +336,12 @@ mknod ipstate c 23 2 mknod ipauth c 23 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 42 0 + chmod 600 pf ;; random) Index: etc/etc.pmax/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.pmax/MAKEDEV,v retrieving revision 1.58 diff -u -r1.58 MAKEDEV --- etc/etc.pmax/MAKEDEV 2003/04/27 13:15:43 1.58 +++ etc/etc.pmax/MAKEDEV 2003/06/30 04:58:27 @@ -42,6 +42,7 @@ # ch* SCSI media changer # fd file descriptors (/dev/fd/*) # ipl IP packet filter +# pf PF packet filter # lkm loadable kernel modules interface (unsupported in 1.3_ALPHA) # random Random number generator # scsibus* SCSI busses, see scsi(4), scsictl(8) @@ -89,7 +90,7 @@ makedev fb0 fb1 fb2 makedev px0 # px1 px2 makedev mouse - makedev ipl random lkm + makedev ipl pf random lkm makedev local makedev audio makedev scsibus0 scsibus1 scsibus2 scsibus3 @@ -344,6 +345,12 @@ mknod ipstate c 91 2 mknod ipauth c 91 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 106 0 + chmod 600 pf ;; random) Index: etc/etc.pmppc/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.pmppc/MAKEDEV,v retrieving revision 1.12 diff -u -r1.12 MAKEDEV --- etc/etc.pmppc/MAKEDEV 2003/04/27 13:15:43 1.12 +++ etc/etc.pmppc/MAKEDEV 2003/06/30 04:58:27 @@ -88,6 +88,7 @@ # fd file descriptors # bpf* packet filter # ipl IP Filter +# pf PF packet filter # random Random number generator # lkm loadable kernel modules interface # tun* network tunnel driver @@ -130,7 +131,7 @@ makedev tty0 tty1 pty0 makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev st0 st1 ch0 cd0 cd1 vnd0 vnd1 lpa0 lpa1 lpa2 - makedev lpt0 lpt1 lpt2 tun0 tun1 ipl + makedev lpt0 lpt1 lpt2 tun0 tun1 ipl pf makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 makedev ccd0 ccd1 ccd2 ccd3 md0 ss0 ch0 uk0 uk1 random ttyCY0 makedev cgd0 cgd1 cgd2 cgd3 @@ -469,6 +470,12 @@ mknod ipstate c 44 2 mknod ipauth c 44 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 63 0 + chmod 600 pf ;; lkm) Index: etc/etc.prep/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.prep/MAKEDEV,v retrieving revision 1.35 diff -u -r1.35 MAKEDEV --- etc/etc.prep/MAKEDEV 2003/04/27 13:15:44 1.35 +++ etc/etc.prep/MAKEDEV 2003/06/30 04:58:28 @@ -87,6 +87,7 @@ # fd file descriptors # bpf* packet filter # ipl IP Filter +# pf PF packet filter # random Random number generator # speaker PC speaker (XXX - installed) # lkm loadable kernel modules interface @@ -132,7 +133,7 @@ makedev tty0 tty1 pty0 makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev st0 st1 ch0 cd0 cd1 vnd0 vnd1 - makedev lpt0 lpt1 lpt2 ttyv0 tun0 tun1 ipl + makedev lpt0 lpt1 lpt2 ttyv0 tun0 tun1 ipl pf makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 makedev ccd0 ccd1 ccd2 ccd3 md0 ss0 ch0 uk0 uk1 random makedev cgd0 cgd1 cgd2 cgd3 @@ -470,6 +471,12 @@ mknod ipstate c 44 2 mknod ipauth c 44 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 77 0 + chmod 600 pf ;; speaker) # (XXX - installed) Index: etc/etc.sandpoint/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.sandpoint/MAKEDEV,v retrieving revision 1.31 diff -u -r1.31 MAKEDEV --- etc/etc.sandpoint/MAKEDEV 2003/04/27 13:15:44 1.31 +++ etc/etc.sandpoint/MAKEDEV 2003/06/30 04:58:28 @@ -88,6 +88,7 @@ # fd file descriptors # bpf* packet filter # ipl IP Filter +# pf PF packet filter # random Random number generator # lkm loadable kernel modules interface # tun* network tunnel driver @@ -129,7 +130,7 @@ makedev tty0 tty1 pty0 makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev st0 st1 ch0 cd0 cd1 vnd0 vnd1 lpa0 lpa1 lpa2 - makedev lpt0 lpt1 lpt2 tun0 tun1 ipl + makedev lpt0 lpt1 lpt2 tun0 tun1 ipl pf makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 makedev ccd0 ccd1 ccd2 ccd3 md0 ss0 ch0 uk0 uk1 random ttyCY0 makedev cgd0 cgd1 cgd2 cgd3 @@ -467,6 +468,12 @@ mknod ipstate c 44 2 mknod ipauth c 44 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 62 0 + chmod 600 pf ;; lkm) Index: etc/etc.sbmips/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.sbmips/MAKEDEV,v retrieving revision 1.14 diff -u -r1.14 MAKEDEV --- etc/etc.sbmips/MAKEDEV 2003/05/07 13:41:38 1.14 +++ etc/etc.sbmips/MAKEDEV 2003/06/30 04:58:28 @@ -97,6 +97,7 @@ # clockctl clock control for non root users # fd file descriptors (/dev/fd/*) # ipl IP Filter +# pf PF packet filter # lkm loadable kernel modules interface # mlx* Mylex DAC960 control interface # pci* PCI bus access devices @@ -147,7 +148,7 @@ makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 makedev tun0 tun1 tun2 tun3 makedev tty00 tty01 - makedev ipl + makedev ipl pf makedev random local satlink0 mlx0 makedev scsibus0 scsibus1 scsibus2 scsibus3 makedev lkm @@ -559,6 +560,12 @@ mknod ipstate c 35 2 mknod ipauth c 35 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 77 0 + chmod 600 pf ;; satlink*) Index: etc/etc.sgimips/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.sgimips/MAKEDEV,v retrieving revision 1.32 diff -u -r1.32 MAKEDEV --- etc/etc.sgimips/MAKEDEV 2003/04/27 13:15:45 1.32 +++ etc/etc.sgimips/MAKEDEV 2003/06/30 04:58:29 @@ -97,6 +97,7 @@ # ch* SCSI media changer # fd file descriptors (/dev/fd/*) # ipl IP Filter +# pf PF packet filter # pci* PCI bus access devices, see pcictl(8) # random Random number generator # scsibus* SCSI busses, see scsi(4), scsictl(8) @@ -438,6 +439,12 @@ mknod ipstate c 29 2 mknod ipauth c 29 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 78 0 + chmod 600 pf ;; random) Index: etc/etc.shark/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.shark/MAKEDEV,v retrieving revision 1.22 diff -u -r1.22 MAKEDEV --- etc/etc.shark/MAKEDEV 2003/05/15 19:01:38 1.22 +++ etc/etc.shark/MAKEDEV 2003/06/30 04:58:29 @@ -106,6 +106,7 @@ # bpf* packet filter # beep Risc PC speaker # ipl IP Filter +# pf PF packet filter # openfirm OpenFirmware accessor # random Random number generator # lkm loadable kernel modules interface @@ -155,7 +156,7 @@ makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev ttyv0 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 - makedev lpa0 lpt0 tun0 tun1 tun2 ipl + makedev lpa0 lpt0 tun0 tun1 tun2 ipl pf makedev audio audio0 beep lkm pms0 local makedev usbs isdns makedev random openfirm @@ -518,6 +519,12 @@ mknod ipstate c 46 2 mknod ipauth c 46 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 87 0 + chmod 600 pf ;; beep) Index: etc/etc.sparc/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.sparc/MAKEDEV,v retrieving revision 1.88 diff -u -r1.88 MAKEDEV --- etc/etc.sparc/MAKEDEV 2003/04/27 13:15:45 1.88 +++ etc/etc.sparc/MAKEDEV 2003/06/30 04:58:30 @@ -95,6 +95,7 @@ # lkm loadable kernel modules interface # tun* network tunnel driver # ipl IP Filter +# pf PF packet filter # random Random number generator # scsibus* SCSI busses, see scsi(4), scsictl(8) # ses* SES/SAF-TE SCSI Devices @@ -154,7 +155,7 @@ makedev cgtwo0 cgfour0 cgeight0 makedev scsibus0 scsibus1 scsibus2 scsibus3 makedev sysmon - makedev lkm random local ipl cfs0 apm apmctl tctrl0 + makedev lkm random local ipl pf cfs0 apm apmctl tctrl0 makedev isdns makedev pci0 makedev clockctl @@ -506,6 +507,12 @@ mknod ipstate c 25 2 mknod ipauth c 25 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 137 0 + chmod 600 pf ;; opty) Index: etc/etc.sparc64/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.sparc64/MAKEDEV,v retrieving revision 1.52 diff -u -r1.52 MAKEDEV --- etc/etc.sparc64/MAKEDEV 2003/05/07 13:41:39 1.52 +++ etc/etc.sparc64/MAKEDEV 2003/06/30 04:58:30 @@ -109,6 +109,7 @@ # lkm loadable kernel modules interface # tun* network tunnel driver # ipl IP Filter +# pf PF packet filter # random Random number generator # scsibus* SCSI busses, see scsi(4), scsictl(8) # ses* SES/SAF-TE SCSI Devices @@ -167,7 +168,7 @@ makedev cgeight0 tcx0 makedev scsibus0 scsibus1 scsibus2 scsibus3 makedev sysmon - makedev ipl lkm random local + makedev ipl pf lkm random local makedev usbs makedev isdns makedev pci0 pci1 pci2 pci3 pci4 pci5 pci6 pci7 @@ -518,6 +519,12 @@ mknod ipstate c 25 2 mknod ipauth c 25 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 137 0 + chmod 600 pf ;; opty) Index: etc/etc.sun2/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.sun2/MAKEDEV,v retrieving revision 1.20 diff -u -r1.20 MAKEDEV --- etc/etc.sun2/MAKEDEV 2003/04/27 13:15:46 1.20 +++ etc/etc.sun2/MAKEDEV 2003/06/30 04:58:30 @@ -68,6 +68,7 @@ # random Random number generator # scsibus* SCSI busses, see scsi(4), scsictl(8) # systrace syscall tracer +# pf PF packet filter # XXX - Keep /usr/etc so SunOS can find chown PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/etc @@ -291,6 +292,12 @@ rm -f systrace mknod systrace c 90 0 chmod 644 systrace + ;; + +pf) + rm -f pf + mknod pf c 93 0 + chmod 600 pf ;; local) Index: etc/etc.sun3/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.sun3/MAKEDEV,v retrieving revision 1.53 diff -u -r1.53 MAKEDEV --- etc/etc.sun3/MAKEDEV 2003/04/27 13:15:47 1.53 +++ etc/etc.sun3/MAKEDEV 2003/06/30 04:58:31 @@ -65,6 +65,7 @@ # cgfour* 8-bit color frame buffer # bpf* packet filter # ipl IP Filter +# pf PF packet filter # lkm loadable kernel modules interface # tun* network tunnel driver # random Random number generator @@ -126,7 +127,7 @@ makedev vnd0 vnd1 vnd2 vnd3 ccd0 ccd1 ccd2 ccd3 makedev cgd0 cgd1 cgd2 cgd3 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 - makedev tun0 tun1 ipl + makedev tun0 tun1 ipl pf makedev lkm random makedev scsibus0 scsibus1 scsibus2 scsibus3 makedev local @@ -325,6 +326,12 @@ rm -f systrace mknod systrace c 85 0 chmod 644 systrace + ;; + +pf) + rm -f pf + mknod pf c 88 0 + chmod 600 pf ;; local) Index: etc/etc.vax/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.vax/MAKEDEV,v retrieving revision 1.57 diff -u -r1.57 MAKEDEV --- etc/etc.vax/MAKEDEV 2003/04/27 13:15:47 1.57 +++ etc/etc.vax/MAKEDEV 2003/06/30 04:58:31 @@ -73,6 +73,7 @@ # ch* SCSI media changer # random Random number generator # systrace syscall tracer +# pf PF packet filter # PATH=/sbin:/usr/sbin:/bin:/usr/bin umask 77 @@ -582,6 +583,12 @@ rm -f systrace mknod systrace c 76 0 chmod 644 systrace + ;; + +pf) + rm -f pf + mknod pf c 80 0 + chmod 600 pf ;; local) Index: etc/etc.x68k/MAKEDEV =================================================================== RCS file: /cvsroot/src/etc/etc.x68k/MAKEDEV,v retrieving revision 1.65 diff -u -r1.65 MAKEDEV --- etc/etc.x68k/MAKEDEV 2003/05/07 13:41:39 1.65 +++ etc/etc.x68k/MAKEDEV 2003/06/30 04:58:32 @@ -104,6 +104,7 @@ # lkm loadable kernel modules interface # bpf* Berkeley Packet Filter # ipl IP Filter +# pf PF packet filter # random Random number generator # audio audio device # sram battery backuped memory @@ -145,7 +146,7 @@ makedev raid0 raid1 raid2 raid3 raid4 raid5 raid6 raid7 makedev cd0 ss0 fd0 fd1 fd2 fd3 makedev bpf0 bpf1 bpf2 bpf3 bpf4 bpf5 bpf6 bpf7 - makedev ccd0 ccd1 ccd2 ccd3 tun0 tun1 par0 lkm ipl + makedev ccd0 ccd1 ccd2 ccd3 tun0 tun1 par0 lkm ipl pf makedev cgd0 cgd1 cgd2 cgd3 makedev sram audio pow0 pow1 bell random local makedev md0 md1 bmd0 bmd1 ch0 uk0 uk1 ss0 @@ -498,6 +499,12 @@ mknod ipstate c 38 2 mknod ipauth c 38 3 chmod 600 ipl ipnat ipstate ipauth + ;; + +pf) + rm -f pf + mknod pf c 55 0 + chmod 600 pf ;; lkm) Index: sys/conf/files =================================================================== RCS file: /cvsroot/src/sys/conf/files,v retrieving revision 1.615 diff -u -r1.615 files --- sys/conf/files 2003/06/02 22:51:49 1.615 +++ sys/conf/files 2003/06/30 04:58:33 @@ -958,6 +958,9 @@ defpseudo gif: ifnet defpseudo faith: ifnet defpseudo stf: ifnet +defpseudo pf: ifnet +defpseudo pflog: ifnet +defpseudo pfsync: ifnet defpseudo sequencer defpseudo clockctl @@ -1180,6 +1183,12 @@ file net/if_tun.c tun needs-flag file net/if_vlan.c vlan needs-flag file net/if_pppoe.c pppoe needs-flag +file net/pf.c pf needs-flag +file net/pf_norm.c pf +file net/pf_ioctl.c pf +file net/pf_table.c pf +file net/if_pflog.c pflog needs-flag +file net/if_pfsync.c pfsync needs-flag file net/pfil.c pfil_hooks | ipfilter file net/ppp-deflate.c ppp & ppp_deflate file net/ppp_tty.c ppp Index: sys/net/Makefile =================================================================== RCS file: /cvsroot/src/sys/net/Makefile,v retrieving revision 1.13 diff -u -r1.13 Makefile --- sys/net/Makefile 2002/11/26 23:30:32 1.13 +++ sys/net/Makefile 2003/06/30 04:58:33 @@ -7,7 +7,7 @@ if_gre.h if_hippi.h if_ieee1394.h if_ieee80211.h if_llc.h if_media.h \ if_ppp.h if_pppvar.h if_pppoe.h if_slvar.h if_sppp.h if_stf.h \ if_stripvar.h if_token.h if_tun.h if_types.h if_vlanvar.h \ - netisr.h pfil.h pfkeyv2.h ppp-comp.h ppp_defs.h radix.h \ + netisr.h pfil.h pfkeyv2.h pfvar.h ppp-comp.h ppp_defs.h radix.h \ raw_cb.h route.h slcompress.h slip.h zlib.h .include Index: sys/net/dlt.h =================================================================== RCS file: /cvsroot/src/sys/net/dlt.h,v retrieving revision 1.5 diff -u -r1.5 dlt.h --- sys/net/dlt.h 2003/04/17 22:55:29 1.5 +++ sys/net/dlt.h 2003/06/30 04:58:33 @@ -65,6 +65,9 @@ #define DLT_HIPPI 15 /* HIPPI */ #define DLT_HDLC 16 /* HDLC framing */ +#define DLT_PFSYNC 18 /* Packet filter state syncing */ +#define DLT_PFLOG 117 /* Packet filter logging, by pcap people */ + /* NetBSD-specific types */ #define DLT_PPP_SERIAL 50 /* PPP over serial (async and sync) */ #define DLT_PPP_ETHER 51 /* XXX - deprecated! PPP over Ethernet; session only, w/o ether header */ Index: sys/net/if_pflog.c =================================================================== RCS file: if_pflog.c diff -N if_pflog.c --- /dev/null Mon Jun 30 04:58:52 2003 +++ if_pflog.c Mon Jun 30 04:58:34 2003 @@ -0,0 +1,244 @@ +/* $NetBSD$ */ +/* $OpenBSD: if_pflog.c,v 1.9 2003/05/14 08:42:00 canacar Exp $ */ +/* + * The authors of this code are John Ioannidis (ji@tla.org), + * Angelos D. Keromytis (kermit@csd.uch.gr) and + * Niels Provos (provos@physnet.uni-hamburg.de). + * + * This code was written by John Ioannidis for BSD/OS in Athens, Greece, + * in November 1995. + * + * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, + * by Angelos D. Keromytis. + * + * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis + * and Niels Provos. + * + * Copyright (C) 1995, 1996, 1997, 1998 by John Ioannidis, Angelos D. Keromytis + * and Niels Provos. + * Copyright (c) 2001, Angelos D. Keromytis, Niels Provos. + * + * Permission to use, copy, and modify this software with or without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#ifdef _KERNEL_OPT +#include "opt_inet.h" +#endif + +#include "bpfilter.h" +#include "pflog.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef INET +#include +#include +#include +#include +#endif + +#ifdef INET6 +#ifndef INET +#include +#endif +#include +#endif /* INET6 */ + +#include +#include + +#define PFLOGMTU (32768 + MHLEN + MLEN) + +#ifdef PFLOGDEBUG +#define DPRINTF(x) do { if (pflogdebug) printf x ; } while (0) +#else +#define DPRINTF(x) +#endif + +struct pflog_softc pflogif[NPFLOG]; + +void pflogattach(int); +int pflogoutput(struct ifnet *, struct mbuf *, struct sockaddr *, + struct rtentry *); +int pflogioctl(struct ifnet *, u_long, caddr_t); +void pflogrtrequest(int, struct rtentry *, struct sockaddr *); +void pflogstart(struct ifnet *); + +extern int ifqmaxlen; + +void +pflogattach(int npflog) +{ + struct ifnet *ifp; + int i; + + bzero(pflogif, sizeof(pflogif)); + + for (i = 0; i < NPFLOG; i++) { + ifp = &pflogif[i].sc_if; + snprintf(ifp->if_xname, sizeof ifp->if_xname, "pflog%d", i); + ifp->if_softc = &pflogif[i]; + ifp->if_mtu = PFLOGMTU; + ifp->if_ioctl = pflogioctl; + ifp->if_output = pflogoutput; + ifp->if_start = pflogstart; + ifp->if_type = IFT_PFLOG; + ifp->if_snd.ifq_maxlen = ifqmaxlen; + ifp->if_hdrlen = PFLOG_HDRLEN; + if_attach(ifp); + if_alloc_sadl(ifp); + +#if NBPFILTER > 0 +#ifdef __OpenBSD__ + bpfattach(&pflogif[i].sc_if.if_bpf, ifp, DLT_PFLOG, + PFLOG_HDRLEN); +#else + bpfattach(ifp, DLT_PFLOG, PFLOG_HDRLEN); +#endif +#endif + } +} + +/* + * Start output on the pflog interface. + */ +void +pflogstart(struct ifnet *ifp) +{ + struct mbuf *m; + int s; + + for (;;) { +#ifdef __OpenBSD__ + s = splimp(); +#else + s = splnet(); +#endif + IF_DROP(&ifp->if_snd); + IF_DEQUEUE(&ifp->if_snd, m); + splx(s); + + if (m == NULL) + return; + else + m_freem(m); + } +} + +int +pflogoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, + struct rtentry *rt) +{ + m_freem(m); + return (0); +} + +/* ARGSUSED */ +void +pflogrtrequest(int cmd, struct rtentry *rt, struct sockaddr *sa) +{ + if (rt) + rt->rt_rmx.rmx_mtu = PFLOGMTU; +} + +/* ARGSUSED */ +int +pflogioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + switch (cmd) { + case SIOCSIFADDR: + case SIOCAIFADDR: + case SIOCSIFDSTADDR: + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) + ifp->if_flags |= IFF_RUNNING; + else + ifp->if_flags &= ~IFF_RUNNING; + break; + default: + return (EINVAL); + } + + return (0); +} + +int +pflog_packet(struct ifnet *ifp, struct mbuf *m, sa_family_t af, u_int8_t dir, + u_int8_t reason, struct pf_rule *rm, struct pf_rule *am, + struct pf_ruleset *ruleset) +{ +#if NBPFILTER > 0 + struct ifnet *ifn; + struct pfloghdr hdr; + struct mbuf m1; + + if (ifp == NULL || m == NULL || rm == NULL) + return (-1); + + hdr.length = PFLOG_REAL_HDRLEN; + hdr.af = af; + hdr.action = rm->action; + hdr.reason = reason; + memcpy(hdr.ifname, ifp->if_xname, sizeof(hdr.ifname)); + + if (am == NULL) { + hdr.rulenr = htonl(rm->nr); + hdr.subrulenr = -1; + bzero(hdr.ruleset, sizeof(hdr.ruleset)); + } else { + hdr.rulenr = htonl(am->nr); + hdr.subrulenr = htonl(rm->nr); + if (ruleset == NULL) + bzero(hdr.ruleset, sizeof(hdr.ruleset)); + else + memcpy(hdr.ruleset, ruleset->name, + sizeof(hdr.ruleset)); + + + } + hdr.dir = dir; + +#ifdef INET + if (af == AF_INET && dir == PF_OUT) { + struct ip *ip; + + ip = mtod(m, struct ip *); + ip->ip_sum = 0; + ip->ip_sum = in_cksum(m, ip->ip_hl << 2); + } +#endif /* INET */ + + m1.m_next = m; + m1.m_len = PFLOG_HDRLEN; + m1.m_data = (char *) &hdr; + + ifn = &(pflogif[0].sc_if); + + if (ifn->if_bpf) + bpf_mtap(ifn->if_bpf, &m1); +#endif + + return (0); +} Index: sys/net/if_pflog.h =================================================================== RCS file: if_pflog.h diff -N if_pflog.h --- /dev/null Mon Jun 30 04:58:52 2003 +++ if_pflog.h Mon Jun 30 04:58:34 2003 @@ -0,0 +1,87 @@ +/* $NetBSD$ */ +/* $OpenBSD: if_pflog.h,v 1.8 2003/05/14 08:42:00 canacar Exp $ */ +/* + * Copyright 2001 Niels Provos + * 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 ``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 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. + */ + +#ifndef _NET_IF_PFLOG_H_ +#define _NET_IF_PFLOG_H_ + +struct pflog_softc { + struct ifnet sc_if; /* the interface */ +}; + +/* XXX keep in sync with pfvar.h */ +#ifndef PF_RULESET_NAME_SIZE +#define PF_RULESET_NAME_SIZE 16 +#endif + +struct pfloghdr { + u_int8_t length; + sa_family_t af; + u_int8_t action; + u_int8_t reason; + char ifname[IFNAMSIZ]; + char ruleset[PF_RULESET_NAME_SIZE]; + u_int32_t rulenr; + u_int32_t subrulenr; + u_int8_t dir; + u_int8_t pad[3]; +}; + +#define PFLOG_HDRLEN sizeof(struct pfloghdr) +/* minus pad, also used as a signature */ +#define PFLOG_REAL_HDRLEN offsetof(struct pfloghdr, pad); + +/* XXX remove later when old format logs are no longer needed */ +struct old_pfloghdr { + u_int32_t af; + char ifname[IFNAMSIZ]; + short rnr; + u_short reason; + u_short action; + u_short dir; +}; +#define OLD_PFLOG_HDRLEN sizeof(struct old_pfloghdr) + +#ifdef _KERNEL + +#if NPFLOG > 0 +#define PFLOG_PACKET(i,x,a,b,c,d,e,f,g) \ + do { \ + if (b == AF_INET) { \ + HTONS(((struct ip *)x)->ip_len); \ + HTONS(((struct ip *)x)->ip_off); \ + pflog_packet(i,a,b,c,d,e,f,g); \ + NTOHS(((struct ip *)x)->ip_len); \ + NTOHS(((struct ip *)x)->ip_off); \ + } else { \ + pflog_packet(i,a,b,c,d,e,f,g); \ + } \ + } while (0) +#else +#define PFLOG_PACKET(i,x,a,b,c,d,e,f,g) ((void)0) +#endif /* NPFLOG > 0 */ +#endif /* _KERNEL */ +#endif /* _NET_IF_PFLOG_H_ */ Index: sys/net/if_pfsync.c =================================================================== RCS file: if_pfsync.c diff -N if_pfsync.c --- /dev/null Mon Jun 30 04:58:52 2003 +++ if_pfsync.c Mon Jun 30 04:58:34 2003 @@ -0,0 +1,388 @@ +/* $NetBSD$ */ +/* $OpenBSD: if_pfsync.c,v 1.6 2003/06/21 09:07:01 djm Exp $ */ + +/* + * Copyright (c) 2002 Michael Shalayeff + * 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 ``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 HIS RELATIVES 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 MIND, 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. + */ + +#ifdef _KERNEL_OPT +#include "opt_inet.h" +#endif + +#include "bpfilter.h" +#include "pfsync.h" + +#include +#include +#include +#include +#include +#include +#ifdef __OpenBSD__ +#include +#else +#include +#endif + +#include +#include +#include +#include + +#ifdef INET +#include +#include +#endif + +#ifdef INET6 +#ifndef INET +#include +#endif +#include +#endif /* INET6 */ + +#include +#include + +#define PFSYNC_MINMTU \ + (sizeof(struct pfsync_header) + sizeof(struct pf_state)) + +#ifdef PFSYNCDEBUG +#define DPRINTF(x) do { if (pfsyncdebug) printf x ; } while (0) +int pfsyncdebug; +#else +#define DPRINTF(x) +#endif + +struct pfsync_softc pfsyncif; + +void pfsyncattach(int); +void pfsync_setmtu(struct pfsync_softc *sc, int); +int pfsyncoutput(struct ifnet *, struct mbuf *, struct sockaddr *, + struct rtentry *); +int pfsyncioctl(struct ifnet *, u_long, caddr_t); +void pfsyncstart(struct ifnet *); + +struct mbuf *pfsync_get_mbuf(struct pfsync_softc *sc, u_int8_t action); +int pfsync_sendout(struct pfsync_softc *sc); +void pfsync_timeout(void *v); + +extern int ifqmaxlen; + +void +pfsyncattach(int npfsync) +{ + struct ifnet *ifp; + + pfsyncif.sc_mbuf = NULL; + pfsyncif.sc_ptr = NULL; + pfsyncif.sc_count = 8; + ifp = &pfsyncif.sc_if; + strlcpy(ifp->if_xname, "pfsync0", sizeof ifp->if_xname); + ifp->if_softc = &pfsyncif; + ifp->if_ioctl = pfsyncioctl; + ifp->if_output = pfsyncoutput; + ifp->if_start = pfsyncstart; + ifp->if_type = IFT_PFSYNC; + ifp->if_snd.ifq_maxlen = ifqmaxlen; + ifp->if_hdrlen = PFSYNC_HDRLEN; + ifp->if_baudrate = IF_Mbps(100); + pfsync_setmtu(&pfsyncif, MCLBYTES); +#ifdef __OpenBSD__ + timeout_set(&pfsyncif.sc_tmo, pfsync_timeout, &pfsyncif); +#else + callout_init(&pfsyncif.sc_tmo); +#endif + if_attach(ifp); + if_alloc_sadl(ifp); + +#if NBPFILTER > 0 +#ifdef __OpenBSD__ + bpfattach(&pfsyncif.sc_if.if_bpf, ifp, DLT_PFSYNC, PFSYNC_HDRLEN); +#else + bpfattach(ifp, DLT_PFSYNC, PFSYNC_HDRLEN); +#endif +#endif +} + +/* + * Start output on the pfsync interface. + */ +void +pfsyncstart(struct ifnet *ifp) +{ + struct mbuf *m; + int s; + + for (;;) { +#ifdef __OpenBSD__ + s = splimp(); +#else + s = splnet(); +#endif + IF_DROP(&ifp->if_snd); + IF_DEQUEUE(&ifp->if_snd, m); + splx(s); + + if (m == NULL) + return; + else + m_freem(m); + } +} + +int +pfsyncoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, + struct rtentry *rt) +{ + m_freem(m); + return (0); +} + +/* ARGSUSED */ +int +pfsyncioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct pfsync_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + int s; + + switch (cmd) { + case SIOCSIFADDR: + case SIOCAIFADDR: + case SIOCSIFDSTADDR: + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) + ifp->if_flags |= IFF_RUNNING; + else + ifp->if_flags &= ~IFF_RUNNING; + break; + case SIOCSIFMTU: + if (ifr->ifr_mtu < PFSYNC_MINMTU) + return (EINVAL); + if (ifr->ifr_mtu > MCLBYTES) + ifr->ifr_mtu = MCLBYTES; + s = splnet(); + if (ifr->ifr_mtu < ifp->if_mtu) + pfsync_sendout(sc); + pfsync_setmtu(sc, ifr->ifr_mtu); + splx(s); + break; + default: + return (ENOTTY); + } + + return (0); +} + +void +pfsync_setmtu(sc, mtu) + struct pfsync_softc *sc; + int mtu; +{ + sc->sc_count = (mtu - sizeof(struct pfsync_header)) / + sizeof(struct pf_state); + sc->sc_if.if_mtu = sizeof(struct pfsync_header) + + sc->sc_count * sizeof(struct pf_state); +} + +struct mbuf * +pfsync_get_mbuf(sc, action) + struct pfsync_softc *sc; + u_int8_t action; +{ + extern int hz; + struct pfsync_header *h; + struct mbuf *m; + int len; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + sc->sc_if.if_oerrors++; + return (NULL); + } + + len = sc->sc_if.if_mtu; + if (len > MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_free(m); + sc->sc_if.if_oerrors++; + return (NULL); + } + } + m->m_pkthdr.rcvif = NULL; + m->m_pkthdr.len = m->m_len = len; + + h = mtod(m, struct pfsync_header *); + h->version = PFSYNC_VERSION; + h->af = 0; + h->count = 0; + h->action = action; + + sc->sc_mbuf = m; + sc->sc_ptr = (struct pf_state *)((char *)h + PFSYNC_HDRLEN); +#ifdef __OpenBSD__ + timeout_add(&sc->sc_tmo, hz); +#else + callout_reset(&pfsyncif.sc_tmo, hz, pfsync_timeout, &pfsyncif); +#endif + + return (m); +} + +int +pfsync_pack_state(action, st) + u_int8_t action; + struct pf_state *st; +{ + extern struct timeval time; + struct ifnet *ifp = &pfsyncif.sc_if; + struct pfsync_softc *sc = ifp->if_softc; + struct pfsync_header *h; + struct pf_state *sp; + struct pf_rule *r = st->rule.ptr; + struct mbuf *m; + u_long secs; + int s, ret; + + if (action >= PFSYNC_ACT_MAX) + return (EINVAL); + + s = splnet(); + m = sc->sc_mbuf; + if (m == NULL) { + if ((m = pfsync_get_mbuf(sc, action)) == NULL) { + splx(s); + return (ENOMEM); + } + h = mtod(m, struct pfsync_header *); + } else { + h = mtod(m, struct pfsync_header *); + if (h->action != action) { + pfsync_sendout(sc); + if ((m = pfsync_get_mbuf(sc, action)) == NULL) { + splx(s); + return (ENOMEM); + } + h = mtod(m, struct pfsync_header *); + } + } + + sp = sc->sc_ptr++; + h->count++; + bzero(sp, sizeof(*sp)); + + bcopy(&st->lan, &sp->lan, sizeof(sp->lan)); + bcopy(&st->gwy, &sp->gwy, sizeof(sp->gwy)); + bcopy(&st->ext, &sp->ext, sizeof(sp->ext)); + + pf_state_peer_hton(&st->src, &sp->src); + pf_state_peer_hton(&st->dst, &sp->dst); + + bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr)); + secs = time.tv_sec; + sp->creation = htonl(secs - st->creation); + if (st->expire <= secs) + sp->expire = htonl(0); + else + sp->expire = htonl(st->expire - secs); + sp->packets[0] = htonl(st->packets[0]); + sp->packets[1] = htonl(st->packets[1]); + sp->bytes[0] = htonl(st->bytes[0]); + sp->bytes[1] = htonl(st->bytes[1]); + if (r == NULL) + sp->rule.nr = htonl(-1); + else + sp->rule.nr = htonl(r->nr); + sp->af = st->af; + sp->proto = st->proto; + sp->direction = st->direction; + sp->log = st->log; + sp->allow_opts = st->allow_opts; + + ret = 0; + if (h->count == sc->sc_count) + ret = pfsync_sendout(sc); + + splx(s); + return (0); +} + +int +pfsync_clear_state(st) + struct pf_state *st; +{ + struct ifnet *ifp = &pfsyncif.sc_if; + struct pfsync_softc *sc = ifp->if_softc; + struct mbuf *m = sc->sc_mbuf; + int s, ret; + + s = splnet(); + if (m == NULL && (m = pfsync_get_mbuf(sc, PFSYNC_ACT_CLR)) == NULL) { + splx(s); + return (ENOMEM); + } + + ret = (pfsync_sendout(sc)); + splx(s); + return (ret); +} + +void +pfsync_timeout(void *v) +{ + struct pfsync_softc *sc = v; + int s; + + s = splnet(); + pfsync_sendout(sc); + splx(s); +} + +int +pfsync_sendout(sc) + struct pfsync_softc *sc; +{ + struct ifnet *ifp = &sc->sc_if; + struct mbuf *m = sc->sc_mbuf; + +#ifdef __OpenBSD__ + timeout_del(&sc->sc_tmo); +#else + callout_stop(&sc->sc_tmo); +#endif + sc->sc_mbuf = NULL; + sc->sc_ptr = NULL; + +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m); +#endif + + m_freem(m); + + return (0); +} Index: sys/net/if_pfsync.h =================================================================== RCS file: if_pfsync.h diff -N if_pfsync.h --- /dev/null Mon Jun 30 04:58:52 2003 +++ if_pfsync.h Mon Jun 30 04:58:34 2003 @@ -0,0 +1,89 @@ +/* $NetBSD$ */ +/* $OpenBSD: if_pfsync.h,v 1.2 2002/12/11 18:31:26 mickey Exp $ */ + +/* + * Copyright (c) 2001 Michael Shalayeff + * 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 ``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 HIS RELATIVES 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 MIND, 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. + */ + +#ifndef _NET_IF_PFSYNC_H_ +#define _NET_IF_PFSYNC_H_ + +#ifdef _KERNEL +struct pfsync_softc { + struct ifnet sc_if; + +#ifdef __OpenBSD__ + struct timeout sc_tmo; +#else + struct callout sc_tmo; +#endif + struct mbuf *sc_mbuf; /* current cummulative mbuf */ + struct pf_state *sc_ptr; /* current ongoing state */ + int sc_count; /* number of states in one mtu */ +}; +#endif + +struct pfsync_header { + u_int8_t version; +#define PFSYNC_VERSION 1 + u_int8_t af; + u_int8_t action; +#define PFSYNC_ACT_CLR 0 +#define PFSYNC_ACT_INS 1 +#define PFSYNC_ACT_UPD 2 +#define PFSYNC_ACT_DEL 3 +#define PFSYNC_ACT_MAX 4 + u_int8_t count; +}; + +#define PFSYNC_HDRLEN sizeof(struct pfsync_header) +#define PFSYNC_ACTIONS \ + "CLR ST", "INS ST", "UPD ST", "DEL ST" + +#define pf_state_peer_hton(s,d) do { \ + (d)->seqlo = htonl((s)->seqlo); \ + (d)->seqhi = htonl((s)->seqhi); \ + (d)->seqdiff = htonl((s)->seqdiff); \ + (d)->max_win = htons((s)->max_win); \ + (d)->state = (s)->state; \ +} while (0) + +#define pf_state_peer_ntoh(s,d) do { \ + (d)->seqlo = ntohl((s)->seqlo); \ + (d)->seqhi = ntohl((s)->seqhi); \ + (d)->seqdiff = ntohl((s)->seqdiff); \ + (d)->max_win = ntohs((s)->max_win); \ + (d)->state = (s)->state; \ +} while (0) + +#ifdef _KERNEL +int pfsync_clear_state(struct pf_state *); +int pfsync_pack_state(u_int8_t, struct pf_state *); +#define pfsync_insert_state(st) pfsync_pack_state(PFSYNC_ACT_INS, (st)) +#define pfsync_update_state(st) pfsync_pack_state(PFSYNC_ACT_UPD, (st)) +#define pfsync_delete_state(st) pfsync_pack_state(PFSYNC_ACT_DEL, (st)) +#endif + +#endif /* _NET_IF_PFSYNC_H_ */ Index: sys/net/if_types.h =================================================================== RCS file: /cvsroot/src/sys/net/if_types.h,v retrieving revision 1.21 diff -u -r1.21 if_types.h --- sys/net/if_types.h 2002/07/10 21:01:06 1.21 +++ sys/net/if_types.h 2003/06/30 04:58:34 @@ -265,4 +265,6 @@ #define IFT_GIF 0xf0 #define IFT_PVC 0xf1 #define IFT_FAITH 0xf2 +#define IFT_PFLOG 0xf5 +#define IFT_PFSYNC 0xf6 #endif /* _NET_IF_TYPES_H_ */ Index: sys/net/pf.c =================================================================== RCS file: pf.c diff -N pf.c --- /dev/null Mon Jun 30 04:58:52 2003 +++ pf.c Mon Jun 30 04:58:37 2003 @@ -0,0 +1,5419 @@ +/* $NetBSD$ */ +/* $OpenBSD: pf.c,v 1.369 2003/06/24 13:55:13 henning Exp $ */ + +/* + * Copyright (c) 2001 Daniel Hartmeier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDERS 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. + * + * Effort sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F30602-01-2-0537. + * + */ + +#ifdef _KERNEL_OPT +#include "opt_inet.h" +#include "opt_altq.h" +#endif + +#include "bpfilter.h" +#include "pflog.h" +#include "pfsync.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __NetBSD__ +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __OpenBSD__ +#include +#else +#include +#endif +#include +#include +#include + +#ifdef INET6 +#include +#include +#ifdef __NetBSD__ +#include +#include +#endif +#include +#include +#endif /* INET6 */ + +#ifdef ALTQ +#include +#endif + +#define DPFPRINTF(n, x) if (pf_status.debug >= (n)) printf x +struct pf_state_tree; + +/* + * Global variables + */ + +struct pf_anchorqueue pf_anchors; +struct pf_ruleset pf_main_ruleset; +struct pf_altqqueue pf_altqs[2]; +struct pf_palist pf_pabuf; +struct pf_altqqueue *pf_altqs_active; +struct pf_altqqueue *pf_altqs_inactive; +struct pf_status pf_status; +struct ifnet *status_ifp; + +u_int32_t ticket_altqs_active; +u_int32_t ticket_altqs_inactive; +u_int32_t ticket_pabuf; + +#ifdef __OpenBSD__ +struct timeout pf_expire_to; /* expire timeout */ +#else +struct callout pf_expire_to; /* expire timeout */ +#endif + +struct pool pf_tree_pl, pf_rule_pl, pf_addr_pl; +struct pool pf_state_pl, pf_altq_pl, pf_pooladdr_pl; + +void pf_dynaddr_update(void *); +void pf_print_host(struct pf_addr *, u_int16_t, u_int8_t); +void pf_print_state(struct pf_state *); +void pf_print_flags(u_int8_t); + +u_int16_t pf_cksum_fixup(u_int16_t, u_int16_t, u_int16_t, + u_int8_t); +void pf_change_ap(struct pf_addr *, u_int16_t *, + u_int16_t *, u_int16_t *, struct pf_addr *, + u_int16_t, u_int8_t, sa_family_t); +#ifdef INET6 +void pf_change_a6(struct pf_addr *, u_int16_t *, + struct pf_addr *, u_int8_t); +#endif /* INET6 */ +void pf_change_icmp(struct pf_addr *, u_int16_t *, + struct pf_addr *, struct pf_addr *, u_int16_t, + u_int16_t *, u_int16_t *, u_int16_t *, + u_int16_t *, u_int8_t, sa_family_t); +void pf_send_tcp(const struct pf_rule *, sa_family_t, + const struct pf_addr *, const struct pf_addr *, + u_int16_t, u_int16_t, u_int32_t, u_int32_t, + u_int8_t, u_int16_t, u_int16_t, u_int8_t); +void pf_send_icmp(struct mbuf *, u_int8_t, u_int8_t, + sa_family_t, struct pf_rule *); +struct pf_rule *pf_match_translation(int, struct ifnet *, u_int8_t, + struct pf_addr *, u_int16_t, struct pf_addr *, + u_int16_t, sa_family_t, int); +struct pf_rule *pf_get_translation(int, struct ifnet *, u_int8_t, + struct pf_addr *, u_int16_t, + struct pf_addr *, u_int16_t, + struct pf_addr *, u_int16_t *, sa_family_t); +int pf_test_tcp(struct pf_rule **, struct pf_state **, + int, struct ifnet *, struct mbuf *, int, int, + void *, struct pf_pdesc *, struct pf_rule **, + struct pf_ruleset **); +int pf_test_udp(struct pf_rule **, struct pf_state **, + int, struct ifnet *, struct mbuf *, int, int, + void *, struct pf_pdesc *, struct pf_rule **, + struct pf_ruleset **); +int pf_test_icmp(struct pf_rule **, struct pf_state **, + int, struct ifnet *, struct mbuf *, int, int, + void *, struct pf_pdesc *, struct pf_rule **, + struct pf_ruleset **); +int pf_test_other(struct pf_rule **, struct pf_state **, + int, struct ifnet *, struct mbuf *, void *, + struct pf_pdesc *, struct pf_rule **, + struct pf_ruleset **); +int pf_test_fragment(struct pf_rule **, int, + struct ifnet *, struct mbuf *, void *, + struct pf_pdesc *, struct pf_rule **, + struct pf_ruleset **); +int pf_test_state_tcp(struct pf_state **, int, + struct ifnet *, struct mbuf *, int, int, + void *, struct pf_pdesc *, u_short *); +int pf_test_state_udp(struct pf_state **, int, + struct ifnet *, struct mbuf *, int, int, + void *, struct pf_pdesc *); +int pf_test_state_icmp(struct pf_state **, int, + struct ifnet *, struct mbuf *, int, int, + void *, struct pf_pdesc *); +int pf_test_state_other(struct pf_state **, int, + struct ifnet *, struct pf_pdesc *); +struct pf_tag *pf_get_tag(struct mbuf *); +int pf_match_tag(struct mbuf *, struct pf_rule *, + struct pf_rule *, struct pf_rule *, + struct pf_tag *, int *); + +#ifdef INET6 +void pf_poolmask(struct pf_addr *, struct pf_addr*, + struct pf_addr *, struct pf_addr *, u_int8_t); +void pf_addr_inc(struct pf_addr *, sa_family_t); +#endif /* INET6 */ + +void pf_hash(struct pf_addr *, struct pf_addr *, + struct pf_poolhashkey *, sa_family_t); +int pf_map_addr(u_int8_t, struct pf_pool *, + struct pf_addr *, struct pf_addr *, + struct pf_addr *); +int pf_get_sport(sa_family_t, u_int8_t, struct pf_pool *, + struct pf_addr *, struct pf_addr *, u_int16_t, + struct pf_addr *, u_int16_t*, u_int16_t, u_int16_t); +void pf_route(struct mbuf **, struct pf_rule *, int, + struct ifnet *, struct pf_state *); +void pf_route6(struct mbuf **, struct pf_rule *, int, + struct ifnet *, struct pf_state *); +int pf_socket_lookup(uid_t *, gid_t *, int, sa_family_t, + int, struct pf_pdesc *); +u_int8_t pf_get_wscale(struct mbuf *, int, u_int16_t, + sa_family_t); +u_int16_t pf_get_mss(struct mbuf *, int, u_int16_t, + sa_family_t); +u_int16_t pf_calc_mss(struct pf_addr *, sa_family_t, + u_int16_t); +int pf_check_proto_cksum(struct mbuf *, int, int, + u_int8_t, sa_family_t); + +struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX] = + { { &pf_state_pl, PFSTATE_HIWAT }, { &pf_frent_pl, PFFRAG_FRENT_HIWAT } }; + +#define STATE_LOOKUP() \ + do { \ + if (direction == PF_IN) \ + *state = pf_find_state(&tree_ext_gwy, &key); \ + else \ + *state = pf_find_state(&tree_lan_ext, &key); \ + if (*state == NULL) \ + return (PF_DROP); \ + if (direction == PF_OUT && \ + (((*state)->rule.ptr->rt == PF_ROUTETO && \ + (*state)->rule.ptr->direction == PF_OUT) || \ + ((*state)->rule.ptr->rt == PF_REPLYTO && \ + (*state)->rule.ptr->direction == PF_IN)) && \ + (*state)->rt_ifp != NULL && \ + (*state)->rt_ifp != ifp) \ + return (PF_PASS); \ + } while (0) + +#define STATE_TRANSLATE(s) \ + (s)->lan.addr.addr32[0] != (s)->gwy.addr.addr32[0] || \ + ((s)->af == AF_INET6 && \ + ((s)->lan.addr.addr32[1] != (s)->gwy.addr.addr32[1] || \ + (s)->lan.addr.addr32[2] != (s)->gwy.addr.addr32[2] || \ + (s)->lan.addr.addr32[3] != (s)->gwy.addr.addr32[3])) || \ + (s)->lan.port != (s)->gwy.port + +static __inline int pf_state_compare(struct pf_tree_node *, + struct pf_tree_node *); + +struct pf_state_tree tree_lan_ext, tree_ext_gwy; +RB_GENERATE(pf_state_tree, pf_tree_node, entry, pf_state_compare); + +static __inline int +pf_state_compare(struct pf_tree_node *a, struct pf_tree_node *b) +{ + int diff; + + if ((diff = a->proto - b->proto) != 0) + return (diff); + if ((diff = a->af - b->af) != 0) + return (diff); + switch (a->af) { +#ifdef INET + case AF_INET: + if (a->addr[0].addr32[0] > b->addr[0].addr32[0]) + return (1); + if (a->addr[0].addr32[0] < b->addr[0].addr32[0]) + return (-1); + if (a->addr[1].addr32[0] > b->addr[1].addr32[0]) + return (1); + if (a->addr[1].addr32[0] < b->addr[1].addr32[0]) + return (-1); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (a->addr[0].addr32[3] > b->addr[0].addr32[3]) + return (1); + if (a->addr[0].addr32[3] < b->addr[0].addr32[3]) + return (-1); + if (a->addr[1].addr32[3] > b->addr[1].addr32[3]) + return (1); + if (a->addr[1].addr32[3] < b->addr[1].addr32[3]) + return (-1); + if (a->addr[0].addr32[2] > b->addr[0].addr32[2]) + return (1); + if (a->addr[0].addr32[2] < b->addr[0].addr32[2]) + return (-1); + if (a->addr[1].addr32[2] > b->addr[1].addr32[2]) + return (1); + if (a->addr[1].addr32[2] < b->addr[1].addr32[2]) + return (-1); + if (a->addr[0].addr32[1] > b->addr[0].addr32[1]) + return (1); + if (a->addr[0].addr32[1] < b->addr[0].addr32[1]) + return (-1); + if (a->addr[1].addr32[1] > b->addr[1].addr32[1]) + return (1); + if (a->addr[1].addr32[1] < b->addr[1].addr32[1]) + return (-1); + if (a->addr[0].addr32[0] > b->addr[0].addr32[0]) + return (1); + if (a->addr[0].addr32[0] < b->addr[0].addr32[0]) + return (-1); + if (a->addr[1].addr32[0] > b->addr[1].addr32[0]) + return (1); + if (a->addr[1].addr32[0] < b->addr[1].addr32[0]) + return (-1); + break; +#endif /* INET6 */ + } + + if ((diff = a->port[0] - b->port[0]) != 0) + return (diff); + if ((diff = a->port[1] - b->port[1]) != 0) + return (diff); + + return (0); +} + +#ifdef INET6 +void +pf_addrcpy(struct pf_addr *dst, struct pf_addr *src, sa_family_t af) +{ + switch (af) { +#ifdef INET + case AF_INET: + dst->addr32[0] = src->addr32[0]; + break; +#endif /* INET */ + case AF_INET6: + dst->addr32[0] = src->addr32[0]; + dst->addr32[1] = src->addr32[1]; + dst->addr32[2] = src->addr32[2]; + dst->addr32[3] = src->addr32[3]; + break; + } +} +#endif + +struct pf_state * +pf_find_state(struct pf_state_tree *tree, struct pf_tree_node *key) +{ + struct pf_tree_node *k; + + pf_status.fcounters[FCNT_STATE_SEARCH]++; + k = RB_FIND(pf_state_tree, tree, key); + if (k) + return (k->state); + else + return (NULL); +} + +int +pf_insert_state(struct pf_state *state) +{ + struct pf_tree_node *keya, *keyb; + + keya = pool_get(&pf_tree_pl, PR_NOWAIT); + if (keya == NULL) + return (-1); + keya->state = state; + keya->proto = state->proto; + keya->af = state->af; + PF_ACPY(&keya->addr[0], &state->lan.addr, state->af); + keya->port[0] = state->lan.port; + PF_ACPY(&keya->addr[1], &state->ext.addr, state->af); + keya->port[1] = state->ext.port; + + /* Thou MUST NOT insert multiple duplicate keys */ + if (RB_INSERT(pf_state_tree, &tree_lan_ext, keya) != NULL) { + if (pf_status.debug >= PF_DEBUG_MISC) { + printf("pf: state insert failed: tree_lan_ext"); + printf(" lan: "); + pf_print_host(&state->lan.addr, state->lan.port, + state->af); + printf(" gwy: "); + pf_print_host(&state->gwy.addr, state->gwy.port, + state->af); + printf(" ext: "); + pf_print_host(&state->ext.addr, state->ext.port, + state->af); + printf("\n"); + } + pool_put(&pf_tree_pl, keya); + return (-1); + } + + keyb = pool_get(&pf_tree_pl, PR_NOWAIT); + if (keyb == NULL) { + /* Need to pull out the other state */ + RB_REMOVE(pf_state_tree, &tree_lan_ext, keya); + pool_put(&pf_tree_pl, keya); + return (-1); + } + keyb->state = state; + keyb->proto = state->proto; + keyb->af = state->af; + PF_ACPY(&keyb->addr[0], &state->ext.addr, state->af); + keyb->port[0] = state->ext.port; + PF_ACPY(&keyb->addr[1], &state->gwy.addr, state->af); + keyb->port[1] = state->gwy.port; + + if (RB_INSERT(pf_state_tree, &tree_ext_gwy, keyb) != NULL) { + if (pf_status.debug >= PF_DEBUG_MISC) { + printf("pf: state insert failed: tree_ext_gwy"); + printf(" lan: "); + pf_print_host(&state->lan.addr, state->lan.port, + state->af); + printf(" gwy: "); + pf_print_host(&state->gwy.addr, state->gwy.port, + state->af); + printf(" ext: "); + pf_print_host(&state->ext.addr, state->ext.port, + state->af); + printf("\n"); + } + RB_REMOVE(pf_state_tree, &tree_lan_ext, keya); + pool_put(&pf_tree_pl, keya); + pool_put(&pf_tree_pl, keyb); + return (-1); + } + + pf_status.fcounters[FCNT_STATE_INSERT]++; + pf_status.states++; +#if NPFSYNC + pfsync_insert_state(state); +#endif + return (0); +} + +void +pf_purge_timeout(void *arg) +{ +#ifdef __OpenBSD__ + struct timeout *to = arg; +#else + struct callout *to = arg; +#endif + int s; + + s = splsoftnet(); + pf_purge_expired_states(); + pf_purge_expired_fragments(); + splx(s); + +#ifdef __OpenBSD__ + timeout_add(to, pf_default_rule.timeout[PFTM_INTERVAL] * hz); +#else + callout_reset(to, pf_default_rule.timeout[PFTM_INTERVAL] * hz, + pf_purge_timeout, to); +#endif +} + +u_int32_t +pf_state_expires(const struct pf_state *state) +{ + u_int32_t timeout; + u_int32_t start; + u_int32_t end; + u_int32_t states; + + /* handle all PFTM_* > PFTM_MAX here */ + if (state->timeout == PFTM_PURGE) + return (time.tv_sec); + if (state->timeout == PFTM_UNTIL_PACKET) + return (0); + KASSERT(state->timeout < PFTM_MAX); + timeout = state->rule.ptr->timeout[state->timeout]; + if (!timeout) + timeout = pf_default_rule.timeout[state->timeout]; + start = state->rule.ptr->timeout[PFTM_ADAPTIVE_START]; + if (start) { + end = state->rule.ptr->timeout[PFTM_ADAPTIVE_END]; + states = state->rule.ptr->states; + } else { + start = pf_default_rule.timeout[PFTM_ADAPTIVE_START]; + end = pf_default_rule.timeout[PFTM_ADAPTIVE_END]; + states = pf_status.states; + } + if (end && states > start && start < end) { + if (states < end) + return (state->expire + timeout * (end - states) / + (end - start)); + else + return (time.tv_sec); + } + return (state->expire + timeout); +} + +void +pf_purge_expired_states(void) +{ + struct pf_tree_node *cur, *peer, *next; + struct pf_tree_node key; + + for (cur = RB_MIN(pf_state_tree, &tree_ext_gwy); cur; cur = next) { + next = RB_NEXT(pf_state_tree, &tree_ext_gwy, cur); + + if (pf_state_expires(cur->state) <= time.tv_sec) { + if (cur->state->src.state == PF_TCPS_PROXY_DST) + pf_send_tcp(cur->state->rule.ptr, + cur->state->af, + &cur->state->ext.addr, + &cur->state->lan.addr, + cur->state->ext.port, + cur->state->lan.port, + cur->state->src.seqhi, + cur->state->src.seqlo + 1, + 0, + TH_RST|TH_ACK, 0, 0); + RB_REMOVE(pf_state_tree, &tree_ext_gwy, cur); + + /* Need this key's peer (in the other tree) */ + key.state = cur->state; + key.proto = cur->state->proto; + key.af = cur->state->af; + PF_ACPY(&key.addr[0], &cur->state->lan.addr, + cur->state->af); + key.port[0] = cur->state->lan.port; + PF_ACPY(&key.addr[1], &cur->state->ext.addr, + cur->state->af); + key.port[1] = cur->state->ext.port; + + peer = RB_FIND(pf_state_tree, &tree_lan_ext, &key); + KASSERT(peer); + KASSERT(peer->state == cur->state); + RB_REMOVE(pf_state_tree, &tree_lan_ext, peer); + +#if NPFSYNC + pfsync_delete_state(cur->state); +#endif + if (--cur->state->rule.ptr->states <= 0) + pf_rm_rule(NULL, cur->state->rule.ptr); + if (cur->state->nat_rule.ptr != NULL) + if (--cur->state->nat_rule.ptr->states <= 0) + pf_rm_rule(NULL, + cur->state->nat_rule.ptr); + if (cur->state->anchor.ptr != NULL) + if (--cur->state->anchor.ptr->states <= 0) + pf_rm_rule(NULL, + cur->state->anchor.ptr); + pf_normalize_tcp_cleanup(cur->state); + pool_put(&pf_state_pl, cur->state); + pool_put(&pf_tree_pl, cur); + pool_put(&pf_tree_pl, peer); + pf_status.fcounters[FCNT_STATE_REMOVALS]++; + pf_status.states--; + } + } +} + +int +pf_tbladdr_setup(struct pf_ruleset *rs, struct pf_addr_wrap *aw) +{ + if (aw->type != PF_ADDR_TABLE) + return (0); + if ((aw->p.tbl = pfr_attach_table(rs, aw->v.tblname)) == NULL) + return (1); + return (0); +} + +void +pf_tbladdr_remove(struct pf_addr_wrap *aw) +{ + if (aw->type != PF_ADDR_TABLE || aw->p.tbl == NULL) + return; + pfr_detach_table(aw->p.tbl); + aw->p.tbl = NULL; +} + +void +pf_tbladdr_copyout(struct pf_addr_wrap *aw) +{ + if (aw->type != PF_ADDR_TABLE || aw->p.tbl == NULL) + return; + aw->p.tblcnt = (aw->p.tbl->pfrkt_flags & PFR_TFLAG_ACTIVE) ? + aw->p.tbl->pfrkt_cnt : -1; +} + +int +pf_dynaddr_setup(struct pf_addr_wrap *aw, sa_family_t af) +{ + if (aw->type != PF_ADDR_DYNIFTL) + return (0); + aw->p.dyn = pool_get(&pf_addr_pl, PR_NOWAIT); + if (aw->p.dyn == NULL) + return (1); + bcopy(aw->v.ifname, aw->p.dyn->ifname, sizeof(aw->p.dyn->ifname)); + aw->p.dyn->ifp = ifunit(aw->p.dyn->ifname); + if (aw->p.dyn->ifp == NULL) { + pool_put(&pf_addr_pl, aw->p.dyn); + aw->p.dyn = NULL; + return (1); + } + aw->p.dyn->addr = &aw->v.a.addr; + aw->p.dyn->af = af; + aw->p.dyn->undefined = 1; +#ifdef __OpenBSD__ + aw->p.dyn->hook_cookie = hook_establish( + aw->p.dyn->ifp->if_addrhooks, 1, + pf_dynaddr_update, aw->p.dyn); + if (aw->p.dyn->hook_cookie == NULL) { + pool_put(&pf_addr_pl, aw->p.dyn); + aw->p.dyn = NULL; + return (1); + } +#endif + pf_dynaddr_update(aw->p.dyn); + return (0); +} + +void +pf_dynaddr_update(void *p) +{ + struct pf_addr_dyn *ad = (struct pf_addr_dyn *)p; + struct ifaddr *ia; + int s, changed = 0; + + if (ad == NULL || ad->ifp == NULL) + panic("pf_dynaddr_update"); + s = splsoftnet(); + TAILQ_FOREACH(ia, &ad->ifp->if_addrlist, ifa_list) + if (ia->ifa_addr != NULL && + ia->ifa_addr->sa_family == ad->af) { + if (ad->af == AF_INET) { + struct in_addr *a, *b; + + a = &ad->addr->v4; + b = &((struct sockaddr_in *)ia->ifa_addr) + ->sin_addr; + if (ad->undefined || + memcmp(a, b, sizeof(*a))) { + bcopy(b, a, sizeof(*a)); + changed = 1; + } + } else if (ad->af == AF_INET6) { + struct in6_addr *a, *b; + + a = &ad->addr->v6; + b = &((struct sockaddr_in6 *)ia->ifa_addr) + ->sin6_addr; + if (ad->undefined || + memcmp(a, b, sizeof(*a))) { + bcopy(b, a, sizeof(*a)); + changed = 1; + } + } + if (changed) + ad->undefined = 0; + break; + } + if (ia == NULL) + ad->undefined = 1; + splx(s); +} + +void +pf_dynaddr_remove(struct pf_addr_wrap *aw) +{ + if (aw->type != PF_ADDR_DYNIFTL || aw->p.dyn == NULL) + return; +#ifdef __OpenBSD__ + hook_disestablish(aw->p.dyn->ifp->if_addrhooks, + aw->p.dyn->hook_cookie); +#endif + pool_put(&pf_addr_pl, aw->p.dyn); + aw->p.dyn = NULL; +} + +void +pf_dynaddr_copyout(struct pf_addr_wrap *aw) +{ + if (aw->type != PF_ADDR_DYNIFTL || aw->p.dyn == NULL) + return; + bcopy(aw->p.dyn->ifname, aw->v.ifname, sizeof(aw->v.ifname)); + aw->p.dyn = (struct pf_addr_dyn *)1; +} + +void +pf_print_host(struct pf_addr *addr, u_int16_t p, sa_family_t af) +{ + switch (af) { +#ifdef INET + case AF_INET: { + u_int32_t a = ntohl(addr->addr32[0]); + printf("%u.%u.%u.%u", (a>>24)&255, (a>>16)&255, + (a>>8)&255, a&255); + if (p) { + p = ntohs(p); + printf(":%u", p); + } + break; + } +#endif /* INET */ +#ifdef INET6 + case AF_INET6: { + u_int16_t b; + u_int8_t i, curstart = 255, curend = 0, + maxstart = 0, maxend = 0; + for (i = 0; i < 8; i++) { + if (!addr->addr16[i]) { + if (curstart == 255) + curstart = i; + else + curend = i; + } else { + if (curstart) { + if ((curend - curstart) > + (maxend - maxstart)) { + maxstart = curstart; + maxend = curend; + curstart = 255; + } + } + } + } + for (i = 0; i < 8; i++) { + if (i >= maxstart && i <= maxend) { + if (maxend != 7) { + if (i == maxstart) + printf(":"); + } else { + if (i == maxend) + printf(":"); + } + } else { + b = ntohs(addr->addr16[i]); + printf("%x", b); + if (i < 7) + printf(":"); + } + } + if (p) { + p = ntohs(p); + printf("[%u]", p); + } + break; + } +#endif /* INET6 */ + } +} + +void +pf_print_state(struct pf_state *s) +{ + switch (s->proto) { + case IPPROTO_TCP: + printf("TCP "); + break; + case IPPROTO_UDP: + printf("UDP "); + break; + case IPPROTO_ICMP: + printf("ICMP "); + break; + case IPPROTO_ICMPV6: + printf("ICMPV6 "); + break; + default: + printf("%u ", s->proto); + break; + } + pf_print_host(&s->lan.addr, s->lan.port, s->af); + printf(" "); + pf_print_host(&s->gwy.addr, s->gwy.port, s->af); + printf(" "); + pf_print_host(&s->ext.addr, s->ext.port, s->af); + printf(" [lo=%u high=%u win=%u modulator=%u", s->src.seqlo, + s->src.seqhi, s->src.max_win, s->src.seqdiff); + if (s->src.wscale && s->dst.wscale) + printf(" wscale=%u", s->src.wscale & PF_WSCALE_MASK); + printf("]"); + printf(" [lo=%u high=%u win=%u modulator=%u", s->dst.seqlo, + s->dst.seqhi, s->dst.max_win, s->dst.seqdiff); + if (s->src.wscale && s->dst.wscale) + printf(" wscale=%u", s->dst.wscale & PF_WSCALE_MASK); + printf("]"); + printf(" %u:%u", s->src.state, s->dst.state); +} + +void +pf_print_flags(u_int8_t f) +{ + if (f) + printf(" "); + if (f & TH_FIN) + printf("F"); + if (f & TH_SYN) + printf("S"); + if (f & TH_RST) + printf("R"); + if (f & TH_PUSH) + printf("P"); + if (f & TH_ACK) + printf("A"); + if (f & TH_URG) + printf("U"); +#ifdef TH_ECE + if (f & TH_ECE) + printf("E"); +#endif +#ifdef TH_CWR + if (f & TH_CWR) + printf("W"); +#endif +} + +#define PF_SET_SKIP_STEPS(i) \ + do { \ + while (head[i] != cur) { \ + head[i]->skip[i].ptr = cur; \ + head[i] = TAILQ_NEXT(head[i], entries); \ + } \ + } while (0) + +void +pf_calc_skip_steps(struct pf_rulequeue *rules) +{ + struct pf_rule *cur, *prev, *head[PF_SKIP_COUNT]; + int i; + + cur = TAILQ_FIRST(rules); + prev = cur; + for (i = 0; i < PF_SKIP_COUNT; ++i) + head[i] = cur; + while (cur != NULL) { + + if (cur->ifp != prev->ifp || cur->ifnot != prev->ifnot) + PF_SET_SKIP_STEPS(PF_SKIP_IFP); + if (cur->direction != prev->direction) + PF_SET_SKIP_STEPS(PF_SKIP_DIR); + if (cur->af != prev->af) + PF_SET_SKIP_STEPS(PF_SKIP_AF); + if (cur->proto != prev->proto) + PF_SET_SKIP_STEPS(PF_SKIP_PROTO); + if (cur->src.addr.type == PF_ADDR_DYNIFTL || + prev->src.addr.type == PF_ADDR_DYNIFTL || + cur->src.addr.type == PF_ADDR_TABLE || + prev->src.addr.type == PF_ADDR_TABLE || + cur->src.not != prev->src.not || + (cur->src.addr.type == PF_ADDR_NOROUTE) != + (prev->src.addr.type == PF_ADDR_NOROUTE) || + !PF_AEQ(&cur->src.addr.v.a.addr, + &prev->src.addr.v.a.addr, 0) || + !PF_AEQ(&cur->src.addr.v.a.mask, + &prev->src.addr.v.a.mask, 0)) + PF_SET_SKIP_STEPS(PF_SKIP_SRC_ADDR); + if (cur->src.port[0] != prev->src.port[0] || + cur->src.port[1] != prev->src.port[1] || + cur->src.port_op != prev->src.port_op) + PF_SET_SKIP_STEPS(PF_SKIP_SRC_PORT); + if (cur->dst.addr.type == PF_ADDR_DYNIFTL || + prev->dst.addr.type == PF_ADDR_DYNIFTL || + cur->dst.addr.type == PF_ADDR_TABLE || + prev->dst.addr.type == PF_ADDR_TABLE || + cur->dst.not != prev->dst.not || + (cur->dst.addr.type == PF_ADDR_NOROUTE) != + (prev->dst.addr.type == PF_ADDR_NOROUTE) || + !PF_AEQ(&cur->dst.addr.v.a.addr, + &prev->dst.addr.v.a.addr, 0) || + !PF_AEQ(&cur->dst.addr.v.a.mask, + &prev->dst.addr.v.a.mask, 0)) + PF_SET_SKIP_STEPS(PF_SKIP_DST_ADDR); + if (cur->dst.port[0] != prev->dst.port[0] || + cur->dst.port[1] != prev->dst.port[1] || + cur->dst.port_op != prev->dst.port_op) + PF_SET_SKIP_STEPS(PF_SKIP_DST_PORT); + + prev = cur; + cur = TAILQ_NEXT(cur, entries); + } + for (i = 0; i < PF_SKIP_COUNT; ++i) + PF_SET_SKIP_STEPS(i); +} + +void +pf_rule_set_qid(struct pf_rulequeue *rules) +{ + struct pf_rule *rule; + + TAILQ_FOREACH(rule, rules, entries) + if (rule->qname[0] != 0) { + rule->qid = pf_qname_to_qid(rule->qname); + if (rule->pqname[0] != 0) + rule->pqid = pf_qname_to_qid(rule->pqname); + else + rule->pqid = rule->qid; + } +} + +u_int32_t +pf_qname_to_qid(char *qname) +{ + struct pf_altq *altq; + + TAILQ_FOREACH(altq, pf_altqs_active, entries) + if (!strcmp(altq->qname, qname)) + return (altq->qid); + + return (0); +} + +void +pf_update_anchor_rules() +{ + struct pf_rule *rule; + int i; + + for (i = 0; i < PF_RULESET_MAX; ++i) + TAILQ_FOREACH(rule, pf_main_ruleset.rules[i].active.ptr, + entries) + if (rule->anchorname[0]) + rule->anchor = pf_find_anchor(rule->anchorname); + else + rule->anchor = NULL; +} + +u_int16_t +pf_cksum_fixup(u_int16_t cksum, u_int16_t old, u_int16_t new, u_int8_t udp) +{ + u_int32_t l; + + if (udp && !cksum) + return (0x0000); + l = cksum + old - new; + l = (l >> 16) + (l & 65535); + l = l & 65535; + if (udp && !l) + return (0xFFFF); + return (l); +} + +void +pf_change_ap(struct pf_addr *a, u_int16_t *p, u_int16_t *ic, u_int16_t *pc, + struct pf_addr *an, u_int16_t pn, u_int8_t u, sa_family_t af) +{ + struct pf_addr ao; + u_int16_t po = *p; + + PF_ACPY(&ao, a, af); + PF_ACPY(a, an, af); + + *p = pn; + + switch (af) { +#ifdef INET + case AF_INET: + *ic = pf_cksum_fixup(pf_cksum_fixup(*ic, + ao.addr16[0], an->addr16[0], 0), + ao.addr16[1], an->addr16[1], 0); + *p = pn; + *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc, + ao.addr16[0], an->addr16[0], u), + ao.addr16[1], an->addr16[1], u), + po, pn, u); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + *pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc, + ao.addr16[0], an->addr16[0], u), + ao.addr16[1], an->addr16[1], u), + ao.addr16[2], an->addr16[2], u), + ao.addr16[3], an->addr16[3], u), + ao.addr16[4], an->addr16[4], u), + ao.addr16[5], an->addr16[5], u), + ao.addr16[6], an->addr16[6], u), + ao.addr16[7], an->addr16[7], u), + po, pn, u); + break; +#endif /* INET6 */ + } +} + + +/* Changes a u_int32_t. Uses a void * so there are no align restrictions */ +void +pf_change_a(void *a, u_int16_t *c, u_int32_t an, u_int8_t u) +{ + u_int32_t ao; + + memcpy(&ao, a, sizeof(ao)); + memcpy(a, &an, sizeof(u_int32_t)); + *c = pf_cksum_fixup(pf_cksum_fixup(*c, ao / 65536, an / 65536, u), + ao % 65536, an % 65536, u); +} + +#ifdef INET6 +void +pf_change_a6(struct pf_addr *a, u_int16_t *c, struct pf_addr *an, u_int8_t u) +{ + struct pf_addr ao; + + PF_ACPY(&ao, a, AF_INET6); + PF_ACPY(a, an, AF_INET6); + + *c = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(*c, + ao.addr16[0], an->addr16[0], u), + ao.addr16[1], an->addr16[1], u), + ao.addr16[2], an->addr16[2], u), + ao.addr16[3], an->addr16[3], u), + ao.addr16[4], an->addr16[4], u), + ao.addr16[5], an->addr16[5], u), + ao.addr16[6], an->addr16[6], u), + ao.addr16[7], an->addr16[7], u); +} +#endif /* INET6 */ + +void +pf_change_icmp(struct pf_addr *ia, u_int16_t *ip, struct pf_addr *oa, + struct pf_addr *na, u_int16_t np, u_int16_t *pc, u_int16_t *h2c, + u_int16_t *ic, u_int16_t *hc, u_int8_t u, sa_family_t af) +{ + struct pf_addr oia, ooa; + + PF_ACPY(&oia, ia, af); + PF_ACPY(&ooa, oa, af); + + /* Change inner protocol port, fix inner protocol checksum. */ + if (ip != NULL) { + u_int16_t oip = *ip; + u_int32_t opc; + + if (pc != NULL) + opc = *pc; + *ip = np; + if (pc != NULL) + *pc = pf_cksum_fixup(*pc, oip, *ip, u); + *ic = pf_cksum_fixup(*ic, oip, *ip, 0); + if (pc != NULL) + *ic = pf_cksum_fixup(*ic, opc, *pc, 0); + } + /* Change inner ip address, fix inner ip and icmp checksums. */ + PF_ACPY(ia, na, af); + switch (af) { +#ifdef INET + case AF_INET: { + u_int32_t oh2c = *h2c; + + *h2c = pf_cksum_fixup(pf_cksum_fixup(*h2c, + oia.addr16[0], ia->addr16[0], 0), + oia.addr16[1], ia->addr16[1], 0); + *ic = pf_cksum_fixup(pf_cksum_fixup(*ic, + oia.addr16[0], ia->addr16[0], 0), + oia.addr16[1], ia->addr16[1], 0); + *ic = pf_cksum_fixup(*ic, oh2c, *h2c, 0); + break; + } +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + *ic = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(*ic, + oia.addr16[0], ia->addr16[0], u), + oia.addr16[1], ia->addr16[1], u), + oia.addr16[2], ia->addr16[2], u), + oia.addr16[3], ia->addr16[3], u), + oia.addr16[4], ia->addr16[4], u), + oia.addr16[5], ia->addr16[5], u), + oia.addr16[6], ia->addr16[6], u), + oia.addr16[7], ia->addr16[7], u); + break; +#endif /* INET6 */ + } + /* Change outer ip address, fix outer ip or icmpv6 checksum. */ + PF_ACPY(oa, na, af); + switch (af) { +#ifdef INET + case AF_INET: + *hc = pf_cksum_fixup(pf_cksum_fixup(*hc, + ooa.addr16[0], oa->addr16[0], 0), + ooa.addr16[1], oa->addr16[1], 0); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + *ic = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup( + pf_cksum_fixup(pf_cksum_fixup(*ic, + ooa.addr16[0], oa->addr16[0], u), + ooa.addr16[1], oa->addr16[1], u), + ooa.addr16[2], oa->addr16[2], u), + ooa.addr16[3], oa->addr16[3], u), + ooa.addr16[4], oa->addr16[4], u), + ooa.addr16[5], oa->addr16[5], u), + ooa.addr16[6], oa->addr16[6], u), + ooa.addr16[7], oa->addr16[7], u); + break; +#endif /* INET6 */ + } +} + +void +pf_send_tcp(const struct pf_rule *r, sa_family_t af, + const struct pf_addr *saddr, const struct pf_addr *daddr, + u_int16_t sport, u_int16_t dport, u_int32_t seq, u_int32_t ack, + u_int8_t flags, u_int16_t win, u_int16_t mss, u_int8_t ttl) +{ + struct mbuf *m; + struct m_tag *mtag; + int len, tlen; +#ifdef INET + struct ip *h; +#endif /* INET */ +#ifdef INET6 + struct ip6_hdr *h6; +#endif /* INET6 */ + struct tcphdr *th; + char *opt; + + /* maximum segment size tcp option */ + tlen = sizeof(struct tcphdr); + if (mss) + tlen += 4; + + switch (af) { +#ifdef INET + case AF_INET: + len = sizeof(struct ip) + tlen; + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + len = sizeof(struct ip6_hdr) + tlen; + break; +#endif /* INET6 */ + } + + /* create outgoing mbuf */ + mtag = m_tag_get(PACKET_TAG_PF_GENERATED, 0, M_NOWAIT); + if (mtag == NULL) + return; + m = m_gethdr(M_DONTWAIT, MT_HEADER); + if (m == NULL) { + m_tag_free(mtag); + return; + } + m_tag_prepend(m, mtag); +#ifdef ALTQ + if (r != NULL && r->qid) { + struct altq_tag *atag; + + mtag = m_tag_get(PACKET_TAG_PF_QID, sizeof(*atag), M_NOWAIT); + if (mtag != NULL) { + atag = (struct altq_tag *)(mtag + 1); + atag->qid = r->qid; + /* add hints for ecn */ + atag->af = af; + atag->hdr = mtod(m, struct ip *); + m_tag_prepend(m, mtag); + } + } +#endif + m->m_data += max_linkhdr; + m->m_pkthdr.len = m->m_len = len; + m->m_pkthdr.rcvif = NULL; + bzero(m->m_data, len); + switch (af) { +#ifdef INET + case AF_INET: + h = mtod(m, struct ip *); + + /* IP header fields included in the TCP checksum */ + h->ip_p = IPPROTO_TCP; + h->ip_len = htons(tlen); + h->ip_src.s_addr = saddr->v4.s_addr; + h->ip_dst.s_addr = daddr->v4.s_addr; + + th = (struct tcphdr *)((caddr_t)h + sizeof(struct ip)); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + h6 = mtod(m, struct ip6_hdr *); + + /* IP header fields included in the TCP checksum */ + h6->ip6_nxt = IPPROTO_TCP; + h6->ip6_plen = htons(tlen); + memcpy(&h6->ip6_src, &saddr->v6, sizeof(struct in6_addr)); + memcpy(&h6->ip6_dst, &daddr->v6, sizeof(struct in6_addr)); + + th = (struct tcphdr *)((caddr_t)h6 + sizeof(struct ip6_hdr)); + break; +#endif /* INET6 */ + } + + /* TCP header */ + th->th_sport = sport; + th->th_dport = dport; + th->th_seq = htonl(seq); + th->th_ack = htonl(ack); + th->th_off = tlen >> 2; + th->th_flags = flags; + th->th_win = htons(win); + + if (mss) { + opt = (char *)(th + 1); + opt[0] = TCPOPT_MAXSEG; + opt[1] = 4; + HTONS(mss); + bcopy((caddr_t)&mss, (caddr_t)(opt + 2), 2); + } + + switch (af) { +#ifdef INET + case AF_INET: + /* TCP checksum */ + th->th_sum = in_cksum(m, len); + + /* Finish the IP header */ + h->ip_v = 4; + h->ip_hl = sizeof(*h) >> 2; + h->ip_tos = IPTOS_LOWDELAY; +#ifdef __OpenBSD__ + h->ip_len = len; + h->ip_off = ip_mtudisc ? IP_DF : 0; +#else + h->ip_len = htons(len); + h->ip_off = htons(ip_mtudisc ? IP_DF : 0); +#endif + h->ip_ttl = ttl ? ttl : ip_defttl; + h->ip_sum = 0; + ip_output(m, (void *)NULL, (void *)NULL, 0, (void *)NULL, + (void *)NULL); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + /* TCP checksum */ + th->th_sum = in6_cksum(m, IPPROTO_TCP, + sizeof(struct ip6_hdr), tlen); + + h6->ip6_vfc |= IPV6_VERSION; + h6->ip6_hlim = IPV6_DEFHLIM; + + ip6_output(m, NULL, NULL, 0, NULL, NULL); +#endif /* INET6 */ + } +} + +void +pf_send_icmp(struct mbuf *m, u_int8_t type, u_int8_t code, sa_family_t af, + struct pf_rule *r) +{ + struct m_tag *mtag; + struct mbuf *m0; + + mtag = m_tag_get(PACKET_TAG_PF_GENERATED, 0, M_NOWAIT); + if (mtag == NULL) + return; + m0 = m_copy(m, 0, M_COPYALL); + if (m0 == NULL) { + m_tag_free(mtag); + return; + } + m_tag_prepend(m0, mtag); + +#ifdef ALTQ + if (r->qid) { + struct altq_tag *atag; + + mtag = m_tag_get(PACKET_TAG_PF_QID, sizeof(*atag), M_NOWAIT); + if (mtag != NULL) { + atag = (struct altq_tag *)(mtag + 1); + atag->qid = r->qid; + /* add hints for ecn */ + atag->af = af; + atag->hdr = mtod(m0, struct ip *); + m_tag_prepend(m0, mtag); + } + } +#endif + + switch (af) { +#ifdef INET + case AF_INET: + icmp_error(m0, type, code, 0, 0); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + icmp6_error(m0, type, code, 0); + break; +#endif /* INET6 */ + } +} + +/* + * Return 1 if the addresses a and b match (with mask m), otherwise return 0. + * If n is 0, they match if they are equal. If n is != 0, they match if they + * are different. + */ +int +pf_match_addr(u_int8_t n, struct pf_addr *a, struct pf_addr *m, + struct pf_addr *b, sa_family_t af) +{ + int match = 0; + + switch (af) { +#ifdef INET + case AF_INET: + if ((a->addr32[0] & m->addr32[0]) == + (b->addr32[0] & m->addr32[0])) + match++; + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (((a->addr32[0] & m->addr32[0]) == + (b->addr32[0] & m->addr32[0])) && + ((a->addr32[1] & m->addr32[1]) == + (b->addr32[1] & m->addr32[1])) && + ((a->addr32[2] & m->addr32[2]) == + (b->addr32[2] & m->addr32[2])) && + ((a->addr32[3] & m->addr32[3]) == + (b->addr32[3] & m->addr32[3]))) + match++; + break; +#endif /* INET6 */ + } + if (match) { + if (n) + return (0); + else + return (1); + } else { + if (n) + return (1); + else + return (0); + } +} + +int +pf_match(u_int8_t op, u_int16_t a1, u_int16_t a2, u_int16_t p) +{ + switch (op) { + case PF_OP_IRG: + return ((p > a1) && (p < a2)); + case PF_OP_XRG: + return ((p < a1) || (p > a2)); + case PF_OP_RRG: + return ((p >= a1) && (p <= a2)); + case PF_OP_EQ: + return (p == a1); + case PF_OP_NE: + return (p != a1); + case PF_OP_LT: + return (p < a1); + case PF_OP_LE: + return (p <= a1); + case PF_OP_GT: + return (p > a1); + case PF_OP_GE: + return (p >= a1); + } + return (0); /* never reached */ +} + +int +pf_match_port(u_int8_t op, u_int16_t a1, u_int16_t a2, u_int16_t p) +{ + NTOHS(a1); + NTOHS(a2); + NTOHS(p); + return (pf_match(op, a1, a2, p)); +} + +int +pf_match_uid(u_int8_t op, uid_t a1, uid_t a2, uid_t u) +{ + if (u == UID_MAX && op != PF_OP_EQ && op != PF_OP_NE) + return (0); + return (pf_match(op, a1, a2, u)); +} + +int +pf_match_gid(u_int8_t op, gid_t a1, gid_t a2, gid_t g) +{ + if (g == GID_MAX && op != PF_OP_EQ && op != PF_OP_NE) + return (0); + return (pf_match(op, a1, a2, g)); +} + +struct pf_tag * +pf_get_tag(struct mbuf *m) +{ + struct m_tag *mtag; + + if ((mtag = m_tag_find(m, PACKET_TAG_PF_TAG, NULL)) != NULL) + return ((struct pf_tag *)(mtag + 1)); + else + return (NULL); +} + +int +pf_match_tag(struct mbuf *m, struct pf_rule *r, struct pf_rule *nat, + struct pf_rule *rdr, struct pf_tag *pftag, int *tag) +{ + if (*tag == -1) { /* find mbuf tag */ + pftag = pf_get_tag(m); + if (pftag != NULL) + *tag = pftag->tag; + else + *tag = 0; + if (nat != NULL && nat->tag) + *tag = nat->tag; + if (rdr != NULL && rdr->tag) + *tag = rdr->tag; + } + + return ((!r->match_tag_not && r->match_tag == *tag) || + (r->match_tag_not && r->match_tag != *tag)); +} + +int +pf_tag_packet(struct mbuf *m, struct pf_tag *pftag, int tag) +{ + struct m_tag *mtag; + + if (tag <= 0) + return (0); + + if (pftag == NULL) { + mtag = m_tag_get(PACKET_TAG_PF_TAG, sizeof(*pftag), M_NOWAIT); + if (mtag == NULL) + return (1); + ((struct pf_tag *)(mtag + 1))->tag = tag; + m_tag_prepend(m, mtag); + } else + pftag->tag = tag; + + return (0); +} + +#define PF_STEP_INTO_ANCHOR(r, a, s, n) \ + do { \ + if ((r) == NULL || (r)->anchor == NULL || \ + (s) != NULL || (a) != NULL) \ + panic("PF_STEP_INTO_ANCHOR"); \ + (a) = (r); \ + (s) = TAILQ_FIRST(&(r)->anchor->rulesets); \ + (r) = NULL; \ + while ((s) != NULL && ((r) = \ + TAILQ_FIRST((s)->rules[n].active.ptr)) == NULL) \ + (s) = TAILQ_NEXT((s), entries); \ + if ((r) == NULL) { \ + (r) = TAILQ_NEXT((a), entries); \ + (a) = NULL; \ + } \ + } while (0) + +#define PF_STEP_OUT_OF_ANCHOR(r, a, s, n) \ + do { \ + if ((r) != NULL || (a) == NULL || (s) == NULL) \ + panic("PF_STEP_OUT_OF_ANCHOR"); \ + (s) = TAILQ_NEXT((s), entries); \ + while ((s) != NULL && ((r) = \ + TAILQ_FIRST((s)->rules[n].active.ptr)) == NULL) \ + (s) = TAILQ_NEXT((s), entries); \ + if ((r) == NULL) { \ + (r) = TAILQ_NEXT((a), entries); \ + (a) = NULL; \ + } \ + } while (0) + +#ifdef INET6 +void +pf_poolmask(struct pf_addr *naddr, struct pf_addr *raddr, + struct pf_addr *rmask, struct pf_addr *saddr, sa_family_t af) +{ + switch (af) { +#ifdef INET + case AF_INET: + naddr->addr32[0] = (raddr->addr32[0] & rmask->addr32[0]) | + ((rmask->addr32[0] ^ 0xffffffff ) & saddr->addr32[0]); + break; +#endif /* INET */ + case AF_INET6: + naddr->addr32[0] = (raddr->addr32[0] & rmask->addr32[0]) | + ((rmask->addr32[0] ^ 0xffffffff ) & saddr->addr32[0]); + naddr->addr32[1] = (raddr->addr32[1] & rmask->addr32[1]) | + ((rmask->addr32[1] ^ 0xffffffff ) & saddr->addr32[1]); + naddr->addr32[2] = (raddr->addr32[2] & rmask->addr32[2]) | + ((rmask->addr32[2] ^ 0xffffffff ) & saddr->addr32[2]); + naddr->addr32[3] = (raddr->addr32[3] & rmask->addr32[3]) | + ((rmask->addr32[3] ^ 0xffffffff ) & saddr->addr32[3]); + break; + } +} + +void +pf_addr_inc(struct pf_addr *addr, u_int8_t af) +{ + switch (af) { +#ifdef INET + case AF_INET: + addr->addr32[0] = htonl(ntohl(addr->addr32[0]) + 1); + break; +#endif /* INET */ + case AF_INET6: + if (addr->addr32[3] == 0xffffffff) { + addr->addr32[3] = 0; + if (addr->addr32[2] == 0xffffffff) { + addr->addr32[2] = 0; + if (addr->addr32[1] == 0xffffffff) { + addr->addr32[1] = 0; + addr->addr32[0] = + htonl(ntohl(addr->addr32[0]) + 1); + } else + addr->addr32[1] = + htonl(ntohl(addr->addr32[1]) + 1); + } else + addr->addr32[2] = + htonl(ntohl(addr->addr32[2]) + 1); + } else + addr->addr32[3] = + htonl(ntohl(addr->addr32[3]) + 1); + break; + } +} +#endif /* INET6 */ + +#define mix(a,b,c) \ + do { \ + a -= b; a -= c; a ^= (c >> 13); \ + b -= c; b -= a; b ^= (a << 8); \ + c -= a; c -= b; c ^= (b >> 13); \ + a -= b; a -= c; a ^= (c >> 12); \ + b -= c; b -= a; b ^= (a << 16); \ + c -= a; c -= b; c ^= (b >> 5); \ + a -= b; a -= c; a ^= (c >> 3); \ + b -= c; b -= a; b ^= (a << 10); \ + c -= a; c -= b; c ^= (b >> 15); \ + } while (0) + +/* + * hash function based on bridge_hash in if_bridge.c + */ +void +pf_hash(struct pf_addr *inaddr, struct pf_addr *hash, + struct pf_poolhashkey *key, sa_family_t af) +{ + u_int32_t a = 0x9e3779b9, b = 0x9e3779b9, c = key->key32[0]; + + switch (af) { +#ifdef INET + case AF_INET: + a += inaddr->addr32[0]; + b += key->key32[1]; + mix(a, b, c); + hash->addr32[0] = c + key->key32[2]; + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + a += inaddr->addr32[0]; + b += inaddr->addr32[2]; + mix(a, b, c); + hash->addr32[0] = c; + a += inaddr->addr32[1]; + b += inaddr->addr32[3]; + c += key->key32[1]; + mix(a, b, c); + hash->addr32[1] = c; + a += inaddr->addr32[2]; + b += inaddr->addr32[1]; + c += key->key32[2]; + mix(a, b, c); + hash->addr32[2] = c; + a += inaddr->addr32[3]; + b += inaddr->addr32[0]; + c += key->key32[3]; + mix(a, b, c); + hash->addr32[3] = c; + break; +#endif /* INET6 */ + } +} + +int +pf_map_addr(u_int8_t af, struct pf_pool *rpool, struct pf_addr *saddr, + struct pf_addr *naddr, struct pf_addr *init_addr) +{ + unsigned char hash[16]; + struct pf_addr *raddr = &rpool->cur->addr.addr.v.a.addr; + struct pf_addr *rmask = &rpool->cur->addr.addr.v.a.mask; + + if (rpool->cur->addr.addr.type == PF_ADDR_NOROUTE || + rpool->cur->addr.addr.type == PF_ADDR_TABLE) + return (1); +#ifdef __OpenBSD__ + if (rpool->cur->addr.addr.type == PF_ADDR_DYNIFTL && + rpool->cur->addr.addr.p.dyn->undefined) + return (1); +#else + if (rpool->cur->addr.addr.type == PF_ADDR_DYNIFTL) { + pf_dynaddr_update(rpool->cur->addr.addr.p.dyn); + if (rpool->cur->addr.addr.p.dyn->undefined) + return (1); + } +#endif + + switch (rpool->opts & PF_POOL_TYPEMASK) { + case PF_POOL_NONE: + PF_ACPY(naddr, raddr, af); + break; + case PF_POOL_BITMASK: + PF_POOLMASK(naddr, raddr, rmask, saddr, af); + break; + case PF_POOL_RANDOM: + if (init_addr != NULL && PF_AZERO(init_addr, af)) { + switch (af) { +#ifdef INET + case AF_INET: + rpool->counter.addr32[0] = arc4random(); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + if (rmask->addr32[3] != 0xffffffff) + rpool->counter.addr32[3] = arc4random(); + else + break; + if (rmask->addr32[2] != 0xffffffff) + rpool->counter.addr32[2] = arc4random(); + else + break; + if (rmask->addr32[1] != 0xffffffff) + rpool->counter.addr32[1] = arc4random(); + else + break; + if (rmask->addr32[0] != 0xffffffff) + rpool->counter.addr32[0] = arc4random(); + break; +#endif /* INET6 */ + } + PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af); + PF_ACPY(init_addr, naddr, af); + + } else { + PF_AINC(&rpool->counter, af); + PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af); + } + break; + case PF_POOL_SRCHASH: + pf_hash(saddr, (struct pf_addr *)&hash, &rpool->key, af); + PF_POOLMASK(naddr, raddr, rmask, (struct pf_addr *)&hash, af); + break; + case PF_POOL_ROUNDROBIN: + if (pf_match_addr(0, &rpool->cur->addr.addr.v.a.addr, + &rpool->cur->addr.addr.v.a.mask, &rpool->counter, af)) { + PF_ACPY(naddr, &rpool->counter, af); + PF_AINC(&rpool->counter, af); + } else { + if ((rpool->cur = + TAILQ_NEXT(rpool->cur, entries)) == NULL) + rpool->cur = TAILQ_FIRST(&rpool->list); + PF_ACPY(naddr, &rpool->cur->addr.addr.v.a.addr, af); + PF_ACPY(&rpool->counter, + &rpool->cur->addr.addr.v.a.addr, af); + PF_AINC(&rpool->counter, af); + } + break; + } + + if (pf_status.debug >= PF_DEBUG_MISC && + (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) { + printf("pf_map_addr: selected address: "); + pf_print_host(naddr, 0, af); + printf("\n"); + } + + return (0); +} + +int +pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_pool *rpool, + struct pf_addr *saddr, struct pf_addr *daddr, u_int16_t dport, + struct pf_addr *naddr, u_int16_t *nport, u_int16_t low, u_int16_t high) +{ + struct pf_tree_node key; + struct pf_addr init_addr; + u_int16_t cut; + + bzero(&init_addr, sizeof(init_addr)); + if (pf_map_addr(af, rpool, saddr, naddr, &init_addr)) + return (1); + + do { + key.af = af; + key.proto = proto; + PF_ACPY(&key.addr[0], daddr, key.af); + PF_ACPY(&key.addr[1], naddr, key.af); + key.port[0] = dport; + + /* + * port search; start random, step; + * similar 2 portloop in in_pcbbind + */ + if (!(proto == IPPROTO_TCP || proto == IPPROTO_UDP)) { + key.port[1] = 0; + if (pf_find_state(&tree_ext_gwy, &key) == NULL) + return (0); + } else if (low == 0 && high == 0) { + key.port[1] = *nport; + if (pf_find_state(&tree_ext_gwy, &key) == NULL) { + return (0); + } + } else if (low == high) { + key.port[1] = htons(low); + if (pf_find_state(&tree_ext_gwy, &key) == NULL) { + *nport = htons(low); + return (0); + } + } else { + u_int16_t tmp; + + if (low > high) { + tmp = low; + low = high; + high = tmp; + } + /* low < high */ + cut = arc4random() % (1 + high - low) + low; + /* low <= cut <= high */ + for (tmp = cut; tmp <= high; ++(tmp)) { + key.port[1] = htons(tmp); + if (pf_find_state(&tree_ext_gwy, &key) == + NULL) { + *nport = htons(tmp); + return (0); + } + } + for (tmp = cut - 1; tmp >= low; --(tmp)) { + key.port[1] = htons(tmp); + if (pf_find_state(&tree_ext_gwy, &key) == + NULL) { + *nport = htons(tmp); + return (0); + } + } + } + + switch (rpool->opts & PF_POOL_TYPEMASK) { + case PF_POOL_RANDOM: + case PF_POOL_ROUNDROBIN: + if (pf_map_addr(af, rpool, saddr, naddr, &init_addr)) + return (1); + break; + case PF_POOL_NONE: + case PF_POOL_SRCHASH: + case PF_POOL_BITMASK: + default: + return (1); + break; + } + } while (! PF_AEQ(&init_addr, naddr, af) ); + + return (1); /* none available */ +} + +struct pf_rule * +pf_match_translation(int direction, struct ifnet *ifp, u_int8_t proto, + struct pf_addr *saddr, u_int16_t sport, struct pf_addr *daddr, + u_int16_t dport, sa_family_t af, int rs_num) +{ + struct pf_rule *r, *rm = NULL, *anchorrule = NULL; + struct pf_ruleset *ruleset = NULL; + + r = TAILQ_FIRST(pf_main_ruleset.rules[rs_num].active.ptr); + while (r && rm == NULL) { + struct pf_rule_addr *src = NULL, *dst = NULL; + + if (r->action == PF_BINAT && direction == PF_IN) { + src = &r->dst; + if (r->rpool.cur != NULL) + dst = &r->rpool.cur->addr; + } else { + src = &r->src; + dst = &r->dst; + } + + r->evaluations++; + if (r->ifp != NULL && ((r->ifp != ifp && !r->ifnot) || + (r->ifp == ifp && r->ifnot))) + r = r->skip[PF_SKIP_IFP].ptr; + else if (r->direction && r->direction != direction) + r = r->skip[PF_SKIP_DIR].ptr; + else if (r->af && r->af != af) + r = r->skip[PF_SKIP_AF].ptr; + else if (r->proto && r->proto != proto) + r = r->skip[PF_SKIP_PROTO].ptr; + else if (PF_MISMATCHAW(&src->addr, saddr, af, src->not)) + r = r->skip[src == &r->src ? PF_SKIP_SRC_ADDR : + PF_SKIP_DST_ADDR].ptr; + else if (src->port_op && !pf_match_port(src->port_op, + src->port[0], src->port[1], sport)) + r = r->skip[src == &r->src ? PF_SKIP_SRC_PORT : + PF_SKIP_DST_PORT].ptr; + else if (dst != NULL && + PF_MISMATCHAW(&dst->addr, daddr, af, dst->not)) + r = dst == &r->dst ? r->skip[PF_SKIP_DST_ADDR].ptr : + TAILQ_NEXT(r, entries); + else if (dst != NULL && dst->port_op && + !pf_match_port(dst->port_op, dst->port[0], + dst->port[1], dport)) + r = dst == &r->dst ? r->skip[PF_SKIP_DST_PORT].ptr : + TAILQ_NEXT(r, entries); + else if (r->anchorname[0] && r->anchor == NULL) + r = TAILQ_NEXT(r, entries); + else if (r->anchor == NULL) + rm = r; + else + PF_STEP_INTO_ANCHOR(r, anchorrule, ruleset, rs_num); + if (r == NULL && anchorrule != NULL) + PF_STEP_OUT_OF_ANCHOR(r, anchorrule, ruleset, + rs_num); + } + if (rm != NULL && (rm->action == PF_NONAT || + rm->action == PF_NORDR || rm->action == PF_NOBINAT)) + return (NULL); + return (rm); +} + +struct pf_rule * +pf_get_translation(int direction, struct ifnet *ifp, u_int8_t proto, + struct pf_addr *saddr, u_int16_t sport, + struct pf_addr *daddr, u_int16_t dport, + struct pf_addr *naddr, u_int16_t *nport, sa_family_t af) +{ + struct pf_rule *r = NULL; + + if (direction == PF_OUT) { + r = pf_match_translation(direction, ifp, proto, + saddr, sport, daddr, dport, af, PF_RULESET_BINAT); + if (r == NULL) + r = pf_match_translation(direction, ifp, proto, + saddr, sport, daddr, dport, af, PF_RULESET_NAT); + } else { + r = pf_match_translation(direction, ifp, proto, + saddr, sport, daddr, dport, af, PF_RULESET_RDR); + if (r == NULL) + r = pf_match_translation(direction, ifp, proto, + saddr, sport, daddr, dport, af, PF_RULESET_BINAT); + } + + if (r != NULL) { + switch (r->action) { + case PF_NONAT: + case PF_NOBINAT: + case PF_NORDR: + return (NULL); + break; + case PF_NAT: + if (pf_get_sport(af, proto, &r->rpool, saddr, daddr, + dport, naddr, nport, r->rpool.proxy_port[0], + r->rpool.proxy_port[1])) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: NAT proxy port allocation " + "(%u-%u) failed\n", + r->rpool.proxy_port[0], + r->rpool.proxy_port[1])); + return (NULL); + } + break; + case PF_BINAT: + switch (direction) { + case PF_OUT: +#ifdef __OpenBSD__ + if (r->rpool.cur->addr.addr.type == + PF_ADDR_DYNIFTL && + r->rpool.cur->addr.addr.p.dyn->undefined) { + return (NULL); + } +#else + if (r->rpool.cur->addr.addr.type == + PF_ADDR_DYNIFTL) { + pf_dynaddr_update(r->rpool.cur->addr.addr.p.dyn); + if (r->rpool.cur->addr.addr.p.dyn->undefined) + return (NULL); + } +#endif + else + PF_POOLMASK(naddr, + &r->rpool.cur->addr.addr.v.a.addr, + &r->rpool.cur->addr.addr.v.a.mask, + saddr, af); + break; + case PF_IN: +#ifdef __OpenBSD__ + if (r->src.addr.type == PF_ADDR_DYNIFTL && + r->src.addr.p.dyn->undefined) + return (NULL); +#else + if (r->src.addr.type == PF_ADDR_DYNIFTL) { + pf_dynaddr_update(r->src.addr.p.dyn); + if (r->src.addr.p.dyn->undefined) + return (NULL); + } +#endif + else + PF_POOLMASK(naddr, + &r->src.addr.v.a.addr, + &r->src.addr.v.a.mask, saddr, af); + break; + } + break; + case PF_RDR: { + if (pf_map_addr(r->af, &r->rpool, saddr, naddr, NULL)) + return (NULL); + + if (r->rpool.proxy_port[1]) { + u_int32_t tmp_nport; + + tmp_nport = ((ntohs(dport) - + ntohs(r->dst.port[0])) % + (r->rpool.proxy_port[1] - + r->rpool.proxy_port[0] + 1)) + + r->rpool.proxy_port[0]; + + /* wrap around if necessary */ + if (tmp_nport > 65535) + tmp_nport -= 65535; + *nport = htons((u_int16_t)tmp_nport); + } else if (r->rpool.proxy_port[0]) + *nport = htons(r->rpool.proxy_port[0]); + break; + } + default: + return (NULL); + break; + } + } + + return (r); +} + +int +pf_socket_lookup(uid_t *uid, gid_t *gid, int direction, sa_family_t af, + int proto, struct pf_pdesc *pd) +{ + struct pf_addr *saddr, *daddr; + u_int16_t sport, dport; + struct inpcbtable *tb; +#ifdef __NetBSD__ + struct in6pcb *tb6; +#endif + struct inpcb *inp; +#ifdef __NetBSD__ + struct in6pcb *in6p; +#endif + + *uid = UID_MAX; + *gid = GID_MAX; + switch (proto) { + case IPPROTO_TCP: + sport = pd->hdr.tcp->th_sport; + dport = pd->hdr.tcp->th_dport; + tb = &tcbtable; +#ifdef __NetBSD__ + tb6 = &tcb6; +#endif + break; + case IPPROTO_UDP: + sport = pd->hdr.udp->uh_sport; + dport = pd->hdr.udp->uh_dport; + tb = &udbtable; +#ifdef __NetBSD__ + tb6 = &udb6; +#endif + break; + default: + return (0); + } + if (direction == PF_IN) { + saddr = pd->src; + daddr = pd->dst; + } else { + u_int16_t p; + + p = sport; + sport = dport; + dport = p; + saddr = pd->dst; + daddr = pd->src; + } + switch(af) { + case AF_INET: +#ifdef __OpenBSD__ + inp = in_pcbhashlookup(tb, saddr->v4, sport, daddr->v4, dport); + if (inp == NULL) { + inp = in_pcblookup(tb, &saddr->v4, sport, &daddr->v4, + dport, INPLOOKUP_WILDCARD); + if (inp == NULL) + return (0); + } +#else + inp = in_pcblookup_connect(tb, saddr->v4, sport, + daddr->v4, dport); + if (inp == NULL) { + inp = in_pcblookup_bind(tb, daddr->v4, dport); + if (inp == NULL) + return (0); + } +#endif + break; +#ifdef INET6 + case AF_INET6: +#ifdef __OpenBSD__ + inp = in6_pcbhashlookup(tb, &saddr->v6, sport, &daddr->v6, + dport); + if (inp == NULL) { + inp = in_pcblookup(tb, &saddr->v6, sport, &daddr->v6, + dport, INPLOOKUP_WILDCARD | INPLOOKUP_IPV6); + if (inp == NULL) + return (0); + } +#else + { + in6p = in6_pcblookup_connect(tb6, &saddr->v6, sport, + &daddr->v6, dport, 0); + if (in6p == NULL) { + in6p = in6_pcblookup_bind(tb6, &daddr->v6, dport, 0); + if (in6p == NULL) + return (0); + } + } +#endif + break; +#endif /* INET6 */ + + default: + return (0); + } +#ifdef __OpenBSD__ + *uid = inp->inp_socket->so_euid; + *gid = inp->inp_socket->so_egid; +#else + switch (af) { + case AF_INET: + *uid = inp->inp_socket->so_uid; + break; + case AF_INET6: + *uid = in6p->in6p_socket->so_uid; + break; + } +#endif + return (1); +} + +u_int8_t +pf_get_wscale(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af) +{ + int hlen; + u_int8_t hdr[60]; + u_int8_t *opt, optlen; + u_int8_t wscale = 0; + + hlen = th_off << 2; /* hlen <= sizeof(hdr) */ + if (hlen <= sizeof(struct tcphdr)) + return (0); + if (!pf_pull_hdr(m, off, hdr, hlen, NULL, NULL, af)) + return (0); + opt = hdr + sizeof(struct tcphdr); + hlen -= sizeof(struct tcphdr); + while (hlen >= 3) { + switch (*opt) { + case TCPOPT_EOL: + case TCPOPT_NOP: + ++opt; + --hlen; + break; + case TCPOPT_WINDOW: + wscale = opt[2]; + if (wscale > TCP_MAX_WINSHIFT) + wscale = TCP_MAX_WINSHIFT; + wscale |= PF_WSCALE_FLAG; + /* fallthrough */ + default: + optlen = opt[1]; + if (optlen < 2) + optlen = 2; + hlen -= optlen; + opt += optlen; + } + } + return (wscale); +} + +u_int16_t +pf_get_mss(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af) +{ + int hlen; + u_int8_t hdr[60]; + u_int8_t *opt, optlen; + u_int16_t mss = tcp_mssdflt; + + hlen = th_off << 2; /* hlen <= sizeof(hdr) */ + if (hlen <= sizeof(struct tcphdr)) + return (0); + if (!pf_pull_hdr(m, off, hdr, hlen, NULL, NULL, af)) + return (0); + opt = hdr + sizeof(struct tcphdr); + hlen -= sizeof(struct tcphdr); + while (hlen >= TCPOLEN_MAXSEG) { + switch (*opt) { + case TCPOPT_EOL: + case TCPOPT_NOP: + ++opt; + --hlen; + break; + case TCPOPT_MAXSEG: + bcopy((caddr_t)(opt + 2), (caddr_t)&mss, 2); + /* fallthrough */ + default: + optlen = opt[1]; + if (optlen < 2) + optlen = 2; + hlen -= optlen; + opt += optlen; + } + } + return (mss); +} + +u_int16_t +pf_calc_mss(struct pf_addr *addr, sa_family_t af, u_int16_t offer) +{ +#ifdef INET + struct sockaddr_in *dst; + struct route ro; +#endif /* INET */ +#ifdef INET6 + struct sockaddr_in6 *dst6; + struct route_in6 ro6; +#endif /* INET6 */ + struct rtentry *rt = NULL; + int hlen; + u_int16_t mss = tcp_mssdflt; + + switch (af) { +#ifdef INET + case AF_INET: + hlen = sizeof(struct ip); + bzero(&ro, sizeof(ro)); + dst = (struct sockaddr_in *)&ro.ro_dst; + dst->sin_family = AF_INET; + dst->sin_len = sizeof(*dst); + dst->sin_addr = addr->v4; +#ifdef __OpenBSD__ + rtalloc_noclone(&ro, NO_CLONING); +#else + rtalloc(&ro); +#endif + rt = ro.ro_rt; + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + hlen = sizeof(struct ip6_hdr); + bzero(&ro6, sizeof(ro6)); + dst6 = (struct sockaddr_in6 *)&ro6.ro_dst; + dst6->sin6_family = AF_INET6; + dst6->sin6_len = sizeof(*dst6); + dst6->sin6_addr = addr->v6; +#ifdef __OpenBSD__ + rtalloc_noclone((struct route *)&ro6, NO_CLONING); +#else + rtalloc((struct route *)&ro6); +#endif + rt = ro6.ro_rt; + break; +#endif /* INET6 */ + } + + if (rt && rt->rt_ifp) { + mss = rt->rt_ifp->if_mtu - hlen - sizeof(struct tcphdr); + mss = max(tcp_mssdflt, mss); + RTFREE(rt); + } + mss = min(mss, offer); + mss = max(mss, 64); /* sanity - at least max opt space */ + return (mss); +} + +int +pf_test_tcp(struct pf_rule **rm, struct pf_state **sm, int direction, + struct ifnet *ifp, struct mbuf *m, int ipoff, int off, void *h, + struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm) +{ + struct pf_rule *nat = NULL, *rdr = NULL; + struct pf_addr *saddr = pd->src, *daddr = pd->dst; + struct pf_addr baddr, naddr; + struct tcphdr *th = pd->hdr.tcp; + u_int16_t bport, nport = 0; + sa_family_t af = pd->af; + int lookup = -1; + uid_t uid; + gid_t gid; + struct pf_rule *r, *a = NULL; + struct pf_ruleset *ruleset = NULL; + u_short reason; + int rewrite = 0; + struct pf_tag *pftag = NULL; + int tag = -1; + u_int16_t mss = tcp_mssdflt; + + if (direction == PF_OUT) { + bport = nport = th->th_sport; + /* check outgoing packet for BINAT/NAT */ + if ((nat = pf_get_translation(PF_OUT, ifp, IPPROTO_TCP, + saddr, th->th_sport, daddr, th->th_dport, + &naddr, &nport, af)) != NULL) { + PF_ACPY(&baddr, saddr, af); + pf_change_ap(saddr, &th->th_sport, pd->ip_sum, + &th->th_sum, &naddr, nport, 0, af); + rewrite++; + } + } else { + bport = nport = th->th_dport; + /* check incoming packet for BINAT/RDR */ + if ((rdr = pf_get_translation(PF_IN, ifp, IPPROTO_TCP, + saddr, th->th_sport, daddr, th->th_dport, + &naddr, &nport, af)) != NULL) { + PF_ACPY(&baddr, daddr, af); + pf_change_ap(daddr, &th->th_dport, pd->ip_sum, + &th->th_sum, &naddr, nport, 0, af); + rewrite++; + } + } + + r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); + while (r != NULL) { + r->evaluations++; + if (r->ifp != NULL && ((r->ifp != ifp && !r->ifnot) || + (r->ifp == ifp && r->ifnot))) + r = r->skip[PF_SKIP_IFP].ptr; + else if (r->direction && r->direction != direction) + r = r->skip[PF_SKIP_DIR].ptr; + else if (r->af && r->af != af) + r = r->skip[PF_SKIP_AF].ptr; + else if (r->proto && r->proto != IPPROTO_TCP) + r = r->skip[PF_SKIP_PROTO].ptr; + else if (PF_MISMATCHAW(&r->src.addr, saddr, af, r->src.not)) + r = r->skip[PF_SKIP_SRC_ADDR].ptr; + else if (r->src.port_op && !pf_match_port(r->src.port_op, + r->src.port[0], r->src.port[1], th->th_sport)) + r = r->skip[PF_SKIP_SRC_PORT].ptr; + else if (PF_MISMATCHAW(&r->dst.addr, daddr, af, r->dst.not)) + r = r->skip[PF_SKIP_DST_ADDR].ptr; + else if (r->dst.port_op && !pf_match_port(r->dst.port_op, + r->dst.port[0], r->dst.port[1], th->th_dport)) + r = r->skip[PF_SKIP_DST_PORT].ptr; + else if (r->tos && !(r->tos & pd->tos)) + r = TAILQ_NEXT(r, entries); + else if (r->rule_flag & PFRULE_FRAGMENT) + r = TAILQ_NEXT(r, entries); + else if ((r->flagset & th->th_flags) != r->flags) + r = TAILQ_NEXT(r, entries); + else if (r->uid.op && (lookup != -1 || (lookup = + pf_socket_lookup(&uid, &gid, direction, af, IPPROTO_TCP, + pd), 1)) && + !pf_match_uid(r->uid.op, r->uid.uid[0], r->uid.uid[1], + uid)) + r = TAILQ_NEXT(r, entries); + else if (r->gid.op && (lookup != -1 || (lookup = + pf_socket_lookup(&uid, &gid, direction, af, IPPROTO_TCP, + pd), 1)) && + !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1], + gid)) + r = TAILQ_NEXT(r, entries); + else if (r->anchorname[0] && r->anchor == NULL) + r = TAILQ_NEXT(r, entries); + else if (r->match_tag && + !pf_match_tag(m, r, nat, rdr, pftag, &tag)) + r = TAILQ_NEXT(r, entries); + else { + if (r->tag) + tag = r->tag; + if (r->anchor == NULL) { + *rm = r; + *am = a; + *rsm = ruleset; + if ((*rm)->quick) + break; + r = TAILQ_NEXT(r, entries); + } else + PF_STEP_INTO_ANCHOR(r, a, ruleset, + PF_RULESET_FILTER); + } + if (r == NULL && a != NULL) + PF_STEP_OUT_OF_ANCHOR(r, a, ruleset, + PF_RULESET_FILTER); + } + r = *rm; + a = *am; + ruleset = *rsm; + + r->packets++; + r->bytes += pd->tot_len; + if (a != NULL) { + a->packets++; + a->bytes += pd->tot_len; + } + REASON_SET(&reason, PFRES_MATCH); + + if (r->log) { + if (rewrite) + m_copyback(m, off, sizeof(*th), (caddr_t)th); + PFLOG_PACKET(ifp, h, m, af, direction, reason, r, a, ruleset); + } + + if ((r->action == PF_DROP) && + ((r->rule_flag & PFRULE_RETURNRST) || + (r->rule_flag & PFRULE_RETURNICMP) || + (r->rule_flag & PFRULE_RETURN))) { + /* undo NAT changes, if they have taken place */ + if (nat != NULL) { + pf_change_ap(saddr, &th->th_sport, pd->ip_sum, + &th->th_sum, &baddr, bport, 0, af); + rewrite++; + } else if (rdr != NULL) { + pf_change_ap(daddr, &th->th_dport, pd->ip_sum, + &th->th_sum, &baddr, bport, 0, af); + rewrite++; + } + if (((r->rule_flag & PFRULE_RETURNRST) || + (r->rule_flag & PFRULE_RETURN)) && + !(th->th_flags & TH_RST)) { + u_int32_t ack = ntohl(th->th_seq) + pd->p_len; + + if (th->th_flags & TH_SYN) + ack++; + if (th->th_flags & TH_FIN) + ack++; + pf_send_tcp(r, af, pd->dst, + pd->src, th->th_dport, th->th_sport, + ntohl(th->th_ack), ack, TH_RST|TH_ACK, 0, 0, + r->return_ttl); + } else if ((af == AF_INET) && r->return_icmp) + pf_send_icmp(m, r->return_icmp >> 8, + r->return_icmp & 255, af, r); + else if ((af == AF_INET6) && r->return_icmp6) + pf_send_icmp(m, r->return_icmp6 >> 8, + r->return_icmp6 & 255, af, r); + } + + if (r->action == PF_DROP) + return (PF_DROP); + + if (pf_tag_packet(m, pftag, tag)) { + REASON_SET(&reason, PFRES_MEMORY); + return (PF_DROP); + } + + if (r->keep_state || nat != NULL || rdr != NULL || + (pd->flags & PFDESC_TCP_NORM)) { + /* create new state */ + u_int16_t len; + struct pf_state *s = NULL; + + len = pd->tot_len - off - (th->th_off << 2); + if (!r->max_states || r->states < r->max_states) + s = pool_get(&pf_state_pl, PR_NOWAIT); + if (s == NULL) { + REASON_SET(&reason, PFRES_MEMORY); + return (PF_DROP); + } + bzero(s, sizeof(*s)); + r->states++; + if (a != NULL) + a->states++; + s->rule.ptr = r; + if (nat != NULL) + s->nat_rule.ptr = nat; + else + s->nat_rule.ptr = rdr; + if (s->nat_rule.ptr != NULL) + s->nat_rule.ptr->states++; + s->anchor.ptr = a; + s->allow_opts = r->allow_opts; + s->log = r->log & 2; + s->proto = IPPROTO_TCP; + s->direction = direction; + s->af = af; + if (direction == PF_OUT) { + PF_ACPY(&s->gwy.addr, saddr, af); + s->gwy.port = th->th_sport; /* sport */ + PF_ACPY(&s->ext.addr, daddr, af); + s->ext.port = th->th_dport; + if (nat != NULL) { + PF_ACPY(&s->lan.addr, &baddr, af); + s->lan.addr = baddr; + s->lan.port = bport; + } else { + PF_ACPY(&s->lan.addr, &s->gwy.addr, af); + s->lan.port = s->gwy.port; + } + } else { + PF_ACPY(&s->lan.addr, daddr, af); + s->lan.port = th->th_dport; + PF_ACPY(&s->ext.addr, saddr, af); + s->ext.port = th->th_sport; + if (rdr != NULL) { + PF_ACPY(&s->gwy.addr, &baddr, af); + s->gwy.port = bport; + } else { + PF_ACPY(&s->gwy.addr, &s->lan.addr, af); + s->gwy.port = s->lan.port; + } + } + + s->src.seqlo = ntohl(th->th_seq); + s->src.seqhi = s->src.seqlo + len + 1; + if ((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN && + r->keep_state == PF_STATE_MODULATE) { + /* Generate sequence number modulator */ + while ((s->src.seqdiff = arc4random()) == 0) + ; + pf_change_a(&th->th_seq, &th->th_sum, + htonl(s->src.seqlo + s->src.seqdiff), 0); + rewrite = 1; + } else + s->src.seqdiff = 0; + if (th->th_flags & TH_SYN) { + s->src.seqhi++; + s->src.wscale = pf_get_wscale(m, off, th->th_off, af); + } + s->src.max_win = MAX(ntohs(th->th_win), 1); + if (s->src.wscale & PF_WSCALE_MASK) { + /* Remove scale factor from initial window */ + int win = s->src.max_win; + win += 1 << (s->src.wscale & PF_WSCALE_MASK); + s->src.max_win = (win - 1) >> + (s->src.wscale & PF_WSCALE_MASK); + } + if (th->th_flags & TH_FIN) + s->src.seqhi++; + s->dst.seqlo = 0; /* Haven't seen these yet */ + s->dst.seqhi = 1; + s->dst.max_win = 1; + s->dst.seqdiff = 0; /* Defer random generation */ + s->src.state = TCPS_SYN_SENT; + s->dst.state = TCPS_CLOSED; + s->creation = time.tv_sec; + s->expire = time.tv_sec; + s->timeout = PFTM_TCP_FIRST_PACKET; + s->packets[0] = 1; + s->bytes[0] = pd->tot_len; + + if ((pd->flags & PFDESC_TCP_NORM) && pf_normalize_tcp_init(m, + off, pd, th, &s->src, &s->dst)) { + REASON_SET(&reason, PFRES_MEMORY); + pool_put(&pf_state_pl, s); + return (PF_DROP); + } + if ((pd->flags & PFDESC_TCP_NORM) && s->src.scrub && + pf_normalize_tcp_stateful(m, off, pd, &reason, th, &s->src, + &s->dst, &rewrite)) { + pf_normalize_tcp_cleanup(s); + pool_put(&pf_state_pl, s); + return (PF_DROP); + } + if (pf_insert_state(s)) { + pf_normalize_tcp_cleanup(s); + REASON_SET(&reason, PFRES_MEMORY); + pool_put(&pf_state_pl, s); + return (PF_DROP); + } else + *sm = s; + if ((th->th_flags & (TH_SYN|TH_ACK)) == TH_SYN && + r->keep_state == PF_STATE_SYNPROXY) { + s->src.state = PF_TCPS_PROXY_SRC; + if (nat != NULL) + pf_change_ap(saddr, &th->th_sport, + pd->ip_sum, &th->th_sum, &baddr, + bport, 0, af); + else if (rdr != NULL) + pf_change_ap(daddr, &th->th_dport, + pd->ip_sum, &th->th_sum, &baddr, + bport, 0, af); + s->src.seqhi = arc4random(); + /* Find mss option */ + mss = pf_get_mss(m, off, th->th_off, af); + mss = pf_calc_mss(saddr, af, mss); + mss = pf_calc_mss(daddr, af, mss); + s->src.mss = mss; + pf_send_tcp(r, af, daddr, saddr, th->th_dport, + th->th_sport, s->src.seqhi, + ntohl(th->th_seq) + 1, TH_SYN|TH_ACK, 0, s->src.mss, 0); + return (PF_SYNPROXY_DROP); + } + } + + /* copy back packet headers if we performed NAT operations */ + if (rewrite) + m_copyback(m, off, sizeof(*th), (caddr_t)th); + + return (PF_PASS); +} + +int +pf_test_udp(struct pf_rule **rm, struct pf_state **sm, int direction, + struct ifnet *ifp, struct mbuf *m, int ipoff, int off, void *h, + struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm) +{ + struct pf_rule *nat = NULL, *rdr = NULL; + struct pf_addr *saddr = pd->src, *daddr = pd->dst; + struct pf_addr baddr, naddr; + struct udphdr *uh = pd->hdr.udp; + u_int16_t bport, nport = 0; + sa_family_t af = pd->af; + int lookup = -1; + uid_t uid; + gid_t gid; + struct pf_rule *r, *a = NULL; + struct pf_ruleset *ruleset = NULL; + u_short reason; + int rewrite = 0; + struct pf_tag *pftag = NULL; + int tag = -1; + + if (direction == PF_OUT) { + bport = nport = uh->uh_sport; + /* check outgoing packet for BINAT/NAT */ + if ((nat = pf_get_translation(PF_OUT, ifp, IPPROTO_UDP, + saddr, uh->uh_sport, daddr, uh->uh_dport, + &naddr, &nport, af)) != NULL) { + PF_ACPY(&baddr, saddr, af); + pf_change_ap(saddr, &uh->uh_sport, pd->ip_sum, + &uh->uh_sum, &naddr, nport, 1, af); + rewrite++; + } + } else { + bport = nport = uh->uh_dport; + /* check incoming packet for BINAT/RDR */ + if ((rdr = pf_get_translation(PF_IN, ifp, IPPROTO_UDP, + saddr, uh->uh_sport, daddr, uh->uh_dport, + &naddr, &nport, af)) != NULL) { + PF_ACPY(&baddr, daddr, af); + pf_change_ap(daddr, &uh->uh_dport, pd->ip_sum, + &uh->uh_sum, &naddr, nport, 1, af); + rewrite++; + } + } + + r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); + while (r != NULL) { + r->evaluations++; + if (r->ifp != NULL && ((r->ifp != ifp && !r->ifnot) || + (r->ifp == ifp && r->ifnot))) + r = r->skip[PF_SKIP_IFP].ptr; + else if (r->direction && r->direction != direction) + r = r->skip[PF_SKIP_DIR].ptr; + else if (r->af && r->af != af) + r = r->skip[PF_SKIP_AF].ptr; + else if (r->proto && r->proto != IPPROTO_UDP) + r = r->skip[PF_SKIP_PROTO].ptr; + else if (PF_MISMATCHAW(&r->src.addr, saddr, af, r->src.not)) + r = r->skip[PF_SKIP_SRC_ADDR].ptr; + else if (r->src.port_op && !pf_match_port(r->src.port_op, + r->src.port[0], r->src.port[1], uh->uh_sport)) + r = r->skip[PF_SKIP_SRC_PORT].ptr; + else if (PF_MISMATCHAW(&r->dst.addr, daddr, af, r->dst.not)) + r = r->skip[PF_SKIP_DST_ADDR].ptr; + else if (r->dst.port_op && !pf_match_port(r->dst.port_op, + r->dst.port[0], r->dst.port[1], uh->uh_dport)) + r = r->skip[PF_SKIP_DST_PORT].ptr; + else if (r->tos && !(r->tos & pd->tos)) + r = TAILQ_NEXT(r, entries); + else if (r->rule_flag & PFRULE_FRAGMENT) + r = TAILQ_NEXT(r, entries); + else if (r->uid.op && (lookup != -1 || (lookup = + pf_socket_lookup(&uid, &gid, direction, af, IPPROTO_UDP, + pd), 1)) && + !pf_match_uid(r->uid.op, r->uid.uid[0], r->uid.uid[1], + uid)) + r = TAILQ_NEXT(r, entries); + else if (r->gid.op && (lookup != -1 || (lookup = + pf_socket_lookup(&uid, &gid, direction, af, IPPROTO_UDP, + pd), 1)) && + !pf_match_gid(r->gid.op, r->gid.gid[0], r->gid.gid[1], + gid)) + r = TAILQ_NEXT(r, entries); + else if (r->match_tag && + !pf_match_tag(m, r, nat, rdr, pftag, &tag)) + r = TAILQ_NEXT(r, entries); + else if (r->anchorname[0] && r->anchor == NULL) + r = TAILQ_NEXT(r, entries); + else { + if (r->tag) + tag = r->tag; + if (r->anchor == NULL) { + *rm = r; + *am = a; + *rsm = ruleset; + if ((*rm)->quick) + break; + r = TAILQ_NEXT(r, entries); + } else + PF_STEP_INTO_ANCHOR(r, a, ruleset, + PF_RULESET_FILTER); + } + if (r == NULL && a != NULL) + PF_STEP_OUT_OF_ANCHOR(r, a, ruleset, + PF_RULESET_FILTER); + } + r = *rm; + a = *am; + ruleset = *rsm; + + r->packets++; + r->bytes += pd->tot_len; + if (a != NULL) { + a->packets++; + a->bytes += pd->tot_len; + } + REASON_SET(&reason, PFRES_MATCH); + + if (r->log) { + if (rewrite) + m_copyback(m, off, sizeof(*uh), (caddr_t)uh); + PFLOG_PACKET(ifp, h, m, af, direction, reason, r, a, ruleset); + } + + if ((r->action == PF_DROP) && + ((r->rule_flag & PFRULE_RETURNICMP) || + (r->rule_flag & PFRULE_RETURN))) { + /* undo NAT changes, if they have taken place */ + if (nat != NULL) { + pf_change_ap(saddr, &uh->uh_sport, pd->ip_sum, + &uh->uh_sum, &baddr, bport, 1, af); + rewrite++; + } else if (rdr != NULL) { + pf_change_ap(daddr, &uh->uh_dport, pd->ip_sum, + &uh->uh_sum, &baddr, bport, 1, af); + rewrite++; + } + if ((af == AF_INET) && r->return_icmp) + pf_send_icmp(m, r->return_icmp >> 8, + r->return_icmp & 255, af, r); + else if ((af == AF_INET6) && r->return_icmp6) + pf_send_icmp(m, r->return_icmp6 >> 8, + r->return_icmp6 & 255, af, r); + } + + if (r->action == PF_DROP) + return (PF_DROP); + + if (pf_tag_packet(m, pftag, tag)) { + REASON_SET(&reason, PFRES_MEMORY); + return (PF_DROP); + } + + if (r->keep_state || nat != NULL || rdr != NULL) { + /* create new state */ + struct pf_state *s = NULL; + + if (!r->max_states || r->states < r->max_states) + s = pool_get(&pf_state_pl, PR_NOWAIT); + if (s == NULL) + return (PF_DROP); + bzero(s, sizeof(*s)); + r->states++; + if (a != NULL) + a->states++; + + s->rule.ptr = r; + if (nat != NULL) + s->nat_rule.ptr = nat; + else + s->nat_rule.ptr = rdr; + if (s->nat_rule.ptr != NULL) + s->nat_rule.ptr->states++; + s->anchor.ptr = a; + s->allow_opts = r->allow_opts; + s->log = r->log & 2; + s->proto = IPPROTO_UDP; + s->direction = direction; + s->af = af; + if (direction == PF_OUT) { + PF_ACPY(&s->gwy.addr, saddr, af); + s->gwy.port = uh->uh_sport; + PF_ACPY(&s->ext.addr, daddr, af); + s->ext.port = uh->uh_dport; + if (nat != NULL) { + PF_ACPY(&s->lan.addr, &baddr, af); + s->lan.port = bport; + } else { + PF_ACPY(&s->lan.addr, &s->gwy.addr, af); + s->lan.port = s->gwy.port; + } + } else { + PF_ACPY(&s->lan.addr, daddr, af); + s->lan.port = uh->uh_dport; + PF_ACPY(&s->ext.addr, saddr, af); + s->ext.port = uh->uh_sport; + if (rdr != NULL) { + PF_ACPY(&s->gwy.addr, &baddr, af); + s->gwy.port = bport; + } else { + PF_ACPY(&s->gwy.addr, &s->lan.addr, af); + s->gwy.port = s->lan.port; + } + } + s->src.seqlo = 0; + s->src.seqhi = 0; + s->src.seqdiff = 0; + s->src.max_win = 0; + s->src.state = PFUDPS_SINGLE; + s->dst.seqlo = 0; + s->dst.seqhi = 0; + s->dst.seqdiff = 0; + s->dst.max_win = 0; + s->dst.state = PFUDPS_NO_TRAFFIC; + s->creation = time.tv_sec; + s->expire = time.tv_sec; + s->timeout = PFTM_UDP_FIRST_PACKET; + s->packets[0] = 1; + s->bytes[0] = pd->tot_len; + if (pf_insert_state(s)) { + REASON_SET(&reason, PFRES_MEMORY); + pool_put(&pf_state_pl, s); + return (PF_DROP); + } else + *sm = s; + } + + /* copy back packet headers if we performed NAT operations */ + if (rewrite) + m_copyback(m, off, sizeof(*uh), (caddr_t)uh); + + return (PF_PASS); +} + +int +pf_test_icmp(struct pf_rule **rm, struct pf_state **sm, int direction, + struct ifnet *ifp, struct mbuf *m, int ipoff, int off, void *h, + struct pf_pdesc *pd, struct pf_rule **am, struct pf_ruleset **rsm) +{ + struct pf_rule *nat = NULL, *rdr = NULL; + struct pf_addr *saddr = pd->src, *daddr = pd->dst; + struct pf_addr baddr, naddr; + struct pf_rule *r, *a = NULL; + struct pf_ruleset *ruleset = NULL; + u_short reason; + u_int16_t icmpid; + sa_family_t af = pd->af; + u_int8_t icmptype, icmpcode; + int state_icmp = 0; + struct pf_tag *pftag = NULL; + int tag = -1; +#ifdef INET6 + int rewrite = 0; +#endif /* INET6 */ + + switch (pd->proto) { +#ifdef INET + case IPPROTO_ICMP: + icmptype = pd->hdr.icmp->icmp_type; + icmpcode = pd->hdr.icmp->icmp_code; + icmpid = pd->hdr.icmp->icmp_id; + + if (icmptype == ICMP_UNREACH || + icmptype == ICMP_SOURCEQUENCH || + icmptype == ICMP_REDIRECT || + icmptype == ICMP_TIMXCEED || + icmptype == ICMP_PARAMPROB) + state_icmp++; + break; +#endif /* INET */ +#ifdef INET6 + case IPPROTO_ICMPV6: + icmptype = pd->hdr.icmp6->icmp6_type; + icmpcode = pd->hdr.icmp6->icmp6_code; + icmpid = pd->hdr.icmp6->icmp6_id; + + if (icmptype == ICMP6_DST_UNREACH || + icmptype == ICMP6_PACKET_TOO_BIG || + icmptype == ICMP6_TIME_EXCEEDED || + icmptype == ICMP6_PARAM_PROB) + state_icmp++; + break; +#endif /* INET6 */ + } + + if (direction == PF_OUT) { + /* check outgoing packet for BINAT/NAT */ + if ((nat = pf_get_translation(PF_OUT, ifp, pd->proto, + saddr, 0, daddr, 0, &naddr, NULL, af)) != NULL) { + PF_ACPY(&baddr, saddr, af); + switch (af) { +#ifdef INET + case AF_INET: + pf_change_a(&saddr->v4.s_addr, pd->ip_sum, + naddr.v4.s_addr, 0); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + pf_change_a6(saddr, &pd->hdr.icmp6->icmp6_cksum, + &naddr, 0); + rewrite++; + break; +#endif /* INET6 */ + } + } + } else { + /* check incoming packet for BINAT/RDR */ + if ((rdr = pf_get_translation(PF_IN, ifp, pd->proto, + saddr, 0, daddr, 0, &naddr, NULL, af)) != NULL) { + PF_ACPY(&baddr, daddr, af); + switch (af) { +#ifdef INET + case AF_INET: + pf_change_a(&daddr->v4.s_addr, + pd->ip_sum, naddr.v4.s_addr, 0); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + pf_change_a6(daddr, &pd->hdr.icmp6->icmp6_cksum, + &naddr, 0); + rewrite++; + break; +#endif /* INET6 */ + } + } + } + + r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); + while (r != NULL) { + r->evaluations++; + if (r->ifp != NULL && ((r->ifp != ifp && !r->ifnot) || + (r->ifp == ifp && r->ifnot))) + r = r->skip[PF_SKIP_IFP].ptr; + else if (r->direction && r->direction != direction) + r = r->skip[PF_SKIP_DIR].ptr; + else if (r->af && r->af != af) + r = r->skip[PF_SKIP_AF].ptr; + else if (r->proto && r->proto != pd->proto) + r = r->skip[PF_SKIP_PROTO].ptr; + else if (PF_MISMATCHAW(&r->src.addr, saddr, af, r->src.not)) + r = r->skip[PF_SKIP_SRC_ADDR].ptr; + else if (PF_MISMATCHAW(&r->dst.addr, daddr, af, r->dst.not)) + r = r->skip[PF_SKIP_DST_ADDR].ptr; + else if (r->type && r->type != icmptype + 1) + r = TAILQ_NEXT(r, entries); + else if (r->code && r->code != icmpcode + 1) + r = TAILQ_NEXT(r, entries); + else if (r->tos && !(r->tos & pd->tos)) + r = TAILQ_NEXT(r, entries); + else if (r->rule_flag & PFRULE_FRAGMENT) + r = TAILQ_NEXT(r, entries); + else if (r->match_tag && + !pf_match_tag(m, r, nat, rdr, pftag, &tag)) + r = TAILQ_NEXT(r, entries); + else if (r->anchorname[0] && r->anchor == NULL) + r = TAILQ_NEXT(r, entries); + else { + if (r->tag) + tag = r->tag; + if (r->anchor == NULL) { + *rm = r; + *am = a; + *rsm = ruleset; + if ((*rm)->quick) + break; + r = TAILQ_NEXT(r, entries); + } else + PF_STEP_INTO_ANCHOR(r, a, ruleset, + PF_RULESET_FILTER); + } + if (r == NULL && a != NULL) + PF_STEP_OUT_OF_ANCHOR(r, a, ruleset, + PF_RULESET_FILTER); + } + r = *rm; + a = *am; + ruleset = *rsm; + + r->packets++; + r->bytes += pd->tot_len; + if (a != NULL) { + a->packets++; + a->bytes += pd->tot_len; + } + REASON_SET(&reason, PFRES_MATCH); + + if (r->log) { +#ifdef INET6 + if (rewrite) + m_copyback(m, off, sizeof(struct icmp6_hdr), + (caddr_t)pd->hdr.icmp6); +#endif /* INET6 */ + PFLOG_PACKET(ifp, h, m, af, direction, reason, r, a, ruleset); + } + + if (r->action != PF_PASS) + return (PF_DROP); + + if (pf_tag_packet(m, pftag, tag)) { + REASON_SET(&reason, PFRES_MEMORY); + return (PF_DROP); + } + + if (!state_icmp && (r->keep_state || + nat != NULL || rdr != NULL)) { + /* create new state */ + struct pf_state *s = NULL; + + if (!r->max_states || r->states < r->max_states) + s = pool_get(&pf_state_pl, PR_NOWAIT); + if (s == NULL) + return (PF_DROP); + bzero(s, sizeof(*s)); + r->states++; + if (a != NULL) + a->states++; + + s->rule.ptr = r; + if (nat != NULL) + s->nat_rule.ptr = nat; + else + s->nat_rule.ptr = rdr; + if (s->nat_rule.ptr != NULL) + s->nat_rule.ptr->states++; + s->anchor.ptr = a; + s->allow_opts = r->allow_opts; + s->log = r->log & 2; + s->proto = pd->proto; + s->direction = direction; + s->af = af; + if (direction == PF_OUT) { + PF_ACPY(&s->gwy.addr, saddr, af); + s->gwy.port = icmpid; + PF_ACPY(&s->ext.addr, daddr, af); + s->ext.port = icmpid; + if (nat != NULL) + PF_ACPY(&s->lan.addr, &baddr, af); + else + PF_ACPY(&s->lan.addr, &s->gwy.addr, af); + s->lan.port = icmpid; + } else { + PF_ACPY(&s->lan.addr, daddr, af); + s->lan.port = icmpid; + PF_ACPY(&s->ext.addr, saddr, af); + s->ext.port = icmpid; + if (rdr != NULL) + PF_ACPY(&s->gwy.addr, &baddr, af); + else + PF_ACPY(&s->gwy.addr, &s->lan.addr, af); + s->gwy.port = icmpid; + } + s->src.seqlo = 0; + s->src.seqhi = 0; + s->src.seqdiff = 0; + s->src.max_win = 0; + s->src.state = 0; + s->dst.seqlo = 0; + s->dst.seqhi = 0; + s->dst.seqdiff = 0; + s->dst.max_win = 0; + s->dst.state = 0; + s->creation = time.tv_sec; + s->expire = time.tv_sec; + s->timeout = PFTM_ICMP_FIRST_PACKET; + s->packets[0] = 1; + s->bytes[0] = pd->tot_len; + if (pf_insert_state(s)) { + REASON_SET(&reason, PFRES_MEMORY); + pool_put(&pf_state_pl, s); + return (PF_DROP); + } else + *sm = s; + } + +#ifdef INET6 + /* copy back packet headers if we performed IPv6 NAT operations */ + if (rewrite) + m_copyback(m, off, sizeof(struct icmp6_hdr), + (caddr_t)pd->hdr.icmp6); +#endif /* INET6 */ + + return (PF_PASS); +} + +int +pf_test_other(struct pf_rule **rm, struct pf_state **sm, int direction, + struct ifnet *ifp, struct mbuf *m, void *h, struct pf_pdesc *pd, + struct pf_rule **am, struct pf_ruleset **rsm) +{ + struct pf_rule *nat = NULL, *rdr = NULL; + struct pf_rule *r, *a = NULL; + struct pf_ruleset *ruleset = NULL; + struct pf_addr *saddr = pd->src, *daddr = pd->dst; + struct pf_addr baddr, naddr; + sa_family_t af = pd->af; + u_short reason; + struct pf_tag *pftag = NULL; + int tag = -1; + + if (direction == PF_OUT) { + /* check outgoing packet for BINAT/NAT */ + if ((nat = pf_get_translation(PF_OUT, ifp, pd->proto, + saddr, 0, daddr, 0, &naddr, NULL, af)) != NULL) { + PF_ACPY(&baddr, saddr, af); + switch (af) { +#ifdef INET + case AF_INET: + pf_change_a(&saddr->v4.s_addr, pd->ip_sum, + naddr.v4.s_addr, 0); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + PF_ACPY(saddr, &naddr, af); + break; +#endif /* INET6 */ + } + } + } else { + /* check incoming packet for BINAT/RDR */ + if ((rdr = pf_get_translation(PF_IN, ifp, pd->proto, + saddr, 0, daddr, 0, &naddr, NULL, af)) != NULL) { + PF_ACPY(&baddr, daddr, af); + switch (af) { +#ifdef INET + case AF_INET: + pf_change_a(&daddr->v4.s_addr, + pd->ip_sum, naddr.v4.s_addr, 0); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + PF_ACPY(daddr, &naddr, af); + break; +#endif /* INET6 */ + } + } + } + + r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); + while (r != NULL) { + r->evaluations++; + if (r->ifp != NULL && ((r->ifp != ifp && !r->ifnot) || + (r->ifp == ifp && r->ifnot))) + r = r->skip[PF_SKIP_IFP].ptr; + else if (r->direction && r->direction != direction) + r = r->skip[PF_SKIP_DIR].ptr; + else if (r->af && r->af != af) + r = r->skip[PF_SKIP_AF].ptr; + else if (r->proto && r->proto != pd->proto) + r = r->skip[PF_SKIP_PROTO].ptr; + else if (PF_MISMATCHAW(&r->src.addr, pd->src, af, r->src.not)) + r = r->skip[PF_SKIP_SRC_ADDR].ptr; + else if (PF_MISMATCHAW(&r->dst.addr, pd->dst, af, r->dst.not)) + r = r->skip[PF_SKIP_DST_ADDR].ptr; + else if (r->tos && !(r->tos & pd->tos)) + r = TAILQ_NEXT(r, entries); + else if (r->rule_flag & PFRULE_FRAGMENT) + r = TAILQ_NEXT(r, entries); + else if (r->match_tag && + !pf_match_tag(m, r, nat, rdr, pftag, &tag)) + r = TAILQ_NEXT(r, entries); + else if (r->anchorname[0] && r->anchor == NULL) + r = TAILQ_NEXT(r, entries); + else { + if (r->tag) + tag = r->tag; + if (r->anchor == NULL) { + *rm = r; + *am = a; + *rsm = ruleset; + if ((*rm)->quick) + break; + r = TAILQ_NEXT(r, entries); + } else + PF_STEP_INTO_ANCHOR(r, a, ruleset, + PF_RULESET_FILTER); + } + if (r == NULL && a != NULL) + PF_STEP_OUT_OF_ANCHOR(r, a, ruleset, + PF_RULESET_FILTER); + } + r = *rm; + a = *am; + ruleset = *rsm; + + r->packets++; + r->bytes += pd->tot_len; + if (a != NULL) { + a->packets++; + a->bytes += pd->tot_len; + } + REASON_SET(&reason, PFRES_MATCH); + if (r->log) + PFLOG_PACKET(ifp, h, m, af, direction, reason, r, a, ruleset); + + if ((r->action == PF_DROP) && + ((r->rule_flag & PFRULE_RETURNICMP) || + (r->rule_flag & PFRULE_RETURN))) { + struct pf_addr *a = NULL; + + if (nat != NULL) + a = saddr; + else if (rdr != NULL) + a = daddr; + if (a != NULL) { + switch (af) { +#ifdef INET + case AF_INET: + pf_change_a(&a->v4.s_addr, pd->ip_sum, + baddr.v4.s_addr, 0); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + PF_ACPY(a, &baddr, af); + break; +#endif /* INET6 */ + } + } + if ((af == AF_INET) && r->return_icmp) + pf_send_icmp(m, r->return_icmp >> 8, + r->return_icmp & 255, af, r); + else if ((af == AF_INET6) && r->return_icmp6) + pf_send_icmp(m, r->return_icmp6 >> 8, + r->return_icmp6 & 255, af, r); + } + + if (r->action != PF_PASS) + return (PF_DROP); + + if (pf_tag_packet(m, pftag, tag)) { + REASON_SET(&reason, PFRES_MEMORY); + return (PF_DROP); + } + + if (r->keep_state || nat != NULL || rdr != NULL) { + /* create new state */ + struct pf_state *s = NULL; + + if (!r->max_states || r->states < r->max_states) + s = pool_get(&pf_state_pl, PR_NOWAIT); + if (s == NULL) + return (PF_DROP); + bzero(s, sizeof(*s)); + r->states++; + if (a != NULL) + a->states++; + + s->rule.ptr = r; + if (nat != NULL) + s->nat_rule.ptr = nat; + else + s->nat_rule.ptr = rdr; + if (s->nat_rule.ptr != NULL) + s->nat_rule.ptr->states++; + s->anchor.ptr = a; + s->allow_opts = r->allow_opts; + s->log = r->log & 2; + s->proto = pd->proto; + s->direction = direction; + s->af = af; + if (direction == PF_OUT) { + PF_ACPY(&s->gwy.addr, saddr, af); + s->gwy.port = 0; + PF_ACPY(&s->ext.addr, daddr, af); + s->ext.port = 0; + if (nat != NULL) + PF_ACPY(&s->lan.addr, &baddr, af); + else + PF_ACPY(&s->lan.addr, &s->gwy.addr, af); + s->lan.port = 0; + } else { + PF_ACPY(&s->lan.addr, daddr, af); + s->lan.port = 0; + PF_ACPY(&s->ext.addr, saddr, af); + s->ext.port = 0; + if (rdr != NULL) + PF_ACPY(&s->gwy.addr, &baddr, af); + else + PF_ACPY(&s->gwy.addr, &s->lan.addr, af); + s->gwy.port = 0; + } + s->src.seqlo = 0; + s->src.seqhi = 0; + s->src.seqdiff = 0; + s->src.max_win = 0; + s->src.state = PFOTHERS_SINGLE; + s->dst.seqlo = 0; + s->dst.seqhi = 0; + s->dst.seqdiff = 0; + s->dst.max_win = 0; + s->dst.state = PFOTHERS_NO_TRAFFIC; + s->creation = time.tv_sec; + s->expire = time.tv_sec; + s->timeout = PFTM_OTHER_FIRST_PACKET; + s->packets[0] = 1; + s->bytes[0] = pd->tot_len; + if (pf_insert_state(s)) { + REASON_SET(&reason, PFRES_MEMORY); + if (r->log) + PFLOG_PACKET(ifp, h, m, af, direction, reason, + r, a, ruleset); + pool_put(&pf_state_pl, s); + return (PF_DROP); + } else + *sm = s; + } + + return (PF_PASS); +} + +int +pf_test_fragment(struct pf_rule **rm, int direction, struct ifnet *ifp, + struct mbuf *m, void *h, struct pf_pdesc *pd, struct pf_rule **am, + struct pf_ruleset **rsm) +{ + struct pf_rule *r, *a = NULL; + struct pf_ruleset *ruleset = NULL; + sa_family_t af = pd->af; + u_short reason; + struct pf_tag *pftag = NULL; + int tag = -1; + + r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); + while (r != NULL) { + r->evaluations++; + if (r->ifp != NULL && ((r->ifp != ifp && !r->ifnot) || + (r->ifp == ifp && r->ifnot))) + r = r->skip[PF_SKIP_IFP].ptr; + else if (r->direction && r->direction != direction) + r = r->skip[PF_SKIP_DIR].ptr; + else if (r->af && r->af != af) + r = r->skip[PF_SKIP_AF].ptr; + else if (r->proto && r->proto != pd->proto) + r = r->skip[PF_SKIP_PROTO].ptr; + else if (PF_MISMATCHAW(&r->src.addr, pd->src, af, r->src.not)) + r = r->skip[PF_SKIP_SRC_ADDR].ptr; + else if (PF_MISMATCHAW(&r->dst.addr, pd->dst, af, r->dst.not)) + r = r->skip[PF_SKIP_DST_ADDR].ptr; + else if (r->tos && !(r->tos & pd->tos)) + r = TAILQ_NEXT(r, entries); + else if (r->src.port_op || r->dst.port_op || + r->flagset || r->type || r->code) + r = TAILQ_NEXT(r, entries); + else if (r->match_tag && + !pf_match_tag(m, r, NULL, NULL, pftag, &tag)) + r = TAILQ_NEXT(r, entries); + else if (r->anchorname[0] && r->anchor == NULL) + r = TAILQ_NEXT(r, entries); + else { + if (r->anchor == NULL) { + *rm = r; + *am = a; + *rsm = ruleset; + if ((*rm)->quick) + break; + r = TAILQ_NEXT(r, entries); + } else + PF_STEP_INTO_ANCHOR(r, a, ruleset, + PF_RULESET_FILTER); + } + if (r == NULL && a != NULL) + PF_STEP_OUT_OF_ANCHOR(r, a, ruleset, + PF_RULESET_FILTER); + } + r = *rm; + a = *am; + ruleset = *rsm; + + r->packets++; + r->bytes += pd->tot_len; + if (a != NULL) { + a->packets++; + a->bytes += pd->tot_len; + } + REASON_SET(&reason, PFRES_MATCH); + if (r->log) + PFLOG_PACKET(ifp, h, m, af, direction, reason, r, a, ruleset); + + if (r->action != PF_PASS) + return (PF_DROP); + + if (pf_tag_packet(m, pftag, tag)) { + REASON_SET(&reason, PFRES_MEMORY); + return (PF_DROP); + } + + return (PF_PASS); +} + +int +pf_test_state_tcp(struct pf_state **state, int direction, struct ifnet *ifp, + struct mbuf *m, int ipoff, int off, void *h, struct pf_pdesc *pd, + u_short *reason) +{ + struct pf_tree_node key; + struct tcphdr *th = pd->hdr.tcp; + u_int16_t win = ntohs(th->th_win); + u_int32_t ack, end, seq; + u_int8_t sws, dws; + int ackskew, dirndx; + int copyback = 0; + struct pf_state_peer *src, *dst; + + key.af = pd->af; + key.proto = IPPROTO_TCP; + PF_ACPY(&key.addr[0], pd->src, key.af); + PF_ACPY(&key.addr[1], pd->dst, key.af); + key.port[0] = th->th_sport; + key.port[1] = th->th_dport; + + STATE_LOOKUP(); + + if (direction == (*state)->direction) { + src = &(*state)->src; + dst = &(*state)->dst; + dirndx = 0; + } else { + src = &(*state)->dst; + dst = &(*state)->src; + dirndx = 1; + } + + if ((*state)->src.state == PF_TCPS_PROXY_SRC) { + if (direction != (*state)->direction) + return (PF_SYNPROXY_DROP); + if (th->th_flags & TH_SYN) { + if (ntohl(th->th_seq) != (*state)->src.seqlo) + return (PF_DROP); + pf_send_tcp((*state)->rule.ptr, pd->af, pd->dst, + pd->src, th->th_dport, th->th_sport, + (*state)->src.seqhi, ntohl(th->th_seq) + 1, + TH_SYN|TH_ACK, 0, (*state)->src.mss, 0); + return (PF_SYNPROXY_DROP); + } else if (!(th->th_flags & TH_ACK) || + (ntohl(th->th_ack) != (*state)->src.seqhi + 1) || + (ntohl(th->th_seq) != (*state)->src.seqlo + 1)) + return (PF_DROP); + else + (*state)->src.state = PF_TCPS_PROXY_DST; + } + if ((*state)->src.state == PF_TCPS_PROXY_DST) { + struct pf_state_host *src, *dst; + + if (direction == PF_OUT) { + src = &(*state)->gwy; + dst = &(*state)->ext; + } else { + src = &(*state)->ext; + dst = &(*state)->lan; + } + if (direction == (*state)->direction) { + if (((th->th_flags & (TH_SYN|TH_ACK)) != TH_ACK) || + (ntohl(th->th_ack) != (*state)->src.seqhi + 1) || + (ntohl(th->th_seq) != (*state)->src.seqlo + 1)) + return (PF_DROP); + (*state)->src.max_win = MAX(ntohs(th->th_win), 1); + if ((*state)->dst.seqhi == 1) + (*state)->dst.seqhi = arc4random(); + pf_send_tcp((*state)->rule.ptr, pd->af, &src->addr, + &dst->addr, src->port, dst->port, + (*state)->dst.seqhi, 0, TH_SYN, 0, (*state)->src.mss, 0); + return (PF_SYNPROXY_DROP); + } else if (((th->th_flags & (TH_SYN|TH_ACK)) != + (TH_SYN|TH_ACK)) || + (ntohl(th->th_ack) != (*state)->dst.seqhi + 1)) + return (PF_DROP); + else { + (*state)->dst.max_win = MAX(ntohs(th->th_win), 1); + (*state)->dst.seqlo = ntohl(th->th_seq); + pf_send_tcp((*state)->rule.ptr, pd->af, pd->dst, + pd->src, th->th_dport, th->th_sport, + ntohl(th->th_ack), ntohl(th->th_seq) + 1, + TH_ACK, (*state)->src.max_win, 0, 0); + pf_send_tcp((*state)->rule.ptr, pd->af, &src->addr, + &dst->addr, src->port, dst->port, + (*state)->src.seqhi + 1, (*state)->src.seqlo + 1, + TH_ACK, (*state)->dst.max_win, 0, 0); + (*state)->src.seqdiff = (*state)->dst.seqhi - + (*state)->src.seqlo; + (*state)->dst.seqdiff = (*state)->src.seqhi - + (*state)->dst.seqlo; + (*state)->src.seqhi = (*state)->src.seqlo + + (*state)->src.max_win; + (*state)->dst.seqhi = (*state)->dst.seqlo + + (*state)->dst.max_win; + (*state)->src.wscale = (*state)->dst.wscale = 0; + (*state)->src.state = (*state)->dst.state = + TCPS_ESTABLISHED; + return (PF_SYNPROXY_DROP); + } + } + + if (src->wscale && dst->wscale && !(th->th_flags & TH_SYN)) { + sws = src->wscale & PF_WSCALE_MASK; + dws = dst->wscale & PF_WSCALE_MASK; + } else + sws = dws = 0; + + /* + * Sequence tracking algorithm from Guido van Rooij's paper: + * http://www.madison-gurkha.com/publications/tcp_filtering/ + * tcp_filtering.ps + */ + + seq = ntohl(th->th_seq); + if (src->seqlo == 0) { + /* First packet from this end. Set its state */ + + if ((pd->flags & PFDESC_TCP_NORM || dst->scrub) && + src->scrub == NULL) { + if (pf_normalize_tcp_init(m, off, pd, th, src, dst)) { + REASON_SET(reason, PFRES_MEMORY); + return (PF_DROP); + } + } + + /* Deferred generation of sequence number modulator */ + if (dst->seqdiff && !src->seqdiff) { + while ((src->seqdiff = arc4random()) == 0) + ; + ack = ntohl(th->th_ack) - dst->seqdiff; + pf_change_a(&th->th_seq, &th->th_sum, htonl(seq + + src->seqdiff), 0); + pf_change_a(&th->th_ack, &th->th_sum, htonl(ack), 0); + copyback = 1; + } else { + ack = ntohl(th->th_ack); + } + + end = seq + pd->p_len; + if (th->th_flags & TH_SYN) { + end++; + if (dst->wscale & PF_WSCALE_FLAG) { + src->wscale = pf_get_wscale(m, off, th->th_off, + pd->af); + if (src->wscale & PF_WSCALE_FLAG) { + /* Remove scale factor from initial + * window */ + sws = src->wscale & PF_WSCALE_MASK; + win = ((u_int32_t)win + (1 << sws) - 1) + >> sws; + dws = dst->wscale & PF_WSCALE_MASK; + } else { + /* fixup other window */ + dst->max_win <<= dst->wscale & + PF_WSCALE_MASK; + /* in case of a retrans SYN|ACK */ + dst->wscale = 0; + } + } + } + if (th->th_flags & TH_FIN) + end++; + + src->seqlo = seq; + if (src->state < TCPS_SYN_SENT) + src->state = TCPS_SYN_SENT; + + /* + * May need to slide the window (seqhi may have been set by + * the crappy stack check or if we picked up the connection + * after establishment) + */ + if (src->seqhi == 1 || + SEQ_GEQ(end + MAX(1, dst->max_win << dws), src->seqhi)) + src->seqhi = end + MAX(1, dst->max_win << dws); + if (win > src->max_win) + src->max_win = win; + + } else { + ack = ntohl(th->th_ack) - dst->seqdiff; + if (src->seqdiff) { + /* Modulate sequence numbers */ + pf_change_a(&th->th_seq, &th->th_sum, htonl(seq + + src->seqdiff), 0); + pf_change_a(&th->th_ack, &th->th_sum, htonl(ack), 0); + copyback = 1; + } + end = seq + pd->p_len; + if (th->th_flags & TH_SYN) + end++; + if (th->th_flags & TH_FIN) + end++; + } + + if ((th->th_flags & TH_ACK) == 0) { + /* Let it pass through the ack skew check */ + ack = dst->seqlo; + } else if ((ack == 0 && + (th->th_flags & (TH_ACK|TH_RST)) == (TH_ACK|TH_RST)) || + /* broken tcp stacks do not set ack */ + (dst->state < TCPS_SYN_SENT)) { + /* + * Many stacks (ours included) will set the ACK number in an + * FIN|ACK if the SYN times out -- no sequence to ACK. + */ + ack = dst->seqlo; + } + + if (seq == end) { + /* Ease sequencing restrictions on no data packets */ + seq = src->seqlo; + end = seq; + } + + ackskew = dst->seqlo - ack; + +#define MAXACKWINDOW (0xffff + 1500) /* 1500 is an arbitrary fudge factor */ + if (SEQ_GEQ(src->seqhi, end) && + /* Last octet inside other's window space */ + SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws)) && + /* Retrans: not more than one window back */ + (ackskew >= -MAXACKWINDOW) && + /* Acking not more than one reassembled fragment backwards */ + (ackskew <= (MAXACKWINDOW << sws))) { + /* Acking not more than one window forward */ + + (*state)->packets[dirndx]++; + (*state)->bytes[dirndx] += pd->tot_len; + + /* update max window */ + if (src->max_win < win) + src->max_win = win; + /* synchronize sequencing */ + if (SEQ_GT(end, src->seqlo)) + src->seqlo = end; + /* slide the window of what the other end can send */ + if (SEQ_GEQ(ack + (win << sws), dst->seqhi)) + dst->seqhi = ack + MAX((win << sws), 1); + + + /* update states */ + if (th->th_flags & TH_SYN) + if (src->state < TCPS_SYN_SENT) + src->state = TCPS_SYN_SENT; + if (th->th_flags & TH_FIN) + if (src->state < TCPS_CLOSING) + src->state = TCPS_CLOSING; + if (th->th_flags & TH_ACK) { + if (dst->state == TCPS_SYN_SENT) + dst->state = TCPS_ESTABLISHED; + else if (dst->state == TCPS_CLOSING) + dst->state = TCPS_FIN_WAIT_2; + } + if (th->th_flags & TH_RST) + src->state = dst->state = TCPS_TIME_WAIT; + + /* update expire time */ + (*state)->expire = time.tv_sec; + if (src->state >= TCPS_FIN_WAIT_2 && + dst->state >= TCPS_FIN_WAIT_2) + (*state)->timeout = PFTM_TCP_CLOSED; + else if (src->state >= TCPS_FIN_WAIT_2 || + dst->state >= TCPS_FIN_WAIT_2) + (*state)->timeout = PFTM_TCP_FIN_WAIT; + else if (src->state < TCPS_ESTABLISHED || + dst->state < TCPS_ESTABLISHED) + (*state)->timeout = PFTM_TCP_OPENING; + else if (src->state >= TCPS_CLOSING || + dst->state >= TCPS_CLOSING) + (*state)->timeout = PFTM_TCP_CLOSING; + else + (*state)->timeout = PFTM_TCP_ESTABLISHED; + + /* Fall through to PASS packet */ + + } else if ((dst->state < TCPS_SYN_SENT || + dst->state >= TCPS_FIN_WAIT_2 || + src->state >= TCPS_FIN_WAIT_2) && + SEQ_GEQ(src->seqhi + MAXACKWINDOW, end) && + /* Within a window forward of the originating packet */ + SEQ_GEQ(seq, src->seqlo - MAXACKWINDOW)) { + /* Within a window backward of the originating packet */ + + /* + * This currently handles three situations: + * 1) Stupid stacks will shotgun SYNs before their peer + * replies. + * 2) When PF catches an already established stream (the + * firewall rebooted, the state table was flushed, routes + * changed...) + * 3) Packets get funky immediately after the connection + * closes (this should catch Solaris spurious ACK|FINs + * that web servers like to spew after a close) + * + * This must be a little more careful than the above code + * since packet floods will also be caught here. We don't + * update the TTL here to mitigate the damage of a packet + * flood and so the same code can handle awkward establishment + * and a loosened connection close. + * In the establishment case, a correct peer response will + * validate the connection, go through the normal state code + * and keep updating the state TTL. + */ + + if (pf_status.debug >= PF_DEBUG_MISC) { + printf("pf: loose state match: "); + pf_print_state(*state); + pf_print_flags(th->th_flags); + printf(" seq=%u ack=%u len=%u ackskew=%d pkts=%d:%d\n", + seq, ack, pd->p_len, ackskew, + (*state)->packets[0], (*state)->packets[1]); + } + + (*state)->packets[dirndx]++; + (*state)->bytes[dirndx] += pd->tot_len; + + /* update max window */ + if (src->max_win < win) + src->max_win = win; + /* synchronize sequencing */ + if (SEQ_GT(end, src->seqlo)) + src->seqlo = end; + /* slide the window of what the other end can send */ + if (SEQ_GEQ(ack + (win << sws), dst->seqhi)) + dst->seqhi = ack + MAX((win << sws), 1); + + /* + * Cannot set dst->seqhi here since this could be a shotgunned + * SYN and not an already established connection. + */ + + if (th->th_flags & TH_FIN) + if (src->state < TCPS_CLOSING) + src->state = TCPS_CLOSING; + if (th->th_flags & TH_RST) + src->state = dst->state = TCPS_TIME_WAIT; + + /* Fall through to PASS packet */ + + } else { + if ((*state)->dst.state == TCPS_SYN_SENT && + (*state)->src.state == TCPS_SYN_SENT) { + /* Send RST for state mismatches during handshake */ + if (!(th->th_flags & TH_RST)) { + u_int32_t ack = ntohl(th->th_seq) + pd->p_len; + + if (th->th_flags & TH_SYN) + ack++; + if (th->th_flags & TH_FIN) + ack++; + pf_send_tcp((*state)->rule.ptr, pd->af, + pd->dst, pd->src, th->th_dport, + th->th_sport, ntohl(th->th_ack), ack, + TH_RST|TH_ACK, 0, 0, + (*state)->rule.ptr->return_ttl); + } + src->seqlo = 0; + src->seqhi = 1; + src->max_win = 1; + } else if (pf_status.debug >= PF_DEBUG_MISC) { + printf("pf: BAD state: "); + pf_print_state(*state); + pf_print_flags(th->th_flags); + printf(" seq=%u ack=%u len=%u ackskew=%d pkts=%d:%d " + "dir=%s,%s\n", seq, ack, pd->p_len, ackskew, + (*state)->packets[0], (*state)->packets[1], + direction == PF_IN ? "in" : "out", + direction == (*state)->direction ? "fwd" : "rev"); + printf("pf: State failure on: %c %c %c %c | %c %c\n", + SEQ_GEQ(src->seqhi, end) ? ' ' : '1', + SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws)) ? + ' ': '2', + (ackskew >= -MAXACKWINDOW) ? ' ' : '3', + (ackskew <= (MAXACKWINDOW << sws)) ? ' ' : '4', + SEQ_GEQ(src->seqhi + MAXACKWINDOW, end) ?' ' :'5', + SEQ_GEQ(seq, src->seqlo - MAXACKWINDOW) ?' ' :'6'); + } + return (PF_DROP); + } + + if (dst->scrub || src->scrub) { + if (pf_normalize_tcp_stateful(m, off, pd, reason, th, src, dst, + ©back)) + return (PF_DROP); + } + + /* Any packets which have gotten here are to be passed */ + + /* translate source/destination address, if needed */ + if (STATE_TRANSLATE(*state)) { + if (direction == PF_OUT) + pf_change_ap(pd->src, &th->th_sport, pd->ip_sum, + &th->th_sum, &(*state)->gwy.addr, + (*state)->gwy.port, 0, pd->af); + else + pf_change_ap(pd->dst, &th->th_dport, pd->ip_sum, + &th->th_sum, &(*state)->lan.addr, + (*state)->lan.port, 0, pd->af); + m_copyback(m, off, sizeof(*th), (caddr_t)th); + } else if (copyback) { + /* Copyback sequence modulation or stateful scrub changes */ + m_copyback(m, off, sizeof(*th), (caddr_t)th); + } + + (*state)->rule.ptr->packets++; + (*state)->rule.ptr->bytes += pd->tot_len; + if ((*state)->nat_rule.ptr != NULL) { + (*state)->nat_rule.ptr->packets++; + (*state)->nat_rule.ptr->bytes += pd->tot_len; + } + if ((*state)->anchor.ptr != NULL) { + (*state)->anchor.ptr->packets++; + (*state)->anchor.ptr->bytes += pd->tot_len; + } + return (PF_PASS); +} + +int +pf_test_state_udp(struct pf_state **state, int direction, struct ifnet *ifp, + struct mbuf *m, int ipoff, int off, void *h, struct pf_pdesc *pd) +{ + struct pf_state_peer *src, *dst; + struct pf_tree_node key; + struct udphdr *uh = pd->hdr.udp; + int dirndx; + + key.af = pd->af; + key.proto = IPPROTO_UDP; + PF_ACPY(&key.addr[0], pd->src, key.af); + PF_ACPY(&key.addr[1], pd->dst, key.af); + key.port[0] = pd->hdr.udp->uh_sport; + key.port[1] = pd->hdr.udp->uh_dport; + + STATE_LOOKUP(); + + if (direction == (*state)->direction) { + src = &(*state)->src; + dst = &(*state)->dst; + dirndx = 0; + } else { + src = &(*state)->dst; + dst = &(*state)->src; + dirndx = 1; + } + + (*state)->packets[dirndx]++; + (*state)->bytes[dirndx] += pd->tot_len; + + /* update states */ + if (src->state < PFUDPS_SINGLE) + src->state = PFUDPS_SINGLE; + if (dst->state == PFUDPS_SINGLE) + dst->state = PFUDPS_MULTIPLE; + + /* update expire time */ + (*state)->expire = time.tv_sec; + if (src->state == PFUDPS_MULTIPLE && dst->state == PFUDPS_MULTIPLE) + (*state)->timeout = PFTM_UDP_MULTIPLE; + else + (*state)->timeout = PFTM_UDP_SINGLE; + + /* translate source/destination address, if necessary */ + if (STATE_TRANSLATE(*state)) { + if (direction == PF_OUT) + pf_change_ap(pd->src, &uh->uh_sport, pd->ip_sum, + &uh->uh_sum, &(*state)->gwy.addr, + (*state)->gwy.port, 1, pd->af); + else + pf_change_ap(pd->dst, &uh->uh_dport, pd->ip_sum, + &uh->uh_sum, &(*state)->lan.addr, + (*state)->lan.port, 1, pd->af); + m_copyback(m, off, sizeof(*uh), (caddr_t)uh); + } + + (*state)->rule.ptr->packets++; + (*state)->rule.ptr->bytes += pd->tot_len; + if ((*state)->nat_rule.ptr != NULL) { + (*state)->nat_rule.ptr->packets++; + (*state)->nat_rule.ptr->bytes += pd->tot_len; + } + if ((*state)->anchor.ptr != NULL) { + (*state)->anchor.ptr->packets++; + (*state)->anchor.ptr->bytes += pd->tot_len; + } + return (PF_PASS); +} + +int +pf_test_state_icmp(struct pf_state **state, int direction, struct ifnet *ifp, + struct mbuf *m, int ipoff, int off, void *h, struct pf_pdesc *pd) +{ + struct pf_addr *saddr = pd->src, *daddr = pd->dst; + u_int16_t icmpid, *icmpsum; + u_int8_t icmptype; + int state_icmp = 0, dirndx; + + switch (pd->proto) { +#ifdef INET + case IPPROTO_ICMP: + icmptype = pd->hdr.icmp->icmp_type; + icmpid = pd->hdr.icmp->icmp_id; + icmpsum = &pd->hdr.icmp->icmp_cksum; + + if (icmptype == ICMP_UNREACH || + icmptype == ICMP_SOURCEQUENCH || + icmptype == ICMP_REDIRECT || + icmptype == ICMP_TIMXCEED || + icmptype == ICMP_PARAMPROB) + state_icmp++; + break; +#endif /* INET */ +#ifdef INET6 + case IPPROTO_ICMPV6: + icmptype = pd->hdr.icmp6->icmp6_type; + icmpid = pd->hdr.icmp6->icmp6_id; + icmpsum = &pd->hdr.icmp6->icmp6_cksum; + + if (icmptype == ICMP6_DST_UNREACH || + icmptype == ICMP6_PACKET_TOO_BIG || + icmptype == ICMP6_TIME_EXCEEDED || + icmptype == ICMP6_PARAM_PROB) + state_icmp++; + break; +#endif /* INET6 */ + } + + if (!state_icmp) { + + /* + * ICMP query/reply message not related to a TCP/UDP packet. + * Search for an ICMP state. + */ + struct pf_tree_node key; + + key.af = pd->af; + key.proto = pd->proto; + PF_ACPY(&key.addr[0], saddr, key.af); + PF_ACPY(&key.addr[1], daddr, key.af); + key.port[0] = icmpid; + key.port[1] = icmpid; + + STATE_LOOKUP(); + + dirndx = (direction == (*state)->direction) ? 0 : 1; + (*state)->packets[dirndx]++; + (*state)->bytes[dirndx] += pd->tot_len; + (*state)->expire = time.tv_sec; + (*state)->timeout = PFTM_ICMP_ERROR_REPLY; + + /* translate source/destination address, if needed */ + if (PF_ANEQ(&(*state)->lan.addr, &(*state)->gwy.addr, pd->af)) { + if (direction == PF_OUT) { + switch (pd->af) { +#ifdef INET + case AF_INET: + pf_change_a(&saddr->v4.s_addr, + pd->ip_sum, + (*state)->gwy.addr.v4.s_addr, 0); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + pf_change_a6(saddr, + &pd->hdr.icmp6->icmp6_cksum, + &(*state)->gwy.addr, 0); + m_copyback(m, off, + sizeof(struct icmp6_hdr), + (caddr_t)pd->hdr.icmp6); + break; +#endif /* INET6 */ + } + } else { + switch (pd->af) { +#ifdef INET + case AF_INET: + pf_change_a(&daddr->v4.s_addr, + pd->ip_sum, + (*state)->lan.addr.v4.s_addr, 0); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + pf_change_a6(daddr, + &pd->hdr.icmp6->icmp6_cksum, + &(*state)->lan.addr, 0); + m_copyback(m, off, + sizeof(struct icmp6_hdr), + (caddr_t)pd->hdr.icmp6); + break; +#endif /* INET6 */ + } + } + } + + return (PF_PASS); + + } else { + /* + * ICMP error message in response to a TCP/UDP packet. + * Extract the inner TCP/UDP header and search for that state. + */ + + struct pf_pdesc pd2; +#ifdef INET + struct ip h2; +#endif /* INET */ +#ifdef INET6 + struct ip6_hdr h2_6; + int terminal = 0; +#endif /* INET6 */ + int ipoff2; + int off2; + + pd2.af = pd->af; + switch (pd->af) { +#ifdef INET + case AF_INET: + /* offset of h2 in mbuf chain */ + ipoff2 = off + ICMP_MINLEN; + + if (!pf_pull_hdr(m, ipoff2, &h2, sizeof(h2), + NULL, NULL, pd2.af)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: ICMP error message too short " + "(ip)\n")); + return (PF_DROP); + } + /* + * ICMP error messages don't refer to non-first + * fragments + */ + if (h2.ip_off & htons(IP_OFFMASK)) + return (PF_DROP); + + /* offset of protocol header that follows h2 */ + off2 = ipoff2 + (h2.ip_hl << 2); + + pd2.proto = h2.ip_p; + pd2.src = (struct pf_addr *)&h2.ip_src; + pd2.dst = (struct pf_addr *)&h2.ip_dst; + pd2.ip_sum = &h2.ip_sum; + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + ipoff2 = off + sizeof(struct icmp6_hdr); + + if (!pf_pull_hdr(m, ipoff2, &h2_6, sizeof(h2_6), + NULL, NULL, pd2.af)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: ICMP error message too short " + "(ip6)\n")); + return (PF_DROP); + } + pd2.proto = h2_6.ip6_nxt; + pd2.src = (struct pf_addr *)&h2_6.ip6_src; + pd2.dst = (struct pf_addr *)&h2_6.ip6_dst; + pd2.ip_sum = NULL; + off2 = ipoff2 + sizeof(h2_6); + do { + switch (pd2.proto) { + case IPPROTO_FRAGMENT: + /* + * ICMPv6 error messages for + * non-first fragments + */ + return (PF_DROP); + case IPPROTO_AH: + case IPPROTO_HOPOPTS: + case IPPROTO_ROUTING: + case IPPROTO_DSTOPTS: { + /* get next header and header length */ + struct ip6_ext opt6; + + if (!pf_pull_hdr(m, off2, &opt6, + sizeof(opt6), NULL, NULL, pd2.af)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: ICMPv6 short opt\n")); + return (PF_DROP); + } + if (pd2.proto == IPPROTO_AH) + off2 += (opt6.ip6e_len + 2) * 4; + else + off2 += (opt6.ip6e_len + 1) * 8; + pd2.proto = opt6.ip6e_nxt; + /* goto the next header */ + break; + } + default: + terminal++; + break; + } + } while (!terminal); + break; +#endif /* INET6 */ + } + + switch (pd2.proto) { + case IPPROTO_TCP: { + struct tcphdr th; + u_int32_t seq; + struct pf_tree_node key; + struct pf_state_peer *src, *dst; + u_int8_t dws; + + /* + * Only the first 8 bytes of the TCP header can be + * expected. Don't access any TCP header fields after + * th_seq, an ackskew test is not possible. + */ + if (!pf_pull_hdr(m, off2, &th, 8, NULL, NULL, pd2.af)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: ICMP error message too short " + "(tcp)\n")); + return (PF_DROP); + } + + key.af = pd2.af; + key.proto = IPPROTO_TCP; + PF_ACPY(&key.addr[0], pd2.dst, pd2.af); + key.port[0] = th.th_dport; + PF_ACPY(&key.addr[1], pd2.src, pd2.af); + key.port[1] = th.th_sport; + + STATE_LOOKUP(); + + if (direction == (*state)->direction) { + src = &(*state)->dst; + dst = &(*state)->src; + } else { + src = &(*state)->src; + dst = &(*state)->dst; + } + + if (src->wscale && dst->wscale && !(th.th_flags & TH_SYN)) + dws = dst->wscale & PF_WSCALE_MASK; + else + dws = 0; + + /* Demodulate sequence number */ + seq = ntohl(th.th_seq) - src->seqdiff; + if (src->seqdiff) + pf_change_a(&th.th_seq, &th.th_sum, + htonl(seq), 0); + + if (!SEQ_GEQ(src->seqhi, seq) || + !SEQ_GEQ(seq, src->seqlo - (dst->max_win << dws))) { + if (pf_status.debug >= PF_DEBUG_MISC) { + printf("pf: BAD ICMP %d:%d ", + icmptype, pd->hdr.icmp->icmp_code); + pf_print_host(pd->src, 0, pd->af); + printf(" -> "); + pf_print_host(pd->dst, 0, pd->af); + printf(" state: "); + pf_print_state(*state); + printf(" seq=%u\n", seq); + } + return (PF_DROP); + } + + if (STATE_TRANSLATE(*state)) { + if (direction == PF_IN) { + pf_change_icmp(pd2.src, &th.th_sport, + saddr, &(*state)->lan.addr, + (*state)->lan.port, NULL, + pd2.ip_sum, icmpsum, + pd->ip_sum, 0, pd2.af); + } else { + pf_change_icmp(pd2.dst, &th.th_dport, + saddr, &(*state)->gwy.addr, + (*state)->gwy.port, NULL, + pd2.ip_sum, icmpsum, + pd->ip_sum, 0, pd2.af); + } + switch (pd2.af) { +#ifdef INET + case AF_INET: + m_copyback(m, off, ICMP_MINLEN, + (caddr_t)pd->hdr.icmp); + m_copyback(m, ipoff2, sizeof(h2), + (caddr_t)&h2); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + m_copyback(m, off, + sizeof(struct icmp6_hdr), + (caddr_t)pd->hdr.icmp6); + m_copyback(m, ipoff2, sizeof(h2_6), + (caddr_t)&h2_6); + break; +#endif /* INET6 */ + } + m_copyback(m, off2, 8, (caddr_t)&th); + } else if (src->seqdiff) { + m_copyback(m, off2, 8, (caddr_t)&th); + } + + return (PF_PASS); + break; + } + case IPPROTO_UDP: { + struct udphdr uh; + struct pf_tree_node key; + + if (!pf_pull_hdr(m, off2, &uh, sizeof(uh), + NULL, NULL, pd2.af)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: ICMP error message too short " + "(udp)\n")); + return (PF_DROP); + } + + key.af = pd2.af; + key.proto = IPPROTO_UDP; + PF_ACPY(&key.addr[0], pd2.dst, pd2.af); + key.port[0] = uh.uh_dport; + PF_ACPY(&key.addr[1], pd2.src, pd2.af); + key.port[1] = uh.uh_sport; + + STATE_LOOKUP(); + + if (STATE_TRANSLATE(*state)) { + if (direction == PF_IN) { + pf_change_icmp(pd2.src, &uh.uh_sport, + daddr, &(*state)->lan.addr, + (*state)->lan.port, &uh.uh_sum, + pd2.ip_sum, icmpsum, + pd->ip_sum, 1, pd2.af); + } else { + pf_change_icmp(pd2.dst, &uh.uh_dport, + saddr, &(*state)->gwy.addr, + (*state)->gwy.port, &uh.uh_sum, + pd2.ip_sum, icmpsum, + pd->ip_sum, 1, pd2.af); + } + switch (pd2.af) { +#ifdef INET + case AF_INET: + m_copyback(m, off, ICMP_MINLEN, + (caddr_t)pd->hdr.icmp); + m_copyback(m, ipoff2, sizeof(h2), + (caddr_t)&h2); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + m_copyback(m, off, + sizeof(struct icmp6_hdr), + (caddr_t)pd->hdr.icmp6); + m_copyback(m, ipoff2, sizeof(h2_6), + (caddr_t)&h2_6); + break; +#endif /* INET6 */ + } + m_copyback(m, off2, sizeof(uh), + (caddr_t)&uh); + } + + return (PF_PASS); + break; + } +#ifdef INET + case IPPROTO_ICMP: { + struct icmp iih; + struct pf_tree_node key; + + if (!pf_pull_hdr(m, off2, &iih, ICMP_MINLEN, + NULL, NULL, pd2.af)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: ICMP error message too short i" + "(icmp)\n")); + return (PF_DROP); + } + + key.af = pd2.af; + key.proto = IPPROTO_ICMP; + PF_ACPY(&key.addr[0], pd2.dst, pd2.af); + key.port[0] = iih.icmp_id; + PF_ACPY(&key.addr[1], pd2.src, pd2.af); + key.port[1] = iih.icmp_id; + + STATE_LOOKUP(); + + if (STATE_TRANSLATE(*state)) { + if (direction == PF_IN) { + pf_change_icmp(pd2.src, &iih.icmp_id, + daddr, &(*state)->lan.addr, + (*state)->lan.port, NULL, + pd2.ip_sum, icmpsum, + pd->ip_sum, 0, AF_INET); + } else { + pf_change_icmp(pd2.dst, &iih.icmp_id, + saddr, &(*state)->gwy.addr, + (*state)->gwy.port, NULL, + pd2.ip_sum, icmpsum, + pd->ip_sum, 0, AF_INET); + } + m_copyback(m, off, ICMP_MINLEN, + (caddr_t)pd->hdr.icmp); + m_copyback(m, ipoff2, sizeof(h2), + (caddr_t)&h2); + m_copyback(m, off2, ICMP_MINLEN, + (caddr_t)&iih); + } + + return (PF_PASS); + break; + } +#endif /* INET */ +#ifdef INET6 + case IPPROTO_ICMPV6: { + struct icmp6_hdr iih; + struct pf_tree_node key; + + if (!pf_pull_hdr(m, off2, &iih, + sizeof(struct icmp6_hdr), NULL, NULL, pd2.af)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: ICMP error message too short " + "(icmp6)\n")); + return (PF_DROP); + } + + key.af = pd2.af; + key.proto = IPPROTO_ICMPV6; + PF_ACPY(&key.addr[0], pd2.dst, pd2.af); + key.port[0] = iih.icmp6_id; + PF_ACPY(&key.addr[1], pd2.src, pd2.af); + key.port[1] = iih.icmp6_id; + + STATE_LOOKUP(); + + if (STATE_TRANSLATE(*state)) { + if (direction == PF_IN) { + pf_change_icmp(pd2.src, &iih.icmp6_id, + daddr, &(*state)->lan.addr, + (*state)->lan.port, NULL, + pd2.ip_sum, icmpsum, + pd->ip_sum, 0, AF_INET6); + } else { + pf_change_icmp(pd2.dst, &iih.icmp6_id, + saddr, &(*state)->gwy.addr, + (*state)->gwy.port, NULL, + pd2.ip_sum, icmpsum, + pd->ip_sum, 0, AF_INET6); + } + m_copyback(m, off, sizeof(struct icmp6_hdr), + (caddr_t)pd->hdr.icmp6); + m_copyback(m, ipoff2, sizeof(h2_6), + (caddr_t)&h2_6); + m_copyback(m, off2, sizeof(struct icmp6_hdr), + (caddr_t)&iih); + } + + return (PF_PASS); + break; + } +#endif /* INET6 */ + default: { + struct pf_tree_node key; + + key.af = pd2.af; + key.proto = pd2.proto; + PF_ACPY(&key.addr[0], pd2.dst, pd2.af); + key.port[0] = 0; + PF_ACPY(&key.addr[1], pd2.src, pd2.af); + key.port[1] = 0; + + STATE_LOOKUP(); + + if (STATE_TRANSLATE(*state)) { + if (direction == PF_IN) { + pf_change_icmp(pd2.src, NULL, + daddr, &(*state)->lan.addr, + 0, NULL, + pd2.ip_sum, icmpsum, + pd->ip_sum, 0, pd2.af); + } else { + pf_change_icmp(pd2.dst, NULL, + saddr, &(*state)->gwy.addr, + 0, NULL, + pd2.ip_sum, icmpsum, + pd->ip_sum, 0, pd2.af); + } + switch (pd2.af) { +#ifdef INET + case AF_INET: + m_copyback(m, off, ICMP_MINLEN, + (caddr_t)pd->hdr.icmp); + m_copyback(m, ipoff2, sizeof(h2), + (caddr_t)&h2); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + m_copyback(m, off, + sizeof(struct icmp6_hdr), + (caddr_t)pd->hdr.icmp6); + m_copyback(m, ipoff2, sizeof(h2_6), + (caddr_t)&h2_6); + break; +#endif /* INET6 */ + } + } + + return (PF_PASS); + break; + } + } + } +} + +int +pf_test_state_other(struct pf_state **state, int direction, struct ifnet *ifp, + struct pf_pdesc *pd) +{ + struct pf_state_peer *src, *dst; + struct pf_tree_node key; + int dirndx; + + key.af = pd->af; + key.proto = pd->proto; + PF_ACPY(&key.addr[0], pd->src, key.af); + PF_ACPY(&key.addr[1], pd->dst, key.af); + key.port[0] = 0; + key.port[1] = 0; + + STATE_LOOKUP(); + + if (direction == (*state)->direction) { + src = &(*state)->src; + dst = &(*state)->dst; + dirndx = 0; + } else { + src = &(*state)->dst; + dst = &(*state)->src; + dirndx = 1; + } + + (*state)->packets[dirndx]++; + (*state)->bytes[dirndx] += pd->tot_len; + + /* update states */ + if (src->state < PFOTHERS_SINGLE) + src->state = PFOTHERS_SINGLE; + if (dst->state == PFOTHERS_SINGLE) + dst->state = PFOTHERS_MULTIPLE; + + /* update expire time */ + (*state)->expire = time.tv_sec; + if (src->state == PFOTHERS_MULTIPLE && dst->state == PFOTHERS_MULTIPLE) + (*state)->timeout = PFTM_OTHER_MULTIPLE; + else + (*state)->timeout = PFTM_OTHER_SINGLE; + + /* translate source/destination address, if necessary */ + if (STATE_TRANSLATE(*state)) { + if (direction == PF_OUT) + switch (pd->af) { +#ifdef INET + case AF_INET: + pf_change_a(&pd->src->v4.s_addr, + pd->ip_sum, (*state)->gwy.addr.v4.s_addr, + 0); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + PF_ACPY(pd->src, &(*state)->gwy.addr, pd->af); + break; +#endif /* INET6 */ + } + else + switch (pd->af) { +#ifdef INET + case AF_INET: + pf_change_a(&pd->dst->v4.s_addr, + pd->ip_sum, (*state)->lan.addr.v4.s_addr, + 0); + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + PF_ACPY(pd->dst, &(*state)->lan.addr, pd->af); + break; +#endif /* INET6 */ + } + } + + (*state)->rule.ptr->packets++; + (*state)->rule.ptr->bytes += pd->tot_len; + if ((*state)->nat_rule.ptr != NULL) { + (*state)->nat_rule.ptr->packets++; + (*state)->nat_rule.ptr->bytes += pd->tot_len; + } + if ((*state)->anchor.ptr != NULL) { + (*state)->anchor.ptr->packets++; + (*state)->anchor.ptr->bytes += pd->tot_len; + } + return (PF_PASS); +} + +/* + * ipoff and off are measured from the start of the mbuf chain. + * h must be at "ipoff" on the mbuf chain. + */ +void * +pf_pull_hdr(struct mbuf *m, int off, void *p, int len, + u_short *actionp, u_short *reasonp, sa_family_t af) +{ + switch (af) { +#ifdef INET + case AF_INET: { + struct ip *h = mtod(m, struct ip *); +#ifdef __OpenBSD__ + u_int16_t fragoff = (h->ip_off & IP_OFFMASK) << 3; +#else + u_int16_t fragoff = (ntohs(h->ip_off) & IP_OFFMASK) << 3; +#endif + + if (fragoff) { + if (fragoff >= len) + ACTION_SET(actionp, PF_PASS); + else { + ACTION_SET(actionp, PF_DROP); + REASON_SET(reasonp, PFRES_FRAG); + } + return (NULL); + } +#ifdef __OpenBSD__ + if (m->m_pkthdr.len < off + len || h->ip_len < off + len) +#else + if (m->m_pkthdr.len < off + len || ntohs(h->ip_len) < off + len) +#endif + { + ACTION_SET(actionp, PF_DROP); + REASON_SET(reasonp, PFRES_SHORT); + return (NULL); + } + break; + } +#endif /* INET */ +#ifdef INET6 + case AF_INET6: { + struct ip6_hdr *h = mtod(m, struct ip6_hdr *); + + if (m->m_pkthdr.len < off + len || + (ntohs(h->ip6_plen) + sizeof(struct ip6_hdr)) < + (unsigned)(off + len)) { + ACTION_SET(actionp, PF_DROP); + REASON_SET(reasonp, PFRES_SHORT); + return (NULL); + } + break; + } +#endif /* INET6 */ + } + m_copydata(m, off, len, p); + return (p); +} + +int +pf_routable(struct pf_addr *addr, sa_family_t af) +{ + struct sockaddr_in *dst; + struct route ro; + int ret = 0; + + bzero(&ro, sizeof(ro)); + dst = satosin(&ro.ro_dst); + dst->sin_family = af; + dst->sin_len = sizeof(*dst); + dst->sin_addr = addr->v4; +#ifdef __OpenBSD__ + rtalloc_noclone(&ro, NO_CLONING); +#else + rtalloc(&ro); +#endif + + if (ro.ro_rt != NULL) { + ret = 1; + RTFREE(ro.ro_rt); + } + + return (ret); +} + +#ifdef INET +void +pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, + struct pf_state *s) +{ + struct mbuf *m0, *m1; + struct route iproute; + struct route *ro; + struct sockaddr_in *dst; + struct ip *ip; + struct ifnet *ifp = NULL; + struct m_tag *mtag; + struct pf_addr naddr; + int error = 0; + + if (m == NULL || *m == NULL || r == NULL || + (dir != PF_IN && dir != PF_OUT) || oifp == NULL) + panic("pf_route: invalid parameters"); + + if (r->rt == PF_DUPTO) { + m0 = *m; + mtag = m_tag_find(m0, PACKET_TAG_PF_ROUTED, NULL); + if (mtag == NULL) { + mtag = m_tag_get(PACKET_TAG_PF_ROUTED, 0, M_NOWAIT); + if (mtag == NULL) + goto bad; + m_tag_prepend(m0, mtag); + } +#ifdef __OpenBSD__ + m0 = m_copym2(*m, 0, M_COPYALL, M_NOWAIT); +#else + m0 = m_dup(*m, 0, M_COPYALL, M_NOWAIT); +#endif + if (m0 == NULL) + return; + } else { + if ((r->rt == PF_REPLYTO) == (r->direction == dir)) + return; + m0 = *m; + } + + if (m0->m_len < sizeof(struct ip)) + panic("pf_route: m0->m_len < sizeof(struct ip)"); + ip = mtod(m0, struct ip *); + + ro = &iproute; + bzero((caddr_t)ro, sizeof(*ro)); + dst = satosin(&ro->ro_dst); + dst->sin_family = AF_INET; + dst->sin_len = sizeof(*dst); + dst->sin_addr = ip->ip_dst; + + if (r->rt == PF_FASTROUTE) { + rtalloc(ro); + if (ro->ro_rt == 0) { + ipstat.ips_noroute++; + goto bad; + } + + ifp = ro->ro_rt->rt_ifp; + ro->ro_rt->rt_use++; + + if (ro->ro_rt->rt_flags & RTF_GATEWAY) + dst = satosin(ro->ro_rt->rt_gateway); + } else { + if (TAILQ_EMPTY(&r->rpool.list)) + panic("pf_route: TAILQ_EMPTY(&r->rpool.list)"); + if (s == NULL) { + pf_map_addr(AF_INET, &r->rpool, + (struct pf_addr *)&ip->ip_src, + &naddr, NULL); + if (!PF_AZERO(&naddr, AF_INET)) + dst->sin_addr.s_addr = naddr.v4.s_addr; + ifp = r->rpool.cur->ifp; + } else { + if (s->rt_ifp == NULL) { + pf_map_addr(AF_INET, &r->rpool, + (struct pf_addr *)&ip->ip_src, + &s->rt_addr, NULL); + s->rt_ifp = r->rpool.cur->ifp; + } + if (!PF_AZERO(&s->rt_addr, AF_INET)) + dst->sin_addr.s_addr = + s->rt_addr.v4.s_addr; + ifp = s->rt_ifp; + } + } + + if (ifp == NULL) + goto bad; + + if (oifp != ifp) { + mtag = m_tag_find(m0, PACKET_TAG_PF_ROUTED, NULL); + if (mtag == NULL) { + mtag = m_tag_get(PACKET_TAG_PF_ROUTED, 0, M_NOWAIT); + if (mtag == NULL) + goto bad; + m_tag_prepend(m0, mtag); + if (pf_test(PF_OUT, ifp, &m0) != PF_PASS) + goto bad; + else if (m0 == NULL) + goto done; + if (m0->m_len < sizeof(struct ip)) + panic("pf_route: m0->m_len < " + "sizeof(struct ip)"); + ip = mtod(m0, struct ip *); + } + } + + /* Copied from ip_output. */ +#ifdef __OpenBSD__ + if (ip->ip_len <= ifp->if_mtu) +#else + if (ntohs(ip->ip_len) <= ifp->if_mtu) +#endif + { +#ifdef __OpenBSD__ + ip->ip_len = htons((u_int16_t)ip->ip_len); + ip->ip_off = htons((u_int16_t)ip->ip_off); + if ((ifp->if_capabilities & IFCAP_CSUM_IPv4) && + ifp->if_bridge == NULL) { + m0->m_pkthdr.csum |= M_IPV4_CSUM_OUT; + ipstat.ips_outhwcsum++; + } else { + ip->ip_sum = 0; + ip->ip_sum = in_cksum(m0, ip->ip_hl << 2); + } + /* Update relevant hardware checksum stats for TCP/UDP */ + if (m0->m_pkthdr.csum & M_TCPV4_CSUM_OUT) + tcpstat.tcps_outhwcsum++; + else if (m0->m_pkthdr.csum & M_UDPV4_CSUM_OUT) + udpstat.udps_outhwcsum++; +#else + ip->ip_sum = 0; + ip->ip_sum = in_cksum(m0, ip->ip_hl << 2); +#endif + error = (*ifp->if_output)(ifp, m0, sintosa(dst), NULL); + goto done; + } + + /* + * Too large for interface; fragment if possible. + * Must be able to put at least 8 bytes per fragment. + */ +#ifdef __OpenBSD__ + if (ip->ip_off & IP_DF) +#else + if (ip->ip_off & htons(IP_DF)) +#endif + { + ipstat.ips_cantfrag++; + if (r->rt != PF_DUPTO) { + icmp_error(m0, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG, 0, + ifp); + goto done; + } else + goto bad; + } + + m1 = m0; + error = ip_fragment(m0, ifp, ifp->if_mtu); + if (error == EMSGSIZE) + goto bad; + + for (m0 = m1; m0; m0 = m1) { + m1 = m0->m_nextpkt; + m0->m_nextpkt = 0; + if (error == 0) + error = (*ifp->if_output)(ifp, m0, sintosa(dst), + NULL); + else + m_freem(m0); + } + + if (error == 0) + ipstat.ips_fragmented++; + +done: + if (r->rt != PF_DUPTO) + *m = NULL; + if (ro == &iproute && ro->ro_rt) + RTFREE(ro->ro_rt); + return; + +bad: + m_freem(m0); + goto done; +} +#endif /* INET */ + +#ifdef INET6 +void +pf_route6(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, + struct pf_state *s) +{ + struct mbuf *m0; + struct m_tag *mtag; + struct route_in6 ip6route; + struct route_in6 *ro; + struct sockaddr_in6 *dst; + struct ip6_hdr *ip6; + struct ifnet *ifp = NULL; + struct pf_addr naddr; + int error = 0; + + if (m == NULL || *m == NULL || r == NULL || + (dir != PF_IN && dir != PF_OUT) || oifp == NULL) + panic("pf_route6: invalid parameters"); + + if (r->rt == PF_DUPTO) { + m0 = *m; + mtag = m_tag_find(m0, PACKET_TAG_PF_ROUTED, NULL); + if (mtag == NULL) { + mtag = m_tag_get(PACKET_TAG_PF_ROUTED, 0, M_NOWAIT); + if (mtag == NULL) + goto bad; + m_tag_prepend(m0, mtag); + } +#ifdef __OpenBSD__ + m0 = m_copym2(*m, 0, M_COPYALL, M_NOWAIT); +#else + m0 = m_dup(*m, 0, M_COPYALL, M_NOWAIT); +#endif + if (m0 == NULL) + return; + } else { + if ((r->rt == PF_REPLYTO) == (r->direction == dir)) + return; + m0 = *m; + } + + if (m0->m_len < sizeof(struct ip6_hdr)) + panic("pf_route6: m0->m_len < sizeof(struct ip6_hdr)"); + ip6 = mtod(m0, struct ip6_hdr *); + + ro = &ip6route; + bzero((caddr_t)ro, sizeof(*ro)); + dst = (struct sockaddr_in6 *)&ro->ro_dst; + dst->sin6_family = AF_INET6; + dst->sin6_len = sizeof(*dst); + dst->sin6_addr = ip6->ip6_dst; + + /* Cheat. */ + if (r->rt == PF_FASTROUTE) { + mtag = m_tag_get(PACKET_TAG_PF_GENERATED, 0, M_NOWAIT); + if (mtag == NULL) + goto bad; + m_tag_prepend(m0, mtag); + ip6_output(m0, NULL, NULL, NULL, NULL, NULL); + return; + } + + if (TAILQ_EMPTY(&r->rpool.list)) + panic("pf_route6: TAILQ_EMPTY(&r->rpool.list)"); + if (s == NULL) { + pf_map_addr(AF_INET6, &r->rpool, + (struct pf_addr *)&ip6->ip6_src, &naddr, NULL); + if (!PF_AZERO(&naddr, AF_INET6)) + PF_ACPY((struct pf_addr *)&dst->sin6_addr, + &naddr, AF_INET6); + ifp = r->rpool.cur->ifp; + } else { + if (s->rt_ifp == NULL) { + pf_map_addr(AF_INET6, &r->rpool, + (struct pf_addr *)&ip6->ip6_src, + &s->rt_addr, NULL); + s->rt_ifp = r->rpool.cur->ifp; + } + if (!PF_AZERO(&s->rt_addr, AF_INET6)) + PF_ACPY((struct pf_addr *)&dst->sin6_addr, + &s->rt_addr, AF_INET6); + ifp = s->rt_ifp; + } + + if (ifp == NULL) + goto bad; + + if (oifp != ifp) { + mtag = m_tag_find(m0, PACKET_TAG_PF_ROUTED, NULL); + if (mtag == NULL) { + mtag = m_tag_get(PACKET_TAG_PF_ROUTED, 0, M_NOWAIT); + if (mtag == NULL) + goto bad; + m_tag_prepend(m0, mtag); + if (pf_test6(PF_OUT, ifp, &m0) != PF_PASS) + goto bad; + else if (m0 == NULL) + goto done; + } + } + + /* + * If the packet is too large for the outgoing interface, + * send back an icmp6 error. + */ + if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr)) + dst->sin6_addr.s6_addr16[1] = htons(ifp->if_index); + if ((u_long)m0->m_pkthdr.len <= ifp->if_mtu) { + error = nd6_output(ifp, ifp, m0, dst, NULL); + } else { + in6_ifstat_inc(ifp, ifs6_in_toobig); + if (r->rt != PF_DUPTO) + icmp6_error(m0, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu); + else + goto bad; + } + +done: + if (r->rt != PF_DUPTO) + *m = NULL; + return; + +bad: + m_freem(m0); + goto done; +} +#endif /* INET6 */ + + +/* + * check protocol (tcp/udp/icmp/icmp6) checksum and set mbuf flag + * off is the offset where the protocol header starts + * len is the total length of protocol header plus payload + * returns 0 when the checksum is valid, otherwise returns 1. + */ +int +pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p, sa_family_t af) +{ + u_int16_t flag_ok, flag_bad; + u_int16_t sum; + + switch (p) { + case IPPROTO_TCP: +#ifdef __OpenBSD__ + flag_ok = M_TCP_CSUM_IN_OK; + flag_bad = M_TCP_CSUM_IN_BAD; +#else + flag_ok = M_CSUM_TCPv4; + flag_bad = M_CSUM_TCP_UDP_BAD; +#endif + break; + case IPPROTO_UDP: +#ifdef __OpenBSD__ + flag_ok = M_UDP_CSUM_IN_OK; + flag_bad = M_UDP_CSUM_IN_BAD; +#else + flag_ok = M_CSUM_UDPv4; + flag_bad = M_CSUM_TCP_UDP_BAD; +#endif + break; + case IPPROTO_ICMP: +#ifdef INET6 + case IPPROTO_ICMPV6: +#endif /* INET6 */ + flag_ok = flag_bad = 0; + break; + default: + return (1); + } +#ifdef __OpenBSD__ + if (m->m_pkthdr.csum & flag_ok) +#else + if (m->m_pkthdr.csum_flags & flag_ok) +#endif + return (0); +#ifdef __OpenBSD__ + if (m->m_pkthdr.csum & flag_bad) +#else + if (m->m_pkthdr.csum_flags & flag_bad) +#endif + return (1); + if (off < sizeof(struct ip) || len < sizeof(struct udphdr)) + return (1); + if (m->m_pkthdr.len < off + len) + return (1); + switch (af) { + case AF_INET: + if (p == IPPROTO_ICMP) { + if (m->m_len < off) + return (1); + m->m_data += off; + m->m_len -= off; + sum = in_cksum(m, len); + m->m_data -= off; + m->m_len += off; + } else { + if (m->m_len < sizeof(struct ip)) + return (1); + sum = in4_cksum(m, p, off, len); + } + break; +#ifdef INET6 + case AF_INET6: + if (m->m_len < sizeof(struct ip6_hdr)) + return (1); + sum = in6_cksum(m, p, off, len); + break; +#endif /* INET6 */ + default: + return (1); + } + if (sum) { +#ifdef __OpenBSD__ + m->m_pkthdr.csum |= flag_bad; +#else + m->m_pkthdr.csum_flags |= flag_bad; +#endif + switch (p) { + case IPPROTO_TCP: + tcpstat.tcps_rcvbadsum++; + break; + case IPPROTO_UDP: + udpstat.udps_badsum++; + break; + case IPPROTO_ICMP: + icmpstat.icps_checksum++; + break; +#ifdef INET6 + case IPPROTO_ICMPV6: + icmp6stat.icp6s_checksum++; + break; +#endif /* INET6 */ + } + return (1); + } +#ifdef __OpenBSD__ + m->m_pkthdr.csum |= flag_ok; +#else + m->m_pkthdr.csum_flags |= flag_ok; +#endif + return (0); +} + +#ifdef INET +int +pf_test(int dir, struct ifnet *ifp, struct mbuf **m0) +{ + u_short action, reason = 0, log = 0; + struct mbuf *m = *m0; + struct ip *h; + struct pf_rule *a = NULL, *r = &pf_default_rule; + struct pf_state *s = NULL; + struct pf_ruleset *ruleset = NULL; + struct pf_pdesc pd; + int off; + int pqid = 0; + + if (!pf_status.running || + (m_tag_find(m, PACKET_TAG_PF_GENERATED, NULL) != NULL)) + return (PF_PASS); + +#ifdef DIAGNOSTIC + if ((m->m_flags & M_PKTHDR) == 0) + panic("non-M_PKTHDR is passed to pf_test"); +#endif + + if (m->m_pkthdr.len < (int)sizeof(*h)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_SHORT); + log = 1; + goto done; + } + + /* We do IP header normalization and packet reassembly here */ + if (pf_normalize_ip(m0, dir, ifp, &reason) != PF_PASS) { + action = PF_DROP; + goto done; + } + m = *m0; + h = mtod(m, struct ip *); + + off = h->ip_hl << 2; + if (off < (int)sizeof(*h)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_SHORT); + log = 1; + goto done; + } + + memset(&pd, 0, sizeof(pd)); + pd.src = (struct pf_addr *)&h->ip_src; + pd.dst = (struct pf_addr *)&h->ip_dst; + pd.ip_sum = &h->ip_sum; + pd.proto = h->ip_p; + pd.af = AF_INET; + pd.tos = h->ip_tos; +#ifdef __OpenBSD__ + pd.tot_len = h->ip_len; +#else + pd.tot_len = ntohs(h->ip_len); +#endif + + /* handle fragments that didn't get reassembled by normalization */ +#ifdef __OpenBSD__ + if (h->ip_off & (IP_MF | IP_OFFMASK)) +#else + if (h->ip_off & htons(IP_MF | IP_OFFMASK)) +#endif + { + action = pf_test_fragment(&r, dir, ifp, m, h, + &pd, &a, &ruleset); + goto done; + } + + switch (h->ip_p) { + + case IPPROTO_TCP: { + struct tcphdr th; + + pd.hdr.tcp = &th; + if (!pf_pull_hdr(m, off, &th, sizeof(th), + &action, &reason, AF_INET)) { + log = action != PF_PASS; + goto done; + } +#ifdef __OpenBSD__ + if (dir == PF_IN && pf_check_proto_cksum(m, off, + h->ip_len - off, IPPROTO_TCP, AF_INET)) +#else + if (dir == PF_IN && pf_check_proto_cksum(m, off, + ntohs(h->ip_len) - off, IPPROTO_TCP, AF_INET)) +#endif + { + action = PF_DROP; + goto done; + } + pd.p_len = pd.tot_len - off - (th.th_off << 2); + if ((th.th_flags & TH_ACK) && pd.p_len == 0) + pqid = 1; + action = pf_normalize_tcp(dir, ifp, m, 0, off, h, &pd); + if (action == PF_DROP) + break; + action = pf_test_state_tcp(&s, dir, ifp, m, 0, off, h, &pd, + &reason); + if (action == PF_PASS) { + r = s->rule.ptr; + log = s->log; + } else if (s == NULL) + action = pf_test_tcp(&r, &s, dir, ifp, + m, 0, off, h, &pd, &a, &ruleset); + break; + } + + case IPPROTO_UDP: { + struct udphdr uh; + + pd.hdr.udp = &uh; + if (!pf_pull_hdr(m, off, &uh, sizeof(uh), + &action, &reason, AF_INET)) { + log = action != PF_PASS; + goto done; + } +#ifdef __OpenBSD__ + if (dir == PF_IN && uh.uh_sum && pf_check_proto_cksum(m, + off, h->ip_len - off, IPPROTO_UDP, AF_INET)) +#else + if (dir == PF_IN && uh.uh_sum && pf_check_proto_cksum(m, + off, ntohs(h->ip_len) - off, IPPROTO_UDP, AF_INET)) +#endif + { + action = PF_DROP; + goto done; + } + action = pf_test_state_udp(&s, dir, ifp, m, 0, off, h, &pd); + if (action == PF_PASS) { + r = s->rule.ptr; + a = s->anchor.ptr; + log = s->log; + } else if (s == NULL) + action = pf_test_udp(&r, &s, dir, ifp, + m, 0, off, h, &pd, &a, &ruleset); + break; + } + + case IPPROTO_ICMP: { + struct icmp ih; + + pd.hdr.icmp = &ih; + if (!pf_pull_hdr(m, off, &ih, ICMP_MINLEN, + &action, &reason, AF_INET)) { + log = action != PF_PASS; + goto done; + } +#ifdef __OpenBSD__ + if (dir == PF_IN && pf_check_proto_cksum(m, off, + h->ip_len - off, IPPROTO_ICMP, AF_INET)) +#else + if (dir == PF_IN && pf_check_proto_cksum(m, off, + ntohs(h->ip_len) - off, IPPROTO_ICMP, AF_INET)) +#endif + { + action = PF_DROP; + goto done; + } + action = pf_test_state_icmp(&s, dir, ifp, m, 0, off, h, &pd); + if (action == PF_PASS) { + r = s->rule.ptr; + r->packets++; +#ifdef __OpenBSD__ + r->bytes += h->ip_len; +#else + r->bytes += ntohs(h->ip_len); +#endif + a = s->anchor.ptr; + if (a != NULL) { + a->packets++; +#ifdef __OpenBSD__ + a->bytes += h->ip_len; +#else + a->bytes += ntohs(h->ip_len); +#endif + } + log = s->log; + } else if (s == NULL) + action = pf_test_icmp(&r, &s, dir, ifp, + m, 0, off, h, &pd, &a, &ruleset); + break; + } + + default: + action = pf_test_state_other(&s, dir, ifp, &pd); + if (action == PF_PASS) { + r = s->rule.ptr; + a = s->anchor.ptr; + log = s->log; + } else if (s == NULL) + action = pf_test_other(&r, &s, dir, ifp, m, h, + &pd, &a, &ruleset); + break; + } + + if (ifp == status_ifp) { + pf_status.bcounters[0][dir == PF_OUT] += pd.tot_len; + pf_status.pcounters[0][dir == PF_OUT][action != PF_PASS]++; + } + +done: + if (r->src.addr.type == PF_ADDR_TABLE) + pfr_update_stats(r->src.addr.p.tbl, + (s == NULL || s->direction == dir) ? pd.src : pd.dst, pd.af, + pd.tot_len, dir == PF_OUT, r->action == PF_PASS, + r->src.not); + if (r->dst.addr.type == PF_ADDR_TABLE) + pfr_update_stats(r->dst.addr.p.tbl, + (s == NULL || s->direction == dir) ? pd.dst : pd.src, pd.af, + pd.tot_len, dir == PF_OUT, r->action == PF_PASS, + r->dst.not); + + if (action == PF_PASS && h->ip_hl > 5 && + !((s && s->allow_opts) || r->allow_opts)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_SHORT); + log = 1; + DPFPRINTF(PF_DEBUG_MISC, + ("pf: dropping packet with ip options\n")); + } + +#ifdef ALTQ + if (action == PF_PASS && r->qid) { + struct m_tag *mtag; + struct altq_tag *atag; + + mtag = m_tag_get(PACKET_TAG_PF_QID, sizeof(*atag), M_NOWAIT); + if (mtag != NULL) { + atag = (struct altq_tag *)(mtag + 1); + if (pqid || pd.tos == IPTOS_LOWDELAY) + atag->qid = r->pqid; + else + atag->qid = r->qid; + /* add hints for ecn */ + atag->af = AF_INET; + atag->hdr = h; + m_tag_prepend(m, mtag); + } + } +#endif + + if (log) + PFLOG_PACKET(ifp, h, m, AF_INET, dir, reason, r, a, ruleset); + + if (action == PF_SYNPROXY_DROP) { + m_freem(*m0); + *m0 = NULL; + action = PF_PASS; + } else if (r->rt) + /* pf_route can free the mbuf causing *m0 to become NULL */ + pf_route(m0, r, dir, ifp, s); + + return (action); +} +#endif /* INET */ + +#ifdef INET6 +int +pf_test6(int dir, struct ifnet *ifp, struct mbuf **m0) +{ + u_short action, reason = 0, log = 0; + struct mbuf *m = *m0; + struct ip6_hdr *h; + struct pf_rule *a = NULL, *r = &pf_default_rule; + struct pf_state *s = NULL; + struct pf_ruleset *ruleset = NULL; + struct pf_pdesc pd; + int off, terminal = 0; + + if (!pf_status.running || + (m_tag_find(m, PACKET_TAG_PF_GENERATED, NULL) != NULL)) + return (PF_PASS); + +#ifdef DIAGNOSTIC + if ((m->m_flags & M_PKTHDR) == 0) + panic("non-M_PKTHDR is passed to pf_test"); +#endif + + if (m->m_pkthdr.len < (int)sizeof(*h)) { + action = PF_DROP; + REASON_SET(&reason, PFRES_SHORT); + log = 1; + goto done; + } + + /* We do IP header normalization and packet reassembly here */ + if (pf_normalize_ip6(m0, dir, ifp, &reason) != PF_PASS) { + action = PF_DROP; + goto done; + } + m = *m0; + h = mtod(m, struct ip6_hdr *); + + memset(&pd, 0, sizeof(pd)); + pd.src = (struct pf_addr *)&h->ip6_src; + pd.dst = (struct pf_addr *)&h->ip6_dst; + pd.ip_sum = NULL; + pd.af = AF_INET6; + pd.tos = 0; + pd.tot_len = ntohs(h->ip6_plen) + sizeof(struct ip6_hdr); + + off = ((caddr_t)h - m->m_data) + sizeof(struct ip6_hdr); + pd.proto = h->ip6_nxt; + do { + switch (pd.proto) { + case IPPROTO_FRAGMENT: + action = pf_test_fragment(&r, dir, ifp, m, h, + &pd, &a, &ruleset); + if (action == PF_DROP) + REASON_SET(&reason, PFRES_FRAG); + goto done; + case IPPROTO_AH: + case IPPROTO_HOPOPTS: + case IPPROTO_ROUTING: + case IPPROTO_DSTOPTS: { + /* get next header and header length */ + struct ip6_ext opt6; + + if (!pf_pull_hdr(m, off, &opt6, sizeof(opt6), + NULL, NULL, pd.af)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: IPv6 short opt\n")); + action = PF_DROP; + REASON_SET(&reason, PFRES_SHORT); + log = 1; + goto done; + } + if (pd.proto == IPPROTO_AH) + off += (opt6.ip6e_len + 2) * 4; + else + off += (opt6.ip6e_len + 1) * 8; + pd.proto = opt6.ip6e_nxt; + /* goto the next header */ + break; + } + default: + terminal++; + break; + } + } while (!terminal); + + switch (pd.proto) { + + case IPPROTO_TCP: { + struct tcphdr th; + + pd.hdr.tcp = &th; + if (!pf_pull_hdr(m, off, &th, sizeof(th), + &action, &reason, AF_INET6)) { + log = action != PF_PASS; + goto done; + } + if (dir == PF_IN && pf_check_proto_cksum(m, off, + ntohs(h->ip6_plen), IPPROTO_TCP, AF_INET6)) { + action = PF_DROP; + goto done; + } + pd.p_len = pd.tot_len - off - (th.th_off << 2); + action = pf_normalize_tcp(dir, ifp, m, 0, off, h, &pd); + if (action == PF_DROP) + break; + action = pf_test_state_tcp(&s, dir, ifp, m, 0, off, h, &pd, + &reason); + if (action == PF_PASS) { + r = s->rule.ptr; + log = s->log; + } else if (s == NULL) + action = pf_test_tcp(&r, &s, dir, ifp, + m, 0, off, h, &pd, &a, &ruleset); + break; + } + + case IPPROTO_UDP: { + struct udphdr uh; + + pd.hdr.udp = &uh; + if (!pf_pull_hdr(m, off, &uh, sizeof(uh), + &action, &reason, AF_INET6)) { + log = action != PF_PASS; + goto done; + } + if (dir == PF_IN && uh.uh_sum && pf_check_proto_cksum(m, + off, ntohs(h->ip6_plen), IPPROTO_UDP, AF_INET6)) { + action = PF_DROP; + goto done; + } + action = pf_test_state_udp(&s, dir, ifp, m, 0, off, h, &pd); + if (action == PF_PASS) { + r = s->rule.ptr; + log = s->log; + } else if (s == NULL) + action = pf_test_udp(&r, &s, dir, ifp, + m, 0, off, h, &pd, &a, &ruleset); + break; + } + + case IPPROTO_ICMPV6: { + struct icmp6_hdr ih; + + pd.hdr.icmp6 = &ih; + if (!pf_pull_hdr(m, off, &ih, sizeof(ih), + &action, &reason, AF_INET6)) { + log = action != PF_PASS; + goto done; + } + if (dir == PF_IN && pf_check_proto_cksum(m, off, + ntohs(h->ip6_plen), IPPROTO_ICMPV6, AF_INET6)) { + action = PF_DROP; + goto done; + } + action = pf_test_state_icmp(&s, dir, ifp, + m, 0, off, h, &pd); + if (action == PF_PASS) { + r = s->rule.ptr; + r->packets++; + r->bytes += h->ip6_plen; + log = s->log; + } else if (s == NULL) + action = pf_test_icmp(&r, &s, dir, ifp, + m, 0, off, h, &pd, &a, &ruleset); + break; + } + + default: + action = pf_test_other(&r, &s, dir, ifp, m, h, + &pd, &a, &ruleset); + break; + } + + if (ifp == status_ifp) { + pf_status.bcounters[1][dir == PF_OUT] += pd.tot_len; + pf_status.pcounters[1][dir == PF_OUT][action != PF_PASS]++; + } + +done: + if (r->src.addr.type == PF_ADDR_TABLE) + pfr_update_stats(r->src.addr.p.tbl, + (s == NULL || s->direction == dir) ? pd.src : pd.dst, pd.af, + pd.tot_len, dir == PF_OUT, r->action == PF_PASS, + r->src.not); + if (r->dst.addr.type == PF_ADDR_TABLE) + pfr_update_stats(r->dst.addr.p.tbl, + (s == NULL || s->direction == dir) ? pd.dst : pd.src, pd.af, + pd.tot_len, dir == PF_OUT, r->action == PF_PASS, + r->dst.not); + + /* XXX handle IPv6 options, if not allowed. not implemented. */ + +#ifdef ALTQ + if (action == PF_PASS && r->qid) { + struct m_tag *mtag; + struct altq_tag *atag; + + mtag = m_tag_get(PACKET_TAG_PF_QID, sizeof(*atag), M_NOWAIT); + if (mtag != NULL) { + atag = (struct altq_tag *)(mtag + 1); + if (pd.tos == IPTOS_LOWDELAY) + atag->qid = r->pqid; + else + atag->qid = r->qid; + /* add hints for ecn */ + atag->af = AF_INET6; + atag->hdr = h; + m_tag_prepend(m, mtag); + } + } +#endif + + if (log) + PFLOG_PACKET(ifp, h, m, AF_INET6, dir, reason, r, a, ruleset); + + if (action == PF_SYNPROXY_DROP) { + m_freem(*m0); + *m0 = NULL; + action = PF_PASS; + } else if (r->rt) + /* pf_route6 can free the mbuf causing *m0 to become NULL */ + pf_route6(m0, r, dir, ifp, s); + + return (action); +} +#endif /* INET6 */ Index: sys/net/pf_ioctl.c =================================================================== RCS file: pf_ioctl.c diff -N pf_ioctl.c --- /dev/null Mon Jun 30 04:58:52 2003 +++ pf_ioctl.c Mon Jun 30 04:58:38 2003 @@ -0,0 +1,2255 @@ +/* $NetBSD$ */ +/* $OpenBSD: pf_ioctl.c,v 1.70 2003/06/23 02:33:39 cedric Exp $ */ + +/* + * Copyright (c) 2001 Daniel Hartmeier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDERS 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. + * + * Effort sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F30602-01-2-0537. + * + */ + +#ifdef _KERNEL_OPT +#include "opt_inet.h" +#include "opt_altq.h" +#include "opt_pfil_hooks.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __OpenBSD__ +#include +#else +#include +#endif +#include +#include +#ifdef __NetBSD__ +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#ifdef INET6 +#include +#include +#endif /* INET6 */ + +#ifdef ALTQ +#include +#endif + +void pfattach(int); +int pfopen(dev_t, int, int, struct proc *); +int pfclose(dev_t, int, int, struct proc *); +struct pf_pool *pf_get_pool(char *, char *, u_int32_t, + u_int8_t, u_int8_t, u_int8_t, u_int8_t, u_int8_t); +int pf_get_ruleset_number(u_int8_t); +void pf_init_ruleset(struct pf_ruleset *); +void pf_mv_pool(struct pf_palist *, struct pf_palist *); +void pf_empty_pool(struct pf_palist *); +int pfioctl(dev_t, u_long, caddr_t, int, struct proc *); +void pf_tag_purge(void); +#ifdef __NetBSD__ +int pfil4_wrapper(void *, struct mbuf **, + struct ifnet *, int); +int pfil6_wrapper(void *, struct mbuf **, + struct ifnet *, int); +int pf_pfil_attach(void); +int pf_pfil_detach(void); +#endif + + +#ifdef __OpenBSD__ +extern struct timeout pf_expire_to; +#else +extern struct callout pf_expire_to; +#endif + +struct pf_rule pf_default_rule; + +#ifdef __NetBSD__ +const struct cdevsw pf_cdevsw = { + pfopen, pfclose, noread, nowrite, pfioctl, + nostop, notty, nopoll, nommap, nokqfilter, +}; +#endif + +#define TAGID_MAX 50000 +static u_int16_t tagid = 0; +TAILQ_HEAD(pf_tags, pf_tagname) pf_tags = TAILQ_HEAD_INITIALIZER(pf_tags); + +#define DPFPRINTF(n, x) if (pf_status.debug >= (n)) printf x + +void +pfattach(int num) +{ + u_int32_t *timeout = pf_default_rule.timeout; + + pool_init(&pf_tree_pl, sizeof(struct pf_tree_node), 0, 0, 0, "pftrpl", + NULL); + pool_init(&pf_rule_pl, sizeof(struct pf_rule), 0, 0, 0, "pfrulepl", + &pool_allocator_nointr); + pool_init(&pf_addr_pl, sizeof(struct pf_addr_dyn), 0, 0, 0, "pfaddrpl", + &pool_allocator_nointr); + pool_init(&pf_state_pl, sizeof(struct pf_state), 0, 0, 0, "pfstatepl", + NULL); + pool_init(&pf_altq_pl, sizeof(struct pf_altq), 0, 0, 0, "pfaltqpl", + NULL); + pool_init(&pf_pooladdr_pl, sizeof(struct pf_pooladdr), 0, 0, 0, + "pfpooladdrpl", NULL); + pfr_initialize(); + + pool_sethardlimit(&pf_state_pl, pf_pool_limits[PF_LIMIT_STATES].limit, + NULL, 0); + + RB_INIT(&tree_lan_ext); + RB_INIT(&tree_ext_gwy); + TAILQ_INIT(&pf_anchors); + pf_init_ruleset(&pf_main_ruleset); + TAILQ_INIT(&pf_altqs[0]); + TAILQ_INIT(&pf_altqs[1]); + TAILQ_INIT(&pf_pabuf); + pf_altqs_active = &pf_altqs[0]; + pf_altqs_inactive = &pf_altqs[1]; + + /* default rule should never be garbage collected */ + pf_default_rule.entries.tqe_prev = &pf_default_rule.entries.tqe_next; + pf_default_rule.action = PF_PASS; + pf_default_rule.nr = -1; + + /* initialize default timeouts */ + timeout[PFTM_TCP_FIRST_PACKET] = 120; /* First TCP packet */ + timeout[PFTM_TCP_OPENING] = 30; /* No response yet */ + timeout[PFTM_TCP_ESTABLISHED] = 24*60*60; /* Established */ + timeout[PFTM_TCP_CLOSING] = 15 * 60; /* Half closed */ + timeout[PFTM_TCP_FIN_WAIT] = 45; /* Got both FINs */ + timeout[PFTM_TCP_CLOSED] = 90; /* Got a RST */ + timeout[PFTM_UDP_FIRST_PACKET] = 60; /* First UDP packet */ + timeout[PFTM_UDP_SINGLE] = 30; /* Unidirectional */ + timeout[PFTM_UDP_MULTIPLE] = 60; /* Bidirectional */ + timeout[PFTM_ICMP_FIRST_PACKET] = 20; /* First ICMP packet */ + timeout[PFTM_ICMP_ERROR_REPLY] = 10; /* Got error response */ + timeout[PFTM_OTHER_FIRST_PACKET] = 60; /* First packet */ + timeout[PFTM_OTHER_SINGLE] = 30; /* Unidirectional */ + timeout[PFTM_OTHER_MULTIPLE] = 60; /* Bidirectional */ + timeout[PFTM_FRAG] = 30; /* Fragment expire */ + timeout[PFTM_INTERVAL] = 10; /* Expire interval */ + +#ifdef __OpenBSD__ + timeout_set(&pf_expire_to, pf_purge_timeout, &pf_expire_to); + timeout_add(&pf_expire_to, timeout[PFTM_INTERVAL] * hz); +#else + callout_init(&pf_expire_to); + callout_reset(&pf_expire_to, timeout[PFTM_INTERVAL] * hz, + pf_purge_timeout, &pf_expire_to); +#endif + + pf_normalize_init(); + pf_status.debug = PF_DEBUG_URGENT; +} + +int +pfopen(dev_t dev, int flags, int fmt, struct proc *p) +{ + if (minor(dev) >= 1) + return (ENXIO); + return (0); +} + +int +pfclose(dev_t dev, int flags, int fmt, struct proc *p) +{ + if (minor(dev) >= 1) + return (ENXIO); + return (0); +} + +struct pf_pool * +pf_get_pool(char *anchorname, char *rulesetname, u_int32_t ticket, + u_int8_t rule_action, u_int8_t rule_number, u_int8_t r_last, + u_int8_t active, u_int8_t check_ticket) +{ + struct pf_ruleset *ruleset; + struct pf_rule *rule; + int rs_num; + + ruleset = pf_find_ruleset(anchorname, rulesetname); + if (ruleset == NULL) + return (NULL); + rs_num = pf_get_ruleset_number(rule_action); + if (rs_num >= PF_RULESET_MAX) + return (NULL); + if (active) { + if (check_ticket && ticket != + ruleset->rules[rs_num].active.ticket) + return (NULL); + if (r_last) + rule = TAILQ_LAST(ruleset->rules[rs_num].active.ptr, + pf_rulequeue); + else + rule = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr); + } else { + if (check_ticket && ticket != + ruleset->rules[rs_num].inactive.ticket) + return (NULL); + if (r_last) + rule = TAILQ_LAST(ruleset->rules[rs_num].inactive.ptr, + pf_rulequeue); + else + rule = TAILQ_FIRST(ruleset->rules[rs_num].inactive.ptr); + } + if (!r_last) { + while ((rule != NULL) && (rule->nr != rule_number)) + rule = TAILQ_NEXT(rule, entries); + } + if (rule == NULL) + return (NULL); + + return (&rule->rpool); +} + +int +pf_get_ruleset_number(u_int8_t action) +{ + switch (action) { + case PF_SCRUB: + return (PF_RULESET_SCRUB); + break; + case PF_PASS: + case PF_DROP: + return (PF_RULESET_FILTER); + break; + case PF_NAT: + case PF_NONAT: + return (PF_RULESET_NAT); + break; + case PF_BINAT: + case PF_NOBINAT: + return (PF_RULESET_BINAT); + break; + case PF_RDR: + case PF_NORDR: + return (PF_RULESET_RDR); + break; + default: + return (PF_RULESET_MAX); + break; + } +} + +void +pf_init_ruleset(struct pf_ruleset *ruleset) +{ + int i; + + memset(ruleset, 0, sizeof(struct pf_ruleset)); + for (i = 0; i < PF_RULESET_MAX; i++) { + TAILQ_INIT(&ruleset->rules[i].queues[0]); + TAILQ_INIT(&ruleset->rules[i].queues[1]); + ruleset->rules[i].active.ptr = &ruleset->rules[i].queues[0]; + ruleset->rules[i].inactive.ptr = &ruleset->rules[i].queues[1]; + } +} + +struct pf_anchor * +pf_find_anchor(const char *anchorname) +{ + struct pf_anchor *anchor; + int n = -1; + + anchor = TAILQ_FIRST(&pf_anchors); + while (anchor != NULL && (n = strcmp(anchor->name, anchorname)) < 0) + anchor = TAILQ_NEXT(anchor, entries); + if (n == 0) + return (anchor); + else + return (NULL); +} + +struct pf_ruleset * +pf_find_ruleset(char *anchorname, char *rulesetname) +{ + struct pf_anchor *anchor; + struct pf_ruleset *ruleset; + + if (!anchorname[0] && !rulesetname[0]) + return (&pf_main_ruleset); + if (!anchorname[0] || !rulesetname[0]) + return (NULL); + anchorname[PF_ANCHOR_NAME_SIZE-1] = 0; + rulesetname[PF_RULESET_NAME_SIZE-1] = 0; + anchor = pf_find_anchor(anchorname); + if (anchor == NULL) + return (NULL); + ruleset = TAILQ_FIRST(&anchor->rulesets); + while (ruleset != NULL && strcmp(ruleset->name, rulesetname) < 0) + ruleset = TAILQ_NEXT(ruleset, entries); + if (ruleset != NULL && !strcmp(ruleset->name, rulesetname)) + return (ruleset); + else + return (NULL); +} + +struct pf_ruleset * +pf_find_or_create_ruleset(char *anchorname, char *rulesetname) +{ + struct pf_anchor *anchor, *a; + struct pf_ruleset *ruleset, *r; + + if (!anchorname[0] && !rulesetname[0]) + return (&pf_main_ruleset); + if (!anchorname[0] || !rulesetname[0]) + return (NULL); + anchorname[PF_ANCHOR_NAME_SIZE-1] = 0; + rulesetname[PF_RULESET_NAME_SIZE-1] = 0; + a = TAILQ_FIRST(&pf_anchors); + while (a != NULL && strcmp(a->name, anchorname) < 0) + a = TAILQ_NEXT(a, entries); + if (a != NULL && !strcmp(a->name, anchorname)) + anchor = a; + else { + anchor = (struct pf_anchor *)malloc(sizeof(struct pf_anchor), + M_TEMP, M_NOWAIT); + if (anchor == NULL) + return (NULL); + memset(anchor, 0, sizeof(struct pf_anchor)); + bcopy(anchorname, anchor->name, sizeof(anchor->name)); + TAILQ_INIT(&anchor->rulesets); + if (a != NULL) + TAILQ_INSERT_BEFORE(a, anchor, entries); + else + TAILQ_INSERT_TAIL(&pf_anchors, anchor, entries); + } + r = TAILQ_FIRST(&anchor->rulesets); + while (r != NULL && strcmp(r->name, rulesetname) < 0) + r = TAILQ_NEXT(r, entries); + if (r != NULL && !strcmp(r->name, rulesetname)) + return (r); + ruleset = (struct pf_ruleset *)malloc(sizeof(struct pf_ruleset), + M_TEMP, M_NOWAIT); + if (ruleset != NULL) { + pf_init_ruleset(ruleset); + bcopy(rulesetname, ruleset->name, sizeof(ruleset->name)); + ruleset->anchor = anchor; + if (r != NULL) + TAILQ_INSERT_BEFORE(r, ruleset, entries); + else + TAILQ_INSERT_TAIL(&anchor->rulesets, ruleset, entries); + } + return (ruleset); +} + +void +pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset) +{ + struct pf_anchor *anchor; + int i; + + if (ruleset == NULL || ruleset->anchor == NULL || ruleset->tables > 0) + return; + for (i = 0; i < PF_RULESET_MAX; ++i) + if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) || + !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr)) + return; + + anchor = ruleset->anchor; + TAILQ_REMOVE(&anchor->rulesets, ruleset, entries); + free(ruleset, M_TEMP); + + if (TAILQ_EMPTY(&anchor->rulesets)) { + TAILQ_REMOVE(&pf_anchors, anchor, entries); + free(anchor, M_TEMP); + } +} + +void +pf_mv_pool(struct pf_palist *poola, struct pf_palist *poolb) +{ + struct pf_pooladdr *mv_pool_pa; + + while ((mv_pool_pa = TAILQ_FIRST(poola)) != NULL) { + TAILQ_REMOVE(poola, mv_pool_pa, entries); + TAILQ_INSERT_TAIL(poolb, mv_pool_pa, entries); + } +} + +void +pf_empty_pool(struct pf_palist *poola) +{ + struct pf_pooladdr *empty_pool_pa; + + while ((empty_pool_pa = TAILQ_FIRST(poola)) != NULL) { + pf_dynaddr_remove(&empty_pool_pa->addr.addr); + TAILQ_REMOVE(poola, empty_pool_pa, entries); + pool_put(&pf_pooladdr_pl, empty_pool_pa); + } +} + +void +pf_rm_rule(struct pf_rulequeue *rulequeue, struct pf_rule *rule) +{ + if (rulequeue != NULL) { + if (rule->states <= 0) { + /* + * XXX - we need to remove the table *before* detaching + * the rule to make sure the table code does not delete + * the anchor under our feet. + */ + pf_tbladdr_remove(&rule->src.addr); + pf_tbladdr_remove(&rule->dst.addr); + } + TAILQ_REMOVE(rulequeue, rule, entries); + rule->entries.tqe_prev = NULL; + rule->nr = -1; + } + pf_tag_unref(rule->tag); + pf_tag_unref(rule->match_tag); + if (rule->states > 0 || rule->entries.tqe_prev != NULL) + return; + pf_dynaddr_remove(&rule->src.addr); + pf_dynaddr_remove(&rule->dst.addr); + if (rulequeue == NULL) { + pf_tbladdr_remove(&rule->src.addr); + pf_tbladdr_remove(&rule->dst.addr); + } + pf_empty_pool(&rule->rpool.list); + pool_put(&pf_rule_pl, rule); +} + +u_int16_t +pf_tagname2tag(char *tagname) +{ + struct pf_tagname *tag, *p; + int wrapped = 0; + + TAILQ_FOREACH(tag, &pf_tags, entries) + if (strcmp(tagname, tag->name) == 0) { + tag->ref++; + return (tag->tag); + } + /* new entry */ + if (++tagid > TAGID_MAX) /* > 50000 reserved for special use */ + tagid = wrapped = 1; + for (p = TAILQ_FIRST(&pf_tags); p != NULL; p = TAILQ_NEXT(p, entries)) + if (p->tag == tagid) { + if (++tagid > TAGID_MAX) { + if (wrapped) + return (0); + else + tagid = wrapped = 1; + } + p = TAILQ_FIRST(&pf_tags); + } + + tag = (struct pf_tagname *)malloc(sizeof(struct pf_tagname), + M_TEMP, M_NOWAIT); + if (tag == NULL) + return (0); + bzero(tag, sizeof(struct pf_tagname)); + strlcpy(tag->name, tagname, sizeof(tag->name)); + tag->tag = tagid; + tag->ref++; + TAILQ_INSERT_TAIL(&pf_tags, tag, entries); + return (tag->tag); +} + +void +pf_tag2tagname(u_int16_t tagid, char *p) +{ + struct pf_tagname *tag; + + TAILQ_FOREACH(tag, &pf_tags, entries) + if (tag->tag == tagid) { + strlcpy(p, tag->name, PF_TAG_NAME_SIZE); + return; + } +} + +void +pf_tag_unref(u_int16_t tag) +{ + struct pf_tagname *p; + + if (tag > 0) + TAILQ_FOREACH(p, &pf_tags, entries) + if (tag == p->tag) { + p->ref--; + return; + } +} + +void +pf_tag_purge(void) +{ + struct pf_tagname *p, *next; + + for (p = TAILQ_LAST(&pf_tags, pf_tags); p != NULL; p = next) { + next = TAILQ_PREV(p, pf_tags, entries); + if (p->ref == 0) { + if (p->tag == tagid) + tagid--; + TAILQ_REMOVE(&pf_tags, p, entries); + free(p, M_TEMP); + } + } +} + +int +pfioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) +{ + struct pf_pooladdr *pa = NULL; + struct pf_pool *pool = NULL; + int s; + int error = 0; + + /* XXX keep in sync with switch() below */ + if (securelevel > 1) + switch (cmd) { + case DIOCGETRULES: + case DIOCGETRULE: + case DIOCGETADDRS: + case DIOCGETADDR: + case DIOCGETSTATE: + case DIOCSETSTATUSIF: + case DIOCGETSTATUS: + case DIOCCLRSTATUS: + case DIOCNATLOOK: + case DIOCSETDEBUG: + case DIOCGETSTATES: + case DIOCGETTIMEOUT: + case DIOCCLRRULECTRS: + case DIOCGETLIMIT: + case DIOCGETALTQS: + case DIOCGETALTQ: + case DIOCGETQSTATS: + case DIOCGETANCHORS: + case DIOCGETANCHOR: + case DIOCGETRULESETS: + case DIOCGETRULESET: + case DIOCRGETTABLES: + case DIOCRGETTSTATS: + case DIOCRCLRTSTATS: + case DIOCRCLRADDRS: + case DIOCRADDADDRS: + case DIOCRDELADDRS: + case DIOCRSETADDRS: + case DIOCRGETADDRS: + case DIOCRGETASTATS: + case DIOCRCLRASTATS: + case DIOCRTSTADDRS: + break; + default: + return (EPERM); + } + + if (!(flags & FWRITE)) + switch (cmd) { + case DIOCGETRULES: + case DIOCGETRULE: + case DIOCGETADDRS: + case DIOCGETADDR: + case DIOCGETSTATE: + case DIOCGETSTATUS: + case DIOCGETSTATES: + case DIOCGETTIMEOUT: + case DIOCGETLIMIT: + case DIOCGETALTQS: + case DIOCGETALTQ: + case DIOCGETQSTATS: + case DIOCGETANCHORS: + case DIOCGETANCHOR: + case DIOCGETRULESETS: + case DIOCGETRULESET: + case DIOCRGETTABLES: + case DIOCRGETTSTATS: + case DIOCRGETADDRS: + case DIOCRGETASTATS: + case DIOCRTSTADDRS: + break; + default: + return (EACCES); + } + + switch (cmd) { + + case DIOCSTART: + if (pf_status.running) + error = EEXIST; + else { + u_int32_t states = pf_status.states; +#ifdef __NetBSD__ + error = pf_pfil_attach(); + if (error) + break; +#endif + bzero(&pf_status, sizeof(struct pf_status)); + pf_status.running = 1; + pf_status.states = states; + pf_status.since = time.tv_sec; + if (status_ifp != NULL) + strlcpy(pf_status.ifname, + status_ifp->if_xname, IFNAMSIZ); + DPFPRINTF(PF_DEBUG_MISC, ("pf: started\n")); + } + break; + + case DIOCSTOP: + if (!pf_status.running) + error = ENOENT; + else { +#ifdef __NetBSD__ + error = pf_pfil_detach(); + if (error) + break; +#endif + pf_status.running = 0; + DPFPRINTF(PF_DEBUG_MISC, ("pf: stopped\n")); + } + break; + + case DIOCBEGINRULES: { + struct pfioc_rule *pr = (struct pfioc_rule *)addr; + struct pf_ruleset *ruleset; + struct pf_rule *rule; + int rs_num; + + ruleset = pf_find_or_create_ruleset(pr->anchor, pr->ruleset); + if (ruleset == NULL) { + error = EINVAL; + break; + } + rs_num = pf_get_ruleset_number(pr->rule.action); + if (rs_num >= PF_RULESET_MAX) { + error = EINVAL; + break; + } + while ((rule = + TAILQ_FIRST(ruleset->rules[rs_num].inactive.ptr)) != NULL) + pf_rm_rule(ruleset->rules[rs_num].inactive.ptr, rule); + pr->ticket = ++ruleset->rules[rs_num].inactive.ticket; + break; + } + + case DIOCADDRULE: { + struct pfioc_rule *pr = (struct pfioc_rule *)addr; + struct pf_ruleset *ruleset; + struct pf_rule *rule, *tail; + int rs_num; + + ruleset = pf_find_ruleset(pr->anchor, pr->ruleset); + if (ruleset == NULL) { + error = EINVAL; + break; + } + rs_num = pf_get_ruleset_number(pr->rule.action); + if (rs_num >= PF_RULESET_MAX) { + error = EINVAL; + break; + } + if (pr->rule.anchorname[0] && ruleset != &pf_main_ruleset) { + error = EINVAL; + break; + } + if (pr->rule.return_icmp >> 8 > ICMP_MAXTYPE) { + error = EINVAL; + break; + } + if (pr->ticket != ruleset->rules[rs_num].inactive.ticket) { + error = EBUSY; + break; + } + if (pr->pool_ticket != ticket_pabuf) { + error = EBUSY; + break; + } + rule = pool_get(&pf_rule_pl, PR_NOWAIT); + if (rule == NULL) { + error = ENOMEM; + break; + } + bcopy(&pr->rule, rule, sizeof(struct pf_rule)); + rule->anchor = NULL; + rule->ifp = NULL; + TAILQ_INIT(&rule->rpool.list); + /* initialize refcounting */ + rule->states = 0; + rule->entries.tqe_prev = NULL; +#ifndef INET + if (rule->af == AF_INET) { + pool_put(&pf_rule_pl, rule); + error = EAFNOSUPPORT; + break; + } +#endif /* INET */ +#ifndef INET6 + if (rule->af == AF_INET6) { + pool_put(&pf_rule_pl, rule); + error = EAFNOSUPPORT; + break; + } +#endif /* INET6 */ + tail = TAILQ_LAST(ruleset->rules[rs_num].inactive.ptr, + pf_rulequeue); + if (tail) + rule->nr = tail->nr + 1; + else + rule->nr = 0; + if (rule->ifname[0]) { + rule->ifp = ifunit(rule->ifname); + if (rule->ifp == NULL) { + pool_put(&pf_rule_pl, rule); + error = EINVAL; + break; + } + } + + if (rule->tagname[0]) + if ((rule->tag = pf_tagname2tag(rule->tagname)) == 0) + error = EBUSY; + if (rule->match_tagname[0]) + if ((rule->match_tag = + pf_tagname2tag(rule->match_tagname)) == 0) + error = EBUSY; + if (rule->rt && !rule->direction) + error = EINVAL; + if (pf_dynaddr_setup(&rule->src.addr, rule->af)) + error = EINVAL; + if (pf_dynaddr_setup(&rule->dst.addr, rule->af)) + error = EINVAL; + if (pf_tbladdr_setup(ruleset, &rule->src.addr)) + error = EINVAL; + if (pf_tbladdr_setup(ruleset, &rule->dst.addr)) + error = EINVAL; + + pf_mv_pool(&pf_pabuf, &rule->rpool.list); + if (((((rule->action == PF_NAT) || (rule->action == PF_RDR) || + (rule->action == PF_BINAT)) && !rule->anchorname[0]) || + (rule->rt > PF_FASTROUTE)) && + (TAILQ_FIRST(&rule->rpool.list) == NULL)) + error = EINVAL; + + if (error) { + pf_rm_rule(NULL, rule); + break; + } + rule->rpool.cur = TAILQ_FIRST(&rule->rpool.list); + rule->evaluations = rule->packets = rule->bytes = 0; + TAILQ_INSERT_TAIL(ruleset->rules[rs_num].inactive.ptr, + rule, entries); + break; + } + + case DIOCCOMMITRULES: { + struct pfioc_rule *pr = (struct pfioc_rule *)addr; + struct pf_ruleset *ruleset; + struct pf_rulequeue *old_rules; + struct pf_rule *rule; + int rs_num; + + ruleset = pf_find_ruleset(pr->anchor, pr->ruleset); + if (ruleset == NULL) { + error = EINVAL; + break; + } + rs_num = pf_get_ruleset_number(pr->rule.action); + if (rs_num >= PF_RULESET_MAX) { + error = EINVAL; + break; + } + if (pr->ticket != ruleset->rules[rs_num].inactive.ticket) { + error = EBUSY; + break; + } + +#ifdef ALTQ + /* set queue IDs */ + if (rs_num == PF_RULESET_FILTER) + pf_rule_set_qid(ruleset->rules[rs_num].inactive.ptr); +#endif + + /* Swap rules, keep the old. */ + s = splsoftnet(); + old_rules = ruleset->rules[rs_num].active.ptr; + ruleset->rules[rs_num].active.ptr = + ruleset->rules[rs_num].inactive.ptr; + ruleset->rules[rs_num].inactive.ptr = old_rules; + ruleset->rules[rs_num].active.ticket = + ruleset->rules[rs_num].inactive.ticket; + pf_calc_skip_steps(ruleset->rules[rs_num].active.ptr); + + /* Purge the old rule list. */ + while ((rule = TAILQ_FIRST(old_rules)) != NULL) + pf_rm_rule(old_rules, rule); + pf_remove_if_empty_ruleset(ruleset); + pf_update_anchor_rules(); + pf_tag_purge(); + splx(s); + break; + } + + case DIOCGETRULES: { + struct pfioc_rule *pr = (struct pfioc_rule *)addr; + struct pf_ruleset *ruleset; + struct pf_rule *tail; + int rs_num; + + ruleset = pf_find_ruleset(pr->anchor, pr->ruleset); + if (ruleset == NULL) { + error = EINVAL; + break; + } + rs_num = pf_get_ruleset_number(pr->rule.action); + if (rs_num >= PF_RULESET_MAX) { + error = EINVAL; + break; + } + s = splsoftnet(); + tail = TAILQ_LAST(ruleset->rules[rs_num].active.ptr, + pf_rulequeue); + if (tail) + pr->nr = tail->nr + 1; + else + pr->nr = 0; + pr->ticket = ruleset->rules[rs_num].active.ticket; + splx(s); + break; + } + + case DIOCGETRULE: { + struct pfioc_rule *pr = (struct pfioc_rule *)addr; + struct pf_ruleset *ruleset; + struct pf_rule *rule; + int rs_num, i; + + ruleset = pf_find_ruleset(pr->anchor, pr->ruleset); + if (ruleset == NULL) { + error = EINVAL; + break; + } + rs_num = pf_get_ruleset_number(pr->rule.action); + if (rs_num >= PF_RULESET_MAX) { + error = EINVAL; + break; + } + if (pr->ticket != ruleset->rules[rs_num].active.ticket) { + error = EBUSY; + break; + } + s = splsoftnet(); + rule = TAILQ_FIRST(ruleset->rules[rs_num].active.ptr); + while ((rule != NULL) && (rule->nr != pr->nr)) + rule = TAILQ_NEXT(rule, entries); + if (rule == NULL) { + error = EBUSY; + splx(s); + break; + } + bcopy(rule, &pr->rule, sizeof(struct pf_rule)); + pf_dynaddr_copyout(&pr->rule.src.addr); + pf_dynaddr_copyout(&pr->rule.dst.addr); + pf_tbladdr_copyout(&pr->rule.src.addr); + pf_tbladdr_copyout(&pr->rule.dst.addr); + for (i = 0; i < PF_SKIP_COUNT; ++i) + if (rule->skip[i].ptr == NULL) + pr->rule.skip[i].nr = -1; + else + pr->rule.skip[i].nr = + rule->skip[i].ptr->nr; + splx(s); + break; + } + + case DIOCCHANGERULE: { + struct pfioc_rule *pcr = (struct pfioc_rule *)addr; + struct pf_ruleset *ruleset; + struct pf_rule *oldrule = NULL, *newrule = NULL; + u_int32_t nr = 0; + int rs_num; + + if (!(pcr->action == PF_CHANGE_REMOVE || + pcr->action == PF_CHANGE_GET_TICKET) && + pcr->pool_ticket != ticket_pabuf) { + error = EBUSY; + break; + } + + if (pcr->action < PF_CHANGE_ADD_HEAD || + pcr->action > PF_CHANGE_GET_TICKET) { + error = EINVAL; + break; + } + ruleset = pf_find_ruleset(pcr->anchor, pcr->ruleset); + if (ruleset == NULL) { + error = EINVAL; + break; + } + rs_num = pf_get_ruleset_number(pcr->rule.action); + if (rs_num >= PF_RULESET_MAX) { + error = EINVAL; + break; + } + + if (pcr->action == PF_CHANGE_GET_TICKET) { + pcr->ticket = ++ruleset->rules[rs_num].active.ticket; + break; + } else { + if (pcr->ticket != + ruleset->rules[rs_num].active.ticket) { + error = EINVAL; + break; + } + if (pcr->rule.return_icmp >> 8 > ICMP_MAXTYPE) { + error = EINVAL; + break; + } + } + + if (pcr->action != PF_CHANGE_REMOVE) { + newrule = pool_get(&pf_rule_pl, PR_NOWAIT); + if (newrule == NULL) { + error = ENOMEM; + break; + } + bcopy(&pcr->rule, newrule, sizeof(struct pf_rule)); + TAILQ_INIT(&newrule->rpool.list); + /* initialize refcounting */ + newrule->states = 0; + newrule->entries.tqe_prev = NULL; +#ifndef INET + if (newrule->af == AF_INET) { + pool_put(&pf_rule_pl, newrule); + error = EAFNOSUPPORT; + break; + } +#endif /* INET */ +#ifndef INET6 + if (newrule->af == AF_INET6) { + pool_put(&pf_rule_pl, newrule); + error = EAFNOSUPPORT; + break; + } +#endif /* INET6 */ + if (newrule->ifname[0]) { + newrule->ifp = ifunit(newrule->ifname); + if (newrule->ifp == NULL) { + pool_put(&pf_rule_pl, newrule); + error = EINVAL; + break; + } + } else + newrule->ifp = NULL; + +#ifdef ALTQ + /* set queue IDs */ + if (newrule->qname[0] != 0) { + newrule->qid = pf_qname_to_qid(newrule->qname); + if (newrule->pqname[0] != 0) + newrule->pqid = + pf_qname_to_qid(newrule->pqname); + else + newrule->pqid = newrule->qid; + } +#endif + if (newrule->rt && !newrule->direction) + error = EINVAL; + if (pf_dynaddr_setup(&newrule->src.addr, newrule->af)) + error = EINVAL; + if (pf_dynaddr_setup(&newrule->dst.addr, newrule->af)) + error = EINVAL; + if (pf_tbladdr_setup(ruleset, &newrule->src.addr)) + error = EINVAL; + if (pf_tbladdr_setup(ruleset, &newrule->dst.addr)) + error = EINVAL; + + pf_mv_pool(&pf_pabuf, &newrule->rpool.list); + if (((((newrule->action == PF_NAT) || + (newrule->action == PF_RDR) || + (newrule->action == PF_BINAT) || + (newrule->rt > PF_FASTROUTE)) && + !newrule->anchorname[0])) && + (TAILQ_FIRST(&newrule->rpool.list) == NULL)) + error = EINVAL; + + if (error) { + pf_rm_rule(NULL, newrule); + break; + } + newrule->rpool.cur = TAILQ_FIRST(&newrule->rpool.list); + newrule->evaluations = newrule->packets = 0; + newrule->bytes = 0; + } + pf_empty_pool(&pf_pabuf); + + s = splsoftnet(); + + if (pcr->action == PF_CHANGE_ADD_HEAD) + oldrule = TAILQ_FIRST( + ruleset->rules[rs_num].active.ptr); + else if (pcr->action == PF_CHANGE_ADD_TAIL) + oldrule = TAILQ_LAST( + ruleset->rules[rs_num].active.ptr, pf_rulequeue); + else { + oldrule = TAILQ_FIRST( + ruleset->rules[rs_num].active.ptr); + while ((oldrule != NULL) && (oldrule->nr != pcr->nr)) + oldrule = TAILQ_NEXT(oldrule, entries); + if (oldrule == NULL) { + pf_rm_rule(NULL, newrule); + error = EINVAL; + splx(s); + break; + } + } + + if (pcr->action == PF_CHANGE_REMOVE) + pf_rm_rule(ruleset->rules[rs_num].active.ptr, oldrule); + else { + if (oldrule == NULL) + TAILQ_INSERT_TAIL( + ruleset->rules[rs_num].active.ptr, + newrule, entries); + else if (pcr->action == PF_CHANGE_ADD_HEAD || + pcr->action == PF_CHANGE_ADD_BEFORE) + TAILQ_INSERT_BEFORE(oldrule, newrule, entries); + else + TAILQ_INSERT_AFTER( + ruleset->rules[rs_num].active.ptr, + oldrule, newrule, entries); + } + + nr = 0; + TAILQ_FOREACH(oldrule, + ruleset->rules[rs_num].active.ptr, entries) + oldrule->nr = nr++; + + pf_calc_skip_steps(ruleset->rules[rs_num].active.ptr); + pf_remove_if_empty_ruleset(ruleset); + pf_update_anchor_rules(); + + ruleset->rules[rs_num].active.ticket++; + splx(s); + break; + } + + case DIOCCLRSTATES: { + struct pf_tree_node *n; + + s = splsoftnet(); + RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) + n->state->timeout = PFTM_PURGE; + pf_purge_expired_states(); + pf_status.states = 0; + splx(s); + break; + } + + case DIOCKILLSTATES: { + struct pf_tree_node *n; + struct pf_state *st; + struct pfioc_state_kill *psk = (struct pfioc_state_kill *)addr; + int killed = 0; + + s = splsoftnet(); + RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) { + st = n->state; + if ((!psk->psk_af || st->af == psk->psk_af) && + (!psk->psk_proto || psk->psk_proto == st->proto) && + PF_MATCHA(psk->psk_src.not, + &psk->psk_src.addr.v.a.addr, + &psk->psk_src.addr.v.a.mask, &st->lan.addr, + st->af) && + PF_MATCHA(psk->psk_dst.not, + &psk->psk_dst.addr.v.a.addr, + &psk->psk_dst.addr.v.a.mask, &st->ext.addr, + st->af) && + (psk->psk_src.port_op == 0 || + pf_match_port(psk->psk_src.port_op, + psk->psk_src.port[0], psk->psk_src.port[1], + st->lan.port)) && + (psk->psk_dst.port_op == 0 || + pf_match_port(psk->psk_dst.port_op, + psk->psk_dst.port[0], psk->psk_dst.port[1], + st->ext.port))) { + st->timeout = PFTM_PURGE; + killed++; + } + } + pf_purge_expired_states(); + splx(s); + psk->psk_af = killed; + break; + } + + case DIOCADDSTATE: { + struct pfioc_state *ps = (struct pfioc_state *)addr; + struct pf_state *state; + + if (ps->state.timeout >= PFTM_MAX && + ps->state.timeout != PFTM_UNTIL_PACKET) { + error = EINVAL; + break; + } + state = pool_get(&pf_state_pl, PR_NOWAIT); + if (state == NULL) { + error = ENOMEM; + break; + } + s = splsoftnet(); + bcopy(&ps->state, state, sizeof(struct pf_state)); + state->rule.ptr = NULL; + state->nat_rule.ptr = NULL; + state->anchor.ptr = NULL; + state->rt_ifp = NULL; + state->creation = time.tv_sec; + state->packets[0] = state->packets[1] = 0; + state->bytes[0] = state->bytes[1] = 0; + if (pf_insert_state(state)) { + pool_put(&pf_state_pl, state); + error = ENOMEM; + } + splx(s); + break; + } + + case DIOCGETSTATE: { + struct pfioc_state *ps = (struct pfioc_state *)addr; + struct pf_tree_node *n; + u_int32_t nr; + + nr = 0; + s = splsoftnet(); + RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) { + if (nr >= ps->nr) + break; + nr++; + } + if (n == NULL) { + error = EBUSY; + splx(s); + break; + } + bcopy(n->state, &ps->state, sizeof(struct pf_state)); + ps->state.rule.nr = n->state->rule.ptr->nr; + ps->state.nat_rule.nr = (n->state->nat_rule.ptr == NULL) ? + -1 : n->state->nat_rule.ptr->nr; + ps->state.anchor.nr = (n->state->anchor.ptr == NULL) ? + -1 : n->state->anchor.ptr->nr; + splx(s); + ps->state.expire = pf_state_expires(n->state); + if (ps->state.expire > time.tv_sec) + ps->state.expire -= time.tv_sec; + else + ps->state.expire = 0; + break; + } + + case DIOCGETSTATES: { + struct pfioc_states *ps = (struct pfioc_states *)addr; + struct pf_tree_node *n; + struct pf_state *p, pstore; + u_int32_t nr = 0; + int space = ps->ps_len; + + if (space == 0) { + s = splsoftnet(); + RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) + nr++; + splx(s); + ps->ps_len = sizeof(struct pf_state) * nr; + return (0); + } + + s = splsoftnet(); + p = ps->ps_states; + RB_FOREACH(n, pf_state_tree, &tree_ext_gwy) { + int secs = time.tv_sec; + + if ((nr + 1) * sizeof(*p) > (unsigned)ps->ps_len) + break; + + bcopy(n->state, &pstore, sizeof(pstore)); + pstore.rule.nr = n->state->rule.ptr->nr; + pstore.nat_rule.nr = (n->state->nat_rule.ptr == NULL) ? + -1 : n->state->nat_rule.ptr->nr; + pstore.anchor.nr = (n->state->anchor.ptr == NULL) ? + -1 : n->state->anchor.ptr->nr; + pstore.creation = secs - pstore.creation; + pstore.expire = pf_state_expires(n->state); + if (pstore.expire > secs) + pstore.expire -= secs; + else + pstore.expire = 0; + error = copyout(&pstore, p, sizeof(*p)); + if (error) { + splx(s); + goto fail; + } + p++; + nr++; + } + ps->ps_len = sizeof(struct pf_state) * nr; + splx(s); + break; + } + + case DIOCSETSTATUSIF: { + struct pfioc_if *pi = (struct pfioc_if *)addr; + struct ifnet *ifp; + + if (pi->ifname[0] == 0) { + status_ifp = NULL; + bzero(pf_status.ifname, IFNAMSIZ); + } else + if ((ifp = ifunit(pi->ifname)) == NULL) + error = EINVAL; + else { + status_ifp = ifp; + strlcpy(pf_status.ifname, ifp->if_xname, + IFNAMSIZ); + } + break; + } + + case DIOCGETSTATUS: { + struct pf_status *s = (struct pf_status *)addr; + bcopy(&pf_status, s, sizeof(struct pf_status)); + break; + } + + case DIOCCLRSTATUS: { + u_int32_t running = pf_status.running; + u_int32_t states = pf_status.states; + u_int32_t since = pf_status.since; + u_int32_t debug = pf_status.debug; + + bzero(&pf_status, sizeof(struct pf_status)); + pf_status.running = running; + pf_status.states = states; + pf_status.since = since; + pf_status.debug = debug; + if (status_ifp != NULL) + strlcpy(pf_status.ifname, + status_ifp->if_xname, IFNAMSIZ); + break; + } + + case DIOCNATLOOK: { + struct pfioc_natlook *pnl = (struct pfioc_natlook *)addr; + struct pf_state *st; + struct pf_tree_node key; + int direction = pnl->direction; + + key.af = pnl->af; + key.proto = pnl->proto; + + /* + * userland gives us source and dest of connetion, reverse + * the lookup so we ask for what happens with the return + * traffic, enabling us to find it in the state tree. + */ + PF_ACPY(&key.addr[1], &pnl->saddr, pnl->af); + key.port[1] = pnl->sport; + PF_ACPY(&key.addr[0], &pnl->daddr, pnl->af); + key.port[0] = pnl->dport; + + if (!pnl->proto || + PF_AZERO(&pnl->saddr, pnl->af) || + PF_AZERO(&pnl->daddr, pnl->af) || + !pnl->dport || !pnl->sport) + error = EINVAL; + else { + s = splsoftnet(); + if (direction == PF_IN) + st = pf_find_state(&tree_ext_gwy, &key); + else + st = pf_find_state(&tree_lan_ext, &key); + if (st != NULL) { + if (direction == PF_IN) { + PF_ACPY(&pnl->rsaddr, &st->lan.addr, + st->af); + pnl->rsport = st->lan.port; + PF_ACPY(&pnl->rdaddr, &pnl->daddr, + pnl->af); + pnl->rdport = pnl->dport; + } else { + PF_ACPY(&pnl->rdaddr, &st->gwy.addr, + st->af); + pnl->rdport = st->gwy.port; + PF_ACPY(&pnl->rsaddr, &pnl->saddr, + pnl->af); + pnl->rsport = pnl->sport; + } + } else + error = ENOENT; + splx(s); + } + break; + } + + case DIOCSETTIMEOUT: { + struct pfioc_tm *pt = (struct pfioc_tm *)addr; + int old; + + if (pt->timeout < 0 || pt->timeout >= PFTM_MAX || + pt->seconds < 0) { + error = EINVAL; + goto fail; + } + old = pf_default_rule.timeout[pt->timeout]; + pf_default_rule.timeout[pt->timeout] = pt->seconds; + pt->seconds = old; + break; + } + + case DIOCGETTIMEOUT: { + struct pfioc_tm *pt = (struct pfioc_tm *)addr; + + if (pt->timeout < 0 || pt->timeout >= PFTM_MAX) { + error = EINVAL; + goto fail; + } + pt->seconds = pf_default_rule.timeout[pt->timeout]; + break; + } + + case DIOCGETLIMIT: { + struct pfioc_limit *pl = (struct pfioc_limit *)addr; + + if (pl->index < 0 || pl->index >= PF_LIMIT_MAX) { + error = EINVAL; + goto fail; + } + pl->limit = pf_pool_limits[pl->index].limit; + break; + } + + case DIOCSETLIMIT: { + struct pfioc_limit *pl = (struct pfioc_limit *)addr; + int old_limit; + + if (pl->index < 0 || pl->index >= PF_LIMIT_MAX) { + error = EINVAL; + goto fail; + } +#ifdef __OpenBSD__ + if (pool_sethardlimit(pf_pool_limits[pl->index].pp, + pl->limit, NULL, 0) != 0) { + error = EBUSY; + goto fail; + } +#else + (void)pool_sethardlimit(pf_pool_limits[pl->index].pp, + pl->limit, NULL, 0); +#endif + old_limit = pf_pool_limits[pl->index].limit; + pf_pool_limits[pl->index].limit = pl->limit; + pl->limit = old_limit; + break; + } + + case DIOCSETDEBUG: { + u_int32_t *level = (u_int32_t *)addr; + + pf_status.debug = *level; + break; + } + + case DIOCCLRRULECTRS: { + struct pf_ruleset *ruleset = &pf_main_ruleset; + struct pf_rule *rule; + + s = splsoftnet(); + TAILQ_FOREACH(rule, + ruleset->rules[PF_RULESET_FILTER].active.ptr, entries) + rule->evaluations = rule->packets = + rule->bytes = 0; + splx(s); + break; + } + +#ifdef ALTQ + case DIOCSTARTALTQ: { + struct pf_altq *altq; + struct ifnet *ifp; + struct tb_profile tb; + + /* enable all altq interfaces on active list */ + s = splsoftnet(); + TAILQ_FOREACH(altq, pf_altqs_active, entries) { + if (altq->qname[0] == 0) { + if ((ifp = ifunit(altq->ifname)) == NULL) { + error = EINVAL; + break; + } + if (ifp->if_snd.altq_type != ALTQT_NONE) + error = altq_enable(&ifp->if_snd); + if (error != 0) + break; + /* set tokenbucket regulator */ + tb.rate = altq->ifbandwidth; + tb.depth = altq->tbrsize; + error = tbr_set(&ifp->if_snd, &tb); + if (error != 0) + break; + } + } + if (error == 0) + pfaltq_running = 1; + splx(s); + DPFPRINTF(PF_DEBUG_MISC, ("altq: started\n")); + break; + } + + case DIOCSTOPALTQ: { + struct pf_altq *altq; + struct ifnet *ifp; + struct tb_profile tb; + int err; + + /* disable all altq interfaces on active list */ + s = splsoftnet(); + TAILQ_FOREACH(altq, pf_altqs_active, entries) { + if (altq->qname[0] == 0) { + if ((ifp = ifunit(altq->ifname)) == NULL) { + error = EINVAL; + break; + } + if (ifp->if_snd.altq_type != ALTQT_NONE) { + err = altq_disable(&ifp->if_snd); + if (err != 0 && error == 0) + error = err; + } + /* clear tokenbucket regulator */ + tb.rate = 0; + err = tbr_set(&ifp->if_snd, &tb); + if (err != 0 && error == 0) + error = err; + } + } + if (error == 0) + pfaltq_running = 0; + splx(s); + DPFPRINTF(PF_DEBUG_MISC, ("altq: stopped\n")); + break; + } + + case DIOCBEGINALTQS: { + u_int32_t *ticket = (u_int32_t *)addr; + struct pf_altq *altq; + + /* Purge the old altq list */ + while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { + TAILQ_REMOVE(pf_altqs_inactive, altq, entries); + if (altq->qname[0] == 0) { + /* detach and destroy the discipline */ + error = altq_remove(altq); + } + pool_put(&pf_altq_pl, altq); + } + *ticket = ++ticket_altqs_inactive; + break; + } + + case DIOCADDALTQ: { + struct pfioc_altq *pa = (struct pfioc_altq *)addr; + struct pf_altq *altq, *a; + + if (pa->ticket != ticket_altqs_inactive) { + error = EBUSY; + break; + } + altq = pool_get(&pf_altq_pl, PR_NOWAIT); + if (altq == NULL) { + error = ENOMEM; + break; + } + bcopy(&pa->altq, altq, sizeof(struct pf_altq)); + + /* + * if this is for a queue, find the discipline and + * copy the necessary fields + */ + if (altq->qname[0] != 0) { + TAILQ_FOREACH(a, pf_altqs_inactive, entries) { + if (strncmp(a->ifname, altq->ifname, + IFNAMSIZ) == 0 && a->qname[0] == 0) { + altq->altq_disc = a->altq_disc; + break; + } + } + } + + error = altq_add(altq); + if (error) { + pool_put(&pf_altq_pl, altq); + break; + } + + TAILQ_INSERT_TAIL(pf_altqs_inactive, altq, entries); + bcopy(altq, &pa->altq, sizeof(struct pf_altq)); + break; + } + + case DIOCCOMMITALTQS: { + u_int32_t *ticket = (u_int32_t *)addr; + struct pf_altqqueue *old_altqs; + struct pf_altq *altq; + struct pf_anchor *anchor; + struct pf_ruleset *ruleset; + int err; + + if (*ticket != ticket_altqs_inactive) { + error = EBUSY; + break; + } + + /* Swap altqs, keep the old. */ + s = splsoftnet(); + old_altqs = pf_altqs_active; + pf_altqs_active = pf_altqs_inactive; + pf_altqs_inactive = old_altqs; + ticket_altqs_active = ticket_altqs_inactive; + + /* Attach new disciplines */ + TAILQ_FOREACH(altq, pf_altqs_active, entries) { + if (altq->qname[0] == 0) { + /* attach the discipline */ + error = altq_pfattach(altq); + if (error) { + splx(s); + goto fail; + } + } + } + + /* Purge the old altq list */ + while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { + TAILQ_REMOVE(pf_altqs_inactive, altq, entries); + if (altq->qname[0] == 0) { + /* detach and destroy the discipline */ + err = altq_pfdetach(altq); + if (err != 0 && error == 0) + error = err; + err = altq_remove(altq); + if (err != 0 && error == 0) + error = err; + } + pool_put(&pf_altq_pl, altq); + } + splx(s); + + /* update queue IDs */ + pf_rule_set_qid( + pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr); + TAILQ_FOREACH(anchor, &pf_anchors, entries) { + TAILQ_FOREACH(ruleset, &anchor->rulesets, entries) { + pf_rule_set_qid( + ruleset->rules[PF_RULESET_FILTER].active.ptr + ); + } + } + break; + } + + case DIOCGETALTQS: { + struct pfioc_altq *pa = (struct pfioc_altq *)addr; + struct pf_altq *altq; + + pa->nr = 0; + s = splsoftnet(); + TAILQ_FOREACH(altq, pf_altqs_active, entries) + pa->nr++; + pa->ticket = ticket_altqs_active; + splx(s); + break; + } + + case DIOCGETALTQ: { + struct pfioc_altq *pa = (struct pfioc_altq *)addr; + struct pf_altq *altq; + u_int32_t nr; + + if (pa->ticket != ticket_altqs_active) { + error = EBUSY; + break; + } + nr = 0; + s = splsoftnet(); + altq = TAILQ_FIRST(pf_altqs_active); + while ((altq != NULL) && (nr < pa->nr)) { + altq = TAILQ_NEXT(altq, entries); + nr++; + } + if (altq == NULL) { + error = EBUSY; + splx(s); + break; + } + bcopy(altq, &pa->altq, sizeof(struct pf_altq)); + splx(s); + break; + } + + case DIOCCHANGEALTQ: + /* CHANGEALTQ not supported yet! */ + error = ENODEV; + break; + + case DIOCGETQSTATS: { + struct pfioc_qstats *pq = (struct pfioc_qstats *)addr; + struct pf_altq *altq; + u_int32_t nr; + int nbytes; + + if (pq->ticket != ticket_altqs_active) { + error = EBUSY; + break; + } + nbytes = pq->nbytes; + nr = 0; + s = splsoftnet(); + altq = TAILQ_FIRST(pf_altqs_active); + while ((altq != NULL) && (nr < pq->nr)) { + altq = TAILQ_NEXT(altq, entries); + nr++; + } + if (altq == NULL) { + error = EBUSY; + splx(s); + break; + } + error = altq_getqstats(altq, pq->buf, &nbytes); + splx(s); + if (error == 0) { + pq->scheduler = altq->scheduler; + pq->nbytes = nbytes; + } + break; + } +#endif /* ALTQ */ + + case DIOCBEGINADDRS: { + struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; + + pf_empty_pool(&pf_pabuf); + pp->ticket = ++ticket_pabuf; + break; + } + + case DIOCADDADDR: { + struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; + +#ifndef INET + if (pp->af == AF_INET) { + error = EAFNOSUPPORT; + break; + } +#endif /* INET */ +#ifndef INET6 + if (pp->af == AF_INET6) { + error = EAFNOSUPPORT; + break; + } +#endif /* INET6 */ + if (pp->addr.addr.addr.type != PF_ADDR_ADDRMASK && + pp->addr.addr.addr.type != PF_ADDR_DYNIFTL) { + error = EINVAL; + break; + } + pa = pool_get(&pf_pooladdr_pl, PR_NOWAIT); + if (pa == NULL) { + error = ENOMEM; + break; + } + bcopy(&pp->addr, pa, sizeof(struct pf_pooladdr)); + if (pa->ifname[0]) { + pa->ifp = ifunit(pa->ifname); + if (pa->ifp == NULL) { + pool_put(&pf_pooladdr_pl, pa); + error = EINVAL; + break; + } + } + if (pf_dynaddr_setup(&pa->addr.addr, pp->af)) { + pf_dynaddr_remove(&pa->addr.addr); + pool_put(&pf_pooladdr_pl, pa); + error = EINVAL; + break; + } + TAILQ_INSERT_TAIL(&pf_pabuf, pa, entries); + break; + } + + case DIOCGETADDRS: { + struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; + + pp->nr = 0; + s = splsoftnet(); + pool = pf_get_pool(pp->anchor, pp->ruleset, pp->ticket, + pp->r_action, pp->r_num, 0, 1, 0); + if (pool == NULL) { + error = EBUSY; + splx(s); + break; + } + TAILQ_FOREACH(pa, &pool->list, entries) + pp->nr++; + splx(s); + break; + } + + case DIOCGETADDR: { + struct pfioc_pooladdr *pp = (struct pfioc_pooladdr *)addr; + u_int32_t nr = 0; + + s = splsoftnet(); + pool = pf_get_pool(pp->anchor, pp->ruleset, pp->ticket, + pp->r_action, pp->r_num, 0, 1, 1); + if (pool == NULL) { + error = EBUSY; + splx(s); + break; + } + pa = TAILQ_FIRST(&pool->list); + while ((pa != NULL) && (nr < pp->nr)) { + pa = TAILQ_NEXT(pa, entries); + nr++; + } + if (pa == NULL) { + error = EBUSY; + splx(s); + break; + } + bcopy(pa, &pp->addr, sizeof(struct pf_pooladdr)); + pf_dynaddr_copyout(&pp->addr.addr.addr); + splx(s); + break; + } + + case DIOCCHANGEADDR: { + struct pfioc_pooladdr *pca = (struct pfioc_pooladdr *)addr; + struct pf_pooladdr *oldpa = NULL, *newpa = NULL; + + if (pca->action < PF_CHANGE_ADD_HEAD || + pca->action > PF_CHANGE_REMOVE) { + error = EINVAL; + break; + } + if (pca->addr.addr.addr.type != PF_ADDR_ADDRMASK && + pca->addr.addr.addr.type != PF_ADDR_DYNIFTL) { + error = EINVAL; + break; + } + + pool = pf_get_pool(pca->anchor, pca->ruleset, 0, + pca->r_action, pca->r_num, pca->r_last, 1, 1); + if (pool == NULL) { + error = EBUSY; + break; + } + if (pca->action != PF_CHANGE_REMOVE) { + newpa = pool_get(&pf_pooladdr_pl, PR_NOWAIT); + if (newpa == NULL) { + error = ENOMEM; + break; + } + bcopy(&pca->addr, newpa, sizeof(struct pf_pooladdr)); +#ifndef INET + if (pca->af == AF_INET) { + pool_put(&pf_pooladdr_pl, newpa); + error = EAFNOSUPPORT; + break; + } +#endif /* INET */ +#ifndef INET6 + if (pca->af == AF_INET6) { + pool_put(&pf_pooladdr_pl, newpa); + error = EAFNOSUPPORT; + break; + } +#endif /* INET6 */ + if (newpa->ifname[0]) { + newpa->ifp = ifunit(newpa->ifname); + if (newpa->ifp == NULL) { + pool_put(&pf_pooladdr_pl, newpa); + error = EINVAL; + break; + } + } else + newpa->ifp = NULL; + if (pf_dynaddr_setup(&newpa->addr.addr, pca->af)) { + pf_dynaddr_remove(&newpa->addr.addr); + pool_put(&pf_pooladdr_pl, newpa); + error = EINVAL; + break; + } + } + + s = splsoftnet(); + + if (pca->action == PF_CHANGE_ADD_HEAD) + oldpa = TAILQ_FIRST(&pool->list); + else if (pca->action == PF_CHANGE_ADD_TAIL) + oldpa = TAILQ_LAST(&pool->list, pf_palist); + else { + int i = 0; + + oldpa = TAILQ_FIRST(&pool->list); + while ((oldpa != NULL) && (i < pca->nr)) { + oldpa = TAILQ_NEXT(oldpa, entries); + i++; + } + if (oldpa == NULL) { + error = EINVAL; + splx(s); + break; + } + } + + if (pca->action == PF_CHANGE_REMOVE) { + TAILQ_REMOVE(&pool->list, oldpa, entries); + pf_dynaddr_remove(&oldpa->addr.addr); + pool_put(&pf_pooladdr_pl, oldpa); + } else { + if (oldpa == NULL) + TAILQ_INSERT_TAIL(&pool->list, newpa, entries); + else if (pca->action == PF_CHANGE_ADD_HEAD || + pca->action == PF_CHANGE_ADD_BEFORE) + TAILQ_INSERT_BEFORE(oldpa, newpa, entries); + else + TAILQ_INSERT_AFTER(&pool->list, oldpa, + newpa, entries); + } + + pool->cur = TAILQ_FIRST(&pool->list); + PF_ACPY(&pool->counter, &pool->cur->addr.addr.v.a.addr, + pca->af); + splx(s); + break; + } + + case DIOCGETANCHORS: { + struct pfioc_anchor *pa = (struct pfioc_anchor *)addr; + struct pf_anchor *anchor; + + pa->nr = 0; + TAILQ_FOREACH(anchor, &pf_anchors, entries) + pa->nr++; + break; + } + + case DIOCGETANCHOR: { + struct pfioc_anchor *pa = (struct pfioc_anchor *)addr; + struct pf_anchor *anchor; + u_int32_t nr = 0; + + anchor = TAILQ_FIRST(&pf_anchors); + while (anchor != NULL && nr < pa->nr) { + anchor = TAILQ_NEXT(anchor, entries); + nr++; + } + if (anchor == NULL) + error = EBUSY; + else + bcopy(anchor->name, pa->name, sizeof(pa->name)); + break; + } + + case DIOCGETRULESETS: { + struct pfioc_ruleset *pr = (struct pfioc_ruleset *)addr; + struct pf_anchor *anchor; + struct pf_ruleset *ruleset; + + pr->anchor[PF_ANCHOR_NAME_SIZE-1] = 0; + if ((anchor = pf_find_anchor(pr->anchor)) == NULL) { + error = EINVAL; + break; + } + pr->nr = 0; + TAILQ_FOREACH(ruleset, &anchor->rulesets, entries) + pr->nr++; + break; + } + + case DIOCGETRULESET: { + struct pfioc_ruleset *pr = (struct pfioc_ruleset *)addr; + struct pf_anchor *anchor; + struct pf_ruleset *ruleset; + u_int32_t nr = 0; + + if ((anchor = pf_find_anchor(pr->anchor)) == NULL) { + error = EINVAL; + break; + } + ruleset = TAILQ_FIRST(&anchor->rulesets); + while (ruleset != NULL && nr < pr->nr) { + ruleset = TAILQ_NEXT(ruleset, entries); + nr++; + } + if (ruleset == NULL) + error = EBUSY; + else + bcopy(ruleset->name, pr->name, sizeof(pr->name)); + break; + } + + case DIOCRCLRTABLES: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != 0) { + error = ENODEV; + break; + } + error = pfr_clr_tables(&io->pfrio_table, &io->pfrio_ndel, + io->pfrio_flags); + break; + } + + case DIOCRADDTABLES: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != sizeof(struct pfr_table)) { + error = ENODEV; + break; + } + error = pfr_add_tables(io->pfrio_buffer, io->pfrio_size, + &io->pfrio_nadd, io->pfrio_flags); + break; + } + + case DIOCRDELTABLES: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != sizeof(struct pfr_table)) { + error = ENODEV; + break; + } + error = pfr_del_tables(io->pfrio_buffer, io->pfrio_size, + &io->pfrio_ndel, io->pfrio_flags); + break; + } + + case DIOCRGETTABLES: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != sizeof(struct pfr_table)) { + error = ENODEV; + break; + } + error = pfr_get_tables(&io->pfrio_table, io->pfrio_buffer, + &io->pfrio_size, io->pfrio_flags); + break; + } + + case DIOCRGETTSTATS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != sizeof(struct pfr_tstats)) { + error = ENODEV; + break; + } + error = pfr_get_tstats(&io->pfrio_table, io->pfrio_buffer, + &io->pfrio_size, io->pfrio_flags); + break; + } + + case DIOCRCLRTSTATS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != sizeof(struct pfr_table)) { + error = ENODEV; + break; + } + error = pfr_clr_tstats(io->pfrio_buffer, io->pfrio_size, + &io->pfrio_nzero, io->pfrio_flags); + break; + } + + case DIOCRSETTFLAGS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != sizeof(struct pfr_table)) { + error = ENODEV; + break; + } + error = pfr_set_tflags(io->pfrio_buffer, io->pfrio_size, + io->pfrio_setflag, io->pfrio_clrflag, &io->pfrio_nchange, + &io->pfrio_ndel, io->pfrio_flags); + break; + } + + case DIOCRCLRADDRS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != 0) { + error = ENODEV; + break; + } + error = pfr_clr_addrs(&io->pfrio_table, &io->pfrio_ndel, + io->pfrio_flags); + break; + } + + case DIOCRADDADDRS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != sizeof(struct pfr_addr)) { + error = ENODEV; + break; + } + error = pfr_add_addrs(&io->pfrio_table, io->pfrio_buffer, + io->pfrio_size, &io->pfrio_nadd, io->pfrio_flags); + break; + } + + case DIOCRDELADDRS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != sizeof(struct pfr_addr)) { + error = ENODEV; + break; + } + error = pfr_del_addrs(&io->pfrio_table, io->pfrio_buffer, + io->pfrio_size, &io->pfrio_ndel, io->pfrio_flags); + break; + } + + case DIOCRSETADDRS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != sizeof(struct pfr_addr)) { + error = ENODEV; + break; + } + error = pfr_set_addrs(&io->pfrio_table, io->pfrio_buffer, + io->pfrio_size, &io->pfrio_size2, &io->pfrio_nadd, + &io->pfrio_ndel, &io->pfrio_nchange, io->pfrio_flags); + break; + } + + case DIOCRGETADDRS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != sizeof(struct pfr_addr)) { + error = ENODEV; + break; + } + error = pfr_get_addrs(&io->pfrio_table, io->pfrio_buffer, + &io->pfrio_size, io->pfrio_flags); + break; + } + + case DIOCRGETASTATS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != sizeof(struct pfr_astats)) { + error = ENODEV; + break; + } + error = pfr_get_astats(&io->pfrio_table, io->pfrio_buffer, + &io->pfrio_size, io->pfrio_flags); + break; + } + + case DIOCRCLRASTATS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != sizeof(struct pfr_addr)) { + error = ENODEV; + break; + } + error = pfr_clr_astats(&io->pfrio_table, io->pfrio_buffer, + io->pfrio_size, &io->pfrio_nzero, io->pfrio_flags); + break; + } + + case DIOCRTSTADDRS: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != sizeof(struct pfr_addr)) { + error = ENODEV; + break; + } + error = pfr_tst_addrs(&io->pfrio_table, io->pfrio_buffer, + io->pfrio_size, &io->pfrio_nmatch, io->pfrio_flags); + break; + } + + case DIOCRINABEGIN: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != 0) { + error = ENODEV; + break; + } + error = pfr_ina_begin(&io->pfrio_ticket, &io->pfrio_ndel, + io->pfrio_flags); + break; + } + + case DIOCRINACOMMIT: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != 0) { + error = ENODEV; + break; + } + error = pfr_ina_commit(io->pfrio_ticket, &io->pfrio_nadd, + &io->pfrio_nchange, io->pfrio_flags); + break; + } + + case DIOCRINADEFINE: { + struct pfioc_table *io = (struct pfioc_table *)addr; + + if (io->pfrio_esize != sizeof(struct pfr_addr)) { + error = ENODEV; + break; + } + error = pfr_ina_define(&io->pfrio_table, io->pfrio_buffer, + io->pfrio_size, &io->pfrio_nadd, &io->pfrio_naddr, + io->pfrio_ticket, io->pfrio_flags); + break; + } + + default: + error = ENODEV; + break; + } +fail: + + return (error); +} + +#ifdef __NetBSD__ +int +pfil4_wrapper(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) +{ + + /* + * If the packet is out-bound, we can't delay checksums + * here. For in-bound, the checksum has already been + * validated. + */ + if (dir == PFIL_OUT) { + if ((*mp)->m_pkthdr.csum_flags & (M_CSUM_TCPv4|M_CSUM_UDPv4)) { + in_delayed_cksum(*mp); + (*mp)->m_pkthdr.csum_flags &= + ~(M_CSUM_TCPv4|M_CSUM_UDPv4); + } + } + + if (pf_test(dir == PFIL_OUT ? PF_OUT : PF_IN, ifp, mp) != PF_PASS) + return EHOSTUNREACH; + else + return (0); +} + +int +pfil6_wrapper(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) +{ + + if (pf_test6(dir == PFIL_OUT ? PF_OUT : PF_IN, ifp, mp) != PF_PASS) + return EHOSTUNREACH; + else + return (0); +} + +int +pf_pfil_attach(void) +{ + struct pfil_head *ph_inet; +#ifdef INET6 + struct pfil_head *ph_inet6; +#endif + int error; + + ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); + if (ph_inet) + error = pfil_add_hook((void *)pfil4_wrapper, NULL, + PFIL_IN|PFIL_OUT, ph_inet); + else + error = ENOENT; +#ifdef INET6 + if (error) + return (error); + ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); + if (ph_inet6) + error = pfil_add_hook((void *)pfil6_wrapper, NULL, + PFIL_IN|PFIL_OUT, ph_inet6); + else + error = ENOENT; + if (error) { + pfil_remove_hook((void *)pfil4_wrapper, NULL, + PFIL_IN|PFIL_OUT, ph_inet); + } +#endif + return (error); +} + +int +pf_pfil_detach(void) +{ + struct pfil_head *ph_inet; +#ifdef INET6 + struct pfil_head *ph_inet6; +#endif + + ph_inet = pfil_head_get(PFIL_TYPE_AF, AF_INET); + if (ph_inet) + pfil_remove_hook((void *)pfil4_wrapper, NULL, + PFIL_IN|PFIL_OUT, ph_inet); +#ifdef INET6 + ph_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6); + if (ph_inet) + pfil_remove_hook((void *)pfil6_wrapper, NULL, + PFIL_IN|PFIL_OUT, ph_inet6); +#endif + return (0); +} +#endif Index: sys/net/pf_norm.c =================================================================== RCS file: pf_norm.c diff -N pf_norm.c --- /dev/null Mon Jun 30 04:58:52 2003 +++ pf_norm.c Mon Jun 30 04:58:39 2003 @@ -0,0 +1,1642 @@ +/* $NetBSD$ */ +/* $OpenBSD: pf_norm.c,v 1.59 2003/05/14 23:46:45 frantzen Exp $ */ + +/* + * Copyright 2001 Niels Provos + * 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 ``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 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. + */ + +#ifdef _KERNEL_OPT +#include "opt_inet.h" +#endif + +#include "pflog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __OpenBSD__ +#include +#else +#include +#endif +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef INET6 +#include +#endif /* INET6 */ + +#include + +struct pf_frent { + LIST_ENTRY(pf_frent) fr_next; + struct ip *fr_ip; + struct mbuf *fr_m; +}; + +struct pf_frcache { + LIST_ENTRY(pf_frcache) fr_next; + uint16_t fr_off; + uint16_t fr_end; +}; + +#define PFFRAG_SEENLAST 0x0001 /* Seen the last fragment for this */ +#define PFFRAG_NOBUFFER 0x0002 /* Non-buffering fragment cache */ +#define PFFRAG_DROP 0x0004 /* Drop all fragments */ +#define BUFFER_FRAGMENTS(fr) (!((fr)->fr_flags & PFFRAG_NOBUFFER)) + +struct pf_fragment { + RB_ENTRY(pf_fragment) fr_entry; + TAILQ_ENTRY(pf_fragment) frag_next; + struct in_addr fr_src; + struct in_addr fr_dst; + u_int8_t fr_p; /* protocol of this fragment */ + u_int8_t fr_flags; /* status flags */ + u_int16_t fr_id; /* fragment id for reassemble */ + u_int16_t fr_max; /* fragment data max */ + u_int32_t fr_timeout; +#define fr_queue fr_u.fru_queue +#define fr_cache fr_u.fru_cache + union { + LIST_HEAD(pf_fragq, pf_frent) fru_queue; /* buffering */ + LIST_HEAD(pf_cacheq, pf_frcache) fru_cache; /* non-buf */ + } fr_u; +}; + +TAILQ_HEAD(pf_fragqueue, pf_fragment) pf_fragqueue; +TAILQ_HEAD(pf_cachequeue, pf_fragment) pf_cachequeue; + +static __inline int pf_frag_compare(struct pf_fragment *, + struct pf_fragment *); +RB_HEAD(pf_frag_tree, pf_fragment) pf_frag_tree, pf_cache_tree; +RB_PROTOTYPE(pf_frag_tree, pf_fragment, fr_entry, pf_frag_compare); +RB_GENERATE(pf_frag_tree, pf_fragment, fr_entry, pf_frag_compare); + +/* Private prototypes */ +void pf_ip2key(struct pf_fragment *, struct ip *); +void pf_remove_fragment(struct pf_fragment *); +void pf_flush_fragments(void); +void pf_free_fragment(struct pf_fragment *); +struct pf_fragment *pf_find_fragment(struct ip *, struct pf_frag_tree *); +struct mbuf *pf_reassemble(struct mbuf **, struct pf_fragment *, + struct pf_frent *, int); +struct mbuf *pf_fragcache(struct mbuf **, struct ip*, + struct pf_fragment *, int, int, int *); +u_int16_t pf_cksum_fixup(u_int16_t, u_int16_t, u_int16_t); +int pf_normalize_tcpopt(struct pf_rule *, struct mbuf *, + struct tcphdr *, int); + +#define DPFPRINTF(x) if (pf_status.debug >= PF_DEBUG_MISC) \ + { printf("%s: ", __func__); printf x ;} + +/* Globals */ +struct pool pf_frent_pl, pf_frag_pl, pf_cache_pl, pf_cent_pl; +struct pool pf_state_scrub_pl; +int pf_nfrents, pf_ncache; + +void +pf_normalize_init(void) +{ + pool_init(&pf_frent_pl, sizeof(struct pf_frent), 0, 0, 0, "pffrent", + NULL); + pool_init(&pf_frag_pl, sizeof(struct pf_fragment), 0, 0, 0, "pffrag", + NULL); + pool_init(&pf_cache_pl, sizeof(struct pf_fragment), 0, 0, 0, + "pffrcache", NULL); + pool_init(&pf_cent_pl, sizeof(struct pf_frcache), 0, 0, 0, "pffrcent", + NULL); + pool_init(&pf_state_scrub_pl, sizeof(struct pf_state_scrub), 0, 0, 0, + "pfstscr", NULL); + + pool_sethiwat(&pf_frag_pl, PFFRAG_FRAG_HIWAT); + pool_sethardlimit(&pf_frent_pl, PFFRAG_FRENT_HIWAT, NULL, 0); + pool_sethardlimit(&pf_cache_pl, PFFRAG_FRCACHE_HIWAT, NULL, 0); + pool_sethardlimit(&pf_cent_pl, PFFRAG_FRCENT_HIWAT, NULL, 0); + + TAILQ_INIT(&pf_fragqueue); + TAILQ_INIT(&pf_cachequeue); +} + +static __inline int +pf_frag_compare(struct pf_fragment *a, struct pf_fragment *b) +{ + int diff; + + if ((diff = a->fr_id - b->fr_id)) + return (diff); + else if ((diff = a->fr_p - b->fr_p)) + return (diff); + else if (a->fr_src.s_addr < b->fr_src.s_addr) + return (-1); + else if (a->fr_src.s_addr > b->fr_src.s_addr) + return (1); + else if (a->fr_dst.s_addr < b->fr_dst.s_addr) + return (-1); + else if (a->fr_dst.s_addr > b->fr_dst.s_addr) + return (1); + return (0); +} + +void +pf_purge_expired_fragments(void) +{ + struct pf_fragment *frag; + u_int32_t expire = time.tv_sec - + pf_default_rule.timeout[PFTM_FRAG]; + + while ((frag = TAILQ_LAST(&pf_fragqueue, pf_fragqueue)) != NULL) { + KASSERT(BUFFER_FRAGMENTS(frag)); + if (frag->fr_timeout > expire) + break; + + DPFPRINTF(("expiring %d(%p)\n", frag->fr_id, frag)); + pf_free_fragment(frag); + } + + while ((frag = TAILQ_LAST(&pf_cachequeue, pf_cachequeue)) != NULL) { + KASSERT(!BUFFER_FRAGMENTS(frag)); + if (frag->fr_timeout > expire) + break; + + DPFPRINTF(("expiring %d(%p)\n", frag->fr_id, frag)); + pf_free_fragment(frag); + KASSERT(TAILQ_EMPTY(&pf_cachequeue) || + TAILQ_LAST(&pf_cachequeue, pf_cachequeue) != frag); + } +} + +/* + * Try to flush old fragments to make space for new ones + */ + +void +pf_flush_fragments(void) +{ + struct pf_fragment *frag; + int goal; + + goal = pf_nfrents * 9 / 10; + DPFPRINTF(("trying to free > %d frents\n", + pf_nfrents - goal)); + while (goal < pf_nfrents) { + frag = TAILQ_LAST(&pf_fragqueue, pf_fragqueue); + if (frag == NULL) + break; + pf_free_fragment(frag); + } + + + goal = pf_ncache * 9 / 10; + DPFPRINTF(("trying to free > %d cache entries\n", + pf_ncache - goal)); + while (goal < pf_ncache) { + frag = TAILQ_LAST(&pf_cachequeue, pf_cachequeue); + if (frag == NULL) + break; + pf_free_fragment(frag); + } +} + +/* Frees the fragments and all associated entries */ + +void +pf_free_fragment(struct pf_fragment *frag) +{ + struct pf_frent *frent; + struct pf_frcache *frcache; + + /* Free all fragments */ + if (BUFFER_FRAGMENTS(frag)) { + for (frent = LIST_FIRST(&frag->fr_queue); frent; + frent = LIST_FIRST(&frag->fr_queue)) { + LIST_REMOVE(frent, fr_next); + + m_freem(frent->fr_m); + pool_put(&pf_frent_pl, frent); + pf_nfrents--; + } + } else { + for (frcache = LIST_FIRST(&frag->fr_cache); frcache; + frcache = LIST_FIRST(&frag->fr_cache)) { + LIST_REMOVE(frcache, fr_next); + + KASSERT(LIST_EMPTY(&frag->fr_cache) || + LIST_FIRST(&frag->fr_cache)->fr_off > + frcache->fr_end); + + pool_put(&pf_cent_pl, frcache); + pf_ncache--; + } + } + + pf_remove_fragment(frag); +} + +void +pf_ip2key(struct pf_fragment *key, struct ip *ip) +{ + key->fr_p = ip->ip_p; + key->fr_id = ip->ip_id; + key->fr_src.s_addr = ip->ip_src.s_addr; + key->fr_dst.s_addr = ip->ip_dst.s_addr; +} + +struct pf_fragment * +pf_find_fragment(struct ip *ip, struct pf_frag_tree *tree) +{ + struct pf_fragment key; + struct pf_fragment *frag; + + pf_ip2key(&key, ip); + + frag = RB_FIND(pf_frag_tree, tree, &key); + if (frag != NULL) { + /* XXX Are we sure we want to update the timeout? */ + frag->fr_timeout = time.tv_sec; + if (BUFFER_FRAGMENTS(frag)) { + TAILQ_REMOVE(&pf_fragqueue, frag, frag_next); + TAILQ_INSERT_HEAD(&pf_fragqueue, frag, frag_next); + } else { + TAILQ_REMOVE(&pf_cachequeue, frag, frag_next); + TAILQ_INSERT_HEAD(&pf_cachequeue, frag, frag_next); + } + } + + return (frag); +} + +/* Removes a fragment from the fragment queue and frees the fragment */ + +void +pf_remove_fragment(struct pf_fragment *frag) +{ + if (BUFFER_FRAGMENTS(frag)) { + RB_REMOVE(pf_frag_tree, &pf_frag_tree, frag); + TAILQ_REMOVE(&pf_fragqueue, frag, frag_next); + pool_put(&pf_frag_pl, frag); + } else { + RB_REMOVE(pf_frag_tree, &pf_cache_tree, frag); + TAILQ_REMOVE(&pf_cachequeue, frag, frag_next); + pool_put(&pf_cache_pl, frag); + } +} + +struct mbuf * +pf_reassemble(struct mbuf **m0, struct pf_fragment *frag, + struct pf_frent *frent, int mff) +{ + struct mbuf *m = *m0, *m2; + struct pf_frent *frea, *next; + struct pf_frent *frep = NULL; + struct ip *ip = frent->fr_ip; + int hlen = ip->ip_hl << 2; +#ifdef __OpenBSD__ + u_int16_t off = ip->ip_off; + u_int16_t max = ip->ip_len + off; +#else + u_int16_t off = ntohs(ip->ip_off); + u_int16_t max = ntohs(ip->ip_len) + off; +#endif + + KASSERT(frag == NULL || BUFFER_FRAGMENTS(frag)); + + /* Strip off ip header */ + m->m_data += hlen; + m->m_len -= hlen; + + /* Create a new reassembly queue for this packet */ + if (frag == NULL) { + frag = pool_get(&pf_frag_pl, PR_NOWAIT); + if (frag == NULL) { + pf_flush_fragments(); + frag = pool_get(&pf_frag_pl, PR_NOWAIT); + if (frag == NULL) + goto drop_fragment; + } + + frag->fr_flags = 0; + frag->fr_max = 0; + frag->fr_src = frent->fr_ip->ip_src; + frag->fr_dst = frent->fr_ip->ip_dst; + frag->fr_p = frent->fr_ip->ip_p; + frag->fr_id = frent->fr_ip->ip_id; + frag->fr_timeout = time.tv_sec; + LIST_INIT(&frag->fr_queue); + + RB_INSERT(pf_frag_tree, &pf_frag_tree, frag); + TAILQ_INSERT_HEAD(&pf_fragqueue, frag, frag_next); + + /* We do not have a previous fragment */ + frep = NULL; + goto insert; + } + + /* + * Find a fragment after the current one: + * - off contains the real shifted offset. + */ + LIST_FOREACH(frea, &frag->fr_queue, fr_next) { +#ifdef __OpenBSD__ + if (frea->fr_ip->ip_off > off) +#else + if (ntohs(frea->fr_ip->ip_off) > off) +#endif + break; + frep = frea; + } + + KASSERT(frep != NULL || frea != NULL); + +#ifdef __OpenBSD__ + if (frep != NULL && frep->fr_ip->ip_off + frep->fr_ip->ip_len > off) +#else + if (frep != NULL && + ntohs(frep->fr_ip->ip_off) + ntohs(frep->fr_ip->ip_len) > off) +#endif + { + u_int16_t precut; + +#ifdef __OpenBSD__ + precut = frep->fr_ip->ip_off + frep->fr_ip->ip_len - off; +#else + precut = ntohs(frep->fr_ip->ip_off) + + ntohs(frep->fr_ip->ip_len) - off; +#endif +#ifdef __OpenBSD__ + if (precut >= ip->ip_len) +#else + if (precut >= ntohs(ip->ip_len)) +#endif + goto drop_fragment; + m_adj(frent->fr_m, precut); + DPFPRINTF(("overlap -%d\n", precut)); + /* Enforce 8 byte boundaries */ +#ifdef __OpenBSD__ + off = ip->ip_off += precut; + ip->ip_len -= precut; +#else + ip->ip_off = htons(ntohs(ip->ip_off) + precut); + off = ntohs(ip->ip_off); + ip->ip_len = htons(ntohs(ip->ip_len) - precut); +#endif + } + +#ifdef __OpenBSD__ + for (; frea != NULL && ip->ip_len + off > frea->fr_ip->ip_off; + frea = next) +#else + for (; frea != NULL && ntohs(ip->ip_len) + off > ntohs(frea->fr_ip->ip_off); + frea = next) +#endif + { + u_int16_t aftercut; + +#ifdef __OpenBSD__ + aftercut = (ip->ip_len + off) - frea->fr_ip->ip_off; +#else + aftercut = (ntohs(ip->ip_len) + off) - ntohs(frea->fr_ip->ip_off); +#endif + DPFPRINTF(("adjust overlap %d\n", aftercut)); +#ifdef __OpenBSD__ + if (aftercut < frea->fr_ip->ip_len) +#else + if (aftercut < ntohs(frea->fr_ip->ip_len)) +#endif + { +#ifdef __OpenBSD__ + frea->fr_ip->ip_len -= aftercut; + frea->fr_ip->ip_off += aftercut; +#else + frea->fr_ip->ip_len = + htons(ntohs(frea->fr_ip->ip_len) - aftercut); + frea->fr_ip->ip_off = + htons(ntohs(frea->fr_ip->ip_off) + aftercut); +#endif + m_adj(frea->fr_m, aftercut); + break; + } + + /* This fragment is completely overlapped, loose it */ + next = LIST_NEXT(frea, fr_next); + m_freem(frea->fr_m); + LIST_REMOVE(frea, fr_next); + pool_put(&pf_frent_pl, frea); + pf_nfrents--; + } + + insert: + /* Update maximum data size */ + if (frag->fr_max < max) + frag->fr_max = max; + /* This is the last segment */ + if (!mff) + frag->fr_flags |= PFFRAG_SEENLAST; + + if (frep == NULL) + LIST_INSERT_HEAD(&frag->fr_queue, frent, fr_next); + else + LIST_INSERT_AFTER(frep, frent, fr_next); + + /* Check if we are completely reassembled */ + if (!(frag->fr_flags & PFFRAG_SEENLAST)) + return (NULL); + + /* Check if we have all the data */ + off = 0; + for (frep = LIST_FIRST(&frag->fr_queue); frep; frep = next) { + next = LIST_NEXT(frep, fr_next); + +#ifdef __OpenBSD__ + off += frep->fr_ip->ip_len; + if (off < frag->fr_max && + (next == NULL || next->fr_ip->ip_off != off)) +#else + off += ntohs(frep->fr_ip->ip_len); + if (off < frag->fr_max && + (next == NULL || ntohs(next->fr_ip->ip_off) != off)) +#endif + { +#ifdef __OpenBSD__ + DPFPRINTF(("missing fragment at %d, next %d, max %d\n", + off, next == NULL ? -1 : next->fr_ip->ip_off, + frag->fr_max)); +#else + DPFPRINTF(("missing fragment at %d, next %d, max %d\n", + off, next == NULL ? -1 : ntohs(next->fr_ip->ip_off), + frag->fr_max)); +#endif + return (NULL); + } + } + DPFPRINTF(("%d < %d?\n", off, frag->fr_max)); + if (off < frag->fr_max) + return (NULL); + + /* We have all the data */ + frent = LIST_FIRST(&frag->fr_queue); + KASSERT(frent != NULL); + if ((frent->fr_ip->ip_hl << 2) + off > IP_MAXPACKET) { + DPFPRINTF(("drop: too big: %d\n", off)); + pf_free_fragment(frag); + return (NULL); + } + next = LIST_NEXT(frent, fr_next); + + /* Magic from ip_input */ + ip = frent->fr_ip; + m = frent->fr_m; + m2 = m->m_next; + m->m_next = NULL; + m_cat(m, m2); + pool_put(&pf_frent_pl, frent); + pf_nfrents--; + for (frent = next; frent != NULL; frent = next) { + next = LIST_NEXT(frent, fr_next); + + m2 = frent->fr_m; + pool_put(&pf_frent_pl, frent); + pf_nfrents--; + m_cat(m, m2); + } + + ip->ip_src = frag->fr_src; + ip->ip_dst = frag->fr_dst; + + /* Remove from fragment queue */ + pf_remove_fragment(frag); + + hlen = ip->ip_hl << 2; +#ifdef __OpenBSD__ + ip->ip_len = off + hlen; +#else + ip->ip_len = htons(off + hlen); +#endif + m->m_len += hlen; + m->m_data -= hlen; + + /* some debugging cruft by sklower, below, will go away soon */ + /* XXX this should be done elsewhere */ + if (m->m_flags & M_PKTHDR) { + int plen = 0; + for (m2 = m; m2; m2 = m2->m_next) + plen += m2->m_len; + m->m_pkthdr.len = plen; + } + +#ifdef __OpenBSD__ + DPFPRINTF(("complete: %p(%d)\n", m, ip->ip_len)); +#else + DPFPRINTF(("complete: %p(%d)\n", m, ntohs(ip->ip_len))); +#endif + return (m); + + drop_fragment: + /* Oops - fail safe - drop packet */ + pool_put(&pf_frent_pl, frent); + pf_nfrents--; + m_freem(m); + return (NULL); +} + +struct mbuf * +pf_fragcache(struct mbuf **m0, struct ip *h, struct pf_fragment *frag, int mff, + int drop, int *nomem) +{ + struct mbuf *m = *m0; + struct pf_frcache *frp, *fra, *cur = NULL; +#ifdef __OpenBSD__ + int ip_len = h->ip_len - (h->ip_hl << 2); + u_int16_t off = h->ip_off << 3; +#else + int ip_len = ntohs(h->ip_len) - (h->ip_hl << 2); + u_int16_t off = ntohs(h->ip_off) << 3; +#endif + u_int16_t max = ip_len + off; + int hosed = 0; + + KASSERT(frag == NULL || !BUFFER_FRAGMENTS(frag)); + + /* Create a new range queue for this packet */ + if (frag == NULL) { + frag = pool_get(&pf_cache_pl, PR_NOWAIT); + if (frag == NULL) { + pf_flush_fragments(); + frag = pool_get(&pf_cache_pl, PR_NOWAIT); + if (frag == NULL) + goto no_mem; + } + + /* Get an entry for the queue */ + cur = pool_get(&pf_cent_pl, PR_NOWAIT); + if (cur == NULL) { + pool_put(&pf_cache_pl, frag); + goto no_mem; + } + pf_ncache++; + + frag->fr_flags = PFFRAG_NOBUFFER; + frag->fr_max = 0; + frag->fr_src = h->ip_src; + frag->fr_dst = h->ip_dst; + frag->fr_p = h->ip_p; + frag->fr_id = h->ip_id; + frag->fr_timeout = time.tv_sec; + + cur->fr_off = off; + cur->fr_end = max; + LIST_INIT(&frag->fr_cache); + LIST_INSERT_HEAD(&frag->fr_cache, cur, fr_next); + + RB_INSERT(pf_frag_tree, &pf_cache_tree, frag); + TAILQ_INSERT_HEAD(&pf_cachequeue, frag, frag_next); + + DPFPRINTF(("fragcache[%d]: new %d-%d\n", h->ip_id, off, max)); + + goto pass; + } + + /* + * Find a fragment after the current one: + * - off contains the real shifted offset. + */ + frp = NULL; + LIST_FOREACH(fra, &frag->fr_cache, fr_next) { + if (fra->fr_off > off) + break; + frp = fra; + } + + KASSERT(frp != NULL || fra != NULL); + + if (frp != NULL) { + int precut; + + precut = frp->fr_end - off; + if (precut >= ip_len) { + /* Fragment is entirely a duplicate */ + DPFPRINTF(("fragcache[%d]: dead (%d-%d) %d-%d\n", + h->ip_id, frp->fr_off, frp->fr_end, off, max)); + goto drop_fragment; + } + if (precut == 0) { + /* They are adjacent. Fixup cache entry */ + DPFPRINTF(("fragcache[%d]: adjacent (%d-%d) %d-%d\n", + h->ip_id, frp->fr_off, frp->fr_end, off, max)); + frp->fr_end = max; + } else if (precut > 0) { + /* The first part of this payload overlaps with a + * fragment that has already been passed. + * Need to trim off the first part of the payload. + * But to do so easily, we need to create another + * mbuf to throw the original header into. + */ + + DPFPRINTF(("fragcache[%d]: chop %d (%d-%d) %d-%d\n", + h->ip_id, precut, frp->fr_off, frp->fr_end, off, + max)); + + off += precut; + max -= precut; + /* Update the previous frag to encompas this one */ + frp->fr_end = max; + + if (!drop) { + /* XXX Optimization opportunity + * This is a very heavy way to trim the payload. + * we could do it much faster by diddling mbuf + * internals but that would be even less legible + * than this mbuf magic. For my next trick, + * I'll pull a rabbit out of my laptop. + */ +#ifdef __OpenBSD__ + *m0 = m_copym2(m, 0, h->ip_hl << 2, M_NOWAIT); +#else + *m0 = m_dup(m, 0, h->ip_hl << 2, M_NOWAIT); +#endif + if (*m0 == NULL) + goto no_mem; + KASSERT((*m0)->m_next == NULL); + m_adj(m, precut + (h->ip_hl << 2)); + m_cat(*m0, m); + m = *m0; + if (m->m_flags & M_PKTHDR) { + int plen = 0; + struct mbuf *t; + for (t = m; t; t = t->m_next) + plen += t->m_len; + m->m_pkthdr.len = plen; + } + + + h = mtod(m, struct ip *); + + +#ifdef __OpenBSD__ + KASSERT((int)m->m_len == h->ip_len - precut); + h->ip_off += precut >> 3; + h->ip_len -= precut; +#else + KASSERT((int)m->m_len == ntohs(h->ip_len) - precut); + h->ip_off = htons(ntohs(h->ip_off) + (precut >> 3)); + h->ip_len = htons(ntohs(h->ip_len) - precut); +#endif + } else { + hosed++; + } + } else { + /* There is a gap between fragments */ + + DPFPRINTF(("fragcache[%d]: gap %d (%d-%d) %d-%d\n", + h->ip_id, -precut, frp->fr_off, frp->fr_end, off, + max)); + + cur = pool_get(&pf_cent_pl, PR_NOWAIT); + if (cur == NULL) + goto no_mem; + pf_ncache++; + + cur->fr_off = off; + cur->fr_end = max; + LIST_INSERT_AFTER(frp, cur, fr_next); + } + } + + if (fra != NULL) { + int aftercut; + int merge = 0; + + aftercut = max - fra->fr_off; + if (aftercut == 0) { + /* Adjacent fragments */ + DPFPRINTF(("fragcache[%d]: adjacent %d-%d (%d-%d)\n", + h->ip_id, off, max, fra->fr_off, fra->fr_end)); + fra->fr_off = off; + merge = 1; + } else if (aftercut > 0) { + /* Need to chop off the tail of this fragment */ + DPFPRINTF(("fragcache[%d]: chop %d %d-%d (%d-%d)\n", + h->ip_id, aftercut, off, max, fra->fr_off, + fra->fr_end)); + fra->fr_off = off; + max -= aftercut; + + merge = 1; + + if (!drop) { + m_adj(m, -aftercut); + if (m->m_flags & M_PKTHDR) { + int plen = 0; + struct mbuf *t; + for (t = m; t; t = t->m_next) + plen += t->m_len; + m->m_pkthdr.len = plen; + } + h = mtod(m, struct ip *); +#ifdef __OpenBSD__ + KASSERT((int)m->m_len == h->ip_len - aftercut); + h->ip_len -= aftercut; +#else + KASSERT((int)m->m_len == ntohs(h->ip_len) - aftercut); + h->ip_len = htons(ntohs(h->ip_len) - aftercut); +#endif + } else { + hosed++; + } + } else { + /* There is a gap between fragments */ + DPFPRINTF(("fragcache[%d]: gap %d %d-%d (%d-%d)\n", + h->ip_id, -aftercut, off, max, fra->fr_off, + fra->fr_end)); + + cur = pool_get(&pf_cent_pl, PR_NOWAIT); + if (cur == NULL) + goto no_mem; + pf_ncache++; + + cur->fr_off = off; + cur->fr_end = max; + LIST_INSERT_BEFORE(fra, cur, fr_next); + } + + + /* Need to glue together two seperate fragment descriptors */ + if (merge) { + if (cur && fra->fr_off <= cur->fr_end) { + /* Need to merge in a previous 'cur' */ + DPFPRINTF(("fragcache[%d]: adjacent(merge " + "%d-%d) %d-%d (%d-%d)\n", + h->ip_id, cur->fr_off, cur->fr_end, off, + max, fra->fr_off, fra->fr_end)); + fra->fr_off = cur->fr_off; + LIST_REMOVE(cur, fr_next); + pool_put(&pf_cent_pl, cur); + pf_ncache--; + cur = NULL; + + } else if (frp && fra->fr_off <= frp->fr_end) { + /* Need to merge in a modified 'frp' */ + KASSERT(cur == NULL); + DPFPRINTF(("fragcache[%d]: adjacent(merge " + "%d-%d) %d-%d (%d-%d)\n", + h->ip_id, frp->fr_off, frp->fr_end, off, + max, fra->fr_off, fra->fr_end)); + fra->fr_off = frp->fr_off; + LIST_REMOVE(frp, fr_next); + pool_put(&pf_cent_pl, frp); + pf_ncache--; + frp = NULL; + + } + } + } + + if (hosed) { + /* + * We must keep tracking the overall fragment even when + * we're going to drop it anyway so that we know when to + * free the overall descriptor. Thus we drop the frag late. + */ + goto drop_fragment; + } + + + pass: + /* Update maximum data size */ + if (frag->fr_max < max) + frag->fr_max = max; + + /* This is the last segment */ + if (!mff) + frag->fr_flags |= PFFRAG_SEENLAST; + + /* Check if we are completely reassembled */ + if ((frag->fr_flags & PFFRAG_SEENLAST) && + LIST_FIRST(&frag->fr_cache)->fr_off == 0 && + LIST_FIRST(&frag->fr_cache)->fr_end == frag->fr_max) { + /* Remove from fragment queue */ + DPFPRINTF(("fragcache[%d]: done 0-%d\n", h->ip_id, + frag->fr_max)); + pf_free_fragment(frag); + } + + return (m); + + no_mem: + *nomem = 1; + + /* Still need to pay attention to !IP_MF */ + if (!mff && frag) + frag->fr_flags |= PFFRAG_SEENLAST; + + m_freem(m); + return (NULL); + + drop_fragment: + + /* Still need to pay attention to !IP_MF */ + if (!mff && frag) + frag->fr_flags |= PFFRAG_SEENLAST; + + if (drop) { + /* This fragment has been deemed bad. Don't reass */ + if ((frag->fr_flags & PFFRAG_DROP) == 0) + DPFPRINTF(("fragcache[%d]: dropping overall fragment\n", + h->ip_id)); + frag->fr_flags |= PFFRAG_DROP; + } + + m_freem(m); + return (NULL); +} + +int +pf_normalize_ip(struct mbuf **m0, int dir, struct ifnet *ifp, u_short *reason) +{ + struct mbuf *m = *m0; + struct pf_rule *r; + struct pf_frent *frent; + struct pf_fragment *frag = NULL; + struct ip *h = mtod(m, struct ip *); +#ifdef __OpenBSD__ + int mff = (h->ip_off & IP_MF); +#else + int mff = (ntohs(h->ip_off) & IP_MF); +#endif + int hlen = h->ip_hl << 2; +#ifdef __OpenBSD__ + u_int16_t fragoff = (h->ip_off & IP_OFFMASK) << 3; +#else + u_int16_t fragoff = (ntohs(h->ip_off) & IP_OFFMASK) << 3; +#endif + u_int16_t max; + int ip_len; + int ip_off; + + r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_SCRUB].active.ptr); + while (r != NULL) { + r->evaluations++; + if (r->ifp != NULL && r->ifp != ifp) + r = r->skip[PF_SKIP_IFP].ptr; + else if (r->direction && r->direction != dir) + r = r->skip[PF_SKIP_DIR].ptr; + else if (r->af && r->af != AF_INET) + r = r->skip[PF_SKIP_AF].ptr; + else if (r->proto && r->proto != h->ip_p) + r = r->skip[PF_SKIP_PROTO].ptr; + else if (PF_MISMATCHAW(&r->src.addr, + (struct pf_addr *)&h->ip_src.s_addr, AF_INET, r->src.not)) + r = r->skip[PF_SKIP_SRC_ADDR].ptr; + else if (PF_MISMATCHAW(&r->dst.addr, + (struct pf_addr *)&h->ip_dst.s_addr, AF_INET, r->dst.not)) + r = r->skip[PF_SKIP_DST_ADDR].ptr; + else + break; + } + + if (r == NULL) + return (PF_PASS); + else + r->packets++; + + /* Check for illegal packets */ + if (hlen < (int)sizeof(struct ip)) + goto drop; + +#ifdef __OpenBSD__ + if (hlen > h->ip_len) +#else + if (hlen > ntohs(h->ip_len)) +#endif + goto drop; + + /* Clear IP_DF if the rule uses the no-df option */ + if (r->rule_flag & PFRULE_NODF) { +#ifdef __OpenBSD__ + h->ip_off &= ~IP_DF; +#else + h->ip_off &= htons(~IP_DF); +#endif + } + + /* We will need other tests here */ + if (!fragoff && !mff) + goto no_fragment; + + /* We're dealing with a fragment now. Don't allow fragments + * with IP_DF to enter the cache. If the flag was cleared by + * no-df above, fine. Otherwise drop it. + */ +#ifdef __OpenBSD__ + if (h->ip_off & IP_DF) +#else + if (h->ip_off & htons(IP_DF)) +#endif + { + DPFPRINTF(("IP_DF\n")); + goto bad; + } + +#ifdef __OpenBSD__ + ip_len = h->ip_len - hlen; + ip_off = h->ip_off << 3; +#else + ip_len = ntohs(h->ip_len) - hlen; + ip_off = ntohs(h->ip_off) << 3; +#endif + + /* All fragments are 8 byte aligned */ + if (mff && (ip_len & 0x7)) { + DPFPRINTF(("mff and %d\n", ip_len)); + goto bad; + } + + max = fragoff + ip_len; + /* Respect maximum length */ + if (max > IP_MAXPACKET) { + DPFPRINTF(("max packet %d\n", max)); + goto bad; + } + + if ((r->rule_flag & (PFRULE_FRAGCROP|PFRULE_FRAGDROP)) == 0) { + /* Fully buffer all of the fragments */ + +#ifdef __OpenBSD__ + h->ip_len = ip_len; /* logic need muddled off/len */ + h->ip_off = ip_off; +#endif + frag = pf_find_fragment(h, &pf_frag_tree); + + /* Check if we saw the last fragment already */ + if (frag != NULL && (frag->fr_flags & PFFRAG_SEENLAST) && + max > frag->fr_max) + goto bad; + + /* Get an entry for the fragment queue */ + frent = pool_get(&pf_frent_pl, PR_NOWAIT); + if (frent == NULL) { + REASON_SET(reason, PFRES_MEMORY); + return (PF_DROP); + } + pf_nfrents++; + frent->fr_ip = h; + frent->fr_m = m; + + /* Might return a completely reassembled mbuf, or NULL */ + DPFPRINTF(("reass frag %d @ %d-%d\n", h->ip_id, fragoff, max)); + *m0 = m = pf_reassemble(m0, frag, frent, mff); + + if (m == NULL) + return (PF_DROP); + + if (frag && (frag->fr_flags & PFFRAG_DROP)) + goto drop; + + h = mtod(m, struct ip *); + } else { + /* non-buffering fragment cache (drops or masks overlaps) */ + int nomem = 0; + + if (dir == PF_OUT) { + if (m_tag_find(m, PACKET_TAG_PF_FRAGCACHE, NULL) != + NULL) { + /* Already passed the fragment cache in the + * input direction. If we continued, it would + * appear to be a dup and would be dropped. + */ + goto fragment_pass; + } + } + + frag = pf_find_fragment(h, &pf_cache_tree); + + /* Check if we saw the last fragment already */ + if (frag != NULL && (frag->fr_flags & PFFRAG_SEENLAST) && + max > frag->fr_max) { + if (r->rule_flag & PFRULE_FRAGDROP) + frag->fr_flags |= PFFRAG_DROP; + goto bad; + } + + *m0 = m = pf_fragcache(m0, h, frag, mff, + (r->rule_flag & PFRULE_FRAGDROP) ? 1 : 0, &nomem); + if (m == NULL) { + if (nomem) + goto no_mem; + goto drop; + } + + if (dir == PF_IN) { + struct m_tag *mtag; + + mtag = m_tag_get(PACKET_TAG_PF_FRAGCACHE, 0, M_NOWAIT); + if (mtag == NULL) + goto no_mem; + m_tag_prepend(m, mtag); + } + if (frag && (frag->fr_flags & PFFRAG_DROP)) + goto drop; + goto fragment_pass; + } + + no_fragment: + /* At this point, only IP_DF is allowed in ip_off */ +#ifdef __OpenBSD__ + h->ip_off &= IP_DF; +#else + h->ip_off &= htons(IP_DF); +#endif + + /* Enforce a minimum ttl, may cause endless packet loops */ + if (r->min_ttl && h->ip_ttl < r->min_ttl) + h->ip_ttl = r->min_ttl; + + if (r->rule_flag & PFRULE_RANDOMID) { +#ifdef __OpenBSD__ + h->ip_id = ip_randomid(); +#else + h->ip_id = htons(ip_id++); +#endif + } + + return (PF_PASS); + + fragment_pass: + /* Enforce a minimum ttl, may cause endless packet loops */ + if (r->min_ttl && h->ip_ttl < r->min_ttl) + h->ip_ttl = r->min_ttl; + + return (PF_PASS); + + no_mem: + REASON_SET(reason, PFRES_MEMORY); + if (r != NULL && r->log) + PFLOG_PACKET(ifp, h, m, AF_INET, dir, *reason, r, NULL, NULL); + return (PF_DROP); + + drop: + REASON_SET(reason, PFRES_NORM); + if (r != NULL && r->log) + PFLOG_PACKET(ifp, h, m, AF_INET, dir, *reason, r, NULL, NULL); + return (PF_DROP); + + bad: + DPFPRINTF(("dropping bad fragment\n")); + + /* Free assoicated fragments */ + if (frag != NULL) + pf_free_fragment(frag); + + REASON_SET(reason, PFRES_FRAG); + if (r != NULL && r->log) + PFLOG_PACKET(ifp, h, m, AF_INET, dir, *reason, r, NULL, NULL); + + return (PF_DROP); +} + +int +pf_normalize_ip6(struct mbuf **m0, int dir, struct ifnet *ifp, u_short *reason) +{ + struct mbuf *m = *m0; + struct pf_rule *r; + struct ip6_hdr *h = mtod(m, struct ip6_hdr *); + int off; + struct ip6_ext ext; + struct ip6_opt opt; + struct ip6_opt_jumbo jumbo; + struct ip6_frag frag; + u_int32_t jumbolen = 0; + u_int16_t fragoff = 0; + int optend; + int ooff; + u_int8_t proto; + int terminal; + + r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_SCRUB].active.ptr); + while (r != NULL) { + r->evaluations++; + if (r->ifp != NULL && r->ifp != ifp) + r = r->skip[PF_SKIP_IFP].ptr; + else if (r->direction && r->direction != dir) + r = r->skip[PF_SKIP_DIR].ptr; + else if (r->af && r->af != AF_INET6) + r = r->skip[PF_SKIP_AF].ptr; +#if 0 /* header chain! */ + else if (r->proto && r->proto != h->ip6_nxt) + r = r->skip[PF_SKIP_PROTO].ptr; +#endif + else if (PF_MISMATCHAW(&r->src.addr, + (struct pf_addr *)&h->ip6_src, AF_INET6, r->src.not)) + r = r->skip[PF_SKIP_SRC_ADDR].ptr; + else if (PF_MISMATCHAW(&r->dst.addr, + (struct pf_addr *)&h->ip6_dst, AF_INET6, r->dst.not)) + r = r->skip[PF_SKIP_DST_ADDR].ptr; + else + break; + } + + if (r == NULL) + return (PF_PASS); + else + r->packets++; + + /* Check for illegal packets */ + if (ntohs(h->ip6_plen) == 0) { + /* jumbo payload option must be present */ + if (sizeof(struct ip6_hdr) + IPV6_MAXPACKET >= m->m_pkthdr.len) goto drop; + } else if (sizeof(struct ip6_hdr) + ntohs(h->ip6_plen) != + m->m_pkthdr.len) + goto drop; + + off = sizeof(struct ip6_hdr); + proto = h->ip6_nxt; + terminal = 0; + do { + switch (proto) { + case IPPROTO_FRAGMENT: + goto fragment; + break; + case IPPROTO_AH: + case IPPROTO_ROUTING: + case IPPROTO_DSTOPTS: + if (!pf_pull_hdr(m, off, &ext, sizeof(ext), NULL, + NULL, AF_INET6)) + goto shortpkt; + if (proto == IPPROTO_AH) + off += (ext.ip6e_len + 2) * 4; + else + off += (ext.ip6e_len + 1) * 8; + proto = ext.ip6e_nxt; + break; + case IPPROTO_HOPOPTS: + if (!pf_pull_hdr(m, off, &ext, sizeof(ext), NULL, + NULL, AF_INET6)) + goto shortpkt; + optend = off + (ext.ip6e_len + 1) * 8; + ooff = off + sizeof(ext); + do { + if (!pf_pull_hdr(m, ooff, &opt.ip6o_type, + sizeof(opt.ip6o_type), NULL, NULL, + AF_INET6)) + goto shortpkt; + if (opt.ip6o_type == IP6OPT_PAD1) { + ooff++; + continue; + } + if (!pf_pull_hdr(m, ooff, &opt, sizeof(opt), + NULL, NULL, AF_INET6)) + goto shortpkt; + if (ooff + sizeof(opt) + opt.ip6o_len > optend) + goto drop; + switch (opt.ip6o_type) { + case IP6OPT_JUMBO: + if (h->ip6_plen != 0) + goto drop; + if (!pf_pull_hdr(m, ooff, &jumbo, + sizeof(jumbo), NULL, NULL, + AF_INET6)) + goto shortpkt; + memcpy(&jumbolen, jumbo.ip6oj_jumbo_len, + sizeof(jumbolen)); + jumbolen = ntohl(jumbolen); + if (jumbolen <= IPV6_MAXPACKET) + goto drop; + if (sizeof(struct ip6_hdr) + jumbolen != + m->m_pkthdr.len) + goto drop; + break; + default: + break; + } + ooff += sizeof(opt) + opt.ip6o_len; + } while (ooff < optend); + + off = optend; + proto = ext.ip6e_nxt; + break; + default: + terminal = 1; + break; + } + } while (!terminal); + + if (ntohs(h->ip6_plen) == 0 && !jumbolen) + goto drop; + + /* Enforce a minimum ttl, may cause endless packet loops */ + if (r->min_ttl && h->ip6_hlim < r->min_ttl) + h->ip6_hlim = r->min_ttl; + + return (PF_PASS); + + fragment: + if (ntohs(h->ip6_plen) == 0 || jumbolen) + goto drop; + + if (!pf_pull_hdr(m, off, &frag, sizeof(frag), NULL, NULL, AF_INET6)) + goto shortpkt; + fragoff = ntohs(frag.ip6f_offlg & IP6F_OFF_MASK); + if (fragoff + (m->m_pkthdr.len - off - sizeof(frag)) > IPV6_MAXPACKET) + goto badfrag; + + /* do something about it */ + return (PF_PASS); + + shortpkt: + REASON_SET(reason, PFRES_SHORT); + if (r != NULL && r->log) + PFLOG_PACKET(ifp, h, m, AF_INET6, dir, *reason, r, NULL, NULL); + return (PF_DROP); + + drop: + REASON_SET(reason, PFRES_NORM); + if (r != NULL && r->log) + PFLOG_PACKET(ifp, h, m, AF_INET6, dir, *reason, r, NULL, NULL); + return (PF_DROP); + + badfrag: + REASON_SET(reason, PFRES_FRAG); + if (r != NULL && r->log) + PFLOG_PACKET(ifp, h, m, AF_INET6, dir, *reason, r, NULL, NULL); + return (PF_DROP); +} + +int +pf_normalize_tcp(int dir, struct ifnet *ifp, struct mbuf *m, int ipoff, + int off, void *h, struct pf_pdesc *pd) +{ + struct pf_rule *r, *rm = NULL; + struct tcphdr *th = pd->hdr.tcp; + int rewrite = 0; + u_short reason; + u_int8_t flags; + sa_family_t af = pd->af; + + r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_SCRUB].active.ptr); + while (r != NULL) { + r->evaluations++; + if (r->ifp != NULL && r->ifp != ifp) + r = r->skip[PF_SKIP_IFP].ptr; + else if (r->direction && r->direction != dir) + r = r->skip[PF_SKIP_DIR].ptr; + else if (r->af && r->af != af) + r = r->skip[PF_SKIP_AF].ptr; + else if (r->proto && r->proto != pd->proto) + r = r->skip[PF_SKIP_PROTO].ptr; + else if (PF_MISMATCHAW(&r->src.addr, pd->src, af, r->src.not)) + r = r->skip[PF_SKIP_SRC_ADDR].ptr; + else if (r->src.port_op && !pf_match_port(r->src.port_op, + r->src.port[0], r->src.port[1], th->th_sport)) + r = r->skip[PF_SKIP_SRC_PORT].ptr; + else if (PF_MISMATCHAW(&r->dst.addr, pd->dst, af, r->dst.not)) + r = r->skip[PF_SKIP_DST_ADDR].ptr; + else if (r->dst.port_op && !pf_match_port(r->dst.port_op, + r->dst.port[0], r->dst.port[1], th->th_dport)) + r = r->skip[PF_SKIP_DST_PORT].ptr; + else { + rm = r; + break; + } + } + + if (rm == NULL) + return (PF_PASS); + else + r->packets++; + + if (rm->rule_flag & PFRULE_REASSEMBLE_TCP) + pd->flags |= PFDESC_TCP_NORM; + + flags = th->th_flags; + if (flags & TH_SYN) { + /* Illegal packet */ + if (flags & TH_RST) + goto tcp_drop; + + if (flags & TH_FIN) + flags &= ~TH_FIN; + } else { + /* Illegal packet */ + if (!(flags & (TH_ACK|TH_RST))) + goto tcp_drop; + } + + if (!(flags & TH_ACK)) { + /* These flags are only valid if ACK is set */ + if ((flags & TH_FIN) || (flags & TH_PUSH) || (flags & TH_URG)) + goto tcp_drop; + } + + /* Check for illegal header length */ + if (th->th_off < (sizeof(struct tcphdr) >> 2)) + goto tcp_drop; + + /* If flags changed, or reserved data set, then adjust */ + if (flags != th->th_flags || th->th_x2 != 0) { + u_int16_t ov, nv; + + ov = *(u_int16_t *)(&th->th_ack + 1); + th->th_flags = flags; + th->th_x2 = 0; + nv = *(u_int16_t *)(&th->th_ack + 1); + + th->th_sum = pf_cksum_fixup(th->th_sum, ov, nv); + rewrite = 1; + } + + /* Remove urgent pointer, if TH_URG is not set */ + if (!(flags & TH_URG) && th->th_urp) { + th->th_sum = pf_cksum_fixup(th->th_sum, th->th_urp, 0); + th->th_urp = 0; + rewrite = 1; + } + + /* Process options */ + if (r->max_mss && pf_normalize_tcpopt(r, m, th, off)) + rewrite = 1; + + /* copy back packet headers if we sanitized */ + if (rewrite) + m_copyback(m, off, sizeof(*th), (caddr_t)th); + + return (PF_PASS); + + tcp_drop: + REASON_SET(&reason, PFRES_NORM); + if (rm != NULL && r->log) + PFLOG_PACKET(ifp, h, m, AF_INET, dir, reason, r, NULL, NULL); + return (PF_DROP); +} + +int +pf_normalize_tcp_init(struct mbuf *m, int off, struct pf_pdesc *pd, + struct tcphdr *th, struct pf_state_peer *src, struct pf_state_peer *dst) +{ + u_int8_t hdr[60]; + u_int8_t *opt; + + KASSERT(src->scrub == NULL); + + src->scrub = pool_get(&pf_state_scrub_pl, PR_NOWAIT); + if (src->scrub == NULL) + return (1); + bzero(src->scrub, sizeof(*src->scrub)); + + switch (pd->af) { +#ifdef INET + case AF_INET: { + struct ip *h = mtod(m, struct ip *); + src->scrub->pfss_ttl = h->ip_ttl; + break; + } +#endif /* INET */ +#ifdef INET6 + case AF_INET6: { + struct ip6_hdr *h = mtod(m, struct ip6_hdr *); + src->scrub->pfss_ttl = h->ip6_hlim; + break; + } +#endif /* INET6 */ + } + + + /* + * All normalizations below are only begun if we see the start of + * the connections. They must all set an enabled bit in pfss_flags + */ + if ((th->th_flags & TH_SYN) == 0) + return 0; + + + if (th->th_off > (sizeof(struct tcphdr) >> 2) && src->scrub && + pf_pull_hdr(m, off, hdr, th->th_off << 2, NULL, NULL, pd->af)) { + /* Diddle with TCP options */ + int hlen; + opt = hdr + sizeof(struct tcphdr); + hlen = (th->th_off << 2) - sizeof(struct tcphdr); + while (hlen >= TCPOLEN_TIMESTAMP) { + switch (*opt) { + case TCPOPT_EOL: /* FALLTHROUH */ + case TCPOPT_NOP: + opt++; + hlen--; + break; + case TCPOPT_TIMESTAMP: + if (opt[1] >= TCPOLEN_TIMESTAMP) { + src->scrub->pfss_flags |= + PFSS_TIMESTAMP; + src->scrub->pfss_ts_mod = arc4random(); + } + /* FALLTHROUGH */ + default: + hlen -= opt[1]; + opt += opt[1]; + break; + } + } + } + + return (0); +} + +void +pf_normalize_tcp_cleanup(struct pf_state *state) +{ + if (state->src.scrub) + pool_put(&pf_state_scrub_pl, state->src.scrub); + if (state->dst.scrub) + pool_put(&pf_state_scrub_pl, state->dst.scrub); + + /* Someday... flush the TCP segment reassembly descriptors. */ +} + +int +pf_normalize_tcp_stateful(struct mbuf *m, int off, struct pf_pdesc *pd, + u_short *reason, struct tcphdr *th, struct pf_state_peer *src, + struct pf_state_peer *dst, int *writeback) +{ + u_int8_t hdr[60]; + u_int8_t *opt; + int copyback = 0; + + KASSERT(src->scrub || dst->scrub); + + /* + * Enforce the minimum TTL seen for this connection. Negate a common + * technique to evade an intrusion detection system and confuse + * firewall state code. + */ + switch (pd->af) { +#ifdef INET + case AF_INET: { + if (src->scrub) { + struct ip *h = mtod(m, struct ip *); + if (h->ip_ttl > src->scrub->pfss_ttl) + src->scrub->pfss_ttl = h->ip_ttl; + h->ip_ttl = src->scrub->pfss_ttl; + } + break; + } +#endif /* INET */ +#ifdef INET6 + case AF_INET6: { + if (dst->scrub) { + struct ip6_hdr *h = mtod(m, struct ip6_hdr *); + if (h->ip6_hlim > src->scrub->pfss_ttl) + src->scrub->pfss_ttl = h->ip6_hlim; + h->ip6_hlim = src->scrub->pfss_ttl; + } + break; + } +#endif /* INET6 */ + } + + if (th->th_off > (sizeof(struct tcphdr) >> 2) && + ((src->scrub && (src->scrub->pfss_flags & PFSS_TIMESTAMP)) || + (dst->scrub && (dst->scrub->pfss_flags & PFSS_TIMESTAMP))) && + pf_pull_hdr(m, off, hdr, th->th_off << 2, NULL, NULL, pd->af)) { + /* Diddle with TCP options */ + int hlen; + opt = hdr + sizeof(struct tcphdr); + hlen = (th->th_off << 2) - sizeof(struct tcphdr); + while (hlen >= TCPOLEN_TIMESTAMP) { + switch (*opt) { + case TCPOPT_EOL: /* FALLTHROUH */ + case TCPOPT_NOP: + opt++; + hlen--; + break; + case TCPOPT_TIMESTAMP: + /* Modulate the timestamps. Can be used for + * NAT detection, OS uptime determination or + * reboot detection. + */ + if (opt[1] >= TCPOLEN_TIMESTAMP) { + u_int32_t ts_value; + if (src->scrub && + (src->scrub->pfss_flags & + PFSS_TIMESTAMP)) { + memcpy(&ts_value, &opt[2], + sizeof(u_int32_t)); + ts_value = htonl(ntohl(ts_value) + + src->scrub->pfss_ts_mod); + pf_change_a(&opt[2], + &th->th_sum, ts_value, 0); + copyback = 1; + } + if (dst->scrub && + (dst->scrub->pfss_flags & + PFSS_TIMESTAMP)) { + memcpy(&ts_value, &opt[6], + sizeof(u_int32_t)); + ts_value = htonl(ntohl(ts_value) + - dst->scrub->pfss_ts_mod); + pf_change_a(&opt[6], + &th->th_sum, ts_value, 0); + copyback = 1; + } + } + /* FALLTHROUGH */ + default: + hlen -= opt[1]; + opt += opt[1]; + break; + } + } + if (copyback) { + /* Copyback the options, caller copys back header */ + *writeback = 1; + m_copyback(m, off + sizeof(struct tcphdr), + (th->th_off << 2) - sizeof(struct tcphdr), hdr + + sizeof(struct tcphdr)); + } + } + + + /* I have a dream.... TCP segment reassembly.... */ + return (0); +} +int +pf_normalize_tcpopt(struct pf_rule *r, struct mbuf *m, struct tcphdr *th, + int off) +{ + u_int16_t *mss; + int thoff; + int opt, cnt, optlen = 0; + int rewrite = 0; + u_char *optp; + + thoff = th->th_off << 2; + cnt = thoff - sizeof(struct tcphdr); + optp = mtod(m, caddr_t) + off + sizeof(struct tcphdr); + + for (; cnt > 0; cnt -= optlen, optp += optlen) { + opt = optp[0]; + if (opt == TCPOPT_EOL) + break; + if (opt == TCPOPT_NOP) + optlen = 1; + else { + if (cnt < 2) + break; + optlen = optp[1]; + if (optlen < 2 || optlen > cnt) + break; + } + switch (opt) { + case TCPOPT_MAXSEG: + mss = (u_int16_t *)(optp + 2); + if ((ntohs(*mss)) > r->max_mss) { + th->th_sum = pf_cksum_fixup(th->th_sum, + *mss, htons(r->max_mss)); + *mss = htons(r->max_mss); + rewrite = 1; + } + break; + default: + break; + } + } + + return (rewrite); +} Index: sys/net/pf_table.c =================================================================== RCS file: pf_table.c diff -N pf_table.c --- /dev/null Mon Jun 30 04:58:52 2003 +++ pf_table.c Mon Jun 30 04:58:39 2003 @@ -0,0 +1,1889 @@ +/* $NetBSD$ */ +/* $OpenBSD: pf_table.c,v 1.38 2003/06/24 13:52:50 henning Exp $ */ + +/* + * Copyright (c) 2002 Cedric Berger + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDERS 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. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef __OpenBSD__ +#include +#endif +#include + +#define ACCEPT_FLAGS(oklist) \ + do { \ + if ((flags & ~(oklist)) & \ + PFR_FLAG_ALLMASK) \ + return (EINVAL); \ + } while (0) + +#define FILLIN_SIN(sin, addr) \ + do { \ + (sin).sin_len = sizeof(sin); \ + (sin).sin_family = AF_INET; \ + (sin).sin_addr = (addr); \ + } while (0) + +#define FILLIN_SIN6(sin6, addr) \ + do { \ + (sin6).sin6_len = sizeof(sin6); \ + (sin6).sin6_family = AF_INET6; \ + (sin6).sin6_addr = (addr); \ + } while (0) + +#define SWAP(type, a1, a2) \ + do { \ + type tmp = a1; \ + a1 = a2; \ + a2 = tmp; \ + } while (0) + +#define AF_BITS(af) (((af)==AF_INET)?32:128) +#define ADDR_NETWORK(ad) ((ad)->pfra_net < AF_BITS((ad)->pfra_af)) +#define KENTRY_NETWORK(ke) ((ke)->pfrke_net < AF_BITS((ke)->pfrke_af)) +#define KENTRY_RNF_ROOT(ke) \ + ((((struct radix_node *)(ke))->rn_flags & RNF_ROOT) != 0) + +#define NO_ADDRESSES (-1) +#define ENQUEUE_UNMARKED_ONLY (1) +#define INVERT_NEG_FLAG (1) + +struct pfr_walktree { + enum pfrw_op { + PFRW_MARK, + PFRW_SWEEP, + PFRW_ENQUEUE, + PFRW_GET_ADDRS, + PFRW_GET_ASTATS + } pfrw_op; + union { + struct pfr_addr *pfrw1_addr; + struct pfr_astats *pfrw1_astats; + struct pfr_kentryworkq *pfrw1_workq; + } pfrw_1; + int pfrw_free; +}; +#define pfrw_addr pfrw_1.pfrw1_addr +#define pfrw_astats pfrw_1.pfrw1_astats +#define pfrw_workq pfrw_1.pfrw1_workq +#define pfrw_cnt pfrw_free + +#define senderr(e) do { rv = (e); goto _bad; } while (0) + +struct pool pfr_ktable_pl; +struct pool pfr_kentry_pl; +struct sockaddr_in pfr_sin; +struct sockaddr_in6 pfr_sin6; + +void pfr_copyout_addr(struct pfr_addr *, + struct pfr_kentry *ke); +int pfr_validate_addr(struct pfr_addr *); +void pfr_enqueue_addrs(struct pfr_ktable *, + struct pfr_kentryworkq *, int *, int); +void pfr_mark_addrs(struct pfr_ktable *); +struct pfr_kentry *pfr_lookup_addr(struct pfr_ktable *, + struct pfr_addr *, int); +struct pfr_kentry *pfr_create_kentry(struct pfr_addr *); +void pfr_destroy_kentries(struct pfr_kentryworkq *); +void pfr_destroy_kentry(struct pfr_kentry *); +void pfr_insert_kentries(struct pfr_ktable *, + struct pfr_kentryworkq *, long); +void pfr_remove_kentries(struct pfr_ktable *, + struct pfr_kentryworkq *); +void pfr_clstats_kentries(struct pfr_kentryworkq *, long, + int); +void pfr_reset_feedback(struct pfr_addr *, int); +void pfr_prepare_network(union sockaddr_union *, int, int); +int pfr_route_kentry(struct pfr_ktable *, + struct pfr_kentry *); +int pfr_unroute_kentry(struct pfr_ktable *, + struct pfr_kentry *); +int pfr_walktree(struct radix_node *, void *); +int pfr_validate_table(struct pfr_table *, int); +void pfr_commit_ktable(struct pfr_ktable *, long); +void pfr_insert_ktables(struct pfr_ktableworkq *); +void pfr_insert_ktable(struct pfr_ktable *); +void pfr_setflags_ktables(struct pfr_ktableworkq *); +void pfr_setflags_ktable(struct pfr_ktable *, int); +void pfr_clstats_ktables(struct pfr_ktableworkq *, long, + int); +void pfr_clstats_ktable(struct pfr_ktable *, long, int); +struct pfr_ktable *pfr_create_ktable(struct pfr_table *, long, int); +void pfr_destroy_ktables(struct pfr_ktableworkq *, int); +void pfr_destroy_ktable(struct pfr_ktable *, int); +int pfr_ktable_compare(struct pfr_ktable *, + struct pfr_ktable *); +struct pfr_ktable *pfr_lookup_table(struct pfr_table *); +void pfr_clean_node_mask(struct pfr_ktable *, + struct pfr_kentryworkq *); +int pfr_table_count(struct pfr_table *, int); +int pfr_skip_table(struct pfr_table *, + struct pfr_ktable *, int); + +RB_PROTOTYPE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare); +RB_GENERATE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare); + +struct pfr_ktablehead pfr_ktables; +struct pfr_table pfr_nulltable; +int pfr_ktable_cnt; +int pfr_ticket; + +void +pfr_initialize(void) +{ + pool_init(&pfr_ktable_pl, sizeof(struct pfr_ktable), 0, 0, 0, + "pfrktable", NULL); + pool_init(&pfr_kentry_pl, sizeof(struct pfr_kentry), 0, 0, 0, + "pfrkentry", NULL); + + pfr_sin.sin_len = sizeof(pfr_sin); + pfr_sin.sin_family = AF_INET; + pfr_sin6.sin6_len = sizeof(pfr_sin6); + pfr_sin6.sin6_family = AF_INET6; + + pfr_ticket = 100; +} + +int +pfr_clr_addrs(struct pfr_table *tbl, int *ndel, int flags) +{ + struct pfr_ktable *kt; + struct pfr_kentryworkq workq; + int s; + + ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY); + if (pfr_validate_table(tbl, 0)) + return (EINVAL); + kt = pfr_lookup_table(tbl); + if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return (ESRCH); + if (kt->pfrkt_flags & PFR_TFLAG_CONST) + return (EPERM); + pfr_enqueue_addrs(kt, &workq, ndel, 0); + + if (!(flags & PFR_FLAG_DUMMY)) { + if (flags & PFR_FLAG_ATOMIC) + s = splsoftnet(); + pfr_remove_kentries(kt, &workq); + if (flags & PFR_FLAG_ATOMIC) + splx(s); + if (kt->pfrkt_cnt) { + printf("pfr_clr_addrs: corruption detected (%d).\n", + kt->pfrkt_cnt); + kt->pfrkt_cnt = 0; + } + } + return (0); +} + +int +pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, + int *nadd, int flags) +{ + struct pfr_ktable *kt, *tmpkt; + struct pfr_kentryworkq workq; + struct pfr_kentry *p, *q; + struct pfr_addr ad; + int i, rv, s, xadd = 0; + long tzero = time.tv_sec; + + ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY+PFR_FLAG_FEEDBACK); + if (pfr_validate_table(tbl, 0)) + return (EINVAL); + kt = pfr_lookup_table(tbl); + if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return (ESRCH); + if (kt->pfrkt_flags & PFR_TFLAG_CONST) + return (EPERM); + tmpkt = pfr_create_ktable(&pfr_nulltable, 0, 0); + if (tmpkt == NULL) + return (ENOMEM); + SLIST_INIT(&workq); + for (i = 0; i < size; i++) { + if (copyin(addr+i, &ad, sizeof(ad))) + senderr(EFAULT); + if (pfr_validate_addr(&ad)) + senderr(EINVAL); + p = pfr_lookup_addr(kt, &ad, 1); + q = pfr_lookup_addr(tmpkt, &ad, 1); + if (flags & PFR_FLAG_FEEDBACK) { + if (q != NULL) + ad.pfra_fback = PFR_FB_DUPLICATE; + else if (p == NULL) + ad.pfra_fback = PFR_FB_ADDED; + else if (p->pfrke_not != ad.pfra_not) + ad.pfra_fback = PFR_FB_CONFLICT; + else + ad.pfra_fback = PFR_FB_NONE; + } + if (p == NULL && q == NULL) { + p = pfr_create_kentry(&ad); + if (p == NULL) + senderr(ENOMEM); + if (pfr_route_kentry(tmpkt, p)) { + pfr_destroy_kentry(p); + ad.pfra_fback = PFR_FB_NONE; + } else { + SLIST_INSERT_HEAD(&workq, p, pfrke_workq); + xadd++; + } + } + if (flags & PFR_FLAG_FEEDBACK) + if (copyout(&ad, addr+i, sizeof(ad))) + senderr(EFAULT); + } + pfr_clean_node_mask(tmpkt, &workq); + if (!(flags & PFR_FLAG_DUMMY)) { + if (flags & PFR_FLAG_ATOMIC) + s = splsoftnet(); + pfr_insert_kentries(kt, &workq, tzero); + if (flags & PFR_FLAG_ATOMIC) + splx(s); + } else + pfr_destroy_kentries(&workq); + if (nadd != NULL) + *nadd = xadd; + pfr_destroy_ktable(tmpkt, 0); + return (0); +_bad: + pfr_clean_node_mask(tmpkt, &workq); + pfr_destroy_kentries(&workq); + if (flags & PFR_FLAG_FEEDBACK) + pfr_reset_feedback(addr, size); + pfr_destroy_ktable(tmpkt, 0); + return (rv); +} + +int +pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, + int *ndel, int flags) +{ + struct pfr_ktable *kt; + struct pfr_kentryworkq workq; + struct pfr_kentry *p; + struct pfr_addr ad; + int i, rv, s, xdel = 0; + + ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY+PFR_FLAG_FEEDBACK); + if (pfr_validate_table(tbl, 0)) + return (EINVAL); + kt = pfr_lookup_table(tbl); + if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return (ESRCH); + if (kt->pfrkt_flags & PFR_TFLAG_CONST) + return (EPERM); + pfr_mark_addrs(kt); + SLIST_INIT(&workq); + for (i = 0; i < size; i++) { + if (copyin(addr+i, &ad, sizeof(ad))) + senderr(EFAULT); + if (pfr_validate_addr(&ad)) + senderr(EINVAL); + p = pfr_lookup_addr(kt, &ad, 1); + if (flags & PFR_FLAG_FEEDBACK) { + if (p == NULL) + ad.pfra_fback = PFR_FB_NONE; + else if (p->pfrke_not != ad.pfra_not) + ad.pfra_fback = PFR_FB_CONFLICT; + else if (p->pfrke_mark) + ad.pfra_fback = PFR_FB_DUPLICATE; + else + ad.pfra_fback = PFR_FB_DELETED; + } + if (p != NULL && p->pfrke_not == ad.pfra_not && + !p->pfrke_mark) { + p->pfrke_mark = 1; + SLIST_INSERT_HEAD(&workq, p, pfrke_workq); + xdel++; + } + if (flags & PFR_FLAG_FEEDBACK) + if (copyout(&ad, addr+i, sizeof(ad))) + senderr(EFAULT); + } + if (!(flags & PFR_FLAG_DUMMY)) { + if (flags & PFR_FLAG_ATOMIC) + s = splsoftnet(); + pfr_remove_kentries(kt, &workq); + if (flags & PFR_FLAG_ATOMIC) + splx(s); + } + if (ndel != NULL) + *ndel = xdel; + return (0); +_bad: + if (flags & PFR_FLAG_FEEDBACK) + pfr_reset_feedback(addr, size); + return (rv); +} + +int +pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, + int *size2, int *nadd, int *ndel, int *nchange, int flags) +{ + struct pfr_ktable *kt, *tmpkt; + struct pfr_kentryworkq addq, delq, changeq; + struct pfr_kentry *p, *q; + struct pfr_addr ad; + int i, rv, s, xadd = 0, xdel = 0, xchange = 0; + long tzero = time.tv_sec; + + ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY+PFR_FLAG_FEEDBACK); + if (pfr_validate_table(tbl, 0)) + return (EINVAL); + kt = pfr_lookup_table(tbl); + if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return (ESRCH); + if (kt->pfrkt_flags & PFR_TFLAG_CONST) + return (EPERM); + tmpkt = pfr_create_ktable(&pfr_nulltable, 0, 0); + if (tmpkt == NULL) + return (ENOMEM); + pfr_mark_addrs(kt); + SLIST_INIT(&addq); + SLIST_INIT(&delq); + SLIST_INIT(&changeq); + for (i = 0; i < size; i++) { + if (copyin(addr+i, &ad, sizeof(ad))) + senderr(EFAULT); + if (pfr_validate_addr(&ad)) + senderr(EINVAL); + ad.pfra_fback = PFR_FB_NONE; + p = pfr_lookup_addr(kt, &ad, 1); + if (p != NULL) { + if (p->pfrke_mark) { + ad.pfra_fback = PFR_FB_DUPLICATE; + goto _skip; + } + p->pfrke_mark = 1; + if (p->pfrke_not != ad.pfra_not) { + SLIST_INSERT_HEAD(&changeq, p, pfrke_workq); + ad.pfra_fback = PFR_FB_CHANGED; + xchange++; + } + } else { + q = pfr_lookup_addr(tmpkt, &ad, 1); + if (q != NULL) { + ad.pfra_fback = PFR_FB_DUPLICATE; + goto _skip; + } + p = pfr_create_kentry(&ad); + if (p == NULL) + senderr(ENOMEM); + if (pfr_route_kentry(tmpkt, p)) { + pfr_destroy_kentry(p); + ad.pfra_fback = PFR_FB_NONE; + } else { + SLIST_INSERT_HEAD(&addq, p, pfrke_workq); + ad.pfra_fback = PFR_FB_ADDED; + xadd++; + } + } +_skip: + if (flags & PFR_FLAG_FEEDBACK) + if (copyout(&ad, addr+i, sizeof(ad))) + senderr(EFAULT); + } + pfr_enqueue_addrs(kt, &delq, &xdel, ENQUEUE_UNMARKED_ONLY); + if ((flags & PFR_FLAG_FEEDBACK) && *size2) { + if (*size2 < size+xdel) { + *size2 = size+xdel; + senderr(0); + } + i = 0; + SLIST_FOREACH(p, &delq, pfrke_workq) { + pfr_copyout_addr(&ad, p); + ad.pfra_fback = PFR_FB_DELETED; + if (copyout(&ad, addr+size+i, sizeof(ad))) + senderr(EFAULT); + i++; + } + } + pfr_clean_node_mask(tmpkt, &addq); + if (!(flags & PFR_FLAG_DUMMY)) { + if (flags & PFR_FLAG_ATOMIC) + s = splsoftnet(); + pfr_insert_kentries(kt, &addq, tzero); + pfr_remove_kentries(kt, &delq); + pfr_clstats_kentries(&changeq, tzero, INVERT_NEG_FLAG); + if (flags & PFR_FLAG_ATOMIC) + splx(s); + } else + pfr_destroy_kentries(&addq); + if (nadd != NULL) + *nadd = xadd; + if (ndel != NULL) + *ndel = xdel; + if (nchange != NULL) + *nchange = xchange; + if ((flags & PFR_FLAG_FEEDBACK) && *size2) + *size2 = size+xdel; + pfr_destroy_ktable(tmpkt, 0); + return (0); +_bad: + pfr_clean_node_mask(tmpkt, &addq); + pfr_destroy_kentries(&addq); + if (flags & PFR_FLAG_FEEDBACK) + pfr_reset_feedback(addr, size); + pfr_destroy_ktable(tmpkt, 0); + return (rv); +} + +int +pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, + int *nmatch, int flags) +{ + struct pfr_ktable *kt; + struct pfr_kentry *p; + struct pfr_addr ad; + int i, xmatch = 0; + + ACCEPT_FLAGS(PFR_FLAG_REPLACE); + if (pfr_validate_table(tbl, 0)) + return (EINVAL); + kt = pfr_lookup_table(tbl); + if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return (ESRCH); + + for (i = 0; i < size; i++) { + if (copyin(addr+i, &ad, sizeof(ad))) + return (EFAULT); + if (pfr_validate_addr(&ad)) + return (EINVAL); + if (ADDR_NETWORK(&ad)) + return (EINVAL); + p = pfr_lookup_addr(kt, &ad, 0); + if (flags & PFR_FLAG_REPLACE) + pfr_copyout_addr(&ad, p); + ad.pfra_fback = (p == NULL) ? PFR_FB_NONE : + (p->pfrke_not ? PFR_FB_NOTMATCH : PFR_FB_MATCH); + if (p != NULL && !p->pfrke_not) + xmatch++; + if (copyout(&ad, addr+i, sizeof(ad))) + return (EFAULT); + } + if (nmatch != NULL) + *nmatch = xmatch; + return (0); +} + +int +pfr_get_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int *size, + int flags) +{ + struct pfr_ktable *kt; + struct pfr_walktree w; + int rv; + + ACCEPT_FLAGS(0); + if (pfr_validate_table(tbl, 0)) + return (EINVAL); + kt = pfr_lookup_table(tbl); + if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return (ESRCH); + if (kt->pfrkt_cnt > *size) { + *size = kt->pfrkt_cnt; + return (0); + } + + bzero(&w, sizeof(w)); + w.pfrw_op = PFRW_GET_ADDRS; + w.pfrw_addr = addr; + w.pfrw_free = kt->pfrkt_cnt; + rv = rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w); + if (!rv) + rv = rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w); + if (rv) + return (rv); + + if (w.pfrw_free) { + printf("pfr_get_addrs: corruption detected (%d).\n", + w.pfrw_free); + return (ENOTTY); + } + *size = kt->pfrkt_cnt; + return (0); +} + +int +pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size, + int flags) +{ + struct pfr_ktable *kt; + struct pfr_walktree w; + struct pfr_kentryworkq workq; + int rv, s; + long tzero = time.tv_sec; + + ACCEPT_FLAGS(PFR_FLAG_ATOMIC); /* XXX PFR_FLAG_CLSTATS disabled */ + if (pfr_validate_table(tbl, 0)) + return (EINVAL); + kt = pfr_lookup_table(tbl); + if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return (ESRCH); + if (kt->pfrkt_cnt > *size) { + *size = kt->pfrkt_cnt; + return (0); + } + + bzero(&w, sizeof(w)); + w.pfrw_op = PFRW_GET_ASTATS; + w.pfrw_astats = addr; + w.pfrw_free = kt->pfrkt_cnt; + if (flags & PFR_FLAG_ATOMIC) + s = splsoftnet(); + rv = rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w); + if (!rv) + rv = rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w); + if (!rv && (flags & PFR_FLAG_CLSTATS)) { + pfr_enqueue_addrs(kt, &workq, NULL, 0); + pfr_clstats_kentries(&workq, tzero, 0); + } + if (flags & PFR_FLAG_ATOMIC) + splx(s); + if (rv) + return (rv); + + if (w.pfrw_free) { + printf("pfr_get_astats: corruption detected (%d).\n", + w.pfrw_free); + return (ENOTTY); + } + *size = kt->pfrkt_cnt; + return (0); +} + +int +pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size, + int *nzero, int flags) +{ + struct pfr_ktable *kt; + struct pfr_kentryworkq workq; + struct pfr_kentry *p; + struct pfr_addr ad; + int i, rv, s, xzero = 0; + + ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY+PFR_FLAG_FEEDBACK); + if (pfr_validate_table(tbl, 0)) + return (EINVAL); + kt = pfr_lookup_table(tbl); + if (kt == NULL || !(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return (ESRCH); + SLIST_INIT(&workq); + for (i = 0; i < size; i++) { + if (copyin(addr+i, &ad, sizeof(ad))) + senderr(EFAULT); + if (pfr_validate_addr(&ad)) + senderr(EINVAL); + p = pfr_lookup_addr(kt, &ad, 1); + if (flags & PFR_FLAG_FEEDBACK) { + ad.pfra_fback = (p != NULL) ? + PFR_FB_CLEARED : PFR_FB_NONE; + if (copyout(&ad, addr+i, sizeof(ad))) + senderr(EFAULT); + } + if (p != NULL) { + SLIST_INSERT_HEAD(&workq, p, pfrke_workq); + xzero++; + } + } + + if (!(flags & PFR_FLAG_DUMMY)) { + if (flags & PFR_FLAG_ATOMIC) + s = splsoftnet(); + pfr_clstats_kentries(&workq, 0, 0); + if (flags & PFR_FLAG_ATOMIC) + splx(s); + } + if (nzero != NULL) + *nzero = xzero; + return (0); +_bad: + if (flags & PFR_FLAG_FEEDBACK) + pfr_reset_feedback(addr, size); + return (rv); +} + +int +pfr_validate_addr(struct pfr_addr *ad) +{ + int i; + + switch (ad->pfra_af) { + case AF_INET: + if (ad->pfra_net > 32) + return (-1); + break; + case AF_INET6: + if (ad->pfra_net > 128) + return (-1); + break; + default: + return (-1); + } + if (ad->pfra_net < 128 && + (((caddr_t)ad)[ad->pfra_net/8] & (0xFF >> (ad->pfra_net%8)))) + return (-1); + for (i = (ad->pfra_net+7)/8; i < sizeof(ad->pfra_u); i++) + if (((caddr_t)ad)[i]) + return (-1); + if (ad->pfra_not && ad->pfra_not != 1) + return (-1); + if (ad->pfra_fback) + return (-1); + return (0); +} + +void +pfr_enqueue_addrs(struct pfr_ktable *kt, struct pfr_kentryworkq *workq, + int *naddr, int sweep) +{ + struct pfr_walktree w; + + SLIST_INIT(workq); + bzero(&w, sizeof(w)); + w.pfrw_op = sweep ? PFRW_SWEEP : PFRW_ENQUEUE; + w.pfrw_workq = workq; + if (kt->pfrkt_ip4 != NULL) + if (rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w)) + printf("pfr_enqueue_addrs: IPv4 walktree failed.\n"); + if (kt->pfrkt_ip6 != NULL) + if (rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w)) + printf("pfr_enqueue_addrs: IPv6 walktree failed.\n"); + if (naddr != NULL) + *naddr = w.pfrw_cnt; +} + +void +pfr_mark_addrs(struct pfr_ktable *kt) +{ + struct pfr_walktree w; + + bzero(&w, sizeof(w)); + w.pfrw_op = PFRW_MARK; + if (rn_walktree(kt->pfrkt_ip4, pfr_walktree, &w)) + printf("pfr_mark_addrs: IPv4 walktree failed.\n"); + if (rn_walktree(kt->pfrkt_ip6, pfr_walktree, &w)) + printf("pfr_mark_addrs: IPv6 walktree failed.\n"); +} + + +struct pfr_kentry * +pfr_lookup_addr(struct pfr_ktable *kt, struct pfr_addr *ad, int exact) +{ + union sockaddr_union sa, mask; + struct radix_node_head *head; + struct pfr_kentry *ke; + int s; + + bzero(&sa, sizeof(sa)); + if (ad->pfra_af == AF_INET) { + FILLIN_SIN(sa.sin, ad->pfra_ip4addr); + head = kt->pfrkt_ip4; + } else { + FILLIN_SIN6(sa.sin6, ad->pfra_ip6addr); + head = kt->pfrkt_ip6; + } + if (ADDR_NETWORK(ad)) { + pfr_prepare_network(&mask, ad->pfra_af, ad->pfra_net); + s = splsoftnet(); /* rn_lookup makes use of globals */ + ke = (struct pfr_kentry *)rn_lookup(&sa, &mask, head); + splx(s); + if (ke && KENTRY_RNF_ROOT(ke)) + ke = NULL; + } else { + ke = (struct pfr_kentry *)rn_match(&sa, head); + if (ke && KENTRY_RNF_ROOT(ke)) + ke = NULL; + if (exact && ke && KENTRY_NETWORK(ke)) + ke = NULL; + } + return (ke); +} + +struct pfr_kentry * +pfr_create_kentry(struct pfr_addr *ad) +{ + struct pfr_kentry *ke; + + ke = pool_get(&pfr_kentry_pl, PR_NOWAIT); + if (ke == NULL) + return (NULL); + bzero(ke, sizeof(*ke)); + + if (ad->pfra_af == AF_INET) + FILLIN_SIN(ke->pfrke_sa.sin, ad->pfra_ip4addr); + else + FILLIN_SIN6(ke->pfrke_sa.sin6, ad->pfra_ip6addr); + ke->pfrke_af = ad->pfra_af; + ke->pfrke_net = ad->pfra_net; + ke->pfrke_not = ad->pfra_not; + return (ke); +} + +void +pfr_destroy_kentries(struct pfr_kentryworkq *workq) +{ + struct pfr_kentry *p, *q; + + for (p = SLIST_FIRST(workq); p != NULL; p = q) { + q = SLIST_NEXT(p, pfrke_workq); + pfr_destroy_kentry(p); + } +} + +void +pfr_destroy_kentry(struct pfr_kentry *ke) +{ + pool_put(&pfr_kentry_pl, ke); +} + +void +pfr_insert_kentries(struct pfr_ktable *kt, + struct pfr_kentryworkq *workq, long tzero) +{ + struct pfr_kentry *p; + int rv, n = 0; + + SLIST_FOREACH(p, workq, pfrke_workq) { + rv = pfr_route_kentry(kt, p); + if (rv) { + printf("pfr_insert_kentries: cannot route entry " + "(code=%d).\n", rv); + break; + } + p->pfrke_tzero = tzero; + n++; + } + kt->pfrkt_cnt += n; +} + +void +pfr_remove_kentries(struct pfr_ktable *kt, + struct pfr_kentryworkq *workq) +{ + struct pfr_kentry *p; + int n = 0; + + SLIST_FOREACH(p, workq, pfrke_workq) { + pfr_unroute_kentry(kt, p); + n++; + } + kt->pfrkt_cnt -= n; + pfr_destroy_kentries(workq); +} + +void +pfr_clean_node_mask(struct pfr_ktable *kt, + struct pfr_kentryworkq *workq) +{ + struct pfr_kentry *p; + + SLIST_FOREACH(p, workq, pfrke_workq) + pfr_unroute_kentry(kt, p); +} + +void +pfr_clstats_kentries(struct pfr_kentryworkq *workq, long tzero, int negchange) +{ + struct pfr_kentry *p; + int s; + + SLIST_FOREACH(p, workq, pfrke_workq) { + s = splsoftnet(); + if (negchange) + p->pfrke_not = !p->pfrke_not; + bzero(p->pfrke_packets, sizeof(p->pfrke_packets)); + bzero(p->pfrke_bytes, sizeof(p->pfrke_bytes)); + splx(s); + p->pfrke_tzero = tzero; + } +} + +void +pfr_reset_feedback(struct pfr_addr *addr, int size) +{ + struct pfr_addr ad; + int i; + + for (i = 0; i < size; i++) { + if (copyin(addr+i, &ad, sizeof(ad))) + break; + ad.pfra_fback = PFR_FB_NONE; + if (copyout(&ad, addr+i, sizeof(ad))) + break; + } +} + +void +pfr_prepare_network(union sockaddr_union *sa, int af, int net) +{ + int i; + + bzero(sa, sizeof(*sa)); + if (af == AF_INET) { + sa->sin.sin_len = sizeof(sa->sin); + sa->sin.sin_family = AF_INET; + sa->sin.sin_addr.s_addr = htonl(-1 << (32-net)); + } else { + sa->sin6.sin6_len = sizeof(sa->sin6); + sa->sin6.sin6_family = AF_INET6; + for (i = 0; i < 4; i++) { + if (net <= 32) { + sa->sin6.sin6_addr.s6_addr32[i] = + htonl(-1 << (32-net)); + break; + } + sa->sin6.sin6_addr.s6_addr32[i] = 0xFFFFFFFF; + net -= 32; + } + } +} + +int +pfr_route_kentry(struct pfr_ktable *kt, struct pfr_kentry *ke) +{ + union sockaddr_union mask; + struct radix_node *rn; + struct radix_node_head *head; + int s; + + bzero(ke->pfrke_node, sizeof(ke->pfrke_node)); + if (ke->pfrke_af == AF_INET) + head = kt->pfrkt_ip4; + else + head = kt->pfrkt_ip6; + + s = splsoftnet(); + if (KENTRY_NETWORK(ke)) { + pfr_prepare_network(&mask, ke->pfrke_af, ke->pfrke_net); + rn = rn_addroute(&ke->pfrke_sa, &mask, head, ke->pfrke_node); + } else + rn = rn_addroute(&ke->pfrke_sa, NULL, head, ke->pfrke_node); + splx(s); + + return (rn == NULL ? -1 : 0); +} + +int +pfr_unroute_kentry(struct pfr_ktable *kt, struct pfr_kentry *ke) +{ + union sockaddr_union mask; + struct radix_node *rn; + struct radix_node_head *head; + int s; + + if (ke->pfrke_af == AF_INET) + head = kt->pfrkt_ip4; + else + head = kt->pfrkt_ip6; + + s = splsoftnet(); + if (KENTRY_NETWORK(ke)) { + pfr_prepare_network(&mask, ke->pfrke_af, ke->pfrke_net); + rn = rn_delete(&ke->pfrke_sa, &mask, head); + } else + rn = rn_delete(&ke->pfrke_sa, NULL, head); + splx(s); + + if (rn == NULL) { + printf("pfr_unroute_kentry: delete failed.\n"); + return (-1); + } + return (0); +} + +void +pfr_copyout_addr(struct pfr_addr *ad, struct pfr_kentry *ke) +{ + bzero(ad, sizeof(*ad)); + if (ke == NULL) + return; + ad->pfra_af = ke->pfrke_af; + ad->pfra_net = ke->pfrke_net; + ad->pfra_not = ke->pfrke_not; + if (ad->pfra_af == AF_INET) + ad->pfra_ip4addr = ke->pfrke_sa.sin.sin_addr; + else + ad->pfra_ip6addr = ke->pfrke_sa.sin6.sin6_addr; +} + +int +pfr_walktree(struct radix_node *rn, void *arg) +{ + struct pfr_kentry *ke = (struct pfr_kentry *)rn; + struct pfr_walktree *w = arg; + int s; + + switch (w->pfrw_op) { + case PFRW_MARK: + ke->pfrke_mark = 0; + break; + case PFRW_SWEEP: + if (ke->pfrke_mark) + break; + /* fall trough */ + case PFRW_ENQUEUE: + SLIST_INSERT_HEAD(w->pfrw_workq, ke, pfrke_workq); + w->pfrw_cnt++; + break; + case PFRW_GET_ADDRS: + if (w->pfrw_free-- > 0) { + struct pfr_addr ad; + + pfr_copyout_addr(&ad, ke); + if (copyout(&ad, w->pfrw_addr, sizeof(ad))) + return (EFAULT); + w->pfrw_addr++; + } + break; + case PFRW_GET_ASTATS: + if (w->pfrw_free-- > 0) { + struct pfr_astats as; + + pfr_copyout_addr(&as.pfras_a, ke); + + s = splsoftnet(); + bcopy(ke->pfrke_packets, as.pfras_packets, + sizeof(as.pfras_packets)); + bcopy(ke->pfrke_bytes, as.pfras_bytes, + sizeof(as.pfras_bytes)); + splx(s); + as.pfras_tzero = ke->pfrke_tzero; + + if (copyout(&as, w->pfrw_astats, sizeof(as))) + return (EFAULT); + w->pfrw_astats++; + } + break; + } + return (0); +} + +int +pfr_clr_tables(struct pfr_table *filter, int *ndel, int flags) +{ + struct pfr_ktableworkq workq; + struct pfr_ktable *p; + int s, xdel = 0; + + ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY+PFR_FLAG_ALLRSETS); + if (pfr_table_count(filter, flags) < 0) + return (ENOENT); + + SLIST_INIT(&workq); + RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { + if (pfr_skip_table(filter, p, flags)) + continue; + if (!(p->pfrkt_flags & PFR_TFLAG_ACTIVE)) + continue; + p->pfrkt_nflags = p->pfrkt_flags & ~PFR_TFLAG_ACTIVE; + SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); + xdel++; + } + if (!(flags & PFR_FLAG_DUMMY)) { + if (flags & PFR_FLAG_ATOMIC) + s = splsoftnet(); + pfr_setflags_ktables(&workq); + if (flags & PFR_FLAG_ATOMIC) + splx(s); + } + if (ndel != NULL) + *ndel = xdel; + return (0); +} + +int +pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags) +{ + struct pfr_ktableworkq addq, changeq; + struct pfr_ktable *p, *q, *r, key; + int i, rv, s, xadd = 0; + long tzero = time.tv_sec; + + ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY); + SLIST_INIT(&addq); + SLIST_INIT(&changeq); + for (i = 0; i < size; i++) { + if (copyin(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) + senderr(EFAULT); + if (pfr_validate_table(&key.pfrkt_t, PFR_TFLAG_USRMASK)) + senderr(EINVAL); + key.pfrkt_flags |= PFR_TFLAG_ACTIVE; + p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); + if (p == NULL) { + p = pfr_create_ktable(&key.pfrkt_t, tzero, 1); + if (p == NULL) + senderr(ENOMEM); + SLIST_FOREACH(q, &addq, pfrkt_workq) { + if (!pfr_ktable_compare(p, q)) + goto _skip; + } + SLIST_INSERT_HEAD(&addq, p, pfrkt_workq); + xadd++; + if (!key.pfrkt_anchor[0]) + goto _skip; + + /* find or create root table */ + bzero(key.pfrkt_anchor, sizeof(key.pfrkt_anchor)); + bzero(key.pfrkt_ruleset, sizeof(key.pfrkt_ruleset)); + r = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); + if (r != NULL) { + p->pfrkt_root = r; + goto _skip; + } + SLIST_FOREACH(q, &addq, pfrkt_workq) { + if (!pfr_ktable_compare(&key, q)) { + p->pfrkt_root = q; + goto _skip; + } + } + key.pfrkt_flags = 0; + r = pfr_create_ktable(&key.pfrkt_t, 0, 1); + if (r == NULL) + senderr(ENOMEM); + SLIST_INSERT_HEAD(&addq, r, pfrkt_workq); + p->pfrkt_root = r; + } else if (!(p->pfrkt_flags & PFR_TFLAG_ACTIVE)) { + SLIST_FOREACH(q, &changeq, pfrkt_workq) + if (!pfr_ktable_compare(&key, q)) + goto _skip; + p->pfrkt_nflags = (p->pfrkt_flags & + ~PFR_TFLAG_USRMASK) | key.pfrkt_flags; + SLIST_INSERT_HEAD(&changeq, p, pfrkt_workq); + xadd++; + } +_skip: + ; + } + if (!(flags & PFR_FLAG_DUMMY)) { + if (flags & PFR_FLAG_ATOMIC) + s = splsoftnet(); + pfr_insert_ktables(&addq); + pfr_setflags_ktables(&changeq); + if (flags & PFR_FLAG_ATOMIC) + splx(s); + } else + pfr_destroy_ktables(&addq, 0); + if (nadd != NULL) + *nadd = xadd; + return (0); +_bad: + pfr_destroy_ktables(&addq, 0); + return (rv); +} + +int +pfr_del_tables(struct pfr_table *tbl, int size, int *ndel, int flags) +{ + struct pfr_ktableworkq workq; + struct pfr_ktable *p, *q, key; + int i, s, xdel = 0; + + ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY); + SLIST_INIT(&workq); + for (i = 0; i < size; i++) { + if (copyin(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) + return (EFAULT); + if (pfr_validate_table(&key.pfrkt_t, 0)) + return (EINVAL); + p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); + if (p != NULL && (p->pfrkt_flags & PFR_TFLAG_ACTIVE)) { + SLIST_FOREACH(q, &workq, pfrkt_workq) + if (!pfr_ktable_compare(p, q)) + goto _skip; + p->pfrkt_nflags = p->pfrkt_flags & ~PFR_TFLAG_ACTIVE; + SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); + xdel++; + } +_skip: + ; + } + + if (!(flags & PFR_FLAG_DUMMY)) { + if (flags & PFR_FLAG_ATOMIC) + s = splsoftnet(); + pfr_setflags_ktables(&workq); + if (flags & PFR_FLAG_ATOMIC) + splx(s); + } + if (ndel != NULL) + *ndel = xdel; + return (0); +} + +int +pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size, + int flags) +{ + struct pfr_ktable *p; + int n, nn; + + ACCEPT_FLAGS(PFR_FLAG_ALLRSETS); + n = nn = pfr_table_count(filter, flags); + if (n < 0) + return (ENOENT); + if (n > *size) { + *size = n; + return (0); + } + RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { + if (pfr_skip_table(filter, p, flags)) + continue; + if (n-- <= 0) + continue; + if (copyout(&p->pfrkt_t, tbl++, sizeof(*tbl))) + return (EFAULT); + } + if (n) { + printf("pfr_get_tables: corruption detected (%d).\n", n); + return (ENOTTY); + } + *size = nn; + return (0); +} + +int +pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size, + int flags) +{ + struct pfr_ktable *p; + struct pfr_ktableworkq workq; + int s, n, nn; + long tzero = time.tv_sec; + + ACCEPT_FLAGS(PFR_FLAG_ATOMIC|PFR_FLAG_ALLRSETS); + /* XXX PFR_FLAG_CLSTATS disabled */ + n = nn = pfr_table_count(filter, flags); + if (n < 0) + return (ENOENT); + if (n > *size) { + *size = n; + return (0); + } + SLIST_INIT(&workq); + if (flags & PFR_FLAG_ATOMIC) + s = splsoftnet(); + RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { + if (pfr_skip_table(filter, p, flags)) + continue; + if (n-- <= 0) + continue; + if (!(flags & PFR_FLAG_ATOMIC)) + s = splsoftnet(); + if (copyout(&p->pfrkt_ts, tbl++, sizeof(*tbl))) { + splx(s); + return (EFAULT); + } + if (!(flags & PFR_FLAG_ATOMIC)) + splx(s); + SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); + } + if (flags & PFR_FLAG_CLSTATS) + pfr_clstats_ktables(&workq, tzero, + flags & PFR_FLAG_ADDRSTOO); + if (flags & PFR_FLAG_ATOMIC) + splx(s); + if (n) { + printf("pfr_get_tstats: corruption detected (%d).\n", n); + return (ENOTTY); + } + *size = nn; + return (0); +} + +int +pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags) +{ + struct pfr_ktableworkq workq; + struct pfr_ktable *p, key; + int i, s, xzero = 0; + long tzero = time.tv_sec; + + ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY+PFR_FLAG_ADDRSTOO); + SLIST_INIT(&workq); + for (i = 0; i < size; i++) { + if (copyin(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) + return (EFAULT); + if (pfr_validate_table(&key.pfrkt_t, 0)) + return (EINVAL); + p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); + if (p != NULL) { + SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); + xzero++; + } + } + if (!(flags & PFR_FLAG_DUMMY)) { + if (flags & PFR_FLAG_ATOMIC) + s = splsoftnet(); + pfr_clstats_ktables(&workq, tzero, flags & PFR_FLAG_ADDRSTOO); + if (flags & PFR_FLAG_ATOMIC) + splx(s); + } + if (nzero != NULL) + *nzero = xzero; + return (0); +} + +int +pfr_set_tflags(struct pfr_table *tbl, int size, int setflag, int clrflag, + int *nchange, int *ndel, int flags) +{ + struct pfr_ktableworkq workq; + struct pfr_ktable *p, *q, key; + int i, s, xchange = 0, xdel = 0; + + ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY); + if ((setflag & ~PFR_TFLAG_USRMASK) || + (clrflag & ~PFR_TFLAG_USRMASK) || + (setflag & clrflag)) + return (EINVAL); + SLIST_INIT(&workq); + for (i = 0; i < size; i++) { + if (copyin(tbl+i, &key.pfrkt_t, sizeof(key.pfrkt_t))) + return (EFAULT); + if (pfr_validate_table(&key.pfrkt_t, 0)) + return (EINVAL); + p = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); + if (p != NULL && (p->pfrkt_flags & PFR_TFLAG_ACTIVE)) { + p->pfrkt_nflags = (p->pfrkt_flags | setflag) & + ~clrflag; + if (p->pfrkt_nflags == p->pfrkt_flags) + goto _skip; + SLIST_FOREACH(q, &workq, pfrkt_workq) + if (!pfr_ktable_compare(p, q)) + goto _skip; + SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); + if ((p->pfrkt_flags & PFR_TFLAG_PERSIST) && + (clrflag & PFR_TFLAG_PERSIST) && + !(p->pfrkt_flags & PFR_TFLAG_REFERENCED)) + xdel++; + else + xchange++; + } +_skip: + ; + } + if (!(flags & PFR_FLAG_DUMMY)) { + if (flags & PFR_FLAG_ATOMIC) + s = splsoftnet(); + pfr_setflags_ktables(&workq); + if (flags & PFR_FLAG_ATOMIC) + splx(s); + } + if (nchange != NULL) + *nchange = xchange; + if (ndel != NULL) + *ndel = xdel; + return (0); +} + +int +pfr_ina_begin(int *ticket, int *ndel, int flags) +{ + struct pfr_ktableworkq workq; + struct pfr_ktable *p; + int xdel = 0; + + ACCEPT_FLAGS(PFR_FLAG_DUMMY); + SLIST_INIT(&workq); + RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { + if (!(p->pfrkt_flags & PFR_TFLAG_INACTIVE)) + continue; + p->pfrkt_nflags = p->pfrkt_flags & ~PFR_TFLAG_INACTIVE; + SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); + xdel++; + } + if (!(flags & PFR_FLAG_DUMMY)) + pfr_setflags_ktables(&workq); + if (ndel != NULL) + *ndel = xdel; + if (ticket != NULL && !(flags & PFR_FLAG_DUMMY)) + *ticket = ++pfr_ticket; + return (0); +} + +int +pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size, + int *nadd, int *naddr, int ticket, int flags) +{ + struct pfr_ktableworkq tableq; + struct pfr_kentryworkq addrq; + struct pfr_ktable *kt, *rt, *shadow, key; + struct pfr_kentry *p; + struct pfr_addr ad; + int i, rv, xadd = 0, xaddr = 0; + + ACCEPT_FLAGS(PFR_FLAG_DUMMY|PFR_FLAG_ADDRSTOO); + if (ticket != pfr_ticket) + return (EBUSY); + if (size && !(flags & PFR_FLAG_ADDRSTOO)) + return (EINVAL); + if (pfr_validate_table(tbl, PFR_TFLAG_USRMASK)) + return (EINVAL); + tbl->pfrt_flags |= PFR_TFLAG_INACTIVE; + SLIST_INIT(&tableq); + kt = RB_FIND(pfr_ktablehead, &pfr_ktables, (struct pfr_ktable *)tbl); + if (kt == NULL) { + kt = pfr_create_ktable(tbl, 0, 1); + if (kt == NULL) + return (ENOMEM); + SLIST_INSERT_HEAD(&tableq, kt, pfrkt_workq); + xadd++; + if (!tbl->pfrt_anchor[0]) + goto _skip; + + /* find or create root table */ + bzero(&key, sizeof(key)); + strlcpy(key.pfrkt_name, tbl->pfrt_name, sizeof(key.pfrkt_name)); + rt = RB_FIND(pfr_ktablehead, &pfr_ktables, &key); + if (rt != NULL) { + kt->pfrkt_root = rt; + goto _skip; + } + rt = pfr_create_ktable(&key.pfrkt_t, 0, 1); + if (rt == NULL) { + pfr_destroy_ktables(&tableq, 0); + return (ENOMEM); + } + SLIST_INSERT_HEAD(&tableq, rt, pfrkt_workq); + kt->pfrkt_root = rt; + } else if (!(kt->pfrkt_flags & PFR_TFLAG_INACTIVE)) + xadd++; +_skip: + shadow = pfr_create_ktable(tbl, 0, 0); + if (shadow == NULL) { + pfr_destroy_ktables(&tableq, 0); + return (ENOMEM); + } + SLIST_INIT(&addrq); + for (i = 0; i < size; i++) { + if (copyin(addr+i, &ad, sizeof(ad))) + senderr(EFAULT); + if (pfr_validate_addr(&ad)) + senderr(EINVAL); + if (pfr_lookup_addr(shadow, &ad, 1) != NULL) + continue; + p = pfr_create_kentry(&ad); + if (p == NULL) + senderr(ENOMEM); + if (pfr_route_kentry(shadow, p)) { + pfr_destroy_kentry(p); + continue; + } + SLIST_INSERT_HEAD(&addrq, p, pfrke_workq); + xaddr++; + } + if (!(flags & PFR_FLAG_DUMMY)) { + if (kt->pfrkt_shadow != NULL) + pfr_destroy_ktable(kt->pfrkt_shadow, 1); + kt->pfrkt_flags |= PFR_TFLAG_INACTIVE; + pfr_insert_ktables(&tableq); + shadow->pfrkt_cnt = (flags & PFR_FLAG_ADDRSTOO) ? + xaddr : NO_ADDRESSES; + kt->pfrkt_shadow = shadow; + } else { + pfr_clean_node_mask(shadow, &addrq); + pfr_destroy_ktable(shadow, 0); + pfr_destroy_ktables(&tableq, 0); + pfr_destroy_kentries(&addrq); + } + if (nadd != NULL) + *nadd = xadd; + if (naddr != NULL) + *naddr = xaddr; + return (0); +_bad: + pfr_destroy_ktable(shadow, 0); + pfr_destroy_ktables(&tableq, 0); + pfr_destroy_kentries(&addrq); + return (rv); +} + +int +pfr_ina_commit(int ticket, int *nadd, int *nchange, int flags) +{ + struct pfr_ktable *p; + struct pfr_ktableworkq workq; + int s, xadd = 0, xchange = 0; + long tzero = time.tv_sec; + + ACCEPT_FLAGS(PFR_FLAG_ATOMIC+PFR_FLAG_DUMMY); + if (ticket != pfr_ticket) + return (EBUSY); + pfr_ticket++; + + SLIST_INIT(&workq); + RB_FOREACH(p, pfr_ktablehead, &pfr_ktables) { + if (!(p->pfrkt_flags & PFR_TFLAG_INACTIVE)) + continue; + SLIST_INSERT_HEAD(&workq, p, pfrkt_workq); + if (p->pfrkt_flags & PFR_TFLAG_ACTIVE) + xchange++; + else + xadd++; + } + + if (!(flags & PFR_FLAG_DUMMY)) { + if (flags & PFR_FLAG_ATOMIC) + s = splsoftnet(); + SLIST_FOREACH(p, &workq, pfrkt_workq) + pfr_commit_ktable(p, tzero); + if (flags & PFR_FLAG_ATOMIC) + splx(s); + } + if (nadd != NULL) + *nadd = xadd; + if (nchange != NULL) + *nchange = xchange; + + return (0); +} + +void +pfr_commit_ktable(struct pfr_ktable *kt, long tzero) +{ + struct pfr_ktable *shadow = kt->pfrkt_shadow; + int nflags; + + if (shadow->pfrkt_cnt == NO_ADDRESSES) { + if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + pfr_clstats_ktable(kt, tzero, 1); + } else if (kt->pfrkt_flags & PFR_TFLAG_ACTIVE) { + /* kt might contain addresses */ + struct pfr_kentryworkq addrq, addq, changeq, delq, garbageq; + struct pfr_kentry *p, *q, *next; + struct pfr_addr ad; + + pfr_enqueue_addrs(shadow, &addrq, NULL, 0); + pfr_mark_addrs(kt); + SLIST_INIT(&addq); + SLIST_INIT(&changeq); + SLIST_INIT(&delq); + SLIST_INIT(&garbageq); + pfr_clean_node_mask(shadow, &addrq); + for (p = SLIST_FIRST(&addrq); p != NULL; p = next) { + next = SLIST_NEXT(p, pfrke_workq); /* XXX */ + pfr_copyout_addr(&ad, p); + q = pfr_lookup_addr(kt, &ad, 1); + if (q != NULL) { + if (q->pfrke_not != p->pfrke_not) + SLIST_INSERT_HEAD(&changeq, q, + pfrke_workq); + q->pfrke_mark = 1; + SLIST_INSERT_HEAD(&garbageq, p, pfrke_workq); + } else { + p->pfrke_tzero = tzero; + SLIST_INSERT_HEAD(&addq, p, pfrke_workq); + } + } + pfr_enqueue_addrs(kt, &delq, NULL, ENQUEUE_UNMARKED_ONLY); + pfr_insert_kentries(kt, &addq, tzero); + pfr_remove_kentries(kt, &delq); + pfr_clstats_kentries(&changeq, tzero, INVERT_NEG_FLAG); + pfr_destroy_kentries(&garbageq); + } else { + /* kt cannot contain addresses */ + SWAP(struct radix_node_head *, kt->pfrkt_ip4, + shadow->pfrkt_ip4); + SWAP(struct radix_node_head *, kt->pfrkt_ip6, + shadow->pfrkt_ip6); + SWAP(int, kt->pfrkt_cnt, shadow->pfrkt_cnt); + pfr_clstats_ktable(kt, tzero, 1); + } + nflags = ((shadow->pfrkt_flags & PFR_TFLAG_USRMASK) | + (kt->pfrkt_flags & PFR_TFLAG_SETMASK) | PFR_TFLAG_ACTIVE) + & ~PFR_TFLAG_INACTIVE; + pfr_destroy_ktable(shadow, 0); + kt->pfrkt_shadow = NULL; + pfr_setflags_ktable(kt, nflags); +} + +int +pfr_validate_table(struct pfr_table *tbl, int allowedflags) +{ + int i; + + if (!tbl->pfrt_name[0]) + return (-1); + if (tbl->pfrt_name[PF_TABLE_NAME_SIZE-1]) + return (-1); + for (i = strlen(tbl->pfrt_name); i < PF_TABLE_NAME_SIZE; i++) + if (tbl->pfrt_name[i]) + return (-1); + if (tbl->pfrt_flags & ~allowedflags) + return (-1); + return (0); +} + +int +pfr_table_count(struct pfr_table *filter, int flags) +{ + struct pf_ruleset *rs; + struct pf_anchor *ac; + + if (flags & PFR_FLAG_ALLRSETS) + return (pfr_ktable_cnt); + if (filter->pfrt_ruleset[0]) { + rs = pf_find_ruleset(filter->pfrt_anchor, + filter->pfrt_ruleset); + return ((rs != NULL) ? rs->tables : -1); + } + if (filter->pfrt_anchor[0]) { + ac = pf_find_anchor(filter->pfrt_anchor); + return ((ac != NULL) ? ac->tables : -1); + } + return (pf_main_ruleset.tables); +} + +int +pfr_skip_table(struct pfr_table *filter, struct pfr_ktable *kt, int flags) +{ + if (flags & PFR_FLAG_ALLRSETS) + return (0); + if (strncmp(filter->pfrt_anchor, kt->pfrkt_anchor, + PF_ANCHOR_NAME_SIZE)) + return (1); + if (!filter->pfrt_ruleset[0]) + return (0); + if (strncmp(filter->pfrt_ruleset, kt->pfrkt_ruleset, + PF_RULESET_NAME_SIZE)) + return (1); + return (0); +} + +void +pfr_insert_ktables(struct pfr_ktableworkq *workq) +{ + struct pfr_ktable *p; + + SLIST_FOREACH(p, workq, pfrkt_workq) + pfr_insert_ktable(p); +} + +void +pfr_insert_ktable(struct pfr_ktable *kt) +{ + RB_INSERT(pfr_ktablehead, &pfr_ktables, kt); + pfr_ktable_cnt++; + if (kt->pfrkt_root != NULL) + if (!kt->pfrkt_root->pfrkt_refcnt[PFR_REFCNT_ANCHOR]++) + pfr_setflags_ktable(kt->pfrkt_root, + kt->pfrkt_root->pfrkt_flags|PFR_TFLAG_REFDANCHOR); +} + +void +pfr_setflags_ktables(struct pfr_ktableworkq *workq) +{ + struct pfr_ktable *p; + + SLIST_FOREACH(p, workq, pfrkt_workq) + pfr_setflags_ktable(p, p->pfrkt_nflags); +} + +void +pfr_setflags_ktable(struct pfr_ktable *kt, int newf) +{ + struct pfr_kentryworkq addrq; + + if (!(newf & PFR_TFLAG_REFERENCED) && + !(newf & PFR_TFLAG_PERSIST)) + newf &= ~PFR_TFLAG_ACTIVE; + if (!(newf & PFR_TFLAG_ACTIVE)) + newf &= ~PFR_TFLAG_USRMASK; + if (!(newf & PFR_TFLAG_SETMASK)) { + RB_REMOVE(pfr_ktablehead, &pfr_ktables, kt); + if (kt->pfrkt_root != NULL) + if (!--kt->pfrkt_root->pfrkt_refcnt[PFR_REFCNT_ANCHOR]) + pfr_setflags_ktable(kt->pfrkt_root, + kt->pfrkt_root->pfrkt_flags & + ~PFR_TFLAG_REFDANCHOR); + pfr_destroy_ktable(kt, 1); + pfr_ktable_cnt--; + return; + } + if (!(newf & PFR_TFLAG_ACTIVE) && kt->pfrkt_cnt) { + pfr_enqueue_addrs(kt, &addrq, NULL, 0); + pfr_remove_kentries(kt, &addrq); + } + if (!(newf & PFR_TFLAG_INACTIVE) && kt->pfrkt_shadow != NULL) { + pfr_destroy_ktable(kt->pfrkt_shadow, 1); + kt->pfrkt_shadow = NULL; + } + kt->pfrkt_flags = newf; +} + +void +pfr_clstats_ktables(struct pfr_ktableworkq *workq, long tzero, int recurse) +{ + struct pfr_ktable *p; + + SLIST_FOREACH(p, workq, pfrkt_workq) + pfr_clstats_ktable(p, tzero, recurse); +} + +void +pfr_clstats_ktable(struct pfr_ktable *kt, long tzero, int recurse) +{ + struct pfr_kentryworkq addrq; + int s; + + if (recurse) { + pfr_enqueue_addrs(kt, &addrq, NULL, 0); + pfr_clstats_kentries(&addrq, tzero, 0); + } + s = splsoftnet(); + bzero(kt->pfrkt_packets, sizeof(kt->pfrkt_packets)); + bzero(kt->pfrkt_bytes, sizeof(kt->pfrkt_bytes)); + kt->pfrkt_match = kt->pfrkt_nomatch = 0; + splx(s); + kt->pfrkt_tzero = tzero; +} + +struct pfr_ktable * +pfr_create_ktable(struct pfr_table *tbl, long tzero, int attachruleset) +{ + struct pfr_ktable *kt; + struct pf_ruleset *rs; + + kt = pool_get(&pfr_ktable_pl, PR_NOWAIT); + if (kt == NULL) + return (NULL); + bzero(kt, sizeof(*kt)); + kt->pfrkt_t = *tbl; + + if (attachruleset) { + rs = pf_find_or_create_ruleset(tbl->pfrt_anchor, + tbl->pfrt_ruleset); + if (!rs) { + pfr_destroy_ktable(kt, 0); + return (NULL); + } + kt->pfrkt_rs = rs; + rs->tables++; + if (rs->anchor != NULL) + rs->anchor->tables++; + } + + if (!rn_inithead((void **)&kt->pfrkt_ip4, + offsetof(struct sockaddr_in, sin_addr) * 8) || + !rn_inithead((void **)&kt->pfrkt_ip6, + offsetof(struct sockaddr_in6, sin6_addr) * 8)) { + pfr_destroy_ktable(kt, 0); + return (NULL); + } + kt->pfrkt_tzero = tzero; + + return (kt); +} + +void +pfr_destroy_ktables(struct pfr_ktableworkq *workq, int flushaddr) +{ + struct pfr_ktable *p, *q; + + for (p = SLIST_FIRST(workq); p; p = q) { + q = SLIST_NEXT(p, pfrkt_workq); + pfr_destroy_ktable(p, flushaddr); + } +} + +void +pfr_destroy_ktable(struct pfr_ktable *kt, int flushaddr) +{ + struct pfr_kentryworkq addrq; + + if (flushaddr) { + pfr_enqueue_addrs(kt, &addrq, NULL, 0); + pfr_clean_node_mask(kt, &addrq); + pfr_destroy_kentries(&addrq); + } + if (kt->pfrkt_ip4 != NULL) + free((caddr_t)kt->pfrkt_ip4, M_RTABLE); + if (kt->pfrkt_ip6 != NULL) + free((caddr_t)kt->pfrkt_ip6, M_RTABLE); + if (kt->pfrkt_shadow != NULL) + pfr_destroy_ktable(kt->pfrkt_shadow, flushaddr); + if (kt->pfrkt_rs != NULL) { + kt->pfrkt_rs->tables--; + if (kt->pfrkt_rs->anchor != NULL) + kt->pfrkt_rs->anchor->tables--; + pf_remove_if_empty_ruleset(kt->pfrkt_rs); + } + pool_put(&pfr_ktable_pl, kt); +} + +int +pfr_ktable_compare(struct pfr_ktable *p, struct pfr_ktable *q) +{ + int d; + + if ((d = strncmp(p->pfrkt_name, q->pfrkt_name, PF_TABLE_NAME_SIZE))) + return (d); + if ((d = strncmp(p->pfrkt_anchor, q->pfrkt_anchor, + PF_ANCHOR_NAME_SIZE))) + return (d); + return strncmp(p->pfrkt_ruleset, q->pfrkt_ruleset, + PF_RULESET_NAME_SIZE); +} + +struct pfr_ktable * +pfr_lookup_table(struct pfr_table *tbl) +{ + /* struct pfr_ktable start like a struct pfr_table */ + return RB_FIND(pfr_ktablehead, &pfr_ktables, (struct pfr_ktable *)tbl); +} + +int +pfr_match_addr(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af) +{ + struct pfr_kentry *ke = NULL; + int match; + + if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL) + kt = kt->pfrkt_root; + if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return 0; + + switch (af) { + case AF_INET: + pfr_sin.sin_addr.s_addr = a->addr32[0]; + ke = (struct pfr_kentry *)rn_match(&pfr_sin, kt->pfrkt_ip4); + if (ke && KENTRY_RNF_ROOT(ke)) + ke = NULL; + break; + case AF_INET6: + bcopy(a, &pfr_sin6.sin6_addr, sizeof(pfr_sin6.sin6_addr)); + ke = (struct pfr_kentry *)rn_match(&pfr_sin6, kt->pfrkt_ip6); + if (ke && KENTRY_RNF_ROOT(ke)) + ke = NULL; + break; + } + match = (ke && !ke->pfrke_not); + if (match) + kt->pfrkt_match++; + else + kt->pfrkt_nomatch++; + return (match); +} + +void +pfr_update_stats(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af, + u_int64_t len, int dir_out, int op_pass, int notrule) +{ + struct pfr_kentry *ke = NULL; + + if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE) && kt->pfrkt_root != NULL) + kt = kt->pfrkt_root; + if (!(kt->pfrkt_flags & PFR_TFLAG_ACTIVE)) + return; + + switch (af) { + case AF_INET: + pfr_sin.sin_addr.s_addr = a->addr32[0]; + ke = (struct pfr_kentry *)rn_match(&pfr_sin, kt->pfrkt_ip4); + if (ke && KENTRY_RNF_ROOT(ke)) + ke = NULL; + break; + case AF_INET6: + bcopy(a, &pfr_sin6.sin6_addr, sizeof(pfr_sin6.sin6_addr)); + ke = (struct pfr_kentry *)rn_match(&pfr_sin6, kt->pfrkt_ip6); + if (ke && KENTRY_RNF_ROOT(ke)) + ke = NULL; + break; + } + if ((ke == NULL || ke->pfrke_not) != notrule) { + if (op_pass != PFR_OP_PASS) + printf("pfr_update_stats: assertion failed.\n"); + op_pass = PFR_OP_XPASS; + } + kt->pfrkt_packets[dir_out][op_pass]++; + kt->pfrkt_bytes[dir_out][op_pass] += len; + if (ke != NULL && op_pass != PFR_OP_XPASS) { + ke->pfrke_packets[dir_out][op_pass]++; + ke->pfrke_bytes[dir_out][op_pass] += len; + } +} + +struct pfr_ktable * +pfr_attach_table(struct pf_ruleset *rs, char *name) +{ + struct pfr_ktable *kt, *rt; + struct pfr_table tbl; + struct pf_anchor *ac = rs->anchor; + + bzero(&tbl, sizeof(tbl)); + strlcpy(tbl.pfrt_name, name, sizeof(tbl.pfrt_name)); + if (ac != NULL) { + strlcpy(tbl.pfrt_anchor, ac->name, sizeof(tbl.pfrt_anchor)); + strlcpy(tbl.pfrt_ruleset, rs->name, sizeof(tbl.pfrt_ruleset)); + } + kt = pfr_lookup_table(&tbl); + if (kt == NULL) { + kt = pfr_create_ktable(&tbl, time.tv_sec, 1); + if (kt == NULL) + return (NULL); + if (ac != NULL) { + bzero(tbl.pfrt_anchor, sizeof(tbl.pfrt_anchor)); + bzero(tbl.pfrt_ruleset, sizeof(tbl.pfrt_ruleset)); + rt = pfr_lookup_table(&tbl); + if (rt == NULL) { + rt = pfr_create_ktable(&tbl, 0, 1); + if (rt == NULL) { + pfr_destroy_ktable(kt, 0); + return (NULL); + } + pfr_insert_ktable(rt); + } + kt->pfrkt_root = rt; + } + pfr_insert_ktable(kt); + } + if (!kt->pfrkt_refcnt[PFR_REFCNT_RULE]++) + pfr_setflags_ktable(kt, kt->pfrkt_flags|PFR_TFLAG_REFERENCED); + return kt; +} + +void +pfr_detach_table(struct pfr_ktable *kt) +{ + if (kt->pfrkt_refcnt[PFR_REFCNT_RULE] <= 0) + printf("pfr_detach_table: refcount = %d.\n", + kt->pfrkt_refcnt[PFR_REFCNT_RULE]); + else if (!--kt->pfrkt_refcnt[PFR_REFCNT_RULE]) + pfr_setflags_ktable(kt, kt->pfrkt_flags&~PFR_TFLAG_REFERENCED); +} Index: sys/net/pfvar.h =================================================================== RCS file: pfvar.h diff -N pfvar.h --- /dev/null Mon Jun 30 04:58:52 2003 +++ pfvar.h Mon Jun 30 04:58:40 2003 @@ -0,0 +1,1143 @@ +/* $NetBSD$ */ +/* $OpenBSD: pfvar.h,v 1.157 2003/06/21 09:07:01 djm Exp $ */ + +/* + * Copyright (c) 2001 Daniel Hartmeier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDERS 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. + * + */ + +#ifndef _NET_PFVAR_H_ +#define _NET_PFVAR_H_ + +#include +#include +#include + +#include +#ifdef __OpenBSD__ +#include +#endif +#include + +#define PF_TCPS_PROXY_SRC ((TCP_NSTATES)+0) +#define PF_TCPS_PROXY_DST ((TCP_NSTATES)+1) + +enum { PF_INOUT, PF_IN, PF_OUT }; +enum { PF_PASS, PF_DROP, PF_SCRUB, PF_NAT, PF_NONAT, + PF_BINAT, PF_NOBINAT, PF_RDR, PF_NORDR, PF_SYNPROXY_DROP }; +enum { PF_RULESET_SCRUB, PF_RULESET_FILTER, PF_RULESET_NAT, + PF_RULESET_BINAT, PF_RULESET_RDR, PF_RULESET_MAX }; +enum { PF_OP_NONE, PF_OP_IRG, PF_OP_EQ, PF_OP_NE, PF_OP_LT, + PF_OP_LE, PF_OP_GT, PF_OP_GE, PF_OP_XRG, PF_OP_RRG }; +enum { PF_DEBUG_NONE, PF_DEBUG_URGENT, PF_DEBUG_MISC }; +enum { PF_CHANGE_NONE, PF_CHANGE_ADD_HEAD, PF_CHANGE_ADD_TAIL, + PF_CHANGE_ADD_BEFORE, PF_CHANGE_ADD_AFTER, + PF_CHANGE_REMOVE, PF_CHANGE_GET_TICKET }; +/* + * Note about PFTM_*: real indices into pf_rule.timeout[] come before + * PFTM_MAX, special cases afterwards. See pf_state_expires(). + */ +enum { PFTM_TCP_FIRST_PACKET, PFTM_TCP_OPENING, PFTM_TCP_ESTABLISHED, + PFTM_TCP_CLOSING, PFTM_TCP_FIN_WAIT, PFTM_TCP_CLOSED, + PFTM_UDP_FIRST_PACKET, PFTM_UDP_SINGLE, PFTM_UDP_MULTIPLE, + PFTM_ICMP_FIRST_PACKET, PFTM_ICMP_ERROR_REPLY, + PFTM_OTHER_FIRST_PACKET, PFTM_OTHER_SINGLE, + PFTM_OTHER_MULTIPLE, PFTM_FRAG, PFTM_INTERVAL, + PFTM_ADAPTIVE_START, PFTM_ADAPTIVE_END, PFTM_MAX, + PFTM_PURGE, PFTM_UNTIL_PACKET }; +enum { PF_NOPFROUTE, PF_FASTROUTE, PF_ROUTETO, PF_DUPTO, PF_REPLYTO }; +enum { PF_LIMIT_STATES, PF_LIMIT_FRAGS, PF_LIMIT_MAX }; +#define PF_POOL_IDMASK 0x0f +enum { PF_POOL_NONE, PF_POOL_BITMASK, PF_POOL_RANDOM, + PF_POOL_SRCHASH, PF_POOL_ROUNDROBIN }; +enum { PF_ADDR_ADDRMASK, PF_ADDR_NOROUTE, PF_ADDR_DYNIFTL, + PF_ADDR_TABLE }; +#define PF_POOL_TYPEMASK 0x0f +#define PF_WSCALE_FLAG 0x80 +#define PF_WSCALE_MASK 0x0f + +struct pf_addr { + union { + struct in_addr v4; + struct in6_addr v6; + u_int8_t addr8[16]; + u_int16_t addr16[8]; + u_int32_t addr32[4]; + } pfa; /* 128-bit address */ +#define v4 pfa.v4 +#define v6 pfa.v6 +#define addr8 pfa.addr8 +#define addr16 pfa.addr16 +#define addr32 pfa.addr32 +}; + +#define PF_TABLE_NAME_SIZE 32 + +struct pf_addr_wrap { + union { + struct { + struct pf_addr addr; + struct pf_addr mask; + } a; + char ifname[IFNAMSIZ]; + char tblname[PF_TABLE_NAME_SIZE]; + } v; + union { + struct pf_addr_dyn *dyn; + struct pfr_ktable *tbl; + int tblcnt; + } p; + u_int8_t type; /* PF_ADDR_* */ +}; + +struct pf_addr_dyn { + char ifname[IFNAMSIZ]; + struct ifnet *ifp; + struct pf_addr *addr; + sa_family_t af; + void *hook_cookie; + u_int8_t undefined; +}; + +/* + * Address manipulation macros + */ + +#ifdef _KERNEL + +#ifdef INET +#ifndef INET6 +#define PF_INET_ONLY +#endif /* ! INET6 */ +#endif /* INET */ + +#ifdef INET6 +#ifndef INET +#define PF_INET6_ONLY +#endif /* ! INET */ +#endif /* INET6 */ + +#ifdef INET +#ifdef INET6 +#define PF_INET_INET6 +#endif /* INET6 */ +#endif /* INET */ + +#else + +#define PF_INET_INET6 + +#endif /* _KERNEL */ + +/* Both IPv4 and IPv6 */ +#ifdef PF_INET_INET6 + +#define PF_AEQ(a, b, c) \ + ((c == AF_INET && (a)->addr32[0] == (b)->addr32[0]) || \ + ((a)->addr32[3] == (b)->addr32[3] && \ + (a)->addr32[2] == (b)->addr32[2] && \ + (a)->addr32[1] == (b)->addr32[1] && \ + (a)->addr32[0] == (b)->addr32[0])) \ + +#define PF_ANEQ(a, b, c) \ + ((c == AF_INET && (a)->addr32[0] != (b)->addr32[0]) || \ + ((a)->addr32[3] != (b)->addr32[3] || \ + (a)->addr32[2] != (b)->addr32[2] || \ + (a)->addr32[1] != (b)->addr32[1] || \ + (a)->addr32[0] != (b)->addr32[0])) \ + +#define PF_AZERO(a, c) \ + ((c == AF_INET && !(a)->addr32[0]) || \ + (!(a)->addr32[0] && !(a)->addr32[1] && \ + !(a)->addr32[2] && !(a)->addr32[3] )) \ + +#define PF_MATCHA(n, a, m, b, f) \ + pf_match_addr(n, a, m, b, f) + +#define PF_ACPY(a, b, f) \ + pf_addrcpy(a, b, f) + +#define PF_AINC(a, f) \ + pf_addr_inc(a, f) + +#define PF_POOLMASK(a, b, c, d, f) \ + pf_poolmask(a, b, c, d, f) + +#else + +/* Just IPv6 */ + +#ifdef PF_INET6_ONLY + +#define PF_AEQ(a, b, c) \ + ((a)->addr32[3] == (b)->addr32[3] && \ + (a)->addr32[2] == (b)->addr32[2] && \ + (a)->addr32[1] == (b)->addr32[1] && \ + (a)->addr32[0] == (b)->addr32[0]) \ + +#define PF_ANEQ(a, b, c) \ + ((a)->addr32[3] != (b)->addr32[3] || \ + (a)->addr32[2] != (b)->addr32[2] || \ + (a)->addr32[1] != (b)->addr32[1] || \ + (a)->addr32[0] != (b)->addr32[0]) \ + +#define PF_AZERO(a, c) \ + (!(a)->addr32[0] && \ + !(a)->addr32[1] && \ + !(a)->addr32[2] && \ + !(a)->addr32[3] ) \ + +#define PF_MATCHA(n, a, m, b, f) \ + pf_match_addr(n, a, m, b, f) + +#define PF_ACPY(a, b, f) \ + pf_addrcpy(a, b, f) + +#define PF_AINC(a, f) \ + pf_addr_inc(a, f) + +#define PF_POOLMASK(a, b, c, d, f) \ + pf_poolmask(a, b, c, d, f) + +#else + +/* Just IPv4 */ +#ifdef PF_INET_ONLY + +#define PF_AEQ(a, b, c) \ + ((a)->addr32[0] == (b)->addr32[0]) + +#define PF_ANEQ(a, b, c) \ + ((a)->addr32[0] != (b)->addr32[0]) + +#define PF_AZERO(a, c) \ + (!(a)->addr32[0]) + +#define PF_MATCHA(n, a, m, b, f) \ + pf_match_addr(n, a, m, b, f) + +#define PF_ACPY(a, b, f) \ + (a)->v4.s_addr = (b)->v4.s_addr + +#define PF_AINC(a, f) \ + do { \ + (a)->addr32[0] = htonl(ntohl((a)->addr32[0]) + 1); \ + } while (0) + +#define PF_POOLMASK(a, b, c, d, f) \ + do { \ + (a)->addr32[0] = ((b)->addr32[0] & (c)->addr32[0]) | \ + (((c)->addr32[0] ^ 0xffffffff ) & (d)->addr32[0]); \ + } while (0) + +#endif /* PF_INET_ONLY */ +#endif /* PF_INET6_ONLY */ +#endif /* PF_INET_INET6 */ + +#define PF_MISMATCHAW(aw, x, af, not) \ + ( \ + (((aw)->type == PF_ADDR_NOROUTE && \ + pf_routable((x), (af))) || \ + ((aw)->type == PF_ADDR_TABLE && \ + !pfr_match_addr((aw)->p.tbl, (x), (af))) || \ + ((aw)->type == PF_ADDR_DYNIFTL && \ + ((aw)->p.dyn->undefined || \ + (!PF_AZERO(&(aw)->v.a.mask, (af)) && \ + !PF_MATCHA(0, &(aw)->v.a.addr, \ + &(aw)->v.a.mask, (x), (af))))) || \ + ((aw)->type == PF_ADDR_ADDRMASK && \ + !PF_AZERO(&(aw)->v.a.mask, (af)) && \ + !PF_MATCHA(0, &(aw)->v.a.addr, \ + &(aw)->v.a.mask, (x), (af)))) != \ + (not) \ + ) + +struct pf_rule_uid { + uid_t uid[2]; + u_int8_t op; +}; + +struct pf_rule_gid { + uid_t gid[2]; + u_int8_t op; +}; + +struct pf_rule_addr { + struct pf_addr_wrap addr; + u_int16_t port[2]; + u_int8_t not; + u_int8_t port_op; +}; + +struct pf_pooladdr { + struct pf_rule_addr addr; + TAILQ_ENTRY(pf_pooladdr) entries; + char ifname[IFNAMSIZ]; + struct ifnet *ifp; +}; + +TAILQ_HEAD(pf_palist, pf_pooladdr); + +struct pf_poolhashkey { + union { + u_int8_t key8[16]; + u_int16_t key16[8]; + u_int32_t key32[4]; + } pfk; /* 128-bit hash key */ +#define key8 pfk.key8 +#define key16 pfk.key16 +#define key32 pfk.key32 +}; + +struct pf_pool { + struct pf_palist list; + struct pf_pooladdr *cur; + struct pf_poolhashkey key; + struct pf_addr counter; + u_int16_t proxy_port[2]; + u_int8_t port_op; + u_int8_t opts; +}; + +union pf_rule_ptr { + struct pf_rule *ptr; + u_int32_t nr; +}; + +struct pf_rule { + struct pf_rule_addr src; + struct pf_rule_addr dst; +#define PF_SKIP_IFP 0 +#define PF_SKIP_DIR 1 +#define PF_SKIP_AF 2 +#define PF_SKIP_PROTO 3 +#define PF_SKIP_SRC_ADDR 4 +#define PF_SKIP_SRC_PORT 5 +#define PF_SKIP_DST_ADDR 6 +#define PF_SKIP_DST_PORT 7 +#define PF_SKIP_COUNT 8 + union pf_rule_ptr skip[PF_SKIP_COUNT]; +#define PF_RULE_LABEL_SIZE 64 + char label[PF_RULE_LABEL_SIZE]; + u_int32_t timeout[PFTM_MAX]; +#define PF_QNAME_SIZE 16 + char ifname[IFNAMSIZ]; + char qname[PF_QNAME_SIZE]; + char pqname[PF_QNAME_SIZE]; +#define PF_ANCHOR_NAME_SIZE 16 + char anchorname[PF_ANCHOR_NAME_SIZE]; +#define PF_TAG_NAME_SIZE 16 + char tagname[PF_TAG_NAME_SIZE]; + char match_tagname[PF_TAG_NAME_SIZE]; + + TAILQ_ENTRY(pf_rule) entries; + struct pf_pool rpool; + + u_int64_t evaluations; + u_int64_t packets; + u_int64_t bytes; + + struct ifnet *ifp; + struct pf_anchor *anchor; + + u_int32_t states; + u_int32_t max_states; + u_int32_t qid; + u_int32_t pqid; + u_int32_t rt_listid; + u_int32_t nr; + + u_int16_t return_icmp; + u_int16_t return_icmp6; + u_int16_t max_mss; + u_int16_t tag; + u_int16_t match_tag; + + struct pf_rule_uid uid; + struct pf_rule_gid gid; + + u_int32_t rule_flag; + u_int8_t action; + u_int8_t direction; + u_int8_t log; + u_int8_t quick; + u_int8_t ifnot; + u_int8_t match_tag_not; + +#define PF_STATE_NORMAL 0x1 +#define PF_STATE_MODULATE 0x2 +#define PF_STATE_SYNPROXY 0x3 + u_int8_t keep_state; + sa_family_t af; + u_int8_t proto; + u_int8_t type; + u_int8_t code; + u_int8_t flags; + u_int8_t flagset; + u_int8_t min_ttl; + u_int8_t allow_opts; + u_int8_t rt; + u_int8_t return_ttl; + u_int8_t tos; +}; + +/* rule flags */ +#define PFRULE_DROP 0x0000 +#define PFRULE_RETURNRST 0x0001 +#define PFRULE_FRAGMENT 0x0002 +#define PFRULE_RETURNICMP 0x0004 +#define PFRULE_RETURN 0x0008 + +/* scrub flags */ +#define PFRULE_NODF 0x0100 +#define PFRULE_FRAGCROP 0x0200 /* non-buffering frag cache */ +#define PFRULE_FRAGDROP 0x0400 /* drop funny fragments */ +#define PFRULE_RANDOMID 0x0800 +#define PFRULE_REASSEMBLE_TCP 0x1000 + +#define PFSTATE_HIWAT 10000 /* default state table size */ + + +struct pf_state_scrub { + u_int16_t pfss_flags; +#define PFSS_TIMESTAMP 0x0001 /* modulate timestamp */ + u_int8_t pfss_ttl; /* stashed TTL */ + u_int8_t pad; + u_int32_t pfss_ts_mod; /* timestamp modulation */ +}; + +struct pf_state_host { + struct pf_addr addr; + u_int16_t port; + u_int16_t pad; +}; + +struct pf_state_peer { + u_int32_t seqlo; /* Max sequence number sent */ + u_int32_t seqhi; /* Max the other end ACKd + win */ + u_int32_t seqdiff; /* Sequence number modulator */ + u_int16_t max_win; /* largest window (pre scaling) */ + u_int8_t state; /* active state level */ + u_int8_t wscale; /* window scaling factor */ + u_int16_t mss; /* Maximum segment size option */ + struct pf_state_scrub *scrub; /* state is scrubbed */ +}; + +struct pf_state { + struct pf_state_host lan; + struct pf_state_host gwy; + struct pf_state_host ext; + struct pf_state_peer src; + struct pf_state_peer dst; + union pf_rule_ptr rule; + union pf_rule_ptr anchor; + union pf_rule_ptr nat_rule; + struct pf_addr rt_addr; + struct ifnet *rt_ifp; + u_int32_t creation; + u_int32_t expire; + u_int32_t packets[2]; + u_int32_t bytes[2]; + sa_family_t af; + u_int8_t proto; + u_int8_t direction; + u_int8_t log; + u_int8_t allow_opts; + u_int8_t timeout; + u_int8_t pad[2]; +}; + +struct pf_tree_node { + RB_ENTRY(pf_tree_node) entry; + struct pf_state *state; + struct pf_addr addr[2]; + u_int16_t port[2]; + sa_family_t af; + u_int8_t proto; +}; + +TAILQ_HEAD(pf_rulequeue, pf_rule); + +struct pf_anchor; + +struct pf_ruleset { + TAILQ_ENTRY(pf_ruleset) entries; +#define PF_RULESET_NAME_SIZE 16 + char name[PF_RULESET_NAME_SIZE]; + struct { + struct pf_rulequeue queues[2]; + struct { + struct pf_rulequeue *ptr; + u_int32_t ticket; + } active, inactive; + } rules[PF_RULESET_MAX]; + struct pf_anchor *anchor; + int tables; +}; + +TAILQ_HEAD(pf_rulesetqueue, pf_ruleset); + +struct pf_anchor { + TAILQ_ENTRY(pf_anchor) entries; + char name[PF_ANCHOR_NAME_SIZE]; + struct pf_rulesetqueue rulesets; + int tables; +}; + +TAILQ_HEAD(pf_anchorqueue, pf_anchor); + +#define PFR_TFLAG_PERSIST 0x00000001 +#define PFR_TFLAG_CONST 0x00000002 +#define PFR_TFLAG_ACTIVE 0x00000004 +#define PFR_TFLAG_INACTIVE 0x00000008 +#define PFR_TFLAG_REFERENCED 0x00000010 +#define PFR_TFLAG_REFDANCHOR 0x00000020 +#define PFR_TFLAG_USRMASK 0x00000003 +#define PFR_TFLAG_SETMASK 0x0000003C +#define PFR_TFLAG_ALLMASK 0x0000003F + +struct pfr_table { + char pfrt_anchor[PF_ANCHOR_NAME_SIZE]; + char pfrt_ruleset[PF_RULESET_NAME_SIZE]; + char pfrt_name[PF_TABLE_NAME_SIZE]; + u_int32_t pfrt_flags; + u_int8_t pfrt_fback; +}; + +enum { PFR_FB_NONE, PFR_FB_MATCH, PFR_FB_ADDED, PFR_FB_DELETED, + PFR_FB_CHANGED, PFR_FB_CLEARED, PFR_FB_DUPLICATE, + PFR_FB_NOTMATCH, PFR_FB_CONFLICT, PFR_FB_MAX }; + +struct pfr_addr { + union { + struct in_addr _pfra_ip4addr; + struct in6_addr _pfra_ip6addr; + } pfra_u; + u_int8_t pfra_af; + u_int8_t pfra_net; + u_int8_t pfra_not; + u_int8_t pfra_fback; +}; +#define pfra_ip4addr pfra_u._pfra_ip4addr +#define pfra_ip6addr pfra_u._pfra_ip6addr + +enum { PFR_DIR_IN, PFR_DIR_OUT, PFR_DIR_MAX }; +enum { PFR_OP_BLOCK, PFR_OP_PASS, PFR_OP_ADDR_MAX, PFR_OP_TABLE_MAX }; +#define PFR_OP_XPASS PFR_OP_ADDR_MAX + +struct pfr_astats { + struct pfr_addr pfras_a; + u_int64_t pfras_packets[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; + u_int64_t pfras_bytes[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; + long pfras_tzero; +}; + +enum { PFR_REFCNT_RULE, PFR_REFCNT_ANCHOR, PFR_REFCNT_MAX }; + +struct pfr_tstats { + struct pfr_table pfrts_t; + u_int64_t pfrts_packets[PFR_DIR_MAX][PFR_OP_TABLE_MAX]; + u_int64_t pfrts_bytes[PFR_DIR_MAX][PFR_OP_TABLE_MAX]; + u_int64_t pfrts_match; + u_int64_t pfrts_nomatch; + long pfrts_tzero; + int pfrts_cnt; + int pfrts_refcnt[PFR_REFCNT_MAX]; +}; +#define pfrts_name pfrts_t.pfrt_name +#define pfrts_flags pfrts_t.pfrt_flags + +#ifndef __OpenBSD__ +union sockaddr_union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; +}; +#endif + +SLIST_HEAD(pfr_kentryworkq, pfr_kentry); +struct pfr_kentry { + struct radix_node pfrke_node[2]; + union sockaddr_union pfrke_sa; + u_int64_t pfrke_packets[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; + u_int64_t pfrke_bytes[PFR_DIR_MAX][PFR_OP_ADDR_MAX]; + SLIST_ENTRY(pfr_kentry) pfrke_workq; + long pfrke_tzero; + u_int8_t pfrke_af; + u_int8_t pfrke_net; + u_int8_t pfrke_not; + u_int8_t pfrke_mark; +}; + +SLIST_HEAD(pfr_ktableworkq, pfr_ktable); +RB_HEAD(pfr_ktablehead, pfr_ktable); +struct pfr_ktable { + struct pfr_tstats pfrkt_ts; + RB_ENTRY(pfr_ktable) pfrkt_tree; + SLIST_ENTRY(pfr_ktable) pfrkt_workq; + struct radix_node_head *pfrkt_ip4; + struct radix_node_head *pfrkt_ip6; + struct pfr_ktable *pfrkt_shadow; + struct pfr_ktable *pfrkt_root; + struct pf_ruleset *pfrkt_rs; + int pfrkt_nflags; +}; +#define pfrkt_t pfrkt_ts.pfrts_t +#define pfrkt_name pfrkt_t.pfrt_name +#define pfrkt_anchor pfrkt_t.pfrt_anchor +#define pfrkt_ruleset pfrkt_t.pfrt_ruleset +#define pfrkt_flags pfrkt_t.pfrt_flags +#define pfrkt_cnt pfrkt_ts.pfrts_cnt +#define pfrkt_refcnt pfrkt_ts.pfrts_refcnt +#define pfrkt_packets pfrkt_ts.pfrts_packets +#define pfrkt_bytes pfrkt_ts.pfrts_bytes +#define pfrkt_match pfrkt_ts.pfrts_match +#define pfrkt_nomatch pfrkt_ts.pfrts_nomatch +#define pfrkt_tzero pfrkt_ts.pfrts_tzero + +struct pf_pdesc { + u_int64_t tot_len; /* Make Mickey money */ + union { + struct tcphdr *tcp; + struct udphdr *udp; + struct icmp *icmp; +#ifdef INET6 + struct icmp6_hdr *icmp6; +#endif /* INET6 */ + void *any; + } hdr; + struct pf_addr *src; + struct pf_addr *dst; + u_int16_t *ip_sum; + u_int32_t p_len; /* total length of payload */ + u_int16_t flags; /* Let SCRUB trigger behavior in + * state code. Easier than tags */ +#define PFDESC_TCP_NORM 0x0001 /* TCP shall be statefully scrubbed */ + sa_family_t af; + u_int8_t proto; + u_int8_t tos; +}; + +/* flags for RDR options */ +#define PF_DPORT_RANGE 0x01 /* Dest port uses range */ +#define PF_RPORT_RANGE 0x02 /* RDR'ed port uses range */ + +/* Reasons code for passing/dropping a packet */ +#define PFRES_MATCH 0 /* Explicit match of a rule */ +#define PFRES_BADOFF 1 /* Bad offset for pull_hdr */ +#define PFRES_FRAG 2 /* Dropping following fragment */ +#define PFRES_SHORT 3 /* Dropping short packet */ +#define PFRES_NORM 4 /* Dropping by normalizer */ +#define PFRES_MEMORY 5 /* Dropped due to lacking mem */ +#define PFRES_MAX 6 /* total+1 */ + +#define PFRES_NAMES { \ + "match", \ + "bad-offset", \ + "fragment", \ + "short", \ + "normalize", \ + "memory", \ + NULL \ +} + +/* UDP state enumeration */ +#define PFUDPS_NO_TRAFFIC 0 +#define PFUDPS_SINGLE 1 +#define PFUDPS_MULTIPLE 2 + +#define PFUDPS_NSTATES 3 /* number of state levels */ + +#define PFUDPS_NAMES { \ + "NO_TRAFFIC", \ + "SINGLE", \ + "MULTIPLE", \ + NULL \ +} + +/* Other protocol state enumeration */ +#define PFOTHERS_NO_TRAFFIC 0 +#define PFOTHERS_SINGLE 1 +#define PFOTHERS_MULTIPLE 2 + +#define PFOTHERS_NSTATES 3 /* number of state levels */ + +#define PFOTHERS_NAMES { \ + "NO_TRAFFIC", \ + "SINGLE", \ + "MULTIPLE", \ + NULL \ +} + +#define FCNT_STATE_SEARCH 0 +#define FCNT_STATE_INSERT 1 +#define FCNT_STATE_REMOVALS 2 +#define FCNT_MAX 3 + + +#define ACTION_SET(a, x) \ + do { \ + if ((a) != NULL) \ + *(a) = (x); \ + } while (0) + +#define REASON_SET(a, x) \ + do { \ + if ((a) != NULL) \ + *(a) = (x); \ + if (x < PFRES_MAX) \ + pf_status.counters[x]++; \ + } while (0) + +struct pf_status { + u_int64_t counters[PFRES_MAX]; + u_int64_t fcounters[FCNT_MAX]; + u_int64_t pcounters[2][2][3]; + u_int64_t bcounters[2][2]; + u_int32_t running; + u_int32_t states; + u_int32_t since; + u_int32_t debug; + char ifname[IFNAMSIZ]; +}; + +struct cbq_opts { + u_int minburst; + u_int maxburst; + u_int pktsize; + u_int maxpktsize; + u_int ns_per_byte; + u_int maxidle; + int minidle; + u_int offtime; + int flags; +}; + +struct priq_opts { + int flags; +}; + +struct hfsc_opts { + /* real-time service curve */ + u_int rtsc_m1; /* slope of the 1st segment in bps */ + u_int rtsc_d; /* the x-projection of m1 in msec */ + u_int rtsc_m2; /* slope of the 2nd segment in bps */ + /* link-sharing service curve */ + u_int lssc_m1; + u_int lssc_d; + u_int lssc_m2; + /* upper-limit service curve */ + u_int ulsc_m1; + u_int ulsc_d; + u_int ulsc_m2; + int flags; +}; + +struct pf_altq { + char ifname[IFNAMSIZ]; + + void *altq_disc; /* discipline-specific state */ + TAILQ_ENTRY(pf_altq) entries; + + /* scheduler spec */ + u_int8_t scheduler; /* scheduler type */ + u_int16_t tbrsize; /* tokenbuket regulator size */ + u_int32_t ifbandwidth; /* interface bandwidth */ + + /* queue spec */ + char qname[PF_QNAME_SIZE]; /* queue name */ + char parent[PF_QNAME_SIZE]; /* parent name */ + u_int32_t parent_qid; /* parent queue id */ + u_int32_t bandwidth; /* queue bandwidth */ + u_int8_t priority; /* priority */ + u_int16_t qlimit; /* queue size limit */ + u_int16_t flags; /* misc flags */ + union { + struct cbq_opts cbq_opts; + struct priq_opts priq_opts; + struct hfsc_opts hfsc_opts; + } pq_u; + + u_int32_t qid; /* return value */ +}; + +struct pf_tag { + u_int16_t tag; /* tag id */ +}; + +struct pf_tagname { + TAILQ_ENTRY(pf_tagname) entries; + char name[PF_TAG_NAME_SIZE]; + u_int16_t tag; + int ref; +}; + +TAILQ_HEAD(pf_tagnames, pf_tagname); + +#define PFFRAG_FRENT_HIWAT 5000 /* Number of fragment entries */ +#define PFFRAG_FRAG_HIWAT 1000 /* Number of fragmented packets */ +#define PFFRAG_FRCENT_HIWAT 50000 /* Number of fragment cache entries */ +#define PFFRAG_FRCACHE_HIWAT 10000 /* Number of fragment descriptors */ + +/* + * ioctl parameter structures + */ + +struct pfioc_pooladdr { + u_int32_t action; + u_int32_t ticket; + u_int32_t nr; + u_int32_t r_num; + u_int8_t r_action; + u_int8_t r_last; + u_int8_t af; + char anchor[PF_ANCHOR_NAME_SIZE]; + char ruleset[PF_RULESET_NAME_SIZE]; + struct pf_pooladdr addr; +}; + +struct pfioc_rule { + u_int32_t action; + u_int32_t ticket; + u_int32_t pool_ticket; + u_int32_t nr; + char anchor[PF_ANCHOR_NAME_SIZE]; + char ruleset[PF_RULESET_NAME_SIZE]; + struct pf_rule rule; +}; + +struct pfioc_natlook { + struct pf_addr saddr; + struct pf_addr daddr; + struct pf_addr rsaddr; + struct pf_addr rdaddr; + u_int16_t sport; + u_int16_t dport; + u_int16_t rsport; + u_int16_t rdport; + sa_family_t af; + u_int8_t proto; + u_int8_t direction; +}; + +struct pfioc_state { + u_int32_t nr; + struct pf_state state; +}; + +struct pfioc_state_kill { + /* XXX returns the number of states killed in psk_af */ + sa_family_t psk_af; + int psk_proto; + struct pf_rule_addr psk_src; + struct pf_rule_addr psk_dst; +}; + +struct pfioc_states { + int ps_len; + union { + caddr_t psu_buf; + struct pf_state *psu_states; + } ps_u; +#define ps_buf ps_u.psu_buf +#define ps_states ps_u.psu_states +}; + +struct pfioc_if { + char ifname[IFNAMSIZ]; +}; + +struct pfioc_tm { + int timeout; + int seconds; +}; + +struct pfioc_limit { + int index; + unsigned limit; +}; + +struct pfioc_altq { + u_int32_t action; + u_int32_t ticket; + u_int32_t nr; + struct pf_altq altq; +}; + +struct pfioc_qstats { + u_int32_t ticket; + u_int32_t nr; + void *buf; + int nbytes; + u_int8_t scheduler; +}; + +struct pfioc_anchor { + u_int32_t nr; + char name[PF_ANCHOR_NAME_SIZE]; +}; + +struct pfioc_ruleset { + u_int32_t nr; + char anchor[PF_ANCHOR_NAME_SIZE]; + char name[PF_RULESET_NAME_SIZE]; +}; + +#define PFR_FLAG_ATOMIC 0x00000001 +#define PFR_FLAG_DUMMY 0x00000002 +#define PFR_FLAG_FEEDBACK 0x00000004 +#define PFR_FLAG_CLSTATS 0x00000008 +#define PFR_FLAG_ADDRSTOO 0x00000010 +#define PFR_FLAG_REPLACE 0x00000020 +#define PFR_FLAG_ALLRSETS 0x00000040 +#define PFR_FLAG_ALLMASK 0x0000007F + +struct pfioc_table { + struct pfr_table pfrio_table; + void *pfrio_buffer; + int pfrio_esize; + int pfrio_size; + int pfrio_size2; + int pfrio_nadd; + int pfrio_ndel; + int pfrio_nchange; + int pfrio_flags; + int pfrio_ticket; +}; +#define pfrio_exists pfrio_nadd +#define pfrio_nzero pfrio_nadd +#define pfrio_nmatch pfrio_nadd +#define pfrio_naddr pfrio_size2 +#define pfrio_setflag pfrio_size2 +#define pfrio_clrflag pfrio_nadd + + +/* + * ioctl operations + */ + +#define DIOCSTART _IO ('D', 1) +#define DIOCSTOP _IO ('D', 2) +#define DIOCBEGINRULES _IOWR('D', 3, struct pfioc_rule) +#define DIOCADDRULE _IOWR('D', 4, struct pfioc_rule) +#define DIOCCOMMITRULES _IOWR('D', 5, struct pfioc_rule) +#define DIOCGETRULES _IOWR('D', 6, struct pfioc_rule) +#define DIOCGETRULE _IOWR('D', 7, struct pfioc_rule) +/* XXX cut 8 - 17 */ +#define DIOCCLRSTATES _IO ('D', 18) +#define DIOCGETSTATE _IOWR('D', 19, struct pfioc_state) +#define DIOCSETSTATUSIF _IOWR('D', 20, struct pfioc_if) +#define DIOCGETSTATUS _IOWR('D', 21, struct pf_status) +#define DIOCCLRSTATUS _IO ('D', 22) +#define DIOCNATLOOK _IOWR('D', 23, struct pfioc_natlook) +#define DIOCSETDEBUG _IOWR('D', 24, u_int32_t) +#define DIOCGETSTATES _IOWR('D', 25, struct pfioc_states) +#define DIOCCHANGERULE _IOWR('D', 26, struct pfioc_rule) +/* XXX cut 26 - 28 */ +#define DIOCSETTIMEOUT _IOWR('D', 29, struct pfioc_tm) +#define DIOCGETTIMEOUT _IOWR('D', 30, struct pfioc_tm) +#define DIOCADDSTATE _IOWR('D', 37, struct pfioc_state) +#define DIOCCLRRULECTRS _IO ('D', 38) +#define DIOCGETLIMIT _IOWR('D', 39, struct pfioc_limit) +#define DIOCSETLIMIT _IOWR('D', 40, struct pfioc_limit) +#define DIOCKILLSTATES _IOWR('D', 41, struct pfioc_state_kill) +#define DIOCSTARTALTQ _IO ('D', 42) +#define DIOCSTOPALTQ _IO ('D', 43) +#define DIOCBEGINALTQS _IOWR('D', 44, u_int32_t) +#define DIOCADDALTQ _IOWR('D', 45, struct pfioc_altq) +#define DIOCCOMMITALTQS _IOWR('D', 46, u_int32_t) +#define DIOCGETALTQS _IOWR('D', 47, struct pfioc_altq) +#define DIOCGETALTQ _IOWR('D', 48, struct pfioc_altq) +#define DIOCCHANGEALTQ _IOWR('D', 49, struct pfioc_altq) +#define DIOCGETQSTATS _IOWR('D', 50, struct pfioc_qstats) +#define DIOCBEGINADDRS _IOWR('D', 51, struct pfioc_pooladdr) +#define DIOCADDADDR _IOWR('D', 52, struct pfioc_pooladdr) +#define DIOCGETADDRS _IOWR('D', 53, struct pfioc_pooladdr) +#define DIOCGETADDR _IOWR('D', 54, struct pfioc_pooladdr) +#define DIOCCHANGEADDR _IOWR('D', 55, struct pfioc_pooladdr) +#define DIOCGETANCHORS _IOWR('D', 56, struct pfioc_anchor) +#define DIOCGETANCHOR _IOWR('D', 57, struct pfioc_anchor) +#define DIOCGETRULESETS _IOWR('D', 58, struct pfioc_ruleset) +#define DIOCGETRULESET _IOWR('D', 59, struct pfioc_ruleset) +#define DIOCRCLRTABLES _IOWR('D', 60, struct pfioc_table) +#define DIOCRADDTABLES _IOWR('D', 61, struct pfioc_table) +#define DIOCRDELTABLES _IOWR('D', 62, struct pfioc_table) +#define DIOCRGETTABLES _IOWR('D', 63, struct pfioc_table) +#define DIOCRGETTSTATS _IOWR('D', 64, struct pfioc_table) +#define DIOCRCLRTSTATS _IOWR('D', 65, struct pfioc_table) +#define DIOCRCLRADDRS _IOWR('D', 66, struct pfioc_table) +#define DIOCRADDADDRS _IOWR('D', 67, struct pfioc_table) +#define DIOCRDELADDRS _IOWR('D', 68, struct pfioc_table) +#define DIOCRSETADDRS _IOWR('D', 69, struct pfioc_table) +#define DIOCRGETADDRS _IOWR('D', 70, struct pfioc_table) +#define DIOCRGETASTATS _IOWR('D', 71, struct pfioc_table) +#define DIOCRCLRASTATS _IOWR('D', 72, struct pfioc_table) +#define DIOCRTSTADDRS _IOWR('D', 73, struct pfioc_table) +#define DIOCRSETTFLAGS _IOWR('D', 74, struct pfioc_table) +#define DIOCRINABEGIN _IOWR('D', 75, struct pfioc_table) +#define DIOCRINACOMMIT _IOWR('D', 76, struct pfioc_table) +#define DIOCRINADEFINE _IOWR('D', 77, struct pfioc_table) + +#ifdef _KERNEL +RB_HEAD(pf_state_tree, pf_tree_node); +RB_PROTOTYPE(pf_state_tree, pf_tree_node, entry, pf_state_compare); +extern struct pf_state_tree tree_lan_ext, tree_ext_gwy; + +extern struct pf_anchorqueue pf_anchors; +extern struct pf_ruleset pf_main_ruleset; +TAILQ_HEAD(pf_poolqueue, pf_pool); +extern struct pf_poolqueue pf_pools[2]; +TAILQ_HEAD(pf_altqqueue, pf_altq); +extern struct pf_altqqueue pf_altqs[2]; +extern struct pf_palist pf_pabuf; + + +extern u_int32_t ticket_altqs_active; +extern u_int32_t ticket_altqs_inactive; +extern u_int32_t ticket_pabuf; +extern struct pf_altqqueue *pf_altqs_active; +extern struct pf_altqqueue *pf_altqs_inactive; +extern struct pf_poolqueue *pf_pools_active; +extern struct pf_poolqueue *pf_pools_inactive; +extern int pf_tbladdr_setup(struct pf_ruleset *, + struct pf_addr_wrap *); +extern void pf_tbladdr_remove(struct pf_addr_wrap *); +extern void pf_tbladdr_copyout(struct pf_addr_wrap *); +extern int pf_dynaddr_setup(struct pf_addr_wrap *, + sa_family_t); +extern void pf_dynaddr_copyout(struct pf_addr_wrap *); +extern void pf_dynaddr_remove(struct pf_addr_wrap *); +extern void pf_calc_skip_steps(struct pf_rulequeue *); +extern void pf_rule_set_qid(struct pf_rulequeue *); +extern u_int32_t pf_qname_to_qid(char *); +extern void pf_update_anchor_rules(void); +extern struct pool pf_tree_pl, pf_rule_pl, pf_addr_pl; +extern struct pool pf_state_pl, pf_altq_pl, pf_pooladdr_pl; +extern struct pool pf_state_scrub_pl; +extern void pf_purge_timeout(void *); +extern void pf_purge_expired_states(void); +extern int pf_insert_state(struct pf_state *); +extern struct pf_state *pf_find_state(struct pf_state_tree *, + struct pf_tree_node *); +extern struct pf_anchor *pf_find_anchor(const char *); +extern struct pf_ruleset *pf_find_ruleset(char *, char *); +extern struct pf_ruleset *pf_find_or_create_ruleset(char *, char *); +extern void pf_remove_if_empty_ruleset( + struct pf_ruleset *); + +extern struct ifnet *status_ifp; +extern struct pf_rule pf_default_rule; +extern void pf_addrcpy(struct pf_addr *, struct pf_addr *, + u_int8_t); +void pf_rm_rule(struct pf_rulequeue *, + struct pf_rule *); + +#ifdef INET +int pf_test(int, struct ifnet *, struct mbuf **); +#endif /* INET */ + +#ifdef INET6 +int pf_test6(int, struct ifnet *, struct mbuf **); +#endif /* INET */ + +void *pf_pull_hdr(struct mbuf *, int, void *, int, u_short *, u_short *, + sa_family_t); +void pf_change_a(void *, u_int16_t *, u_int32_t, u_int8_t); +int pflog_packet(struct ifnet *, struct mbuf *, sa_family_t, u_int8_t, + u_int8_t, struct pf_rule *, struct pf_rule *, struct pf_ruleset *); +int pf_match_addr(u_int8_t, struct pf_addr *, struct pf_addr *, + struct pf_addr *, sa_family_t); +int pf_match(u_int8_t, u_int16_t, u_int16_t, u_int16_t); +int pf_match_port(u_int8_t, u_int16_t, u_int16_t, u_int16_t); +int pf_match_uid(u_int8_t, uid_t, uid_t, uid_t); +int pf_match_gid(u_int8_t, gid_t, gid_t, gid_t); + +void pf_normalize_init(void); +int pf_normalize_ip(struct mbuf **, int, struct ifnet *, u_short *); +int pf_normalize_ip6(struct mbuf **, int, struct ifnet *, u_short *); +int pf_normalize_tcp(int, struct ifnet *, struct mbuf *, int, int, void *, + struct pf_pdesc *); +void pf_normalize_tcp_cleanup(struct pf_state *); +int pf_normalize_tcp_init(struct mbuf *, int, struct pf_pdesc *, + struct tcphdr *, struct pf_state_peer *, struct pf_state_peer *); +int pf_normalize_tcp_stateful(struct mbuf *, int, struct pf_pdesc *, + u_short *, struct tcphdr *, struct pf_state_peer *, + struct pf_state_peer *, int *); +u_int32_t + pf_state_expires(const struct pf_state *); +void pf_purge_expired_fragments(void); +int pf_routable(struct pf_addr *addr, sa_family_t af); +void pfr_initialize(void); +int pfr_match_addr(struct pfr_ktable *, struct pf_addr *, sa_family_t); +void pfr_update_stats(struct pfr_ktable *, struct pf_addr *, sa_family_t, + u_int64_t, int, int, int); +struct pfr_ktable * + pfr_attach_table(struct pf_ruleset *, char *); +void pfr_detach_table(struct pfr_ktable *); +int pfr_clr_tables(struct pfr_table *, int *, int); +int pfr_add_tables(struct pfr_table *, int, int *, int); +int pfr_del_tables(struct pfr_table *, int, int *, int); +int pfr_get_tables(struct pfr_table *, struct pfr_table *, int *, int); +int pfr_get_tstats(struct pfr_table *, struct pfr_tstats *, int *, int); +int pfr_clr_tstats(struct pfr_table *, int, int *, int); +int pfr_set_tflags(struct pfr_table *, int, int, int, int *, int *, int); +int pfr_clr_addrs(struct pfr_table *, int *, int); +int pfr_add_addrs(struct pfr_table *, struct pfr_addr *, int, int *, + int); +int pfr_del_addrs(struct pfr_table *, struct pfr_addr *, int, int *, + int); +int pfr_set_addrs(struct pfr_table *, struct pfr_addr *, int, int *, + int *, int *, int *, int); +int pfr_get_addrs(struct pfr_table *, struct pfr_addr *, int *, int); +int pfr_get_astats(struct pfr_table *, struct pfr_astats *, int *, int); +int pfr_clr_astats(struct pfr_table *, struct pfr_addr *, int, int *, + int); +int pfr_tst_addrs(struct pfr_table *, struct pfr_addr *, int, int *, + int); +int pfr_ina_begin(int *, int *, int); +int pfr_ina_commit(int, int *, int *, int); +int pfr_ina_define(struct pfr_table *, struct pfr_addr *, int, int *, + int *, int, int); + +u_int16_t pf_tagname2tag(char *); +void pf_tag2tagname(u_int16_t, char *); +void pf_tag_unref(u_int16_t); +int pf_tag_packet(struct mbuf *, struct pf_tag *, int); + +extern struct pf_status pf_status; +extern struct pool pf_frent_pl, pf_frag_pl; + +struct pf_pool_limit { + void *pp; + unsigned limit; +}; +extern struct pf_pool_limit pf_pool_limits[PF_LIMIT_MAX]; + +#endif /* _KERNEL */ + +#endif /* _NET_PFVAR_H_ */ Index: sys/netinet/ip_output.c =================================================================== RCS file: /cvsroot/src/sys/netinet/ip_output.c,v retrieving revision 1.107 diff -u -r1.107 ip_output.c --- sys/netinet/ip_output.c 2003/06/30 02:08:28 1.107 +++ sys/netinet/ip_output.c 2003/06/30 04:58:41 @@ -165,11 +165,11 @@ va_dcl #endif { - struct ip *ip, *mhip; + struct ip *ip; struct ifnet *ifp; struct mbuf *m = m0; int hlen = sizeof (struct ip); - int len, off, error = 0; + int len, error = 0; struct route iproute; struct sockaddr_in *dst; struct in_ifaddr *ia; @@ -573,7 +573,6 @@ if (state.encap) ifp = ro->ro_rt->rt_ifp; } - } skip_ipsec: #endif /*IPSEC*/ @@ -658,18 +657,85 @@ ipstat.ips_cantfrag++; goto bad; } - len = (mtu - hlen) &~ 7; - if (len < 8) { - error = EMSGSIZE; + + error = ip_fragment(m, ifp, mtu); + if (error == EMSGSIZE) goto bad; + + for (m = m0; m; m = m0) { + m0 = m->m_nextpkt; + m->m_nextpkt = 0; + if (error == 0) { +#if IFA_STATS + /* + * search for the source address structure to + * maintain output statistics. + */ + INADDR_TO_IA(ip->ip_src, ia); + if (ia) { + ia->ia_ifa.ifa_data.ifad_outbytes += + ntohs(ip->ip_len); + } +#endif +#ifdef IPSEC + /* clean ipsec history once it goes out of the node */ + ipsec_delaux(m); +#endif + KASSERT((m->m_pkthdr.csum_flags & + (M_CSUM_UDPv4 | M_CSUM_TCPv4)) == 0); + error = (*ifp->if_output)(ifp, m, sintosa(dst), + ro->ro_rt); + } else + m_freem(m); } - { - int mhlen, firstlen = len; - struct mbuf **mnext = &m->m_nextpkt; + if (error == 0) + ipstat.ips_fragmented++; + } +done: + if (ro == &iproute && (flags & IP_ROUTETOIF) == 0 && ro->ro_rt) { + RTFREE(ro->ro_rt); + ro->ro_rt = 0; + } + +#ifdef IPSEC + if (sp != NULL) { + KEYDEBUG(KEYDEBUG_IPSEC_STAMP, + printf("DP ip_output call free SP:%p\n", sp)); + key_freesp(sp); + } +#endif /* IPSEC */ + + return (error); +bad: + m_freem(m); + goto done; +} + +int +ip_fragment(struct mbuf *m, struct ifnet *ifp, u_long mtu) +{ + struct ip *ip, *mhip; + struct mbuf *m0; + int len, hlen, off; + int mhlen, firstlen; + struct mbuf **mnext; + int sw_csum; int fragments = 0; int s; + int error = 0; + + ip = mtod(m, struct ip *); + hlen = ip->ip_hl << 2; + sw_csum = m->m_pkthdr.csum_flags & ~ifp->if_csum_flags_tx; + + len = (mtu - hlen) &~ 7; + if (len < 8) + return (EMSGSIZE); + firstlen = len; + mnext = &m->m_nextpkt; + /* * Loop through length of segment after first fragment, * make new header and copy data of each part and link onto chain. @@ -748,54 +814,8 @@ if (ifp->if_snd.ifq_maxlen - ifp->if_snd.ifq_len < fragments) error = ENOBUFS; splx(s); - for (m = m0; m; m = m0) { - m0 = m->m_nextpkt; - m->m_nextpkt = 0; - if (error == 0) { -#if IFA_STATS - /* - * search for the source address structure to - * maintain output statistics. - */ - INADDR_TO_IA(ip->ip_src, ia); - if (ia) { - ia->ia_ifa.ifa_data.ifad_outbytes += - ntohs(ip->ip_len); - } -#endif -#ifdef IPSEC - /* clean ipsec history once it goes out of the node */ - ipsec_delaux(m); -#endif - KASSERT((m->m_pkthdr.csum_flags & - (M_CSUM_UDPv4 | M_CSUM_TCPv4)) == 0); - error = (*ifp->if_output)(ifp, m, sintosa(dst), - ro->ro_rt); - } else - m_freem(m); - } - if (error == 0) - ipstat.ips_fragmented++; - } -done: - if (ro == &iproute && (flags & IP_ROUTETOIF) == 0 && ro->ro_rt) { - RTFREE(ro->ro_rt); - ro->ro_rt = 0; - } - -#ifdef IPSEC - if (sp != NULL) { - KEYDEBUG(KEYDEBUG_IPSEC_STAMP, - printf("DP ip_output call free SP:%p\n", sp)); - key_freesp(sp); - } -#endif /* IPSEC */ - return (error); -bad: - m_freem(m); - goto done; } /* Index: sys/netinet/ip_var.h =================================================================== RCS file: /cvsroot/src/sys/netinet/ip_var.h,v retrieving revision 1.56 diff -u -r1.56 ip_var.h --- sys/netinet/ip_var.h 2003/06/29 22:31:57 1.56 +++ sys/netinet/ip_var.h 2003/06/30 04:58:41 @@ -229,6 +229,7 @@ int ip_optcopy __P((struct ip *, struct ip *)); u_int ip_optlen __P((struct inpcb *)); int ip_output __P((struct mbuf *, ...)); +int ip_fragment(struct mbuf *, struct ifnet *, u_long); int ip_pcbopts __P((struct mbuf **, struct mbuf *)); struct mbuf * ip_reass __P((struct ipqent *, struct ipq *)); Index: sys/netinet6/in6_ifattach.c =================================================================== RCS file: /cvsroot/src/sys/netinet6/in6_ifattach.c,v retrieving revision 1.54 diff -u -r1.54 in6_ifattach.c --- sys/netinet6/in6_ifattach.c 2002/11/02 07:30:55 1.54 +++ sys/netinet6/in6_ifattach.c 2003/06/30 04:58:41 @@ -579,6 +579,8 @@ /* some of the interfaces are inherently not IPv6 capable */ switch (ifp->if_type) { case IFT_BRIDGE: + case IFT_PFLOG: + case IFT_PFSYNC: return; } Index: sys/arch/acorn26/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/acorn26/conf/GENERIC,v retrieving revision 1.13 diff -u -r1.13 GENERIC --- sys/arch/acorn26/conf/GENERIC 2003/04/26 14:10:15 1.13 +++ sys/arch/acorn26/conf/GENERIC 2003/06/30 04:58:42 @@ -234,3 +234,6 @@ pseudo-device rnd # /dev/random and in-kernel generator pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/acorn26/conf/majors.acorn26 =================================================================== RCS file: /cvsroot/src/sys/arch/acorn26/conf/majors.acorn26,v retrieving revision 1.4 diff -u -r1.4 majors.acorn26 --- sys/arch/acorn26/conf/majors.acorn26 2003/04/25 21:10:46 1.4 +++ sys/arch/acorn26/conf/majors.acorn26 2003/06/30 04:58:42 @@ -34,3 +34,4 @@ device-major systrace char 28 systrace device-major cgd char 29 block 8 cgd device-major ksyms char 30 ksyms +device-major pf char 31 pf Index: sys/arch/acorn32/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/acorn32/conf/GENERIC,v retrieving revision 1.26 diff -u -r1.26 GENERIC --- sys/arch/acorn32/conf/GENERIC 2003/04/26 14:10:14 1.26 +++ sys/arch/acorn32/conf/GENERIC 2003/06/30 04:58:42 @@ -357,3 +357,6 @@ #makeoptions MONITOR="AKF60" makeoptions MODES="1024,768,60 1024,768,70 800,600,60 640,480,60 1280,1024 1152,900" pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/acorn32/conf/majors.acorn32 =================================================================== RCS file: /cvsroot/src/sys/arch/acorn32/conf/majors.acorn32,v retrieving revision 1.5 diff -u -r1.5 majors.acorn32 --- sys/arch/acorn32/conf/majors.acorn32 2003/06/08 20:12:33 1.5 +++ sys/arch/acorn32/conf/majors.acorn32 2003/06/30 04:58:42 @@ -72,3 +72,4 @@ device-major netsmb char 87 netsmb device-major nsmb char 88 nsmb +device-major pf char 89 pf Index: sys/arch/algor/conf/majors.algor =================================================================== RCS file: /cvsroot/src/sys/arch/algor/conf/majors.algor,v retrieving revision 1.4 diff -u -r1.4 majors.algor --- sys/arch/algor/conf/majors.algor 2003/04/25 21:10:47 1.4 +++ sys/arch/algor/conf/majors.algor 2003/06/30 04:58:42 @@ -67,3 +67,4 @@ device-major systrace char 67 systrace device-major cgd char 68 block 18 cgd device-major ksyms char 69 ksyms +device-major pf char 70 pf Index: sys/arch/alpha/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/alpha/conf/GENERIC,v retrieving revision 1.230 diff -u -r1.230 GENERIC --- sys/arch/alpha/conf/GENERIC 2003/05/04 01:19:17 1.230 +++ sys/arch/alpha/conf/GENERIC 2003/06/30 04:58:42 @@ -578,3 +578,6 @@ pseudo-device clockctl # user control of clock subsystem pseudo-device wsfont pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/alpha/conf/majors.alpha =================================================================== RCS file: /cvsroot/src/sys/arch/alpha/conf/majors.alpha,v retrieving revision 1.8 diff -u -r1.8 majors.alpha --- sys/arch/alpha/conf/majors.alpha 2003/06/01 20:19:38 1.8 +++ sys/arch/alpha/conf/majors.alpha 2003/06/30 04:58:42 @@ -76,3 +76,4 @@ device-major ksyms char 75 ksyms device-major wsfont char 76 wsfont device-major nsmb char 77 nsmb +device-major pf char 78 pf Index: sys/arch/amd64/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/amd64/conf/GENERIC,v retrieving revision 1.5 diff -u -r1.5 GENERIC --- sys/arch/amd64/conf/GENERIC 2003/05/29 20:22:33 1.5 +++ sys/arch/amd64/conf/GENERIC 2003/06/30 04:58:43 @@ -752,6 +752,9 @@ #options RND_COM # use "com" randomness as well (BROKEN) pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if # a pseudo device needed for Coda # also needs CODA (above) pseudo-device vcoda 4 # coda minicache <-> venus comm. Index: sys/arch/amd64/conf/majors.amd64 =================================================================== RCS file: /cvsroot/src/sys/arch/amd64/conf/majors.amd64,v retrieving revision 1.1 diff -u -r1.1 majors.amd64 --- sys/arch/amd64/conf/majors.amd64 2003/04/26 18:39:36 1.1 +++ sys/arch/amd64/conf/majors.amd64 2003/06/30 04:58:43 @@ -80,3 +80,4 @@ device-major pci char 83 pci device-major irframe char 84 irframe device-major ksyms char 85 ksyms +device-major pf char 86 pf Index: sys/arch/amiga/conf/GENERIC.in =================================================================== RCS file: /cvsroot/src/sys/arch/amiga/conf/GENERIC.in,v retrieving revision 1.12 diff -u -r1.12 GENERIC.in --- sys/arch/amiga/conf/GENERIC.in 2003/04/16 20:42:34 1.12 +++ sys/arch/amiga/conf/GENERIC.in 2003/06/30 04:58:43 @@ -554,6 +554,9 @@ pseudo-device vlan # IEEE 802.1q encapsulation pseudo-device bridge # simple inter-network bridging #options BRIDGE_IPF # bridge uses IP/IPv6 pfil hooks too +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if pseudo-device view 4 # views (needed for grfcc) m4_ifdef(`INSTALL_CONFIGURATION', `m4_dnl Index: sys/arch/amiga/conf/majors.amiga =================================================================== RCS file: /cvsroot/src/sys/arch/amiga/conf/majors.amiga,v retrieving revision 1.4 diff -u -r1.4 majors.amiga --- sys/arch/amiga/conf/majors.amiga 2003/04/25 21:10:48 1.4 +++ sys/arch/amiga/conf/majors.amiga 2003/06/30 04:58:44 @@ -55,3 +55,4 @@ device-major systrace char 56 systrace device-major cgd char 57 block 18 cgd device-major ksyms char 58 ksyms +device-major pf char 59 pf Index: sys/arch/arc/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/arc/conf/GENERIC,v retrieving revision 1.90 diff -u -r1.90 GENERIC --- sys/arch/arc/conf/GENERIC 2003/06/15 06:10:42 1.90 +++ sys/arch/arc/conf/GENERIC 2003/06/30 04:58:44 @@ -414,3 +414,6 @@ # mouse & keyboard multiplexor pseudo-devices #pseudo-device wsmux pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/arc/conf/majors.arc =================================================================== RCS file: /cvsroot/src/sys/arch/arc/conf/majors.arc,v retrieving revision 1.4 diff -u -r1.4 majors.arc --- sys/arch/arc/conf/majors.arc 2003/04/25 21:10:48 1.4 +++ sys/arch/arc/conf/majors.arc 2003/06/30 04:58:44 @@ -49,3 +49,4 @@ device-major systrace char 53 systrace device-major cgd char 54 block 10 cgd device-major ksyms char 55 ksyms +device-major pf char 56 pf Index: sys/arch/arm/conf/majors.arm32 =================================================================== RCS file: /cvsroot/src/sys/arch/arm/conf/majors.arm32,v retrieving revision 1.7 diff -u -r1.7 majors.arm32 --- sys/arch/arm/conf/majors.arm32 2003/04/25 21:10:49 1.7 +++ sys/arch/arm/conf/majors.arm32 2003/06/30 04:58:44 @@ -102,3 +102,4 @@ device-major apm char 103 apm device-major sscom char 104 sscom device-major ksyms char 105 ksyms +device-major pf char 106 pf Index: sys/arch/atari/conf/GENERIC.in =================================================================== RCS file: /cvsroot/src/sys/arch/atari/conf/GENERIC.in,v retrieving revision 1.32 diff -u -r1.32 GENERIC.in --- sys/arch/atari/conf/GENERIC.in 2003/04/10 22:06:54 1.32 +++ sys/arch/atari/conf/GENERIC.in 2003/06/30 04:58:44 @@ -246,6 +246,9 @@ pseudo-device ipfilter # IP filtering device pseudo-device rnd # /dev/random and in-kernel generator pseudo-device clockctl # user control of clock subsystem +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if #endif #if defined(HADES_KERNEL) || defined(MILAN_KERNEL) Index: sys/arch/atari/conf/majors.atari =================================================================== RCS file: /cvsroot/src/sys/arch/atari/conf/majors.atari,v retrieving revision 1.5 diff -u -r1.5 majors.atari --- sys/arch/atari/conf/majors.atari 2003/04/25 21:10:49 1.5 +++ sys/arch/atari/conf/majors.atari 2003/06/30 04:58:44 @@ -55,3 +55,4 @@ device-major cgd char 54 block 16 cgd device-major wskbd char 55 wskbd device-major ksyms char 56 ksyms +device-major pf char 57 pf Index: sys/arch/bebox/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/bebox/conf/GENERIC,v retrieving revision 1.63 diff -u -r1.63 GENERIC --- sys/arch/bebox/conf/GENERIC 2003/04/26 14:10:13 1.63 +++ sys/arch/bebox/conf/GENERIC 2003/06/30 04:58:44 @@ -117,6 +117,9 @@ pseudo-device rnd # /dev/random and in-kernel generator #options RND_COM # use "com" randomness as well (BROKEN) pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if # # device Index: sys/arch/bebox/conf/majors.bebox =================================================================== RCS file: /cvsroot/src/sys/arch/bebox/conf/majors.bebox,v retrieving revision 1.4 diff -u -r1.4 majors.bebox --- sys/arch/bebox/conf/majors.bebox 2003/04/25 21:10:50 1.4 +++ sys/arch/bebox/conf/majors.bebox 2003/06/30 04:58:44 @@ -60,3 +60,4 @@ device-major systrace char 59 systrace device-major cgd char 60 block 19 cgd device-major ksyms char 61 ksyms +device-major pf char 62 pf Index: sys/arch/cats/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/cats/conf/GENERIC,v retrieving revision 1.48 diff -u -r1.48 GENERIC --- sys/arch/cats/conf/GENERIC 2003/05/04 01:27:53 1.48 +++ sys/arch/cats/conf/GENERIC 2003/06/30 04:58:45 @@ -532,3 +532,6 @@ pseudo-device wsmux pseudo-device wsfont pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/cesfic/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/cesfic/conf/GENERIC,v retrieving revision 1.19 diff -u -r1.19 GENERIC --- sys/arch/cesfic/conf/GENERIC 2003/04/26 14:10:13 1.19 +++ sys/arch/cesfic/conf/GENERIC 2003/06/30 04:58:45 @@ -121,3 +121,6 @@ pseudo-device rnd pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/cesfic/conf/majors.cesfic =================================================================== RCS file: /cvsroot/src/sys/arch/cesfic/conf/majors.cesfic,v retrieving revision 1.4 diff -u -r1.4 majors.cesfic --- sys/arch/cesfic/conf/majors.cesfic 2003/04/25 21:10:50 1.4 +++ sys/arch/cesfic/conf/majors.cesfic 2003/06/30 04:58:45 @@ -25,3 +25,4 @@ device-major systrace char 23 systrace device-major cgd char 24 block 4 cgd device-major ksyms char 25 ksyms +device-major pf char 26 pf Index: sys/arch/cobalt/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/cobalt/conf/GENERIC,v retrieving revision 1.43 diff -u -r1.43 GENERIC --- sys/arch/cobalt/conf/GENERIC 2003/04/26 14:10:13 1.43 +++ sys/arch/cobalt/conf/GENERIC 2003/06/30 04:58:45 @@ -285,3 +285,6 @@ # A pseudo device needed for Coda # also needs CODA (above) #pseudo-device vcoda 4 # coda minicache <-> venus comm. pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/cobalt/conf/majors.cobalt =================================================================== RCS file: /cvsroot/src/sys/arch/cobalt/conf/majors.cobalt,v retrieving revision 1.5 diff -u -r1.5 majors.cobalt --- sys/arch/cobalt/conf/majors.cobalt 2003/04/25 21:10:50 1.5 +++ sys/arch/cobalt/conf/majors.cobalt 2003/06/30 04:58:45 @@ -41,3 +41,4 @@ device-major cgd char 35 block 11 cgd device-major panel char 36 panel device-major ksyms char 37 ksyms +device-major pf char 38 pf Index: sys/arch/dreamcast/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/dreamcast/conf/GENERIC,v retrieving revision 1.36 diff -u -r1.36 GENERIC --- sys/arch/dreamcast/conf/GENERIC 2003/06/14 16:28:32 1.36 +++ sys/arch/dreamcast/conf/GENERIC 2003/06/30 04:58:45 @@ -193,3 +193,6 @@ pseudo-device clockctl # user control of clock subsystem pseudo-device wsmux # mouse & keyboard multiplexor pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/dreamcast/conf/majors.dreamcast =================================================================== RCS file: /cvsroot/src/sys/arch/dreamcast/conf/majors.dreamcast,v retrieving revision 1.5 diff -u -r1.5 majors.dreamcast --- sys/arch/dreamcast/conf/majors.dreamcast 2003/04/25 21:10:51 1.5 +++ sys/arch/dreamcast/conf/majors.dreamcast 2003/06/30 04:58:45 @@ -56,3 +56,4 @@ device-major mmem char 62 block 21 mmem device-major mlcd char 63 mlcd device-major ksyms char 64 ksyms +device-major pf char 65 pf Index: sys/arch/evbmips/conf/majors.evbmips =================================================================== RCS file: /cvsroot/src/sys/arch/evbmips/conf/majors.evbmips,v retrieving revision 1.5 diff -u -r1.5 majors.evbmips --- sys/arch/evbmips/conf/majors.evbmips 2003/04/25 21:10:52 1.5 +++ sys/arch/evbmips/conf/majors.evbmips 2003/06/30 04:58:45 @@ -75,3 +75,4 @@ device-major sysmon char 74 sysmon_envsys | sysmon_wdog device-major cgd char 75 block 18 cgd device-major ksyms char 76 ksyms +device-major pf char 77 pf Index: sys/arch/evbppc/conf/majors.evbppc =================================================================== RCS file: /cvsroot/src/sys/arch/evbppc/conf/majors.evbppc,v retrieving revision 1.8 diff -u -r1.8 majors.evbppc --- sys/arch/evbppc/conf/majors.evbppc 2003/04/25 21:10:52 1.8 +++ sys/arch/evbppc/conf/majors.evbppc 2003/06/30 04:58:45 @@ -77,3 +77,4 @@ device-major radio char 72 radio device-major wsfont char 73 wsfont device-major ksyms char 74 ksyms +device-major pf char 75 pf Index: sys/arch/evbsh3/conf/majors.evbsh3 =================================================================== RCS file: /cvsroot/src/sys/arch/evbsh3/conf/majors.evbsh3,v retrieving revision 1.4 diff -u -r1.4 majors.evbsh3 --- sys/arch/evbsh3/conf/majors.evbsh3 2003/04/25 21:10:52 1.4 +++ sys/arch/evbsh3/conf/majors.evbsh3 2003/06/30 04:58:45 @@ -47,3 +47,4 @@ device-major systrace char 53 systrace device-major cgd char 54 block 19 cgd device-major ksyms char 55 ksyms +device-major pf char 56 pf Index: sys/arch/evbsh5/conf/majors.evbsh5 =================================================================== RCS file: /cvsroot/src/sys/arch/evbsh5/conf/majors.evbsh5,v retrieving revision 1.1 diff -u -r1.1 majors.evbsh5 --- sys/arch/evbsh5/conf/majors.evbsh5 2002/11/23 09:13:19 1.1 +++ sys/arch/evbsh5/conf/majors.evbsh5 2003/06/30 04:58:45 @@ -4,3 +4,4 @@ # device-major alphaled char 9 alphaled +device-major pf char 10 pf Index: sys/arch/hp300/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/hp300/conf/GENERIC,v retrieving revision 1.93 diff -u -r1.93 GENERIC --- sys/arch/hp300/conf/GENERIC 2003/05/24 06:21:21 1.93 +++ sys/arch/hp300/conf/GENERIC 2003/06/30 04:58:46 @@ -266,3 +266,6 @@ pseudo-device rnd # /dev/random and in-kernel generator pseudo-device vcoda 4 # coda minicache <-> venus comm. pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/hp300/conf/majors.hp300 =================================================================== RCS file: /cvsroot/src/sys/arch/hp300/conf/majors.hp300,v retrieving revision 1.4 diff -u -r1.4 majors.hp300 --- sys/arch/hp300/conf/majors.hp300 2003/04/25 21:10:53 1.4 +++ sys/arch/hp300/conf/majors.hp300 2003/06/30 04:58:46 @@ -40,3 +40,4 @@ device-major systrace char 43 systrace device-major cgd char 44 block 16 cgd device-major ksyms char 45 ksyms +device-major pf char 46 pf Index: sys/arch/hp700/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/hp700/conf/GENERIC,v retrieving revision 1.15 diff -u -r1.15 GENERIC --- sys/arch/hp700/conf/GENERIC 2003/04/26 14:10:12 1.15 +++ sys/arch/hp700/conf/GENERIC 2003/06/30 04:58:46 @@ -585,3 +585,6 @@ pseudo-device wsmux # mouse & keyboard multiplexor pseudo-device wsfont pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/hp700/conf/majors.hp700 =================================================================== RCS file: /cvsroot/src/sys/arch/hp700/conf/majors.hp700,v retrieving revision 1.4 diff -u -r1.4 majors.hp700 --- sys/arch/hp700/conf/majors.hp700 2003/04/25 21:10:53 1.4 +++ sys/arch/hp700/conf/majors.hp700 2003/06/30 04:58:46 @@ -40,3 +40,4 @@ device-major systrace char 34 systrace device-major cgd char 35 block 8 cgd device-major ksyms char 36 ksyms +device-major pf char 37 pf Index: sys/arch/hpcmips/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/hpcmips/conf/GENERIC,v retrieving revision 1.138 diff -u -r1.138 GENERIC --- sys/arch/hpcmips/conf/GENERIC 2003/04/26 14:10:11 1.138 +++ sys/arch/hpcmips/conf/GENERIC 2003/06/30 04:58:46 @@ -624,3 +624,6 @@ pseudo-device bridge #options BRIDGE_IPF # bridge uses IP/IPv6 pfil hooks too pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/hpcmips/conf/majors.hpcmips =================================================================== RCS file: /cvsroot/src/sys/arch/hpcmips/conf/majors.hpcmips,v retrieving revision 1.8 diff -u -r1.8 majors.hpcmips --- sys/arch/hpcmips/conf/majors.hpcmips 2003/05/01 07:01:58 1.8 +++ sys/arch/hpcmips/conf/majors.hpcmips 2003/06/30 04:58:47 @@ -61,3 +61,4 @@ device-major ksyms char 55 ksyms device-major flash char 100 flash device-major vr4181aiu char 101 vr4181aiu +device-major pf char 102 pf Index: sys/arch/hpcsh/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/hpcsh/conf/GENERIC,v retrieving revision 1.28 diff -u -r1.28 GENERIC --- sys/arch/hpcsh/conf/GENERIC 2003/04/26 14:10:11 1.28 +++ sys/arch/hpcsh/conf/GENERIC 2003/06/30 04:58:47 @@ -167,3 +167,6 @@ # mouse & keyboard multiplexor pseudo-devices pseudo-device wsmux pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/hpcsh/conf/majors.hpcsh =================================================================== RCS file: /cvsroot/src/sys/arch/hpcsh/conf/majors.hpcsh,v retrieving revision 1.4 diff -u -r1.4 majors.hpcsh --- sys/arch/hpcsh/conf/majors.hpcsh 2003/04/25 21:10:54 1.4 +++ sys/arch/hpcsh/conf/majors.hpcsh 2003/06/30 04:58:47 @@ -41,3 +41,4 @@ device-major systrace char 35 systrace device-major cgd char 36 block 10 cgd device-major ksyms char 37 ksyms +device-major pf char 38 pf Index: sys/arch/i386/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/i386/conf/GENERIC,v retrieving revision 1.564 diff -u -r1.564 GENERIC --- sys/arch/i386/conf/GENERIC 2003/06/29 22:28:23 1.564 +++ sys/arch/i386/conf/GENERIC 2003/06/30 04:58:47 @@ -1111,6 +1111,9 @@ pseudo-device vlan # IEEE 802.1q encapsulation pseudo-device bridge # simple inter-network bridging #options BRIDGE_IPF # bridge uses IP/IPv6 pfil hooks too +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if # miscellaneous pseudo-devices pseudo-device pty # pseudo-terminals Index: sys/arch/i386/conf/majors.i386 =================================================================== RCS file: /cvsroot/src/sys/arch/i386/conf/majors.i386,v retrieving revision 1.14 diff -u -r1.14 majors.i386 --- sys/arch/i386/conf/majors.i386 2003/06/02 04:03:04 1.14 +++ sys/arch/i386/conf/majors.i386 2003/06/30 04:58:48 @@ -107,3 +107,4 @@ device-major rd char 105 block 22 rd device-major ct char 106 block 23 ct device-major mt char 107 block 24 mt +device-major pf char 108 pf Index: sys/arch/luna68k/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/luna68k/conf/GENERIC,v retrieving revision 1.41 diff -u -r1.41 GENERIC --- sys/arch/luna68k/conf/GENERIC 2003/04/26 14:10:10 1.41 +++ sys/arch/luna68k/conf/GENERIC 2003/06/30 04:58:48 @@ -172,3 +172,6 @@ pseudo-device rnd # /dev/random and in-kernel generator pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/luna68k/conf/majors.luna68k =================================================================== RCS file: /cvsroot/src/sys/arch/luna68k/conf/majors.luna68k,v retrieving revision 1.4 diff -u -r1.4 majors.luna68k --- sys/arch/luna68k/conf/majors.luna68k 2003/04/25 21:10:55 1.4 +++ sys/arch/luna68k/conf/majors.luna68k 2003/06/30 04:58:48 @@ -37,3 +37,4 @@ device-major systrace char 36 systrace device-major cgd char 37 block 15 cgd device-major ksyms char 38 ksyms +device-major pf char 39 pf Index: sys/arch/mac68k/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/mac68k/conf/GENERIC,v retrieving revision 1.138 diff -u -r1.138 GENERIC --- sys/arch/mac68k/conf/GENERIC 2003/04/26 14:10:10 1.138 +++ sys/arch/mac68k/conf/GENERIC 2003/06/30 04:58:48 @@ -316,3 +316,6 @@ pseudo-device rnd # /dev/random and in-kernel generator #options RND_COM # use "com" randomness as well (BROKEN) pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/mac68k/conf/majors.mac68k =================================================================== RCS file: /cvsroot/src/sys/arch/mac68k/conf/majors.mac68k,v retrieving revision 1.4 diff -u -r1.4 majors.mac68k --- sys/arch/mac68k/conf/majors.mac68k 2003/04/25 21:10:55 1.4 +++ sys/arch/mac68k/conf/majors.mac68k 2003/06/30 04:58:48 @@ -49,3 +49,4 @@ device-major systrace char 50 systrace device-major cgd char 51 block 23 cgd device-major ksyms char 52 ksyms +device-major pf char 53 pf Index: sys/arch/macppc/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/macppc/conf/GENERIC,v retrieving revision 1.158 diff -u -r1.158 GENERIC --- sys/arch/macppc/conf/GENERIC 2003/06/11 16:24:13 1.158 +++ sys/arch/macppc/conf/GENERIC 2003/06/30 04:58:49 @@ -371,9 +371,9 @@ fw* at fwbus? # IP over 1394 # IEEE1394 nodes -#fwnode* at fwbus? idhi ? idlo ? -#sbpscsi* at fwnode? -#scsibus* at sbpscsi? +fwnode* at fwbus? idhi ? idlo ? +sbpscsi* at fwnode? +scsibus* at sbpscsi? # Audio Devices @@ -443,3 +443,6 @@ pseudo-device wsmux # mouse and keyboard multiplexor pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/macppc/conf/majors.macppc =================================================================== RCS file: /cvsroot/src/sys/arch/macppc/conf/majors.macppc,v retrieving revision 1.10 diff -u -r1.10 majors.macppc --- sys/arch/macppc/conf/majors.macppc 2003/04/25 21:10:56 1.10 +++ sys/arch/macppc/conf/majors.macppc 2003/06/30 04:58:49 @@ -74,3 +74,4 @@ device-major radio char 72 radio device-major wsfont char 73 wsfont device-major ksyms char 74 ksyms +device-major pf char 75 pf Index: sys/arch/mipsco/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/mipsco/conf/GENERIC,v retrieving revision 1.30 diff -u -r1.30 GENERIC --- sys/arch/mipsco/conf/GENERIC 2003/05/03 18:10:52 1.30 +++ sys/arch/mipsco/conf/GENERIC 2003/06/30 04:58:49 @@ -170,3 +170,6 @@ pseudo-device rnd # /dev/random and in-kernel generator #options RND_COM # use "com" randomness as well (BROKEN) pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/mipsco/conf/majors.mipsco =================================================================== RCS file: /cvsroot/src/sys/arch/mipsco/conf/majors.mipsco,v retrieving revision 1.4 diff -u -r1.4 majors.mipsco --- sys/arch/mipsco/conf/majors.mipsco 2003/04/25 21:10:56 1.4 +++ sys/arch/mipsco/conf/majors.mipsco 2003/06/30 04:58:49 @@ -41,3 +41,4 @@ device-major clockctl char 75 clockctl device-major systrace char 76 systrace device-major ksyms char 77 ksyms +device-major pf char 78 pf Index: sys/arch/mmeye/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/mmeye/conf/GENERIC,v retrieving revision 1.50 diff -u -r1.50 GENERIC --- sys/arch/mmeye/conf/GENERIC 2003/04/26 14:10:09 1.50 +++ sys/arch/mmeye/conf/GENERIC 2003/06/30 04:58:49 @@ -200,6 +200,9 @@ #pseudo-device md 1 # memory disk device (ramdisk) pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if # Enable the hooks used for initializing the root memory-disk. #options MEMORY_DISK_HOOKS Index: sys/arch/mmeye/conf/majors.mmeye =================================================================== RCS file: /cvsroot/src/sys/arch/mmeye/conf/majors.mmeye,v retrieving revision 1.4 diff -u -r1.4 majors.mmeye --- sys/arch/mmeye/conf/majors.mmeye 2003/04/25 21:10:56 1.4 +++ sys/arch/mmeye/conf/majors.mmeye 2003/06/30 04:58:49 @@ -44,3 +44,4 @@ device-major systrace char 53 systrace device-major cgd char 54 block 19 cgd device-major ksyms char 55 ksyms +device-major pf char 56 pf Index: sys/arch/mvme68k/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/mvme68k/conf/GENERIC,v retrieving revision 1.42 diff -u -r1.42 GENERIC --- sys/arch/mvme68k/conf/GENERIC 2003/04/26 14:10:09 1.42 +++ sys/arch/mvme68k/conf/GENERIC 2003/06/30 04:58:49 @@ -112,6 +112,9 @@ pseudo-device vnd 2 pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if # random number generator pseudo-device pseudo-device rnd # /dev/random and in-kernel generator Index: sys/arch/mvme68k/conf/majors.mvme68k =================================================================== RCS file: /cvsroot/src/sys/arch/mvme68k/conf/majors.mvme68k,v retrieving revision 1.4 diff -u -r1.4 majors.mvme68k --- sys/arch/mvme68k/conf/majors.mvme68k 2003/04/25 21:10:56 1.4 +++ sys/arch/mvme68k/conf/majors.mvme68k 2003/06/30 04:58:49 @@ -38,3 +38,4 @@ device-major systrace char 40 systrace device-major cgd char 41 block 17 cgd device-major ksyms char 42 ksyms +device-major pf char 43 pf Index: sys/arch/mvmeppc/conf/majors.mvmeppc =================================================================== RCS file: /cvsroot/src/sys/arch/mvmeppc/conf/majors.mvmeppc,v retrieving revision 1.4 diff -u -r1.4 majors.mvmeppc --- sys/arch/mvmeppc/conf/majors.mvmeppc 2003/04/25 21:10:57 1.4 +++ sys/arch/mvmeppc/conf/majors.mvmeppc 2003/06/30 04:58:49 @@ -48,3 +48,4 @@ device-major systrace char 59 systrace device-major cgd char 60 block 19 cgd device-major ksyms char 61 ksyms +device-major pf char 62 pf Index: sys/arch/netwinder/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/netwinder/conf/GENERIC,v retrieving revision 1.36 diff -u -r1.36 GENERIC --- sys/arch/netwinder/conf/GENERIC 2003/06/14 19:15:38 1.36 +++ sys/arch/netwinder/conf/GENERIC 2003/06/30 04:58:49 @@ -273,6 +273,9 @@ pseudo-device rnd # /dev/random and in-kernel generator pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if # wscons pseudo-devices pseudo-device wsmux # mouse & keyboard multiplexor Index: sys/arch/news68k/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/news68k/conf/GENERIC,v retrieving revision 1.46 diff -u -r1.46 GENERIC --- sys/arch/news68k/conf/GENERIC 2003/04/26 14:10:09 1.46 +++ sys/arch/news68k/conf/GENERIC 2003/06/30 04:58:49 @@ -230,3 +230,6 @@ #pseudo-device vcoda 4 # coda minicache <-> venus comm. pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/news68k/conf/majors.news68k =================================================================== RCS file: /cvsroot/src/sys/arch/news68k/conf/majors.news68k,v retrieving revision 1.5 diff -u -r1.5 majors.news68k --- sys/arch/news68k/conf/majors.news68k 2003/04/25 21:10:57 1.5 +++ sys/arch/news68k/conf/majors.news68k 2003/06/30 04:58:49 @@ -45,3 +45,4 @@ device-major systrace char 77 systrace device-major cgd char 78 block 33 cgd device-major ksyms char 79 ksyms +device-major pf char 80 pf Index: sys/arch/newsmips/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/newsmips/conf/GENERIC,v retrieving revision 1.60 diff -u -r1.60 GENERIC --- sys/arch/newsmips/conf/GENERIC 2003/05/21 13:10:08 1.60 +++ sys/arch/newsmips/conf/GENERIC 2003/06/30 04:58:49 @@ -205,3 +205,6 @@ pseudo-device rnd # /dev/random and in-kernel generator pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/newsmips/conf/majors.newsmips =================================================================== RCS file: /cvsroot/src/sys/arch/newsmips/conf/majors.newsmips,v retrieving revision 1.5 diff -u -r1.5 majors.newsmips --- sys/arch/newsmips/conf/majors.newsmips 2003/04/25 21:10:59 1.5 +++ sys/arch/newsmips/conf/majors.newsmips 2003/06/30 04:58:50 @@ -44,3 +44,4 @@ device-major systrace char 76 systrace device-major cgd char 77 block 33 cgd device-major ksyms char 78 ksyms +device-major pf char 79 pf Index: sys/arch/next68k/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/next68k/conf/GENERIC,v retrieving revision 1.71 diff -u -r1.71 GENERIC --- sys/arch/next68k/conf/GENERIC 2003/04/26 14:10:08 1.71 +++ sys/arch/next68k/conf/GENERIC 2003/06/30 04:58:50 @@ -239,3 +239,6 @@ pseudo-device ccd 4 # concatenated disks pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/next68k/conf/majors.next68k =================================================================== RCS file: /cvsroot/src/sys/arch/next68k/conf/majors.next68k,v retrieving revision 1.5 diff -u -r1.5 majors.next68k --- sys/arch/next68k/conf/majors.next68k 2003/04/25 21:10:59 1.5 +++ sys/arch/next68k/conf/majors.next68k 2003/06/30 04:58:50 @@ -44,3 +44,4 @@ device-major systrace char 47 systrace device-major cgd char 48 block 21 cgd device-major ksyms char 49 ksyms +device-major pf char 50 pf Index: sys/arch/ofppc/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/ofppc/conf/GENERIC,v retrieving revision 1.62 diff -u -r1.62 GENERIC --- sys/arch/ofppc/conf/GENERIC 2003/04/26 14:10:08 1.62 +++ sys/arch/ofppc/conf/GENERIC 2003/06/30 04:58:50 @@ -182,6 +182,9 @@ pseudo-device clockctl # user control of clock subsystem pseudo-device openfirm # /dev/openfirm pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if options FIREPOWER_ES # Firepower ES options FIREPOWER_MX # Firepower MX Index: sys/arch/ofppc/conf/majors.ofppc =================================================================== RCS file: /cvsroot/src/sys/arch/ofppc/conf/majors.ofppc,v retrieving revision 1.4 diff -u -r1.4 majors.ofppc --- sys/arch/ofppc/conf/majors.ofppc 2003/04/25 21:11:00 1.4 +++ sys/arch/ofppc/conf/majors.ofppc 2003/06/30 04:58:50 @@ -26,3 +26,4 @@ device-major systrace char 20 systrace device-major cgd char 21 block 8 cgd device-major ksyms char 22 ksyms +device-major pf char 23 pf Index: sys/arch/pc532/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/pc532/conf/GENERIC,v retrieving revision 1.33 diff -u -r1.33 GENERIC --- sys/arch/pc532/conf/GENERIC 2003/04/26 14:10:08 1.33 +++ sys/arch/pc532/conf/GENERIC 2003/06/30 04:58:50 @@ -108,3 +108,6 @@ pseudo-device rnd # kernel random number generator pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/pc532/conf/majors.pc532 =================================================================== RCS file: /cvsroot/src/sys/arch/pc532/conf/majors.pc532,v retrieving revision 1.4 diff -u -r1.4 majors.pc532 --- sys/arch/pc532/conf/majors.pc532 2003/04/25 21:11:00 1.4 +++ sys/arch/pc532/conf/majors.pc532 2003/06/30 04:58:50 @@ -35,3 +35,4 @@ device-major systrace char 33 systrace device-major cgd char 34 block 14 cgd device-major ksyms char 35 ksyms +device-major pf char 36 pf Index: sys/arch/playstation2/conf/majors.playstation2 =================================================================== RCS file: /cvsroot/src/sys/arch/playstation2/conf/majors.playstation2,v retrieving revision 1.4 diff -u -r1.4 majors.playstation2 --- sys/arch/playstation2/conf/majors.playstation2 2003/04/25 21:11:00 1.4 +++ sys/arch/playstation2/conf/majors.playstation2 2003/06/30 04:58:50 @@ -44,3 +44,4 @@ device-major systrace char 39 systrace device-major cgd char 40 block 10 cgd device-major ksyms char 41 ksyms +device-major pf char 42 pf Index: sys/arch/pmax/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/pmax/conf/GENERIC,v retrieving revision 1.111 diff -u -r1.111 GENERIC --- sys/arch/pmax/conf/GENERIC 2003/04/26 14:10:08 1.111 +++ sys/arch/pmax/conf/GENERIC 2003/06/30 04:58:50 @@ -248,3 +248,6 @@ pseudo-device rnd # /dev/random and in-kernel generator pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/pmax/conf/majors.pmax =================================================================== RCS file: /cvsroot/src/sys/arch/pmax/conf/majors.pmax,v retrieving revision 1.4 diff -u -r1.4 majors.pmax --- sys/arch/pmax/conf/majors.pmax 2003/04/25 21:11:00 1.4 +++ sys/arch/pmax/conf/majors.pmax 2003/06/30 04:58:50 @@ -52,3 +52,4 @@ device-major systrace char 103 systrace device-major cgd char 104 block 33 cgd device-major ksyms char 105 ksyms +device-major pf char 106 pf Index: sys/arch/pmppc/conf/majors.pmppc =================================================================== RCS file: /cvsroot/src/sys/arch/pmppc/conf/majors.pmppc,v retrieving revision 1.5 diff -u -r1.5 majors.pmppc --- sys/arch/pmppc/conf/majors.pmppc 2003/04/25 21:11:01 1.5 +++ sys/arch/pmppc/conf/majors.pmppc 2003/06/30 04:58:50 @@ -53,3 +53,4 @@ device-major pci char 60 pci device-major cgd char 61 block 19 cgd device-major ksyms char 62 ksyms +device-major pf char 63 pf Index: sys/arch/prep/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/prep/conf/GENERIC,v retrieving revision 1.66 diff -u -r1.66 GENERIC --- sys/arch/prep/conf/GENERIC 2003/04/26 14:10:07 1.66 +++ sys/arch/prep/conf/GENERIC 2003/06/30 04:58:51 @@ -499,6 +499,9 @@ #options RND_COM # use "com" randomness as well pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if # a pseudo device needed for Coda # also needs CODA (above) #pseudo-device vcoda 4 # coda minicache <-> venus comm. Index: sys/arch/prep/conf/majors.prep =================================================================== RCS file: /cvsroot/src/sys/arch/prep/conf/majors.prep,v retrieving revision 1.4 diff -u -r1.4 majors.prep --- sys/arch/prep/conf/majors.prep 2003/04/25 21:11:01 1.4 +++ sys/arch/prep/conf/majors.prep 2003/06/30 04:58:51 @@ -64,3 +64,4 @@ device-major systrace char 74 systrace device-major cgd char 75 block 19 cgd device-major ksyms char 76 ksyms +device-major pf char 77 pf Index: sys/arch/sandpoint/conf/GENERIC.sandpoint =================================================================== RCS file: /cvsroot/src/sys/arch/sandpoint/conf/GENERIC.sandpoint,v retrieving revision 1.6 diff -u -r1.6 GENERIC.sandpoint --- sys/arch/sandpoint/conf/GENERIC.sandpoint 2003/04/26 14:34:16 1.6 +++ sys/arch/sandpoint/conf/GENERIC.sandpoint 2003/06/30 04:58:51 @@ -97,6 +97,9 @@ pseudo-device pty pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if # # device # Index: sys/arch/sandpoint/conf/majors.sandpoint =================================================================== RCS file: /cvsroot/src/sys/arch/sandpoint/conf/majors.sandpoint,v retrieving revision 1.4 diff -u -r1.4 majors.sandpoint --- sys/arch/sandpoint/conf/majors.sandpoint 2003/04/25 21:11:01 1.4 +++ sys/arch/sandpoint/conf/majors.sandpoint 2003/06/30 04:58:51 @@ -56,3 +56,4 @@ device-major systrace char 59 systrace device-major cgd char 60 block 19 cgd device-major ksyms char 61 ksyms +device-major pf char 62 pf Index: sys/arch/sbmips/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/sbmips/conf/GENERIC,v retrieving revision 1.21 diff -u -r1.21 GENERIC --- sys/arch/sbmips/conf/GENERIC 2003/04/26 14:10:07 1.21 +++ sys/arch/sbmips/conf/GENERIC 2003/06/30 04:58:52 @@ -167,6 +167,9 @@ pseudo-device rnd # /dev/random & kernel generator pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if # A pseudo device needed for Coda # also needs CODA (above) #pseudo-device vcoda 4 # coda minicache <-> venus comm. Index: sys/arch/sgimips/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/sgimips/conf/GENERIC,v retrieving revision 1.20 diff -u -r1.20 GENERIC --- sys/arch/sgimips/conf/GENERIC 2003/05/04 01:36:53 1.20 +++ sys/arch/sgimips/conf/GENERIC 2003/06/30 04:58:52 @@ -271,6 +271,9 @@ pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms #pseudo-device wsfont +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if # a pseudo device needed for Coda # also needs CODA (above) pseudo-device vcoda 4 # coda minicache <-> venus comm. Index: sys/arch/sgimips/conf/majors.sgimips =================================================================== RCS file: /cvsroot/src/sys/arch/sgimips/conf/majors.sgimips,v retrieving revision 1.5 diff -u -r1.5 majors.sgimips --- sys/arch/sgimips/conf/majors.sgimips 2003/04/25 21:11:01 1.5 +++ sys/arch/sgimips/conf/majors.sgimips 2003/06/30 04:58:52 @@ -66,3 +66,4 @@ device-major sequencer char 75 sequencer device-major uscanner char 76 uscanner device-major ksyms char 77 ksyms +device-major pf char 78 pf Index: sys/arch/sh5/conf/majors.sh5 =================================================================== RCS file: /cvsroot/src/sys/arch/sh5/conf/majors.sh5,v retrieving revision 1.6 diff -u -r1.6 majors.sh5 --- sys/arch/sh5/conf/majors.sh5 2003/04/25 21:11:02 1.6 +++ sys/arch/sh5/conf/majors.sh5 2003/06/30 04:58:52 @@ -47,3 +47,4 @@ device-major pci char 54 pci device-major cgd char 55 block 19 cgd device-major ksyms char 56 ksyms +device-major pf char 57 pf Index: sys/arch/shark/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/shark/conf/GENERIC,v retrieving revision 1.20 diff -u -r1.20 GENERIC --- sys/arch/shark/conf/GENERIC 2003/06/25 01:07:55 1.20 +++ sys/arch/shark/conf/GENERIC 2003/06/30 04:58:52 @@ -233,3 +233,6 @@ #pseudo-device vcoda 4 # coda kernel <-> cachemanager pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/shark/conf/majors.shark =================================================================== RCS file: /cvsroot/src/sys/arch/shark/conf/majors.shark,v retrieving revision 1.4 diff -u -r1.4 majors.shark --- sys/arch/shark/conf/majors.shark 2003/04/25 21:11:02 1.4 +++ sys/arch/shark/conf/majors.shark 2003/06/30 04:58:52 @@ -58,3 +58,4 @@ device-major clockctl char 84 clockctl device-major systrace char 85 systrace device-major ksyms char 86 ksyms +device-major pf char 87 pf Index: sys/arch/sparc/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/sparc/conf/GENERIC,v retrieving revision 1.147 diff -u -r1.147 GENERIC --- sys/arch/sparc/conf/GENERIC 2003/04/26 14:10:06 1.147 +++ sys/arch/sparc/conf/GENERIC 2003/06/30 04:58:53 @@ -632,3 +632,6 @@ pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/sparc/conf/majors.sparc =================================================================== RCS file: /cvsroot/src/sys/arch/sparc/conf/majors.sparc,v retrieving revision 1.8 diff -u -r1.8 majors.sparc --- sys/arch/sparc/conf/majors.sparc 2003/04/25 21:11:02 1.8 +++ sys/arch/sparc/conf/majors.sparc 2003/06/30 04:58:53 @@ -104,3 +104,4 @@ device-major sysmon char 135 sysmon_envsys | sysmon_wdog | sysmon_power device-major ksyms char 136 ksyms +device-major pf char 137 pf Index: sys/arch/sparc64/conf/GENERIC32 =================================================================== RCS file: /cvsroot/src/sys/arch/sparc64/conf/GENERIC32,v retrieving revision 1.69 diff -u -r1.69 GENERIC32 --- sys/arch/sparc64/conf/GENERIC32 2003/06/20 03:15:06 1.69 +++ sys/arch/sparc64/conf/GENERIC32 2003/06/30 04:58:54 @@ -657,3 +657,6 @@ pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/sparc64/conf/majors.sparc64 =================================================================== RCS file: /cvsroot/src/sys/arch/sparc64/conf/majors.sparc64,v retrieving revision 1.9 diff -u -r1.9 majors.sparc64 --- sys/arch/sparc64/conf/majors.sparc64 2003/04/25 21:11:03 1.9 +++ sys/arch/sparc64/conf/majors.sparc64 2003/06/30 04:58:54 @@ -101,3 +101,4 @@ device-major sysmon char 135 sysmon_envsys | sysmon_wdog | sysmon_power device-major ksyms char 136 ksyms +device-major pf char 137 pf Index: sys/arch/sun2/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/sun2/conf/GENERIC,v retrieving revision 1.25 diff -u -r1.25 GENERIC --- sys/arch/sun2/conf/GENERIC 2003/04/26 14:10:06 1.25 +++ sys/arch/sun2/conf/GENERIC 2003/06/30 04:58:54 @@ -267,3 +267,6 @@ # options RF_INCLUDE_PARITY_DECLUSTERING_DS=1 pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/sun2/conf/majors.sun2 =================================================================== RCS file: /cvsroot/src/sys/arch/sun2/conf/majors.sun2,v retrieving revision 1.4 diff -u -r1.4 majors.sun2 --- sys/arch/sun2/conf/majors.sun2 2003/04/25 21:11:03 1.4 +++ sys/arch/sun2/conf/majors.sun2 2003/06/30 04:58:54 @@ -55,3 +55,4 @@ device-major systrace char 90 systrace device-major cgd char 91 block 26 cgd device-major ksyms char 92 ksyms +device-major pf char 93 pf Index: sys/arch/sun3/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/sun3/conf/GENERIC,v retrieving revision 1.100 diff -u -r1.100 GENERIC --- sys/arch/sun3/conf/GENERIC 2003/04/26 14:10:06 1.100 +++ sys/arch/sun3/conf/GENERIC 2003/06/30 04:58:54 @@ -265,3 +265,6 @@ # options RF_INCLUDE_PARITY_DECLUSTERING_DS=1 pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/sun3/conf/GENERIC3X =================================================================== RCS file: /cvsroot/src/sys/arch/sun3/conf/GENERIC3X,v retrieving revision 1.57 diff -u -r1.57 GENERIC3X --- sys/arch/sun3/conf/GENERIC3X 2003/04/27 13:22:38 1.57 +++ sys/arch/sun3/conf/GENERIC3X 2003/06/30 04:58:54 @@ -251,3 +251,6 @@ # options RF_INCLUDE_PARITY_DECLUSTERING_DS=1 pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/sun3/conf/majors.sun3 =================================================================== RCS file: /cvsroot/src/sys/arch/sun3/conf/majors.sun3,v retrieving revision 1.5 diff -u -r1.5 majors.sun3 --- sys/arch/sun3/conf/majors.sun3 2003/04/25 21:11:03 1.5 +++ sys/arch/sun3/conf/majors.sun3 2003/06/30 04:58:54 @@ -62,3 +62,4 @@ device-major systrace char 85 systrace device-major cgd char 86 block 26 cgd device-major ksyms char 87 ksyms +device-major pf char 88 pf Index: sys/arch/vax/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/vax/conf/GENERIC,v retrieving revision 1.113 diff -u -r1.113 GENERIC --- sys/arch/vax/conf/GENERIC 2003/04/26 14:10:06 1.113 +++ sys/arch/vax/conf/GENERIC 2003/06/30 04:58:54 @@ -292,3 +292,6 @@ #pseudo-device leds # control the leds on vaxstations. pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/vax/conf/majors.vax =================================================================== RCS file: /cvsroot/src/sys/arch/vax/conf/majors.vax,v retrieving revision 1.6 diff -u -r1.6 majors.vax --- sys/arch/vax/conf/majors.vax 2003/04/25 21:11:03 1.6 +++ sys/arch/vax/conf/majors.vax 2003/06/30 04:58:55 @@ -81,6 +81,7 @@ device-major cgd char 77 block 26 cgd device-major rf char 78 block 27 rf device-major ksyms char 79 ksyms +device-major pf char 80 pf # # block-device-only devices Index: sys/arch/x68k/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/x68k/conf/GENERIC,v retrieving revision 1.90 diff -u -r1.90 GENERIC --- sys/arch/x68k/conf/GENERIC 2003/04/26 14:10:05 1.90 +++ sys/arch/x68k/conf/GENERIC 2003/06/30 04:58:55 @@ -479,3 +479,6 @@ pseudo-device clockctl # user control of clock subsystem pseudo-device ksyms # /dev/ksyms +#pseudo-device pf # PF packet filter +#pseudo-device pflog # PF log if +#pseudo-device pfsync # PF sync if Index: sys/arch/x68k/conf/majors.x68k =================================================================== RCS file: /cvsroot/src/sys/arch/x68k/conf/majors.x68k,v retrieving revision 1.5 diff -u -r1.5 majors.x68k --- sys/arch/x68k/conf/majors.x68k 2003/04/25 21:11:04 1.5 +++ sys/arch/x68k/conf/majors.x68k 2003/06/30 04:58:55 @@ -53,3 +53,4 @@ device-major cgd char 52 block 17 cgd device-major bmd char 53 block 18 bmd device-major ksyms char 54 ksyms +device-major pf char 55 pf Index: distrib/sets/lists/comp/mi =================================================================== RCS file: /cvsroot/src/distrib/sets/lists/comp/mi,v retrieving revision 1.586 diff -u -r1.586 mi --- distrib/sets/lists/comp/mi 2003/06/27 05:21:51 1.586 +++ distrib/sets/lists/comp/mi 2003/06/30 04:59:02 @@ -379,6 +379,7 @@ ./usr/include/net/netisr.h comp-c-include ./usr/include/net/pfil.h comp-c-include ./usr/include/net/pfkeyv2.h comp-c-include +./usr/include/net/pfvar.h comp-c-include ./usr/include/net/ppp-comp.h comp-c-include ./usr/include/net/ppp_defs.h comp-c-include ./usr/include/net/radix.h comp-c-include