aboutsummaryrefslogtreecommitdiff
path: root/sbin
diff options
context:
space:
mode:
Diffstat (limited to 'sbin')
-rw-r--r--sbin/bectl/bectl.892
-rw-r--r--sbin/bectl/bectl.c9
-rw-r--r--sbin/bsdlabel/disktab2
-rw-r--r--sbin/camcontrol/camcontrol.82
-rw-r--r--sbin/camcontrol/camcontrol.c18
-rw-r--r--sbin/camcontrol/depop.c2
-rw-r--r--sbin/camcontrol/epc.c8
-rw-r--r--sbin/camcontrol/fwdownload.c59
-rw-r--r--sbin/ccdconfig/ccdconfig.84
-rw-r--r--sbin/ccdconfig/ccdconfig.c8
-rw-r--r--sbin/devd/Makefile7
-rw-r--r--sbin/devd/apple.conf6
-rw-r--r--sbin/devd/asus.conf12
-rw-r--r--sbin/devd/devd.conf19
-rw-r--r--sbin/devd/hyperv.conf2
-rw-r--r--sbin/devfs/devfs.c2
-rw-r--r--sbin/dhclient/bpf.c53
-rw-r--r--sbin/dhclient/packet.c3
-rw-r--r--sbin/dhclient/tests/fake.c2
-rw-r--r--sbin/dmesg/dmesg.819
-rw-r--r--sbin/dmesg/dmesg.c4
-rw-r--r--sbin/dump/traverse.c8
-rw-r--r--sbin/dumpon/dumpon.c36
-rw-r--r--sbin/fsck/fsck.82
-rw-r--r--sbin/fsck/fsck.c3
-rw-r--r--sbin/fsck/fsutil.c39
-rw-r--r--sbin/fsck/fsutil.h8
-rw-r--r--sbin/fsck/preen.c24
-rw-r--r--sbin/fsck_ffs/fsck.h111
-rw-r--r--sbin/fsck_ffs/fsutil.c4
-rw-r--r--sbin/fsck_ffs/globs.c6
-rw-r--r--sbin/fsck_ffs/inode.c2
-rw-r--r--sbin/fsck_ffs/main.c275
-rw-r--r--sbin/fsck_ffs/setup.c248
-rw-r--r--sbin/fsdb/fsdb.c2
-rw-r--r--sbin/fsdb/fsdbutil.c7
-rw-r--r--sbin/geom/core/geom.85
-rw-r--r--sbin/geom/core/geom.c23
-rw-r--r--sbin/geom/core/geom.h2
-rw-r--r--sbin/ggate/ggatec/ggatec.c112
-rw-r--r--sbin/ggate/ggated/ggated.c14
-rw-r--r--sbin/ggate/shared/ggate.h1
-rw-r--r--sbin/gvinum/gvinum.c15
-rw-r--r--sbin/hastd/hooks.c2
-rw-r--r--sbin/ifconfig/ifconfig.8758
-rw-r--r--sbin/ifconfig/ifpfsync.c2
-rw-r--r--sbin/ifconfig/ifvlan.c20
-rw-r--r--sbin/init/init.c108
-rw-r--r--sbin/ipf/Makefile2
-rw-r--r--sbin/ipf/Makefile.inc11
-rw-r--r--sbin/ipf/common/genmask.c68
-rw-r--r--sbin/ipf/common/ipf.h378
-rw-r--r--sbin/ipf/common/ipf_y.y2738
-rw-r--r--sbin/ipf/common/ipmon.h142
-rw-r--r--sbin/ipf/common/ipt.h40
-rw-r--r--sbin/ipf/common/kmem.h30
-rw-r--r--sbin/ipf/common/lexer.c737
-rw-r--r--sbin/ipf/common/lexer.h38
-rw-r--r--sbin/ipf/common/opts.h69
-rw-r--r--sbin/ipf/common/pcap-ipf.h35
-rw-r--r--sbin/ipf/ipf/Makefile1
-rw-r--r--sbin/ipf/ipf/bpf-ipf.h440
-rw-r--r--sbin/ipf/ipf/bpf_filter.c595
-rw-r--r--sbin/ipf/ipf/ipf.4254
-rw-r--r--sbin/ipf/ipf/ipf.51698
-rw-r--r--sbin/ipf/ipf/ipf.8184
-rw-r--r--sbin/ipf/ipf/ipf.c578
-rw-r--r--sbin/ipf/ipf/ipfcomp.c1355
-rw-r--r--sbin/ipf/ipf/ipfilter.4241
-rw-r--r--sbin/ipf/ipf/ipfilter.511
-rw-r--r--sbin/ipf/ipf/ipl.481
-rw-r--r--sbin/ipf/ipfs/ipfs.8127
-rw-r--r--sbin/ipf/ipfs/ipfs.c855
-rw-r--r--sbin/ipf/ipfstat/ipfstat.8199
-rw-r--r--sbin/ipf/ipfstat/ipfstat.c2316
-rw-r--r--sbin/ipf/ipfsync/ipfsyncd.c671
-rw-r--r--sbin/ipf/ipfsync/ipsyncm.c256
-rw-r--r--sbin/ipf/ipfsync/ipsyncs.c274
-rw-r--r--sbin/ipf/ipftest/Makefile5
-rw-r--r--sbin/ipf/ipftest/ip_fil.c812
-rw-r--r--sbin/ipf/ipftest/ipftest.1205
-rw-r--r--sbin/ipf/ipftest/ipftest.c715
-rw-r--r--sbin/ipf/ipftest/md5.c302
-rw-r--r--sbin/ipf/ipftest/md5.h64
-rw-r--r--sbin/ipf/iplang/BNF69
-rw-r--r--sbin/ipf/iplang/Makefile31
-rw-r--r--sbin/ipf/iplang/iplang.h54
-rw-r--r--sbin/ipf/iplang/iplang.tst11
-rw-r--r--sbin/ipf/iplang/iplang_l.l317
-rw-r--r--sbin/ipf/iplang/iplang_y.y1757
-rw-r--r--sbin/ipf/ipmon/Makefile1
-rw-r--r--sbin/ipf/ipmon/ipmon.5226
-rw-r--r--sbin/ipf/ipmon/ipmon.8196
-rw-r--r--sbin/ipf/ipmon/ipmon.c1855
-rw-r--r--sbin/ipf/ipmon/ipmon_y.y1038
-rw-r--r--sbin/ipf/ipnat/Makefile1
-rw-r--r--sbin/ipf/ipnat/ipnat.148
-rw-r--r--sbin/ipf/ipnat/ipnat.497
-rw-r--r--sbin/ipf/ipnat/ipnat.5728
-rw-r--r--sbin/ipf/ipnat/ipnat.876
-rw-r--r--sbin/ipf/ipnat/ipnat.c833
-rw-r--r--sbin/ipf/ipnat/ipnat_y.y1746
-rw-r--r--sbin/ipf/ippool/Makefile3
-rw-r--r--sbin/ipf/ippool/ippool.5320
-rw-r--r--sbin/ipf/ippool/ippool.8130
-rw-r--r--sbin/ipf/ippool/ippool.c1114
-rw-r--r--sbin/ipf/ippool/ippool_y.y824
-rw-r--r--sbin/ipf/ipresend/Makefile2
-rw-r--r--sbin/ipf/ipscan/Makefile18
-rw-r--r--sbin/ipf/ipscan/ipscan.552
-rw-r--r--sbin/ipf/ipscan/ipscan.844
-rw-r--r--sbin/ipf/ipscan/ipscan_y.y569
-rw-r--r--sbin/ipf/ipsend/44arp.c116
-rw-r--r--sbin/ipf/ipsend/Crashable21
-rw-r--r--sbin/ipf/ipsend/Makefile183
-rw-r--r--sbin/ipf/ipsend/arp.c134
-rw-r--r--sbin/ipf/ipsend/dlcommon.c1283
-rw-r--r--sbin/ipf/ipsend/dltest.h34
-rw-r--r--sbin/ipf/ipsend/ip.c351
-rw-r--r--sbin/ipf/ipsend/ipresend.1108
-rw-r--r--sbin/ipf/ipsend/ipresend.c132
-rw-r--r--sbin/ipf/ipsend/ipsend.1111
-rw-r--r--sbin/ipf/ipsend/ipsend.5402
-rw-r--r--sbin/ipf/ipsend/ipsend.c411
-rw-r--r--sbin/ipf/ipsend/ipsend.h62
-rw-r--r--sbin/ipf/ipsend/ipsopt.c192
-rw-r--r--sbin/ipf/ipsend/iptest.1103
-rw-r--r--sbin/ipf/ipsend/iptest.c196
-rw-r--r--sbin/ipf/ipsend/iptests.c1373
-rw-r--r--sbin/ipf/ipsend/resend.c138
-rw-r--r--sbin/ipf/ipsend/sbpf.c148
-rw-r--r--sbin/ipf/ipsend/sdlpi.c166
-rw-r--r--sbin/ipf/ipsend/snit.c160
-rw-r--r--sbin/ipf/ipsend/sock.c314
-rw-r--r--sbin/ipf/ipsend/sockraw.c90
-rw-r--r--sbin/ipf/libipf/addicmp.c21
-rw-r--r--sbin/ipf/libipf/addipopt.c62
-rw-r--r--sbin/ipf/libipf/alist_free.c19
-rw-r--r--sbin/ipf/libipf/alist_new.c93
-rw-r--r--sbin/ipf/libipf/allocmbt.c23
-rw-r--r--sbin/ipf/libipf/assigndefined.c27
-rw-r--r--sbin/ipf/libipf/bcopywrap.c19
-rw-r--r--sbin/ipf/libipf/binprint.c30
-rw-r--r--sbin/ipf/libipf/buildopts.c49
-rw-r--r--sbin/ipf/libipf/checkrev.c46
-rw-r--r--sbin/ipf/libipf/connecttcp.c48
-rw-r--r--sbin/ipf/libipf/count4bits.c40
-rw-r--r--sbin/ipf/libipf/count6bits.c29
-rw-r--r--sbin/ipf/libipf/debug.c43
-rw-r--r--sbin/ipf/libipf/dupmbt.c24
-rw-r--r--sbin/ipf/libipf/facpri.c149
-rw-r--r--sbin/ipf/libipf/facpri.h39
-rw-r--r--sbin/ipf/libipf/familyname.c13
-rw-r--r--sbin/ipf/libipf/fill6bits.c47
-rw-r--r--sbin/ipf/libipf/findword.c24
-rw-r--r--sbin/ipf/libipf/flags.c25
-rw-r--r--sbin/ipf/libipf/freembt.c16
-rw-r--r--sbin/ipf/libipf/ftov.c15
-rw-r--r--sbin/ipf/libipf/gethost.c74
-rw-r--r--sbin/ipf/libipf/geticmptype.c28
-rw-r--r--sbin/ipf/libipf/getifname.c54
-rw-r--r--sbin/ipf/libipf/getnattype.c69
-rw-r--r--sbin/ipf/libipf/getport.c88
-rw-r--r--sbin/ipf/libipf/getportproto.c39
-rw-r--r--sbin/ipf/libipf/getproto.c35
-rw-r--r--sbin/ipf/libipf/getsumd.c23
-rw-r--r--sbin/ipf/libipf/hostname.c59
-rw-r--r--sbin/ipf/libipf/icmpcode.c24
-rw-r--r--sbin/ipf/libipf/icmptypename.c28
-rw-r--r--sbin/ipf/libipf/icmptypes.c107
-rw-r--r--sbin/ipf/libipf/inet_addr.c201
-rw-r--r--sbin/ipf/libipf/initparse.c20
-rw-r--r--sbin/ipf/libipf/interror.c578
-rw-r--r--sbin/ipf/libipf/ionames.c41
-rw-r--r--sbin/ipf/libipf/ipf_dotuning.c71
-rw-r--r--sbin/ipf/libipf/ipf_perror.c40
-rw-r--r--sbin/ipf/libipf/ipft_hx.c182
-rw-r--r--sbin/ipf/libipf/ipft_pc.c249
-rw-r--r--sbin/ipf/libipf/ipft_tx.c500
-rw-r--r--sbin/ipf/libipf/ipoptsec.c61
-rw-r--r--sbin/ipf/libipf/kmem.c114
-rw-r--r--sbin/ipf/libipf/kmem.h30
-rw-r--r--sbin/ipf/libipf/kmemcpywrap.c22
-rw-r--r--sbin/ipf/libipf/kvatoname.c38
-rw-r--r--sbin/ipf/libipf/load_dstlist.c66
-rw-r--r--sbin/ipf/libipf/load_dstlistnode.c67
-rw-r--r--sbin/ipf/libipf/load_file.c96
-rw-r--r--sbin/ipf/libipf/load_hash.c100
-rw-r--r--sbin/ipf/libipf/load_hashnode.c63
-rw-r--r--sbin/ipf/libipf/load_http.c208
-rw-r--r--sbin/ipf/libipf/load_pool.c70
-rw-r--r--sbin/ipf/libipf/load_poolnode.c66
-rw-r--r--sbin/ipf/libipf/load_url.c31
-rw-r--r--sbin/ipf/libipf/mb_hexdump.c30
-rw-r--r--sbin/ipf/libipf/msgdsize.c19
-rw-r--r--sbin/ipf/libipf/mutex_emul.c123
-rw-r--r--sbin/ipf/libipf/nametokva.c37
-rw-r--r--sbin/ipf/libipf/nat_setgroupmap.c34
-rw-r--r--sbin/ipf/libipf/ntomask.c46
-rw-r--r--sbin/ipf/libipf/optname.c63
-rw-r--r--sbin/ipf/libipf/optprint.c82
-rw-r--r--sbin/ipf/libipf/optprintv6.c46
-rw-r--r--sbin/ipf/libipf/optvalue.c34
-rw-r--r--sbin/ipf/libipf/parsefields.c51
-rw-r--r--sbin/ipf/libipf/parseipfexpr.c281
-rw-r--r--sbin/ipf/libipf/parsewhoisline.c129
-rw-r--r--sbin/ipf/libipf/poolio.c53
-rw-r--r--sbin/ipf/libipf/portname.c43
-rw-r--r--sbin/ipf/libipf/prependmbt.c16
-rw-r--r--sbin/ipf/libipf/print_toif.c46
-rw-r--r--sbin/ipf/libipf/printactiveaddr.c34
-rw-r--r--sbin/ipf/libipf/printactivenat.c163
-rw-r--r--sbin/ipf/libipf/printaddr.c73
-rw-r--r--sbin/ipf/libipf/printaps.c111
-rw-r--r--sbin/ipf/libipf/printbuf.c32
-rw-r--r--sbin/ipf/libipf/printdstl_live.c80
-rw-r--r--sbin/ipf/libipf/printdstlist.c55
-rw-r--r--sbin/ipf/libipf/printdstlistdata.c45
-rw-r--r--sbin/ipf/libipf/printdstlistnode.c75
-rw-r--r--sbin/ipf/libipf/printdstlistpolicy.c30
-rw-r--r--sbin/ipf/libipf/printfieldhdr.c53
-rw-r--r--sbin/ipf/libipf/printfr.c471
-rw-r--r--sbin/ipf/libipf/printfraginfo.c40
-rw-r--r--sbin/ipf/libipf/printhash.c54
-rw-r--r--sbin/ipf/libipf/printhash_live.c65
-rw-r--r--sbin/ipf/libipf/printhashdata.c92
-rw-r--r--sbin/ipf/libipf/printhashnode.c94
-rw-r--r--sbin/ipf/libipf/printhost.c33
-rw-r--r--sbin/ipf/libipf/printhostmap.c29
-rw-r--r--sbin/ipf/libipf/printhostmask.c37
-rw-r--r--sbin/ipf/libipf/printifname.c20
-rw-r--r--sbin/ipf/libipf/printip.c41
-rw-r--r--sbin/ipf/libipf/printipfexpr.c194
-rw-r--r--sbin/ipf/libipf/printiphdr.c19
-rw-r--r--sbin/ipf/libipf/printlog.c38
-rw-r--r--sbin/ipf/libipf/printlookup.c40
-rw-r--r--sbin/ipf/libipf/printmask.c28
-rw-r--r--sbin/ipf/libipf/printnat.c351
-rw-r--r--sbin/ipf/libipf/printnataddr.c44
-rw-r--r--sbin/ipf/libipf/printnatfield.c218
-rw-r--r--sbin/ipf/libipf/printnatside.c53
-rw-r--r--sbin/ipf/libipf/printpacket.c108
-rw-r--r--sbin/ipf/libipf/printpacket6.c58
-rw-r--r--sbin/ipf/libipf/printpool.c61
-rw-r--r--sbin/ipf/libipf/printpool_live.c67
-rw-r--r--sbin/ipf/libipf/printpooldata.c48
-rw-r--r--sbin/ipf/libipf/printpoolfield.c165
-rw-r--r--sbin/ipf/libipf/printpoolnode.c68
-rw-r--r--sbin/ipf/libipf/printportcmp.c28
-rw-r--r--sbin/ipf/libipf/printproto.c39
-rw-r--r--sbin/ipf/libipf/printsbuf.c40
-rw-r--r--sbin/ipf/libipf/printstate.c217
-rw-r--r--sbin/ipf/libipf/printstatefields.c356
-rw-r--r--sbin/ipf/libipf/printtcpflags.c29
-rw-r--r--sbin/ipf/libipf/printtqtable.c25
-rw-r--r--sbin/ipf/libipf/printtunable.c29
-rw-r--r--sbin/ipf/libipf/printunit.c46
-rw-r--r--sbin/ipf/libipf/remove_hash.c48
-rw-r--r--sbin/ipf/libipf/remove_hashnode.c52
-rw-r--r--sbin/ipf/libipf/remove_pool.c45
-rw-r--r--sbin/ipf/libipf/remove_poolnode.c51
-rw-r--r--sbin/ipf/libipf/resetlexer.c25
-rw-r--r--sbin/ipf/libipf/rwlock_emul.c150
-rw-r--r--sbin/ipf/libipf/save_execute.c76
-rw-r--r--sbin/ipf/libipf/save_file.c123
-rw-r--r--sbin/ipf/libipf/save_nothing.c59
-rw-r--r--sbin/ipf/libipf/save_syslog.c133
-rw-r--r--sbin/ipf/libipf/save_v1trap.c443
-rw-r--r--sbin/ipf/libipf/save_v2trap.c444
-rw-r--r--sbin/ipf/libipf/tcp_flags.c47
-rw-r--r--sbin/ipf/libipf/tcpflags.c44
-rw-r--r--sbin/ipf/libipf/tcpoptnames.c22
-rw-r--r--sbin/ipf/libipf/v6ionames.c28
-rw-r--r--sbin/ipf/libipf/v6optvalue.c37
-rw-r--r--sbin/ipf/libipf/var.c173
-rw-r--r--sbin/ipf/libipf/verbose.c39
-rw-r--r--sbin/ipf/libipf/vtof.c15
-rw-r--r--sbin/ipfw/ipfw.86
-rw-r--r--sbin/ipfw/main.c121
-rw-r--r--sbin/kldstat/kldstat.c107
-rw-r--r--sbin/ldconfig/ldconfig.c16
-rw-r--r--sbin/md5/md5.16
-rw-r--r--sbin/mount/mount.88
-rw-r--r--sbin/mount/mount.c29
-rw-r--r--sbin/mount/vfslist.c1
-rw-r--r--sbin/mount_fusefs/mount_fusefs.c6
-rw-r--r--sbin/mount_nfs/mount_nfs.814
-rw-r--r--sbin/mount_nfs/mount_nfs.c23
-rw-r--r--sbin/mount_nullfs/mount_nullfs.810
-rw-r--r--sbin/newfs/mkfs.c2
-rw-r--r--sbin/newfs/runtest00.sh2
-rw-r--r--sbin/newfs/runtest01.sh4
-rw-r--r--sbin/newfs_msdos/Makefile5
-rw-r--r--sbin/newfs_msdos/mkfs_msdos.c13
-rw-r--r--sbin/newfs_msdos/tests/Makefile6
-rw-r--r--sbin/nvmecontrol/logpage.c2
-rw-r--r--sbin/pfctl/parse.y495
-rw-r--r--sbin/pfctl/pf_ruleset.c197
-rw-r--r--sbin/pfctl/pfctl.812
-rw-r--r--sbin/pfctl/pfctl.c574
-rw-r--r--sbin/pfctl/pfctl.h9
-rw-r--r--sbin/pfctl/pfctl_optimize.c3
-rw-r--r--sbin/pfctl/pfctl_parser.c108
-rw-r--r--sbin/pfctl/pfctl_parser.h17
-rw-r--r--sbin/pfctl/pfctl_radix.c84
-rw-r--r--sbin/pfctl/tests/files/pf1007.in1
-rw-r--r--sbin/pfctl/tests/files/pf1007.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1008.in1
-rw-r--r--sbin/pfctl/tests/files/pf1008.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1009.in1
-rw-r--r--sbin/pfctl/tests/files/pf1009.ok1
-rw-r--r--sbin/pfctl/tests/pfctl_test_list.inc3
-rw-r--r--sbin/ping/main.h4
-rw-r--r--sbin/ping/ping.831
-rw-r--r--sbin/ping/ping.c23
-rw-r--r--sbin/ping/ping6.c21
-rw-r--r--sbin/reboot/nextboot.83
-rw-r--r--sbin/reboot/reboot.c10
-rw-r--r--sbin/route/route.819
-rw-r--r--sbin/route/route.c33
-rw-r--r--sbin/routed/parms.c2
-rw-r--r--sbin/savecore/savecore.826
-rw-r--r--sbin/savecore/savecore.c323
-rw-r--r--sbin/sconfig/Makefile2
-rw-r--r--sbin/setkey/setkey.860
-rw-r--r--sbin/setkey/setkey.c15
-rw-r--r--sbin/shutdown/shutdown.c19
-rw-r--r--sbin/swapon/swapon.c10
-rw-r--r--sbin/sysctl/sysctl.87
-rw-r--r--sbin/umount/umount.c2
-rw-r--r--sbin/veriexec/veriexec.816
331 files changed, 49985 insertions, 1341 deletions
diff --git a/sbin/bectl/bectl.8 b/sbin/bectl/bectl.8
index 4b7868e093eb..79bdec751a86 100644
--- a/sbin/bectl/bectl.8
+++ b/sbin/bectl/bectl.8
@@ -17,7 +17,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd January 6, 2021
+.Dd March 31, 2022
.Dt BECTL 8
.Os
.Sh NAME
@@ -131,6 +131,9 @@ Create a new boot environment named
If the
.Fl r
flag is given, a recursive boot environment will be made.
+See
+.Sx Boot Environment Structures
+for a discussion on different layouts.
.Pp
If the
.Fl e
@@ -155,6 +158,9 @@ If the
.Fl r
flag is given, a recursive snapshot of the boot environment will be created.
A snapshot is created for each descendant dataset of the boot environment.
+See
+.Sx Boot Environment Structures
+for a discussion on different layouts.
.Pp
No new boot environment is created with this command.
.It Xo
@@ -345,6 +351,90 @@ prints usage information if
or
.Fl \&?
is specified.
+.Ss Boot Environment Structures
+The traditional
+.Fx
+boot environment layout, as created by the Auto ZFS option to
+.Xr bsdinstall 8 ,
+is a
+.Dq shallow
+boot environment structure, where boot environment datasets do not have any
+directly subordinate datasets.
+Instead, they're organized off in
+.Pa zroot/ROOT ,
+and they rely on datasets elsewhere in the pool having
+.Dv canmount
+set to
+.Dv off .
+For instance, a simplified pool may be laid out as such:
+.Bd -literal -offset indent
+% zfs list -o name,canmount,mountpoint
+NAME CANMOUNT MOUNTPOINT
+zroot
+zroot/ROOT noauto none
+zroot/ROOT/default noauto none
+zroot/usr off /usr
+zroot/usr/home on /usr/home
+zroot/var on /var
+.Ed
+.Pp
+In that example,
+.Pa zroot/usr
+has
+.Dv canmount
+set to
+.Dv off ,
+thus files in
+.Pa /usr
+typically fall into the boot environment because this dataset is not mounted.
+.Pa zroot/usr/home
+is mounted, thus files in
+.Pa /usr/home
+are not in the boot environment.
+.Pp
+The other style of boot environments in use, frequently called
+.Dq deep boot environments ,
+organizes some or all of the boot environment as subordinate to the boot
+environment dataset.
+For example:
+.Bd -literal -offset indent
+% zfs list -o name,canmount,mountpoint
+NAME CANMOUNT MOUNTPOINT
+zroot
+zroot/ROOT noauto none
+zroot/ROOT/default noauto none
+zroot/ROOT/default/usr noauto /usr
+zroot/ROOT/default/usr/local noauto /usr/local
+zroot/var on /var
+.Ed
+.Pp
+Note that the subordinate datasets now have
+.Dv canmount
+set to
+.Dv noauto .
+These are more obviously a part of the boot environment, as indicated by their
+positioning in the layout.
+These subordinate datasets will be mounted by the
+.Dv zfsbe
+.Xr rc 8
+script at boot time.
+In this example,
+.Pa /var
+is excluded from the boot environment.
+.Pp
+.Nm
+commands that have their own
+.Fl r
+operate on this second,
+.Dq deep
+style of boot environment, when the
+.Fl r
+flag is set.
+A future version of
+.Nm
+may default to handling both styles and deprecate the various
+.Fl r
+flags.
\" .Sh EXAMPLES
\" .Bl -bullet
\" .It
diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c
index d3de58ea2982..2b7af4e55419 100644
--- a/sbin/bectl/bectl.c
+++ b/sbin/bectl/bectl.c
@@ -134,7 +134,6 @@ get_cmd_info(const char *cmd)
return (NULL);
}
-
static int
bectl_cmd_activate(int argc, char *argv[])
{
@@ -233,10 +232,7 @@ bectl_cmd_create(int argc, char *argv[])
bootenv = *argv;
err = BE_ERR_SUCCESS;
- if (strchr(bootenv, ' ') != NULL)
- /* BE datasets with spaces are not bootable */
- err = BE_ERR_INVALIDNAME;
- else if ((atpos = strchr(bootenv, '@')) != NULL) {
+ if ((atpos = strchr(bootenv, '@')) != NULL) {
/*
* This is the "create a snapshot variant". No new boot
* environment is to be created here.
@@ -478,7 +474,6 @@ bectl_cmd_rename(int argc, char *argv[])
dest = argv[2];
err = be_rename(be, src, dest);
-
switch (err) {
case BE_ERR_SUCCESS:
break;
@@ -487,7 +482,7 @@ bectl_cmd_rename(int argc, char *argv[])
src, dest);
}
- return (0);
+ return (err);
}
static int
diff --git a/sbin/bsdlabel/disktab b/sbin/bsdlabel/disktab
index b9bf610d597f..cb0fba033699 100644
--- a/sbin/bsdlabel/disktab
+++ b/sbin/bsdlabel/disktab
@@ -9,7 +9,7 @@
#
# To make a filesystem on a floppy:
# fdformat [-f <size>] fd<drive>[.<size>]
-# disklabel -B -r -w fd<drive>[.<size>] fd<size>
+# disklabel -B -w fd<drive>[.<size>] fd<size>
# newfs <opts> fd<drive>[.<size>]
#
# with <opts>:
diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8
index 6f1d00269633..3d65487cc60e 100644
--- a/sbin/camcontrol/camcontrol.8
+++ b/sbin/camcontrol/camcontrol.8
@@ -1803,7 +1803,7 @@ One of those two options is required.
Persistent reservations are complex, and fully explaining them is outside
the scope of this manual.
Please visit
-http://www.t10.org
+https://www.t10.org
and download the latest SPC spec for a full explanation of persistent
reservations.
.Bl -tag -width 8n
diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c
index facabbe2ecdc..489c3026537a 100644
--- a/sbin/camcontrol/camcontrol.c
+++ b/sbin/camcontrol/camcontrol.c
@@ -9203,31 +9203,31 @@ atapm_proc_resp(struct cam_device *device, union ccb *ccb)
printf("%s%d: ", device->device_name, device->dev_unit_num);
switch (count) {
- case 0x00:
+ case ATA_PM_STANDBY:
printf("Standby mode\n");
break;
- case 0x01:
+ case ATA_PM_STANDBY_Y:
printf("Standby_y mode\n");
break;
- case 0x40:
+ case 0x40: /* obsolete since ACS-3 */
printf("NV Cache Power Mode and the spindle is spun down or spinning down\n");
break;
- case 0x41:
+ case 0x41: /* obsolete since ACS-3 */
printf("NV Cache Power Mode and the spindle is spun up or spinning up\n");
break;
- case 0x80:
+ case ATA_PM_IDLE:
printf("Idle mode\n");
break;
- case 0x81:
+ case ATA_PM_IDLE_A:
printf("Idle_a mode\n");
break;
- case 0x82:
+ case ATA_PM_IDLE_B:
printf("Idle_b mode\n");
break;
- case 0x83:
+ case ATA_PM_IDLE_C:
printf("Idle_c mode\n");
break;
- case 0xff:
+ case ATA_PM_ACTIVE_IDLE:
printf("Active or Idle mode\n");
break;
default:
diff --git a/sbin/camcontrol/depop.c b/sbin/camcontrol/depop.c
index 3dbd2ba5358d..cf205ab6b471 100644
--- a/sbin/camcontrol/depop.c
+++ b/sbin/camcontrol/depop.c
@@ -32,7 +32,7 @@
*
* The standard defines 'storage elements' as the generic way of referring to a
* disk drive head. Each storage element has an identifier and an active status.
- * The health of an element can be querried. Active elements may be removed from
+ * The health of an element can be queried. Active elements may be removed from
* service with a REMOVE ELEMENT AND TRUNCATE (RET) command. Inactive element
* may be returned to service with a RESTORE ELEMENTS AND REBUILD (RER)
* command. GET PHYSICAL ELEMENT STATUS (GPES) will return a list of elements,
diff --git a/sbin/camcontrol/epc.c b/sbin/camcontrol/epc.c
index f96ca6e68ba9..aac94ccaa4e5 100644
--- a/sbin/camcontrol/epc.c
+++ b/sbin/camcontrol/epc.c
@@ -475,18 +475,18 @@ check_power_mode:
|| (ident->enabled2 & ATA_ENABLED_EPC)) {
if (mode_name != NULL)
printf("%s", mode_name);
- else if (count == 0xff) {
+ else if (count == ATA_PM_ACTIVE_IDLE) {
printf("PM0:Active or PM1:Idle");
}
} else {
switch (count) {
- case 0x00:
+ case ATA_PM_STANDBY:
printf("PM2:Standby");
break;
- case 0x80:
+ case ATA_PM_IDLE:
printf("PM1:Idle");
break;
- case 0xff:
+ case ATA_PM_ACTIVE_IDLE:
printf("PM0:Active or PM1:Idle");
break;
}
diff --git a/sbin/camcontrol/fwdownload.c b/sbin/camcontrol/fwdownload.c
index dc97b3a221db..02b60f25df04 100644
--- a/sbin/camcontrol/fwdownload.c
+++ b/sbin/camcontrol/fwdownload.c
@@ -57,12 +57,15 @@ __FBSDID("$FreeBSD$");
#include <err.h>
#include <fcntl.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <cam/cam.h>
#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_pass.h>
#include <cam/scsi/scsi_message.h>
#include <camlib.h>
@@ -759,6 +762,59 @@ bailout:
return (retval);
}
+/*
+ * After the firmware is downloaded, we know the sense data has changed (or is
+ * likely to change since it contains the firmware version). Rescan the target
+ * with a flag to tell the kernel it's OK. This allows us to continnue using the
+ * old periph/disk in the kernel, which is less disruptive. We rescan the target
+ * because multilun devices usually update all the luns after the first firmware
+ * download.
+ */
+static int
+fw_rescan_target(struct cam_device *dev, bool printerrors, bool sim_mode)
+{
+ union ccb ccb;
+ int fd;
+
+ printf("Rescanning target %d:%d:* to pick up new fw revision / parameters.\n",
+ dev->path_id, dev->target_id);
+ if (sim_mode)
+ return (0);
+
+ /* Can only send XPT_SCAN_TGT via /dev/xpt, not pass device in *dev */
+ if ((fd = open(XPT_DEVICE, O_RDWR)) < 0) {
+ warnx("error opening transport layer device %s\n",
+ XPT_DEVICE);
+ warn("%s", XPT_DEVICE);
+ return (1);
+ }
+
+ /* Rescan the target */
+ bzero(&ccb, sizeof(union ccb));
+ ccb.ccb_h.func_code = XPT_SCAN_TGT;
+ ccb.ccb_h.path_id = dev->path_id;
+ ccb.ccb_h.target_id = dev->target_id;
+ ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
+ ccb.crcn.flags = CAM_EXPECT_INQ_CHANGE;
+ ccb.ccb_h.pinfo.priority = 5; /* run this at a low priority */
+
+ if (ioctl(fd, CAMIOCOMMAND, &ccb) < 0) {
+ warn("CAMIOCOMMAND XPT_SCAN_TGT ioctl failed");
+ close(fd);
+ return (1);
+ }
+ if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ warn("Can't send rescan lun");
+ if (printerrors)
+ cam_error_print(dev, &ccb, CAM_ESF_ALL, CAM_EPF_ALL,
+ stderr);
+ close(fd);
+ return (1);
+ }
+ close(fd);
+ return (0);
+}
+
/*
* Download firmware stored in buf to cam_dev. If simulation mode
* is enabled, only show what packet sizes would be sent to the
@@ -912,6 +968,9 @@ bailout:
if (quiet == 0)
progress_complete(&progress, size - img_size);
cam_freeccb(ccb);
+ if (retval == 0) {
+ fw_rescan_target(cam_dev, printerrors, sim_mode);
+ }
return (retval);
}
diff --git a/sbin/ccdconfig/ccdconfig.8 b/sbin/ccdconfig/ccdconfig.8
index 778a7f921c70..e73864bc1fef 100644
--- a/sbin/ccdconfig/ccdconfig.8
+++ b/sbin/ccdconfig/ccdconfig.8
@@ -28,7 +28,7 @@
.\" $NetBSD: ccdconfig.8,v 1.4 1996/02/28 01:01:17 thorpej Exp $
.\" $FreeBSD$
.\"
-.Dd October 3, 2016
+.Dd March 17, 2022
.Dt CCDCONFIG 8
.Os
.Sh NAME
@@ -204,7 +204,7 @@ you have A ccd disk with 10000 sectors you might create a 'd' partition
with offset 16 and size 9984.
.Bd -literal
# disklabel ccd0 > /tmp/disklabel.ccd0
-# disklabel -Rr ccd0 /tmp/disklabel.ccd0
+# disklabel -R ccd0 /tmp/disklabel.ccd0
# disklabel -e ccd0
.Ed
.Pp
diff --git a/sbin/ccdconfig/ccdconfig.c b/sbin/ccdconfig/ccdconfig.c
index 6d22b14deed8..1429426e8638 100644
--- a/sbin/ccdconfig/ccdconfig.c
+++ b/sbin/ccdconfig/ccdconfig.c
@@ -263,7 +263,9 @@ do_single(int argc, char **argv, int action)
cp += strlen(_PATH_DEV);
gctl_ro_param(grq, buf1, -1, cp);
}
- gctl_rw_param(grq, "output", sizeof(buf1), buf1);
+ buf1[0] = '\0';
+ gctl_add_param(grq, "output", sizeof(buf1), buf1,
+ GCTL_PARAM_WR | GCTL_PARAM_ASCII);
errstr = gctl_issue(grq);
if (errstr == NULL) {
if (verbose) {
@@ -371,10 +373,12 @@ dumpout(int unit)
grq = gctl_get_handle();
ncp = 65536;
cp = malloc(ncp);
+ cp[0] = '\0';
gctl_ro_param(grq, "verb", -1, "list");
gctl_ro_param(grq, "class", -1, "CCD");
gctl_ro_param(grq, "unit", sizeof(unit), &unit);
- gctl_rw_param(grq, "output", ncp, cp);
+ gctl_add_param(grq, "output", ncp, cp,
+ GCTL_PARAM_WR | GCTL_PARAM_ASCII);
errstr = gctl_issue(grq);
if (errstr != NULL)
errx(1, "%s\nor possibly kernel and ccdconfig out of sync",
diff --git a/sbin/devd/Makefile b/sbin/devd/Makefile
index 8120126807ce..0bda9e1ecf95 100644
--- a/sbin/devd/Makefile
+++ b/sbin/devd/Makefile
@@ -3,7 +3,7 @@
.include <src.opts.mk>
WARNS?= 3
-PACKAGE=utilities
+PACKAGE=runtime
CONFGROUPS= CONFS DEVD
CONFS= devd.conf
DEVD= devmatch.conf
@@ -13,7 +13,10 @@ DEVD+= asus.conf
.endif
.if ${MK_HYPERV} != "no"
-DEVD+= hyperv.conf
+CONFGROUPS+= HYPERV
+HYPERVDIR=${DEVDDIR}
+HYPERV+= hyperv.conf
+HYPERVPACKAGE= hyperv-tools
.endif
.if ${MK_USB} != "no"
diff --git a/sbin/devd/apple.conf b/sbin/devd/apple.conf
index 3084cd459fd4..0729a9c86e7a 100644
--- a/sbin/devd/apple.conf
+++ b/sbin/devd/apple.conf
@@ -44,7 +44,7 @@ notify 0 {
match "system" "PMU";
match "subsystem" "keys";
match "type" "mute";
- action "mixer 0";
+ action "mixer vol.mute=^";
};
notify 0 {
@@ -52,7 +52,7 @@ notify 0 {
match "subsystem" "keys";
match "type" "volume";
match "notify" "down";
- action "mixer vol -10";
+ action "mixer vol.volume=-10";
};
notify 0 {
@@ -60,7 +60,7 @@ notify 0 {
match "subsystem" "keys";
match "type" "volume";
match "notify" "up";
- action "mixer vol +10";
+ action "mixer vol.volume=+10";
};
# Eject key
diff --git a/sbin/devd/asus.conf b/sbin/devd/asus.conf
index a195a58bc51d..eed369f6ca4d 100644
--- a/sbin/devd/asus.conf
+++ b/sbin/devd/asus.conf
@@ -7,21 +7,21 @@ notify 0 {
match "system" "ACPI";
match "subsystem" "ASUS";
match "notify" "0x32";
- action "mixer 0";
+ action "mixer vol.volume=0";
};
notify 0 {
match "system" "ACPI";
match "subsystem" "ASUS";
match "notify" "0x31";
- action "mixer vol -10";
+ action "mixer vol.volume=-10";
};
notify 0 {
match "system" "ACPI";
match "subsystem" "ASUS";
match "notify" "0x30";
- action "mixer vol +10";
+ action "mixer vol.volume=+10";
};
# The next blocks enable volume hotkeys that can be found on the Asus EeePC
@@ -29,21 +29,21 @@ notify 0 {
match "system" "ACPI";
match "subsystem" "ASUS-Eee";
match "notify" "0x13";
- action "mixer 0";
+ action "mixer vol.volume=0";
};
notify 0 {
match "system" "ACPI";
match "subsystem" "ASUS-Eee";
match "notify" "0x14";
- action "mixer vol -10";
+ action "mixer vol.volume=-10";
};
notify 0 {
match "system" "ACPI";
match "subsystem" "ASUS-Eee";
match "notify" "0x15";
- action "mixer vol +10";
+ action "mixer vol.volume=+10";
};
# Enable user hotkeys that can be found on the Asus EeePC
diff --git a/sbin/devd/devd.conf b/sbin/devd/devd.conf
index d88f1fde743c..15b37ee8fd90 100644
--- a/sbin/devd/devd.conf
+++ b/sbin/devd/devd.conf
@@ -17,14 +17,10 @@ options {
pid-file "/var/run/devd.pid";
# Setup some shorthand for regex that we use later in the file.
- #XXX Yes, these are gross -- imp
- set scsi-controller-regex
- "(aac|aacraid|ahc|ahd|amr|ciss|\
- esp|ida|iir|ips|isp|mlx|mly|mpr|mps|mpt|sym|trm)\
- [0-9]+";
+ #XXX Yes, this is gross -- imp
set wifi-driver-regex
- "(ath|bwi|bwn|ipw|iwi|iwm|iwn|malo|mwl|otus|ral|rsu|rtwn|rum|\
- run|uath|upgt|ural|urtw|wi|wpi|wtap|zyd)[0-9]+";
+ "(ath|bwi|bwn|ipw|iwlwifi|iwi|iwm|iwn|malo|mwl|otus|ral|rsu|rtw|rtwn|rum|\
+ run|uath|upgt|ural|urtw|wpi|wtap|zyd)[0-9]+";
};
# Note that the attach/detach with the highest value wins, so that one can
@@ -155,15 +151,6 @@ notify 100 {
action "service moused stop $cdev";
};
-#
-# Rescan SCSI device-names on attach, but not detach. However, it is
-# disabled by default due to reports of problems.
-#
-attach 0 {
- device-name "$scsi-controller-regex";
-// action "camcontrol rescan all";
-};
-
# Don't even try to second guess what to do about drivers that don't
# match here. Instead, pass it off to syslog. Commented out for the
# moment, as the pnpinfo variable isn't set in devd yet. Individual
diff --git a/sbin/devd/hyperv.conf b/sbin/devd/hyperv.conf
index e90aea3e23b4..7c3e92ed099f 100644
--- a/sbin/devd/hyperv.conf
+++ b/sbin/devd/hyperv.conf
@@ -78,7 +78,7 @@ notify 11 {
# 1) ETHERNET/IFATTACH -> VF interface up (delayed by rc.conf hyperv_vf_delay
# seconds). This operation will trigger HYPERV_NIC_VF/VF_UP.
# 2) HYPERV_NIC_VF/VF_UP:
-# a) Create laggX coresponding to hnX.
+# a) Create laggX corresponding to hnX.
# b) Add hnX and VF to laggX.
# c) Whack all previous network configuration on hnX, including stopping
# dhclient.
diff --git a/sbin/devfs/devfs.c b/sbin/devfs/devfs.c
index 7be94c4737de..b5a6bf4015a2 100644
--- a/sbin/devfs/devfs.c
+++ b/sbin/devfs/devfs.c
@@ -228,7 +228,7 @@ usage(void)
{
fprintf(stderr, "usage: %s\n%s\n",
- "\tdevfs [-m mount-point] [-s ruleset] rule ...",
+ "\tdevfs [-m mount-point] rule [-s ruleset] ...",
"\tdevfs [-m mount-point] ruleset ...");
exit(1);
}
diff --git a/sbin/dhclient/bpf.c b/sbin/dhclient/bpf.c
index 41eb4f402e66..d938b68c7c94 100644
--- a/sbin/dhclient/bpf.c
+++ b/sbin/dhclient/bpf.c
@@ -5,6 +5,7 @@
/*-
* SPDX-License-Identifier: BSD-3-Clause
*
+ * Copyright (c) 2021 Franco Fichtner <franco@opnsense.org>
* Copyright (c) 1995, 1996, 1998, 1999
* The Internet Software Consortium. All rights reserved.
*
@@ -187,20 +188,58 @@ if_register_send(struct interface_info *info)
* Packet filter program...
*/
static const struct bpf_insn dhcp_bpf_filter[] = {
+ /* Use relative index (0) for IP packet... */
+ BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, 0),
+
+ /*
+ * Test whether this is a VLAN packet...
+ *
+ * In case the server packet is using a VLAN ID
+ * of 0, meaning an untagged priority was set, the
+ * response shall be read and replied to.
+ */
+ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 12),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_VLAN, 0, 4),
+
+ /* Test whether it has a VID of 0 */
+ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 14),
+ BPF_STMT(BPF_ALU + BPF_AND + BPF_K, EVL_VLID_MASK),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 17),
+
+ /* Correct the relative index for VLAN packet (4)... */
+ BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, 4),
+
/* Make sure this is an IP packet... */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12),
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8),
+ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 12),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 14),
/* Make sure it's a UDP packet... */
- BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23),
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
+ BPF_STMT(BPF_LD + BPF_B + BPF_IND, 23),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 12),
/* Make sure this isn't a fragment... */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20),
- BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, IP_MF|IP_OFFMASK, 4, 0),
+ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 20),
+ BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, IP_MF|IP_OFFMASK, 10, 0),
- /* Get the IP header length... */
+ /*
+ * Get the IP header length...
+ *
+ * To find the correct position of the IP header
+ * length field store the index (0 or 4) in the
+ * accumulator and compare it with 0.
+ */
+ BPF_STMT(BPF_MISC + BPF_TXA, 0),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 2),
+ /* Store IP header length of IP packet in index. */
BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
+ /* Skip over following VLAN handling instruction. */
+ BPF_JUMP(BPF_JMP + BPF_JA, 1, 0, 0),
+ /* Store IP header length of VLAN packet in index. */
+ BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 18),
+ /* Add IP header length to previous relative index. */
+ BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0),
+ /* Move result back to index to reach UDP header below. */
+ BPF_STMT(BPF_MISC + BPF_TAX, 0),
/* Make sure it's to the right port... */
BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16),
diff --git a/sbin/dhclient/packet.c b/sbin/dhclient/packet.c
index 21726aaaeb84..276bb5c2457f 100644
--- a/sbin/dhclient/packet.c
+++ b/sbin/dhclient/packet.c
@@ -157,7 +157,8 @@ decode_hw_header(unsigned char *buf, int bufix, struct hardware *from)
from->htype = ARPHRD_ETHER;
from->hlen = sizeof(eh.ether_shost);
- return (sizeof(eh));
+ return (sizeof(eh) + (ntohs(eh.ether_type) == ETHERTYPE_VLAN ?
+ ETHER_VLAN_ENCAP_LEN : 0));
}
ssize_t
diff --git a/sbin/dhclient/tests/fake.c b/sbin/dhclient/tests/fake.c
index d684f96e99d3..6a170953beb0 100644
--- a/sbin/dhclient/tests/fake.c
+++ b/sbin/dhclient/tests/fake.c
@@ -33,7 +33,7 @@ warning(const char *fmt, ...)
/*
* The original warning() would return "ret" here. We do this to
- * check warnings explicitely.
+ * check warnings explicitly.
*/
longjmp(env, 1);
}
diff --git a/sbin/dmesg/dmesg.8 b/sbin/dmesg/dmesg.8
index e40d8d5c9ff7..50b5e3e26404 100644
--- a/sbin/dmesg/dmesg.8
+++ b/sbin/dmesg/dmesg.8
@@ -28,7 +28,7 @@
.\" @(#)dmesg.8 8.1 (Berkeley) 6/5/93
.\" $FreeBSD$
.\"
-.Dd October 3, 2016
+.Dd May 7, 2022
.Dt DMESG 8
.Os
.Sh NAME
@@ -70,6 +70,23 @@ is also specified,
extract the name list from the specified system instead of the default,
which is the kernel image the system has booted from.
.El
+.Sh SYSCTL VARIABLES
+The following
+.Xr sysctl 8
+variables control how the kernel timestamps entries in the message buffer:
+The default value is shown next to each variable.
+.Bl -tag -width indent
+.It kern.msgbuf_show_timestamp : No 0
+If set to 0, no timetamps are added.
+If set to 1, then a 1-second granularity timestamp will be added to most lines
+in the message buffer.
+If set to 2, then a microsecond granularity timestamp will be added.
+This may also be set as a boot
+.Xr loader 8
+tunable.
+The timestamps are placed at the start of most lines that the kernel generates.
+Some multi-line messages will have only the first line tagged with a timestamp.
+.El
.Sh FILES
.Bl -tag -width ".Pa /var/run/dmesg.boot" -compact
.It Pa /var/run/dmesg.boot
diff --git a/sbin/dmesg/dmesg.c b/sbin/dmesg/dmesg.c
index 469582204f7b..f266e6b98b39 100644
--- a/sbin/dmesg/dmesg.c
+++ b/sbin/dmesg/dmesg.c
@@ -184,10 +184,6 @@ main(int argc, char *argv[])
/* Strip leading \0's */
while (*p == '\0')
p++;
- } else if (!all) {
- /* Skip the first line, since it is probably incomplete. */
- p = memchr(p, '\n', ep - p);
- p++;
}
for (; p < ep; p = nextp) {
nextp = memchr(p, '\n', ep - p);
diff --git a/sbin/dump/traverse.c b/sbin/dump/traverse.c
index 3630d2240f58..08e902667759 100644
--- a/sbin/dump/traverse.c
+++ b/sbin/dump/traverse.c
@@ -525,12 +525,8 @@ dumpino(union dinode *dp, ino_t ino)
spcl.c_count = 1;
added = appendextdata(dp);
writeheader(ino);
- if (sblock->fs_magic == FS_UFS1_MAGIC)
- memmove(buf, (caddr_t)dp->dp1.di_db,
- (u_long)DIP(dp, di_size));
- else
- memmove(buf, (caddr_t)dp->dp2.di_db,
- (u_long)DIP(dp, di_size));
+ memmove(buf, DIP(dp, di_shortlink),
+ (u_long)DIP(dp, di_size));
buf[DIP(dp, di_size)] = '\0';
writerec(buf, 0);
writeextdata(dp, ino, added);
diff --git a/sbin/dumpon/dumpon.c b/sbin/dumpon/dumpon.c
index a11f07a01200..626350427595 100644
--- a/sbin/dumpon/dumpon.c
+++ b/sbin/dumpon/dumpon.c
@@ -187,6 +187,25 @@ find_gateway(const char *ifname)
}
static void
+check_link_status(const char *ifname)
+{
+ struct ifaddrs *ifap, *ifa;
+
+ if (getifaddrs(&ifap) != 0)
+ err(EX_OSERR, "getifaddrs");
+
+ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+ if (strcmp(ifname, ifa->ifa_name) != 0)
+ continue;
+ if ((ifa->ifa_flags & IFF_UP) == 0) {
+ warnx("warning: %s's link is down", ifname);
+ }
+ break;
+ }
+ freeifaddrs(ifap);
+}
+
+static void
check_size(int fd, const char *fn)
{
int name[] = { CTL_HW, HW_PHYSMEM };
@@ -650,6 +669,18 @@ main(int argc, char *argv[])
error = ioctl(fd, DIOCSKERNELDUMP, kdap);
if (error != 0)
error = errno;
+ if (error == EINVAL && (gzip || zstd)) {
+ /* Retry without compression in case kernel lacks support. */
+ kdap->kda_compression = KERNELDUMP_COMP_NONE;
+ error = ioctl(fd, DIOCSKERNELDUMP, kdap);
+ if (error == 0)
+ warnx("Compression disabled; kernel may lack gzip or zstd support.");
+ else
+ error = errno;
+ }
+ /* Emit a warning if the user configured a downed interface. */
+ if (error == 0 && netdump)
+ check_link_status(kdap->kda_iface);
explicit_bzero(kdap->kda_encryptedkey, kdap->kda_encryptedkeysize);
free(kdap->kda_encryptedkey);
explicit_bzero(kdap, sizeof(*kdap));
@@ -660,10 +691,7 @@ main(int argc, char *argv[])
* errors, especially as users don't have any great
* discoverability into which NICs support netdump.
*/
- if (error == ENXIO)
- errx(EX_OSERR, "Unable to configure netdump "
- "because the interface's link is down.");
- else if (error == ENODEV)
+ if (error == ENODEV)
errx(EX_OSERR, "Unable to configure netdump "
"because the interface driver does not yet "
"support netdump.");
diff --git a/sbin/fsck/fsck.8 b/sbin/fsck/fsck.8
index 998f3d2fc4f4..5ec0c02bbbfd 100644
--- a/sbin/fsck/fsck.8
+++ b/sbin/fsck/fsck.8
@@ -118,7 +118,7 @@ or
flags are not specified,
.Nm
will attempt to determine the file system type and call the
-appropriated file system check utility.
+appropriate file system check utility.
Failure to detect the file system type will cause
.Nm
to fail with a message that the partition has an unknown file system type.
diff --git a/sbin/fsck/fsck.c b/sbin/fsck/fsck.c
index 84d2e2fe422f..bb053fe56253 100644
--- a/sbin/fsck/fsck.c
+++ b/sbin/fsck/fsck.c
@@ -254,6 +254,9 @@ isok(struct fstab *fs)
return (0);
if (!selected(fs->fs_vfstype))
return (0);
+ /* If failok, always check now */
+ if (getfsopt(fs, "failok"))
+ return (1);
/*
* If the -B flag has been given, then process the needed
* background checks. Background checks cannot be run on
diff --git a/sbin/fsck/fsutil.c b/sbin/fsck/fsutil.c
index 012bd434dce6..9644697e2530 100644
--- a/sbin/fsck/fsutil.c
+++ b/sbin/fsck/fsutil.c
@@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mount.h>
#include <err.h>
+#include <fstab.h>
#include <paths.h>
#include <stdarg.h>
#include <stdio.h>
@@ -55,6 +56,44 @@ static int preen = 0;
static void vmsg(int, const char *, va_list) __printflike(2, 0);
+/*
+ * The getfsopt() function checks whether an option is present in
+ * an fstab(5) fs_mntops entry. There are six possible cases:
+ *
+ * fs_mntops getfsopt result
+ * rw,foo foo true
+ * rw,nofoo nofoo true
+ * rw,nofoo foo false
+ * rw,foo nofoo false
+ * rw foo false
+ * rw nofoo false
+ *
+ * This function should be part of and documented in getfsent(3).
+ */
+int
+getfsopt(struct fstab *fs, const char *option)
+{
+ int negative, found;
+ char *opt, *optbuf;
+
+ if (option[0] == 'n' && option[1] == 'o') {
+ negative = 1;
+ option += 2;
+ } else
+ negative = 0;
+ optbuf = strdup(fs->fs_mntops);
+ found = 0;
+ for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) {
+ if (opt[0] == 'n' && opt[1] == 'o') {
+ if (!strcasecmp(opt + 2, option))
+ found = negative;
+ } else if (!strcasecmp(opt, option))
+ found = !negative;
+ }
+ free(optbuf);
+ return (found);
+}
+
void
setcdevname(const char *cd, int pr)
{
diff --git a/sbin/fsck/fsutil.h b/sbin/fsck/fsutil.h
index c6300a0cc0c4..e65f5ddecb01 100644
--- a/sbin/fsck/fsutil.h
+++ b/sbin/fsck/fsutil.h
@@ -28,6 +28,10 @@
* $FreeBSD$
*/
+struct fstab;
+int checkfstab(int, int (*)(struct fstab *),
+ int (*) (const char *, const char *, const char *, const char *, pid_t *));
+int getfsopt(struct fstab *, const char *);
void pfatal(const char *, ...) __printflike(1, 2);
void pwarn(const char *, ...) __printflike(1, 2);
void perr(const char *, ...) __printflike(1, 2);
@@ -46,7 +50,3 @@ char *estrdup(const char *);
#define CHECK_BACKGRD 0x0008
#define DO_BACKGRD 0x0010
#define CHECK_CLEAN 0x0020
-
-struct fstab;
-int checkfstab(int, int (*)(struct fstab *),
- int (*) (const char *, const char *, const char *, const char *, pid_t *));
diff --git a/sbin/fsck/preen.c b/sbin/fsck/preen.c
index e40ec5d4fb91..c8a36a608695 100644
--- a/sbin/fsck/preen.c
+++ b/sbin/fsck/preen.c
@@ -62,6 +62,7 @@ struct partentry {
char *p_devname; /* device name */
char *p_mntpt; /* mount point */
char *p_type; /* file system type */
+ int p_failok; /* failok option set */
};
static TAILQ_HEAD(part, partentry) badh;
@@ -78,7 +79,7 @@ static TAILQ_HEAD(disk, diskentry) diskh;
static int nrun = 0, ndisks = 0;
static struct diskentry *finddisk(const char *);
-static void addpart(const char *, const char *, const char *);
+static void addpart(const char *, const char *, const char *, const int);
static int startdisk(struct diskentry *,
int (*)(const char *, const char *, const char *, const char *, pid_t *));
static void printpart(void);
@@ -132,7 +133,6 @@ checkfstab(int flags, int (*docheck)(struct fstab *),
}
sumstatus = (*checkit)(fs->fs_vfstype,
name, fs->fs_file, NULL, NULL);
-
if (sumstatus)
return (sumstatus);
continue;
@@ -143,7 +143,8 @@ checkfstab(int flags, int (*docheck)(struct fstab *),
sumstatus |= 8;
continue;
}
- addpart(fs->fs_vfstype, name, fs->fs_file);
+ addpart(fs->fs_vfstype, name, fs->fs_file,
+ getfsopt(fs, "failok"));
}
if ((flags & CHECK_PREEN) == 0 || passno == 1 ||
@@ -172,12 +173,17 @@ checkfstab(int flags, int (*docheck)(struct fstab *),
continue;
}
- if (WIFEXITED(status))
+ p = TAILQ_FIRST(&d->d_part);
+
+ if (WIFEXITED(status) == 0) {
+ retcode = 0;
+ } else if (p->p_failok == 0) {
retcode = WEXITSTATUS(status);
- else
+ } else {
retcode = 0;
-
- p = TAILQ_FIRST(&d->d_part);
+ fprintf(stderr, "%s: failok SPECIFIED, FSCK "
+ "ERROR(S) IGNORED\n", p->p_devname);
+ }
if (flags & (CHECK_DEBUG|CHECK_VERBOSE))
(void) printf("done %s: %s (%s) = 0x%x\n",
@@ -243,7 +249,6 @@ checkfstab(int flags, int (*docheck)(struct fstab *),
return (0);
}
-
static struct diskentry *
finddisk(const char *name)
{
@@ -297,7 +302,7 @@ printpart(void)
static void
-addpart(const char *type, const char *dev, const char *mntpt)
+addpart(const char *type, const char *dev, const char *mntpt, const int failok)
{
struct diskentry *d = finddisk(dev);
struct partentry *p;
@@ -312,6 +317,7 @@ addpart(const char *type, const char *dev, const char *mntpt)
p->p_devname = estrdup(dev);
p->p_mntpt = estrdup(mntpt);
p->p_type = estrdup(type);
+ p->p_failok = failok;
TAILQ_INSERT_TAIL(&d->d_part, p, p_entries);
}
diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h
index 9ecc5793e644..1fb0df0c5124 100644
--- a/sbin/fsck_ffs/fsck.h
+++ b/sbin/fsck_ffs/fsck.h
@@ -226,7 +226,6 @@ extern struct timespec startprog;
extern struct bufarea *icachebp; /* inode cache buffer */
extern struct bufarea sblk; /* file system superblock */
extern struct bufarea *pdirbp; /* current directory contents */
-extern int sujrecovery; /* 1 => doing check using the journal */
#define dirty(bp) do { \
if (fswritefd < 0) \
@@ -320,60 +319,61 @@ extern unsigned long numdirs, listmax;
extern long countdirs; /* number of directories we actually found */
#define MIBSIZE 3 /* size of fsck sysctl MIBs */
-extern int adjrefcnt[MIBSIZE]; /* MIB command to adjust inode reference cnt */
-extern int adjblkcnt[MIBSIZE]; /* MIB command to adjust inode block count */
-extern int setsize[MIBSIZE]; /* MIB command to set inode size */
-extern int adjndir[MIBSIZE]; /* MIB command to adjust number of directories */
-extern int adjnbfree[MIBSIZE]; /* MIB command to adjust number of free blocks */
-extern int adjnifree[MIBSIZE]; /* MIB command to adjust number of free inodes */
-extern int adjnffree[MIBSIZE]; /* MIB command to adjust number of free frags */
-extern int adjnumclusters[MIBSIZE]; /* MIB command to adjust number of free clusters */
-extern int freefiles[MIBSIZE]; /* MIB command to free a set of files */
-extern int freedirs[MIBSIZE]; /* MIB command to free a set of directories */
-extern int freeblks[MIBSIZE]; /* MIB command to free a set of data blocks */
-extern struct fsck_cmd cmd; /* sysctl file system update commands */
-extern char snapname[BUFSIZ]; /* when doing snapshots, the name of the file */
-extern char *cdevname; /* name of device being checked */
-extern long dev_bsize; /* computed value of DEV_BSIZE */
-extern long secsize; /* actual disk sector size */
-extern u_int real_dev_bsize; /* actual disk sector size, not overridden */
-extern char nflag; /* assume a no response */
-extern char yflag; /* assume a yes response */
-extern int bkgrdflag; /* use a snapshot to run on an active system */
-extern off_t bflag; /* location of alternate super block */
-extern int debug; /* output debugging info */
-extern int Eflag; /* delete empty data blocks */
-extern int Zflag; /* zero empty data blocks */
-extern int zflag; /* zero unused directory space */
-extern int inoopt; /* trim out unused inodes */
-extern char ckclean; /* only do work if not cleanly unmounted */
-extern int cvtlevel; /* convert to newer file system format */
-extern int ckhashadd; /* check hashes to be added */
-extern int bkgrdcheck; /* determine if background check is possible */
-extern int bkgrdsumadj; /* whether the kernel have ability to adjust superblock summary */
-extern char usedsoftdep; /* just fix soft dependency inconsistencies */
-extern char preen; /* just fix normal inconsistencies */
-extern char rerun; /* rerun fsck. Only used in non-preen mode */
-extern int returntosingle; /* 1 => return to single user mode on exit */
-extern char resolved; /* cleared if unresolved changes => not clean */
-extern char havesb; /* superblock has been read */
-extern char skipclean; /* skip clean file systems if preening */
-extern int fsmodified; /* 1 => write done to file system */
-extern int fsreadfd; /* file descriptor for reading file system */
-extern int fswritefd; /* file descriptor for writing file system */
-extern int surrender; /* Give up if reads fail */
-extern int wantrestart; /* Restart fsck on early termination */
-
-extern ufs2_daddr_t maxfsblock; /* number of blocks in the file system */
-extern char *blockmap; /* ptr to primary blk allocation map */
-extern ino_t maxino; /* number of inodes in file system */
-
-extern ino_t lfdir; /* lost & found directory inode number */
-extern const char *lfname; /* lost & found directory name */
-extern int lfmode; /* lost & found directory creation mode */
-
-extern ufs2_daddr_t n_blks; /* number of blocks in use */
-extern ino_t n_files; /* number of files in use */
+extern int adjblkcnt[MIBSIZE]; /* MIB cmd to adjust inode block count */
+extern int adjrefcnt[MIBSIZE]; /* MIB cmd to adjust inode reference count */
+extern int adjndir[MIBSIZE]; /* MIB cmd to adjust number of directories */
+extern int adjnbfree[MIBSIZE]; /* MIB cmd to adjust number of free blocks */
+extern int adjnifree[MIBSIZE]; /* MIB cmd to adjust number of free inodes */
+extern int adjnffree[MIBSIZE]; /* MIB cmd to adjust number of free frags */
+extern int adjnumclusters[MIBSIZE]; /* MIB cmd adjust number of free clusters */
+extern int freefiles[MIBSIZE]; /* MIB cmd to free a set of files */
+extern int freedirs[MIBSIZE]; /* MIB cmd to free a set of directories */
+extern int freeblks[MIBSIZE]; /* MIB cmd to free a set of data blocks */
+extern int setsize[MIBSIZE]; /* MIB cmd to set inode size */
+extern struct fsck_cmd cmd; /* sysctl file system update commands */
+
+extern int bkgrdcheck; /* determine if background check is possible */
+extern int bkgrdsumadj; /* whether the kernel has the ability to adjust
+ the superblock summary fields */
+extern off_t bflag; /* location of alternate super block */
+extern int bkgrdflag; /* use a snapshot to run on an active system */
+extern char *blockmap; /* ptr to primary blk allocation map */
+extern char *cdevname; /* name of device being checked */
+extern char ckclean; /* only do work if not cleanly unmounted */
+extern int ckhashadd; /* check hashes to be added */
+extern int cvtlevel; /* convert to newer file system format */
+extern long dev_bsize; /* computed value of DEV_BSIZE */
+extern u_int real_dev_bsize; /* actual disk sector size, not overridden */
+extern int debug; /* output debugging info */
+extern int Eflag; /* delete empty data blocks */
+extern int fsmodified; /* 1 => write done to file system */
+extern int fsreadfd; /* file descriptor for reading file system */
+extern int fswritefd; /* file descriptor for writing file system */
+extern char havesb; /* superblock has been read */
+extern int inoopt; /* trim out unused inodes */
+extern ino_t lfdir; /* lost & found directory inode number */
+extern int lfmode; /* lost & found directory creation mode */
+extern const char *lfname; /* lost & found directory name */
+extern ufs2_daddr_t maxfsblock; /* number of blocks in the file system */
+extern ino_t maxino; /* number of inodes in file system */
+extern ufs2_daddr_t n_blks; /* number of blocks in use */
+extern ino_t n_files; /* number of files in use */
+extern char nflag; /* assume a no response */
+extern char preen; /* just fix normal inconsistencies */
+extern char rerun; /* rerun fsck. Only used in non-preen mode */
+extern char resolved; /* cleared if unresolved changes => not clean */
+extern int returntosingle; /* 1 => return to single user mode on exit */
+extern int sbhashfailed; /* when reading superblock check hash failed */
+extern long secsize; /* actual disk sector size */
+extern char skipclean; /* skip clean file systems if preening */
+extern char snapname[BUFSIZ]; /* when doing snapshots, the name of the file */
+extern int sujrecovery; /* 1 => doing check using the journal */
+extern int surrender; /* Give up if reads fail */
+extern char usedsoftdep; /* just fix soft dependency inconsistencies */
+extern int wantrestart; /* Restart fsck on early termination */
+extern char yflag; /* assume a yes response */
+extern int zflag; /* zero unused directory space */
+extern int Zflag; /* zero empty data blocks */
extern volatile sig_atomic_t got_siginfo; /* received a SIGINFO */
extern volatile sig_atomic_t got_sigalarm; /* received a SIGALRM */
@@ -489,6 +489,7 @@ struct inostat *inoinfo(ino_t inum);
void IOstats(char *what);
int linkup(ino_t orphan, ino_t parentdir, char *name);
int makeentry(ino_t parent, ino_t ino, const char *name);
+int openfilesys(char *dev);
void panic(const char *fmt, ...) __printflike(1, 2);
void pass1(void);
void pass1b(void);
diff --git a/sbin/fsck_ffs/fsutil.c b/sbin/fsck_ffs/fsutil.c
index db22ee5b20cf..711c9bb63549 100644
--- a/sbin/fsck_ffs/fsutil.c
+++ b/sbin/fsck_ffs/fsutil.c
@@ -250,6 +250,7 @@ cglookup(int cg)
if (cgp == NULL) {
if (sujrecovery)
errx(EEXIT,"Ran out of memory during journal recovery");
+ flush(fswritefd, &cgblk);
getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
return (&cgblk);
}
@@ -564,7 +565,7 @@ ckfini(int markclean)
cmd.size = markclean ? -1 : 1;
if (sysctlbyname("vfs.ffs.setflags", 0, 0,
&cmd, sizeof cmd) == -1)
- rwerror("SET FILE SYSTEM FLAGS", FS_UNCLEAN);
+ pwarn("CANNOT SET FILE SYSTEM DIRTY FLAG\n");
if (!preen) {
printf("\n***** FILE SYSTEM MARKED %s *****\n",
markclean ? "CLEAN" : "DIRTY");
@@ -575,6 +576,7 @@ ckfini(int markclean)
printf("\n***** FILE SYSTEM STILL DIRTY *****\n");
rerun = 1;
}
+ bkgrdflag = 0;
}
if (debug && cachelookups > 0)
printf("cache with %d buffers missed %d of %d (%d%%)\n",
diff --git a/sbin/fsck_ffs/globs.c b/sbin/fsck_ffs/globs.c
index be4434ce38ca..09dbcc6694a2 100644
--- a/sbin/fsck_ffs/globs.c
+++ b/sbin/fsck_ffs/globs.c
@@ -96,6 +96,7 @@ char preen; /* just fix normal inconsistencies */
char rerun; /* rerun fsck. Only used in non-preen mode */
int returntosingle; /* 1 => return to single user mode on exit */
char resolved; /* cleared if unresolved changes => not clean */
+int sbhashfailed; /* when reading superblock check hash failed */
char havesb; /* superblock has been read */
char skipclean; /* skip clean file systems if preening */
int fsmodified; /* 1 => write done to file system */
@@ -155,8 +156,9 @@ fsckinit(void)
resolved = 0;
havesb = 0;
fsmodified = 0;
- fsreadfd = 0;
- fswritefd = 0;
+ sbhashfailed = 0;
+ fsreadfd = -1;
+ fswritefd = -1;
maxfsblock = 0;
maxino = 0;
lfdir = 0;
diff --git a/sbin/fsck_ffs/inode.c b/sbin/fsck_ffs/inode.c
index dafc99bd92da..47f72c84a1f2 100644
--- a/sbin/fsck_ffs/inode.c
+++ b/sbin/fsck_ffs/inode.c
@@ -213,7 +213,7 @@ iblock(struct inodesc *idesc, off_t isize, int type)
idesc->id_lbn++;
n = (*func)(idesc);
} else {
- n = iblock(idesc, isize, type);
+ n = iblock(idesc, isize, type - 1);
idesc->id_level++;
}
if (n & STOP) {
diff --git a/sbin/fsck_ffs/main.c b/sbin/fsck_ffs/main.c
index 90eb745ddabc..18634a93c05c 100644
--- a/sbin/fsck_ffs/main.c
+++ b/sbin/fsck_ffs/main.c
@@ -75,6 +75,7 @@ static int restarts;
static void usage(void) __dead2;
static intmax_t argtoimax(int flag, const char *req, const char *str, int base);
static int checkfilesys(char *filesys);
+static int setup_bkgrdchk(struct statfs *mntp, int sbrdfailed, char **filesys);
static int chkdoreload(struct statfs *mntp);
static struct statfs *getmntpt(const char *);
@@ -181,6 +182,11 @@ main(int argc, char *argv[])
if (!argc)
usage();
+ if (bkgrdflag && cvtlevel > 0) {
+ pfatal("CANNOT CONVERT A SNAPSHOT\n");
+ exit(EEXIT);
+ }
+
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
(void)signal(SIGINT, catch);
if (ckclean)
@@ -237,18 +243,10 @@ checkfilesys(char *filesys)
ufs2_daddr_t n_ffree, n_bfree;
struct dups *dp;
struct statfs *mntp;
- struct stat snapdir;
- struct group *grp;
- struct iovec *iov;
- char errmsg[255];
- int ofsmodified;
- int iovlen;
intmax_t blks, files;
size_t size;
+ int sbreadfailed, ofsmodified;
- iov = NULL;
- iovlen = 0;
- errmsg[0] = '\0';
fsutilinit();
fsckinit();
@@ -272,10 +270,12 @@ checkfilesys(char *filesys)
* exit status will cause a foreground check to be run.
*/
sblock_init();
+ sbreadfailed = 0;
+ if (openfilesys(filesys) == 0 || readsb(0) == 0)
+ sbreadfailed = 1;
if (bkgrdcheck) {
- if ((fsreadfd = open(filesys, O_RDONLY)) < 0 || readsb(0) == 0)
+ if (sbreadfailed)
exit(3); /* Cannot read superblock */
- close(fsreadfd);
/* Earlier background failed or journaled */
if (sblock.fs_flags & (FS_NEEDSFSCK | FS_SUJ))
exit(4);
@@ -293,7 +293,7 @@ checkfilesys(char *filesys)
/*
* If file system is gjournaled, check it here.
*/
- if ((fsreadfd = open(filesys, O_RDONLY)) < 0 || readsb(0) == 0)
+ if (sbreadfailed)
exit(3); /* Cannot read superblock */
if (bkgrdflag == 0 &&
(nflag || (fswritefd = open(filesys, O_WRONLY)) < 0)) {
@@ -307,106 +307,30 @@ checkfilesys(char *filesys)
pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n");
exit(0);
}
- if ((sblock.fs_flags & (FS_UNCLEAN | FS_NEEDSFSCK)) == 0) {
+ if ((sblock.fs_flags &
+ (FS_UNCLEAN | FS_NEEDSFSCK)) == 0) {
bufinit();
gjournal_check(filesys);
if (chkdoreload(mntp) == 0)
exit(0);
exit(4);
} else {
- pfatal(
- "UNEXPECTED INCONSISTENCY, CANNOT RUN FAST FSCK\n");
+ pfatal("FULL FSCK NEEDED, CANNOT RUN FAST "
+ "FSCK\n");
}
}
- close(fsreadfd);
close(fswritefd);
+ fswritefd = -1;
}
- /*
- * If we are to do a background check:
- * Get the mount point information of the file system
- * create snapshot file
- * return created snapshot file
- * if not found, clear bkgrdflag and proceed with normal fsck
- */
if (bkgrdflag) {
- if (mntp == NULL) {
- bkgrdflag = 0;
- pfatal("NOT MOUNTED, CANNOT RUN IN BACKGROUND\n");
- } else if ((mntp->f_flags & MNT_SOFTDEP) == 0) {
- bkgrdflag = 0;
- pfatal(
- "NOT USING SOFT UPDATES, CANNOT RUN IN BACKGROUND\n");
- } else if ((mntp->f_flags & MNT_RDONLY) != 0) {
- bkgrdflag = 0;
- pfatal("MOUNTED READ-ONLY, CANNOT RUN IN BACKGROUND\n");
- } else if ((fsreadfd = open(filesys, O_RDONLY)) >= 0) {
- if (readsb(0) != 0) {
- if (sblock.fs_flags & (FS_NEEDSFSCK | FS_SUJ)) {
- bkgrdflag = 0;
- pfatal(
- "UNEXPECTED INCONSISTENCY, CANNOT RUN IN BACKGROUND\n");
- }
- if ((sblock.fs_flags & FS_UNCLEAN) == 0 &&
- skipclean && ckclean) {
- /*
- * file system is clean;
- * skip snapshot and report it clean
- */
- pwarn(
- "FILE SYSTEM CLEAN; SKIPPING CHECKS\n");
- goto clean;
- }
- }
- close(fsreadfd);
- }
- if (bkgrdflag) {
- snprintf(snapname, sizeof snapname, "%s/.snap",
- mntp->f_mntonname);
- if (stat(snapname, &snapdir) < 0) {
- if (errno != ENOENT) {
- bkgrdflag = 0;
- pfatal(
- "CANNOT FIND SNAPSHOT DIRECTORY %s: %s, CANNOT RUN IN BACKGROUND\n",
- snapname, strerror(errno));
- } else if ((grp = getgrnam("operator")) == NULL ||
- mkdir(snapname, 0770) < 0 ||
- chown(snapname, -1, grp->gr_gid) < 0 ||
- chmod(snapname, 0770) < 0) {
- bkgrdflag = 0;
- pfatal(
- "CANNOT CREATE SNAPSHOT DIRECTORY %s: %s, CANNOT RUN IN BACKGROUND\n",
- snapname, strerror(errno));
- }
- } else if (!S_ISDIR(snapdir.st_mode)) {
- bkgrdflag = 0;
- pfatal(
- "%s IS NOT A DIRECTORY, CANNOT RUN IN BACKGROUND\n",
- snapname);
- }
- }
- if (bkgrdflag) {
- snprintf(snapname, sizeof snapname,
- "%s/.snap/fsck_snapshot", mntp->f_mntonname);
- build_iovec(&iov, &iovlen, "fstype", "ffs", 4);
- build_iovec(&iov, &iovlen, "from", snapname,
- (size_t)-1);
- build_iovec(&iov, &iovlen, "fspath", mntp->f_mntonname,
- (size_t)-1);
- build_iovec(&iov, &iovlen, "errmsg", errmsg,
- sizeof(errmsg));
- build_iovec(&iov, &iovlen, "update", NULL, 0);
- build_iovec(&iov, &iovlen, "snapshot", NULL, 0);
-
- while (nmount(iov, iovlen, mntp->f_flags) < 0) {
- if (errno == EEXIST && unlink(snapname) == 0)
- continue;
- bkgrdflag = 0;
- pfatal("CANNOT CREATE SNAPSHOT %s: %s %s\n",
- snapname, strerror(errno), errmsg);
- break;
- }
- if (bkgrdflag != 0)
- filesys = snapname;
+ switch (setup_bkgrdchk(mntp, sbreadfailed, &filesys)) {
+ case -1: /* filesystem clean */
+ goto clean;
+ case 0: /* cannot do background, give up */
+ exit(EEXIT);
+ case 1: /* doing background check, preen rules apply */
+ preen = 1;
+ break;
}
}
@@ -529,10 +453,10 @@ checkfilesys(char *filesys)
*/
if (duplist) {
if (preen || usedsoftdep)
- pfatal("INTERNAL ERROR: dups with %s%s%s",
+ pfatal("INTERNAL ERROR: DUPS WITH %s%s%s",
preen ? "-p" : "",
- (preen && usedsoftdep) ? " and " : "",
- usedsoftdep ? "softupdates" : "");
+ (preen && usedsoftdep) ? " AND " : "",
+ usedsoftdep ? "SOFTUPDATES" : "");
printf("** Phase 1b - Rescan For More DUPS\n");
pass1b();
IOstats("Pass1b");
@@ -647,6 +571,149 @@ checkfilesys(char *filesys)
return (rerun ? ERERUN : 0);
}
+/*
+ * If we are to do a background check:
+ * Get the mount point information of the file system
+ * If already clean, return -1
+ * Check that kernel supports background fsck
+ * Find or create the snapshot directory
+ * Create the snapshot file
+ * Open snapshot
+ * If anything fails print reason and return 0 which exits
+ */
+static int
+setup_bkgrdchk(struct statfs *mntp, int sbreadfailed, char **filesys)
+{
+ struct stat snapdir;
+ struct group *grp;
+ struct iovec *iov;
+ char errmsg[255];
+ int iovlen;
+ size_t size;
+
+ /* Get the mount point information of the file system */
+ if (mntp == NULL) {
+ pwarn("NOT MOUNTED, CANNOT RUN IN BACKGROUND\n");
+ return (0);
+ }
+ if ((mntp->f_flags & MNT_RDONLY) != 0) {
+ pwarn("MOUNTED READ-ONLY, CANNOT RUN IN BACKGROUND\n");
+ return (0);
+ }
+ if ((mntp->f_flags & MNT_SOFTDEP) == 0) {
+ pwarn("NOT USING SOFT UPDATES, CANNOT RUN IN BACKGROUND\n");
+ return (0);
+ }
+ if (sbreadfailed) {
+ pwarn("SUPERBLOCK READ FAILED, CANNOT RUN IN BACKGROUND\n");
+ return (0);
+ }
+ if ((sblock.fs_flags & FS_NEEDSFSCK) != 0) {
+ pwarn("FULL FSCK NEEDED, CANNOT RUN IN BACKGROUND\n");
+ return (0);
+ }
+ if ((sblock.fs_flags & FS_SUJ) != 0) {
+ pwarn("JOURNALED FILESYSTEM, CANNOT RUN IN BACKGROUND\n");
+ return (0);
+ }
+ if (skipclean && ckclean &&
+ (sblock.fs_flags & (FS_UNCLEAN|FS_NEEDSFSCK)) == 0) {
+ /*
+ * file system is clean;
+ * skip snapshot and report it clean
+ */
+ pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n");
+ return (-1);
+ }
+ /* Check that kernel supports background fsck */
+ size = MIBSIZE;
+ if (sysctlnametomib("vfs.ffs.adjrefcnt", adjrefcnt, &size) < 0||
+ sysctlnametomib("vfs.ffs.adjblkcnt", adjblkcnt, &size) < 0||
+ sysctlnametomib("vfs.ffs.setsize", setsize, &size) < 0 ||
+ sysctlnametomib("vfs.ffs.freefiles", freefiles, &size) < 0||
+ sysctlnametomib("vfs.ffs.freedirs", freedirs, &size) < 0 ||
+ sysctlnametomib("vfs.ffs.freeblks", freeblks, &size) < 0) {
+ pwarn("KERNEL LACKS BACKGROUND FSCK SUPPORT\n");
+ return (0);
+ }
+ /*
+ * When kernel lacks runtime bgfsck superblock summary
+ * adjustment functionality, it does not mean we can not
+ * continue, as old kernels will recompute the summary at
+ * mount time. However, it will be an unexpected softupdates
+ * inconsistency if it turns out that the summary is still
+ * incorrect. Set a flag so subsequent operation can know this.
+ */
+ bkgrdsumadj = 1;
+ if (sysctlnametomib("vfs.ffs.adjndir", adjndir, &size) < 0 ||
+ sysctlnametomib("vfs.ffs.adjnbfree", adjnbfree, &size) < 0 ||
+ sysctlnametomib("vfs.ffs.adjnifree", adjnifree, &size) < 0 ||
+ sysctlnametomib("vfs.ffs.adjnffree", adjnffree, &size) < 0 ||
+ sysctlnametomib("vfs.ffs.adjnumclusters", adjnumclusters,
+ &size) < 0) {
+ bkgrdsumadj = 0;
+ pwarn("KERNEL LACKS RUNTIME SUPERBLOCK SUMMARY ADJUSTMENT "
+ "SUPPORT\n");
+ }
+ /* Find or create the snapshot directory */
+ snprintf(snapname, sizeof snapname, "%s/.snap",
+ mntp->f_mntonname);
+ if (stat(snapname, &snapdir) < 0) {
+ if (errno != ENOENT) {
+ pwarn("CANNOT FIND SNAPSHOT DIRECTORY %s: %s, CANNOT "
+ "RUN IN BACKGROUND\n", snapname, strerror(errno));
+ return (0);
+ }
+ if ((grp = getgrnam("operator")) == NULL ||
+ mkdir(snapname, 0770) < 0 ||
+ chown(snapname, -1, grp->gr_gid) < 0 ||
+ chmod(snapname, 0770) < 0) {
+ pwarn("CANNOT CREATE SNAPSHOT DIRECTORY %s: %s, "
+ "CANNOT RUN IN BACKGROUND\n", snapname,
+ strerror(errno));
+ return (0);
+ }
+ } else if (!S_ISDIR(snapdir.st_mode)) {
+ pwarn("%s IS NOT A DIRECTORY, CANNOT RUN IN BACKGROUND\n",
+ snapname);
+ return (0);
+ }
+ /* Create the snapshot file */
+ iov = NULL;
+ iovlen = 0;
+ errmsg[0] = '\0';
+ snprintf(snapname, sizeof snapname, "%s/.snap/fsck_snapshot",
+ mntp->f_mntonname);
+ build_iovec(&iov, &iovlen, "fstype", "ffs", 4);
+ build_iovec(&iov, &iovlen, "from", snapname, (size_t)-1);
+ build_iovec(&iov, &iovlen, "fspath", mntp->f_mntonname, (size_t)-1);
+ build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
+ build_iovec(&iov, &iovlen, "update", NULL, 0);
+ build_iovec(&iov, &iovlen, "snapshot", NULL, 0);
+ /* Create snapshot, removing old snapshot if it exists */
+ while (nmount(iov, iovlen, mntp->f_flags) < 0) {
+ if (errno == EEXIST && unlink(snapname) == 0)
+ continue;
+ pwarn("CANNOT CREATE SNAPSHOT %s: %s %s\n", snapname,
+ strerror(errno), errmsg);
+ return (0);
+ }
+ /* Open snapshot */
+ if (openfilesys(snapname) == 0) {
+ unlink(snapname);
+ pwarn("CANNOT OPEN SNAPSHOT %s: %s, CANNOT RUN IN "
+ "BACKGROUND\n", snapname, strerror(errno));
+ return (0);
+ }
+ free(sblock.fs_csp);
+ free(sblock.fs_si);
+ havesb = 0;
+ *filesys = snapname;
+ cmd.version = FFS_CMD_VERSION;
+ cmd.handle = fsreadfd;
+ return (1);
+}
+
static int
chkdoreload(struct statfs *mntp)
{
diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c
index 0ae7f1bbb28f..0f0d9b61a076 100644
--- a/sbin/fsck_ffs/setup.c
+++ b/sbin/fsck_ffs/setup.c
@@ -76,114 +76,19 @@ static int chkrecovery(int devfd);
int
setup(char *dev)
{
- long cg, asked, i, j;
- long bmapsize;
- struct stat statb;
+ long cg, bmapsize;
struct fs proto;
- size_t size;
- havesb = 0;
- fswritefd = -1;
- cursnapshot = 0;
- if (stat(dev, &statb) < 0) {
- printf("Can't stat %s: %s\n", dev, strerror(errno));
- if (bkgrdflag) {
- unlink(snapname);
- bkgrdflag = 0;
- }
- return (0);
- }
- if ((statb.st_mode & S_IFMT) != S_IFCHR &&
- (statb.st_mode & S_IFMT) != S_IFBLK) {
- if (bkgrdflag != 0 && (statb.st_flags & SF_SNAPSHOT) == 0) {
- unlink(snapname);
- printf("background fsck lacks a snapshot\n");
- exit(EEXIT);
- }
- if ((statb.st_flags & SF_SNAPSHOT) != 0 && cvtlevel == 0) {
- cursnapshot = statb.st_ino;
- } else {
- if (cvtlevel == 0 ||
- (statb.st_flags & SF_SNAPSHOT) == 0) {
- if (preen && bkgrdflag) {
- unlink(snapname);
- bkgrdflag = 0;
- }
- pfatal("%s is not a disk device", dev);
- if (reply("CONTINUE") == 0) {
- if (bkgrdflag) {
- unlink(snapname);
- bkgrdflag = 0;
- }
- return (0);
- }
- } else {
- if (bkgrdflag) {
- unlink(snapname);
- bkgrdflag = 0;
- }
- pfatal("cannot convert a snapshot");
- exit(EEXIT);
- }
- }
- }
- if ((fsreadfd = open(dev, O_RDONLY)) < 0) {
- if (bkgrdflag) {
- unlink(snapname);
- bkgrdflag = 0;
- }
- printf("Can't open %s: %s\n", dev, strerror(errno));
+ /*
+ * We are expected to have an open file descriptor
+ */
+ if (fsreadfd < 0)
return (0);
- }
- if (bkgrdflag) {
- unlink(snapname);
- size = MIBSIZE;
- if (sysctlnametomib("vfs.ffs.adjrefcnt", adjrefcnt, &size) < 0||
- sysctlnametomib("vfs.ffs.adjblkcnt", adjblkcnt, &size) < 0||
- sysctlnametomib("vfs.ffs.setsize", setsize, &size) < 0 ||
- sysctlnametomib("vfs.ffs.freefiles", freefiles, &size) < 0||
- sysctlnametomib("vfs.ffs.freedirs", freedirs, &size) < 0 ||
- sysctlnametomib("vfs.ffs.freeblks", freeblks, &size) < 0) {
- pfatal("kernel lacks background fsck support\n");
- exit(EEXIT);
- }
- /*
- * When kernel is lack of runtime bgfsck superblock summary
- * adjustment functionality, it does not mean we can not
- * continue, as old kernels will recompute the summary at
- * mount time. However, it will be an unexpected softupdates
- * inconsistency if it turns out that the summary is still
- * incorrect. Set a flag so subsequent operation can know
- * this.
- */
- bkgrdsumadj = 1;
- if (sysctlnametomib("vfs.ffs.adjndir", adjndir, &size) < 0 ||
- sysctlnametomib("vfs.ffs.adjnbfree", adjnbfree, &size) < 0 ||
- sysctlnametomib("vfs.ffs.adjnifree", adjnifree, &size) < 0 ||
- sysctlnametomib("vfs.ffs.adjnffree", adjnffree, &size) < 0 ||
- sysctlnametomib("vfs.ffs.adjnumclusters", adjnumclusters, &size) < 0) {
- bkgrdsumadj = 0;
- pwarn("kernel lacks runtime superblock summary adjustment support");
- }
- cmd.version = FFS_CMD_VERSION;
- cmd.handle = fsreadfd;
- fswritefd = -1;
- }
- if (preen == 0)
- printf("** %s", dev);
- if (bkgrdflag == 0 &&
- (nflag || (fswritefd = open(dev, O_WRONLY)) < 0)) {
- fswritefd = -1;
- if (preen)
- pfatal("NO WRITE ACCESS");
- printf(" (NO WRITE)");
- }
- if (preen == 0)
- printf("\n");
/*
- * Read in the superblock, looking for alternates if necessary
+ * If we do not yet have a superblock, read it in looking
+ * for alternates if necessary.
*/
- if (readsb(1) == 0) {
+ if (havesb == 0 && readsb(1) == 0) {
skipclean = 0;
if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0)
return(0);
@@ -195,19 +100,38 @@ setup(char *dev)
break;
}
if (cg >= proto.fs_ncg) {
- printf("%s %s\n%s %s\n%s %s\n",
- "SEARCH FOR ALTERNATE SUPER-BLOCK",
- "FAILED. YOU MUST USE THE",
- "-b OPTION TO FSCK TO SPECIFY THE",
- "LOCATION OF AN ALTERNATE",
- "SUPER-BLOCK TO SUPPLY NEEDED",
- "INFORMATION; SEE fsck_ffs(8).");
+ printf("SEARCH FOR ALTERNATE SUPER-BLOCK FAILED. "
+ "YOU MUST USE THE\n-b OPTION TO FSCK TO SPECIFY "
+ "THE LOCATION OF AN ALTERNATE\nSUPER-BLOCK TO "
+ "SUPPLY NEEDED INFORMATION; SEE fsck_ffs(8).\n");
bflag = 0;
return(0);
}
pwarn("USING ALTERNATE SUPERBLOCK AT %jd\n", bflag);
bflag = 0;
}
+ if (preen == 0)
+ printf("** %s", dev);
+ if (bkgrdflag == 0 &&
+ (nflag || (fswritefd = open(dev, O_WRONLY)) < 0)) {
+ fswritefd = -1;
+ if (preen)
+ pfatal("NO WRITE ACCESS");
+ printf(" (NO WRITE)");
+ }
+ if (preen == 0)
+ printf("\n");
+ if (sbhashfailed != 0) {
+ pwarn("SUPERBLOCK CHECK HASH FAILED");
+ if (fswritefd == -1)
+ pwarn("OPENED READONLY SO CANNOT CORRECT CHECK HASH\n");
+ else if (preen || reply("CORRECT CHECK HASH") != 0) {
+ if (preen)
+ printf(" (CORRECTED)\n");
+ sblock.fs_clean = 0;
+ sbdirty();
+ }
+ }
if (skipclean && ckclean && sblock.fs_clean) {
pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n");
return (-1);
@@ -248,30 +172,6 @@ setup(char *dev)
reply("SAVE DATA TO FIND ALTERNATE SUPERBLOCKS") != 0)
saverecovery(fsreadfd, fswritefd);
/*
- * read in the summary info.
- */
- asked = 0;
- sblock.fs_csp = Calloc(1, sblock.fs_cssize);
- if (sblock.fs_csp == NULL) {
- printf("cannot alloc %u bytes for cg summary info\n",
- (unsigned)sblock.fs_cssize);
- goto badsb;
- }
- for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
- size = MIN(sblock.fs_cssize - i, sblock.fs_bsize);
- readcnt[sblk.b_type]++;
- if (blread(fsreadfd, (char *)sblock.fs_csp + i,
- fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
- size) != 0 && !asked) {
- pfatal("BAD SUMMARY INFORMATION");
- if (reply("CONTINUE") == 0) {
- ckfini(0);
- exit(EEXIT);
- }
- asked++;
- }
- }
- /*
* allocate and initialize the necessary maps
*/
bmapsize = roundup(howmany(maxfsblock, CHAR_BIT), sizeof(short));
@@ -311,6 +211,41 @@ badsb:
}
/*
+ * Open a device or file to be checked by fsck.
+ */
+int
+openfilesys(char *dev)
+{
+ struct stat statb;
+ int saved_fsreadfd;
+
+ if (stat(dev, &statb) < 0)
+ return (0);
+ if ((statb.st_mode & S_IFMT) != S_IFCHR &&
+ (statb.st_mode & S_IFMT) != S_IFBLK) {
+ if (bkgrdflag != 0 && (statb.st_flags & SF_SNAPSHOT) == 0) {
+ pfatal("BACKGROUND FSCK LACKS A SNAPSHOT\n");
+ exit(EEXIT);
+ }
+ if (bkgrdflag != 0) {
+ cursnapshot = statb.st_ino;
+ } else {
+ pfatal("%s IS NOT A DISK DEVICE\n", dev);
+ if (reply("CONTINUE") == 0)
+ return (0);
+ }
+ }
+ saved_fsreadfd = fsreadfd;
+ if ((fsreadfd = open(dev, O_RDONLY)) < 0) {
+ fsreadfd = saved_fsreadfd;
+ return (0);
+ }
+ if (saved_fsreadfd != -1)
+ close(saved_fsreadfd);
+ return (1);
+}
+
+/*
* Read in the super block and its summary info.
*/
int
@@ -320,13 +255,17 @@ readsb(int listerr)
int bad, ret;
struct fs *fs;
- super = bflag ? bflag * dev_bsize : STDSB_NOHASHFAIL;
+ super = bflag ? bflag * dev_bsize :
+ sbhashfailed ? STDSB_NOHASHFAIL_NOMSG : STDSB_NOMSG;
readcnt[sblk.b_type]++;
- if ((ret = sbget(fsreadfd, &fs, super)) != 0) {
+ while ((ret = sbget(fsreadfd, &fs, super)) != 0) {
switch (ret) {
- case EINVAL:
- /* Superblock check-hash failed */
- return (0);
+ case EINTEGRITY:
+ if (bflag || super == STDSB_NOHASHFAIL_NOMSG)
+ return (0);
+ super = STDSB_NOHASHFAIL_NOMSG;
+ sbhashfailed = 1;
+ continue;
case ENOENT:
if (bflag)
printf("%jd is not a file system "
@@ -427,6 +366,7 @@ void
sblock_init(void)
{
+ fsreadfd = -1;
fswritefd = -1;
fsmodified = 0;
lfdir = 0;
@@ -495,17 +435,19 @@ chkrecovery(int devfd)
{
struct fsrecovery *fsr;
char *fsrbuf;
- u_int secsize;
+ u_int secsize, rdsize;
/*
* Could not determine if backup material exists, so do not
* offer to create it.
*/
fsrbuf = NULL;
+ rdsize = sblock.fs_fsize;
if (ioctl(devfd, DIOCGSECTORSIZE, &secsize) == -1 ||
- (fsrbuf = Malloc(secsize)) == NULL ||
- blread(devfd, fsrbuf, (SBLOCK_UFS2 - secsize) / dev_bsize,
- secsize) != 0) {
+ rdsize % secsize != 0 ||
+ (fsrbuf = Malloc(rdsize)) == NULL ||
+ blread(devfd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize,
+ rdsize) != 0) {
free(fsrbuf);
return (1);
}
@@ -513,7 +455,7 @@ chkrecovery(int devfd)
* Recovery material has already been created, so do not
* need to create it again.
*/
- fsr = (struct fsrecovery *)&fsrbuf[secsize - sizeof *fsr];
+ fsr = (struct fsrecovery *)&fsrbuf[rdsize - sizeof *fsr];
if (fsr->fsr_magic == FS_UFS2_MAGIC) {
free(fsrbuf);
return (1);
@@ -526,8 +468,8 @@ chkrecovery(int devfd)
}
/*
- * Read the last sector of the boot block, replace the last
- * 20 bytes with the recovery information, then write it back.
+ * Read the last filesystem-size piece of the boot block, replace the
+ * last 20 bytes with the recovery information, then write it back.
* The recovery information only works for UFS2 filesystems.
*/
static void
@@ -535,24 +477,26 @@ saverecovery(int readfd, int writefd)
{
struct fsrecovery *fsr;
char *fsrbuf;
- u_int secsize;
+ u_int secsize, rdsize;
fsrbuf = NULL;
+ rdsize = sblock.fs_fsize;
if (sblock.fs_magic != FS_UFS2_MAGIC ||
ioctl(readfd, DIOCGSECTORSIZE, &secsize) == -1 ||
- (fsrbuf = Malloc(secsize)) == NULL ||
- blread(readfd, fsrbuf, (SBLOCK_UFS2 - secsize) / dev_bsize,
- secsize) != 0) {
+ rdsize % secsize != 0 ||
+ (fsrbuf = Malloc(rdsize)) == NULL ||
+ blread(readfd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize,
+ rdsize) != 0) {
printf("RECOVERY DATA COULD NOT BE CREATED\n");
free(fsrbuf);
return;
}
- fsr = (struct fsrecovery *)&fsrbuf[secsize - sizeof *fsr];
+ fsr = (struct fsrecovery *)&fsrbuf[rdsize - sizeof *fsr];
fsr->fsr_magic = sblock.fs_magic;
fsr->fsr_fpg = sblock.fs_fpg;
fsr->fsr_fsbtodb = sblock.fs_fsbtodb;
fsr->fsr_sblkno = sblock.fs_sblkno;
fsr->fsr_ncg = sblock.fs_ncg;
- blwrite(writefd, fsrbuf, (SBLOCK_UFS2 - secsize) / secsize, secsize);
+ blwrite(writefd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize, rdsize);
free(fsrbuf);
}
diff --git a/sbin/fsdb/fsdb.c b/sbin/fsdb/fsdb.c
index 785aeb2b5a75..c935f88952b4 100644
--- a/sbin/fsdb/fsdb.c
+++ b/sbin/fsdb/fsdb.c
@@ -111,7 +111,7 @@ main(int argc, char *argv[])
fsys = argv[0];
sblock_init();
- if (!setup(fsys))
+ if (openfilesys(fsys) == 0 || readsb(0) == 0 || setup(fsys) == 0)
errx(1, "cannot set up file system `%s'", fsys);
if (fswritefd < 0)
nflag++;
diff --git a/sbin/fsdb/fsdbutil.c b/sbin/fsdb/fsdbutil.c
index 2c18d9910c6f..c8a3a8a525e3 100644
--- a/sbin/fsdb/fsdbutil.c
+++ b/sbin/fsdb/fsdbutil.c
@@ -136,11 +136,8 @@ printstat(const char *cp, ino_t inum, union dinode *dp)
if (DIP(dp, di_size) > 0 &&
DIP(dp, di_size) < sblock.fs_maxsymlinklen &&
DIP(dp, di_blocks) == 0) {
- if (sblock.fs_magic == FS_UFS1_MAGIC)
- p = (caddr_t)dp->dp1.di_db;
- else
- p = (caddr_t)dp->dp2.di_db;
- printf(" to `%.*s'\n", (int) DIP(dp, di_size), p);
+ printf(" to `%.*s'\n", (int) DIP(dp, di_size),
+ DIP(dp, di_shortlink));
} else {
putchar('\n');
}
diff --git a/sbin/geom/core/geom.8 b/sbin/geom/core/geom.8
index 298fc2b1d4fd..db0556fb9505 100644
--- a/sbin/geom/core/geom.8
+++ b/sbin/geom/core/geom.8
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd September 14, 2018
+.Dd January 19, 2022
.Dt GEOM 8
.Os
.Sh NAME
@@ -162,6 +162,8 @@ SHSEC
.It
STRIPE
.It
+UNION
+.It
VIRSTOR
.El
.Sh ENVIRONMENT
@@ -210,6 +212,7 @@ geom md unload
.Xr gsched 8 ,
.Xr gshsec 8 ,
.Xr gstripe 8 ,
+.Xr gunion 8 ,
.Xr gvirstor 8
.Sh HISTORY
The
diff --git a/sbin/geom/core/geom.c b/sbin/geom/core/geom.c
index 0202be9a063e..9b43910b88f9 100644
--- a/sbin/geom/core/geom.c
+++ b/sbin/geom/core/geom.c
@@ -75,6 +75,8 @@ static struct g_command *class_commands = NULL;
static struct g_command *find_command(const char *cmdstr, int flags);
static void list_one_geom_by_provider(const char *provider_name);
static int std_available(const char *name);
+static int std_list_available(void);
+static int std_load_available(void);
static void std_help(struct gctl_req *req, unsigned flags);
static void std_list(struct gctl_req *req, unsigned flags);
@@ -487,7 +489,7 @@ run_command(int argc, char *argv[])
gctl_ro_param(req, "version", sizeof(*version), version);
parse_arguments(cmd, req, &argc, &argv);
- bzero(buf, sizeof(buf));
+ buf[0] = '\0';
if (cmd->gc_func != NULL) {
unsigned flags;
@@ -495,7 +497,8 @@ run_command(int argc, char *argv[])
cmd->gc_func(req, flags);
errstr = req->error;
} else {
- gctl_rw_param(req, "output", sizeof(buf), buf);
+ gctl_add_param(req, "output", sizeof(buf), buf,
+ GCTL_PARAM_WR | GCTL_PARAM_ASCII);
errstr = gctl_issue(req);
}
if (errstr != NULL && errstr[0] != '\0') {
@@ -656,7 +659,7 @@ get_class(int *argc, char ***argv)
set_class_name();
/* If we can't load or list, it's not a class. */
- if (!std_available("load") && !std_available("list"))
+ if (!std_load_available() && !std_list_available())
errx(EXIT_FAILURE, "Invalid class name '%s'.", class_name);
if (*argc < 1)
@@ -993,7 +996,7 @@ std_list_available(void)
struct gclass *classp;
int error;
- error = geom_gettree(&mesh);
+ error = geom_gettree_geom(&mesh, gclass_name, "", 0);
if (error != 0)
errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
classp = find_class(&mesh, gclass_name);
@@ -1012,7 +1015,12 @@ std_list(struct gctl_req *req, unsigned flags __unused)
const char *name;
int all, error, i, nargs;
- error = geom_gettree(&mesh);
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs == 1) {
+ error = geom_gettree_geom(&mesh, gclass_name,
+ gctl_get_ascii(req, "arg0"), 1);
+ } else
+ error = geom_gettree(&mesh);
if (error != 0)
errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
classp = find_class(&mesh, gclass_name);
@@ -1020,7 +1028,6 @@ std_list(struct gctl_req *req, unsigned flags __unused)
geom_deletetree(&mesh);
errx(EXIT_FAILURE, "Class '%s' not found.", gclass_name);
}
- nargs = gctl_get_int(req, "nargs");
all = gctl_get_int(req, "all");
if (nargs > 0) {
for (i = 0; i < nargs; i++) {
@@ -1318,10 +1325,10 @@ std_load_available(void)
snprintf(name, sizeof(name), "g_%s", class_name);
/*
- * If already in kernel, "load" command is not available.
+ * If already in kernel, "load" command is NOP.
*/
if (modfind(name) >= 0)
- return (0);
+ return (1);
bzero(paths, sizeof(paths));
len = sizeof(paths);
if (sysctlbyname("kern.module_path", paths, &len, NULL, 0) < 0)
diff --git a/sbin/geom/core/geom.h b/sbin/geom/core/geom.h
index 38a99032f692..a76021c8aca5 100644
--- a/sbin/geom/core/geom.h
+++ b/sbin/geom/core/geom.h
@@ -34,7 +34,7 @@
/*
* The G_FLAG_VERBOSE flag on a command specification means that the
- * comand will accept a -v option and the GEOM framework will print
+ * command will accept a -v option and the GEOM framework will print
* out status information after the command when it is run with -v.
* Additionally a GEOM command can explicitly specify a -v option and
* handle it as it would any other option. If both a -v option and
diff --git a/sbin/ggate/ggatec/ggatec.c b/sbin/ggate/ggatec/ggatec.c
index 0695dae0dca2..0de8504ce3c4 100644
--- a/sbin/ggate/ggatec/ggatec.c
+++ b/sbin/ggate/ggatec/ggatec.c
@@ -74,6 +74,7 @@ static int sendfd, recvfd;
static uint32_t token;
static pthread_t sendtd, recvtd;
static int reconnect;
+static int initialbuffersize = 131072;
static void
usage(void)
@@ -94,18 +95,25 @@ send_thread(void *arg __unused)
{
struct g_gate_ctl_io ggio;
struct g_gate_hdr hdr;
- char buf[MAXPHYS];
- ssize_t data;
+ size_t buf_capacity;
+ ssize_t numbytesprocd;
int error;
+ char *newbuf;
g_gate_log(LOG_NOTICE, "%s: started!", __func__);
+ buf_capacity = initialbuffersize;
+
ggio.gctl_version = G_GATE_VERSION;
ggio.gctl_unit = unit;
- ggio.gctl_data = buf;
+ ggio.gctl_data = malloc(buf_capacity);
+ if (ggio.gctl_data == NULL) {
+ g_gate_log(LOG_ERR, "%s: Cannot alloc buffer.", __func__);
+ pthread_exit(NULL);
+ }
for (;;) {
- ggio.gctl_length = sizeof(buf);
+ ggio.gctl_length = buf_capacity;
ggio.gctl_error = 0;
g_gate_ioctl(G_GATE_CMD_START, &ggio);
error = ggio.gctl_error;
@@ -118,17 +126,22 @@ send_thread(void *arg __unused)
/* Exit gracefully. */
g_gate_close_device();
exit(EXIT_SUCCESS);
-#if 0
+
case ENOMEM:
+ {
/* Buffer too small. */
- ggio.gctl_data = realloc(ggio.gctl_data,
+ g_gate_log(LOG_DEBUG, "buffer too small. new size: %u",
ggio.gctl_length);
- if (ggio.gctl_data != NULL) {
- bsize = ggio.gctl_length;
- goto once_again;
+ newbuf = malloc(ggio.gctl_length);
+ if (newbuf != NULL) {
+ free(ggio.gctl_data);
+ ggio.gctl_data = newbuf;
+ buf_capacity = ggio.gctl_length;
+ continue;
}
/* FALLTHROUGH */
-#endif
+ }
+
case ENXIO:
default:
g_gate_xlog("ioctl(/dev/%s): %s.", G_GATE_CTL_NAME,
@@ -145,16 +158,12 @@ send_thread(void *arg __unused)
case BIO_WRITE:
hdr.gh_cmd = GGATE_CMD_WRITE;
break;
+ case BIO_FLUSH:
+ hdr.gh_cmd = GGATE_CMD_FLUSH;
+ break;
default:
- g_gate_log(LOG_NOTICE, "Unknown gctl_cmd: %i", ggio.gctl_cmd);
- ggio.gctl_error = EOPNOTSUPP;
- g_gate_ioctl(G_GATE_CMD_DONE, &ggio);
- continue;
- }
-
- /* Don't send requests for more data than we can handle the response for! */
- if (ggio.gctl_length > MAXPHYS) {
- g_gate_log(LOG_ERR, "Request too big: %zd", ggio.gctl_length);
+ g_gate_log(LOG_NOTICE, "Unknown gctl_cmd: %i",
+ ggio.gctl_cmd);
ggio.gctl_error = EOPNOTSUPP;
g_gate_ioctl(G_GATE_CMD_DONE, &ggio);
continue;
@@ -166,12 +175,12 @@ send_thread(void *arg __unused)
hdr.gh_error = 0;
g_gate_swap2n_hdr(&hdr);
- data = g_gate_send(sendfd, &hdr, sizeof(hdr), MSG_NOSIGNAL);
+ numbytesprocd = g_gate_send(sendfd, &hdr, sizeof(hdr), MSG_NOSIGNAL);
g_gate_log(LOG_DEBUG, "Sent hdr packet.");
g_gate_swap2h_hdr(&hdr);
if (reconnect)
break;
- if (data != sizeof(hdr)) {
+ if (numbytesprocd != sizeof(hdr)) {
g_gate_log(LOG_ERR, "Lost connection 1.");
reconnect = 1;
pthread_kill(recvtd, SIGUSR1);
@@ -179,18 +188,19 @@ send_thread(void *arg __unused)
}
if (hdr.gh_cmd == GGATE_CMD_WRITE) {
- data = g_gate_send(sendfd, ggio.gctl_data,
+ numbytesprocd = g_gate_send(sendfd, ggio.gctl_data,
ggio.gctl_length, MSG_NOSIGNAL);
if (reconnect)
break;
- if (data != ggio.gctl_length) {
- g_gate_log(LOG_ERR, "Lost connection 2 (%zd != %zd).", data, (ssize_t)ggio.gctl_length);
+ if (numbytesprocd != ggio.gctl_length) {
+ g_gate_log(LOG_ERR, "Lost connection 2 (%zd != %zd).",
+ numbytesprocd, (ssize_t)ggio.gctl_length);
reconnect = 1;
pthread_kill(recvtd, SIGUSR1);
break;
}
g_gate_log(LOG_DEBUG, "Sent %zd bytes (offset=%"
- PRIu64 ", length=%" PRIu32 ").", data,
+ PRIu64 ", length=%" PRIu32 ").", numbytesprocd,
hdr.gh_offset, hdr.gh_length);
}
}
@@ -203,22 +213,29 @@ recv_thread(void *arg __unused)
{
struct g_gate_ctl_io ggio;
struct g_gate_hdr hdr;
- char buf[MAXPHYS];
- ssize_t data;
+ ssize_t buf_capacity;
+ ssize_t numbytesprocd;
+ char *newbuf;
g_gate_log(LOG_NOTICE, "%s: started!", __func__);
+ buf_capacity = initialbuffersize;
+
ggio.gctl_version = G_GATE_VERSION;
ggio.gctl_unit = unit;
- ggio.gctl_data = buf;
+ ggio.gctl_data = malloc(buf_capacity);
+ if (ggio.gctl_data == NULL) {
+ g_gate_log(LOG_ERR, "%s: Cannot alloc buffer.", __func__);
+ pthread_exit(NULL);
+ }
for (;;) {
- data = g_gate_recv(recvfd, &hdr, sizeof(hdr), MSG_WAITALL);
+ numbytesprocd = g_gate_recv(recvfd, &hdr, sizeof(hdr), MSG_WAITALL);
if (reconnect)
break;
g_gate_swap2h_hdr(&hdr);
- if (data != sizeof(hdr)) {
- if (data == -1 && errno == EAGAIN)
+ if (numbytesprocd != sizeof(hdr)) {
+ if (numbytesprocd == -1 && errno == EAGAIN)
continue;
g_gate_log(LOG_ERR, "Lost connection 3.");
reconnect = 1;
@@ -233,26 +250,33 @@ recv_thread(void *arg __unused)
ggio.gctl_length = hdr.gh_length;
ggio.gctl_error = hdr.gh_error;
- /* Do not overflow our buffer if there is a bogus response. */
- if (ggio.gctl_length > (off_t) sizeof(buf)) {
- g_gate_log(LOG_ERR, "Received too big response: %zd", ggio.gctl_length);
- break;
+ if (ggio.gctl_length > buf_capacity) {
+ newbuf = malloc(ggio.gctl_length);
+ if (newbuf != NULL) {
+ free(ggio.gctl_data);
+ ggio.gctl_data = newbuf;
+ buf_capacity = ggio.gctl_length;
+ } else {
+ g_gate_log(LOG_ERR, "Received too big response: %zd",
+ ggio.gctl_length);
+ break;
+ }
}
if (ggio.gctl_error == 0 && ggio.gctl_cmd == GGATE_CMD_READ) {
- data = g_gate_recv(recvfd, ggio.gctl_data,
+ numbytesprocd = g_gate_recv(recvfd, ggio.gctl_data,
ggio.gctl_length, MSG_WAITALL);
if (reconnect)
break;
g_gate_log(LOG_DEBUG, "Received data packet.");
- if (data != ggio.gctl_length) {
+ if (numbytesprocd != ggio.gctl_length) {
g_gate_log(LOG_ERR, "Lost connection 4.");
reconnect = 1;
pthread_kill(sendtd, SIGUSR1);
break;
}
g_gate_log(LOG_DEBUG, "Received %d bytes (offset=%"
- PRIu64 ", length=%" PRIu32 ").", data,
+ PRIu64 ", length=%" PRIu32 ").", numbytesprocd,
hdr.gh_offset, hdr.gh_length);
}
@@ -509,6 +533,16 @@ g_gatec_rescue(void)
g_gatec_loop();
}
+static void
+init_initial_buffer_size(void)
+{
+ int value;
+ size_t intsize;
+ intsize = sizeof(initialbuffersize);
+ if (sysctlbyname("kern.maxphys", &value, &intsize, NULL, 0) == 0)
+ initialbuffersize = value;
+}
+
int
main(int argc, char *argv[])
{
@@ -624,6 +658,8 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
+ init_initial_buffer_size();
+
switch (action) {
case CREATE:
if (argc != 2)
diff --git a/sbin/ggate/ggated/ggated.c b/sbin/ggate/ggated/ggated.c
index 226ba1ce72de..7cacbf58037e 100644
--- a/sbin/ggate/ggated/ggated.c
+++ b/sbin/ggate/ggated/ggated.c
@@ -726,7 +726,6 @@ disk_thread(void *arg)
/*
* Check the request.
*/
- assert(req->r_cmd == GGATE_CMD_READ || req->r_cmd == GGATE_CMD_WRITE);
assert(req->r_offset + req->r_length <= (uintmax_t)conn->c_mediasize);
assert((req->r_offset % conn->c_sectorsize) == 0);
assert((req->r_length % conn->c_sectorsize) == 0);
@@ -750,6 +749,19 @@ disk_thread(void *arg)
free(req->r_data);
req->r_data = NULL;
break;
+ case GGATE_CMD_FLUSH:
+ data = fsync(fd);
+ if (data != 0)
+ req->r_error = errno;
+ break;
+ default:
+ g_gate_log(LOG_DEBUG, "Unsupported request: %i", req->r_cmd);
+ req->r_error = EOPNOTSUPP;
+ if (req->r_data != NULL) {
+ free(req->r_data);
+ req->r_data = NULL;
+ }
+ break;
}
if (data != (ssize_t)req->r_length) {
/* Report short reads/writes as I/O errors. */
diff --git a/sbin/ggate/shared/ggate.h b/sbin/ggate/shared/ggate.h
index e2e1a57d817c..d399b247cd75 100644
--- a/sbin/ggate/shared/ggate.h
+++ b/sbin/ggate/shared/ggate.h
@@ -57,6 +57,7 @@
#define GGATE_CMD_READ 0
#define GGATE_CMD_WRITE 1
+#define GGATE_CMD_FLUSH 3
extern int g_gate_devfd;
extern int g_gate_verbose;
diff --git a/sbin/gvinum/gvinum.c b/sbin/gvinum/gvinum.c
index 50947a30a725..5a081e92a8df 100644
--- a/sbin/gvinum/gvinum.c
+++ b/sbin/gvinum/gvinum.c
@@ -575,13 +575,15 @@ find_name(const char *prefix, int type, int namelen)
char line[1024];
comment[0] = '\0';
+ buf[0] = '\0';
/* Find a name. Fetch out configuration first. */
req = gctl_get_handle();
gctl_ro_param(req, "class", -1, "VINUM");
gctl_ro_param(req, "verb", -1, "getconfig");
gctl_ro_param(req, "comment", -1, comment);
- gctl_rw_param(req, "config", sizeof(buf), buf);
+ gctl_add_param(req, "config", sizeof(buf), buf,
+ GCTL_PARAM_WR | GCTL_PARAM_ASCII);
errstr = gctl_issue(req);
if (errstr != NULL) {
warnx("can't get configuration: %s", errstr);
@@ -841,13 +843,16 @@ gvinum_list(int argc, char * const *argv)
}
+ config[0] = '\0';
+
req = gctl_get_handle();
gctl_ro_param(req, "class", -1, "VINUM");
gctl_ro_param(req, "verb", -1, "list");
gctl_ro_param(req, "cmd", -1, cmd);
gctl_ro_param(req, "argc", sizeof(int), &argc);
gctl_ro_param(req, "flags", sizeof(int), &flags);
- gctl_rw_param(req, "config", sizeof(config), config);
+ gctl_add_param(req, "config", sizeof(config), config,
+ GCTL_PARAM_WR | GCTL_PARAM_ASCII);
if (argc) {
for (i = 0; i < argc; i++) {
snprintf(buf, sizeof(buf), "argv%d", i);
@@ -1418,15 +1423,17 @@ printconfig(FILE *of, const char *comment)
const char *errstr;
time_t now;
char buf[GV_CFG_LEN + 1];
-
+
uname(&uname_s);
time(&now);
+ buf[0] = '\0';
req = gctl_get_handle();
gctl_ro_param(req, "class", -1, "VINUM");
gctl_ro_param(req, "verb", -1, "getconfig");
gctl_ro_param(req, "comment", -1, comment);
- gctl_rw_param(req, "config", sizeof(buf), buf);
+ gctl_add_param(req, "config", sizeof(buf), buf,
+ GCTL_PARAM_WR | GCTL_PARAM_ASCII);
errstr = gctl_issue(req);
if (errstr != NULL) {
warnx("can't get configuration: %s", errstr);
diff --git a/sbin/hastd/hooks.c b/sbin/hastd/hooks.c
index 0aeb1e076f7d..13890b5fcb10 100644
--- a/sbin/hastd/hooks.c
+++ b/sbin/hastd/hooks.c
@@ -312,7 +312,7 @@ hook_check(void)
}
/*
- * Skip proccesses younger than 1 minute.
+ * Skip processes younger than 1 minute.
*/
if (now - hp->hp_lastreport < REPORT_INTERVAL)
continue;
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8
index 621e22452c59..bd1a79bee897 100644
--- a/sbin/ifconfig/ifconfig.8
+++ b/sbin/ifconfig/ifconfig.8
@@ -28,7 +28,7 @@
.\" From: @(#)ifconfig.8 8.3 (Berkeley) 1/5/94
.\" $FreeBSD$
.\"
-.Dd November 8, 2021
+.Dd April 11, 2022
.Dt IFCONFIG 8
.Os
.Sh NAME
@@ -36,46 +36,40 @@
.Nd configure network interface parameters
.Sh SYNOPSIS
.Nm
-.Op Fl f Ar type Ns Cm \&: Ns Ar format Ns Op Cm \&, Ns Ar type Ns Cm \& : Ns Ar format ...
-.Op Fl L
-.Op Fl k
-.Op Fl m
-.Op Fl n
+.Op Fl kLmn
+.Op Fl f Ar type Ns Cm \&: Ns Ar format
.Ar interface
.Op Cm create
+.Oo
.Ar address_family
.Oo
.Ar address
.Op Ar dest_address
.Oc
+.Oc
.Op Ar parameters
.Nm
.Ar interface
.Cm destroy
.Nm
.Fl a
-.Op Fl L
-.Op Fl d
-.Op Fl [gG] Ar groupname
-.Op Fl m
-.Op Fl u
-.Op Fl v
+.Op Fl dkLmuv
+.Op Fl f Ar type Ns Cm \&: Ns Ar format
+.Op Fl G Ar groupname
+.Op Fl g Ar groupname
.Op Ar address_family
.Nm
-.Fl l
-.Op Fl d
-.Op Fl u
-.Op Ar address_family
+.Fl C
.Nm
-.Op Fl L
-.Op Fl d
-.Op Fl k
-.Op Fl m
-.Op Fl u
-.Op Fl v
-.Op Fl C
+.Fl g Ar groupname
.Nm
+.Fl l
+.Op Fl du
.Op Fl g Ar groupname
+.Op Ar address_family
+.Nm
+.Op Fl dkLmuv
+.Op Fl f Ar type Ns Cm \&: Ns Ar format
.Sh DESCRIPTION
The
.Nm
@@ -91,6 +85,194 @@ or other operating parameters.
.Pp
The following options are available:
.Bl -tag -width indent
+.It Fl a
+Display information about all interfaces in the system.
+.Pp
+The
+.Fl a
+flag may be used instead of the
+.Ar interface
+argument.
+.It Fl C
+List all the interface cloners available on the system,
+with no additional information.
+Use of this flag is mutually exclusive with all other flags and commands.
+.It Fl d
+Display only the interfaces that are down.
+.It Fl f Xo
+.Ar type Ns Cm \&: Ns Ar format Ns
+.Op Cm \&, Ns Ar type Ns Cm \&: Ns Ar format Ar ...
+.Xc
+Control the output format of
+.Nm .
+The format is specified as a comma-separated list of
+.Ar type Ns Cm \&: Ns Ar format
+pairs
+.Po see the
+.Sx EXAMPLES
+section for more information
+.Pc .
+.Pp
+The output format can also be specified via the
+.Ev IFCONFIG_FORMAT
+environment variable.
+The
+.Fl f
+flag can be supplied multiple times.
+.Pp
+The
+.Ar type Ns s
+and their associated
+.Ar format
+strings are:
+.Pp
+.Bl -tag -width ether
+.It Cm addr
+Adjust the display of inet and inet6 addresses:
+.Pp
+.Bl -tag -width default -compact
+.It Cm default
+Default format,
+.Cm numeric
+.It Cm fqdn
+Fully qualified domain names
+.Pq FQDN
+.It Cm host
+Unqualified hostnames
+.It Cm numeric
+Numeric format
+.El
+.It Cm ether
+Adjust the display of link-level ethernet (MAC) addresses:
+.Pp
+.Bl -tag -width default -compact
+.It Cm colon
+Separate address segments with a colon
+.It Cm dash
+Separate address segments with a dash
+.It Cm default
+Default format,
+.Cm colon
+.El
+.It Cm inet
+Adjust the display of inet address subnet masks:
+.Pp
+.Bl -tag -width default -compact
+.It Cm cidr
+CIDR notation, for example:
+.Ql 203.0.113.224/26
+.It Cm default
+Default format,
+.Cm hex
+.It Cm dotted
+Dotted quad notation, for example:
+.Ql 255.255.255.192
+.It Cm hex
+Hexadecimal format, for example:
+.Ql 0xffffffc0
+.El
+.It Cm inet6
+Adjust the display of inet6 address prefixes (subnet masks):
+.Pp
+.Bl -tag -width default -compact
+.It Cm cidr
+CIDR notation, for example:
+.Ql ::1/128
+or
+.Ql fe80::1%lo0/64
+.It Cm default
+Default format,
+.Cm numeric
+.It Cm numeric
+Integer format, for example:
+.Ql prefixlen 64
+.El
+.El
+.It Fl G Ar groupname
+Exclude members of the specified
+.Ar groupname
+from the output.
+.Ar groupname .
+.Pp
+Only one option
+.Fl G
+should be specified as later override previous ones
+.Ar groupname
+may contain shell patterns in which case it should be quoted.
+.It Fl g Ar groupname
+Limit the output to the members of the specified
+.Ar groupname .
+.Pp
+If
+.Fl g
+is specified before other significant flags like, e.g.,
+.Fl a ,
+.Fl l ,
+or
+.Fl C ,
+then
+.Nm
+lists names of interfaces beloning to
+.Ar groupname .
+Any other flags and arguments are ignored in this case.
+.Pp
+Only one option
+.Fl g
+should be specified as later override previous ones
+.Ar groupname
+may contain shell patterns in which case it should be quoted.
+.It Fl k
+Print keying information for the
+.Ar interface ,
+if available.
+.Pp
+For example, the values of 802.11 WEP keys and
+.Xr carp 4
+passphrases will be printed, if accessible to the current user.
+.Pp
+This information is not printed by default, as it may be considered
+sensitive.
+.It Fl L
+Display address lifetime for IPv6 addresses as time offset string.
+.It Fl l
+List all available interfaces on the system,
+with no other additional information.
+.Pp
+If an
+.Ar address_family
+is specified, only interfaces of that type will be listed.
+.Pp
+If the
+.Ar address_family
+is set to
+.Cm ether ,
+then
+.Fl l
+will exclude loopback interfaces from the list of Ethernet interfaces.
+This is a special case, because all the other synonyms of the
+.Cm link
+address family will include loopback interfaces in the list.
+.Pp
+Use of this flag is mutually exclusive
+with all other flags and commands, except for
+.Fl d ,
+.Fl g ,
+and
+.Fl u .
+.It Fl m
+Display the capability list and all
+of the supported media for the specified interface.
+.It Fl n
+Disable automatic loading of network interface drivers.
+.Pp
+If the network interface driver is not present in the kernel then
+.Nm
+will attempt to load it.
+This flag disables this behavior.
+.It Fl u
+Display only the interfaces that are up.
+.It Fl v
+Get more verbose status for an interface.
.It Ar address
For the DARPA-Internet family,
the address is either a host name present in the host name data
@@ -105,49 +287,32 @@ That is, one can specify an address like
.Li 192.168.0.1/16 .
.Pp
For the
-.Dq inet6
+.Cm inet6
family, it is also possible to specify the prefix length using the slash
notation, like
.Li ::1/128 .
See the
.Cm prefixlen
parameter below for more information.
-.\" For the Xerox Network Systems(tm) family,
-.\" addresses are
-.\" .Ar net:a.b.c.d.e.f ,
-.\" where
-.\" .Ar net
-.\" is the assigned network number (in decimal),
-.\" and each of the six bytes of the host number,
-.\" .Ar a
-.\" through
-.\" .Ar f ,
-.\" are specified in hexadecimal.
-.\" The host number may be omitted on IEEE 802 protocol
-.\" (Ethernet, FDDI, and Token Ring) interfaces,
-.\" which use the hardware physical address,
-.\" and on interfaces other than the first.
-.\" For the ISO family, addresses are specified as a long hexadecimal string,
-.\" as in the Xerox family.
-.\" However, two consecutive dots imply a zero
-.\" byte, and the dots are optional, if the user wishes to (carefully)
-.\" count out long strings of digits in network byte order.
.Pp
The link-level
-.Pq Dq link
+.Pq Cm link
address
is specified as a series of colon-separated hex digits.
This can be used to, for example,
set a new MAC address on an Ethernet interface, though the
mechanism used is not Ethernet specific.
+.Pp
Use the
-.Pq Dq random
+.Cm random
keyword to set a randomly generated MAC address.
A randomly-generated MAC address might be the same as one already in use
in the network.
Such duplications are extremely unlikely.
+.Pp
If the interface is already
-up when this option is used, it will be briefly brought down and
+up when the link-level address is modified,
+it will be briefly brought down and
then brought back up again in order to ensure that the receive
filter in the underlying Ethernet hardware is properly reprogrammed.
.It Ar address_family
@@ -157,32 +322,25 @@ which affects interpretation of the remaining parameters.
Since an interface can receive transmissions in differing protocols
with different naming schemes, specifying the address family is recommended.
The address or protocol families currently
-supported are
-.Dq inet ,
-.Dq inet6 ,
-and
-.Dq link .
-The default if available is
-.Dq inet
-or otherwise
-.Dq link .
-.Dq ether
-and
-.Dq lladdr
-are synonyms for
-.Dq link .
-When using the
+supported are:
+.Bl -tag
+.It Cm ether
+Synonymous with
+.Cm link
+.Po with some exceptions, see
.Fl l
-flag, the
-.Dq ether
-address family has special meaning and is no longer synonymous with
-.Dq link
-or
-.Dq lladdr .
-Specifying
-.Fl l Dq ether
-will list only Ethernet interfaces, excluding all other interface types,
-including the loopback interface.
+.Pc .
+.It Cm inet
+Default, if available.
+.It Cm inet6
+.It Cm link
+Default, if
+.Cm inet
+is not available.
+.It Cm lladdr
+Synonymous with
+.Cm link .
+.El
.It Ar dest_address
Specify the address of the correspondent on the other end
of a point to point link.
@@ -192,91 +350,25 @@ parameter is a string of the form
.Dq name unit ,
for example,
.Dq Li em0 .
-.It Ar groupname
-List the interfaces in the given group.
.El
.Pp
-The output format of
-.Nm
-can be controlled using the
-.Fl f
-flag or the
-.Ev IFCONFIG_FORMAT
-environment variable.
-The format is specified as a comma separated list of
-.Sy type:format
-pairs.
-See the
-.Sx EXAMPLES
-section for more information.
The
-.Sy types
-and their associated
-.Sy format
-strings are:
-.Bl -tag -width ether
-.It Sy addr
-Adjust the display of inet and inet6 addresses
-.Bl -tag -width default
-.It Sy default
-Display inet and inet6 addresses in the default format,
-.Sy numeric
-.It Sy fqdn
-Display inet and inet6 addresses as fully qualified domain names
-.Pq FQDN
-.It Sy host
-Display inet and inet6 addresses as unqualified hostnames
-.It Sy numeric
-Display inet and inet6 addresses in numeric format
-.El
-.It Sy ether
-Adjust the display of link-level ethernet (MAC) addresses
-.Bl -tag -width default
-.It Sy colon
-Separate address segments with a colon
-.It Sy dash
-Separate address segments with a dash
-.It Sy default
-Display ethernet addresses in the default format,
-.Sy colon
-.El
-.It Sy inet
-Adjust the display of inet address subnet masks:
-.Bl -tag -width default
-.It Sy cidr
-Display subnet masks in CIDR notation, for example:
-.br
-10.0.0.0/8 or 203.0.113.224/26
-.It Sy default
-Display subnet masks in the default format,
-.Sy hex
-.It Sy dotted
-Display subnet masks in dotted quad notation, for example:
-.br
-255.255.0.0 or 255.255.255.192
-.It Sy hex
-Display subnet masks in hexadecimal, for example:
-.br
-0xffff0000 or 0xffffffc0
-.El
-.It Sy inet6
-Adjust the display of inet6 address prefixes (subnet masks):
-.Bl -tag -width default
-.It Sy cidr
-Display subnet prefix in CIDR notation, for example:
-.br
-::1/128 or fe80::1%lo0/64
-.It Sy default
-Display subnet prefix in the default format
-.Sy numeric
-.It Sy numeric
-Display subnet prefix in integer format, for example:
-.br
-prefixlen 64
-.El
-.El
+.Nm
+utility displays the current configuration for a network interface
+when no optional parameters are supplied.
+If a protocol family is specified,
+.Nm
+will report only the details specific to that protocol family.
+.Pp
+When no arguments are given,
+.Fl a
+is implied.
.Pp
-The following parameters may be set with
+Only the super-user may modify the configuration of a network interface.
+.Sh PARAMETERS
+The following
+.Ar parameter Ns s
+may be set with
.Nm :
.Bl -tag -width indent
.It Cm add
@@ -570,7 +662,10 @@ There are three types of packets that may wake a system:
ucast (directed solely to the machine's mac address),
mcast (directed to a broadcast or multicast address),
or
-magic (unicast or multicast frames with a ``magic contents'').
+magic
+.Po unicast or multicast frames with a
+.Dq magic contents
+.Pc .
Not all devices support WOL, those that do indicate the mechanisms
they support in their capabilities.
.Cm wol
@@ -750,7 +845,7 @@ It happens automatically when setting the first address on an interface.
If the interface was reset when previously marked down,
the hardware will be re-initialized.
.El
-.Pp
+.Ss ICMPv6 Neighbor Discovery Protocol Parameters
The following parameters are for ICMPv6 Neighbor Discovery Protocol.
Note that the address family keyword
.Dq Li inet6
@@ -832,7 +927,7 @@ Set a flag to disable Duplicate Address Detection.
Clear a flag
.Cm no_dad .
.El
-.Pp
+.Ss IPv6 Parameters
The following parameters are specific for IPv6 addresses.
Note that the address family keyword
.Dq Li inet6
@@ -857,7 +952,7 @@ Clear a flag
.It Cm vltime Ar n
Set valid lifetime for the address.
.El
-.Pp
+.Ss IEEE 802.11 Wireless Interfaces Cloning Parameters
The following parameters are specific to cloning
IEEE 802.11 wireless interfaces with the
.Cm create
@@ -909,7 +1004,8 @@ the device (if supported).
.It Cm wdslegacy
Mark a
.Cm wds
-device as operating in ``legacy mode''.
+device as operating in
+.Dq legacy mode .
Legacy
.Cm wds
devices have a fixed peer relationship and do not, for example, roam
@@ -935,7 +1031,7 @@ be transmitted; this can be useful when creating a WDS configuration but
.Cm wds
interfaces can only be created as companions to an access point.
.El
-.Pp
+.Ss Cloned IEEE 802.11 Wireless Interface Parameters
The following parameters are specific to IEEE 802.11 wireless interfaces
cloned with a
.Cm create
@@ -1129,9 +1225,9 @@ Channels range from 1 to 255, but the exact selection available
depends on the region your adaptor was manufactured for.
Setting
the channel to
-.Li any ,
+.Cm any ,
or
-.Cm -
+.Dq Cm -
will clear any desired channel and, if the device is marked up,
force a scan for a channel to operate on.
Alternatively the frequency, in megahertz, may be specified
@@ -1141,42 +1237,71 @@ When there are several ways to use a channel the channel
number/frequency may be appended with attributes to clarify.
For example, if a device is capable of operating on channel 6
with 802.11n and 802.11g then one can specify that g-only use
-should be used by specifying ``6:g''.
+should be used by specifying
+.Cm 6:g .
Similarly the channel width can be specified by appending it
-with ``/''; e.g., ``6/40'' specifies a 40MHz wide channel,
-These attributes can be combined as in: ``6:ht/40''.
-The full set of flags specified following a ``:'' are:
-.Cm a
-(802.11a),
-.Cm b
-(802.11b),
-.Cm d
-(Atheros Dynamic Turbo mode),
-.Cm g
-(802.11g),
-.Cm h
-or
+with
+.Dq Cm \&/ ;
+e.g.,
+.Cm 6/40
+specifies a 40MHz wide channel.
+These attributes can be combined as in:
+.Cm 6:ht/40 .
+.Pp
+The full set of flags specified following a
+.Dq Cm \&:
+are:
+.Pp
+.Bl -tag -compact
+.It Cm a
+802.11a
+.It Cm b
+802.11b
+.It Cm d
+Atheros Dynamic Turbo mode
+.It Cm g
+802.11g
+.It Cm h
+Same as
.Cm n
-(802.11n aka HT),
-.Cm s
-(Atheros Static Turbo mode),
-and
-.Cm t
-(Atheros Dynamic Turbo mode, or appended to ``st'' and ``dt'').
-The full set of channel widths following a '/' are:
-.Cm 5
-(5MHz aka quarter-rate channel),
-.Cm 10
-(10MHz aka half-rate channel),
-.Cm 20
-(20MHz mostly for use in specifying ht20),
+.It Cm n
+802.11n aka HT
+.It Cm s
+Atheros Static Turbo mode
+.It Cm t
+Atheros Dynamic Turbo mode, or appended to
+.Cm st
and
-.Cm 40
-(40MHz mostly for use in specifying ht40).
+.Cm dt
+.El
+.Pp
+The full set of channel widths following a
+.Cm \&/
+are:
+.Pp
+.Bl -tag -compact
+.It Cm 5
+5MHz aka quarter-rate channel
+.It Cm 10
+10MHz aka half-rate channel
+.It Cm 20
+20MHz mostly for use in specifying
+.Cm ht20
+.It Cm 40
+40MHz mostly for use in specifying
+.Cm ht40
+.El
+.Pp
In addition,
a 40MHz HT channel specification may include the location
-of the extension channel by appending ``+'' or ``-'' for above and below,
-respectively; e.g., ``2437:ht/40+'' specifies 40MHz wide HT operation
+of the extension channel by appending
+.Dq Cm \&+
+or
+.Dq Cm \&-
+for above and below,
+respectively; e.g.,
+.Cm 2437:ht/40+
+specifies 40MHz wide HT operation
with the center channel at frequency 2437 and the extension channel above.
.It Cm country Ar name
Set the country code to use in calculating the regulatory constraints
@@ -1190,7 +1315,9 @@ e.g., "ES" and "Spain".
The set of country codes are taken from
.Pa /etc/regdomain.xml
and can also
-be viewed with the ``list countries'' request.
+be viewed with the
+.Cm list countries
+request.
Note that not all devices support changing the country code from a default
setting; typically stored in EEPROM.
See also
@@ -1297,7 +1424,8 @@ The value should be less than beacon interval.
Enable the use of Atheros Dynamic Turbo mode when communicating with
another Dynamic Turbo-capable station.
Dynamic Turbo mode is an Atheros-specific mechanism by which
-stations switch between normal 802.11 operation and a ``boosted''
+stations switch between normal 802.11 operation and a
+.Dq boosted
mode in which a 40MHz wide channel is used for communication.
Stations using Dynamic Turbo mode operate boosted only when the
channel is free of non-dturbo stations; when a non-dturbo station
@@ -1322,7 +1450,9 @@ DWDS extends the normal WDS mechanism by leveraging existing security
protocols and eliminating static binding.
.Pp
When DWDS is enabled on an access point 4-address frames received from
-an authorized station will generate a ``DWDS discovery'' event to user
+an authorized station will generate a
+.Dq DWDS discovery
+event to user
applications.
This event should be used to create a WDS interface that is bound
to the remote station (and usually plumbed into a bridge).
@@ -1388,7 +1518,9 @@ To disable use of HT20 (e.g., to force only HT40 use) use
To disable use of HT40 use
.Fl ht40 .
.Pp
-HT configuration is used to ``auto promote'' operation
+HT configuration is used to
+.Dq auto promote
+operation
when several choices are available.
For example, if a station associates to an 11n-capable access point
it controls whether the station uses legacy operation, HT20, or HT40.
@@ -1409,7 +1541,8 @@ for old devices are different.
When compatibility support is enabled both standard and compatible data
will be provided.
Stations that associate using the compatibility mechanisms are flagged
-in ``list sta''.
+in
+.Cm list sta .
To disable compatibility support use
.Fl htcompat .
.It Cm htprotmode Ar technique
@@ -1428,7 +1561,8 @@ access point (default).
When operating as an access point the 802.11 layer monitors
the activity of each associated station.
When a station is inactive for 5 minutes it will send several
-``probe frames'' to see if the station is still present.
+.Dq probe frames
+to see if the station is still present.
If no response is received then the station is deauthenticated.
Applications that prefer to handle this work can disable this
facility by using
@@ -1515,7 +1649,7 @@ with a
.Cm scan
request or through background scanning.
Depending on the capabilities of the stations the following
-flags can be included in the output:
+flags (capability codes) can be included in the output:
.Bl -tag -width 3n
.It Li A
Channel agility.
@@ -1527,21 +1661,33 @@ Poll request capability.
DSSS/OFDM capability.
.It Li E
Extended Service Set (ESS).
+Indicates that the station is part of an infrastructure network
+rather than an IBSS/ad-hoc network.
.It Li I
Independent Basic Service Set (IBSS).
+Indicates that the station is part of an ad-hoc network
+rather than an ESS network.
.It Li P
Privacy capability.
-The station requires authentication.
+The station requires authentication and encryption
+for all data frames exchanged within the BSS using cryptographic means
+such as WEP, TKIP, or AES-CCMP.
.It Li R
Robust Secure Network (RSN).
.It Li S
Short Preamble.
-Indicates that the station is doing short preamble to optionally
+Indicates that the network is using short preambles,
+defined in 802.11b High Rate/DSSS PHY,
+and utilizes a 56 bit sync field
+rather than the 128 bit field used in long preamble mode.
+Short preambles are used to optionally
improve throughput performance with 802.11g and 802.11b.
.It Li c
Pollable capability.
.It Li s
Short slot time capability.
+Indicates that the 802.11g network is using a short slot time
+because there are no legacy (802.11b) stations present.
.El
.Pp
By default interesting information elements captured from the neighboring
@@ -1716,7 +1862,9 @@ can be used on a channel are defined by this setting.
Regdomain codes (SKU's) are taken from
.Pa /etc/regdomain.xml
and can also
-be viewed with the ``list countries'' request.
+be viewed with the
+.Cm list countries
+request.
Note that not all devices support changing the regdomain from a default
setting; typically stored in EEPROM.
See also
@@ -1923,7 +2071,7 @@ The
.Ar power
argument is specified in .5 dBm units.
Out of range values are truncated.
-Typically only a few discreet power settings are available and
+Typically only a few discrete power settings are available and
the driver will use the setting closest to the specified value.
Not all adapters support changing the transmit power.
.It Cm ucastrate Ar rate
@@ -2082,7 +2230,7 @@ Note that WPS support requires a WPS-capable supplicant.
To disable this function use
.Fl wps .
.El
-.Pp
+.Ss MAC-Based Access Control List Parameters
The following parameters support an optional access control list
feature available with some adapters when operating in ap mode; see
.Xr wlan_acl 4 .
@@ -2120,7 +2268,7 @@ program be configured to do the right thing
as it handles the RADIUS processing
(and marks stations as authorized).
.El
-.Pp
+.Ss Mesh Mode Wireless Interface Parameters
The following parameters are related to a wireless interface operating in mesh
mode:
.Bl -tag -width indent
@@ -2130,7 +2278,9 @@ The Mesh ID is a string up to 32 characters in length.
A mesh interface must have a Mesh Identifier specified
to reach an operational state.
.It Cm meshttl Ar ttl
-Set the desired ``time to live'' for mesh forwarded packets;
+Set the desired
+.Dq time to live
+for mesh forwarded packets;
this is the number of hops a packet may be forwarded before
it is discarded.
The default setting for
@@ -2169,7 +2319,8 @@ The only available protocol at the moment is called
(Hybrid Wireless Mesh Protocol).
The mesh interface will restart after changing this setting.
.It Cm hwmprootmode Ar mode
-Stations on a mesh network can operate as ``root nodes.''
+Stations on a mesh network can operate as
+.Dq root nodes .
Root nodes try to find paths to all mesh nodes and advertise themselves
regularly.
When there is a root mesh node on a network, other mesh nodes can setup
@@ -2205,7 +2356,7 @@ The default setting for
.Cm hwmpmaxhops
is 31.
.El
-.Pp
+.Ss Compatibility Parameters
The following parameters are for compatibility with other systems:
.Bl -tag -width indent
.It Cm nwid Ar ssid
@@ -2262,7 +2413,7 @@ Included for
.Nx
compatibility.
.El
-.Pp
+.Ss Bridge Interface Parameters
The following parameters are specific to bridge interfaces:
.Bl -tag -width indent
.It Cm addm Ar interface
@@ -2467,7 +2618,7 @@ source addresses are dropped until an existing host cache entry expires or is
removed.
Set to 0 to disable.
.El
-.Pp
+.Ss Link Aggregation and Link Failover Parameters
The following parameters are specific to lagg interfaces:
.Bl -tag -width indent
.It Cm laggtype Ar type
@@ -2577,7 +2728,7 @@ Disable lacp strict compliance on the interface.
Configure a stride for an interface in round-robin mode.
The default stride is 1.
.El
-.Pp
+.Ss Generic IP Tunnel Parameters
The following parameters apply to IP tunnel interfaces,
.Xr gif 4 :
.Bl -tag -width indent
@@ -2627,7 +2778,7 @@ This is for backward compatibility with
Clear a flag
.Cm send_rev_ethip_ver .
.El
-.Pp
+.Ss GRE Tunnel Parameters
The following parameters apply to GRE tunnel interfaces,
.Xr gre 4 :
.Bl -tag -width indent
@@ -2654,7 +2805,7 @@ Note that
.Xr gre 4 will always accept GRE packets with invalid or absent keys.
This command will result in a four byte MTU reduction on the interface.
.El
-.Pp
+.Ss Packet Filter State Table Sychronisation Parameters
The following parameters are specific to
.Xr pfsync 4
interfaces:
@@ -2682,7 +2833,7 @@ acknowledged that the associated state has been inserted.
Do not defer the first packet in a state.
This is the default.
.El
-.Pp
+.Ss VLAN Parameters
The following parameters are specific to
.Xr vlan 4
interfaces:
@@ -2794,7 +2945,7 @@ The
.Ar iface
argument is useless and hence deprecated.
.El
-.Pp
+.Ss Virtual eXtensible LAN Parameters
The following parameters are used to configure
.Xr vxlan 4
interfaces.
@@ -2864,7 +3015,7 @@ Delete all dynamically-learned addresses from the forwarding table.
.It Cm vxlanflushall
Delete all addresses, including static addresses, from the forwarding table.
.El
-.Pp
+.Ss CARP Parameters
The following parameters are used to configure
.Xr carp 4
protocol on an interface:
@@ -2904,105 +3055,23 @@ The default value is 0.
.It Cm pass Ar phrase
Set the authentication key to
.Ar phrase .
-.It Cm state Ar MASTER|BACKUP
+.It Cm state Ar state
Forcibly change state of a given vhid.
-.El
-.Pp
-The
-.Nm
-utility displays the current configuration for a network interface
-when no optional parameters are supplied.
-If a protocol family is specified,
-.Nm
-will report only the details specific to that protocol family.
-.Pp
-If the
-.Fl m
-flag is passed before an interface name,
-.Nm
-will display the capability list and all
-of the supported media for the specified interface.
-If
-.Fl L
-flag is supplied, address lifetime is displayed for IPv6 addresses,
-as time offset string.
-.Pp
-Optionally, the
-.Fl a
-flag may be used instead of an interface name.
-This flag instructs
-.Nm
-to display information about all interfaces in the system.
-The
-.Fl d
-flag limits this to interfaces that are down,
-.Fl u
-limits this to interfaces that are up,
-.Fl g
-limits this to members of the specified group of interfaces, and
-.Fl G
-excludes members of the specified group from the list.
-Both
-.Fl g
-and
-.Fl G
-flags may be specified to apply both conditions.
-Only one option
-.Fl g
-should be specified as later override previous ones
-(same for
-.Fl G ) .
-.Sy groupname
-may contain shell patterns in which case it should be quoted.
-When no arguments are given,
-.Fl a
-is implied.
-.Pp
-The
-.Fl l
-flag may be used to list all available interfaces on the system, with
-no other additional information.
-If an
-.Ar address_family
-is specified, only interfaces of that type will be listed.
-.Fl l Dq ether
-will list only Ethernet adapters, excluding the loopback interface.
-Use of this flag is mutually exclusive
-with all other flags and commands, except for
-.Fl d
-(only list interfaces that are down)
+The following states are recognized:
+.Cm MASTER
and
-.Fl u
-(only list interfaces that are up).
-.Pp
-The
-.Fl v
-flag may be used to get more verbose status for an interface.
-.Pp
-The
-.Fl C
-flag may be used to list all of the interface cloners available on
-the system, with no additional information.
-Use of this flag is mutually exclusive with all other flags and commands.
-.Pp
-The
-.Fl k
-flag causes keying information for the interface, if available, to be
-printed.
-For example, the values of 802.11 WEP keys and
-.Xr carp 4
-passphrases will be printed, if accessible to the current user.
-This information is not printed by default, as it may be considered
-sensitive.
-.Pp
-If the network interface driver is not present in the kernel then
-.Nm
-will attempt to load it.
-The
-.Fl n
-flag disables this behavior.
-.Pp
-Only the super-user may modify the configuration of a network interface.
+.Cm BACKUP .
+.El
+.Sh ENVIRONMENT
+The following environment variables affect the execution of
+.Nm :
+.Bl -tag -width IFCONFIG_FORMAT
+.It Ev IFCONFIG_FORMAT
+This variable can contain a specification of the output format.
+See the description of the
+.Fl f
+flag for more details.
+.El
.Sh EXAMPLES
Assign the IPv4 address
.Li 192.0.2.10 ,
@@ -3017,12 +3086,8 @@ Add the IPv4 address
with the CIDR network prefix
.Li /28 ,
to the interface
-.Li em0 ,
-using
-.Cm add
-as a synonym for the canonical form of the option
-.Cm alias :
-.Dl # ifconfig em0 inet 192.0.2.45/28 add
+.Li em0 :
+.Dl # ifconfig em0 inet 192.0.2.45/28 alias
.Pp
Remove the IPv4 address
.Li 192.0.2.45
@@ -3043,17 +3108,15 @@ Note that lower case hexadecimal IPv6 addresses are acceptable.
Remove the IPv6 address added in the above example,
using the
.Li /
-character as shorthand for the network prefix,
-and using
-.Cm delete
-as a synonym for the canonical form of the option
-.Fl alias :
-.Dl # ifconfig em0 inet6 2001:db8:bdbd::123/48 delete
+character as shorthand for the network prefix:
+.Dl # ifconfig em0 inet6 2001:db8:bdbd::123/48 -alias
.Pp
Configure a single CARP redundant address on igb0, and then switch it
to be master:
-.Dl # ifconfig igb0 vhid 1 10.0.0.1/24 pass foobar up
-.Dl # ifconfig igb0 vhid 1 state master
+.Bd -literal -offset indent -compact
+# ifconfig igb0 vhid 1 10.0.0.1/24 pass foobar up
+# ifconfig igb0 vhid 1 state master
+.Ed
.Pp
Configure the interface
.Li xl0 ,
@@ -3080,6 +3143,46 @@ Display inet and inet6 address subnet masks in CIDR notation
.Pp
Display interfaces that are up with the exception of loopback
.Dl # ifconfig -a -u -G lo
+.Pp
+Display a list of interface names beloning to the wlan group:
+.Bd -literal -offset indent -compact
+# ifconfig -g wlan
+wlan0
+wlan1
+.Ed
+.Pp
+Display details about the interfaces belonging to the wlan group:
+.Bd -literal -offset indent -compact
+# ifconfig -a -g wlan
+wlan0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
+ ether 75:4c:61:6b:7a:73
+ inet6 fe80::4c75:636a:616e:ffd8%wlan0 prefixlen 64 scopeid 0x3
+ inet6 2001:5761:6e64:6152:6f6d:616e:fea4:ffe2 prefixlen 64 autoconf
+ inet 192.168.10.5 netmask 0xffffff00 broadcast 192.168.10.255
+ groups: wlan
+ ssid "Hotspot" channel 11 (2462 MHz 11g) bssid 12:34:ff:ff:43:21
+ regdomain ETSI country DE authmode WPA2/802.11i privacy ON
+ deftxkey UNDEF AES-CCM 2:128-bit AES-CCM 3:128-bit txpower 30 bmiss 10
+ scanvalid 60 protmode CTS wme roaming MANUAL
+ parent interface: iwm0
+ media: IEEE 802.11 Wireless Ethernet DS/2Mbps mode 11g
+ status: associated
+ nd6 options=23<PERFORMNUD,ACCEPT_RTADV,AUTO_LINKLOCAL>
+wlan1: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
+ ether 00:50:69:6f:74:72
+ groups: wlan
+ ssid "" channel 2 (2417 MHz 11g)
+ regdomain FCC country US authmode OPEN privacy OFF txpower 30 bmiss 7
+ scanvalid 60 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7
+ roam:rate 5 protmode CTS wme bintval 0
+ parent interface: rum0
+ media: IEEE 802.11 Wireless Ethernet autoselect (autoselect)
+ status: no carrier
+ nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
+.Ed
+.Pp
+Set a randomly-generated MAC address on tap0:
+.Dl # ifconfig tap0 ether random
.Sh DIAGNOSTICS
Messages indicating the specified interface does not exist, the
requested address is unknown, or the user is not privileged and
@@ -3094,7 +3197,6 @@ tried to alter an interface's configuration.
.Xr vlan 4 ,
.Xr vxlan 4 ,
.Xr devd.conf 5 ,
-.\" .Xr eon 5 ,
.Xr devd 8 ,
.Xr jail 8 ,
.Xr rc 8 ,
diff --git a/sbin/ifconfig/ifpfsync.c b/sbin/ifconfig/ifpfsync.c
index 60e3d4360aa3..e3b5e6a30bbc 100644
--- a/sbin/ifconfig/ifpfsync.c
+++ b/sbin/ifconfig/ifpfsync.c
@@ -178,7 +178,7 @@ setpfsync_defer(const char *val, int d, int s, const struct afswtch *rafp)
if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1)
err(1, "SIOCGETPFSYNC");
- preq.pfsyncr_defer = d;
+ preq.pfsyncr_defer = d ? PFSYNCF_DEFER : 0;
if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1)
err(1, "SIOCSETPFSYNC");
}
diff --git a/sbin/ifconfig/ifvlan.c b/sbin/ifconfig/ifvlan.c
index 60f97338ee27..8b7b6e9daf9a 100644
--- a/sbin/ifconfig/ifvlan.c
+++ b/sbin/ifconfig/ifvlan.c
@@ -127,11 +127,6 @@ vlan_parse_ethervid(const char *name)
if ((cp = strrchr(ifname, '.')) == NULL)
return;
/*
- * Don't mix vlan/vlandev parameters with dot notation.
- */
- if (params.vlr_tag != NOTAG || params.vlr_parent[0] != '\0')
- errx(1, "ambiguous vlan specification");
- /*
* Derive params from interface name: "parent.vid".
*/
*cp++ = '\0';
@@ -144,8 +139,19 @@ vlan_parse_ethervid(const char *name)
if ((*cp != '\0') || (vid & ~0xFFF))
errx(1, "invalid vlan tag");
- strlcpy(params.vlr_parent, ifname, IFNAMSIZ);
- params.vlr_tag = (vid & 0xFFF);
+ /*
+ * allow "devX.Y vlandev devX vlan Y" syntax
+ */
+ if (params.vlr_tag == NOTAG || params.vlr_tag == vid)
+ params.vlr_tag = vid;
+ else
+ errx(1, "ambiguous vlan specification");
+
+ /* Restrict overriding interface name */
+ if (params.vlr_parent[0] == '\0' || !strcmp(params.vlr_parent, ifname))
+ strlcpy(params.vlr_parent, ifname, IFNAMSIZ);
+ else
+ errx(1, "ambiguous vlan specification");
}
static void
diff --git a/sbin/init/init.c b/sbin/init/init.c
index 230c141bd351..813387f63c81 100644
--- a/sbin/init/init.c
+++ b/sbin/init/init.c
@@ -47,21 +47,26 @@ static const char rcsid[] =
#endif /* not lint */
#include <sys/param.h>
+#include <sys/boottrace.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/mount.h>
-#include <sys/sysctl.h>
-#include <sys/wait.h>
+#include <sys/reboot.h>
#include <sys/stat.h>
+#include <sys/sysctl.h>
#include <sys/uio.h>
+#include <sys/wait.h>
#include <db.h>
+#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <kenv.h>
#include <libutil.h>
#include <paths.h>
#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -69,10 +74,6 @@ static const char rcsid[] =
#include <time.h>
#include <ttyent.h>
#include <unistd.h>
-#include <sys/reboot.h>
-#include <err.h>
-
-#include <stdarg.h>
#ifdef SECURE
#include <pwd.h>
@@ -133,13 +134,11 @@ static state_func_t reroot_phase_two(void);
static state_func_t run_script(const char *);
static enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT;
-#define FALSE 0
-#define TRUE 1
-static int Reboot = FALSE;
+static bool Reboot = false;
static int howto = RB_AUTOBOOT;
-static int devfs;
+static bool devfs = false;
static char *init_path_argv0;
static void transition(state_t);
@@ -189,7 +188,7 @@ static int setupargv(session_t *, struct ttyent *);
#ifdef LOGIN_CAP
static void setprocresources(const char *);
#endif
-static int clang;
+static bool clang;
static int start_session_db(void);
static void add_session(session_t *);
@@ -213,6 +212,8 @@ main(int argc, char *argv[])
if (getuid() != 0)
errx(1, "%s", strerror(EPERM));
+ BOOTTRACE("init(8) starting...");
+
/* System V users like to reexec init. */
if (getpid() != 1) {
#ifdef COMPAT_SYSV_INIT
@@ -284,7 +285,7 @@ invalid:
while ((c = getopt(argc, argv, "dsfr")) != -1)
switch (c) {
case 'd':
- devfs = 1;
+ devfs = true;
break;
case 's':
initial_transition = single_user;
@@ -361,7 +362,7 @@ invalid:
if (stat("/dev", &stst) != 0)
warning("Can't stat /dev: %m");
else if (stst.st_dev == root_devno)
- devfs++;
+ devfs = true;
}
if (devfs) {
@@ -876,6 +877,7 @@ single_user(void)
if (Reboot) {
/* Instead of going single user, let's reboot the machine */
+ BOOTTRACE("shutting down the system");
sync();
/* Run scripts after all processes have been terminated. */
runfinal();
@@ -887,6 +889,7 @@ single_user(void)
_exit(0); /* panic as well */
}
+ BOOTTRACE("going to single user mode");
shell = get_shell();
if ((pid = fork()) == 0) {
@@ -1028,8 +1031,10 @@ runcom(void)
{
state_func_t next_transition;
+ BOOTTRACE("/etc/rc starting...");
if ((next_transition = run_script(_PATH_RUNCOM)) != NULL)
return next_transition;
+ BOOTTRACE("/etc/rc finished");
runcom_mode = AUTOBOOT; /* the default */
return (state_func_t) read_ttys;
@@ -1598,6 +1603,59 @@ collect_child(pid_t pid)
add_session(sp);
}
+static const char *
+get_current_state(void)
+{
+
+ if (current_state == single_user)
+ return ("single-user");
+ if (current_state == runcom)
+ return ("runcom");
+ if (current_state == read_ttys)
+ return ("read-ttys");
+ if (current_state == multi_user)
+ return ("multi-user");
+ if (current_state == clean_ttys)
+ return ("clean-ttys");
+ if (current_state == catatonia)
+ return ("catatonia");
+ if (current_state == death)
+ return ("death");
+ if (current_state == death_single)
+ return ("death-single");
+ return ("unknown");
+}
+
+static void
+boottrace_transition(int sig)
+{
+ const char *action;
+
+ switch (sig) {
+ case SIGUSR2:
+ action = "halt & poweroff";
+ break;
+ case SIGUSR1:
+ action = "halt";
+ break;
+ case SIGINT:
+ action = "reboot";
+ break;
+ case SIGWINCH:
+ action = "powercycle";
+ break;
+ case SIGTERM:
+ action = Reboot ? "reboot" : "single-user";
+ break;
+ default:
+ BOOTTRACE("signal %d from %s", sig, get_current_state());
+ return;
+ }
+
+ /* Trace the shutdown reason. */
+ SHUTTRACE("%s from %s", action, get_current_state());
+}
+
/*
* Catch a signal and request a state transition.
*/
@@ -1605,6 +1663,7 @@ static void
transition_handler(int sig)
{
+ boottrace_transition(sig);
switch (sig) {
case SIGHUP:
if (current_state == read_ttys || current_state == multi_user ||
@@ -1619,7 +1678,7 @@ transition_handler(int sig)
case SIGINT:
if (sig == SIGWINCH)
howto |= RB_POWERCYCLE;
- Reboot = TRUE;
+ Reboot = true;
case SIGTERM:
if (current_state == read_ttys || current_state == multi_user ||
current_state == clean_ttys || current_state == catatonia)
@@ -1648,6 +1707,7 @@ transition_handler(int sig)
static state_func_t
multi_user(void)
{
+ static bool inmultiuser = false;
pid_t pid;
session_t *sp;
@@ -1677,6 +1737,11 @@ multi_user(void)
add_session(sp);
}
+ if (requested_transition == 0 && !inmultiuser) {
+ inmultiuser = true;
+ /* This marks the change from boot-time tracing to run-time. */
+ RUNTRACE("multi-user start");
+ }
while (!requested_transition)
if ((pid = waitpid(-1, (int *) 0, 0)) != -1)
collect_child(pid);
@@ -1792,7 +1857,7 @@ alrm_handler(int sig)
{
(void)sig;
- clang = 1;
+ clang = true;
}
/*
@@ -1843,16 +1908,17 @@ death_single(void)
revoke(_PATH_CONSOLE);
+ BOOTTRACE("start killing user processes");
for (i = 0; i < 2; ++i) {
if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH)
return (state_func_t) single_user;
- clang = 0;
+ clang = false;
alarm(DEATH_WATCH);
do
if ((pid = waitpid(-1, (int *)0, 0)) != -1)
collect_child(pid);
- while (clang == 0 && errno != ECHILD);
+ while (!clang && errno != ECHILD);
if (errno == ECHILD)
return (state_func_t) single_user;
@@ -1894,6 +1960,8 @@ runshutdown(void)
char *argv[4];
struct stat sb;
+ BOOTTRACE("init(8): start rc.shutdown");
+
/*
* rc.shutdown is optional, so to prevent any unnecessary
* complaints from the shell we simply don't run it if the
@@ -1931,7 +1999,7 @@ runshutdown(void)
NULL, 0) == -1 || shutdowntimeout < 2)
shutdowntimeout = DEATH_SCRIPT;
alarm(shutdowntimeout);
- clang = 0;
+ clang = false;
/*
* Copied from single_user(). This is a bit paranoid.
* Use the same ALRM handler.
@@ -1939,11 +2007,13 @@ runshutdown(void)
do {
if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1)
collect_child(wpid);
- if (clang == 1) {
+ if (clang) {
/* we were waiting for the sub-shell */
kill(wpid, SIGTERM);
warning("timeout expired for %s: %m; going to "
"single user mode", _PATH_RUNDOWN);
+ BOOTTRACE("rc.shutdown's %d sec timeout expired",
+ shutdowntimeout);
return -1;
}
if (wpid == -1) {
diff --git a/sbin/ipf/Makefile b/sbin/ipf/Makefile
index 6126564e9821..075119abd542 100644
--- a/sbin/ipf/Makefile
+++ b/sbin/ipf/Makefile
@@ -3,7 +3,7 @@
SUBDIR= libipf .WAIT
SUBDIR+= ipf ipfs ipfstat ipmon ipnat ippool
# XXX Temporarily disconnected.
-# SUBDIR+= ipftest ipresend
+# SUBDIR+= ipftest ipresend ipsend
SUBDIR_PARALLEL=
.include <bsd.subdir.mk>
diff --git a/sbin/ipf/Makefile.inc b/sbin/ipf/Makefile.inc
index a38edf3481db..1f256a343b9a 100644
--- a/sbin/ipf/Makefile.inc
+++ b/sbin/ipf/Makefile.inc
@@ -6,10 +6,9 @@ WARNS?= 2
NO_WFORMAT=
NO_WARRAY_BOUNDS=
-CFLAGS+= -I${SRCTOP}/contrib/ipfilter
-CFLAGS+= -I${SRCTOP}/contrib/ipfilter/tools
CFLAGS+= -I${SRCTOP}/sys
-CFLAGS+= -I${SRCTOP}/sys/contrib/ipfilter
+CFLAGS+= -I${SRCTOP}/sys/netpfil/ipfilter
+CFLAGS+= -I${SRCTOP}/sbin/ipf/common
CFLAGS+= -DSTATETOP -D__UIO_EXPOSE
.if ${MK_INET6_SUPPORT} != "no"
@@ -24,9 +23,7 @@ LIBADD+= ipf
CLEANFILES+= y.tab.c y.tab.h
-.PATH: ${SRCTOP}/contrib/ipfilter \
- ${SRCTOP}/contrib/ipfilter/lib \
- ${SRCTOP}/contrib/ipfilter/tools \
- ${SRCTOP}/contrib/ipfilter/man
+.PATH: ${SRCTOP}/sbin/ipf/libipf \
+ ${SRCTOP}/sbin/ipf/common
.include "../Makefile.inc"
diff --git a/sbin/ipf/common/genmask.c b/sbin/ipf/common/genmask.c
new file mode 100644
index 000000000000..5b715cf4c901
--- /dev/null
+++ b/sbin/ipf/common/genmask.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2012 by Darren Reed.
+ *
+ * See the IPFILTER.LICENCE file for details on licencing.
+ *
+ * $Id$
+ */
+
+#include "ipf.h"
+
+
+int genmask(family, msk, mskp)
+ int family;
+ char *msk;
+ i6addr_t *mskp;
+{
+ char *endptr = 0L;
+ u_32_t addr;
+ int bits;
+
+ if (strchr(msk, '.') || strchr(msk, 'x') || strchr(msk, ':')) {
+ /* possibly of the form xxx.xxx.xxx.xxx
+ * or 0xYYYYYYYY */
+ switch (family)
+ {
+#ifdef USE_INET6
+ case AF_INET6 :
+ if (inet_pton(AF_INET6, msk, &mskp->in4) != 1)
+ return (-1);
+ break;
+#endif
+ case AF_INET :
+ if (inet_aton(msk, &mskp->in4) == 0)
+ return (-1);
+ break;
+ default :
+ return (-1);
+ /*NOTREACHED*/
+ }
+ } else {
+ /*
+ * set x most significant bits
+ */
+ bits = (int)strtol(msk, &endptr, 0);
+
+ switch (family)
+ {
+ case AF_INET6 :
+ if ((*endptr != '\0') || (bits < 0) || (bits > 128))
+ return (-1);
+ fill6bits(bits, mskp->i6);
+ break;
+ case AF_INET :
+ if (*endptr != '\0' || bits > 32 || bits < 0)
+ return (-1);
+ if (bits == 0)
+ addr = 0;
+ else
+ addr = htonl(0xffffffff << (32 - bits));
+ mskp->in4.s_addr = addr;
+ break;
+ default :
+ return (-1);
+ /*NOTREACHED*/
+ }
+ }
+ return (0);
+}
diff --git a/sbin/ipf/common/ipf.h b/sbin/ipf/common/ipf.h
new file mode 100644
index 000000000000..b278d8ec5d6c
--- /dev/null
+++ b/sbin/ipf/common/ipf.h
@@ -0,0 +1,378 @@
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2012 by Darren Reed.
+ *
+ * See the IPFILTER.LICENCE file for details on licencing.
+ *
+ * @(#)ipf.h 1.12 6/5/96
+ * $Id$
+ */
+
+#ifndef __IPF_H__
+#define __IPF_H__
+
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/file.h>
+/*
+ * This is a workaround for <sys/uio.h> troubles on FreeBSD, HPUX, OpenBSD.
+ * Needed here because on some systems <sys/uio.h> gets included by things
+ * like <sys/socket.h>
+ */
+#ifndef _KERNEL
+# define ADD_KERNEL
+# define _KERNEL
+# define KERNEL
+#endif
+#include <sys/uio.h>
+#ifdef ADD_KERNEL
+# undef _KERNEL
+# undef KERNEL
+#endif
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+# include <netinet/tcp.h>
+#include <netinet/udp.h>
+
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#if !defined(__SVR4) && !defined(__svr4__) && defined(sun)
+# include <strings.h>
+#endif
+#include <string.h>
+#include <unistd.h>
+
+#include "netinet/ip_compat.h"
+#include "netinet/ip_fil.h"
+#include "netinet/ip_nat.h"
+#include "netinet/ip_frag.h"
+#include "netinet/ip_state.h"
+#include "netinet/ip_proxy.h"
+#include "netinet/ip_auth.h"
+#include "netinet/ip_lookup.h"
+#include "netinet/ip_pool.h"
+#include "netinet/ip_scan.h"
+#include "netinet/ip_htable.h"
+#include "netinet/ip_sync.h"
+#include "netinet/ip_dstlist.h"
+
+#include "opts.h"
+
+#ifndef __P
+# define __P(x) x
+#endif
+
+#ifndef U_32_T
+# define U_32_T 1
+# if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || \
+ defined(__sgi)
+typedef u_int32_t u_32_t;
+# else
+# if defined(__alpha__) || defined(__alpha) || defined(_LP64)
+typedef unsigned int u_32_t;
+# else
+# if SOLARIS2 >= 6
+typedef uint32_t u_32_t;
+# else
+typedef unsigned int u_32_t;
+# endif
+# endif
+# endif /* __NetBSD__ || __OpenBSD__ || __FreeBSD__ || __sgi */
+#endif /* U_32_T */
+
+#ifndef MAXHOSTNAMELEN
+# define MAXHOSTNAMELEN 256
+#endif
+
+#define MAX_ICMPCODE 16
+#define MAX_ICMPTYPE 19
+
+#define PRINTF (void)printf
+#define FPRINTF (void)fprintf
+#define FORMAT_IF(_a) (_a != NULL ? _a : "(null)")
+
+
+struct ipopt_names {
+ int on_value;
+ int on_bit;
+ int on_siz;
+ char *on_name;
+};
+
+
+typedef struct alist_s {
+ struct alist_s *al_next;
+ int al_not;
+ int al_family;
+ i6addr_t al_i6addr;
+ i6addr_t al_i6mask;
+} alist_t;
+
+#define al_addr al_i6addr.in4_addr
+#define al_mask al_i6mask.in4_addr
+#define al_1 al_addr
+#define al_2 al_mask
+
+
+typedef struct plist_s {
+ struct plist_s *pl_next;
+ int pl_compare;
+ u_short pl_port1;
+ u_short pl_port2;
+} plist_t;
+
+
+typedef struct {
+ u_short fb_c;
+ u_char fb_t;
+ u_char fb_f;
+ u_32_t fb_k;
+} fakebpf_t;
+
+
+typedef struct {
+ char *it_name;
+ int it_v4;
+ int it_v6;
+} icmptype_t;
+
+
+typedef struct wordtab {
+ char *w_word;
+ int w_value;
+} wordtab_t;
+
+
+typedef struct namelist {
+ struct namelist *na_next;
+ char *na_name;
+ int na_value;
+} namelist_t;
+
+
+typedef struct proxyrule {
+ struct proxyrule *pr_next;
+ char *pr_proxy;
+ char *pr_conf;
+ namelist_t *pr_names;
+ int pr_proto;
+} proxyrule_t;
+
+
+#if defined(__NetBSD__) || defined(__FreeBSD__) || \
+ SOLARIS
+# include <stdarg.h>
+typedef int (* ioctlfunc_t)(int, ioctlcmd_t, ...);
+#else
+typedef int (* ioctlfunc_t)(dev_t, ioctlcmd_t, void *);
+#endif
+typedef int (* addfunc_t)(int, ioctlfunc_t, void *);
+typedef int (* copyfunc_t)(void *, void *, size_t);
+
+
+extern char thishost[MAXHOSTNAMELEN];
+extern char flagset[];
+extern u_char flags[];
+extern struct ipopt_names ionames[];
+extern struct ipopt_names secclass[];
+extern char *icmpcodes[MAX_ICMPCODE + 1];
+extern char *icmptypes[MAX_ICMPTYPE + 1];
+extern int use_inet6;
+extern int lineNum;
+extern int debuglevel;
+extern struct ipopt_names v6ionames[];
+extern icmptype_t icmptypelist[];
+extern wordtab_t statefields[];
+extern wordtab_t natfields[];
+extern wordtab_t poolfields[];
+
+
+extern int addicmp(char ***, struct frentry *, int);
+extern int addipopt(char *, struct ipopt_names *, int, char *);
+extern int addkeep(char ***, struct frentry *, int);
+extern alist_t *alist_new(int, char *);
+extern void alist_free(alist_t *);
+extern void assigndefined(char *);
+extern void binprint(void *, size_t);
+extern u_32_t buildopts(char *, char *, int);
+extern int checkrev(char *);
+extern int connecttcp(char *, int);
+extern int count6bits(u_32_t *);
+extern int count4bits(u_32_t);
+extern char *fac_toname(int);
+extern int fac_findname(char *);
+extern const char *familyname(const int);
+extern void fill6bits(int, u_int *);
+extern wordtab_t *findword(wordtab_t *, char *);
+extern int ftov(int);
+extern char *ipf_geterror(int, ioctlfunc_t *);
+extern int genmask(int, char *, i6addr_t *);
+extern int gethost(int, char *, i6addr_t *);
+extern int geticmptype(int, char *);
+extern int getport(struct frentry *, char *, u_short *, char *);
+extern int getportproto(char *, int);
+extern int getproto(char *);
+extern char *getnattype(struct nat *);
+extern char *getsumd(u_32_t);
+extern u_32_t getoptbyname(char *);
+extern u_32_t getoptbyvalue(int);
+extern u_32_t getv6optbyname(char *);
+extern u_32_t getv6optbyvalue(int);
+extern char *icmptypename(int, int);
+extern void initparse(void);
+extern void ipf_dotuning(int, char *, ioctlfunc_t);
+extern int ipf_addrule(int, ioctlfunc_t, void *);
+extern void ipf_mutex_clean(void);
+extern int ipf_parsefile(int, addfunc_t, ioctlfunc_t *, char *);
+extern int ipf_parsesome(int, addfunc_t, ioctlfunc_t *, FILE *);
+extern void ipf_perror(int, char *);
+extern int ipf_perror_fd( int, ioctlfunc_t, char *);
+extern void ipf_rwlock_clean(void);
+extern char *ipf_strerror(int);
+extern void ipferror(int, char *);
+extern int ipmon_parsefile(char *);
+extern int ipmon_parsesome(FILE *);
+extern int ipnat_addrule(int, ioctlfunc_t, void *);
+extern int ipnat_parsefile(int, addfunc_t, ioctlfunc_t, char *);
+extern int ipnat_parsesome(int, addfunc_t, ioctlfunc_t, FILE *);
+extern int ippool_parsefile(int, char *, ioctlfunc_t);
+extern int ippool_parsesome(int, FILE *, ioctlfunc_t);
+extern int kmemcpywrap(void *, void *, size_t);
+extern char *kvatoname(ipfunc_t, ioctlfunc_t);
+extern int load_dstlist(struct ippool_dst *, ioctlfunc_t,
+ ipf_dstnode_t *);
+extern int load_dstlistnode(int, char *, struct ipf_dstnode *,
+ ioctlfunc_t);
+extern alist_t *load_file(char *);
+extern int load_hash(struct iphtable_s *, struct iphtent_s *,
+ ioctlfunc_t);
+extern int load_hashnode(int, char *, struct iphtent_s *, int,
+ ioctlfunc_t);
+extern alist_t *load_http(char *);
+extern int load_pool(struct ip_pool_s *list, ioctlfunc_t);
+extern int load_poolnode(int, char *, ip_pool_node_t *, int, ioctlfunc_t);
+extern alist_t *load_url(char *);
+extern alist_t *make_range(int, struct in_addr, struct in_addr);
+extern void mb_hexdump(mb_t *, FILE *);
+extern ipfunc_t nametokva(char *, ioctlfunc_t);
+extern void nat_setgroupmap(struct ipnat *);
+extern int ntomask(int, int, u_32_t *);
+extern u_32_t optname(char ***, u_short *, int);
+extern wordtab_t *parsefields(wordtab_t *, char *);
+extern int *parseipfexpr(char *, char **);
+extern int parsewhoisline(char *, addrfamily_t *, addrfamily_t *);
+extern void pool_close(void);
+extern int pool_fd(void);
+extern int pool_ioctl(ioctlfunc_t, ioctlcmd_t, void *);
+extern int pool_open(void);
+extern char *portname(int, int);
+extern int pri_findname(char *);
+extern char *pri_toname(int);
+extern void print_toif(int, char *, char *, struct frdest *);
+extern void printaps(ap_session_t *, int, int);
+extern void printaddr(int, int, char *, int, u_32_t *, u_32_t *);
+extern void printbuf(char *, int, int);
+extern void printfieldhdr(wordtab_t *, wordtab_t *);
+extern void printfr(struct frentry *, ioctlfunc_t);
+extern struct iphtable_s *printhash(struct iphtable_s *, copyfunc_t,
+ char *, int, wordtab_t *);
+extern struct iphtable_s *printhash_live(iphtable_t *, int, char *,
+ int, wordtab_t *);
+extern ippool_dst_t *printdstl_live(ippool_dst_t *, int, char *,
+ int, wordtab_t *);
+extern void printhashdata(iphtable_t *, int);
+extern struct iphtent_s *printhashnode(struct iphtable_s *,
+ struct iphtent_s *,
+ copyfunc_t, int, wordtab_t *);
+extern void printhost(int, u_32_t *);
+extern void printhostmask(int, u_32_t *, u_32_t *);
+extern void printip(int, u_32_t *);
+extern void printlog(struct frentry *);
+extern void printlookup(char *, i6addr_t *addr, i6addr_t *mask);
+extern void printmask(int, u_32_t *);
+extern void printnataddr(int, char *, nat_addr_t *, int);
+extern void printnatfield(nat_t *, int);
+extern void printnatside(char *, nat_stat_side_t *);
+extern void printpacket(int, mb_t *);
+extern void printpacket6(int, mb_t *);
+extern struct ippool_dst *printdstlist(struct ippool_dst *, copyfunc_t,
+ char *, int, ipf_dstnode_t *,
+ wordtab_t *);
+extern void printdstlistdata(ippool_dst_t *, int);
+extern ipf_dstnode_t *printdstlistnode(ipf_dstnode_t *, copyfunc_t,
+ int, wordtab_t *);
+extern void printdstlistpolicy(ippool_policy_t);
+extern struct ip_pool_s *printpool(struct ip_pool_s *, copyfunc_t,
+ char *, int, wordtab_t *);
+extern struct ip_pool_s *printpool_live(struct ip_pool_s *, int,
+ char *, int, wordtab_t *);
+extern void printpooldata(ip_pool_t *, int);
+extern void printpoolfield(void *, int, int);
+extern struct ip_pool_node *printpoolnode(struct ip_pool_node *,
+ int, wordtab_t *);
+extern void printproto(struct protoent *, int, struct ipnat *);
+extern void printportcmp(int, struct frpcmp *);
+extern void printstatefield(ipstate_t *, int);
+extern void printtqtable(ipftq_t *);
+extern void printtunable(ipftune_t *);
+extern void printunit(int);
+extern void optprint(u_short *, u_long, u_long);
+#ifdef USE_INET6
+extern void optprintv6(u_short *, u_long, u_long);
+#endif
+extern int remove_hash(struct iphtable_s *, ioctlfunc_t);
+extern int remove_hashnode(int, char *, struct iphtent_s *, ioctlfunc_t);
+extern int remove_pool(ip_pool_t *, ioctlfunc_t);
+extern int remove_poolnode(int, char *, ip_pool_node_t *, ioctlfunc_t);
+extern u_char tcpflags(char *);
+extern void printc(struct frentry *);
+extern void printC(int);
+extern void emit(int, int, void *, struct frentry *);
+extern u_char secbit(int);
+extern u_char seclevel(char *);
+extern void printfraginfo(char *, struct ipfr *);
+extern void printifname(char *, char *, void *);
+extern char *hostname(int, void *);
+extern struct ipstate *printstate(struct ipstate *, int, u_long);
+extern void printsbuf(char *);
+extern void printnat(struct ipnat *, int);
+extern void printactiveaddress(int, char *, i6addr_t *, char *);
+extern void printactivenat(struct nat *, int, u_long);
+extern void printhostmap(struct hostmap *, u_int);
+extern void printtcpflags(u_32_t, u_32_t);
+extern void printipfexpr(int *);
+extern void printstatefield(ipstate_t *, int);
+extern void printstatefieldhdr(int);
+extern int sendtrap_v1_0(int, char *, char *, int, time_t);
+extern int sendtrap_v2_0(int, char *, char *, int);
+extern int vtof(int);
+
+extern void set_variable(char *, char *);
+extern char *get_variable(char *, char **, int);
+extern void resetlexer(void);
+
+extern void debug(int, char *, ...);
+extern void verbose(int, char *, ...);
+extern void ipfkdebug(char *, ...);
+extern void ipfkverbose(char *, ...);
+
+#if SOLARIS
+extern int gethostname(char *, int );
+extern void sync(void);
+#endif
+
+#endif /* __IPF_H__ */
diff --git a/sbin/ipf/common/ipf_y.y b/sbin/ipf/common/ipf_y.y
new file mode 100644
index 000000000000..ad4200023781
--- /dev/null
+++ b/sbin/ipf/common/ipf_y.y
@@ -0,0 +1,2738 @@
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2012 by Darren Reed.
+ *
+ * See the IPFILTER.LICENCE file for details on licencing.
+ */
+%{
+#include "ipf.h"
+#include <sys/ioctl.h>
+#include <syslog.h>
+#include <err.h>
+#ifdef IPFILTER_BPF
+# include <pcap.h>
+#endif
+#include "netinet/ip_pool.h"
+#include "netinet/ip_htable.h"
+#include "netinet/ipl.h"
+#include "ipf_l.h"
+
+#define YYDEBUG 1
+#define DOALL(x) for (fr = frc; fr != NULL; fr = fr->fr_next) { x }
+#define DOREM(x) for (; fr != NULL; fr = fr->fr_next) { x }
+
+extern void yyerror(char *);
+extern int yyparse(void);
+extern int yylex(void);
+extern int yydebug;
+extern FILE *yyin;
+extern int yylineNum;
+
+static int addname(frentry_t **, char *);
+static frentry_t *addrule(void);
+static frentry_t *allocfr(void);
+static void build_dstaddr_af(frentry_t *, void *);
+static void build_srcaddr_af(frentry_t *, void *);
+static void dobpf(int, char *);
+static void doipfexpr(char *);
+static void do_tuneint(char *, int);
+static void do_tunestr(char *, char *);
+static void fillgroup(frentry_t *);
+static int lookuphost(char *, i6addr_t *);
+static u_int makehash(struct alist_s *);
+static int makepool(struct alist_s *);
+static struct alist_s *newalist(struct alist_s *);
+static void newrule(void);
+static void resetaddr(void);
+static void setgroup(frentry_t **, char *);
+static void setgrhead(frentry_t **, char *);
+static void seticmphead(frentry_t **, char *);
+static void setifname(frentry_t **, int, char *);
+static void setipftype(void);
+static void setsyslog(void);
+static void unsetsyslog(void);
+
+frentry_t *fr = NULL, *frc = NULL, *frtop = NULL, *frold = NULL;
+
+static int ifpflag = 0;
+static int nowith = 0;
+static int dynamic = -1;
+static int pooled = 0;
+static int hashed = 0;
+static int nrules = 0;
+static int newlist = 0;
+static int added = 0;
+static int ipffd = -1;
+static int *yycont = NULL;
+static ioctlfunc_t ipfioctls[IPL_LOGSIZE];
+static addfunc_t ipfaddfunc = NULL;
+
+%}
+%union {
+ char *str;
+ u_32_t num;
+ frentry_t fr;
+ frtuc_t *frt;
+ struct alist_s *alist;
+ u_short port;
+ struct in_addr ip4;
+ struct {
+ u_short p1;
+ u_short p2;
+ int pc;
+ } pc;
+ struct ipp_s {
+ int type;
+ int ifpos;
+ int f;
+ int v;
+ int lif;
+ union i6addr a;
+ union i6addr m;
+ char *name;
+ } ipp;
+ struct {
+ i6addr_t adr;
+ int f;
+ } adr;
+ i6addr_t ip6;
+ struct {
+ char *if1;
+ char *if2;
+ } ifs;
+ char gname[FR_GROUPLEN];
+};
+
+%type <port> portnum
+%type <num> facility priority icmpcode seclevel secname icmptype
+%type <num> opt compare range opttype flagset optlist ipv6hdrlist ipv6hdr
+%type <num> portc porteq ipmask maskopts
+%type <ip4> ipv4 ipv4_16 ipv4_24
+%type <adr> hostname
+%type <ipp> addr ipaddr
+%type <str> servicename name interfacename groupname
+%type <pc> portrange portcomp
+%type <alist> addrlist poollist
+%type <ifs> onname
+
+%token <num> YY_NUMBER YY_HEX
+%token <str> YY_STR
+%token YY_COMMENT
+%token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT
+%token YY_RANGE_OUT YY_RANGE_IN
+%token <ip6> YY_IPV6
+
+%token IPFY_SET
+%token IPFY_PASS IPFY_BLOCK IPFY_COUNT IPFY_CALL IPFY_NOMATCH
+%token IPFY_RETICMP IPFY_RETRST IPFY_RETICMPASDST
+%token IPFY_IN IPFY_OUT
+%token IPFY_QUICK IPFY_ON IPFY_OUTVIA IPFY_INVIA
+%token IPFY_DUPTO IPFY_TO IPFY_FROUTE IPFY_REPLY_TO IPFY_ROUTETO
+%token IPFY_TOS IPFY_TTL IPFY_PROTO IPFY_INET IPFY_INET6
+%token IPFY_HEAD IPFY_GROUP
+%token IPFY_AUTH IPFY_PREAUTH
+%token IPFY_LOG IPFY_BODY IPFY_FIRST IPFY_LEVEL IPFY_ORBLOCK IPFY_L5AS
+%token IPFY_LOGTAG IPFY_MATCHTAG IPFY_SETTAG IPFY_SKIP IPFY_DECAPS
+%token IPFY_FROM IPFY_ALL IPFY_ANY IPFY_BPFV4 IPFY_BPFV6 IPFY_POOL IPFY_HASH
+%token IPFY_IPFEXPR IPFY_PPS IPFY_FAMILY IPFY_DSTLIST
+%token IPFY_ESP IPFY_AH
+%token IPFY_WITH IPFY_AND IPFY_NOT IPFY_NO IPFY_OPT
+%token IPFY_TCPUDP IPFY_TCP IPFY_UDP
+%token IPFY_FLAGS IPFY_MULTICAST
+%token IPFY_MASK IPFY_BROADCAST IPFY_NETWORK IPFY_NETMASKED IPFY_PEER
+%token IPFY_RPC IPFY_PORT
+%token IPFY_NOW IPFY_COMMENT IPFY_RULETTL
+%token IPFY_ICMP IPFY_ICMPTYPE IPFY_ICMPCODE
+%token IPFY_IPOPTS IPFY_SHORT IPFY_NAT IPFY_BADSRC IPFY_LOWTTL IPFY_FRAG
+%token IPFY_MBCAST IPFY_BAD IPFY_BADNAT IPFY_OOW IPFY_NEWISN IPFY_NOICMPERR
+%token IPFY_KEEP IPFY_STATE IPFY_FRAGS IPFY_LIMIT IPFY_STRICT IPFY_AGE
+%token IPFY_SYNC IPFY_FRAGBODY IPFY_ICMPHEAD IPFY_NOLOG IPFY_LOOSE
+%token IPFY_MAX_SRCS IPFY_MAX_PER_SRC
+%token IPFY_IPOPT_NOP IPFY_IPOPT_RR IPFY_IPOPT_ZSU IPFY_IPOPT_MTUP
+%token IPFY_IPOPT_MTUR IPFY_IPOPT_ENCODE IPFY_IPOPT_TS IPFY_IPOPT_TR
+%token IPFY_IPOPT_SEC IPFY_IPOPT_LSRR IPFY_IPOPT_ESEC IPFY_IPOPT_CIPSO
+%token IPFY_IPOPT_SATID IPFY_IPOPT_SSRR IPFY_IPOPT_ADDEXT IPFY_IPOPT_VISA
+%token IPFY_IPOPT_IMITD IPFY_IPOPT_EIP IPFY_IPOPT_FINN IPFY_IPOPT_DPS
+%token IPFY_IPOPT_SDB IPFY_IPOPT_NSAPA IPFY_IPOPT_RTRALRT IPFY_IPOPT_UMP
+%token IPFY_SECCLASS IPFY_SEC_UNC IPFY_SEC_CONF IPFY_SEC_RSV1 IPFY_SEC_RSV2
+%token IPFY_SEC_RSV4 IPFY_SEC_SEC IPFY_SEC_TS IPFY_SEC_RSV3 IPFY_DOI
+
+%token IPFY_V6HDRS IPFY_IPV6OPT IPFY_IPV6OPT_DSTOPTS IPFY_IPV6OPT_HOPOPTS
+%token IPFY_IPV6OPT_IPV6 IPFY_IPV6OPT_NONE IPFY_IPV6OPT_ROUTING IPFY_V6HDR
+%token IPFY_IPV6OPT_MOBILITY IPFY_IPV6OPT_ESP IPFY_IPV6OPT_FRAG
+
+%token IPFY_ICMPT_UNR IPFY_ICMPT_ECHO IPFY_ICMPT_ECHOR IPFY_ICMPT_SQUENCH
+%token IPFY_ICMPT_REDIR IPFY_ICMPT_TIMEX IPFY_ICMPT_PARAMP IPFY_ICMPT_TIMEST
+%token IPFY_ICMPT_TIMESTREP IPFY_ICMPT_INFOREQ IPFY_ICMPT_INFOREP
+%token IPFY_ICMPT_MASKREQ IPFY_ICMPT_MASKREP IPFY_ICMPT_ROUTERAD
+%token IPFY_ICMPT_ROUTERSOL
+
+%token IPFY_ICMPC_NETUNR IPFY_ICMPC_HSTUNR IPFY_ICMPC_PROUNR IPFY_ICMPC_PORUNR
+%token IPFY_ICMPC_NEEDF IPFY_ICMPC_SRCFAIL IPFY_ICMPC_NETUNK IPFY_ICMPC_HSTUNK
+%token IPFY_ICMPC_ISOLATE IPFY_ICMPC_NETPRO IPFY_ICMPC_HSTPRO
+%token IPFY_ICMPC_NETTOS IPFY_ICMPC_HSTTOS IPFY_ICMPC_FLTPRO IPFY_ICMPC_HSTPRE
+%token IPFY_ICMPC_CUTPRE
+
+%token IPFY_FAC_KERN IPFY_FAC_USER IPFY_FAC_MAIL IPFY_FAC_DAEMON IPFY_FAC_AUTH
+%token IPFY_FAC_SYSLOG IPFY_FAC_LPR IPFY_FAC_NEWS IPFY_FAC_UUCP IPFY_FAC_CRON
+%token IPFY_FAC_LOCAL0 IPFY_FAC_LOCAL1 IPFY_FAC_LOCAL2 IPFY_FAC_LOCAL3
+%token IPFY_FAC_LOCAL4 IPFY_FAC_LOCAL5 IPFY_FAC_LOCAL6 IPFY_FAC_LOCAL7
+%token IPFY_FAC_SECURITY IPFY_FAC_FTP IPFY_FAC_AUTHPRIV IPFY_FAC_AUDIT
+%token IPFY_FAC_LFMT IPFY_FAC_CONSOLE
+
+%token IPFY_PRI_EMERG IPFY_PRI_ALERT IPFY_PRI_CRIT IPFY_PRI_ERR IPFY_PRI_WARN
+%token IPFY_PRI_NOTICE IPFY_PRI_INFO IPFY_PRI_DEBUG
+%%
+file: settings rules
+ | rules
+ ;
+
+settings:
+ YY_COMMENT
+ | setting
+ | settings setting
+ ;
+
+rules: line
+ | assign
+ | rules line
+ | rules assign
+ ;
+
+setting:
+ IPFY_SET YY_STR YY_NUMBER ';' { do_tuneint($2, $3); }
+ | IPFY_SET YY_STR YY_HEX ';' { do_tuneint($2, $3); }
+ | IPFY_SET YY_STR YY_STR ';' { do_tunestr($2, $3); }
+ ;
+
+line: rule { while ((fr = frtop) != NULL) {
+ frtop = fr->fr_next;
+ fr->fr_next = NULL;
+ if ((fr->fr_type == FR_T_IPF) &&
+ (fr->fr_ip.fi_v == 0))
+ fr->fr_mip.fi_v = 0;
+ /* XXX validate ? */
+ (*ipfaddfunc)(ipffd, ipfioctls[IPL_LOGIPF], fr);
+ fr->fr_next = frold;
+ frold = fr;
+ }
+ resetlexer();
+ }
+ | YY_COMMENT
+ ;
+
+xx: { newrule(); }
+ ;
+
+assign: YY_STR assigning YY_STR ';' { set_variable($1, $3);
+ resetlexer();
+ free($1);
+ free($3);
+ yyvarnext = 0;
+ }
+ ;
+
+assigning:
+ '=' { yyvarnext = 1; }
+ ;
+
+rule: inrule eol
+ | outrule eol
+ ;
+
+eol: | ';'
+ ;
+
+inrule:
+ rulehead markin inopts rulemain ruletail intag ruletail2
+ ;
+
+outrule:
+ rulehead markout outopts rulemain ruletail outtag ruletail2
+ ;
+
+rulehead:
+ xx collection action
+ | xx insert collection action
+ ;
+
+markin: IPFY_IN { fr->fr_flags |= FR_INQUE; }
+ ;
+
+markout:
+ IPFY_OUT { fr->fr_flags |= FR_OUTQUE; }
+ ;
+
+rulemain:
+ ipfrule
+ | bpfrule
+ | exprrule
+ ;
+
+ipfrule:
+ family tos ttl proto ip
+ ;
+
+family: | IPFY_FAMILY IPFY_INET { if (use_inet6 == 1) {
+ YYERROR;
+ } else {
+ frc->fr_family = AF_INET;
+ }
+ }
+ | IPFY_INET { if (use_inet6 == 1) {
+ YYERROR;
+ } else {
+ frc->fr_family = AF_INET;
+ }
+ }
+ | IPFY_FAMILY IPFY_INET6 { if (use_inet6 == -1) {
+ YYERROR;
+ } else {
+ frc->fr_family = AF_INET6;
+ }
+ }
+ | IPFY_INET6 { if (use_inet6 == -1) {
+ YYERROR;
+ } else {
+ frc->fr_family = AF_INET6;
+ }
+ }
+ ;
+
+bpfrule:
+ IPFY_BPFV4 '{' YY_STR '}' { dobpf(4, $3); free($3); }
+ | IPFY_BPFV6 '{' YY_STR '}' { dobpf(6, $3); free($3); }
+ ;
+
+exprrule:
+ IPFY_IPFEXPR '{' YY_STR '}' { doipfexpr($3); }
+ ;
+
+ruletail:
+ with keep head group
+ ;
+
+ruletail2:
+ pps age new rulettl comment
+ ;
+
+intag: settagin matchtagin
+ ;
+
+outtag: settagout matchtagout
+ ;
+
+insert:
+ '@' YY_NUMBER { fr->fr_hits = (U_QUAD_T)$2 + 1; }
+ ;
+
+collection:
+ | YY_NUMBER { fr->fr_collect = $1; }
+ ;
+
+action: block
+ | IPFY_PASS { fr->fr_flags |= FR_PASS; }
+ | IPFY_NOMATCH { fr->fr_flags |= FR_NOMATCH; }
+ | log
+ | IPFY_COUNT { fr->fr_flags |= FR_ACCOUNT; }
+ | decaps { fr->fr_flags |= FR_DECAPSULATE; }
+ | auth
+ | IPFY_SKIP YY_NUMBER { fr->fr_flags |= FR_SKIP;
+ fr->fr_arg = $2; }
+ | IPFY_CALL func
+ | IPFY_CALL IPFY_NOW func { fr->fr_flags |= FR_CALLNOW; }
+ ;
+
+block: blocked
+ | blocked blockreturn
+ ;
+
+blocked:
+ IPFY_BLOCK { fr->fr_flags = FR_BLOCK; }
+ ;
+blockreturn:
+ IPFY_RETICMP { fr->fr_flags |= FR_RETICMP; }
+ | IPFY_RETICMP returncode { fr->fr_flags |= FR_RETICMP; }
+ | IPFY_RETICMPASDST { fr->fr_flags |= FR_FAKEICMP; }
+ | IPFY_RETICMPASDST returncode { fr->fr_flags |= FR_FAKEICMP; }
+ | IPFY_RETRST { fr->fr_flags |= FR_RETRST; }
+ ;
+
+decaps: IPFY_DECAPS
+ | IPFY_DECAPS IPFY_L5AS '(' YY_STR ')'
+ { fr->fr_icode = atoi($4); }
+ ;
+
+log: IPFY_LOG { fr->fr_flags |= FR_LOG; }
+ | IPFY_LOG logoptions { fr->fr_flags |= FR_LOG; }
+ ;
+
+auth: IPFY_AUTH { fr->fr_flags |= FR_AUTH; }
+ | IPFY_AUTH blockreturn { fr->fr_flags |= FR_AUTH;}
+ | IPFY_PREAUTH { fr->fr_flags |= FR_PREAUTH; }
+ ;
+
+func: YY_STR '/' YY_NUMBER
+ { fr->fr_func = nametokva($1, ipfioctls[IPL_LOGIPF]);
+ fr->fr_arg = $3;
+ free($1);
+ }
+ ;
+
+inopts:
+ | inopts inopt
+ ;
+
+inopt:
+ logopt
+ | quick
+ | on
+ | dup
+ | froute
+ | proute
+ | replyto
+ ;
+
+outopts:
+ | outopts outopt
+ ;
+
+outopt:
+ logopt
+ | quick
+ | on
+ | dup
+ | proute
+ | froute
+ | replyto
+ ;
+
+tos: | settos YY_NUMBER { DOALL(fr->fr_tos = $2; fr->fr_mtos = 0xff;) }
+ | settos YY_HEX { DOALL(fr->fr_tos = $2; fr->fr_mtos = 0xff;) }
+ | settos lstart toslist lend
+ ;
+
+settos: IPFY_TOS { setipftype(); }
+ ;
+
+toslist:
+ YY_NUMBER { DOALL(fr->fr_tos = $1; fr->fr_mtos = 0xff;) }
+ | YY_HEX { DOREM(fr->fr_tos = $1; fr->fr_mtos = 0xff;) }
+ | toslist lmore YY_NUMBER
+ { DOREM(fr->fr_tos = $3; fr->fr_mtos = 0xff;) }
+ | toslist lmore YY_HEX
+ { DOREM(fr->fr_tos = $3; fr->fr_mtos = 0xff;) }
+ ;
+
+ttl: | setttl YY_NUMBER
+ { DOALL(fr->fr_ttl = $2; fr->fr_mttl = 0xff;) }
+ | setttl lstart ttllist lend
+ ;
+
+lstart: '{' { newlist = 1; fr = frc; added = 0; }
+ ;
+
+lend: '}' { nrules += added; }
+ ;
+
+lmore: lanother { if (newlist == 1) {
+ newlist = 0;
+ }
+ fr = addrule();
+ if (yycont != NULL)
+ *yycont = 1;
+ }
+ ;
+
+lanother:
+ | ','
+ ;
+
+setttl: IPFY_TTL { setipftype(); }
+ ;
+
+ttllist:
+ YY_NUMBER { DOREM(fr->fr_ttl = $1; fr->fr_mttl = 0xff;) }
+ | ttllist lmore YY_NUMBER
+ { DOREM(fr->fr_ttl = $3; fr->fr_mttl = 0xff;) }
+ ;
+
+proto: | protox protocol { yyresetdict(); }
+ ;
+
+protox: IPFY_PROTO { setipftype();
+ fr = frc;
+ yysetdict(NULL); }
+ ;
+
+ip: srcdst flags icmp
+ ;
+
+group: | IPFY_GROUP groupname { DOALL(setgroup(&fr, $2); \
+ fillgroup(fr););
+ free($2);
+ }
+ ;
+
+head: | IPFY_HEAD groupname { DOALL(setgrhead(&fr, $2););
+ free($2);
+ }
+ ;
+
+groupname:
+ YY_STR { $$ = $1;
+ if (strlen($$) >= FR_GROUPLEN)
+ $$[FR_GROUPLEN - 1] = '\0';
+ }
+ | YY_NUMBER { $$ = malloc(16);
+ sprintf($$, "%d", $1);
+ }
+ ;
+
+settagin:
+ | IPFY_SETTAG '(' taginlist ')'
+ ;
+
+taginlist:
+ taginspec
+ | taginlist ',' taginspec
+ ;
+
+taginspec:
+ logtag
+ ;
+
+nattag: IPFY_NAT '=' YY_STR { DOALL(strncpy(fr->fr_nattag.ipt_tag,\
+ $3, IPFTAG_LEN););
+ free($3); }
+ | IPFY_NAT '=' YY_NUMBER { DOALL(sprintf(fr->fr_nattag.ipt_tag,\
+ "%d", $3 & 0xffffffff);) }
+ ;
+
+logtag: IPFY_LOG '=' YY_NUMBER { DOALL(fr->fr_logtag = $3;) }
+ ;
+
+settagout:
+ | IPFY_SETTAG '(' tagoutlist ')'
+ ;
+
+tagoutlist:
+ tagoutspec
+ | tagoutlist ',' tagoutspec
+ ;
+
+tagoutspec:
+ logtag
+ | nattag
+ ;
+
+matchtagin:
+ | IPFY_MATCHTAG '(' tagoutlist ')'
+ ;
+
+matchtagout:
+ | IPFY_MATCHTAG '(' taginlist ')'
+ ;
+
+pps: | IPFY_PPS YY_NUMBER { DOALL(fr->fr_pps = $2;) }
+ ;
+
+new: | savegroup file restoregroup
+ ;
+
+rulettl:
+ | IPFY_RULETTL YY_NUMBER { DOALL(fr->fr_die = $2;) }
+ ;
+
+comment:
+ | IPFY_COMMENT YY_STR { DOALL(fr->fr_comment = addname(&fr, \
+ $2);) }
+ ;
+
+savegroup:
+ '{'
+ ;
+
+restoregroup:
+ '}'
+ ;
+
+logopt: log
+ ;
+
+quick: IPFY_QUICK { fr->fr_flags |= FR_QUICK; }
+ ;
+
+on: IPFY_ON onname { setifname(&fr, 0, $2.if1);
+ free($2.if1);
+ if ($2.if2 != NULL) {
+ setifname(&fr, 1,
+ $2.if2);
+ free($2.if2);
+ }
+ }
+ | IPFY_ON lstart onlist lend
+ | IPFY_ON onname IPFY_INVIA vianame { setifname(&fr, 0, $2.if1);
+ free($2.if1);
+ if ($2.if2 != NULL) {
+ setifname(&fr, 1,
+ $2.if2);
+ free($2.if2);
+ }
+ }
+ | IPFY_ON onname IPFY_OUTVIA vianame { setifname(&fr, 0, $2.if1);
+ free($2.if1);
+ if ($2.if2 != NULL) {
+ setifname(&fr, 1,
+ $2.if2);
+ free($2.if2);
+ }
+ }
+ ;
+
+onlist: onname { DOREM(setifname(&fr, 0, $1.if1); \
+ if ($1.if2 != NULL) \
+ setifname(&fr, 1, $1.if2); \
+ )
+ free($1.if1);
+ if ($1.if2 != NULL)
+ free($1.if2);
+ }
+ | onlist lmore onname { DOREM(setifname(&fr, 0, $3.if1); \
+ if ($3.if2 != NULL) \
+ setifname(&fr, 1, $3.if2); \
+ )
+ free($3.if1);
+ if ($3.if2 != NULL)
+ free($3.if2);
+ }
+ ;
+
+onname: interfacename { $$.if1 = $1;
+ $$.if2 = NULL;
+ }
+ | interfacename ',' interfacename
+ { $$.if1 = $1;
+ $$.if2 = $3;
+ }
+ ;
+
+vianame:
+ name { setifname(&fr, 2, $1);
+ free($1);
+ }
+ | name ',' name { setifname(&fr, 2, $1);
+ free($1);
+ setifname(&fr, 3, $3);
+ free($3);
+ }
+ ;
+
+dup: IPFY_DUPTO name
+ { int idx = addname(&fr, $2);
+ fr->fr_dif.fd_name = idx;
+ free($2);
+ }
+ | IPFY_DUPTO IPFY_DSTLIST '/' name
+ { int idx = addname(&fr, $4);
+ fr->fr_dif.fd_name = idx;
+ fr->fr_dif.fd_type = FRD_DSTLIST;
+ free($4);
+ }
+ | IPFY_DUPTO name duptoseparator hostname
+ { int idx = addname(&fr, $2);
+ fr->fr_dif.fd_name = idx;
+ fr->fr_dif.fd_ptr = (void *)-1;
+ fr->fr_dif.fd_ip6 = $4.adr;
+ if (fr->fr_family == AF_UNSPEC && $4.f != AF_UNSPEC)
+ fr->fr_family = $4.f;
+ yyexpectaddr = 0;
+ free($2);
+ }
+ ;
+
+duptoseparator:
+ ':' { yyexpectaddr = 1; yycont = &yyexpectaddr; resetaddr(); }
+ ;
+
+froute: IPFY_FROUTE { fr->fr_flags |= FR_FASTROUTE; }
+ ;
+
+proute: routeto name
+ { int idx = addname(&fr, $2);
+ fr->fr_tif.fd_name = idx;
+ free($2);
+ }
+ | routeto IPFY_DSTLIST '/' name
+ { int idx = addname(&fr, $4);
+ fr->fr_tif.fd_name = idx;
+ fr->fr_tif.fd_type = FRD_DSTLIST;
+ free($4);
+ }
+ | routeto name duptoseparator hostname
+ { int idx = addname(&fr, $2);
+ fr->fr_tif.fd_name = idx;
+ fr->fr_tif.fd_ptr = (void *)-1;
+ fr->fr_tif.fd_ip6 = $4.adr;
+ if (fr->fr_family == AF_UNSPEC && $4.f != AF_UNSPEC)
+ fr->fr_family = $4.f;
+ yyexpectaddr = 0;
+ free($2);
+ }
+ ;
+
+routeto:
+ IPFY_TO
+ | IPFY_ROUTETO
+ ;
+
+replyto:
+ IPFY_REPLY_TO name
+ { int idx = addname(&fr, $2);
+ fr->fr_rif.fd_name = idx;
+ free($2);
+ }
+ | IPFY_REPLY_TO IPFY_DSTLIST '/' name
+ { fr->fr_rif.fd_name = addname(&fr, $4);
+ fr->fr_rif.fd_type = FRD_DSTLIST;
+ free($4);
+ }
+ | IPFY_REPLY_TO name duptoseparator hostname
+ { int idx = addname(&fr, $2);
+ fr->fr_rif.fd_name = idx;
+ fr->fr_rif.fd_ptr = (void *)-1;
+ fr->fr_rif.fd_ip6 = $4.adr;
+ if (fr->fr_family == AF_UNSPEC && $4.f != AF_UNSPEC)
+ fr->fr_family = $4.f;
+ free($2);
+ }
+ ;
+
+logoptions:
+ logoption
+ | logoptions logoption
+ ;
+
+logoption:
+ IPFY_BODY { fr->fr_flags |= FR_LOGBODY; }
+ | IPFY_FIRST { fr->fr_flags |= FR_LOGFIRST; }
+ | IPFY_ORBLOCK { fr->fr_flags |= FR_LOGORBLOCK; }
+ | level loglevel { unsetsyslog(); }
+ ;
+
+returncode:
+ starticmpcode icmpcode ')' { fr->fr_icode = $2; yyresetdict(); }
+ ;
+
+starticmpcode:
+ '(' { yysetdict(icmpcodewords); }
+ ;
+
+srcdst: | IPFY_ALL
+ | fromto
+ ;
+
+protocol:
+ YY_NUMBER { DOALL(fr->fr_proto = $1; \
+ fr->fr_mproto = 0xff;)
+ }
+ | YY_STR { if (!strcmp($1, "tcp-udp")) {
+ DOALL(fr->fr_flx |= FI_TCPUDP; \
+ fr->fr_mflx |= FI_TCPUDP;)
+ } else {
+ int p = getproto($1);
+ if (p == -1)
+ yyerror("protocol unknown");
+ DOALL(fr->fr_proto = p; \
+ fr->fr_mproto = 0xff;)
+ }
+ free($1);
+ }
+ | YY_STR nextstring YY_STR
+ { if (!strcmp($1, "tcp") &&
+ !strcmp($3, "udp")) {
+ DOREM(fr->fr_flx |= FI_TCPUDP; \
+ fr->fr_mflx |= FI_TCPUDP;)
+ } else {
+ YYERROR;
+ }
+ free($1);
+ free($3);
+ }
+ ;
+
+nextstring:
+ '/' { yysetdict(NULL); }
+ ;
+
+fromto: from srcobject to dstobject { yyexpectaddr = 0; yycont = NULL; }
+ | to dstobject { yyexpectaddr = 0; yycont = NULL; }
+ | from srcobject { yyexpectaddr = 0; yycont = NULL; }
+ ;
+
+from: IPFY_FROM { setipftype();
+ if (fr == NULL)
+ fr = frc;
+ yyexpectaddr = 1;
+ if (yydebug)
+ printf("set yyexpectaddr\n");
+ yycont = &yyexpectaddr;
+ yysetdict(addrwords);
+ resetaddr(); }
+ ;
+
+to: IPFY_TO { if (fr == NULL)
+ fr = frc;
+ yyexpectaddr = 1;
+ if (yydebug)
+ printf("set yyexpectaddr\n");
+ yycont = &yyexpectaddr;
+ yysetdict(addrwords);
+ resetaddr();
+ }
+ ;
+
+with: | andwith withlist
+ ;
+
+andwith:
+ IPFY_WITH { nowith = 0; setipftype(); }
+ | IPFY_AND { nowith = 0; setipftype(); }
+ ;
+
+flags: | startflags flagset
+ { DOALL(fr->fr_tcpf = $2; fr->fr_tcpfm = FR_TCPFMAX;) }
+ | startflags flagset '/' flagset
+ { DOALL(fr->fr_tcpf = $2; fr->fr_tcpfm = $4;) }
+ | startflags '/' flagset
+ { DOALL(fr->fr_tcpf = 0; fr->fr_tcpfm = $3;) }
+ | startflags YY_NUMBER
+ { DOALL(fr->fr_tcpf = $2; fr->fr_tcpfm = FR_TCPFMAX;) }
+ | startflags '/' YY_NUMBER
+ { DOALL(fr->fr_tcpf = 0; fr->fr_tcpfm = $3;) }
+ | startflags YY_NUMBER '/' YY_NUMBER
+ { DOALL(fr->fr_tcpf = $2; fr->fr_tcpfm = $4;) }
+ | startflags flagset '/' YY_NUMBER
+ { DOALL(fr->fr_tcpf = $2; fr->fr_tcpfm = $4;) }
+ | startflags YY_NUMBER '/' flagset
+ { DOALL(fr->fr_tcpf = $2; fr->fr_tcpfm = $4;) }
+ ;
+
+startflags:
+ IPFY_FLAGS { if (frc->fr_type != FR_T_IPF)
+ yyerror("flags with non-ipf type rule");
+ if (frc->fr_proto != IPPROTO_TCP)
+ yyerror("flags with non-TCP rule");
+ }
+ ;
+
+flagset:
+ YY_STR { $$ = tcpflags($1); free($1); }
+ | YY_HEX { $$ = $1; }
+ ;
+
+srcobject:
+ { yyresetdict(); } fromport
+ | srcaddr srcport
+ | '!' srcaddr srcport
+ { DOALL(fr->fr_flags |= FR_NOTSRCIP;) }
+ ;
+
+srcaddr:
+ addr { build_srcaddr_af(fr, &$1); }
+ | lstart srcaddrlist lend
+ ;
+
+srcaddrlist:
+ addr { build_srcaddr_af(fr, &$1); }
+ | srcaddrlist lmore addr
+ { build_srcaddr_af(fr, &$3); }
+ ;
+
+srcport:
+ | portcomp
+ { DOALL(fr->fr_scmp = $1.pc; fr->fr_sport = $1.p1;) }
+ | portrange
+ { DOALL(fr->fr_scmp = $1.pc; fr->fr_sport = $1.p1; \
+ fr->fr_stop = $1.p2;) }
+ | porteq lstart srcportlist lend
+ { yyresetdict(); }
+ ;
+
+fromport:
+ portcomp
+ { DOALL(fr->fr_scmp = $1.pc; fr->fr_sport = $1.p1;) }
+ | portrange
+ { DOALL(fr->fr_scmp = $1.pc; fr->fr_sport = $1.p1; \
+ fr->fr_stop = $1.p2;) }
+ | porteq lstart srcportlist lend
+ { yyresetdict(); }
+ ;
+
+srcportlist:
+ portnum { DOREM(fr->fr_scmp = FR_EQUAL; fr->fr_sport = $1;) }
+ | portnum ':' portnum
+ { DOREM(fr->fr_scmp = FR_INCRANGE; fr->fr_sport = $1; \
+ fr->fr_stop = $3;) }
+ | portnum YY_RANGE_IN portnum
+ { DOREM(fr->fr_scmp = FR_INRANGE; fr->fr_sport = $1; \
+ fr->fr_stop = $3;) }
+ | srcportlist lmore portnum
+ { DOREM(fr->fr_scmp = FR_EQUAL; fr->fr_sport = $3;) }
+ | srcportlist lmore portnum ':' portnum
+ { DOREM(fr->fr_scmp = FR_INCRANGE; fr->fr_sport = $3; \
+ fr->fr_stop = $5;) }
+ | srcportlist lmore portnum YY_RANGE_IN portnum
+ { DOREM(fr->fr_scmp = FR_INRANGE; fr->fr_sport = $3; \
+ fr->fr_stop = $5;) }
+ ;
+
+dstobject:
+ { yyresetdict(); } toport
+ | dstaddr dstport
+ | '!' dstaddr dstport
+ { DOALL(fr->fr_flags |= FR_NOTDSTIP;) }
+ ;
+
+dstaddr:
+ addr { if (($1.f != AF_UNSPEC) && (frc->fr_family != AF_UNSPEC) &&
+ ($1.f != frc->fr_family))
+ yyerror("1.src/dst address family mismatch");
+ build_dstaddr_af(fr, &$1);
+ }
+ | lstart dstaddrlist lend
+ ;
+
+dstaddrlist:
+ addr { if (($1.f != AF_UNSPEC) && (frc->fr_family != AF_UNSPEC) &&
+ ($1.f != frc->fr_family))
+ yyerror("2.src/dst address family mismatch");
+ build_dstaddr_af(fr, &$1);
+ }
+ | dstaddrlist lmore addr
+ { if (($3.f != AF_UNSPEC) && (frc->fr_family != AF_UNSPEC) &&
+ ($3.f != frc->fr_family))
+ yyerror("3.src/dst address family mismatch");
+ build_dstaddr_af(fr, &$3);
+ }
+ ;
+
+
+dstport:
+ | portcomp
+ { DOALL(fr->fr_dcmp = $1.pc; fr->fr_dport = $1.p1;) }
+ | portrange
+ { DOALL(fr->fr_dcmp = $1.pc; fr->fr_dport = $1.p1; \
+ fr->fr_dtop = $1.p2;) }
+ | porteq lstart dstportlist lend
+ { yyresetdict(); }
+ ;
+
+toport:
+ portcomp
+ { DOALL(fr->fr_dcmp = $1.pc; fr->fr_dport = $1.p1;) }
+ | portrange
+ { DOALL(fr->fr_dcmp = $1.pc; fr->fr_dport = $1.p1; \
+ fr->fr_dtop = $1.p2;) }
+ | porteq lstart dstportlist lend
+ { yyresetdict(); }
+ ;
+
+dstportlist:
+ portnum { DOREM(fr->fr_dcmp = FR_EQUAL; fr->fr_dport = $1;) }
+ | portnum ':' portnum
+ { DOREM(fr->fr_dcmp = FR_INCRANGE; fr->fr_dport = $1; \
+ fr->fr_dtop = $3;) }
+ | portnum YY_RANGE_IN portnum
+ { DOREM(fr->fr_dcmp = FR_INRANGE; fr->fr_dport = $1; \
+ fr->fr_dtop = $3;) }
+ | dstportlist lmore portnum
+ { DOREM(fr->fr_dcmp = FR_EQUAL; fr->fr_dport = $3;) }
+ | dstportlist lmore portnum ':' portnum
+ { DOREM(fr->fr_dcmp = FR_INCRANGE; fr->fr_dport = $3; \
+ fr->fr_dtop = $5;) }
+ | dstportlist lmore portnum YY_RANGE_IN portnum
+ { DOREM(fr->fr_dcmp = FR_INRANGE; fr->fr_dport = $3; \
+ fr->fr_dtop = $5;) }
+ ;
+
+addr: pool '/' YY_NUMBER { pooled = 1;
+ yyexpectaddr = 0;
+ $$.type = FRI_LOOKUP;
+ $$.v = 0;
+ $$.ifpos = -1;
+ $$.f = AF_UNSPEC;
+ $$.a.iplookuptype = IPLT_POOL;
+ $$.a.iplookupsubtype = 0;
+ $$.a.iplookupnum = $3; }
+ | pool '/' YY_STR { pooled = 1;
+ $$.ifpos = -1;
+ $$.f = AF_UNSPEC;
+ $$.type = FRI_LOOKUP;
+ $$.a.iplookuptype = IPLT_POOL;
+ $$.a.iplookupsubtype = 1;
+ $$.a.iplookupname = addname(&fr, $3);
+ }
+ | pool '=' '(' { yyexpectaddr = 1;
+ pooled = 1;
+ }
+ poollist ')' { yyexpectaddr = 0;
+ $$.v = 0;
+ $$.ifpos = -1;
+ $$.f = AF_UNSPEC;
+ $$.type = FRI_LOOKUP;
+ $$.a.iplookuptype = IPLT_POOL;
+ $$.a.iplookupsubtype = 0;
+ $$.a.iplookupnum = makepool($5);
+ }
+ | hash '/' YY_NUMBER { hashed = 1;
+ yyexpectaddr = 0;
+ $$.v = 0;
+ $$.ifpos = -1;
+ $$.f = AF_UNSPEC;
+ $$.type = FRI_LOOKUP;
+ $$.a.iplookuptype = IPLT_HASH;
+ $$.a.iplookupsubtype = 0;
+ $$.a.iplookupnum = $3;
+ }
+ | hash '/' YY_STR { hashed = 1;
+ $$.type = FRI_LOOKUP;
+ $$.v = 0;
+ $$.ifpos = -1;
+ $$.f = AF_UNSPEC;
+ $$.a.iplookuptype = IPLT_HASH;
+ $$.a.iplookupsubtype = 1;
+ $$.a.iplookupname = addname(&fr, $3);
+ }
+ | hash '=' '(' { hashed = 1;
+ yyexpectaddr = 1;
+ }
+ addrlist ')' { yyexpectaddr = 0;
+ $$.v = 0;
+ $$.ifpos = -1;
+ $$.f = AF_UNSPEC;
+ $$.type = FRI_LOOKUP;
+ $$.a.iplookuptype = IPLT_HASH;
+ $$.a.iplookupsubtype = 0;
+ $$.a.iplookupnum = makehash($5);
+ }
+ | ipaddr { $$ = $1;
+ yyexpectaddr = 0; }
+ ;
+
+ipaddr: IPFY_ANY { memset(&($$), 0, sizeof($$));
+ $$.type = FRI_NORMAL;
+ $$.ifpos = -1;
+ yyexpectaddr = 0;
+ }
+ | hostname { memset(&($$), 0, sizeof($$));
+ $$.a = $1.adr;
+ $$.f = $1.f;
+ if ($1.f == AF_INET6)
+ fill6bits(128, $$.m.i6);
+ else if ($1.f == AF_INET)
+ fill6bits(32, $$.m.i6);
+ $$.v = ftov($1.f);
+ $$.ifpos = dynamic;
+ $$.type = FRI_NORMAL;
+ }
+ | hostname { yyresetdict(); }
+ maskspace { yysetdict(maskwords);
+ yyexpectaddr = 2; }
+ ipmask { memset(&($$), 0, sizeof($$));
+ ntomask($1.f, $5, $$.m.i6);
+ $$.a = $1.adr;
+ $$.a.i6[0] &= $$.m.i6[0];
+ $$.a.i6[1] &= $$.m.i6[1];
+ $$.a.i6[2] &= $$.m.i6[2];
+ $$.a.i6[3] &= $$.m.i6[3];
+ $$.f = $1.f;
+ $$.v = ftov($1.f);
+ $$.type = ifpflag;
+ $$.ifpos = dynamic;
+ if (ifpflag != 0 && $$.v == 0) {
+ if (frc->fr_family == AF_INET6){
+ $$.v = 6;
+ $$.f = AF_INET6;
+ } else {
+ $$.v = 4;
+ $$.f = AF_INET;
+ }
+ }
+ yyresetdict();
+ yyexpectaddr = 0;
+ }
+ | '(' YY_STR ')' { memset(&($$), 0, sizeof($$));
+ $$.type = FRI_DYNAMIC;
+ ifpflag = FRI_DYNAMIC;
+ $$.ifpos = addname(&fr, $2);
+ $$.lif = 0;
+ }
+ | '(' YY_STR ')' '/'
+ { ifpflag = FRI_DYNAMIC; yysetdict(maskwords); }
+ maskopts
+ { memset(&($$), 0, sizeof($$));
+ $$.type = ifpflag;
+ $$.ifpos = addname(&fr, $2);
+ $$.lif = 0;
+ if (frc->fr_family == AF_UNSPEC)
+ frc->fr_family = AF_INET;
+ if (ifpflag == FRI_DYNAMIC) {
+ ntomask(frc->fr_family,
+ $6, $$.m.i6);
+ }
+ yyresetdict();
+ yyexpectaddr = 0;
+ }
+ | '(' YY_STR ':' YY_NUMBER ')' '/'
+ { ifpflag = FRI_DYNAMIC; yysetdict(maskwords); }
+ maskopts
+ { memset(&($$), 0, sizeof($$));
+ $$.type = ifpflag;
+ $$.ifpos = addname(&fr, $2);
+ $$.lif = $4;
+ if (frc->fr_family == AF_UNSPEC)
+ frc->fr_family = AF_INET;
+ if (ifpflag == FRI_DYNAMIC) {
+ ntomask(frc->fr_family,
+ $8, $$.m.i6);
+ }
+ yyresetdict();
+ yyexpectaddr = 0;
+ }
+ ;
+
+maskspace:
+ '/'
+ | IPFY_MASK
+ ;
+
+ipmask: ipv4 { $$ = count4bits($1.s_addr); }
+ | YY_HEX { $$ = count4bits(htonl($1)); }
+ | YY_NUMBER { $$ = $1; }
+ | YY_IPV6 { $$ = count6bits($1.i6); }
+ | maskopts { $$ = $1; }
+ ;
+
+maskopts:
+ IPFY_BROADCAST { if (ifpflag == FRI_DYNAMIC) {
+ ifpflag = FRI_BROADCAST;
+ } else {
+ YYERROR;
+ }
+ $$ = 0;
+ }
+ | IPFY_NETWORK { if (ifpflag == FRI_DYNAMIC) {
+ ifpflag = FRI_NETWORK;
+ } else {
+ YYERROR;
+ }
+ $$ = 0;
+ }
+ | IPFY_NETMASKED { if (ifpflag == FRI_DYNAMIC) {
+ ifpflag = FRI_NETMASKED;
+ } else {
+ YYERROR;
+ }
+ $$ = 0;
+ }
+ | IPFY_PEER { if (ifpflag == FRI_DYNAMIC) {
+ ifpflag = FRI_PEERADDR;
+ } else {
+ YYERROR;
+ }
+ $$ = 0;
+ }
+ | YY_NUMBER { $$ = $1; }
+ ;
+
+hostname:
+ ipv4 { memset(&($$), 0, sizeof($$));
+ $$.adr.in4 = $1;
+ if (frc->fr_family == AF_INET6)
+ YYERROR;
+ $$.f = AF_INET;
+ yyexpectaddr = 2;
+ }
+ | YY_NUMBER { memset(&($$), 0, sizeof($$));
+ if (frc->fr_family == AF_INET6)
+ YYERROR;
+ $$.adr.in4_addr = $1;
+ $$.f = AF_INET;
+ yyexpectaddr = 2;
+ }
+ | YY_HEX { memset(&($$), 0, sizeof($$));
+ if (frc->fr_family == AF_INET6)
+ YYERROR;
+ $$.adr.in4_addr = $1;
+ $$.f = AF_INET;
+ yyexpectaddr = 2;
+ }
+ | YY_STR { memset(&($$), 0, sizeof($$));
+ if (lookuphost($1, &$$.adr) == 0)
+ $$.f = AF_INET;
+ free($1);
+ yyexpectaddr = 2;
+ }
+ | YY_IPV6 { memset(&($$), 0, sizeof($$));
+ if (frc->fr_family == AF_INET)
+ YYERROR;
+ $$.adr = $1;
+ $$.f = AF_INET6;
+ yyexpectaddr = 2;
+ }
+ ;
+
+addrlist:
+ ipaddr { $$ = newalist(NULL);
+ $$->al_family = $1.f;
+ $$->al_i6addr = $1.a;
+ $$->al_i6mask = $1.m;
+ }
+ | ipaddr ',' { yyexpectaddr = 1; } addrlist
+ { $$ = newalist($4);
+ $$->al_family = $1.f;
+ $$->al_i6addr = $1.a;
+ $$->al_i6mask = $1.m;
+ }
+ ;
+
+pool: IPFY_POOL { yyexpectaddr = 0; yycont = NULL; yyresetdict(); }
+ ;
+
+hash: IPFY_HASH { yyexpectaddr = 0; yycont = NULL; yyresetdict(); }
+ ;
+
+poollist:
+ ipaddr { $$ = newalist(NULL);
+ $$->al_family = $1.f;
+ $$->al_i6addr = $1.a;
+ $$->al_i6mask = $1.m;
+ }
+ | '!' ipaddr { $$ = newalist(NULL);
+ $$->al_not = 1;
+ $$->al_family = $2.f;
+ $$->al_i6addr = $2.a;
+ $$->al_i6mask = $2.m;
+ }
+ | poollist ',' ipaddr
+ { $$ = newalist($1);
+ $$->al_family = $3.f;
+ $$->al_i6addr = $3.a;
+ $$->al_i6mask = $3.m;
+ }
+ | poollist ',' '!' ipaddr
+ { $$ = newalist($1);
+ $$->al_not = 1;
+ $$->al_family = $4.f;
+ $$->al_i6addr = $4.a;
+ $$->al_i6mask = $4.m;
+ }
+ ;
+
+port: IPFY_PORT { yyexpectaddr = 0;
+ yycont = NULL;
+ if (frc->fr_proto != 0 &&
+ frc->fr_proto != IPPROTO_UDP &&
+ frc->fr_proto != IPPROTO_TCP)
+ yyerror("port use incorrect");
+ }
+ ;
+
+portc: port compare { $$ = $2;
+ yysetdict(NULL);
+ }
+ | porteq { $$ = $1; }
+ ;
+
+porteq: port '=' { $$ = FR_EQUAL;
+ yysetdict(NULL);
+ }
+ ;
+
+portr: IPFY_PORT { yyexpectaddr = 0;
+ yycont = NULL;
+ yysetdict(NULL);
+ }
+ ;
+
+portcomp:
+ portc portnum { $$.pc = $1;
+ $$.p1 = $2;
+ yyresetdict();
+ }
+ ;
+
+portrange:
+ portr portnum range portnum { $$.p1 = $2;
+ $$.pc = $3;
+ $$.p2 = $4;
+ yyresetdict();
+ }
+ ;
+
+icmp: | itype icode
+ ;
+
+itype: seticmptype icmptype
+ { DOALL(fr->fr_icmp = htons($2 << 8); fr->fr_icmpm = htons(0xff00););
+ yyresetdict();
+ }
+ | seticmptype lstart typelist lend { yyresetdict(); }
+ ;
+
+seticmptype:
+ IPFY_ICMPTYPE { if (frc->fr_family == AF_UNSPEC)
+ frc->fr_family = AF_INET;
+ if (frc->fr_family == AF_INET &&
+ frc->fr_type == FR_T_IPF &&
+ frc->fr_proto != IPPROTO_ICMP) {
+ yyerror("proto not icmp");
+ }
+ if (frc->fr_family == AF_INET6 &&
+ frc->fr_type == FR_T_IPF &&
+ frc->fr_proto != IPPROTO_ICMPV6) {
+ yyerror("proto not ipv6-icmp");
+ }
+ setipftype();
+ DOALL(if (fr->fr_family == AF_INET) { \
+ fr->fr_ip.fi_v = 4; \
+ fr->fr_mip.fi_v = 0xf; \
+ }
+ if (fr->fr_family == AF_INET6) { \
+ fr->fr_ip.fi_v = 6; \
+ fr->fr_mip.fi_v = 0xf; \
+ }
+ )
+ yysetdict(NULL);
+ }
+ ;
+
+icode: | seticmpcode icmpcode
+ { DOALL(fr->fr_icmp |= htons($2); fr->fr_icmpm |= htons(0xff););
+ yyresetdict();
+ }
+ | seticmpcode lstart codelist lend { yyresetdict(); }
+ ;
+
+seticmpcode:
+ IPFY_ICMPCODE { yysetdict(icmpcodewords); }
+ ;
+
+typelist:
+ icmptype
+ { DOREM(fr->fr_icmp = htons($1 << 8); fr->fr_icmpm = htons(0xff00);) }
+ | typelist lmore icmptype
+ { DOREM(fr->fr_icmp = htons($3 << 8); fr->fr_icmpm = htons(0xff00);) }
+ ;
+
+codelist:
+ icmpcode
+ { DOREM(fr->fr_icmp |= htons($1); fr->fr_icmpm |= htons(0xff);) }
+ | codelist lmore icmpcode
+ { DOREM(fr->fr_icmp &= htons(0xff00); fr->fr_icmp |= htons($3); \
+ fr->fr_icmpm |= htons(0xff);) }
+ ;
+
+age: | IPFY_AGE YY_NUMBER { DOALL(fr->fr_age[0] = $2; \
+ fr->fr_age[1] = $2;) }
+ | IPFY_AGE YY_NUMBER '/' YY_NUMBER
+ { DOALL(fr->fr_age[0] = $2; \
+ fr->fr_age[1] = $4;) }
+ ;
+
+keep: | IPFY_KEEP keepstate keep
+ | IPFY_KEEP keepfrag keep
+ ;
+
+keepstate:
+ IPFY_STATE stateoptlist { DOALL(fr->fr_flags |= FR_KEEPSTATE;)}
+ ;
+
+keepfrag:
+ IPFY_FRAGS fragoptlist { DOALL(fr->fr_flags |= FR_KEEPFRAG;) }
+ | IPFY_FRAG fragoptlist { DOALL(fr->fr_flags |= FR_KEEPFRAG;) }
+ ;
+
+fragoptlist:
+ | '(' fragopts ')'
+ ;
+
+fragopts:
+ fragopt lanother fragopts
+ | fragopt
+ ;
+
+fragopt:
+ IPFY_STRICT { DOALL(fr->fr_flags |= FR_FRSTRICT;) }
+ ;
+
+stateoptlist:
+ | '(' stateopts ')'
+ ;
+
+stateopts:
+ stateopt lanother stateopts
+ | stateopt
+ ;
+
+stateopt:
+ IPFY_LIMIT YY_NUMBER { DOALL(fr->fr_statemax = $2;) }
+ | IPFY_STRICT { DOALL(if (fr->fr_proto != IPPROTO_TCP) { \
+ YYERROR; \
+ } else if (fr->fr_flags & FR_STLOOSE) {\
+ YYERROR; \
+ } else \
+ fr->fr_flags |= FR_STSTRICT;)
+ }
+ | IPFY_LOOSE { DOALL(if (fr->fr_proto != IPPROTO_TCP) { \
+ YYERROR; \
+ } else if (fr->fr_flags & FR_STSTRICT){\
+ YYERROR; \
+ } else \
+ fr->fr_flags |= FR_STLOOSE;)
+ }
+ | IPFY_NEWISN { DOALL(if (fr->fr_proto != IPPROTO_TCP) { \
+ YYERROR; \
+ } else \
+ fr->fr_flags |= FR_NEWISN;)
+ }
+ | IPFY_NOICMPERR { DOALL(fr->fr_flags |= FR_NOICMPERR;) }
+
+ | IPFY_SYNC { DOALL(fr->fr_flags |= FR_STATESYNC;) }
+ | IPFY_AGE YY_NUMBER { DOALL(fr->fr_age[0] = $2; \
+ fr->fr_age[1] = $2;) }
+ | IPFY_AGE YY_NUMBER '/' YY_NUMBER
+ { DOALL(fr->fr_age[0] = $2; \
+ fr->fr_age[1] = $4;) }
+ | IPFY_ICMPHEAD groupname
+ { DOALL(seticmphead(&fr, $2);)
+ free($2);
+ }
+ | IPFY_NOLOG
+ { DOALL(fr->fr_nostatelog = 1;) }
+ | IPFY_RPC
+ { DOALL(fr->fr_rpc = 1;) }
+ | IPFY_RPC IPFY_IN YY_STR
+ { DOALL(fr->fr_rpc = 1;) }
+ | IPFY_MAX_SRCS YY_NUMBER
+ { DOALL(fr->fr_srctrack.ht_max_nodes = $2;) }
+ | IPFY_MAX_PER_SRC YY_NUMBER
+ { DOALL(fr->fr_srctrack.ht_max_per_node = $2; \
+ fr->fr_srctrack.ht_netmask = \
+ fr->fr_family == AF_INET ? 32: 128;)
+ }
+ | IPFY_MAX_PER_SRC YY_NUMBER '/' YY_NUMBER
+ { DOALL(fr->fr_srctrack.ht_max_per_node = $2; \
+ fr->fr_srctrack.ht_netmask = $4;)
+ }
+ ;
+
+portnum:
+ servicename { if (getport(frc, $1,
+ &($$), NULL) == -1)
+ yyerror("service unknown");
+ $$ = ntohs($$);
+ free($1);
+ }
+ | YY_NUMBER { if ($1 > 65535) /* Unsigned */
+ yyerror("invalid port number");
+ else
+ $$ = $1;
+ }
+ ;
+
+withlist:
+ withopt { nowith = 0; }
+ | withlist withopt { nowith = 0; }
+ | withlist ',' withopt { nowith = 0; }
+ ;
+
+withopt:
+ opttype { DOALL(fr->fr_flx |= $1; fr->fr_mflx |= $1;) }
+ | notwith opttype { DOALL(fr->fr_mflx |= $2;) }
+ | ipopt ipopts { yyresetdict(); }
+ | notwith ipopt ipopts { yyresetdict(); }
+ | startv6hdr ipv6hdrs { yyresetdict(); }
+ ;
+
+ipopt: IPFY_OPT { yysetdict(ipv4optwords); }
+ ;
+
+startv6hdr:
+ IPFY_V6HDR { if (frc->fr_family != AF_INET6)
+ yyerror("only available with IPv6");
+ yysetdict(ipv6optwords);
+ }
+ ;
+
+notwith:
+ IPFY_NOT { nowith = 1; }
+ | IPFY_NO { nowith = 1; }
+ ;
+
+opttype:
+ IPFY_IPOPTS { $$ = FI_OPTIONS; }
+ | IPFY_SHORT { $$ = FI_SHORT; }
+ | IPFY_NAT { $$ = FI_NATED; }
+ | IPFY_BAD { $$ = FI_BAD; }
+ | IPFY_BADNAT { $$ = FI_BADNAT; }
+ | IPFY_BADSRC { $$ = FI_BADSRC; }
+ | IPFY_LOWTTL { $$ = FI_LOWTTL; }
+ | IPFY_FRAG { $$ = FI_FRAG; }
+ | IPFY_FRAGBODY { $$ = FI_FRAGBODY; }
+ | IPFY_FRAGS { $$ = FI_FRAG; }
+ | IPFY_MBCAST { $$ = FI_MBCAST; }
+ | IPFY_MULTICAST { $$ = FI_MULTICAST; }
+ | IPFY_BROADCAST { $$ = FI_BROADCAST; }
+ | IPFY_STATE { $$ = FI_STATE; }
+ | IPFY_OOW { $$ = FI_OOW; }
+ | IPFY_AH { $$ = FI_AH; }
+ | IPFY_V6HDRS { $$ = FI_V6EXTHDR; }
+ ;
+
+ipopts: optlist { DOALL(fr->fr_mip.fi_optmsk |= $1;
+ if (fr->fr_family == AF_UNSPEC) {
+ fr->fr_family = AF_INET;
+ fr->fr_ip.fi_v = 4;
+ fr->fr_mip.fi_v = 0xf;
+ } else if (fr->fr_family != AF_INET) {
+ YYERROR;
+ }
+ if (!nowith)
+ fr->fr_ip.fi_optmsk |= $1;)
+ }
+ ;
+
+optlist:
+ opt { $$ |= $1; }
+ | optlist ',' opt { $$ |= $1 | $3; }
+ ;
+
+ipv6hdrs:
+ ipv6hdrlist { DOALL(fr->fr_mip.fi_optmsk |= $1;
+ if (!nowith)
+ fr->fr_ip.fi_optmsk |= $1;)
+ }
+ ;
+
+ipv6hdrlist:
+ ipv6hdr { $$ |= $1; }
+ | ipv6hdrlist ',' ipv6hdr { $$ |= $1 | $3; }
+ ;
+
+secname:
+ seclevel { $$ |= $1; }
+ | secname ',' seclevel { $$ |= $1 | $3; }
+ ;
+
+seclevel:
+ IPFY_SEC_UNC { $$ = secbit(IPSO_CLASS_UNCL); }
+ | IPFY_SEC_CONF { $$ = secbit(IPSO_CLASS_CONF); }
+ | IPFY_SEC_RSV1 { $$ = secbit(IPSO_CLASS_RES1); }
+ | IPFY_SEC_RSV2 { $$ = secbit(IPSO_CLASS_RES2); }
+ | IPFY_SEC_RSV3 { $$ = secbit(IPSO_CLASS_RES3); }
+ | IPFY_SEC_RSV4 { $$ = secbit(IPSO_CLASS_RES4); }
+ | IPFY_SEC_SEC { $$ = secbit(IPSO_CLASS_SECR); }
+ | IPFY_SEC_TS { $$ = secbit(IPSO_CLASS_TOPS); }
+ ;
+
+icmptype:
+ YY_NUMBER { $$ = $1; }
+ | YY_STR { $$ = geticmptype(frc->fr_family, $1);
+ if ($$ == -1)
+ yyerror("unrecognised icmp type");
+ }
+ ;
+
+icmpcode:
+ YY_NUMBER { $$ = $1; }
+ | IPFY_ICMPC_NETUNR { $$ = ICMP_UNREACH_NET; }
+ | IPFY_ICMPC_HSTUNR { $$ = ICMP_UNREACH_HOST; }
+ | IPFY_ICMPC_PROUNR { $$ = ICMP_UNREACH_PROTOCOL; }
+ | IPFY_ICMPC_PORUNR { $$ = ICMP_UNREACH_PORT; }
+ | IPFY_ICMPC_NEEDF { $$ = ICMP_UNREACH_NEEDFRAG; }
+ | IPFY_ICMPC_SRCFAIL { $$ = ICMP_UNREACH_SRCFAIL; }
+ | IPFY_ICMPC_NETUNK { $$ = ICMP_UNREACH_NET_UNKNOWN; }
+ | IPFY_ICMPC_HSTUNK { $$ = ICMP_UNREACH_HOST_UNKNOWN; }
+ | IPFY_ICMPC_ISOLATE { $$ = ICMP_UNREACH_ISOLATED; }
+ | IPFY_ICMPC_NETPRO { $$ = ICMP_UNREACH_NET_PROHIB; }
+ | IPFY_ICMPC_HSTPRO { $$ = ICMP_UNREACH_HOST_PROHIB; }
+ | IPFY_ICMPC_NETTOS { $$ = ICMP_UNREACH_TOSNET; }
+ | IPFY_ICMPC_HSTTOS { $$ = ICMP_UNREACH_TOSHOST; }
+ | IPFY_ICMPC_FLTPRO { $$ = ICMP_UNREACH_ADMIN_PROHIBIT; }
+ | IPFY_ICMPC_HSTPRE { $$ = 14; }
+ | IPFY_ICMPC_CUTPRE { $$ = 15; }
+ ;
+
+opt:
+ IPFY_IPOPT_NOP { $$ = getoptbyvalue(IPOPT_NOP); }
+ | IPFY_IPOPT_RR { $$ = getoptbyvalue(IPOPT_RR); }
+ | IPFY_IPOPT_ZSU { $$ = getoptbyvalue(IPOPT_ZSU); }
+ | IPFY_IPOPT_MTUP { $$ = getoptbyvalue(IPOPT_MTUP); }
+ | IPFY_IPOPT_MTUR { $$ = getoptbyvalue(IPOPT_MTUR); }
+ | IPFY_IPOPT_ENCODE { $$ = getoptbyvalue(IPOPT_ENCODE); }
+ | IPFY_IPOPT_TS { $$ = getoptbyvalue(IPOPT_TS); }
+ | IPFY_IPOPT_TR { $$ = getoptbyvalue(IPOPT_TR); }
+ | IPFY_IPOPT_SEC { $$ = getoptbyvalue(IPOPT_SECURITY); }
+ | IPFY_IPOPT_LSRR { $$ = getoptbyvalue(IPOPT_LSRR); }
+ | IPFY_IPOPT_ESEC { $$ = getoptbyvalue(IPOPT_E_SEC); }
+ | IPFY_IPOPT_CIPSO { $$ = getoptbyvalue(IPOPT_CIPSO); }
+ | IPFY_IPOPT_CIPSO doi { $$ = getoptbyvalue(IPOPT_CIPSO); }
+ | IPFY_IPOPT_SATID { $$ = getoptbyvalue(IPOPT_SATID); }
+ | IPFY_IPOPT_SSRR { $$ = getoptbyvalue(IPOPT_SSRR); }
+ | IPFY_IPOPT_ADDEXT { $$ = getoptbyvalue(IPOPT_ADDEXT); }
+ | IPFY_IPOPT_VISA { $$ = getoptbyvalue(IPOPT_VISA); }
+ | IPFY_IPOPT_IMITD { $$ = getoptbyvalue(IPOPT_IMITD); }
+ | IPFY_IPOPT_EIP { $$ = getoptbyvalue(IPOPT_EIP); }
+ | IPFY_IPOPT_FINN { $$ = getoptbyvalue(IPOPT_FINN); }
+ | IPFY_IPOPT_DPS { $$ = getoptbyvalue(IPOPT_DPS); }
+ | IPFY_IPOPT_SDB { $$ = getoptbyvalue(IPOPT_SDB); }
+ | IPFY_IPOPT_NSAPA { $$ = getoptbyvalue(IPOPT_NSAPA); }
+ | IPFY_IPOPT_RTRALRT { $$ = getoptbyvalue(IPOPT_RTRALRT); }
+ | IPFY_IPOPT_UMP { $$ = getoptbyvalue(IPOPT_UMP); }
+ | setsecclass secname
+ { DOALL(fr->fr_mip.fi_secmsk |= $2;
+ if (fr->fr_family == AF_UNSPEC) {
+ fr->fr_family = AF_INET;
+ fr->fr_ip.fi_v = 4;
+ fr->fr_mip.fi_v = 0xf;
+ } else if (fr->fr_family != AF_INET) {
+ YYERROR;
+ }
+ if (!nowith)
+ fr->fr_ip.fi_secmsk |= $2;)
+ $$ = 0;
+ yyresetdict();
+ }
+ ;
+
+setsecclass:
+ IPFY_SECCLASS { yysetdict(ipv4secwords); }
+ ;
+
+doi: IPFY_DOI YY_NUMBER { DOALL(fr->fr_doimask = 0xffffffff; \
+ if (!nowith) \
+ fr->fr_doi = $2;) }
+ | IPFY_DOI YY_HEX { DOALL(fr->fr_doimask = 0xffffffff; \
+ if (!nowith) \
+ fr->fr_doi = $2;) }
+ ;
+
+ipv6hdr:
+ IPFY_AH { $$ = getv6optbyvalue(IPPROTO_AH); }
+ | IPFY_IPV6OPT_DSTOPTS { $$ = getv6optbyvalue(IPPROTO_DSTOPTS); }
+ | IPFY_IPV6OPT_ESP { $$ = getv6optbyvalue(IPPROTO_ESP); }
+ | IPFY_IPV6OPT_HOPOPTS { $$ = getv6optbyvalue(IPPROTO_HOPOPTS); }
+ | IPFY_IPV6OPT_IPV6 { $$ = getv6optbyvalue(IPPROTO_IPV6); }
+ | IPFY_IPV6OPT_NONE { $$ = getv6optbyvalue(IPPROTO_NONE); }
+ | IPFY_IPV6OPT_ROUTING { $$ = getv6optbyvalue(IPPROTO_ROUTING); }
+ | IPFY_IPV6OPT_FRAG { $$ = getv6optbyvalue(IPPROTO_FRAGMENT); }
+ | IPFY_IPV6OPT_MOBILITY { $$ = getv6optbyvalue(IPPROTO_MOBILITY); }
+ ;
+
+level: IPFY_LEVEL { setsyslog(); }
+ ;
+
+loglevel:
+ priority { fr->fr_loglevel = LOG_LOCAL0|$1; }
+ | facility '.' priority { fr->fr_loglevel = $1 | $3; }
+ ;
+
+facility:
+ IPFY_FAC_KERN { $$ = LOG_KERN; }
+ | IPFY_FAC_USER { $$ = LOG_USER; }
+ | IPFY_FAC_MAIL { $$ = LOG_MAIL; }
+ | IPFY_FAC_DAEMON { $$ = LOG_DAEMON; }
+ | IPFY_FAC_AUTH { $$ = LOG_AUTH; }
+ | IPFY_FAC_SYSLOG { $$ = LOG_SYSLOG; }
+ | IPFY_FAC_LPR { $$ = LOG_LPR; }
+ | IPFY_FAC_NEWS { $$ = LOG_NEWS; }
+ | IPFY_FAC_UUCP { $$ = LOG_UUCP; }
+ | IPFY_FAC_CRON { $$ = LOG_CRON; }
+ | IPFY_FAC_FTP { $$ = LOG_FTP; }
+ | IPFY_FAC_AUTHPRIV { $$ = LOG_AUTHPRIV; }
+ | IPFY_FAC_AUDIT { $$ = LOG_AUDIT; }
+ | IPFY_FAC_LFMT { $$ = LOG_LFMT; }
+ | IPFY_FAC_LOCAL0 { $$ = LOG_LOCAL0; }
+ | IPFY_FAC_LOCAL1 { $$ = LOG_LOCAL1; }
+ | IPFY_FAC_LOCAL2 { $$ = LOG_LOCAL2; }
+ | IPFY_FAC_LOCAL3 { $$ = LOG_LOCAL3; }
+ | IPFY_FAC_LOCAL4 { $$ = LOG_LOCAL4; }
+ | IPFY_FAC_LOCAL5 { $$ = LOG_LOCAL5; }
+ | IPFY_FAC_LOCAL6 { $$ = LOG_LOCAL6; }
+ | IPFY_FAC_LOCAL7 { $$ = LOG_LOCAL7; }
+ | IPFY_FAC_SECURITY { $$ = LOG_SECURITY; }
+ ;
+
+priority:
+ IPFY_PRI_EMERG { $$ = LOG_EMERG; }
+ | IPFY_PRI_ALERT { $$ = LOG_ALERT; }
+ | IPFY_PRI_CRIT { $$ = LOG_CRIT; }
+ | IPFY_PRI_ERR { $$ = LOG_ERR; }
+ | IPFY_PRI_WARN { $$ = LOG_WARNING; }
+ | IPFY_PRI_NOTICE { $$ = LOG_NOTICE; }
+ | IPFY_PRI_INFO { $$ = LOG_INFO; }
+ | IPFY_PRI_DEBUG { $$ = LOG_DEBUG; }
+ ;
+
+compare:
+ YY_CMP_EQ { $$ = FR_EQUAL; }
+ | YY_CMP_NE { $$ = FR_NEQUAL; }
+ | YY_CMP_LT { $$ = FR_LESST; }
+ | YY_CMP_LE { $$ = FR_LESSTE; }
+ | YY_CMP_GT { $$ = FR_GREATERT; }
+ | YY_CMP_GE { $$ = FR_GREATERTE; }
+ ;
+
+range: YY_RANGE_IN { $$ = FR_INRANGE; }
+ | YY_RANGE_OUT { $$ = FR_OUTRANGE; }
+ | ':' { $$ = FR_INCRANGE; }
+ ;
+
+servicename:
+ YY_STR { $$ = $1; }
+ ;
+
+interfacename: name { $$ = $1; }
+ | name ':' YY_NUMBER
+ { $$ = $1;
+ fprintf(stderr, "%d: Logical interface %s:%d unsupported, "
+ "use the physical interface %s instead.\n",
+ yylineNum, $1, $3, $1);
+ }
+ ;
+
+name: YY_STR { $$ = $1; }
+ | '-' { $$ = strdup("-"); }
+ ;
+
+ipv4_16:
+ YY_NUMBER '.' YY_NUMBER
+ { if ($1 > 255 || $3 > 255) {
+ yyerror("Invalid octet string for IP address");
+ return(0);
+ }
+ $$.s_addr = ($1 << 24) | ($3 << 16);
+ $$.s_addr = htonl($$.s_addr);
+ }
+ ;
+
+ipv4_24:
+ ipv4_16 '.' YY_NUMBER
+ { if ($3 > 255) {
+ yyerror("Invalid octet string for IP address");
+ return(0);
+ }
+ $$.s_addr |= htonl($3 << 8);
+ }
+ ;
+
+ipv4: ipv4_24 '.' YY_NUMBER
+ { if ($3 > 255) {
+ yyerror("Invalid octet string for IP address");
+ return(0);
+ }
+ $$.s_addr |= htonl($3);
+ }
+ | ipv4_24
+ | ipv4_16
+ ;
+
+%%
+
+
+static struct wordtab ipfwords[] = {
+ { "age", IPFY_AGE },
+ { "ah", IPFY_AH },
+ { "all", IPFY_ALL },
+ { "and", IPFY_AND },
+ { "auth", IPFY_AUTH },
+ { "bad", IPFY_BAD },
+ { "bad-nat", IPFY_BADNAT },
+ { "bad-src", IPFY_BADSRC },
+ { "bcast", IPFY_BROADCAST },
+ { "block", IPFY_BLOCK },
+ { "body", IPFY_BODY },
+ { "bpf-v4", IPFY_BPFV4 },
+#ifdef USE_INET6
+ { "bpf-v6", IPFY_BPFV6 },
+#endif
+ { "call", IPFY_CALL },
+ { "code", IPFY_ICMPCODE },
+ { "comment", IPFY_COMMENT },
+ { "count", IPFY_COUNT },
+ { "decapsulate", IPFY_DECAPS },
+ { "dstlist", IPFY_DSTLIST },
+ { "doi", IPFY_DOI },
+ { "dup-to", IPFY_DUPTO },
+ { "eq", YY_CMP_EQ },
+ { "esp", IPFY_ESP },
+ { "exp", IPFY_IPFEXPR },
+ { "family", IPFY_FAMILY },
+ { "fastroute", IPFY_FROUTE },
+ { "first", IPFY_FIRST },
+ { "flags", IPFY_FLAGS },
+ { "frag", IPFY_FRAG },
+ { "frag-body", IPFY_FRAGBODY },
+ { "frags", IPFY_FRAGS },
+ { "from", IPFY_FROM },
+ { "ge", YY_CMP_GE },
+ { "group", IPFY_GROUP },
+ { "gt", YY_CMP_GT },
+ { "head", IPFY_HEAD },
+ { "icmp", IPFY_ICMP },
+ { "icmp-head", IPFY_ICMPHEAD },
+ { "icmp-type", IPFY_ICMPTYPE },
+ { "in", IPFY_IN },
+ { "in-via", IPFY_INVIA },
+ { "inet", IPFY_INET },
+ { "inet6", IPFY_INET6 },
+ { "ipopt", IPFY_IPOPTS },
+ { "ipopts", IPFY_IPOPTS },
+ { "keep", IPFY_KEEP },
+ { "l5-as", IPFY_L5AS },
+ { "le", YY_CMP_LE },
+ { "level", IPFY_LEVEL },
+ { "limit", IPFY_LIMIT },
+ { "log", IPFY_LOG },
+ { "loose", IPFY_LOOSE },
+ { "lowttl", IPFY_LOWTTL },
+ { "lt", YY_CMP_LT },
+ { "mask", IPFY_MASK },
+ { "match-tag", IPFY_MATCHTAG },
+ { "max-per-src", IPFY_MAX_PER_SRC },
+ { "max-srcs", IPFY_MAX_SRCS },
+ { "mbcast", IPFY_MBCAST },
+ { "mcast", IPFY_MULTICAST },
+ { "multicast", IPFY_MULTICAST },
+ { "nat", IPFY_NAT },
+ { "ne", YY_CMP_NE },
+ { "net", IPFY_NETWORK },
+ { "newisn", IPFY_NEWISN },
+ { "no", IPFY_NO },
+ { "no-icmp-err", IPFY_NOICMPERR },
+ { "nolog", IPFY_NOLOG },
+ { "nomatch", IPFY_NOMATCH },
+ { "now", IPFY_NOW },
+ { "not", IPFY_NOT },
+ { "oow", IPFY_OOW },
+ { "on", IPFY_ON },
+ { "opt", IPFY_OPT },
+ { "or-block", IPFY_ORBLOCK },
+ { "out", IPFY_OUT },
+ { "out-via", IPFY_OUTVIA },
+ { "pass", IPFY_PASS },
+ { "port", IPFY_PORT },
+ { "pps", IPFY_PPS },
+ { "preauth", IPFY_PREAUTH },
+ { "proto", IPFY_PROTO },
+ { "quick", IPFY_QUICK },
+ { "reply-to", IPFY_REPLY_TO },
+ { "return-icmp", IPFY_RETICMP },
+ { "return-icmp-as-dest", IPFY_RETICMPASDST },
+ { "return-rst", IPFY_RETRST },
+ { "route-to", IPFY_ROUTETO },
+ { "rule-ttl", IPFY_RULETTL },
+ { "rpc", IPFY_RPC },
+ { "sec-class", IPFY_SECCLASS },
+ { "set", IPFY_SET },
+ { "set-tag", IPFY_SETTAG },
+ { "skip", IPFY_SKIP },
+ { "short", IPFY_SHORT },
+ { "state", IPFY_STATE },
+ { "state-age", IPFY_AGE },
+ { "strict", IPFY_STRICT },
+ { "sync", IPFY_SYNC },
+ { "tcp", IPFY_TCP },
+ { "tcp-udp", IPFY_TCPUDP },
+ { "tos", IPFY_TOS },
+ { "to", IPFY_TO },
+ { "ttl", IPFY_TTL },
+ { "udp", IPFY_UDP },
+ { "v6hdr", IPFY_V6HDR },
+ { "v6hdrs", IPFY_V6HDRS },
+ { "with", IPFY_WITH },
+ { NULL, 0 }
+};
+
+static struct wordtab addrwords[] = {
+ { "any", IPFY_ANY },
+ { "hash", IPFY_HASH },
+ { "pool", IPFY_POOL },
+ { NULL, 0 }
+};
+
+static struct wordtab maskwords[] = {
+ { "broadcast", IPFY_BROADCAST },
+ { "netmasked", IPFY_NETMASKED },
+ { "network", IPFY_NETWORK },
+ { "peer", IPFY_PEER },
+ { NULL, 0 }
+};
+
+static struct wordtab icmpcodewords[] = {
+ { "cutoff-preced", IPFY_ICMPC_CUTPRE },
+ { "filter-prohib", IPFY_ICMPC_FLTPRO },
+ { "isolate", IPFY_ICMPC_ISOLATE },
+ { "needfrag", IPFY_ICMPC_NEEDF },
+ { "net-prohib", IPFY_ICMPC_NETPRO },
+ { "net-tos", IPFY_ICMPC_NETTOS },
+ { "host-preced", IPFY_ICMPC_HSTPRE },
+ { "host-prohib", IPFY_ICMPC_HSTPRO },
+ { "host-tos", IPFY_ICMPC_HSTTOS },
+ { "host-unk", IPFY_ICMPC_HSTUNK },
+ { "host-unr", IPFY_ICMPC_HSTUNR },
+ { "net-unk", IPFY_ICMPC_NETUNK },
+ { "net-unr", IPFY_ICMPC_NETUNR },
+ { "port-unr", IPFY_ICMPC_PORUNR },
+ { "proto-unr", IPFY_ICMPC_PROUNR },
+ { "srcfail", IPFY_ICMPC_SRCFAIL },
+ { NULL, 0 },
+};
+
+static struct wordtab ipv4optwords[] = {
+ { "addext", IPFY_IPOPT_ADDEXT },
+ { "cipso", IPFY_IPOPT_CIPSO },
+ { "dps", IPFY_IPOPT_DPS },
+ { "e-sec", IPFY_IPOPT_ESEC },
+ { "eip", IPFY_IPOPT_EIP },
+ { "encode", IPFY_IPOPT_ENCODE },
+ { "finn", IPFY_IPOPT_FINN },
+ { "imitd", IPFY_IPOPT_IMITD },
+ { "lsrr", IPFY_IPOPT_LSRR },
+ { "mtup", IPFY_IPOPT_MTUP },
+ { "mtur", IPFY_IPOPT_MTUR },
+ { "nop", IPFY_IPOPT_NOP },
+ { "nsapa", IPFY_IPOPT_NSAPA },
+ { "rr", IPFY_IPOPT_RR },
+ { "rtralrt", IPFY_IPOPT_RTRALRT },
+ { "satid", IPFY_IPOPT_SATID },
+ { "sdb", IPFY_IPOPT_SDB },
+ { "sec", IPFY_IPOPT_SEC },
+ { "ssrr", IPFY_IPOPT_SSRR },
+ { "tr", IPFY_IPOPT_TR },
+ { "ts", IPFY_IPOPT_TS },
+ { "ump", IPFY_IPOPT_UMP },
+ { "visa", IPFY_IPOPT_VISA },
+ { "zsu", IPFY_IPOPT_ZSU },
+ { NULL, 0 },
+};
+
+static struct wordtab ipv4secwords[] = {
+ { "confid", IPFY_SEC_CONF },
+ { "reserv-1", IPFY_SEC_RSV1 },
+ { "reserv-2", IPFY_SEC_RSV2 },
+ { "reserv-3", IPFY_SEC_RSV3 },
+ { "reserv-4", IPFY_SEC_RSV4 },
+ { "secret", IPFY_SEC_SEC },
+ { "topsecret", IPFY_SEC_TS },
+ { "unclass", IPFY_SEC_UNC },
+ { NULL, 0 },
+};
+
+static struct wordtab ipv6optwords[] = {
+ { "dstopts", IPFY_IPV6OPT_DSTOPTS },
+ { "esp", IPFY_IPV6OPT_ESP },
+ { "frag", IPFY_IPV6OPT_FRAG },
+ { "hopopts", IPFY_IPV6OPT_HOPOPTS },
+ { "ipv6", IPFY_IPV6OPT_IPV6 },
+ { "mobility", IPFY_IPV6OPT_MOBILITY },
+ { "none", IPFY_IPV6OPT_NONE },
+ { "routing", IPFY_IPV6OPT_ROUTING },
+ { NULL, 0 },
+};
+
+static struct wordtab logwords[] = {
+ { "kern", IPFY_FAC_KERN },
+ { "user", IPFY_FAC_USER },
+ { "mail", IPFY_FAC_MAIL },
+ { "daemon", IPFY_FAC_DAEMON },
+ { "auth", IPFY_FAC_AUTH },
+ { "syslog", IPFY_FAC_SYSLOG },
+ { "lpr", IPFY_FAC_LPR },
+ { "news", IPFY_FAC_NEWS },
+ { "uucp", IPFY_FAC_UUCP },
+ { "cron", IPFY_FAC_CRON },
+ { "ftp", IPFY_FAC_FTP },
+ { "authpriv", IPFY_FAC_AUTHPRIV },
+ { "audit", IPFY_FAC_AUDIT },
+ { "logalert", IPFY_FAC_LFMT },
+ { "console", IPFY_FAC_CONSOLE },
+ { "security", IPFY_FAC_SECURITY },
+ { "local0", IPFY_FAC_LOCAL0 },
+ { "local1", IPFY_FAC_LOCAL1 },
+ { "local2", IPFY_FAC_LOCAL2 },
+ { "local3", IPFY_FAC_LOCAL3 },
+ { "local4", IPFY_FAC_LOCAL4 },
+ { "local5", IPFY_FAC_LOCAL5 },
+ { "local6", IPFY_FAC_LOCAL6 },
+ { "local7", IPFY_FAC_LOCAL7 },
+ { "emerg", IPFY_PRI_EMERG },
+ { "alert", IPFY_PRI_ALERT },
+ { "crit", IPFY_PRI_CRIT },
+ { "err", IPFY_PRI_ERR },
+ { "warn", IPFY_PRI_WARN },
+ { "notice", IPFY_PRI_NOTICE },
+ { "info", IPFY_PRI_INFO },
+ { "debug", IPFY_PRI_DEBUG },
+ { NULL, 0 },
+};
+
+
+
+
+int
+ipf_parsefile(int fd, addfunc_t addfunc, ioctlfunc_t *iocfuncs, char *filename)
+{
+ FILE *fp = NULL;
+ char *s;
+
+ yylineNum = 1;
+ yysettab(ipfwords);
+
+ s = getenv("YYDEBUG");
+ if (s != NULL)
+ yydebug = atoi(s);
+ else
+ yydebug = 0;
+
+ if (strcmp(filename, "-")) {
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "fopen(%s) failed: %s\n", filename,
+ STRERROR(errno));
+ return(-1);
+ }
+ } else
+ fp = stdin;
+
+ while (ipf_parsesome(fd, addfunc, iocfuncs, fp) == 1)
+ ;
+ if (fp != NULL)
+ fclose(fp);
+ return(0);
+}
+
+
+int
+ipf_parsesome(int fd, addfunc_t addfunc, ioctlfunc_t *iocfuncs, FILE *fp)
+{
+ char *s;
+ int i;
+
+ ipffd = fd;
+ for (i = 0; i <= IPL_LOGMAX; i++)
+ ipfioctls[i] = iocfuncs[i];
+ ipfaddfunc = addfunc;
+
+ if (feof(fp))
+ return(0);
+ i = fgetc(fp);
+ if (i == EOF)
+ return(0);
+ if (ungetc(i, fp) == 0)
+ return(0);
+ if (feof(fp))
+ return(0);
+ s = getenv("YYDEBUG");
+ if (s != NULL)
+ yydebug = atoi(s);
+ else
+ yydebug = 0;
+
+ yyin = fp;
+ yyparse();
+ return(1);
+}
+
+
+static void
+newrule(void)
+{
+ frentry_t *frn;
+
+ frn = allocfr();
+ for (fr = frtop; fr != NULL && fr->fr_next != NULL; fr = fr->fr_next)
+ ;
+ if (fr != NULL) {
+ fr->fr_next = frn;
+ frn->fr_pnext = &fr->fr_next;
+ }
+ if (frtop == NULL) {
+ frtop = frn;
+ frn->fr_pnext = &frtop;
+ }
+ fr = frn;
+ frc = frn;
+ fr->fr_loglevel = 0xffff;
+ fr->fr_isc = (void *)-1;
+ fr->fr_logtag = FR_NOLOGTAG;
+ fr->fr_type = FR_T_NONE;
+ fr->fr_flineno = yylineNum;
+
+ if (use_inet6 == 1)
+ fr->fr_family = AF_INET6;
+ else if (use_inet6 == -1)
+ fr->fr_family = AF_INET;
+
+ nrules = 1;
+}
+
+
+static void
+setipftype(void)
+{
+ for (fr = frc; fr != NULL; fr = fr->fr_next) {
+ if (fr->fr_type == FR_T_NONE) {
+ fr->fr_type = FR_T_IPF;
+ fr->fr_data = (void *)calloc(sizeof(fripf_t), 1);
+ fr->fr_dsize = sizeof(fripf_t);
+ fr->fr_family = frc->fr_family;
+ if (fr->fr_family == AF_INET) {
+ fr->fr_ip.fi_v = 4;
+ }
+ else if (fr->fr_family == AF_INET6) {
+ fr->fr_ip.fi_v = 6;
+ }
+ fr->fr_mip.fi_v = 0xf;
+ fr->fr_ipf->fri_sifpidx = -1;
+ fr->fr_ipf->fri_difpidx = -1;
+ }
+ if (fr->fr_type != FR_T_IPF) {
+ fprintf(stderr, "IPF Type not set\n");
+ }
+ }
+}
+
+
+static frentry_t *
+addrule(void)
+{
+ frentry_t *f, *f1, *f2;
+ int count;
+
+ for (f2 = frc; f2->fr_next != NULL; f2 = f2->fr_next)
+ ;
+
+ count = nrules;
+ f = f2;
+ for (f1 = frc; count > 0; count--, f1 = f1->fr_next) {
+ f->fr_next = allocfr();
+ if (f->fr_next == NULL)
+ return(NULL);
+ f->fr_next->fr_pnext = &f->fr_next;
+ added++;
+ f = f->fr_next;
+ *f = *f1;
+ f->fr_next = NULL;
+ if (f->fr_caddr != NULL) {
+ f->fr_caddr = malloc(f->fr_dsize);
+ bcopy(f1->fr_caddr, f->fr_caddr, f->fr_dsize);
+ }
+ }
+
+ return(f2->fr_next);
+}
+
+
+static int
+lookuphost(char *name, i6addr_t *addrp)
+{
+ int i;
+
+ hashed = 0;
+ pooled = 0;
+ dynamic = -1;
+
+ for (i = 0; i < 4; i++) {
+ if (fr->fr_ifnames[i] == -1)
+ continue;
+ if (strcmp(name, fr->fr_names + fr->fr_ifnames[i]) == 0) {
+ ifpflag = FRI_DYNAMIC;
+ dynamic = addname(&fr, name);
+ return(1);
+ }
+ }
+
+ if (gethost(AF_INET, name, addrp) == -1) {
+ fprintf(stderr, "unknown name \"%s\"\n", name);
+ return(-1);
+ }
+ return(0);
+}
+
+
+static void
+dobpf(int v, char *phrase)
+{
+#ifdef IPFILTER_BPF
+ struct bpf_program bpf;
+ struct pcap *p;
+#endif
+ fakebpf_t *fb;
+ u_32_t l;
+ char *s;
+ int i;
+
+ for (fr = frc; fr != NULL; fr = fr->fr_next) {
+ if (fr->fr_type != FR_T_NONE) {
+ fprintf(stderr, "cannot mix IPF and BPF matching\n");
+ return;
+ }
+ fr->fr_family = vtof(v);
+ fr->fr_type = FR_T_BPFOPC;
+
+ if (!strncmp(phrase, "0x", 2)) {
+ fb = malloc(sizeof(fakebpf_t));
+
+ for (i = 0, s = strtok(phrase, " \r\n\t"); s != NULL;
+ s = strtok(NULL, " \r\n\t"), i++) {
+ fb = reallocarray(fb, i / 4 + 1, sizeof(*fb));
+ if (fb == NULL) {
+ warnx("memory allocation error at %d in %s in %s", __LINE__, __FUNCTION__, __FILE__);
+ abort();
+ }
+ l = (u_32_t)strtol(s, NULL, 0);
+ switch (i & 3)
+ {
+ case 0 :
+ fb[i / 4].fb_c = l & 0xffff;
+ break;
+ case 1 :
+ fb[i / 4].fb_t = l & 0xff;
+ break;
+ case 2 :
+ fb[i / 4].fb_f = l & 0xff;
+ break;
+ case 3 :
+ fb[i / 4].fb_k = l;
+ break;
+ }
+ }
+ if ((i & 3) != 0) {
+ fprintf(stderr,
+ "Odd number of bytes in BPF code\n");
+ exit(1);
+ }
+ i--;
+ fr->fr_dsize = (i / 4 + 1) * sizeof(*fb);
+ fr->fr_data = fb;
+ return;
+ }
+
+#ifdef IPFILTER_BPF
+ bzero((char *)&bpf, sizeof(bpf));
+ p = pcap_open_dead(DLT_RAW, 1);
+ if (!p) {
+ fprintf(stderr, "pcap_open_dead failed\n");
+ return;
+ }
+
+ if (pcap_compile(p, &bpf, phrase, 1, 0xffffffff)) {
+ pcap_perror(p, "ipf");
+ pcap_close(p);
+ fprintf(stderr, "pcap parsing failed (%s)\n", phrase);
+ return;
+ }
+ pcap_close(p);
+
+ fr->fr_dsize = bpf.bf_len * sizeof(struct bpf_insn);
+ fr->fr_data = malloc(fr->fr_dsize);
+ bcopy((char *)bpf.bf_insns, fr->fr_data, fr->fr_dsize);
+ if (!bpf_validate(fr->fr_data, bpf.bf_len)) {
+ fprintf(stderr, "BPF validation failed\n");
+ return;
+ }
+#endif
+ }
+
+#ifdef IPFILTER_BPF
+ if (opts & OPT_DEBUG)
+ bpf_dump(&bpf, 0);
+#else
+ fprintf(stderr, "BPF filter expressions not supported\n");
+ exit(1);
+#endif
+}
+
+
+static void
+resetaddr(void)
+{
+ hashed = 0;
+ pooled = 0;
+ dynamic = -1;
+}
+
+
+static alist_t *
+newalist(alist_t *ptr)
+{
+ alist_t *al;
+
+ al = malloc(sizeof(*al));
+ if (al == NULL)
+ return(NULL);
+ al->al_not = 0;
+ al->al_next = ptr;
+ return(al);
+}
+
+
+static int
+makepool(alist_t *list)
+{
+ ip_pool_node_t *n, *top;
+ ip_pool_t pool;
+ alist_t *a;
+ int num;
+
+ if (list == NULL)
+ return(0);
+ top = calloc(1, sizeof(*top));
+ if (top == NULL)
+ return(0);
+
+ for (n = top, a = list; (n != NULL) && (a != NULL); a = a->al_next) {
+ if (use_inet6 == 1) {
+#ifdef USE_INET6
+ n->ipn_addr.adf_family = AF_INET6;
+ n->ipn_addr.adf_addr = a->al_i6addr;
+ n->ipn_addr.adf_len = offsetof(addrfamily_t,
+ adf_addr) + 16;
+ n->ipn_mask.adf_family = AF_INET6;
+ n->ipn_mask.adf_addr = a->al_i6mask;
+ n->ipn_mask.adf_len = offsetof(addrfamily_t,
+ adf_addr) + 16;
+
+#endif
+ } else {
+ n->ipn_addr.adf_family = AF_INET;
+ n->ipn_addr.adf_addr.in4.s_addr = a->al_1;
+ n->ipn_addr.adf_len = offsetof(addrfamily_t,
+ adf_addr) + 4;
+ n->ipn_mask.adf_family = AF_INET;
+ n->ipn_mask.adf_addr.in4.s_addr = a->al_2;
+ n->ipn_mask.adf_len = offsetof(addrfamily_t,
+ adf_addr) + 4;
+ }
+ n->ipn_info = a->al_not;
+ if (a->al_next != NULL) {
+ n->ipn_next = calloc(1, sizeof(*n));
+ n = n->ipn_next;
+ }
+ }
+
+ bzero((char *)&pool, sizeof(pool));
+ pool.ipo_unit = IPL_LOGIPF;
+ pool.ipo_list = top;
+ num = load_pool(&pool, ipfioctls[IPL_LOGLOOKUP]);
+
+ while ((n = top) != NULL) {
+ top = n->ipn_next;
+ free(n);
+ }
+ return(num);
+}
+
+
+static u_int
+makehash(alist_t *list)
+{
+ iphtent_t *n, *top;
+ iphtable_t iph;
+ alist_t *a;
+ int num;
+
+ if (list == NULL)
+ return(0);
+ top = calloc(1, sizeof(*top));
+ if (top == NULL)
+ return(0);
+
+ for (n = top, a = list; (n != NULL) && (a != NULL); a = a->al_next) {
+ if (a->al_family == AF_INET6) {
+ n->ipe_family = AF_INET6;
+ n->ipe_addr = a->al_i6addr;
+ n->ipe_mask = a->al_i6mask;
+ } else {
+ n->ipe_family = AF_INET;
+ n->ipe_addr.in4_addr = a->al_1;
+ n->ipe_mask.in4_addr = a->al_2;
+ }
+ n->ipe_value = 0;
+ if (a->al_next != NULL) {
+ n->ipe_next = calloc(1, sizeof(*n));
+ n = n->ipe_next;
+ }
+ }
+
+ bzero((char *)&iph, sizeof(iph));
+ iph.iph_unit = IPL_LOGIPF;
+ iph.iph_type = IPHASH_LOOKUP;
+ *iph.iph_name = '\0';
+
+ if (load_hash(&iph, top, ipfioctls[IPL_LOGLOOKUP]) == 0)
+ sscanf(iph.iph_name, "%u", &num);
+ else
+ num = 0;
+
+ while ((n = top) != NULL) {
+ top = n->ipe_next;
+ free(n);
+ }
+ return(num);
+}
+
+
+int
+ipf_addrule(int fd, ioctlfunc_t ioctlfunc, void *ptr)
+{
+ ioctlcmd_t add, del;
+ frentry_t *fr;
+ ipfobj_t obj;
+
+ if (ptr == NULL)
+ return(0);
+
+ fr = ptr;
+ add = 0;
+ del = 0;
+
+ bzero((char *)&obj, sizeof(obj));
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_size = fr->fr_size;
+ obj.ipfo_type = IPFOBJ_FRENTRY;
+ obj.ipfo_ptr = ptr;
+
+ if ((opts & OPT_DONOTHING) != 0)
+ fd = -1;
+
+ if (opts & OPT_ZERORULEST) {
+ add = SIOCZRLST;
+ } else if (opts & OPT_INACTIVE) {
+ add = (u_int)fr->fr_hits ? SIOCINIFR :
+ SIOCADIFR;
+ del = SIOCRMIFR;
+ } else {
+ add = (u_int)fr->fr_hits ? SIOCINAFR :
+ SIOCADAFR;
+ del = SIOCRMAFR;
+ }
+
+ if ((opts & OPT_OUTQUE) != 0)
+ fr->fr_flags |= FR_OUTQUE;
+ if (fr->fr_hits)
+ fr->fr_hits--;
+ if ((opts & OPT_VERBOSE) != 0)
+ printfr(fr, ioctlfunc);
+
+ if ((opts & OPT_DEBUG) != 0) {
+ binprint(fr, sizeof(*fr));
+ if (fr->fr_data != NULL)
+ binprint(fr->fr_data, fr->fr_dsize);
+ }
+
+ if ((opts & OPT_ZERORULEST) != 0) {
+ if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) {
+ if ((opts & OPT_DONOTHING) == 0) {
+ char msg[80];
+
+ snprintf(msg, sizeof(msg), "%d:ioctl(zero rule)",
+ fr->fr_flineno);
+ return(ipf_perror_fd(fd, ioctlfunc, msg));
+ }
+ } else {
+#ifdef USE_QUAD_T
+ printf("hits %qd bytes %qd ",
+ (long long)fr->fr_hits,
+ (long long)fr->fr_bytes);
+#else
+ printf("hits %ld bytes %ld ",
+ fr->fr_hits, fr->fr_bytes);
+#endif
+ printfr(fr, ioctlfunc);
+ }
+ } else if ((opts & OPT_REMOVE) != 0) {
+ if ((*ioctlfunc)(fd, del, (void *)&obj) == -1) {
+ if ((opts & OPT_DONOTHING) == 0) {
+ char msg[80];
+
+ snprintf(msg, sizeof(msg), "%d:ioctl(delete rule)",
+ fr->fr_flineno);
+ return(ipf_perror_fd(fd, ioctlfunc, msg));
+ }
+ }
+ } else {
+ if ((*ioctlfunc)(fd, add, (void *)&obj) == -1) {
+ if ((opts & OPT_DONOTHING) == 0) {
+ char msg[80];
+
+ snprintf(msg, sizeof(msg), "%d:ioctl(add/insert rule)",
+ fr->fr_flineno);
+ return(ipf_perror_fd(fd, ioctlfunc, msg));
+ }
+ }
+ }
+ return(0);
+}
+
+static void
+setsyslog(void)
+{
+ yysetdict(logwords);
+ yybreakondot = 1;
+}
+
+
+static void
+unsetsyslog(void)
+{
+ yyresetdict();
+ yybreakondot = 0;
+}
+
+
+static void
+fillgroup(frentry_t *fr)
+{
+ frentry_t *f;
+
+ for (f = frold; f != NULL; f = f->fr_next) {
+ if (f->fr_grhead == -1 && fr->fr_group == -1)
+ break;
+ if (f->fr_grhead == -1 || fr->fr_group == -1)
+ continue;
+ if (strcmp(f->fr_names + f->fr_grhead,
+ fr->fr_names + fr->fr_group) == 0)
+ break;
+ }
+
+ if (f == NULL)
+ return;
+
+ /*
+ * Only copy down matching fields if the rules are of the same type
+ * and are of ipf type. The only fields that are copied are those
+ * that impact the rule parsing itself, eg. need for knowing what the
+ * protocol should be for rules with port comparisons in them.
+ */
+ if (f->fr_type != fr->fr_type || f->fr_type != FR_T_IPF)
+ return;
+
+ if (fr->fr_family == 0 && f->fr_family != 0)
+ fr->fr_family = f->fr_family;
+
+ if (fr->fr_mproto == 0 && f->fr_mproto != 0)
+ fr->fr_mproto = f->fr_mproto;
+ if (fr->fr_proto == 0 && f->fr_proto != 0)
+ fr->fr_proto = f->fr_proto;
+
+ if ((fr->fr_mproto == 0) && ((fr->fr_flx & FI_TCPUDP) == 0) &&
+ ((f->fr_flx & FI_TCPUDP) != 0)) {
+ fr->fr_flx |= FI_TCPUDP;
+ fr->fr_mflx |= FI_TCPUDP;
+ }
+}
+
+
+static void
+doipfexpr(char *line)
+{
+ int *array;
+ char *error;
+
+ array = parseipfexpr(line, &error);
+ if (array == NULL) {
+ fprintf(stderr, "%s:", error);
+ yyerror("error parsing ipf matching expression");
+ return;
+ }
+
+ fr->fr_type = FR_T_IPFEXPR;
+ fr->fr_data = array;
+ fr->fr_dsize = array[0] * sizeof(*array);
+}
+
+
+static void
+do_tuneint(char *varname, int value)
+{
+ char buffer[80];
+
+ strncpy(buffer, varname, 60);
+ buffer[59] = '\0';
+ strcat(buffer, "=");
+ snprintf(buffer, sizeof(buffer), "%u", value);
+ ipf_dotuning(ipffd, buffer, ioctl);
+}
+
+
+static void
+do_tunestr(char *varname, char *value)
+{
+
+ if (!strcasecmp(value, "true")) {
+ do_tuneint(varname, 1);
+ } else if (!strcasecmp(value, "false")) {
+ do_tuneint(varname, 0);
+ } else {
+ yyerror("did not find true/false where expected");
+ }
+}
+
+
+static void
+setifname(frentry_t **frp, int idx, char *name)
+{
+ int pos;
+
+ pos = addname(frp, name);
+ if (pos == -1)
+ return;
+ (*frp)->fr_ifnames[idx] = pos;
+}
+
+
+static int
+addname(frentry_t **frp, char *name)
+{
+ frentry_t *f;
+ int nlen;
+ int pos;
+
+ nlen = strlen(name) + 1;
+ f = realloc(*frp, (*frp)->fr_size + nlen);
+ if (*frp == frc)
+ frc = f;
+ *frp = f;
+ if (f == NULL)
+ return(-1);
+ if (f->fr_pnext != NULL)
+ *f->fr_pnext = f;
+ f->fr_size += nlen;
+ pos = f->fr_namelen;
+ f->fr_namelen += nlen;
+ strcpy(f->fr_names + pos, name);
+ f->fr_names[f->fr_namelen] = '\0';
+ return(pos);
+}
+
+
+static frentry_t *
+allocfr(void)
+{
+ frentry_t *fr;
+
+ fr = calloc(1, sizeof(*fr));
+ if (fr != NULL) {
+ fr->fr_size = sizeof(*fr);
+ fr->fr_comment = -1;
+ fr->fr_group = -1;
+ fr->fr_grhead = -1;
+ fr->fr_icmphead = -1;
+ fr->fr_ifnames[0] = -1;
+ fr->fr_ifnames[1] = -1;
+ fr->fr_ifnames[2] = -1;
+ fr->fr_ifnames[3] = -1;
+ fr->fr_tif.fd_name = -1;
+ fr->fr_rif.fd_name = -1;
+ fr->fr_dif.fd_name = -1;
+ }
+ return(fr);
+}
+
+
+static void
+setgroup(frentry_t **frp, char *name)
+{
+ int pos;
+
+ pos = addname(frp, name);
+ if (pos == -1)
+ return;
+ (*frp)->fr_group = pos;
+}
+
+
+static void
+setgrhead(frentry_t **frp, char *name)
+{
+ int pos;
+
+ pos = addname(frp, name);
+ if (pos == -1)
+ return;
+ (*frp)->fr_grhead = pos;
+}
+
+
+static void
+seticmphead(frentry_t **frp, char *name)
+{
+ int pos;
+
+ pos = addname(frp, name);
+ if (pos == -1)
+ return;
+ (*frp)->fr_icmphead = pos;
+}
+
+
+static void
+build_dstaddr_af(frentry_t *fp, void *ptr)
+{
+ struct ipp_s *ipp = ptr;
+ frentry_t *f = fp;
+
+ if (f->fr_family != AF_UNSPEC && ipp->f == AF_UNSPEC) {
+ ipp->f = f->fr_family;
+ ipp->v = f->fr_ip.fi_v;
+ }
+ if (ipp->f == AF_INET)
+ ipp->v = 4;
+ else if (ipp->f == AF_INET6)
+ ipp->v = 6;
+
+ for (; f != NULL; f = f->fr_next) {
+ f->fr_ip.fi_dst = ipp->a;
+ f->fr_mip.fi_dst = ipp->m;
+ f->fr_family = ipp->f;
+ f->fr_ip.fi_v = ipp->v;
+ f->fr_mip.fi_v = 0xf;
+ f->fr_datype = ipp->type;
+ if (ipp->ifpos != -1)
+ f->fr_ipf->fri_difpidx = ipp->ifpos;
+ }
+ fr = NULL;
+}
+
+
+static void
+build_srcaddr_af(frentry_t *fp, void *ptr)
+{
+ struct ipp_s *ipp = ptr;
+ frentry_t *f = fp;
+
+ if (f->fr_family != AF_UNSPEC && ipp->f == AF_UNSPEC) {
+ ipp->f = f->fr_family;
+ ipp->v = f->fr_ip.fi_v;
+ }
+ if (ipp->f == AF_INET)
+ ipp->v = 4;
+ else if (ipp->f == AF_INET6)
+ ipp->v = 6;
+
+ for (; f != NULL; f = f->fr_next) {
+ f->fr_ip.fi_src = ipp->a;
+ f->fr_mip.fi_src = ipp->m;
+ f->fr_family = ipp->f;
+ f->fr_ip.fi_v = ipp->v;
+ f->fr_mip.fi_v = 0xf;
+ f->fr_satype = ipp->type;
+ f->fr_ipf->fri_sifpidx = ipp->ifpos;
+ }
+ fr = NULL;
+}
diff --git a/sbin/ipf/common/ipmon.h b/sbin/ipf/common/ipmon.h
new file mode 100644
index 000000000000..4807299c49d2
--- /dev/null
+++ b/sbin/ipf/common/ipmon.h
@@ -0,0 +1,142 @@
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2012 by Darren Reed.
+ *
+ * See the IPFILTER.LICENCE file for details on licencing.
+ *
+ * @(#)ip_fil.h 1.35 6/5/96
+ * $Id$
+ */
+
+typedef struct ipmon_msg_s {
+ int imm_msglen;
+ char *imm_msg;
+ int imm_dsize;
+ void *imm_data;
+ time_t imm_when;
+ int imm_loglevel;
+} ipmon_msg_t;
+
+typedef void (*ims_destroy_func_t)(void *);
+typedef void *(*ims_dup_func_t)(void *);
+typedef int (*ims_match_func_t)(void *, void *);
+typedef void *(*ims_parse_func_t)(char **);
+typedef void (*ims_print_func_t)(void *);
+typedef int (*ims_store_func_t)(void *, ipmon_msg_t *);
+
+typedef struct ipmon_saver_s {
+ char *ims_name;
+ ims_destroy_func_t ims_destroy;
+ ims_dup_func_t ims_dup;
+ ims_match_func_t ims_match;
+ ims_parse_func_t ims_parse;
+ ims_print_func_t ims_print;
+ ims_store_func_t ims_store;
+} ipmon_saver_t;
+
+typedef struct ipmon_saver_int_s {
+ struct ipmon_saver_int_s *imsi_next;
+ ipmon_saver_t *imsi_stor;
+ void *imsi_handle;
+} ipmon_saver_int_t;
+
+typedef struct ipmon_doing_s {
+ struct ipmon_doing_s *ipmd_next;
+ void *ipmd_token;
+ ipmon_saver_t *ipmd_saver;
+ /*
+ * ipmd_store is "cached" in this structure to avoid a double
+ * deref when doing saves....
+ */
+ int (*ipmd_store)(void *, ipmon_msg_t *);
+} ipmon_doing_t;
+
+
+typedef struct ipmon_action {
+ struct ipmon_action *ac_next;
+ int ac_mflag; /* collection of things to compare */
+ int ac_dflag; /* flags to compliment the doing fields */
+ int ac_logpri;
+ int ac_direction;
+ char ac_group[FR_GROUPLEN];
+ char ac_nattag[16];
+ u_32_t ac_logtag;
+ int ac_type; /* nat/state/ipf */
+ int ac_proto;
+ int ac_rule;
+ int ac_packet;
+ int ac_second;
+ int ac_result;
+ u_32_t ac_sip;
+ u_32_t ac_smsk;
+ u_32_t ac_dip;
+ u_32_t ac_dmsk;
+ u_short ac_sport;
+ u_short ac_dport;
+ char *ac_iface;
+ /*
+ * used with ac_packet/ac_second
+ */
+ struct timeval ac_last;
+ int ac_pktcnt;
+ /*
+ * What to do with matches
+ */
+ ipmon_doing_t *ac_doing;
+} ipmon_action_t;
+
+#define ac_lastsec ac_last.tv_sec
+#define ac_lastusec ac_last.tv_usec
+
+/*
+ * Flags indicating what fields to do matching upon (ac_mflag).
+ */
+#define IPMAC_DIRECTION 0x0001
+#define IPMAC_DSTIP 0x0002
+#define IPMAC_DSTPORT 0x0004
+#define IPMAC_EVERY 0x0008
+#define IPMAC_GROUP 0x0010
+#define IPMAC_INTERFACE 0x0020
+#define IPMAC_LOGTAG 0x0040
+#define IPMAC_NATTAG 0x0080
+#define IPMAC_PROTOCOL 0x0100
+#define IPMAC_RESULT 0x0200
+#define IPMAC_RULE 0x0400
+#define IPMAC_SRCIP 0x0800
+#define IPMAC_SRCPORT 0x1000
+#define IPMAC_TYPE 0x2000
+#define IPMAC_WITH 0x4000
+
+#define IPMR_BLOCK 1
+#define IPMR_PASS 2
+#define IPMR_NOMATCH 3
+#define IPMR_LOG 4
+
+#define IPMON_SYSLOG 0x001
+#define IPMON_RESOLVE 0x002
+#define IPMON_HEXBODY 0x004
+#define IPMON_HEXHDR 0x010
+#define IPMON_TAIL 0x020
+#define IPMON_VERBOSE 0x040
+#define IPMON_NAT 0x080
+#define IPMON_STATE 0x100
+#define IPMON_FILTER 0x200
+#define IPMON_PORTNUM 0x400
+#define IPMON_LOGALL (IPMON_NAT|IPMON_STATE|IPMON_FILTER)
+#define IPMON_LOGBODY 0x800
+
+#define HOSTNAME_V4(a,b) hostname((a), 4, (u_32_t *)&(b))
+
+#ifndef LOGFAC
+#define LOGFAC LOG_LOCAL0
+#endif
+
+extern void dump_config(void);
+extern int load_config(char *);
+extern void unload_config(void);
+extern void dumphex(FILE *, int, char *, int);
+extern int check_action(char *, char *, int, int);
+extern char *getword(int);
+extern void *add_doing(ipmon_saver_t *);
+
diff --git a/sbin/ipf/common/ipt.h b/sbin/ipf/common/ipt.h
new file mode 100644
index 000000000000..9a4d75a85ccb
--- /dev/null
+++ b/sbin/ipf/common/ipt.h
@@ -0,0 +1,40 @@
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2012 by Darren Reed.
+ *
+ * See the IPFILTER.LICENCE file for details on licencing.
+ *
+ * $Id$
+ */
+
+#ifndef __IPT_H__
+#define __IPT_H__
+
+#ifndef __P
+# define P_DEF
+# ifdef __STDC__
+# define __P(x) x
+# else
+# define __P(x) ()
+# endif
+#endif
+
+#include <fcntl.h>
+
+
+struct ipread {
+ int (*r_open)(char *);
+ int (*r_close)(void);
+ int (*r_readip)(mb_t *, char **, int *);
+ int r_flags;
+};
+
+#define R_DO_CKSUM 0x01
+
+#ifdef P_DEF
+# undef __P
+# undef P_DEF
+#endif
+
+#endif /* __IPT_H__ */
diff --git a/sbin/ipf/common/kmem.h b/sbin/ipf/common/kmem.h
new file mode 100644
index 000000000000..c4b65ed63ce9
--- /dev/null
+++ b/sbin/ipf/common/kmem.h
@@ -0,0 +1,30 @@
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2012 by Darren Reed.
+ *
+ * See the IPFILTER.LICENCE file for details on licencing.
+ * $Id$
+ */
+
+#ifndef __KMEM_H__
+#define __KMEM_H__
+
+#ifndef __P
+# define __P(x) x
+#endif
+extern int openkmem(char *, char *);
+extern int kmemcpy(char *, long, int);
+extern int kstrncpy(char *, long, int);
+
+#if defined(__NetBSD__) || defined(__OpenBSD)
+# include <paths.h>
+#endif
+
+#ifdef _PATH_KMEM
+# define KMEM _PATH_KMEM
+#else
+# define KMEM "/dev/kmem"
+#endif
+
+#endif /* __KMEM_H__ */
diff --git a/sbin/ipf/common/lexer.c b/sbin/ipf/common/lexer.c
new file mode 100644
index 000000000000..16fbb2272034
--- /dev/null
+++ b/sbin/ipf/common/lexer.c
@@ -0,0 +1,737 @@
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2012 by Darren Reed.
+ *
+ * See the IPFILTER.LICENCE file for details on licencing.
+ */
+#include <ctype.h>
+#include "ipf.h"
+#ifdef IPFILTER_SCAN
+# include "netinet/ip_scan.h"
+#endif
+#include <sys/ioctl.h>
+#include <syslog.h>
+#ifdef TEST_LEXER
+# define NO_YACC
+union {
+ int num;
+ char *str;
+ struct in_addr ipa;
+ i6addr_t ip6;
+} yylval;
+#endif
+#include "lexer.h"
+#include "y.tab.h"
+
+FILE *yyin;
+
+#define ishex(c) (ISDIGIT(c) || ((c) >= 'a' && (c) <= 'f') || \
+ ((c) >= 'A' && (c) <= 'F'))
+#define TOOLONG -3
+
+extern int string_start;
+extern int string_end;
+extern char *string_val;
+extern int pos;
+extern int yydebug;
+
+char *yystr = NULL;
+int yytext[YYBUFSIZ+1];
+char yychars[YYBUFSIZ+1];
+int yylineNum = 1;
+int yypos = 0;
+int yylast = -1;
+int yydictfixed = 0;
+int yyexpectaddr = 0;
+int yybreakondot = 0;
+int yyvarnext = 0;
+int yytokentype = 0;
+wordtab_t *yywordtab = NULL;
+int yysavedepth = 0;
+wordtab_t *yysavewords[30];
+
+
+static wordtab_t *yyfindkey(char *);
+static int yygetc(int);
+static void yyunputc(int);
+static int yyswallow(int);
+static char *yytexttostr(int, int);
+static void yystrtotext(char *);
+static char *yytexttochar(void);
+
+static int
+yygetc(int docont)
+{
+ int c;
+
+ if (yypos < yylast) {
+ c = yytext[yypos++];
+ if (c == '\n')
+ yylineNum++;
+ return (c);
+ }
+
+ if (yypos == YYBUFSIZ)
+ return (TOOLONG);
+
+ if (pos >= string_start && pos <= string_end) {
+ c = string_val[pos - string_start];
+ yypos++;
+ } else {
+ c = fgetc(yyin);
+ if (docont && (c == '\\')) {
+ c = fgetc(yyin);
+ if (c == '\n') {
+ yylineNum++;
+ c = fgetc(yyin);
+ }
+ }
+ }
+ if (c == '\n')
+ yylineNum++;
+ yytext[yypos++] = c;
+ yylast = yypos;
+ yytext[yypos] = '\0';
+
+ return (c);
+}
+
+
+static void
+yyunputc(int c)
+{
+ if (c == '\n')
+ yylineNum--;
+ yytext[--yypos] = c;
+}
+
+
+static int
+yyswallow(int last)
+{
+ int c;
+
+ while (((c = yygetc(0)) > '\0') && (c != last))
+ ;
+
+ if (c != EOF)
+ yyunputc(c);
+ if (c == last)
+ return (0);
+ return (-1);
+}
+
+
+static char *
+yytexttochar(void)
+{
+ int i;
+
+ for (i = 0; i < yypos; i++)
+ yychars[i] = (char)(yytext[i] & 0xff);
+ yychars[i] = '\0';
+ return (yychars);
+}
+
+
+static void
+yystrtotext(char *str)
+{
+ int len;
+ char *s;
+
+ len = strlen(str);
+ if (len > YYBUFSIZ)
+ len = YYBUFSIZ;
+
+ for (s = str; *s != '\0' && len > 0; s++, len--)
+ yytext[yylast++] = *s;
+ yytext[yylast] = '\0';
+}
+
+
+static char *
+yytexttostr(int offset, int max)
+{
+ char *str;
+ int i;
+
+ if ((yytext[offset] == '\'' || yytext[offset] == '"') &&
+ (yytext[offset] == yytext[offset + max - 1])) {
+ offset++;
+ max--;
+ }
+
+ if (max > yylast)
+ max = yylast;
+ str = malloc(max + 1);
+ if (str != NULL) {
+ for (i = offset; i < max; i++)
+ str[i - offset] = (char)(yytext[i] & 0xff);
+ str[i - offset] = '\0';
+ }
+ return (str);
+}
+
+
+int
+yylex(void)
+{
+ static int prior = 0;
+ static int priornum = 0;
+ int c, n, isbuilding, rval, lnext, nokey = 0;
+ char *name;
+ int triedv6 = 0;
+
+ isbuilding = 0;
+ lnext = 0;
+ rval = 0;
+
+ if (yystr != NULL) {
+ free(yystr);
+ yystr = NULL;
+ }
+
+nextchar:
+ c = yygetc(0);
+ if (yydebug > 1)
+ printf("yygetc = (%x) %c [%*.*s]\n",
+ c, c, yypos, yypos, yytexttochar());
+
+ switch (c)
+ {
+ case '\n' :
+ lnext = 0;
+ nokey = 0;
+ case '\t' :
+ case '\r' :
+ case ' ' :
+ if (isbuilding == 1) {
+ yyunputc(c);
+ goto done;
+ }
+ if (yylast > yypos) {
+ bcopy(yytext + yypos, yytext,
+ sizeof(yytext[0]) * (yylast - yypos + 1));
+ }
+ yylast -= yypos;
+ if (yyexpectaddr == 2)
+ yyexpectaddr = 0;
+ yypos = 0;
+ lnext = 0;
+ nokey = 0;
+ goto nextchar;
+
+ case '\\' :
+ if (lnext == 0) {
+ lnext = 1;
+ if (yylast == yypos) {
+ yylast--;
+ yypos--;
+ } else
+ yypos--;
+ if (yypos == 0)
+ nokey = 1;
+ goto nextchar;
+ }
+ break;
+ }
+
+ if (lnext == 1) {
+ lnext = 0;
+ if ((isbuilding == 0) && !ISALNUM(c)) {
+ prior = c;
+ return (c);
+ }
+ goto nextchar;
+ }
+
+ switch (c)
+ {
+ case '#' :
+ if (isbuilding == 1) {
+ yyunputc(c);
+ goto done;
+ }
+ yyswallow('\n');
+ rval = YY_COMMENT;
+ goto done;
+
+ case '$' :
+ if (isbuilding == 1) {
+ yyunputc(c);
+ goto done;
+ }
+ n = yygetc(0);
+ if (n == '{') {
+ if (yyswallow('}') == -1) {
+ rval = -2;
+ goto done;
+ }
+ (void) yygetc(0);
+ } else {
+ if (!ISALPHA(n)) {
+ yyunputc(n);
+ break;
+ }
+ do {
+ n = yygetc(1);
+ } while (ISALPHA(n) || ISDIGIT(n) || n == '_');
+ yyunputc(n);
+ }
+
+ name = yytexttostr(1, yypos); /* skip $ */
+
+ if (name != NULL) {
+ string_val = get_variable(name, NULL, yylineNum);
+ free(name);
+ if (string_val != NULL) {
+ name = yytexttostr(yypos, yylast);
+ if (name != NULL) {
+ yypos = 0;
+ yylast = 0;
+ yystrtotext(string_val);
+ yystrtotext(name);
+ free(string_val);
+ free(name);
+ goto nextchar;
+ }
+ free(string_val);
+ }
+ }
+ break;
+
+ case '\'':
+ case '"' :
+ if (isbuilding == 1) {
+ goto done;
+ }
+ do {
+ n = yygetc(1);
+ if (n == EOF || n == TOOLONG) {
+ rval = -2;
+ goto done;
+ }
+ if (n == '\n') {
+ yyunputc(' ');
+ yypos++;
+ }
+ } while (n != c);
+ rval = YY_STR;
+ goto done;
+ /* NOTREACHED */
+
+ case EOF :
+ yylineNum = 1;
+ yypos = 0;
+ yylast = -1;
+ yyexpectaddr = 0;
+ yybreakondot = 0;
+ yyvarnext = 0;
+ yytokentype = 0;
+ if (yydebug)
+ fprintf(stderr, "reset at EOF\n");
+ prior = 0;
+ return (0);
+ }
+
+ if (strchr("=,/;{}()@", c) != NULL) {
+ if (isbuilding == 1) {
+ yyunputc(c);
+ goto done;
+ }
+ rval = c;
+ goto done;
+ } else if (c == '.') {
+ if (isbuilding == 0) {
+ rval = c;
+ goto done;
+ }
+ if (yybreakondot != 0) {
+ yyunputc(c);
+ goto done;
+ }
+ }
+
+ switch (c)
+ {
+ case '-' :
+ n = yygetc(0);
+ if (n == '>') {
+ isbuilding = 1;
+ goto done;
+ }
+ yyunputc(n);
+ if (yyexpectaddr) {
+ if (isbuilding == 1)
+ yyunputc(c);
+ else
+ rval = '-';
+ goto done;
+ }
+ if (isbuilding == 1)
+ break;
+ rval = '-';
+ goto done;
+
+ case '!' :
+ if (isbuilding == 1) {
+ yyunputc(c);
+ goto done;
+ }
+ n = yygetc(0);
+ if (n == '=') {
+ rval = YY_CMP_NE;
+ goto done;
+ }
+ yyunputc(n);
+ rval = '!';
+ goto done;
+
+ case '<' :
+ if (yyexpectaddr)
+ break;
+ if (isbuilding == 1) {
+ yyunputc(c);
+ goto done;
+ }
+ n = yygetc(0);
+ if (n == '=') {
+ rval = YY_CMP_LE;
+ goto done;
+ }
+ if (n == '>') {
+ rval = YY_RANGE_OUT;
+ goto done;
+ }
+ yyunputc(n);
+ rval = YY_CMP_LT;
+ goto done;
+
+ case '>' :
+ if (yyexpectaddr)
+ break;
+ if (isbuilding == 1) {
+ yyunputc(c);
+ goto done;
+ }
+ n = yygetc(0);
+ if (n == '=') {
+ rval = YY_CMP_GE;
+ goto done;
+ }
+ if (n == '<') {
+ rval = YY_RANGE_IN;
+ goto done;
+ }
+ yyunputc(n);
+ rval = YY_CMP_GT;
+ goto done;
+ }
+
+ /*
+ * Now for the reason this is here...IPv6 address parsing.
+ * The longest string we can expect is of this form:
+ * 0000:0000:0000:0000:0000:0000:000.000.000.000
+ * not:
+ * 0000:0000:0000:0000:0000:0000:0000:0000
+ */
+#ifdef USE_INET6
+ if (yyexpectaddr != 0 && isbuilding == 0 &&
+ (ishex(c) || isdigit(c) || c == ':')) {
+ char ipv6buf[45 + 1], *s, oc;
+ int start;
+
+buildipv6:
+ start = yypos;
+ s = ipv6buf;
+ oc = c;
+
+ if (prior == YY_NUMBER && c == ':') {
+ snprintf(s, sizeof(s), "%d", priornum);
+ s += strlen(s);
+ }
+
+ /*
+ * Perhaps we should implement stricter controls on what we
+ * swallow up here, but surely it would just be duplicating
+ * the code in inet_pton() anyway.
+ */
+ do {
+ *s++ = c;
+ c = yygetc(1);
+ } while ((ishex(c) || c == ':' || c == '.') &&
+ (s - ipv6buf < 46));
+ yyunputc(c);
+ *s = '\0';
+
+ if (inet_pton(AF_INET6, ipv6buf, &yylval.ip6) == 1) {
+ rval = YY_IPV6;
+ yyexpectaddr = 0;
+ goto done;
+ }
+ yypos = start;
+ c = oc;
+ }
+#endif
+
+ if ((c == ':') && (rval != YY_IPV6) && (triedv6 == 0)) {
+#ifdef USE_INET6
+ yystr = yytexttostr(0, yypos - 1);
+ if (yystr != NULL) {
+ char *s;
+
+ for (s = yystr; *s && ishex(*s); s++)
+ ;
+ if (!*s && *yystr) {
+ isbuilding = 0;
+ c = *yystr;
+ free(yystr);
+ triedv6 = 1;
+ yypos = 1;
+ goto buildipv6;
+ }
+ free(yystr);
+ }
+#endif
+ if (isbuilding == 1) {
+ yyunputc(c);
+ goto done;
+ }
+ rval = ':';
+ goto done;
+ }
+
+ if (isbuilding == 0 && c == '0') {
+ n = yygetc(0);
+ if (n == 'x') {
+ do {
+ n = yygetc(1);
+ } while (ishex(n));
+ yyunputc(n);
+ rval = YY_HEX;
+ goto done;
+ }
+ yyunputc(n);
+ }
+
+ /*
+ * No negative numbers with leading - sign..
+ */
+ if (isbuilding == 0 && ISDIGIT(c)) {
+ do {
+ n = yygetc(1);
+ } while (ISDIGIT(n));
+ yyunputc(n);
+ rval = YY_NUMBER;
+ goto done;
+ }
+
+ isbuilding = 1;
+ goto nextchar;
+
+done:
+ yystr = yytexttostr(0, yypos);
+
+ if (yydebug)
+ printf("isbuilding %d yyvarnext %d nokey %d fixed %d addr %d\n",
+ isbuilding, yyvarnext, nokey, yydictfixed, yyexpectaddr);
+ if (isbuilding == 1) {
+ wordtab_t *w;
+
+ w = NULL;
+ isbuilding = 0;
+
+ if ((yyvarnext == 0) && (nokey == 0)) {
+ w = yyfindkey(yystr);
+ if (w == NULL && yywordtab != NULL && !yydictfixed) {
+ yyresetdict();
+ w = yyfindkey(yystr);
+ }
+ } else
+ yyvarnext = 0;
+ if (w != NULL)
+ rval = w->w_value;
+ else
+ rval = YY_STR;
+ }
+
+ if (rval == YY_STR) {
+ if (yysavedepth > 0 && !yydictfixed)
+ yyresetdict();
+ if (yyexpectaddr != 0)
+ yyexpectaddr = 0;
+ }
+
+ yytokentype = rval;
+
+ if (yydebug)
+ printf("lexed(%s) %d,%d,%d [%d,%d,%d] => %d @%d\n",
+ yystr, isbuilding, yyexpectaddr, yysavedepth,
+ string_start, string_end, pos, rval, yysavedepth);
+
+ switch (rval)
+ {
+ case YY_NUMBER :
+ sscanf(yystr, "%u", &yylval.num);
+ break;
+
+ case YY_HEX :
+ sscanf(yystr, "0x%x", (u_int *)&yylval.num);
+ break;
+
+ case YY_STR :
+ yylval.str = strdup(yystr);
+ break;
+
+ default :
+ break;
+ }
+
+ if (yylast > 0) {
+ bcopy(yytext + yypos, yytext,
+ sizeof(yytext[0]) * (yylast - yypos + 1));
+ yylast -= yypos;
+ yypos = 0;
+ }
+
+ if (rval == YY_NUMBER)
+ priornum = yylval.num;
+ prior = rval;
+ return (rval);
+}
+
+
+static wordtab_t *yyfindkey(key)
+ char *key;
+{
+ wordtab_t *w;
+
+ if (yywordtab == NULL)
+ return (NULL);
+
+ for (w = yywordtab; w->w_word != 0; w++)
+ if (strcasecmp(key, w->w_word) == 0)
+ return (w);
+ return (NULL);
+}
+
+
+char *
+yykeytostr(int num)
+{
+ wordtab_t *w;
+
+ if (yywordtab == NULL)
+ return ("<unknown>");
+
+ for (w = yywordtab; w->w_word; w++)
+ if (w->w_value == num)
+ return (w->w_word);
+ return ("<unknown>");
+}
+
+
+wordtab_t *
+yysettab(wordtab_t *words)
+{
+ wordtab_t *save;
+
+ save = yywordtab;
+ yywordtab = words;
+ return (save);
+}
+
+
+void
+yyerror(char *msg)
+{
+ char *txt, letter[2];
+ int freetxt = 0;
+
+ if (yytokentype < 256) {
+ letter[0] = yytokentype;
+ letter[1] = '\0';
+ txt = letter;
+ } else if (yytokentype == YY_STR || yytokentype == YY_HEX ||
+ yytokentype == YY_NUMBER) {
+ if (yystr == NULL) {
+ txt = yytexttostr(yypos, YYBUFSIZ);
+ freetxt = 1;
+ } else
+ txt = yystr;
+ } else {
+ txt = yykeytostr(yytokentype);
+ }
+ fprintf(stderr, "%s error at \"%s\", line %d\n", msg, txt, yylineNum);
+ if (freetxt == 1)
+ free(txt);
+ exit(1);
+}
+
+
+void
+yysetfixeddict(wordtab_t *newdict)
+{
+ if (yydebug)
+ printf("yysetfixeddict(%lx)\n", (u_long)newdict);
+
+ if (yysavedepth == sizeof(yysavewords)/sizeof(yysavewords[0])) {
+ fprintf(stderr, "%d: at maximum dictionary depth\n",
+ yylineNum);
+ return;
+ }
+
+ yysavewords[yysavedepth++] = yysettab(newdict);
+ if (yydebug)
+ printf("yysavedepth++ => %d\n", yysavedepth);
+ yydictfixed = 1;
+}
+
+
+void
+yysetdict(wordtab_t *newdict)
+{
+ if (yydebug)
+ printf("yysetdict(%lx)\n", (u_long)newdict);
+
+ if (yysavedepth == sizeof(yysavewords)/sizeof(yysavewords[0])) {
+ fprintf(stderr, "%d: at maximum dictionary depth\n",
+ yylineNum);
+ return;
+ }
+
+ yysavewords[yysavedepth++] = yysettab(newdict);
+ if (yydebug)
+ printf("yysavedepth++ => %d\n", yysavedepth);
+}
+
+void
+yyresetdict(void)
+{
+ if (yydebug)
+ printf("yyresetdict(%d)\n", yysavedepth);
+ if (yysavedepth > 0) {
+ yysettab(yysavewords[--yysavedepth]);
+ if (yydebug)
+ printf("yysavedepth-- => %d\n", yysavedepth);
+ }
+ yydictfixed = 0;
+}
+
+
+
+#ifdef TEST_LEXER
+int
+main(int argc, char *argv[])
+{
+ int n;
+
+ yyin = stdin;
+
+ while ((n = yylex()) != 0)
+ printf("%d.n = %d [%s] %d %d\n",
+ yylineNum, n, yystr, yypos, yylast);
+}
+#endif
diff --git a/sbin/ipf/common/lexer.h b/sbin/ipf/common/lexer.h
new file mode 100644
index 000000000000..cc200f1cad41
--- /dev/null
+++ b/sbin/ipf/common/lexer.h
@@ -0,0 +1,38 @@
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2012 by Darren Reed.
+ *
+ * See the IPFILTER.LICENCE file for details on licencing.
+ */
+
+#ifdef NO_YACC
+#define YY_COMMENT 1000
+#define YY_CMP_NE 1001
+#define YY_CMP_LE 1002
+#define YY_RANGE_OUT 1003
+#define YY_CMP_GE 1004
+#define YY_RANGE_IN 1005
+#define YY_HEX 1006
+#define YY_NUMBER 1007
+#define YY_IPV6 1008
+#define YY_STR 1009
+#define YY_IPADDR 1010
+#endif
+
+#define YYBUFSIZ 8192
+
+extern wordtab_t *yysettab(wordtab_t *);
+extern void yysetdict(wordtab_t *);
+extern void yysetfixeddict(wordtab_t *);
+extern int yylex(void);
+extern void yyerror(char *);
+extern char *yykeytostr(int);
+extern void yyresetdict(void);
+
+extern FILE *yyin;
+extern int yylineNum;
+extern int yyexpectaddr;
+extern int yybreakondot;
+extern int yyvarnext;
+
diff --git a/sbin/ipf/common/opts.h b/sbin/ipf/common/opts.h
new file mode 100644
index 000000000000..17844e89ecfc
--- /dev/null
+++ b/sbin/ipf/common/opts.h
@@ -0,0 +1,69 @@
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2012 by Darren Reed.
+ *
+ * See the IPFILTER.LICENCE file for details on licencing.
+ *
+ * $Id$
+ */
+
+#ifndef __OPTS_H__
+#define __OPTS_H__
+
+#ifndef SOLARIS
+# if defined(sun) && (defined(__svr4__) || defined(__SVR4))
+# define SOLARIS 1
+# else
+# define SOLARIS 0
+# endif
+#endif
+#define OPT_REMOVE 0x000001
+#define OPT_DEBUG 0x000002
+#define OPT_AUTHSTATS 0x000004
+#define OPT_RAW 0x000008
+#define OPT_LOG 0x000010
+#define OPT_SHOWLIST 0x000020
+#define OPT_VERBOSE 0x000040
+#define OPT_DONOTHING 0x000080
+#define OPT_HITS 0x000100
+#define OPT_BRIEF 0x000200
+#define OPT_ACCNT 0x000400
+#define OPT_FRSTATES 0x000800
+#define OPT_SHOWLINENO 0x001000
+#define OPT_PRINTFR 0x002000
+#define OPT_OUTQUE FR_OUTQUE /* 0x4000 */
+#define OPT_INQUE FR_INQUE /* 0x8000 */
+#define OPT_ZERORULEST 0x010000
+#define OPT_SAVEOUT 0x020000
+#define OPT_IPSTATES 0x040000
+#define OPT_INACTIVE 0x080000
+#define OPT_NAT 0x100000
+#define OPT_GROUPS 0x200000
+#define OPT_STATETOP 0x400000
+#define OPT_FLUSH 0x800000
+#define OPT_CLEAR 0x1000000
+#define OPT_HEX 0x2000000
+#define OPT_ASCII 0x4000000
+#define OPT_NORESOLVE 0x8000000
+#define OPT_DONTOPEN 0x10000000
+#define OPT_PURGE 0x20000000
+
+#define OPT_STAT OPT_FRSTATES
+#define OPT_LIST OPT_SHOWLIST
+
+
+#ifndef __P
+# define __P(x) x
+#endif
+
+#if defined(sun) && !SOLARIS
+# define STRERROR(x) sys_errlist[x]
+extern char *sys_errlist[];
+#else
+# define STRERROR(x) strerror(x)
+#endif
+
+extern int opts;
+
+#endif /* __OPTS_H__ */
diff --git a/sbin/ipf/common/pcap-ipf.h b/sbin/ipf/common/pcap-ipf.h
new file mode 100644
index 000000000000..b856760eaa53
--- /dev/null
+++ b/sbin/ipf/common/pcap-ipf.h
@@ -0,0 +1,35 @@
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2012 by Darren Reed.
+ *
+ * See the IPFILTER.LICENCE file for details on licencing.
+ *
+ */
+/*
+ * This header file is constructed to match the version described by
+ * PCAP_VERSION_MAJ.
+ *
+ * The structure largely derives from libpcap which wouldn't include
+ * nicely without bpf.
+ */
+typedef struct pcap_filehdr {
+ u_int pc_id;
+ u_short pc_v_maj;
+ u_short pc_v_min;
+ u_int pc_zone;
+ u_int pc_sigfigs;
+ u_int pc_slen;
+ u_int pc_type;
+} pcaphdr_t;
+
+#define TCPDUMP_MAGIC 0xa1b2c3d4
+
+#define PCAP_VERSION_MAJ 2
+
+typedef struct pcap_pkthdr {
+ struct timeval ph_ts;
+ u_int ph_clen;
+ u_int ph_len;
+} pcappkt_t;
+
diff --git a/sbin/ipf/ipf/Makefile b/sbin/ipf/ipf/Makefile
index 7e934ccc5984..f1b87ac20fae 100644
--- a/sbin/ipf/ipf/Makefile
+++ b/sbin/ipf/ipf/Makefile
@@ -13,7 +13,6 @@ CLEANFILES+= ${GENHDRS} ipf_y.c ipf_l.c
ipf_y.c: ipf_y.y
${YACC} -d ${.ALLSRC}
sed -e 's/yy/ipf_yy/g' \
- -e 's/"ipf_y.y"/"..\/tools\/ipf_y.y"/' \
y.tab.c > ${.TARGET}
sed -e 's/yy/ipf_yy/g' \
y.tab.h > ${.TARGET:.c=.h}
diff --git a/sbin/ipf/ipf/bpf-ipf.h b/sbin/ipf/ipf/bpf-ipf.h
new file mode 100644
index 000000000000..2350e6cf0692
--- /dev/null
+++ b/sbin/ipf/ipf/bpf-ipf.h
@@ -0,0 +1,440 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from the Stanford/CMU enet packet filter,
+ * (net/enet.c) distributed as part of 4.3BSD, and code contributed
+ * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence
+ * Berkeley Laboratory.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * @(#)bpf.h 7.1 (Berkeley) 5/7/91
+ *
+ * @(#) $Header: /devel/CVS/IP-Filter/bpf-ipf.h,v 2.1 2002/10/26 12:14:26 darrenr Exp $ (LBL)
+ */
+
+#ifndef BPF_MAJOR_VERSION
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* BSD style release date */
+#define BPF_RELEASE 199606
+
+typedef int bpf_int32;
+typedef u_int bpf_u_int32;
+
+/*
+ * Alignment macros. BPF_WORDALIGN rounds up to the next
+ * even multiple of BPF_ALIGNMENT.
+ */
+#ifndef __NetBSD__
+#define BPF_ALIGNMENT sizeof(bpf_int32)
+#else
+#define BPF_ALIGNMENT sizeof(long)
+#endif
+#define BPF_WORDALIGN(x) (((x)+(BPF_ALIGNMENT-1))&~(BPF_ALIGNMENT-1))
+
+#define BPF_MAXINSNS 512
+#define BPF_MAXBUFSIZE 0x8000
+#define BPF_MINBUFSIZE 32
+
+/*
+ * Structure for BIOCSETF.
+ */
+struct bpf_program {
+ u_int bf_len;
+ struct bpf_insn *bf_insns;
+};
+
+/*
+ * Struct returned by BIOCGSTATS.
+ */
+struct bpf_stat {
+ u_int bs_recv; /* number of packets received */
+ u_int bs_drop; /* number of packets dropped */
+};
+
+/*
+ * Struct return by BIOCVERSION. This represents the version number of
+ * the filter language described by the instruction encodings below.
+ * bpf understands a program iff kernel_major == filter_major &&
+ * kernel_minor >= filter_minor, that is, if the value returned by the
+ * running kernel has the same major number and a minor number equal
+ * equal to or less than the filter being downloaded. Otherwise, the
+ * results are undefined, meaning an error may be returned or packets
+ * may be accepted haphazardly.
+ * It has nothing to do with the source code version.
+ */
+struct bpf_version {
+ u_short bv_major;
+ u_short bv_minor;
+};
+/* Current version number of filter architecture. */
+#define BPF_MAJOR_VERSION 1
+#define BPF_MINOR_VERSION 1
+
+/*
+ * BPF ioctls
+ *
+ * The first set is for compatibility with Sun's pcc style
+ * header files. If your using gcc, we assume that you
+ * have run fixincludes so the latter set should work.
+ */
+#if (defined(sun) || defined(ibm032)) && !defined(__GNUC__)
+#define BIOCGBLEN _IOR(B,102, u_int)
+#define BIOCSBLEN _IOWR(B,102, u_int)
+#define BIOCSETF _IOW(B,103, struct bpf_program)
+#define BIOCFLUSH _IO(B,104)
+#define BIOCPROMISC _IO(B,105)
+#define BIOCGDLT _IOR(B,106, u_int)
+#define BIOCGETIF _IOR(B,107, struct ifreq)
+#define BIOCSETIF _IOW(B,108, struct ifreq)
+#define BIOCSRTIMEOUT _IOW(B,109, struct timeval)
+#define BIOCGRTIMEOUT _IOR(B,110, struct timeval)
+#define BIOCGSTATS _IOR(B,111, struct bpf_stat)
+#define BIOCIMMEDIATE _IOW(B,112, u_int)
+#define BIOCVERSION _IOR(B,113, struct bpf_version)
+#define BIOCSTCPF _IOW(B,114, struct bpf_program)
+#define BIOCSUDPF _IOW(B,115, struct bpf_program)
+#else
+#define BIOCGBLEN _IOR('B',102, u_int)
+#define BIOCSBLEN _IOWR('B',102, u_int)
+#define BIOCSETF _IOW('B',103, struct bpf_program)
+#define BIOCFLUSH _IO('B',104)
+#define BIOCPROMISC _IO('B',105)
+#define BIOCGDLT _IOR('B',106, u_int)
+#define BIOCGETIF _IOR('B',107, struct ifreq)
+#define BIOCSETIF _IOW('B',108, struct ifreq)
+#define BIOCSRTIMEOUT _IOW('B',109, struct timeval)
+#define BIOCGRTIMEOUT _IOR('B',110, struct timeval)
+#define BIOCGSTATS _IOR('B',111, struct bpf_stat)
+#define BIOCIMMEDIATE _IOW('B',112, u_int)
+#define BIOCVERSION _IOR('B',113, struct bpf_version)
+#define BIOCSTCPF _IOW('B',114, struct bpf_program)
+#define BIOCSUDPF _IOW('B',115, struct bpf_program)
+#endif
+
+/*
+ * Structure prepended to each packet.
+ */
+struct bpf_hdr {
+ struct timeval bh_tstamp; /* time stamp */
+ bpf_u_int32 bh_caplen; /* length of captured portion */
+ bpf_u_int32 bh_datalen; /* original length of packet */
+ u_short bh_hdrlen; /* length of bpf header (this struct
+ plus alignment padding) */
+};
+/*
+ * Because the structure above is not a multiple of 4 bytes, some compilers
+ * will insist on inserting padding; hence, sizeof(struct bpf_hdr) won't work.
+ * Only the kernel needs to know about it; applications use bh_hdrlen.
+ */
+#if defined(KERNEL) || defined(_KERNEL)
+#define SIZEOF_BPF_HDR 18
+#endif
+
+/*
+ * Data-link level type codes.
+ */
+
+/*
+ * These are the types that are the same on all platforms; on other
+ * platforms, a <net/bpf.h> should be supplied that defines the additional
+ * DLT_* codes appropriately for that platform (the BSDs, for example,
+ * should not just pick up this version of "bpf.h"; they should also define
+ * the additional DLT_* codes used by their kernels, as well as the values
+ * defined here - and, if the values they use for particular DLT_ types
+ * differ from those here, they should use their values, not the ones
+ * here).
+ */
+#define DLT_NULL 0 /* no link-layer encapsulation */
+#define DLT_EN10MB 1 /* Ethernet (10Mb) */
+#define DLT_EN3MB 2 /* Experimental Ethernet (3Mb) */
+#define DLT_AX25 3 /* Amateur Radio AX.25 */
+#define DLT_PRONET 4 /* Proteon ProNET Token Ring */
+#define DLT_CHAOS 5 /* Chaos */
+#define DLT_IEEE802 6 /* IEEE 802 Networks */
+#define DLT_ARCNET 7 /* ARCNET */
+#define DLT_SLIP 8 /* Serial Line IP */
+#define DLT_PPP 9 /* Point-to-point Protocol */
+#define DLT_FDDI 10 /* FDDI */
+
+/*
+ * These are values from the traditional libpcap "bpf.h".
+ * Ports of this to particular platforms should replace these definitions
+ * with the ones appropriate to that platform, if the values are
+ * different on that platform.
+ */
+#define DLT_ATM_RFC1483 11 /* LLC/SNAP encapsulated atm */
+#define DLT_RAW 12 /* raw IP */
+
+/*
+ * These are values from BSD/OS's "bpf.h".
+ * These are not the same as the values from the traditional libpcap
+ * "bpf.h"; however, these values shouldn't be generated by any
+ * OS other than BSD/OS, so the correct values to use here are the
+ * BSD/OS values.
+ *
+ * Platforms that have already assigned these values to other
+ * DLT_ codes, however, should give these codes the values
+ * from that platform, so that programs that use these codes will
+ * continue to compile - even though they won't correctly read
+ * files of these types.
+ */
+#ifdef __NetBSD__
+#ifndef DLT_SLIP_BSDOS
+#define DLT_SLIP_BSDOS 13 /* BSD/OS Serial Line IP */
+#define DLT_PPP_BSDOS 14 /* BSD/OS Point-to-point Protocol */
+#endif
+#else
+#define DLT_SLIP_BSDOS 15 /* BSD/OS Serial Line IP */
+#define DLT_PPP_BSDOS 16 /* BSD/OS Point-to-point Protocol */
+#endif
+
+#define DLT_ATM_CLIP 19 /* Linux Classical-IP over ATM */
+
+/*
+ * These values are defined by NetBSD; other platforms should refrain from
+ * using them for other purposes, so that NetBSD savefiles with link
+ * types of 50 or 51 can be read as this type on all platforms.
+ */
+#define DLT_PPP_SERIAL 50 /* PPP over serial with HDLC encapsulation */
+#define DLT_PPP_ETHER 51 /* PPP over Ethernet */
+
+/*
+ * Values between 100 and 103 are used in capture file headers as
+ * link-layer types corresponding to DLT_ types that differ
+ * between platforms; don't use those values for new DLT_ new types.
+ */
+
+/*
+ * This value was defined by libpcap 0.5; platforms that have defined
+ * it with a different value should define it here with that value -
+ * a link type of 104 in a save file will be mapped to DLT_C_HDLC,
+ * whatever value that happens to be, so programs will correctly
+ * handle files with that link type regardless of the value of
+ * DLT_C_HDLC.
+ *
+ * The name DLT_C_HDLC was used by BSD/OS; we use that name for source
+ * compatibility with programs written for BSD/OS.
+ *
+ * libpcap 0.5 defined it as DLT_CHDLC; we define DLT_CHDLC as well,
+ * for source compatibility with programs written for libpcap 0.5.
+ */
+#define DLT_C_HDLC 104 /* Cisco HDLC */
+#define DLT_CHDLC DLT_C_HDLC
+
+#define DLT_IEEE802_11 105 /* IEEE 802.11 wireless */
+
+/*
+ * Values between 106 and 107 are used in capture file headers as
+ * link-layer types corresponding to DLT_ types that might differ
+ * between platforms; don't use those values for new DLT_ new types.
+ */
+
+/*
+ * OpenBSD DLT_LOOP, for loopback devices; it's like DLT_NULL, except
+ * that the AF_ type in the link-layer header is in network byte order.
+ *
+ * OpenBSD defines it as 12, but that collides with DLT_RAW, so we
+ * define it as 108 here. If OpenBSD picks up this file, it should
+ * define DLT_LOOP as 12 in its version, as per the comment above -
+ * and should not use 108 as a DLT_ value.
+ */
+#define DLT_LOOP 108
+
+/*
+ * Values between 109 and 112 are used in capture file headers as
+ * link-layer types corresponding to DLT_ types that might differ
+ * between platforms; don't use those values for new DLT_ types
+ * other than the corresponding DLT_ types.
+ */
+
+/*
+ * This is for Linux cooked sockets.
+ */
+#define DLT_LINUX_SLL 113
+
+/*
+ * Apple LocalTalk hardware.
+ */
+#define DLT_LTALK 114
+
+/*
+ * Acorn Econet.
+ */
+#define DLT_ECONET 115
+
+/*
+ * Reserved for use with OpenBSD ipfilter.
+ */
+#define DLT_IPFILTER 116
+
+/*
+ * Reserved for use in capture-file headers as a link-layer type
+ * corresponding to OpenBSD DLT_PFLOG; DLT_PFLOG is 17 in OpenBSD,
+ * but that's DLT_LANE8023 in SuSE 6.3, so we can't use 17 for it
+ * in capture-file headers.
+ */
+#define DLT_PFLOG 117
+
+/*
+ * Registered for Cisco-internal use.
+ */
+#define DLT_CISCO_IOS 118
+
+/*
+ * Reserved for 802.11 cards using the Prism II chips, with a link-layer
+ * header including Prism monitor mode information plus an 802.11
+ * header.
+ */
+#define DLT_PRISM_HEADER 119
+
+/*
+ * Reserved for Aironet 802.11 cards, with an Aironet link-layer header
+ * (see Doug Ambrisko's FreeBSD patches).
+ */
+#define DLT_AIRONET_HEADER 120
+
+/*
+ * Reserved for Siemens HiPath HDLC.
+ */
+#define DLT_HHDLC 121
+
+/*
+ * Reserved for RFC 2625 IP-over-Fibre Channel, as per a request from
+ * Don Lee <donlee@cray.com>.
+ *
+ * This is not for use with raw Fibre Channel, where the link-layer
+ * header starts with a Fibre Channel frame header; it's for IP-over-FC,
+ * where the link-layer header starts with an RFC 2625 Network_Header
+ * field.
+ */
+#define DLT_IP_OVER_FC 122
+
+/*
+ * The instruction encodings.
+ */
+/* instruction classes */
+#define BPF_CLASS(code) ((code) & 0x07)
+#define BPF_LD 0x00
+#define BPF_LDX 0x01
+#define BPF_ST 0x02
+#define BPF_STX 0x03
+#define BPF_ALU 0x04
+#define BPF_JMP 0x05
+#define BPF_RET 0x06
+#define BPF_MISC 0x07
+
+/* ld/ldx fields */
+#define BPF_SIZE(code) ((code) & 0x18)
+#define BPF_W 0x00
+#define BPF_H 0x08
+#define BPF_B 0x10
+#define BPF_MODE(code) ((code) & 0xe0)
+#define BPF_IMM 0x00
+#define BPF_ABS 0x20
+#define BPF_IND 0x40
+#define BPF_MEM 0x60
+#define BPF_LEN 0x80
+#define BPF_MSH 0xa0
+
+/* alu/jmp fields */
+#define BPF_OP(code) ((code) & 0xf0)
+#define BPF_ADD 0x00
+#define BPF_SUB 0x10
+#define BPF_MUL 0x20
+#define BPF_DIV 0x30
+#define BPF_OR 0x40
+#define BPF_AND 0x50
+#define BPF_LSH 0x60
+#define BPF_RSH 0x70
+#define BPF_NEG 0x80
+#define BPF_JA 0x00
+#define BPF_JEQ 0x10
+#define BPF_JGT 0x20
+#define BPF_JGE 0x30
+#define BPF_JSET 0x40
+#define BPF_SRC(code) ((code) & 0x08)
+#define BPF_K 0x00
+#define BPF_X 0x08
+
+/* ret - BPF_K and BPF_X also apply */
+#define BPF_RVAL(code) ((code) & 0x18)
+#define BPF_A 0x10
+
+/* misc */
+#define BPF_MISCOP(code) ((code) & 0xf8)
+#define BPF_TAX 0x00
+#define BPF_TXA 0x80
+
+/*
+ * The instruction data structure.
+ */
+struct bpf_insn {
+ u_short code;
+ u_char jt;
+ u_char jf;
+ bpf_int32 k;
+};
+
+/*
+ * Macros for insn array initializers.
+ */
+#define BPF_STMT(code, k) { (u_short)(code), 0, 0, k }
+#define BPF_JUMP(code, k, jt, jf) { (u_short)(code), jt, jf, k }
+
+#if defined(BSD) && (defined(KERNEL) || defined(_KERNEL))
+/*
+ * Systems based on non-BSD kernels don't have ifnet's (or they don't mean
+ * anything if it is in <net/if.h>) and won't work like this.
+ */
+extern void bpf_tap(struct ifnet *, u_char *, u_int);
+extern void bpf_mtap(struct ifnet *, struct mbuf *);
+extern void bpfattach(struct ifnet *, u_int, u_int);
+extern void bpfilterattach(int);
+#endif /* BSD && (_KERNEL || KERNEL) */
+extern int bpf_validate(struct bpf_insn *, int);
+extern u_int bpf_filter(struct bpf_insn *, u_char *, u_int, u_int);
+
+/*
+ * Number of scratch memory words (for BPF_LD|BPF_MEM and BPF_ST).
+ */
+#define BPF_MEMWORDS 16
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sbin/ipf/ipf/bpf_filter.c b/sbin/ipf/ipf/bpf_filter.c
new file mode 100644
index 000000000000..fbb0138f51d8
--- /dev/null
+++ b/sbin/ipf/ipf/bpf_filter.c
@@ -0,0 +1,595 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from the Stanford/CMU enet packet filter,
+ * (net/enet.c) distributed as part of 4.3BSD, and code contributed
+ * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence
+ * Berkeley Laboratory.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ * @(#)bpf.c 7.5 (Berkeley) 7/15/91
+ */
+
+#if !(defined(lint) || defined(KERNEL) || defined(_KERNEL))
+static const char rcsid[] =
+ "@(#) $Header: /devel/CVS/IP-Filter/bpf_filter.c,v 2.2.2.3 2006/10/03 11:25:56 darrenr Exp $ (LBL)";
+#endif
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include "netinet/ip_compat.h"
+#include "bpf-ipf.h"
+
+
+#if (defined(__hpux) || SOLARIS) && (defined(_KERNEL) || defined(KERNEL))
+# include <sys/sysmacros.h>
+# include <sys/stream.h>
+#endif
+
+#include "pcap-ipf.h"
+
+#if !defined(KERNEL) && !defined(_KERNEL)
+#include <stdlib.h>
+#endif
+
+#define int32 bpf_int32
+#define u_int32 bpf_u_int32
+
+static int m_xword(mb_t *, int, int *);
+static int m_xhalf(mb_t *, int, int *);
+
+#ifndef LBL_ALIGN
+/*
+ * XXX - IA-64? If not, this probably won't work on Win64 IA-64
+ * systems, unless LBL_ALIGN is defined elsewhere for them.
+ * XXX - SuperH? If not, this probably won't work on WinCE SuperH
+ * systems, unless LBL_ALIGN is defined elsewhere for them.
+ */
+#if defined(sparc) || defined(__sparc__) || defined(mips) || \
+ defined(ibm032) || defined(__alpha) || defined(__hpux) || \
+ defined(__arm__)
+#define LBL_ALIGN
+#endif
+#endif
+
+#ifndef LBL_ALIGN
+
+#define EXTRACT_SHORT(p) ((u_short)ntohs(*(u_short *)p))
+#define EXTRACT_LONG(p) (ntohl(*(u_int32 *)p))
+#else
+#define EXTRACT_SHORT(p)\
+ ((u_short)\
+ ((u_short)*((u_char *)p+0)<<8|\
+ (u_short)*((u_char *)p+1)<<0))
+#define EXTRACT_LONG(p)\
+ ((u_int32)*((u_char *)p+0)<<24|\
+ (u_int32)*((u_char *)p+1)<<16|\
+ (u_int32)*((u_char *)p+2)<<8|\
+ (u_int32)*((u_char *)p+3)<<0)
+#endif
+
+#define MINDEX(len, _m, _k) \
+{ \
+ len = M_LEN(m); \
+ while ((_k) >= len) { \
+ (_k) -= len; \
+ (_m) = (_m)->m_next; \
+ if ((_m) == 0) \
+ return (0); \
+ len = M_LEN(m); \
+ } \
+}
+
+static int
+m_xword(m, k, err)
+ register mb_t *m;
+ register int k, *err;
+{
+ register int len;
+ register u_char *cp, *np;
+ register mb_t *m0;
+
+ MINDEX(len, m, k);
+ cp = MTOD(m, u_char *) + k;
+ if (len - k >= 4) {
+ *err = 0;
+ return (EXTRACT_LONG(cp));
+ }
+ m0 = m->m_next;
+ if (m0 == NULL || M_LEN(m0) + len - k < 4)
+ goto bad;
+ *err = 0;
+ np = MTOD(m0, u_char *);
+ switch (len - k) {
+
+ case 1:
+ return (cp[0] << 24) | (np[0] << 16) | (np[1] << 8) | np[2];
+
+ case 2:
+ return (cp[0] << 24) | (cp[1] << 16) | (np[0] << 8) | np[1];
+
+ default:
+ return (cp[0] << 24) | (cp[1] << 16) | (cp[2] << 8) | np[0];
+ }
+ bad:
+ *err = 1;
+ return (0);
+}
+
+static int
+m_xhalf(m, k, err)
+ register mb_t *m;
+ register int k, *err;
+{
+ register int len;
+ register u_char *cp;
+ register mb_t *m0;
+
+ MINDEX(len, m, k);
+ cp = MTOD(m, u_char *) + k;
+ if (len - k >= 2) {
+ *err = 0;
+ return (EXTRACT_SHORT(cp));
+ }
+ m0 = m->m_next;
+ if (m0 == NULL)
+ goto bad;
+ *err = 0;
+ return (cp[0] << 8) | MTOD(m0, u_char *)[0];
+ bad:
+ *err = 1;
+ return (0);
+}
+
+/*
+ * Execute the filter program starting at pc on the packet p
+ * wirelen is the length of the original packet
+ * buflen is the amount of data present
+ * For the kernel, p is assumed to be a pointer to an mbuf if buflen is 0,
+ * in all other cases, p is a pointer to a buffer and buflen is its size.
+ */
+u_int
+bpf_filter(pc, p, wirelen, buflen)
+ register struct bpf_insn *pc;
+ register u_char *p;
+ u_int wirelen;
+ register u_int buflen;
+{
+ register u_int32 A, X;
+ register int k;
+ int32 mem[BPF_MEMWORDS];
+ mb_t *m, *n;
+ int merr = 0; /* XXX: GCC */
+ int len;
+
+ if (buflen == 0) {
+ m = (mb_t *)p;
+ p = MTOD(m, u_char *);
+ buflen = M_LEN(m);
+ } else
+ m = NULL;
+
+ if (pc == NULL)
+ /*
+ * No filter means accept all.
+ */
+ return (u_int)-1;
+ A = 0;
+ X = 0;
+ --pc;
+ while (1) {
+ ++pc;
+ switch (pc->code) {
+
+ default:
+ return (0);
+ case BPF_RET|BPF_K:
+ return (u_int)pc->k;
+
+ case BPF_RET|BPF_A:
+ return (u_int)A;
+
+ case BPF_LD|BPF_W|BPF_ABS:
+ k = pc->k;
+ if (k + sizeof(int32) > buflen) {
+ if (m == NULL)
+ return (0);
+ A = m_xword(m, k, &merr);
+ if (merr != 0)
+ return (0);
+ continue;
+ }
+ A = EXTRACT_LONG(&p[k]);
+ continue;
+
+ case BPF_LD|BPF_H|BPF_ABS:
+ k = pc->k;
+ if (k + sizeof(short) > buflen) {
+ if (m == NULL)
+ return (0);
+ A = m_xhalf(m, k, &merr);
+ if (merr != 0)
+ return (0);
+ continue;
+ }
+ A = EXTRACT_SHORT(&p[k]);
+ continue;
+
+ case BPF_LD|BPF_B|BPF_ABS:
+ k = pc->k;
+ if (k >= buflen) {
+ if (m == NULL)
+ return (0);
+ n = m;
+ MINDEX(len, n, k);
+ A = MTOD(n, u_char *)[k];
+ continue;
+ }
+ A = p[k];
+ continue;
+
+ case BPF_LD|BPF_W|BPF_LEN:
+ A = wirelen;
+ continue;
+
+ case BPF_LDX|BPF_W|BPF_LEN:
+ X = wirelen;
+ continue;
+
+ case BPF_LD|BPF_W|BPF_IND:
+ k = X + pc->k;
+ if (k + sizeof(int32) > buflen) {
+ if (m == NULL)
+ return (0);
+ A = m_xword(m, k, &merr);
+ if (merr != 0)
+ return (0);
+ continue;
+ }
+ A = EXTRACT_LONG(&p[k]);
+ continue;
+
+ case BPF_LD|BPF_H|BPF_IND:
+ k = X + pc->k;
+ if (k + sizeof(short) > buflen) {
+ if (m == NULL)
+ return (0);
+ A = m_xhalf(m, k, &merr);
+ if (merr != 0)
+ return (0);
+ continue;
+ }
+ A = EXTRACT_SHORT(&p[k]);
+ continue;
+
+ case BPF_LD|BPF_B|BPF_IND:
+ k = X + pc->k;
+ if (k >= buflen) {
+ if (m == NULL)
+ return (0);
+ n = m;
+ MINDEX(len, n, k);
+ A = MTOD(n, u_char *)[k];
+ continue;
+ }
+ A = p[k];
+ continue;
+
+ case BPF_LDX|BPF_MSH|BPF_B:
+ k = pc->k;
+ if (k >= buflen) {
+ if (m == NULL)
+ return (0);
+ n = m;
+ MINDEX(len, n, k);
+ X = (MTOD(n, char *)[k] & 0xf) << 2;
+ continue;
+ }
+ X = (p[pc->k] & 0xf) << 2;
+ continue;
+
+ case BPF_LD|BPF_IMM:
+ A = pc->k;
+ continue;
+
+ case BPF_LDX|BPF_IMM:
+ X = pc->k;
+ continue;
+
+ case BPF_LD|BPF_MEM:
+ A = mem[pc->k];
+ continue;
+
+ case BPF_LDX|BPF_MEM:
+ X = mem[pc->k];
+ continue;
+
+ case BPF_ST:
+ mem[pc->k] = A;
+ continue;
+
+ case BPF_STX:
+ mem[pc->k] = X;
+ continue;
+
+ case BPF_JMP|BPF_JA:
+ pc += pc->k;
+ continue;
+
+ case BPF_JMP|BPF_JGT|BPF_K:
+ pc += (A > pc->k) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JGE|BPF_K:
+ pc += (A >= pc->k) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JEQ|BPF_K:
+ pc += (A == pc->k) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JSET|BPF_K:
+ pc += (A & pc->k) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JGT|BPF_X:
+ pc += (A > X) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JGE|BPF_X:
+ pc += (A >= X) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JEQ|BPF_X:
+ pc += (A == X) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JSET|BPF_X:
+ pc += (A & X) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_ALU|BPF_ADD|BPF_X:
+ A += X;
+ continue;
+
+ case BPF_ALU|BPF_SUB|BPF_X:
+ A -= X;
+ continue;
+
+ case BPF_ALU|BPF_MUL|BPF_X:
+ A *= X;
+ continue;
+
+ case BPF_ALU|BPF_DIV|BPF_X:
+ if (X == 0)
+ return (0);
+ A /= X;
+ continue;
+
+ case BPF_ALU|BPF_AND|BPF_X:
+ A &= X;
+ continue;
+
+ case BPF_ALU|BPF_OR|BPF_X:
+ A |= X;
+ continue;
+
+ case BPF_ALU|BPF_LSH|BPF_X:
+ A <<= X;
+ continue;
+
+ case BPF_ALU|BPF_RSH|BPF_X:
+ A >>= X;
+ continue;
+
+ case BPF_ALU|BPF_ADD|BPF_K:
+ A += pc->k;
+ continue;
+
+ case BPF_ALU|BPF_SUB|BPF_K:
+ A -= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_MUL|BPF_K:
+ A *= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_DIV|BPF_K:
+ A /= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_AND|BPF_K:
+ A &= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_OR|BPF_K:
+ A |= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_LSH|BPF_K:
+ A <<= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_RSH|BPF_K:
+ A >>= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_NEG:
+ A = -A;
+ continue;
+
+ case BPF_MISC|BPF_TAX:
+ X = A;
+ continue;
+
+ case BPF_MISC|BPF_TXA:
+ A = X;
+ continue;
+ }
+ }
+}
+
+
+/*
+ * Return true if the 'fcode' is a valid filter program.
+ * The constraints are that each jump be forward and to a valid
+ * code, that memory accesses are within valid ranges (to the
+ * extent that this can be checked statically; loads of packet
+ * data have to be, and are, also checked at run time), and that
+ * the code terminates with either an accept or reject.
+ *
+ * The kernel needs to be able to verify an application's filter code.
+ * Otherwise, a bogus program could easily crash the system.
+ */
+int
+bpf_validate(f, len)
+ struct bpf_insn *f;
+ int len;
+{
+ u_int i, from;
+ const struct bpf_insn *p;
+
+ if (len == 0)
+ return (1);
+
+ if (len < 1 || len > BPF_MAXINSNS)
+ return (0);
+
+ for (i = 0; i < len; ++i) {
+ p = &f[i];
+ switch (BPF_CLASS(p->code)) {
+ /*
+ * Check that memory operations use valid addresses.
+ */
+ case BPF_LD:
+ case BPF_LDX:
+ switch (BPF_MODE(p->code)) {
+ case BPF_IMM:
+ break;
+ case BPF_ABS:
+ case BPF_IND:
+ case BPF_MSH:
+ /*
+ * More strict check with actual packet length
+ * is done runtime.
+ */
+#if 0
+ if (p->k >= bpf_maxbufsize)
+ return (0);
+#endif
+ break;
+ case BPF_MEM:
+ if (p->k >= BPF_MEMWORDS)
+ return (0);
+ break;
+ case BPF_LEN:
+ break;
+ default:
+ return (0);
+ }
+ break;
+ case BPF_ST:
+ case BPF_STX:
+ if (p->k >= BPF_MEMWORDS)
+ return (0);
+ break;
+ case BPF_ALU:
+ switch (BPF_OP(p->code)) {
+ case BPF_ADD:
+ case BPF_SUB:
+ case BPF_OR:
+ case BPF_AND:
+ case BPF_LSH:
+ case BPF_RSH:
+ case BPF_NEG:
+ break;
+ case BPF_DIV:
+ /*
+ * Check for constant division by 0.
+ */
+ if (BPF_RVAL(p->code) == BPF_K && p->k == 0)
+ return (0);
+ default:
+ return (0);
+ }
+ break;
+ case BPF_JMP:
+ /*
+ * Check that jumps are within the code block,
+ * and that unconditional branches don't go
+ * backwards as a result of an overflow.
+ * Unconditional branches have a 32-bit offset,
+ * so they could overflow; we check to make
+ * sure they don't. Conditional branches have
+ * an 8-bit offset, and the from address is <=
+ * BPF_MAXINSNS, and we assume that BPF_MAXINSNS
+ * is sufficiently small that adding 255 to it
+ * won't overflow.
+ *
+ * We know that len is <= BPF_MAXINSNS, and we
+ * assume that BPF_MAXINSNS is < the maximum size
+ * of a u_int, so that i + 1 doesn't overflow.
+ */
+ from = i + 1;
+ switch (BPF_OP(p->code)) {
+ case BPF_JA:
+ if (from + p->k < from || from + p->k >= len)
+ return (0);
+ break;
+ case BPF_JEQ:
+ case BPF_JGT:
+ case BPF_JGE:
+ case BPF_JSET:
+ if (from + p->jt >= len || from + p->jf >= len)
+ return (0);
+ break;
+ default:
+ return (0);
+ }
+ break;
+ case BPF_RET:
+ break;
+ case BPF_MISC:
+ break;
+ default:
+ return (0);
+ }
+ }
+ return (BPF_CLASS(f[len - 1].code) == BPF_RET);
+}
diff --git a/sbin/ipf/ipf/ipf.4 b/sbin/ipf/ipf/ipf.4
new file mode 100644
index 000000000000..73a17a0cc8d3
--- /dev/null
+++ b/sbin/ipf/ipf/ipf.4
@@ -0,0 +1,254 @@
+.\" $FreeBSD$
+.TH IPF 4
+.SH NAME
+ipf \- packet filtering kernel interface
+.SH SYNOPSIS
+#include <netinet/ip_compat.h>
+.br
+#include <netinet/ip_fil.h>
+.SH IOCTLS
+.PP
+To add and delete rules to the filter list, three 'basic' ioctls are provided
+for use. The ioctl's are called as:
+.LP
+.nf
+ ioctl(fd, SIOCADDFR, struct frentry **)
+ ioctl(fd, SIOCDELFR, struct frentry **)
+ ioctl(fd, SIOCIPFFL, int *)
+.fi
+.PP
+However, the full complement is as follows:
+.LP
+.nf
+ ioctl(fd, SIOCADAFR, struct frentry **) (same as SIOCADDFR)
+ ioctl(fd, SIOCRMAFR, struct frentry **) (same as SIOCDELFR)
+ ioctl(fd, SIOCADIFR, struct frentry **)
+ ioctl(fd, SIOCRMIFR, struct frentry **)
+ ioctl(fd, SIOCINAFR, struct frentry **)
+ ioctl(fd, SIOCINIFR, struct frentry **)
+ ioctl(fd, SIOCSETFF, u_int *)
+ ioctl(fd, SIOGGETFF, u_int *)
+ ioctl(fd, SIOCGETFS, struct friostat **)
+ ioctl(fd, SIOCIPFFL, int *)
+ ioctl(fd, SIOCIPFFB, int *)
+ ioctl(fd, SIOCSWAPA, u_int *)
+ ioctl(fd, SIOCFRENB, u_int *)
+ ioctl(fd, SIOCFRSYN, u_int *)
+ ioctl(fd, SIOCFRZST, struct friostat **)
+ ioctl(fd, SIOCZRLST, struct frentry **)
+ ioctl(fd, SIOCAUTHW, struct fr_info **)
+ ioctl(fd, SIOCAUTHR, struct fr_info **)
+ ioctl(fd, SIOCATHST, struct fr_authstat **)
+.fi
+.PP
+The variations, SIOCADAFR vs. SIOCADIFR, allow operation on the two lists,
+active and inactive, respectively. All of these ioctl's are implemented
+as being routing ioctls and thus the same rules for the various routing
+ioctls and the file descriptor are employed, mainly being that the fd must
+be that of the device associated with the module (i.e., /dev/ipl).
+.PP
+The three groups of ioctls above perform adding rules to the end of the
+list (SIOCAD*), deletion of rules from any place in the list (SIOCRM*)
+and insertion of a rule into the list (SIOCIN*). The rule place into
+which it is inserted is stored in the "fr_hits" field, below.
+.LP
+.nf
+typedef struct frentry {
+ struct frentry *fr_next;
+ u_short fr_group; /* group to which this rule belongs */
+ u_short fr_grhead; /* group # which this rule starts */
+ struct frentry *fr_grp;
+ int fr_ref; /* reference count - for grouping */
+ void *fr_ifa;
+#ifdef BSD
+ void *fr_oifa;
+#endif
+ /*
+ * These are only incremented when a packet matches this rule and
+ * it is the last match
+ */
+ U_QUAD_T fr_hits;
+ U_QUAD_T fr_bytes;
+ /*
+ * Fields after this may not change whilst in the kernel.
+ */
+ struct fr_ip fr_ip;
+ struct fr_ip fr_mip; /* mask structure */
+
+ u_char fr_tcpfm; /* tcp flags mask */
+ u_char fr_tcpf; /* tcp flags */
+
+ u_short fr_icmpm; /* data for ICMP packets (mask) */
+ u_short fr_icmp;
+
+ u_char fr_scmp; /* data for port comparisons */
+ u_char fr_dcmp;
+ u_short fr_dport;
+ u_short fr_sport;
+ u_short fr_stop; /* top port for <> and >< */
+ u_short fr_dtop; /* top port for <> and >< */
+ u_32_t fr_flags; /* per-rule flags && options (see below) */
+ u_short fr_skip; /* # of rules to skip */
+ u_short fr_loglevel; /* syslog log facility + priority */
+ int (*fr_func)(int, ip_t *, fr_info_t *));
+ char fr_icode; /* return ICMP code */
+ char fr_ifname[IFNAMSIZ];
+#ifdef BSD
+ char fr_oifname[IFNAMSIZ];
+#endif
+ struct frdest fr_tif; /* "to" interface */
+ struct frdest fr_dif; /* duplicate packet interfaces */
+} frentry_t;
+.fi
+.PP
+When adding a new rule, all unused fields (in the filter rule) should be
+initialised to be zero. To insert a rule, at a particular position in the
+filter list, the number of the rule which it is to be inserted before must
+be put in the "fr_hits" field (the first rule is number 0).
+.PP
+Flags which are recognised in fr_flags:
+.nf
+
+ FR_BLOCK 0x000001 /* do not allow packet to pass */
+ FR_PASS 0x000002 /* allow packet to pass */
+ FR_OUTQUE 0x000004 /* outgoing packets */
+ FR_INQUE 0x000008 /* ingoing packets */
+ FR_LOG 0x000010 /* Log */
+ FR_LOGB 0x000011 /* Log-fail */
+ FR_LOGP 0x000012 /* Log-pass */
+ FR_LOGBODY 0x000020 /* log the body of packets too */
+ FR_LOGFIRST 0x000040 /* log only the first packet to match */
+ FR_RETRST 0x000080 /* return a TCP RST packet if blocked */
+ FR_RETICMP 0x000100 /* return an ICMP packet if blocked */
+ FR_FAKEICMP 0x00180 /* Return ICMP unreachable with fake source */
+ FR_NOMATCH 0x000200 /* no match occured */
+ FR_ACCOUNT 0x000400 /* count packet bytes */
+ FR_KEEPFRAG 0x000800 /* keep fragment information */
+ FR_KEEPSTATE 0x001000 /* keep `connection' state information */
+ FR_INACTIVE 0x002000
+ FR_QUICK 0x004000 /* match & stop processing list */
+ FR_FASTROUTE 0x008000 /* bypass normal routing */
+ FR_CALLNOW 0x010000 /* call another function (fr_func) if matches */
+ FR_DUP 0x020000 /* duplicate the packet */
+ FR_LOGORBLOCK 0x040000 /* block the packet if it can't be logged */
+ FR_NOTSRCIP 0x080000 /* not the src IP# */
+ FR_NOTDSTIP 0x100000 /* not the dst IP# */
+ FR_AUTH 0x200000 /* use authentication */
+ FR_PREAUTH 0x400000 /* require preauthentication */
+
+.fi
+.PP
+Values for fr_scomp and fr_dcomp (source and destination port value
+comparisons) :
+.LP
+.nf
+ FR_NONE 0
+ FR_EQUAL 1
+ FR_NEQUAL 2
+ FR_LESST 3
+ FR_GREATERT 4
+ FR_LESSTE 5
+ FR_GREATERTE 6
+ FR_OUTRANGE 7
+ FR_INRANGE 8
+.fi
+.PP
+The third ioctl, SIOCIPFFL, flushes either the input filter list, the
+output filter list or both and it returns the number of filters removed
+from the list(s). The values which it will take and recognise are FR_INQUE
+and FR_OUTQUE (see above). This ioctl is also implemented for
+\fB/dev/ipstate\fP and will flush all state tables entries if passed 0
+or just all those which are not established if passed 1.
+
+.IP "\fBGeneral Logging Flags\fP" 0
+There are two flags which can be set to log packets independently of the
+rules used. These allow for packets which are either passed or blocked
+to be logged. To set (and clear)/get these flags, two ioctls are
+provided:
+.IP SIOCSETFF 16
+Takes an unsigned integer as the parameter. The flags are then set to
+those provided (clearing/setting all in one).
+.nf
+
+ FF_LOGPASS 0x10000000
+ FF_LOGBLOCK 0x20000000
+ FF_LOGNOMATCH 0x40000000
+ FF_BLOCKNONIP 0x80000000 /* Solaris 2.x only */
+.fi
+.IP SIOCGETFF 16
+Takes a pointer to an unsigned integer as the parameter. A copy of the
+flags currently in used is copied to user space.
+.IP "\fBFilter statistics\fP" 0
+Statistics on the various operations performed by this package on packets
+is kept inside the kernel. These statistics apply to packets traversing
+through the kernel. To retrieve this structure, use this ioctl:
+.nf
+
+ ioctl(fd, SIOCGETFS, struct friostat *)
+
+struct friostat {
+ struct filterstats f_st[2];
+ struct frentry *f_fin[2];
+ struct frentry *f_fout[2];
+ struct frentry *f_acctin[2];
+ struct frentry *f_acctout[2];
+ struct frentry *f_auth;
+ u_long f_froute[2];
+ int f_active; /* 1 or 0 - active rule set */
+ int f_defpass; /* default pass - from fr_pass */
+ int f_running; /* 1 if running, else 0 */
+ int f_logging; /* 1 if enabled, else 0 */
+ char f_version[32]; /* version string */
+};
+
+struct filterstats {
+ u_long fr_pass; /* packets allowed */
+ u_long fr_block; /* packets denied */
+ u_long fr_nom; /* packets which don't match any rule */
+ u_long fr_ppkl; /* packets allowed and logged */
+ u_long fr_bpkl; /* packets denied and logged */
+ u_long fr_npkl; /* packets unmatched and logged */
+ u_long fr_pkl; /* packets logged */
+ u_long fr_skip; /* packets to be logged but buffer full */
+ u_long fr_ret; /* packets for which a return is sent */
+ u_long fr_acct; /* packets for which counting was performed */
+ u_long fr_bnfr; /* bad attempts to allocate fragment state */
+ u_long fr_nfr; /* new fragment state kept */
+ u_long fr_cfr; /* add new fragment state but complete pkt */
+ u_long fr_bads; /* bad attempts to allocate packet state */
+ u_long fr_ads; /* new packet state kept */
+ u_long fr_chit; /* cached hit */
+ u_long fr_pull[2]; /* good and bad pullup attempts */
+#if SOLARIS
+ u_long fr_notdata; /* PROTO/PCPROTO that have no data */
+ u_long fr_nodata; /* mblks that have no data */
+ u_long fr_bad; /* bad IP packets to the filter */
+ u_long fr_notip; /* packets passed through no on ip queue */
+ u_long fr_drop; /* packets dropped - no info for them! */
+#endif
+};
+.fi
+If we wanted to retrieve all the statistics and reset the counters back to
+0, then the ioctl() call would be made to SIOCFRZST rather than SIOCGETFS.
+In addition to the statistics above, each rule keeps a hit count, counting
+both number of packets and bytes. To reset these counters for a rule,
+load the various rule information into a frentry structure and call
+SIOCZRLST.
+.IP "Swapping Active lists" 0
+IP Filter supports two lists of rules for filtering and accounting: an
+active list and an inactive list. This allows for large scale rule base
+changes to be put in place atomically with otherwise minimal interruption.
+Which of the two is active can be changed using the SIOCSWAPA ioctl. It
+is important to note that no passed argument is recognised and that the
+value returned is that of the list which is now inactive.
+.br
+.SH FILES
+/dev/ipauth
+.br
+/dev/ipl
+.br
+/dev/ipnat
+.br
+/dev/ipstate
+.SH SEE ALSO
+ipl(4), ipnat(4), ipf(5), ipf(8), ipfstat(8)
diff --git a/sbin/ipf/ipf/ipf.5 b/sbin/ipf/ipf/ipf.5
new file mode 100644
index 000000000000..7f72a817617b
--- /dev/null
+++ b/sbin/ipf/ipf/ipf.5
@@ -0,0 +1,1698 @@
+.\" $FreeBSD$
+.TH IPF 5
+.SH NAME
+ipf, ipf.conf \- IPFilter firewall rules file format
+.SH DESCRIPTION
+.PP
+The ipf.conf file is used to specify rules for the firewall, packet
+authentication and packet accounting components of IPFilter. To load rules
+specified in the ipf.conf file, the ipf(8) program is used.
+.PP
+For use as a firewall, there are two important rule types: those that block
+and drop packets (block rules) and those that allow packets through (pass
+rules.) Accompanying the decision to apply is a collection of statements
+that specify under what conditions the result is to be applied and how.
+.PP
+The simplest rules that can be used in ipf.conf are expressed like this:
+.PP
+.nf
+block in all
+pass out all
+.fi
+.PP
+Each rule must contain at least the following three components
+.RS
+.IP *
+a decision keyword (pass, block, etc.)
+.IP *
+the direction of the packet (in or out)
+.IP *
+address patterns or "all" to match any address information
+.RE
+.SS Long lines
+.PP
+For rules lines that are particularly long, it is possible to split
+them over multiple lines implicity like this:
+.PP
+.nf
+pass in on bgeo proto tcp from 1.1.1.1 port > 1000
+ to 2.2.2.2 port < 5000 flags S keep state
+.fi
+.PP
+or explicitly using the backslash ('\\') character:
+.PP
+.nf
+pass in on bgeo proto tcp from 1.1.1.1 port > 1000 \\
+ to 2.2.2.2 port < 5000 flags S keep state
+.fi
+.SS Comments
+.PP
+Comments in the ipf.conf file are indicated by the use of the '#' character.
+This can either be at the start of the line, like this:
+.PP
+.nf
+# Allow all ICMP packets in
+pass in proto icmp from any to any
+.fi
+.PP
+Or at the end of a like, like this:
+.PP
+.nf
+pass in proto icmp from any to any # Allow all ICMP packets in
+.fi
+.SH Firewall rules
+.PP
+This section goes into detail on how to construct firewall rules that
+are placed in the ipf.conf file.
+.PP
+It is beyond the scope of this document to describe what makes a good
+firewall rule set or which packets should be blocked or allowed in.
+Some suggestions will be provided but further reading is expected to
+fully understand what is safe and unsafe to allow in/out.
+.SS Filter rule keywords
+.PP
+The first word found in any filter rule describes what the eventual outcome
+of a packet that matches it will be. Descriptions of the many and various
+sections that can be used to match on the contents of packet headers will
+follow on below.
+.PP
+The complete list of keywords, along with what they do is as follows:
+.RS
+.HP
+pass
+rules that match a packet indicate to ipfilter that it should be
+allowed to continue on in the direction it is flowing.
+.HP
+block
+rules are used when it is desirable to prevent a packet from going
+any further. Packets that are blocked on the "in" side are never seen by
+TCP/IP and those that are blocked going "out" are never seen on the wire.
+.HP
+log
+when IPFilter successfully matches a packet against a log rule a log
+record is generated and made available for ipmon(8) to read. These rules
+have no impact on whether or not a packet is allowed through or not.
+So if a packet first matched a block rule and then matched a log rule,
+the status of the packet after the log rule is that it will still be
+blocked.
+.HP
+count
+rules provide the administrator with the ability to count packets and
+bytes that match the criteria laid out in the configuration file.
+The count rules are applied after NAT and filter rules on the inbound
+path. For outbound packets, count rules are applied before NAT and
+before the packet is dropped. Thus the count rule cannot be used as
+a true indicator of link layer
+.HP
+auth
+rules cause the matching packet to be queued up for processing by a
+user space program. The user space program is responsible for making
+an ioctl system call to collect the information about the queued
+packet and another ioctl system call to return the verdict (block,
+pass, etc) on what to do with the packet. In the event that the queue
+becomes full, the packets will end up being dropped.
+.HP
+call
+provides access to functions built into IPFilter that allow for more
+complex actions to be taken as part of the decision making that goes
+with the rule.
+.HP
+decapsulate
+rules instruct ipfilter to remove any
+other headers (IP, UDP, AH) and then process what is inside as a new packet.
+For non-UDP packets, there are builtin checks that are applied in addition
+to whatever is specified in the rule, to only allow decapsulation of
+recognised protocols. After decapsulating the inner packet, any filtering
+result that is applied to the inner packet is also applied to the other
+packet.
+.PP
+The default way in which filter rules are applied is for the last
+matching rule to be used as the decision maker. So even if the first
+rule to match a packet is a pass, if there is a later matching rule
+that is a block and no further rules match the packet, then it will
+be blocked.
+.SS Matching Network Interfaces
+.PP
+On systems with more than one network interface, it is necessary
+to be able to specify different filter rules for each of them.
+In the first instance, this is because different networks will send us
+packets via each network interface but it is also because of the hosts,
+the role and the resulting security policy that we need to be able to
+distinguish which network interface a packet is on.
+.PP
+To accommodate systems where the presence of a network interface is
+dynamic, it is not necessary for the network interface named in a
+filter rule to be present in the system when the rule is loaded.
+This can lead to silent errors being introduced and unexpected
+behaviour with the simplest of keyboard mistakes - for example,
+typing in hem0 instead of hme0 or hme2 instead of hme3.
+.PP
+On Solaris systems prior to Solaris 10 Update 4, it is not possible
+to filter packets on the loopback interface (lo0) so filter rules
+that specify it will have no impact on the corresponding flow of
+packets. See below for Solaris specific tips on how to enable this.
+.PP
+Some examples of including the network interface in filter rules are:
+.PP
+.nf
+block in on bge0 all
+pass out on bge0 all
+.fi
+.SS Address matching (basic)
+.PP
+The first and most basic part of matching for filtering rules is to
+specify IP addresses and TCP/UDP port numbers. The source address
+information is matched by the "from" information in a filter rule
+and the destination address information is matched with the "to"
+information in a filter rule.
+.PP
+The typical format used for IP addresses is CIDR notation, where an
+IP address (or network) is followed by a '/' and a number representing
+the size of the netmask in bits. This notation is used for specifying
+address matching in both IPv4 and IPv6. If the '/' and bitmask size
+are excluded from the matching string, it is assumed that the address
+specified is a host address and that the netmask applied should be
+all 1's.
+.PP
+Some examples of this are:
+.PP
+.nf
+pass in from 10.1.0.0/24 to any
+block out from any to 10.1.1.1
+.fi
+.PP
+It is not possible to specify a range of addresses that does not
+have a boundary that can be defined by a standard subnet mask.
+.IP
+.B Names instead of addresses
+.RS
+.PP
+Hostnames, resolved either via DNS or /etc/hosts, or network names,
+resolved via /etc/networks, may be used in place of actual addresses
+in the filter rules. WARNING: if a hostname expands to more than one
+address, only the *first* is used in building the filter rule.
+.PP
+Caution should be exercised when relying on DNS for filter rules in
+case the sending and receiving of DNS packets is blocked when ipf(8)
+is processing that part of the configuration file, leading to long
+delays, if not errors, in loading the filter rules.
+.RE
+.SS Protocol Matching
+.PP
+To match packets based on TCP/UDP port information, it is first necessary
+to indicate which protocol the packet must be. This is done using the
+"proto" keyword, followed by either the protocol number or a name which
+is mapped to the protocol number, usually through the /etc/protocols file.
+.PP
+.nf
+pass in proto tcp from 10.1.0.0/24 to any
+block out proto udp from any to 10.1.1.1
+pass in proto icmp from any to 192.168.0.0/16
+.fi
+.SS Sending back error packets
+.PP
+When a packet is just discarded using a block rule, there is no feedback given
+to the host that sent the packet. This is both good and bad. If this is the
+desired behaviour and it is not desirable to send any feedback about packets
+that are to be denied. The catch is that often a host trying to connect to a
+TCP port or with a UDP based application will send more than one packet
+because it assumes that just one packet may be discarded so a retry is
+required. The end result being logs can become cluttered with duplicate
+entries due to the retries.
+.PP
+To address this problem, a block rule can be qualified in two ways.
+The first of these is specific to TCP and instructs IPFilter to send back
+a reset (RST) packet. This packet indicates to the remote system that the
+packet it sent has been rejected and that it shouldn't make any further
+attempts( to send packets to that port. Telling IPFilter to return a TCP);
+RST packet in response to something that has been received is achieved
+with the return-rst keyword like this:
+.PP
+.nf
+block return-rst in proto tcp from 10.0.0.0/8 to any
+.fi
+.PP
+When sending back a TCP RST packet, IPFilter must construct a new packet
+that has the source address of the intended target, not the source address
+of the system it is running on (if they are different.)
+.PP
+For all of the other protocols handled by the IP protocol suite, to send
+back an error indicating that the received packet was dropped requires
+sending back an ICMP error packet. Whilst these can also be used for TCP,
+the sending host may not treat the received ICMP error as a hard error
+in( the same way as it does the TCP RST packet. To return an ICMP error);
+it is necessary to place return-icmp after the block keyword like this:
+.PP
+.nf
+block return-icmp in proto udp from any to 192.168.0.1/24
+.fi
+.PP
+When( electing to return an ICMP error packet, it is also possible to);
+select what type of ICMP error is returned. Whilst the full compliment
+of ICMP unreachable codes can be used by specifying a number instead of
+the string below, only the following should be used in conjunction with
+return-icmp.( Which return code to use is a choice to be made when);
+weighing up the pro's and con's. Using some of the codes may make it
+more obvious that a firewall is being used rather than just the host
+not responding.
+.RS
+.HP
+filter-prohib
+(prohibited by filter)
+sending packets to the destination given in the received packet is
+prohibited due to the application of a packet filter
+.HP
+net-prohib
+(prohibited network)
+sending packets to the destination given in the received packet is
+administratively prohibited.
+.HP
+host-unk
+(host unknown)
+the destination host address is not known by the system receiving
+the packet and therefore cannot be reached.
+.HP
+host-unr
+(host unreachable)
+it is not possible to reach the host as given by the destination address
+in the packet header.
+.HP
+net-unk
+(network unknown)
+the destination network address is not known by the system receiving
+the packet and therefore cannot be reached.
+.HP
+net-unr
+(network unreachable)
+it is not possible to forward the packet on to its final destination
+as given by the destination address
+.HP
+port-unr
+(port unreachable)
+there is no application using the given destination port and therefore
+it is not possible to reach that port.
+.HP
+proto-unr
+(protocol unreachable)
+the IP protocol specified in the packet is not available to receive
+packets.
+.DE
+.PP
+An example that shows how to send back a port unreachable packet for
+UDP packets to 192.168.1.0/24 is as follows:
+.PP
+.nf
+block return-icmp(port-unr) in proto udp from any to 192.168.1.0/24
+.fi
+.PP
+In the above examples, when sending the ICMP packet, IPFilter will construct
+a new ICMP packet with a source address of the network interface used to
+send the packet back to the original source. This can give away that there
+is an intermediate system blocking packets. To have IPFilter send back
+ICMP packets where the source address is the original destination, regardless
+of whether or not it is on the local host, return-icmp-as-dest is used like
+this:
+.PP
+.nf
+block return-icmp-as-dest(port-unr) in proto udp \\
+ from any to 192.168.1.0/24
+.fi
+.SS TCP/UDP Port Matching
+.PP
+Having specified which protocol is being matched, it is then possible to
+indicate which port numbers a packet must have in order to match the rule.
+Due to port numbers being used differently to addresses, it is therefore
+possible to match on them in different ways. IPFilter allows you to use
+the following logical operations:
+.IP "< x"
+is true if the port number is greater than or equal to x and less than or
+equal to y
+is true if the port number in the packet is less than x
+.IP "<= x"
+is true if the port number in the packet is less than or equal to x
+.IP "> x"
+is true if the port number in the packet is greater than x
+.IP ">= x"
+is true if the port number in the packet is greater or equal to x
+.IP "= x"
+is true if the port number in the packet is equal to x
+.IP "!= x"
+is true if the port number in the packet is not equal to x
+.PP
+Additionally, there are a number of ways to specify a range of ports:
+.IP "x <> y"
+is true if the port number is less than a and greater than y
+.IP "x >< y"
+is true if the port number is greater than x and less than y
+.IP "x:y"
+is true if the port number is greater than or equal to x and less than or
+equal to y
+.PP
+Some examples of this are:
+.PP
+.nf
+block in proto tcp from any port >= 1024 to any port < 1024
+pass in proto tcp from 10.1.0.0/24 to any port = 22
+block out proto udp from any to 10.1.1.1 port = 135
+pass in proto udp from 1.1.1.1 port = 123 to 10.1.1.1 port = 123
+pass in proto tcp from 127.0.0.0/8 to any port 6000:6009
+.fi
+.PP
+If there is no desire to mention any specific source or destintion
+information in a filter rule then the word "all" can be used to
+indicate that all addresses are considered to match the rule.
+.SS IPv4 or IPv6
+.PP
+If a filter rule is constructed without any addresses then IPFilter
+will attempt to match both IPv4 and IPv6 packets with it. In the
+next list of rules, each one can be applied to either network protocol
+because there is no address specified from which IPFilter can derive
+with network protocol to expect.
+.PP
+.nf
+pass in proto udp from any to any port = 53
+block in proto tcp from any port < 1024 to any
+.fi
+.PP
+To explicitly match a particular network address family with a specific
+rule, the family must be added to the rule. For IPv4 it is necessary to
+add family inet and for IPv6, family inet6. Thus the next rule will
+block all packets (both IPv4 and IPv6:
+.PP
+.nf
+block in all
+.fi
+.PP
+but in the following example, we block all IPv4 packets and only allow
+in IPv6 packets:
+.PP
+.nf
+block in family inet all
+pass in family inet6 all
+.fi
+.PP
+To continue on from the example where we allowed either IPv4 or IPv6
+packets to port 53 in, to change that such that only IPv6 packets to
+port 53 need to allowed blocked then it is possible to add in a
+protocol family qualifier:
+.PP
+.nf
+pass in family inet6 proto udp from any to any port = 53
+.fi
+.SS First match vs last match
+.PP
+To change the default behaviour from being the last matched rule decides
+the outcome to being the first matched rule, the word "quick" is inserted
+to the rule.
+.SH Extended Packet Matching
+.SS Beyond using plain addresses
+.PP
+On firewalls that are working with large numbers of hosts and networks
+or simply trying to filter discretely against various hosts, it can
+be an easier administration task to define a pool of addresses and have
+a filter rule reference that address pool rather than have a rule for
+each address.
+.PP
+In addition to being able to use address pools, it is possible to use
+the interface name(s) in the from/to address fields of a rule. If the
+name being used in the address section can be matched to any of the
+interface names mentioned in the rule's "on" or "via" fields then it
+can be used with one of the following keywords for extended effect:
+.HP
+broadcast
+use the primary broadcast address of the network interface for matching
+packets with this filter rule;
+.IP
+.nf
+pass in on fxp0 proto udp from any to fxp0/broadcast port = 123
+.fi
+.HP
+peer
+use the peer address on point to point network interfaces for matching
+packets with this filter rule. This option typically only has meaningful
+use with link protocols such as SLIP and PPP.
+For example, this rule allows ICMP packets from the remote peer of ppp0
+to be received if they're destined for the address assigned to the link
+at the firewall end.
+.IP
+.nf
+pass in on ppp0 proto icmp from ppp0/peer to ppp0/32
+.fi
+.HP
+netmasked
+use the primary network address, with its netmask, of the network interface
+for matching packets with this filter rule. If a network interface had an
+IP address of 192.168.1.1 and its netmask was 255.255.255.0 (/24), then
+using the word "netmasked" after the interface name would match any
+addresses that would match 192.168.1.0/24. If we assume that bge0 has
+this IP address and netmask then the following two rules both serve
+to produce the same effect:
+.IP
+.nf
+pass in on bge0 proto icmp from any to 192.168.1.0/24
+pass in on bge0 proto icmp from any to bge0/netmasked
+.fi
+.HP
+network
+using the primary network address, and its netmask, of the network interface,
+construct an address for exact matching. If a network interface has an
+address of 192.168.1.1 and its netmask is 255.255.255.0, using this
+option would only match packets to 192.168.1.0.
+.IP
+.nf
+pass in on bge0 proto icmp from any to bge0/network
+.fi
+.PP
+Another way to use the name of a network interface to get the address
+is to wrap the name in ()'s. In the above method, IPFilter
+looks at the interface names in use and to decide whether or not
+the name given is a hostname or network interface name. With the
+use of ()'s, it is possible to tell IPFilter that the name should
+be treated as a network interface name even though it doesn't
+appear in the list of network interface that the rule ias associated
+with.
+.IP
+.nf
+pass in proto icmp from any to (bge0)/32
+.fi
+.SS Using address pools
+.PP
+Rather than list out multiple rules that either allow or deny specific
+addresses, it is possible to create a single object, call an address
+pool, that contains all of those addresses and reference that in the
+filter rule. For documentation on how to write the configuration file
+for those pools and load them, see ippool.conf(5) and ippool(8).
+There are two types of address pools that can be defined in ippool.conf(5):
+trees and hash tables. To refer to a tree defined in ippool.conf(5),
+use this syntax:
+.PP
+.nf
+pass in from pool/trusted to any
+.fi
+.PP
+Either a name or number can be used after the '/', just so long as it
+matches up with something that has already been defined in ipool.conf(5)
+and loaded in with ippool(8). Loading a filter rule that references a
+pool that does not exist will result in an error.
+.PP
+If hash tables have been used in ippool.conf(5) to store the addresses
+in instead of a tree, then replace the word pool with hash:
+.IP
+.nf
+pass in from any to hash/webservers
+.fi
+.PP
+There are different operational characteristics with each, so there
+may be some situations where a pool works better than hash and vice
+versa.
+.SS Matching TCP flags
+.PP
+The TCP header contains a field of flags that is used to decide if the
+packet is a connection request, connection termination, data, etc.
+By matching on the flags in conjunction with port numbers, it is
+possible to restrict the way in which IPFilter allows connections to
+be created. A quick overview of the TCP
+flags is below. Each is listed with the letter used in IPFilter
+rules, followed by its three or four letter pneumonic.
+.HP
+S
+SYN - this bit is set when a host is setting up a connection.
+The initiator typically sends a packet with the SYN bit and the
+responder sends back SYN plus ACK.
+.HP
+A
+ACK - this bit is set when the sender wishes to acknowledge the receipt
+of a packet from another host
+.HP
+P
+PUSH - this bit is set when a sending host has send some data that
+is yet to be acknowledged and a reply is sought
+.HP
+F
+FIN - this bit is set when one end of a connection starts to close
+the connection down
+.HP
+U
+URG - this bit is set to indicate that the packet contains urgent data
+.HP
+R
+RST - this bit is set only in packets that are a reply to another
+that has been received but is not targetted at any open port
+.HP
+C
+CWN
+.HP
+E
+ECN
+.PP
+When matching TCP flags, it is normal to just list the flag that you
+wish to be set. By default the set of flags it is compared against
+is "FSRPAU". Rules that say "flags S" will be displayed by ipfstat(8)
+as having "flags S/FSRPAU". This is normal.
+The last two flags, "C" and "E", are optional - they
+may or may not be used by an end host and have no bearing on either
+the acceptance of data nor control of the connection. Masking them
+out with "flags S/FSRPAUCE" may cause problems for remote hosts
+making a successful connection.
+.PP
+.nf
+pass in quick proto tcp from any to any port = 22 flags S/SAFR
+pass out quick proto tcp from any port = 22 to any flags SA
+.fi
+.PP
+By itself, filtering based on the TCP flags becomes more work but when
+combined with stateful filtering (see below), the situation changes.
+.SS Matching on ICMP header information
+.PP
+The TCP and UDP are not the only protocols for which filtering beyond
+just the IP header is possible, extended matching on ICMP packets is
+also available. The list of valid ICMP types is different for IPv4
+vs IPv6.
+.PP
+As a practical example, to allow the ping command to work
+against a specific target requires allowing two different types of
+ICMP packets, like this:
+.PP
+.nf
+pass in proto icmp from any to webserver icmp-type echo
+pass out proto icmp from webserver to any icmp-type echorep
+.fi
+.PP
+The ICMP header has two fields that are of interest for filtering:
+the ICMP type and code. Filter rules can accept either a name or
+number for both the type and code. The list of names supported for
+ICMP types is listed below, however only ICMP unreachable errors
+have named codes (see above.)
+.PP
+The list of ICMP types that are available for matching an IPv4 packet
+are as follows:
+.PP
+echo (echo request),
+echorep (echo reply),
+inforeq (information request),
+inforep (information reply),
+maskreq (mask request),
+maskrep (mask reply),
+paramprob (parameter problem),
+redir (redirect),
+routerad (router advertisement),
+routersol (router solicit),
+squence (source quence),
+timest (timestamp),
+timestreq (timestamp reply),
+timex (time exceeded),
+unreach (unreachable).
+.PP
+The list of ICMP types that are available for matching an IPv6 packet
+are as follows:
+.PP
+echo (echo request),
+echorep (echo reply),
+fqdnquery (FQDN query),
+fqdnreply (FQDN reply),
+inforeq (information request),
+inforep (information reply),
+listendone (MLD listener done),
+listendqry (MLD listener query),
+listendrep (MLD listener reply),
+neighadvert (neighbour advert),
+neighborsol (neighbour solicit),
+paramprob (parameter problem),
+redir (redirect),
+renumber (router renumbering),
+routerad (router advertisement),
+routersol (router solicit),
+timex (time exceeded),
+toobig (packet too big),
+unreach (unreachable,
+whoreq (WRU request),
+whorep (WRU reply).
+.SH Stateful Packet Filtering
+.PP
+Stateful packet filtering is where IPFilter remembers some information from
+one or more packets that it has seen and is able to apply it to future
+packets that it receives from the network.
+.PP
+What this means for each transport layer protocol is different.
+For TCP it means that if IPFilter
+sees the very first packet of an attempt to make a connection, it has enough
+information to allow all other subsequent packets without there needing to
+be any explicit rules to match them. IPFilter uses the TCP port numbers,
+TCP flags, window size and sequence numbers to determine which packets
+should be matched. For UDP, only the UDP port numbers are available.
+For ICMP, the ICMP types can be combined with the ICMP id field to
+determine which reply packets match a request/query that has already
+been seen. For all other protocols, only matching on IP address and
+protocol number is available for determining if a packet received is a mate
+to one that has already been let through.
+.PP
+The difference this makes is a reduction in the number of rules from
+2 or 4 to 1. For example, these 4 rules:
+.PP
+.nf
+pass in on bge0 proto tcp from any to any port = 22
+pass out on bge1 proto tcp from any to any port = 22
+pass in on bge1 proto tcp from any port = 22 to any
+pass out on bge0 proto tcp from any port = 22 to any
+.fi
+.PP
+can be replaced with this single rule:
+.PP
+.nf
+pass in on bge0 proto tcp from any to any port = 22 flags S keep state
+.fi
+.PP
+Similar rules for UDP and ICMP might be:
+.PP
+.nf
+pass in on bge0 proto udp from any to any port = 53 keep state
+pass in on bge0 proto icmp all icmp-type echo keep state
+.fi
+.PP
+When using stateful filtering with TCP it is best to add "flags S" to the
+rule to ensure that state is only created when a packet is seen that is
+an indication of a new connection. Although IPFilter can gather some
+information from packets in the middle of a TCP connection to do stateful
+filtering, there are some options that are only sent at the start of the
+connection which alter the valid window of what TCP accepts. The end result
+of trying to pickup TCP state in mid connection is that some later packets
+that are part of the connection may not match the known state information
+and be dropped or blocked, causing problems. If a TCP packet matches IP
+addresses and port numbers but does not fit into the recognised window,
+it will not be automatically allowed and will be flagged inside of
+IPFitler as "out of window" (oow). See below, "Extra packet attributes",
+for how to match on this attribute.
+.PP
+Once a TCP connection has reached the established state, the default
+timeout allows for it to be idle for 5 days before it is removed from
+the state table. The timeouts for the other TCP connection states
+vary from 240 seconds to 30 seconds.
+Both UDP and ICMP state entries have asymetric timeouts where the timeout
+set upon seeing packets in the forward direction is much larger than
+for the reverse direction. For UDP the default timeouts are 120 and
+12 seconds, for ICMP 60 and 6 seconds. This is a reflection of the
+use of these protocols being more for query-response than for ongoing
+connections. For all other protocols the
+timeout is 60 seconds in both directions.
+.SS Stateful filtering options
+.PP
+The following options can be used with stateful filtering:
+.HP
+limit
+limit the number of state table entries that this rule can create to
+the number given after limit. A rule that has a limit specified is
+always permitted that many state table entries, even if creating an
+additional entry would cause the table to have more entries than the
+otherwise global limit.
+.IP
+.nf
+pass ... keep state(limit 100)
+.fi
+.HP
+age
+sets the timeout for the state entry when it sees packets going through
+it. Additionally it is possible to set the tieout for the reply packets
+that come back through the firewall to a different value than for the
+forward path. allowing a short timeout to be set after the reply has
+been seen and the state no longer required.
+.RS
+.PP
+.nf
+pass in quick proto icmp all icmp-type echo \\
+ keep state (age 3)
+pass in quick proto udp from any \\
+ to any port = 53 keep state (age 30/1)
+.fi
+.RE
+.HP
+strict
+only has an impact when used with TCP. It forces all packets that are
+allowed through the firewall to be sequential: no out of order delivery
+of packets is allowed. This can cause significant slowdown for some
+connections and may stall others. Use with caution.
+.IP
+.nf
+pass in proto tcp ... keep state(strict)
+.fi
+.HP
+noicmperr
+prevents ICMP error packets from being able to match state table entries
+created with this flag using the contents of the original packet included.
+.IP
+.nf
+pass ... keep state(noicmperr)
+.fi
+.HP
+sync
+indicates to IPFilter that it needs to provide information to the user
+land daemons responsible for syncing other machines state tables up
+with this one.
+.IP
+.nf
+pass ... keep state(sync)
+.fi
+.HP
+nolog
+do not generate any log records for the creation or deletion of state
+table entries.
+.IP
+.nf
+pass ... keep state(nolog)
+.fi
+.HP
+icmp-head
+rather than just precent ICMP error packets from being able to match
+state table entries, allow an ACL to be processed that can filter in or
+out ICMP error packets based as you would with normal firewall rules.
+The icmp-head option requires a filter rule group number or name to
+be present, just as you would use with head.
+.RS
+.PP
+.nf
+pass in quick proto tcp ... keep state(icmp-head 101)
+block in proto icmp from 10.0.0.0/8 to any group 101
+.fi
+.RE
+.HP
+max-srcs
+allows the number of distinct hosts that can create a state entry to
+be defined.
+.IP
+.nf
+pass ... keep state(max-srcs 100)
+pass ... keep state(limit 1000, max-srcs 100)
+.fi
+.HP
+max-per-src
+whilst max-srcs limits the number of individual hosts that may cause
+the creation of a state table entry, each one of those hosts is still
+table to fill up the state table with new entries until the global
+maximum is reached. This option allows the number of state table entries
+per address to be limited.
+.IP
+.nf
+pass ... keep state(max-srcs 100, max-per-src 1)
+pass ... keep state(limit 100, max-srcs 100, max-per-src 1)
+.fi
+.IP
+Whilst these two rules might seem identical, in that they both
+ultimately limit the number of hosts and state table entries created
+from the rule to 100, there is a subtle difference: the second will
+always allow up to 100 state table entries to be created whereas the
+first may not if the state table fills up from other rules.
+.IP
+Further, it is possible to specify a netmask size after the per-host
+limit that enables the per-host limit to become a per-subnet or
+per-network limit.
+.IP
+.nf
+pass ... keep state(max-srcs 100, max-per-src 1/24)
+.fi
+.IP
+If there is no IP protocol implied by addresses or other features of
+the rule, IPFilter will assume that no netmask is an all ones netmask
+for both IPv4 and IPv6.
+.SS Tieing down a connection
+.PP
+For any connection that transits a firewall, each packet will be seen
+twice: once going in and once going out. Thus a connection has 4 flows
+of packets:
+.HP
+forward
+inbound packets
+.HP
+forward
+outbound packets
+.HP
+reverse
+inbound packets
+.HP
+reverse
+outbound packets
+.PP
+IPFilter allows you to define the network interface to be used at all
+four points in the flow of packets. For rules that match inbound packets,
+out-via is used to specify which interfaces the packets go out, For rules
+that match outbound packets, in-via is used to match the inbound packets.
+In each case, the syntax generalises to this:
+.PP
+.nf
+pass ... in on forward-in,reverse-in \\
+ out-via forward-out,reverse-out ...
+
+pass ... out on forward-out,reverse-out \\
+ in-via forward-in,reverse-in ...
+.fi
+.PP
+An example that pins down all 4 network interfaces used by an ssh
+connection might look something like this:
+.PP
+.nf
+pass in on bge0,bge1 out-via bge1,bge0 proto tcp \\
+ from any to any port = 22 flags S keep state
+.fi
+.SS Working with packet fragments
+.PP
+Fragmented packets result in 1 packet containing all of the layer 3 and 4
+header information whilst the data is split across a number of other packets.
+.PP
+To enforce access control on fragmented packets, one of two approaches
+can be taken. The first is to allow through all of the data fragments
+(those that made up the body of the original packet) and rely on matching
+the header information in the "first" fragment, when it is seen. The
+reception of body fragments without the first will result in the receiving
+host being unable to completely reassemble the packet and discarding all
+of the fragments. The following three rules deny all fragmented packets
+from being received except those that are UDP and even then only allows
+those destined for port 2049 to be completed.
+.PP
+.nf
+block in all with frags
+pass in proto udp from any to any with frag-body
+pass in proto udp from any to any port = 2049 with frags
+.fi
+.PP
+Another mechanism that is available is to track "fragment state".
+This relies on the first fragment of a packet that arrives to be
+the fragment that contains all of the layer 3 and layer 4 header
+information. With the receipt of that fragment before any other,
+it is possible to determine which other fragments are to be allowed
+through without needing to explicitly allow all fragment body packets.
+An example of how this is done is as follows:
+.PP
+.nf
+pass in proto udp from any port = 2049 to any with frags keep frags
+.fi
+.SH Building a tree of rules
+.PP
+Writing your filter rules as one long list of rules can be both inefficient
+in terms of processing the rules and difficult to understand. To make the
+construction of filter rules easier, it is possible to place them in groups.
+A rule can be both a member of a group and the head of a new group.
+.PP
+Using filter groups requires at least two rules: one to be in the group
+one one to send matchign packets to the group. If a packet matches a
+filtre rule that is a group head but does not match any of the rules
+in that group, then the packet is considered to have matched the head
+rule.
+.PP
+Rules that are a member of a group contain the word group followed by
+either a name or number that defines which group they're in. Rules that
+form the branch point or starting point for the group must use the
+word head, followed by either a group name or number. If rules are
+loaded in that define a group but there is no matching head then they
+will effectively be orphaned rules. It is possible to have more than
+one head rule point to the same group, allowing groups to be used
+like subroutines to implement specific common policies.
+.PP
+A common use of filter groups is to define head rules that exist in the
+filter "main line" for each direction with the interfaces in use. For
+example:
+.PP
+.nf
+block in quick on bge0 all head 100
+block out quick on bge0 all head 101
+block in quick on fxp0 all head internal-in
+block out quick on fxp0 all head internal-out
+pass in quick proto icmp all icmp-type echo group 100
+.fi
+.PP
+In the above set of rules, there are four groups defined but only one
+of them has a member rule. The only packets that would be allowed
+through the above ruleset would be ICMP echo packets that are
+received on bge0.
+.PP
+Rules can be both a member of a group and the head of a new group,
+allowing groups to specialise.
+.PP
+.nf
+block in quick on bge0 all head 100
+block in quick proto tcp all head 1006 group 100
+.fi
+.PP
+Another use of filter rule groups is to provide a place for rules to
+be dynamically added without needing to worry about their specific
+ordering amongst the entire ruleset. For example, if I was using this
+simple ruleset:
+.PP
+.nf
+block in quick all with bad
+block in proto tcp from any to any port = smtp head spammers
+pass in quick proto tcp from any to any port = smtp flags S keep state
+.fi
+.PP
+and I was getting lots of connections to my email server from 10.1.1.1
+to deliver spam, I could load the following rule to complement the above:
+.IP
+.nf
+block in quick from 10.1.1.1 to any group spammers
+.fi
+.SS Decapsulation
+.PP
+Rule groups also form a different but vital role for decapsulation rules.
+With the following simple rule, if IPFilter receives an IP packet that has
+an AH header as its layer 4 payload, IPFilter would adjust its view of the
+packet internally and then jump to group 1001 using the data beyond the
+AH header as the new transport header.
+.PP
+.nf
+decapsulate in proto ah all head 1001
+.fi
+.PP
+For protocols that
+are recognised as being used with tunnelling or otherwise encapsulating
+IP protocols, IPFilter is able to decide what it has on the inside
+without any assistance. Some tunnelling protocols use UDP as the
+transport mechanism. In this case, it is necessary to instruct IPFilter
+as to what protocol is inside UDP.
+.PP
+.nf
+decapsulate l5-as(ip) in proto udp from any \\
+ to any port = 1520 head 1001
+.fi
+.PP
+Currently IPFilter only supports finding IPv4 and IPv6 headers
+directly after the UDP header.
+.PP
+If a packet matches a decapsulate rule but fails to match any of the rules
+that are within the specified group, processing of the packet continues
+to the next rule after the decapsulate and IPFilter's internal view of the
+packet is returned to what it was prior to the decapsulate rule.
+.PP
+It is possible to construct a decapsulate rule without the group
+head at the end that ipf(8) will accept but such rules will not
+result in anything happening.
+.SS Policy Based Routing
+.PP
+With firewalls being in the position they often are, at the boundary
+of different networks connecting together and multiple connections that
+have different properties, it is often desirable to have packets flow
+in a direction different to what the routing table instructs the kernel.
+These decisions can often be extended to changing the route based on
+both source and destination address or even port numbers.
+.PP
+To support this kind of configuration, IPFilter allows the next hop
+destination to be specified with a filter rule. The next hop is given
+with the interface name to use for output. The syntax for this is
+interface:ip.address. It is expected that the address given as the next
+hop is directly connected to the network to which the interface is.
+.PP
+.nf
+pass in on bge0 to bge1:1.1.1.1 proto tcp \\
+ from 1.1.2.3 to any port = 80 flags S keep state
+.fi
+.PP
+When this feature is combined with stateful filtering, it becomes
+possible to influence the network interface used to transmit packets
+in both directions because we now have a sense for what its reverse
+flow of packets is.
+.PP
+.nf
+pass in on bge0 to bge1:1.1.1.1 reply-to hme1:2.1.1.2 \\
+ proto tcp from 1.1.2.3 to any port = 80 flags S keep state
+.fi
+.PP
+If the actions of the routing table are perfectly acceptable, but
+you would like to mask the presence of the firewall by not changing
+the TTL in IP packets as they transit it, IPFilter can be instructed
+to do a "fastroute" action like this:
+.PP
+.nf
+pass in on bge0 fastroute proto icmp all
+.fi
+.PP
+This should be used with caution as it can lead to endless packet
+loops. Additionally, policy based routing does not change the IP
+header's TTL value.
+.PP
+A variation on this type of rule supports a duplicate of the original
+packet being created and sent out a different network. This can be
+useful for monitoring traffic and other purposes.
+.PP
+.nf
+pass in on bge0 to bge1:1.1.1.1 reply-to hme1:2.1.1.2 \\
+ dup-to fxp0:10.0.0.1 proto tcp from 1.1.2.3 \\
+ to any port = 80 flags S keep state
+.fi
+.SS Matching IPv4 options
+.PP
+The design for IPv4 allows for the header to be upto 64 bytes long,
+however most traffic only uses the basic header which is 20 bytes long.
+The other 44 bytes can be uesd to store IP options. These options are
+generally not necessary for proper interaction and function on the
+Internet today. For most people it is sufficient to block and drop
+all packets that have any options set. This can be achieved with this
+rule:
+.PP
+.nf
+block in quick all with ipopts
+.fi
+.PP
+This rule is usually placed towards the top of the configuration
+so that all incoming packets are blocked.
+.PP
+If you wanted to allow in a specific IP option type, the syntax
+changes slightly:
+.PP
+.nf
+pass in quick proto igmp all with opt rtralrt
+.fi
+.PP
+The following is a list of IP options that most people encounter and
+what their use/threat is.
+.HP
+lsrr
+(loose source route) the sender of the packet includes a list of addresses
+that they wish the packet to be routed through to on the way to the
+destination. Because replies to such packets are expected to use the
+list of addresses in reverse, hackers are able to very effectively use
+this header option in address spoofing attacks.
+.HP
+rr
+(record route) the sender allocates some buffer space for recording the
+IP address of each router that the packet goes through. This is most often
+used with ping, where the ping response contains a copy of all addresses
+from the original packet, telling the sender what route the packet took
+to get there. Due to performance and security issues with IP header
+options, this is almost no longer used.
+.HP
+rtralrt
+(router alert) this option is often used in IGMP messages as a flag to
+routers that the packet needs to be handled differently. It is unlikely
+to ever be received from an unknown sender. It may be found on LANs or
+otherwise controlled networks where the RSVP protocol and multicast
+traffic is in heavy use.
+.HP
+ssrr
+(strict source route) the sender of the packet includes a list of addresses
+that they wish the packet to be routed through to on the way to the
+destination. Where the lsrr option allows the sender to specify only
+some of the nodes the packet must go through, with the ssrr option,
+every next hop router must be specified.
+.PP
+The complete list of IPv4 options that can be matched on is:
+addext (Address Extention),
+cipso (Classical IP Security Option),
+dps (Dynamic Packet State),
+e-sec (Extended Security),
+eip (Extended Internet Protocol),
+encode (ENCODE),
+finn (Experimental Flow Control),
+imitd (IMI Traffic Descriptor),
+lsrr (Loose Source Route),
+mtup (MTU Probe - obsolete),
+mtur (MTU response - obsolete),
+nop (No Operation),
+nsapa (NSAP Address),
+rr (Record Route),
+rtralrt (Router Alert),
+satid (Stream Identifier),
+sdb (Selective Directed Broadcast),
+sec (Security),
+ssrr (Strict Source Route),
+tr (Tracerote),
+ts (Timestamp),
+ump (Upstream Multicast Packet),
+visa (Experimental Access Control)
+and zsu (Experimental Measurement).
+.SS Security with CIPSO and IPSO
+.PP
+IPFilter supports filtering on IPv4 packets using security attributes embedded
+in the IP options part of the packet. These options are usually only used on
+networks and systems that are using lablled security. Unless you know that
+you are using labelled security and your networking is also labelled, it
+is highly unlikely that this section will be relevant to you.
+.PP
+With the traditional IP Security Options (IPSO), packets can be tagged with
+a security level. The following keywords are recognised and match with the
+relevant RFC with respect to the bit patterns matched:
+confid (confidential),
+rserve-1 (1st reserved value),
+rserve-2 (2nd reserved value),
+rserve-3 (3rd reserved value),
+rserve-4 (4th reserved value),
+secret (secret),
+topsecret (top secret),
+unclass (unclassified).
+.PP
+.nf
+block in quick all with opt sec-class unclass
+pass in all with opt sec-class secret
+.fi
+.SS Matching IPv6 extension headers
+.PP
+Just as it is possible to filter on the various IPv4 header options,
+so too it is possible to filter on the IPv6 extension headers that are
+placed between the IPv6 header and the transport protocol header.
+.PP
+dstopts (destination options),
+esp (encrypted, secure, payload),
+frag (fragment),
+hopopts (hop-by-hop options),
+ipv6 (IPv6 header),
+mobility (IP mobility),
+none,
+routing.
+.SS Logging
+.PP
+There are two ways in which packets can be logged with IPFilter. The
+first is with a rule that specifically says log these types of packets
+and the second is a qualifier to one of the other keywords. Thus it is
+possible to both log and allow or deny a packet with a single rule.
+.PP
+.nf
+pass in log quick proto tcp from any to any port = 22
+.fi
+.PP
+When using stateful filtering, the log action becomes part of the result
+that is remembered about a packet. Thus if the above rule was qualified
+with keep state, every packet in the connection would be logged. To only
+log the first packet from every packet flow tracked with keep state, it
+is necessary to indicate to IPFilter that you only wish to log the first
+packet.
+.PP
+.nf
+pass in log first quick proto tcp from any to any port = 22 \\
+ flags S keep state
+.fi
+.PP
+If it is a requirement that the logging provide an accurate representation
+of which connections are allowed, the log action can be qualified with the
+option or-block. This allows the administrator to instruct IPFilter to
+block the packet if the attempt to record the packet in IPFilter's kernel
+log records (which have an upper bound on size) failed. Unless the system
+shuts down or reboots, once a log record is written into the kernel buffer,
+it is there until ipmon(8) reads it.
+.PP
+.nf
+block in log proto tcp from any to any port = smtp
+pass in log or-block first quick proto tcp from any \\
+ to any port = 22 flags S keep state
+.fi
+.PP
+By default, IPFilter will only log the header portion of a packet received
+on the network. Up to 128 bytes of a packet's body can also
+be logged with the body keyword. ipmon(8) will display the contents of the
+portion of the body logged in hex.
+.PP
+.nf
+block in log body proto icmp all
+.fi
+.PP
+When logging packets from ipmon(8) to syslog, by default ipmon(8) will
+control what syslog facility and priority a packet will be logged with.
+This can be tuned on a per rule basis like this:
+.PP
+.nf
+block in quick log level err all with bad
+pass in log level local1.info proto tcp \\
+ from any to any port = 22 flags S keep state
+.fi
+.PP
+ipfstat(8) reports how many packets have been successfully logged and how
+many failed attempts to log a packet there were.
+.SS Filter rule comments
+.PP
+If there is a desire to associate a text string, be it an administrative
+comment or otherwise, with an IPFilter rule, this can be achieved by giving
+the filter rule a comment. The comment is loaded with the rule into the
+kernel and can be seen when the rules are listed with ipfstat.
+.PP
+.nf
+pass in quick proto tcp from any \\
+ to port = 80 comment "all web server traffic is ok"
+pass out quick proto tcp from any port = 80 \\
+ to any comment "all web server traffic is ok"
+.fi
+.SS Tags
+.PP
+To enable filtering and NAT to correctly match up packets with rules,
+tags can be added at with NAT (for inbound packets) and filtering (for
+outbound packets.) This allows a filter to be correctly mated with its
+NAT rule in the event that the NAT rule changed the packet in a way
+that would mean it is not obvious what it was.
+.PP
+For inbound packets, IPFilter can match the tag used in the filter
+rules with that set by NAT. For outbound rules, it is the reverse,
+the filter sets the tag and the NAT rule matches up with it.
+.PP
+.nf
+pass in ... match-tag(nat=proxy)
+pass out ... set-tag(nat=proxy)
+.fi
+.PP
+Another use of tags is to supply a number that is only used with logging.
+When packets match these rules, the log tag is carried over into the
+log file records generated by ipmon(8). With the correct use of tools
+such as grep, extracting log records of interest is simplified.
+.PP
+.nf
+block in quick log ... set-tag(log=33)
+.fi
+.SH Filter Rule Expiration
+.PP
+IPFilter allows rules to be added into the kernel that it will remove after
+a specific period of time by specifying rule-ttl at the end of a rule.
+When listing rules in the kernel using ipfstat(8), rules that are going
+to expire will NOT display "rule-ttl" with the timeout, rather what will
+be seen is a comment with how many ipfilter ticks left the rule has to
+live.
+.PP
+The time to live is specified in seconds.
+.PP
+.nf
+pass in on fxp0 proto tcp from any \\
+ to port = 22 flags S keep state rule-ttl 30
+.fi
+.SH Internal packet attributes
+.PP
+In addition to being able to filter on very specific network and transport
+header fields, it is possible to filter on other attributes that IPFilter
+attaches to a packet. These attributes are placed in a rule after the
+keyword "with", as can be seen with frags and frag-body above. The
+following is a list of the other attributes available:
+.HP
+oow
+the packet's IP addresses and TCP ports match an existing entry in the
+state table but the sequence numbers indicate that it is outside of the
+accepted window.
+.IP
+.nf
+block return-rst in quick proto tcp from any to any with not oow
+.fi
+.HP
+bcast
+this is set by IPFilter when it receives notification that the link
+layer packet was a broadcast packet. No checking of the IP addresses
+is performned to determine if it is a broadcast destination or not.
+.IP
+.nf
+block in quick proto udp all with bcast
+.fi
+.HP
+mcast
+this is set by IPFilter when it receives notification that the link
+layer packet was a multicast packet. No checking of the IP addresses
+is performned to determine if it is a multicast destination or not.
+.IP
+.nf
+pass in quick proto udp from any to any port = dns with mcast
+.fi
+.HP
+mbcast
+can be used to match a packet that is either a multicast or broadcast
+packet at the link layer, as indicated by the operating system.
+.IP
+.nf
+pass in quick proto udp from any to any port = ntp with mbcast
+.fi
+.HP
+nat
+the packet positively matched a NAT table entry.
+.HP
+bad
+sanity checking of the packet failed. This could indicate that the
+layer 3/4 headers are not properly formed.
+.HP
+bad-src
+when reverse path verification is enabled, this flag will be set when
+the interface the packet is received on does not match that which would
+be used to send a packet out of to the source address in the received
+packet.
+.HP
+bad-nat
+an attempt to perform NAT on the packet failed.
+.HP
+not
+each one of the attributes matched using the "with" keyword can also be
+looked for to not be present. For example, to only allow in good packets,
+I can do this:
+.PP
+.nf
+block in all
+pass in all with not bad
+.fi
+.SH Tuning IPFilter
+.PP
+The ipf.conf file can also be used to tune the behaviour of IPFilter,
+allowing, for example, timeouts for the NAT/state table(s) to be set
+along with their sizes. The presence and names of tunables may change
+from one release of IPFilter to the next. The tunables that can be
+changed via ipf.conf is the same as those that can be seen and modified
+using the -T command line option to ipf(8).
+.PP
+NOTE: When parsing ipf.conf, ipf(8) will apply the settings before
+loading any rules. Thus if your settings are at the top, these may
+be applied whilst the rules not applied if there is an error further
+down in the configuration file.
+.PP
+To set one of the values below, the syntax is simple: "set", followed
+by the name of the tuneable to set and then the value to set it to.
+.PP
+.nf
+set state_max 9999;
+set state_size 10101;
+.fi
+.PP
+A list of the currently available variables inside IPFilter that may
+be tuned from ipf.conf are as follows:
+.HP
+active
+set through -s command line switch of ipf(8). See ipf(8) for detals.
+.HP
+chksrc
+when set, enables reverse path verification on source addresses and
+for filters to match packets with bad-src attribute.
+.HP
+control_forwarding
+when set turns off kernel forwarding when IPFilter is disabled or unloaded.
+.HP
+default_pass
+the default policy - whether packets are blocked or passed, etc - is
+represented by the value of this variable. It is a bit field and the
+bits that can be set are found in <netinet/ip_fil.h>. It is not
+recommended to tune this value directly.
+.HP
+ftp_debug
+set the debugging level of the in-kernel FTP proxy.
+Debug messages will be printed to the system console.
+.HP
+ftp_forcepasv
+when set the FTP proxy must see a PASV/EPSV command before creating
+the state/NAT entries for the 227 response.
+.HP
+ftp_insecure
+when set the FTP proxy will not wait for a user to login before allowing
+data connections to be created.
+.HP
+ftp_pasvonly
+when set the proxy will not create state/NAT entries for when it
+sees either the PORT or EPRT command.
+.HP
+ftp_pasvrdr
+when enabled causes the FTP proxy to create very insecure NAT/state
+entries that will allow any connection between the client and server
+hosts when a 227 reply is seen. Use with extreme caution.
+.HP
+ftp_single_xfer
+when set the FTP proxy will only allow one data connection at a time.
+.HP
+hostmap_size
+sets the size of the hostmap table used by NAT to store address mappings
+for use with sticky rules.
+.HP
+icmp_ack_timeout
+default timeout used for ICMP NAT/state when a reply packet is seen for
+an ICMP state that already exists
+.HP
+icmp_minfragmtu
+sets the minimum MTU that is considered acceptable in an ICMP error
+before deciding it is a bad packet.
+.HP
+icmp_timeout
+default timeout used for ICMP NAT/state when the packet matches the rule
+.HP
+ip_timeout
+default timeout used for NAT/state entries that are not TCP/UDP/ICMP.
+.HP
+ipf_flags
+.HP
+ips_proxy_debug
+this sets the debugging level for the proxy support code.
+When enabled, debugging messages will be printed to the system console.
+.HP
+log_all
+when set it changes the behaviour of "log body" to log the entire packet
+rather than just the first 128 bytes.
+.HP
+log_size
+sets the size of the in-kernel log buffer in bytes.
+.HP
+log_suppress
+when set, IPFilter will check to see if the packet it is logging is
+similar to the one it previously logged and if so, increases
+the occurance count for that packet. The previously logged packet
+must not have yet been read by ipmon(8).
+.HP
+min_ttl
+is used to set the TTL value that packets below will be marked with
+the low-ttl attribute.
+.HP
+nat_doflush
+if set it will cause the NAT code to do a more aggressive flush of the
+NAT table at the next opportunity. Once the flush has been done, the
+value is reset to 0.
+.HP
+nat_lock
+this should only be changed using ipfs(8)
+.HP
+nat_logging
+when set, NAT will create log records that can be read from /dev/ipnat.
+.HP
+nat_maxbucket
+maximum number of entries allowed to exist in each NAT hash bucket.
+This prevents an attacker trying to load up the hash table with
+entries in a single bucket, reducing performance.
+.HP
+nat_rules_size
+size of the hash table to store map rules.
+.HP
+nat_table_max
+maximum number of entries allowed into the NAT table
+.HP
+nat_table_size
+size of the hash table used for NAT
+.HP
+nat_table_wm_high
+when the fill percentage of the NAT table exceeds this mark, more
+aggressive flushing is enabled.
+.HP
+nat_table_wm_low
+this sets the percentage at which the NAT table's agressive flushing
+will turn itself off at.
+.HP
+rdr_rules_size
+size of the hash table to store rdr rules.
+.HP
+state_lock
+this should only be changed using ipfs(8)
+.HP
+state_logging
+when set, the stateful filtering will create log records
+that can be read from /dev/ipstate.
+.HP
+state_max
+maximum number of entries allowed into the state table
+.HP
+state_maxbucket
+maximum number of entries allowed to exist in each state hash bucket.
+This prevents an attacker trying to load up the hash table with
+entries in a single bucket, reducing performance.
+.HP
+state_size
+size of the hash table used for stateful filtering
+.HP
+state_wm_freq
+this controls how often the agressive flushing should be run once the
+state table exceeds state_wm_high in percentage full.
+.HP
+state_wm_high
+when the fill percentage of the state table exceeds this mark, more
+aggressive flushing is enabled.
+.HP
+state_wm_low
+this sets the percentage at which the state table's agressive flushing
+will turn itself off at.
+.HP
+tcp_close_wait
+timeout used when a TCP state entry reaches the FIN_WAIT_2 state.
+.HP
+tcp_closed
+timeout used when a TCP state entry is ready to be removed after either
+a RST packet is seen.
+.HP
+tcp_half_closed
+timeout used when a TCP state entry reaches the CLOSE_WAIT state.
+.HP
+tcp_idle_timeout
+timeout used when a TCP state entry reaches the ESTABLISHED state.
+.HP
+tcp_last_ack
+timeout used when a TCP NAT/state entry reaches the LAST_ACK state.
+.HP
+tcp_syn_received
+timeout applied to a TCP NAT/state entry after SYN-ACK packet has been seen.
+.HP
+tcp_syn_sent
+timeout applied to a TCP NAT/state entry after SYN packet has been seen.
+.HP
+tcp_time_wait
+timeout used when a TCP NAT/state entry reaches the TIME_WAIT state.
+.HP
+tcp_timeout
+timeout used when a TCP NAT/state entry reaches either the half established
+state (one ack is seen after a SYN-ACK) or one side is in FIN_WAIT_1.
+.HP
+udp_ack_timeout
+default timeout used for UDP NAT/state when a reply packet is seen for
+a UDP state that already exists
+.HP
+udp_timeout
+default timeout used for UDP NAT/state when the packet matches the rule
+.HP
+update_ipid
+when set, turns on changing the IP id field in NAT'd packets to a random
+number.
+.SS Table of visible variables
+.PP
+A list of all of the tunables, their minimum, maximum and current
+values is as follows.
+.PP
+.nf
+Name Min Max Current
+active 0 0 0
+chksrc 0 1 0
+control_forwarding 0 1 0
+default_pass 0 MAXUINT 134217730
+ftp_debug 0 10 0
+ftp_forcepasv 0 1 1
+ftp_insecure 0 1 0
+ftp_pasvonly 0 1 0
+ftp_pasvrdr 0 1 0
+ftp_single_xfer 0 1 0
+hostmap_size 1 MAXINT 2047
+icmp_ack_timeout 1 MAXINT 12
+icmp_minfragmtu 0 1 68
+icmp_timeout 1 MAXINT 120
+ip_timeout 1 MAXINT 120
+ipf_flags 0 MAXUINT 0
+ips_proxy_debug 0 10 0
+log_all 0 1 0
+log_size 0 524288 32768
+log_suppress 0 1 1
+min_ttl 0 1 4
+nat_doflush 0 1 0
+nat_lock 0 1 0
+nat_logging 0 1 1
+nat_maxbucket 1 MAXINT 22
+nat_rules_size 1 MAXINT 127
+nat_table_max 1 MAXINT 30000
+nat_table_size 1 MAXINT 2047
+nat_table_wm_high 2 100 99
+nat_table_wm_low 1 99 90
+rdr_rules_size 1 MAXINT 127
+state_lock 0 1 0
+state_logging 0 1 1
+state_max 1 MAXINT 4013
+state_maxbucket 1 MAXINT 26
+state_size 1 MAXINT 5737
+state_wm_freq 2 999999 20
+state_wm_high 2 100 99
+state_wm_low 1 99 90
+tcp_close_wait 1 MAXINT 480
+tcp_closed 1 MAXINT 60
+tcp_half_closed 1 MAXINT 14400
+tcp_idle_timeout 1 MAXINT 864000
+tcp_last_ack 1 MAXINT 60
+tcp_syn_received 1 MAXINT 480
+tcp_syn_sent 1 MAXINT 480
+tcp_time_wait 1 MAXINT 480
+tcp_timeout 1 MAXINT 480
+udp_ack_timeout 1 MAXINT 24
+udp_timeout 1 MAXINT 240
+update_ipid 0 1 0
+.fi
+.SH Calling out to internal functions
+.PP
+IPFilter provides a pair of functions that can be called from a rule
+that allow for a single rule to jump out to a group rather than walk
+through a list of rules to find the group. If you've got multiple
+networks, each with its own group of rules, this feature may help
+provide better filtering performance.
+.PP
+The lookup to find which rule group to jump to is done on either the
+source address or the destination address but not both.
+.PP
+In this example below, we are blocking all packets by default but then
+doing a lookup on the source address from group 1010. The two rules in
+the ipf.conf section are lone members of their group. For an incoming
+packet that is from 1.1.1.1, it will go through three rules: (1) the
+block rule, (2) the call rule and (3) the pass rule for group 1020.
+For a packet that is from 3.3.2.2, it will also go through three rules:
+(1) the block rule, (2) the call rule and (3) the pass rule for group
+1030. Should a packet from 3.1.1.1 arrive, it will be blocked as it
+does not match any of the entries in group 1010, leaving it to only
+match the first rule.
+.PP
+.nf
+from ipf.conf
+-------------
+block in all
+call now srcgrpmap/1010 in all
+pass in proto tcp from any to any port = 80 group 1020
+pass in proto icmp all icmp-type echo group 1030
+
+from ippool.conf
+----------------
+group-map in role=ipf number=1010
+ { 1.1.1.1 group = 1020, 3.3.0.0/16 group = 1030; };
+.fi
+.SS IPFilter matching expressions
+.PP
+An experimental feature that has been added to filter rules is to use
+the same expression matching that is available with various commands
+to flush and list state/NAT table entries. The use of such an expression
+precludes the filter rule from using the normal IP header matching.
+.PP
+.nf
+pass in exp { "tcp.sport 23 or tcp.sport 50" } keep state
+.fi
+.SS Filter rules with BPF
+.PP
+On platforms that have the BPF built into the kernel, IPFilter can be
+built to allow BPF expressions in filter rules. This allows for packet
+matching to be on arbitrary data in the packt. The use of a BPF expression
+replaces all of the other protocol header matching done by IPFilter.
+.PP
+.nf
+pass in bpf-v4 { "tcp and (src port 23 or src port 50)" } \\
+ keep state
+.fi
+.PP
+These rules tend to be
+write-only because the act of compiling the filter expression into the
+BPF instructions loaded into the kernel can make it difficut to
+accurately reconstruct the original text filter. The end result is that
+while ipf.conf() can be easy to read, understanding the output from
+ipfstat might not be.
+.SH VARIABLES
+.PP
+This configuration file, like all others used with IPFilter, supports the
+use of variable substitution throughout the text.
+.PP
+.nf
+nif="ppp0";
+pass in on $nif from any to any
+.fi
+.PP
+would become
+.PP
+.nf
+pass in on ppp0 from any to any
+.fi
+.PP
+Variables can be used recursively, such as 'foo="$bar baz";', so long as
+$bar exists when the parser reaches the assignment for foo.
+.PP
+See
+.B ipf(8)
+for instructions on how to define variables to be used from a shell
+environment.
+.DT
+.SH FILES
+/dev/ipf
+/etc/ipf.conf
+.br
+/usr/share/examples/ipfilter Directory with examples.
+.SH SEE ALSO
+ipf(8), ipfstat(8), ippool.conf(5), ippool(8)
diff --git a/sbin/ipf/ipf/ipf.8 b/sbin/ipf/ipf/ipf.8
new file mode 100644
index 000000000000..3a84e7776b47
--- /dev/null
+++ b/sbin/ipf/ipf/ipf.8
@@ -0,0 +1,184 @@
+.\" $FreeBSD$
+.TH IPF 8
+.SH NAME
+ipf \- alters packet filtering lists for IP packet input and output
+.SH SYNOPSIS
+.B ipf
+[
+.B \-6AcdDEInoPrsvVyzZ
+] [
+.B \-l
+<block|pass|nomatch>
+] [
+.B \-T
+<optionlist>
+] [
+.B \-F
+<i|o|a|s|S>
+]
+.B \-f
+<\fIfilename\fP>
+[
+.B \-f
+<\fIfilename\fP>
+[...]]
+.SH DESCRIPTION
+.PP
+\fBipf\fP opens the filenames listed (treating "\-" as stdin) and parses the
+file for a set of rules which are to be added or removed from the packet
+filter rule set.
+.PP
+Each rule processed by \fBipf\fP
+is added to the kernel's internal lists if there are no parsing problems.
+Rules are added to the end of the internal lists, matching the order in
+which they appear when given to \fBipf\fP.
+.SH OPTIONS
+.TP
+.B \-6
+IPv4 and IPv6 rules are stored in a single table and can be read from a
+single file. This option is no longer required to load IPv6 rules. This
+option is ignored when specified with the -F option and the -F option
+will flush IPv4 rules even if this option is specified.
+.TP
+.B \-A
+Set the list to make changes to the active list (default).
+.TP
+.B \-c <language>
+This option causes \fBipf\fP to generate output files for a compiler that
+supports \fBlanguage\fI. At present, the only target language supported is
+\fBC\fB (-cc) for which two files - \fBip_rules.c\fP
+and \fBip_rules.h\fP are generated in the \fBCURRENT DIRECTORY\fP when
+\fBipf\fP is being run. These files can be used with the
+\fBIPFILTER_COMPILED\fP kernel option to build filter rules staticlly into
+the kernel.
+.TP
+.B \-d
+Turn debug mode on. Causes a hexdump of filter rules to be generated as
+it processes each one.
+.TP
+.B \-D
+Disable the filter (if enabled). Not effective for loadable kernel versions.
+.TP
+.B \-E
+Enable the filter (if disabled). Not effective for loadable kernel versions.
+.TP
+.BR \-F \0<i|o|a>
+This option specifies which filter list to flush. The parameter should
+either be "i" (input), "o" (output) or "a" (remove all filter rules).
+Either a single letter or an entire word starting with the appropriate
+letter maybe used. This option maybe before, or after, any other with
+the order on the command line being that used to execute options.
+.TP
+.BR \-F \0<s|S>
+To flush entries from the state table, the \fB-F\fP option is used in
+conjunction with either "s" (removes state information about any non-fully
+established connections) or "S" (deletes the entire state table). Only
+one of the two options may be given. A fully established connection
+will show up in \fBipfstat -s\fP output as 5/5, with deviations either
+way indicating it is not fully established any more.
+.TP
+.BR \-F <5|6|7|8|9|10|11>
+For the TCP states that represent the closing of a connection has begun,
+be it only one side or the complete connection, it is possible to flush
+those states directly using the number corresponding to that state.
+The numbers relate to the states as follows: 5 = close-wait, 6 = fin-wait-1,
+7 = closing, 8 = last-ack, 9 = fin-wait-2, 10 = time-wait, 11 = closed.
+.TP
+.BR \-F <number>
+If the argument supplied to \fB-F\fP is greater than 30, then state table
+entries that have been idle for more than this many seconds will be flushed.
+.TP
+.BR \-f \0<filename>
+This option specifies which files
+\fBipf\fP should use to get input from for modifying the packet filter rule
+lists.
+.TP
+.B \-I
+Set the list to make changes to the inactive list.
+.TP
+.B \-l \0<pass|block|nomatch>
+Use of the \fB-l\fP flag toggles default logging of packets. Valid
+arguments to this option are \fBpass\fP, \fBblock\fP and \fBnomatch\fP.
+When an option is set, any packet which exits filtering and matches the
+set category is logged. This is most useful for causing all packets
+which don't match any of the loaded rules to be logged.
+.TP
+.B \-n
+This flag (no-change) prevents \fBipf\fP from actually making any ioctl
+calls or doing anything which would alter the currently running kernel.
+.TP
+.B \-o
+Force rules by default to be added/deleted to/from the output list, rather
+than the (default) input list.
+.TP
+.B \-P
+Add rules as temporary entries in the authentication rule table.
+.TP
+.B \-r
+Remove matching filter rules rather than add them to the internal lists
+.TP
+.B \-s
+Swap the active filter list in use to be the "other" one.
+.TP
+.B \-T <optionlist>
+This option allows run-time changing of IPFilter kernel variables. Some
+variables require IPFilter to be in a disabled state (\fB-D\fP) for changing,
+others do not. The optionlist parameter is a comma separated list of tuning
+commands. A tuning command is either "list" (retrieve a list of all variables
+in the kernel, their maximum, minimum and current value), a single variable
+name (retrieve its current value) and a variable name with a following
+assignment to set a new value. Some examples follow.
+.nf
+# Print out all IPFilter kernel tunable parameters
+ipf -T list
+# Display the current TCP idle timeout and then set it to 3600
+ipf -D -T fr_tcpidletimeout,fr_tcpidletimeout=3600 -E
+# Display current values for fr_pass and fr_chksrc, then set fr_chksrc to 1.
+ipf -T fr_pass,fr_chksrc,fr_chksrc=1
+.fi
+.TP
+.B \-v
+Turn verbose mode on. Displays information relating to rule processing.
+.TP
+.B \-V
+Show version information. This will display the version information compiled
+into the ipf binary and retrieve it from the kernel code (if running/present).
+If it is present in the kernel, information about its current state will be
+displayed (whether logging is active, default filtering, etc).
+.TP
+.B \-y
+Manually resync the in-kernel interface list maintained by IP Filter with
+the current interface status list.
+.TP
+.B \-z
+For each rule in the input file, reset the statistics for it to zero and
+display the statistics prior to them being zeroed.
+.TP
+.B \-Z
+Zero global statistics held in the kernel for filtering only (this doesn't
+affect fragment or state statistics).
+.DT
+.SH ENVIRONMENT
+.NM utilizes the following environment variable.
+.TP
+.B IPF_PREDEFINED
+ipfilter variables, see VARIABLES in ipf(5), can be specified in this
+environment variable providing shell access to ipfilter and ipnat variables.
+For example,
+.br
+IPF_PREDEFINED='my_server="10.1.1.1"; my_client="10.1.1.2";'
+.SH FILES
+/dev/ipauth
+.br
+/dev/ipl
+.br
+/dev/ipstate
+.SH SEE ALSO
+ipftest(1), mkfilters(1), ipf(4), ipl(4), ipf(5), ipfstat(8), ipmon(8), ipnat(8)
+.SH DIAGNOSTICS
+.PP
+Needs to be run as root for the packet filtering lists to actually
+be affected inside the kernel.
+.SH BUGS
+.PP
+If you find any, please send email to me at darrenr@pobox.com
diff --git a/sbin/ipf/ipf/ipf.c b/sbin/ipf/ipf/ipf.c
new file mode 100644
index 000000000000..de5121d94767
--- /dev/null
+++ b/sbin/ipf/ipf/ipf.c
@@ -0,0 +1,578 @@
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2012 by Darren Reed.
+ *
+ * See the IPFILTER.LICENCE file for details on licencing.
+ */
+#include "ipf.h"
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include "netinet/ipl.h"
+
+#if !defined(lint)
+static const char sccsid[] = "@(#)ipf.c 1.23 6/5/96 (C) 1993-2000 Darren Reed";
+static const char rcsid[] = "@(#)$Id$";
+#endif
+
+#if !defined(__SVR4) && defined(__GNUC__)
+extern char *index(const char *, int);
+#endif
+
+extern char *optarg;
+extern int optind;
+extern frentry_t *frtop;
+
+
+void ipf_frsync(void);
+void zerostats(void);
+int main(int, char *[]);
+
+int opts = 0;
+int outputc = 0;
+int use_inet6 = 0;
+int exitstatus = 0;
+
+static void procfile(char *);
+static void flushfilter(char *, int *);
+static void set_state(u_int);
+static void showstats(friostat_t *);
+static void packetlogon(char *);
+static void swapactive(void);
+static int opendevice(char *, int);
+static void closedevice(void);
+static char *ipfname = IPL_NAME;
+static void usage(void);
+static int showversion(void);
+static int get_flags(void);
+static int ipf_interceptadd(int, ioctlfunc_t, void *);
+
+static int fd = -1;
+static ioctlfunc_t iocfunctions[IPL_LOGSIZE] = { ioctl, ioctl, ioctl,
+ ioctl, ioctl, ioctl,
+ ioctl, ioctl };
+
+/* XXX The following was added to satisfy a rescue/rescue/ build
+ XXX requirement. */
+int nohdrfields;
+
+static void usage()
+{
+ fprintf(stderr, "usage: ipf [-6AdDEInoPrRsvVyzZ] %s %s %s\n",
+ "[-l block|pass|nomatch|state|nat]", "[-cc] [-F i|o|a|s|S|u]",
+ "[-f filename] [-T <tuneopts>]");
+ exit(1);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ int c, *filter = NULL;
+
+ if (argc < 2)
+ usage();
+
+ assigndefined(getenv("IPF_PREDEFINED"));
+
+ while ((c = getopt(argc, argv, "46Ac:dDEf:F:Il:m:noPrRsT:vVyzZ")) != -1) {
+ switch (c)
+ {
+ case '?' :
+ usage();
+ break;
+ case '4' :
+ use_inet6 = -1;
+ break;
+ case '6' :
+ use_inet6 = 1;
+ break;
+ case 'A' :
+ opts &= ~OPT_INACTIVE;
+ break;
+ case 'c' :
+ if (strcmp(optarg, "c") == 0)
+ outputc = 1;
+ break;
+ case 'E' :
+ set_state((u_int)1);
+ break;
+ case 'D' :
+ set_state((u_int)0);
+ break;
+ case 'd' :
+ opts ^= OPT_DEBUG;
+ break;
+ case 'f' :
+ procfile(optarg);
+ break;
+ case 'F' :
+ flushfilter(optarg, filter);
+ break;
+ case 'I' :
+ opts ^= OPT_INACTIVE;
+ break;
+ case 'l' :
+ packetlogon(optarg);
+ break;
+ case 'm' :
+ filter = parseipfexpr(optarg, NULL);
+ break;
+ case 'n' :
+ opts ^= OPT_DONOTHING|OPT_DONTOPEN;
+ break;
+ case 'o' :
+ break;
+ case 'P' :
+ ipfname = IPAUTH_NAME;
+ break;
+ case 'R' :
+ opts ^= OPT_NORESOLVE;
+ break;
+ case 'r' :
+ opts ^= OPT_REMOVE;
+ break;
+ case 's' :
+ swapactive();
+ break;
+ case 'T' :
+ if (opendevice(ipfname, 1) >= 0)
+ ipf_dotuning(fd, optarg, ioctl);
+ break;
+ case 'v' :
+ opts += OPT_VERBOSE;
+ break;
+ case 'V' :
+ if (showversion())
+ exit(1);
+ break;
+ case 'y' :
+ ipf_frsync();
+ break;
+ case 'z' :
+ opts ^= OPT_ZERORULEST;
+ break;
+ case 'Z' :
+ zerostats();
+ break;
+ }
+ }
+
+ if (optind < 2)
+ usage();
+
+ if (fd != -1)
+ (void) close(fd);
+
+ return (exitstatus);
+ /* NOTREACHED */
+}
+
+
+static int
+opendevice(char *ipfdev, int check)
+{
+ if (opts & OPT_DONOTHING)
+ return (-2);
+
+ if (check && checkrev(ipfname) == -1) {
+ fprintf(stderr, "User/kernel version check failed\n");
+ return (-2);
+ }
+
+ if (!ipfdev)
+ ipfdev = ipfname;
+
+ if (fd == -1)
+ if ((fd = open(ipfdev, O_RDWR)) == -1)
+ if ((fd = open(ipfdev, O_RDONLY)) == -1)
+ ipferror(fd, "open device");
+ return (fd);
+}
+
+
+static void
+closedevice(void)
+{
+ close(fd);
+ fd = -1;
+}
+
+
+static int
+get_flags(void)
+{
+ int i = 0;
+
+ if ((opendevice(ipfname, 1) != -2) &&
+ (ioctl(fd, SIOCGETFF, &i) == -1)) {
+ ipferror(fd, "SIOCGETFF");
+ return (0);
+ }
+ return (i);
+}
+
+
+static void
+set_state(u_int enable)
+{
+ if (opendevice(ipfname, 0) != -2) {
+ if (ioctl(fd, SIOCFRENB, &enable) == -1) {
+ if (errno == EBUSY) {
+ fprintf(stderr,
+ "IP FIlter: already initialized\n");
+ } else {
+ ipferror(fd, "SIOCFRENB");
+ }
+ }
+ }
+ return;
+}
+
+
+static void
+procfile(char *file)
+{
+ (void) opendevice(ipfname, 1);
+
+ initparse();
+
+ ipf_parsefile(fd, ipf_interceptadd, iocfunctions, file);
+
+ if (outputc) {
+ printC(0);
+ printC(1);
+ emit(-1, -1, NULL, NULL);
+ }
+}
+
+
+static int
+ipf_interceptadd(int fd, ioctlfunc_t ioctlfunc, void *ptr)
+{
+ if (outputc)
+ printc(ptr);
+
+ if (ipf_addrule(fd, ioctlfunc, ptr) != 0)
+ exitstatus = 1;
+ return (0);
+}
+
+
+static void
+packetlogon(char *opt)
+{
+ int flag, xfd, logopt, change = 0;
+
+ flag = get_flags();
+ if (flag != 0) {
+ if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE)
+ printf("log flag is currently %#x\n", flag);
+ }
+
+ flag &= ~(FF_LOGPASS|FF_LOGNOMATCH|FF_LOGBLOCK);
+
+ if (strstr(opt, "pass")) {
+ flag |= FF_LOGPASS;
+ if (opts & OPT_VERBOSE)
+ printf("set log flag: pass\n");
+ change = 1;
+ }
+ if (strstr(opt, "nomatch")) {
+ flag |= FF_LOGNOMATCH;
+ if (opts & OPT_VERBOSE)
+ printf("set log flag: nomatch\n");
+ change = 1;
+ }
+ if (strstr(opt, "block") || strchr(opt, 'd')) {
+ flag |= FF_LOGBLOCK;
+ if (opts & OPT_VERBOSE)
+ printf("set log flag: block\n");
+ change = 1;
+ }
+ if (strstr(opt, "none")) {
+ if (opts & OPT_VERBOSE)
+ printf("disable all log flags\n");
+ change = 1;
+ }
+
+ if (change == 1) {
+ if (opendevice(ipfname, 1) != -2 &&
+ (ioctl(fd, SIOCSETFF, &flag) != 0))
+ ipferror(fd, "ioctl(SIOCSETFF)");
+ }
+
+ if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) {
+ flag = get_flags();
+ printf("log flags are now %#x\n", flag);
+ }
+
+ if (strstr(opt, "state")) {
+ if (opts & OPT_VERBOSE)
+ printf("set state log flag\n");
+ xfd = open(IPSTATE_NAME, O_RDWR);
+ if (xfd >= 0) {
+ logopt = 0;
+ if (ioctl(xfd, SIOCGETLG, &logopt))
+ ipferror(fd, "ioctl(SIOCGETLG)");
+ else {
+ logopt = 1 - logopt;
+ if (ioctl(xfd, SIOCSETLG, &logopt))
+ ipferror(xfd, "ioctl(SIOCSETLG)");
+ }
+ close(xfd);
+ }
+ }
+
+ if (strstr(opt, "nat")) {
+ if (opts & OPT_VERBOSE)
+ printf("set nat log flag\n");
+ xfd = open(IPNAT_NAME, O_RDWR);
+ if (xfd >= 0) {
+ logopt = 0;
+ if (ioctl(xfd, SIOCGETLG, &logopt))
+ ipferror(xfd, "ioctl(SIOCGETLG)");
+ else {
+ logopt = 1 - logopt;
+ if (ioctl(xfd, SIOCSETLG, &logopt))
+ ipferror(xfd, "ioctl(SIOCSETLG)");
+ }
+ close(xfd);
+ }
+ }
+}
+
+
+static void
+flushfilter(char *arg, int *filter)
+{
+ int fl = 0, rem;
+
+ if (!arg || !*arg)
+ return;
+ if (!strcmp(arg, "s") || !strcmp(arg, "S") || ISDIGIT(*arg)) {
+ if (*arg == 'S')
+ fl = 0;
+ else if (*arg == 's')
+ fl = 1;
+ else
+ fl = atoi(arg);
+ rem = fl;
+
+ closedevice();
+ if (opendevice(IPSTATE_NAME, 1) == -2)
+ exit(1);
+
+ if (!(opts & OPT_DONOTHING)) {
+ if (use_inet6) {
+ fprintf(stderr,
+ "IPv6 rules are no longer seperate\n");
+ } else if (filter != NULL) {
+ ipfobj_t obj;
+
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_size = filter[0] * sizeof(int);
+ obj.ipfo_type = IPFOBJ_IPFEXPR;
+ obj.ipfo_ptr = filter;
+ if (ioctl(fd, SIOCMATCHFLUSH, &obj) == -1) {
+ ipferror(fd, "ioctl(SIOCMATCHFLUSH)");
+ fl = -1;
+ } else {
+ fl = obj.ipfo_retval;
+ }
+ } else {
+ if (ioctl(fd, SIOCIPFFL, &fl) == -1) {
+ ipferror(fd, "ioctl(SIOCIPFFL)");
+ exit(1);
+ }
+ }
+ }
+ if ((opts & (OPT_DONOTHING|OPT_DEBUG)) == OPT_DEBUG) {
+ printf("remove flags %s (%d)\n", arg, rem);
+ }
+ if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) {
+ printf("%d state entries removed\n", fl);
+ }
+ closedevice();
+ return;
+ } else if (strchr(arg, 'i') || strchr(arg, 'I'))
+ fl = FR_INQUE;
+ else if (strchr(arg, 'o') || strchr(arg, 'O'))
+ fl = FR_OUTQUE;
+ else if (strchr(arg, 'a') || strchr(arg, 'A'))
+ fl = FR_OUTQUE|FR_INQUE;
+ else {
+ fprintf(stderr, "Incorrect flush argument: %s\n", arg);
+ usage();
+ }
+ if (opts & OPT_INACTIVE)
+ fl |= FR_INACTIVE;
+ rem = fl;
+
+ if (opendevice(ipfname, 1) == -2)
+ exit(1);
+
+ if (!(opts & OPT_DONOTHING)) {
+ if (use_inet6) {
+ if (ioctl(fd, SIOCIPFL6, &fl) == -1) {
+ ipferror(fd, "ioctl(SIOCIPFL6)");
+ exit(1);
+ }
+ } else {
+ if (ioctl(fd, SIOCIPFFL, &fl) == -1) {
+ ipferror(fd, "ioctl(SIOCIPFFL)");
+ exit(1);
+ }
+ }
+ }
+
+ if ((opts & (OPT_DONOTHING|OPT_DEBUG)) == OPT_DEBUG) {
+ printf("remove flags %s%s (%d)\n", (rem & FR_INQUE) ? "I" : "",
+ (rem & FR_OUTQUE) ? "O" : "", rem);
+ }
+ if ((opts & (OPT_DONOTHING|OPT_VERBOSE)) == OPT_VERBOSE) {
+ printf("%d filter rules removed\n", fl);
+ }
+ return;
+}
+
+
+static void
+swapactive(void)
+{
+ int in = 2;
+
+ if (opendevice(ipfname, 1) != -2 && ioctl(fd, SIOCSWAPA, &in) == -1)
+ ipferror(fd, "ioctl(SIOCSWAPA)");
+ else
+ printf("Set %d now inactive\n", in);
+}
+
+
+void
+ipf_frsync(void)
+{
+ int frsyn = 0;
+
+ if (opendevice(ipfname, 1) != -2 && ioctl(fd, SIOCFRSYN, &frsyn) == -1)
+ ipferror(fd, "SIOCFRSYN");
+ else
+ printf("filter sync'd\n");
+}
+
+
+void
+zerostats(void)
+{
+ ipfobj_t obj;
+ friostat_t fio;
+
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_type = IPFOBJ_IPFSTAT;
+ obj.ipfo_size = sizeof(fio);
+ obj.ipfo_ptr = &fio;
+ obj.ipfo_offset = 0;
+
+ if (opendevice(ipfname, 1) != -2) {
+ if (ioctl(fd, SIOCFRZST, &obj) == -1) {
+ ipferror(fd, "ioctl(SIOCFRZST)");
+ exit(-1);
+ }
+ showstats(&fio);
+ }
+
+}
+
+
+/*
+ * read the kernel stats for packets blocked and passed
+ */
+static void
+showstats(friostat_t *fp)
+{
+ printf("bad packets:\t\tin %lu\tout %lu\n",
+ fp->f_st[0].fr_bad, fp->f_st[1].fr_bad);
+ printf(" input packets:\t\tblocked %lu passed %lu nomatch %lu",
+ fp->f_st[0].fr_block, fp->f_st[0].fr_pass,
+ fp->f_st[0].fr_nom);
+ printf(" counted %lu\n", fp->f_st[0].fr_acct);
+ printf("output packets:\t\tblocked %lu passed %lu nomatch %lu",
+ fp->f_st[1].fr_block, fp->f_st[1].fr_pass,
+ fp->f_st[1].fr_nom);
+ printf(" counted %lu\n", fp->f_st[0].fr_acct);
+ printf(" input packets logged:\tblocked %lu passed %lu\n",
+ fp->f_st[0].fr_bpkl, fp->f_st[0].fr_ppkl);
+ printf("output packets logged:\tblocked %lu passed %lu\n",
+ fp->f_st[1].fr_bpkl, fp->f_st[1].fr_ppkl);
+}
+
+
+static int
+showversion(void)
+{
+ struct friostat fio;
+ ipfobj_t ipfo;
+ u_32_t flags;
+ char *s;
+ int vfd;
+
+ bzero((caddr_t)&ipfo, sizeof(ipfo));
+ ipfo.ipfo_rev = IPFILTER_VERSION;
+ ipfo.ipfo_size = sizeof(fio);
+ ipfo.ipfo_ptr = (void *)&fio;
+ ipfo.ipfo_type = IPFOBJ_IPFSTAT;
+
+ printf("ipf: %s (%d)\n", IPL_VERSION, (int)sizeof(frentry_t));
+
+ if ((vfd = open(ipfname, O_RDONLY)) == -1) {
+ perror("open device");
+ return (1);
+ }
+
+ if (ioctl(vfd, SIOCGETFS, &ipfo)) {
+ ipferror(vfd, "ioctl(SIOCGETFS)");
+ close(vfd);
+ return (1);
+ }
+ close(vfd);
+ flags = get_flags();
+
+ printf("Kernel: %-*.*s\n", (int)sizeof(fio.f_version),
+ (int)sizeof(fio.f_version), fio.f_version);
+ printf("Running: %s\n", (fio.f_running > 0) ? "yes" : "no");
+ printf("Log Flags: %#x = ", flags);
+ s = "";
+ if (flags & FF_LOGPASS) {
+ printf("pass");
+ s = ", ";
+ }
+ if (flags & FF_LOGBLOCK) {
+ printf("%sblock", s);
+ s = ", ";
+ }
+ if (flags & FF_LOGNOMATCH) {
+ printf("%snomatch", s);
+ s = ", ";
+ }
+ if (flags & FF_BLOCKNONIP) {
+ printf("%snonip", s);
+ s = ", ";
+ }
+ if (!*s)
+ printf("none set");
+ putchar('\n');
+
+ printf("Default: ");
+ if (FR_ISPASS(fio.f_defpass))
+ s = "pass";
+ else if (FR_ISBLOCK(fio.f_defpass))
+ s = "block";
+ else
+ s = "nomatch -> block";
+ printf("%s all, Logging: %savailable\n", s, fio.f_logging ? "" : "un");
+ printf("Active list: %d\n", fio.f_active);
+ printf("Feature mask: %#x\n", fio.f_features);
+
+ return (0);
+}
diff --git a/sbin/ipf/ipf/ipfcomp.c b/sbin/ipf/ipf/ipfcomp.c
new file mode 100644
index 000000000000..cf01838d7966
--- /dev/null
+++ b/sbin/ipf/ipf/ipfcomp.c
@@ -0,0 +1,1355 @@
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2012 by Darren Reed.
+ *
+ * See the IPFILTER.LICENCE file for details on licencing.
+ */
+#if !defined(lint)
+static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed";
+static const char rcsid[] = "@(#)$Id$";
+#endif
+
+#include "ipf.h"
+
+
+typedef struct {
+ int c;
+ int e;
+ int n;
+ int p;
+ int s;
+} mc_t;
+
+
+static char *portcmp[] = { "*", "==", "!=", "<", ">", "<=", ">=", "**", "***" };
+static int count = 0;
+
+int intcmp(const void *, const void *);
+static void indent(FILE *, int);
+static void printeq(FILE *, char *, int, int, int);
+static void printipeq(FILE *, char *, int, int, int);
+static void addrule(FILE *, frentry_t *);
+static void printhooks(FILE *, int, int, frgroup_t *);
+static void emitheader(frgroup_t *, u_int, u_int);
+static void emitGroup(int, int, void *, frentry_t *, char *,
+ u_int, u_int);
+static void emittail(void);
+static void printCgroup(int, frentry_t *, mc_t *, char *);
+
+#define FRC_IFN 0
+#define FRC_V 1
+#define FRC_P 2
+#define FRC_FL 3
+#define FRC_TOS 4
+#define FRC_TTL 5
+#define FRC_SRC 6
+#define FRC_DST 7
+#define FRC_TCP 8
+#define FRC_SP 9
+#define FRC_DP 10
+#define FRC_OPT 11
+#define FRC_SEC 12
+#define FRC_ATH 13
+#define FRC_ICT 14
+#define FRC_ICC 15
+#define FRC_MAX 16
+
+
+static FILE *cfile = NULL;
+
+/*
+ * This is called once per filter rule being loaded to emit data structures
+ * required.
+ */
+void
+printc(frentry_t *fr)
+{
+ u_long *ulp;
+ char *and;
+ FILE *fp;
+ int i;
+
+ if (fr->fr_family == 6)
+ return;
+ if ((fr->fr_type != FR_T_IPF) && (fr->fr_type != FR_T_NONE))
+ return;
+ if ((fr->fr_type == FR_T_IPF) &&
+ ((fr->fr_datype != FRI_NORMAL) || (fr->fr_satype != FRI_NORMAL)))
+ return;
+
+ if (cfile == NULL)
+ cfile = fopen("ip_rules.c", "w");
+ if (cfile == NULL)
+ return;
+ fp = cfile;
+ if (count == 0) {
+ fprintf(fp, "/*\n");
+ fprintf(fp, "* Copyright (C) 2012 by Darren Reed.\n");
+ fprintf(fp, "*\n");
+ fprintf(fp, "* Redistribution and use in source and binary forms are permitted\n");
+ fprintf(fp, "* provided that this notice is preserved and due credit is given\n");
+ fprintf(fp, "* to the original author and the contributors.\n");
+ fprintf(fp, "*/\n\n");
+
+ fprintf(fp, "#include <sys/param.h>\n");
+ fprintf(fp, "#include <sys/types.h>\n");
+ fprintf(fp, "#include <sys/time.h>\n");
+ fprintf(fp, "#include <sys/socket.h>\n");
+ fprintf(fp, "#if (__FreeBSD_version >= 40000)\n");
+ fprintf(fp, "# if defined(_KERNEL)\n");
+ fprintf(fp, "# include <sys/libkern.h>\n");
+ fprintf(fp, "# else\n");
+ fprintf(fp, "# include <sys/unistd.h>\n");
+ fprintf(fp, "# endif\n");
+ fprintf(fp, "#endif\n");
+ fprintf(fp, "#if (__NetBSD_Version__ >= 399000000)\n");
+ fprintf(fp, "#else\n");
+ fprintf(fp, "# if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__sgi)\n");
+ fprintf(fp, "# include <sys/systm.h>\n");
+ fprintf(fp, "# endif\n");
+ fprintf(fp, "#endif\n");
+ fprintf(fp, "#include <sys/errno.h>\n");
+ fprintf(fp, "#include <sys/param.h>\n");
+ fprintf(fp,
+"#if !defined(__SVR4) && !defined(__svr4__) && !defined(__hpux)\n");
+ fprintf(fp, "# include <sys/mbuf.h>\n");
+ fprintf(fp, "#endif\n");
+ fprintf(fp,
+"#if defined(__FreeBSD__) && (__FreeBSD_version > 220000)\n");
+ fprintf(fp, "# include <sys/sockio.h>\n");
+ fprintf(fp, "#else\n");
+ fprintf(fp, "# include <sys/ioctl.h>\n");
+ fprintf(fp, "#endif /* FreeBSD */\n");
+ fprintf(fp, "#include <net/if.h>\n");
+ fprintf(fp, "#include <netinet/in.h>\n");
+ fprintf(fp, "#include <netinet/in_systm.h>\n");
+ fprintf(fp, "#include <netinet/ip.h>\n");
+ fprintf(fp, "#include <netinet/tcp.h>\n");
+ fprintf(fp, "#include \"netinet/ip_compat.h\"\n");
+ fprintf(fp, "#include \"netinet/ip_fil.h\"\n\n");
+ fprintf(fp, "#include \"netinet/ip_rules.h\"\n\n");
+ fprintf(fp, "#ifndef _KERNEL\n");
+ fprintf(fp, "# include <string.h>\n");
+ fprintf(fp, "#endif /* _KERNEL */\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#ifdef IPFILTER_COMPILED\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "extern ipf_main_softc_t ipfmain;\n");
+ fprintf(fp, "\n");
+ }
+
+ addrule(fp, fr);
+ fr->fr_type |= FR_T_BUILTIN;
+ and = "";
+ fr->fr_ref = 1;
+ i = sizeof(*fr);
+ if (i & -(1 - sizeof(*ulp)))
+ i += sizeof(u_long);
+ for (i /= sizeof(u_long), ulp = (u_long *)fr; i > 0; i--) {
+ fprintf(fp, "%s%#lx", and, *ulp++);
+ and = ", ";
+ }
+ fprintf(fp, "\n};\n");
+ fr->fr_type &= ~FR_T_BUILTIN;
+
+ count++;
+
+ fflush(fp);
+}
+
+
+static frgroup_t *groups = NULL;
+
+
+static void
+addrule(FILE *fp, frentry_t *fr)
+{
+ frentry_t *f, **fpp;
+ frgroup_t *g;
+ u_long *ulp;
+ char *ghead;
+ char *gname;
+ char *and;
+ int i;
+
+ f = (frentry_t *)malloc(sizeof(*f));
+ bcopy((char *)fr, (char *)f, sizeof(*fr));
+ if (fr->fr_ipf) {
+ f->fr_ipf = (fripf_t *)malloc(sizeof(*f->fr_ipf));
+ bcopy((char *)fr->fr_ipf, (char *)f->fr_ipf,
+ sizeof(*fr->fr_ipf));
+ }
+
+ f->fr_next = NULL;
+ gname = FR_NAME(fr, fr_group);
+
+ for (g = groups; g != NULL; g = g->fg_next)
+ if ((strncmp(g->fg_name, gname, FR_GROUPLEN) == 0) &&
+ (g->fg_flags == (f->fr_flags & FR_INOUT)))
+ break;
+
+ if (g == NULL) {
+ g = (frgroup_t *)calloc(1, sizeof(*g));
+ g->fg_next = groups;
+ groups = g;
+ g->fg_head = f;
+ strncpy(g->fg_name, gname, FR_GROUPLEN);
+ g->fg_ref = 0;
+ g->fg_flags = f->fr_flags & FR_INOUT;
+ }
+
+ for (fpp = &g->fg_start; *fpp != NULL; )
+ fpp = &((*fpp)->fr_next);
+ *fpp = f;
+
+ if (fr->fr_dsize > 0) {
+ fprintf(fp, "\
+static u_long ipf%s_rule_data_%s_%u[] = {\n",
+ f->fr_flags & FR_INQUE ? "in" : "out",
+ g->fg_name, g->fg_ref);
+ and = "";
+ i = fr->fr_dsize;
+ ulp = fr->fr_data;
+ for (i /= sizeof(u_long); i > 0; i--) {
+ fprintf(fp, "%s%#lx", and, *ulp++);
+ and = ", ";
+ }
+ fprintf(fp, "\n};\n");
+ }
+
+ fprintf(fp, "\nstatic u_long %s_rule_%s_%d[] = {\n",
+ f->fr_flags & FR_INQUE ? "in" : "out", g->fg_name, g->fg_ref);
+
+ g->fg_ref++;
+
+ if (f->fr_grhead != -1) {
+ ghead = FR_NAME(f, fr_grhead);
+ for (g = groups; g != NULL; g = g->fg_next)
+ if ((strncmp(g->fg_name, ghead, FR_GROUPLEN) == 0) &&
+ g->fg_flags == (f->fr_flags & FR_INOUT))
+ break;
+ if (g == NULL) {
+ g = (frgroup_t *)calloc(1, sizeof(*g));
+ g->fg_next = groups;
+ groups = g;
+ g->fg_head = f;
+ strncpy(g->fg_name, ghead, FR_GROUPLEN);
+ g->fg_ref = 0;
+ g->fg_flags = f->fr_flags & FR_INOUT;
+ }
+ }
+}
+
+
+int
+intcmp(const void *c1, const void *c2)
+{
+ const mc_t *i1 = (const mc_t *)c1, *i2 = (const mc_t *)c2;
+
+ if (i1->n == i2->n) {
+ return (i1->c - i2->c);
+ }
+ return (i2->n - i1->n);
+}
+
+
+static void
+indent(FILE *fp, int in)
+{
+ for (; in; in--)
+ fputc('\t', fp);
+}
+
+static void
+printeq(FILE *fp, char *var, int m, int max, int v)
+{
+ if (m == max)
+ fprintf(fp, "%s == %#x) {\n", var, v);
+ else
+ fprintf(fp, "(%s & %#x) == %#x) {\n", var, m, v);
+}
+
+/*
+ * Parameters: var - IP# being compared
+ * fl - 0 for positive match, 1 for negative match
+ * m - netmask
+ * v - required address
+ */
+static void
+printipeq(FILE *fp, char *var, int fl, int m, int v)
+{
+ if (m == 0xffffffff)
+ fprintf(fp, "%s ", var);
+ else
+ fprintf(fp, "(%s & %#x) ", var, m);
+ fprintf(fp, "%c", fl ? '!' : '=');
+ fprintf(fp, "= %#x) {\n", v);
+}
+
+
+void
+emit(int num, int dir, void *v, frentry_t *fr)
+{
+ u_int incnt, outcnt;
+ frgroup_t *g;
+ frentry_t *f;
+
+ for (g = groups; g != NULL; g = g->fg_next) {
+ if (dir == 0 || dir == -1) {
+ if ((g->fg_flags & FR_INQUE) == 0)
+ continue;
+ for (incnt = 0, f = g->fg_start; f != NULL;
+ f = f->fr_next)
+ incnt++;
+ emitGroup(num, dir, v, fr, g->fg_name, incnt, 0);
+ }
+ if (dir == 1 || dir == -1) {
+ if ((g->fg_flags & FR_OUTQUE) == 0)
+ continue;
+ for (outcnt = 0, f = g->fg_start; f != NULL;
+ f = f->fr_next)
+ outcnt++;
+ emitGroup(num, dir, v, fr, g->fg_name, 0, outcnt);
+ }
+ }
+
+ if (num == -1 && dir == -1) {
+ for (g = groups; g != NULL; g = g->fg_next) {
+ if ((g->fg_flags & FR_INQUE) != 0) {
+ for (incnt = 0, f = g->fg_start; f != NULL;
+ f = f->fr_next)
+ incnt++;
+ if (incnt > 0)
+ emitheader(g, incnt, 0);
+ }
+ if ((g->fg_flags & FR_OUTQUE) != 0) {
+ for (outcnt = 0, f = g->fg_start; f != NULL;
+ f = f->fr_next)
+ outcnt++;
+ if (outcnt > 0)
+ emitheader(g, 0, outcnt);
+ }
+ }
+ emittail();
+ fprintf(cfile, "#endif /* IPFILTER_COMPILED */\n");
+ }
+
+}
+
+
+static void
+emitheader(frgroup_t *grp, u_int incount, u_int outcount)
+{
+ static FILE *fph = NULL;
+ frgroup_t *g;
+
+ if (fph == NULL) {
+ fph = fopen("ip_rules.h", "w");
+ if (fph == NULL)
+ return;
+
+ fprintf(fph, "extern int ipfrule_add(void));\n");
+ fprintf(fph, "extern int ipfrule_remove(void));\n");
+ }
+
+ printhooks(cfile, incount, outcount, grp);
+
+ if (incount) {
+ fprintf(fph, "\n\
+extern frentry_t *ipfrule_match_in_%s(fr_info_t *, u_32_t *));\n\
+extern frentry_t *ipf_rules_in_%s[%d];\n",
+ grp->fg_name, grp->fg_name, incount);
+
+ for (g = groups; g != grp; g = g->fg_next)
+ if ((strncmp(g->fg_name, grp->fg_name,
+ FR_GROUPLEN) == 0) &&
+ g->fg_flags == grp->fg_flags)
+ break;
+ if (g == grp) {
+ fprintf(fph, "\n\
+extern int ipfrule_add_in_%s(void));\n\
+extern int ipfrule_remove_in_%s(void));\n", grp->fg_name, grp->fg_name);
+ }
+ }
+ if (outcount) {
+ fprintf(fph, "\n\
+extern frentry_t *ipfrule_match_out_%s(fr_info_t *, u_32_t *));\n\
+extern frentry_t *ipf_rules_out_%s[%d];\n",
+ grp->fg_name, grp->fg_name, outcount);
+
+ for (g = groups; g != grp; g = g->fg_next)
+ if ((strncmp(g->fg_name, grp->fg_name,
+ FR_GROUPLEN) == 0) &&
+ g->fg_flags == grp->fg_flags)
+ break;
+ if (g == grp) {
+ fprintf(fph, "\n\
+extern int ipfrule_add_out_%s(void));\n\
+extern int ipfrule_remove_out_%s(void));\n",
+ grp->fg_name, grp->fg_name);
+ }
+ }
+}
+
+static void
+emittail(void)
+{
+ frgroup_t *g;
+
+ fprintf(cfile, "\n\
+int ipfrule_add()\n\
+{\n\
+ int err;\n\
+\n");
+ for (g = groups; g != NULL; g = g->fg_next)
+ fprintf(cfile, "\
+ err = ipfrule_add_%s_%s();\n\
+ if (err != 0)\n\
+ return (err);\n",
+ (g->fg_flags & FR_INQUE) ? "in" : "out", g->fg_name);
+ fprintf(cfile, "\
+ return (0);\n");
+ fprintf(cfile, "}\n\
+\n");
+
+ fprintf(cfile, "\n\
+int ipfrule_remove()\n\
+{\n\
+ int err;\n\
+\n");
+ for (g = groups; g != NULL; g = g->fg_next)
+ fprintf(cfile, "\
+ err = ipfrule_remove_%s_%s();\n\
+ if (err != 0)\n\
+ return (err);\n",
+ (g->fg_flags & FR_INQUE) ? "in" : "out", g->fg_name);
+ fprintf(cfile, "\
+ return (0);\n");
+ fprintf(cfile, "}\n");
+}
+
+
+static void
+emitGroup(int num, int dir, void *v, frentry_t *fr, char *group,
+ u_int incount, u_int outcount)
+{
+ static FILE *fp = NULL;
+ static int header[2] = { 0, 0 };
+ static char egroup[FR_GROUPLEN] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ static int openfunc = 0;
+ static mc_t *n = NULL;
+ static int sin = 0;
+ frentry_t *f;
+ frgroup_t *g;
+ fripf_t *ipf;
+ int i, in, j;
+ mc_t *m = v;
+
+ if (fp == NULL)
+ fp = cfile;
+ if (fp == NULL)
+ return;
+ if (strncmp(egroup, group, FR_GROUPLEN)) {
+ for (sin--; sin > 0; sin--) {
+ indent(fp, sin);
+ fprintf(fp, "}\n");
+ }
+ if (openfunc == 1) {
+ fprintf(fp, "\treturn (fr);\n}\n");
+ openfunc = 0;
+ if (n != NULL) {
+ free(n);
+ n = NULL;
+ }
+ }
+ sin = 0;
+ header[0] = 0;
+ header[1] = 0;
+ strncpy(egroup, group, FR_GROUPLEN);
+ } else if (openfunc == 1 && num < 0) {
+ if (n != NULL) {
+ free(n);
+ n = NULL;
+ }
+ for (sin--; sin > 0; sin--) {
+ indent(fp, sin);
+ fprintf(fp, "}\n");
+ }
+ if (openfunc == 1) {
+ fprintf(fp, "\treturn (fr);\n}\n");
+ openfunc = 0;
+ }
+ }
+
+ if (dir == -1)
+ return;
+
+ for (g = groups; g != NULL; g = g->fg_next) {
+ if (dir == 0 && (g->fg_flags & FR_INQUE) == 0)
+ continue;
+ else if (dir == 1 && (g->fg_flags & FR_OUTQUE) == 0)
+ continue;
+ if (strncmp(g->fg_name, group, FR_GROUPLEN) != 0)
+ continue;
+ break;
+ }
+
+ /*
+ * Output the array of pointers to rules for this group.
+ */
+ if (g != NULL && num == -2 && dir == 0 && header[0] == 0 &&
+ incount != 0) {
+ fprintf(fp, "\nfrentry_t *ipf_rules_in_%s[%d] = {",
+ group, incount);
+ for (f = g->fg_start, i = 0; f != NULL; f = f->fr_next) {
+ if ((f->fr_flags & FR_INQUE) == 0)
+ continue;
+ if ((i & 1) == 0) {
+ fprintf(fp, "\n\t");
+ }
+ fprintf(fp, "(frentry_t *)&in_rule_%s_%d",
+ FR_NAME(f, fr_group), i);
+ if (i + 1 < incount)
+ fprintf(fp, ", ");
+ i++;
+ }
+ fprintf(fp, "\n};\n");
+ }
+
+ if (g != NULL && num == -2 && dir == 1 && header[0] == 0 &&
+ outcount != 0) {
+ fprintf(fp, "\nfrentry_t *ipf_rules_out_%s[%d] = {",
+ group, outcount);
+ for (f = g->fg_start, i = 0; f != NULL; f = f->fr_next) {
+ if ((f->fr_flags & FR_OUTQUE) == 0)
+ continue;
+ if ((i & 1) == 0) {
+ fprintf(fp, "\n\t");
+ }
+ fprintf(fp, "(frentry_t *)&out_rule_%s_%d",
+ FR_NAME(f, fr_group), i);
+ if (i + 1 < outcount)
+ fprintf(fp, ", ");
+ i++;
+ }
+ fprintf(fp, "\n};\n");
+ fp = NULL;
+ }
+
+ if (num < 0)
+ return;
+
+ in = 0;
+ ipf = fr->fr_ipf;
+
+ /*
+ * If the function header has not been printed then print it now.
+ */
+ if (g != NULL && header[dir] == 0) {
+ int pdst = 0, psrc = 0;
+
+ openfunc = 1;
+ fprintf(fp, "\nfrentry_t *ipfrule_match_%s_%s(fin, passp)\n",
+ (dir == 0) ? "in" : "out", group);
+ fprintf(fp, "fr_info_t *fin;\n");
+ fprintf(fp, "u_32_t *passp;\n");
+ fprintf(fp, "{\n");
+ fprintf(fp, "\tfrentry_t *fr = NULL;\n");
+
+ /*
+ * Print out any variables that need to be declared.
+ */
+ for (f = g->fg_start, i = 0; f != NULL; f = f->fr_next) {
+ if (incount + outcount > m[FRC_SRC].e + 1)
+ psrc = 1;
+ if (incount + outcount > m[FRC_DST].e + 1)
+ pdst = 1;
+ }
+ if (psrc == 1)
+ fprintf(fp, "\tu_32_t src = ntohl(%s);\n",
+ "fin->fin_fi.fi_saddr");
+ if (pdst == 1)
+ fprintf(fp, "\tu_32_t dst = ntohl(%s);\n",
+ "fin->fin_fi.fi_daddr");
+ }
+
+ for (i = 0; i < FRC_MAX; i++) {
+ switch(m[i].c)
+ {
+ case FRC_IFN :
+ if (fr->fr_ifnames[0] != -1)
+ m[i].s = 1;
+ break;
+ case FRC_V :
+ if (ipf != NULL && ipf->fri_mip.fi_v != 0)
+ m[i].s = 1;
+ break;
+ case FRC_FL :
+ if (ipf != NULL && ipf->fri_mip.fi_flx != 0)
+ m[i].s = 1;
+ break;
+ case FRC_P :
+ if (ipf != NULL && ipf->fri_mip.fi_p != 0)
+ m[i].s = 1;
+ break;
+ case FRC_TTL :
+ if (ipf != NULL && ipf->fri_mip.fi_ttl != 0)
+ m[i].s = 1;
+ break;
+ case FRC_TOS :
+ if (ipf != NULL && ipf->fri_mip.fi_tos != 0)
+ m[i].s = 1;
+ break;
+ case FRC_TCP :
+ if (ipf == NULL)
+ break;
+ if ((ipf->fri_ip.fi_p == IPPROTO_TCP) &&
+ fr->fr_tcpfm != 0)
+ m[i].s = 1;
+ break;
+ case FRC_SP :
+ if (ipf == NULL)
+ break;
+ if (fr->fr_scmp == FR_INRANGE)
+ m[i].s = 1;
+ else if (fr->fr_scmp == FR_OUTRANGE)
+ m[i].s = 1;
+ else if (fr->fr_scmp != 0)
+ m[i].s = 1;
+ break;
+ case FRC_DP :
+ if (ipf == NULL)
+ break;
+ if (fr->fr_dcmp == FR_INRANGE)
+ m[i].s = 1;
+ else if (fr->fr_dcmp == FR_OUTRANGE)
+ m[i].s = 1;
+ else if (fr->fr_dcmp != 0)
+ m[i].s = 1;
+ break;
+ case FRC_SRC :
+ if (ipf == NULL)
+ break;
+ if (fr->fr_satype == FRI_LOOKUP) {
+ ;
+ } else if ((fr->fr_smask != 0) ||
+ (fr->fr_flags & FR_NOTSRCIP) != 0)
+ m[i].s = 1;
+ break;
+ case FRC_DST :
+ if (ipf == NULL)
+ break;
+ if (fr->fr_datype == FRI_LOOKUP) {
+ ;
+ } else if ((fr->fr_dmask != 0) ||
+ (fr->fr_flags & FR_NOTDSTIP) != 0)
+ m[i].s = 1;
+ break;
+ case FRC_OPT :
+ if (ipf == NULL)
+ break;
+ if (fr->fr_optmask != 0)
+ m[i].s = 1;
+ break;
+ case FRC_SEC :
+ if (ipf == NULL)
+ break;
+ if (fr->fr_secmask != 0)
+ m[i].s = 1;
+ break;
+ case FRC_ATH :
+ if (ipf == NULL)
+ break;
+ if (fr->fr_authmask != 0)
+ m[i].s = 1;
+ break;
+ case FRC_ICT :
+ if (ipf == NULL)
+ break;
+ if ((fr->fr_icmpm & 0xff00) != 0)
+ m[i].s = 1;
+ break;
+ case FRC_ICC :
+ if (ipf == NULL)
+ break;
+ if ((fr->fr_icmpm & 0xff) != 0)
+ m[i].s = 1;
+ break;
+ }
+ }
+
+ if (!header[dir]) {
+ fprintf(fp, "\n");
+ header[dir] = 1;
+ sin = 0;
+ }
+
+ qsort(m, FRC_MAX, sizeof(mc_t), intcmp);
+
+ if (n) {
+ /*
+ * Calculate the indentation interval upto the last common
+ * common comparison being made.
+ */
+ for (i = 0, in = 1; i < FRC_MAX; i++) {
+ if (n[i].c != m[i].c)
+ break;
+ if (n[i].s != m[i].s)
+ break;
+ if (n[i].s) {
+ if (n[i].n && (n[i].n > n[i].e)) {
+ m[i].p++;
+ in += m[i].p;
+ break;
+ }
+ if (n[i].e > 0) {
+ in++;
+ } else
+ break;
+ }
+ }
+ if (sin != in) {
+ for (j = sin - 1; j >= in; j--) {
+ indent(fp, j);
+ fprintf(fp, "}\n");
+ }
+ }
+ } else {
+ in = 1;
+ i = 0;
+ }
+
+ /*
+ * print out C code that implements a filter rule.
+ */
+ for (; i < FRC_MAX; i++) {
+ switch(m[i].c)
+ {
+ case FRC_IFN :
+ if (m[i].s) {
+ indent(fp, in);
+ fprintf(fp, "if (fin->fin_ifp == ");
+ fprintf(fp, "ipf_rules_%s_%s[%d]->fr_ifa) {\n",
+ dir ? "out" : "in", group, num);
+ in++;
+ }
+ break;
+ case FRC_V :
+ if (m[i].s) {
+ indent(fp, in);
+ fprintf(fp, "if (fin->fin_v == %d) {\n",
+ ipf->fri_ip.fi_v);
+ in++;
+ }
+ break;
+ case FRC_FL :
+ if (m[i].s) {
+ indent(fp, in);
+ fprintf(fp, "if (");
+ printeq(fp, "fin->fin_flx",
+ ipf->fri_mip.fi_flx, 0xf,
+ ipf->fri_ip.fi_flx);
+ in++;
+ }
+ break;
+ case FRC_P :
+ if (m[i].s) {
+ indent(fp, in);
+ fprintf(fp, "if (fin->fin_p == %d) {\n",
+ ipf->fri_ip.fi_p);
+ in++;
+ }
+ break;
+ case FRC_TTL :
+ if (m[i].s) {
+ indent(fp, in);
+ fprintf(fp, "if (");
+ printeq(fp, "fin->fin_ttl",
+ ipf->fri_mip.fi_ttl, 0xff,
+ ipf->fri_ip.fi_ttl);
+ in++;
+ }
+ break;
+ case FRC_TOS :
+ if (m[i].s) {
+ indent(fp, in);
+ fprintf(fp, "if (fin->fin_tos");
+ printeq(fp, "fin->fin_tos",
+ ipf->fri_mip.fi_tos, 0xff,
+ ipf->fri_ip.fi_tos);
+ in++;
+ }
+ break;
+ case FRC_TCP :
+ if (m[i].s) {
+ indent(fp, in);
+ fprintf(fp, "if (");
+ printeq(fp, "fin->fin_tcpf", fr->fr_tcpfm,
+ 0xff, fr->fr_tcpf);
+ in++;
+ }
+ break;
+ case FRC_SP :
+ if (!m[i].s)
+ break;
+ if (fr->fr_scmp == FR_INRANGE) {
+ indent(fp, in);
+ fprintf(fp, "if ((fin->fin_data[0] > %d) && ",
+ fr->fr_sport);
+ fprintf(fp, "(fin->fin_data[0] < %d)",
+ fr->fr_stop);
+ fprintf(fp, ") {\n");
+ in++;
+ } else if (fr->fr_scmp == FR_OUTRANGE) {
+ indent(fp, in);
+ fprintf(fp, "if ((fin->fin_data[0] < %d) || ",
+ fr->fr_sport);
+ fprintf(fp, "(fin->fin_data[0] > %d)",
+ fr->fr_stop);
+ fprintf(fp, ") {\n");
+ in++;
+ } else if (fr->fr_scmp) {
+ indent(fp, in);
+ fprintf(fp, "if (fin->fin_data[0] %s %d)",
+ portcmp[fr->fr_scmp], fr->fr_sport);
+ fprintf(fp, " {\n");
+ in++;
+ }
+ break;
+ case FRC_DP :
+ if (!m[i].s)
+ break;
+ if (fr->fr_dcmp == FR_INRANGE) {
+ indent(fp, in);
+ fprintf(fp, "if ((fin->fin_data[1] > %d) && ",
+ fr->fr_dport);
+ fprintf(fp, "(fin->fin_data[1] < %d)",
+ fr->fr_dtop);
+ fprintf(fp, ") {\n");
+ in++;
+ } else if (fr->fr_dcmp == FR_OUTRANGE) {
+ indent(fp, in);
+ fprintf(fp, "if ((fin->fin_data[1] < %d) || ",
+ fr->fr_dport);
+ fprintf(fp, "(fin->fin_data[1] > %d)",
+ fr->fr_dtop);
+ fprintf(fp, ") {\n");
+ in++;
+ } else if (fr->fr_dcmp) {
+ indent(fp, in);
+ fprintf(fp, "if (fin->fin_data[1] %s %d)",
+ portcmp[fr->fr_dcmp], fr->fr_dport);
+ fprintf(fp, " {\n");
+ in++;
+ }
+ break;
+ case FRC_SRC :
+ if (!m[i].s)
+ break;
+ if (fr->fr_satype == FRI_LOOKUP) {
+ ;
+ } else if ((fr->fr_smask != 0) ||
+ (fr->fr_flags & FR_NOTSRCIP) != 0) {
+ indent(fp, in);
+ fprintf(fp, "if (");
+ printipeq(fp, "src",
+ fr->fr_flags & FR_NOTSRCIP,
+ fr->fr_smask, fr->fr_saddr);
+ in++;
+ }
+ break;
+ case FRC_DST :
+ if (!m[i].s)
+ break;
+ if (fr->fr_datype == FRI_LOOKUP) {
+ ;
+ } else if ((fr->fr_dmask != 0) ||
+ (fr->fr_flags & FR_NOTDSTIP) != 0) {
+ indent(fp, in);
+ fprintf(fp, "if (");
+ printipeq(fp, "dst",
+ fr->fr_flags & FR_NOTDSTIP,
+ fr->fr_dmask, fr->fr_daddr);
+ in++;
+ }
+ break;
+ case FRC_OPT :
+ if (m[i].s) {
+ indent(fp, in);
+ fprintf(fp, "if (");
+ printeq(fp, "fin->fin_fi.fi_optmsk",
+ fr->fr_optmask, 0xffffffff,
+ fr->fr_optbits);
+ in++;
+ }
+ break;
+ case FRC_SEC :
+ if (m[i].s) {
+ indent(fp, in);
+ fprintf(fp, "if (");
+ printeq(fp, "fin->fin_fi.fi_secmsk",
+ fr->fr_secmask, 0xffff,
+ fr->fr_secbits);
+ in++;
+ }
+ break;
+ case FRC_ATH :
+ if (m[i].s) {
+ indent(fp, in);
+ fprintf(fp, "if (");
+ printeq(fp, "fin->fin_fi.fi_authmsk",
+ fr->fr_authmask, 0xffff,
+ fr->fr_authbits);
+ in++;
+ }
+ break;
+ case FRC_ICT :
+ if (m[i].s) {
+ indent(fp, in);
+ fprintf(fp, "if (");
+ printeq(fp, "fin->fin_data[0]",
+ fr->fr_icmpm & 0xff00, 0xffff,
+ fr->fr_icmp & 0xff00);
+ in++;
+ }
+ break;
+ case FRC_ICC :
+ if (m[i].s) {
+ indent(fp, in);
+ fprintf(fp, "if (");
+ printeq(fp, "fin->fin_data[0]",
+ fr->fr_icmpm & 0xff, 0xffff,
+ fr->fr_icmp & 0xff);
+ in++;
+ }
+ break;
+ }
+
+ }
+
+ indent(fp, in);
+ if (fr->fr_flags & FR_QUICK) {
+ fprintf(fp, "return ((frentry_t *)&%s_rule_%s_%d);\n",
+ fr->fr_flags & FR_INQUE ? "in" : "out",
+ FR_NAME(fr, fr_group), num);
+ } else {
+ fprintf(fp, "fr = (frentry_t *)&%s_rule_%s_%d;\n",
+ fr->fr_flags & FR_INQUE ? "in" : "out",
+ FR_NAME(fr, fr_group), num);
+ }
+ if (n == NULL)
+ n = (mc_t *)malloc(sizeof(*n) * FRC_MAX);
+ bcopy((char *)m, (char *)n, sizeof(*n) * FRC_MAX);
+ sin = in;
+}
+
+
+void
+printC(int dir)
+{
+ static mc_t *m = NULL;
+ frgroup_t *g;
+
+ if (m == NULL)
+ m = (mc_t *)calloc(FRC_MAX, sizeof(*m));
+
+ for (g = groups; g != NULL; g = g->fg_next) {
+ if ((dir == 0) && ((g->fg_flags & FR_INQUE) != 0))
+ printCgroup(dir, g->fg_start, m, g->fg_name);
+ if ((dir == 1) && ((g->fg_flags & FR_OUTQUE) != 0))
+ printCgroup(dir, g->fg_start, m, g->fg_name);
+ }
+
+ emit(-1, dir, m, NULL);
+}
+
+
+/*
+ * Now print out code to implement all of the rules.
+ */
+static void
+printCgroup(int dir, frentry_t *top, mc_t *m, char *group)
+{
+ frentry_t *fr, *fr1;
+ int i, n, rn;
+ u_int count;
+
+ for (count = 0, fr1 = top; fr1 != NULL; fr1 = fr1->fr_next) {
+ if ((dir == 0) && ((fr1->fr_flags & FR_INQUE) != 0))
+ count++;
+ else if ((dir == 1) && ((fr1->fr_flags & FR_OUTQUE) != 0))
+ count++;
+ }
+
+ if (dir == 0)
+ emitGroup(-2, dir, m, fr1, group, count, 0);
+ else if (dir == 1)
+ emitGroup(-2, dir, m, fr1, group, 0, count);
+
+ /*
+ * Before printing each rule, check to see how many of its fields are
+ * matched by subsequent rules.
+ */
+ for (fr1 = top, rn = 0; fr1 != NULL; fr1 = fr1->fr_next, rn++) {
+ if (!dir && !(fr1->fr_flags & FR_INQUE))
+ continue;
+ if (dir && !(fr1->fr_flags & FR_OUTQUE))
+ continue;
+ n = 0xfffffff;
+
+ for (i = 0; i < FRC_MAX; i++)
+ m[i].e = 0;
+ qsort(m, FRC_MAX, sizeof(mc_t), intcmp);
+
+ for (i = 0; i < FRC_MAX; i++) {
+ m[i].c = i;
+ m[i].e = 0;
+ m[i].n = 0;
+ m[i].s = 0;
+ }
+
+ for (fr = fr1->fr_next; fr; fr = fr->fr_next) {
+ if (!dir && !(fr->fr_flags & FR_INQUE))
+ continue;
+ if (dir && !(fr->fr_flags & FR_OUTQUE))
+ continue;
+
+ if ((n & 0x0001) &&
+ !strcmp(fr1->fr_names + fr1->fr_ifnames[0],
+ fr->fr_names + fr->fr_ifnames[0])) {
+ m[FRC_IFN].e++;
+ m[FRC_IFN].n++;
+ } else
+ n &= ~0x0001;
+
+ if ((n & 0x0002) && (fr1->fr_family == fr->fr_family)) {
+ m[FRC_V].e++;
+ m[FRC_V].n++;
+ } else
+ n &= ~0x0002;
+
+ if ((n & 0x0004) &&
+ (fr->fr_type == fr1->fr_type) &&
+ (fr->fr_type == FR_T_IPF) &&
+ (fr1->fr_mip.fi_flx == fr->fr_mip.fi_flx) &&
+ (fr1->fr_ip.fi_flx == fr->fr_ip.fi_flx)) {
+ m[FRC_FL].e++;
+ m[FRC_FL].n++;
+ } else
+ n &= ~0x0004;
+
+ if ((n & 0x0008) &&
+ (fr->fr_type == fr1->fr_type) &&
+ (fr->fr_type == FR_T_IPF) &&
+ (fr1->fr_proto == fr->fr_proto)) {
+ m[FRC_P].e++;
+ m[FRC_P].n++;
+ } else
+ n &= ~0x0008;
+
+ if ((n & 0x0010) &&
+ (fr->fr_type == fr1->fr_type) &&
+ (fr->fr_type == FR_T_IPF) &&
+ (fr1->fr_ttl == fr->fr_ttl)) {
+ m[FRC_TTL].e++;
+ m[FRC_TTL].n++;
+ } else
+ n &= ~0x0010;
+
+ if ((n & 0x0020) &&
+ (fr->fr_type == fr1->fr_type) &&
+ (fr->fr_type == FR_T_IPF) &&
+ (fr1->fr_tos == fr->fr_tos)) {
+ m[FRC_TOS].e++;
+ m[FRC_TOS].n++;
+ } else
+ n &= ~0x0020;
+
+ if ((n & 0x0040) &&
+ (fr->fr_type == fr1->fr_type) &&
+ (fr->fr_type == FR_T_IPF) &&
+ ((fr1->fr_tcpfm == fr->fr_tcpfm) &&
+ (fr1->fr_tcpf == fr->fr_tcpf))) {
+ m[FRC_TCP].e++;
+ m[FRC_TCP].n++;
+ } else
+ n &= ~0x0040;
+
+ if ((n & 0x0080) &&
+ (fr->fr_type == fr1->fr_type) &&
+ (fr->fr_type == FR_T_IPF) &&
+ ((fr1->fr_scmp == fr->fr_scmp) &&
+ (fr1->fr_stop == fr->fr_stop) &&
+ (fr1->fr_sport == fr->fr_sport))) {
+ m[FRC_SP].e++;
+ m[FRC_SP].n++;
+ } else
+ n &= ~0x0080;
+
+ if ((n & 0x0100) &&
+ (fr->fr_type == fr1->fr_type) &&
+ (fr->fr_type == FR_T_IPF) &&
+ ((fr1->fr_dcmp == fr->fr_dcmp) &&
+ (fr1->fr_dtop == fr->fr_dtop) &&
+ (fr1->fr_dport == fr->fr_dport))) {
+ m[FRC_DP].e++;
+ m[FRC_DP].n++;
+ } else
+ n &= ~0x0100;
+
+ if ((n & 0x0200) &&
+ (fr->fr_type == fr1->fr_type) &&
+ (fr->fr_type == FR_T_IPF) &&
+ ((fr1->fr_satype == FRI_LOOKUP) &&
+ (fr->fr_satype == FRI_LOOKUP) &&
+ (fr1->fr_srcnum == fr->fr_srcnum))) {
+ m[FRC_SRC].e++;
+ m[FRC_SRC].n++;
+ } else if ((n & 0x0200) &&
+ (fr->fr_type == fr1->fr_type) &&
+ (fr->fr_type == FR_T_IPF) &&
+ (((fr1->fr_flags & FR_NOTSRCIP) ==
+ (fr->fr_flags & FR_NOTSRCIP)))) {
+ if ((fr1->fr_smask == fr->fr_smask) &&
+ (fr1->fr_saddr == fr->fr_saddr))
+ m[FRC_SRC].e++;
+ else
+ n &= ~0x0200;
+ if (fr1->fr_smask &&
+ (fr1->fr_saddr & fr1->fr_smask) ==
+ (fr->fr_saddr & fr1->fr_smask)) {
+ m[FRC_SRC].n++;
+ n |= 0x0200;
+ }
+ } else {
+ n &= ~0x0200;
+ }
+
+ if ((n & 0x0400) &&
+ (fr->fr_type == fr1->fr_type) &&
+ (fr->fr_type == FR_T_IPF) &&
+ ((fr1->fr_datype == FRI_LOOKUP) &&
+ (fr->fr_datype == FRI_LOOKUP) &&
+ (fr1->fr_dstnum == fr->fr_dstnum))) {
+ m[FRC_DST].e++;
+ m[FRC_DST].n++;
+ } else if ((n & 0x0400) &&
+ (fr->fr_type == fr1->fr_type) &&
+ (fr->fr_type == FR_T_IPF) &&
+ (((fr1->fr_flags & FR_NOTDSTIP) ==
+ (fr->fr_flags & FR_NOTDSTIP)))) {
+ if ((fr1->fr_dmask == fr->fr_dmask) &&
+ (fr1->fr_daddr == fr->fr_daddr))
+ m[FRC_DST].e++;
+ else
+ n &= ~0x0400;
+ if (fr1->fr_dmask &&
+ (fr1->fr_daddr & fr1->fr_dmask) ==
+ (fr->fr_daddr & fr1->fr_dmask)) {
+ m[FRC_DST].n++;
+ n |= 0x0400;
+ }
+ } else {
+ n &= ~0x0400;
+ }
+
+ if ((n & 0x0800) &&
+ (fr->fr_type == fr1->fr_type) &&
+ (fr->fr_type == FR_T_IPF) &&
+ (fr1->fr_optmask == fr->fr_optmask) &&
+ (fr1->fr_optbits == fr->fr_optbits)) {
+ m[FRC_OPT].e++;
+ m[FRC_OPT].n++;
+ } else
+ n &= ~0x0800;
+
+ if ((n & 0x1000) &&
+ (fr->fr_type == fr1->fr_type) &&
+ (fr->fr_type == FR_T_IPF) &&
+ (fr1->fr_secmask == fr->fr_secmask) &&
+ (fr1->fr_secbits == fr->fr_secbits)) {
+ m[FRC_SEC].e++;
+ m[FRC_SEC].n++;
+ } else
+ n &= ~0x1000;
+
+ if ((n & 0x10000) &&
+ (fr->fr_type == fr1->fr_type) &&
+ (fr->fr_type == FR_T_IPF) &&
+ (fr1->fr_authmask == fr->fr_authmask) &&
+ (fr1->fr_authbits == fr->fr_authbits)) {
+ m[FRC_ATH].e++;
+ m[FRC_ATH].n++;
+ } else
+ n &= ~0x10000;
+
+ if ((n & 0x20000) &&
+ (fr->fr_type == fr1->fr_type) &&
+ (fr->fr_type == FR_T_IPF) &&
+ ((fr1->fr_icmpm & 0xff00) ==
+ (fr->fr_icmpm & 0xff00)) &&
+ ((fr1->fr_icmp & 0xff00) ==
+ (fr->fr_icmp & 0xff00))) {
+ m[FRC_ICT].e++;
+ m[FRC_ICT].n++;
+ } else
+ n &= ~0x20000;
+
+ if ((n & 0x40000) &&
+ (fr->fr_type == fr1->fr_type) &&
+ (fr->fr_type == FR_T_IPF) &&
+ ((fr1->fr_icmpm & 0xff) == (fr->fr_icmpm & 0xff)) &&
+ ((fr1->fr_icmp & 0xff) == (fr->fr_icmp & 0xff))) {
+ m[FRC_ICC].e++;
+ m[FRC_ICC].n++;
+ } else
+ n &= ~0x40000;
+ }
+ /*msort(m);*/
+
+ if (dir == 0)
+ emitGroup(rn, dir, m, fr1, group, count, 0);
+ else if (dir == 1)
+ emitGroup(rn, dir, m, fr1, group, 0, count);
+ }
+}
+
+static void
+printhooks(FILE *fp, int in, int out, frgroup_t *grp)
+{
+ frentry_t *fr;
+ char *group;
+ int dogrp, i;
+ char *instr;
+
+ group = grp->fg_name;
+ dogrp = 0;
+
+ if (in && out) {
+ fprintf(stderr,
+ "printhooks called with both in and out set\n");
+ exit(1);
+ }
+
+ if (in) {
+ instr = "in";
+ } else if (out) {
+ instr = "out";
+ } else {
+ instr = "???";
+ }
+ fprintf(fp, "static frentry_t ipfrule_%s_%s;\n", instr, group);
+
+ fprintf(fp, "\
+\n\
+int ipfrule_add_%s_%s()\n", instr, group);
+ fprintf(fp, "\
+{\n\
+ int i, j, err = 0, max;\n\
+ frentry_t *fp;\n");
+
+ if (dogrp)
+ fprintf(fp, "\
+ frgroup_t *fg;\n");
+
+ fprintf(fp, "\n");
+
+ for (i = 0, fr = grp->fg_start; fr != NULL; i++, fr = fr->fr_next)
+ if (fr->fr_dsize > 0) {
+ fprintf(fp, "\
+ ipf_rules_%s_%s[%d]->fr_data = &ipf%s_rule_data_%s_%u;\n",
+ instr, grp->fg_name, i,
+ instr, grp->fg_name, i);
+ }
+ fprintf(fp, "\
+ max = sizeof(ipf_rules_%s_%s)/sizeof(frentry_t *);\n\
+ for (i = 0; i < max; i++) {\n\
+ fp = ipf_rules_%s_%s[i];\n\
+ fp->fr_next = NULL;\n", instr, group, instr, group);
+
+ fprintf(fp, "\
+ for (j = i + 1; j < max; j++)\n\
+ if (strncmp(fp->fr_names + fp->fr_group,\n\
+ ipf_rules_%s_%s[j]->fr_names +\n\
+ ipf_rules_%s_%s[j]->fr_group,\n\
+ FR_GROUPLEN) == 0) {\n\
+ if (ipf_rules_%s_%s[j] != NULL)\n\
+ ipf_rules_%s_%s[j]->fr_pnext =\n\
+ &fp->fr_next;\n\
+ fp->fr_pnext = &ipf_rules_%s_%s[j];\n\
+ fp->fr_next = ipf_rules_%s_%s[j];\n\
+ break;\n\
+ }\n", instr, group, instr, group, instr, group,
+ instr, group, instr, group, instr, group);
+ if (dogrp)
+ fprintf(fp, "\
+\n\
+ if (fp->fr_grhead != -1) {\n\
+ fg = fr_addgroup(fp->fr_names + fp->fr_grhead,\n\
+ fp, FR_INQUE, IPL_LOGIPF, 0);\n\
+ if (fg != NULL)\n\
+ fp->fr_grp = &fg->fg_start;\n\
+ }\n");
+ fprintf(fp, "\
+ }\n\
+\n\
+ fp = &ipfrule_%s_%s;\n", instr, group);
+ fprintf(fp, "\
+ bzero((char *)fp, sizeof(*fp));\n\
+ fp->fr_type = FR_T_CALLFUNC_BUILTIN;\n\
+ fp->fr_flags = FR_%sQUE|FR_NOMATCH;\n\
+ fp->fr_data = (void *)ipf_rules_%s_%s[0];\n",
+ (in != 0) ? "IN" : "OUT", instr, group);
+ fprintf(fp, "\
+ fp->fr_dsize = sizeof(ipf_rules_%s_%s[0]);\n",
+ instr, group);
+
+ fprintf(fp, "\
+ fp->fr_family = AF_INET;\n\
+ fp->fr_func = (ipfunc_t)ipfrule_match_%s_%s;\n\
+ err = frrequest(&ipfmain, IPL_LOGIPF, SIOCADDFR, (caddr_t)fp,\n\
+ ipfmain.ipf_active, 0);\n",
+ instr, group);
+ fprintf(fp, "\treturn (err);\n}\n");
+
+ fprintf(fp, "\n\n\
+int ipfrule_remove_%s_%s()\n", instr, group);
+ fprintf(fp, "\
+{\n\
+ int err = 0, i;\n\
+ frentry_t *fp;\n\
+\n\
+ /*\n\
+ * Try to remove the %sbound rule.\n", instr);
+
+ fprintf(fp, "\
+ */\n\
+ if (ipfrule_%s_%s.fr_ref > 0) {\n", instr, group);
+
+ fprintf(fp, "\
+ err = EBUSY;\n\
+ } else {\n");
+
+ fprintf(fp, "\
+ i = sizeof(ipf_rules_%s_%s)/sizeof(frentry_t *) - 1;\n\
+ for (; i >= 0; i--) {\n\
+ fp = ipf_rules_%s_%s[i];\n\
+ if (fp->fr_ref > 1) {\n\
+ err = EBUSY;\n\
+ break;\n\
+ }\n\
+ }\n\
+ }\n\
+ if (err == 0)\n\
+ err = frrequest(&ipfmain, IPL_LOGIPF, SIOCDELFR,\n\
+ (caddr_t)&ipfrule_%s_%s,\n\
+ ipfmain.ipf_active, 0);\n",
+ instr, group, instr, group, instr, group);
+ fprintf(fp, "\
+ if (err)\n\
+ return (err);\n\
+\n\n");
+
+ fprintf(fp, "\treturn (err);\n}\n");
+}
diff --git a/sbin/ipf/ipf/ipfilter.4 b/sbin/ipf/ipf/ipfilter.4
new file mode 100644
index 000000000000..10fd18e0606f
--- /dev/null
+++ b/sbin/ipf/ipf/ipfilter.4
@@ -0,0 +1,241 @@
+.\" $FreeBSD$
+.\"
+.TH IP\ FILTER 4
+.SH NAME
+ipfilter \- Introduction to IP packet filtering
+.SH DESCRIPTION
+IP Filter is a TCP/IP packet filter, suitable for use in a firewall
+environment. To use, it can either be used as a loadable kernel module or
+incorporated into your UNIX kernel; use as a loadable kernel module where
+possible is highly recommended. Scripts are provided to install and patch
+system files, as required.
+.SH FEATURES
+The IP packet filter can:
+.IP
+explicitly deny/permit any packet from passing through
+.IP
+distinguish between various interfaces
+.IP
+filter by IP networks or hosts
+.IP
+selectively filter any IP protocol
+.IP
+selectively filter fragmented IP packets
+.IP
+selectively filter packets with IP options
+.IP
+send back an ICMP error/TCP reset for blocked packets
+.IP
+keep packet state information for TCP, UDP and ICMP packet flows
+.IP
+keep fragment state information for any IP packet, applying the same rule
+to all fragments.
+.IP
+act as a Network Address Translator (NAT)
+.IP
+use redirection to setup true transparent proxy connections
+.IP
+provide packet header details to a user program for authentication
+.IP
+in addition, supports temporary storage of pre-authenticated rules for passing packets through
+.PP
+Special provision is made for the three most common Internet protocols, TCP,
+UDP and ICMP. The IP Packet filter allows filtering of:
+.IP
+Inverted host/net matchingTCP/UDP packets by port number or a port number
+range
+.IP
+ICMP packets by type/code
+.IP
+"established" TCP packets
+.IP
+On any arbitrary combination of TCP flags
+.IP
+"short" (fragmented) IP packets with incomplete headers can be filtered
+.IP
+any of the 19 IP options or 8 registered IP security classes TOS (Type of
+Service) field in packets
+.PP
+To keep track of the performance of the IP packet filter, a logging device
+is used which supports logging of:
+.IP
+the TCP/UDP/ICMP and IP packet headers
+.IP
+the first 128 bytes of the packet (including headers)
+.PP
+A packet can be logged when:
+.IP
+it is successfully passed through
+.IP
+it is blocked from passing through
+.IP
+it matches a rule setup to look for suspicious packets
+.PP
+IP Filter keeps its own set of statistics on:
+.IP
+packets blocked
+.IP
+packets (and bytes!) used for accounting
+.IP
+packets passed
+.IP
+packets logged
+.IP
+attempts to log which failed (buffer full)
+.IP
+and much more, for packets going both in and out.
+
+.SH Tools
+The current implementation provides a small set of tools, which can easily
+be used and integrated with regular unix shells and tools. A brief description
+of the tools provided:
+.PP
+ipf(8)
+reads in a set of rules, from either stdin or a file, and adds them to
+the kernels current list (appending them). It can also be used to flush the
+current filter set or delete individual filter rules. The file format is
+described in ipf(5).
+.PP
+ipfs(8)
+is a utility to temporarily lock the IP Filter kernel tables (state tables
+and NAT mappings) and write them to disk. After that the system can be
+rebooted, and ipfs can be used to read these tables from disk and restore
+them into the kernel. This way the system can be rebooted without the
+connections being terminated.
+.PP
+ipfstat(8)
+interrogates the kernel for statistics on packet filtering, so
+far, and retrieves the list of filters in operation for inbound and outbound
+packets.
+.PP
+ipftest(1)
+reads in a filter rule file and then applies sample IP packets to
+the rule file. This allows for testing of filter list and examination of how
+a packet is passed along through it.
+.PP
+ipmon(8)
+reads buffered data from the logging device (default is /dev/ipl)
+for output to either:
+.IP
+screen (standard output)
+.IP
+file
+.IP
+syslog
+.PP
+ipsend(1)
+generates arbitary IP packets for ethernet connected machines.
+.PP
+ipresend(1)
+reads in a data file of saved IP packets (ie
+snoop/tcpdump/etherfind output) and sends it back across the network.
+.PP
+iptest(1)
+contains a set of test "programs" which send out a series of IP
+packets, aimed at testing the strength of the TCP/IP stack at which it is
+aimed at. WARNING: this may crash machine(s) targeted!
+.PP
+ipnat(8)
+reads in a set of rules, from either stdin or a file and adds them
+to the kernels current list of active NAT rules. NAT rules can also be
+deleted using ipnat. The format of the configuration file to be used
+with ipnat is described in ipnat(5).
+.PP
+For use in your own programs (e.g. for writing of transparent application
+proxies), the programming interface and the associated ioctl's are
+documented in ipf(4).
+
+Documentation on ioctl's and the format of data saved
+to the logging character device is provided in ipl(4)
+so that you may develop your own applications to work with or in place of any
+of the above.
+
+Similar, the interface to the NAT code is documented in ipnat(4).
+
+.SH PACKET PROCESSING FLOW
+The following diagram illustrates the flow of TCP/IP packets through the
+various stages introduced by IP Filter.
+.PP
+.nf
+ IN
+ |
+ V
+ +-------------------------+--------------------------+
+ | | |
+ | V |
+ | Network Address Translation |
+ | | |
+ | authenticated | |
+ | +-------<---------+ |
+ | | | |
+ | | V |
+ | V IP Accounting |
+ | | | |
+ | | V |
+ | | Fragment Cache Check--+ |
+ | | | | |
+ | V V V |
+ | | Packet State Check-->+ |
+ | | | | |
+ | | +->--+ | | |
+ | | | | V | |
+ | V groups IP Filtering V |
+ | | | | | | |
+ | | +--<-+ | | |
+ | | | | |
+ | +---------------->|<-----------+ |
+ | | |
+ | V |
+ | +---<----+ |
+ | | | |
+ | function | |
+ | | V |
+ | +--->----+ |
+ | | |
+ | V |
+ +--|---<--- fast-route ---<--+ |
+ | | | |
+ | | V |
+ | +-------------------------+--------------------------+
+ | |
+ | pass only
+ | |
+ | V
+ V [KERNEL TCP/IP Processing]
+ | |
+ | +-------------------------+--------------------------+
+ | | | |
+ | | V |
+ | | Fragment Cache Check--+ |
+ | | | | |
+ | | V V |
+ | | Packet State Check-->+ |
+ | | | | |
+ | | V | |
+ V | IP Filtering | |
+ | | | V |
+ | | |<-----------+ |
+ | | V |
+ | | IP Accounting |
+ | | | |
+ | | V |
+ | | Network Address Translation |
+ | | | |
+ | | V |
+ | +-------------------------+--------------------------+
+ | |
+ | pass only
+ V |
+ +--------------------------->|
+ V
+ OUT
+.fi
+
+.SH MORE INFORMATION
+More information (including pointers to the FAQ and the mailing list) can be
+obtained from the sofware's official homepage: www.ipfilter.org
+
+.SH SEE ALSO
+ipf(4), ipf(5), ipf(8), ipfilter(5), ipfs(8), ipfstat(8), ipftest(1),
+ipl(4), ipmon(8), ipnat(8), ipnat(4),
+
diff --git a/sbin/ipf/ipf/ipfilter.5 b/sbin/ipf/ipf/ipfilter.5
new file mode 100644
index 000000000000..97e504df15fa
--- /dev/null
+++ b/sbin/ipf/ipf/ipfilter.5
@@ -0,0 +1,11 @@
+.\" $FreeBSD$
+.TH IPFILTER 1
+.SH NAME
+IP Filter
+.SH DESCRIPTION
+.PP
+IP Filter is a package providing packet filtering capabilities for a variety
+of operating systems. On a properly setup system, it can be used to build a
+firewall.
+.SH SEE ALSO
+ipf(8), ipf(1), ipf(5), ipnat(8), ipnat(5), mkfilters(1)
diff --git a/sbin/ipf/ipf/ipl.4 b/sbin/ipf/ipf/ipl.4
new file mode 100644
index 000000000000..da1d9e61ce0f
--- /dev/null
+++ b/sbin/ipf/ipf/ipl.4
@@ -0,0 +1,81 @@
+.\" $FreeBSD$
+.\"
+.TH IPL 4
+.SH NAME
+ipl \- IP packet log device
+.SH DESCRIPTION
+The \fBipl\fP pseudo device's purpose is to provide an easy way to gather
+packet headers of packets you wish to log. If a packet header is to be
+logged, the entire header is logged (including any IP options \- TCP/UDP
+options are not included when it calculates header size) or not at all.
+The packet contents are also logged after the header. If the log reader
+is busy or otherwise unable to read log records, up to IPLLOGSIZE (8192 is the
+default) bytes of data are stored.
+.PP
+Prepending every packet header logged is a structure containing information
+relevant to the packet following and why it was logged. The structure's
+format is as follows:
+.LP
+.nf
+/*
+ * Log structure. Each packet header logged is prepended by one of these.
+ * Following this in the log records read from the device will be an ipflog
+ * structure which is then followed by any packet data.
+ */
+typedef struct iplog {
+ u_long ipl_sec;
+ u_long ipl_usec;
+ u_int ipl_len;
+ u_int ipl_count;
+ size_t ipl_dsize;
+ struct iplog *ipl_next;
+} iplog_t;
+
+
+typedef struct ipflog {
+#if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199603))
+ u_char fl_ifname[IFNAMSIZ];
+#else
+ u_int fl_unit;
+ u_char fl_ifname[4];
+#endif
+ u_char fl_plen; /* extra data after hlen */
+ u_char fl_hlen; /* length of IP headers saved */
+ u_short fl_rule; /* assume never more than 64k rules, total */
+ u_32_t fl_flags;
+} ipflog_t;
+
+.fi
+.PP
+When reading from the \fBipl\fP device, it is necessary to call read(2) with
+a buffer big enough to hold at least 1 complete log record - reading of partial
+log records is not supported.
+.PP
+If the packet contents are more than 128 bytes when \fBlog body\fP is used,
+then only 128 bytes of the packet contents are logged.
+.PP
+Although it is only possible to read from the \fBipl\fP device, opening it
+for writing is required when using an ioctl which changes any kernel data.
+.PP
+The ioctls which are loaded with this device can be found under \fBipf(4)\fP.
+The ioctls which are for use with logging and don't affect the filter are:
+.LP
+.nf
+ ioctl(fd, SIOCIPFFB, int *)
+ ioctl(fd, FIONREAD, int *)
+.fi
+.PP
+The SIOCIPFFB ioctl flushes the log buffer and returns the number of bytes
+flushed. FIONREAD returns the number of bytes currently used for storing
+log data. If IPFILTER_LOG is not defined when compiling, SIOCIPFFB is not
+available and FIONREAD will return but not do anything.
+.PP
+There is currently no support for non-blocking IO with this device, meaning
+all read operations should be considered blocking in nature (if there is no
+data to read, it will sleep until some is made available).
+.SH SEE ALSO
+ipf(4)
+.SH BUGS
+Packet headers are dropped when the internal buffer (static size) fills.
+.SH FILES
+/dev/ipl0
diff --git a/sbin/ipf/ipfs/ipfs.8 b/sbin/ipf/ipfs/ipfs.8
new file mode 100644
index 000000000000..01d0c707d60c
--- /dev/null
+++ b/sbin/ipf/ipfs/ipfs.8
@@ -0,0 +1,127 @@
+.\" $FreeBSD$
+.\"
+.TH IPFS 8
+.SH NAME
+ipfs \- saves and restores information for NAT and state tables.
+.SH SYNOPSIS
+.B ipfs
+[-nv] -l
+.PP
+.B ipfs
+[-nv] -u
+.PP
+.B ipfs
+[-nv] [
+.B \-d
+<\fIdirname\fP>
+] -R
+.PP
+.B ipfs
+[-nv] [
+.B \-d
+<\fIdirname\fP>
+] -W
+.PP
+.B ipfs
+[-nNSv] [
+.B \-f
+<\fIfilename\fP>
+] -r
+.PP
+.B ipfs
+[-nNSv] [
+.B \-f
+<\fIfilename\fP>
+] -w
+.PP
+.B ipfs
+[-nNSv]
+.B \-f
+<\fIfilename\fP>
+.B \-i
+<if1>,<if2>
+.SH DESCRIPTION
+.PP
+\fBipfs\fP allows state information created for NAT entries and rules using
+\fIkeep state\fP to be locked (modification prevented) and then saved to disk,
+allowing for the system to experience a reboot, followed by the restoration
+of that information, resulting in connections not being interrupted.
+.SH OPTIONS
+.TP
+.B \-d
+Change the default directory used with
+.B \-R
+and
+.B \-W
+options for saving state information.
+.TP
+.B \-n
+Don't actually take any action that would affect information stored in
+the kernel or on disk.
+.TP
+.B \-v
+Provides a verbose description of what's being done.
+.TP
+.B \-i <ifname1>,<ifname2>
+Change all instances of interface name ifname1 in the state save file to
+ifname2. Useful if you're restoring state information after a hardware
+reconfiguration or change.
+.TP
+.B \-N
+Operate on NAT information.
+.TP
+.B \-S
+Operate on filtering state information.
+.TP
+.B \-u
+Unlock state tables in the kernel.
+.TP
+.B \-l
+Lock state tables in the kernel.
+.TP
+.B \-r
+Read information in from the specified file and load it into the
+kernel. This requires the state tables to have already been locked
+and does not change the lock once complete.
+.TP
+.B \-w
+Write information out to the specified file and from the kernel.
+This requires the state tables to have already been locked
+and does not change the lock once complete.
+.TP
+.B \-R
+Restores all saved state information, if any, from two files,
+\fIipstate.ipf\fP and \fIipnat.ipf\fP, stored in the \fI/var/db/ipf\fP
+directory unless otherwise specified by the
+.B \-d
+option. The state tables are locked at the beginning of this
+operation and unlocked once complete.
+.TP
+.B \-W
+Saves in-kernel state information, if any, out to two files,
+\fIipstate.ipf\fP and \fIipnat.ipf\fP, stored in the \fI/var/db/ipf\fP
+directory unless otherwise specified by the
+.B \-d
+option. The state tables are locked at the beginning of this
+operation and unlocked once complete.
+.DT
+.SH FILES
+/var/db/ipf/ipstate.ipf
+.br
+/var/db/ipf/ipnat.ipf
+.br
+/dev/ipl
+.br
+/dev/ipstate
+.br
+/dev/ipnat
+.SH SEE ALSO
+ipf(8), ipl(4), ipmon(8), ipnat(8)
+.SH DIAGNOSTICS
+.PP
+Perhaps the -W and -R operations should set the locking but rather than
+undo it, restore it to what it was previously. Fragment table information
+is currently not saved.
+.SH BUGS
+.PP
+If you find any, please send email to me at darrenr@pobox.com
diff --git a/sbin/ipf/ipfs/ipfs.c b/sbin/ipf/ipfs/ipfs.c
new file mode 100644
index 000000000000..3f1202894fad
--- /dev/null
+++ b/sbin/ipf/ipfs/ipfs.c
@@ -0,0 +1,855 @@
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2012 by Darren Reed.
+ *
+ * See the IPFILTER.LICENCE file for details on licencing.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#if !defined(__SVR4) && !defined(__GNUC__)
+#include <strings.h>
+#endif
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <netinet/ip.h>
+#include <netdb.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include "ipf.h"
+#include "netinet/ipl.h"
+
+#if !defined(lint)
+static const char rcsid[] = "@(#)$Id$";
+#endif
+
+#ifndef IPF_SAVEDIR
+# define IPF_SAVEDIR "/var/db/ipf"
+#endif
+#ifndef IPF_NATFILE
+# define IPF_NATFILE "ipnat.ipf"
+#endif
+#ifndef IPF_STATEFILE
+# define IPF_STATEFILE "ipstate.ipf"
+#endif
+
+#if !defined(__SVR4) && defined(__GNUC__)
+extern char *index(const char *, int);
+#endif
+
+extern char *optarg;
+extern int optind;
+
+int main(int, char *[]);
+void usage(void);
+int changestateif(char *, char *);
+int changenatif(char *, char *);
+int readstate(int, char *);
+int readnat(int, char *);
+int writestate(int, char *);
+int opendevice(char *);
+void closedevice(int);
+int setlock(int, int);
+int writeall(char *);
+int readall(char *);
+int writenat(int, char *);
+
+int opts = 0;
+char *progname;
+
+
+void usage()
+{
+ fprintf(stderr, "usage: %s [-nv] -l\n", progname);
+ fprintf(stderr, "usage: %s [-nv] -u\n", progname);
+ fprintf(stderr, "usage: %s [-nv] [-d <dir>] -R\n", progname);
+ fprintf(stderr, "usage: %s [-nv] [-d <dir>] -W\n", progname);
+ fprintf(stderr, "usage: %s [-nNSv] [-f <file>] -r\n", progname);
+ fprintf(stderr, "usage: %s [-nNSv] [-f <file>] -w\n", progname);
+ fprintf(stderr, "usage: %s [-nNSv] -f <filename> -i <if1>,<if2>\n",
+ progname);
+ exit(1);
+}
+
+
+/*
+ * Change interface names in state information saved out to disk.
+ */
+int changestateif(char *ifs, char *fname)
+{
+ int fd, olen, nlen, rw;
+ ipstate_save_t ips;
+ off_t pos;
+ char *s;
+
+ s = strchr(ifs, ',');
+ if (!s)
+ usage();
+ *s++ = '\0';
+ nlen = strlen(s);
+ olen = strlen(ifs);
+ if (nlen >= sizeof(ips.ips_is.is_ifname) ||
+ olen >= sizeof(ips.ips_is.is_ifname))
+ usage();
+
+ fd = open(fname, O_RDWR);
+ if (fd == -1) {
+ perror("open");
+ exit(1);
+ }
+
+ for (pos = 0; read(fd, &ips, sizeof(ips)) == sizeof(ips); ) {
+ rw = 0;
+ if (!strncmp(ips.ips_is.is_ifname[0], ifs, olen + 1)) {
+ strcpy(ips.ips_is.is_ifname[0], s);
+ rw = 1;
+ }
+ if (!strncmp(ips.ips_is.is_ifname[1], ifs, olen + 1)) {
+ strcpy(ips.ips_is.is_ifname[1], s);
+ rw = 1;
+ }
+ if (!strncmp(ips.ips_is.is_ifname[2], ifs, olen + 1)) {
+ strcpy(ips.ips_is.is_ifname[2], s);
+ rw = 1;
+ }
+ if (!strncmp(ips.ips_is.is_ifname[3], ifs, olen + 1)) {
+ strcpy(ips.ips_is.is_ifname[3], s);
+ rw = 1;
+ }
+ if (rw == 1) {
+ if (lseek(fd, pos, SEEK_SET) != pos) {
+ perror("lseek");
+ exit(1);
+ }
+ if (write(fd, &ips, sizeof(ips)) != sizeof(ips)) {
+ perror("write");
+ exit(1);
+ }
+ }
+ pos = lseek(fd, 0, SEEK_CUR);
+ }
+ close(fd);
+
+ return (0);
+}
+
+
+/*
+ * Change interface names in NAT information saved out to disk.
+ */
+int changenatif(char *ifs, char *fname)
+{
+ int fd, olen, nlen, rw;
+ nat_save_t ipn;
+ nat_t *nat;
+ off_t pos;
+ char *s;
+
+ s = strchr(ifs, ',');
+ if (!s)
+ usage();
+ *s++ = '\0';
+ nlen = strlen(s);
+ olen = strlen(ifs);
+ nat = &ipn.ipn_nat;
+ if (nlen >= sizeof(nat->nat_ifnames[0]) ||
+ olen >= sizeof(nat->nat_ifnames[0]))
+ usage();
+
+ fd = open(fname, O_RDWR);
+ if (fd == -1) {
+ perror("open");
+ exit(1);
+ }
+
+ for (pos = 0; read(fd, &ipn, sizeof(ipn)) == sizeof(ipn); ) {
+ rw = 0;
+ if (!strncmp(nat->nat_ifnames[0], ifs, olen + 1)) {
+ strcpy(nat->nat_ifnames[0], s);
+ rw = 1;
+ }
+ if (!strncmp(nat->nat_ifnames[1], ifs, olen + 1)) {
+ strcpy(nat->nat_ifnames[1], s);
+ rw = 1;
+ }
+ if (rw == 1) {
+ if (lseek(fd, pos, SEEK_SET) != pos) {
+ perror("lseek");
+ exit(1);
+ }
+ if (write(fd, &ipn, sizeof(ipn)) != sizeof(ipn)) {
+ perror("write");
+ exit(1);
+ }
+ }
+ pos = lseek(fd, 0, SEEK_CUR);
+ }
+ close(fd);
+
+ return (0);
+}
+
+
+int main(int argc, char *argv[])
+{
+ int c, lock = -1, devfd = -1, err = 0, rw = -1, ns = -1, set = 0;
+ char *dirname = NULL, *filename = NULL, *ifs = NULL;
+
+ progname = argv[0];
+ while ((c = getopt(argc, argv, "d:f:i:lNnSRruvWw")) != -1)
+ switch (c)
+ {
+ case 'd' :
+ if ((set == 0) && !dirname && !filename)
+ dirname = optarg;
+ else
+ usage();
+ break;
+ case 'f' :
+ if ((set != 0) && !dirname && !filename)
+ filename = optarg;
+ else
+ usage();
+ break;
+ case 'i' :
+ ifs = optarg;
+ set = 1;
+ break;
+ case 'l' :
+ if (filename || dirname || set)
+ usage();
+ lock = 1;
+ set = 1;
+ break;
+ case 'n' :
+ opts |= OPT_DONOTHING;
+ break;
+ case 'N' :
+ if ((ns >= 0) || dirname || (rw != -1) || set)
+ usage();
+ ns = 0;
+ set = 1;
+ break;
+ case 'r' :
+ if (dirname || (rw != -1) || (ns == -1))
+ usage();
+ rw = 0;
+ set = 1;
+ break;
+ case 'R' :
+ rw = 2;
+ set = 1;
+ break;
+ case 'S' :
+ if ((ns >= 0) || dirname || (rw != -1) || set)
+ usage();
+ ns = 1;
+ set = 1;
+ break;
+ case 'u' :
+ if (filename || dirname || set)
+ usage();
+ lock = 0;
+ set = 1;
+ break;
+ case 'v' :
+ opts |= OPT_VERBOSE;
+ break;
+ case 'w' :
+ if (dirname || (rw != -1) || (ns == -1))
+ usage();
+ rw = 1;
+ set = 1;
+ break;
+ case 'W' :
+ rw = 3;
+ set = 1;
+ break;
+ case '?' :
+ default :
+ usage();
+ }
+
+ if (ifs) {
+ if (!filename || ns < 0)
+ usage();
+ if (ns == 0)
+ return (changenatif(ifs, filename));
+ else
+ return (changestateif(ifs, filename));
+ }
+
+ if ((ns >= 0) || (lock >= 0)) {
+ if (lock >= 0)
+ devfd = opendevice(NULL);
+ else if (ns >= 0) {
+ if (ns == 1)
+ devfd = opendevice(IPSTATE_NAME);
+ else if (ns == 0)
+ devfd = opendevice(IPNAT_NAME);
+ }
+ if (devfd == -1)
+ exit(1);
+ }
+
+ if (lock >= 0)
+ err = setlock(devfd, lock);
+ else if (rw >= 0) {
+ if (rw & 1) { /* WRITE */
+ if (rw & 2)
+ err = writeall(dirname);
+ else {
+ if (ns == 0)
+ err = writenat(devfd, filename);
+ else if (ns == 1)
+ err = writestate(devfd, filename);
+ }
+ } else {
+ if (rw & 2)
+ err = readall(dirname);
+ else {
+ if (ns == 0)
+ err = readnat(devfd, filename);
+ else if (ns == 1)
+ err = readstate(devfd, filename);
+ }
+ }
+ }
+ return (err);
+}
+
+
+int opendevice(char *ipfdev)
+{
+ int fd = -1;
+
+ if (opts & OPT_DONOTHING)
+ return (-2);
+
+ if (!ipfdev)
+ ipfdev = IPL_NAME;
+
+ if ((fd = open(ipfdev, O_RDWR)) == -1)
+ if ((fd = open(ipfdev, O_RDONLY)) == -1)
+ perror("open device");
+ return (fd);
+}
+
+
+void closedevice(int fd)
+{
+ close(fd);
+}
+
+
+int setlock(int fd, int lock)
+{
+ if (opts & OPT_VERBOSE)
+ printf("Turn lock %s\n", lock ? "on" : "off");
+ if (!(opts & OPT_DONOTHING)) {
+ if (ioctl(fd, SIOCSTLCK, &lock) == -1) {
+ perror("SIOCSTLCK");
+ return (1);
+ }
+ if (opts & OPT_VERBOSE)
+ printf("Lock now %s\n", lock ? "on" : "off");
+ }
+ return (0);
+}
+
+
+int writestate(int fd, char *file)
+{
+ ipstate_save_t ips, *ipsp;
+ ipfobj_t obj;
+ int wfd = -1;
+
+ if (!file)
+ file = IPF_STATEFILE;
+
+ wfd = open(file, O_WRONLY|O_TRUNC|O_CREAT, 0600);
+ if (wfd == -1) {
+ fprintf(stderr, "%s ", file);
+ perror("state:open");
+ return (1);
+ }
+
+ ipsp = &ips;
+ bzero((char *)&obj, sizeof(obj));
+ bzero((char *)ipsp, sizeof(ips));
+
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_size = sizeof(*ipsp);
+ obj.ipfo_type = IPFOBJ_STATESAVE;
+ obj.ipfo_ptr = ipsp;
+
+ do {
+
+ if (opts & OPT_VERBOSE)
+ printf("Getting state from addr %p\n", ips.ips_next);
+ if (ioctl(fd, SIOCSTGET, &obj)) {
+ if (errno == ENOENT)
+ break;
+ perror("state:SIOCSTGET");
+ close(wfd);
+ return (1);
+ }
+ if (opts & OPT_VERBOSE)
+ printf("Got state next %p\n", ips.ips_next);
+ if (write(wfd, ipsp, sizeof(ips)) != sizeof(ips)) {
+ perror("state:write");
+ close(wfd);
+ return (1);
+ }
+ } while (ips.ips_next != NULL);
+ close(wfd);
+
+ return (0);
+}
+
+
+int readstate(int fd, char *file)
+{
+ ipstate_save_t ips, *is, *ipshead = NULL, *is1, *ipstail = NULL;
+ int sfd = -1, i;
+ ipfobj_t obj;
+
+ if (!file)
+ file = IPF_STATEFILE;
+
+ sfd = open(file, O_RDONLY, 0600);
+ if (sfd == -1) {
+ fprintf(stderr, "%s ", file);
+ perror("open");
+ return (1);
+ }
+
+ bzero((char *)&ips, sizeof(ips));
+
+ /*
+ * 1. Read all state information in.
+ */
+ do {
+ i = read(sfd, &ips, sizeof(ips));
+ if (i == -1) {
+ perror("read");
+ goto freeipshead;
+ }
+ if (i == 0)
+ break;
+ if (i != sizeof(ips)) {
+ fprintf(stderr, "state:incomplete read: %d != %d\n",
+ i, (int)sizeof(ips));
+ goto freeipshead;
+ }
+ is = (ipstate_save_t *)malloc(sizeof(*is));
+ if (is == NULL) {
+ fprintf(stderr, "malloc failed\n");
+ goto freeipshead;
+ }
+
+ bcopy((char *)&ips, (char *)is, sizeof(ips));
+
+ /*
+ * Check to see if this is the first state entry that will
+ * reference a particular rule and if so, flag it as such
+ * else just adjust the rule pointer to become a pointer to
+ * the other. We do this so we have a means later for tracking
+ * who is referencing us when we get back the real pointer
+ * in is_rule after doing the ioctl.
+ */
+ for (is1 = ipshead; is1 != NULL; is1 = is1->ips_next)
+ if (is1->ips_rule == is->ips_rule)
+ break;
+ if (is1 == NULL)
+ is->ips_is.is_flags |= SI_NEWFR;
+ else
+ is->ips_rule = (void *)&is1->ips_rule;
+
+ /*
+ * Use a tail-queue type list (add things to the end)..
+ */
+ is->ips_next = NULL;
+ if (!ipshead)
+ ipshead = is;
+ if (ipstail)
+ ipstail->ips_next = is;
+ ipstail = is;
+ } while (1);
+
+ close(sfd);
+
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_size = sizeof(*is);
+ obj.ipfo_type = IPFOBJ_STATESAVE;
+
+ while ((is = ipshead) != NULL) {
+ if (opts & OPT_VERBOSE)
+ printf("Loading new state table entry\n");
+ if (is->ips_is.is_flags & SI_NEWFR) {
+ if (opts & OPT_VERBOSE)
+ printf("Loading new filter rule\n");
+ }
+
+ obj.ipfo_ptr = is;
+ if (!(opts & OPT_DONOTHING))
+ if (ioctl(fd, SIOCSTPUT, &obj)) {
+ perror("SIOCSTPUT");
+ goto freeipshead;
+ }
+
+ if (is->ips_is.is_flags & SI_NEWFR) {
+ if (opts & OPT_VERBOSE)
+ printf("Real rule addr %p\n", is->ips_rule);
+ for (is1 = is->ips_next; is1; is1 = is1->ips_next)
+ if (is1->ips_rule == (frentry_t *)&is->ips_rule)
+ is1->ips_rule = is->ips_rule;
+ }
+
+ ipshead = is->ips_next;
+ free(is);
+ }
+
+ return (0);
+
+freeipshead:
+ while ((is = ipshead) != NULL) {
+ ipshead = is->ips_next;
+ free(is);
+ }
+ if (sfd != -1)
+ close(sfd);
+ return (1);
+}
+
+
+int readnat(int fd, char *file)
+{
+ nat_save_t ipn, *in, *ipnhead = NULL, *in1, *ipntail = NULL;
+ ipfobj_t obj;
+ int nfd, i;
+ nat_t *nat;
+ char *s;
+ int n;
+
+ nfd = -1;
+ in = NULL;
+ ipnhead = NULL;
+ ipntail = NULL;
+
+ if (!file)
+ file = IPF_NATFILE;
+
+ nfd = open(file, O_RDONLY);
+ if (nfd == -1) {
+ fprintf(stderr, "%s ", file);
+ perror("nat:open");
+ return (1);
+ }
+
+ bzero((char *)&ipn, sizeof(ipn));
+
+ /*
+ * 1. Read all state information in.
+ */
+ do {
+ i = read(nfd, &ipn, sizeof(ipn));
+ if (i == -1) {
+ perror("read");
+ goto freenathead;
+ }
+ if (i == 0)
+ break;
+ if (i != sizeof(ipn)) {
+ fprintf(stderr, "nat:incomplete read: %d != %d\n",
+ i, (int)sizeof(ipn));
+ goto freenathead;
+ }
+
+ in = (nat_save_t *)malloc(ipn.ipn_dsize);
+ if (in == NULL) {
+ fprintf(stderr, "nat:cannot malloc nat save atruct\n");
+ goto freenathead;
+ }
+
+ if (ipn.ipn_dsize > sizeof(ipn)) {
+ n = ipn.ipn_dsize - sizeof(ipn);
+ if (n > 0) {
+ s = in->ipn_data + sizeof(in->ipn_data);
+ i = read(nfd, s, n);
+ if (i == 0)
+ break;
+ if (i != n) {
+ fprintf(stderr,
+ "nat:incomplete read: %d != %d\n",
+ i, n);
+ goto freenathead;
+ }
+ }
+ }
+ bcopy((char *)&ipn, (char *)in, sizeof(ipn));
+
+ /*
+ * Check to see if this is the first NAT entry that will
+ * reference a particular rule and if so, flag it as such
+ * else just adjust the rule pointer to become a pointer to
+ * the other. We do this so we have a means later for tracking
+ * who is referencing us when we get back the real pointer
+ * in is_rule after doing the ioctl.
+ */
+ nat = &in->ipn_nat;
+ if (nat->nat_fr != NULL) {
+ for (in1 = ipnhead; in1 != NULL; in1 = in1->ipn_next)
+ if (in1->ipn_rule == nat->nat_fr)
+ break;
+ if (in1 == NULL)
+ nat->nat_flags |= SI_NEWFR;
+ else
+ nat->nat_fr = &in1->ipn_fr;
+ }
+
+ /*
+ * Use a tail-queue type list (add things to the end)..
+ */
+ in->ipn_next = NULL;
+ if (!ipnhead)
+ ipnhead = in;
+ if (ipntail)
+ ipntail->ipn_next = in;
+ ipntail = in;
+ } while (1);
+
+ close(nfd);
+ nfd = -1;
+
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_type = IPFOBJ_NATSAVE;
+
+ while ((in = ipnhead) != NULL) {
+ if (opts & OPT_VERBOSE)
+ printf("Loading new NAT table entry\n");
+ nat = &in->ipn_nat;
+ if (nat->nat_flags & SI_NEWFR) {
+ if (opts & OPT_VERBOSE)
+ printf("Loading new filter rule\n");
+ }
+
+ obj.ipfo_ptr = in;
+ obj.ipfo_size = in->ipn_dsize;
+ if (!(opts & OPT_DONOTHING))
+ if (ioctl(fd, SIOCSTPUT, &obj)) {
+ fprintf(stderr, "in=%p:", in);
+ perror("SIOCSTPUT");
+ return (1);
+ }
+
+ if (nat->nat_flags & SI_NEWFR) {
+ if (opts & OPT_VERBOSE)
+ printf("Real rule addr %p\n", nat->nat_fr);
+ for (in1 = in->ipn_next; in1; in1 = in1->ipn_next)
+ if (in1->ipn_rule == &in->ipn_fr)
+ in1->ipn_rule = nat->nat_fr;
+ }
+
+ ipnhead = in->ipn_next;
+ free(in);
+ }
+
+ return (0);
+
+freenathead:
+ while ((in = ipnhead) != NULL) {
+ ipnhead = in->ipn_next;
+ free(in);
+ }
+ if (nfd != -1)
+ close(nfd);
+ return (1);
+}
+
+
+int writenat(int fd, char *file)
+{
+ nat_save_t *ipnp = NULL, *next = NULL;
+ ipfobj_t obj;
+ int nfd = -1;
+ natget_t ng;
+
+ if (!file)
+ file = IPF_NATFILE;
+
+ nfd = open(file, O_WRONLY|O_TRUNC|O_CREAT, 0600);
+ if (nfd == -1) {
+ fprintf(stderr, "%s ", file);
+ perror("nat:open");
+ return (1);
+ }
+
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_type = IPFOBJ_NATSAVE;
+
+ do {
+ if (opts & OPT_VERBOSE)
+ printf("Getting nat from addr %p\n", ipnp);
+ ng.ng_ptr = next;
+ ng.ng_sz = 0;
+ if (ioctl(fd, SIOCSTGSZ, &ng)) {
+ perror("nat:SIOCSTGSZ");
+ close(nfd);
+ if (ipnp != NULL)
+ free(ipnp);
+ return (1);
+ }
+
+ if (opts & OPT_VERBOSE)
+ printf("NAT size %d from %p\n", ng.ng_sz, ng.ng_ptr);
+
+ if (ng.ng_sz == 0)
+ break;
+
+ if (!ipnp)
+ ipnp = malloc(ng.ng_sz);
+ else
+ ipnp = realloc((char *)ipnp, ng.ng_sz);
+ if (!ipnp) {
+ fprintf(stderr,
+ "malloc for %d bytes failed\n", ng.ng_sz);
+ break;
+ }
+
+ bzero((char *)ipnp, ng.ng_sz);
+ obj.ipfo_size = ng.ng_sz;
+ obj.ipfo_ptr = ipnp;
+ ipnp->ipn_dsize = ng.ng_sz;
+ ipnp->ipn_next = next;
+ if (ioctl(fd, SIOCSTGET, &obj)) {
+ if (errno == ENOENT)
+ break;
+ perror("nat:SIOCSTGET");
+ close(nfd);
+ free(ipnp);
+ return (1);
+ }
+
+ if (opts & OPT_VERBOSE)
+ printf("Got nat next %p ipn_dsize %d ng_sz %d\n",
+ ipnp->ipn_next, ipnp->ipn_dsize, ng.ng_sz);
+ if (write(nfd, ipnp, ipnp->ipn_dsize) != ipnp->ipn_dsize) {
+ perror("nat:write");
+ close(nfd);
+ free(ipnp);
+ return (1);
+ }
+ next = ipnp->ipn_next;
+ } while (ipnp && next);
+ if (ipnp != NULL)
+ free(ipnp);
+ close(nfd);
+
+ return (0);
+}
+
+
+int writeall(char *dirname)
+{
+ int fd, devfd;
+
+ if (!dirname)
+ dirname = IPF_SAVEDIR;
+
+ if (chdir(dirname)) {
+ fprintf(stderr, "IPF_SAVEDIR=%s: ", dirname);
+ perror("chdir(IPF_SAVEDIR)");
+ return (1);
+ }
+
+ fd = opendevice(NULL);
+ if (fd == -1)
+ return (1);
+ if (setlock(fd, 1)) {
+ close(fd);
+ return (1);
+ }
+
+ devfd = opendevice(IPSTATE_NAME);
+ if (devfd == -1)
+ goto bad;
+ if (writestate(devfd, NULL))
+ goto bad;
+ close(devfd);
+
+ devfd = opendevice(IPNAT_NAME);
+ if (devfd == -1)
+ goto bad;
+ if (writenat(devfd, NULL))
+ goto bad;
+ close(devfd);
+
+ if (setlock(fd, 0)) {
+ close(fd);
+ return (1);
+ }
+
+ close(fd);
+ return (0);
+
+bad:
+ setlock(fd, 0);
+ close(fd);
+ return (1);
+}
+
+
+int readall(char *dirname)
+{
+ int fd, devfd;
+
+ if (!dirname)
+ dirname = IPF_SAVEDIR;
+
+ if (chdir(dirname)) {
+ perror("chdir(IPF_SAVEDIR)");
+ return (1);
+ }
+
+ fd = opendevice(NULL);
+ if (fd == -1)
+ return (1);
+ if (setlock(fd, 1)) {
+ close(fd);
+ return (1);
+ }
+
+ devfd = opendevice(IPSTATE_NAME);
+ if (devfd == -1)
+ return (1);
+ if (readstate(devfd, NULL))
+ return (1);
+ close(devfd);
+
+ devfd = opendevice(IPNAT_NAME);
+ if (devfd == -1)
+ return (1);
+ if (readnat(devfd, NULL))
+ return (1);
+ close(devfd);
+
+ if (setlock(fd, 0)) {
+ close(fd);
+ return (1);
+ }
+
+ return (0);
+}
diff --git a/sbin/ipf/ipfstat/ipfstat.8 b/sbin/ipf/ipfstat/ipfstat.8
new file mode 100644
index 000000000000..3762bccbdccf
--- /dev/null
+++ b/sbin/ipf/ipfstat/ipfstat.8
@@ -0,0 +1,199 @@
+.\" $FreeBSD$
+.TH ipfstat 8
+.SH NAME
+ipfstat \- reports on packet filter statistics and filter list
+.SH SYNOPSIS
+.B ipfstat
+[
+.B \-46aAdfghIilnoRsv
+]
+.br
+.B ipfstat -t
+[
+.B \-6C
+] [
+.B \-D
+<addrport>
+] [
+.B \-P
+<protocol>
+] [
+.B \-S
+<addrport>
+] [
+.B \-T
+<refresh time>
+]
+.SH DESCRIPTION
+\fBipfstat\fP examines /dev/kmem using the symbols \fB_fr_flags\fP,
+\fB_frstats\fP, \fB_filterin\fP, and \fB_filterout\fP.
+To run and work, it needs to be able to read both /dev/kmem and the
+kernel itself. The kernel name defaults to \fB/boot/kernel/kernel\fP.
+.PP
+The default behaviour of \fBipfstat\fP
+is to retrieve and display the accumulated statistics which have been
+accumulated over time as the kernel has put packets through the filter.
+.SH OPTIONS
+.TP
+.B \-4
+Display filter lists and states for IPv4, if available. This is the default
+when displaying states. \fB-4\fP and \fB-6\fP is the default when
+displaying lists.
+.TP
+.B \-6
+Display filter lists and states for IPv6, if available.
+.TP
+.B \-a
+Display the accounting filter list and show bytes counted against each rule.
+.TP
+.B \-A
+Display packet authentication statistics.
+.TP
+.B \-C
+This option is only valid in combination with \fB\-t\fP.
+Display "closed" states as well in the top. Normally, a TCP connection is
+not displayed when it reaches the CLOSE_WAIT protocol state. With this
+option enabled, all state entries are displayed.
+.TP
+.BR \-d
+Produce debugging output when displaying data.
+.TP
+.BR \-D \0<addrport>
+This option is only valid in combination with \fB\-t\fP. Limit the state top
+display to show only state entries whose destination IP address and port
+match the addrport argument. The addrport specification is of the form
+ipaddress[,port]. The ipaddress and port should be either numerical or the
+string "any" (specifying any IP address resp. any port). If the \fB\-D\fP
+option is not specified, it defaults to "\fB\-D\fP any,any".
+.TP
+.B \-f
+Show fragment state information (statistics) and held state information (in
+the kernel) if any is present.
+.TP
+.B \-g
+Show groups currently configured (both active and inactive).
+.TP
+.B \-h
+Show per-rule the number of times each one scores a "hit".
+.TP
+.B \-i
+Display the filter list used for the input side of the kernel IP processing.
+.TP
+.B \-I
+Swap between retrieving "inactive"/"active" filter list details. For use
+in combination with \fB\-i\fP.
+.TP
+.B \-n
+Show the "rule number" for each rule as it is printed.
+.TP
+.B \-o
+Display the filter list used for the output side of the kernel IP processing.
+.TP
+.BR \-P \0<protocol>
+This option is only valid in combination with \fB\-t\fP. Limit the state top
+display to show only state entries that match a specific protocol. The
+argument can be a protocol name (as defined in \fB/etc/protocols\fP) or a
+protocol number. If this option is not specified, state entries for any
+protocol are specified.
+.TP
+.BR \-R
+Don't try to resolve addresses to hostnames and ports to services while
+printing statistics.
+.TP
+.B \-s
+Show packet/flow state information (statistics only).
+.TP
+.B \-sl
+Show held state information (in the kernel) if any is present (no statistics).
+.TP
+.BR \-S \0<addrport>
+This option is only valid in combination with \fB\-t\fP. Limit the state top
+display to show only state entries whose source IP address and port match
+the addrport argument. The addrport specification is of the form
+ipaddress[,port]. The ipaddress and port should be either numerical or the
+string "any" (specifying any IP address resp. any port). If the \fB\-S\fP
+option is not specified, it defaults to "\fB\-S\fP any,any".
+.TP
+.B \-t
+Show the state table in a way similar to the way \fBtop(1)\fP shows the process
+table. States can be sorted using a number of different ways. This option
+requires \fBcurses(3)\fP and needs to be compiled in. It may not be available on
+all operating systems. See below, for more information on the keys that can
+be used while ipfstat is in top mode.
+.TP
+.BR \-T \0<refreshtime>
+This option is only valid in combination with \fB\-t\fP. Specifies how often
+the state top display should be updated. The refresh time is the number of
+seconds between an update. Any positive integer can be used. The default (and
+minimal update time) is 1.
+.TP
+.B \-v
+Turn verbose mode on. Displays more debugging information. When used with
+either \fB-i\fP or \fB-o\fP, counters associated with the rule, such as the
+number of times it has been matched and the number of bytes from such packets
+is displayed. For "keep state" rules, a count of the number of state sessions
+active against the rule is also displayed.
+.SH SYNOPSIS
+The role of \fBipfstat\fP is to display current kernel statistics gathered
+as a result of applying the filters in place (if any) to packets going in and
+out of the kernel. This is the default operation when no command line
+parameters are present.
+.PP
+When supplied with either \fB\-i\fP or \fB\-o\fP, it will retrieve and display
+the appropriate list of filter rules currently installed and in use by the
+kernel.
+.PP
+One of the statistics that \fBipfstat\fP shows is \fBticks\fP.
+This number indicates how long the filter has been enabled.
+The number is incremented every half\-second.
+.SH STATE TOP
+Using the \fB\-t\fP option \fBipfstat\fP will enter the state top mode. In
+this mode the state table is displayed similar to the way \fBtop\fP displays
+the process table. The \fB\-C\fP, \fB\-D\fP, \fB\-P\fP, \fB\-S\fP and \fB\-T\fP
+command line options can be used to restrict the state entries that will be
+shown and to specify the frequency of display updates.
+.PP
+In state top mode, the following keys can be used to influence the displayed
+information:
+.TP
+\fBb\fP show packets/bytes from backward direction.
+.TP
+\fBf\fP show packets/bytes from forward direction. (default)
+.TP
+\fBl\fP redraw the screen.
+.TP
+\fBq\fP quit the program.
+.TP
+\fBs\fP switch between different sorting criterion.
+.TP
+\fBr\fP reverse the sorting criterion.
+.PP
+States can be sorted by protocol number, by number of IP packets, by number
+of bytes and by time-to-live of the state entry. The default is to sort by
+the number of bytes. States are sorted in descending order, but you can use
+the \fBr\fP key to sort them in ascending order.
+.SH STATE TOP LIMITATIONS
+It is currently not possible to interactively change the source, destination
+and protocol filters or the refresh frequency. This must be done from the
+command line.
+.PP
+The screen must have at least 80 columns. This is however not checked.
+When running state top in IPv6 mode, the screen must be much wider to display
+the very long IPv6 addresses.
+.PP
+Only the first X-5 entries that match the sort and filter criteria are
+displayed (where X is the number of rows on the display. The only way to see
+more entries is to resize the screen.
+.SH FILES
+/dev/kmem
+.br
+/dev/ipl
+.br
+/dev/ipstate
+.br
+/kernel
+.SH SEE ALSO
+ipf(8)
+.SH BUGS
+\fB-4\fP and \fB-6\fP are only valid with \fB-i\fP, \fB-o\fP, and \fB-t\fP.
+An error should result when used with other arguments.
diff --git a/sbin/ipf/ipfstat/ipfstat.c b/sbin/ipf/ipfstat/ipfstat.c
new file mode 100644
index 000000000000..11b3043f919c
--- /dev/null
+++ b/sbin/ipf/ipfstat/ipfstat.c
@@ -0,0 +1,2316 @@
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2012 by Darren Reed.
+ *
+ * See the IPFILTER.LICENCE file for details on licencing.
+ */
+#include <sys/ioctl.h>
+#include <ctype.h>
+#include <fcntl.h>
+# include <nlist.h>
+#include <ctype.h>
+#if defined(sun) && defined(__SVR4)
+# include <stddef.h>
+#endif
+#include "ipf.h"
+#include "netinet/ipl.h"
+#if defined(STATETOP)
+# if defined(sun) && defined(__SVR4)
+# include <sys/select.h>
+# endif
+# include <netinet/ip_var.h>
+# include <netinet/tcp_fsm.h>
+# include <ctype.h>
+# include <signal.h>
+# include <time.h>
+# if SOLARIS || defined(__NetBSD__)
+# ifdef ERR
+# undef ERR
+# endif
+# include <curses.h>
+# else /* SOLARIS */
+# include <ncurses.h>
+# endif /* SOLARIS */
+#endif /* STATETOP */
+#include "kmem.h"
+#if defined(__NetBSD__)
+# include <paths.h>
+#endif
+
+#if !defined(lint)
+static const char sccsid[] = "@(#)fils.c 1.21 4/20/96 (C) 1993-2000 Darren Reed";
+static const char rcsid[] = "@(#)$Id$";
+#endif
+
+
+extern char *optarg;
+extern int optind;
+extern int opterr;
+
+#define PRINTF (void)printf
+#define FPRINTF (void)fprintf
+static char *filters[4] = { "ipfilter(in)", "ipfilter(out)",
+ "ipacct(in)", "ipacct(out)" };
+static int state_logging = -1;
+static wordtab_t *state_fields = NULL;
+
+int nohdrfields = 0;
+int opts = 0;
+#ifdef USE_INET6
+int use_inet4 = 0;
+int use_inet6 = 0;
+#endif
+int live_kernel = 1;
+int state_fd = -1;
+int ipf_fd = -1;
+int auth_fd = -1;
+int nat_fd = -1;
+frgroup_t *grtop = NULL;
+frgroup_t *grtail = NULL;
+
+char *blockreasons[FRB_MAX_VALUE + 1] = {
+ "packet blocked",
+ "log rule failure",
+ "pps rate exceeded",
+ "jumbogram",
+ "makefrip failed",
+ "cannot add state",
+ "IP ID update failed",
+ "log-or-block failed",
+ "decapsulate failure",
+ "cannot create new auth entry",
+ "packet queued for auth",
+ "buffer coalesce failure",
+ "buffer pullup failure",
+ "auth feedback",
+ "bad fragment",
+ "IPv4 NAT failure",
+ "IPv6 NAT failure"
+};
+
+#ifdef STATETOP
+#define STSTRSIZE 80
+#define STGROWSIZE 16
+#define HOSTNMLEN 40
+
+#define STSORT_PR 0
+#define STSORT_PKTS 1
+#define STSORT_BYTES 2
+#define STSORT_TTL 3
+#define STSORT_SRCIP 4
+#define STSORT_SRCPT 5
+#define STSORT_DSTIP 6
+#define STSORT_DSTPT 7
+#define STSORT_MAX STSORT_DSTPT
+#define STSORT_DEFAULT STSORT_BYTES
+
+
+typedef struct statetop {
+ i6addr_t st_src;
+ i6addr_t st_dst;
+ u_short st_sport;
+ u_short st_dport;
+ u_char st_p;
+ u_char st_v;
+ u_char st_state[2];
+ U_QUAD_T st_pkts;
+ U_QUAD_T st_bytes;
+ u_long st_age;
+} statetop_t;
+#endif
+
+int main(int, char *[]);
+
+static int fetchfrag(int, int, ipfr_t *);
+static void showstats(friostat_t *, u_32_t);
+static void showfrstates(ipfrstat_t *, u_long);
+static void showlist(friostat_t *);
+static void showstatestats(ips_stat_t *);
+static void showipstates(ips_stat_t *, int *);
+static void showauthstates(ipf_authstat_t *);
+static void showtqtable_live(int);
+static void showgroups(friostat_t *);
+static void usage(char *);
+static int state_matcharray(ipstate_t *, int *);
+static int printlivelist(friostat_t *, int, int, frentry_t *,
+ char *, char *);
+static void printdeadlist(friostat_t *, int, int, frentry_t *,
+ char *, char *);
+static void printside(char *, ipf_statistics_t *);
+static void parse_ipportstr(const char *, i6addr_t *, int *);
+static void ipfstate_live(char *, friostat_t **, ips_stat_t **,
+ ipfrstat_t **, ipf_authstat_t **, u_32_t *);
+static void ipfstate_dead(char *, friostat_t **, ips_stat_t **,
+ ipfrstat_t **, ipf_authstat_t **, u_32_t *);
+static ipstate_t *fetchstate(ipstate_t *, ipstate_t *);
+#ifdef STATETOP
+static void topipstates(i6addr_t, i6addr_t, int, int, int,
+ int, int, int, int *);
+static void sig_break(int);
+static void sig_resize(int);
+static char *getip(int, i6addr_t *);
+static char *ttl_to_string(long);
+static int sort_p(const void *, const void *);
+static int sort_pkts(const void *, const void *);
+static int sort_bytes(const void *, const void *);
+static int sort_ttl(const void *, const void *);
+static int sort_srcip(const void *, const void *);
+static int sort_srcpt(const void *, const void *);
+static int sort_dstip(const void *, const void *);
+static int sort_dstpt(const void *, const void *);
+#endif
+
+
+static void usage(name)
+ char *name;
+{
+#ifdef USE_INET6
+ fprintf(stderr, "Usage: %s [-46aAdfghIilnoRsv]\n", name);
+#else
+ fprintf(stderr, "Usage: %s [-4aAdfghIilnoRsv]\n", name);
+#endif
+ fprintf(stderr, " %s [-M corefile] [-N symbol-list]\n", name);
+#ifdef STATETOP
+#ifdef USE_INET6
+ fprintf(stderr, " %s -t [-46C] ", name);
+#else
+ fprintf(stderr, " %s -t [-4C] ", name);
+#endif
+#endif
+ fprintf(stderr, "[-D destination address] [-P protocol] [-S source address] [-T refresh time]\n");
+ exit(1);
+}
+
+
+int main(int argc, char *argv[])
+{
+ ipf_authstat_t frauthst;
+ ipf_authstat_t *frauthstp = &frauthst;
+ friostat_t fio;
+ friostat_t *fiop = &fio;
+ ips_stat_t ipsst;
+ ips_stat_t *ipsstp = &ipsst;
+ ipfrstat_t ifrst;
+ ipfrstat_t *ifrstp = &ifrst;
+ char *options;
+ char *kern = NULL;
+ char *memf = NULL;
+ int c;
+ int myoptind;
+ int *filter = NULL;
+
+ int protocol = -1; /* -1 = wild card for any protocol */
+ int refreshtime = 1; /* default update time */
+ int sport = -1; /* -1 = wild card for any source port */
+ int dport = -1; /* -1 = wild card for any dest port */
+ int topclosed = 0; /* do not show closed tcp sessions */
+ i6addr_t saddr, daddr;
+ u_32_t frf;
+
+#ifdef USE_INET6
+ options = "46aACdfghIilnostvD:m:M:N:O:P:RS:T:";
+#else
+ options = "4aACdfghIilnostvD:m:M:N:O:P:RS:T:";
+#endif
+
+ saddr.in4.s_addr = INADDR_ANY; /* default any v4 source addr */
+ daddr.in4.s_addr = INADDR_ANY; /* default any v4 dest addr */
+#ifdef USE_INET6
+ saddr.in6 = in6addr_any; /* default any v6 source addr */
+ daddr.in6 = in6addr_any; /* default any v6 dest addr */
+#endif
+
+ /* Don't warn about invalid flags when we run getopt for the 1st time */
+ opterr = 0;
+
+ /*
+ * Parse these two arguments now lest there be any buffer overflows
+ * in the parsing of the rest.
+ */
+ myoptind = optind;
+ while ((c = getopt(argc, argv, options)) != -1) {
+ switch (c)
+ {
+ case 'M' :
+ memf = optarg;
+ live_kernel = 0;
+ break;
+ case 'N' :
+ kern = optarg;
+ live_kernel = 0;
+ break;
+ }
+ }
+ optind = myoptind;
+
+ if (live_kernel == 1) {
+ if ((state_fd = open(IPSTATE_NAME, O_RDONLY)) == -1) {
+ perror("open(IPSTATE_NAME)");
+ exit(-1);
+ }
+ if ((auth_fd = open(IPAUTH_NAME, O_RDONLY)) == -1) {
+ perror("open(IPAUTH_NAME)");
+ exit(-1);
+ }
+ if ((nat_fd = open(IPNAT_NAME, O_RDONLY)) == -1) {
+ perror("open(IPAUTH_NAME)");
+ exit(-1);
+ }
+ if ((ipf_fd = open(IPL_NAME, O_RDONLY)) == -1) {
+ fprintf(stderr, "open(%s)", IPL_NAME);
+ perror("");
+ exit(-1);
+ }
+ }
+
+ if (kern != NULL || memf != NULL) {
+ (void)setgid(getgid());
+ (void)setuid(getuid());
+ }
+
+ if (live_kernel == 1) {
+ (void) checkrev(IPL_NAME);
+ } else {
+ if (openkmem(kern, memf) == -1)
+ exit(-1);
+ }
+
+ (void)setgid(getgid());
+ (void)setuid(getuid());
+
+ opterr = 1;
+
+ while ((c = getopt(argc, argv, options)) != -1)
+ {
+ switch (c)
+ {
+#ifdef USE_INET6
+ case '4' :
+ use_inet4 = 1;
+ break;
+ case '6' :
+ use_inet6 = 1;
+ break;
+#endif
+ case 'a' :
+ opts |= OPT_ACCNT|OPT_SHOWLIST;
+ break;
+ case 'A' :
+ opts |= OPT_AUTHSTATS;
+ break;
+ case 'C' :
+ topclosed = 1;
+ break;
+ case 'd' :
+ opts |= OPT_DEBUG;
+ break;
+ case 'D' :
+ parse_ipportstr(optarg, &daddr, &dport);
+ break;
+ case 'f' :
+ opts |= OPT_FRSTATES;
+ break;
+ case 'g' :
+ opts |= OPT_GROUPS;
+ break;
+ case 'h' :
+ opts |= OPT_HITS;
+ break;
+ case 'i' :
+ opts |= OPT_INQUE|OPT_SHOWLIST;
+ break;
+ case 'I' :
+ opts |= OPT_INACTIVE;
+ break;
+ case 'l' :
+ opts |= OPT_SHOWLIST;
+ break;
+ case 'm' :
+ filter = parseipfexpr(optarg, NULL);
+ if (filter == NULL) {
+ fprintf(stderr, "Error parseing '%s'\n",
+ optarg);
+ exit(1);
+ }
+ break;
+ case 'M' :
+ break;
+ case 'N' :
+ break;
+ case 'n' :
+ opts |= OPT_SHOWLINENO;
+ break;
+ case 'o' :
+ opts |= OPT_OUTQUE|OPT_SHOWLIST;
+ break;
+ case 'O' :
+ state_fields = parsefields(statefields, optarg);
+ break;
+ case 'P' :
+ protocol = getproto(optarg);
+ if (protocol == -1) {
+ fprintf(stderr, "%s: Invalid protocol: %s\n",
+ argv[0], optarg);
+ exit(-2);
+ }
+ break;
+ case 'R' :
+ opts |= OPT_NORESOLVE;
+ break;
+ case 's' :
+ opts |= OPT_IPSTATES;
+ break;
+ case 'S' :
+ parse_ipportstr(optarg, &saddr, &sport);
+ break;
+ case 't' :
+#ifdef STATETOP
+ opts |= OPT_STATETOP;
+ break;
+#else
+ fprintf(stderr,
+ "%s: state top facility not compiled in\n",
+ argv[0]);
+ exit(-2);
+#endif
+ case 'T' :
+ if (!sscanf(optarg, "%d", &refreshtime) ||
+ (refreshtime <= 0)) {
+ fprintf(stderr,
+ "%s: Invalid refreshtime < 1 : %s\n",
+ argv[0], optarg);
+ exit(-2);
+ }
+ break;
+ case 'v' :
+ opts |= OPT_VERBOSE;
+ break;
+ default :
+ usage(argv[0]);
+ break;
+ }
+ }
+#ifdef USE_INET6
+ if ((use_inet4 || use_inet6) &&
+ !(opts & (OPT_INQUE | OPT_OUTQUE | OPT_STATETOP))) {
+#ifdef STATETOP
+ FPRINTF(stderr, "No -i, -o, or -t given with -4 or -6\n");
+#else
+ FPRINTF(stderr, "No -i or -o given with -4 or -6\n");
+#endif
+ exit(-2);
+ }
+ if (use_inet4 == 0 && use_inet6 == 0)
+ use_inet4 = use_inet6 = 1;
+#endif
+
+ if (live_kernel == 1) {
+ bzero((char *)&fio, sizeof(fio));
+ bzero((char *)&ipsst, sizeof(ipsst));
+ bzero((char *)&ifrst, sizeof(ifrst));
+
+ ipfstate_live(IPL_NAME, &fiop, &ipsstp, &ifrstp,
+ &frauthstp, &frf);
+ } else {
+ ipfstate_dead(kern, &fiop, &ipsstp, &ifrstp, &frauthstp, &frf);
+ }
+
+ if (opts & OPT_IPSTATES) {
+ showipstates(ipsstp, filter);
+ } else if (opts & OPT_SHOWLIST) {
+ showlist(fiop);
+ if ((opts & OPT_OUTQUE) && (opts & OPT_INQUE)){
+ opts &= ~OPT_OUTQUE;
+ showlist(fiop);
+ }
+ } else if (opts & OPT_FRSTATES)
+ showfrstates(ifrstp, fiop->f_ticks);
+#ifdef STATETOP
+ else if (opts & OPT_STATETOP)
+ topipstates(saddr, daddr, sport, dport, protocol,
+#ifdef USE_INET6
+ use_inet6 && use_inet4 ? 0 : use_inet6 && !use_inet4 ? 6 : 4,
+#else
+ 4,
+#endif
+#endif
+ refreshtime, topclosed, filter);
+ else if (opts & OPT_AUTHSTATS)
+ showauthstates(frauthstp);
+ else if (opts & OPT_GROUPS)
+ showgroups(fiop);
+ else
+ showstats(fiop, frf);
+
+ return (0);
+}
+
+
+/*
+ * Fill in the stats structures from the live kernel, using a combination
+ * of ioctl's and copying directly from kernel memory.
+ */
+static void ipfstate_live(char *device, friostat_t **fiopp,
+ ips_stat_t **ipsstpp, ipfrstat_t **ifrstpp,
+ ipf_authstat_t **frauthstpp, u_32_t *frfp)
+{
+ ipfobj_t ipfo;
+
+ if (checkrev(device) == -1) {
+ fprintf(stderr, "User/kernel version check failed\n");
+ exit(1);
+ }
+
+ if ((opts & OPT_AUTHSTATS) == 0) {
+ bzero((caddr_t)&ipfo, sizeof(ipfo));
+ ipfo.ipfo_rev = IPFILTER_VERSION;
+ ipfo.ipfo_type = IPFOBJ_IPFSTAT;
+ ipfo.ipfo_size = sizeof(friostat_t);
+ ipfo.ipfo_ptr = (void *)*fiopp;
+
+ if (ioctl(ipf_fd, SIOCGETFS, &ipfo) == -1) {
+ ipferror(ipf_fd, "ioctl(ipf:SIOCGETFS)");
+ exit(-1);
+ }
+
+ if (ioctl(ipf_fd, SIOCGETFF, frfp) == -1)
+ ipferror(ipf_fd, "ioctl(SIOCGETFF)");
+ }
+
+ if ((opts & OPT_IPSTATES) != 0) {
+
+ bzero((caddr_t)&ipfo, sizeof(ipfo));
+ ipfo.ipfo_rev = IPFILTER_VERSION;
+ ipfo.ipfo_type = IPFOBJ_STATESTAT;
+ ipfo.ipfo_size = sizeof(ips_stat_t);
+ ipfo.ipfo_ptr = (void *)*ipsstpp;
+
+ if ((ioctl(state_fd, SIOCGETFS, &ipfo) == -1)) {
+ ipferror(state_fd, "ioctl(state:SIOCGETFS)");
+ exit(-1);
+ }
+ if (ioctl(state_fd, SIOCGETLG, &state_logging) == -1) {
+ ipferror(state_fd, "ioctl(state:SIOCGETLG)");
+ exit(-1);
+ }
+ }
+
+ if ((opts & OPT_FRSTATES) != 0) {
+ bzero((caddr_t)&ipfo, sizeof(ipfo));
+ ipfo.ipfo_rev = IPFILTER_VERSION;
+ ipfo.ipfo_type = IPFOBJ_FRAGSTAT;
+ ipfo.ipfo_size = sizeof(ipfrstat_t);
+ ipfo.ipfo_ptr = (void *)*ifrstpp;
+
+ if (ioctl(ipf_fd, SIOCGFRST, &ipfo) == -1) {
+ ipferror(ipf_fd, "ioctl(SIOCGFRST)");
+ exit(-1);
+ }
+ }
+
+ if (opts & OPT_DEBUG)
+ PRINTF("opts %#x name %s\n", opts, device);
+
+ if ((opts & OPT_AUTHSTATS) != 0) {
+ bzero((caddr_t)&ipfo, sizeof(ipfo));
+ ipfo.ipfo_rev = IPFILTER_VERSION;
+ ipfo.ipfo_type = IPFOBJ_AUTHSTAT;
+ ipfo.ipfo_size = sizeof(ipf_authstat_t);
+ ipfo.ipfo_ptr = (void *)*frauthstpp;
+
+ if (ioctl(auth_fd, SIOCATHST, &ipfo) == -1) {
+ ipferror(auth_fd, "ioctl(SIOCATHST)");
+ exit(-1);
+ }
+ }
+}
+
+
+/*
+ * Build up the stats structures from data held in the "core" memory.
+ * This is mainly useful when looking at data in crash dumps and ioctl's
+ * just won't work any more.
+ */
+static void ipfstate_dead( char *kernel, friostat_t **fiopp,
+ ips_stat_t **ipsstpp, ipfrstat_t **ifrstpp,
+ ipf_authstat_t **frauthstpp, u_32_t *frfp)
+{
+ static ipf_authstat_t frauthst, *frauthstp;
+ static ipftq_t ipstcptab[IPF_TCP_NSTATES];
+ static ips_stat_t ipsst, *ipsstp;
+ static ipfrstat_t ifrst, *ifrstp;
+ static friostat_t fio, *fiop;
+ int temp;
+
+ void *rules[2][2];
+ struct nlist deadlist[44] = {
+ { "ipf_auth_stats", 0, 0, 0, 0 }, /* 0 */
+ { "fae_list", 0, 0, 0, 0 },
+ { "ipauth", 0, 0, 0, 0 },
+ { "ipf_auth_list", 0, 0, 0, 0 },
+ { "ipf_auth_start", 0, 0, 0, 0 },
+ { "ipf_auth_end", 0, 0, 0, 0 }, /* 5 */
+ { "ipf_auth_next", 0, 0, 0, 0 },
+ { "ipf_auth", 0, 0, 0, 0 },
+ { "ipf_auth_used", 0, 0, 0, 0 },
+ { "ipf_auth_size", 0, 0, 0, 0 },
+ { "ipf_auth_defaultage", 0, 0, 0, 0 }, /* 10 */
+ { "ipf_auth_pkts", 0, 0, 0, 0 },
+ { "ipf_auth_lock", 0, 0, 0, 0 },
+ { "frstats", 0, 0, 0, 0 },
+ { "ips_stats", 0, 0, 0, 0 },
+ { "ips_num", 0, 0, 0, 0 }, /* 15 */
+ { "ips_wild", 0, 0, 0, 0 },
+ { "ips_list", 0, 0, 0, 0 },
+ { "ips_table", 0, 0, 0, 0 },
+ { "ipf_state_max", 0, 0, 0, 0 },
+ { "ipf_state_size", 0, 0, 0, 0 }, /* 20 */
+ { "ipf_state_doflush", 0, 0, 0, 0 },
+ { "ipf_state_lock", 0, 0, 0, 0 },
+ { "ipfr_heads", 0, 0, 0, 0 },
+ { "ipfr_nattab", 0, 0, 0, 0 },
+ { "ipfr_stats", 0, 0, 0, 0 }, /* 25 */
+ { "ipfr_inuse", 0, 0, 0, 0 },
+ { "ipf_ipfrttl", 0, 0, 0, 0 },
+ { "ipf_frag_lock", 0, 0, 0, 0 },
+ { "ipfr_timer_id", 0, 0, 0, 0 },
+ { "ipf_nat_lock", 0, 0, 0, 0 }, /* 30 */
+ { "ipf_rules", 0, 0, 0, 0 },
+ { "ipf_acct", 0, 0, 0, 0 },
+ { "ipl_frouteok", 0, 0, 0, 0 },
+ { "ipf_running", 0, 0, 0, 0 },
+ { "ipf_groups", 0, 0, 0, 0 }, /* 35 */
+ { "ipf_active", 0, 0, 0, 0 },
+ { "ipf_pass", 0, 0, 0, 0 },
+ { "ipf_flags", 0, 0, 0, 0 },
+ { "ipf_state_logging", 0, 0, 0, 0 },
+ { "ips_tqtqb", 0, 0, 0, 0 }, /* 40 */
+ { NULL, 0, 0, 0, 0 }
+ };
+
+
+ frauthstp = &frauthst;
+ ipsstp = &ipsst;
+ ifrstp = &ifrst;
+ fiop = &fio;
+
+ *frfp = 0;
+ *fiopp = fiop;
+ *ipsstpp = ipsstp;
+ *ifrstpp = ifrstp;
+ *frauthstpp = frauthstp;
+
+ bzero((char *)fiop, sizeof(*fiop));
+ bzero((char *)ipsstp, sizeof(*ipsstp));
+ bzero((char *)ifrstp, sizeof(*ifrstp));
+ bzero((char *)frauthstp, sizeof(*frauthstp));
+
+ if (nlist(kernel, deadlist) == -1) {
+ fprintf(stderr, "nlist error\n");
+ return;
+ }
+
+ /*
+ * This is for SIOCGETFF.
+ */
+ kmemcpy((char *)frfp, (u_long)deadlist[40].n_value, sizeof(*frfp));
+
+ /*
+ * f_locks is a combination of the lock variable from each part of
+ * ipfilter (state, auth, nat, fragments).
+ */
+ kmemcpy((char *)fiop, (u_long)deadlist[13].n_value, sizeof(*fiop));
+ kmemcpy((char *)&fiop->f_locks[0], (u_long)deadlist[22].n_value,
+ sizeof(fiop->f_locks[0]));
+ kmemcpy((char *)&fiop->f_locks[0], (u_long)deadlist[30].n_value,
+ sizeof(fiop->f_locks[1]));
+ kmemcpy((char *)&fiop->f_locks[2], (u_long)deadlist[28].n_value,
+ sizeof(fiop->f_locks[2]));
+ kmemcpy((char *)&fiop->f_locks[3], (u_long)deadlist[12].n_value,
+ sizeof(fiop->f_locks[3]));
+
+ /*
+ * Get pointers to each list of rules (active, inactive, in, out)
+ */
+ kmemcpy((char *)&rules, (u_long)deadlist[31].n_value, sizeof(rules));
+ fiop->f_fin[0] = rules[0][0];
+ fiop->f_fin[1] = rules[0][1];
+ fiop->f_fout[0] = rules[1][0];
+ fiop->f_fout[1] = rules[1][1];
+
+ /*
+ * Now get accounting rules pointers.
+ */
+ kmemcpy((char *)&rules, (u_long)deadlist[33].n_value, sizeof(rules));
+ fiop->f_acctin[0] = rules[0][0];
+ fiop->f_acctin[1] = rules[0][1];
+ fiop->f_acctout[0] = rules[1][0];
+ fiop->f_acctout[1] = rules[1][1];
+
+ /*
+ * A collection of "global" variables used inside the kernel which
+ * are all collected in friostat_t via ioctl.
+ */
+ kmemcpy((char *)&fiop->f_froute, (u_long)deadlist[33].n_value,
+ sizeof(fiop->f_froute));
+ kmemcpy((char *)&fiop->f_running, (u_long)deadlist[34].n_value,
+ sizeof(fiop->f_running));
+ kmemcpy((char *)&fiop->f_groups, (u_long)deadlist[35].n_value,
+ sizeof(fiop->f_groups));
+ kmemcpy((char *)&fiop->f_active, (u_long)deadlist[36].n_value,
+ sizeof(fiop->f_active));
+ kmemcpy((char *)&fiop->f_defpass, (u_long)deadlist[37].n_value,
+ sizeof(fiop->f_defpass));
+
+ /*
+ * Build up the state information stats structure.
+ */
+ kmemcpy((char *)ipsstp, (u_long)deadlist[14].n_value, sizeof(*ipsstp));
+ kmemcpy((char *)&temp, (u_long)deadlist[15].n_value, sizeof(temp));
+ kmemcpy((char *)ipstcptab, (u_long)deadlist[40].n_value,
+ sizeof(ipstcptab));
+ ipsstp->iss_active = temp;
+ ipsstp->iss_table = (void *)deadlist[18].n_value;
+ ipsstp->iss_list = (void *)deadlist[17].n_value;
+ ipsstp->iss_tcptab = ipstcptab;
+
+ /*
+ * Build up the authentiation information stats structure.
+ */
+ kmemcpy((char *)frauthstp, (u_long)deadlist[0].n_value,
+ sizeof(*frauthstp));
+ frauthstp->fas_faelist = (void *)deadlist[1].n_value;
+
+ /*
+ * Build up the fragment information stats structure.
+ */
+ kmemcpy((char *)ifrstp, (u_long)deadlist[25].n_value,
+ sizeof(*ifrstp));
+ ifrstp->ifs_table = (void *)deadlist[23].n_value;
+ ifrstp->ifs_nattab = (void *)deadlist[24].n_value;
+ kmemcpy((char *)&ifrstp->ifs_inuse, (u_long)deadlist[26].n_value,
+ sizeof(ifrstp->ifs_inuse));
+
+ /*
+ * Get logging on/off switches
+ */
+ kmemcpy((char *)&state_logging, (u_long)deadlist[41].n_value,
+ sizeof(state_logging));
+}
+
+
+static void printside(char *side, ipf_statistics_t *frs)
+{
+ int i;
+
+ PRINTF("%lu\t%s bad packets\n", frs->fr_bad, side);
+#ifdef USE_INET6
+ PRINTF("%lu\t%s IPv6 packets\n", frs->fr_ipv6, side);
+#endif
+ PRINTF("%lu\t%s packets blocked\n", frs->fr_block, side);
+ PRINTF("%lu\t%s packets passed\n", frs->fr_pass, side);
+ PRINTF("%lu\t%s packets not matched\n", frs->fr_nom, side);
+ PRINTF("%lu\t%s packets counted\n", frs->fr_acct, side);
+ PRINTF("%lu\t%s packets short\n", frs->fr_short, side);
+ PRINTF("%lu\t%s packets logged and blocked\n", frs->fr_bpkl, side);
+ PRINTF("%lu\t%s packets logged and passed\n", frs->fr_ppkl, side);
+ PRINTF("%lu\t%s fragment state kept\n", frs->fr_nfr, side);
+ PRINTF("%lu\t%s fragment state lost\n", frs->fr_bnfr, side);
+ PRINTF("%lu\t%s packet state kept\n", frs->fr_ads, side);
+ PRINTF("%lu\t%s packet state lost\n", frs->fr_bads, side);
+ PRINTF("%lu\t%s invalid source\n", frs->fr_v4_badsrc, side);
+ PRINTF("%lu\t%s cache hits\n", frs->fr_chit, side);
+ PRINTF("%lu\t%s cache misses\n", frs->fr_cmiss, side);
+ PRINTF("%lu\t%s bad coalesces\n", frs->fr_badcoalesces, side);
+ PRINTF("%lu\t%s pullups succeeded\n", frs->fr_pull[0], side);
+ PRINTF("%lu\t%s pullups failed\n", frs->fr_pull[1], side);
+ PRINTF("%lu\t%s TCP checksum failures\n", frs->fr_tcpbad, side);
+ for (i = 0; i <= FRB_MAX_VALUE; i++)
+ PRINTF("%lu\t%s block reason %s\n",
+ frs->fr_blocked[i], side, blockreasons[i]);
+}
+
+
+/*
+ * Display the kernel stats for packets blocked and passed and other
+ * associated running totals which are kept.
+ */
+static void showstats( struct friostat *fp, u_32_t frf)
+{
+ printside("input", &fp->f_st[0]);
+ printside("output", &fp->f_st[1]);
+
+ PRINTF("%lu\tpackets logged\n", fp->f_log_ok);
+ PRINTF("%lu\tlog failures\n", fp->f_log_fail);
+ PRINTF("%lu\tred-black no memory\n", fp->f_rb_no_mem);
+ PRINTF("%lu\tred-black node maximum\n", fp->f_rb_node_max);
+ PRINTF("%lu\tICMP replies sent\n", fp->f_st[0].fr_ret);
+ PRINTF("%lu\tTCP RSTs sent\n", fp->f_st[1].fr_ret);
+ PRINTF("%lu\tfastroute successes\n", fp->f_froute[0]);
+ PRINTF("%lu\tfastroute failures\n", fp->f_froute[1]);
+ PRINTF("%u\tIPF Ticks\n", fp->f_ticks);
+
+ PRINTF("%x\tPacket log flags set:\n", frf);
+ if (frf & FF_LOGPASS)
+ PRINTF("\tpackets passed through filter\n");
+ if (frf & FF_LOGBLOCK)
+ PRINTF("\tpackets blocked by filter\n");
+ if (frf & FF_LOGNOMATCH)
+ PRINTF("\tpackets not matched by filter\n");
+ if (!frf)
+ PRINTF("\tnone\n");
+}
+
+
+/*
+ * Print out a list of rules from the kernel, starting at the one passed.
+ */
+static int
+printlivelist( struct friostat *fiop, int out, int set, frentry_t *fp,
+ char *group, char *comment)
+{
+ struct frentry fb;
+ ipfruleiter_t rule;
+ frentry_t zero;
+ frgroup_t *g;
+ ipfobj_t obj;
+ int rules;
+ int num;
+
+ rules = 0;
+
+ rule.iri_inout = out;
+ rule.iri_active = set;
+ rule.iri_rule = &fb;
+ rule.iri_nrules = 1;
+ if (group != NULL)
+ strncpy(rule.iri_group, group, FR_GROUPLEN);
+ else
+ rule.iri_group[0] = '\0';
+
+ bzero((char *)&zero, sizeof(zero));
+
+ bzero((char *)&obj, sizeof(obj));
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_type = IPFOBJ_IPFITER;
+ obj.ipfo_size = sizeof(rule);
+ obj.ipfo_ptr = &rule;
+
+ while (rule.iri_rule != NULL) {
+ u_long array[1000];
+
+ memset(array, 0xff, sizeof(array));
+ fp = (frentry_t *)array;
+ rule.iri_rule = fp;
+ if (ioctl(ipf_fd, SIOCIPFITER, &obj) == -1) {
+ ipferror(ipf_fd, "ioctl(SIOCIPFITER)");
+ num = IPFGENITER_IPF;
+ (void) ioctl(ipf_fd,SIOCIPFDELTOK, &num);
+ return (rules);
+ }
+ if (bcmp(fp, &zero, sizeof(zero)) == 0)
+ break;
+ if (rule.iri_rule == NULL)
+ break;
+#ifdef USE_INET6
+ if (use_inet6 != 0 && use_inet4 == 0) {
+ if (fp->fr_family != 0 && fp->fr_family != AF_INET6)
+ continue;
+ } else if (use_inet4 != 0 && use_inet6 == 0) {
+#endif
+ if (fp->fr_family != 0 && fp->fr_family != AF_INET)
+ continue;
+#ifdef USE_INET6
+ } else {
+ if (fp->fr_family != 0 &&
+ fp->fr_family != AF_INET && fp->fr_family != AF_INET6)
+ continue;
+ }
+#endif
+
+ if (fp->fr_data != NULL)
+ fp->fr_data = (char *)fp + fp->fr_size;
+
+ rules++;
+
+ if (opts & (OPT_HITS|OPT_DEBUG))
+#ifdef USE_QUAD_T
+ PRINTF("%"PRIu64" ", (unsigned long long) fp->fr_hits);
+#else
+ PRINTF("%lu ", fp->fr_hits);
+#endif
+ if (opts & (OPT_ACCNT|OPT_DEBUG))
+#ifdef USE_QUAD_T
+ PRINTF("%"PRIu64" ", (unsigned long long) fp->fr_bytes);
+#else
+ PRINTF("%lu ", fp->fr_bytes);
+#endif
+ if (opts & OPT_SHOWLINENO)
+ PRINTF("@%d ", rules);
+
+ if (fp->fr_die != 0)
+ fp->fr_die -= fiop->f_ticks;
+
+ printfr(fp, ioctl);
+ if (opts & OPT_DEBUG) {
+ binprint(fp, fp->fr_size);
+ if (fp->fr_data != NULL && fp->fr_dsize > 0)
+ binprint(fp->fr_data, fp->fr_dsize);
+ }
+ if (fp->fr_grhead != -1) {
+ for (g = grtop; g != NULL; g = g->fg_next) {
+ if (!strncmp(fp->fr_names + fp->fr_grhead,
+ g->fg_name,
+ FR_GROUPLEN))
+ break;
+ }
+ if (g == NULL) {
+ g = calloc(1, sizeof(*g));
+
+ if (g != NULL) {
+ strncpy(g->fg_name,
+ fp->fr_names + fp->fr_grhead,
+ FR_GROUPLEN);
+ if (grtop == NULL) {
+ grtop = g;
+ grtail = g;
+ } else {
+ grtail->fg_next = g;
+ grtail = g;
+ }
+ }
+ }
+ }
+ if (fp->fr_type == FR_T_CALLFUNC) {
+ rules += printlivelist(fiop, out, set, fp->fr_data,
+ group, "# callfunc: ");
+ }
+ }
+
+ num = IPFGENITER_IPF;
+ (void) ioctl(ipf_fd,SIOCIPFDELTOK, &num);
+
+ return (rules);
+}
+
+
+static void printdeadlist(friostat_t *fiop, int out, int set, frentry_t *fp,
+ char *group, char *comment)
+{
+ frgroup_t *grtop, *grtail, *g;
+ struct frentry fb;
+ char *data;
+ u_32_t type;
+ int n;
+
+ fb.fr_next = fp;
+ n = 0;
+ grtop = NULL;
+ grtail = NULL;
+
+ for (n = 1; fp; fp = fb.fr_next, n++) {
+ if (kmemcpy((char *)&fb, (u_long)fb.fr_next,
+ fb.fr_size) == -1) {
+ perror("kmemcpy");
+ return;
+ }
+ fp = &fb;
+#ifdef USE_INET6
+ if (use_inet6 != 0 && use_inet4 == 0) {
+ if (fp->fr_family != 0 && fp->fr_family != AF_INET6)
+ continue;
+ } else if (use_inet4 != 0 && use_inet6 == 0) {
+#endif
+ if (fp->fr_family != 0 && fp->fr_family != AF_INET)
+ continue;
+#ifdef USE_INET6
+ } else {
+ if (fp->fr_family != 0 &&
+ fp->fr_family != AF_INET && fp->fr_family != AF_INET6)
+ continue;
+ }
+#endif
+
+ data = NULL;
+ type = fb.fr_type & ~FR_T_BUILTIN;
+ if (type == FR_T_IPF || type == FR_T_BPFOPC) {
+ if (fb.fr_dsize) {
+ data = malloc(fb.fr_dsize);
+
+ if (kmemcpy(data, (u_long)fb.fr_data,
+ fb.fr_dsize) == -1) {
+ perror("kmemcpy");
+ return;
+ }
+ fb.fr_data = data;
+ }
+ }
+
+ if (opts & OPT_HITS)
+#ifdef USE_QUAD_T
+ PRINTF("%"PRIu64" ", (unsigned long long) fb.fr_hits);
+#else
+ PRINTF("%lu ", fb.fr_hits);
+#endif
+ if (opts & OPT_ACCNT)
+#ifdef USE_QUAD_T
+ PRINTF("%"PRIu64" ", (unsigned long long) fb.fr_bytes);
+#else
+ PRINTF("%lu ", fb.fr_bytes);
+#endif
+ if (opts & OPT_SHOWLINENO)
+ PRINTF("@%d ", n);
+
+ printfr(fp, ioctl);
+ if (opts & OPT_DEBUG) {
+ binprint(fp, fp->fr_size);
+ if (fb.fr_data != NULL && fb.fr_dsize > 0)
+ binprint(fb.fr_data, fb.fr_dsize);
+ }
+ if (data != NULL)
+ free(data);
+ if (fb.fr_grhead != -1) {
+ g = calloc(1, sizeof(*g));
+
+ if (g != NULL) {
+ strncpy(g->fg_name, fb.fr_names + fb.fr_grhead,
+ FR_GROUPLEN);
+ if (grtop == NULL) {
+ grtop = g;
+ grtail = g;
+ } else {
+ grtail->fg_next = g;
+ grtail = g;
+ }
+ }
+ }
+ if (type == FR_T_CALLFUNC) {
+ printdeadlist(fiop, out, set, fb.fr_data, group,
+ "# callfunc: ");
+ }
+ }
+
+ while ((g = grtop) != NULL) {
+ printdeadlist(fiop, out, set, NULL, g->fg_name, comment);
+ grtop = g->fg_next;
+ free(g);
+ }
+}
+
+/*
+ * print out all of the asked for rule sets, using the stats struct as
+ * the base from which to get the pointers.
+ */
+static void showlist(struct friostat *fiop)
+{
+ struct frentry *fp = NULL;
+ int i, set;
+
+ set = fiop->f_active;
+ if (opts & OPT_INACTIVE)
+ set = 1 - set;
+ if (opts & OPT_ACCNT) {
+ if (opts & OPT_OUTQUE) {
+ i = F_ACOUT;
+ fp = (struct frentry *)fiop->f_acctout[set];
+ } else if (opts & OPT_INQUE) {
+ i = F_ACIN;
+ fp = (struct frentry *)fiop->f_acctin[set];
+ } else {
+ FPRINTF(stderr, "No -i or -o given with -a\n");
+ return;
+ }
+ } else {
+ if (opts & OPT_OUTQUE) {
+ i = F_OUT;
+ fp = (struct frentry *)fiop->f_fout[set];
+ } else if (opts & OPT_INQUE) {
+ i = F_IN;
+ fp = (struct frentry *)fiop->f_fin[set];
+ } else
+ return;
+ }
+ if (opts & OPT_DEBUG)
+ FPRINTF(stderr, "showlist:opts %#x i %d\n", opts, i);
+
+ if (opts & OPT_DEBUG)
+ PRINTF("fp %p set %d\n", fp, set);
+
+ if (live_kernel == 1) {
+ int printed;
+
+ printed = printlivelist(fiop, i, set, fp, NULL, NULL);
+ if (printed == 0) {
+ FPRINTF(stderr, "# empty list for %s%s\n",
+ (opts & OPT_INACTIVE) ? "inactive " : "",
+ filters[i]);
+ }
+ } else {
+ if (!fp) {
+ FPRINTF(stderr, "# empty list for %s%s\n",
+ (opts & OPT_INACTIVE) ? "inactive " : "",
+ filters[i]);
+ } else {
+ printdeadlist(fiop, i, set, fp, NULL, NULL);
+ }
+ }
+}
+
+
+/*
+ * Display ipfilter stateful filtering information
+ */
+static void showipstates(ips_stat_t *ipsp, int *filter)
+{
+ ipstate_t *is;
+ int i;
+
+ /*
+ * If a list of states hasn't been asked for, only print out stats
+ */
+ if (!(opts & OPT_SHOWLIST)) {
+ showstatestats(ipsp);
+ return;
+ }
+
+ if ((state_fields != NULL) && (nohdrfields == 0)) {
+ for (i = 0; state_fields[i].w_value != 0; i++) {
+ printfieldhdr(statefields, state_fields + i);
+ if (state_fields[i + 1].w_value != 0)
+ printf("\t");
+ }
+ printf("\n");
+ }
+
+ /*
+ * Print out all the state information currently held in the kernel.
+ */
+ for (is = ipsp->iss_list; is != NULL; ) {
+ ipstate_t ips;
+
+ is = fetchstate(is, &ips);
+
+ if (is == NULL)
+ break;
+
+ is = ips.is_next;
+ if ((filter != NULL) &&
+ (state_matcharray(&ips, filter) == 0)) {
+ continue;
+ }
+ if (state_fields != NULL) {
+ for (i = 0; state_fields[i].w_value != 0; i++) {
+ printstatefield(&ips, state_fields[i].w_value);
+ if (state_fields[i + 1].w_value != 0)
+ printf("\t");
+ }
+ printf("\n");
+ } else {
+ printstate(&ips, opts, ipsp->iss_ticks);
+ }
+ }
+}
+
+
+static void showstatestats(ips_stat_t *ipsp)
+{
+ int minlen, maxlen, totallen;
+ ipftable_t table;
+ u_int *buckets;
+ ipfobj_t obj;
+ int i, sz;
+
+ /*
+ * If a list of states hasn't been asked for, only print out stats
+ */
+
+ sz = sizeof(*buckets) * ipsp->iss_state_size;
+ buckets = (u_int *)malloc(sz);
+
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_type = IPFOBJ_GTABLE;
+ obj.ipfo_size = sizeof(table);
+ obj.ipfo_ptr = &table;
+
+ table.ita_type = IPFTABLE_BUCKETS;
+ table.ita_table = buckets;
+
+ if (live_kernel == 1) {
+ if (ioctl(state_fd, SIOCGTABL, &obj) != 0) {
+ free(buckets);
+ return;
+ }
+ } else {
+ if (kmemcpy((char *)buckets,
+ (u_long)ipsp->iss_bucketlen, sz)) {
+ free(buckets);
+ return;
+ }
+ }
+
+ PRINTF("%u\tactive state table entries\n",ipsp->iss_active);
+ PRINTF("%lu\tadd bad\n", ipsp->iss_add_bad);
+ PRINTF("%lu\tadd duplicate\n", ipsp->iss_add_dup);
+ PRINTF("%lu\tadd locked\n", ipsp->iss_add_locked);
+ PRINTF("%lu\tadd oow\n", ipsp->iss_add_oow);
+ PRINTF("%lu\tbucket full\n", ipsp->iss_bucket_full);
+ PRINTF("%lu\tcheck bad\n", ipsp->iss_check_bad);
+ PRINTF("%lu\tcheck miss\n", ipsp->iss_check_miss);
+ PRINTF("%lu\tcheck nattag\n", ipsp->iss_check_nattag);
+ PRINTF("%lu\tclone nomem\n", ipsp->iss_clone_nomem);
+ PRINTF("%lu\tcheck notag\n", ipsp->iss_check_notag);
+ PRINTF("%lu\tcheck success\n", ipsp->iss_hits);
+ PRINTF("%lu\tcloned\n", ipsp->iss_cloned);
+ PRINTF("%lu\texpired\n", ipsp->iss_expire);
+ PRINTF("%lu\tflush all\n", ipsp->iss_flush_all);
+ PRINTF("%lu\tflush closing\n", ipsp->iss_flush_closing);
+ PRINTF("%lu\tflush queue\n", ipsp->iss_flush_queue);
+ PRINTF("%lu\tflush state\n", ipsp->iss_flush_state);
+ PRINTF("%lu\tflush timeout\n", ipsp->iss_flush_timeout);
+ PRINTF("%u\thash buckets in use\n", ipsp->iss_inuse);
+ PRINTF("%lu\tICMP bad\n", ipsp->iss_icmp_bad);
+ PRINTF("%lu\tICMP banned\n", ipsp->iss_icmp_banned);
+ PRINTF("%lu\tICMP errors\n", ipsp->iss_icmp_icmperr);
+ PRINTF("%lu\tICMP head block\n", ipsp->iss_icmp_headblock);
+ PRINTF("%lu\tICMP hits\n", ipsp->iss_icmp_hits);
+ PRINTF("%lu\tICMP not query\n", ipsp->iss_icmp_notquery);
+ PRINTF("%lu\tICMP short\n", ipsp->iss_icmp_short);
+ PRINTF("%lu\tICMP too many\n", ipsp->iss_icmp_toomany);
+ PRINTF("%lu\tICMPv6 errors\n", ipsp->iss_icmp6_icmperr);
+ PRINTF("%lu\tICMPv6 miss\n", ipsp->iss_icmp6_miss);
+ PRINTF("%lu\tICMPv6 not info\n", ipsp->iss_icmp6_notinfo);
+ PRINTF("%lu\tICMPv6 not query\n", ipsp->iss_icmp6_notquery);
+ PRINTF("%lu\tlog fail\n", ipsp->iss_log_fail);
+ PRINTF("%lu\tlog ok\n", ipsp->iss_log_ok);
+ PRINTF("%lu\tlookup interface mismatch\n", ipsp->iss_lookup_badifp);
+ PRINTF("%lu\tlookup mask mismatch\n", ipsp->iss_miss_mask);
+ PRINTF("%lu\tlookup port mismatch\n", ipsp->iss_lookup_badport);
+ PRINTF("%lu\tlookup miss\n", ipsp->iss_lookup_miss);
+ PRINTF("%lu\tmaximum rule references\n", ipsp->iss_max_ref);
+ PRINTF("%lu\tmaximum hosts per rule\n", ipsp->iss_max_track);
+ PRINTF("%lu\tno memory\n", ipsp->iss_nomem);
+ PRINTF("%lu\tout of window\n", ipsp->iss_oow);
+ PRINTF("%lu\torphans\n", ipsp->iss_orphan);
+ PRINTF("%lu\tscan block\n", ipsp->iss_scan_block);
+ PRINTF("%lu\tstate table maximum reached\n", ipsp->iss_max);
+ PRINTF("%lu\tTCP closing\n", ipsp->iss_tcp_closing);
+ PRINTF("%lu\tTCP OOW\n", ipsp->iss_tcp_oow);
+ PRINTF("%lu\tTCP RST add\n", ipsp->iss_tcp_rstadd);
+ PRINTF("%lu\tTCP too small\n", ipsp->iss_tcp_toosmall);
+ PRINTF("%lu\tTCP bad options\n", ipsp->iss_tcp_badopt);
+ PRINTF("%lu\tTCP removed\n", ipsp->iss_fin);
+ PRINTF("%lu\tTCP FSM\n", ipsp->iss_tcp_fsm);
+ PRINTF("%lu\tTCP strict\n", ipsp->iss_tcp_strict);
+ PRINTF("%lu\tTCP wild\n", ipsp->iss_wild);
+ PRINTF("%lu\tMicrosoft Windows SACK\n", ipsp->iss_winsack);
+
+ PRINTF("State logging %sabled\n", state_logging ? "en" : "dis");
+
+ PRINTF("IP states added:\n");
+ for (i = 0; i < 256; i++) {
+ if (ipsp->iss_proto[i] != 0) {
+ struct protoent *proto;
+
+ proto = getprotobynumber(i);
+ PRINTF("%lu", ipsp->iss_proto[i]);
+ if (proto != NULL)
+ PRINTF("\t%s\n", proto->p_name);
+ else
+ PRINTF("\t%d\n", i);
+ }
+ }
+
+ PRINTF("\nState table bucket statistics:\n");
+ PRINTF("%u\tin use\n", ipsp->iss_inuse);
+
+ minlen = ipsp->iss_max;
+ totallen = 0;
+ maxlen = 0;
+
+ for (i = 0; i < ipsp->iss_state_size; i++) {
+ if (buckets[i] > maxlen)
+ maxlen = buckets[i];
+ if (buckets[i] < minlen)
+ minlen = buckets[i];
+ totallen += buckets[i];
+ }
+
+ PRINTF("%d\thash efficiency\n",
+ totallen ? ipsp->iss_inuse * 100 / totallen : 0);
+ PRINTF("%2.2f%%\tbucket usage\n%u\tminimal length\n",
+ ((float)ipsp->iss_inuse / ipsp->iss_state_size) * 100.0,
+ minlen);
+ PRINTF("%u\tmaximal length\n%.3f\taverage length\n",
+ maxlen,
+ ipsp->iss_inuse ? (float) totallen/ ipsp->iss_inuse :
+ 0.0);
+
+#define ENTRIES_PER_LINE 5
+
+ if (opts & OPT_VERBOSE) {
+ PRINTF("\nCurrent bucket sizes :\n");
+ for (i = 0; i < ipsp->iss_state_size; i++) {
+ if ((i % ENTRIES_PER_LINE) == 0)
+ PRINTF("\t");
+ PRINTF("%4d -> %4u", i, buckets[i]);
+ if ((i % ENTRIES_PER_LINE) ==
+ (ENTRIES_PER_LINE - 1))
+ PRINTF("\n");
+ else
+ PRINTF(" ");
+ }
+ PRINTF("\n");
+ }
+ PRINTF("\n");
+
+ free(buckets);
+
+ if (live_kernel == 1) {
+ showtqtable_live(state_fd);
+ } else {
+ printtqtable(ipsp->iss_tcptab);
+ }
+}
+
+
+#ifdef STATETOP
+static int handle_resize = 0, handle_break = 0;
+
+static void topipstates(i6addr_t saddr, i6addr_t daddr, int sport, int dport,
+ int protocol, int ver, int refreshtime, int topclosed, int *filter)
+{
+ char str1[STSTRSIZE], str2[STSTRSIZE], str3[STSTRSIZE], str4[STSTRSIZE];
+ int maxtsentries = 0, reverse = 0, sorting = STSORT_DEFAULT;
+ int i, j, winy, tsentry, maxx, maxy, redraw = 0, ret = 0;
+ int len, srclen, dstlen, forward = 1, c = 0;
+ ips_stat_t ipsst, *ipsstp = &ipsst;
+ int token_type = IPFGENITER_STATE;
+ statetop_t *tstable = NULL, *tp;
+ const char *errstr = "";
+ ipstate_t ips;
+ ipfobj_t ipfo;
+ struct timeval selecttimeout;
+ char hostnm[HOSTNMLEN];
+ struct protoent *proto;
+ fd_set readfd;
+ time_t t;
+
+ /* install signal handlers */
+ signal(SIGINT, sig_break);
+ signal(SIGQUIT, sig_break);
+ signal(SIGTERM, sig_break);
+ signal(SIGWINCH, sig_resize);
+
+ /* init ncurses stuff */
+ initscr();
+ cbreak();
+ noecho();
+ curs_set(0);
+ timeout(0);
+ getmaxyx(stdscr, maxy, maxx);
+
+ /* init hostname */
+ gethostname(hostnm, sizeof(hostnm) - 1);
+ hostnm[sizeof(hostnm) - 1] = '\0';
+
+ /* init ipfobj_t stuff */
+ bzero((caddr_t)&ipfo, sizeof(ipfo));
+ ipfo.ipfo_rev = IPFILTER_VERSION;
+ ipfo.ipfo_type = IPFOBJ_STATESTAT;
+ ipfo.ipfo_size = sizeof(*ipsstp);
+ ipfo.ipfo_ptr = (void *)ipsstp;
+
+ /* repeat until user aborts */
+ while ( 1 ) {
+
+ /* get state table */
+ bzero((char *)&ipsst, sizeof(ipsst));
+ if ((ioctl(state_fd, SIOCGETFS, &ipfo) == -1)) {
+ errstr = "ioctl(SIOCGETFS)";
+ ret = -1;
+ goto out;
+ }
+
+ /* clear the history */
+ tsentry = -1;
+
+ /* reset max str len */
+ srclen = dstlen = 0;
+
+ /* read the state table and store in tstable */
+ for (; ipsstp->iss_list; ipsstp->iss_list = ips.is_next) {
+
+ ipsstp->iss_list = fetchstate(ipsstp->iss_list, &ips);
+ if (ipsstp->iss_list == NULL)
+ break;
+
+ if (ver != 0 && ips.is_v != ver)
+ continue;
+
+ if ((filter != NULL) &&
+ (state_matcharray(&ips, filter) == 0))
+ continue;
+
+ /* check v4 src/dest addresses */
+ if (ips.is_v == 4) {
+ if ((saddr.in4.s_addr != INADDR_ANY &&
+ saddr.in4.s_addr != ips.is_saddr) ||
+ (daddr.in4.s_addr != INADDR_ANY &&
+ daddr.in4.s_addr != ips.is_daddr))
+ continue;
+ }
+#ifdef USE_INET6
+ /* check v6 src/dest addresses */
+ if (ips.is_v == 6) {
+ if ((IP6_NEQ(&saddr, &in6addr_any) &&
+ IP6_NEQ(&saddr, &ips.is_src)) ||
+ (IP6_NEQ(&daddr, &in6addr_any) &&
+ IP6_NEQ(&daddr, &ips.is_dst)))
+ continue;
+ }
+#endif
+ /* check protocol */
+ if (protocol > 0 && protocol != ips.is_p)
+ continue;
+
+ /* check ports if protocol is TCP or UDP */
+ if (((ips.is_p == IPPROTO_TCP) ||
+ (ips.is_p == IPPROTO_UDP)) &&
+ (((sport > 0) && (htons(sport) != ips.is_sport)) ||
+ ((dport > 0) && (htons(dport) != ips.is_dport))))
+ continue;
+
+ /* show closed TCP sessions ? */
+ if ((topclosed == 0) && (ips.is_p == IPPROTO_TCP) &&
+ (ips.is_state[0] >= IPF_TCPS_LAST_ACK) &&
+ (ips.is_state[1] >= IPF_TCPS_LAST_ACK))
+ continue;
+
+ /*
+ * if necessary make room for this state
+ * entry
+ */
+ tsentry++;
+ if (!maxtsentries || tsentry == maxtsentries) {
+ maxtsentries += STGROWSIZE;
+ tstable = reallocarray(tstable, maxtsentries,
+ sizeof(statetop_t));
+ if (tstable == NULL) {
+ perror("realloc");
+ exit(-1);
+ }
+ }
+
+ /* get max src/dest address string length */
+ len = strlen(getip(ips.is_v, &ips.is_src));
+ if (srclen < len)
+ srclen = len;
+ len = strlen(getip(ips.is_v, &ips.is_dst));
+ if (dstlen < len)
+ dstlen = len;
+
+ /* fill structure */
+ tp = tstable + tsentry;
+ tp->st_src = ips.is_src;
+ tp->st_dst = ips.is_dst;
+ tp->st_p = ips.is_p;
+ tp->st_v = ips.is_v;
+ tp->st_state[0] = ips.is_state[0];
+ tp->st_state[1] = ips.is_state[1];
+ if (forward) {
+ tp->st_pkts = ips.is_pkts[0]+ips.is_pkts[1];
+ tp->st_bytes = ips.is_bytes[0]+ips.is_bytes[1];
+ } else {
+ tp->st_pkts = ips.is_pkts[2]+ips.is_pkts[3];
+ tp->st_bytes = ips.is_bytes[2]+ips.is_bytes[3];
+ }
+ tp->st_age = ips.is_die - ipsstp->iss_ticks;
+ if ((ips.is_p == IPPROTO_TCP) ||
+ (ips.is_p == IPPROTO_UDP)) {
+ tp->st_sport = ips.is_sport;
+ tp->st_dport = ips.is_dport;
+ }
+ }
+
+ (void) ioctl(state_fd, SIOCIPFDELTOK, &token_type);
+
+ /* sort the array */
+ if (tsentry != -1) {
+ switch (sorting)
+ {
+ case STSORT_PR:
+ qsort(tstable, tsentry + 1,
+ sizeof(statetop_t), sort_p);
+ break;
+ case STSORT_PKTS:
+ qsort(tstable, tsentry + 1,
+ sizeof(statetop_t), sort_pkts);
+ break;
+ case STSORT_BYTES:
+ qsort(tstable, tsentry + 1,
+ sizeof(statetop_t), sort_bytes);
+ break;
+ case STSORT_TTL:
+ qsort(tstable, tsentry + 1,
+ sizeof(statetop_t), sort_ttl);
+ break;
+ case STSORT_SRCIP:
+ qsort(tstable, tsentry + 1,
+ sizeof(statetop_t), sort_srcip);
+ break;
+ case STSORT_SRCPT:
+ qsort(tstable, tsentry +1,
+ sizeof(statetop_t), sort_srcpt);
+ break;
+ case STSORT_DSTIP:
+ qsort(tstable, tsentry + 1,
+ sizeof(statetop_t), sort_dstip);
+ break;
+ case STSORT_DSTPT:
+ qsort(tstable, tsentry + 1,
+ sizeof(statetop_t), sort_dstpt);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* handle window resizes */
+ if (handle_resize) {
+ endwin();
+ initscr();
+ cbreak();
+ noecho();
+ curs_set(0);
+ timeout(0);
+ getmaxyx(stdscr, maxy, maxx);
+ redraw = 1;
+ handle_resize = 0;
+ }
+
+ /* stop program? */
+ if (handle_break)
+ break;
+
+ /* print title */
+ erase();
+ attron(A_BOLD);
+ winy = 0;
+ move(winy,0);
+ snprintf(str1, sizeof(str1), "%s - %s - state top", hostnm, IPL_VERSION);
+ for (j = 0 ; j < (maxx - 8 - strlen(str1)) / 2; j++)
+ printw(" ");
+ printw("%s", str1);
+ attroff(A_BOLD);
+
+ /* just for fun add a clock */
+ move(winy, maxx - 8);
+ t = time(NULL);
+ strftime(str1, 80, "%T", localtime(&t));
+ printw("%s\n", str1);
+
+ /*
+ * print the display filters, this is placed in the loop,
+ * because someday I might add code for changing these
+ * while the programming is running :-)
+ */
+ if (sport >= 0)
+ snprintf(str1, sizeof(str1), "%s,%d", getip(ver, &saddr), sport);
+ else
+ snprintf(str1, sizeof(str1), "%s", getip(ver, &saddr));
+
+ if (dport >= 0)
+ snprintf(str2, sizeof(str2), "%s,%d", getip(ver, &daddr), dport);
+ else
+ snprintf(str2, sizeof(str2), "%s", getip(ver, &daddr));
+
+ if (protocol < 0)
+ strcpy(str3, "any");
+ else if ((proto = getprotobynumber(protocol)) != NULL)
+ snprintf(str3, sizeof(str3), "%s", proto->p_name);
+ else
+ snprintf(str3, sizeof(str3), "%d", protocol);
+
+ switch (sorting)
+ {
+ case STSORT_PR:
+ snprintf(str4, sizeof(str4), "proto");
+ break;
+ case STSORT_PKTS:
+ snprintf(str4, sizeof(str4), "# pkts");
+ break;
+ case STSORT_BYTES:
+ snprintf(str4, sizeof(str4), "# bytes");
+ break;
+ case STSORT_TTL:
+ snprintf(str4, sizeof(str4), "ttl");
+ break;
+ case STSORT_SRCIP:
+ snprintf(str4, sizeof(str4), "src ip");
+ break;
+ case STSORT_SRCPT:
+ snprintf(str4, sizeof(str4), "src port");
+ break;
+ case STSORT_DSTIP:
+ snprintf(str4, sizeof(str4), "dest ip");
+ break;
+ case STSORT_DSTPT:
+ snprintf(str4, sizeof(str4), "dest port");
+ break;
+ default:
+ snprintf(str4, sizeof(str4), "unknown");
+ break;
+ }
+
+ if (reverse)
+ strcat(str4, " (reverse)");
+
+ winy += 2;
+ move(winy,0);
+ printw("Src: %s, Dest: %s, Proto: %s, Sorted by: %s\n\n",
+ str1, str2, str3, str4);
+
+ /*
+ * For an IPv4 IP address we need at most 15 characters,
+ * 4 tuples of 3 digits, separated by 3 dots. Enforce this
+ * length, so the colums do not change positions based
+ * on the size of the IP address. This length makes the
+ * output fit in a 80 column terminal.
+ * We are lacking a good solution for IPv6 addresses (that
+ * can be longer that 15 characters), so we do not enforce
+ * a maximum on the IP field size.
+ */
+ if (srclen < 15)
+ srclen = 15;
+ if (dstlen < 15)
+ dstlen = 15;
+
+ /* print column description */
+ winy += 2;
+ move(winy,0);
+ attron(A_BOLD);
+ printw("%-*s %-*s %3s %4s %7s %9s %9s\n",
+ srclen + 6, "Source IP", dstlen + 6, "Destination IP",
+ "ST", "PR", "#pkts", "#bytes", "ttl");
+ attroff(A_BOLD);
+
+ /* print all the entries */
+ tp = tstable;
+ if (reverse)
+ tp += tsentry;
+
+ if (tsentry > maxy - 6)
+ tsentry = maxy - 6;
+ for (i = 0; i <= tsentry; i++) {
+ /* print src/dest and port */
+ if ((tp->st_p == IPPROTO_TCP) ||
+ (tp->st_p == IPPROTO_UDP)) {
+ snprintf(str1, sizeof(str1), "%s,%hu",
+ getip(tp->st_v, &tp->st_src),
+ ntohs(tp->st_sport));
+ snprintf(str2, sizeof(str2), "%s,%hu",
+ getip(tp->st_v, &tp->st_dst),
+ ntohs(tp->st_dport));
+ } else {
+ snprintf(str1, sizeof(str1), "%s", getip(tp->st_v,
+ &tp->st_src));
+ snprintf(str2, sizeof(str2), "%s", getip(tp->st_v,
+ &tp->st_dst));
+ }
+ winy++;
+ move(winy, 0);
+ printw("%-*s %-*s", srclen + 6, str1, dstlen + 6, str2);
+
+ /* print state */
+ snprintf(str1, sizeof(str1), "%X/%X", tp->st_state[0],
+ tp->st_state[1]);
+ printw(" %3s", str1);
+
+ /* print protocol */
+ proto = getprotobynumber(tp->st_p);
+ if (proto) {
+ strncpy(str1, proto->p_name, 4);
+ str1[4] = '\0';
+ } else {
+ snprintf(str1, sizeof(str1), "%d", tp->st_p);
+ }
+ /* just print icmp for IPv6-ICMP */
+ if (tp->st_p == IPPROTO_ICMPV6)
+ strcpy(str1, "icmp");
+ printw(" %4s", str1);
+
+ /* print #pkt/#bytes */
+#ifdef USE_QUAD_T
+ printw(" %7qu %9qu", (unsigned long long) tp->st_pkts,
+ (unsigned long long) tp->st_bytes);
+#else
+ printw(" %7lu %9lu", tp->st_pkts, tp->st_bytes);
+#endif
+ printw(" %9s", ttl_to_string(tp->st_age));
+
+ if (reverse)
+ tp--;
+ else
+ tp++;
+ }
+
+ /* screen data structure is filled, now update the screen */
+ if (redraw)
+ clearok(stdscr,1);
+
+ if (refresh() == ERR)
+ break;
+ if (redraw) {
+ clearok(stdscr,0);
+ redraw = 0;
+ }
+
+ /* wait for key press or a 1 second time out period */
+ selecttimeout.tv_sec = refreshtime;
+ selecttimeout.tv_usec = 0;
+ FD_ZERO(&readfd);
+ FD_SET(0, &readfd);
+ select(1, &readfd, NULL, NULL, &selecttimeout);
+
+ /* if key pressed, read all waiting keys */
+ if (FD_ISSET(0, &readfd)) {
+ c = wgetch(stdscr);
+ if (c == ERR)
+ continue;
+
+ if (ISALPHA(c) && ISUPPER(c))
+ c = TOLOWER(c);
+ if (c == 'l') {
+ redraw = 1;
+ } else if (c == 'q') {
+ break;
+ } else if (c == 'r') {
+ reverse = !reverse;
+ } else if (c == 'b') {
+ forward = 0;
+ } else if (c == 'f') {
+ forward = 1;
+ } else if (c == 's') {
+ if (++sorting > STSORT_MAX)
+ sorting = 0;
+ }
+ }
+ } /* while */
+
+out:
+ printw("\n");
+ curs_set(1);
+ /* nocbreak(); XXX - endwin() should make this redundant */
+ endwin();
+
+ free(tstable);
+ if (ret != 0)
+ perror(errstr);
+}
+#endif
+
+
+/*
+ * Show fragment cache information that's held in the kernel.
+ */
+static void showfrstates(ipfrstat_t *ifsp, u_long ticks)
+{
+ struct ipfr *ipfrtab[IPFT_SIZE], ifr;
+ int i;
+
+ /*
+ * print out the numeric statistics
+ */
+ PRINTF("IP fragment states:\n%lu\tnew\n%lu\texpired\n%lu\thits\n",
+ ifsp->ifs_new, ifsp->ifs_expire, ifsp->ifs_hits);
+ PRINTF("%lu\tretrans\n%lu\ttoo short\n",
+ ifsp->ifs_retrans0, ifsp->ifs_short);
+ PRINTF("%lu\tno memory\n%lu\talready exist\n",
+ ifsp->ifs_nomem, ifsp->ifs_exists);
+ PRINTF("%lu\tinuse\n", ifsp->ifs_inuse);
+ PRINTF("\n");
+
+ if (live_kernel == 0) {
+ if (kmemcpy((char *)ipfrtab, (u_long)ifsp->ifs_table,
+ sizeof(ipfrtab)))
+ return;
+ }
+
+ /*
+ * Print out the contents (if any) of the fragment cache table.
+ */
+ if (live_kernel == 1) {
+ do {
+ if (fetchfrag(ipf_fd, IPFGENITER_FRAG, &ifr) != 0)
+ break;
+ if (ifr.ipfr_ifp == NULL)
+ break;
+ ifr.ipfr_ttl -= ticks;
+ printfraginfo("", &ifr);
+ } while (ifr.ipfr_next != NULL);
+ } else {
+ for (i = 0; i < IPFT_SIZE; i++)
+ while (ipfrtab[i] != NULL) {
+ if (kmemcpy((char *)&ifr, (u_long)ipfrtab[i],
+ sizeof(ifr)) == -1)
+ break;
+ printfraginfo("", &ifr);
+ ipfrtab[i] = ifr.ipfr_next;
+ }
+ }
+ /*
+ * Print out the contents (if any) of the NAT fragment cache table.
+ */
+
+ if (live_kernel == 0) {
+ if (kmemcpy((char *)ipfrtab, (u_long)ifsp->ifs_nattab,
+ sizeof(ipfrtab)))
+ return;
+ }
+
+ if (live_kernel == 1) {
+ do {
+ if (fetchfrag(nat_fd, IPFGENITER_NATFRAG, &ifr) != 0)
+ break;
+ if (ifr.ipfr_ifp == NULL)
+ break;
+ ifr.ipfr_ttl -= ticks;
+ printfraginfo("NAT: ", &ifr);
+ } while (ifr.ipfr_next != NULL);
+ } else {
+ for (i = 0; i < IPFT_SIZE; i++)
+ while (ipfrtab[i] != NULL) {
+ if (kmemcpy((char *)&ifr, (u_long)ipfrtab[i],
+ sizeof(ifr)) == -1)
+ break;
+ printfraginfo("NAT: ", &ifr);
+ ipfrtab[i] = ifr.ipfr_next;
+ }
+ }
+}
+
+
+/*
+ * Show stats on how auth within IPFilter has been used
+ */
+static void showauthstates(ipf_authstat_t *asp)
+{
+ frauthent_t *frap, fra;
+ ipfgeniter_t auth;
+ ipfobj_t obj;
+
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_type = IPFOBJ_GENITER;
+ obj.ipfo_size = sizeof(auth);
+ obj.ipfo_ptr = &auth;
+
+ auth.igi_type = IPFGENITER_AUTH;
+ auth.igi_nitems = 1;
+ auth.igi_data = &fra;
+
+#ifdef USE_QUAD_T
+ printf("Authorisation hits: %"PRIu64"\tmisses %"PRIu64"\n",
+ (unsigned long long) asp->fas_hits,
+ (unsigned long long) asp->fas_miss);
+#else
+ printf("Authorisation hits: %ld\tmisses %ld\n", asp->fas_hits,
+ asp->fas_miss);
+#endif
+ printf("nospace %ld\nadded %ld\nsendfail %ld\nsendok %ld\n",
+ asp->fas_nospace, asp->fas_added, asp->fas_sendfail,
+ asp->fas_sendok);
+ printf("queok %ld\nquefail %ld\nexpire %ld\n",
+ asp->fas_queok, asp->fas_quefail, asp->fas_expire);
+
+ frap = asp->fas_faelist;
+ while (frap) {
+ if (live_kernel == 1) {
+ if (ioctl(auth_fd, SIOCGENITER, &obj))
+ break;
+ } else {
+ if (kmemcpy((char *)&fra, (u_long)frap,
+ sizeof(fra)) == -1)
+ break;
+ }
+ printf("age %ld\t", fra.fae_age);
+ printfr(&fra.fae_fr, ioctl);
+ frap = fra.fae_next;
+ }
+}
+
+
+/*
+ * Display groups used for each of filter rules, accounting rules and
+ * authentication, separately.
+ */
+static void showgroups(struct friostat *fiop)
+{
+ static char *gnames[3] = { "Filter", "Accounting", "Authentication" };
+ static int gnums[3] = { IPL_LOGIPF, IPL_LOGCOUNT, IPL_LOGAUTH };
+ frgroup_t *fp, grp;
+ int on, off, i;
+
+ on = fiop->f_active;
+ off = 1 - on;
+
+ for (i = 0; i < 3; i++) {
+ printf("%s groups (active):\n", gnames[i]);
+ for (fp = fiop->f_groups[gnums[i]][on]; fp != NULL;
+ fp = grp.fg_next)
+ if (kmemcpy((char *)&grp, (u_long)fp, sizeof(grp)))
+ break;
+ else
+ printf("%s\n", grp.fg_name);
+ printf("%s groups (inactive):\n", gnames[i]);
+ for (fp = fiop->f_groups[gnums[i]][off]; fp != NULL;
+ fp = grp.fg_next)
+ if (kmemcpy((char *)&grp, (u_long)fp, sizeof(grp)))
+ break;
+ else
+ printf("%s\n", grp.fg_name);
+ }
+}
+
+
+static void parse_ipportstr(const char *argument, i6addr_t *ip, int *port)
+{
+ char *s, *comma;
+ int ok = 0;
+
+ /* make working copy of argument, Theoretically you must be able
+ * to write to optarg, but that seems very ugly to me....
+ */
+ s = strdup(argument);
+ if (s == NULL)
+ return;
+
+ /* get port */
+ if ((comma = strchr(s, ',')) != NULL) {
+ if (!strcasecmp(comma + 1, "any")) {
+ *port = -1;
+ } else if (!sscanf(comma + 1, "%d", port) ||
+ (*port < 0) || (*port > 65535)) {
+ fprintf(stderr, "Invalid port specification in %s\n",
+ argument);
+ free(s);
+ exit(-2);
+ }
+ *comma = '\0';
+ }
+
+
+ /* get ip address */
+ if (!strcasecmp(s, "any")) {
+ ip->in4.s_addr = INADDR_ANY;
+ ok = 1;
+#ifdef USE_INET6
+ ip->in6 = in6addr_any;
+ } else if (use_inet6 && !use_inet4 && inet_pton(AF_INET6, s, &ip->in6)) {
+ ok = 1;
+#endif
+ } else if (inet_aton(s, &ip->in4))
+ ok = 1;
+
+ if (ok == 0) {
+ fprintf(stderr, "Invalid IP address: %s\n", s);
+ free(s);
+ exit(-2);
+ }
+
+ /* free allocated memory */
+ free(s);
+}
+
+
+#ifdef STATETOP
+static void sig_resize(int s)
+{
+ handle_resize = 1;
+}
+
+static void sig_break(int s)
+{
+ handle_break = 1;
+}
+
+static char *getip(int v, i6addr_t *addr)
+{
+#ifdef USE_INET6
+ static char hostbuf[MAXHOSTNAMELEN+1];
+#endif
+
+ if (v == 0)
+ return ("any");
+
+ if (v == 4)
+ return (inet_ntoa(addr->in4));
+
+#ifdef USE_INET6
+ (void) inet_ntop(AF_INET6, &addr->in6, hostbuf, sizeof(hostbuf) - 1);
+ hostbuf[MAXHOSTNAMELEN] = '\0';
+ return (hostbuf);
+#else
+ return ("IPv6");
+#endif
+}
+
+
+static char *ttl_to_string(long int ttl)
+{
+ static char ttlbuf[STSTRSIZE];
+ int hours, minutes, seconds;
+
+ /* ttl is in half seconds */
+ ttl /= 2;
+
+ hours = ttl / 3600;
+ ttl = ttl % 3600;
+ minutes = ttl / 60;
+ seconds = ttl % 60;
+
+ if (hours > 0)
+ snprintf(ttlbuf, sizeof(ttlbuf), "%2d:%02d:%02d", hours, minutes, seconds);
+ else
+ snprintf(ttlbuf, sizeof(ttlbuf), "%2d:%02d", minutes, seconds);
+ return (ttlbuf);
+}
+
+
+static int sort_pkts(const void *a, const void *b)
+{
+
+ register const statetop_t *ap = a;
+ register const statetop_t *bp = b;
+
+ if (ap->st_pkts == bp->st_pkts)
+ return (0);
+ else if (ap->st_pkts < bp->st_pkts)
+ return (1);
+ return (-1);
+}
+
+
+static int sort_bytes(const void *a, const void *b)
+{
+ register const statetop_t *ap = a;
+ register const statetop_t *bp = b;
+
+ if (ap->st_bytes == bp->st_bytes)
+ return (0);
+ else if (ap->st_bytes < bp->st_bytes)
+ return (1);
+ return (-1);
+}
+
+
+static int sort_p(const void *a, const void *b)
+{
+ register const statetop_t *ap = a;
+ register const statetop_t *bp = b;
+
+ if (ap->st_p == bp->st_p)
+ return (0);
+ else if (ap->st_p < bp->st_p)
+ return (1);
+ return (-1);
+}
+
+
+static int sort_ttl(const void *a, const void *b)
+{
+ register const statetop_t *ap = a;
+ register const statetop_t *bp = b;
+
+ if (ap->st_age == bp->st_age)
+ return (0);
+ else if (ap->st_age < bp->st_age)
+ return (1);
+ return (-1);
+}
+
+static int sort_srcip(const void *a, const void *b)
+{
+ register const statetop_t *ap = a;
+ register const statetop_t *bp = b;
+
+#ifdef USE_INET6
+ if (use_inet6 && !use_inet4) {
+ if (IP6_EQ(&ap->st_src, &bp->st_src))
+ return (0);
+ else if (IP6_GT(&ap->st_src, &bp->st_src))
+ return (1);
+ } else
+#endif
+ {
+ if (ntohl(ap->st_src.in4.s_addr) ==
+ ntohl(bp->st_src.in4.s_addr))
+ return (0);
+ else if (ntohl(ap->st_src.in4.s_addr) >
+ ntohl(bp->st_src.in4.s_addr))
+ return (1);
+ }
+ return (-1);
+}
+
+static int sort_srcpt(const void *a, const void *b)
+{
+ register const statetop_t *ap = a;
+ register const statetop_t *bp = b;
+
+ if (htons(ap->st_sport) == htons(bp->st_sport))
+ return (0);
+ else if (htons(ap->st_sport) > htons(bp->st_sport))
+ return (1);
+ return (-1);
+}
+
+static int sort_dstip(const void *a, const void *b)
+{
+ register const statetop_t *ap = a;
+ register const statetop_t *bp = b;
+
+#ifdef USE_INET6
+ if (use_inet6 && !use_inet4) {
+ if (IP6_EQ(&ap->st_dst, &bp->st_dst))
+ return (0);
+ else if (IP6_GT(&ap->st_dst, &bp->st_dst))
+ return (1);
+ } else
+#endif
+ {
+ if (ntohl(ap->st_dst.in4.s_addr) ==
+ ntohl(bp->st_dst.in4.s_addr))
+ return (0);
+ else if (ntohl(ap->st_dst.in4.s_addr) >
+ ntohl(bp->st_dst.in4.s_addr))
+ return (1);
+ }
+ return (-1);
+}
+
+static int sort_dstpt(const void *a, const void *b)
+{
+ register const statetop_t *ap = a;
+ register const statetop_t *bp = b;
+
+ if (htons(ap->st_dport) == htons(bp->st_dport))
+ return (0);
+ else if (htons(ap->st_dport) > htons(bp->st_dport))
+ return (1);
+ return (-1);
+}
+
+#endif
+
+
+ipstate_t *fetchstate(ipstate_t *src, ipstate_t *dst)
+{
+
+ if (live_kernel == 1) {
+ ipfgeniter_t state;
+ ipfobj_t obj;
+
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_type = IPFOBJ_GENITER;
+ obj.ipfo_size = sizeof(state);
+ obj.ipfo_ptr = &state;
+
+ state.igi_type = IPFGENITER_STATE;
+ state.igi_nitems = 1;
+ state.igi_data = dst;
+
+ if (ioctl(state_fd, SIOCGENITER, &obj) != 0)
+ return (NULL);
+ if (dst->is_next == NULL) {
+ int n = IPFGENITER_STATE;
+ (void) ioctl(ipf_fd,SIOCIPFDELTOK, &n);
+ }
+ } else {
+ if (kmemcpy((char *)dst, (u_long)src, sizeof(*dst)))
+ return (NULL);
+ }
+ return (dst);
+}
+
+
+static int fetchfrag( int fd, int type, ipfr_t *frp)
+{
+ ipfgeniter_t frag;
+ ipfobj_t obj;
+
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_type = IPFOBJ_GENITER;
+ obj.ipfo_size = sizeof(frag);
+ obj.ipfo_ptr = &frag;
+
+ frag.igi_type = type;
+ frag.igi_nitems = 1;
+ frag.igi_data = frp;
+
+ if (ioctl(fd, SIOCGENITER, &obj))
+ return (EFAULT);
+ return (0);
+}
+
+
+static int state_matcharray(ipstate_t *stp, int *array)
+{
+ int i, n, *x, rv, p;
+ ipfexp_t *e;
+
+ rv = 0;
+
+ for (n = array[0], x = array + 1; n > 0; x += e->ipfe_size) {
+ e = (ipfexp_t *)x;
+ if (e->ipfe_cmd == IPF_EXP_END)
+ break;
+ n -= e->ipfe_size;
+
+ rv = 0;
+ /*
+ * The upper 16 bits currently store the protocol value.
+ * This is currently used with TCP and UDP port compares and
+ * allows "tcp.port = 80" without requiring an explicit
+ " "ip.pr = tcp" first.
+ */
+ p = e->ipfe_cmd >> 16;
+ if ((p != 0) && (p != stp->is_p))
+ break;
+
+ switch (e->ipfe_cmd)
+ {
+ case IPF_EXP_IP_PR :
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= (stp->is_p == e->ipfe_arg0[i]);
+ }
+ break;
+
+ case IPF_EXP_IP_SRCADDR :
+ if (stp->is_v != 4)
+ break;
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= ((stp->is_saddr &
+ e->ipfe_arg0[i * 2 + 1]) ==
+ e->ipfe_arg0[i * 2]);
+ }
+ break;
+
+ case IPF_EXP_IP_DSTADDR :
+ if (stp->is_v != 4)
+ break;
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= ((stp->is_daddr &
+ e->ipfe_arg0[i * 2 + 1]) ==
+ e->ipfe_arg0[i * 2]);
+ }
+ break;
+
+ case IPF_EXP_IP_ADDR :
+ if (stp->is_v != 4)
+ break;
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= ((stp->is_saddr &
+ e->ipfe_arg0[i * 2 + 1]) ==
+ e->ipfe_arg0[i * 2]) ||
+ ((stp->is_daddr &
+ e->ipfe_arg0[i * 2 + 1]) ==
+ e->ipfe_arg0[i * 2]);
+ }
+ break;
+
+#ifdef USE_INET6
+ case IPF_EXP_IP6_SRCADDR :
+ if (stp->is_v != 6)
+ break;
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= IP6_MASKEQ(&stp->is_src,
+ &e->ipfe_arg0[i * 8 + 4],
+ &e->ipfe_arg0[i * 8]);
+ }
+ break;
+
+ case IPF_EXP_IP6_DSTADDR :
+ if (stp->is_v != 6)
+ break;
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= IP6_MASKEQ(&stp->is_dst,
+ &e->ipfe_arg0[i * 8 + 4],
+ &e->ipfe_arg0[i * 8]);
+ }
+ break;
+
+ case IPF_EXP_IP6_ADDR :
+ if (stp->is_v != 6)
+ break;
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= IP6_MASKEQ(&stp->is_src,
+ &e->ipfe_arg0[i * 8 + 4],
+ &e->ipfe_arg0[i * 8]) ||
+ IP6_MASKEQ(&stp->is_dst,
+ &e->ipfe_arg0[i * 8 + 4],
+ &e->ipfe_arg0[i * 8]);
+ }
+ break;
+#endif
+
+ case IPF_EXP_UDP_PORT :
+ case IPF_EXP_TCP_PORT :
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= (stp->is_sport == e->ipfe_arg0[i]) ||
+ (stp->is_dport == e->ipfe_arg0[i]);
+ }
+ break;
+
+ case IPF_EXP_UDP_SPORT :
+ case IPF_EXP_TCP_SPORT :
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= (stp->is_sport == e->ipfe_arg0[i]);
+ }
+ break;
+
+ case IPF_EXP_UDP_DPORT :
+ case IPF_EXP_TCP_DPORT :
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= (stp->is_dport == e->ipfe_arg0[i]);
+ }
+ break;
+
+ case IPF_EXP_IDLE_GT :
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= (stp->is_die < e->ipfe_arg0[i]);
+ }
+ break;
+
+ case IPF_EXP_TCP_STATE :
+ for (i = 0; !rv && i < e->ipfe_narg; i++) {
+ rv |= (stp->is_state[0] == e->ipfe_arg0[i]) ||
+ (stp->is_state[1] == e->ipfe_arg0[i]);
+ }
+ break;
+ }
+ rv ^= e->ipfe_not;
+
+ if (rv == 0)
+ break;
+ }
+
+ return (rv);
+}
+
+
+static void showtqtable_live(int fd)
+{
+ ipftq_t table[IPF_TCP_NSTATES];
+ ipfobj_t obj;
+
+ bzero((char *)&obj, sizeof(obj));
+ obj.ipfo_rev = IPFILTER_VERSION;
+ obj.ipfo_size = sizeof(table);
+ obj.ipfo_ptr = (void *)table;
+ obj.ipfo_type = IPFOBJ_STATETQTAB;
+
+ if (ioctl(fd, SIOCGTQTAB, &obj) == 0) {
+ printtqtable(table);
+ }
+}
diff --git a/sbin/ipf/ipfsync/ipfsyncd.c b/sbin/ipf/ipfsync/ipfsyncd.c
new file mode 100644
index 000000000000..ead92b70371c
--- /dev/null
+++ b/sbin/ipf/ipfsync/ipfsyncd.c
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) 2012 by Darren Reed.
+ *
+ * See the IPFILTER.LICENCE file for details on licencing.
+ */
+#if !defined(lint)
+static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed";
+static const char rcsid[] = "@(#)$Id: ipfsyncd.c,v 1.1.2.2 2012/07/22 08:04:24 darren_r Exp $";
+#endif
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/sockio.h>
+#include <sys/errno.h>
+
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+
+#include "ipf.h"
+#include "opts.h"
+
+
+#define R_IO_ERROR -1
+#define R_OKAY 0
+#define R_MORE 1
+#define R_SKIP 2
+#if defined(sun) && !defined(SOLARIS2)
+# define STRERROR(x) sys_errlist[x]
+extern char *sys_errlist[];
+#else
+# define STRERROR(x) strerror(x)
+#endif
+
+
+int main(int, char *[]);
+void usage(char *);
+void printsynchdr(synchdr_t *);
+void printtable(int);
+void printsmcproto(char *);
+void printcommand(int);
+int do_kbuff(int, char *, int *);
+int do_packet(int, char *);
+int buildsocket(char *, struct sockaddr_in *);
+void do_io(void);
+void handleterm(int);
+
+int terminate = 0;
+int igmpfd = -1;
+int nfd = -1;
+int lfd = -1;
+int opts = 0;
+
+void
+usage(progname)
+ char *progname;
+{
+ fprintf(stderr,
+ "Usage: %s [-d] [-p port] [-i address] -I <interface>\n",
+ progname);
+}
+
+void
+handleterm(sig)
+ int sig;
+{
+ terminate = sig;
+}
+
+
+/* should be large enough to hold header + any datatype */
+#define BUFFERLEN 1400
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct sockaddr_in sin;
+ char *interface;
+ char *progname;
+ int opt, tries;
+
+ progname = strrchr(argv[0], '/');
+ if (progname) {
+ progname++;
+ } else {
+ progname = argv[0];
+ }
+
+ opts = 0;
+ tries = 0;
+ interface = NULL;
+
+ bzero((char *)&sin, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(0xaf6c);
+ sin.sin_addr.s_addr = htonl(INADDR_UNSPEC_GROUP | 0x697066);
+
+ while ((opt = getopt(argc, argv, "di:I:p:")) != -1)
+ switch (opt)
+ {
+ case 'd' :
+ debuglevel++;
+ break;
+ case 'I' :
+ interface = optarg;
+ break;
+ case 'i' :
+ sin.sin_addr.s_addr = inet_addr(optarg);
+ break;
+ case 'p' :
+ sin.sin_port = htons(atoi(optarg));
+ break;
+ }
+
+ if (interface == NULL) {
+ usage(progname);
+ exit(1);
+ }
+
+ if (!debuglevel) {
+
+#ifdef BSD
+ daemon(0, 0);
+#else
+ int fd = open("/dev/null", O_RDWR);
+
+ switch (fork())
+ {
+ case 0 :
+ break;
+
+ case -1 :
+ fprintf(stderr, "%s: fork() failed: %s\n",
+ argv[0], STRERROR(errno));
+ exit(1);
+ /* NOTREACHED */
+
+ default :
+ exit(0);
+ /* NOTREACHED */
+ }
+
+ dup2(fd, 0);
+ dup2(fd, 1);
+ dup2(fd, 2);
+ close(fd);
+
+ setsid();
+#endif
+ }
+
+ signal(SIGHUP, handleterm);
+ signal(SIGINT, handleterm);
+ signal(SIGTERM, handleterm);
+
+ openlog(progname, LOG_PID, LOG_SECURITY);
+
+ while (!terminate) {
+ if (lfd != -1) {
+ close(lfd);
+ lfd = -1;
+ }
+ if (nfd != -1) {
+ close(nfd);
+ nfd = -1;
+ }
+ if (igmpfd != -1) {
+ close(igmpfd);
+ igmpfd = -1;
+ }
+
+ if (buildsocket(interface, &sin) == -1)
+ goto tryagain;
+
+ lfd = open(IPSYNC_NAME, O_RDWR);
+ if (lfd == -1) {
+ syslog(LOG_ERR, "open(%s):%m", IPSYNC_NAME);
+ debug(1, "open(%s): %s\n", IPSYNC_NAME,
+ STRERROR(errno));
+ goto tryagain;
+ }
+
+ tries = -1;
+ do_io();
+tryagain:
+ tries++;
+ syslog(LOG_INFO, "retry in %d seconds", 1 << tries);
+ debug(1, "wait %d seconds\n", 1 << tries);
+ sleep(1 << tries);
+ }
+
+
+ /* terminate */
+ if (lfd != -1)
+ close(lfd);
+ if (nfd != -1)
+ close(nfd);
+
+ syslog(LOG_ERR, "signal %d received, exiting...", terminate);
+ debug(1, "signal %d received, exiting...", terminate);
+
+ exit(1);
+}
+
+
+void
+do_io()
+{
+ char nbuff[BUFFERLEN];
+ char buff[BUFFERLEN];
+ fd_set mrd, rd;
+ int maxfd;
+ int inbuf;
+ int n1;
+ int left;
+
+ FD_ZERO(&mrd);
+ FD_SET(lfd, &mrd);
+ FD_SET(nfd, &mrd);
+ maxfd = nfd;
+ if (lfd > maxfd)
+ maxfd = lfd;
+ debug(2, "nfd %d lfd %d maxfd %d\n", nfd, lfd, maxfd);
+
+ inbuf = 0;
+ /*
+ * A threaded approach to this loop would have one thread
+ * work on reading lfd (only) all the time and another thread
+ * working on reading nfd all the time.
+ */
+ while (!terminate) {
+ int n;
+
+ rd = mrd;
+
+ n = select(maxfd + 1, &rd, NULL, NULL, NULL);
+ if (n < 0) {
+ switch (errno)
+ {
+ case EINTR :
+ continue;
+ default :
+ syslog(LOG_ERR, "select error: %m");
+ debug(1, "select error: %s\n", STRERROR(errno));
+ return;
+ }
+ }
+
+ if (FD_ISSET(lfd, &rd)) {
+ n1 = read(lfd, buff+inbuf, BUFFERLEN-inbuf);
+
+ debug(3, "read(K):%d\n", n1);
+
+ if (n1 <= 0) {
+ syslog(LOG_ERR, "read error (k-header): %m");
+ debug(1, "read error (k-header): %s\n",
+ STRERROR(errno));
+ return;
+ }
+
+ left = 0;
+
+ switch (do_kbuff(n1, buff, &left))
+ {
+ case R_IO_ERROR :
+ return;
+ case R_MORE :
+ inbuf += left;
+ break;
+ default :
+ inbuf = 0;
+ break;
+ }
+ }
+
+ if (FD_ISSET(nfd, &rd)) {
+ n1 = recv(nfd, nbuff, sizeof(nbuff), 0);
+
+ debug(3, "read(N):%d\n", n1);
+
+ if (n1 <= 0) {
+ syslog(LOG_ERR, "read error (n-header): %m");
+ debug(1, "read error (n-header): %s\n",
+ STRERROR(errno));
+ return;
+ }
+
+ switch (do_packet(n1, nbuff))
+ {
+ case R_IO_ERROR :
+ return;
+ default :
+ break;
+ }
+ }
+ }
+}
+
+
+int
+buildsocket(nicname, sinp)
+ char *nicname;
+ struct sockaddr_in *sinp;
+{
+ struct sockaddr_in *reqip;
+ struct ifreq req;
+ char opt;
+
+ debug(2, "binding to %s:%s\n", nicname, inet_ntoa(sinp->sin_addr));
+
+ if (IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) {
+ struct in_addr addr;
+ struct ip_mreq mreq;
+
+ igmpfd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
+ if (igmpfd == -1) {
+ syslog(LOG_ERR, "socket:%m");
+ debug(1, "socket:%s\n", STRERROR(errno));
+ return -1;
+ }
+
+ bzero((char *)&req, sizeof(req));
+ strncpy(req.ifr_name, nicname, sizeof(req.ifr_name));
+ req.ifr_name[sizeof(req.ifr_name) - 1] = '\0';
+ if (ioctl(igmpfd, SIOCGIFADDR, &req) == -1) {
+ syslog(LOG_ERR, "ioctl(SIOCGIFADDR):%m");
+ debug(1, "ioctl(SIOCGIFADDR):%s\n", STRERROR(errno));
+ close(igmpfd);
+ igmpfd = -1;
+ return -1;
+ }
+ reqip = (struct sockaddr_in *)&req.ifr_addr;
+
+ addr = reqip->sin_addr;
+ if (setsockopt(igmpfd, IPPROTO_IP, IP_MULTICAST_IF,
+ (char *)&addr, sizeof(addr)) == -1) {
+ syslog(LOG_ERR, "setsockopt(IP_MULTICAST_IF(%s)):%m",
+ inet_ntoa(addr));
+ debug(1, "setsockopt(IP_MULTICAST_IF(%s)):%s\n",
+ inet_ntoa(addr), STRERROR(errno));
+ close(igmpfd);
+ igmpfd = -1;
+ return -1;
+ }
+
+ opt = 0;
+ if (setsockopt(igmpfd, IPPROTO_IP, IP_MULTICAST_LOOP,
+ (char *)&opt, sizeof(opt)) == -1) {
+ syslog(LOG_ERR, "setsockopt(IP_MULTICAST_LOOP=0):%m");
+ debug(1, "setsockopt(IP_MULTICAST_LOOP=0):%s\n",
+ STRERROR(errno));
+ close(igmpfd);
+ igmpfd = -1;
+ return -1;
+ }
+
+ opt = 63;
+ if (setsockopt(igmpfd, IPPROTO_IP, IP_MULTICAST_TTL,
+ (char *)&opt, sizeof(opt)) == -1) {
+ syslog(LOG_ERR, "setsockopt(IP_MULTICAST_TTL=%d):%m",
+ opt);
+ debug(1, "setsockopt(IP_MULTICAST_TTL=%d):%s\n", opt,
+ STRERROR(errno));
+ close(igmpfd);
+ igmpfd = -1;
+ return -1;
+ }
+
+ mreq.imr_multiaddr.s_addr = sinp->sin_addr.s_addr;
+ mreq.imr_interface.s_addr = reqip->sin_addr.s_addr;
+
+ if (setsockopt(igmpfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq)) == -1) {
+ char buffer[80];
+
+ snprintf(buffer, sizeof(buffer), "%s,", inet_ntoa(sinp->sin_addr));
+ strcat(buffer, inet_ntoa(reqip->sin_addr));
+
+ syslog(LOG_ERR,
+ "setsockpt(IP_ADD_MEMBERSHIP,%s):%m", buffer);
+ debug(1, "setsockpt(IP_ADD_MEMBERSHIP,%s):%s\n",
+ buffer, STRERROR(errno));
+ close(igmpfd);
+ igmpfd = -1;
+ return -1;
+ }
+ }
+ nfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (nfd == -1) {
+ syslog(LOG_ERR, "socket:%m");
+ if (igmpfd != -1) {
+ close(igmpfd);
+ igmpfd = -1;
+ }
+ return -1;
+ }
+ bzero((char *)&req, sizeof(req));
+ strncpy(req.ifr_name, nicname, sizeof(req.ifr_name));
+ req.ifr_name[sizeof(req.ifr_name) - 1] = '\0';
+ if (ioctl(nfd, SIOCGIFADDR, &req) == -1) {
+ syslog(LOG_ERR, "ioctl(SIOCGIFADDR):%m");
+ debug(1, "ioctl(SIOCGIFADDR):%s\n", STRERROR(errno));
+ close(igmpfd);
+ igmpfd = -1;
+ return -1;
+ }
+
+ if (bind(nfd, (struct sockaddr *)&req.ifr_addr,
+ sizeof(req.ifr_addr)) == -1) {
+ syslog(LOG_ERR, "bind:%m");
+ debug(1, "bind:%s\n", STRERROR(errno));
+ close(nfd);
+ if (igmpfd != -1) {
+ close(igmpfd);
+ igmpfd = -1;
+ }
+ nfd = -1;
+ return -1;
+ }
+
+ if (connect(nfd, (struct sockaddr *)sinp, sizeof(*sinp)) == -1) {
+ syslog(LOG_ERR, "connect:%m");
+ debug(1, "connect:%s\n", S