aboutsummaryrefslogtreecommitdiff
path: root/sbin
diff options
context:
space:
mode:
Diffstat (limited to 'sbin')
-rw-r--r--sbin/Makefile2
-rw-r--r--sbin/Makefile.amd641
-rw-r--r--sbin/Makefile.i3861
-rw-r--r--sbin/adjkerntz/adjkerntz.c5
-rw-r--r--sbin/camcontrol/Makefile2
-rw-r--r--sbin/camcontrol/camcontrol.811
-rw-r--r--sbin/camcontrol/camcontrol.c262
-rw-r--r--sbin/camcontrol/camcontrol.h7
-rw-r--r--sbin/camcontrol/fwdownload.c215
-rw-r--r--sbin/camcontrol/modeedit.c2
-rw-r--r--sbin/camcontrol/progress.c186
-rw-r--r--sbin/camcontrol/progress.h60
-rw-r--r--sbin/ccdconfig/ccdconfig.c9
-rw-r--r--sbin/ddb/ddb.c2
-rw-r--r--sbin/devd/devd.cc4
-rw-r--r--sbin/devd/devd.conf.52
-rw-r--r--sbin/devfs/devfs.812
-rw-r--r--sbin/dhclient/dhclient.c11
-rw-r--r--sbin/dhclient/dhcpd.h1
-rw-r--r--sbin/dump/traverse.c11
-rw-r--r--sbin/dumpfs/dumpfs.c7
-rw-r--r--sbin/dumpon/dumpon.810
-rw-r--r--sbin/dumpon/dumpon.c43
-rw-r--r--sbin/etherswitchcfg/etherswitchcfg.c4
-rw-r--r--sbin/etherswitchcfg/ifmedia.c4
-rw-r--r--sbin/fsck/Makefile1
-rw-r--r--sbin/fsck/fsck.c31
-rw-r--r--sbin/fsck/fsutil.c33
-rw-r--r--sbin/fsck/fsutil.h11
-rw-r--r--sbin/fsck/preen.c16
-rw-r--r--sbin/fsck_ffs/fsck.h2
-rw-r--r--sbin/fsck_ffs/fsutil.c11
-rw-r--r--sbin/fsck_ffs/gjournal.c6
-rw-r--r--sbin/fsck_ffs/inode.c11
-rw-r--r--sbin/fsck_ffs/main.c57
-rw-r--r--sbin/fsck_ffs/pass1.c8
-rw-r--r--sbin/fsck_ffs/pass2.c20
-rw-r--r--sbin/fsck_ffs/pass4.c6
-rw-r--r--sbin/fsck_ffs/suj.c153
-rw-r--r--sbin/fsck_msdosfs/Makefile1
-rw-r--r--sbin/fsck_msdosfs/boot.c16
-rw-r--r--sbin/fsck_msdosfs/check.c5
-rw-r--r--sbin/fsck_msdosfs/dir.c33
-rw-r--r--sbin/fsck_msdosfs/ext.h2
-rw-r--r--sbin/fsck_msdosfs/fat.c36
-rw-r--r--sbin/fsdb/fsdb.c26
-rw-r--r--sbin/fsdb/fsdbutil.c13
-rw-r--r--sbin/fsirand/fsirand.c4
-rw-r--r--sbin/geom/class/eli/geli.8228
-rw-r--r--sbin/geom/class/multipath/geom_multipath.c26
-rw-r--r--sbin/geom/class/multipath/gmultipath.89
-rw-r--r--sbin/geom/class/part/gpart.85
-rw-r--r--sbin/geom/class/raid/graid.88
-rw-r--r--sbin/geom/class/sched/gsched.814
-rw-r--r--sbin/geom/class/virstor/gvirstor.89
-rw-r--r--sbin/geom/core/geom.c2
-rw-r--r--sbin/ggate/ggatec/ggatec.c2
-rw-r--r--sbin/ggate/ggated/ggated.c6
-rw-r--r--sbin/ggate/ggatel/ggatel.c2
-rw-r--r--sbin/ggate/shared/ggate.h4
-rw-r--r--sbin/growfs/growfs.c9
-rw-r--r--sbin/gvinum/Makefile2
-rw-r--r--sbin/hastd/hast.conf.58
-rw-r--r--sbin/hastd/primary.c83
-rw-r--r--sbin/hastd/proto_common.c2
-rw-r--r--sbin/hastd/synch.h2
-rw-r--r--sbin/ifconfig/af_inet6.c2
-rw-r--r--sbin/ifconfig/ifconfig.810
-rw-r--r--sbin/ifconfig/ifconfig.c4
-rw-r--r--sbin/ipf/ipf/Makefile4
-rw-r--r--sbin/ipfw/dummynet.c14
-rw-r--r--sbin/ipfw/ipfw.8142
-rw-r--r--sbin/ipfw/ipfw2.c8
-rw-r--r--sbin/ipfw/ipv6.c54
-rw-r--r--sbin/ipfw/nat.c8
-rw-r--r--sbin/md5/Makefile6
-rw-r--r--sbin/md5/md5.118
-rw-r--r--sbin/md5/md5.c22
-rw-r--r--sbin/mdconfig/Makefile1
-rw-r--r--sbin/mdconfig/mdconfig.811
-rw-r--r--sbin/mdconfig/mdconfig.c55
-rw-r--r--sbin/mount/getmntopts.34
-rw-r--r--sbin/mount/mount.818
-rw-r--r--sbin/mount/mount.c4
-rw-r--r--sbin/mount_fusefs/Makefile33
-rw-r--r--sbin/mount_fusefs/mount_fusefs.8363
-rw-r--r--sbin/mount_fusefs/mount_fusefs.c499
-rw-r--r--sbin/natd/natd.812
-rw-r--r--sbin/natd/natd.c4
-rw-r--r--sbin/newfs/mkfs.c4
-rw-r--r--sbin/newfs/newfs.c2
-rw-r--r--sbin/newfs/newfs.h1
-rw-r--r--sbin/nvmecontrol/Makefile6
-rw-r--r--sbin/nvmecontrol/nvmecontrol.886
-rw-r--r--sbin/nvmecontrol/nvmecontrol.c600
-rw-r--r--sbin/pfctl/Makefile14
-rw-r--r--sbin/pfctl/missing/altq/altq.h204
-rw-r--r--sbin/pfctl/missing/altq/altq_cbq.h232
-rw-r--r--sbin/pfctl/missing/altq/altq_classq.h203
-rw-r--r--sbin/pfctl/missing/altq/altq_hfsc.h325
-rw-r--r--sbin/pfctl/missing/altq/altq_priq.h172
-rw-r--r--sbin/pfctl/missing/altq/altq_red.h199
-rw-r--r--sbin/pfctl/missing/altq/altq_rio.h145
-rw-r--r--sbin/pfctl/missing/altq/altq_rmclass.h263
-rw-r--r--sbin/pfctl/missing/altq/altq_rmclass_debug.h113
-rw-r--r--sbin/pfctl/missing/altq/altq_var.h267
-rw-r--r--sbin/pfctl/missing/altq/altq_wfq.h129
-rw-r--r--sbin/pfctl/parse.y6038
-rw-r--r--sbin/pfctl/pf_print_state.c362
-rw-r--r--sbin/pfctl/pfctl.8687
-rw-r--r--sbin/pfctl/pfctl.c2391
-rw-r--r--sbin/pfctl/pfctl.h130
-rw-r--r--sbin/pfctl/pfctl_altq.c1258
-rw-r--r--sbin/pfctl/pfctl_optimize.c1655
-rw-r--r--sbin/pfctl/pfctl_osfp.c1108
-rw-r--r--sbin/pfctl/pfctl_parser.c1746
-rw-r--r--sbin/pfctl/pfctl_parser.h305
-rw-r--r--sbin/pfctl/pfctl_qstats.c449
-rw-r--r--sbin/pfctl/pfctl_radix.c585
-rw-r--r--sbin/pfctl/pfctl_table.c634
-rw-r--r--sbin/ping/ping.c5
-rw-r--r--sbin/ping6/ping6.c6
-rw-r--r--sbin/quotacheck/quotacheck.c10
-rw-r--r--sbin/rcorder/rcorder.c2
-rw-r--r--sbin/restore/dirs.c14
-rw-r--r--sbin/restore/interactive.c4
-rw-r--r--sbin/restore/restore.c17
-rw-r--r--sbin/restore/symtab.c7
-rw-r--r--sbin/restore/tape.c34
-rw-r--r--sbin/setkey/setkey.84
-rw-r--r--sbin/shutdown/shutdown.c6
-rw-r--r--sbin/tunefs/tunefs.c31
132 files changed, 20539 insertions, 3007 deletions
diff --git a/sbin/Makefile b/sbin/Makefile
index fcdb5672c53d..16d006da790c 100644
--- a/sbin/Makefile
+++ b/sbin/Makefile
@@ -49,9 +49,9 @@ SUBDIR=adjkerntz \
mksnap_ffs \
mount \
mount_cd9660 \
+ mount_fusefs \
mount_msdosfs \
mount_nfs \
- mount_ntfs \
mount_nullfs \
mount_udf \
mount_unionfs \
diff --git a/sbin/Makefile.amd64 b/sbin/Makefile.amd64
index 2d231b0cb2b0..d3cf350ee2cc 100644
--- a/sbin/Makefile.amd64
+++ b/sbin/Makefile.amd64
@@ -2,3 +2,4 @@
SUBDIR += bsdlabel
SUBDIR += fdisk
+SUBDIR += nvmecontrol
diff --git a/sbin/Makefile.i386 b/sbin/Makefile.i386
index 4135c44082c9..0ced312832f8 100644
--- a/sbin/Makefile.i386
+++ b/sbin/Makefile.i386
@@ -2,4 +2,5 @@
SUBDIR += bsdlabel
SUBDIR += fdisk
+SUBDIR += nvmecontrol
SUBDIR += sconfig
diff --git a/sbin/adjkerntz/adjkerntz.c b/sbin/adjkerntz/adjkerntz.c
index 35d8da9d6c51..c42379d71915 100644
--- a/sbin/adjkerntz/adjkerntz.c
+++ b/sbin/adjkerntz/adjkerntz.c
@@ -315,10 +315,7 @@ recalculate:
* restoring disrtcset, since we don't clean up
* anything.
*/
- if (gettimeofday(&tv, (struct timezone *)NULL)) {
- syslog(LOG_ERR, "gettimeofday: %m");
- return 1;
- }
+ (void)gettimeofday(&tv, NULL);
tv.tv_sec += diff;
stv = &tv;
}
diff --git a/sbin/camcontrol/Makefile b/sbin/camcontrol/Makefile
index 0cbde1226e33..68633757c3df 100644
--- a/sbin/camcontrol/Makefile
+++ b/sbin/camcontrol/Makefile
@@ -3,7 +3,7 @@
PROG= camcontrol
SRCS= camcontrol.c util.c
.if !defined(RELEASE_CRUNCH)
-SRCS+= fwdownload.c modeedit.c
+SRCS+= fwdownload.c modeedit.c progress.c
.else
CFLAGS+= -DMINIMALISTIC
.endif
diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8
index fdcec66cec60..66a45eb7af1d 100644
--- a/sbin/camcontrol/camcontrol.8
+++ b/sbin/camcontrol/camcontrol.8
@@ -27,7 +27,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd November 24, 2011
+.Dd June 4, 2012
.Dt CAMCONTROL 8
.Os
.Sh NAME
@@ -175,6 +175,7 @@
.Op Fl S
.Op Fl X
.Op Fl c
+.Op Fl p
.Aq all|off|bus Ns Op :target Ns Op :lun
.Nm
.Ic tags
@@ -496,6 +497,8 @@ is specified,
.Nm
will print out the number of defects given in the READ DEFECT DATA header
returned from the drive.
+Some drives will report 0 defects if neither the primary or grown defect
+lists are requested.
.It Ic modepage
Allows the user to display and optionally edit a SCSI mode page.
The mode
@@ -737,7 +740,7 @@ Set the partial pathway timeout value, in microseconds.
See the
.Tn ANSI
.Tn SAS
-Protcol Layer (SPL)
+Protocol Layer (SPL)
specification for more information on this field.
.It Fl a Ar enable|disable
Enable or disable SATA slumber phy power conditions.
@@ -796,6 +799,8 @@ Enable CAM_DEBUG_XPT printfs.
Enable CAM_DEBUG_CDB printfs.
This will cause the kernel to print out the
SCSI CDBs sent to the specified device(s).
+.It Fl p
+Enable CAM_DEBUG_PROBE printfs.
.It all
Enable debugging for all devices.
.It off
@@ -1106,7 +1111,7 @@ Do not ask for confirmation.
Run in simulation mode.
Packet sizes that will be sent are shown, but no actual packet is sent to the
device.
-No confimation is asked in simulation mode.
+No confirmation is asked in simulation mode.
.It Fl v
Besides showing sense information in case of a failure, the verbose option
causes
diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c
index e24fff1f13ef..a3c7f0515c9d 100644
--- a/sbin/camcontrol/camcontrol.c
+++ b/sbin/camcontrol/camcontrol.c
@@ -123,6 +123,7 @@ typedef enum {
CAM_ARG_DEBUG_CDB = 0x08000000,
CAM_ARG_DEBUG_XPT = 0x10000000,
CAM_ARG_DEBUG_PERIPH = 0x20000000,
+ CAM_ARG_DEBUG_PROBE = 0x40000000,
} cam_argmask;
struct camcontrol_opts {
@@ -176,7 +177,7 @@ static struct camcontrol_opts option_table[] = {
{"tags", CAM_CMD_TAG, CAM_ARG_NONE, "N:q"},
{"negotiate", CAM_CMD_RATE, CAM_ARG_NONE, negotiate_opts},
{"rate", CAM_CMD_RATE, CAM_ARG_NONE, negotiate_opts},
- {"debug", CAM_CMD_DEBUG, CAM_ARG_NONE, "IPTSXc"},
+ {"debug", CAM_CMD_DEBUG, CAM_ARG_NONE, "IPTSXcp"},
{"format", CAM_CMD_FORMAT, CAM_ARG_NONE, "qrwy"},
{"idle", CAM_CMD_IDLE, CAM_ARG_NONE, "t:"},
{"standby", CAM_CMD_STANDBY, CAM_ARG_NONE, "t:"},
@@ -1025,11 +1026,11 @@ camxferrate(struct cam_device *device)
if (sas->valid & CTS_SAS_VALID_SPEED)
speed = sas->bitrate;
} else if (ccb->cts.transport == XPORT_ATA) {
- struct ccb_trans_settings_ata *ata =
+ struct ccb_trans_settings_pata *pata =
&ccb->cts.xport_specific.ata;
- if (ata->valid & CTS_ATA_VALID_MODE)
- speed = ata_mode2speed(ata->mode);
+ if (pata->valid & CTS_ATA_VALID_MODE)
+ speed = ata_mode2speed(pata->mode);
} else if (ccb->cts.transport == XPORT_SATA) {
struct ccb_trans_settings_sata *sata =
&ccb->cts.xport_specific.sata;
@@ -1072,16 +1073,16 @@ camxferrate(struct cam_device *device)
fprintf(stdout, ")");
}
} else if (ccb->cts.transport == XPORT_ATA) {
- struct ccb_trans_settings_ata *ata =
+ struct ccb_trans_settings_pata *pata =
&ccb->cts.xport_specific.ata;
printf(" (");
- if (ata->valid & CTS_ATA_VALID_MODE)
- printf("%s, ", ata_mode2string(ata->mode));
- if ((ata->valid & CTS_ATA_VALID_ATAPI) && ata->atapi != 0)
- printf("ATAPI %dbytes, ", ata->atapi);
- if (ata->valid & CTS_ATA_VALID_BYTECOUNT)
- printf("PIO %dbytes", ata->bytecount);
+ if (pata->valid & CTS_ATA_VALID_MODE)
+ printf("%s, ", ata_mode2string(pata->mode));
+ if ((pata->valid & CTS_ATA_VALID_ATAPI) && pata->atapi != 0)
+ printf("ATAPI %dbytes, ", pata->atapi);
+ if (pata->valid & CTS_ATA_VALID_BYTECOUNT)
+ printf("PIO %dbytes", pata->bytecount);
printf(")");
} else if (ccb->cts.transport == XPORT_SATA) {
struct ccb_trans_settings_sata *sata =
@@ -1798,13 +1799,14 @@ readdefects(struct cam_device *device, int argc, char **argv,
union ccb *ccb = NULL;
struct scsi_read_defect_data_10 *rdd_cdb;
u_int8_t *defect_list = NULL;
- u_int32_t dlist_length = 65000;
+ u_int32_t max_dlist_length = SRDD10_MAX_LENGTH, dlist_length = 0;
u_int32_t returned_length = 0;
u_int32_t num_returned = 0;
u_int8_t returned_format;
unsigned int i;
int c, error = 0;
- int lists_specified = 0;
+ int lists_specified;
+ int get_length = 1;
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch(c){
@@ -1841,20 +1843,33 @@ readdefects(struct cam_device *device, int argc, char **argv,
ccb = cam_getccb(device);
/*
- * Hopefully 65000 bytes is enough to hold the defect list. If it
- * isn't, the disk is probably dead already. We'd have to go with
- * 12 byte command (i.e. alloc_length is 32 bits instead of 16)
- * to hold them all.
+ * Eventually we should probably support the 12 byte READ DEFECT
+ * DATA command. It supports a longer parameter list, which may be
+ * necessary on newer drives with lots of defects. According to
+ * the SBC-3 spec, drives are supposed to return an illegal request
+ * if they have more defect data than will fit in 64K.
*/
- defect_list = malloc(dlist_length);
+ defect_list = malloc(max_dlist_length);
if (defect_list == NULL) {
warnx("can't malloc memory for defect list");
error = 1;
goto defect_bailout;
}
+ /*
+ * We start off asking for just the header to determine how much
+ * defect data is available. Some Hitachi drives return an error
+ * if you ask for more data than the drive has. Once we know the
+ * length, we retry the command with the returned length.
+ */
+ dlist_length = sizeof(struct scsi_read_defect_data_hdr_10);
+
rdd_cdb =(struct scsi_read_defect_data_10 *)&ccb->csio.cdb_io.cdb_bytes;
+retry:
+
+ lists_specified = 0;
+
/*
* cam_getccb() zeros the CCB header only. So we need to zero the
* payload portion of the ccb.
@@ -1916,6 +1931,51 @@ readdefects(struct cam_device *device, int argc, char **argv,
returned_length = scsi_2btoul(((struct
scsi_read_defect_data_hdr_10 *)defect_list)->length);
+ if (get_length != 0) {
+ get_length = 0;
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) ==
+ CAM_SCSI_STATUS_ERROR) {
+ struct scsi_sense_data *sense;
+ int error_code, sense_key, asc, ascq;
+
+ sense = &ccb->csio.sense_data;
+ scsi_extract_sense_len(sense, ccb->csio.sense_len -
+ ccb->csio.sense_resid, &error_code, &sense_key,
+ &asc, &ascq, /*show_errors*/ 1);
+
+ /*
+ * If the drive is reporting that it just doesn't
+ * support the defect list format, go ahead and use
+ * the length it reported. Otherwise, the length
+ * may not be valid, so use the maximum.
+ */
+ if ((sense_key == SSD_KEY_RECOVERED_ERROR)
+ && (asc == 0x1c) && (ascq == 0x00)
+ && (returned_length > 0)) {
+ dlist_length = returned_length +
+ sizeof(struct scsi_read_defect_data_hdr_10);
+ dlist_length = min(dlist_length,
+ SRDD10_MAX_LENGTH);
+ } else
+ dlist_length = max_dlist_length;
+ } else if ((ccb->ccb_h.status & CAM_STATUS_MASK) !=
+ CAM_REQ_CMP){
+ error = 1;
+ warnx("Error reading defect header");
+ if (arglist & CAM_ARG_VERBOSE)
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ goto defect_bailout;
+ } else {
+ dlist_length = returned_length +
+ sizeof(struct scsi_read_defect_data_hdr_10);
+ dlist_length = min(dlist_length, SRDD10_MAX_LENGTH);
+ }
+
+ goto retry;
+ }
+
returned_format = ((struct scsi_read_defect_data_hdr_10 *)
defect_list)->format;
@@ -2640,6 +2700,10 @@ camdebug(int argc, char **argv, char *combinedopt)
arglist |= CAM_ARG_DEBUG_CDB;
ccb.cdbg.flags |= CAM_DEBUG_CDB;
break;
+ case 'p':
+ arglist |= CAM_ARG_DEBUG_PROBE;
+ ccb.cdbg.flags |= CAM_DEBUG_PROBE;
+ break;
default:
break;
}
@@ -2669,7 +2733,7 @@ camdebug(int argc, char **argv, char *combinedopt)
ccb.cdbg.flags = CAM_DEBUG_NONE;
arglist &= ~(CAM_ARG_DEBUG_INFO|CAM_ARG_DEBUG_PERIPH|
CAM_ARG_DEBUG_TRACE|CAM_ARG_DEBUG_SUBTRACE|
- CAM_ARG_DEBUG_XPT);
+ CAM_ARG_DEBUG_XPT|CAM_ARG_DEBUG_PROBE);
} else if (strncmp(tstr, "all", 3) != 0) {
tmpstr = (char *)strtok(tstr, ":");
if ((tmpstr != NULL) && (*tmpstr != '\0')){
@@ -2891,21 +2955,45 @@ cts_print(struct cam_device *device, struct ccb_trans_settings *cts)
"enabled" : "disabled");
}
}
+ if (cts->transport == XPORT_FC) {
+ struct ccb_trans_settings_fc *fc =
+ &cts->xport_specific.fc;
+
+ if (fc->valid & CTS_FC_VALID_WWNN)
+ fprintf(stdout, "%sWWNN: 0x%llx", pathstr,
+ (long long) fc->wwnn);
+ if (fc->valid & CTS_FC_VALID_WWPN)
+ fprintf(stdout, "%sWWPN: 0x%llx", pathstr,
+ (long long) fc->wwpn);
+ if (fc->valid & CTS_FC_VALID_PORT)
+ fprintf(stdout, "%sPortID: 0x%x", pathstr, fc->port);
+ if (fc->valid & CTS_FC_VALID_SPEED)
+ fprintf(stdout, "%stransfer speed: %d.%03dMB/s\n",
+ pathstr, fc->bitrate / 1000, fc->bitrate % 1000);
+ }
+ if (cts->transport == XPORT_SAS) {
+ struct ccb_trans_settings_sas *sas =
+ &cts->xport_specific.sas;
+
+ if (sas->valid & CTS_SAS_VALID_SPEED)
+ fprintf(stdout, "%stransfer speed: %d.%03dMB/s\n",
+ pathstr, sas->bitrate / 1000, sas->bitrate % 1000);
+ }
if (cts->transport == XPORT_ATA) {
- struct ccb_trans_settings_ata *ata =
+ struct ccb_trans_settings_pata *pata =
&cts->xport_specific.ata;
- if ((ata->valid & CTS_ATA_VALID_MODE) != 0) {
+ if ((pata->valid & CTS_ATA_VALID_MODE) != 0) {
fprintf(stdout, "%sATA mode: %s\n", pathstr,
- ata_mode2string(ata->mode));
+ ata_mode2string(pata->mode));
}
- if ((ata->valid & CTS_ATA_VALID_ATAPI) != 0) {
+ if ((pata->valid & CTS_ATA_VALID_ATAPI) != 0) {
fprintf(stdout, "%sATAPI packet length: %d\n", pathstr,
- ata->atapi);
+ pata->atapi);
}
- if ((ata->valid & CTS_ATA_VALID_BYTECOUNT) != 0) {
+ if ((pata->valid & CTS_ATA_VALID_BYTECOUNT) != 0) {
fprintf(stdout, "%sPIO transaction length: %d\n",
- pathstr, ata->bytecount);
+ pathstr, pata->bytecount);
}
}
if (cts->transport == XPORT_SATA) {
@@ -2941,12 +3029,22 @@ cts_print(struct cam_device *device, struct ccb_trans_settings *cts)
sata->caps);
}
}
+ if (cts->protocol == PROTO_ATA) {
+ struct ccb_trans_settings_ata *ata=
+ &cts->proto_specific.ata;
+
+ if (ata->valid & CTS_ATA_VALID_TQ) {
+ fprintf(stdout, "%stagged queueing: %s\n", pathstr,
+ (ata->flags & CTS_ATA_FLAGS_TAG_ENB) ?
+ "enabled" : "disabled");
+ }
+ }
if (cts->protocol == PROTO_SCSI) {
struct ccb_trans_settings_scsi *scsi=
&cts->proto_specific.scsi;
if (scsi->valid & CTS_SCSI_VALID_TQ) {
- fprintf(stdout, "%stagged queueing is %s\n", pathstr,
+ fprintf(stdout, "%stagged queueing: %s\n", pathstr,
(scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) ?
"enabled" : "disabled");
}
@@ -3032,6 +3130,26 @@ get_cgd_bailout:
return(retval);
}
+/* return the type of disk (really the command type) */
+static const char *
+get_disk_type(struct cam_device *device)
+{
+ struct ccb_getdev cgd;
+
+ (void) memset(&cgd, 0x0, sizeof(cgd));
+ get_cgd(device, &cgd);
+ switch(cgd.protocol) {
+ case PROTO_SCSI:
+ return "scsi";
+ case PROTO_ATA:
+ case PROTO_ATAPI:
+ case PROTO_SATAPM:
+ return "ata";
+ default:
+ return "unknown";
+ }
+}
+
static void
cpi_print(struct ccb_pathinq *cpi)
{
@@ -3384,16 +3502,19 @@ ratecontrol(struct cam_device *device, int retry_count, int timeout,
if (change_settings) {
int didsettings = 0;
struct ccb_trans_settings_spi *spi = NULL;
- struct ccb_trans_settings_ata *ata = NULL;
+ struct ccb_trans_settings_pata *pata = NULL;
struct ccb_trans_settings_sata *sata = NULL;
+ struct ccb_trans_settings_ata *ata = NULL;
struct ccb_trans_settings_scsi *scsi = NULL;
if (ccb->cts.transport == XPORT_SPI)
spi = &ccb->cts.xport_specific.spi;
if (ccb->cts.transport == XPORT_ATA)
- ata = &ccb->cts.xport_specific.ata;
+ pata = &ccb->cts.xport_specific.ata;
if (ccb->cts.transport == XPORT_SATA)
sata = &ccb->cts.xport_specific.sata;
+ if (ccb->cts.protocol == PROTO_ATA)
+ ata = &ccb->cts.proto_specific.ata;
if (ccb->cts.protocol == PROTO_SCSI)
scsi = &ccb->cts.proto_specific.scsi;
ccb->cts.xport_specific.valid = 0;
@@ -3406,19 +3527,28 @@ ratecontrol(struct cam_device *device, int retry_count, int timeout,
spi->flags |= CTS_SPI_FLAGS_DISC_ENB;
didsettings++;
}
- if (scsi && tag_enable != -1) {
+ if (tag_enable != -1) {
if ((cpi.hba_inquiry & PI_TAG_ABLE) == 0) {
warnx("HBA does not support tagged queueing, "
"so you cannot modify tag settings");
retval = 1;
goto ratecontrol_bailout;
}
- scsi->valid |= CTS_SCSI_VALID_TQ;
- if (tag_enable == 0)
- scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB;
- else
- scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB;
- didsettings++;
+ if (ata) {
+ ata->valid |= CTS_SCSI_VALID_TQ;
+ if (tag_enable == 0)
+ ata->flags &= ~CTS_ATA_FLAGS_TAG_ENB;
+ else
+ ata->flags |= CTS_ATA_FLAGS_TAG_ENB;
+ didsettings++;
+ } else if (scsi) {
+ scsi->valid |= CTS_SCSI_VALID_TQ;
+ if (tag_enable == 0)
+ scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB;
+ else
+ scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB;
+ didsettings++;
+ }
}
if (spi && offset != -1) {
if ((cpi.hba_inquiry & PI_SDTR_ABLE) == 0) {
@@ -3465,6 +3595,12 @@ ratecontrol(struct cam_device *device, int retry_count, int timeout,
retval = 1;
goto ratecontrol_bailout;
}
+ if (!user_settings) {
+ warnx("You can modify only user rate "
+ "settings for SATA");
+ retval = 1;
+ goto ratecontrol_bailout;
+ }
sata->revision = ata_speed2revision(syncrate * 100);
if (sata->revision < 0) {
warnx("Invalid rate %f", syncrate);
@@ -3474,16 +3610,22 @@ ratecontrol(struct cam_device *device, int retry_count, int timeout,
sata->valid |= CTS_SATA_VALID_REVISION;
didsettings++;
}
- if ((ata || sata) && mode != -1) {
+ if ((pata || sata) && mode != -1) {
if ((cpi.hba_inquiry & PI_SDTR_ABLE) == 0) {
warnx("HBA is not capable of changing "
"transfer rates");
retval = 1;
goto ratecontrol_bailout;
}
- if (ata) {
- ata->mode = mode;
- ata->valid |= CTS_ATA_VALID_MODE;
+ if (!user_settings) {
+ warnx("You can modify only user mode "
+ "settings for ATA/SATA");
+ retval = 1;
+ goto ratecontrol_bailout;
+ }
+ if (pata) {
+ pata->mode = mode;
+ pata->valid |= CTS_ATA_VALID_MODE;
} else {
sata->mode = mode;
sata->valid |= CTS_SATA_VALID_MODE;
@@ -3530,11 +3672,6 @@ ratecontrol(struct cam_device *device, int retry_count, int timeout,
if (didsettings == 0) {
goto ratecontrol_bailout;
}
- if (!user_settings && (ata || sata)) {
- warnx("You can modify only user settings for ATA/SATA");
- retval = 1;
- goto ratecontrol_bailout;
- }
ccb->ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
if (cam_send_ccb(device, ccb) < 0) {
perror("error sending XPT_SET_TRAN_SETTINGS CCB");
@@ -3566,13 +3703,10 @@ ratecontrol(struct cam_device *device, int retry_count, int timeout,
fprintf(stderr, "Test Unit Ready failed\n");
goto ratecontrol_bailout;
}
- /*
- * If the user wants things quiet, there's no sense in
- * getting the transfer settings, if we're not going
- * to print them.
- */
- if (quiet != 0)
- goto ratecontrol_bailout;
+ }
+ if ((change_settings || send_tur) && !quiet &&
+ (ccb->cts.transport == XPORT_ATA ||
+ ccb->cts.transport == XPORT_SATA || send_tur)) {
fprintf(stdout, "New parameters:\n");
retval = get_print_cts(device, user_settings, 0, NULL);
}
@@ -4330,7 +4464,7 @@ static int
smpcmd(struct cam_device *device, int argc, char **argv, char *combinedopt,
int retry_count, int timeout)
{
- int c, error;
+ int c, error = 0;
union ccb *ccb;
uint8_t *smp_request = NULL, *smp_response = NULL;
int request_size = 0, response_size = 0;
@@ -4624,7 +4758,10 @@ try_long:
smp_report_general_sbuf(response, sizeof(*response), sb);
- sbuf_finish(sb);
+ if (sbuf_finish(sb) != 0) {
+ warnx("%s: sbuf_finish", __func__);
+ goto bailout;
+ }
printf("%s", sbuf_data(sb));
@@ -4995,7 +5132,10 @@ smpmaninfo(struct cam_device *device, int argc, char **argv,
smp_report_manuf_info_sbuf(&response, sizeof(response), sb);
- sbuf_finish(sb);
+ if (sbuf_finish(sb) != 0) {
+ warnx("%s: sbuf_finish", __func__);
+ goto bailout;
+ }
printf("%s", sbuf_data(sb));
@@ -5325,6 +5465,7 @@ smpphylist(struct cam_device *device, int argc, char **argv,
bzero(&(&ccb->ccb_h)[1],
sizeof(union ccb) - sizeof(struct ccb_hdr));
+ STAILQ_INIT(&devlist.dev_queue);
rgrequest = malloc(sizeof(*rgrequest));
if (rgrequest == NULL) {
@@ -5393,7 +5534,6 @@ smpphylist(struct cam_device *device, int argc, char **argv,
goto bailout;
}
- STAILQ_INIT(&devlist.dev_queue);
devlist.path_id = device->path_id;
retval = buildbusdevlist(&devlist);
@@ -5643,9 +5783,10 @@ bailout:
#endif /* MINIMALISTIC */
void
-usage(int verbose)
+usage(int printlong)
{
- fprintf(verbose ? stdout : stderr,
+
+ fprintf(printlong ? stdout : stderr,
"usage: camcontrol <command> [device id][generic args][command args]\n"
" camcontrol devlist [-v]\n"
#ifndef MINIMALISTIC
@@ -5694,7 +5835,7 @@ usage(int verbose)
" camcontrol fwdownload [dev_id][generic args] <-f fw_image> [-y][-s]\n"
#endif /* MINIMALISTIC */
" camcontrol help\n");
- if (!verbose)
+ if (!printlong)
return;
#ifndef MINIMALISTIC
fprintf(stdout,
@@ -6142,7 +6283,8 @@ main(int argc, char **argv)
break;
case CAM_CMD_DOWNLOAD_FW:
error = fwdownload(cam_dev, argc, argv, combinedopt,
- arglist & CAM_ARG_VERBOSE, retry_count, timeout);
+ arglist & CAM_ARG_VERBOSE, retry_count, timeout,
+ get_disk_type(cam_dev));
break;
#endif /* MINIMALISTIC */
case CAM_CMD_USAGE:
diff --git a/sbin/camcontrol/camcontrol.h b/sbin/camcontrol/camcontrol.h
index 189d74c856d7..616236a8b762 100644
--- a/sbin/camcontrol/camcontrol.h
+++ b/sbin/camcontrol/camcontrol.h
@@ -40,8 +40,11 @@ struct get_hook
int got;
};
+extern int verbose;
+
int fwdownload(struct cam_device *device, int argc, char **argv,
- char *combinedopt, int verbose, int retry_count, int timeout);
+ char *combinedopt, int printerrors, int retry_count, int timeout,
+ const char */*type*/);
void mode_sense(struct cam_device *device, int mode_page, int page_control,
int dbd, int retry_count, int timeout, u_int8_t *data,
int datalen);
@@ -57,5 +60,5 @@ char *cget(void *hook, char *name);
int iget(void *hook, char *name);
void arg_put(void *hook, int letter, void *arg, int count, char *name);
int get_confirmation(void);
-void usage(int verbose);
+void usage(int printlong);
#endif /* _CAMCONTROL_H */
diff --git a/sbin/camcontrol/fwdownload.c b/sbin/camcontrol/fwdownload.c
index abb3726fa297..daa1520eb471 100644
--- a/sbin/camcontrol/fwdownload.c
+++ b/sbin/camcontrol/fwdownload.c
@@ -64,6 +64,8 @@ __FBSDID("$FreeBSD$");
#include <cam/scsi/scsi_message.h>
#include <camlib.h>
+#include "progress.h"
+
#include "camcontrol.h"
#define CMD_TIMEOUT 50000 /* 50 seconds */
@@ -73,6 +75,7 @@ typedef enum {
VENDOR_HP,
VENDOR_IBM,
VENDOR_PLEXTOR,
+ VENDOR_QUALSTAR,
VENDOR_QUANTUM,
VENDOR_SEAGATE,
VENDOR_UNKNOWN
@@ -93,17 +96,43 @@ static const struct fw_vendor vendors_list[] = {
{VENDOR_HP, "HP", 0x8000, 0x07, 0x07, 0, 1},
{VENDOR_IBM, "IBM", 0x8000, 0x05, 0x05, 1, 0},
{VENDOR_PLEXTOR, "PLEXTOR", 0x2000, 0x04, 0x05, 0, 1},
+ {VENDOR_QUALSTAR, "QUALSTAR", 0x2030, 0x05, 0x05, 0, 0},
{VENDOR_QUANTUM, "QUANTUM", 0x2000, 0x04, 0x05, 0, 1},
{VENDOR_SEAGATE, "SEAGATE", 0x8000, 0x07, 0x07, 0, 1},
+ /* the next 2 are SATA disks going through SAS HBA */
+ {VENDOR_SEAGATE, "ATA ST", 0x8000, 0x07, 0x07, 0, 1},
+ {VENDOR_HITACHI, "ATA HDS", 0x8000, 0x05, 0x05, 1, 0},
{VENDOR_UNKNOWN, NULL, 0x0000, 0x00, 0x00, 0, 0}
};
+#ifndef ATA_DOWNLOAD_MICROCODE
+#define ATA_DOWNLOAD_MICROCODE 0x92
+#endif
+
+#define USE_OFFSETS_FEATURE 0x3
+
+#ifndef LOW_SECTOR_SIZE
+#define LOW_SECTOR_SIZE 512
+#endif
+
+#define ATA_MAKE_LBA(o, p) \
+ ((((((o) / LOW_SECTOR_SIZE) >> 8) & 0xff) << 16) | \
+ ((((o) / LOW_SECTOR_SIZE) & 0xff) << 8) | \
+ ((((p) / LOW_SECTOR_SIZE) >> 8) & 0xff))
+
+#define ATA_MAKE_SECTORS(p) (((p) / 512) & 0xff)
+
+#ifndef UNKNOWN_MAX_PKT_SIZE
+#define UNKNOWN_MAX_PKT_SIZE 0x8000
+#endif
+
static const struct fw_vendor *fw_get_vendor(struct cam_device *cam_dev);
static char *fw_read_img(const char *fw_img_path,
const struct fw_vendor *vp, int *num_bytes);
static int fw_download_img(struct cam_device *cam_dev,
const struct fw_vendor *vp, char *buf, int img_size,
- int sim_mode, int verbose, int retry_count, int timeout);
+ int sim_mode, int printerrors, int retry_count, int timeout,
+ const char */*name*/, const char */*type*/);
/*
* Find entry in vendors list that belongs to
@@ -173,6 +202,9 @@ fw_read_img(const char *fw_img_path, const struct fw_vendor *vp, int *num_bytes)
(img_size % 512 == 80))
skip_bytes = 80;
break;
+ case VENDOR_QUALSTAR:
+ skip_bytes = img_size % 1030;
+ break;
default:
break;
}
@@ -206,28 +238,59 @@ bailout1:
*/
static int
fw_download_img(struct cam_device *cam_dev, const struct fw_vendor *vp,
- char *buf, int img_size, int sim_mode, int verbose, int retry_count,
- int timeout)
+ char *buf, int img_size, int sim_mode, int printerrors, int retry_count,
+ int timeout, const char *imgname, const char *type)
{
struct scsi_write_buffer cdb;
+ progress_t progress;
+ int size;
union ccb *ccb;
int pkt_count = 0;
+ int max_pkt_size;
u_int32_t pkt_size = 0;
char *pkt_ptr = buf;
u_int32_t offset;
int last_pkt = 0;
+ int16_t *ptr;
if ((ccb = cam_getccb(cam_dev)) == NULL) {
warnx("Could not allocate CCB");
return (1);
}
- scsi_test_unit_ready(&ccb->csio, 0, NULL, MSG_SIMPLE_Q_TAG,
- SSD_FULL_SIZE, 5000);
+ if (strcmp(type, "scsi") == 0) {
+ scsi_test_unit_ready(&ccb->csio, 0, NULL, MSG_SIMPLE_Q_TAG,
+ SSD_FULL_SIZE, 5000);
+ } else if (strcmp(type, "ata") == 0) {
+ /* cam_getccb cleans up the header, caller has to zero the payload */
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_ataio) - sizeof(struct ccb_hdr));
+
+ ptr = (uint16_t *)malloc(sizeof(struct ata_params));
+
+ if (ptr == NULL) {
+ cam_freeccb(ccb);
+ warnx("can't malloc memory for identify\n");
+ return(1);
+ }
+ bzero(ptr, sizeof(struct ata_params));
+ cam_fill_ataio(&ccb->ataio,
+ 1,
+ NULL,
+ /*flags*/CAM_DIR_IN,
+ MSG_SIMPLE_Q_TAG,
+ /*data_ptr*/(uint8_t *)ptr,
+ /*dxfer_len*/sizeof(struct ata_params),
+ timeout ? timeout : 30 * 1000);
+ ata_28bit_cmd(&ccb->ataio, ATA_ATA_IDENTIFY, 0, 0, 0);
+ } else {
+ warnx("weird disk type '%s'", type);
+ return 1;
+ }
/* Disable freezing the device queue. */
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
if (cam_send_ccb(cam_dev, ccb) < 0) {
- warnx("Error sending test unit ready");
- if (verbose)
+ warnx("Error sending identify/test unit ready");
+ if (printerrors)
cam_error_print(cam_dev, ccb, CAM_ESF_ALL,
CAM_EPF_ALL, stderr);
cam_freeccb(ccb);
@@ -235,89 +298,108 @@ fw_download_img(struct cam_device *cam_dev, const struct fw_vendor *vp,
}
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
warnx("Device is not ready");
- if (verbose)
+ if (printerrors)
cam_error_print(cam_dev, ccb, CAM_ESF_ALL,
CAM_EPF_ALL, stderr);
cam_freeccb(ccb);
return (1);
}
- pkt_size = vp->max_pkt_size;
- if (verbose || sim_mode) {
- fprintf(stdout,
- "--------------------------------------------------\n");
- fprintf(stdout,
- "PktNo. PktSize BytesRemaining LastPkt\n");
- fprintf(stdout,
- "--------------------------------------------------\n");
+ max_pkt_size = vp->max_pkt_size;
+ if (vp->max_pkt_size == 0 && strcmp(type, "ata") == 0) {
+ max_pkt_size = UNKNOWN_MAX_PKT_SIZE;
}
+ pkt_size = vp->max_pkt_size;
+ progress_init(&progress, imgname, size = img_size);
/* Download single fw packets. */
do {
- if (img_size <= vp->max_pkt_size) {
+ if (img_size <= max_pkt_size) {
last_pkt = 1;
pkt_size = img_size;
}
- if (verbose || sim_mode)
- fprintf(stdout, "%3u %5u (0x%05X) %7u (0x%06X) "
- "%d\n", pkt_count, pkt_size, pkt_size,
- img_size - pkt_size, img_size - pkt_size,
- last_pkt);
+ progress_update(&progress, size - img_size);
+ progress_draw(&progress);
bzero(&cdb, sizeof(cdb));
- cdb.opcode = WRITE_BUFFER;
- cdb.control = 0;
- /* Parameter list length. */
- scsi_ulto3b(pkt_size, &cdb.length[0]);
- offset = vp->inc_cdb_offset ? (pkt_ptr - buf) : 0;
- scsi_ulto3b(offset, &cdb.offset[0]);
- cdb.byte2 = last_pkt ? vp->cdb_byte2_last : vp->cdb_byte2;
- cdb.buffer_id = vp->inc_cdb_buffer_id ? pkt_count : 0;
- /* Zero out payload of ccb union after ccb header. */
- bzero((u_char *)ccb + sizeof(struct ccb_hdr),
- sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
- /* Copy previously constructed cdb into ccb_scsiio struct. */
- bcopy(&cdb, &ccb->csio.cdb_io.cdb_bytes[0],
- sizeof(struct scsi_write_buffer));
- /* Fill rest of ccb_scsiio struct. */
+ if (strcmp(type, "scsi") == 0) {
+ cdb.opcode = WRITE_BUFFER;
+ cdb.control = 0;
+ /* Parameter list length. */
+ scsi_ulto3b(pkt_size, &cdb.length[0]);
+ offset = vp->inc_cdb_offset ? (pkt_ptr - buf) : 0;
+ scsi_ulto3b(offset, &cdb.offset[0]);
+ cdb.byte2 = last_pkt ? vp->cdb_byte2_last : vp->cdb_byte2;
+ cdb.buffer_id = vp->inc_cdb_buffer_id ? pkt_count : 0;
+ /* Zero out payload of ccb union after ccb header. */
+ bzero((u_char *)ccb + sizeof(struct ccb_hdr),
+ sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+ /* Copy previously constructed cdb into ccb_scsiio struct. */
+ bcopy(&cdb, &ccb->csio.cdb_io.cdb_bytes[0],
+ sizeof(struct scsi_write_buffer));
+ /* Fill rest of ccb_scsiio struct. */
+ if (!sim_mode) {
+ cam_fill_csio(&ccb->csio, /* ccb_scsiio */
+ retry_count, /* retries */
+ NULL, /* cbfcnp */
+ CAM_DIR_OUT | CAM_DEV_QFRZDIS, /* flags */
+ CAM_TAG_ACTION_NONE, /* tag_action */
+ (u_char *)pkt_ptr, /* data_ptr */
+ pkt_size, /* dxfer_len */
+ SSD_FULL_SIZE, /* sense_len */
+ sizeof(struct scsi_write_buffer), /* cdb_len */
+ timeout ? timeout : CMD_TIMEOUT); /* timeout */
+ }
+ } else if (strcmp(type, "ata") == 0) {
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_ataio) - sizeof(struct ccb_hdr));
+ if (!sim_mode) {
+ uint32_t off;
+
+ cam_fill_ataio(&ccb->ataio,
+ (last_pkt) ? 256 : retry_count,
+ NULL,
+ /*flags*/CAM_DIR_OUT | CAM_DEV_QFRZDIS,
+ CAM_TAG_ACTION_NONE,
+ /*data_ptr*/(uint8_t *)pkt_ptr,
+ /*dxfer_len*/pkt_size,
+ timeout ? timeout : 30 * 1000);
+ off = (uint32_t)(pkt_ptr - buf);
+ ata_28bit_cmd(&ccb->ataio, ATA_DOWNLOAD_MICROCODE,
+ USE_OFFSETS_FEATURE,
+ ATA_MAKE_LBA(off, pkt_size),
+ ATA_MAKE_SECTORS(pkt_size));
+ }
+ }
if (!sim_mode) {
- cam_fill_csio(&ccb->csio, /* ccb_scsiio */
- retry_count, /* retries */
- NULL, /* cbfcnp */
- CAM_DIR_OUT | CAM_DEV_QFRZDIS, /* flags */
- CAM_TAG_ACTION_NONE, /* tag_action */
- (u_char *)pkt_ptr, /* data_ptr */
- pkt_size, /* dxfer_len */
- SSD_FULL_SIZE, /* sense_len */
- sizeof(struct scsi_write_buffer), /* cdb_len */
- timeout ? timeout : CMD_TIMEOUT); /* timeout */
/* Execute the command. */
if (cam_send_ccb(cam_dev, ccb) < 0) {
warnx("Error writing image to device");
- if (verbose)
+ if (printerrors)
cam_error_print(cam_dev, ccb, CAM_ESF_ALL,
- CAM_EPF_ALL, stderr);
+ CAM_EPF_ALL, stderr);
goto bailout;
}
+ if (ccb->ataio.res.status != 0 /*&& !last_pkt*/) {
+ cam_error_print(cam_dev, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
}
/* Prepare next round. */
pkt_count++;
pkt_ptr += pkt_size;
img_size -= pkt_size;
} while(!last_pkt);
- if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
- if (verbose)
- cam_error_print(cam_dev, ccb, CAM_ESF_ALL,
- CAM_EPF_ALL, stderr);
- goto bailout;
- }
+ progress_complete(&progress, size - img_size);
cam_freeccb(ccb);
return (0);
bailout:
+ progress_complete(&progress, size - img_size);
cam_freeccb(ccb);
return (1);
}
int
fwdownload(struct cam_device *device, int argc, char **argv,
- char *combinedopt, int verbose, int retry_count, int timeout)
+ char *combinedopt, int printerrors, int retry_count, int timeout,
+ const char *type)
{
const struct fw_vendor *vp;
char *fw_img_path = NULL;
@@ -345,12 +427,13 @@ fwdownload(struct cam_device *device, int argc, char **argv,
}
if (fw_img_path == NULL)
- errx(1,
- "you must specify a firmware image file using -f option");
+ errx(1, "you must specify a firmware image file using -f option");
vp = fw_get_vendor(device);
- if (vp == NULL || vp->type == VENDOR_UNKNOWN)
- errx(1, "Unsupported device");
+ if (vp == NULL)
+ errx(1, "NULL vendor");
+ if (vp->type == VENDOR_UNKNOWN)
+ warnx("Unsupported device - flashing through an HBA?");
buf = fw_read_img(fw_img_path, vp, &img_size);
if (buf == NULL)
@@ -360,11 +443,6 @@ fwdownload(struct cam_device *device, int argc, char **argv,
fprintf(stdout, "You are about to download firmware image (%s)"
" into the following device:\n",
fw_img_path);
- if (scsidoinquiry(device, argc, argv, combinedopt, 0,
- 5000) != 0) {
- warnx("Error sending inquiry");
- goto fail;
- }
fprintf(stdout, "\nIt may damage your drive. ");
if (!get_confirmation())
goto fail;
@@ -372,11 +450,12 @@ fwdownload(struct cam_device *device, int argc, char **argv,
if (sim_mode)
fprintf(stdout, "Running in simulation mode\n");
- if (fw_download_img(device, vp, buf, img_size, sim_mode, verbose,
- retry_count, timeout) != 0) {
+ if (fw_download_img(device, vp, buf, img_size, sim_mode, printerrors,
+ retry_count, timeout, fw_img_path, type) != 0) {
fprintf(stderr, "Firmware download failed\n");
goto fail;
- } else
+ }
+ else
fprintf(stdout, "Firmware download successful\n");
free(buf);
diff --git a/sbin/camcontrol/modeedit.c b/sbin/camcontrol/modeedit.c
index 6a628e897911..8504208cfcc4 100644
--- a/sbin/camcontrol/modeedit.c
+++ b/sbin/camcontrol/modeedit.c
@@ -48,8 +48,6 @@ __FBSDID("$FreeBSD$");
#include <camlib.h>
#include "camcontrol.h"
-int verbose = 0;
-
#define DEFAULT_SCSI_MODE_DB "/usr/share/misc/scsi_modes"
#define DEFAULT_EDITOR "vi"
#define MAX_FORMAT_SPEC 4096 /* Max CDB format specifier. */
diff --git a/sbin/camcontrol/progress.c b/sbin/camcontrol/progress.c
new file mode 100644
index 000000000000..dc6109f48806
--- /dev/null
+++ b/sbin/camcontrol/progress.c
@@ -0,0 +1,186 @@
+/* $NetBSD: progressbar.c,v 1.21 2009/04/12 10:18:52 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1997-2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "progress.h"
+
+static const char * const suffixes[] = {
+ "", /* 2^0 (byte) */
+ "KiB", /* 2^10 Kibibyte */
+ "MiB", /* 2^20 Mebibyte */
+ "GiB", /* 2^30 Gibibyte */
+ "TiB", /* 2^40 Tebibyte */
+ "PiB", /* 2^50 Pebibyte */
+ "EiB", /* 2^60 Exbibyte */
+};
+
+#define NSUFFIXES (sizeof(suffixes) / sizeof(suffixes[0]))
+#define SECSPERHOUR (60 * 60)
+#define DEFAULT_TTYWIDTH 80
+
+/* initialise progress meter structure */
+int
+progress_init(progress_t *prog, const char *prefix, uint64_t total)
+{
+ struct winsize winsize;
+ int oerrno = errno;
+
+ (void) memset(prog, 0x0, sizeof(*prog));
+ prog->size = total;
+ prog->prefix = strdup(prefix);
+ prog->start = time(NULL);
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) != -1 &&
+ winsize.ws_col != 0) {
+ prog->ttywidth = winsize.ws_col;
+ } else {
+ prog->ttywidth = DEFAULT_TTYWIDTH;
+ }
+ errno = oerrno;
+ return 1;
+}
+
+/* update the values in the progress meter */
+int
+progress_update(progress_t *prog, uint64_t done)
+{
+ prog->done = done;
+ prog->percent = (prog->done * 100) / prog->size;
+ prog->now = time(NULL);
+ prog->elapsed = prog->now - prog->start;
+ if (done == 0 || prog->elapsed == 0 || prog->done / prog->elapsed == 0) {
+ prog->eta = 0;
+ } else {
+ prog->eta = prog->size / (prog->done / prog->elapsed) - prog->elapsed;
+ }
+ return 1;
+}
+
+/* update the values in the progress meter */
+int
+progress_reset_size(progress_t *prog, uint64_t size)
+{
+ prog->size = size;
+ return 1;
+}
+
+/* make it look pretty at the end - display done bytes (usually total) */
+int
+progress_complete(progress_t *prog, uint64_t done)
+{
+ progress_update(prog, done);
+ progress_draw(prog);
+ printf("\n");
+ return 1;
+}
+
+/* draw the progress meter */
+int
+progress_draw(progress_t *prog)
+{
+#define BAROVERHEAD 45 /* non `*' portion of progress bar */
+ /*
+ * stars should contain at least
+ * sizeof(buf) - BAROVERHEAD entries
+ */
+ static const char stars[] =
+"*****************************************************************************"
+"*****************************************************************************"
+"*****************************************************************************";
+ unsigned bytesabbrev;
+ unsigned bpsabbrev;
+ int64_t secs;
+ uint64_t bytespersec;
+ uint64_t abbrevsize;
+ int64_t secsleft;
+ size_t barlength;
+ size_t starc;
+ char hours[12];
+ char buf[256];
+ int len;
+
+ barlength = MIN(sizeof(buf) - 1, (unsigned)prog->ttywidth) - BAROVERHEAD - strlen(prog->prefix);
+ starc = (barlength * prog->percent) / 100;
+ abbrevsize = prog->done;
+ for (bytesabbrev = 0; abbrevsize >= 100000 && bytesabbrev < NSUFFIXES; bytesabbrev++) {
+ abbrevsize >>= 10;
+ }
+ if (bytesabbrev == NSUFFIXES) {
+ bytesabbrev--;
+ }
+ bytespersec = 0;
+ if (prog->done > 0) {
+ bytespersec = prog->done;
+ if (prog->elapsed > 0) {
+ bytespersec /= prog->elapsed;
+ }
+ }
+ for (bpsabbrev = 1; bytespersec >= 1024000 && bpsabbrev < NSUFFIXES; bpsabbrev++) {
+ bytespersec >>= 10;
+ }
+ if (prog->done == 0 || prog->elapsed <= 0 || prog->done > prog->size) {
+ secsleft = 0;
+ } else {
+ secsleft = prog->eta;
+ }
+ if ((secs = secsleft / SECSPERHOUR) > 0) {
+ (void) snprintf(hours, sizeof(hours), "%2lld:", (long long)secs);
+ } else {
+ (void) snprintf(hours, sizeof(hours), " ");
+ }
+ secs = secsleft % SECSPERHOUR;
+ len = snprintf(buf, sizeof(buf),
+ "\r%s %3lld%% |%.*s%*s| %5lld %-3s %3lld.%02d %.2sB/s %s%02d:%02d ETA",
+ (prog->prefix) ? prog->prefix : "",
+ (long long)prog->percent,
+ (int)starc, stars, (int)(barlength - starc), "",
+ (long long)abbrevsize,
+ suffixes[bytesabbrev],
+ (long long)(bytespersec / 1024),
+ (int)((bytespersec % 1024) * 100 / 1024),
+ suffixes[bpsabbrev],
+ hours,
+ (int)secs / 60, (int)secs % 60);
+ return (int)write(STDOUT_FILENO, buf, len);
+}
diff --git a/sbin/camcontrol/progress.h b/sbin/camcontrol/progress.h
new file mode 100644
index 000000000000..a457c328eb2b
--- /dev/null
+++ b/sbin/camcontrol/progress.h
@@ -0,0 +1,60 @@
+/* $NetBSD: progressbar.c,v 1.21 2009/04/12 10:18:52 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1997-2012 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef PROGRESS_H_
+#define PROGRESS_H_ 20100228
+
+#include <sys/types.h>
+
+#include <inttypes.h>
+
+/* structure used to display a progress meter */
+typedef struct progress_t {
+ char *prefix; /* any prefix explanation */
+ uint64_t size; /* total of bytes/units to be counted */
+ uint64_t done; /* number of units counted to date */
+ uint64_t percent; /* cache the percentage complete */
+ time_t start; /* time we started this */
+ time_t now; /* time now */
+ time_t eta; /* estimated # of secs until completion */
+ int64_t elapsed; /* cached # of elapsed seconds */
+ int32_t ttywidth; /* width of tty in columns */
+} progress_t;
+
+int progress_init(progress_t */*meter*/, const char */*prefix*/, uint64_t /*size*/);
+int progress_update(progress_t */*meter*/, uint64_t /*done*/);
+int progress_draw(progress_t */*meter*/);
+int progress_reset_size(progress_t */*meter*/, uint64_t /*size*/);
+int progress_complete(progress_t */*meter*/, uint64_t /*done*/);
+
+#endif
diff --git a/sbin/ccdconfig/ccdconfig.c b/sbin/ccdconfig/ccdconfig.c
index 6324150b0ff7..76867ba8ff07 100644
--- a/sbin/ccdconfig/ccdconfig.c
+++ b/sbin/ccdconfig/ccdconfig.c
@@ -288,13 +288,16 @@ do_all(int action)
rval = 0;
egid = getegid();
- setegid(getgid());
+ if (setegid(getgid()) != 0)
+ err(1, "setegid failed");
if ((f = fopen(ccdconf, "r")) == NULL) {
- setegid(egid);
+ if (setegid(egid) != 0)
+ err(1, "setegid failed");
warn("fopen: %s", ccdconf);
return (1);
}
- setegid(egid);
+ if (setegid(egid) != 0)
+ err(1, "setegid failed");
while (fgets(line, sizeof(line), f) != NULL) {
argc = 0;
diff --git a/sbin/ddb/ddb.c b/sbin/ddb/ddb.c
index 901d494aa28a..edba7f8509a5 100644
--- a/sbin/ddb/ddb.c
+++ b/sbin/ddb/ddb.c
@@ -80,7 +80,7 @@ ddb_readfile(char *filename)
argc++;
spn = strcspn(argv[0], WHITESP);
- argv[1] = argv[0] + spn + strspn(argv[0] + spn, WHITESP);;
+ argv[1] = argv[0] + spn + strspn(argv[0] + spn, WHITESP);
argv[0][spn] = '\0';
if (*argv[1] != '\0')
argc++;
diff --git a/sbin/devd/devd.cc b/sbin/devd/devd.cc
index 453d3df35b81..772471985a13 100644
--- a/sbin/devd/devd.cc
+++ b/sbin/devd/devd.cc
@@ -855,11 +855,9 @@ event_loop(void)
timeval tv;
fd_set fds;
- fd = open(PATH_DEVCTL, O_RDONLY);
+ fd = open(PATH_DEVCTL, O_RDONLY | O_CLOEXEC);
if (fd == -1)
err(1, "Can't open devctl device %s", PATH_DEVCTL);
- if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0)
- err(1, "Can't set close-on-exec flag on devctl");
server_fd = create_socket(PIPE);
max_fd = max(fd, server_fd) + 1;
while (1) {
diff --git a/sbin/devd/devd.conf.5 b/sbin/devd/devd.conf.5
index 944bdbbcef8e..4c1893d366f7 100644
--- a/sbin/devd/devd.conf.5
+++ b/sbin/devd/devd.conf.5
@@ -323,6 +323,8 @@ Button state ($notify=0x00 is power, 0x01 is sleep).
Battery events.
.It Li Lid
Lid state ($notify=0x00 is closed, 0x01 is open).
+.It Li PROCESSOR
+Processor state/configuration ($notify=0x81 is a change in available Cx states).
.It Li Thermal
Thermal zone events.
.El
diff --git a/sbin/devfs/devfs.8 b/sbin/devfs/devfs.8
index aa2f81abd239..8c41b13f37d6 100644
--- a/sbin/devfs/devfs.8
+++ b/sbin/devfs/devfs.8
@@ -52,7 +52,7 @@ most of the commands related to the rule subsystem must be preceded by the
.Cm rule
keyword.
The following flags are common to all keywords:
-.Bl -tag -offset indent
+.Bl -tag -width 15n
.It Fl m Ar mount-point
Operate on
.Ar mount-point ,
@@ -88,7 +88,7 @@ Rule manipulation commands follow the
.Cm rule
keyword.
The following flags are common to all of the rule manipulation commands:
-.Bl -tag -offset indent
+.Bl -tag -width 15n
.It Fl s Ar ruleset
Operate on the ruleset with the number
.Ar ruleset .
@@ -98,7 +98,7 @@ specified mount-point.
.El
.Pp
The following commands are recognized:
-.Bl -tag -offset indent
+.Bl -tag -width 15n
.It Cm rule add Oo Ar rulenum Oc Ar rulespec
Add the rule described by
.Ar rulespec
@@ -156,7 +156,7 @@ is ignored.
The following conditions are recognized.
Conditions are ANDed together when matching a device;
if OR is desired, multiple rules can be written.
-.Bl -tag -offset indent
+.Bl -tag -width 15n
.It Cm path Ar pattern
Matches any node with a path that matches
.Ar pattern ,
@@ -175,7 +175,7 @@ and
The following actions are recognized.
Although there is no explicit delimiter between conditions and actions,
they may not be intermixed.
-.Bl -tag -offset indent
+.Bl -tag -width 15n
.It Cm group Ar gid
Set the GID of the node to
.Ar gid ,
@@ -238,7 +238,7 @@ will return the same information regardless of the mount-point specified with
The mount-point is only relevant when changing what its current ruleset is
or when using one of the apply commands.
.Sh FILES
-.Bl -tag -compact
+.Bl -tag -width "Pa /usr/share/examples/etc/devfs.conf" -compact
.It Pa /etc/defaults/devfs.rules
Default
.Nm
diff --git a/sbin/dhclient/dhclient.c b/sbin/dhclient/dhclient.c
index 4f2405a3409e..f71c8c836050 100644
--- a/sbin/dhclient/dhclient.c
+++ b/sbin/dhclient/dhclient.c
@@ -218,6 +218,7 @@ routehandler(struct protocol *p)
struct sockaddr *sa;
struct iaddr a;
ssize_t n;
+ int linkstat;
n = read(routefd, &msg, sizeof(msg));
rtm = (struct rt_msghdr *)msg;
@@ -278,6 +279,15 @@ routehandler(struct protocol *p)
ifi->name);
goto die;
}
+ linkstat = interface_link_status(ifi->name);
+ if (linkstat != ifi->linkstat) {
+ debug("%s link state %s -> %s", ifi->name,
+ ifi->linkstat ? "up" : "down",
+ linkstat ? "up" : "down");
+ ifi->linkstat = linkstat;
+ if (linkstat)
+ state_reboot(ifi);
+ }
break;
case RTM_IFANNOUNCE:
ifan = (struct if_announcemsghdr *)rtm;
@@ -430,6 +440,7 @@ main(int argc, char *argv[])
}
fprintf(stderr, " got link\n");
}
+ ifi->linkstat = 1;
if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1)
error("cannot open %s: %m", _PATH_DEVNULL);
diff --git a/sbin/dhclient/dhcpd.h b/sbin/dhclient/dhcpd.h
index 1334a7c4a055..4762cbd05f86 100644
--- a/sbin/dhclient/dhcpd.h
+++ b/sbin/dhclient/dhcpd.h
@@ -208,6 +208,7 @@ struct interface_info {
int errors;
int dead;
u_int16_t index;
+ int linkstat;
};
struct timeout {
diff --git a/sbin/dump/traverse.c b/sbin/dump/traverse.c
index f593244f40d1..8258f079c0ed 100644
--- a/sbin/dump/traverse.c
+++ b/sbin/dump/traverse.c
@@ -197,8 +197,8 @@ mapfiles(ino_t maxino, long *tapesize)
(mode & IFMT) == 0)
continue;
if (ino >= maxino) {
- msg("Skipping inode %d >= maxino %d\n",
- ino, maxino);
+ msg("Skipping inode %ju >= maxino %ju\n",
+ (uintmax_t)ino, (uintmax_t)maxino);
continue;
}
/*
@@ -400,15 +400,16 @@ searchdir(
for (loc = 0; loc < size; ) {
dp = (struct direct *)(dblk + loc);
if (dp->d_reclen == 0) {
- msg("corrupted directory, inumber %d\n", ino);
+ msg("corrupted directory, inumber %ju\n",
+ (uintmax_t)ino);
break;
}
loc += dp->d_reclen;
if (dp->d_ino == 0)
continue;
if (dp->d_ino >= maxino) {
- msg("corrupted directory entry, d_ino %d >= %d\n",
- dp->d_ino, maxino);
+ msg("corrupted directory entry, d_ino %ju >= %ju\n",
+ (uintmax_t)dp->d_ino, (uintmax_t)maxino);
break;
}
if (dp->d_name[0] == '.') {
diff --git a/sbin/dumpfs/dumpfs.c b/sbin/dumpfs/dumpfs.c
index 2926d1ad90fc..a41ca4dda719 100644
--- a/sbin/dumpfs/dumpfs.c
+++ b/sbin/dumpfs/dumpfs.c
@@ -277,8 +277,9 @@ dumpfs(const char *name)
printf("unknown flags (%#x)", fsflags);
putchar('\n');
printf("fsmnt\t%s\n", afs.fs_fsmnt);
- printf("volname\t%s\tswuid\t%ju\n",
- afs.fs_volname, (uintmax_t)afs.fs_swuid);
+ printf("volname\t%s\tswuid\t%ju\tprovidersize\t%ju\n",
+ afs.fs_volname, (uintmax_t)afs.fs_swuid,
+ (uintmax_t)afs.fs_providersize);
printf("\ncs[].cs_(nbfree,ndir,nifree,nffree):\n\t");
afs.fs_csp = calloc(1, afs.fs_cssize);
if (bread(&disk, fsbtodb(&afs, afs.fs_csaddr), afs.fs_csp, afs.fs_cssize) == -1)
@@ -419,6 +420,8 @@ marshal(const char *name)
/* -i is dumb */
if (fs->fs_flags & FS_SUJ)
printf("-j ");
+ if (fs->fs_flags & FS_GJOURNAL)
+ printf("-J ");
/* -k..l unimplemented */
printf("-m %d ", fs->fs_minfree);
/* -n unimplemented */
diff --git a/sbin/dumpon/dumpon.8 b/sbin/dumpon/dumpon.8
index ff47bcb19479..2c09e85cda85 100644
--- a/sbin/dumpon/dumpon.8
+++ b/sbin/dumpon/dumpon.8
@@ -41,6 +41,9 @@
.Nm
.Op Fl v
.Cm off
+.Nm
+.Op Fl v
+.Fl l
.Sh DESCRIPTION
The
.Nm
@@ -72,6 +75,13 @@ total amount of physical memory as reported by the
variable.
.Pp
The
+.Fl l
+flag causes
+.Nm
+to print the current dump device or _PATH_DEVNULL ("/dev/null") if no device is
+configured.
+.Pp
+The
.Fl v
flag causes
.Nm
diff --git a/sbin/dumpon/dumpon.c b/sbin/dumpon/dumpon.c
index 73d46b00eb3c..eebcc694e68d 100644
--- a/sbin/dumpon/dumpon.c
+++ b/sbin/dumpon/dumpon.c
@@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h>
#include <err.h>
+#include <errno.h>
#include <fcntl.h>
#include <paths.h>
#include <stdint.h>
@@ -60,9 +61,10 @@ static int verbose;
static void
usage(void)
{
- fprintf(stderr, "%s\n%s\n",
+ fprintf(stderr, "%s\n%s\n%s\n",
"usage: dumpon [-v] special_file",
- " dumpon [-v] off");
+ " dumpon [-v] off",
+ " dumpon [-v] -l");
exit(EX_USAGE);
}
@@ -92,15 +94,45 @@ check_size(int fd, const char *fn)
}
}
+static void
+listdumpdev(void)
+{
+ char dumpdev[PATH_MAX];
+ size_t len;
+ const char *sysctlname = "kern.shutdown.dumpdevname";
+
+ len = sizeof(dumpdev);
+ if (sysctlbyname(sysctlname, &dumpdev, &len, NULL, 0) != 0) {
+ if (errno == ENOMEM) {
+ err(EX_OSERR, "Kernel returned too large of a buffer for '%s'\n",
+ sysctlname);
+ } else {
+ err(EX_OSERR, "Sysctl get '%s'\n", sysctlname);
+ }
+ }
+ if (verbose) {
+ printf("kernel dumps on ");
+ }
+ if (strlen(dumpdev) == 0) {
+ printf("%s\n", _PATH_DEVNULL);
+ } else {
+ printf("%s\n", dumpdev);
+ }
+}
+
int
main(int argc, char *argv[])
{
int ch;
int i, fd;
u_int u;
+ int do_listdumpdev = 0;
- while ((ch = getopt(argc, argv, "v")) != -1)
+ while ((ch = getopt(argc, argv, "lv")) != -1)
switch((char)ch) {
+ case 'l':
+ do_listdumpdev = 1;
+ break;
case 'v':
verbose = 1;
break;
@@ -111,6 +143,11 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
+ if (do_listdumpdev) {
+ listdumpdev();
+ exit(EX_OK);
+ }
+
if (argc != 1)
usage();
diff --git a/sbin/etherswitchcfg/etherswitchcfg.c b/sbin/etherswitchcfg/etherswitchcfg.c
index e6129f3e4ad3..ea2cec8bd7e7 100644
--- a/sbin/etherswitchcfg/etherswitchcfg.c
+++ b/sbin/etherswitchcfg/etherswitchcfg.c
@@ -79,7 +79,7 @@ struct cmds {
int args;
void (*f)(struct cfg *, char *argv[]);
};
-struct cmds cmds[];
+static struct cmds cmds[];
static void usage(void);
@@ -501,7 +501,7 @@ main(int argc, char *argv[])
return (0);
}
-struct cmds cmds[] = {
+static struct cmds cmds[] = {
{ MODE_PORT, "vlangroup", 1, set_port_vlangroup },
{ MODE_PORT, "media", 1, set_port_media },
{ MODE_PORT, "mediaopt", 1, set_port_mediaopt },
diff --git a/sbin/etherswitchcfg/ifmedia.c b/sbin/etherswitchcfg/ifmedia.c
index 258c3d5b9ed3..b9bd3b9c56ff 100644
--- a/sbin/etherswitchcfg/ifmedia.c
+++ b/sbin/etherswitchcfg/ifmedia.c
@@ -396,10 +396,10 @@ static struct ifmedia_description ifm_subtype_ieee80211_aliases[] =
static struct ifmedia_description ifm_subtype_ieee80211_option_descriptions[] =
IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS;
-struct ifmedia_description ifm_subtype_ieee80211_mode_descriptions[] =
+static struct ifmedia_description ifm_subtype_ieee80211_mode_descriptions[] =
IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS;
-struct ifmedia_description ifm_subtype_ieee80211_mode_aliases[] =
+static struct ifmedia_description ifm_subtype_ieee80211_mode_aliases[] =
IFM_SUBTYPE_IEEE80211_MODE_ALIASES;
static struct ifmedia_description ifm_subtype_atm_descriptions[] =
diff --git a/sbin/fsck/Makefile b/sbin/fsck/Makefile
index bc445fda26f7..22de03c4da78 100644
--- a/sbin/fsck/Makefile
+++ b/sbin/fsck/Makefile
@@ -3,7 +3,6 @@
PROG= fsck
SRCS= fsck.c fsutil.c preen.c
-WARNS?= 2
MAN= fsck.8
.include <bsd.prog.mk>
diff --git a/sbin/fsck/fsck.c b/sbin/fsck/fsck.c
index 768e670d6672..6bc702e90280 100644
--- a/sbin/fsck/fsck.c
+++ b/sbin/fsck/fsck.c
@@ -1,4 +1,4 @@
-/* $NetBSD: fsck.c,v 1.21 1999/04/22 04:20:53 abs Exp $ */
+/* $NetBSD: fsck.c,v 1.30 2003/08/07 10:04:15 agc Exp $ */
/*
* Copyright (c) 1996 Christos Zoulas. All rights reserved.
@@ -13,11 +13,7 @@
* 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
+ * 3. 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.
*
@@ -35,7 +31,7 @@
*
* From: @(#)mount.c 8.19 (Berkeley) 4/19/94
* From: $NetBSD: mount.c,v 1.24 1995/11/18 03:34:29 cgd Exp
- * $NetBSD: fsck.c,v 1.21 1999/04/22 04:20:53 abs Exp $
+ * $NetBSD: fsck.c,v 1.30 2003/08/07 10:04:15 agc Exp $
*/
#include <sys/cdefs.h>
@@ -77,14 +73,14 @@ static char *options = NULL;
static int flags = 0;
static int forceflag = 0;
-static int checkfs(const char *, const char *, const char *, char *, pid_t *);
+static int checkfs(const char *, const char *, const char *, const char *, pid_t *);
static int selected(const char *);
static void addoption(char *);
static const char *getoptions(const char *);
static void addentry(struct fstypelist *, const char *, const char *);
static void maketypelist(char *);
static void catopt(char **, const char *);
-static void mangle(char *, int *, const char ***, int *);
+static void mangle(char *, int *, const char ** volatile *, int *);
static const char *getfslab(const char *);
static void usage(void) __dead2;
static int isok(struct fstab *);
@@ -191,6 +187,7 @@ main(int argc, char *argv[])
char device[MAXPATHLEN];
struct statfs *mntp;
+ mntpt = NULL;
spec = *argv;
cp = strrchr(spec, '/');
if (cp == 0) {
@@ -289,9 +286,9 @@ isok(struct fstab *fs)
static int
checkfs(const char *pvfstype, const char *spec, const char *mntpt,
- char *auxopt, pid_t *pidp)
+ const char *auxopt, pid_t *pidp)
{
- const char **argv;
+ const char ** volatile argv;
pid_t pid;
int argc, i, status, maxargc;
char *optbuf, execbase[MAXPATHLEN];
@@ -315,8 +312,8 @@ checkfs(const char *pvfstype, const char *spec, const char *mntpt,
*/
vfstype = strdup(pvfstype);
if (vfstype == NULL)
- perror("strdup(pvfstype)");
- for (i = 0; i < strlen(vfstype); i++) {
+ perr("strdup(pvfstype)");
+ for (i = 0; i < (int)strlen(vfstype); i++) {
vfstype[i] = tolower(vfstype[i]);
if (vfstype[i] == ' ')
vfstype[i] = '_';
@@ -365,7 +362,7 @@ checkfs(const char *pvfstype, const char *spec, const char *mntpt,
_exit(0);
/* Go find an executable. */
- execvP(execbase, _PATH_SYSPATH, (char * const *)argv);
+ execvP(execbase, _PATH_SYSPATH, __DECONST(char * const *, argv));
if (spec)
warn("exec %s for %s in %s", execbase, spec, _PATH_SYSPATH);
else
@@ -502,7 +499,7 @@ catopt(char **sp, const char *o)
static void
-mangle(char *options, int *argcp, const char ***argvp, int *maxargcp)
+mangle(char *opts, int *argcp, const char ** volatile *argvp, int *maxargcp)
{
char *p, *s;
int argc, maxargc;
@@ -512,7 +509,7 @@ mangle(char *options, int *argcp, const char ***argvp, int *maxargcp)
argv = *argvp;
maxargc = *maxargcp;
- for (s = options; (p = strsep(&s, ",")) != NULL;) {
+ for (s = opts; (p = strsep(&s, ",")) != NULL;) {
/* Always leave space for one more argument and the NULL. */
if (argc >= maxargc - 3) {
maxargc <<= 1;
@@ -539,7 +536,7 @@ mangle(char *options, int *argcp, const char ***argvp, int *maxargcp)
}
-const static char *
+static const char *
getfslab(const char *str)
{
struct disklabel dl;
diff --git a/sbin/fsck/fsutil.c b/sbin/fsck/fsutil.c
index a4578bd63eb6..935992e9bda4 100644
--- a/sbin/fsck/fsutil.c
+++ b/sbin/fsck/fsutil.c
@@ -1,4 +1,4 @@
-/* $NetBSD: fsutil.c,v 1.7 1998/07/30 17:41:03 thorpej Exp $ */
+/* $NetBSD: fsutil.c,v 1.15 2006/06/05 16:52:05 christos Exp $ */
/*
* Copyright (c) 1990, 1993
@@ -12,7 +12,7 @@
* 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.
- * 4. Neither the name of the University nor the names of its contributors
+ * 3. 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.
*
@@ -31,7 +31,7 @@
#include <sys/cdefs.h>
#ifndef lint
-__RCSID("$NetBSD: fsutil.c,v 1.7 1998/07/30 17:41:03 thorpej Exp $");
+__RCSID("$NetBSD: fsutil.c,v 1.15 2006/06/05 16:52:05 christos Exp $");
#endif /* not lint */
__FBSDID("$FreeBSD$");
@@ -110,9 +110,13 @@ pwarn(const char *fmt, ...)
}
void
-perror(const char *s)
+perr(const char *fmt, ...)
{
- pfatal("%s (%s)", s, strerror(errno));
+ va_list ap;
+
+ va_start(ap, fmt);
+ vmsg(1, fmt, ap);
+ va_end(ap);
}
void
@@ -132,18 +136,15 @@ devcheck(const char *origname)
struct stat stslash, stchar;
if (stat("/", &stslash) < 0) {
- perror("/");
- printf("Can't stat root\n");
+ perr("Can't stat `/'");
return (origname);
}
if (stat(origname, &stchar) < 0) {
- perror(origname);
- printf("Can't stat %s\n", origname);
+ perr("Can't stat %s\n", origname);
return (origname);
}
if (!S_ISCHR(stchar.st_mode)) {
- perror(origname);
- printf("%s is not a char device\n", origname);
+ perr("%s is not a char device\n", origname);
}
return (origname);
}
@@ -156,7 +157,7 @@ getmntpt(const char *name)
{
struct stat devstat, mntdevstat;
char device[sizeof(_PATH_DEV) - 1 + MNAMELEN];
- char *devname;
+ char *dev_name;
struct statfs *mntbuf, *statfsp;
int i, mntsize, isdev;
@@ -169,10 +170,10 @@ getmntpt(const char *name)
mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
for (i = 0; i < mntsize; i++) {
statfsp = &mntbuf[i];
- devname = statfsp->f_mntfromname;
- if (*devname != '/') {
+ dev_name = statfsp->f_mntfromname;
+ if (*dev_name != '/') {
strcpy(device, _PATH_DEV);
- strcat(device, devname);
+ strcat(device, dev_name);
strcpy(statfsp->f_mntfromname, device);
}
if (isdev == 0) {
@@ -180,7 +181,7 @@ getmntpt(const char *name)
continue;
return (statfsp);
}
- if (stat(devname, &mntdevstat) == 0 &&
+ if (stat(dev_name, &mntdevstat) == 0 &&
mntdevstat.st_rdev == devstat.st_rdev)
return (statfsp);
}
diff --git a/sbin/fsck/fsutil.h b/sbin/fsck/fsutil.h
index 657668ede4ce..013b821b29d0 100644
--- a/sbin/fsck/fsutil.h
+++ b/sbin/fsck/fsutil.h
@@ -1,4 +1,4 @@
-/* $NetBSD: fsutil.h,v 1.4 1998/07/26 20:02:36 mycroft Exp $ */
+/* $NetBSD: fsutil.h,v 1.114 2009/10/21 01:07:46 snj Exp $ */
/*
* Copyright (c) 1996 Christos Zoulas. All rights reserved.
@@ -11,11 +11,6 @@
* 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 Christos Zoulas.
- * 4. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -31,9 +26,9 @@
* $FreeBSD$
*/
-void perror(const char *);
void pfatal(const char *, ...) __printflike(1, 2);
void pwarn(const char *, ...) __printflike(1, 2);
+void perr(const char *, ...) __printflike(1, 2);
void panic(const char *, ...) __dead2 __printflike(1, 2);
const char *devcheck(const char *);
const char *cdevname(void);
@@ -52,4 +47,4 @@ char *estrdup(const char *);
struct fstab;
int checkfstab(int, int (*)(struct fstab *),
- int (*) (const char *, const char *, const char *, char *, pid_t *));
+ int (*) (const char *, const char *, const char *, const char *, pid_t *));
diff --git a/sbin/fsck/preen.c b/sbin/fsck/preen.c
index 787bed0e2713..18f280197895 100644
--- a/sbin/fsck/preen.c
+++ b/sbin/fsck/preen.c
@@ -78,12 +78,12 @@ static int nrun = 0, ndisks = 0;
static struct diskentry *finddisk(const char *);
static void addpart(const char *, const char *, const char *);
static int startdisk(struct diskentry *,
- int (*)(const char *, const char *, const char *, char *, pid_t *));
+ int (*)(const char *, const char *, const char *, const char *, pid_t *));
static void printpart(void);
int
checkfstab(int flags, int (*docheck)(struct fstab *),
- int (*checkit)(const char *, const char *, const char *, char *, pid_t *))
+ int (*checkit)(const char *, const char *, const char *, const char *, pid_t *))
{
struct fstab *fs;
struct diskentry *d, *nextdisk;
@@ -295,19 +295,19 @@ printpart(void)
static void
-addpart(const char *type, const char *devname, const char *mntpt)
+addpart(const char *type, const char *dev, const char *mntpt)
{
- struct diskentry *d = finddisk(devname);
+ struct diskentry *d = finddisk(dev);
struct partentry *p;
TAILQ_FOREACH(p, &d->d_part, p_entries)
- if (strcmp(p->p_devname, devname) == 0) {
- warnx("%s in fstab more than once!\n", devname);
+ if (strcmp(p->p_devname, dev) == 0) {
+ warnx("%s in fstab more than once!\n", dev);
return;
}
p = emalloc(sizeof(*p));
- p->p_devname = estrdup(devname);
+ p->p_devname = estrdup(dev);
p->p_mntpt = estrdup(mntpt);
p->p_type = estrdup(type);
@@ -317,7 +317,7 @@ addpart(const char *type, const char *devname, const char *mntpt)
static int
startdisk(struct diskentry *d, int (*checkit)(const char *, const char *,
- const char *, char *, pid_t *))
+ const char *, const char *, pid_t *))
{
struct partentry *p = TAILQ_FIRST(&d->d_part);
int rv;
diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h
index 6ac004e4fb52..4717760acad9 100644
--- a/sbin/fsck_ffs/fsck.h
+++ b/sbin/fsck_ffs/fsck.h
@@ -337,7 +337,7 @@ void blkerror(ino_t ino, const char *type, ufs2_daddr_t blk);
char *blockcheck(char *name);
int blread(int fd, char *buf, ufs2_daddr_t blk, long size);
void bufinit(void);
-void blwrite(int fd, char *buf, ufs2_daddr_t blk, long size);
+void blwrite(int fd, char *buf, ufs2_daddr_t blk, ssize_t size);
void blerase(int fd, ufs2_daddr_t blk, long size);
void cacheino(union dinode *dp, ino_t inumber);
void catch(int);
diff --git a/sbin/fsck_ffs/fsutil.c b/sbin/fsck_ffs/fsutil.c
index 59b73c81ad21..9bf61d136333 100644
--- a/sbin/fsck_ffs/fsutil.c
+++ b/sbin/fsck_ffs/fsutil.c
@@ -137,7 +137,8 @@ inoinfo(ino_t inum)
int iloff;
if (inum > maxino)
- errx(EEXIT, "inoinfo: inumber %d out of range", inum);
+ errx(EEXIT, "inoinfo: inumber %ju out of range",
+ (uintmax_t)inum);
ilp = &inostathead[inum / sblock.fs_ipg];
iloff = inum % sblock.fs_ipg;
if (iloff >= ilp->il_numalloced)
@@ -245,7 +246,7 @@ flush(int fd, struct bufarea *bp)
(bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
(long long)bp->b_bno);
bp->b_errs = 0;
- blwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
+ blwrite(fd, bp->b_un.b_buf, bp->b_bno, bp->b_size);
if (bp != &sblk)
return;
for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
@@ -392,7 +393,7 @@ blread(int fd, char *buf, ufs2_daddr_t blk, long size)
}
void
-blwrite(int fd, char *buf, ufs2_daddr_t blk, long size)
+blwrite(int fd, char *buf, ufs2_daddr_t blk, ssize_t size)
{
int i;
char *cp;
@@ -404,7 +405,7 @@ blwrite(int fd, char *buf, ufs2_daddr_t blk, long size)
offset *= dev_bsize;
if (lseek(fd, offset, 0) < 0)
rwerror("SEEK BLK", blk);
- else if (write(fd, buf, (int)size) == size) {
+ else if (write(fd, buf, size) == size) {
fsmodified = 1;
return;
}
@@ -414,7 +415,7 @@ blwrite(int fd, char *buf, ufs2_daddr_t blk, long size)
rwerror("SEEK BLK", blk);
printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
- if (write(fd, cp, (int)dev_bsize) != dev_bsize) {
+ if (write(fd, cp, dev_bsize) != dev_bsize) {
(void)lseek(fd, offset + i + dev_bsize, 0);
printf(" %jd,", (intmax_t)blk + i / dev_bsize);
}
diff --git a/sbin/fsck_ffs/gjournal.c b/sbin/fsck_ffs/gjournal.c
index e8ecaf0c1bbf..ba8dc3416a96 100644
--- a/sbin/fsck_ffs/gjournal.c
+++ b/sbin/fsck_ffs/gjournal.c
@@ -448,7 +448,8 @@ gjournal_check(const char *filesys)
if (isclr(inosused, cino))
continue;
if (getino(disk, &p, ino, &mode) == -1)
- err(1, "getino(cg=%d ino=%d)", cg, ino);
+ err(1, "getino(cg=%d ino=%ju)",
+ cg, (uintmax_t)ino);
dino = p;
/* Not a regular file nor directory? Skip it. */
if (!S_ISREG(dino->di_mode) && !S_ISDIR(dino->di_mode))
@@ -480,7 +481,8 @@ gjournal_check(const char *filesys)
*dino = ufs2_zino;
/* Write the inode back. */
if (putino(disk) == -1)
- err(1, "putino(cg=%d ino=%d)", cg, ino);
+ err(1, "putino(cg=%d ino=%ju)",
+ cg, (uintmax_t)ino);
if (cgp->cg_unrefs == 0) {
//printf("No more unreferenced inodes in cg=%d.\n", cg);
break;
diff --git a/sbin/fsck_ffs/inode.c b/sbin/fsck_ffs/inode.c
index 4ef70fce3d55..389150a3d5f2 100644
--- a/sbin/fsck_ffs/inode.c
+++ b/sbin/fsck_ffs/inode.c
@@ -285,7 +285,8 @@ ginode(ino_t inumber)
ufs2_daddr_t iblk;
if (inumber < ROOTINO || inumber > maxino)
- errx(EEXIT, "bad inode number %d to ginode", inumber);
+ errx(EEXIT, "bad inode number %ju to ginode",
+ (uintmax_t)inumber);
if (startinum == 0 ||
inumber < startinum || inumber >= startinum + INOPB(&sblock)) {
iblk = ino_to_fsba(&sblock, inumber);
@@ -319,7 +320,8 @@ getnextinode(ino_t inumber, int rebuildcg)
static caddr_t nextinop;
if (inumber != nextino++ || inumber > lastvalidinum)
- errx(EEXIT, "bad inode number %d to nextinode", inumber);
+ errx(EEXIT, "bad inode number %ju to nextinode",
+ (uintmax_t)inumber);
if (inumber >= lastinum) {
readcnt++;
dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
@@ -398,7 +400,8 @@ setinodebuf(ino_t inum)
{
if (inum % sblock.fs_ipg != 0)
- errx(EEXIT, "bad inode number %d to setinodebuf", inum);
+ errx(EEXIT, "bad inode number %ju to setinodebuf",
+ (uintmax_t)inum);
lastvalidinum = inum + sblock.fs_ipg - 1;
startinum = 0;
nextino = inum;
@@ -489,7 +492,7 @@ getinoinfo(ino_t inumber)
continue;
return (inp);
}
- errx(EEXIT, "cannot find inode %d", inumber);
+ errx(EEXIT, "cannot find inode %ju", (uintmax_t)inumber);
return ((struct inoinfo *)0);
}
diff --git a/sbin/fsck_ffs/main.c b/sbin/fsck_ffs/main.c
index 37258f73ed01..69f157ab195e 100644
--- a/sbin/fsck_ffs/main.c
+++ b/sbin/fsck_ffs/main.c
@@ -210,12 +210,11 @@ checkfilesys(char *filesys)
struct statfs *mntp;
struct stat snapdir;
struct group *grp;
- ufs2_daddr_t blks;
struct iovec *iov;
char errmsg[255];
int iovlen;
int cylno;
- ino_t files;
+ intmax_t blks, files;
size_t size;
iov = NULL;
@@ -279,8 +278,8 @@ checkfilesys(char *filesys)
exit(0);
exit(4);
} else {
- pfatal("UNEXPECTED INCONSISTENCY, %s\n",
- "CANNOT RUN FAST FSCK\n");
+ pfatal(
+ "UNEXPECTED INCONSISTENCY, CANNOT RUN FAST FSCK\n");
}
}
}
@@ -297,8 +296,8 @@ checkfilesys(char *filesys)
pfatal("NOT MOUNTED, CANNOT RUN IN BACKGROUND\n");
} else if ((mntp->f_flags & MNT_SOFTDEP) == 0) {
bkgrdflag = 0;
- pfatal("NOT USING SOFT UPDATES, %s\n",
- "CANNOT RUN IN BACKGROUND");
+ 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");
@@ -306,8 +305,8 @@ checkfilesys(char *filesys)
if (readsb(0) != 0) {
if (sblock.fs_flags & (FS_NEEDSFSCK | FS_SUJ)) {
bkgrdflag = 0;
- pfatal("UNEXPECTED INCONSISTENCY, %s\n",
- "CANNOT RUN IN BACKGROUND\n");
+ pfatal(
+ "UNEXPECTED INCONSISTENCY, CANNOT RUN IN BACKGROUND\n");
}
if ((sblock.fs_flags & FS_UNCLEAN) == 0 &&
skipclean && ckclean) {
@@ -315,8 +314,8 @@ checkfilesys(char *filesys)
* file system is clean;
* skip snapshot and report it clean
*/
- pwarn("FILE SYSTEM CLEAN; %s\n",
- "SKIPPING CHECKS");
+ pwarn(
+ "FILE SYSTEM CLEAN; SKIPPING CHECKS\n");
goto clean;
}
}
@@ -328,24 +327,23 @@ checkfilesys(char *filesys)
if (stat(snapname, &snapdir) < 0) {
if (errno != ENOENT) {
bkgrdflag = 0;
- pfatal("CANNOT FIND %s %s: %s, %s\n",
- "SNAPSHOT DIRECTORY",
- snapname, strerror(errno),
- "CANNOT RUN IN BACKGROUND");
+ pfatal(
+ "CANNOT FIND SNAPSHOT DIRECTORY %s: %s, CANNOT RUN IN BACKGROUND\n",
+ snapname, strerror(errno));
} else if ((grp = getgrnam("operator")) == 0 ||
mkdir(snapname, 0770) < 0 ||
chown(snapname, -1, grp->gr_gid) < 0 ||
chmod(snapname, 0770) < 0) {
bkgrdflag = 0;
- pfatal("CANNOT CREATE %s %s: %s, %s\n",
- "SNAPSHOT DIRECTORY",
- snapname, strerror(errno),
- "CANNOT RUN IN BACKGROUND");
+ 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, %s\n", snapname,
- "CANNOT RUN IN BACKGROUND");
+ pfatal(
+ "%s IS NOT A DIRECTORY, CANNOT RUN IN BACKGROUND\n",
+ snapname);
}
}
if (bkgrdflag) {
@@ -383,9 +381,9 @@ checkfilesys(char *filesys)
clean:
pwarn("clean, %ld free ", (long)(sblock.fs_cstotal.cs_nffree +
sblock.fs_frag * sblock.fs_cstotal.cs_nbfree));
- printf("(%lld frags, %lld blocks, %.1f%% fragmentation)\n",
- (long long)sblock.fs_cstotal.cs_nffree,
- (long long)sblock.fs_cstotal.cs_nbfree,
+ printf("(%jd frags, %jd blocks, %.1f%% fragmentation)\n",
+ (intmax_t)sblock.fs_cstotal.cs_nffree,
+ (intmax_t)sblock.fs_cstotal.cs_nbfree,
sblock.fs_cstotal.cs_nffree * 100.0 / sblock.fs_dsize);
return (0);
}
@@ -482,8 +480,8 @@ checkfilesys(char *filesys)
blks = maxfsblock - (n_ffree + sblock.fs_frag * n_bfree) - blks;
if (bkgrdflag && (files > 0 || blks > 0)) {
countdirs = sblock.fs_cstotal.cs_ndir - countdirs;
- pwarn("Reclaimed: %ld directories, %ld files, %lld fragments\n",
- countdirs, (long)files - countdirs, (long long)blks);
+ pwarn("Reclaimed: %ld directories, %jd files, %jd fragments\n",
+ countdirs, files - countdirs, blks);
}
pwarn("%ld files, %jd used, %ju free ",
(long)n_files, (intmax_t)n_blks,
@@ -493,13 +491,13 @@ checkfilesys(char *filesys)
n_ffree * 100.0 / sblock.fs_dsize);
if (debug) {
if (files < 0)
- printf("%d inodes missing\n", -files);
+ printf("%jd inodes missing\n", -files);
if (blks < 0)
- printf("%lld blocks missing\n", -(long long)blks);
+ printf("%jd blocks missing\n", -blks);
if (duplist != NULL) {
printf("The following duplicate blocks remain:");
for (dp = duplist; dp; dp = dp->next)
- printf(" %lld,", (long long)dp->dup);
+ printf(" %jd,", (intmax_t)dp->dup);
printf("\n");
}
}
@@ -636,8 +634,7 @@ static void
usage(void)
{
(void) fprintf(stderr,
- "usage: %s [-BEFfnpry] [-b block] [-c level] [-m mode] "
- "filesystem ...\n",
+"usage: %s [-BEFfnpry] [-b block] [-c level] [-m mode] filesystem ...\n",
getprogname());
exit(1);
}
diff --git a/sbin/fsck_ffs/pass1.c b/sbin/fsck_ffs/pass1.c
index fa30bb2018ca..08e84b94aff1 100644
--- a/sbin/fsck_ffs/pass1.c
+++ b/sbin/fsck_ffs/pass1.c
@@ -99,10 +99,10 @@ pass1(void)
if (!rebuildcg && sblock.fs_magic == FS_UFS2_MAGIC) {
inosused = cgrp.cg_initediblk;
if (inosused > sblock.fs_ipg) {
- pfatal("%s (%d > %d) %s %d\nReset to %d\n",
- "Too many initialized inodes", inosused,
- sblock.fs_ipg, "in cylinder group", c,
- sblock.fs_ipg);
+ pfatal(
+"Too many initialized inodes (%ju > %d) in cylinder group %d\nReset to %d\n",
+ (uintmax_t)inosused,
+ sblock.fs_ipg, c, sblock.fs_ipg);
inosused = sblock.fs_ipg;
}
} else {
diff --git a/sbin/fsck_ffs/pass2.c b/sbin/fsck_ffs/pass2.c
index bd9bf97e92c8..82c3b947fc63 100644
--- a/sbin/fsck_ffs/pass2.c
+++ b/sbin/fsck_ffs/pass2.c
@@ -223,13 +223,14 @@ pass2(void)
* inp->i_parent is directory to which ".." should point.
*/
getpathname(pathbuf, inp->i_parent, inp->i_number);
- printf("BAD INODE NUMBER FOR '..' in DIR I=%d (%s)\n",
- inp->i_number, pathbuf);
+ printf("BAD INODE NUMBER FOR '..' in DIR I=%ju (%s)\n",
+ (uintmax_t)inp->i_number, pathbuf);
getpathname(pathbuf, inp->i_dotdot, inp->i_dotdot);
- printf("CURRENTLY POINTS TO I=%d (%s), ", inp->i_dotdot,
- pathbuf);
+ printf("CURRENTLY POINTS TO I=%ju (%s), ",
+ (uintmax_t)inp->i_dotdot, pathbuf);
getpathname(pathbuf, inp->i_parent, inp->i_parent);
- printf("SHOULD POINT TO I=%d (%s)", inp->i_parent, pathbuf);
+ printf("SHOULD POINT TO I=%ju (%s)",
+ (uintmax_t)inp->i_parent, pathbuf);
if (cursnapshot != 0) {
/*
* We need to:
@@ -443,8 +444,8 @@ again:
} else {
getpathname(dirname, idesc->id_number,
dirp->d_ino);
- pwarn("ZERO LENGTH DIRECTORY %s I=%d",
- dirname, dirp->d_ino);
+ pwarn("ZERO LENGTH DIRECTORY %s I=%ju",
+ dirname, (uintmax_t)dirp->d_ino);
/*
* We need to:
* setcwd(idesc->id_parent);
@@ -507,8 +508,9 @@ again:
break;
default:
- errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
- inoinfo(dirp->d_ino)->ino_state, dirp->d_ino);
+ errx(EEXIT, "BAD STATE %d FOR INODE I=%ju",
+ inoinfo(dirp->d_ino)->ino_state,
+ (uintmax_t)dirp->d_ino);
}
}
if (n == 0)
diff --git a/sbin/fsck_ffs/pass4.c b/sbin/fsck_ffs/pass4.c
index 4b2af7be89de..80a32c14b14c 100644
--- a/sbin/fsck_ffs/pass4.c
+++ b/sbin/fsck_ffs/pass4.c
@@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$");
#include <ufs/ffs/fs.h>
#include <err.h>
+#include <stdint.h>
#include <string.h>
#include "fsck.h"
@@ -114,8 +115,9 @@ pass4(void)
break;
default:
- errx(EEXIT, "BAD STATE %d FOR INODE I=%d",
- inoinfo(inumber)->ino_state, inumber);
+ errx(EEXIT, "BAD STATE %d FOR INODE I=%ju",
+ inoinfo(inumber)->ino_state,
+ (uintmax_t)inumber);
}
}
}
diff --git a/sbin/fsck_ffs/suj.c b/sbin/fsck_ffs/suj.c
index b784519062b2..0800a5c7b6eb 100644
--- a/sbin/fsck_ffs/suj.c
+++ b/sbin/fsck_ffs/suj.c
@@ -831,8 +831,8 @@ ino_clrat(ino_t parent, off_t diroff, ino_t child)
int doff;
if (debug)
- printf("Clearing inode %d from parent %d at offset %jd\n",
- child, parent, diroff);
+ printf("Clearing inode %ju from parent %ju at offset %jd\n",
+ (uintmax_t)child, (uintmax_t)parent, diroff);
lbn = lblkno(fs, diroff);
doff = blkoff(fs, diroff);
@@ -842,8 +842,8 @@ ino_clrat(ino_t parent, off_t diroff, ino_t child)
block = dblk_read(blk, blksize);
dp = (struct direct *)&block[doff];
if (dp->d_ino != child)
- errx(1, "Inode %d does not exist in %d at %jd",
- child, parent, diroff);
+ errx(1, "Inode %ju does not exist in %ju at %jd",
+ (uintmax_t)child, (uintmax_t)parent, diroff);
dp->d_ino = 0;
dblk_dirty(blk);
/*
@@ -879,10 +879,11 @@ ino_isat(ino_t parent, off_t diroff, ino_t child, int *mode, int *isdot)
* was reallocated.
*/
if (*mode != 0)
- printf("Directory %d has bad mode %o\n",
- parent, *mode);
+ printf("Directory %ju has bad mode %o\n",
+ (uintmax_t)parent, *mode);
else
- printf("Directory %d zero inode\n", parent);
+ printf("Directory %ju has zero mode\n",
+ (uintmax_t)parent);
}
return (0);
}
@@ -891,15 +892,16 @@ ino_isat(ino_t parent, off_t diroff, ino_t child, int *mode, int *isdot)
blksize = sblksize(fs, DIP(dip, di_size), lbn);
if (diroff + DIRECTSIZ(1) > DIP(dip, di_size) || doff >= blksize) {
if (debug)
- printf("ino %d absent from %d due to offset %jd"
+ printf("ino %ju absent from %ju due to offset %jd"
" exceeding size %jd\n",
- child, parent, diroff, DIP(dip, di_size));
+ (uintmax_t)child, (uintmax_t)parent, diroff,
+ DIP(dip, di_size));
return (0);
}
blk = ino_blkatoff(dip, parent, lbn, &frags);
if (blk <= 0) {
if (debug)
- printf("Sparse directory %d", parent);
+ printf("Sparse directory %ju", (uintmax_t)parent);
return (0);
}
block = dblk_read(blk, blksize);
@@ -918,12 +920,13 @@ ino_isat(ino_t parent, off_t diroff, ino_t child, int *mode, int *isdot)
dpoff += dp->d_reclen;
} while (dpoff <= doff);
if (dpoff > fs->fs_bsize)
- err_suj("Corrupt directory block in dir ino %d\n", parent);
+ err_suj("Corrupt directory block in dir ino %ju\n",
+ (uintmax_t)parent);
/* Not found. */
if (dpoff != doff) {
if (debug)
- printf("ino %d not found in %d, lbn %jd, dpoff %d\n",
- child, parent, lbn, dpoff);
+ printf("ino %ju not found in %ju, lbn %jd, dpoff %d\n",
+ (uintmax_t)child, (uintmax_t)parent, lbn, dpoff);
return (0);
}
/*
@@ -940,8 +943,8 @@ ino_isat(ino_t parent, off_t diroff, ino_t child, int *mode, int *isdot)
return (1);
}
if (debug)
- printf("ino %d doesn't match dirent ino %d in parent %d\n",
- child, dp->d_ino, parent);
+ printf("ino %ju doesn't match dirent ino %ju in parent %ju\n",
+ (uintmax_t)child, (uintmax_t)dp->d_ino, (uintmax_t)parent);
return (0);
}
@@ -977,8 +980,8 @@ indir_visit(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, uint64_t *frags,
err_suj("Invalid level for lbn %jd\n", lbn);
if ((flags & VISIT_ROOT) == 0 && blk_isindir(blk, ino, lbn) == 0) {
if (debug)
- printf("blk %jd ino %d lbn %jd(%d) is not indir.\n",
- blk, ino, lbn, level);
+ printf("blk %jd ino %ju lbn %jd(%d) is not indir.\n",
+ blk, (uintmax_t)ino, lbn, level);
goto out;
}
lbnadd = 1;
@@ -1131,8 +1134,8 @@ ino_adjblks(struct suj_ino *sino)
if (blocks == DIP(ip, di_blocks))
return;
if (debug)
- printf("ino %d adjusting block count from %jd to %jd\n",
- ino, DIP(ip, di_blocks), blocks);
+ printf("ino %ju adjusting block count from %jd to %jd\n",
+ (uintmax_t)ino, DIP(ip, di_blocks), blocks);
DIP_SET(ip, di_blocks, blocks);
ino_dirty(ino);
}
@@ -1264,8 +1267,8 @@ ino_free_children(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags)
if (isdotdot && skipparent == 1)
continue;
if (debug)
- printf("Directory %d removing ino %d name %s\n",
- ino, dp->d_ino, dp->d_name);
+ printf("Directory %ju removing ino %ju name %s\n",
+ (uintmax_t)ino, (uintmax_t)dp->d_ino, dp->d_name);
diroff = lblktosize(fs, lbn) + dpoff;
ino_remref(ino, dp->d_ino, diroff, isdotdot);
}
@@ -1283,8 +1286,8 @@ ino_reclaim(union dinode *ip, ino_t ino, int mode)
if (ino == ROOTINO)
err_suj("Attempting to free ROOTINO\n");
if (debug)
- printf("Truncating and freeing ino %d, nlink %d, mode %o\n",
- ino, DIP(ip, di_nlink), DIP(ip, di_mode));
+ printf("Truncating and freeing ino %ju, nlink %d, mode %o\n",
+ (uintmax_t)ino, DIP(ip, di_nlink), DIP(ip, di_mode));
/* We are freeing an inode or directory. */
if ((DIP(ip, di_mode) & IFMT) == IFDIR)
@@ -1328,8 +1331,8 @@ ino_decr(ino_t ino)
reqlink = 1;
if (nlink < reqlink) {
if (debug)
- printf("ino %d not enough links to live %d < %d\n",
- ino, nlink, reqlink);
+ printf("ino %ju not enough links to live %d < %d\n",
+ (uintmax_t)ino, nlink, reqlink);
ino_reclaim(ip, ino, mode);
return;
}
@@ -1374,7 +1377,7 @@ ino_adjust(struct suj_ino *sino)
break;
}
if (srec == NULL)
- errx(1, "Directory %d name not found", ino);
+ errx(1, "Directory %ju name not found", (uintmax_t)ino);
}
/*
* If it's a directory with no real names pointing to it go ahead
@@ -1398,21 +1401,22 @@ ino_adjust(struct suj_ino *sino)
ip = ino_read(ino);
mode = DIP(ip, di_mode) & IFMT;
if (nlink > LINK_MAX)
- err_suj(
- "ino %d nlink manipulation error, new link %d, old link %d\n",
- ino, nlink, DIP(ip, di_nlink));
+ err_suj("ino %ju nlink manipulation error, new %d, old %d\n",
+ (uintmax_t)ino, nlink, DIP(ip, di_nlink));
if (debug)
- printf("Adjusting ino %d, nlink %d, old link %d lastmode %o\n",
- ino, nlink, DIP(ip, di_nlink), sino->si_mode);
+ printf("Adjusting ino %ju, nlink %d, old link %d lastmode %o\n",
+ (uintmax_t)ino, nlink, DIP(ip, di_nlink), sino->si_mode);
if (mode == 0) {
if (debug)
- printf("ino %d, zero inode freeing bitmap\n", ino);
+ printf("ino %ju, zero inode freeing bitmap\n",
+ (uintmax_t)ino);
ino_free(ino, sino->si_mode);
return;
}
/* XXX Should be an assert? */
if (mode != sino->si_mode && debug)
- printf("ino %d, mode %o != %o\n", ino, mode, sino->si_mode);
+ printf("ino %ju, mode %o != %o\n",
+ (uintmax_t)ino, mode, sino->si_mode);
if ((mode & IFMT) == IFDIR)
reqlink = 2;
else
@@ -1420,15 +1424,16 @@ ino_adjust(struct suj_ino *sino)
/* If the inode doesn't have enough links to live, free it. */
if (nlink < reqlink) {
if (debug)
- printf("ino %d not enough links to live %d < %d\n",
- ino, nlink, reqlink);
+ printf("ino %ju not enough links to live %d < %d\n",
+ (uintmax_t)ino, nlink, reqlink);
ino_reclaim(ip, ino, mode);
return;
}
/* If required write the updated link count. */
if (DIP(ip, di_nlink) == nlink) {
if (debug)
- printf("ino %d, link matches, skipping.\n", ino);
+ printf("ino %ju, link matches, skipping.\n",
+ (uintmax_t)ino);
return;
}
DIP_SET(ip, di_nlink, nlink);
@@ -1527,8 +1532,8 @@ ino_trunc(ino_t ino, off_t size)
mode = DIP(ip, di_mode) & IFMT;
cursize = DIP(ip, di_size);
if (debug)
- printf("Truncating ino %d, mode %o to size %jd from size %jd\n",
- ino, mode, size, cursize);
+ printf("Truncating ino %ju, mode %o to size %jd from size %jd\n",
+ (uintmax_t)ino, mode, size, cursize);
/* Skip datablocks for short links and devices. */
if (mode == 0 || mode == IFBLK || mode == IFCHR ||
@@ -1586,7 +1591,8 @@ ino_trunc(ino_t ino, off_t size)
bn = DIP(ip, di_db[visitlbn]);
if (bn == 0)
- err_suj("Bad blk at ino %d lbn %jd\n", ino, visitlbn);
+ err_suj("Bad blk at ino %ju lbn %jd\n",
+ (uintmax_t)ino, visitlbn);
oldspace = sblksize(fs, cursize, visitlbn);
newspace = sblksize(fs, size, visitlbn);
if (oldspace != newspace) {
@@ -1610,8 +1616,8 @@ ino_trunc(ino_t ino, off_t size)
bn = ino_blkatoff(ip, ino, visitlbn, &frags);
if (bn == 0)
- err_suj("Block missing from ino %d at lbn %jd\n",
- ino, visitlbn);
+ err_suj("Block missing from ino %ju at lbn %jd\n",
+ (uintmax_t)ino, visitlbn);
clrsize = frags * fs->fs_fsize;
buf = dblk_read(bn, clrsize);
clrsize -= off;
@@ -1656,11 +1662,11 @@ ino_check(struct suj_ino *sino)
err_suj("Inode mode/directory type mismatch %o != %o\n",
mode, rrec->jr_mode);
if (debug)
- printf("jrefrec: op %d ino %d, nlink %d, parent %d, "
+ printf("jrefrec: op %d ino %ju, nlink %d, parent %d, "
"diroff %jd, mode %o, isat %d, isdot %d\n",
- rrec->jr_op, rrec->jr_ino, rrec->jr_nlink,
- rrec->jr_parent, rrec->jr_diroff, rrec->jr_mode,
- isat, isdot);
+ rrec->jr_op, (uintmax_t)rrec->jr_ino,
+ rrec->jr_nlink, rrec->jr_parent, rrec->jr_diroff,
+ rrec->jr_mode, isat, isdot);
mode = rrec->jr_mode & IFMT;
if (rrec->jr_op == JOP_REMREF)
removes++;
@@ -1676,8 +1682,8 @@ ino_check(struct suj_ino *sino)
* by one.
*/
if (debug)
- printf("ino %d nlink %d newlinks %d removes %d dotlinks %d\n",
- ino, nlink, newlinks, removes, dotlinks);
+ printf("ino %ju nlink %d newlinks %d removes %d dotlinks %d\n",
+ (uintmax_t)ino, nlink, newlinks, removes, dotlinks);
nlink += newlinks;
nlink -= removes;
sino->si_linkadj = 1;
@@ -1718,9 +1724,9 @@ blk_check(struct suj_blk *sblk)
sino->si_blkadj = 1;
}
if (debug)
- printf("op %d blk %jd ino %d lbn %jd frags %d isat %d (%d)\n",
- brec->jb_op, blk, brec->jb_ino, brec->jb_lbn,
- brec->jb_frags, isat, frags);
+ printf("op %d blk %jd ino %ju lbn %jd frags %d isat %d (%d)\n",
+ brec->jb_op, blk, (uintmax_t)brec->jb_ino,
+ brec->jb_lbn, brec->jb_frags, isat, frags);
/*
* If we found the block at this address we still have to
* determine if we need to free the tail end that was
@@ -1789,6 +1795,20 @@ cg_trunc(struct suj_cg *sc)
}
}
+static void
+cg_adj_blk(struct suj_cg *sc)
+{
+ struct suj_ino *sino;
+ int i;
+
+ for (i = 0; i < SUJ_HASHSIZE; i++) {
+ LIST_FOREACH(sino, &sc->sc_inohash[i], si_next) {
+ if (sino->si_blkadj)
+ ino_adjblks(sino);
+ }
+ }
+}
+
/*
* Free any partially allocated blocks and then resolve inode block
* counts.
@@ -1923,12 +1943,12 @@ ino_unlinked(void)
*/
if (DIP(ip, di_nlink) == 0) {
if (debug)
- printf("Freeing unlinked ino %d mode %o\n",
- ino, mode);
+ printf("Freeing unlinked ino %ju mode %o\n",
+ (uintmax_t)ino, mode);
ino_reclaim(ip, ino, mode);
} else if (debug)
- printf("Skipping ino %d mode %o with link %d\n",
- ino, mode, DIP(ip, di_nlink));
+ printf("Skipping ino %ju mode %o with link %d\n",
+ (uintmax_t)ino, mode, DIP(ip, di_nlink));
ino = inon;
}
}
@@ -2351,27 +2371,27 @@ suj_verifyino(union dinode *ip)
{
if (DIP(ip, di_nlink) != 1) {
- printf("Invalid link count %d for journal inode %d\n",
- DIP(ip, di_nlink), sujino);
+ printf("Invalid link count %d for journal inode %ju\n",
+ DIP(ip, di_nlink), (uintmax_t)sujino);
return (-1);
}
if ((DIP(ip, di_flags) & (SF_IMMUTABLE | SF_NOUNLINK)) !=
(SF_IMMUTABLE | SF_NOUNLINK)) {
- printf("Invalid flags 0x%X for journal inode %d\n",
- DIP(ip, di_flags), sujino);
+ printf("Invalid flags 0x%X for journal inode %ju\n",
+ DIP(ip, di_flags), (uintmax_t)sujino);
return (-1);
}
if (DIP(ip, di_mode) != (IFREG | IREAD)) {
- printf("Invalid mode %o for journal inode %d\n",
- DIP(ip, di_mode), sujino);
+ printf("Invalid mode %o for journal inode %ju\n",
+ DIP(ip, di_mode), (uintmax_t)sujino);
return (-1);
}
- if (DIP(ip, di_size) < SUJ_MIN || DIP(ip, di_size) > SUJ_MAX) {
- printf("Invalid size %jd for journal inode %d\n",
- DIP(ip, di_size), sujino);
+ if (DIP(ip, di_size) < SUJ_MIN) {
+ printf("Invalid size %jd for journal inode %ju\n",
+ DIP(ip, di_size), (uintmax_t)sujino);
return (-1);
}
@@ -2699,12 +2719,12 @@ suj_check(const char *filesys)
* Build a list of journal blocks in jblocks before parsing the
* available journal blocks in with suj_read().
*/
- printf("** Reading %jd byte journal from inode %d.\n",
- DIP(jip, di_size), sujino);
+ printf("** Reading %jd byte journal from inode %ju.\n",
+ DIP(jip, di_size), (uintmax_t)sujino);
suj_jblocks = jblocks_create();
blocks = ino_visit(jip, sujino, suj_add_block, 0);
if (blocks != numfrags(fs, DIP(jip, di_size))) {
- printf("Sparse journal inode %d.\n", sujino);
+ printf("Sparse journal inode %ju.\n", (uintmax_t)sujino);
return (-1);
}
suj_read();
@@ -2720,6 +2740,7 @@ suj_check(const char *filesys)
printf("** Processing journal entries.\n");
cg_apply(cg_trunc);
cg_apply(cg_check_blk);
+ cg_apply(cg_adj_blk);
cg_apply(cg_check_ino);
}
if (preen == 0 && (jrecs > 0 || jbytes > 0) && reply("WRITE CHANGES") == 0)
diff --git a/sbin/fsck_msdosfs/Makefile b/sbin/fsck_msdosfs/Makefile
index ddb8e8a255f0..f9dd9fae401b 100644
--- a/sbin/fsck_msdosfs/Makefile
+++ b/sbin/fsck_msdosfs/Makefile
@@ -9,6 +9,5 @@ MAN= fsck_msdosfs.8
SRCS= main.c check.c boot.c fat.c dir.c fsutil.c
CFLAGS+= -I${FSCK}
-WARNS?= 2
.include <bsd.prog.mk>
diff --git a/sbin/fsck_msdosfs/boot.c b/sbin/fsck_msdosfs/boot.c
index fe60f3a49994..319518349eb3 100644
--- a/sbin/fsck_msdosfs/boot.c
+++ b/sbin/fsck_msdosfs/boot.c
@@ -26,7 +26,7 @@
#include <sys/cdefs.h>
#ifndef lint
-__RCSID("$NetBSD: boot.c,v 1.9 2003/07/24 19:25:46 ws Exp $");
+__RCSID("$NetBSD: boot.c,v 1.11 2006/06/05 16:51:18 christos Exp ");
static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
@@ -48,8 +48,8 @@ readboot(int dosfs, struct bootblock *boot)
int ret = FSOK;
int i;
- if (read(dosfs, block, sizeof block) != sizeof block) {
- perror("could not read boot block");
+ if ((size_t)read(dosfs, block, sizeof block) != sizeof block) {
+ perr("could not read boot block");
return FSFATAL;
}
@@ -103,7 +103,7 @@ readboot(int dosfs, struct bootblock *boot)
if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec,
SEEK_SET) != boot->bpbFSInfo * boot->bpbBytesPerSec
|| read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) {
- perror("could not read fsinfo block");
+ perr("could not read fsinfo block");
return FSFATAL;
}
if (memcmp(fsinfo, "RRaA", 4)
@@ -131,7 +131,7 @@ readboot(int dosfs, struct bootblock *boot)
!= boot->bpbFSInfo * boot->bpbBytesPerSec
|| write(dosfs, fsinfo, sizeof fsinfo)
!= sizeof fsinfo) {
- perror("Unable to write bpbFSInfo");
+ perr("Unable to write bpbFSInfo");
return FSFATAL;
}
ret = FSBOOTMOD;
@@ -151,7 +151,7 @@ readboot(int dosfs, struct bootblock *boot)
SEEK_SET)
!= boot->bpbBackup * boot->bpbBytesPerSec
|| read(dosfs, backup, sizeof backup) != sizeof backup) {
- perror("could not read backup bootblock");
+ perr("could not read backup bootblock");
return FSFATAL;
}
backup[65] = block[65]; /* XXX */
@@ -242,7 +242,7 @@ writefsinfo(int dosfs, struct bootblock *boot)
if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET)
!= boot->bpbFSInfo * boot->bpbBytesPerSec
|| read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) {
- perror("could not read fsinfo block");
+ perr("could not read fsinfo block");
return FSFATAL;
}
fsinfo[0x1e8] = (u_char)boot->FSFree;
@@ -257,7 +257,7 @@ writefsinfo(int dosfs, struct bootblock *boot)
!= boot->bpbFSInfo * boot->bpbBytesPerSec
|| write(dosfs, fsinfo, sizeof fsinfo)
!= sizeof fsinfo) {
- perror("Unable to write bpbFSInfo");
+ perr("Unable to write bpbFSInfo");
return FSFATAL;
}
/*
diff --git a/sbin/fsck_msdosfs/check.c b/sbin/fsck_msdosfs/check.c
index 2f558bd5f7e6..1fb005ddd8d8 100644
--- a/sbin/fsck_msdosfs/check.c
+++ b/sbin/fsck_msdosfs/check.c
@@ -26,7 +26,7 @@
#include <sys/cdefs.h>
#ifndef lint
-__RCSID("$NetBSD: check.c,v 1.10 2000/04/25 23:02:51 jdolecek Exp $");
+__RCSID("$NetBSD: check.c,v 1.14 2006/06/05 16:51:18 christos Exp $");
static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
@@ -67,7 +67,8 @@ checkfilesys(const char *fname)
printf("\n");
if (dosfs < 0) {
- perror("Can't open");
+ perr("Can't open `%s'", fname);
+ printf("\n");
return 8;
}
diff --git a/sbin/fsck_msdosfs/dir.c b/sbin/fsck_msdosfs/dir.c
index 1dc4456bc3f3..008d0b3398f1 100644
--- a/sbin/fsck_msdosfs/dir.c
+++ b/sbin/fsck_msdosfs/dir.c
@@ -28,7 +28,7 @@
#include <sys/cdefs.h>
#ifndef lint
-__RCSID("$NetBSD: dir.c,v 1.14 1998/08/25 19:18:15 ross Exp $");
+__RCSID("$NetBSD: dir.c,v 1.20 2006/06/05 16:51:18 christos Exp $");
static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
@@ -218,25 +218,26 @@ resetDosDirSection(struct bootblock *boot, struct fatEntry *fat)
int b1, b2;
cl_t cl;
int ret = FSOK;
+ size_t len;
b1 = boot->bpbRootDirEnts * 32;
b2 = boot->bpbSecPerClust * boot->bpbBytesPerSec;
- if ((buffer = malloc( b1 > b2 ? b1 : b2)) == NULL) {
- perror("No space for directory buffer");
+ if ((buffer = malloc(len = b1 > b2 ? b1 : b2)) == NULL) {
+ perr("No space for directory buffer (%zu)", len);
return FSFATAL;
}
- if ((delbuf = malloc(b2)) == NULL) {
+ if ((delbuf = malloc(len = b2)) == NULL) {
free(buffer);
- perror("No space for directory delbuf");
+ perr("No space for directory delbuf (%zu)", len);
return FSFATAL;
}
if ((rootDir = newDosDirEntry()) == NULL) {
free(buffer);
free(delbuf);
- perror("No space for directory entry");
+ perr("No space for directory entry");
return FSFATAL;
}
@@ -328,7 +329,7 @@ delete(int f, struct bootblock *boot, struct fatEntry *fat, cl_t startcl,
off *= boot->bpbBytesPerSec;
if (lseek(f, off, SEEK_SET) != off
|| read(f, delbuf, clsz) != clsz) {
- perror("Unable to read directory");
+ perr("Unable to read directory");
return FSFATAL;
}
while (s < e) {
@@ -337,7 +338,7 @@ delete(int f, struct bootblock *boot, struct fatEntry *fat, cl_t startcl,
}
if (lseek(f, off, SEEK_SET) != off
|| write(f, delbuf, clsz) != clsz) {
- perror("Unable to write directory");
+ perr("Unable to write directory");
return FSFATAL;
}
if (startcl == endcl)
@@ -475,7 +476,7 @@ readDosDirSection(int f, struct bootblock *boot, struct fatEntry *fat,
off *= boot->bpbBytesPerSec;
if (lseek(f, off, SEEK_SET) != off
|| read(f, buffer, last) != last) {
- perror("Unable to read directory");
+ perr("Unable to read directory");
return FSFATAL;
}
last /= 32;
@@ -821,7 +822,7 @@ readDosDirSection(int f, struct bootblock *boot, struct fatEntry *fat,
/* create directory tree node */
if (!(d = newDosDirEntry())) {
- perror("No space for directory");
+ perr("No space for directory");
return FSFATAL;
}
memcpy(d, &dirent, sizeof(struct dosDirEntry));
@@ -830,7 +831,7 @@ readDosDirSection(int f, struct bootblock *boot, struct fatEntry *fat,
/* Enter this directory into the todo list */
if (!(n = newDirTodo())) {
- perror("No space for todo list");
+ perr("No space for todo list");
return FSFATAL;
}
n->next = pendingDirectories;
@@ -851,7 +852,7 @@ readDosDirSection(int f, struct bootblock *boot, struct fatEntry *fat,
last *= 32;
if (lseek(f, off, SEEK_SET) != off
|| write(f, buffer, last) != last) {
- perror("Unable to write directory");
+ perr("Unable to write directory");
return FSFATAL;
}
mod &= ~THISMOD;
@@ -870,7 +871,7 @@ readDosDirSection(int f, struct bootblock *boot, struct fatEntry *fat,
last *= 32;
if (lseek(f, off, SEEK_SET) != off
|| write(f, buffer, last) != last) {
- perror("Unable to write directory");
+ perr("Unable to write directory");
return FSFATAL;
}
mod &= ~THISMOD;
@@ -941,7 +942,7 @@ reconnect(int dosfs, struct bootblock *boot, struct fatEntry *fat, cl_t head)
if (!lfbuf) {
lfbuf = malloc(boot->ClusterSize);
if (!lfbuf) {
- perror("No space for buffer");
+ perr("No space for buffer");
return FSFATAL;
}
p = NULL;
@@ -965,7 +966,7 @@ reconnect(int dosfs, struct bootblock *boot, struct fatEntry *fat, cl_t head)
+ boot->ClusterOffset * boot->bpbBytesPerSec;
if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
|| (size_t)read(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
- perror("could not read LOST.DIR");
+ perr("could not read LOST.DIR");
return FSFATAL;
}
p = lfbuf;
@@ -995,7 +996,7 @@ reconnect(int dosfs, struct bootblock *boot, struct fatEntry *fat, cl_t head)
fat[head].flags |= FAT_USED;
if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
|| (size_t)write(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
- perror("could not write LOST.DIR");
+ perr("could not write LOST.DIR");
return FSFATAL;
}
return FSDIRMOD;
diff --git a/sbin/fsck_msdosfs/ext.h b/sbin/fsck_msdosfs/ext.h
index a4fd19b8cd72..681cde998e28 100644
--- a/sbin/fsck_msdosfs/ext.h
+++ b/sbin/fsck_msdosfs/ext.h
@@ -133,7 +133,7 @@ void finishlf(void);
/*
* Return the type of a reserved cluster as text
*/
-char *rsrvdcltype(cl_t);
+const char *rsrvdcltype(cl_t);
/*
* Clear a cluster chain in a FAT
diff --git a/sbin/fsck_msdosfs/fat.c b/sbin/fsck_msdosfs/fat.c
index 7a6368c025f6..d2d144449ec5 100644
--- a/sbin/fsck_msdosfs/fat.c
+++ b/sbin/fsck_msdosfs/fat.c
@@ -26,7 +26,7 @@
#include <sys/cdefs.h>
#ifndef lint
-__RCSID("$NetBSD: fat.c,v 1.12 2000/10/10 20:24:52 is Exp $");
+__RCSID("$NetBSD: fat.c,v 1.18 2006/06/05 16:51:18 christos Exp $");
static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
@@ -69,6 +69,7 @@ checkdirty(int fs, struct bootblock *boot)
off_t off;
u_char *buffer;
int ret = 0;
+ size_t len;
if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK)
return 0;
@@ -76,20 +77,20 @@ checkdirty(int fs, struct bootblock *boot)
off = boot->bpbResSectors;
off *= boot->bpbBytesPerSec;
- buffer = malloc(boot->bpbBytesPerSec);
+ buffer = malloc(len = boot->bpbBytesPerSec);
if (buffer == NULL) {
- perror("No space for FAT");
+ perr("No space for FAT sectors (%zu)", len);
return 1;
}
if (lseek(fs, off, SEEK_SET) != off) {
- perror("Unable to read FAT");
+ perr("Unable to read FAT");
goto err;
}
if ((size_t)read(fs, buffer, boot->bpbBytesPerSec) !=
boot->bpbBytesPerSec) {
- perror("Unable to read FAT");
+ perr("Unable to read FAT");
goto err;
}
@@ -163,10 +164,11 @@ static int
_readfat(int fs, struct bootblock *boot, u_int no, u_char **buffer)
{
off_t off;
+ size_t len;
- *buffer = malloc(boot->FATsecs * boot->bpbBytesPerSec);
+ *buffer = malloc(len = boot->FATsecs * boot->bpbBytesPerSec);
if (*buffer == NULL) {
- perror("No space for FAT");
+ perr("No space for FAT sectors (%zu)", len);
return 0;
}
@@ -174,13 +176,13 @@ _readfat(int fs, struct bootblock *boot, u_int no, u_char **buffer)
off *= boot->bpbBytesPerSec;
if (lseek(fs, off, SEEK_SET) != off) {
- perror("Unable to read FAT");
+ perr("Unable to read FAT");
goto err;
}
if ((size_t)read(fs, *buffer, boot->FATsecs * boot->bpbBytesPerSec)
!= boot->FATsecs * boot->bpbBytesPerSec) {
- perror("Unable to read FAT");
+ perr("Unable to read FAT");
goto err;
}
@@ -207,10 +209,10 @@ readfat(int fs, struct bootblock *boot, u_int no, struct fatEntry **fp)
if (!_readfat(fs, boot, no, &buffer))
return FSFATAL;
-
+
fat = malloc(len = boot->NumClusters * sizeof(struct fatEntry));
if (fat == NULL) {
- perror("No space for FAT");
+ perr("No space for FAT clusters (%zu)", len);
free(buffer);
return FSFATAL;
}
@@ -318,7 +320,7 @@ readfat(int fs, struct bootblock *boot, u_int no, struct fatEntry **fp)
/*
* Get type of reserved cluster
*/
-char *
+const char *
rsrvdcltype(cl_t cl)
{
if (cl == CLUST_FREE)
@@ -428,13 +430,13 @@ clearchain(struct bootblock *boot, struct fatEntry *fat, cl_t head)
}
int
-tryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *trunc)
+tryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *truncp)
{
if (ask(0, "Clear chain starting at %u", head)) {
clearchain(boot, fat, head);
return FSFATMOD;
} else if (ask(0, "Truncate")) {
- *trunc = CLUST_EOF;
+ *truncp = CLUST_EOF;
return FSFATMOD;
} else
return FSERROR;
@@ -549,7 +551,7 @@ writefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat)
buffer = malloc(fatsz = boot->FATsecs * boot->bpbBytesPerSec);
if (buffer == NULL) {
- perror("No space for FAT");
+ perr("No space for FAT sectors (%zu)", fatsz);
return FSFATAL;
}
memset(buffer, 0, fatsz);
@@ -598,7 +600,7 @@ writefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat)
free(old_fat);
p += count;
}
-
+
for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) {
switch (boot->ClustMask) {
case CLUST32_MASK:
@@ -634,7 +636,7 @@ writefat(int fs, struct bootblock *boot, struct fatEntry *fat, int correct_fat)
off *= boot->bpbBytesPerSec;
if (lseek(fs, off, SEEK_SET) != off
|| (size_t)write(fs, buffer, fatsz) != fatsz) {
- perror("Unable to write FAT");
+ perr("Unable to write FAT");
ret = FSFATAL; /* Return immediately? XXX */
}
}
diff --git a/sbin/fsdb/fsdb.c b/sbin/fsdb/fsdb.c
index 172265368208..e169061e9f65 100644
--- a/sbin/fsdb/fsdb.c
+++ b/sbin/fsdb/fsdb.c
@@ -39,6 +39,7 @@ static const char rcsid[] =
#include <grp.h>
#include <histedit.h>
#include <pwd.h>
+#include <stdint.h>
#include <string.h>
#include <time.h>
#include <timeconv.h>
@@ -211,7 +212,8 @@ char *
prompt(EditLine *el)
{
static char pstring[64];
- snprintf(pstring, sizeof(pstring), "fsdb (inum: %d)> ", curinum);
+ snprintf(pstring, sizeof(pstring), "fsdb (inum: %ju)> ",
+ (uintmax_t)curinum);
return pstring;
}
@@ -298,8 +300,8 @@ ino_t curinum, ocurrent;
#define GETINUM(ac,inum) inum = strtoul(argv[ac], &cp, 0); \
if (inum < ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \
- printf("inode %d out of range; range is [%d,%d]\n", \
- inum, ROOTINO, maxino); \
+ printf("inode %ju out of range; range is [%ju,%ju]\n", \
+ (uintmax_t)inum, (uintmax_t)ROOTINO, (uintmax_t)maxino); \
return 1; \
}
@@ -364,7 +366,8 @@ CMDFUNCSTART(uplink)
if (!checkactive())
return 1;
DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) + 1);
- printf("inode %d link count now %d\n", curinum, DIP(curinode, di_nlink));
+ printf("inode %ju link count now %d\n",
+ (uintmax_t)curinum, DIP(curinode, di_nlink));
inodirty();
return 0;
}
@@ -374,7 +377,8 @@ CMDFUNCSTART(downlink)
if (!checkactive())
return 1;
DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) - 1);
- printf("inode %d link count now %d\n", curinum, DIP(curinode, di_nlink));
+ printf("inode %ju link count now %d\n",
+ (uintmax_t)curinum, DIP(curinode, di_nlink));
inodirty();
return 0;
}
@@ -493,11 +497,11 @@ CMDFUNCSTART(findblk)
if (is_ufs2 ?
compare_blk64(wantedblk64, ino_to_fsba(&sblock, inum)) :
compare_blk32(wantedblk32, ino_to_fsba(&sblock, inum))) {
- printf("block %llu: inode block (%d-%d)\n",
+ printf("block %llu: inode block (%ju-%ju)\n",
(unsigned long long)fsbtodb(&sblock,
ino_to_fsba(&sblock, inum)),
- (inum / INOPB(&sblock)) * INOPB(&sblock),
- (inum / INOPB(&sblock) + 1) * INOPB(&sblock));
+ (uintmax_t)(inum / INOPB(&sblock)) * INOPB(&sblock),
+ (uintmax_t)(inum / INOPB(&sblock) + 1) * INOPB(&sblock));
findblk_numtofind--;
if (findblk_numtofind == 0)
goto end;
@@ -593,8 +597,8 @@ static int
founddatablk(uint64_t blk)
{
- printf("%llu: data block of inode %d\n",
- (unsigned long long)fsbtodb(&sblock, blk), curinum);
+ printf("%llu: data block of inode %ju\n",
+ (unsigned long long)fsbtodb(&sblock, blk), (uintmax_t)curinum);
findblk_numtofind--;
if (findblk_numtofind == 0)
return 1;
@@ -753,7 +757,7 @@ CMDFUNCSTART(ln)
return 1;
rval = makeentry(curinum, inum, argv[2]);
if (rval)
- printf("Ino %d entered as `%s'\n", inum, argv[2]);
+ printf("Ino %ju entered as `%s'\n", (uintmax_t)inum, argv[2]);
else
printf("could not enter name? weird.\n");
curinode = ginode(curinum);
diff --git a/sbin/fsdb/fsdbutil.c b/sbin/fsdb/fsdbutil.c
index 5d6f16d5d864..eaea3db79fd5 100644
--- a/sbin/fsdb/fsdbutil.c
+++ b/sbin/fsdb/fsdbutil.c
@@ -152,7 +152,7 @@ printstat(const char *cp, ino_t inum, union dinode *dp)
puts("fifo");
break;
}
- printf("I=%lu MODE=%o SIZE=%ju", (u_long)inum, DIP(dp, di_mode),
+ printf("I=%ju MODE=%o SIZE=%ju", (uintmax_t)inum, DIP(dp, di_mode),
(uintmax_t)DIP(dp, di_size));
if (sblock.fs_magic != FS_UFS1_MAGIC) {
t = _time64_to_time(dp->dp2.di_birthtime);
@@ -290,7 +290,7 @@ printblocks(ino_t inum, union dinode *dp)
long ndb, offset;
ufs2_daddr_t blkno;
- printf("Blocks for inode %d:\n", inum);
+ printf("Blocks for inode %ju:\n", (uintmax_t)inum);
printf("Direct blocks:\n");
ndb = howmany(DIP(dp, di_size), sblock.fs_bsize);
for (i = 0; i < NDADDR && i < ndb; i++) {
@@ -338,7 +338,7 @@ checkactivedir(void)
return 0;
}
if ((DIP(curinode, di_mode) & IFMT) != IFDIR) {
- warnx("inode %d not a directory", curinum);
+ warnx("inode %ju not a directory", (uintmax_t)curinum);
return 0;
}
return 1;
@@ -363,11 +363,12 @@ printactive(int doblocks)
printstat("current inode", curinum, curinode);
break;
case 0:
- printf("current inode %d: unallocated inode\n", curinum);
+ printf("current inode %ju: unallocated inode\n", (uintmax_t)curinum);
break;
default:
- printf("current inode %d: screwy itype 0%o (mode 0%o)?\n",
- curinum, DIP(curinode, di_mode) & IFMT, DIP(curinode, di_mode));
+ printf("current inode %ju: screwy itype 0%o (mode 0%o)?\n",
+ (uintmax_t)curinum, DIP(curinode, di_mode) & IFMT,
+ DIP(curinode, di_mode));
break;
}
return 0;
diff --git a/sbin/fsirand/fsirand.c b/sbin/fsirand/fsirand.c
index ca7841a46f3e..c373c5bb56f5 100644
--- a/sbin/fsirand/fsirand.c
+++ b/sbin/fsirand/fsirand.c
@@ -274,8 +274,8 @@ fsirand(char *device)
dp2 = &((struct ufs2_dinode *)inodebuf)[n];
if (inumber >= ROOTINO) {
if (printonly)
- (void)printf("ino %d gen %08x\n",
- inumber,
+ (void)printf("ino %ju gen %08x\n",
+ (uintmax_t)inumber,
sblock->fs_magic == FS_UFS1_MAGIC ?
dp1->di_gen : dp2->di_gen);
else if (sblock->fs_magic == FS_UFS1_MAGIC)
diff --git a/sbin/geom/class/eli/geli.8 b/sbin/geom/class/eli/geli.8
index 0803480450f1..3cb1f211769f 100644
--- a/sbin/geom/class/eli/geli.8
+++ b/sbin/geom/class/eli/geli.8
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd April 28, 2012
+.Dd June 18, 2012
.Dt GELI 8
.Os
.Sh NAME
@@ -186,14 +186,15 @@ one of the following algorithms:
or
.Nm HMAC/SHA512 .
.It
-Can create a key from a couple of components (user entered passphrase, random
-bits from a file, etc.).
+Can create a User Key from up to two, piecewise components: a passphrase
+entered via prompt or read from one or more passfiles; a keyfile read from
+one or more files.
.It
Allows encryption of the root partition.
The user will be asked for the
passphrase before the root file system is mounted.
.It
-The passphrase of the user is strengthened with:
+Strengthens the passphrase component of the User Key with:
.Rs
.%A B. Kaliski
.%T "PKCS #5: Password-Based Cryptography Specification, Version 2.0."
@@ -201,7 +202,7 @@ The passphrase of the user is strengthened with:
.%N 2898
.Re
.It
-Allows the use of two independent keys (e.g., a
+Allows the use of two independent User Keys (e.g., a
.Qq "user key"
and a
.Qq "company key" ) .
@@ -210,8 +211,8 @@ It is fast -
.Nm
performs simple sector-to-sector encryption.
.It
-Allows Master Keys to be backed up and restored,
-so that if a user has to quickly destroy his keys,
+Allows the encrypted Master Key to be backed up and restored,
+so that if a user has to quickly destroy key material,
it is possible to get the data back by restoring keys from
backup.
.It
@@ -219,8 +220,8 @@ Providers can be configured to automatically detach on last close
(so users do not have to remember to detach providers after unmounting
the file systems).
.It
-Allows attaching a provider with a random, one-time key - useful for swap
-partitions and temporary file systems.
+Allows attaching a provider with a random, one-time Master Key -
+useful for swap partitions and temporary file systems.
.It
Allows verification of data integrity (data authentication).
.It
@@ -233,7 +234,8 @@ indicates an action to be performed:
.Bl -tag -width ".Cm configure"
.It Cm init
Initialize the provider which needs to be encrypted.
-Here you can set up the cryptographic algorithm to use, key length, etc.
+Here you can set up the cryptographic algorithm to use, Data Key length,
+etc.
The last sector of the provider is used to store metadata.
The
.Cm init
@@ -289,37 +291,58 @@ and
The default and recommended algorithm is
.Nm AES-XTS .
.It Fl i Ar iterations
-Number of iterations to use with PKCS#5v2.
+Number of iterations to use with PKCS#5v2 when processing User Key
+passphrase component.
If this option is not specified,
.Nm
will find the number of iterations which is equal to 2 seconds of crypto work.
If 0 is given, PKCS#5v2 will not be used.
+PKCS#5v2 processing is performed once, after all parts of the passphrase
+component have been read.
.It Fl J Ar newpassfile
-Specifies a file which contains the passphrase or its part.
+Specifies a file which contains the passphrase component of the User Key
+(or part of it).
If
.Ar newpassfile
is given as -, standard input will be used.
Only the first line (excluding new-line character) is taken from the given file.
-This argument can be specified multiple times.
+This argument can be specified multiple times, which has the effect of
+reassembling a single passphrase split across multiple files.
+Cannot be combined with the
+.Fl P
+option.
.It Fl K Ar newkeyfile
-Specifies a file which contains part of the key.
+Specifies a file which contains the keyfile component of the User Key
+(or part of it).
If
.Ar newkeyfile
is given as -, standard input will be used.
-This argument can be specified multiple times.
+This argument can be specified multiple times, which has the effect of
+reassembling a single keyfile split across multiple keyfile parts.
.It Fl l Ar keylen
-Key length to use with the given cryptographic algorithm.
-If not given, the default key length for the given algorithm is used, which is:
-128 for
-.Nm AES-XTS ,
-.Nm AES-CBC ,
-.Nm Blowfish-CBC
-and
-.Nm Camellia-CBC
-and 192 for
-.Nm 3DES-CBC .
+Data Key length to use with the given cryptographic algorithm.
+If the length is not specified, the selected algorithm uses its
+.Em default
+key length.
+.Bl -ohang -offset indent
+.It Nm AES-XTS
+.Em 128 ,
+256
+.It Nm AES-CBC , Nm Camilla-CBC
+.Em 128 ,
+192,
+256
+.It Nm Blowfish-CBC
+.Em 128
++ n * 32, for n=[0..10]
+.It Nm 3DES-CBC
+.Em 192
+.El
.It Fl P
-Do not use passphrase as the key component.
+Do not use a passphrase as a component of the User Key.
+Cannot be combined with the
+.Fl J
+option.
.It Fl s Ar sectorsize
Change decrypted provider's sector size.
Increasing the sector size allows increased performance,
@@ -337,9 +360,9 @@ Note that using older metadata version may limit numer of features available.
.El
.It Cm attach
Attach the given provider.
-The master key will be decrypted using the given
-passphrase/keyfile and a new GEOM provider will be created using the given
-provider's name with an
+The encrypted Master Key will be loaded from the metadata and decrypted
+using the given passphrase/keyfile and a new GEOM provider will be created
+using the given provider's name with an
.Qq .eli
suffix.
.Pp
@@ -357,28 +380,33 @@ option for the
.Cm detach
subcommand.
.It Fl j Ar passfile
-Specifies a file which contains the passphrase or its part.
+Specifies a file which contains the passphrase component of the User Key
+(or part of it).
For more information see the description of the
.Fl J
option for the
.Cm init
subcommand.
.It Fl k Ar keyfile
-Specifies a file which contains part of the key.
+Specifies a file which contains the keyfile component of the User Key
+(or part of it).
For more information see the description of the
.Fl K
option for the
.Cm init
subcommand.
.It Fl p
-Do not use passphrase as the key component.
+Do not use a passphrase as a component of the User Key.
+Cannot be combined with the
+.Fl j
+option.
.It Fl r
Attach read-only provider.
It will not be opened for writing.
.El
.It Cm detach
Detach the given providers, which means remove the devfs entry
-and clear the keys from memory.
+and clear the Master Key and Data Keys from memory.
.Pp
Additional options include:
.Bl -tag -width ".Fl f"
@@ -391,7 +419,7 @@ while it is open, but will be automatically detached when it is closed for the
last time even if it was only opened for reading.
.El
.It Cm onetime
-Attach the given providers with random, one-time keys.
+Attach the given providers with a random, one-time (ephemeral) Master Key.
The command can be used to encrypt swap partitions or temporary file systems.
.Pp
Additional options include:
@@ -415,7 +443,7 @@ For more information, see the description of the
.Cm attach
subcommand.
.It Fl l Ar keylen
-Key length to use with the given cryptographic algorithm.
+Data Key length to use with the given cryptographic algorithm.
For more information, see the description of the
.Cm init
subcommand.
@@ -439,15 +467,18 @@ subcommand.
Remove the BOOT flag from the given providers.
.El
.It Cm setkey
-Change or setup (if not yet initialized) selected key.
-There is one master key, which can be encrypted with two independent user keys.
+Install a copy of the Master Key into the selected slot, encrypted with
+a new User Key.
+If the selected slot is populated, replace the existing copy.
+A provider has one Master Key, which can be stored in one or both slots,
+each encrypted with an independent User Key.
With the
.Cm init
subcommand, only key number 0 is initialized.
-The key can always be changed: for an attached provider,
+The User Key can be changed at any time: for an attached provider,
for a detached provider, or on the backup file.
When a provider is attached, the user does not have to provide
-an old passphrase/keyfile.
+an existing passphrase/keyfile.
.Pp
Additional options include:
.Bl -tag -width ".Fl J Ar newpassfile"
@@ -458,44 +489,54 @@ To be able to use this option with the
.Cm setkey
subcommand, only one key has to be defined and this key must be changed.
.It Fl j Ar passfile
-Specifies a file which contains the old passphrase or its part.
+Specifies a file which contains the passphrase component of a current User Key
+(or part of it).
.It Fl J Ar newpassfile
-Specifies a file which contains the new passphrase or its part.
+Specifies a file which contains the passphrase component of the new User Key
+(or part of it).
.It Fl k Ar keyfile
-Specifies a file which contains part of the old key.
+Specifies a file which contains the keyfile component of a current User Key
+(or part of it).
.It Fl K Ar newkeyfile
-Specifies a file which contains part of the new key.
+Specifies a file which contains the keyfile component of the new User Key
+(or part of it).
.It Fl n Ar keyno
-Specifies the number of the key to change (could be 0 or 1).
+Specifies the index number of the Master Key copy to change (could be 0 or 1).
If the provider is attached and no key number is given, the key
used for attaching the provider will be changed.
If the provider is detached (or we are operating on a backup file)
-and no key number is given, the key decrypted with the passphrase/keyfile
-will be changed.
+and no key number is given, the first Master Key copy to be successfully
+decrypted with the provided User Key passphrase/keyfile will be changed.
.It Fl p
-Do not use passphrase as the old key component.
+Do not use a passphrase as a component of the current User Key.
+Cannot be combined with the
+.Fl j
+option.
.It Fl P
-Do not use passphrase as the new key component.
+Do not use a passphrase as a component of the new User Key.
+Cannot be combined with the
+.Fl J
+option.
.El
.It Cm delkey
-Destroy (overwrite with random data) the selected key.
+Destroy (overwrite with random data) the selected Master Key copy.
If one is destroying keys for an attached provider, the provider
-will not be detached even if all keys are destroyed.
+will not be detached even if all copies of the Master Key are destroyed.
It can even be rescued with the
.Cm setkey
-subcommand.
+subcommand because the Master Key is still in memory.
.Pp
Additional options include:
.Bl -tag -width ".Fl a Ar keyno"
.It Fl a
-Destroy all keys (does not need
+Destroy all copies of the Master Key (does not need
.Fl f
option).
.It Fl f
Force key destruction.
-This option is needed to destroy the last key.
+This option is needed to destroy the last copy of the Master Key.
.It Fl n Ar keyno
-Specifies the key number.
+Specifies the index number of the Master Key copy.
If the provider is attached and no key number is given, the key
used for attaching the provider will be destroyed.
If provider is detached (or we are operating on a backup file) the key number
@@ -503,8 +544,8 @@ has to be given.
.El
.It Cm kill
This command should be used only in emergency situations.
-It will destroy all the keys on a given provider and will detach it forcibly
-(if it is attached).
+It will destroy all copies of the Master Key on a given provider and will
+detach it forcibly (if it is attached).
This is absolutely a one-way command - if you do not have a metadata
backup, your data is gone for good.
In case the provider was attached with the
@@ -542,8 +583,8 @@ and
.El
.It Cm suspend
Suspend device by waiting for all inflight requests to finish, clearing all
-sensitive information (like keys) from kernel memory, and blocking all
-further I/O requests until the
+sensitive information (like the Master Key and Data Keys) from kernel memory,
+and blocking all further I/O requests until the
.Cm resume
subcommand is executed.
This functionality is useful for laptops: when one wants to suspend a
@@ -553,8 +594,8 @@ on an encrypted device, unmounting the file system, and detaching the device,
the
.Cm suspend
subcommand can be used.
-Any access to the encrypted device will be blocked until the keys are
-recovered through the
+Any access to the encrypted device will be blocked until the Master Key is
+reloaded through the
.Cm resume
subcommand.
Thus there is no need to close nor unmount anything.
@@ -584,21 +625,26 @@ utility is stored is bad idea.
Additional options include:
.Bl -tag -width ".Fl j Ar passfile"
.It Fl j Ar passfile
-Specifies a file which contains the passphrase or its part.
+Specifies a file which contains the passphrase component of the User Key
+(or part of it).
For more information see the description of the
.Fl J
option for the
.Cm init
subcommand.
.It Fl k Ar keyfile
-Specifies a file which contains part of the key.
+Specifies a file which contains the keyfile component of the User Key
+(or part of it).
For more information see the description of the
.Fl K
option for the
.Cm init
subcommand.
.It Fl p
-Do not use passphrase as the key component.
+Do not use a passphrase as a component of the User Key.
+Cannot be combined with the
+.Fl j
+option.
.El
.It Cm resize
Inform
@@ -626,6 +672,9 @@ If GEOM providers are specified, the
subcommand will print metadata version used by each of them.
.It Cm clear
Clear metadata from the given providers.
+.Em WARNING :
+This will erase with zeros the encrypted Master Key copies stored in the
+metadata.
.It Cm dump
Dump metadata stored on the given providers.
.It Cm list
@@ -647,6 +696,36 @@ Additional options include:
.It Fl v
Be more verbose.
.El
+.Sh KEY SUMMARY
+.Ss Master Key
+Upon
+.Cm init ,
+the
+.Nm
+utility generates a random Master Key for the provider.
+The Master Key never changes during the lifetime of the provider.
+Each copy of the provider metadata, active or backed up to a file, can store
+up to two, independently-encrypted copies of the Master Key.
+.Ss User Key
+Each stored copy of the Master Key is encrypted with a User Key, which
+is generated by the
+.Nm
+utility from a passphrase and/or a keyfile.
+The
+.Nm
+utility first reads all parts of the keyfile in the order specified on the
+command line, then reads all parts of the stored passphrase in the order
+specified on the command line.
+If no passphrase parts are specified, the system prompts the user to enter
+the passphrase.
+The passphrase is optionally strengthened by PKCS#5v2.
+The User Key is a digest computed over the concatenated keyfile and passphrase.
+.Ss Data Key
+During operation, one or more Data Keys are deterministically derived by
+the kernel from the Master Key and cached in memory.
+The number of Data Keys used by a given provider, and the way they are
+derived, depend on the GELI version and whether the provider is configured to
+use data authentication.
.Sh SYSCTL VARIABLES
The following
.Xr sysctl 8
@@ -677,7 +756,7 @@ If set to 0, attaching providers on boot will be disabled.
This variable should be set in
.Pa /boot/loader.conf .
.It Va kern.geom.eli.overwrites : No 5
-Specifies how many times the Master-Key will be overwritten
+Specifies how many times the Master Key will be overwritten
with random values when it is destroyed.
After this operation it is filled with zeros.
.It Va kern.geom.eli.visible_passphrase : No 0
@@ -699,18 +778,19 @@ Batching reduces the number of interrupts by responding to a group of
crypto requests with one interrupt.
The crypto card and the driver has to support this feature.
.It Va kern.geom.eli.key_cache_limit : No 8192
-Specifies how many encryption keys to cache.
+Specifies how many Data Keys to cache.
The default limit
(8192 keys) will allow caching of all keys for a 4TB provider with 512 byte
sectors and will take around 1MB of memory.
.It Va kern.geom.eli.key_cache_hits
-Reports how many times we were looking up a key and it was already in cache.
-This sysctl is not updated for providers that need less keys than the limit
-specified in
+Reports how many times we were looking up a Data Key and it was already in
+cache.
+This sysctl is not updated for providers that need fewer Data Keys than
+the limit specified in
.Va kern.geom.eli.key_cache_limit .
.It Va kern.geom.eli.key_cache_misses
-Reports how many times we were looking up a key and it was not in cache.
-This sysctl is not updated for providers that need fewer keys than the limit
+Reports how many times we were looking up a Data Key and it was not in cache.
+This sysctl is not updated for providers that need fewer Data Keys than the limit
specified in
.Va kern.geom.eli.key_cache_limit .
.El
@@ -738,7 +818,7 @@ Enter passphrase:
# geli detach da2.eli
.Ed
.Pp
-Create an encrypted provider, but use two keys:
+Create an encrypted provider, but use two User Keys:
one for your employee and one for you as the company's security officer
(so it's not a tragedy if the employee
.Qq accidentally
@@ -760,7 +840,7 @@ forget their passphrases, so backup the Master Key with your own random key:
# dd if=/dev/random of=/mnt/pendrive/keys/`hostname` bs=64 count=1
# geli init -P -K /mnt/pendrive/keys/`hostname` /dev/ad0s1e
# geli backup /dev/ad0s1e /mnt/pendrive/backups/`hostname`
-(use key number 0, so the encrypted Master Key will be overwritten by this)
+(use key number 0, so the encrypted Master Key will be re-encrypted by this)
# geli setkey -n 0 -k /mnt/pendrive/keys/`hostname` /dev/ad0s1e
(allow the user to enter his passphrase)
Enter new passphrase:
@@ -776,8 +856,8 @@ Encrypted swap partition setup:
.Pp
The example below shows how to configure two providers which will be attached
on boot (before the root file system is mounted).
-One of them is using passphrase and three keyfiles and the other is using only a
-keyfile:
+One of them is using passphrase and three keyfile parts and the other is
+using only a keyfile in one part:
.Bd -literal -offset indent
# dd if=/dev/random of=/dev/da0 bs=1m
# dd if=/dev/random of=/boot/keys/da0.key0 bs=32k count=1
diff --git a/sbin/geom/class/multipath/geom_multipath.c b/sbin/geom/class/multipath/geom_multipath.c
index a821951d3bee..cdf35d0381ec 100644
--- a/sbin/geom/class/multipath/geom_multipath.c
+++ b/sbin/geom/class/multipath/geom_multipath.c
@@ -49,6 +49,7 @@ uint32_t version = G_MULTIPATH_VERSION;
static void mp_main(struct gctl_req *, unsigned int);
static void mp_label(struct gctl_req *);
static void mp_clear(struct gctl_req *);
+static void mp_prefer(struct gctl_req *);
struct g_command class_commands[] = {
{
@@ -87,6 +88,10 @@ struct g_command class_commands[] = {
"[-v] name prov"
},
{
+ "prefer", G_FLAG_VERBOSE, mp_main, G_NULL_OPTS,
+ "[-v] prov ..."
+ },
+ {
"fail", G_FLAG_VERBOSE, NULL, G_NULL_OPTS,
"[-v] name prov"
},
@@ -131,6 +136,8 @@ mp_main(struct gctl_req *req, unsigned int flags __unused)
mp_label(req);
} else if (strcmp(name, "clear") == 0) {
mp_clear(req);
+ } else if (strcmp(name, "prefer") == 0) {
+ mp_prefer(req);
} else {
gctl_error(req, "Unknown command: %s.", name);
}
@@ -294,3 +301,22 @@ mp_clear(struct gctl_req *req)
}
}
+static void
+mp_prefer(struct gctl_req *req)
+{
+ const char *name, *comp, *errstr;
+ int nargs;
+
+ nargs = gctl_get_int(req, "nargs");
+ if (nargs != 2) {
+ gctl_error(req, "Usage: prefer GEOM PROVIDER");
+ return;
+ }
+ name = gctl_get_ascii(req, "arg0");
+ comp = gctl_get_ascii(req, "arg1");
+ errstr = gctl_issue (req);
+ if (errstr != NULL) {
+ fprintf(stderr, "Can't set %s preferred provider to %s: %s.\n",
+ name, comp, errstr);
+ }
+}
diff --git a/sbin/geom/class/multipath/gmultipath.8 b/sbin/geom/class/multipath/gmultipath.8
index 55a86f339314..81b85ddbd642 100644
--- a/sbin/geom/class/multipath/gmultipath.8
+++ b/sbin/geom/class/multipath/gmultipath.8
@@ -66,6 +66,11 @@
.Op Fl v
.Ar name
.Nm
+.Cm prefer
+.Op Fl v
+.Ar name
+.Ar prov
+.Nm
.Cm getactive
.Op Fl v
.Ar name
@@ -171,7 +176,9 @@ If there are other paths present, new requests will be forwarded there.
Mark specified provider as a path of the specified multipath device as
operational, allowing it to handle requests.
.It Cm rotate
-Change the active provider/path in Active/Passive mode.
+Change the active provider/path to the next available provider in Active/Passive mode.
+.It Cm prefer
+Change the active provider/path to the specified provider in Active/Passive mode.
.It Cm getactive
Get the currently active provider(s)/path(s).
.It Cm destroy
diff --git a/sbin/geom/class/part/gpart.8 b/sbin/geom/class/part/gpart.8
index 2e745c40fca3..7a91d3dbc07c 100644
--- a/sbin/geom/class/part/gpart.8
+++ b/sbin/geom/class/part/gpart.8
@@ -1006,11 +1006,12 @@ or
but smaller than 545 kB since the first-stage loader will load the
entire partition into memory during boot, regardless of how much data
it actually contains.
-This example uses 94 blocks (47 kB) so the next partition will be
+This example uses 88 blocks (44 kB) so the next partition will be
aligned on a 64 kB boundary without the need to specify an explicit
offset or alignment.
+The boot partition itself is aligned on a 4 kB boundary.
.Bd -literal -offset indent
-/sbin/gpart add -b 34 -s 94 -t freebsd-boot ad0
+/sbin/gpart add -b 40 -s 88 -t freebsd-boot ad0
/sbin/gpart bootcode -p /boot/gptboot -i 1 ad0
.Ed
.Pp
diff --git a/sbin/geom/class/raid/graid.8 b/sbin/geom/class/raid/graid.8
index 6b8cd61dbf56..630cd018e621 100644
--- a/sbin/geom/class/raid/graid.8
+++ b/sbin/geom/class/raid/graid.8
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd May 6, 2012
+.Dd September 13, 2012
.Dt GRAID 8
.Os
.Sh NAME
@@ -85,7 +85,7 @@ utility is used to manage software RAID configurations, supported by the
GEOM RAID class.
GEOM RAID class uses on-disk metadata to provide access to software-RAID
volumes defined by different RAID BIOSes.
-Depending on RAID BIOS type and it's metadata format, different subsets of
+Depending on RAID BIOS type and its metadata format, different subsets of
configurations and features are supported.
To allow booting from RAID volume, the metadata format should match the
RAID BIOS type and its capabilities.
@@ -293,6 +293,8 @@ Mark volume as clean when idle for the specified number of seconds.
Debug level of the
.Nm RAID
GEOM class.
+.It Va kern.geom.raid.enable : No 1
+Enable on-disk metadata taste.
.It Va kern.geom.raid.idle_threshold : No 1000000
Time in microseconds to consider a volume idle for rebuild purposes.
.It Va kern.geom.raid.name_format : No 0
@@ -302,6 +304,8 @@ Number of read errors equated to disk failure.
Write errors are always considered as disk failures.
.It Va kern.geom.raid.start_timeout : No 30
Time to wait for missing array components on startup.
+.It Va kern.geom.raid. Ns Ar X Ns Va .enable : No 1
+Enable taste for specific metadata or transformation module.
.El
.Sh EXIT STATUS
Exit status is 0 on success, and non-zero if the command fails.
diff --git a/sbin/geom/class/sched/gsched.8 b/sbin/geom/class/sched/gsched.8
index bb3c1cfe5cb5..ae04865a21b1 100644
--- a/sbin/geom/class/sched/gsched.8
+++ b/sbin/geom/class/sched/gsched.8
@@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd August 29, 2010
+.Dd July 26, 2012
.Dt GSCHED 8
.Os
.Sh NAME
@@ -135,19 +135,19 @@ maximum amount of debug information is printed.
Exit status is 0 on success, and 1 if the command fails.
.Sh EXAMPLES
The following example shows how to create a scheduling provider for disk
-.Pa /dev/ad0 ,
+.Pa /dev/ada0 ,
and how to destroy it.
.Bd -literal -offset indent
# Load the geom_sched module:
kldload geom_sched
# Load some scheduler classes used by geom_sched:
kldload gsched_rr
-# Configure device ad0 to use scheduler "rr":
-geom sched insert -a rr ad0
-# Now provider ad0 uses the "rr" algorithm;
-# the new geom is ad0.sched.
+# Configure device ada0 to use scheduler "rr":
+geom sched insert -a rr ada0
+# Now provider ada0 uses the "rr" algorithm;
+# the new geom is ada0.sched.
# Remove the scheduler on the device:
-geom sched destroy -v ad0.sched.
+geom sched destroy -v ada0.sched.
.Ed
.Sh SEE ALSO
.Xr geom 4 ,
diff --git a/sbin/geom/class/virstor/gvirstor.8 b/sbin/geom/class/virstor/gvirstor.8
index cdb50b1d0f53..99eff52fe571 100644
--- a/sbin/geom/class/virstor/gvirstor.8
+++ b/sbin/geom/class/virstor/gvirstor.8
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd January 24, 2011
+.Dd August 3, 2012
.Dt GVIRSTOR 8
.Os
.Sh NAME
@@ -43,6 +43,10 @@
.Op Fl fv
.Ar name ...
.Nm
+.Cm destroy
+.Op Fl fv
+.Ar name ...
+.Nm
.Cm add
.Op Fl vh
.Ar name prov ...
@@ -107,6 +111,9 @@ Turn off an existing virtual device with the given
.Ar name .
This command does not touch on-disk metadata.
As with other GEOM classes, stopped geoms cannot be started manually.
+.It Cm destroy
+Same as
+.Cm stop.
.It Cm add
Adds new components to existing virtual device with the given
.Ar name .
diff --git a/sbin/geom/core/geom.c b/sbin/geom/core/geom.c
index 770e960f3c05..8c15c2143e1e 100644
--- a/sbin/geom/core/geom.c
+++ b/sbin/geom/core/geom.c
@@ -74,7 +74,7 @@ static void std_status(struct gctl_req *req, unsigned flags);
static void std_load(struct gctl_req *req, unsigned flags);
static void std_unload(struct gctl_req *req, unsigned flags);
-struct g_command std_commands[] = {
+static struct g_command std_commands[] = {
{ "help", 0, std_help, G_NULL_OPTS, NULL },
{ "list", 0, std_list,
{
diff --git a/sbin/ggate/ggatec/ggatec.c b/sbin/ggate/ggatec/ggatec.c
index 660bd8ab469b..6f9263c8c30d 100644
--- a/sbin/ggate/ggatec/ggatec.c
+++ b/sbin/ggate/ggatec/ggatec.c
@@ -55,7 +55,7 @@
#include "ggate.h"
-enum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET;
+static enum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET;
static const char *path = NULL;
static const char *host = NULL;
diff --git a/sbin/ggate/ggated/ggated.c b/sbin/ggate/ggated/ggated.c
index 2997a9cf2c69..01aa00aca645 100644
--- a/sbin/ggate/ggated/ggated.c
+++ b/sbin/ggate/ggated/ggated.c
@@ -92,12 +92,12 @@ struct ggd_export {
static const char *exports_file = GGATED_EXPORT_FILE;
static int got_sighup = 0;
-in_addr_t bindaddr;
+static in_addr_t bindaddr;
static TAILQ_HEAD(, ggd_request) inqueue = TAILQ_HEAD_INITIALIZER(inqueue);
static TAILQ_HEAD(, ggd_request) outqueue = TAILQ_HEAD_INITIALIZER(outqueue);
-pthread_mutex_t inqueue_mtx, outqueue_mtx;
-pthread_cond_t inqueue_cond, outqueue_cond;
+static pthread_mutex_t inqueue_mtx, outqueue_mtx;
+static pthread_cond_t inqueue_cond, outqueue_cond;
static SLIST_HEAD(, ggd_export) exports = SLIST_HEAD_INITIALIZER(exports);
static LIST_HEAD(, ggd_connection) connections = LIST_HEAD_INITIALIZER(connections);
diff --git a/sbin/ggate/ggatel/ggatel.c b/sbin/ggate/ggatel/ggatel.c
index d9e49db074a2..abfe7c18e41d 100644
--- a/sbin/ggate/ggatel/ggatel.c
+++ b/sbin/ggate/ggatel/ggatel.c
@@ -47,7 +47,7 @@
#include "ggate.h"
-enum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET;
+static enum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET;
static const char *path = NULL;
static int unit = G_GATE_UNIT_AUTO;
diff --git a/sbin/ggate/shared/ggate.h b/sbin/ggate/shared/ggate.h
index 3e26253c360c..898efea6507e 100644
--- a/sbin/ggate/shared/ggate.h
+++ b/sbin/ggate/shared/ggate.h
@@ -95,8 +95,8 @@ struct g_gate_hdr {
void g_gate_vlog(int priority, const char *message, va_list ap);
void g_gate_log(int priority, const char *message, ...);
-void g_gate_xvlog(const char *message, va_list ap);
-void g_gate_xlog(const char *message, ...);
+void g_gate_xvlog(const char *message, va_list ap) __dead2;
+void g_gate_xlog(const char *message, ...) __dead2;
off_t g_gate_mediasize(int fd);
unsigned g_gate_sectorsize(int fd);
void g_gate_open_device(void);
diff --git a/sbin/growfs/growfs.c b/sbin/growfs/growfs.c
index b610bd2f8c8f..ad837a82f52e 100644
--- a/sbin/growfs/growfs.c
+++ b/sbin/growfs/growfs.c
@@ -324,6 +324,7 @@ initcg(int cylno, time_t modtime, int fso, unsigned int Nflag)
DBG_FUNC("initcg")
static caddr_t iobuf;
long blkno, start;
+ ino_t ino;
ufs2_daddr_t i, cbase, dmax;
struct ufs1_dinode *dp1;
struct csum *cs;
@@ -392,8 +393,8 @@ initcg(int cylno, time_t modtime, int fso, unsigned int Nflag)
}
acg.cg_cs.cs_nifree += sblock.fs_ipg;
if (cylno == 0)
- for (i = 0; i < ROOTINO; i++) {
- setbit(cg_inosused(&acg), i);
+ for (ino = 0; ino < ROOTINO; ino++) {
+ setbit(cg_inosused(&acg), ino);
acg.cg_cs.cs_nifree--;
}
/*
@@ -803,7 +804,6 @@ updcsloc(time_t modtime, int fsi, int fso, unsigned int Nflag)
DBG_FUNC("updcsloc")
struct csum *cs;
int ocscg, ncscg;
- int blocks;
ufs2_daddr_t d;
int lcs = 0;
int block;
@@ -820,8 +820,6 @@ updcsloc(time_t modtime, int fsi, int fso, unsigned int Nflag)
}
ocscg = dtog(&osblock, osblock.fs_csaddr);
cs = fscs + ocscg;
- blocks = 1 + howmany(sblock.fs_cssize, sblock.fs_bsize) -
- howmany(osblock.fs_cssize, osblock.fs_bsize);
/*
* Read original cylinder group from disk, and make a copy.
@@ -1500,6 +1498,7 @@ main(int argc, char **argv)
}
sblock.fs_size = dbtofsb(&osblock, size / DEV_BSIZE);
+ sblock.fs_providersize = dbtofsb(&osblock, mediasize / DEV_BSIZE);
/*
* Are we really growing?
diff --git a/sbin/gvinum/Makefile b/sbin/gvinum/Makefile
index 8cccf56ecdbb..c9716e94670c 100644
--- a/sbin/gvinum/Makefile
+++ b/sbin/gvinum/Makefile
@@ -5,7 +5,7 @@ SRCS= gvinum.c gvinum.h geom_vinum_share.c
MAN= gvinum.8
WARNS?= 2
-CFLAGS= -I${.CURDIR}/../../sys -I${DESTDIR}/${INCLUDEDIR}/edit
+CFLAGS+= -I${.CURDIR}/../../sys -I${DESTDIR}/${INCLUDEDIR}/edit
DPADD= ${LIBEDIT} ${LIBTERMCAP} ${LIBDEVSTAT} ${LIBKVM} ${LIBGEOM}
LDADD= -ledit -ltermcap -ldevstat -lkvm -lgeom
diff --git a/sbin/hastd/hast.conf.5 b/sbin/hastd/hast.conf.5
index d78f539500c8..f6368bcf913e 100644
--- a/sbin/hastd/hast.conf.5
+++ b/sbin/hastd/hast.conf.5
@@ -63,7 +63,7 @@ checksum <algorithm>
compression <algorithm>
timeout <seconds>
exec <path>
-metaflush "on" | "off"
+metaflush on | off
pidfile <path>
on <node> {
@@ -89,14 +89,14 @@ resource <name> {
local <path>
timeout <seconds>
exec <path>
- metaflush "on" | "off"
+ metaflush on | off
on <node> {
# Resource-node section
name <name>
# Required
local <path>
- metaflush "on" | "off"
+ metaflush on | off
# Required
remote <addr>
source <addr>
@@ -106,7 +106,7 @@ resource <name> {
name <name>
# Required
local <path>
- metaflush "on" | "off"
+ metaflush on | off
# Required
remote <addr>
source <addr>
diff --git a/sbin/hastd/primary.c b/sbin/hastd/primary.c
index 81d08eb648ad..88159cb6bec1 100644
--- a/sbin/hastd/primary.c
+++ b/sbin/hastd/primary.c
@@ -543,6 +543,27 @@ primary_connect(struct hast_resource *res, struct proto_conn **connp)
return (0);
}
+
+/*
+ * Function instructs GEOM_GATE to handle reads directly from within the kernel.
+ */
+static void
+enable_direct_reads(struct hast_resource *res)
+{
+ struct g_gate_ctl_modify ggiomodify;
+
+ bzero(&ggiomodify, sizeof(ggiomodify));
+ ggiomodify.gctl_version = G_GATE_VERSION;
+ ggiomodify.gctl_unit = res->hr_ggateunit;
+ ggiomodify.gctl_modify = GG_MODIFY_READPROV | GG_MODIFY_READOFFSET;
+ strlcpy(ggiomodify.gctl_readprov, res->hr_localpath,
+ sizeof(ggiomodify.gctl_readprov));
+ ggiomodify.gctl_readoffset = res->hr_localoff;
+ if (ioctl(res->hr_ggatefd, G_GATE_CMD_MODIFY, &ggiomodify) == 0)
+ pjdlog_debug(1, "Direct reads enabled.");
+ else
+ pjdlog_errno(LOG_WARNING, "Failed to enable direct reads");
+}
static int
init_remote(struct hast_resource *res, struct proto_conn **inp,
@@ -692,6 +713,8 @@ init_remote(struct hast_resource *res, struct proto_conn **inp,
res->hr_secondary_localcnt = nv_get_uint64(nvin, "localcnt");
res->hr_secondary_remotecnt = nv_get_uint64(nvin, "remotecnt");
res->hr_syncsrc = nv_get_uint8(nvin, "syncsrc");
+ if (res->hr_syncsrc == HAST_SYNCSRC_PRIMARY)
+ enable_direct_reads(res);
if (nv_exists(nvin, "virgin")) {
/*
* Secondary was reinitialized, bump localcnt if it is 0 as
@@ -990,36 +1013,33 @@ reqlog(int loglevel, int debuglevel, struct g_gate_ctl_io *ggio, const char *fmt
{
char msg[1024];
va_list ap;
- int len;
va_start(ap, fmt);
- len = vsnprintf(msg, sizeof(msg), fmt, ap);
+ (void)vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
- if ((size_t)len < sizeof(msg)) {
- switch (ggio->gctl_cmd) {
- case BIO_READ:
- (void)snprintf(msg + len, sizeof(msg) - len,
- "READ(%ju, %ju).", (uintmax_t)ggio->gctl_offset,
- (uintmax_t)ggio->gctl_length);
- break;
- case BIO_DELETE:
- (void)snprintf(msg + len, sizeof(msg) - len,
- "DELETE(%ju, %ju).", (uintmax_t)ggio->gctl_offset,
- (uintmax_t)ggio->gctl_length);
- break;
- case BIO_FLUSH:
- (void)snprintf(msg + len, sizeof(msg) - len, "FLUSH.");
- break;
- case BIO_WRITE:
- (void)snprintf(msg + len, sizeof(msg) - len,
- "WRITE(%ju, %ju).", (uintmax_t)ggio->gctl_offset,
- (uintmax_t)ggio->gctl_length);
- break;
- default:
- (void)snprintf(msg + len, sizeof(msg) - len,
- "UNKNOWN(%u).", (unsigned int)ggio->gctl_cmd);
- break;
- }
+ switch (ggio->gctl_cmd) {
+ case BIO_READ:
+ (void)snprlcat(msg, sizeof(msg), "READ(%ju, %ju).",
+ (uintmax_t)ggio->gctl_offset,
+ (uintmax_t)ggio->gctl_length);
+ break;
+ case BIO_DELETE:
+ (void)snprlcat(msg, sizeof(msg), "DELETE(%ju, %ju).",
+ (uintmax_t)ggio->gctl_offset,
+ (uintmax_t)ggio->gctl_length);
+ break;
+ case BIO_FLUSH:
+ (void)snprlcat(msg, sizeof(msg), "FLUSH.");
+ break;
+ case BIO_WRITE:
+ (void)snprlcat(msg, sizeof(msg), "WRITE(%ju, %ju).",
+ (uintmax_t)ggio->gctl_offset,
+ (uintmax_t)ggio->gctl_length);
+ break;
+ default:
+ (void)snprlcat(msg, sizeof(msg), "UNKNOWN(%u).",
+ (unsigned int)ggio->gctl_cmd);
+ break;
}
pjdlog_common(loglevel, debuglevel, -1, "%s", msg);
}
@@ -1792,13 +1812,14 @@ sync_thread(void *arg __unused)
struct timeval tstart, tend, tdiff;
unsigned int ii, ncomp, ncomps;
off_t offset, length, synced;
- bool dorewind;
+ bool dorewind, directreads;
int syncext;
ncomps = HAST_NCOMPONENTS;
dorewind = true;
synced = 0;
offset = -1;
+ directreads = false;
for (;;) {
mtx_lock(&sync_lock);
@@ -1870,6 +1891,8 @@ sync_thread(void *arg __unused)
event_send(res, EVENT_SYNCDONE);
}
mtx_lock(&metadata_lock);
+ if (res->hr_syncsrc == HAST_SYNCSRC_SECONDARY)
+ directreads = true;
res->hr_syncsrc = HAST_SYNCSRC_UNDEF;
res->hr_primary_localcnt =
res->hr_secondary_remotecnt;
@@ -1883,6 +1906,10 @@ sync_thread(void *arg __unused)
mtx_unlock(&metadata_lock);
}
rw_unlock(&hio_remote_lock[ncomp]);
+ if (directreads) {
+ directreads = false;
+ enable_direct_reads(res);
+ }
continue;
}
pjdlog_debug(2, "sync: Taking free request.");
diff --git a/sbin/hastd/proto_common.c b/sbin/hastd/proto_common.c
index 59b1e39bb2fb..843366bda228 100644
--- a/sbin/hastd/proto_common.c
+++ b/sbin/hastd/proto_common.c
@@ -181,7 +181,7 @@ proto_descriptor_recv(int sock, int *fdp)
return (errno);
cmsg = CMSG_FIRSTHDR(&msg);
- if (cmsg->cmsg_level != SOL_SOCKET ||
+ if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET ||
cmsg->cmsg_type != SCM_RIGHTS) {
return (EINVAL);
}
diff --git a/sbin/hastd/synch.h b/sbin/hastd/synch.h
index 36e19278921d..65360fd493ef 100644
--- a/sbin/hastd/synch.h
+++ b/sbin/hastd/synch.h
@@ -168,7 +168,7 @@ cv_timedwait(pthread_cond_t *cv, pthread_mutex_t *lock, int timeout)
return (false);
}
- error = clock_gettime(CLOCK_MONOTONIC, &ts);
+ error = clock_gettime(CLOCK_MONOTONIC, &ts);
PJDLOG_ASSERT(error == 0);
ts.tv_sec += timeout;
error = pthread_cond_timedwait(cv, lock, &ts);
diff --git a/sbin/ifconfig/af_inet6.c b/sbin/ifconfig/af_inet6.c
index 0731238bf9bc..d74b0d21492b 100644
--- a/sbin/ifconfig/af_inet6.c
+++ b/sbin/ifconfig/af_inet6.c
@@ -509,8 +509,6 @@ static struct cmd inet6_cmds[] = {
DEF_CMD("-ifdisabled", -ND6_IFF_IFDISABLED, setnd6flags),
DEF_CMD("nud", ND6_IFF_PERFORMNUD, setnd6flags),
DEF_CMD("-nud", -ND6_IFF_PERFORMNUD, setnd6flags),
- DEF_CMD("prefer_source",ND6_IFF_PREFER_SOURCE, setnd6flags),
- DEF_CMD("-prefer_source",-ND6_IFF_PREFER_SOURCE,setnd6flags),
DEF_CMD("auto_linklocal",ND6_IFF_AUTO_LINKLOCAL,setnd6flags),
DEF_CMD("-auto_linklocal",-ND6_IFF_AUTO_LINKLOCAL,setnd6flags),
DEF_CMD_ARG("pltime", setip6pltime),
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8
index 975605521513..f22cc50598ca 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 May 27, 2012
+.Dd July 9, 2012
.Dt IFCONFIG 8
.Os
.Sh NAME
@@ -716,12 +716,6 @@ Set a flag to enable Neighbor Unreachability Detection.
.It Cm -nud
Clear a flag
.Cm nud .
-.It Cm prefer_source
-Set a flag to prefer addresses on the interface as candidates of the
-source address for outgoing packets.
-.It Cm -prefer_source
-Clear a flag
-.Cm prefer_source .
.El
.Pp
The following parameters are specific to cloning
@@ -2051,7 +2045,7 @@ Send broadcast path requests every two seconds.
Nodes on the mesh without a path to this root mesh station with try to
discover a path to us.
.It Cm PROACTIVE
-Send broadcast path requests every two seconds and every node must reply with
+Send broadcast path requests every two seconds and every node must reply
with a path reply even if it already has a path to this root mesh station.
.It Cm RANN
Send broadcast root announcement (RANN) frames.
diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c
index 082e15d61e2a..870acdd0445c 100644
--- a/sbin/ifconfig/ifconfig.c
+++ b/sbin/ifconfig/ifconfig.c
@@ -916,7 +916,7 @@ unsetifdescr(const char *val, int value, int s, const struct afswtch *afp)
#define IFCAPBITS \
"\020\1RXCSUM\2TXCSUM\3NETCONS\4VLAN_MTU\5VLAN_HWTAGGING\6JUMBO_MTU\7POLLING" \
"\10VLAN_HWCSUM\11TSO4\12TSO6\13LRO\14WOL_UCAST\15WOL_MCAST\16WOL_MAGIC" \
-"\21VLAN_HWFILTER\23VLAN_HWTSO\24LINKSTATE\25NETMAP" \
+"\17TOE4\20TOE6\21VLAN_HWFILTER\23VLAN_HWTSO\24LINKSTATE\25NETMAP" \
"\26RXCSUM_IPV6\27TXCSUM_IPV6"
/*
@@ -1212,6 +1212,8 @@ static struct cmd basic_cmds[] = {
DEF_CMD("-tso4", -IFCAP_TSO4, setifcap),
DEF_CMD("tso", IFCAP_TSO, setifcap),
DEF_CMD("-tso", -IFCAP_TSO, setifcap),
+ DEF_CMD("toe", IFCAP_TOE, setifcap),
+ DEF_CMD("-toe", -IFCAP_TOE, setifcap),
DEF_CMD("lro", IFCAP_LRO, setifcap),
DEF_CMD("-lro", -IFCAP_LRO, setifcap),
DEF_CMD("wol", IFCAP_WOL, setifcap),
diff --git a/sbin/ipf/ipf/Makefile b/sbin/ipf/ipf/Makefile
index 0a5f4a82c3a2..62a9b47d6cca 100644
--- a/sbin/ipf/ipf/Makefile
+++ b/sbin/ipf/ipf/Makefile
@@ -2,8 +2,8 @@
PROG= ipf
SRCS= ${GENHDRS} ipf.c ipfcomp.c ipf_y.c ipf_l.c bpf_filter.c
-MAN= ipf.8 ipf.4 ipf.5 ipl.4
-MLINKS= ipl.4 ipfilter.4 ipf.5 ipf.conf.5 ipf.5 ipf6.conf.5
+MAN= ipfilter.4 ipfilter.5 ipf.8 ipf.4 ipf.5 ipl.4
+MLINKS= ipf.5 ipf.conf.5 ipf.5 ipf6.conf.5
CFLAGS+= -I. -DIPFILTER_BPF
GENHDRS= ipf_l.h ipf_y.h
diff --git a/sbin/ipfw/dummynet.c b/sbin/ipfw/dummynet.c
index 3719e9b5cd88..28dc2c77a020 100644
--- a/sbin/ipfw/dummynet.c
+++ b/sbin/ipfw/dummynet.c
@@ -759,7 +759,8 @@ load_extra_delays(const char *filename, struct dn_profile *p,
void
ipfw_config_pipe(int ac, char **av)
{
- int i, j;
+ int i;
+ u_int j;
char *end;
struct dn_id *buf, *base;
struct dn_sch *sch = NULL;
@@ -1282,8 +1283,8 @@ parse_range(int ac, char *av[], uint32_t *v, int len)
av--;
}
if (v[1] < v[0] ||
- v[1] < 0 || v[1] >= DN_MAX_ID-1 ||
- v[0] < 0 || v[1] >= DN_MAX_ID-1) {
+ v[1] >= DN_MAX_ID-1 ||
+ v[1] >= DN_MAX_ID-1) {
continue; /* invalid entry */
}
n++;
@@ -1310,11 +1311,12 @@ void
dummynet_list(int ac, char *av[], int show_counters)
{
struct dn_id *oid, *x = NULL;
- int ret, i, l;
+ int ret, i;
int n; /* # of ranges */
- int buflen;
- int max_size; /* largest obj passed up */
+ u_int buflen, l;
+ u_int max_size; /* largest obj passed up */
+ (void)show_counters; // XXX unused, but we should use it.
ac--;
av++; /* skip 'list' | 'show' word */
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index 5d1625ea60a2..db0dfc0a505e 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -1,7 +1,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd March 9, 2012
+.Dd October 25, 2012
.Dt IPFW 8
.Os
.Sh NAME
@@ -141,7 +141,7 @@ the firewall will have a
.Em stateful
behaviour, i.e., upon a match it will create
.Em dynamic rules ,
-i.e. rules that match packets with the same 5-tuple
+i.e., rules that match packets with the same 5-tuple
(protocol, source and destination addresses and ports)
as the packet which caused their creation.
Dynamic rules, which have a limited lifetime, are checked
@@ -223,14 +223,15 @@ When listing and
is specified, also show expired dynamic rules.
.It Fl f
Do not ask for confirmation for commands that can cause problems
-if misused,
-.No i.e. Cm flush .
+if misused, i.e.,
+.Cm flush .
If there is no tty associated with the process, this is implied.
.It Fl i
When listing a table (see the
.Sx LOOKUP TABLES
section below for more information on lookup tables), format values
-as IP addresses. By default, values are shown as integers.
+as IP addresses.
+By default, values are shown as integers.
.It Fl n
Only check syntax of the command strings, without actually passing
them to the kernel.
@@ -421,7 +422,7 @@ Keywords are case-sensitive, whereas arguments may
or may not be case-sensitive depending on their nature
(e.g.\& uid's are, hostnames are not).
.Pp
-Some arguments (e.g. port or address lists) are comma-separated
+Some arguments (e.g., port or address lists) are comma-separated
lists of values.
In this case, spaces after commas ',' are allowed to make
the line more readable.
@@ -560,7 +561,22 @@ is set to 0 (default), one can use
.Xr bpf 4
attached to the
.Li ipfw0
-pseudo interface. There is no overhead if no
+pseudo interface.
+This pseudo interface can be created after a boot
+manually by using the following command:
+.Bd -literal -offset indent
+# ifconfig ipfw0 create
+.Ed
+.Pp
+Or, automatically at boot time by adding the following
+line to the
+.Xr rc.conf 5
+file:
+.Bd -literal -offset indent
+firewall_logif="YES"
+.Ed
+.Pp
+There is no overhead if no
.Xr bpf 4
is attached to the pseudo interface.
.Pp
@@ -758,11 +774,6 @@ This makes the
.Xr netstat 1
entry look rather weird but is intended for
use with transparent proxy servers.
-.Pp
-To enable
-.Cm fwd
-a custom kernel needs to be compiled with the option
-.Cd "options IPFIREWALL_FORWARD" .
.It Cm nat Ar nat_nr | tablearg
Pass packet to a
nat instance
@@ -858,7 +869,8 @@ Takes rule number saved to internal stack by the last
action and returns ruleset processing to the first rule
with number greater than number of corresponding
.Cm call
-rule. See description of the
+rule.
+See description of the
.Cm call
action for more details.
.Pp
@@ -940,28 +952,36 @@ actions.
The packet is tagged so as to use the FIB (routing table)
.Ar fibnum
in any subsequent forwarding decisions.
-Initially this is limited to the values 0 through 15, see
-.Xr setfib 1 .
+In the current implementation, this is limited to the values 0 through 15, see
+.Xr setfib 2 .
Processing continues at the next rule.
It is possible to use the
.Cm tablearg
-keyword with a setfib. If tablearg value is not within compiled FIB range packet fib is set to 0.
+keyword with setfib.
+If the tablearg value is not within the compiled range of fibs,
+the packet's fib is set to 0.
.It Cm reass
-Queue and reassemble ip fragments.
-If the packet is not fragmented, counters are updated and processing continues with the next rule.
+Queue and reassemble IP fragments.
+If the packet is not fragmented, counters are updated and
+processing continues with the next rule.
If the packet is the last logical fragment, the packet is reassembled and, if
.Va net.inet.ip.fw.one_pass
-is set to 0, processing continues with the next rule, else packet is allowed to pass and search terminates.
-If the packet is a fragment in the middle, it is consumed and processing stops immediately.
+is set to 0, processing continues with the next rule.
+Otherwise, the packet is allowed to pass and the search terminates.
+If the packet is a fragment in the middle of a logical group of fragments,
+it is consumed and
+processing stops immediately.
.Pp
-Fragments handling can be tuned via
+Fragment handling can be tuned via
.Va net.inet.ip.maxfragpackets
and
.Va net.inet.ip.maxfragsperpacket
-which limit, respectively, the maximum number of processable fragments (default: 800) and
+which limit, respectively, the maximum number of processable
+fragments (default: 800) and
the maximum number of fragments per packet (default: 16).
.Pp
-NOTA BENE: since fragments do not contain port numbers, they should be avoided with the
+NOTA BENE: since fragments do not contain port numbers,
+they should be avoided with the
.Nm reass
rule.
Alternatively, direction-based (like
@@ -1581,7 +1601,8 @@ This is the short form of
.It Cm sockarg
Matches packets that are associated to a local socket and
for which the SO_USER_COOKIE socket option has been set
-to a non-zero value. As a side effect, the value of the
+to a non-zero value.
+As a side effect, the value of the
option is made available as
.Cm tablearg
value, which in turn can be used as
@@ -1731,9 +1752,9 @@ connected networks instead of all source addresses.
.El
.Sh LOOKUP TABLES
Lookup tables are useful to handle large sparse sets of
-addresses or other search keys (e.g. ports, jail IDs, interface names).
+addresses or other search keys (e.g., ports, jail IDs, interface names).
In the rest of this section we will use the term ``address''.
-There may be up to 4096 different lookup tables, numbered 0 to 4095.
+There may be up to 65535 different lookup tables, numbered 0 to 65534.
.Pp
Each entry is represented by an
.Ar addr Ns Op / Ns Ar masklen
@@ -1769,7 +1790,8 @@ the routing table (see
.Xr route 4 ) .
.Pp
Lookup tables currently support only ports, jail IDs, IPv4/IPv6 addresses
-and interface names. Wildcards is not supported for interface names.
+and interface names.
+Wildcards is not supported for interface names.
.Pp
The
.Cm tablearg
@@ -1798,7 +1820,8 @@ Section for example usage of tables and the tablearg keyword.
When used with the
.Cm skipto
action, the user should be aware that the code will walk the ruleset
-up to a rule equal to, or past, the given number, and should therefore try keep the
+up to a rule equal to, or past, the given number,
+and should therefore try keep the
ruleset compact between the skipto and the target rules.
.Sh SETS OF RULES
Each rule belongs to one of 32 different
@@ -2006,10 +2029,12 @@ As an example, using ``src-ip 0xffffff00'' creates one instance
for each /24 destination subnet.
.Pp
The FLOW_MASK, together with the SCHED_MASK, is used to split
-packets into flows. As an example, using
+packets into flows.
+As an example, using
``src-ip 0x000000ff''
together with the previous SCHED_MASK makes a flow for
-each individual source address. In turn, flows for each /24
+each individual source address.
+In turn, flows for each /24
subnet will be sent to the same scheduler instance.
.Pp
The above diagram holds even for the
@@ -2128,12 +2153,13 @@ A file specifying the additional overhead incurred in the transmission
of a packet on the link.
.Pp
Some link types introduce extra delays in the transmission
-of a packet, e.g. because of MAC level framing, contention on
+of a packet, e.g., because of MAC level framing, contention on
the use of the channel, MAC level retransmissions and so on.
From our point of view, the channel is effectively unavailable
for this extra time, which is constant or variable depending
-on the link type. Additionally, packets may be dropped after this
-time (e.g. on a wireless link after too many retransmissions).
+on the link type.
+Additionally, packets may be dropped after this
+time (e.g., on a wireless link after too many retransmissions).
We can model the additional delay with an empirical curve
that represents its distribution.
.Bd -literal -offset indent
@@ -2169,7 +2195,7 @@ If not specified here, it must be present
explicitly as a configuration parameter for the pipe;
.It Cm loss-level Ar L
the probability above which packets are lost.
-(0.0 <= L <= 1.0, default 1.0 i.e. no loss);
+(0.0 <= L <= 1.0, default 1.0 i.e., no loss);
.It Cm samples Ar N
the number of samples used in the internal
representation of the curve (2..1024; default 100);
@@ -2218,22 +2244,24 @@ Specifies the weight to be used for flows matching this queue.
The weight must be in the range 1..100, and defaults to 1.
.El
.Pp
-The following parameters can be configured for a scheduler:
+The following case-insensitive parameters can be configured for a
+scheduler:
.Pp
.Bl -tag -width indent -compact
-.It Cm type Ar {fifo | wf2qp | rr | qfq}
+.It Cm type Ar {fifo | wf2q+ | rr | qfq}
specifies the scheduling algorithm to use.
.Bl -tag -width indent -compact
-.It cm fifo
+.It Cm fifo
is just a FIFO scheduler (which means that all packets
are stored in the same queue as they arrive to the scheduler).
FIFO has O(1) per-packet time complexity, with very low
constants (estimate 60-80ns on a 2GHz desktop machine)
but gives no service guarantees.
-.It Cm wf2qp
+.It Cm wf2q+
implements the WF2Q+ algorithm, which is a Weighted Fair Queueing
algorithm which permits flows to share bandwidth according to
-their weights. Note that weights are not priorities; even a flow
+their weights.
+Note that weights are not priorities; even a flow
with a minuscule weight will never starve.
WF2Q+ has O(log N) per-packet processing cost, where N is the number
of flows, and is the default algorithm used by previous versions
@@ -2519,7 +2547,8 @@ in
.Xr natd 8
for more information.
.It Cm tablearg
-Uses argument supplied in lookup table. See
+Uses argument supplied in lookup table.
+See
.Sx LOOKUP TABLES
section below for more information on lookup tables.
.El
@@ -2581,11 +2610,13 @@ or
before ipfw module gets loaded.
.Bl -tag -width indent
.It Va net.inet.ip.fw.default_to_accept: No 0
-Defines ipfw last rule behavior. This value overrides
+Defines ipfw last rule behavior.
+This value overrides
.Cd "options IPFW_DEFAULT_TO_(ACCEPT|DENY)"
from kernel configuration file.
.It Va net.inet.ip.fw.tables_max: No 128
-Defines number of tables available in ipfw. Number cannot exceed 65534.
+Defines number of tables available in ipfw.
+Number cannot exceed 65534.
.El
.Sh SYSCTL VARIABLES
A set of
@@ -2615,12 +2646,14 @@ Option 1 should never be selected as this forms a security risk.
An attacker can
establish multiple fake associations by sending AddIP messages.
.It Va net.inet.ip.alias.sctp.chunk_proc_limit: No 5
-Defines the maximum number of chunks in an SCTP packet that will be parsed for a
+Defines the maximum number of chunks in an SCTP packet that will be
+parsed for a
packet that matches an existing association.
This value is enforced to be greater or equal than
.Cm net.inet.ip.alias.sctp.initialising_chunk_proc_limit .
A high value is
-a DoS risk yet setting too low a value may result in important control chunks in
+a DoS risk yet setting too low a value may result in
+important control chunks in
the packet not being located and parsed.
.It Va net.inet.ip.alias.sctp.error_on_ootb: No 1
Defines when the
@@ -2642,7 +2675,8 @@ This value is only useful if the
.Nm nat
is tracking global IP addresses.
.It Cm 3
-ErrorM is sent in response to all OOTB packets on both the local and global side
+ErrorM is sent in response to all OOTB packets on both
+the local and global side
(DoS risk).
.El
.Pp
@@ -2693,12 +2727,14 @@ will only be an INIT or ASCONF-AddIP packet.
A higher value may become a DoS
risk as malformed packets can consume processing resources.
.It Va net.inet.ip.alias.sctp.param_proc_limit: No 25
-Defines the maximum number of parameters within a chunk that will be parsed in a
+Defines the maximum number of parameters within a chunk that will be
+parsed in a
packet.
As for other similar sysctl variables, larger values pose a DoS risk.
.It Va net.inet.ip.alias.sctp.log_level: No 0
Level of detail in the system log messages (0 \- minimal, 1 \- event,
-2 \- info, 3 \- detail, 4 \- debug, 5 \- max debug). May be a good
+2 \- info, 3 \- detail, 4 \- debug, 5 \- max debug).
+May be a good
option in high loss environments.
.It Va net.inet.ip.alias.sctp.shutdown_time: No 15
Timeout value while waiting for SHUTDOWN-COMPLETE.
@@ -2717,7 +2753,8 @@ association is limited to this value
.El
.Pp
This variable is fully dynamic, the new value will be adopted for all newly
-arriving associations, existing associations are treated as they were previously.
+arriving associations, existing associations are treated
+as they were previously.
Global tracking will decrease the number of collisions within the
.Nm nat
at a cost
@@ -2951,9 +2988,11 @@ This will let the firewall install dynamic rules only for
those connection which start with a regular SYN packet coming
from the inside of our network.
Dynamic rules are checked when encountering the first
-.Cm check-state
-or
+occurrence of a
+.Cm check-state ,
.Cm keep-state
+or
+.Cm limit
rule.
A
.Cm check-state
@@ -3262,7 +3301,8 @@ Some early work (1999-2000) on the
traffic shaper supported by Akamba Corp.
.Pp
The ipfw core (ipfw2) has been completely redesigned and
-reimplemented by Luigi Rizzo in summer 2002. Further
+reimplemented by Luigi Rizzo in summer 2002.
+Further
actions and
options have been added by various developer over the years.
.Pp
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index dd22ad0c6e4a..530f3195a1f2 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -412,7 +412,7 @@ do_cmd(int optname, void *optval, uintptr_t optlen)
* and calls setsockopt().
* Function returns 0 on success or -1 otherwise.
*/
-int
+static int
do_setcmd3(int optname, void *optval, socklen_t optlen)
{
socklen_t len;
@@ -976,8 +976,9 @@ print_icmptypes(ipfw_insn_u32 *cmd)
#define HAVE_OPTIONS 0x8000
static void
-show_prerequisites(int *flags, int want, int cmd __unused)
+show_prerequisites(int *flags, int want, int cmd)
{
+ (void)cmd; /* UNUSED */
if (co.comment_only)
return;
if ( (*flags & HAVE_IP) == HAVE_IP)
@@ -3930,6 +3931,7 @@ ipfw_table_handler(int ac, char *av[])
uint32_t a, type, mask, addrlen;
uint32_t tables_max;
+ mask = 0; // XXX uninitialized ?
len = sizeof(tables_max);
if (sysctlbyname("net.inet.ip.fw.tables_max", &tables_max, &len,
NULL, 0) == -1)
@@ -4135,7 +4137,7 @@ table_list(uint16_t num, int need_header)
if (sz < xent->len)
break;
sz -= xent->len;
- xent = (void *)xent + xent->len;
+ xent = (ipfw_table_xentry *)((char *)xent + xent->len);
}
free(tbl);
diff --git a/sbin/ipfw/ipv6.c b/sbin/ipfw/ipv6.c
index ee93d989aed2..6326590a46af 100644
--- a/sbin/ipfw/ipv6.c
+++ b/sbin/ipfw/ipv6.c
@@ -336,24 +336,40 @@ fill_ip6(ipfw_insn_ip6 *cmd, char *av)
* Note d[1] points to struct in6_add r mask6 of cmd
*/
- cmd->o.len &= ~F_LEN_MASK; /* zero len */
+ cmd->o.len &= ~F_LEN_MASK; /* zero len */
- if (strcmp(av, "any") == 0)
- return (1);
+ if (strcmp(av, "any") == 0)
+ return (1);
- if (strcmp(av, "me") == 0) { /* Set the data for "me" opt*/
- cmd->o.len |= F_INSN_SIZE(ipfw_insn);
- return (1);
- }
+ if (strcmp(av, "me") == 0) { /* Set the data for "me" opt*/
+ cmd->o.len |= F_INSN_SIZE(ipfw_insn);
+ return (1);
+ }
- if (strcmp(av, "me6") == 0) { /* Set the data for "me" opt*/
- cmd->o.len |= F_INSN_SIZE(ipfw_insn);
- return (1);
- }
+ if (strcmp(av, "me6") == 0) { /* Set the data for "me" opt*/
+ cmd->o.len |= F_INSN_SIZE(ipfw_insn);
+ return (1);
+ }
+
+ if (strncmp(av, "table(", 6) == 0) {
+ char *p = strchr(av + 6, ',');
+ uint32_t *dm = ((ipfw_insn_u32 *)cmd)->d;
+
+ if (p)
+ *p++ = '\0';
+ cmd->o.opcode = O_IP_DST_LOOKUP;
+ cmd->o.arg1 = strtoul(av + 6, NULL, 0);
+ if (p) {
+ cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
+ dm[0] = strtoul(p, NULL, 0);
+ } else
+ cmd->o.len |= F_INSN_SIZE(ipfw_insn);
+ return (1);
+ }
- av = strdup(av);
- while (av) {
+ av = strdup(av);
+ while (av) {
/*
* After the address we can have '/' indicating a mask,
* or ',' indicating another address follows.
@@ -469,7 +485,11 @@ add_srcip6(ipfw_insn *cmd, char *av)
{
fill_ip6((ipfw_insn_ip6 *)cmd, av);
- if (F_LEN(cmd) == 0) { /* any */
+ if (cmd->opcode == O_IP_DST_SET) /* set */
+ cmd->opcode = O_IP_SRC_SET;
+ else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */
+ cmd->opcode = O_IP_SRC_LOOKUP;
+ else if (F_LEN(cmd) == 0) { /* any */
} else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */
cmd->opcode = O_IP6_SRC_ME;
} else if (F_LEN(cmd) ==
@@ -487,7 +507,11 @@ add_dstip6(ipfw_insn *cmd, char *av)
{
fill_ip6((ipfw_insn_ip6 *)cmd, av);
- if (F_LEN(cmd) == 0) { /* any */
+ if (cmd->opcode == O_IP_DST_SET) /* set */
+ ;
+ else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */
+ ;
+ else if (F_LEN(cmd) == 0) { /* any */
} else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) { /* "me" */
cmd->opcode = O_IP6_DST_ME;
} else if (F_LEN(cmd) ==
diff --git a/sbin/ipfw/nat.c b/sbin/ipfw/nat.c
index 6bec36c27396..bff28e156c77 100644
--- a/sbin/ipfw/nat.c
+++ b/sbin/ipfw/nat.c
@@ -318,6 +318,7 @@ estimate_redir_addr(int *ac, char ***av)
char *sep = **av;
u_int c = 0;
+ (void)ac; /* UNUSED */
while ((sep = strchr(sep, ',')) != NULL) {
c++;
sep++;
@@ -379,6 +380,7 @@ estimate_redir_port(int *ac, char ***av)
char *sep = **av;
u_int c = 0;
+ (void)ac; /* UNUSED */
while ((sep = strchr(sep, ',')) != NULL) {
c++;
sep++;
@@ -419,7 +421,7 @@ setup_redir_port(char *buf, int *ac, char ***av)
/*
* Extract local address.
*/
- if ((sep = strchr(**av, ',')) != NULL) {
+ if (strchr(**av, ',') != NULL) {
r->laddr.s_addr = INADDR_NONE;
r->lport = ~0;
numLocalPorts = 1;
@@ -452,7 +454,7 @@ setup_redir_port(char *buf, int *ac, char ***av)
/*
* Extract public port and optionally address.
*/
- if ((sep = strchr(**av, ':')) != NULL) {
+ if (strchr(**av, ':') != NULL) {
if (StrToAddrAndPortRange(**av, &r->paddr, protoName,
&portRange) != 0)
errx(EX_DATAERR, "redirect_port: "
@@ -480,7 +482,7 @@ setup_redir_port(char *buf, int *ac, char ***av)
* option for this redirect entry, else stop here processing arg[cv].
*/
if (*ac != 0 && isdigit(***av)) {
- if ((sep = strchr(**av, ':')) != NULL) {
+ if (strchr(**av, ':') != NULL) {
if (StrToAddrAndPortRange(**av, &r->raddr, protoName,
&portRange) != 0)
errx(EX_DATAERR, "redirect_port: "
diff --git a/sbin/md5/Makefile b/sbin/md5/Makefile
index 7df4d859c93b..c3c5a9fe77fd 100644
--- a/sbin/md5/Makefile
+++ b/sbin/md5/Makefile
@@ -5,11 +5,13 @@ PROG= md5
LINKS= ${BINDIR}/md5 ${BINDIR}/rmd160 \
${BINDIR}/md5 ${BINDIR}/sha1 \
- ${BINDIR}/md5 ${BINDIR}/sha256
+ ${BINDIR}/md5 ${BINDIR}/sha256 \
+ ${BINDIR}/md5 ${BINDIR}/sha512
MLINKS= md5.1 rmd160.1 \
md5.1 sha1.1 \
- md5.1 sha256.1
+ md5.1 sha256.1 \
+ md5.1 sha512.1
WFORMAT?= 1
diff --git a/sbin/md5/md5.1 b/sbin/md5/md5.1
index 0dfb145669d6..04efa3750d06 100644
--- a/sbin/md5/md5.1
+++ b/sbin/md5/md5.1
@@ -1,9 +1,9 @@
.\" $FreeBSD$
-.Dd September 7, 2008
+.Dd July 31, 2012
.Dt MD5 1
.Os
.Sh NAME
-.Nm md5 , sha1 , sha256 , rmd160
+.Nm md5 , sha1 , sha256 , sha512, rmd160
.Nd calculate a message-digest fingerprint (checksum) for a file
.Sh SYNOPSIS
.Nm md5
@@ -21,6 +21,11 @@
.Op Fl c Ar string
.Op Fl s Ar string
.Op Ar
+.Nm sha512
+.Op Fl pqrtx
+.Op Fl c Ar string
+.Op Fl s Ar string
+.Op Ar
.Nm rmd160
.Op Fl pqrtx
.Op Fl c Ar string
@@ -28,7 +33,7 @@
.Op Ar
.Sh DESCRIPTION
The
-.Nm md5 , sha1 , sha256
+.Nm md5 , sha1 , sha256 , sha512
and
.Nm rmd160
utilities take as input a message of arbitrary length and produce as
@@ -41,7 +46,7 @@ It is conjectured that it is computationally infeasible to
produce two messages having the same message digest, or to produce any
message having a given prespecified target message digest.
The
-.Tn MD5 , SHA-1 , SHA-256
+.Tn MD5 , SHA-1 , SHA-256 , SHA-512
and
.Tn RIPEMD-160
algorithms are intended for digital signature applications, where a
@@ -104,7 +109,7 @@ Run a built-in test script.
.El
.Sh EXIT STATUS
The
-.Nm md5 , sha1 , sha256
+.Nm md5 , sha1 , sha256 , sha512
and
.Nm rmd160
utilities exit 0 on success,
@@ -115,7 +120,8 @@ and 2 if at least one file does not have the same hash as the -c option.
.Xr md5 3 ,
.Xr ripemd 3 ,
.Xr sha 3 ,
-.Xr sha256 3
+.Xr sha256 3 ,
+.Xr sha512 3
.Rs
.%A R. Rivest
.%T The MD5 Message-Digest Algorithm
diff --git a/sbin/md5/md5.c b/sbin/md5/md5.c
index 29d50bb708fc..823d09b2c08a 100644
--- a/sbin/md5/md5.c
+++ b/sbin/md5/md5.c
@@ -28,6 +28,7 @@ __FBSDID("$FreeBSD$");
#include <ripemd.h>
#include <sha.h>
#include <sha256.h>
+#include <sha512.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -54,6 +55,7 @@ typedef char *(DIGEST_End)(void *, char *);
extern const char *MD5TestOutput[MDTESTCOUNT];
extern const char *SHA1_TestOutput[MDTESTCOUNT];
extern const char *SHA256_TestOutput[MDTESTCOUNT];
+extern const char *SHA512_TestOutput[MDTESTCOUNT];
extern const char *RIPEMD160_TestOutput[MDTESTCOUNT];
typedef struct Algorithm_t {
@@ -78,12 +80,14 @@ typedef union {
MD5_CTX md5;
SHA1_CTX sha1;
SHA256_CTX sha256;
+ SHA512_CTX sha512;
RIPEMD160_CTX ripemd160;
} DIGEST_CTX;
/* max(MD5_DIGEST_LENGTH, SHA_DIGEST_LENGTH,
- SHA256_DIGEST_LENGTH, RIPEMD160_DIGEST_LENGTH)*2+1 */
-#define HEX_DIGEST_LENGTH 65
+ SHA256_DIGEST_LENGTH, SHA512_DIGEST_LENGTH,
+ RIPEMD160_DIGEST_LENGTH)*2+1 */
+#define HEX_DIGEST_LENGTH 129
/* algorithm function table */
@@ -97,6 +101,9 @@ struct Algorithm_t Algorithm[] = {
{ "sha256", "SHA256", &SHA256_TestOutput, (DIGEST_Init*)&SHA256_Init,
(DIGEST_Update*)&SHA256_Update, (DIGEST_End*)&SHA256_End,
&SHA256_Data, &SHA256_File },
+ { "sha512", "SHA512", &SHA512_TestOutput, (DIGEST_Init*)&SHA512_Init,
+ (DIGEST_Update*)&SHA512_Update, (DIGEST_End*)&SHA512_End,
+ &SHA512_Data, &SHA512_File },
{ "rmd160", "RMD160", &RIPEMD160_TestOutput,
(DIGEST_Init*)&RIPEMD160_Init, (DIGEST_Update*)&RIPEMD160_Update,
(DIGEST_End*)&RIPEMD160_End, &RIPEMD160_Data, &RIPEMD160_File }
@@ -320,6 +327,17 @@ const char *SHA256_TestOutput[MDTESTCOUNT] = {
"e6eae09f10ad4122a0e2a4075761d185a272ebd9f5aa489e998ff2f09cbfdd9f"
};
+const char *SHA512_TestOutput[MDTESTCOUNT] = {
+ "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
+ "1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75",
+ "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
+ "107dbf389d9e9f71a3a95f6c055b9251bc5268c2be16d6c13492ea45b0199f3309e16455ab1e96118e8a905d5597b72038ddb372a89826046de66687bb420e7c",
+ "4dbff86cc2ca1bae1e16468a05cb9881c97f1753bce3619034898faa1aabe429955a1bf8ec483d7421fe3c1646613a59ed5441fb0f321389f77f48a879c7b1f1",
+ "1e07be23c26a86ea37ea810c8ec7809352515a970e9253c26f536cfc7a9996c45c8370583e0a78fa4a90041d71a4ceab7423f19c71b9d5a3e01249f0bebd5894",
+ "72ec1ef1124a45b047e8b7c75a932195135bb61de24ec0d1914042246e0aec3a2354e093d76f3048b456764346900cb130d2a4fd5dd16abb5e30bcb850dee843",
+ "e8a835195e039708b13d9131e025f4441dbdc521ce625f245a436dcd762f54bf5cb298d96235e6c6a304e087ec8189b9512cbdf6427737ea82793460c367b9c3"
+};
+
const char *RIPEMD160_TestOutput[MDTESTCOUNT] = {
"9c1185a5c5e9fc54612808977ee8f548b2258d31",
"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe",
diff --git a/sbin/mdconfig/Makefile b/sbin/mdconfig/Makefile
index f4d8adcfde8d..be8b1b556dde 100644
--- a/sbin/mdconfig/Makefile
+++ b/sbin/mdconfig/Makefile
@@ -2,7 +2,6 @@
PROG= mdconfig
MAN= mdconfig.8
-MLINKS= mdconfig.8 vnconfig.8
DPADD= ${LIBUTIL} ${LIBGEOM} ${LIBBSDXML} ${LIBSBUF}
LDADD= -lutil -lgeom -lbsdxml -lsbuf
diff --git a/sbin/mdconfig/mdconfig.8 b/sbin/mdconfig/mdconfig.8
index 41141370bbc9..89f150e8d1a4 100644
--- a/sbin/mdconfig/mdconfig.8
+++ b/sbin/mdconfig/mdconfig.8
@@ -41,7 +41,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd March 21, 2012
+.Dd June 27, 2012
.Dt MDCONFIG 8
.Os
.Sh NAME
@@ -64,6 +64,11 @@
.Fl u Ar unit
.Op Fl o Oo Cm no Oc Ns Ar force
.Nm
+.Fl r
+.Fl u Ar unit
+.Fl s Ar size
+.Op Fl o Oo Cm no Oc Ns Ar force
+.Nm
.Fl l
.Op Fl n
.Op Fl v
@@ -85,6 +90,8 @@ This will configure and attach a memory disk with the
parameters specified and attach it to the system.
.It Fl d
Detach a memory disk from the system and release all resources.
+.It Fl r
+Resize a memory disk.
.It Fl t Ar type
Select the type of the memory disk.
.Bl -tag -width "malloc"
@@ -148,7 +155,7 @@ and
.Fl t Ar swap
options are implied if not specified.
.It Fl S Ar sectorsize
-Sectorsize to use for malloc backed device.
+Sectorsize to use for the memory disk, in bytes.
.It Fl x Ar sectors/track
See the description of the
.Fl y
diff --git a/sbin/mdconfig/mdconfig.c b/sbin/mdconfig/mdconfig.c
index 122fc7d09c06..55d1567ca970 100644
--- a/sbin/mdconfig/mdconfig.c
+++ b/sbin/mdconfig/mdconfig.c
@@ -54,7 +54,7 @@
#include <unistd.h>
static struct md_ioctl mdio;
-static enum {UNSET, ATTACH, DETACH, LIST} action = UNSET;
+static enum {UNSET, ATTACH, DETACH, RESIZE, LIST} action = UNSET;
static int nflag;
static void usage(void);
@@ -81,6 +81,7 @@ usage(void)
" [-s size] [-S sectorsize] [-u unit]\n"
" [-x sectors/track] [-y heads/cylinder]\n"
" mdconfig -d -u unit [-o [no]force]\n"
+" mdconfig -r -u unit -s size [-o [no]force]\n"
" mdconfig -l [-v] [-n] [-u unit]\n"
" mdconfig file\n");
fprintf(stderr, "\t\ttype = {malloc, preload, vnode, swap}\n");
@@ -96,7 +97,7 @@ main(int argc, char **argv)
{
int ch, fd, i, vflag;
char *p;
- char *fflag = NULL, *tflag = NULL, *uflag = NULL;
+ char *fflag = NULL, *sflag = NULL, *tflag = NULL, *uflag = NULL;
bzero(&mdio, sizeof(mdio));
mdio.md_file = malloc(PATH_MAX);
@@ -108,25 +109,32 @@ main(int argc, char **argv)
if (argc == 1)
usage();
- while ((ch = getopt(argc, argv, "ab:df:lno:s:S:t:u:vx:y:")) != -1) {
+ while ((ch = getopt(argc, argv, "ab:df:lno:rs:S:t:u:vx:y:")) != -1) {
switch (ch) {
case 'a':
if (action != UNSET && action != ATTACH)
- errx(1,
- "-a is mutually exclusive with -d and -l");
+ errx(1, "-a is mutually exclusive "
+ "with -d, -r, and -l");
action = ATTACH;
break;
case 'd':
if (action != UNSET && action != DETACH)
- errx(1,
- "-d is mutually exclusive with -a and -l");
+ errx(1, "-d is mutually exclusive "
+ "with -a, -r, and -l");
action = DETACH;
mdio.md_options |= MD_AUTOUNIT;
break;
+ case 'r':
+ if (action != UNSET && action != RESIZE)
+ errx(1, "-r is mutually exclusive "
+ "with -a, -d, and -l");
+ action = RESIZE;
+ mdio.md_options |= MD_AUTOUNIT;
+ break;
case 'l':
if (action != UNSET && action != LIST)
- errx(1,
- "-l is mutually exclusive with -a and -d");
+ errx(1, "-l is mutually exclusive "
+ "with -a, -r, and -d");
action = LIST;
mdio.md_options |= MD_AUTOUNIT;
break;
@@ -188,6 +196,9 @@ main(int argc, char **argv)
mdio.md_sectorsize = strtoul(optarg, &p, 0);
break;
case 's':
+ if (sflag != NULL)
+ errx(1, "-s can be passed only once");
+ sflag = optarg;
mdio.md_mediasize = (off_t)strtoumax(optarg, &p, 0);
if (p == NULL || *p == '\0')
mdio.md_mediasize *= DEV_BSIZE;
@@ -242,7 +253,7 @@ main(int argc, char **argv)
mdio.md_type = MD_VNODE;
mdio.md_options |= MD_CLUSTER | MD_AUTOUNIT |
MD_COMPRESS;
- } else if (mdio.md_mediasize != 0) {
+ } else if (sflag != NULL) {
/* Imply ``-t swap'' */
mdio.md_type = MD_SWAP;
mdio.md_options |= MD_CLUSTER | MD_AUTOUNIT |
@@ -276,15 +287,15 @@ main(int argc, char **argv)
}
if ((mdio.md_type == MD_MALLOC || mdio.md_type == MD_SWAP) &&
- mdio.md_mediasize == 0)
+ sflag == NULL)
errx(1, "must specify -s for -t malloc or -t swap");
if (mdio.md_type == MD_VNODE && mdio.md_file[0] == '\0')
errx(1, "must specify -f for -t vnode");
} else {
if (mdio.md_sectorsize != 0)
errx(1, "-S can only be used with -a");
- if (mdio.md_mediasize != 0)
- errx(1, "-s can only be used with -a");
+ if (action != RESIZE && sflag != NULL)
+ errx(1, "-s can only be used with -a and -r");
if (mdio.md_fwsectors != 0)
errx(1, "-x can only be used with -a");
if (mdio.md_fwheads != 0)
@@ -295,13 +306,20 @@ main(int argc, char **argv)
errx(1, "-t can only be used with -a");
if (argc > 0)
errx(1, "file can only be used with -a");
- if (action != DETACH && (mdio.md_options & ~MD_AUTOUNIT) != 0)
- errx(1, "-o can only be used with -a and -d");
+ if ((action != DETACH && action != RESIZE) &&
+ (mdio.md_options & ~MD_AUTOUNIT) != 0)
+ errx(1, "-o can only be used with -a, -d, and -r");
if (action == DETACH &&
(mdio.md_options & ~(MD_FORCE | MD_AUTOUNIT)) != 0)
errx(1, "only -o [no]force can be used with -d");
+ if (action == RESIZE &&
+ (mdio.md_options & ~(MD_FORCE | MD_RESERVE | MD_AUTOUNIT)) != 0)
+ errx(1, "only -o [no]force and -o [no]reserve can be used with -r");
}
+ if (action == RESIZE && sflag == NULL)
+ errx(1, "must specify -s for -r");
+
if (action != LIST && vflag == OPT_VERBOSE)
errx(1, "-v can only be used with -l");
@@ -333,6 +351,12 @@ main(int argc, char **argv)
i = ioctl(fd, MDIOCDETACH, &mdio);
if (i < 0)
err(1, "ioctl(/dev/%s)", MDCTL_NAME);
+ } else if (action == RESIZE) {
+ if (mdio.md_options & MD_AUTOUNIT)
+ errx(1, "-r requires -u");
+ i = ioctl(fd, MDIOCRESIZE, &mdio);
+ if (i < 0)
+ err(1, "ioctl(/dev/%s)", MDCTL_NAME);
} else if (action == LIST) {
if (mdio.md_options & MD_AUTOUNIT) {
/*
@@ -342,7 +366,6 @@ main(int argc, char **argv)
md_list(NULL, OPT_LIST | vflag);
} else
return (md_query(uflag));
-
} else
usage();
close(fd);
diff --git a/sbin/mount/getmntopts.3 b/sbin/mount/getmntopts.3
index b8380adb7267..48c69404572b 100644
--- a/sbin/mount/getmntopts.3
+++ b/sbin/mount/getmntopts.3
@@ -70,8 +70,8 @@ has the following format:
.Bd -literal
struct mntopt {
char *m_option; /* option name */
- int m_inverse; /* is this a negative option, e.g. "dev" */
- int m_flag; /* bit to set, e.g. MNT_RDONLY */
+ int m_inverse; /* is this a negative option, e.g., "dev" */
+ int m_flag; /* bit to set, e.g., MNT_RDONLY */
int m_altloc; /* non-zero to use altflagp rather than flagp */
};
.Ed
diff --git a/sbin/mount/mount.8 b/sbin/mount/mount.8
index e1673780f8df..6bc4b5cc565b 100644
--- a/sbin/mount/mount.8
+++ b/sbin/mount/mount.8
@@ -28,7 +28,7 @@
.\" @(#)mount.8 8.8 (Berkeley) 6/16/94
.\" $FreeBSD$
.\"
-.Dd June 6, 2011
+.Dd July 12, 2012
.Dt MOUNT 8
.Os
.Sh NAME
@@ -145,6 +145,11 @@ When used with the
.Fl u
flag, this is the same as specifying the options currently in effect for
the mounted file system.
+.It Cm failok
+If this option is specified,
+.Nm
+will return 0 even if an error occurs
+during the mount of the filesystem.
.It Cm force
The same as
.Fl f ;
@@ -171,7 +176,8 @@ Force
.Nm
to use the specified program to mount the file system, instead of calling
.Xr nmount 2
-directly. For example:
+directly.
+For example:
.Bd -literal
mount -t foofs -o mountprog=/mydir/fooprog /dev/acd0 /mnt
.Ed
@@ -440,12 +446,8 @@ However, for the following file system types:
.Cm mfs ,
.Cm msdosfs ,
.Cm nfs ,
-.Cm ntfs ,
-.Cm nwfs ,
.Cm nullfs ,
.Cm oldnfs ,
-.Cm portalfs ,
-.Cm smbfs ,
.Cm udf ,
and
.Cm unionfs .
@@ -539,11 +541,7 @@ support for a particular file system might be provided either on a static
.Xr mount_cd9660 8 ,
.Xr mount_msdosfs 8 ,
.Xr mount_nfs 8 ,
-.Xr mount_ntfs 8 ,
.Xr mount_nullfs 8 ,
-.Xr mount_nwfs 8 ,
-.Xr mount_portalfs 8 ,
-.Xr mount_smbfs 8 ,
.Xr mount_udf 8 ,
.Xr mount_unionfs 8 ,
.Xr umount 8 ,
diff --git a/sbin/mount/mount.c b/sbin/mount/mount.c
index ca81795c0d69..1984eaced0f7 100644
--- a/sbin/mount/mount.c
+++ b/sbin/mount/mount.c
@@ -142,8 +142,8 @@ use_mountprog(const char *vfstype)
*/
unsigned int i;
const char *fs[] = {
- "cd9660", "mfs", "msdosfs", "nfs", "ntfs",
- "nwfs", "nullfs", "oldnfs", "portalfs", "smbfs", "udf", "unionfs",
+ "cd9660", "mfs", "msdosfs", "nfs",
+ "nullfs", "oldnfs", "udf", "unionfs",
NULL
};
diff --git a/sbin/mount_fusefs/Makefile b/sbin/mount_fusefs/Makefile
new file mode 100644
index 000000000000..0af8a80c8c99
--- /dev/null
+++ b/sbin/mount_fusefs/Makefile
@@ -0,0 +1,33 @@
+# $FreeBSD$
+
+.if defined(DEBUG)
+DEBUG_FLAGS+= -D_DEBUG -g
+.endif
+
+.if defined(DEBUG2G)
+DEBUG_FLAGS+= -D_DEBUG2G -g
+.endif
+
+.if defined(DEBUG3G)
+DEBUG_FLAGS+= -D_DEBUG3G -g
+.endif
+
+.if defined(DEBUG_MSG)
+DEBUG_FLAGS+= -D_DEBUG_MSG
+.endif
+
+.if defined(F4BVERS)
+DEBUG_FLAGS+= -DFUSE4BSD_VERSION="\"${F4BVERS}\""
+.endif
+
+PROG= mount_fusefs
+SRCS= mount_fusefs.c getmntopts.c
+MAN8= mount_fusefs.8
+NO_MANCOMPRESS?= yes
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${MOUNT}
+
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/sbin/mount_fusefs/mount_fusefs.8 b/sbin/mount_fusefs/mount_fusefs.8
new file mode 100644
index 000000000000..7c8bb67a7771
--- /dev/null
+++ b/sbin/mount_fusefs/mount_fusefs.8
@@ -0,0 +1,363 @@
+.\" Copyright (c) 1980, 1989, 1991, 1993
+.\" The Regents of the University of California.
+.\" Copyright (c) 2005, 2006 Csaba Henk
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 11, 2012
+.Dt MOUNT_FUSEFS 8
+.Os
+.Sh NAME
+.Nm mount_fusefs
+.Nd mount a Fuse file system daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl A
+.Op Fl S
+.Op Fl v
+.Op Fl D Ar fuse_daemon
+.Op Fl O Ar daemon_opts
+.Op Fl s Ar special
+.Op Fl m Ar node
+.Op Fl h
+.Op Fl V
+.Op Fl o Ar option ...
+.Ar special node
+.Op Ar fuse_daemon ...
+.Sh DESCRIPTION
+Basic usage is to start a fuse daemon on the given
+.Ar special
+file. In practice, the daemon is assigned a
+.Ar special
+file automatically, which can then be indentified via
+.Xr fstat 1 .
+That special file can then be mounted by
+.Nm .
+.Pp
+However, the procedure of spawning a daemon will usually be automated
+so that it is performed by
+.Nm .
+If the command invoking a given
+.Ar fuse_daemon
+is appended to the list of arguments,
+.Nm
+will call the
+.Ar fuse_daemon
+via that command. In that way the
+.Ar fuse_daemon
+will be instructed to attach itself to
+.Ar special .
+From that on mounting goes as in the simple case. (See
+.Sx DAEMON MOUNTS . )
+.Pp
+The
+.Ar special
+argument will normally be treated as the path of the special file to mount.
+.Pp
+However, if
+.Pa auto
+is passed as
+.Ar special ,
+then
+.Nm
+will look for a suitable free fuse device by itself.
+.Pp
+Finally, if
+.Ar special
+is an integer it will be interpreted as the number
+of the file descriptor of an already open fuse device
+(used when the Fuse library invokes
+.Nm .
+(See
+.Sx DAEMON MOUNTS ) .
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl A , Ic --reject-allow_other
+Prohibit the
+.Cm allow_other
+mount flag. Intended for use in scripts and the
+.Xr sudoers 5
+file.
+.It Fl S , Ic --safe
+Run in safe mode (i.e. reject invoking a filesystem daemon)
+.It Fl v
+Be verbose
+.It Fl D, Ic --daemon Ar daemon
+Call the specified
+.Ar daemon
+.It Fl O, Ic --daemon_opts Ar opts
+Add
+.Ar opts
+to the daemon's command line
+.It Fl s, Ic --special Ar special
+Use
+.Ar special
+as special
+.It Fl m, Ic --mountpath Ar node
+Mount on
+.Ar node
+.It Fl h, Ic --help
+Show help
+.It Fl V, Ic --version
+Show version information
+.It Fl o
+Mount options are specified via
+.Fl o .
+The following options are available (and also their negated versions,
+by prefixing them with
+.Dq no ) :
+.Bl -tag -width indent
+.It Cm default_permissions
+Enable traditional (file mode based) permission checking in kernel
+.It Cm allow_other
+Do not apply
+.Sx STRICT ACCESS POLICY .
+Only root can use this option
+.It Cm max_read Ns = Ns Ar n
+Limit size of read requests to
+.Ar n
+.It Cm private
+Refuse shared mounting of the daemon. This is the default behaviour,
+to allow sharing, expicitly use
+.Fl o Cm noprivate
+.It Cm neglect_shares
+Do not refuse unmounting if there are secondary mounts
+.It Cm push_symlinks_in
+Prefix absolute symlinks with the mountpoint
+.El
+.Pp
+.El
+.Pp
+Besides the above mount options, there is a set of pseudo-mount options which
+are supported by the Fuse library. One can list these by passing
+.Fl h
+to a Fuse daemon. Most of these options have effect only on the behaviour of
+the daemon (that is, their scope is limited to userspace). However,
+there are some which do require in-kernel support.
+Currently the options supported by the kernel are:
+.Bl -tag -width indent
+.It Cm direct_io
+Bypass the buffer cache system
+.It Cm kernel_cache
+By default cached buffers of a given file are flushed at each
+.Xr open 2 .
+This option disables this behaviour
+.El
+.Sh DAEMON MOUNTS
+Usually users do not need to use
+.Nm
+directly, as the Fuse library enables Fuse daemons to invoke
+.Nm .
+That is,
+.Pp
+.Dl fuse_daemon device mountpoint
+.Pp
+has the same effect as
+.Pp
+.Dl mount_fusefs auto mountpoint fuse_daemon
+.Pp
+This is the recommended usage when you want basic usage
+(eg, run the daemon at a low privilege level but mount it as root).
+.Sh STRICT ACCESS POLICY
+The strict access policy for Fuse filesystems lets one to use the filesystem
+only if the filesystem daemon has the same credentials (uid, real uid, gid,
+real gid) as the user.
+.Pp
+This is applied for Fuse mounts by default and only root can mount without
+the strict access policy (ie. the
+.Cm allow_other
+mount option).
+.Pp
+This is to shield users from the daemon
+.Dq spying
+on their I/O activities.
+.Pp
+Users might opt to willingly relax strict access policy (as far they
+are concerned) by doing their own secondary mount (See
+.Sx SHARED MOUNTS ) .
+.Sh SHARED MOUNTS
+A Fuse daemon can be shared (ie. mounted multiple times).
+When doing the first (primary) mount, the spawner and the mounter of the daemon
+must have the same uid, or the mounter should be the superuser.
+.Pp
+After the primary mount is in place, secondary mounts can be done by anyone
+unless this feature is disabled by
+.Cm private .
+The behaviour of a secondary mount is analogous to that of symbolic
+links: they redirect all filesystem operations to the primary mount.
+.Pp
+Doing a secondary mount is like signing an agreement: by this action, the mounter
+agrees that the Fuse daemon can trace her I/O activities. From then on
+she is not banned from using the filesystem (either via her own mount or
+via the primary mount), regardless whether
+.Cm allow_other
+is used or not.
+.Pp
+The device name of a secondary mount is the device name of the corresponding
+primary mount, followed by a '#' character and the index of the secondary
+mount; e.g.
+.Pa /dev/fuse0#3 .
+.Sh SECURITY
+System administrators might want to use a custom mount policy (ie., one going
+beyond the
+.Va vfs.usermount
+sysctl). The primary tool for such purposes is
+.Xr sudo 8 .
+However, given that
+.Nm
+is capable of invoking an arbitrary program, one must be careful when doing this.
+.Nm
+is designed in a way such that it makes that easy. For this purpose,
+there are options which disable certain risky features (ie.
+.Fl S
+and
+.Fl A ) ,
+and command line parsing is done in a flexible way: mixing options and
+non-options is allowed, but processing them stops at the third non-option
+argument (after the first two has been utilized as device and mountpoint).
+The rest of the command line specifies the daemon and its arguments.
+(Alternatively, the daemon, the special and the mount path can be
+specified using the respective options.) Note that
+.Nm
+ignores the environment variable
+.Ev POSIXLY_CORRECT
+and always behaves as described.
+.Pp
+In general, to be as scripting /
+.Xr sudoers 5
+friendly as possible, no information has a fixed
+position in the command line, but once a given piece of information is
+provided, subsequent arguments/options cannot override it (with the
+exception of some non-critical ones).
+.Sh ENVIRONMENT
+.Bl -tag -width ".Ev MOUNT_FUSEFS_SAFE"
+.It Ev MOUNT_FUSEFS_SAFE
+This has the same effect as the
+.Fl S
+option.
+.It Ev MOUNT_FUSEFS_VERBOSE
+This has the same effect as the
+.Fl v
+option.
+.It Ev MOUNT_FUSEFS_IGNORE_UNKNOWN
+If set,
+.Nm
+will ignore uknown mount options.
+.It Ev MOUNT_FUSEFS_CALL_BY_LIB
+Adjust behaviour to the needs of the FUSE library. Currently it effects
+help output.
+.El
+.Pp
+Although the following variables do not have any effect on
+.Nm
+itself, they affect the behaviour of fuse daemons:
+.Bl -tag -width ".Ev FUSE_DEV_NAME"
+.It Ev FUSE_DEV_NAME
+Device to attach. If not set, the multiplexer path
+.Ar /dev/fuse
+is used.
+.It Ev FUSE_DEV_FD
+File desciptor of an opened Fuse device to use. Overrides
+.Ev FUSE_DEV_NAME .
+.It Ev FUSE_NO_MOUNT
+If set, the library will not attempt to mount the filesystem, even
+if a mountpoint argument is supplied.
+.El
+.Sh FILES
+.Bl -tag -width /dev/fuse
+.It Pa /dev/fuse
+Fuse device with which the kernel and Fuse daemons can communicate.
+.It Pa /dev/fuse
+The multiplexer path. An
+.Xr open 2
+performed on it automatically is passed to a free Fuse device by the kernel
+(which might be created just for this puprose).
+.El
+.Sh EXAMPLES
+Mount the example filesystem in the Fuse distribution (from its directory):
+either
+.Pp
+.Dl ./fusexmp /mnt/fuse
+.Pp
+or
+.Pp
+.Dl mount_fusefs auto /mnt/fuse ./fusexmp
+.Pp
+Doing the same in two steps, using
+.Pa /dev/fuse0 :
+.Pp
+.Dl FUSE_DEV_NAME=/dev/fuse ./fusexmp &&
+.Dl mount_fusefs /dev/fuse /mnt/fuse
+.Pp
+A script wrapper for fusexmp which ensures that
+.Nm
+does not call any external utility and also provides a hacky
+(non race-free) automatic device selection:
+.Pp
+.Dl #!/bin/sh -e
+.Pp
+.Dl FUSE_DEV_NAME=/dev/fuse fusexmp
+.Dl mount_fusefs -S /dev/fuse /mnt/fuse \(lq$@\(rq
+.Sh SEE ALSO
+.Xr fstat 1 ,
+.Xr mount 8 ,
+.Xr umount 8 ,
+.Xr sudo 8
+.Sh HISTORY
+.Nm
+appears as the part of the FreeBSD implementation of the Fuse userspace filesystem
+framework (see http://fuse.sourceforge.net). This user interface is FreeBSD specific.
+.Sh CAVEATS
+Secondary mounts should be unmounted via their device name. If an attempt is
+made to be unmount them via their filesystem root path, the unmount request
+will be forwarded to the primary mount path.
+In general, unmounting by device name is less error-prone than by mount path
+(although the latter will also work under normal circumstances).
+.Pp
+If the daemon is specified via the
+.Fl D
+and
+.Fl O
+options, it will be invoked via
+.Xr system 3 ,
+and the daemon's command line will also have an
+.Dq &
+control operator appended, so that we do not have to wait for its termination.
+You should use a simple command line when invoking the daemon via these options.
+.Sh BUGS
+.Ar special
+is treated as a multiplexer if and only if it is literally the same as
+.Pa auto
+or
+.Pa /dev/fuse .
+Other paths which are equivalent with
+.Pa /dev/fuse
+(eg.,
+.Pa /../dev/fuse )
+are not.
diff --git a/sbin/mount_fusefs/mount_fusefs.c b/sbin/mount_fusefs/mount_fusefs.c
new file mode 100644
index 000000000000..797aba7228a4
--- /dev/null
+++ b/sbin/mount_fusefs/mount_fusefs.c
@@ -0,0 +1,499 @@
+/*-
+ * Copyright (c) 2005 Jean-Sebastien Pedron
+ * Copyright (c) 2005 Csaba Henk
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <getopt.h>
+#include <limits.h>
+#include <osreldate.h>
+#include <paths.h>
+
+#include "mntopts.h"
+
+#ifndef FUSE4BSD_VERSION
+#define FUSE4BSD_VERSION "0.3.9-pre1"
+#endif
+
+void __usage_short(void);
+void usage(void);
+void helpmsg(void);
+void showversion(void);
+int init_backgrounded(void);
+
+static struct mntopt mopts[] = {
+ #define ALTF_PRIVATE 0x01
+ { "private", 0, ALTF_PRIVATE, 1 },
+ { "neglect_shares", 0, 0x02, 1 },
+ { "push_symlinks_in", 0, 0x04, 1 },
+ { "allow_other", 0, 0x08, 1 },
+ { "default_permissions", 0, 0x10, 1 },
+ #define ALTF_MAXREAD 0x20
+ { "max_read=", 0, ALTF_MAXREAD, 1 },
+ #define ALTF_SUBTYPE 0x40
+ { "subtype=", 0, ALTF_SUBTYPE, 1 },
+ #define ALTF_SYNC_UNMOUNT 0x80
+ { "sync_unmount", 0, ALTF_SYNC_UNMOUNT, 1 },
+ /* Linux specific options, we silently ignore them */
+ { "fsname=", 0, 0x00, 1 },
+ { "fd=", 0, 0x00, 1 },
+ { "rootmode=", 0, 0x00, 1 },
+ { "user_id=", 0, 0x00, 1 },
+ { "group_id=", 0, 0x00, 1 },
+ { "large_read", 0, 0x00, 1 },
+ /* "nonempty", just the first two chars are stripped off during parsing */
+ { "nempty", 0, 0x00, 1 },
+ MOPT_STDOPTS,
+ MOPT_END
+};
+
+struct mntval {
+ int mv_flag;
+ void *mv_value;
+ int mv_len;
+};
+
+static struct mntval mvals[] = {
+ { ALTF_MAXREAD, NULL, 0 },
+ { ALTF_SUBTYPE, NULL, 0 },
+ { 0, NULL, 0 }
+};
+
+#define DEFAULT_MOUNT_FLAGS ALTF_PRIVATE | ALTF_SYNC_UNMOUNT
+
+int
+main(int argc, char *argv[])
+{
+ struct iovec *iov;
+ int mntflags, iovlen, verbose = 0;
+ char *dev = NULL, *dir = NULL, mntpath[MAXPATHLEN];
+ char *devo = NULL, *diro = NULL;
+ char ndev[128], fdstr[15];
+ int i, done = 0, reject_allow_other = 0, safe_level = 0;
+ int altflags = DEFAULT_MOUNT_FLAGS;
+ int __altflags = DEFAULT_MOUNT_FLAGS;
+ int ch = 0;
+ struct mntopt *mo;
+ struct mntval *mv;
+ static struct option longopts[] = {
+ {"reject-allow_other", no_argument, NULL, 'A'},
+ {"safe", no_argument, NULL, 'S'},
+ {"daemon", required_argument, NULL, 'D'},
+ {"daemon_opts", required_argument, NULL, 'O'},
+ {"special", required_argument, NULL, 's'},
+ {"mountpath", required_argument, NULL, 'm'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'h'},
+ {0,0,0,0}
+ };
+ int pid = 0;
+ int fd = -1, fdx;
+ char *ep;
+ char *daemon_str = NULL, *daemon_opts = NULL;
+
+ /*
+ * We want a parsing routine which is not sensitive to
+ * the position of args/opts; it should extract the
+ * first two args and stop at the beginning of the rest.
+ * (This makes it easier to call mount_fusefs from external
+ * utils than it is with a strict "util flags args" syntax.)
+ */
+
+ iov = NULL;
+ iovlen = 0;
+ mntflags = 0;
+ /* All in all, I feel it more robust this way... */
+ unsetenv("POSIXLY_CORRECT");
+ if (getenv("MOUNT_FUSEFS_IGNORE_UNKNOWN"))
+ getmnt_silent = 1;
+ if (getenv("MOUNT_FUSEFS_VERBOSE"))
+ verbose = 1;
+
+ do {
+ for (i = 0; i < 3; i++) {
+ if (optind < argc && argv[optind][0] != '-') {
+ if (dir) {
+ done = 1;
+ break;
+ }
+ if (dev)
+ dir = argv[optind];
+ else
+ dev = argv[optind];
+ optind++;
+ }
+ }
+ switch(ch) {
+ case 'A':
+ reject_allow_other = 1;
+ break;
+ case 'S':
+ safe_level = 1;
+ break;
+ case 'D':
+ if (daemon_str)
+ errx(1, "daemon specified inconsistently");
+ daemon_str = optarg;
+ break;
+ case 'O':
+ if (daemon_opts)
+ errx(1, "daemon opts specified inconsistently");
+ daemon_opts = optarg;
+ break;
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, &altflags);
+ for (mv = mvals; mv->mv_flag; ++mv) {
+ if (! (altflags & mv->mv_flag))
+ continue;
+ for (mo = mopts; mo->m_flag; ++mo) {
+ char *p, *q;
+
+ if (mo->m_flag != mv->mv_flag)
+ continue;
+ p = strstr(optarg, mo->m_option);
+ if (p) {
+ p += strlen(mo->m_option);
+ q = p;
+ while (*q != '\0' && *q != ',')
+ q++;
+ mv->mv_len = q - p + 1;
+ mv->mv_value = malloc(mv->mv_len);
+ memcpy(mv->mv_value, p, mv->mv_len - 1);
+ ((char *)mv->mv_value)[mv->mv_len - 1] = '\0';
+ break;
+ }
+ }
+ }
+ break;
+ case 's':
+ if (devo)
+ errx(1, "special specified inconsistently");
+ devo = optarg;
+ break;
+ case 'm':
+ if (diro)
+ errx(1, "mount path specified inconsistently");
+ diro = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'h':
+ helpmsg();
+ break;
+ case 'V':
+ showversion();
+ break;
+ case '\0':
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ if (done)
+ break;
+ } while ((ch = getopt_long(argc, argv, "AvVho:SD:O:s:m:", longopts, NULL)) != -1);
+
+ argc -= optind;
+ argv += optind;
+
+ if (devo) {
+ if (dev)
+ errx(1, "special specified inconsistently");
+ dev = devo;
+ } else if (diro)
+ errx(1, "if mountpoint is given via an option, special should also be given via an option");
+
+ if (diro) {
+ if (dir)
+ errx(1, "mount path specified inconsistently");
+ dir = diro;
+ }
+
+ if ((! dev) && argc > 0) {
+ dev = *argv++;
+ argc--;
+ }
+
+ if ((! dir) && argc > 0) {
+ dir = *argv++;
+ argc--;
+ }
+
+ if (! (dev && dir))
+ errx(1, "missing special and/or mountpoint");
+
+ for (mo = mopts; mo->m_flag; ++mo) {
+ if (altflags & mo->m_flag) {
+ int iov_done = 0;
+
+ if (reject_allow_other &&
+ strcmp(mo->m_option, "allow_other") == 0)
+ /*
+ * reject_allow_other is stronger than a
+ * negative of allow_other: if this is set,
+ * allow_other is blocked, period.
+ */
+ errx(1, "\"allow_other\" usage is banned by respective option");
+
+ for (mv = mvals; mv->mv_flag; ++mv) {
+ if (mo->m_flag != mv->mv_flag)
+ continue;
+ if (mv->mv_value) {
+ build_iovec(&iov, &iovlen, mo->m_option, mv->mv_value, mv->mv_len);
+ iov_done = 1;
+ break;
+ }
+ }
+ if (! iov_done)
+ build_iovec(&iov, &iovlen, mo->m_option,
+ __DECONST(void *, ""), -1);
+ }
+ if (__altflags & mo->m_flag) {
+ char *uscore_opt;
+
+ if (asprintf(&uscore_opt, "__%s", mo->m_option) == -1)
+ err(1, "failed to allocate memory");
+ build_iovec(&iov, &iovlen, uscore_opt,
+ __DECONST(void *, ""), -1);
+ free(uscore_opt);
+ }
+ }
+
+ if (getenv("MOUNT_FUSEFS_SAFE"))
+ safe_level = 1;
+
+ if (safe_level > 0 && (argc > 0 || daemon_str || daemon_opts))
+ errx(1, "safe mode, spawning daemon not allowed");
+
+ if ((argc > 0 && (daemon_str || daemon_opts)) ||
+ (daemon_opts && ! daemon_str))
+ errx(1, "daemon specified inconsistently");
+
+ /*
+ * Resolve the mountpoint with realpath(3) and remove unnecessary
+ * slashes from the devicename if there are any.
+ */
+ if (checkpath(dir, mntpath) != 0)
+ err(1, "%s", mntpath);
+ (void)rmslashes(dev, dev);
+
+ if (strcmp(dev, "auto") == 0)
+ dev = __DECONST(char *, "/dev/fuse");
+
+ if (strcmp(dev, "/dev/fuse") == 0) {
+ if (! (argc > 0 || daemon_str)) {
+ fprintf(stderr, "Please also specify the fuse daemon to run when mounting via the multiplexer!\n");
+ usage();
+ }
+ if ((fd = open(dev, O_RDWR)) < 0)
+ err(1, "failed to open fuse device");
+ } else {
+ fdx = strtol(dev, &ep, 10);
+ if (*ep == '\0')
+ fd = fdx;
+ }
+
+ /* Identifying device */
+ if (fd >= 0) {
+ struct stat sbuf;
+ char *ndevbas, *lep;
+
+ if (fstat(fd, &sbuf) == -1)
+ err(1, "cannot stat device file descriptor");
+
+ strcpy(ndev, _PATH_DEV);
+ ndevbas = ndev + strlen(_PATH_DEV);
+ devname_r(sbuf.st_rdev, S_IFCHR, ndevbas,
+ sizeof(ndev) - strlen(_PATH_DEV));
+
+ if (strncmp(ndevbas, "fuse", 4))
+ errx(1, "mounting inappropriate device");
+
+ strtol(ndevbas + 4, &lep, 10);
+ if (*lep != '\0')
+ errx(1, "mounting inappropriate device");
+
+ dev = ndev;
+ }
+
+ if (argc > 0 || daemon_str) {
+ char *fds;
+
+ if (fd < 0 && (fd = open(dev, O_RDWR)) < 0)
+ err(1, "failed to open fuse device");
+
+ if (asprintf(&fds, "%d", fd) == -1)
+ err(1, "failed to allocate memory");
+ setenv("FUSE_DEV_FD", fds, 1);
+ free(fds);
+ setenv("FUSE_NO_MOUNT", "1", 1);
+
+ if (daemon_str) {
+ char *bgdaemon;
+ int len;
+
+ if (! daemon_opts)
+ daemon_opts = __DECONST(char *, "");
+
+ len = strlen(daemon_str) + 1 + strlen(daemon_opts) +
+ 2 + 1;
+ bgdaemon = calloc(1, len);
+
+ if (! bgdaemon)
+ err(1, "failed to allocate memory");
+
+ strlcpy(bgdaemon, daemon_str, len);
+ strlcat(bgdaemon, " ", len);
+ strlcat(bgdaemon, daemon_opts, len);
+ strlcat(bgdaemon, " &", len);
+
+ if (system(bgdaemon))
+ err(1, "failed to call fuse daemon");
+ } else {
+ if ((pid = fork()) < 0)
+ err(1, "failed to fork for fuse daemon");
+
+ if (pid == 0) {
+ execvp(argv[0], argv);
+ err(1, "failed to exec fuse daemon");
+ }
+ }
+ }
+
+ if (fd >= 0 && ! init_backgrounded() && close(fd) < 0) {
+ if (pid)
+ kill(pid, SIGKILL);
+ err(1, "failed to close fuse device");
+ }
+
+ /* Prepare the options vector for nmount(). build_iovec() is declared
+ * in mntopts.h. */
+ sprintf(fdstr, "%d", fd);
+ build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
+ build_iovec(&iov, &iovlen, "fspath", mntpath, -1);
+ build_iovec(&iov, &iovlen, "from", dev, -1);
+ build_iovec(&iov, &iovlen, "fd", fdstr, -1);
+
+ if (verbose)
+ fprintf(stderr, "mounting fuse daemon on device %s\n", dev);
+
+ if (nmount(iov, iovlen, mntflags) < 0)
+ err(EX_OSERR, "%s on %s", dev, mntpath);
+
+ exit(0);
+}
+
+void
+__usage_short(void) {
+ fprintf(stderr,
+ "usage:\n%s [-A|-S|-v|-V|-h|-D daemon|-O args|-s special|-m node|-o option...] special node [daemon args...]\n\n",
+ getprogname());
+}
+
+void
+usage(void)
+{
+ struct mntopt *mo;
+
+ __usage_short();
+
+ fprintf(stderr, "known options:\n");
+ for (mo = mopts; mo->m_flag; ++mo)
+ fprintf(stderr, "\t%s\n", mo->m_option);
+
+ fprintf(stderr, "\n(use -h for a detailed description of these options)\n");
+ exit(EX_USAGE);
+}
+
+void
+helpmsg(void)
+{
+ if (! getenv("MOUNT_FUSEFS_CALL_BY_LIB")) {
+ __usage_short();
+ fprintf(stderr, "description of options:\n");
+ }
+
+ /*
+ * The main use case of this function is giving info embedded in general
+ * FUSE lib help output. Therefore the style and the content of the output
+ * tries to fit there as much as possible.
+ */
+ fprintf(stderr,
+ " -o allow_other allow access to other users\n"
+ /* " -o nonempty allow mounts over non-empty file/dir\n" */
+ " -o default_permissions enable permission checking by kernel\n"
+ /*
+ " -o fsname=NAME set filesystem name\n"
+ " -o large_read issue large read requests (2.4 only)\n"
+ */
+ " -o subtype=NAME set filesystem type\n"
+ " -o max_read=N set maximum size of read requests\n"
+ " -o noprivate allow secondary mounting of the filesystem\n"
+ " -o neglect_shares don't report EBUSY when unmount attempted\n"
+ " in presence of secondary mounts\n"
+ " -o push_symlinks_in prefix absolute symlinks with mountpoint\n"
+ " -o sync_unmount do unmount synchronously\n"
+ );
+ exit(EX_USAGE);
+}
+
+void
+showversion(void)
+{
+ puts("mount_fusefs [fuse4bsd] version: " FUSE4BSD_VERSION);
+ exit(EX_USAGE);
+}
+
+int
+init_backgrounded(void)
+{
+ int ibg;
+ size_t len;
+
+ len = sizeof(ibg);
+
+ if (sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0))
+ return (0);
+
+ return (ibg);
+}
diff --git a/sbin/natd/natd.8 b/sbin/natd/natd.8
index cba4ed4a48d2..b4b37ed1fb5d 100644
--- a/sbin/natd/natd.8
+++ b/sbin/natd/natd.8
@@ -130,9 +130,9 @@ According to RFC 1918, unregistered source addresses are 10.0.0.0/8,
172.16.0.0/12 and 192.168.0.0/16.
.It Fl redirect_port Ar proto Xo
.Ar targetIP Ns : Ns Xo
-.Ar targetPORT Ns Op - Ns Ar targetPORT Xc
-.Op Ar aliasIP Ns : Ns Xo
-.Ar aliasPORT Ns Op - Ns Ar aliasPORT Xc
+.Ar targetPORT Ns Oo - Ns Ar targetPORT Oc Xc
+.Oo Ar aliasIP Ns : Oc Ns Xo
+.Ar aliasPORT Ns Oo - Ns Ar aliasPORT Oc Xc
.Oo Ar remoteIP Ns Oo : Ns
.Ar remotePORT Ns Op - Ns Ar remotePORT
.Oc Oc
@@ -247,10 +247,8 @@ to appear from the specified
.Ar targetIP Ns : Ns Xo
.Ar targetPORT Ns Oo , Ns
.Ar ...\&
-.Oc Oc
-.Xc
-.Xc
-.Op Ar aliasIP Ns : Ns Xo
+.Oc Xc Oc Xc
+.Oo Ar aliasIP Ns : Oc Ns Xo
.Ar aliasPORT
.Xc
.Oo Ar remoteIP Ns
diff --git a/sbin/natd/natd.c b/sbin/natd/natd.c
index 3dbfef0fe958..959e1eeaf52c 100644
--- a/sbin/natd/natd.c
+++ b/sbin/natd/natd.c
@@ -1330,7 +1330,7 @@ static void ParseOption (const char* option, const char* parms)
struct in_addr addrValue;
int max;
char* end;
- CODE* fac_record = NULL;
+ const CODE* fac_record = NULL;
/*
* Find option from table.
*/
@@ -1509,7 +1509,7 @@ static void ParseOption (const char* option, const char* parms)
break;
case LogIpfwDenied:
- logIpfwDenied = yesNoValue;;
+ logIpfwDenied = yesNoValue;
break;
case PidFile:
diff --git a/sbin/newfs/mkfs.c b/sbin/newfs/mkfs.c
index 5e2a57955907..0a547f90e3bc 100644
--- a/sbin/newfs/mkfs.c
+++ b/sbin/newfs/mkfs.c
@@ -263,6 +263,7 @@ restart:
}
sblock.fs_fsbtodb = ilog2(sblock.fs_fsize / sectorsize);
sblock.fs_size = fssize = dbtofsb(&sblock, fssize);
+ sblock.fs_providersize = dbtofsb(&sblock, mediasize / sectorsize);
/*
* Before the filesystem is finally initialized, mark it
@@ -1003,7 +1004,8 @@ iput(union dinode *ip, ino_t ino)
sblock.fs_cstotal.cs_nifree--;
fscs[0].cs_nifree--;
if (ino >= (unsigned long)sblock.fs_ipg * sblock.fs_ncg) {
- printf("fsinit: inode value out of range (%d).\n", ino);
+ printf("fsinit: inode value out of range (%ju).\n",
+ (uintmax_t)ino);
exit(32);
}
d = fsbtodb(&sblock, ino_to_fsba(&sblock, ino));
diff --git a/sbin/newfs/newfs.c b/sbin/newfs/newfs.c
index 61637749c212..3ce0be75f687 100644
--- a/sbin/newfs/newfs.c
+++ b/sbin/newfs/newfs.c
@@ -94,6 +94,7 @@ int lflag; /* enable multilabel for file system */
int nflag; /* do not create .snap directory */
int tflag; /* enable TRIM */
intmax_t fssize; /* file system size */
+off_t mediasize; /* device size */
int sectorsize; /* bytes/sector */
int realsectorsize; /* bytes/sector in hardware */
int fsize = 0; /* fragment size */
@@ -135,7 +136,6 @@ main(int argc, char *argv[])
char *cp, *special;
intmax_t reserved;
int ch, i, rval;
- off_t mediasize;
char part_name; /* partition name, default to full disk */
part_name = 'c';
diff --git a/sbin/newfs/newfs.h b/sbin/newfs/newfs.h
index 25bfcc8f68fa..9e1da0db8924 100644
--- a/sbin/newfs/newfs.h
+++ b/sbin/newfs/newfs.h
@@ -88,6 +88,7 @@ extern int lflag; /* enable multilabel MAC for file system */
extern int nflag; /* do not create .snap directory */
extern int tflag; /* enable TRIM */
extern intmax_t fssize; /* file system size */
+extern off_t mediasize; /* device size */
extern int sectorsize; /* bytes/sector */
extern int realsectorsize; /* bytes/sector in hardware*/
extern int fsize; /* fragment size */
diff --git a/sbin/nvmecontrol/Makefile b/sbin/nvmecontrol/Makefile
new file mode 100644
index 000000000000..04cefaaa191e
--- /dev/null
+++ b/sbin/nvmecontrol/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= nvmecontrol
+MAN= nvmecontrol.8
+
+.include <bsd.prog.mk>
diff --git a/sbin/nvmecontrol/nvmecontrol.8 b/sbin/nvmecontrol/nvmecontrol.8
new file mode 100644
index 000000000000..e726cbc08927
--- /dev/null
+++ b/sbin/nvmecontrol/nvmecontrol.8
@@ -0,0 +1,86 @@
+.\"
+.\" Copyright (c) 2012 Intel Corporation
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions, and the following disclaimer,
+.\" without modification.
+.\" 2. Redistributions in binary form must reproduce at minimum a disclaimer
+.\" substantially similar to the "NO WARRANTY" disclaimer below
+.\" ("Disclaimer") and any redistribution must be conditioned upon
+.\" including a substantially similar Disclaimer requirement for further
+.\" binary redistribution.
+.\"
+.\" NO WARRANTY
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+.\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+.\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+.\" HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
+.\"
+.\" nvmecontrol man page.
+.\"
+.\" Author: Jim Harris <jimharris@FreeBSD.org>
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 17, 2012
+.Dt NVMECONTROL 8
+.Os
+.Sh NAME
+.Nm nvmecontrol
+.Nd NVM Express control utility
+.Sh SYNOPSIS
+.Nm
+.Ic devlist
+.Nm
+.Ic identify
+.Op Fl v
+.Op Fl x
+.Aq device id
+.Nm
+.Ic perftest
+.Aq Fl n Ar num_threads
+.Aq Fl o Ar read|write
+.Op Fl p
+.Aq Fl s Ar size_in_bytes
+.Aq Fl t Ar time_in_sec
+.Aq device id
+.Sh DESCRIPTION
+NVM Express (NVMe) is a storage protocol standard, for SSDs and other
+high-speed storage devices over PCI Express.
+.Sh EXAMPLES
+.Dl nvmecontrol devlist
+.Pp
+Display a list of NVMe controllers and namespaces along with their device nodes.
+.Dl nvmecontrol identify nvme0
+.Pp
+Display a human-readable summary of the nvme0 IDENTIFY_CONTROLLER data.
+.Pp
+.Dl nvmecontrol identify -x -v nvme0ns1
+.Pp
+Display a hexadecimal dump of the nvme0 IDENTIFY_NAMESPACE data for namespace
+1.
+.Pp
+.Dl nvmecontrol perftest -n 32 -o read -s 512 -t 30 nvme0ns1
+.Pp
+Run a performance test on nvme0ns1 using 32 kernel threads for 30 seconds. Each
+thread will issue a single 512 byte read command. Results are printed to
+stdout when 30 seconds expires.
+.Sh AUTHORS
+.An -nosplit
+.Nm
+was developed by Intel and originally written by
+.An Jim Harris Aq jimharris@FreeBSD.org .
+.Pp
+This man page was written by
+.An Jim Harris Aq jimharris@FreeBSD.org .
diff --git a/sbin/nvmecontrol/nvmecontrol.c b/sbin/nvmecontrol/nvmecontrol.c
new file mode 100644
index 000000000000..6abd3f5d1d7a
--- /dev/null
+++ b/sbin/nvmecontrol/nvmecontrol.c
@@ -0,0 +1,600 @@
+/*-
+ * Copyright (C) 2012 Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/ioccom.h>
+#include <sys/stat.h>
+
+#include <dev/nvme/nvme.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#define DEVLIST_USAGE \
+" nvmecontrol devlist\n"
+
+#define IDENTIFY_USAGE \
+" nvmecontrol identify <controller id|namespace id>\n"
+
+#define PERFTEST_USAGE \
+" nvmecontrol perftest <-n num_threads> <-o read|write>\n" \
+" <-s size_in_bytes> <-t time_in_seconds>\n" \
+" <-i intr|wait> [-f refthread] [-p]\n" \
+" <namespace id>\n"
+
+static void perftest_usage(void);
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, DEVLIST_USAGE);
+ fprintf(stderr, IDENTIFY_USAGE);
+ fprintf(stderr, PERFTEST_USAGE);
+ exit(EX_USAGE);
+}
+
+static void
+print_controller_hex(struct nvme_controller_data *cdata, uint32_t length)
+{
+ uint32_t *p;
+ uint32_t i, j;
+
+ p = (uint32_t *)cdata;
+ length /= sizeof(uint32_t);
+
+ for (i = 0; i < length; i+=8) {
+ printf("%03x: ", i*4);
+ for (j = 0; j < 8; j++)
+ printf("%08x ", p[i+j]);
+ printf("\n");
+ }
+
+ printf("\n");
+}
+
+static void
+print_controller(struct nvme_controller_data *cdata)
+{
+ printf("Controller Capabilities/Features\n");
+ printf("================================\n");
+ printf("Vendor ID: %04x\n", cdata->vid);
+ printf("Subsystem Vendor ID: %04x\n", cdata->ssvid);
+ printf("Serial Number: %s\n", cdata->sn);
+ printf("Model Number: %s\n", cdata->mn);
+ printf("Firmware Version: %s\n", cdata->fr);
+ printf("Recommended Arb Burst: %d\n", cdata->rab);
+ printf("IEEE OUI Identifier: %02x %02x %02x\n",
+ cdata->ieee[0], cdata->ieee[1], cdata->ieee[2]);
+ printf("Multi-Interface Cap: %02x\n", cdata->mic);
+ /* TODO: Use CAP.MPSMIN to determine true memory page size. */
+ printf("Max Data Transfer Size: ");
+ if (cdata->mdts == 0)
+ printf("Unlimited\n");
+ else
+ printf("%d\n", PAGE_SIZE * (1 << cdata->mdts));
+ printf("\n");
+
+ printf("Admin Command Set Attributes\n");
+ printf("============================\n");
+ printf("Security Send/Receive: %s\n",
+ cdata->oacs.security ? "Supported" : "Not Supported");
+ printf("Format NVM: %s\n",
+ cdata->oacs.format ? "Supported" : "Not Supported");
+ printf("Firmware Activate/Download: %s\n",
+ cdata->oacs.firmware ? "Supported" : "Not Supported");
+ printf("Abort Command Limit: %d\n", cdata->acl+1);
+ printf("Async Event Request Limit: %d\n", cdata->aerl+1);
+ printf("Number of Firmware Slots: ");
+ if (cdata->oacs.firmware != 0)
+ printf("%d\n", cdata->frmw.num_slots);
+ else
+ printf("N/A\n");
+ printf("Firmware Slot 1 Read-Only: ");
+ if (cdata->oacs.firmware != 0)
+ printf("%s\n", cdata->frmw.slot1_ro ? "Yes" : "No");
+ else
+ printf("N/A\n");
+ printf("Per-Namespace SMART Log: %s\n",
+ cdata->lpa.ns_smart ? "Yes" : "No");
+ printf("Error Log Page Entries: %d\n", cdata->elpe+1);
+ printf("Number of Power States: %d\n", cdata->npss+1);
+ printf("\n");
+
+ printf("NVM Command Set Attributes\n");
+ printf("==========================\n");
+ printf("Submission Queue Entry Size\n");
+ printf(" Max: %d\n", 1 << cdata->sqes.max);
+ printf(" Min: %d\n", 1 << cdata->sqes.min);
+ printf("Completion Queue Entry Size\n");
+ printf(" Max: %d\n", 1 << cdata->cqes.max);
+ printf(" Min: %d\n", 1 << cdata->cqes.min);
+ printf("Number of Namespaces: %d\n", cdata->nn);
+ printf("Compare Command: %s\n",
+ cdata->oncs.compare ? "Supported" : "Not Supported");
+ printf("Write Uncorrectable Command: %s\n",
+ cdata->oncs.write_unc ? "Supported" : "Not Supported");
+ printf("Dataset Management Command: %s\n",
+ cdata->oncs.dsm ? "Supported" : "Not Supported");
+ printf("Volatile Write Cache: %s\n",
+ cdata->vwc.present ? "Present" : "Not Present");
+}
+
+static void
+print_namespace_hex(struct nvme_namespace_data *nsdata, uint32_t length)
+{
+ uint32_t *p;
+ uint32_t i, j;
+
+ p = (uint32_t *)nsdata;
+ length /= sizeof(uint32_t);
+
+ for (i = 0; i < length; i+=8) {
+ printf("%03x: ", i*4);
+ for (j = 0; j < 8; j++)
+ printf("%08x ", p[i+j]);
+ printf("\n");
+ }
+
+ printf("\n");
+}
+
+static void
+print_namespace(struct nvme_namespace_data *nsdata)
+{
+ uint32_t i;
+
+ printf("Size (in LBAs): %lld (%lldM)\n",
+ (long long)nsdata->nsze,
+ (long long)nsdata->nsze / 1024 / 1024);
+ printf("Capacity (in LBAs): %lld (%lldM)\n",
+ (long long)nsdata->ncap,
+ (long long)nsdata->ncap / 1024 / 1024);
+ printf("Utilization (in LBAs): %lld (%lldM)\n",
+ (long long)nsdata->nuse,
+ (long long)nsdata->nuse / 1024 / 1024);
+ printf("Thin Provisioning: %s\n",
+ nsdata->nsfeat.thin_prov ? "Supported" : "Not Supported");
+ printf("Number of LBA Formats: %d\n", nsdata->nlbaf+1);
+ printf("Current LBA Format: LBA Format #%d\n",
+ nsdata->flbas.format);
+ for (i = 0; i <= nsdata->nlbaf; i++) {
+ printf("LBA Format #%d:\n", i);
+ printf(" LBA Data Size: %d\n",
+ 1 << nsdata->lbaf[i].lbads);
+ }
+}
+
+static uint32_t
+ns_get_sector_size(struct nvme_namespace_data *nsdata)
+{
+
+ return (1 << nsdata->lbaf[0].lbads);
+}
+
+
+static void
+devlist(int argc, char *argv[])
+{
+ struct nvme_controller_data cdata;
+ struct nvme_namespace_data nsdata;
+ struct stat devstat;
+ char name[64], path[64];
+ uint32_t i;
+ int ch, ctrlr, exit_code, fd, found;
+
+ exit_code = EX_OK;
+
+ while ((ch = getopt(argc, argv, "")) != -1) {
+ switch ((char)ch) {
+ default:
+ usage();
+ }
+ }
+
+ ctrlr = -1;
+ found = 0;
+
+ while (1) {
+ ctrlr++;
+ sprintf(name, "nvme%d", ctrlr);
+ sprintf(path, "/dev/%s", name);
+
+ if (stat(path, &devstat) != 0)
+ break;
+
+ found++;
+
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ printf("Could not open %s.\n", path);
+ exit_code = EX_NOPERM;
+ continue;
+ }
+
+ if (ioctl(fd, NVME_IDENTIFY_CONTROLLER, &cdata) == -1) {
+ printf("ioctl to %s failed.\n", path);
+ exit_code = EX_IOERR;
+ continue;
+ }
+
+ printf("%6s: %s\n", name, cdata.mn);
+
+ for (i = 0; i < cdata.nn; i++) {
+ sprintf(name, "nvme%dns%d", ctrlr, i+1);
+ sprintf(path, "/dev/%s", name);
+
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ printf("Could not open %s.\n", path);
+ exit_code = EX_NOPERM;
+ continue;
+ }
+ if (ioctl(fd, NVME_IDENTIFY_NAMESPACE, &nsdata) == -1) {
+ printf("ioctl to %s failed.\n", path);
+ exit_code = EX_IOERR;
+ continue;
+ }
+ printf(" %10s (%lldGB)\n",
+ name,
+ nsdata.nsze *
+ (long long)ns_get_sector_size(&nsdata) /
+ 1024 / 1024 / 1024);
+ }
+ }
+
+ if (found == 0)
+ printf("No NVMe controllers found.\n");
+
+ exit(exit_code);
+}
+
+static void
+identify_ctrlr(int argc, char *argv[])
+{
+ struct nvme_controller_data cdata;
+ struct stat devstat;
+ char path[64];
+ int ch, fd, hexflag = 0, hexlength;
+ int verboseflag = 0;
+
+ while ((ch = getopt(argc, argv, "vx")) != -1) {
+ switch ((char)ch) {
+ case 'v':
+ verboseflag = 1;
+ break;
+ case 'x':
+ hexflag = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ sprintf(path, "/dev/%s", argv[optind]);
+
+ if (stat(path, &devstat) != 0) {
+ printf("Invalid device node '%s'.\n", path);
+ exit(EX_IOERR);
+ }
+
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ printf("Could not open %s.\n", path);
+ exit(EX_NOPERM);
+ }
+
+ if (ioctl(fd, NVME_IDENTIFY_CONTROLLER, &cdata) == -1) {
+ printf("ioctl to %s failed.\n", path);
+ exit(EX_IOERR);
+ }
+
+ if (hexflag == 1) {
+ if (verboseflag == 1)
+ hexlength = sizeof(struct nvme_controller_data);
+ else
+ hexlength = offsetof(struct nvme_controller_data,
+ reserved5);
+ print_controller_hex(&cdata, hexlength);
+ exit(EX_OK);
+ }
+
+ if (verboseflag == 1) {
+ printf("-v not currently supported without -x.\n");
+ usage();
+ }
+
+ print_controller(&cdata);
+ exit(EX_OK);
+}
+
+static void
+identify_ns(int argc, char *argv[])
+{
+ struct nvme_namespace_data nsdata;
+ struct stat devstat;
+ char path[64];
+ int ch, fd, hexflag = 0, hexlength;
+ int verboseflag = 0;
+
+ while ((ch = getopt(argc, argv, "vx")) != -1) {
+ switch ((char)ch) {
+ case 'v':
+ verboseflag = 1;
+ break;
+ case 'x':
+ hexflag = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ sprintf(path, "/dev/%s", argv[optind]);
+
+ if (stat(path, &devstat) != 0) {
+ printf("Invalid device node '%s'.\n", path);
+ exit(EX_IOERR);
+ }
+
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ printf("Could not open %s.\n", path);
+ exit(EX_NOPERM);
+ }
+
+ if (ioctl(fd, NVME_IDENTIFY_NAMESPACE, &nsdata) == -1) {
+ printf("ioctl to %s failed.\n", path);
+ exit(EX_IOERR);
+ }
+
+ if (hexflag == 1) {
+ if (verboseflag == 1)
+ hexlength = sizeof(struct nvme_namespace_data);
+ else
+ hexlength = offsetof(struct nvme_namespace_data,
+ reserved6);
+ print_namespace_hex(&nsdata, hexlength);
+ exit(EX_OK);
+ }
+
+ if (verboseflag == 1) {
+ printf("-v not currently supported without -x.\n");
+ usage();
+ }
+
+ print_namespace(&nsdata);
+ exit(EX_OK);
+}
+
+static void
+identify(int argc, char *argv[])
+{
+ char *target;
+
+ if (argc < 2)
+ usage();
+
+ while (getopt(argc, argv, "vx") != -1) ;
+
+ target = argv[optind];
+
+ /* Specified device node must have "nvme" in it. */
+ if (strstr(argv[optind], "nvme") == NULL) {
+ printf("Invalid device node '%s'.\n", argv[optind]);
+ exit(EX_IOERR);
+ }
+
+ optreset = 1;
+ optind = 1;
+
+ /*
+ * If devicde node contains "ns", we consider it a namespace,
+ * otherwise, consider it a controller.
+ */
+ if (strstr(target, "ns") == NULL)
+ identify_ctrlr(argc, argv);
+ else
+ identify_ns(argc, argv);
+}
+
+static void
+print_perftest(struct nvme_io_test *io_test, bool perthread)
+{
+ uint32_t i, io_completed = 0, iops, mbps;
+
+ for (i = 0; i < io_test->num_threads; i++)
+ io_completed += io_test->io_completed[i];
+
+ iops = io_completed/io_test->time;
+ mbps = iops * io_test->size / (1024*1024);
+
+ printf("Threads: %2d Size: %6d %5s Time: %3d IO/s: %7d MB/s: %4d\n",
+ io_test->num_threads, io_test->size,
+ io_test->opc == NVME_OPC_READ ? "READ" : "WRITE",
+ io_test->time, iops, mbps);
+
+ if (perthread)
+ for (i = 0; i < io_test->num_threads; i++)
+ printf("\t%3d: %8d IO/s\n", i,
+ io_test->io_completed[i]/io_test->time);
+
+ exit(1);
+}
+
+static void
+perftest_usage(void)
+{
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, PERFTEST_USAGE);
+ exit(EX_USAGE);
+}
+
+static void
+perftest(int argc, char *argv[])
+{
+ struct nvme_io_test io_test;
+ int fd;
+ char ch;
+ char *p;
+ const char *name;
+ char path[64];
+ u_long ioctl_cmd = NVME_IO_TEST;
+ bool nflag, oflag, sflag, tflag;
+ int err, perthread = 0;
+
+ nflag = oflag = sflag = tflag = false;
+ name = NULL;
+
+ memset(&io_test, 0, sizeof(io_test));
+
+ while ((ch = getopt(argc, argv, "f:i:n:o:ps:t:")) != -1) {
+ switch (ch) {
+ case 'f':
+ if (!strcmp(optarg, "refthread"))
+ io_test.flags |= NVME_TEST_FLAG_REFTHREAD;
+ break;
+ case 'i':
+ if (!strcmp(optarg, "bio") ||
+ !strcmp(optarg, "wait"))
+ ioctl_cmd = NVME_BIO_TEST;
+ else if (!strcmp(optarg, "io") ||
+ !strcmp(optarg, "intr"))
+ ioctl_cmd = NVME_IO_TEST;
+ break;
+ case 'n':
+ nflag = true;
+ io_test.num_threads = strtoul(optarg, &p, 0);
+ if (p != NULL && *p != '\0') {
+ fprintf(stderr,
+ "\"%s\" not valid number of threads.\n",
+ optarg);
+ perftest_usage();
+ } else if (io_test.num_threads == 0 ||
+ io_test.num_threads > 128) {
+ fprintf(stderr,
+ "\"%s\" not valid number of threads.\n",
+ optarg);
+ perftest_usage();
+ }
+ break;
+ case 'o':
+ oflag = true;
+ if (!strcmp(optarg, "read") || !strcmp(optarg, "READ"))
+ io_test.opc = NVME_OPC_READ;
+ else if (!strcmp(optarg, "write") ||
+ !strcmp(optarg, "WRITE"))
+ io_test.opc = NVME_OPC_WRITE;
+ else {
+ fprintf(stderr, "\"%s\" not valid opcode.\n",
+ optarg);
+ perftest_usage();
+ }
+ break;
+ case 'p':
+ perthread = 1;
+ break;
+ case 's':
+ sflag = true;
+ io_test.size = strtoul(optarg, &p, 0);
+ if (p == NULL || *p == '\0' || toupper(*p) == 'B') {
+ // do nothing
+ } else if (toupper(*p) == 'K') {
+ io_test.size *= 1024;
+ } else if (toupper(*p) == 'M') {
+ io_test.size *= 1024 * 1024;
+ } else {
+ fprintf(stderr, "\"%s\" not valid size.\n",
+ optarg);
+ perftest_usage();
+ }
+ break;
+ case 't':
+ tflag = true;
+ io_test.time = strtoul(optarg, &p, 0);
+ if (p != NULL && *p != '\0') {
+ fprintf(stderr,
+ "\"%s\" not valid time duration.\n",
+ optarg);
+ perftest_usage();
+ }
+ break;
+ }
+ }
+
+ name = argv[optind];
+
+ if (!nflag || !oflag || !sflag || !tflag || name == NULL)
+ perftest_usage();
+
+ sprintf(path, "/dev/%s", name);
+
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "%s not valid device.\n", path);
+ perftest_usage();
+ }
+
+ err = ioctl(fd, ioctl_cmd, &io_test);
+
+ if (err) {
+ fprintf(stderr, "NVME_IO_TEST returned %d\n", errno);
+ exit(EX_IOERR);
+ }
+
+ print_perftest(&io_test, perthread);
+ exit(EX_OK);
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ if (argc < 2)
+ usage();
+
+ if (strcmp(argv[1], "devlist") == 0)
+ devlist(argc-1, &argv[1]);
+ else if (strcmp(argv[1], "identify") == 0)
+ identify(argc-1, &argv[1]);
+ else if (strcmp(argv[1], "perftest") == 0)
+ perftest(argc-1, &argv[1]);
+
+ usage();
+
+ return (0);
+}
diff --git a/sbin/pfctl/Makefile b/sbin/pfctl/Makefile
index 2475baf94818..26c2258a5dd4 100644
--- a/sbin/pfctl/Makefile
+++ b/sbin/pfctl/Makefile
@@ -1,11 +1,10 @@
# $FreeBSD$
-.PATH: ${.CURDIR}/../../contrib/pf/pfctl
-.PATH: ${.CURDIR}/../../sys/contrib/pf/net
-.PATH: ${.CURDIR}/../../contrib/pf/man
+# pf_ruleset.c is shared between kernel and pfctl
+.PATH: ${.CURDIR}/../../sys/netpfil/pf
PROG= pfctl
-MAN= pfctl.8 pf.4 pflog.4 pfsync.4 pf.conf.5 pf.os.5
+MAN= pfctl.8
SRCS = pfctl.c parse.y pfctl_parser.c pf_print_state.c pfctl_altq.c
SRCS+= pfctl_osfp.c pfctl_radix.c pfctl_table.c pfctl_qstats.c
@@ -14,11 +13,8 @@ SRCS+= pf_ruleset.c
WARNS?= 2
CFLAGS+= -Wall -Wmissing-prototypes -Wno-uninitialized
-CFLAGS+= -Wstrict-prototypes -I${.CURDIR}/../../contrib/pf/pfctl
-
-# XXX ALTQ
-CFLAGS+= -DENABLE_ALTQ
-#CFLAGS+= -I${.CURDIR}/missing
+CFLAGS+= -Wstrict-prototypes
+CFLAGS+= -DENABLE_ALTQ -I${.CURDIR}
YFLAGS=
diff --git a/sbin/pfctl/missing/altq/altq.h b/sbin/pfctl/missing/altq/altq.h
deleted file mode 100644
index c740ed359ad1..000000000000
--- a/sbin/pfctl/missing/altq/altq.h
+++ /dev/null
@@ -1,204 +0,0 @@
-/* $FreeBSD$ */
-/* $KAME: altq.h,v 1.10 2003/07/10 12:07:47 kjc Exp $ */
-
-/*
- * Copyright (C) 1998-2003
- * Sony Computer Science Laboratories Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY SONY CSL 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 SONY CSL OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#ifndef _ALTQ_ALTQ_H_
-#define _ALTQ_ALTQ_H_
-
-#if 0
-/*
- * allow altq-3 (altqd(8) and /dev/altq) to coexist with the new pf-based altq.
- * altq3 is mainly for research experiments. pf-based altq is for daily use.
- */
-#define ALTQ3_COMPAT /* for compatibility with altq-3 */
-#define ALTQ3_CLFIER_COMPAT /* for compatibility with altq-3 classifier */
-#endif
-
-#ifdef ALTQ3_COMPAT
-#include <sys/param.h>
-#include <sys/ioccom.h>
-#include <sys/queue.h>
-#include <netinet/in.h>
-
-#ifndef IFNAMSIZ
-#define IFNAMSIZ 16
-#endif
-#endif /* ALTQ3_COMPAT */
-
-/* altq discipline type */
-#define ALTQT_NONE 0 /* reserved */
-#define ALTQT_CBQ 1 /* cbq */
-#define ALTQT_WFQ 2 /* wfq */
-#define ALTQT_AFMAP 3 /* afmap */
-#define ALTQT_FIFOQ 4 /* fifoq */
-#define ALTQT_RED 5 /* red */
-#define ALTQT_RIO 6 /* rio */
-#define ALTQT_LOCALQ 7 /* local use */
-#define ALTQT_HFSC 8 /* hfsc */
-#define ALTQT_CDNR 9 /* traffic conditioner */
-#define ALTQT_BLUE 10 /* blue */
-#define ALTQT_PRIQ 11 /* priority queue */
-#define ALTQT_JOBS 12 /* JoBS */
-#define ALTQT_MAX 13 /* should be max discipline type + 1 */
-
-#ifdef ALTQ3_COMPAT
-struct altqreq {
- char ifname[IFNAMSIZ]; /* if name, e.g. "en0" */
- u_long arg; /* request-specific argument */
-};
-#endif
-
-/* simple token backet meter profile */
-struct tb_profile {
- u_int rate; /* rate in bit-per-sec */
- u_int depth; /* depth in bytes */
-};
-
-#ifdef ALTQ3_COMPAT
-struct tbrreq {
- char ifname[IFNAMSIZ]; /* if name, e.g. "en0" */
- struct tb_profile tb_prof; /* token bucket profile */
-};
-
-#ifdef ALTQ3_CLFIER_COMPAT
-/*
- * common network flow info structure
- */
-struct flowinfo {
- u_char fi_len; /* total length */
- u_char fi_family; /* address family */
- u_int8_t fi_data[46]; /* actually longer; address family
- specific flow info. */
-};
-
-/*
- * flow info structure for internet protocol family.
- * (currently this is the only protocol family supported)
- */
-struct flowinfo_in {
- u_char fi_len; /* sizeof(struct flowinfo_in) */
- u_char fi_family; /* AF_INET */
- u_int8_t fi_proto; /* IPPROTO_XXX */
- u_int8_t fi_tos; /* type-of-service */
- struct in_addr fi_dst; /* dest address */
- struct in_addr fi_src; /* src address */
- u_int16_t fi_dport; /* dest port */
- u_int16_t fi_sport; /* src port */
- u_int32_t fi_gpi; /* generalized port id for ipsec */
- u_int8_t _pad[28]; /* make the size equal to
- flowinfo_in6 */
-};
-
-#ifdef SIN6_LEN
-struct flowinfo_in6 {
- u_char fi6_len; /* sizeof(struct flowinfo_in6) */
- u_char fi6_family; /* AF_INET6 */
- u_int8_t fi6_proto; /* IPPROTO_XXX */
- u_int8_t fi6_tclass; /* traffic class */
- u_int32_t fi6_flowlabel; /* ipv6 flowlabel */
- u_int16_t fi6_dport; /* dest port */
- u_int16_t fi6_sport; /* src port */
- u_int32_t fi6_gpi; /* generalized port id */
- struct in6_addr fi6_dst; /* dest address */
- struct in6_addr fi6_src; /* src address */
-};
-#endif /* INET6 */
-
-/*
- * flow filters for AF_INET and AF_INET6
- */
-struct flow_filter {
- int ff_ruleno;
- struct flowinfo_in ff_flow;
- struct {
- struct in_addr mask_dst;
- struct in_addr mask_src;
- u_int8_t mask_tos;
- u_int8_t _pad[3];
- } ff_mask;
- u_int8_t _pad2[24]; /* make the size equal to flow_filter6 */
-};
-
-#ifdef SIN6_LEN
-struct flow_filter6 {
- int ff_ruleno;
- struct flowinfo_in6 ff_flow6;
- struct {
- struct in6_addr mask6_dst;
- struct in6_addr mask6_src;
- u_int8_t mask6_tclass;
- u_int8_t _pad[3];
- } ff_mask6;
-};
-#endif /* INET6 */
-#endif /* ALTQ3_CLFIER_COMPAT */
-#endif /* ALTQ3_COMPAT */
-
-/*
- * generic packet counter
- */
-struct pktcntr {
- u_int64_t packets;
- u_int64_t bytes;
-};
-
-#define PKTCNTR_ADD(cntr, len) \
- do { (cntr)->packets++; (cntr)->bytes += len; } while (/*CONSTCOND*/ 0)
-
-#ifdef ALTQ3_COMPAT
-/*
- * altq related ioctls
- */
-#define ALTQGTYPE _IOWR('q', 0, struct altqreq) /* get queue type */
-#if 0
-/*
- * these ioctls are currently discipline-specific but could be shared
- * in the future.
- */
-#define ALTQATTACH _IOW('q', 1, struct altqreq) /* attach discipline */
-#define ALTQDETACH _IOW('q', 2, struct altqreq) /* detach discipline */
-#define ALTQENABLE _IOW('q', 3, struct altqreq) /* enable discipline */
-#define ALTQDISABLE _IOW('q', 4, struct altqreq) /* disable discipline*/
-#define ALTQCLEAR _IOW('q', 5, struct altqreq) /* (re)initialize */
-#define ALTQCONFIG _IOWR('q', 6, struct altqreq) /* set config params */
-#define ALTQADDCLASS _IOWR('q', 7, struct altqreq) /* add a class */
-#define ALTQMODCLASS _IOWR('q', 8, struct altqreq) /* modify a class */
-#define ALTQDELCLASS _IOWR('q', 9, struct altqreq) /* delete a class */
-#define ALTQADDFILTER _IOWR('q', 10, struct altqreq) /* add a filter */
-#define ALTQDELFILTER _IOWR('q', 11, struct altqreq) /* delete a filter */
-#define ALTQGETSTATS _IOWR('q', 12, struct altqreq) /* get statistics */
-#define ALTQGETCNTR _IOWR('q', 13, struct altqreq) /* get a pkt counter */
-#endif /* 0 */
-#define ALTQTBRSET _IOW('q', 14, struct tbrreq) /* set tb regulator */
-#define ALTQTBRGET _IOWR('q', 15, struct tbrreq) /* get tb regulator */
-#endif /* ALTQ3_COMPAT */
-
-#ifdef _KERNEL
-#include <altq/altq_var.h>
-#endif
-
-#endif /* _ALTQ_ALTQ_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_cbq.h b/sbin/pfctl/missing/altq/altq_cbq.h
deleted file mode 100644
index 3c41c6bd836f..000000000000
--- a/sbin/pfctl/missing/altq/altq_cbq.h
+++ /dev/null
@@ -1,232 +0,0 @@
-/* $FreeBSD$ */
-/* $KAME: altq_cbq.h,v 1.10 2003/08/20 23:30:23 itojun Exp $ */
-
-/*
- * Copyright (c) Sun Microsystems, Inc. 1993-1998 All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the SMCC Technology
- * Development Group at Sun Microsystems, Inc.
- *
- * 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or
- * promote products derived from this software without specific prior
- * written permission.
- *
- * SUN MICROSYSTEMS DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE OR THE
- * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE. The software is
- * provided "as is" without express or implied warranty of any kind.
- *
- * These notices must be retained in any copies of any part of this software.
- */
-
-#ifndef _ALTQ_ALTQ_CBQ_H_
-#define _ALTQ_ALTQ_CBQ_H_
-
-#include <altq/altq.h>
-#include <altq/altq_rmclass.h>
-#include <altq/altq_red.h>
-#include <altq/altq_rio.h>
-
-/* #pragma ident "@(#)cbq.h 1.18 98/05/13 SMI" */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Define a well known class handles
- */
-#define NULL_CLASS_HANDLE 0xffffffff
-#define ROOT_CLASS_HANDLE 0xfffffffe
-#define DEFAULT_CLASS_HANDLE 0xfffffffd
-#ifdef ALTQ3_COMPAT
-#define CTL_CLASS_HANDLE 0xfffffffc
-#endif
-
-/* class flags shoud be same as class flags in rm_class.h */
-#define CBQCLF_RED 0x0001 /* use RED */
-#define CBQCLF_ECN 0x0002 /* use RED/ECN */
-#define CBQCLF_RIO 0x0004 /* use RIO */
-#define CBQCLF_FLOWVALVE 0x0008 /* use flowvalve (aka penalty-box) */
-#define CBQCLF_CLEARDSCP 0x0010 /* clear diffserv codepoint */
-#define CBQCLF_BORROW 0x0020 /* borrow from parent */
-
-/* class flags only for root class */
-#define CBQCLF_WRR 0x0100 /* weighted-round robin */
-#define CBQCLF_EFFICIENT 0x0200 /* work-conserving */
-
-/* class flags for special classes */
-#define CBQCLF_ROOTCLASS 0x1000 /* root class */
-#define CBQCLF_DEFCLASS 0x2000 /* default class */
-#ifdef ALTQ3_COMPAT
-#define CBQCLF_CTLCLASS 0x4000 /* control class */
-#endif
-#define CBQCLF_CLASSMASK 0xf000 /* class mask */
-
-#define CBQ_MAXQSIZE 200
-#define CBQ_MAXPRI RM_MAXPRIO
-
-typedef struct _cbq_class_stats_ {
- u_int32_t handle;
- u_int depth;
-
- struct pktcntr xmit_cnt; /* packets sent in this class */
- struct pktcntr drop_cnt; /* dropped packets */
- u_int over; /* # times went over limit */
- u_int borrows; /* # times tried to borrow */
- u_int overactions; /* # times invoked overlimit action */
- u_int delays; /* # times invoked delay actions */
-
- /* other static class parameters useful for debugging */
- int priority;
- int maxidle;
- int minidle;
- int offtime;
- int qmax;
- int ns_per_byte;
- int wrr_allot;
-
- int qcnt; /* # packets in queue */
- int avgidle;
-
- /* red and rio related info */
- int qtype;
- struct redstats red[3];
-} class_stats_t;
-
-#ifdef ALTQ3_COMPAT
-/*
- * Define structures associated with IOCTLS for cbq.
- */
-
-/*
- * Define the CBQ interface structure. This must be included in all
- * IOCTL's such that the CBQ driver may find the appropriate CBQ module
- * associated with the network interface to be affected.
- */
-struct cbq_interface {
- char cbq_ifacename[IFNAMSIZ];
-};
-
-typedef struct cbq_class_spec {
- u_int priority;
- u_int nano_sec_per_byte;
- u_int maxq;
- u_int maxidle;
- int minidle;
- u_int offtime;
- u_int32_t parent_class_handle;
- u_int32_t borrow_class_handle;
-
- u_int pktsize;
- int flags;
-} cbq_class_spec_t;
-
-struct cbq_add_class {
- struct cbq_interface cbq_iface;
-
- cbq_class_spec_t cbq_class;
- u_int32_t cbq_class_handle;
-};
-
-struct cbq_delete_class {
- struct cbq_interface cbq_iface;
- u_int32_t cbq_class_handle;
-};
-
-struct cbq_modify_class {
- struct cbq_interface cbq_iface;
-
- cbq_class_spec_t cbq_class;
- u_int32_t cbq_class_handle;
-};
-
-struct cbq_add_filter {
- struct cbq_interface cbq_iface;
- u_int32_t cbq_class_handle;
- struct flow_filter cbq_filter;
-
- u_long cbq_filter_handle;
-};
-
-struct cbq_delete_filter {
- struct cbq_interface cbq_iface;
- u_long cbq_filter_handle;
-};
-
-/* number of classes are returned in nclasses field */
-struct cbq_getstats {
- struct cbq_interface iface;
- int nclasses;
- class_stats_t *stats;
-};
-
-/*
- * Define IOCTLs for CBQ.
- */
-#define CBQ_IF_ATTACH _IOW('Q', 1, struct cbq_interface)
-#define CBQ_IF_DETACH _IOW('Q', 2, struct cbq_interface)
-#define CBQ_ENABLE _IOW('Q', 3, struct cbq_interface)
-#define CBQ_DISABLE _IOW('Q', 4, struct cbq_interface)
-#define CBQ_CLEAR_HIERARCHY _IOW('Q', 5, struct cbq_interface)
-#define CBQ_ADD_CLASS _IOWR('Q', 7, struct cbq_add_class)
-#define CBQ_DEL_CLASS _IOW('Q', 8, struct cbq_delete_class)
-#define CBQ_MODIFY_CLASS _IOWR('Q', 9, struct cbq_modify_class)
-#define CBQ_ADD_FILTER _IOWR('Q', 10, struct cbq_add_filter)
-#define CBQ_DEL_FILTER _IOW('Q', 11, struct cbq_delete_filter)
-#define CBQ_GETSTATS _IOWR('Q', 12, struct cbq_getstats)
-#endif /* ALTQ3_COMPAT */
-
-#ifdef _KERNEL
-/*
- * Define macros only good for kernel drivers and modules.
- */
-#define CBQ_WATCHDOG (hz / 20)
-#define CBQ_TIMEOUT 10
-#define CBQ_LS_TIMEOUT (20 * hz / 1000)
-
-#define CBQ_MAX_CLASSES 256
-
-#ifdef ALTQ3_COMPAT
-#define CBQ_MAX_FILTERS 256
-
-#define DISABLE 0x00
-#define ENABLE 0x01
-#endif /* ALTQ3_COMPAT */
-
-/*
- * Define State structures.
- */
-typedef struct cbqstate {
-#ifdef ALTQ3_COMPAT
- struct cbqstate *cbq_next;
-#endif
- int cbq_qlen; /* # of packets in cbq */
- struct rm_class *cbq_class_tbl[CBQ_MAX_CLASSES];
-
- struct rm_ifdat ifnp;
- struct callout cbq_callout; /* for timeouts */
-#ifdef ALTQ3_CLFIER_COMPAT
- struct acc_classifier cbq_classifier;
-#endif
-} cbq_state_t;
-
-#endif /* _KERNEL */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* !_ALTQ_ALTQ_CBQ_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_classq.h b/sbin/pfctl/missing/altq/altq_classq.h
deleted file mode 100644
index 43045b25a630..000000000000
--- a/sbin/pfctl/missing/altq/altq_classq.h
+++ /dev/null
@@ -1,203 +0,0 @@
-/* $FreeBSD$ */
-/* $KAME: altq_classq.h,v 1.6 2003/01/07 07:33:38 kjc Exp $ */
-
-/*
- * Copyright (c) 1991-1997 Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 4. Neither the name of the University nor of the Laboratory 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.
- */
-/*
- * class queue definitions extracted from rm_class.h.
- */
-#ifndef _ALTQ_ALTQ_CLASSQ_H_
-#define _ALTQ_ALTQ_CLASSQ_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Packet Queue types: RED or DROPHEAD.
- */
-#define Q_DROPHEAD 0x00
-#define Q_RED 0x01
-#define Q_RIO 0x02
-#define Q_DROPTAIL 0x03
-
-#ifdef _KERNEL
-
-/*
- * Packet Queue structures and macros to manipulate them.
- */
-struct _class_queue_ {
- struct mbuf *tail_; /* Tail of packet queue */
- int qlen_; /* Queue length (in number of packets) */
- int qlim_; /* Queue limit (in number of packets*) */
- int qtype_; /* Queue type */
-};
-
-typedef struct _class_queue_ class_queue_t;
-
-#define qtype(q) (q)->qtype_ /* Get queue type */
-#define qlimit(q) (q)->qlim_ /* Max packets to be queued */
-#define qlen(q) (q)->qlen_ /* Current queue length. */
-#define qtail(q) (q)->tail_ /* Tail of the queue */
-#define qhead(q) ((q)->tail_ ? (q)->tail_->m_nextpkt : NULL)
-
-#define qempty(q) ((q)->qlen_ == 0) /* Is the queue empty?? */
-#define q_is_red(q) ((q)->qtype_ == Q_RED) /* Is the queue a red queue */
-#define q_is_rio(q) ((q)->qtype_ == Q_RIO) /* Is the queue a rio queue */
-#define q_is_red_or_rio(q) ((q)->qtype_ == Q_RED || (q)->qtype_ == Q_RIO)
-
-#if !defined(__GNUC__) || defined(ALTQ_DEBUG)
-
-extern void _addq(class_queue_t *, struct mbuf *);
-extern struct mbuf *_getq(class_queue_t *);
-extern struct mbuf *_getq_tail(class_queue_t *);
-extern struct mbuf *_getq_random(class_queue_t *);
-extern void _removeq(class_queue_t *, struct mbuf *);
-extern void _flushq(class_queue_t *);
-
-#else /* __GNUC__ && !ALTQ_DEBUG */
-/*
- * inlined versions
- */
-static __inline void
-_addq(class_queue_t *q, struct mbuf *m)
-{
- struct mbuf *m0;
-
- if ((m0 = qtail(q)) != NULL)
- m->m_nextpkt = m0->m_nextpkt;
- else
- m0 = m;
- m0->m_nextpkt = m;
- qtail(q) = m;
- qlen(q)++;
-}
-
-static __inline struct mbuf *
-_getq(class_queue_t *q)
-{
- struct mbuf *m, *m0;
-
- if ((m = qtail(q)) == NULL)
- return (NULL);
- if ((m0 = m->m_nextpkt) != m)
- m->m_nextpkt = m0->m_nextpkt;
- else
- qtail(q) = NULL;
- qlen(q)--;
- m0->m_nextpkt = NULL;
- return (m0);
-}
-
-/* drop a packet at the tail of the queue */
-static __inline struct mbuf *
-_getq_tail(class_queue_t *q)
-{
- struct mbuf *m, *m0, *prev;
-
- if ((m = m0 = qtail(q)) == NULL)
- return NULL;
- do {
- prev = m0;
- m0 = m0->m_nextpkt;
- } while (m0 != m);
- prev->m_nextpkt = m->m_nextpkt;
- if (prev == m)
- qtail(q) = NULL;
- else
- qtail(q) = prev;
- qlen(q)--;
- m->m_nextpkt = NULL;
- return (m);
-}
-
-/* randomly select a packet in the queue */
-static __inline struct mbuf *
-_getq_random(class_queue_t *q)
-{
- struct mbuf *m;
- int i, n;
-
- if ((m = qtail(q)) == NULL)
- return NULL;
- if (m->m_nextpkt == m)
- qtail(q) = NULL;
- else {
- struct mbuf *prev = NULL;
-
- n = random() % qlen(q) + 1;
- for (i = 0; i < n; i++) {
- prev = m;
- m = m->m_nextpkt;
- }
- prev->m_nextpkt = m->m_nextpkt;
- if (m == qtail(q))
- qtail(q) = prev;
- }
- qlen(q)--;
- m->m_nextpkt = NULL;
- return (m);
-}
-
-static __inline void
-_removeq(class_queue_t *q, struct mbuf *m)
-{
- struct mbuf *m0, *prev;
-
- m0 = qtail(q);
- do {
- prev = m0;
- m0 = m0->m_nextpkt;
- } while (m0 != m);
- prev->m_nextpkt = m->m_nextpkt;
- if (prev == m)
- qtail(q) = NULL;
- else if (qtail(q) == m)
- qtail(q) = prev;
- qlen(q)--;
-}
-
-static __inline void
-_flushq(class_queue_t *q)
-{
- struct mbuf *m;
-
- while ((m = _getq(q)) != NULL)
- m_freem(m);
-}
-
-#endif /* __GNUC__ && !ALTQ_DEBUG */
-
-#endif /* _KERNEL */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _ALTQ_ALTQ_CLASSQ_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_hfsc.h b/sbin/pfctl/missing/altq/altq_hfsc.h
deleted file mode 100644
index ac242114c084..000000000000
--- a/sbin/pfctl/missing/altq/altq_hfsc.h
+++ /dev/null
@@ -1,325 +0,0 @@
-/* $FreeBSD$ */
-/* $KAME: altq_hfsc.h,v 1.10 2003/07/10 12:07:48 kjc Exp $ */
-
-/*
- * Copyright (c) 1997-1999 Carnegie Mellon University. All Rights Reserved.
- *
- * Permission to use, copy, modify, and distribute this software and
- * its documentation is hereby granted (including for commercial or
- * for-profit use), provided that both the copyright notice and this
- * permission notice appear in all copies of the software, derivative
- * works, or modified versions, and any portions thereof, and that
- * both notices appear in supporting documentation, and that credit
- * is given to Carnegie Mellon University in all publications reporting
- * on direct or indirect use of this code or its derivatives.
- *
- * THIS SOFTWARE IS EXPERIMENTAL AND IS KNOWN TO HAVE BUGS, SOME OF
- * WHICH MAY HAVE SERIOUS CONSEQUENCES. CARNEGIE MELLON PROVIDES THIS
- * SOFTWARE IN ITS ``AS IS'' CONDITION, 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 CARNEGIE MELLON UNIVERSITY 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.
- *
- * Carnegie Mellon encourages (but does not require) users of this
- * software to return any improvements or extensions that they make,
- * and to grant Carnegie Mellon the rights to redistribute these
- * changes without encumbrance.
- */
-#ifndef _ALTQ_ALTQ_HFSC_H_
-#define _ALTQ_ALTQ_HFSC_H_
-
-#include <altq/altq.h>
-#include <altq/altq_classq.h>
-#include <altq/altq_red.h>
-#include <altq/altq_rio.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct service_curve {
- u_int m1; /* slope of the first segment in bits/sec */
- u_int d; /* the x-projection of the first segment in msec */
- u_int m2; /* slope of the second segment in bits/sec */
-};
-
-/* special class handles */
-#define HFSC_NULLCLASS_HANDLE 0
-#define HFSC_ROOTCLASS_HANDLE 1
-#define HFSC_MAX_CLASSES 64
-
-/* hfsc class flags */
-#define HFCF_RED 0x0001 /* use RED */
-#define HFCF_ECN 0x0002 /* use RED/ECN */
-#define HFCF_RIO 0x0004 /* use RIO */
-#define HFCF_CLEARDSCP 0x0010 /* clear diffserv codepoint */
-#define HFCF_DEFAULTCLASS 0x1000 /* default class */
-
-/* service curve types */
-#define HFSC_REALTIMESC 1
-#define HFSC_LINKSHARINGSC 2
-#define HFSC_UPPERLIMITSC 4
-#define HFSC_DEFAULTSC (HFSC_REALTIMESC|HFSC_LINKSHARINGSC)
-
-struct hfsc_classstats {
- u_int class_id;
- u_int32_t class_handle;
- struct service_curve rsc;
- struct service_curve fsc;
- struct service_curve usc; /* upper limit service curve */
-
- u_int64_t total; /* total work in bytes */
- u_int64_t cumul; /* cumulative work in bytes
- done by real-time criteria */
- u_int64_t d; /* deadline */
- u_int64_t e; /* eligible time */
- u_int64_t vt; /* virtual time */
- u_int64_t f; /* fit time for upper-limit */
-
- /* info helpful for debugging */
- u_int64_t initvt; /* init virtual time */
- u_int64_t vtoff; /* cl_vt_ipoff */
- u_int64_t cvtmax; /* cl_maxvt */
- u_int64_t myf; /* cl_myf */
- u_int64_t cfmin; /* cl_mincf */
- u_int64_t cvtmin; /* cl_mincvt */
- u_int64_t myfadj; /* cl_myfadj */
- u_int64_t vtadj; /* cl_vtadj */
- u_int64_t cur_time;
- u_int32_t machclk_freq;
-
- u_int qlength;
- u_int qlimit;
- struct pktcntr xmit_cnt;
- struct pktcntr drop_cnt;
- u_int period;
-
- u_int vtperiod; /* vt period sequence no */
- u_int parentperiod; /* parent's vt period seqno */
- int nactive; /* number of active children */
-
- /* red and rio related info */
- int qtype;
- struct redstats red[3];
-};
-
-#ifdef ALTQ3_COMPAT
-struct hfsc_interface {
- char hfsc_ifname[IFNAMSIZ]; /* interface name (e.g., fxp0) */
-};
-
-struct hfsc_attach {
- struct hfsc_interface iface;
- u_int bandwidth; /* link bandwidth in bits/sec */
-};
-
-struct hfsc_add_class {
- struct hfsc_interface iface;
- u_int32_t parent_handle;
- struct service_curve service_curve;
- int qlimit;
- int flags;
-
- u_int32_t class_handle; /* return value */
-};
-
-struct hfsc_delete_class {
- struct hfsc_interface iface;
- u_int32_t class_handle;
-};
-
-struct hfsc_modify_class {
- struct hfsc_interface iface;
- u_int32_t class_handle;
- struct service_curve service_curve;
- int sctype;
-};
-
-struct hfsc_add_filter {
- struct hfsc_interface iface;
- u_int32_t class_handle;
- struct flow_filter filter;
-
- u_long filter_handle; /* return value */
-};
-
-struct hfsc_delete_filter {
- struct hfsc_interface iface;
- u_long filter_handle;
-};
-
-struct hfsc_class_stats {
- struct hfsc_interface iface;
- int nskip; /* skip # of classes */
- int nclasses; /* # of class stats (WR) */
- u_int64_t cur_time; /* current time */
- u_int32_t machclk_freq; /* machine clock frequency */
- u_int hif_classes; /* # of classes in the tree */
- u_int hif_packets; /* # of packets in the tree */
- struct hfsc_classstats *stats; /* pointer to stats array */
-};
-
-#define HFSC_IF_ATTACH _IOW('Q', 1, struct hfsc_attach)
-#define HFSC_IF_DETACH _IOW('Q', 2, struct hfsc_interface)
-#define HFSC_ENABLE _IOW('Q', 3, struct hfsc_interface)
-#define HFSC_DISABLE _IOW('Q', 4, struct hfsc_interface)
-#define HFSC_CLEAR_HIERARCHY _IOW('Q', 5, struct hfsc_interface)
-#define HFSC_ADD_CLASS _IOWR('Q', 7, struct hfsc_add_class)
-#define HFSC_DEL_CLASS _IOW('Q', 8, struct hfsc_delete_class)
-#define HFSC_MOD_CLASS _IOW('Q', 9, struct hfsc_modify_class)
-#define HFSC_ADD_FILTER _IOWR('Q', 10, struct hfsc_add_filter)
-#define HFSC_DEL_FILTER _IOW('Q', 11, struct hfsc_delete_filter)
-#define HFSC_GETSTATS _IOWR('Q', 12, struct hfsc_class_stats)
-#endif /* ALTQ3_COMPAT */
-
-#ifdef _KERNEL
-/*
- * kernel internal service curve representation
- * coordinates are given by 64 bit unsigned integers.
- * x-axis: unit is clock count. for the intel x86 architecture,
- * the raw Pentium TSC (Timestamp Counter) value is used.
- * virtual time is also calculated in this time scale.
- * y-axis: unit is byte.
- *
- * the service curve parameters are converted to the internal
- * representation.
- * the slope values are scaled to avoid overflow.
- * the inverse slope values as well as the y-projection of the 1st
- * segment are kept in order to to avoid 64-bit divide operations
- * that are expensive on 32-bit architectures.
- *
- * note: Intel Pentium TSC never wraps around in several thousands of years.
- * x-axis doesn't wrap around for 1089 years with 1GHz clock.
- * y-axis doesn't wrap around for 4358 years with 1Gbps bandwidth.
- */
-
-/* kernel internal representation of a service curve */
-struct internal_sc {
- u_int64_t sm1; /* scaled slope of the 1st segment */
- u_int64_t ism1; /* scaled inverse-slope of the 1st segment */
- u_int64_t dx; /* the x-projection of the 1st segment */
- u_int64_t dy; /* the y-projection of the 1st segment */
- u_int64_t sm2; /* scaled slope of the 2nd segment */
- u_int64_t ism2; /* scaled inverse-slope of the 2nd segment */
-};
-
-/* runtime service curve */
-struct runtime_sc {
- u_int64_t x; /* current starting position on x-axis */
- u_int64_t y; /* current starting position on x-axis */
- u_int64_t sm1; /* scaled slope of the 1st segment */
- u_int64_t ism1; /* scaled inverse-slope of the 1st segment */
- u_int64_t dx; /* the x-projection of the 1st segment */
- u_int64_t dy; /* the y-projection of the 1st segment */
- u_int64_t sm2; /* scaled slope of the 2nd segment */
- u_int64_t ism2; /* scaled inverse-slope of the 2nd segment */
-};
-
-/* for TAILQ based ellist and actlist implementation */
-struct hfsc_class;
-typedef TAILQ_HEAD(_eligible, hfsc_class) ellist_t;
-typedef TAILQ_ENTRY(hfsc_class) elentry_t;
-typedef TAILQ_HEAD(_active, hfsc_class) actlist_t;
-typedef TAILQ_ENTRY(hfsc_class) actentry_t;
-#define ellist_first(s) TAILQ_FIRST(s)
-#define actlist_first(s) TAILQ_FIRST(s)
-#define actlist_last(s) TAILQ_LAST(s, _active)
-
-struct hfsc_class {
- u_int cl_id; /* class id (just for debug) */
- u_int32_t cl_handle; /* class handle */
- struct hfsc_if *cl_hif; /* back pointer to struct hfsc_if */
- int cl_flags; /* misc flags */
-
- struct hfsc_class *cl_parent; /* parent class */
- struct hfsc_class *cl_siblings; /* sibling classes */
- struct hfsc_class *cl_children; /* child classes */
-
- class_queue_t *cl_q; /* class queue structure */
- struct red *cl_red; /* RED state */
- struct altq_pktattr *cl_pktattr; /* saved header used by ECN */
-
- u_int64_t cl_total; /* total work in bytes */
- u_int64_t cl_cumul; /* cumulative work in bytes
- done by real-time criteria */
- u_int64_t cl_d; /* deadline */
- u_int64_t cl_e; /* eligible time */
- u_int64_t cl_vt; /* virtual time */
- u_int64_t cl_f; /* time when this class will fit for
- link-sharing, max(myf, cfmin) */
- u_int64_t cl_myf; /* my fit-time (as calculated from this
- class's own upperlimit curve) */
- u_int64_t cl_myfadj; /* my fit-time adjustment
- (to cancel history dependence) */
- u_int64_t cl_cfmin; /* earliest children's fit-time (used
- with cl_myf to obtain cl_f) */
- u_int64_t cl_cvtmin; /* minimal virtual time among the
- children fit for link-sharing
- (monotonic within a period) */
- u_int64_t cl_vtadj; /* intra-period cumulative vt
- adjustment */
- u_int64_t cl_vtoff; /* inter-period cumulative vt offset */
- u_int64_t cl_cvtmax; /* max child's vt in the last period */
-
- u_int64_t cl_initvt; /* init virtual time (for debugging) */
-
- struct internal_sc *cl_rsc; /* internal real-time service curve */
- struct internal_sc *cl_fsc; /* internal fair service curve */
- struct internal_sc *cl_usc; /* internal upperlimit service curve */
- struct runtime_sc cl_deadline; /* deadline curve */
- struct runtime_sc cl_eligible; /* eligible curve */
- struct runtime_sc cl_virtual; /* virtual curve */
- struct runtime_sc cl_ulimit; /* upperlimit curve */
-
- u_int cl_vtperiod; /* vt period sequence no */
- u_int cl_parentperiod; /* parent's vt period seqno */
- int cl_nactive; /* number of active children */
- actlist_t *cl_actc; /* active children list */
-
- actentry_t cl_actlist; /* active children list entry */
- elentry_t cl_ellist; /* eligible list entry */
-
- struct {
- struct pktcntr xmit_cnt;
- struct pktcntr drop_cnt;
- u_int period;
- } cl_stats;
-};
-
-/*
- * hfsc interface state
- */
-struct hfsc_if {
- struct hfsc_if *hif_next; /* interface state list */
- struct ifaltq *hif_ifq; /* backpointer to ifaltq */
- struct hfsc_class *hif_rootclass; /* root class */
- struct hfsc_class *hif_defaultclass; /* default class */
- struct hfsc_class *hif_class_tbl[HFSC_MAX_CLASSES];
- struct hfsc_class *hif_pollcache; /* cache for poll operation */
-
- u_int hif_classes; /* # of classes in the tree */
- u_int hif_packets; /* # of packets in the tree */
- u_int hif_classid; /* class id sequence number */
-
- ellist_t *hif_eligible; /* eligible list */
-
-#ifdef ALTQ3_CLFIER_COMPAT
- struct acc_classifier hif_classifier;
-#endif
-};
-
-#endif /* _KERNEL */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _ALTQ_ALTQ_HFSC_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_priq.h b/sbin/pfctl/missing/altq/altq_priq.h
deleted file mode 100644
index 526b09235e39..000000000000
--- a/sbin/pfctl/missing/altq/altq_priq.h
+++ /dev/null
@@ -1,172 +0,0 @@
-/* $FreeBSD$ */
-/* $KAME: altq_priq.h,v 1.5 2003/07/10 12:07:48 kjc Exp $ */
-/*
- * Copyright (C) 2000-2003
- * Sony Computer Science Laboratories Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY SONY CSL 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 SONY CSL OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef _ALTQ_ALTQ_PRIQ_H_
-#define _ALTQ_ALTQ_PRIQ_H_
-
-#include <altq/altq.h>
-#include <altq/altq_classq.h>
-#include <altq/altq_red.h>
-#include <altq/altq_rio.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define PRIQ_MAXPRI 16 /* upper limit of the number of priorities */
-#define PRIQ_MAXQID 256 /* upper limit of queues */
-
-#ifdef ALTQ3_COMPAT
-struct priq_interface {
- char ifname[IFNAMSIZ]; /* interface name (e.g., fxp0) */
- u_long arg; /* request-specific argument */
-};
-
-struct priq_add_class {
- struct priq_interface iface;
- int pri; /* priority (0 is the lowest) */
- int qlimit; /* queue size limit */
- int flags; /* misc flags (see below) */
-
- u_int32_t class_handle; /* return value */
-};
-#endif /* ALTQ3_COMPAT */
-
-/* priq class flags */
-#define PRCF_RED 0x0001 /* use RED */
-#define PRCF_ECN 0x0002 /* use RED/ECN */
-#define PRCF_RIO 0x0004 /* use RIO */
-#define PRCF_CLEARDSCP 0x0010 /* clear diffserv codepoint */
-#define PRCF_DEFAULTCLASS 0x1000 /* default class */
-
-/* special class handles */
-#define PRIQ_NULLCLASS_HANDLE 0
-
-#ifdef ALTQ3_COMPAT
-struct priq_delete_class {
- struct priq_interface iface;
- u_int32_t class_handle;
-};
-
-struct priq_modify_class {
- struct priq_interface iface;
- u_int32_t class_handle;
- int pri;
- int qlimit;
- int flags;
-};
-
-struct priq_add_filter {
- struct priq_interface iface;
- u_int32_t class_handle;
- struct flow_filter filter;
-
- u_long filter_handle; /* return value */
-};
-
-struct priq_delete_filter {
- struct priq_interface iface;
- u_long filter_handle;
-};
-#endif /* ALTQ3_COMPAT */
-
-struct priq_classstats {
- u_int32_t class_handle;
-
- u_int qlength;
- u_int qlimit;
- u_int period;
- struct pktcntr xmitcnt; /* transmitted packet counter */
- struct pktcntr dropcnt; /* dropped packet counter */
-
- /* red and rio related info */
- int qtype;
- struct redstats red[3]; /* rio has 3 red stats */
-};
-
-#ifdef ALTQ3_COMPAT
-struct priq_class_stats {
- struct priq_interface iface;
- int maxpri; /* in/out */
-
- struct priq_classstats *stats; /* pointer to stats array */
-};
-
-#define PRIQ_IF_ATTACH _IOW('Q', 1, struct priq_interface)
-#define PRIQ_IF_DETACH _IOW('Q', 2, struct priq_interface)
-#define PRIQ_ENABLE _IOW('Q', 3, struct priq_interface)
-#define PRIQ_DISABLE _IOW('Q', 4, struct priq_interface)
-#define PRIQ_CLEAR _IOW('Q', 5, struct priq_interface)
-#define PRIQ_ADD_CLASS _IOWR('Q', 7, struct priq_add_class)
-#define PRIQ_DEL_CLASS _IOW('Q', 8, struct priq_delete_class)
-#define PRIQ_MOD_CLASS _IOW('Q', 9, struct priq_modify_class)
-#define PRIQ_ADD_FILTER _IOWR('Q', 10, struct priq_add_filter)
-#define PRIQ_DEL_FILTER _IOW('Q', 11, struct priq_delete_filter)
-#define PRIQ_GETSTATS _IOWR('Q', 12, struct priq_class_stats)
-
-#endif /* ALTQ3_COMPAT */
-
-#ifdef _KERNEL
-
-struct priq_class {
- u_int32_t cl_handle; /* class handle */
- class_queue_t *cl_q; /* class queue structure */
- struct red *cl_red; /* RED state */
- int cl_pri; /* priority */
- int cl_flags; /* class flags */
- struct priq_if *cl_pif; /* back pointer to pif */
- struct altq_pktattr *cl_pktattr; /* saved header used by ECN */
-
- /* statistics */
- u_int cl_period; /* backlog period */
- struct pktcntr cl_xmitcnt; /* transmitted packet counter */
- struct pktcntr cl_dropcnt; /* dropped packet counter */
-};
-
-/*
- * priq interface state
- */
-struct priq_if {
- struct priq_if *pif_next; /* interface state list */
- struct ifaltq *pif_ifq; /* backpointer to ifaltq */
- u_int pif_bandwidth; /* link bandwidth in bps */
- int pif_maxpri; /* max priority in use */
- struct priq_class *pif_default; /* default class */
- struct priq_class *pif_classes[PRIQ_MAXPRI]; /* classes */
-#ifdef ALTQ3_CLFIER_COMPAT
- struct acc_classifier pif_classifier; /* classifier */
-#endif
-};
-
-#endif /* _KERNEL */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _ALTQ_ALTQ_PRIQ_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_red.h b/sbin/pfctl/missing/altq/altq_red.h
deleted file mode 100644
index 96e3fae94fc6..000000000000
--- a/sbin/pfctl/missing/altq/altq_red.h
+++ /dev/null
@@ -1,199 +0,0 @@
-/* $FreeBSD$ */
-/* $KAME: altq_red.h,v 1.8 2003/07/10 12:07:49 kjc Exp $ */
-
-/*
- * Copyright (C) 1997-2003
- * Sony Computer Science Laboratories Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY SONY CSL 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 SONY CSL OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef _ALTQ_ALTQ_RED_H_
-#define _ALTQ_ALTQ_RED_H_
-
-#include <altq/altq_classq.h>
-
-#ifdef ALTQ3_COMPAT
-struct red_interface {
- char red_ifname[IFNAMSIZ];
-};
-
-struct red_stats {
- struct red_interface iface;
- int q_len;
- int q_avg;
-
- struct pktcntr xmit_cnt;
- struct pktcntr drop_cnt;
- u_int drop_forced;
- u_int drop_unforced;
- u_int marked_packets;
-
- /* static red parameters */
- int q_limit;
- int weight;
- int inv_pmax;
- int th_min;
- int th_max;
-
- /* flowvalve related stuff */
- u_int fv_flows;
- u_int fv_pass;
- u_int fv_predrop;
- u_int fv_alloc;
- u_int fv_escape;
-};
-
-struct red_conf {
- struct red_interface iface;
- int red_weight; /* weight for EWMA */
- int red_inv_pmax; /* inverse of max drop probability */
- int red_thmin; /* red min threshold */
- int red_thmax; /* red max threshold */
- int red_limit; /* max queue length */
- int red_pkttime; /* average packet time in usec */
- int red_flags; /* see below */
-};
-#endif /* ALTQ3_COMPAT */
-
-/* red flags */
-#define REDF_ECN4 0x01 /* use packet marking for IPv4 packets */
-#define REDF_ECN6 0x02 /* use packet marking for IPv6 packets */
-#define REDF_ECN (REDF_ECN4 | REDF_ECN6)
-#define REDF_FLOWVALVE 0x04 /* use flowvalve (aka penalty-box) */
-
-/*
- * simpler versions of red parameters and statistics used by other
- * disciplines (e.g., CBQ)
- */
-struct redparams {
- int th_min; /* red min threshold */
- int th_max; /* red max threshold */
- int inv_pmax; /* inverse of max drop probability */
-};
-
-struct redstats {
- int q_avg;
- struct pktcntr xmit_cnt;
- struct pktcntr drop_cnt;
- u_int drop_forced;
- u_int drop_unforced;
- u_int marked_packets;
-};
-
-#ifdef ALTQ3_COMPAT
-/*
- * IOCTLs for RED
- */
-#define RED_IF_ATTACH _IOW('Q', 1, struct red_interface)
-#define RED_IF_DETACH _IOW('Q', 2, struct red_interface)
-#define RED_ENABLE _IOW('Q', 3, struct red_interface)
-#define RED_DISABLE _IOW('Q', 4, struct red_interface)
-#define RED_CONFIG _IOWR('Q', 6, struct red_conf)
-#define RED_GETSTATS _IOWR('Q', 12, struct red_stats)
-#define RED_SETDEFAULTS _IOW('Q', 30, struct redparams)
-#endif /* ALTQ3_COMPAT */
-
-#ifdef _KERNEL
-
-#ifdef ALTQ3_COMPAT
-struct flowvalve;
-#endif
-
-/* weight table structure for idle time calibration */
-struct wtab {
- struct wtab *w_next;
- int w_weight;
- int w_param_max;
- int w_refcount;
- int32_t w_tab[32];
-};
-
-typedef struct red {
- int red_pkttime; /* average packet time in micro sec
- used for idle calibration */
- int red_flags; /* red flags */
-
- /* red parameters */
- int red_weight; /* weight for EWMA */
- int red_inv_pmax; /* inverse of max drop probability */
- int red_thmin; /* red min threshold */
- int red_thmax; /* red max threshold */
-
- /* variables for internal use */
- int red_wshift; /* log(red_weight) */
- int red_thmin_s; /* th_min scaled by avgshift */
- int red_thmax_s; /* th_max scaled by avgshift */
- int red_probd; /* drop probability denominator */
-
- int red_avg; /* queue len avg scaled by avgshift */
- int red_count; /* packet count since last dropped/
- marked packet */
- int red_idle; /* queue was empty */
- int red_old; /* avg is above th_min */
- struct wtab *red_wtab; /* weight table */
- struct timeval red_last; /* time when the queue becomes idle */
-
-#ifdef ALTQ3_COMPAT
- struct flowvalve *red_flowvalve; /* flowvalve state */
-#endif
-
- struct {
- struct pktcntr xmit_cnt;
- struct pktcntr drop_cnt;
- u_int drop_forced;
- u_int drop_unforced;
- u_int marked_packets;
- } red_stats;
-} red_t;
-
-#ifdef ALTQ3_COMPAT
-typedef struct red_queue {
- struct red_queue *rq_next; /* next red_state in the list */
- struct ifaltq *rq_ifq; /* backpointer to ifaltq */
-
- class_queue_t *rq_q;
-
- red_t *rq_red;
-} red_queue_t;
-#endif /* ALTQ3_COMPAT */
-
-/* red drop types */
-#define DTYPE_NODROP 0 /* no drop */
-#define DTYPE_FORCED 1 /* a "forced" drop */
-#define DTYPE_EARLY 2 /* an "unforced" (early) drop */
-
-extern red_t *red_alloc(int, int, int, int, int, int);
-extern void red_destroy(red_t *);
-extern void red_getstats(red_t *, struct redstats *);
-extern int red_addq(red_t *, class_queue_t *, struct mbuf *,
- struct altq_pktattr *);
-extern struct mbuf *red_getq(red_t *, class_queue_t *);
-extern int drop_early(int, int, int);
-extern int mark_ecn(struct mbuf *, struct altq_pktattr *, int);
-extern struct wtab *wtab_alloc(int);
-extern int wtab_destroy(struct wtab *);
-extern int32_t pow_w(struct wtab *, int);
-
-#endif /* _KERNEL */
-
-#endif /* _ALTQ_ALTQ_RED_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_rio.h b/sbin/pfctl/missing/altq/altq_rio.h
deleted file mode 100644
index eda338ae2f1d..000000000000
--- a/sbin/pfctl/missing/altq/altq_rio.h
+++ /dev/null
@@ -1,145 +0,0 @@
-/* $FreeBSD$ */
-/* $KAME: altq_rio.h,v 1.9 2003/07/10 12:07:49 kjc Exp $ */
-
-/*
- * Copyright (C) 1998-2003
- * Sony Computer Science Laboratories Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY SONY CSL 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 SONY CSL OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef _ALTQ_ALTQ_RIO_H_
-#define _ALTQ_ALTQ_RIO_H_
-
-#include <altq/altq_classq.h>
-
-/*
- * RIO: RED with IN/OUT bit
- * (extended to support more than 2 drop precedence values)
- */
-#define RIO_NDROPPREC 3 /* number of drop precedence values */
-
-#ifdef ALTQ3_COMPAT
-struct rio_interface {
- char rio_ifname[IFNAMSIZ];
-};
-
-struct rio_stats {
- struct rio_interface iface;
- int q_len[RIO_NDROPPREC];
- struct redstats q_stats[RIO_NDROPPREC];
-
- /* static red parameters */
- int q_limit;
- int weight;
- int flags;
- struct redparams q_params[RIO_NDROPPREC];
-};
-
-struct rio_conf {
- struct rio_interface iface;
- struct redparams q_params[RIO_NDROPPREC];
- int rio_weight; /* weight for EWMA */
- int rio_limit; /* max queue length */
- int rio_pkttime; /* average packet time in usec */
- int rio_flags; /* see below */
-};
-#endif /* ALTQ3_COMPAT */
-
-/* rio flags */
-#define RIOF_ECN4 0x01 /* use packet marking for IPv4 packets */
-#define RIOF_ECN6 0x02 /* use packet marking for IPv6 packets */
-#define RIOF_ECN (RIOF_ECN4 | RIOF_ECN6)
-#define RIOF_CLEARDSCP 0x200 /* clear diffserv codepoint */
-
-#ifdef ALTQ3_COMPAT
-/*
- * IOCTLs for RIO
- */
-#define RIO_IF_ATTACH _IOW('Q', 1, struct rio_interface)
-#define RIO_IF_DETACH _IOW('Q', 2, struct rio_interface)
-#define RIO_ENABLE _IOW('Q', 3, struct rio_interface)
-#define RIO_DISABLE _IOW('Q', 4, struct rio_interface)
-#define RIO_CONFIG _IOWR('Q', 6, struct rio_conf)
-#define RIO_GETSTATS _IOWR('Q', 12, struct rio_stats)
-#define RIO_SETDEFAULTS _IOW('Q', 30, struct redparams[RIO_NDROPPREC])
-#endif /* ALTQ3_COMPAT */
-
-#ifdef _KERNEL
-
-typedef struct rio {
- /* per drop precedence structure */
- struct dropprec_state {
- /* red parameters */
- int inv_pmax; /* inverse of max drop probability */
- int th_min; /* red min threshold */
- int th_max; /* red max threshold */
-
- /* variables for internal use */
- int th_min_s; /* th_min scaled by avgshift */
- int th_max_s; /* th_max scaled by avgshift */
- int probd; /* drop probability denominator */
-
- int qlen; /* queue length */
- int avg; /* (scaled) queue length average */
- int count; /* packet count since the last dropped/
- marked packet */
- int idle; /* queue was empty */
- int old; /* avg is above th_min */
- struct timeval last; /* timestamp when queue becomes idle */
- } rio_precstate[RIO_NDROPPREC];
-
- int rio_wshift; /* log(red_weight) */
- int rio_weight; /* weight for EWMA */
- struct wtab *rio_wtab; /* weight table */
-
- int rio_pkttime; /* average packet time in micro sec
- used for idle calibration */
- int rio_flags; /* rio flags */
-
- u_int8_t rio_codepoint; /* codepoint value to tag packets */
- u_int8_t rio_codepointmask; /* codepoint mask bits */
-
- struct redstats q_stats[RIO_NDROPPREC]; /* statistics */
-} rio_t;
-
-#ifdef ALTQ3_COMPAT
-typedef struct rio_queue {
- struct rio_queue *rq_next; /* next red_state in the list */
- struct ifaltq *rq_ifq; /* backpointer to ifaltq */
-
- class_queue_t *rq_q;
-
- rio_t *rq_rio;
-} rio_queue_t;
-#endif /* ALTQ3_COMPAT */
-
-extern rio_t *rio_alloc(int, struct redparams *, int, int);
-extern void rio_destroy(rio_t *);
-extern void rio_getstats(rio_t *, struct redstats *);
-extern int rio_addq(rio_t *, class_queue_t *, struct mbuf *,
- struct altq_pktattr *);
-extern struct mbuf *rio_getq(rio_t *, class_queue_t *);
-
-#endif /* _KERNEL */
-
-#endif /* _ALTQ_ALTQ_RIO_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_rmclass.h b/sbin/pfctl/missing/altq/altq_rmclass.h
deleted file mode 100644
index 87e3e630bb8a..000000000000
--- a/sbin/pfctl/missing/altq/altq_rmclass.h
+++ /dev/null
@@ -1,263 +0,0 @@
-/* $FreeBSD$ */
-/* $KAME: altq_rmclass.h,v 1.10 2003/08/20 23:30:23 itojun Exp $ */
-
-/*
- * Copyright (c) 1991-1997 Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 4. Neither the name of the University nor of the Laboratory 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.
- */
-
-#ifndef _ALTQ_ALTQ_RMCLASS_H_
-#define _ALTQ_ALTQ_RMCLASS_H_
-
-#include <altq/altq_classq.h>
-
-/* #pragma ident "@(#)rm_class.h 1.20 97/10/23 SMI" */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define RM_MAXPRIO 8 /* Max priority */
-
-#ifdef _KERNEL
-
-typedef struct mbuf mbuf_t;
-typedef struct rm_ifdat rm_ifdat_t;
-typedef struct rm_class rm_class_t;
-
-struct red;
-
-/*
- * Macros for dealing with time values. We assume all times are
- * 'timevals'. `microtime' is used to get the best available clock
- * resolution. If `microtime' *doesn't* return a value that's about
- * ten times smaller than the average packet time on the fastest
- * link that will use these routines, a slightly different clock
- * scheme than this one should be used.
- * (Bias due to truncation error in this scheme will overestimate utilization
- * and discriminate against high bandwidth classes. To remove this bias an
- * integrator needs to be added. The simplest integrator uses a history of
- * 10 * avg.packet.time / min.tick.time packet completion entries. This is
- * straight forward to add but we don't want to pay the extra memory
- * traffic to maintain it if it's not necessary (occasionally a vendor
- * accidentally builds a workstation with a decent clock - e.g., Sun & HP).)
- */
-
-#define RM_GETTIME(now) microtime(&now)
-
-#define TV_LT(a, b) (((a)->tv_sec < (b)->tv_sec) || \
- (((a)->tv_usec < (b)->tv_usec) && ((a)->tv_sec <= (b)->tv_sec)))
-
-#define TV_DELTA(a, b, delta) { \
- register int xxs; \
- \
- delta = (a)->tv_usec - (b)->tv_usec; \
- if ((xxs = (a)->tv_sec - (b)->tv_sec)) { \
- switch (xxs) { \
- default: \
- /* if (xxs < 0) \
- printf("rm_class: bogus time values\n"); */ \
- delta = 0; \
- /* fall through */ \
- case 2: \
- delta += 1000000; \
- /* fall through */ \
- case 1: \
- delta += 1000000; \
- break; \
- } \
- } \
-}
-
-#define TV_ADD_DELTA(a, delta, res) { \
- register int xxus = (a)->tv_usec + (delta); \
- \
- (res)->tv_sec = (a)->tv_sec; \
- while (xxus >= 1000000) { \
- ++((res)->tv_sec); \
- xxus -= 1000000; \
- } \
- (res)->tv_usec = xxus; \
-}
-
-#define RM_TIMEOUT 2 /* 1 Clock tick. */
-
-#if 1
-#define RM_MAXQUEUED 1 /* this isn't used in ALTQ/CBQ */
-#else
-#define RM_MAXQUEUED 16 /* Max number of packets downstream of CBQ */
-#endif
-#define RM_MAXQUEUE 64 /* Max queue length */
-#define RM_FILTER_GAIN 5 /* log2 of gain, e.g., 5 => 31/32 */
-#define RM_POWER (1 << RM_FILTER_GAIN)
-#define RM_MAXDEPTH 32
-#define RM_NS_PER_SEC (1000000000)
-
-typedef struct _rm_class_stats_ {
- u_int handle;
- u_int depth;
-
- struct pktcntr xmit_cnt; /* packets sent in this class */
- struct pktcntr drop_cnt; /* dropped packets */
- u_int over; /* # times went over limit */
- u_int borrows; /* # times tried to borrow */
- u_int overactions; /* # times invoked overlimit action */
- u_int delays; /* # times invoked delay actions */
-} rm_class_stats_t;
-
-/*
- * CBQ Class state structure
- */
-struct rm_class {
- class_queue_t *q_; /* Queue of packets */
- rm_ifdat_t *ifdat_;
- int pri_; /* Class priority. */
- int depth_; /* Class depth */
- u_int ns_per_byte_; /* NanoSeconds per byte. */
- u_int maxrate_; /* Bytes per second for this class. */
- u_int allotment_; /* Fraction of link bandwidth. */
- u_int w_allotment_; /* Weighted allotment for WRR */
- int bytes_alloc_; /* Allocation for round of WRR */
-
- int avgidle_;
- int maxidle_;
- int minidle_;
- int offtime_;
- int sleeping_; /* != 0 if delaying */
- int qthresh_; /* Queue threshold for formal link sharing */
- int leaf_; /* Note whether leaf class or not.*/
-
- rm_class_t *children_; /* Children of this class */
- rm_class_t *next_; /* Next pointer, used if child */
-
- rm_class_t *peer_; /* Peer class */
- rm_class_t *borrow_; /* Borrow class */
- rm_class_t *parent_; /* Parent class */
-
- void (*overlimit)(struct rm_class *, struct rm_class *);
- void (*drop)(struct rm_class *); /* Class drop action. */
-
- struct red *red_; /* RED state pointer */
- struct altq_pktattr *pktattr_; /* saved hdr used by RED/ECN */
- int flags_;
-
- int last_pkttime_; /* saved pkt_time */
- struct timeval undertime_; /* time can next send */
- struct timeval last_; /* time last packet sent */
- struct timeval overtime_;
- struct callout callout_; /* for timeout() calls */
-
- rm_class_stats_t stats_; /* Class Statistics */
-};
-
-/*
- * CBQ Interface state
- */
-struct rm_ifdat {
- int queued_; /* # pkts queued downstream */
- int efficient_; /* Link Efficency bit */
- int wrr_; /* Enable Weighted Round-Robin */
- u_long ns_per_byte_; /* Link byte speed. */
- int maxqueued_; /* Max packets to queue */
- int maxpkt_; /* Max packet size. */
- int qi_; /* In/out pointers for downstream */
- int qo_; /* packets */
-
- /*
- * Active class state and WRR state.
- */
- rm_class_t *active_[RM_MAXPRIO]; /* Active cl's in each pri */
- int na_[RM_MAXPRIO]; /* # of active cl's in a pri */
- int num_[RM_MAXPRIO]; /* # of cl's per pri */
- int alloc_[RM_MAXPRIO]; /* Byte Allocation */
- u_long M_[RM_MAXPRIO]; /* WRR weights. */
-
- /*
- * Network Interface/Solaris Queue state pointer.
- */
- struct ifaltq *ifq_;
- rm_class_t *default_; /* Default Pkt class, BE */
- rm_class_t *root_; /* Root Link class. */
- rm_class_t *ctl_; /* Control Traffic class. */
- void (*restart)(struct ifaltq *); /* Restart routine. */
-
- /*
- * Current packet downstream packet state and dynamic state.
- */
- rm_class_t *borrowed_[RM_MAXQUEUED]; /* Class borrowed last */
- rm_class_t *class_[RM_MAXQUEUED]; /* class sending */
- int curlen_[RM_MAXQUEUED]; /* Current pktlen */
- struct timeval now_[RM_MAXQUEUED]; /* Current packet time. */
- int is_overlimit_[RM_MAXQUEUED];/* Current packet time. */
-
- int cutoff_; /* Cut-off depth for borrowing */
-
- struct timeval ifnow_; /* expected xmit completion time */
-#if 1 /* ALTQ4PPP */
- int maxiftime_; /* max delay inside interface */
-#endif
- rm_class_t *pollcache_; /* cached rm_class by poll operation */
-};
-
-/* flags for rmc_init and rmc_newclass */
-/* class flags */
-#define RMCF_RED 0x0001
-#define RMCF_ECN 0x0002
-#define RMCF_RIO 0x0004
-#define RMCF_FLOWVALVE 0x0008 /* use flowvalve (aka penalty-box) */
-#define RMCF_CLEARDSCP 0x0010 /* clear diffserv codepoint */
-
-/* flags for rmc_init */
-#define RMCF_WRR 0x0100
-#define RMCF_EFFICIENT 0x0200
-
-#define is_a_parent_class(cl) ((cl)->children_ != NULL)
-
-extern rm_class_t *rmc_newclass(int, struct rm_ifdat *, u_int,
- void (*)(struct rm_class *, struct rm_class *),
- int, struct rm_class *, struct rm_class *,
- u_int, int, u_int, int, int);
-extern void rmc_delete_class(struct rm_ifdat *, struct rm_class *);
-extern int rmc_modclass(struct rm_class *, u_int, int,
- u_int, int, u_int, int);
-extern void rmc_init(struct ifaltq *, struct rm_ifdat *, u_int,
- void (*)(struct ifaltq *),
- int, int, u_int, int, u_int, int);
-extern int rmc_queue_packet(struct rm_class *, mbuf_t *);
-extern mbuf_t *rmc_dequeue_next(struct rm_ifdat *, int);
-extern void rmc_update_class_util(struct rm_ifdat *);
-extern void rmc_delay_action(struct rm_class *, struct rm_class *);
-extern void rmc_dropall(struct rm_class *);
-extern int rmc_get_weight(struct rm_ifdat *, int);
-
-#endif /* _KERNEL */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _ALTQ_ALTQ_RMCLASS_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_rmclass_debug.h b/sbin/pfctl/missing/altq/altq_rmclass_debug.h
deleted file mode 100644
index 7fcc86ba129b..000000000000
--- a/sbin/pfctl/missing/altq/altq_rmclass_debug.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/* $FreeBSD$ */
-/* $KAME: altq_rmclass_debug.h,v 1.3 2002/11/29 04:36:24 kjc Exp $ */
-
-/*
- * Copyright (c) Sun Microsystems, Inc. 1998 All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the SMCC Technology
- * Development Group at Sun Microsystems, Inc.
- *
- * 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or
- * promote products derived from this software without specific prior
- * written permission.
- *
- * SUN MICROSYSTEMS DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE OR THE
- * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE. The software is
- * provided "as is" without express or implied warranty of any kind.
- *
- * These notices must be retained in any copies of any part of this software.
- */
-
-#ifndef _ALTQ_ALTQ_RMCLASS_DEBUG_H_
-#define _ALTQ_ALTQ_RMCLASS_DEBUG_H_
-
-/* #pragma ident "@(#)rm_class_debug.h 1.7 98/05/04 SMI" */
-
-/*
- * Cbq debugging macros
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifdef CBQ_TRACE
-#ifndef NCBQTRACE
-#define NCBQTRACE (16 * 1024)
-#endif
-
-/*
- * To view the trace output, using adb, type:
- * adb -k /dev/ksyms /dev/mem <cr>, then type
- * cbqtrace_count/D to get the count, then type
- * cbqtrace_buffer,0tcount/Dp4C" "Xn
- * This will dump the trace buffer from 0 to count.
- */
-/*
- * in ALTQ, "call cbqtrace_dump(N)" from DDB to display 20 events
- * from Nth event in the circular buffer.
- */
-
-struct cbqtrace {
- int count;
- int function; /* address of function */
- int trace_action; /* descriptive 4 characters */
- int object; /* object operated on */
-};
-
-extern struct cbqtrace cbqtrace_buffer[];
-extern struct cbqtrace *cbqtrace_ptr;
-extern int cbqtrace_count;
-
-#define CBQTRACEINIT() { \
- if (cbqtrace_ptr == NULL) \
- cbqtrace_ptr = cbqtrace_buffer; \
- else { \
- cbqtrace_ptr = cbqtrace_buffer; \
- bzero((void *)cbqtrace_ptr, sizeof(cbqtrace_buffer)); \
- cbqtrace_count = 0; \
- } \
-}
-
-#define LOCK_TRACE() splimp()
-#define UNLOCK_TRACE(x) splx(x)
-
-#define CBQTRACE(func, act, obj) { \
- int __s = LOCK_TRACE(); \
- int *_p = &cbqtrace_ptr->count; \
- *_p++ = ++cbqtrace_count; \
- *_p++ = (int)(func); \
- *_p++ = (int)(act); \
- *_p++ = (int)(obj); \
- if ((struct cbqtrace *)(void *)_p >= &cbqtrace_buffer[NCBQTRACE])\
- cbqtrace_ptr = cbqtrace_buffer; \
- else \
- cbqtrace_ptr = (struct cbqtrace *)(void *)_p; \
- UNLOCK_TRACE(__s); \
- }
-#else
-
-/* If no tracing, define no-ops */
-#define CBQTRACEINIT()
-#define CBQTRACE(a, b, c)
-
-#endif /* !CBQ_TRACE */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _ALTQ_ALTQ_RMCLASS_DEBUG_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_var.h b/sbin/pfctl/missing/altq/altq_var.h
deleted file mode 100644
index 165b5afe3204..000000000000
--- a/sbin/pfctl/missing/altq/altq_var.h
+++ /dev/null
@@ -1,267 +0,0 @@
-/* $FreeBSD$ */
-/* $KAME: altq_var.h,v 1.15 2003/07/10 12:07:49 kjc Exp $ */
-
-/*
- * Copyright (C) 1998-2003
- * Sony Computer Science Laboratories Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY SONY CSL 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 SONY CSL OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#ifndef _ALTQ_ALTQ_VAR_H_
-#define _ALTQ_ALTQ_VAR_H_
-
-#ifdef _KERNEL
-
-#include <sys/param.h>
-#include <sys/kernel.h>
-#include <sys/queue.h>
-
-#ifdef ALTQ3_CLFIER_COMPAT
-/*
- * filter structure for altq common classifier
- */
-struct acc_filter {
- LIST_ENTRY(acc_filter) f_chain;
- void *f_class; /* pointer to the class */
- u_long f_handle; /* filter id */
- u_int32_t f_fbmask; /* filter bitmask */
- struct flow_filter f_filter; /* filter value */
-};
-
-/*
- * XXX ACC_FILTER_TABLESIZE can't be larger than 2048 unless we fix
- * the handle assignment.
- */
-#define ACC_FILTER_TABLESIZE (256+1)
-#define ACC_FILTER_MASK (ACC_FILTER_TABLESIZE - 2)
-#define ACC_WILDCARD_INDEX (ACC_FILTER_TABLESIZE - 1)
-#ifdef __GNUC__
-#define ACC_GET_HASH_INDEX(addr) \
- ({int x = (addr) + ((addr) >> 16); (x + (x >> 8)) & ACC_FILTER_MASK;})
-#else
-#define ACC_GET_HASH_INDEX(addr) \
- (((addr) + ((addr) >> 8) + ((addr) >> 16) + ((addr) >> 24)) \
- & ACC_FILTER_MASK)
-#endif
-#define ACC_GET_HINDEX(handle) ((handle) >> 20)
-
-#if (__FreeBSD_version > 500000)
-#define ACC_LOCK_INIT(ac) mtx_init(&(ac)->acc_mtx, "classifier", MTX_DEF)
-#define ACC_LOCK_DESTROY(ac) mtx_destroy(&(ac)->acc_mtx)
-#define ACC_LOCK(ac) mtx_lock(&(ac)->acc_mtx)
-#define ACC_UNLOCK(ac) mtx_unlock(&(ac)->acc_mtx)
-#else
-#define ACC_LOCK_INIT(ac)
-#define ACC_LOCK_DESTROY(ac)
-#define ACC_LOCK(ac)
-#define ACC_UNLOCK(ac)
-#endif
-
-struct acc_classifier {
- u_int32_t acc_fbmask;
- LIST_HEAD(filt, acc_filter) acc_filters[ACC_FILTER_TABLESIZE];
-
-#if (__FreeBSD_version > 500000)
- struct mtx acc_mtx;
-#endif
-};
-
-/*
- * flowinfo mask bits used by classifier
- */
-/* for ipv4 */
-#define FIMB4_PROTO 0x0001
-#define FIMB4_TOS 0x0002
-#define FIMB4_DADDR 0x0004
-#define FIMB4_SADDR 0x0008
-#define FIMB4_DPORT 0x0010
-#define FIMB4_SPORT 0x0020
-#define FIMB4_GPI 0x0040
-#define FIMB4_ALL 0x007f
-/* for ipv6 */
-#define FIMB6_PROTO 0x0100
-#define FIMB6_TCLASS 0x0200
-#define FIMB6_DADDR 0x0400
-#define FIMB6_SADDR 0x0800
-#define FIMB6_DPORT 0x1000
-#define FIMB6_SPORT 0x2000
-#define FIMB6_GPI 0x4000
-#define FIMB6_FLABEL 0x8000
-#define FIMB6_ALL 0xff00
-
-#define FIMB_ALL (FIMB4_ALL|FIMB6_ALL)
-
-#define FIMB4_PORTS (FIMB4_DPORT|FIMB4_SPORT|FIMB4_GPI)
-#define FIMB6_PORTS (FIMB6_DPORT|FIMB6_SPORT|FIMB6_GPI)
-#endif /* ALTQ3_CLFIER_COMPAT */
-
-/*
- * machine dependent clock
- * a 64bit high resolution time counter.
- */
-extern int machclk_usepcc;
-extern u_int32_t machclk_freq;
-extern u_int32_t machclk_per_tick;
-extern void init_machclk(void);
-extern u_int64_t read_machclk(void);
-
-/*
- * debug support
- */
-#ifdef ALTQ_DEBUG
-#ifdef __STDC__
-#define ASSERT(e) ((e) ? (void)0 : altq_assert(__FILE__, __LINE__, #e))
-#else /* PCC */
-#define ASSERT(e) ((e) ? (void)0 : altq_assert(__FILE__, __LINE__, "e"))
-#endif
-#else
-#define ASSERT(e) ((void)0)
-#endif
-
-/*
- * misc stuff for compatibility
- */
-/* ioctl cmd type */
-#if defined(__FreeBSD__) && (__FreeBSD__ < 3)
-typedef int ioctlcmd_t;
-#else
-typedef u_long ioctlcmd_t;
-#endif
-
-/*
- * queue macros:
- * the interface of TAILQ_LAST macro changed after the introduction
- * of softupdate. redefine it here to make it work with pre-2.2.7.
- */
-#undef TAILQ_LAST
-#define TAILQ_LAST(head, headname) \
- (*(((struct headname *)((head)->tqh_last))->tqh_last))
-
-#ifndef TAILQ_EMPTY
-#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
-#endif
-#ifndef TAILQ_FOREACH
-#define TAILQ_FOREACH(var, head, field) \
- for (var = TAILQ_FIRST(head); var; var = TAILQ_NEXT(var, field))
-#endif
-
-/* macro for timeout/untimeout */
-#if (__FreeBSD_version > 300000) || defined(__NetBSD__)
-/* use callout */
-#include <sys/callout.h>
-
-#if (__FreeBSD_version > 500000)
-#define CALLOUT_INIT(c) callout_init((c), 0)
-#else
-#define CALLOUT_INIT(c) callout_init((c))
-#endif
-#define CALLOUT_RESET(c,t,f,a) callout_reset((c),(t),(f),(a))
-#define CALLOUT_STOP(c) callout_stop((c))
-#ifndef CALLOUT_INITIALIZER
-#define CALLOUT_INITIALIZER { { { NULL } }, 0, NULL, NULL, 0 }
-#endif
-#elif defined(__OpenBSD__)
-#include <sys/timeout.h>
-/* callout structure as a wrapper of struct timeout */
-struct callout {
- struct timeout c_to;
-};
-#define CALLOUT_INIT(c) do { bzero((c), sizeof(*(c))); } while (/*CONSTCOND*/ 0)
-#define CALLOUT_RESET(c,t,f,a) do { if (!timeout_initialized(&(c)->c_to)) \
- timeout_set(&(c)->c_to, (f), (a)); \
- timeout_add(&(c)->c_to, (t)); } while (/*CONSTCOND*/ 0)
-#define CALLOUT_STOP(c) timeout_del(&(c)->c_to)
-#define CALLOUT_INITIALIZER { { { NULL }, NULL, NULL, 0, 0 } }
-#else
-/* use old-style timeout/untimeout */
-/* dummy callout structure */
-struct callout {
- void *c_arg; /* function argument */
- void (*c_func)(void *); /* functiuon to call */
-};
-#define CALLOUT_INIT(c) do { bzero((c), sizeof(*(c))); } while (/*CONSTCOND*/ 0)
-#define CALLOUT_RESET(c,t,f,a) do { (c)->c_arg = (a); \
- (c)->c_func = (f); \
- timeout((f),(a),(t)); } while (/*CONSTCOND*/ 0)
-#define CALLOUT_STOP(c) untimeout((c)->c_func,(c)->c_arg)
-#define CALLOUT_INITIALIZER { NULL, NULL }
-#endif
-#if !defined(__FreeBSD__)
-typedef void (timeout_t)(void *);
-#endif
-
-#define m_pktlen(m) ((m)->m_pkthdr.len)
-
-extern int pfaltq_running;
-
-struct ifnet; struct mbuf;
-struct pf_altq;
-#ifdef ALTQ3_CLFIER_COMPAT
-struct flowinfo;
-#endif
-
-void *altq_lookup(char *, int);
-#ifdef ALTQ3_CLFIER_COMPAT
-int altq_extractflow(struct mbuf *, int, struct flowinfo *, u_int32_t);
-int acc_add_filter(struct acc_classifier *, struct flow_filter *,
- void *, u_long *);
-int acc_delete_filter(struct acc_classifier *, u_long);
-int acc_discard_filters(struct acc_classifier *, void *, int);
-void *acc_classify(void *, struct mbuf *, int);
-#endif
-u_int8_t read_dsfield(struct mbuf *, struct altq_pktattr *);
-void write_dsfield(struct mbuf *, struct altq_pktattr *, u_int8_t);
-void altq_assert(const char *, int, const char *);
-int tbr_set(struct ifaltq *, struct tb_profile *);
-int tbr_get(struct ifaltq *, struct tb_profile *);
-
-int altq_pfattach(struct pf_altq *);
-int altq_pfdetach(struct pf_altq *);
-int altq_add(struct pf_altq *);
-int altq_remove(struct pf_altq *);
-int altq_add_queue(struct pf_altq *);
-int altq_remove_queue(struct pf_altq *);
-int altq_getqstats(struct pf_altq *, void *, int *);
-
-int cbq_pfattach(struct pf_altq *);
-int cbq_add_altq(struct pf_altq *);
-int cbq_remove_altq(struct pf_altq *);
-int cbq_add_queue(struct pf_altq *);
-int cbq_remove_queue(struct pf_altq *);
-int cbq_getqstats(struct pf_altq *, void *, int *);
-
-int priq_pfattach(struct pf_altq *);
-int priq_add_altq(struct pf_altq *);
-int priq_remove_altq(struct pf_altq *);
-int priq_add_queue(struct pf_altq *);
-int priq_remove_queue(struct pf_altq *);
-int priq_getqstats(struct pf_altq *, void *, int *);
-
-int hfsc_pfattach(struct pf_altq *);
-int hfsc_add_altq(struct pf_altq *);
-int hfsc_remove_altq(struct pf_altq *);
-int hfsc_add_queue(struct pf_altq *);
-int hfsc_remove_queue(struct pf_altq *);
-int hfsc_getqstats(struct pf_altq *, void *, int *);
-
-#endif /* _KERNEL */
-#endif /* _ALTQ_ALTQ_VAR_H_ */
diff --git a/sbin/pfctl/missing/altq/altq_wfq.h b/sbin/pfctl/missing/altq/altq_wfq.h
deleted file mode 100644
index ca163cf3652d..000000000000
--- a/sbin/pfctl/missing/altq/altq_wfq.h
+++ /dev/null
@@ -1,129 +0,0 @@
-/* $FreeBSD$ */
-/* $KAME: altq_wfq.h,v 1.8 2003/07/10 12:07:49 kjc Exp $ */
-
-/*
- * Copyright (C) 1997-2002
- * Sony Computer Science Laboratories Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY SONY CSL 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 SONY CSL 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.
- */
-/*
- * March 27, 1997. Written by Hiroshi Kyusojin of Keio University
- * (kyu@mt.cs.keio.ac.jp).
- */
-
-#ifndef _ALTQ_ALTQ_WFQ_H_
-#define _ALTQ_ALTQ_WFQ_H_
-
-#include <altq/altq.h>
-
-#define DEFAULT_QSIZE 256
-#define MAX_QSIZE 2048
-
-struct wfq_interface{
- char wfq_ifacename[IFNAMSIZ];
-};
-
-struct wfq_getqid{
- struct wfq_interface iface;
-#ifdef ALTQ3_CLFIER_COMPAT
- struct flowinfo flow;
-#endif
- u_long qid;
-};
-
-struct wfq_setweight {
- struct wfq_interface iface;
- int qid;
- int weight;
-};
-
-typedef struct each_queue_stats {
- int bytes; /* bytes in this queue */
- int weight; /* weight in percent */
- struct pktcntr xmit_cnt;
- struct pktcntr drop_cnt;
-} queue_stats;
-
-struct wfq_getstats {
- struct wfq_interface iface;
- int qid;
- queue_stats stats;
-};
-
-struct wfq_conf {
- struct wfq_interface iface;
- int hash_policy; /* hash policy */
- int nqueues; /* number of queues */
- int qlimit; /* queue size in bytes */
-};
-
-#define WFQ_HASH_DSTADDR 0 /* hash by dst address */
-#define WFQ_HASH_SRCPORT 1 /* hash by src port */
-#define WFQ_HASH_FULL 2 /* hash by all fields */
-
-#define WFQ_IF_ATTACH _IOW('Q', 1, struct wfq_interface)
-#define WFQ_IF_DETACH _IOW('Q', 2, struct wfq_interface)
-#define WFQ_ENABLE _IOW('Q', 3, struct wfq_interface)
-#define WFQ_DISABLE _IOW('Q', 4, struct wfq_interface)
-#define WFQ_CONFIG _IOWR('Q', 6, struct wfq_conf)
-#define WFQ_GET_STATS _IOWR('Q', 12, struct wfq_getstats)
-#define WFQ_GET_QID _IOWR('Q', 30, struct wfq_getqid)
-#define WFQ_SET_WEIGHT _IOWR('Q', 31, struct wfq_setweight)
-
-#ifdef _KERNEL
-
-#define HWM (64 * 1024)
-#define WFQ_QUOTA 512 /* quota bytes to send at a time */
-#define WFQ_ADDQUOTA(q) ((q)->quota += WFQ_QUOTA * (q)->weight / 100)
-#define ENABLE 0
-#define DISABLE 1
-
-typedef struct weighted_fair_queue{
- struct weighted_fair_queue *next, *prev;
- struct mbuf *head, *tail;
- int bytes; /* bytes in this queue */
- int quota; /* bytes sent in this round */
- int weight; /* weight in percent */
-
- struct pktcntr xmit_cnt;
- struct pktcntr drop_cnt;
-} wfq;
-
-
-typedef struct wfqstate {
- struct wfqstate *next; /* for wfqstate list */
- struct ifaltq *ifq;
- int nums; /* number of queues */
- int hwm; /* high water mark */
- int bytes; /* total bytes in all the queues */
- wfq *rrp; /* round robin pointer */
- wfq *queue; /* pointer to queue list */
-#ifdef ALTQ3_CLFIER_COMPAT
- u_long (*hash_func)(struct flowinfo *, int);
-#endif
- u_int32_t fbmask; /* filter bitmask */
-} wfq_state_t;
-
-#endif /* _KERNEL */
-
-#endif /* _ALTQ_ALTQ_WFQ_H */
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
new file mode 100644
index 000000000000..99c26c0a33df
--- /dev/null
+++ b/sbin/pfctl/parse.y
@@ -0,0 +1,6038 @@
+/* $OpenBSD: parse.y,v 1.554 2008/10/17 12:59:53 henning Exp $ */
+
+/*
+ * Copyright (c) 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt. All rights reserved.
+ * Copyright (c) 2002,2003 Henning Brauer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+%{
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#ifdef __FreeBSD__
+#include <sys/sysctl.h>
+#endif
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/icmp6.h>
+#include <net/pfvar.h>
+#include <arpa/inet.h>
+#include <altq/altq.h>
+#include <altq/altq_cbq.h>
+#include <altq/altq_priq.h>
+#include <altq/altq_hfsc.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <err.h>
+#include <limits.h>
+#include <pwd.h>
+#include <grp.h>
+#include <md5.h>
+
+#include "pfctl_parser.h"
+#include "pfctl.h"
+
+static struct pfctl *pf = NULL;
+static int debug = 0;
+static int rulestate = 0;
+static u_int16_t returnicmpdefault =
+ (ICMP_UNREACH << 8) | ICMP_UNREACH_PORT;
+static u_int16_t returnicmp6default =
+ (ICMP6_DST_UNREACH << 8) | ICMP6_DST_UNREACH_NOPORT;
+static int blockpolicy = PFRULE_DROP;
+static int require_order = 1;
+static int default_statelock;
+
+TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
+static struct file {
+ TAILQ_ENTRY(file) entry;
+ FILE *stream;
+ char *name;
+ int lineno;
+ int errors;
+} *file;
+struct file *pushfile(const char *, int);
+int popfile(void);
+int check_file_secrecy(int, const char *);
+int yyparse(void);
+int yylex(void);
+int yyerror(const char *, ...);
+int kw_cmp(const void *, const void *);
+int lookup(char *);
+int lgetc(int);
+int lungetc(int);
+int findeol(void);
+
+TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
+struct sym {
+ TAILQ_ENTRY(sym) entry;
+ int used;
+ int persist;
+ char *nam;
+ char *val;
+};
+int symset(const char *, const char *, int);
+char *symget(const char *);
+
+int atoul(char *, u_long *);
+
+enum {
+ PFCTL_STATE_NONE,
+ PFCTL_STATE_OPTION,
+ PFCTL_STATE_SCRUB,
+ PFCTL_STATE_QUEUE,
+ PFCTL_STATE_NAT,
+ PFCTL_STATE_FILTER
+};
+
+struct node_proto {
+ u_int8_t proto;
+ struct node_proto *next;
+ struct node_proto *tail;
+};
+
+struct node_port {
+ u_int16_t port[2];
+ u_int8_t op;
+ struct node_port *next;
+ struct node_port *tail;
+};
+
+struct node_uid {
+ uid_t uid[2];
+ u_int8_t op;
+ struct node_uid *next;
+ struct node_uid *tail;
+};
+
+struct node_gid {
+ gid_t gid[2];
+ u_int8_t op;
+ struct node_gid *next;
+ struct node_gid *tail;
+};
+
+struct node_icmp {
+ u_int8_t code;
+ u_int8_t type;
+ u_int8_t proto;
+ struct node_icmp *next;
+ struct node_icmp *tail;
+};
+
+enum { PF_STATE_OPT_MAX, PF_STATE_OPT_NOSYNC, PF_STATE_OPT_SRCTRACK,
+ PF_STATE_OPT_MAX_SRC_STATES, PF_STATE_OPT_MAX_SRC_CONN,
+ PF_STATE_OPT_MAX_SRC_CONN_RATE, PF_STATE_OPT_MAX_SRC_NODES,
+ PF_STATE_OPT_OVERLOAD, PF_STATE_OPT_STATELOCK,
+ PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY, };
+
+enum { PF_SRCTRACK_NONE, PF_SRCTRACK, PF_SRCTRACK_GLOBAL, PF_SRCTRACK_RULE };
+
+struct node_state_opt {
+ int type;
+ union {
+ u_int32_t max_states;
+ u_int32_t max_src_states;
+ u_int32_t max_src_conn;
+ struct {
+ u_int32_t limit;
+ u_int32_t seconds;
+ } max_src_conn_rate;
+ struct {
+ u_int8_t flush;
+ char tblname[PF_TABLE_NAME_SIZE];
+ } overload;
+ u_int32_t max_src_nodes;
+ u_int8_t src_track;
+ u_int32_t statelock;
+ struct {
+ int number;
+ u_int32_t seconds;
+ } timeout;
+ } data;
+ struct node_state_opt *next;
+ struct node_state_opt *tail;
+};
+
+struct peer {
+ struct node_host *host;
+ struct node_port *port;
+};
+
+struct node_queue {
+ char queue[PF_QNAME_SIZE];
+ char parent[PF_QNAME_SIZE];
+ char ifname[IFNAMSIZ];
+ int scheduler;
+ struct node_queue *next;
+ struct node_queue *tail;
+} *queues = NULL;
+
+struct node_qassign {
+ char *qname;
+ char *pqname;
+};
+
+struct filter_opts {
+ int marker;
+#define FOM_FLAGS 0x01
+#define FOM_ICMP 0x02
+#define FOM_TOS 0x04
+#define FOM_KEEP 0x08
+#define FOM_SRCTRACK 0x10
+ struct node_uid *uid;
+ struct node_gid *gid;
+ struct {
+ u_int8_t b1;
+ u_int8_t b2;
+ u_int16_t w;
+ u_int16_t w2;
+ } flags;
+ struct node_icmp *icmpspec;
+ u_int32_t tos;
+ u_int32_t prob;
+ struct {
+ int action;
+ struct node_state_opt *options;
+ } keep;
+ int fragment;
+ int allowopts;
+ char *label;
+ struct node_qassign queues;
+ char *tag;
+ char *match_tag;
+ u_int8_t match_tag_not;
+ u_int rtableid;
+ struct {
+ struct node_host *addr;
+ u_int16_t port;
+ } divert;
+} filter_opts;
+
+struct antispoof_opts {
+ char *label;
+ u_int rtableid;
+} antispoof_opts;
+
+struct scrub_opts {
+ int marker;
+#define SOM_MINTTL 0x01
+#define SOM_MAXMSS 0x02
+#define SOM_FRAGCACHE 0x04
+#define SOM_SETTOS 0x08
+ int nodf;
+ int minttl;
+ int maxmss;
+ int settos;
+ int fragcache;
+ int randomid;
+ int reassemble_tcp;
+ char *match_tag;
+ u_int8_t match_tag_not;
+ u_int rtableid;
+} scrub_opts;
+
+struct queue_opts {
+ int marker;
+#define QOM_BWSPEC 0x01
+#define QOM_SCHEDULER 0x02
+#define QOM_PRIORITY 0x04
+#define QOM_TBRSIZE 0x08
+#define QOM_QLIMIT 0x10
+ struct node_queue_bw queue_bwspec;
+ struct node_queue_opt scheduler;
+ int priority;
+ int tbrsize;
+ int qlimit;
+} queue_opts;
+
+struct table_opts {
+ int flags;
+ int init_addr;
+ struct node_tinithead init_nodes;
+} table_opts;
+
+struct pool_opts {
+ int marker;
+#define POM_TYPE 0x01
+#define POM_STICKYADDRESS 0x02
+ u_int8_t opts;
+ int type;
+ int staticport;
+ struct pf_poolhashkey *key;
+
+} pool_opts;
+
+
+struct node_hfsc_opts hfsc_opts;
+struct node_state_opt *keep_state_defaults = NULL;
+
+int disallow_table(struct node_host *, const char *);
+int disallow_urpf_failed(struct node_host *, const char *);
+int disallow_alias(struct node_host *, const char *);
+int rule_consistent(struct pf_rule *, int);
+int filter_consistent(struct pf_rule *, int);
+int nat_consistent(struct pf_rule *);
+int rdr_consistent(struct pf_rule *);
+int process_tabledef(char *, struct table_opts *);
+void expand_label_str(char *, size_t, const char *, const char *);
+void expand_label_if(const char *, char *, size_t, const char *);
+void expand_label_addr(const char *, char *, size_t, u_int8_t,
+ struct node_host *);
+void expand_label_port(const char *, char *, size_t,
+ struct node_port *);
+void expand_label_proto(const char *, char *, size_t, u_int8_t);
+void expand_label_nr(const char *, char *, size_t);
+void expand_label(char *, size_t, const char *, u_int8_t,
+ struct node_host *, struct node_port *, struct node_host *,
+ struct node_port *, u_int8_t);
+void expand_rule(struct pf_rule *, struct node_if *,
+ struct node_host *, struct node_proto *, struct node_os *,
+ struct node_host *, struct node_port *, struct node_host *,
+ struct node_port *, struct node_uid *, struct node_gid *,
+ struct node_icmp *, const char *);
+int expand_altq(struct pf_altq *, struct node_if *,
+ struct node_queue *, struct node_queue_bw bwspec,
+ struct node_queue_opt *);
+int expand_queue(struct pf_altq *, struct node_if *,
+ struct node_queue *, struct node_queue_bw,
+ struct node_queue_opt *);
+int expand_skip_interface(struct node_if *);
+
+int check_rulestate(int);
+int getservice(char *);
+int rule_label(struct pf_rule *, char *);
+int rt_tableid_max(void);
+
+void mv_rules(struct pf_ruleset *, struct pf_ruleset *);
+void decide_address_family(struct node_host *, sa_family_t *);
+void remove_invalid_hosts(struct node_host **, sa_family_t *);
+int invalid_redirect(struct node_host *, sa_family_t);
+u_int16_t parseicmpspec(char *, sa_family_t);
+
+TAILQ_HEAD(loadanchorshead, loadanchors)
+ loadanchorshead = TAILQ_HEAD_INITIALIZER(loadanchorshead);
+
+struct loadanchors {
+ TAILQ_ENTRY(loadanchors) entries;
+ char *anchorname;
+ char *filename;
+};
+
+typedef struct {
+ union {
+ int64_t number;
+ double probability;
+ int i;
+ char *string;
+ u_int rtableid;
+ struct {
+ u_int8_t b1;
+ u_int8_t b2;
+ u_int16_t w;
+ u_int16_t w2;
+ } b;
+ struct range {
+ int a;
+ int b;
+ int t;
+ } range;
+ struct node_if *interface;
+ struct node_proto *proto;
+ struct node_icmp *icmp;
+ struct node_host *host;
+ struct node_os *os;
+ struct node_port *port;
+ struct node_uid *uid;
+ struct node_gid *gid;
+ struct node_state_opt *state_opt;
+ struct peer peer;
+ struct {
+ struct peer src, dst;
+ struct node_os *src_os;
+ } fromto;
+ struct {
+ struct node_host *host;
+ u_int8_t rt;
+ u_int8_t pool_opts;
+ sa_family_t af;
+ struct pf_poolhashkey *key;
+ } route;
+ struct redirection {
+ struct node_host *host;
+ struct range rport;
+ } *redirection;
+ struct {
+ int action;
+ struct node_state_opt *options;
+ } keep_state;
+ struct {
+ u_int8_t log;
+ u_int8_t logif;
+ u_int8_t quick;
+ } logquick;
+ struct {
+ int neg;
+ char *name;
+ } tagged;
+ struct pf_poolhashkey *hashkey;
+ struct node_queue *queue;
+ struct node_queue_opt queue_options;
+ struct node_queue_bw queue_bwspec;
+ struct node_qassign qassign;
+ struct filter_opts filter_opts;
+ struct antispoof_opts antispoof_opts;
+ struct queue_opts queue_opts;
+ struct scrub_opts scrub_opts;
+ struct table_opts table_opts;
+ struct pool_opts pool_opts;
+ struct node_hfsc_opts hfsc_opts;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+#define PPORT_RANGE 1
+#define PPORT_STAR 2
+int parseport(char *, struct range *r, int);
+
+#define DYNIF_MULTIADDR(addr) ((addr).type == PF_ADDR_DYNIFTL && \
+ (!((addr).iflags & PFI_AFLAG_NOALIAS) || \
+ !isdigit((addr).v.ifname[strlen((addr).v.ifname)-1])))
+
+%}
+
+%token PASS BLOCK SCRUB RETURN IN OS OUT LOG QUICK ON FROM TO FLAGS
+%token RETURNRST RETURNICMP RETURNICMP6 PROTO INET INET6 ALL ANY ICMPTYPE
+%token ICMP6TYPE CODE KEEP MODULATE STATE PORT RDR NAT BINAT ARROW NODF
+%token MINTTL ERROR ALLOWOPTS FASTROUTE FILENAME ROUTETO DUPTO REPLYTO NO LABEL
+%token NOROUTE URPFFAILED FRAGMENT USER GROUP MAXMSS MAXIMUM TTL TOS DROP TABLE
+%token REASSEMBLE FRAGDROP FRAGCROP ANCHOR NATANCHOR RDRANCHOR BINATANCHOR
+%token SET OPTIMIZATION TIMEOUT LIMIT LOGINTERFACE BLOCKPOLICY RANDOMID
+%token REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID
+%token ANTISPOOF FOR INCLUDE
+%token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY
+%token ALTQ CBQ PRIQ HFSC BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT
+%token QUEUE PRIORITY QLIMIT RTABLE
+%token LOAD RULESET_OPTIMIZATION
+%token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
+%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY
+%token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS
+%token DIVERTTO DIVERTREPLY
+%token <v.string> STRING
+%token <v.number> NUMBER
+%token <v.i> PORTBINARY
+%type <v.interface> interface if_list if_item_not if_item
+%type <v.number> number icmptype icmp6type uid gid
+%type <v.number> tos not yesno
+%type <v.probability> probability
+%type <v.i> no dir af fragcache optimizer
+%type <v.i> sourcetrack flush unaryop statelock
+%type <v.b> action nataction natpasslog scrubaction
+%type <v.b> flags flag blockspec
+%type <v.range> portplain portstar portrange
+%type <v.hashkey> hashkey
+%type <v.proto> proto proto_list proto_item
+%type <v.number> protoval
+%type <v.icmp> icmpspec
+%type <v.icmp> icmp_list icmp_item
+%type <v.icmp> icmp6_list icmp6_item
+%type <v.number> reticmpspec reticmp6spec
+%type <v.fromto> fromto
+%type <v.peer> ipportspec from to
+%type <v.host> ipspec toipspec xhost host dynaddr host_list
+%type <v.host> redir_host_list redirspec
+%type <v.host> route_host route_host_list routespec
+%type <v.os> os xos os_list
+%type <v.port> portspec port_list port_item
+%type <v.uid> uids uid_list uid_item
+%type <v.gid> gids gid_list gid_item
+%type <v.route> route
+%type <v.redirection> redirection redirpool
+%type <v.string> label stringall tag anchorname
+%type <v.string> string varstring numberstring
+%type <v.keep_state> keep
+%type <v.state_opt> state_opt_spec state_opt_list state_opt_item
+%type <v.logquick> logquick quick log logopts logopt
+%type <v.interface> antispoof_ifspc antispoof_iflst antispoof_if
+%type <v.qassign> qname
+%type <v.queue> qassign qassign_list qassign_item
+%type <v.queue_options> scheduler
+%type <v.number> cbqflags_list cbqflags_item
+%type <v.number> priqflags_list priqflags_item
+%type <v.hfsc_opts> hfscopts_list hfscopts_item hfsc_opts
+%type <v.queue_bwspec> bandwidth
+%type <v.filter_opts> filter_opts filter_opt filter_opts_l
+%type <v.antispoof_opts> antispoof_opts antispoof_opt antispoof_opts_l
+%type <v.queue_opts> queue_opts queue_opt queue_opts_l
+%type <v.scrub_opts> scrub_opts scrub_opt scrub_opts_l
+%type <v.table_opts> table_opts table_opt table_opts_l
+%type <v.pool_opts> pool_opts pool_opt pool_opts_l
+%type <v.tagged> tagged
+%type <v.rtableid> rtable
+%%
+
+ruleset : /* empty */
+ | ruleset include '\n'
+ | ruleset '\n'
+ | ruleset option '\n'
+ | ruleset scrubrule '\n'
+ | ruleset natrule '\n'
+ | ruleset binatrule '\n'
+ | ruleset pfrule '\n'
+ | ruleset anchorrule '\n'
+ | ruleset loadrule '\n'
+ | ruleset altqif '\n'
+ | ruleset queuespec '\n'
+ | ruleset varset '\n'
+ | ruleset antispoof '\n'
+ | ruleset tabledef '\n'
+ | '{' fakeanchor '}' '\n';
+ | ruleset error '\n' { file->errors++; }
+ ;
+
+include : INCLUDE STRING {
+ struct file *nfile;
+
+ if ((nfile = pushfile($2, 0)) == NULL) {
+ yyerror("failed to include file %s", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+
+ file = nfile;
+ lungetc('\n');
+ }
+ ;
+
+/*
+ * apply to previouslys specified rule: must be careful to note
+ * what that is: pf or nat or binat or rdr
+ */
+fakeanchor : fakeanchor '\n'
+ | fakeanchor anchorrule '\n'
+ | fakeanchor binatrule '\n'
+ | fakeanchor natrule '\n'
+ | fakeanchor pfrule '\n'
+ | fakeanchor error '\n'
+ ;
+
+optimizer : string {
+ if (!strcmp($1, "none"))
+ $$ = 0;
+ else if (!strcmp($1, "basic"))
+ $$ = PF_OPTIMIZE_BASIC;
+ else if (!strcmp($1, "profile"))
+ $$ = PF_OPTIMIZE_BASIC | PF_OPTIMIZE_PROFILE;
+ else {
+ yyerror("unknown ruleset-optimization %s", $1);
+ YYERROR;
+ }
+ }
+ ;
+
+option : SET OPTIMIZATION STRING {
+ if (check_rulestate(PFCTL_STATE_OPTION)) {
+ free($3);
+ YYERROR;
+ }
+ if (pfctl_set_optimization(pf, $3) != 0) {
+ yyerror("unknown optimization %s", $3);
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ }
+ | SET RULESET_OPTIMIZATION optimizer {
+ if (!(pf->opts & PF_OPT_OPTIMIZE)) {
+ pf->opts |= PF_OPT_OPTIMIZE;
+ pf->optimize = $3;
+ }
+ }
+ | SET TIMEOUT timeout_spec
+ | SET TIMEOUT '{' optnl timeout_list '}'
+ | SET LIMIT limit_spec
+ | SET LIMIT '{' optnl limit_list '}'
+ | SET LOGINTERFACE stringall {
+ if (check_rulestate(PFCTL_STATE_OPTION)) {
+ free($3);
+ YYERROR;
+ }
+ if (pfctl_set_logif(pf, $3) != 0) {
+ yyerror("error setting loginterface %s", $3);
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ }
+ | SET HOSTID number {
+ if ($3 == 0 || $3 > UINT_MAX) {
+ yyerror("hostid must be non-zero");
+ YYERROR;
+ }
+ if (pfctl_set_hostid(pf, $3) != 0) {
+ yyerror("error setting hostid %08x", $3);
+ YYERROR;
+ }
+ }
+ | SET BLOCKPOLICY DROP {
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf("set block-policy drop\n");
+ if (check_rulestate(PFCTL_STATE_OPTION))
+ YYERROR;
+ blockpolicy = PFRULE_DROP;
+ }
+ | SET BLOCKPOLICY RETURN {
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf("set block-policy return\n");
+ if (check_rulestate(PFCTL_STATE_OPTION))
+ YYERROR;
+ blockpolicy = PFRULE_RETURN;
+ }
+ | SET REQUIREORDER yesno {
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf("set require-order %s\n",
+ $3 == 1 ? "yes" : "no");
+ require_order = $3;
+ }
+ | SET FINGERPRINTS STRING {
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf("set fingerprints \"%s\"\n", $3);
+ if (check_rulestate(PFCTL_STATE_OPTION)) {
+ free($3);
+ YYERROR;
+ }
+ if (!pf->anchor->name[0]) {
+ if (pfctl_file_fingerprints(pf->dev,
+ pf->opts, $3)) {
+ yyerror("error loading "
+ "fingerprints %s", $3);
+ free($3);
+ YYERROR;
+ }
+ }
+ free($3);
+ }
+ | SET STATEPOLICY statelock {
+ if (pf->opts & PF_OPT_VERBOSE)
+ switch ($3) {
+ case 0:
+ printf("set state-policy floating\n");
+ break;
+ case PFRULE_IFBOUND:
+ printf("set state-policy if-bound\n");
+ break;
+ }
+ default_statelock = $3;
+ }
+ | SET DEBUG STRING {
+ if (check_rulestate(PFCTL_STATE_OPTION)) {
+ free($3);
+ YYERROR;
+ }
+ if (pfctl_set_debug(pf, $3) != 0) {
+ yyerror("error setting debuglevel %s", $3);
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ }
+ | SET SKIP interface {
+ if (expand_skip_interface($3) != 0) {
+ yyerror("error setting skip interface(s)");
+ YYERROR;
+ }
+ }
+ | SET STATEDEFAULTS state_opt_list {
+ if (keep_state_defaults != NULL) {
+ yyerror("cannot redefine state-defaults");
+ YYERROR;
+ }
+ keep_state_defaults = $3;
+ }
+ ;
+
+stringall : STRING { $$ = $1; }
+ | ALL {
+ if (($$ = strdup("all")) == NULL) {
+ err(1, "stringall: strdup");
+ }
+ }
+ ;
+
+string : STRING string {
+ if (asprintf(&$$, "%s %s", $1, $2) == -1)
+ err(1, "string: asprintf");
+ free($1);
+ free($2);
+ }
+ | STRING
+ ;
+
+varstring : numberstring varstring {
+ if (asprintf(&$$, "%s %s", $1, $2) == -1)
+ err(1, "string: asprintf");
+ free($1);
+ free($2);
+ }
+ | numberstring
+ ;
+
+numberstring : NUMBER {
+ char *s;
+ if (asprintf(&s, "%lld", (long long)$1) == -1) {
+ yyerror("string: asprintf");
+ YYERROR;
+ }
+ $$ = s;
+ }
+ | STRING
+ ;
+
+varset : STRING '=' varstring {
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf("%s = \"%s\"\n", $1, $3);
+ if (symset($1, $3, 0) == -1)
+ err(1, "cannot store variable %s", $1);
+ free($1);
+ free($3);
+ }
+ ;
+
+anchorname : STRING { $$ = $1; }
+ | /* empty */ { $$ = NULL; }
+ ;
+
+pfa_anchorlist : /* empty */
+ | pfa_anchorlist '\n'
+ | pfa_anchorlist pfrule '\n'
+ | pfa_anchorlist anchorrule '\n'
+ ;
+
+pfa_anchor : '{'
+ {
+ char ta[PF_ANCHOR_NAME_SIZE];
+ struct pf_ruleset *rs;
+
+ /* steping into a brace anchor */
+ pf->asd++;
+ pf->bn++;
+ pf->brace = 1;
+
+ /* create a holding ruleset in the root */
+ snprintf(ta, PF_ANCHOR_NAME_SIZE, "_%d", pf->bn);
+ rs = pf_find_or_create_ruleset(ta);
+ if (rs == NULL)
+ err(1, "pfa_anchor: pf_find_or_create_ruleset");
+ pf->astack[pf->asd] = rs->anchor;
+ pf->anchor = rs->anchor;
+ } '\n' pfa_anchorlist '}'
+ {
+ pf->alast = pf->anchor;
+ pf->asd--;
+ pf->anchor = pf->astack[pf->asd];
+ }
+ | /* empty */
+ ;
+
+anchorrule : ANCHOR anchorname dir quick interface af proto fromto
+ filter_opts pfa_anchor
+ {
+ struct pf_rule r;
+ struct node_proto *proto;
+
+ if (check_rulestate(PFCTL_STATE_FILTER)) {
+ if ($2)
+ free($2);
+ YYERROR;
+ }
+
+ if ($2 && ($2[0] == '_' || strstr($2, "/_") != NULL)) {
+ free($2);
+ yyerror("anchor names beginning with '_' "
+ "are reserved for internal use");
+ YYERROR;
+ }
+
+ memset(&r, 0, sizeof(r));
+ if (pf->astack[pf->asd + 1]) {
+ /* move inline rules into relative location */
+ pf_anchor_setup(&r,
+ &pf->astack[pf->asd]->ruleset,
+ $2 ? $2 : pf->alast->name);
+
+ if (r.anchor == NULL)
+ err(1, "anchorrule: unable to "
+ "create ruleset");
+
+ if (pf->alast != r.anchor) {
+ if (r.anchor->match) {
+ yyerror("inline anchor '%s' "
+ "already exists",
+ r.anchor->name);
+ YYERROR;
+ }
+ mv_rules(&pf->alast->ruleset,
+ &r.anchor->ruleset);
+ }
+ pf_remove_if_empty_ruleset(&pf->alast->ruleset);
+ pf->alast = r.anchor;
+ } else {
+ if (!$2) {
+ yyerror("anchors without explicit "
+ "rules must specify a name");
+ YYERROR;
+ }
+ }
+ r.direction = $3;
+ r.quick = $4.quick;
+ r.af = $6;
+ r.prob = $9.prob;
+ r.rtableid = $9.rtableid;
+
+ if ($9.tag)
+ if (strlcpy(r.tagname, $9.tag,
+ PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+ yyerror("tag too long, max %u chars",
+ PF_TAG_NAME_SIZE - 1);
+ YYERROR;
+ }
+ if ($9.match_tag)
+ if (strlcpy(r.match_tagname, $9.match_tag,
+ PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+ yyerror("tag too long, max %u chars",
+ PF_TAG_NAME_SIZE - 1);
+ YYERROR;
+ }
+ r.match_tag_not = $9.match_tag_not;
+ if (rule_label(&r, $9.label))
+ YYERROR;
+ free($9.label);
+ r.flags = $9.flags.b1;
+ r.flagset = $9.flags.b2;
+ if (($9.flags.b1 & $9.flags.b2) != $9.flags.b1) {
+ yyerror("flags always false");
+ YYERROR;
+ }
+ if ($9.flags.b1 || $9.flags.b2 || $8.src_os) {
+ for (proto = $7; proto != NULL &&
+ proto->proto != IPPROTO_TCP;
+ proto = proto->next)
+ ; /* nothing */
+ if (proto == NULL && $7 != NULL) {
+ if ($9.flags.b1 || $9.flags.b2)
+ yyerror(
+ "flags only apply to tcp");
+ if ($8.src_os)
+ yyerror(
+ "OS fingerprinting only "
+ "applies to tcp");
+ YYERROR;
+ }
+ }
+
+ r.tos = $9.tos;
+
+ if ($9.keep.action) {
+ yyerror("cannot specify state handling "
+ "on anchors");
+ YYERROR;
+ }
+
+ if ($9.match_tag)
+ if (strlcpy(r.match_tagname, $9.match_tag,
+ PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+ yyerror("tag too long, max %u chars",
+ PF_TAG_NAME_SIZE - 1);
+ YYERROR;
+ }
+ r.match_tag_not = $9.match_tag_not;
+
+ decide_address_family($8.src.host, &r.af);
+ decide_address_family($8.dst.host, &r.af);
+
+ expand_rule(&r, $5, NULL, $7, $8.src_os,
+ $8.src.host, $8.src.port, $8.dst.host, $8.dst.port,
+ $9.uid, $9.gid, $9.icmpspec,
+ pf->astack[pf->asd + 1] ? pf->alast->name : $2);
+ free($2);
+ pf->astack[pf->asd + 1] = NULL;
+ }
+ | NATANCHOR string interface af proto fromto rtable {
+ struct pf_rule r;
+
+ if (check_rulestate(PFCTL_STATE_NAT)) {
+ free($2);
+ YYERROR;
+ }
+
+ memset(&r, 0, sizeof(r));
+ r.action = PF_NAT;
+ r.af = $4;
+ r.rtableid = $7;
+
+ decide_address_family($6.src.host, &r.af);
+ decide_address_family($6.dst.host, &r.af);
+
+ expand_rule(&r, $3, NULL, $5, $6.src_os,
+ $6.src.host, $6.src.port, $6.dst.host, $6.dst.port,
+ 0, 0, 0, $2);
+ free($2);
+ }
+ | RDRANCHOR string interface af proto fromto rtable {
+ struct pf_rule r;
+
+ if (check_rulestate(PFCTL_STATE_NAT)) {
+ free($2);
+ YYERROR;
+ }
+
+ memset(&r, 0, sizeof(r));
+ r.action = PF_RDR;
+ r.af = $4;
+ r.rtableid = $7;
+
+ decide_address_family($6.src.host, &r.af);
+ decide_address_family($6.dst.host, &r.af);
+
+ if ($6.src.port != NULL) {
+ yyerror("source port parameter not supported"
+ " in rdr-anchor");
+ YYERROR;
+ }
+ if ($6.dst.port != NULL) {
+ if ($6.dst.port->next != NULL) {
+ yyerror("destination port list "
+ "expansion not supported in "
+ "rdr-anchor");
+ YYERROR;
+ } else if ($6.dst.port->op != PF_OP_EQ) {
+ yyerror("destination port operators"
+ " not supported in rdr-anchor");
+ YYERROR;
+ }
+ r.dst.port[0] = $6.dst.port->port[0];
+ r.dst.port[1] = $6.dst.port->port[1];
+ r.dst.port_op = $6.dst.port->op;
+ }
+
+ expand_rule(&r, $3, NULL, $5, $6.src_os,
+ $6.src.host, $6.src.port, $6.dst.host, $6.dst.port,
+ 0, 0, 0, $2);
+ free($2);
+ }
+ | BINATANCHOR string interface af proto fromto rtable {
+ struct pf_rule r;
+
+ if (check_rulestate(PFCTL_STATE_NAT)) {
+ free($2);
+ YYERROR;
+ }
+
+ memset(&r, 0, sizeof(r));
+ r.action = PF_BINAT;
+ r.af = $4;
+ r.rtableid = $7;
+ if ($5 != NULL) {
+ if ($5->next != NULL) {
+ yyerror("proto list expansion"
+ " not supported in binat-anchor");
+ YYERROR;
+ }
+ r.proto = $5->proto;
+ free($5);
+ }
+
+ if ($6.src.host != NULL || $6.src.port != NULL ||
+ $6.dst.host != NULL || $6.dst.port != NULL) {
+ yyerror("fromto parameter not supported"
+ " in binat-anchor");
+ YYERROR;
+ }
+
+ decide_address_family($6.src.host, &r.af);
+ decide_address_family($6.dst.host, &r.af);
+
+ pfctl_add_rule(pf, &r, $2);
+ free($2);
+ }
+ ;
+
+loadrule : LOAD ANCHOR string FROM string {
+ struct loadanchors *loadanchor;
+
+ if (strlen(pf->anchor->name) + 1 +
+ strlen($3) >= MAXPATHLEN) {
+ yyerror("anchorname %s too long, max %u\n",
+ $3, MAXPATHLEN - 1);
+ free($3);
+ YYERROR;
+ }
+ loadanchor = calloc(1, sizeof(struct loadanchors));
+ if (loadanchor == NULL)
+ err(1, "loadrule: calloc");
+ if ((loadanchor->anchorname = malloc(MAXPATHLEN)) ==
+ NULL)
+ err(1, "loadrule: malloc");
+ if (pf->anchor->name[0])
+ snprintf(loadanchor->anchorname, MAXPATHLEN,
+ "%s/%s", pf->anchor->name, $3);
+ else
+ strlcpy(loadanchor->anchorname, $3, MAXPATHLEN);
+ if ((loadanchor->filename = strdup($5)) == NULL)
+ err(1, "loadrule: strdup");
+
+ TAILQ_INSERT_TAIL(&loadanchorshead, loadanchor,
+ entries);
+
+ free($3);
+ free($5);
+ };
+
+scrubaction : no SCRUB {
+ $$.b2 = $$.w = 0;
+ if ($1)
+ $$.b1 = PF_NOSCRUB;
+ else
+ $$.b1 = PF_SCRUB;
+ }
+ ;
+
+scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts
+ {
+ struct pf_rule r;
+
+ if (check_rulestate(PFCTL_STATE_SCRUB))
+ YYERROR;
+
+ memset(&r, 0, sizeof(r));
+
+ r.action = $1.b1;
+ r.direction = $2;
+
+ r.log = $3.log;
+ r.logif = $3.logif;
+ if ($3.quick) {
+ yyerror("scrub rules do not support 'quick'");
+ YYERROR;
+ }
+
+ r.af = $5;
+ if ($8.nodf)
+ r.rule_flag |= PFRULE_NODF;
+ if ($8.randomid)
+ r.rule_flag |= PFRULE_RANDOMID;
+ if ($8.reassemble_tcp) {
+ if (r.direction != PF_INOUT) {
+ yyerror("reassemble tcp rules can not "
+ "specify direction");
+ YYERROR;
+ }
+ r.rule_flag |= PFRULE_REASSEMBLE_TCP;
+ }
+ if ($8.minttl)
+ r.min_ttl = $8.minttl;
+ if ($8.maxmss)
+ r.max_mss = $8.maxmss;
+ if ($8.marker & SOM_SETTOS) {
+ r.rule_flag |= PFRULE_SET_TOS;
+ r.set_tos = $8.settos;
+ }
+ if ($8.fragcache)
+ r.rule_flag |= $8.fragcache;
+ if ($8.match_tag)
+ if (strlcpy(r.match_tagname, $8.match_tag,
+ PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+ yyerror("tag too long, max %u chars",
+ PF_TAG_NAME_SIZE - 1);
+ YYERROR;
+ }
+ r.match_tag_not = $8.match_tag_not;
+ r.rtableid = $8.rtableid;
+
+ expand_rule(&r, $4, NULL, $6, $7.src_os,
+ $7.src.host, $7.src.port, $7.dst.host, $7.dst.port,
+ NULL, NULL, NULL, "");
+ }
+ ;
+
+scrub_opts : {
+ bzero(&scrub_opts, sizeof scrub_opts);
+ scrub_opts.rtableid = -1;
+ }
+ scrub_opts_l
+ { $$ = scrub_opts; }
+ | /* empty */ {
+ bzero(&scrub_opts, sizeof scrub_opts);
+ scrub_opts.rtableid = -1;
+ $$ = scrub_opts;
+ }
+ ;
+
+scrub_opts_l : scrub_opts_l scrub_opt
+ | scrub_opt
+ ;
+
+scrub_opt : NODF {
+ if (scrub_opts.nodf) {
+ yyerror("no-df cannot be respecified");
+ YYERROR;
+ }
+ scrub_opts.nodf = 1;
+ }
+ | MINTTL NUMBER {
+ if (scrub_opts.marker & SOM_MINTTL) {
+ yyerror("min-ttl cannot be respecified");
+ YYERROR;
+ }
+ if ($2 < 0 || $2 > 255) {
+ yyerror("illegal min-ttl value %d", $2);
+ YYERROR;
+ }
+ scrub_opts.marker |= SOM_MINTTL;
+ scrub_opts.minttl = $2;
+ }
+ | MAXMSS NUMBER {
+ if (scrub_opts.marker & SOM_MAXMSS) {
+ yyerror("max-mss cannot be respecified");
+ YYERROR;
+ }
+ if ($2 < 0 || $2 > 65535) {
+ yyerror("illegal max-mss value %d", $2);
+ YYERROR;
+ }
+ scrub_opts.marker |= SOM_MAXMSS;
+ scrub_opts.maxmss = $2;
+ }
+ | SETTOS tos {
+ if (scrub_opts.marker & SOM_SETTOS) {
+ yyerror("set-tos cannot be respecified");
+ YYERROR;
+ }
+ scrub_opts.marker |= SOM_SETTOS;
+ scrub_opts.settos = $2;
+ }
+ | fragcache {
+ if (scrub_opts.marker & SOM_FRAGCACHE) {
+ yyerror("fragcache cannot be respecified");
+ YYERROR;
+ }
+ scrub_opts.marker |= SOM_FRAGCACHE;
+ scrub_opts.fragcache = $1;
+ }
+ | REASSEMBLE STRING {
+ if (strcasecmp($2, "tcp") != 0) {
+ yyerror("scrub reassemble supports only tcp, "
+ "not '%s'", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ if (scrub_opts.reassemble_tcp) {
+ yyerror("reassemble tcp cannot be respecified");
+ YYERROR;
+ }
+ scrub_opts.reassemble_tcp = 1;
+ }
+ | RANDOMID {
+ if (scrub_opts.randomid) {
+ yyerror("random-id cannot be respecified");
+ YYERROR;
+ }
+ scrub_opts.randomid = 1;
+ }
+ | RTABLE NUMBER {
+ if ($2 < 0 || $2 > rt_tableid_max()) {
+ yyerror("invalid rtable id");
+ YYERROR;
+ }
+ scrub_opts.rtableid = $2;
+ }
+ | not TAGGED string {
+ scrub_opts.match_tag = $3;
+ scrub_opts.match_tag_not = $1;
+ }
+ ;
+
+fragcache : FRAGMENT REASSEMBLE { $$ = 0; /* default */ }
+ | FRAGMENT FRAGCROP { $$ = PFRULE_FRAGCROP; }
+ | FRAGMENT FRAGDROP { $$ = PFRULE_FRAGDROP; }
+ ;
+
+antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts {
+ struct pf_rule r;
+ struct node_host *h = NULL, *hh;
+ struct node_if *i, *j;
+
+ if (check_rulestate(PFCTL_STATE_FILTER))
+ YYERROR;
+
+ for (i = $3; i; i = i->next) {
+ bzero(&r, sizeof(r));
+
+ r.action = PF_DROP;
+ r.direction = PF_IN;
+ r.log = $2.log;
+ r.logif = $2.logif;
+ r.quick = $2.quick;
+ r.af = $4;
+ if (rule_label(&r, $5.label))
+ YYERROR;
+ r.rtableid = $5.rtableid;
+ j = calloc(1, sizeof(struct node_if));
+ if (j == NULL)
+ err(1, "antispoof: calloc");
+ if (strlcpy(j->ifname, i->ifname,
+ sizeof(j->ifname)) >= sizeof(j->ifname)) {
+ free(j);
+ yyerror("interface name too long");
+ YYERROR;
+ }
+ j->not = 1;
+ if (i->dynamic) {
+ h = calloc(1, sizeof(*h));
+ if (h == NULL)
+ err(1, "address: calloc");
+ h->addr.type = PF_ADDR_DYNIFTL;
+ set_ipmask(h, 128);
+ if (strlcpy(h->addr.v.ifname, i->ifname,
+ sizeof(h->addr.v.ifname)) >=
+ sizeof(h->addr.v.ifname)) {
+ free(h);
+ yyerror(
+ "interface name too long");
+ YYERROR;
+ }
+ hh = malloc(sizeof(*hh));
+ if (hh == NULL)
+ err(1, "address: malloc");
+ bcopy(h, hh, sizeof(*hh));
+ h->addr.iflags = PFI_AFLAG_NETWORK;
+ } else {
+ h = ifa_lookup(j->ifname,
+ PFI_AFLAG_NETWORK);
+ hh = NULL;
+ }
+
+ if (h != NULL)
+ expand_rule(&r, j, NULL, NULL, NULL, h,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL, "");
+
+ if ((i->ifa_flags & IFF_LOOPBACK) == 0) {
+ bzero(&r, sizeof(r));
+
+ r.action = PF_DROP;
+ r.direction = PF_IN;
+ r.log = $2.log;
+ r.logif = $2.logif;
+ r.quick = $2.quick;
+ r.af = $4;
+ if (rule_label(&r, $5.label))
+ YYERROR;
+ r.rtableid = $5.rtableid;
+ if (hh != NULL)
+ h = hh;
+ else
+ h = ifa_lookup(i->ifname, 0);
+ if (h != NULL)
+ expand_rule(&r, NULL, NULL,
+ NULL, NULL, h, NULL, NULL,
+ NULL, NULL, NULL, NULL, "");
+ } else
+ free(hh);
+ }
+ free($5.label);
+ }
+ ;
+
+antispoof_ifspc : FOR antispoof_if { $$ = $2; }
+ | FOR '{' optnl antispoof_iflst '}' { $$ = $4; }
+ ;
+
+antispoof_iflst : antispoof_if optnl { $$ = $1; }
+ | antispoof_iflst comma antispoof_if optnl {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+antispoof_if : if_item { $$ = $1; }
+ | '(' if_item ')' {
+ $2->dynamic = 1;
+ $$ = $2;
+ }
+ ;
+
+antispoof_opts : {
+ bzero(&antispoof_opts, sizeof antispoof_opts);
+ antispoof_opts.rtableid = -1;
+ }
+ antispoof_opts_l
+ { $$ = antispoof_opts; }
+ | /* empty */ {
+ bzero(&antispoof_opts, sizeof antispoof_opts);
+ antispoof_opts.rtableid = -1;
+ $$ = antispoof_opts;
+ }
+ ;
+
+antispoof_opts_l : antispoof_opts_l antispoof_opt
+ | antispoof_opt
+ ;
+
+antispoof_opt : label {
+ if (antispoof_opts.label) {
+ yyerror("label cannot be redefined");
+ YYERROR;
+ }
+ antispoof_opts.label = $1;
+ }
+ | RTABLE NUMBER {
+ if ($2 < 0 || $2 > rt_tableid_max()) {
+ yyerror("invalid rtable id");
+ YYERROR;
+ }
+ antispoof_opts.rtableid = $2;
+ }
+ ;
+
+not : '!' { $$ = 1; }
+ | /* empty */ { $$ = 0; }
+ ;
+
+tabledef : TABLE '<' STRING '>' table_opts {
+ struct node_host *h, *nh;
+ struct node_tinit *ti, *nti;
+
+ if (strlen($3) >= PF_TABLE_NAME_SIZE) {
+ yyerror("table name too long, max %d chars",
+ PF_TABLE_NAME_SIZE - 1);
+ free($3);
+ YYERROR;
+ }
+ if (pf->loadopt & PFCTL_FLAG_TABLE)
+ if (process_tabledef($3, &$5)) {
+ free($3);
+ YYERROR;
+ }
+ free($3);
+ for (ti = SIMPLEQ_FIRST(&$5.init_nodes);
+ ti != SIMPLEQ_END(&$5.init_nodes); ti = nti) {
+ if (ti->file)
+ free(ti->file);
+ for (h = ti->host; h != NULL; h = nh) {
+ nh = h->next;
+ free(h);
+ }
+ nti = SIMPLEQ_NEXT(ti, entries);
+ free(ti);
+ }
+ }
+ ;
+
+table_opts : {
+ bzero(&table_opts, sizeof table_opts);
+ SIMPLEQ_INIT(&table_opts.init_nodes);
+ }
+ table_opts_l
+ { $$ = table_opts; }
+ | /* empty */
+ {
+ bzero(&table_opts, sizeof table_opts);
+ SIMPLEQ_INIT(&table_opts.init_nodes);
+ $$ = table_opts;
+ }
+ ;
+
+table_opts_l : table_opts_l table_opt
+ | table_opt
+ ;
+
+table_opt : STRING {
+ if (!strcmp($1, "const"))
+ table_opts.flags |= PFR_TFLAG_CONST;
+ else if (!strcmp($1, "persist"))
+ table_opts.flags |= PFR_TFLAG_PERSIST;
+ else if (!strcmp($1, "counters"))
+ table_opts.flags |= PFR_TFLAG_COUNTERS;
+ else {
+ yyerror("invalid table option '%s'", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ | '{' optnl '}' { table_opts.init_addr = 1; }
+ | '{' optnl host_list '}' {
+ struct node_host *n;
+ struct node_tinit *ti;
+
+ for (n = $3; n != NULL; n = n->next) {
+ switch (n->addr.type) {
+ case PF_ADDR_ADDRMASK:
+ continue; /* ok */
+ case PF_ADDR_RANGE:
+ yyerror("address ranges are not "
+ "permitted inside tables");
+ break;
+ case PF_ADDR_DYNIFTL:
+ yyerror("dynamic addresses are not "
+ "permitted inside tables");
+ break;
+ case PF_ADDR_TABLE:
+ yyerror("tables cannot contain tables");
+ break;
+ case PF_ADDR_NOROUTE:
+ yyerror("\"no-route\" is not permitted "
+ "inside tables");
+ break;
+ case PF_ADDR_URPFFAILED:
+ yyerror("\"urpf-failed\" is not "
+ "permitted inside tables");
+ break;
+ default:
+ yyerror("unknown address type %d",
+ n->addr.type);
+ }
+ YYERROR;
+ }
+ if (!(ti = calloc(1, sizeof(*ti))))
+ err(1, "table_opt: calloc");
+ ti->host = $3;
+ SIMPLEQ_INSERT_TAIL(&table_opts.init_nodes, ti,
+ entries);
+ table_opts.init_addr = 1;
+ }
+ | FILENAME STRING {
+ struct node_tinit *ti;
+
+ if (!(ti = calloc(1, sizeof(*ti))))
+ err(1, "table_opt: calloc");
+ ti->file = $2;
+ SIMPLEQ_INSERT_TAIL(&table_opts.init_nodes, ti,
+ entries);
+ table_opts.init_addr = 1;
+ }
+ ;
+
+altqif : ALTQ interface queue_opts QUEUE qassign {
+ struct pf_altq a;
+
+ if (check_rulestate(PFCTL_STATE_QUEUE))
+ YYERROR;
+
+ memset(&a, 0, sizeof(a));
+ if ($3.scheduler.qtype == ALTQT_NONE) {
+ yyerror("no scheduler specified!");
+ YYERROR;
+ }
+ a.scheduler = $3.scheduler.qtype;
+ a.qlimit = $3.qlimit;
+ a.tbrsize = $3.tbrsize;
+ if ($5 == NULL) {
+ yyerror("no child queues specified");
+ YYERROR;
+ }
+ if (expand_altq(&a, $2, $5, $3.queue_bwspec,
+ &$3.scheduler))
+ YYERROR;
+ }
+ ;
+
+queuespec : QUEUE STRING interface queue_opts qassign {
+ struct pf_altq a;
+
+ if (check_rulestate(PFCTL_STATE_QUEUE)) {
+ free($2);
+ YYERROR;
+ }
+
+ memset(&a, 0, sizeof(a));
+
+ if (strlcpy(a.qname, $2, sizeof(a.qname)) >=
+ sizeof(a.qname)) {
+ yyerror("queue name too long (max "
+ "%d chars)", PF_QNAME_SIZE-1);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ if ($4.tbrsize) {
+ yyerror("cannot specify tbrsize for queue");
+ YYERROR;
+ }
+ if ($4.priority > 255) {
+ yyerror("priority out of range: max 255");
+ YYERROR;
+ }
+ a.priority = $4.priority;
+ a.qlimit = $4.qlimit;
+ a.scheduler = $4.scheduler.qtype;
+ if (expand_queue(&a, $3, $5, $4.queue_bwspec,
+ &$4.scheduler)) {
+ yyerror("errors in queue definition");
+ YYERROR;
+ }
+ }
+ ;
+
+queue_opts : {
+ bzero(&queue_opts, sizeof queue_opts);
+ queue_opts.priority = DEFAULT_PRIORITY;
+ queue_opts.qlimit = DEFAULT_QLIMIT;
+ queue_opts.scheduler.qtype = ALTQT_NONE;
+ queue_opts.queue_bwspec.bw_percent = 100;
+ }
+ queue_opts_l
+ { $$ = queue_opts; }
+ | /* empty */ {
+ bzero(&queue_opts, sizeof queue_opts);
+ queue_opts.priority = DEFAULT_PRIORITY;
+ queue_opts.qlimit = DEFAULT_QLIMIT;
+ queue_opts.scheduler.qtype = ALTQT_NONE;
+ queue_opts.queue_bwspec.bw_percent = 100;
+ $$ = queue_opts;
+ }
+ ;
+
+queue_opts_l : queue_opts_l queue_opt
+ | queue_opt
+ ;
+
+queue_opt : BANDWIDTH bandwidth {
+ if (queue_opts.marker & QOM_BWSPEC) {
+ yyerror("bandwidth cannot be respecified");
+ YYERROR;
+ }
+ queue_opts.marker |= QOM_BWSPEC;
+ queue_opts.queue_bwspec = $2;
+ }
+ | PRIORITY NUMBER {
+ if (queue_opts.marker & QOM_PRIORITY) {
+ yyerror("priority cannot be respecified");
+ YYERROR;
+ }
+ if ($2 < 0 || $2 > 255) {
+ yyerror("priority out of range: max 255");
+ YYERROR;
+ }
+ queue_opts.marker |= QOM_PRIORITY;
+ queue_opts.priority = $2;
+ }
+ | QLIMIT NUMBER {
+ if (queue_opts.marker & QOM_QLIMIT) {
+ yyerror("qlimit cannot be respecified");
+ YYERROR;
+ }
+ if ($2 < 0 || $2 > 65535) {
+ yyerror("qlimit out of range: max 65535");
+ YYERROR;
+ }
+ queue_opts.marker |= QOM_QLIMIT;
+ queue_opts.qlimit = $2;
+ }
+ | scheduler {
+ if (queue_opts.marker & QOM_SCHEDULER) {
+ yyerror("scheduler cannot be respecified");
+ YYERROR;
+ }
+ queue_opts.marker |= QOM_SCHEDULER;
+ queue_opts.scheduler = $1;
+ }
+ | TBRSIZE NUMBER {
+ if (queue_opts.marker & QOM_TBRSIZE) {
+ yyerror("tbrsize cannot be respecified");
+ YYERROR;
+ }
+ if ($2 < 0 || $2 > 65535) {
+ yyerror("tbrsize too big: max 65535");
+ YYERROR;
+ }
+ queue_opts.marker |= QOM_TBRSIZE;
+ queue_opts.tbrsize = $2;
+ }
+ ;
+
+bandwidth : STRING {
+ double bps;
+ char *cp;
+
+ $$.bw_percent = 0;
+
+ bps = strtod($1, &cp);
+ if (cp != NULL) {
+ if (!strcmp(cp, "b"))
+ ; /* nothing */
+ else if (!strcmp(cp, "Kb"))
+ bps *= 1000;
+ else if (!strcmp(cp, "Mb"))
+ bps *= 1000 * 1000;
+ else if (!strcmp(cp, "Gb"))
+ bps *= 1000 * 1000 * 1000;
+ else if (!strcmp(cp, "%")) {
+ if (bps < 0 || bps > 100) {
+ yyerror("bandwidth spec "
+ "out of range");
+ free($1);
+ YYERROR;
+ }
+ $$.bw_percent = bps;
+ bps = 0;
+ } else {
+ yyerror("unknown unit %s", cp);
+ free($1);
+ YYERROR;
+ }
+ }
+ free($1);
+ $$.bw_absolute = (u_int32_t)bps;
+ }
+ | NUMBER {
+ if ($1 < 0 || $1 > UINT_MAX) {
+ yyerror("bandwidth number too big");
+ YYERROR;
+ }
+ $$.bw_percent = 0;
+ $$.bw_absolute = $1;
+ }
+ ;
+
+scheduler : CBQ {
+ $$.qtype = ALTQT_CBQ;
+ $$.data.cbq_opts.flags = 0;
+ }
+ | CBQ '(' cbqflags_list ')' {
+ $$.qtype = ALTQT_CBQ;
+ $$.data.cbq_opts.flags = $3;
+ }
+ | PRIQ {
+ $$.qtype = ALTQT_PRIQ;
+ $$.data.priq_opts.flags = 0;
+ }
+ | PRIQ '(' priqflags_list ')' {
+ $$.qtype = ALTQT_PRIQ;
+ $$.data.priq_opts.flags = $3;
+ }
+ | HFSC {
+ $$.qtype = ALTQT_HFSC;
+ bzero(&$$.data.hfsc_opts,
+ sizeof(struct node_hfsc_opts));
+ }
+ | HFSC '(' hfsc_opts ')' {
+ $$.qtype = ALTQT_HFSC;
+ $$.data.hfsc_opts = $3;
+ }
+ ;
+
+cbqflags_list : cbqflags_item { $$ |= $1; }
+ | cbqflags_list comma cbqflags_item { $$ |= $3; }
+ ;
+
+cbqflags_item : STRING {
+ if (!strcmp($1, "default"))
+ $$ = CBQCLF_DEFCLASS;
+ else if (!strcmp($1, "borrow"))
+ $$ = CBQCLF_BORROW;
+ else if (!strcmp($1, "red"))
+ $$ = CBQCLF_RED;
+ else if (!strcmp($1, "ecn"))
+ $$ = CBQCLF_RED|CBQCLF_ECN;
+ else if (!strcmp($1, "rio"))
+ $$ = CBQCLF_RIO;
+ else {
+ yyerror("unknown cbq flag \"%s\"", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+priqflags_list : priqflags_item { $$ |= $1; }
+ | priqflags_list comma priqflags_item { $$ |= $3; }
+ ;
+
+priqflags_item : STRING {
+ if (!strcmp($1, "default"))
+ $$ = PRCF_DEFAULTCLASS;
+ else if (!strcmp($1, "red"))
+ $$ = PRCF_RED;
+ else if (!strcmp($1, "ecn"))
+ $$ = PRCF_RED|PRCF_ECN;
+ else if (!strcmp($1, "rio"))
+ $$ = PRCF_RIO;
+ else {
+ yyerror("unknown priq flag \"%s\"", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+hfsc_opts : {
+ bzero(&hfsc_opts,
+ sizeof(struct node_hfsc_opts));
+ }
+ hfscopts_list {
+ $$ = hfsc_opts;
+ }
+ ;
+
+hfscopts_list : hfscopts_item
+ | hfscopts_list comma hfscopts_item
+ ;
+
+hfscopts_item : LINKSHARE bandwidth {
+ if (hfsc_opts.linkshare.used) {
+ yyerror("linkshare already specified");
+ YYERROR;
+ }
+ hfsc_opts.linkshare.m2 = $2;
+ hfsc_opts.linkshare.used = 1;
+ }
+ | LINKSHARE '(' bandwidth comma NUMBER comma bandwidth ')'
+ {
+ if ($5 < 0 || $5 > INT_MAX) {
+ yyerror("timing in curve out of range");
+ YYERROR;
+ }
+ if (hfsc_opts.linkshare.used) {
+ yyerror("linkshare already specified");
+ YYERROR;
+ }
+ hfsc_opts.linkshare.m1 = $3;
+ hfsc_opts.linkshare.d = $5;
+ hfsc_opts.linkshare.m2 = $7;
+ hfsc_opts.linkshare.used = 1;
+ }
+ | REALTIME bandwidth {
+ if (hfsc_opts.realtime.used) {
+ yyerror("realtime already specified");
+ YYERROR;
+ }
+ hfsc_opts.realtime.m2 = $2;
+ hfsc_opts.realtime.used = 1;
+ }
+ | REALTIME '(' bandwidth comma NUMBER comma bandwidth ')'
+ {
+ if ($5 < 0 || $5 > INT_MAX) {
+ yyerror("timing in curve out of range");
+ YYERROR;
+ }
+ if (hfsc_opts.realtime.used) {
+ yyerror("realtime already specified");
+ YYERROR;
+ }
+ hfsc_opts.realtime.m1 = $3;
+ hfsc_opts.realtime.d = $5;
+ hfsc_opts.realtime.m2 = $7;
+ hfsc_opts.realtime.used = 1;
+ }
+ | UPPERLIMIT bandwidth {
+ if (hfsc_opts.upperlimit.used) {
+ yyerror("upperlimit already specified");
+ YYERROR;
+ }
+ hfsc_opts.upperlimit.m2 = $2;
+ hfsc_opts.upperlimit.used = 1;
+ }
+ | UPPERLIMIT '(' bandwidth comma NUMBER comma bandwidth ')'
+ {
+ if ($5 < 0 || $5 > INT_MAX) {
+ yyerror("timing in curve out of range");
+ YYERROR;
+ }
+ if (hfsc_opts.upperlimit.used) {
+ yyerror("upperlimit already specified");
+ YYERROR;
+ }
+ hfsc_opts.upperlimit.m1 = $3;
+ hfsc_opts.upperlimit.d = $5;
+ hfsc_opts.upperlimit.m2 = $7;
+ hfsc_opts.upperlimit.used = 1;
+ }
+ | STRING {
+ if (!strcmp($1, "default"))
+ hfsc_opts.flags |= HFCF_DEFAULTCLASS;
+ else if (!strcmp($1, "red"))
+ hfsc_opts.flags |= HFCF_RED;
+ else if (!strcmp($1, "ecn"))
+ hfsc_opts.flags |= HFCF_RED|HFCF_ECN;
+ else if (!strcmp($1, "rio"))
+ hfsc_opts.flags |= HFCF_RIO;
+ else {
+ yyerror("unknown hfsc flag \"%s\"", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+qassign : /* empty */ { $$ = NULL; }
+ | qassign_item { $$ = $1; }
+ | '{' optnl qassign_list '}' { $$ = $3; }
+ ;
+
+qassign_list : qassign_item optnl { $$ = $1; }
+ | qassign_list comma qassign_item optnl {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+qassign_item : STRING {
+ $$ = calloc(1, sizeof(struct node_queue));
+ if ($$ == NULL)
+ err(1, "qassign_item: calloc");
+ if (strlcpy($$->queue, $1, sizeof($$->queue)) >=
+ sizeof($$->queue)) {
+ yyerror("queue name '%s' too long (max "
+ "%d chars)", $1, sizeof($$->queue)-1);
+ free($1);
+ free($$);
+ YYERROR;
+ }
+ free($1);
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+pfrule : action dir logquick interface route af proto fromto
+ filter_opts
+ {
+ struct pf_rule r;
+ struct node_state_opt *o;
+ struct node_proto *proto;
+ int srctrack = 0;
+ int statelock = 0;
+ int adaptive = 0;
+ int defaults = 0;
+
+ if (check_rulestate(PFCTL_STATE_FILTER))
+ YYERROR;
+
+ memset(&r, 0, sizeof(r));
+
+ r.action = $1.b1;
+ switch ($1.b2) {
+ case PFRULE_RETURNRST:
+ r.rule_flag |= PFRULE_RETURNRST;
+ r.return_ttl = $1.w;
+ break;
+ case PFRULE_RETURNICMP:
+ r.rule_flag |= PFRULE_RETURNICMP;
+ r.return_icmp = $1.w;
+ r.return_icmp6 = $1.w2;
+ break;
+ case PFRULE_RETURN:
+ r.rule_flag |= PFRULE_RETURN;
+ r.return_icmp = $1.w;
+ r.return_icmp6 = $1.w2;
+ break;
+ }
+ r.direction = $2;
+ r.log = $3.log;
+ r.logif = $3.logif;
+ r.quick = $3.quick;
+ r.prob = $9.prob;
+ r.rtableid = $9.rtableid;
+
+ r.af = $6;
+ if ($9.tag)
+ if (strlcpy(r.tagname, $9.tag,
+ PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+ yyerror("tag too long, max %u chars",
+ PF_TAG_NAME_SIZE - 1);
+ YYERROR;
+ }
+ if ($9.match_tag)
+ if (strlcpy(r.match_tagname, $9.match_tag,
+ PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+ yyerror("tag too long, max %u chars",
+ PF_TAG_NAME_SIZE - 1);
+ YYERROR;
+ }
+ r.match_tag_not = $9.match_tag_not;
+ if (rule_label(&r, $9.label))
+ YYERROR;
+ free($9.label);
+ r.flags = $9.flags.b1;
+ r.flagset = $9.flags.b2;
+ if (($9.flags.b1 & $9.flags.b2) != $9.flags.b1) {
+ yyerror("flags always false");
+ YYERROR;
+ }
+ if ($9.flags.b1 || $9.flags.b2 || $8.src_os) {
+ for (proto = $7; proto != NULL &&
+ proto->proto != IPPROTO_TCP;
+ proto = proto->next)
+ ; /* nothing */
+ if (proto == NULL && $7 != NULL) {
+ if ($9.flags.b1 || $9.flags.b2)
+ yyerror(
+ "flags only apply to tcp");
+ if ($8.src_os)
+ yyerror(
+ "OS fingerprinting only "
+ "apply to tcp");
+ YYERROR;
+ }
+#if 0
+ if (($9.flags.b1 & parse_flags("S")) == 0 &&
+ $8.src_os) {
+ yyerror("OS fingerprinting requires "
+ "the SYN TCP flag (flags S/SA)");
+ YYERROR;
+ }
+#endif
+ }
+
+ r.tos = $9.tos;
+ r.keep_state = $9.keep.action;
+ o = $9.keep.options;
+
+ /* 'keep state' by default on pass rules. */
+ if (!r.keep_state && !r.action &&
+ !($9.marker & FOM_KEEP)) {
+ r.keep_state = PF_STATE_NORMAL;
+ o = keep_state_defaults;
+ defaults = 1;
+ }
+
+ while (o) {
+ struct node_state_opt *p = o;
+
+ switch (o->type) {
+ case PF_STATE_OPT_MAX:
+ if (r.max_states) {
+ yyerror("state option 'max' "
+ "multiple definitions");
+ YYERROR;
+ }
+ r.max_states = o->data.max_states;
+ break;
+ case PF_STATE_OPT_NOSYNC:
+ if (r.rule_flag & PFRULE_NOSYNC) {
+ yyerror("state option 'sync' "
+ "multiple definitions");
+ YYERROR;
+ }
+ r.rule_flag |= PFRULE_NOSYNC;
+ break;
+ case PF_STATE_OPT_SRCTRACK:
+ if (srctrack) {
+ yyerror("state option "
+ "'source-track' "
+ "multiple definitions");
+ YYERROR;
+ }
+ srctrack = o->data.src_track;
+ r.rule_flag |= PFRULE_SRCTRACK;
+ break;
+ case PF_STATE_OPT_MAX_SRC_STATES:
+ if (r.max_src_states) {
+ yyerror("state option "
+ "'max-src-states' "
+ "multiple definitions");
+ YYERROR;
+ }
+ if (o->data.max_src_states == 0) {
+ yyerror("'max-src-states' must "
+ "be > 0");
+ YYERROR;
+ }
+ r.max_src_states =
+ o->data.max_src_states;
+ r.rule_flag |= PFRULE_SRCTRACK;
+ break;
+ case PF_STATE_OPT_OVERLOAD:
+ if (r.overload_tblname[0]) {
+ yyerror("multiple 'overload' "
+ "table definitions");
+ YYERROR;
+ }
+ if (strlcpy(r.overload_tblname,
+ o->data.overload.tblname,
+ PF_TABLE_NAME_SIZE) >=
+ PF_TABLE_NAME_SIZE) {
+ yyerror("state option: "
+ "strlcpy");
+ YYERROR;
+ }
+ r.flush = o->data.overload.flush;
+ break;
+ case PF_STATE_OPT_MAX_SRC_CONN:
+ if (r.max_src_conn) {
+ yyerror("state option "
+ "'max-src-conn' "
+ "multiple definitions");
+ YYERROR;
+ }
+ if (o->data.max_src_conn == 0) {
+ yyerror("'max-src-conn' "
+ "must be > 0");
+ YYERROR;
+ }
+ r.max_src_conn =
+ o->data.max_src_conn;
+ r.rule_flag |= PFRULE_SRCTRACK |
+ PFRULE_RULESRCTRACK;
+ break;
+ case PF_STATE_OPT_MAX_SRC_CONN_RATE:
+ if (r.max_src_conn_rate.limit) {
+ yyerror("state option "
+ "'max-src-conn-rate' "
+ "multiple definitions");
+ YYERROR;
+ }
+ if (!o->data.max_src_conn_rate.limit ||
+ !o->data.max_src_conn_rate.seconds) {
+ yyerror("'max-src-conn-rate' "
+ "values must be > 0");
+ YYERROR;
+ }
+ if (o->data.max_src_conn_rate.limit >
+ PF_THRESHOLD_MAX) {
+ yyerror("'max-src-conn-rate' "
+ "maximum rate must be < %u",
+ PF_THRESHOLD_MAX);
+ YYERROR;
+ }
+ r.max_src_conn_rate.limit =
+ o->data.max_src_conn_rate.limit;
+ r.max_src_conn_rate.seconds =
+ o->data.max_src_conn_rate.seconds;
+ r.rule_flag |= PFRULE_SRCTRACK |
+ PFRULE_RULESRCTRACK;
+ break;
+ case PF_STATE_OPT_MAX_SRC_NODES:
+ if (r.max_src_nodes) {
+ yyerror("state option "
+ "'max-src-nodes' "
+ "multiple definitions");
+ YYERROR;
+ }
+ if (o->data.max_src_nodes == 0) {
+ yyerror("'max-src-nodes' must "
+ "be > 0");
+ YYERROR;
+ }
+ r.max_src_nodes =
+ o->data.max_src_nodes;
+ r.rule_flag |= PFRULE_SRCTRACK |
+ PFRULE_RULESRCTRACK;
+ break;
+ case PF_STATE_OPT_STATELOCK:
+ if (statelock) {
+ yyerror("state locking option: "
+ "multiple definitions");
+ YYERROR;
+ }
+ statelock = 1;
+ r.rule_flag |= o->data.statelock;
+ break;
+ case PF_STATE_OPT_SLOPPY:
+ if (r.rule_flag & PFRULE_STATESLOPPY) {
+ yyerror("state sloppy option: "
+ "multiple definitions");
+ YYERROR;
+ }
+ r.rule_flag |= PFRULE_STATESLOPPY;
+ break;
+ case PF_STATE_OPT_TIMEOUT:
+ if (o->data.timeout.number ==
+ PFTM_ADAPTIVE_START ||
+ o->data.timeout.number ==
+ PFTM_ADAPTIVE_END)
+ adaptive = 1;
+ if (r.timeout[o->data.timeout.number]) {
+ yyerror("state timeout %s "
+ "multiple definitions",
+ pf_timeouts[o->data.
+ timeout.number].name);
+ YYERROR;
+ }
+ r.timeout[o->data.timeout.number] =
+ o->data.timeout.seconds;
+ }
+ o = o->next;
+ if (!defaults)
+ free(p);
+ }
+
+ /* 'flags S/SA' by default on stateful rules */
+ if (!r.action && !r.flags && !r.flagset &&
+ !$9.fragment && !($9.marker & FOM_FLAGS) &&
+ r.keep_state) {
+ r.flags = parse_flags("S");
+ r.flagset = parse_flags("SA");
+ }
+ if (!adaptive && r.max_states) {
+ r.timeout[PFTM_ADAPTIVE_START] =
+ (r.max_states / 10) * 6;
+ r.timeout[PFTM_ADAPTIVE_END] =
+ (r.max_states / 10) * 12;
+ }
+ if (r.rule_flag & PFRULE_SRCTRACK) {
+ if (srctrack == PF_SRCTRACK_GLOBAL &&
+ r.max_src_nodes) {
+ yyerror("'max-src-nodes' is "
+ "incompatible with "
+ "'source-track global'");
+ YYERROR;
+ }
+ if (srctrack == PF_SRCTRACK_GLOBAL &&
+ r.max_src_conn) {
+ yyerror("'max-src-conn' is "
+ "incompatible with "
+ "'source-track global'");
+ YYERROR;
+ }
+ if (srctrack == PF_SRCTRACK_GLOBAL &&
+ r.max_src_conn_rate.seconds) {
+ yyerror("'max-src-conn-rate' is "
+ "incompatible with "
+ "'source-track global'");
+ YYERROR;
+ }
+ if (r.timeout[PFTM_SRC_NODE] <
+ r.max_src_conn_rate.seconds)
+ r.timeout[PFTM_SRC_NODE] =
+ r.max_src_conn_rate.seconds;
+ r.rule_flag |= PFRULE_SRCTRACK;
+ if (srctrack == PF_SRCTRACK_RULE)
+ r.rule_flag |= PFRULE_RULESRCTRACK;
+ }
+ if (r.keep_state && !statelock)
+ r.rule_flag |= default_statelock;
+
+ if ($9.fragment)
+ r.rule_flag |= PFRULE_FRAGMENT;
+ r.allow_opts = $9.allowopts;
+
+ decide_address_family($8.src.host, &r.af);
+ decide_address_family($8.dst.host, &r.af);
+
+ if ($5.rt) {
+ if (!r.direction) {
+ yyerror("direction must be explicit "
+ "with rules that specify routing");
+ YYERROR;
+ }
+ r.rt = $5.rt;
+ r.rpool.opts = $5.pool_opts;
+ if ($5.key != NULL)
+ memcpy(&r.rpool.key, $5.key,
+ sizeof(struct pf_poolhashkey));
+ }
+ if (r.rt && r.rt != PF_FASTROUTE) {
+ decide_address_family($5.host, &r.af);
+ remove_invalid_hosts(&$5.host, &r.af);
+ if ($5.host == NULL) {
+ yyerror("no routing address with "
+ "matching address family found.");
+ YYERROR;
+ }
+ if ((r.rpool.opts & PF_POOL_TYPEMASK) ==
+ PF_POOL_NONE && ($5.host->next != NULL ||
+ $5.host->addr.type == PF_ADDR_TABLE ||
+ DYNIF_MULTIADDR($5.host->addr)))
+ r.rpool.opts |= PF_POOL_ROUNDROBIN;
+ if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
+ PF_POOL_ROUNDROBIN &&
+ disallow_table($5.host, "tables are only "
+ "supported in round-robin routing pools"))
+ YYERROR;
+ if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
+ PF_POOL_ROUNDROBIN &&
+ disallow_alias($5.host, "interface (%s) "
+ "is only supported in round-robin "
+ "routing pools"))
+ YYERROR;
+ if ($5.host->next != NULL) {
+ if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
+ PF_POOL_ROUNDROBIN) {
+ yyerror("r.rpool.opts must "
+ "be PF_POOL_ROUNDROBIN");
+ YYERROR;
+ }
+ }
+ }
+ if ($9.queues.qname != NULL) {
+ if (strlcpy(r.qname, $9.queues.qname,
+ sizeof(r.qname)) >= sizeof(r.qname)) {
+ yyerror("rule qname too long (max "
+ "%d chars)", sizeof(r.qname)-1);
+ YYERROR;
+ }
+ free($9.queues.qname);
+ }
+ if ($9.queues.pqname != NULL) {
+ if (strlcpy(r.pqname, $9.queues.pqname,
+ sizeof(r.pqname)) >= sizeof(r.pqname)) {
+ yyerror("rule pqname too long (max "
+ "%d chars)", sizeof(r.pqname)-1);
+ YYERROR;
+ }
+ free($9.queues.pqname);
+ }
+#ifdef __FreeBSD__
+ r.divert.port = $9.divert.port;
+#else
+ if ((r.divert.port = $9.divert.port)) {
+ if (r.direction == PF_OUT) {
+ if ($9.divert.addr) {
+ yyerror("address specified "
+ "for outgoing divert");
+ YYERROR;
+ }
+ bzero(&r.divert.addr,
+ sizeof(r.divert.addr));
+ } else {
+ if (!$9.divert.addr) {
+ yyerror("no address specified "
+ "for incoming divert");
+ YYERROR;
+ }
+ if ($9.divert.addr->af != r.af) {
+ yyerror("address family "
+ "mismatch for divert");
+ YYERROR;
+ }
+ r.divert.addr =
+ $9.divert.addr->addr.v.a.addr;
+ }
+ }
+#endif
+
+ expand_rule(&r, $4, $5.host, $7, $8.src_os,
+ $8.src.host, $8.src.port, $8.dst.host, $8.dst.port,
+ $9.uid, $9.gid, $9.icmpspec, "");
+ }
+ ;
+
+filter_opts : {
+ bzero(&filter_opts, sizeof filter_opts);
+ filter_opts.rtableid = -1;
+ }
+ filter_opts_l
+ { $$ = filter_opts; }
+ | /* empty */ {
+ bzero(&filter_opts, sizeof filter_opts);
+ filter_opts.rtableid = -1;
+ $$ = filter_opts;
+ }
+ ;
+
+filter_opts_l : filter_opts_l filter_opt
+ | filter_opt
+ ;
+
+filter_opt : USER uids {
+ if (filter_opts.uid)
+ $2->tail->next = filter_opts.uid;
+ filter_opts.uid = $2;
+ }
+ | GROUP gids {
+ if (filter_opts.gid)
+ $2->tail->next = filter_opts.gid;
+ filter_opts.gid = $2;
+ }
+ | flags {
+ if (filter_opts.marker & FOM_FLAGS) {
+ yyerror("flags cannot be redefined");
+ YYERROR;
+ }
+ filter_opts.marker |= FOM_FLAGS;
+ filter_opts.flags.b1 |= $1.b1;
+ filter_opts.flags.b2 |= $1.b2;
+ filter_opts.flags.w |= $1.w;
+ filter_opts.flags.w2 |= $1.w2;
+ }
+ | icmpspec {
+ if (filter_opts.marker & FOM_ICMP) {
+ yyerror("icmp-type cannot be redefined");
+ YYERROR;
+ }
+ filter_opts.marker |= FOM_ICMP;
+ filter_opts.icmpspec = $1;
+ }
+ | TOS tos {
+ if (filter_opts.marker & FOM_TOS) {
+ yyerror("tos cannot be redefined");
+ YYERROR;
+ }
+ filter_opts.marker |= FOM_TOS;
+ filter_opts.tos = $2;
+ }
+ | keep {
+ if (filter_opts.marker & FOM_KEEP) {
+ yyerror("modulate or keep cannot be redefined");
+ YYERROR;
+ }
+ filter_opts.marker |= FOM_KEEP;
+ filter_opts.keep.action = $1.action;
+ filter_opts.keep.options = $1.options;
+ }
+ | FRAGMENT {
+ filter_opts.fragment = 1;
+ }
+ | ALLOWOPTS {
+ filter_opts.allowopts = 1;
+ }
+ | label {
+ if (filter_opts.label) {
+ yyerror("label cannot be redefined");
+ YYERROR;
+ }
+ filter_opts.label = $1;
+ }
+ | qname {
+ if (filter_opts.queues.qname) {
+ yyerror("queue cannot be redefined");
+ YYERROR;
+ }
+ filter_opts.queues = $1;
+ }
+ | TAG string {
+ filter_opts.tag = $2;
+ }
+ | not TAGGED string {
+ filter_opts.match_tag = $3;
+ filter_opts.match_tag_not = $1;
+ }
+ | PROBABILITY probability {
+ double p;
+
+ p = floor($2 * UINT_MAX + 0.5);
+ if (p < 0.0 || p > UINT_MAX) {
+ yyerror("invalid probability: %lf", p);
+ YYERROR;
+ }
+ filter_opts.prob = (u_int32_t)p;
+ if (filter_opts.prob == 0)
+ filter_opts.prob = 1;
+ }
+ | RTABLE NUMBER {
+ if ($2 < 0 || $2 > rt_tableid_max()) {
+ yyerror("invalid rtable id");
+ YYERROR;
+ }
+ filter_opts.rtableid = $2;
+ }
+ | DIVERTTO portplain {
+#ifdef __FreeBSD__
+ filter_opts.divert.port = $2.a;
+ if (!filter_opts.divert.port) {
+ yyerror("invalid divert port: %u", ntohs($2.a));
+ YYERROR;
+ }
+#endif
+ }
+ | DIVERTTO STRING PORT portplain {
+#ifndef __FreeBSD__
+ if ((filter_opts.divert.addr = host($2)) == NULL) {
+ yyerror("could not parse divert address: %s",
+ $2);
+ free($2);
+ YYERROR;
+ }
+#else
+ if ($2)
+#endif
+ free($2);
+ filter_opts.divert.port = $4.a;
+ if (!filter_opts.divert.port) {
+ yyerror("invalid divert port: %u", ntohs($4.a));
+ YYERROR;
+ }
+ }
+ | DIVERTREPLY {
+#ifdef __FreeBSD__
+ yyerror("divert-reply has no meaning in FreeBSD pf(4)");
+ YYERROR;
+#else
+ filter_opts.divert.port = 1; /* some random value */
+#endif
+ }
+ ;
+
+probability : STRING {
+ char *e;
+ double p = strtod($1, &e);
+
+ if (*e == '%') {
+ p *= 0.01;
+ e++;
+ }
+ if (*e) {
+ yyerror("invalid probability: %s", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ $$ = p;
+ }
+ | NUMBER {
+ $$ = (double)$1;
+ }
+ ;
+
+
+action : PASS { $$.b1 = PF_PASS; $$.b2 = $$.w = 0; }
+ | BLOCK blockspec { $$ = $2; $$.b1 = PF_DROP; }
+ ;
+
+blockspec : /* empty */ {
+ $$.b2 = blockpolicy;
+ $$.w = returnicmpdefault;
+ $$.w2 = returnicmp6default;
+ }
+ | DROP {
+ $$.b2 = PFRULE_DROP;
+ $$.w = 0;
+ $$.w2 = 0;
+ }
+ | RETURNRST {
+ $$.b2 = PFRULE_RETURNRST;
+ $$.w = 0;
+ $$.w2 = 0;
+ }
+ | RETURNRST '(' TTL NUMBER ')' {
+ if ($4 < 0 || $4 > 255) {
+ yyerror("illegal ttl value %d", $4);
+ YYERROR;
+ }
+ $$.b2 = PFRULE_RETURNRST;
+ $$.w = $4;
+ $$.w2 = 0;
+ }
+ | RETURNICMP {
+ $$.b2 = PFRULE_RETURNICMP;
+ $$.w = returnicmpdefault;
+ $$.w2 = returnicmp6default;
+ }
+ | RETURNICMP6 {
+ $$.b2 = PFRULE_RETURNICMP;
+ $$.w = returnicmpdefault;
+ $$.w2 = returnicmp6default;
+ }
+ | RETURNICMP '(' reticmpspec ')' {
+ $$.b2 = PFRULE_RETURNICMP;
+ $$.w = $3;
+ $$.w2 = returnicmpdefault;
+ }
+ | RETURNICMP6 '(' reticmp6spec ')' {
+ $$.b2 = PFRULE_RETURNICMP;
+ $$.w = returnicmpdefault;
+ $$.w2 = $3;
+ }
+ | RETURNICMP '(' reticmpspec comma reticmp6spec ')' {
+ $$.b2 = PFRULE_RETURNICMP;
+ $$.w = $3;
+ $$.w2 = $5;
+ }
+ | RETURN {
+ $$.b2 = PFRULE_RETURN;
+ $$.w = returnicmpdefault;
+ $$.w2 = returnicmp6default;
+ }
+ ;
+
+reticmpspec : STRING {
+ if (!($$ = parseicmpspec($1, AF_INET))) {
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ | NUMBER {
+ u_int8_t icmptype;
+
+ if ($1 < 0 || $1 > 255) {
+ yyerror("invalid icmp code %lu", $1);
+ YYERROR;
+ }
+ icmptype = returnicmpdefault >> 8;
+ $$ = (icmptype << 8 | $1);
+ }
+ ;
+
+reticmp6spec : STRING {
+ if (!($$ = parseicmpspec($1, AF_INET6))) {
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ | NUMBER {
+ u_int8_t icmptype;
+
+ if ($1 < 0 || $1 > 255) {
+ yyerror("invalid icmp code %lu", $1);
+ YYERROR;
+ }
+ icmptype = returnicmp6default >> 8;
+ $$ = (icmptype << 8 | $1);
+ }
+ ;
+
+dir : /* empty */ { $$ = PF_INOUT; }
+ | IN { $$ = PF_IN; }
+ | OUT { $$ = PF_OUT; }
+ ;
+
+quick : /* empty */ { $$.quick = 0; }
+ | QUICK { $$.quick = 1; }
+ ;
+
+logquick : /* empty */ { $$.log = 0; $$.quick = 0; $$.logif = 0; }
+ | log { $$ = $1; $$.quick = 0; }
+ | QUICK { $$.quick = 1; $$.log = 0; $$.logif = 0; }
+ | log QUICK { $$ = $1; $$.quick = 1; }
+ | QUICK log { $$ = $2; $$.quick = 1; }
+ ;
+
+log : LOG { $$.log = PF_LOG; $$.logif = 0; }
+ | LOG '(' logopts ')' {
+ $$.log = PF_LOG | $3.log;
+ $$.logif = $3.logif;
+ }
+ ;
+
+logopts : logopt { $$ = $1; }
+ | logopts comma logopt {
+ $$.log = $1.log | $3.log;
+ $$.logif = $3.logif;
+ if ($$.logif == 0)
+ $$.logif = $1.logif;
+ }
+ ;
+
+logopt : ALL { $$.log = PF_LOG_ALL; $$.logif = 0; }
+ | USER { $$.log = PF_LOG_SOCKET_LOOKUP; $$.logif = 0; }
+ | GROUP { $$.log = PF_LOG_SOCKET_LOOKUP; $$.logif = 0; }
+ | TO string {
+ const char *errstr;
+ u_int i;
+
+ $$.log = 0;
+ if (strncmp($2, "pflog", 5)) {
+ yyerror("%s: should be a pflog interface", $2);
+ free($2);
+ YYERROR;
+ }
+ i = strtonum($2 + 5, 0, 255, &errstr);
+ if (errstr) {
+ yyerror("%s: %s", $2, errstr);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ $$.logif = i;
+ }
+ ;
+
+interface : /* empty */ { $$ = NULL; }
+ | ON if_item_not { $$ = $2; }
+ | ON '{' optnl if_list '}' { $$ = $4; }
+ ;
+
+if_list : if_item_not optnl { $$ = $1; }
+ | if_list comma if_item_not optnl {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+if_item_not : not if_item { $$ = $2; $$->not = $1; }
+ ;
+
+if_item : STRING {
+ struct node_host *n;
+
+ $$ = calloc(1, sizeof(struct node_if));
+ if ($$ == NULL)
+ err(1, "if_item: calloc");
+ if (strlcpy($$->ifname, $1, sizeof($$->ifname)) >=
+ sizeof($$->ifname)) {
+ free($1);
+ free($$);
+ yyerror("interface name too long");
+ YYERROR;
+ }
+
+ if ((n = ifa_exists($1)) != NULL)
+ $$->ifa_flags = n->ifa_flags;
+
+ free($1);
+ $$->not = 0;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+af : /* empty */ { $$ = 0; }
+ | INET { $$ = AF_INET; }
+ | INET6 { $$ = AF_INET6; }
+ ;
+
+proto : /* empty */ { $$ = NULL; }
+ | PROTO proto_item { $$ = $2; }
+ | PROTO '{' optnl proto_list '}' { $$ = $4; }
+ ;
+
+proto_list : proto_item optnl { $$ = $1; }
+ | proto_list comma proto_item optnl {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+proto_item : protoval {
+ u_int8_t pr;
+
+ pr = (u_int8_t)$1;
+ if (pr == 0) {
+ yyerror("proto 0 cannot be used");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_proto));
+ if ($$ == NULL)
+ err(1, "proto_item: calloc");
+ $$->proto = pr;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+protoval : STRING {
+ struct protoent *p;
+
+ p = getprotobyname($1);
+ if (p == NULL) {
+ yyerror("unknown protocol %s", $1);
+ free($1);
+ YYERROR;
+ }
+ $$ = p->p_proto;
+ free($1);
+ }
+ | NUMBER {
+ if ($1 < 0 || $1 > 255) {
+ yyerror("protocol outside range");
+ YYERROR;
+ }
+ }
+ ;
+
+fromto : ALL {
+ $$.src.host = NULL;
+ $$.src.port = NULL;
+ $$.dst.host = NULL;
+ $$.dst.port = NULL;
+ $$.src_os = NULL;
+ }
+ | from os to {
+ $$.src = $1;
+ $$.src_os = $2;
+ $$.dst = $3;
+ }
+ ;
+
+os : /* empty */ { $$ = NULL; }
+ | OS xos { $$ = $2; }
+ | OS '{' optnl os_list '}' { $$ = $4; }
+ ;
+
+xos : STRING {
+ $$ = calloc(1, sizeof(struct node_os));
+ if ($$ == NULL)
+ err(1, "os: calloc");
+ $$->os = $1;
+ $$->tail = $$;
+ }
+ ;
+
+os_list : xos optnl { $$ = $1; }
+ | os_list comma xos optnl {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+from : /* empty */ {
+ $$.host = NULL;
+ $$.port = NULL;
+ }
+ | FROM ipportspec {
+ $$ = $2;
+ }
+ ;
+
+to : /* empty */ {
+ $$.host = NULL;
+ $$.port = NULL;
+ }
+ | TO ipportspec {
+ if (disallow_urpf_failed($2.host, "\"urpf-failed\" is "
+ "not permitted in a destination address"))
+ YYERROR;
+ $$ = $2;
+ }
+ ;
+
+ipportspec : ipspec {
+ $$.host = $1;
+ $$.port = NULL;
+ }
+ | ipspec PORT portspec {
+ $$.host = $1;
+ $$.port = $3;
+ }
+ | PORT portspec {
+ $$.host = NULL;
+ $$.port = $2;
+ }
+ ;
+
+optnl : '\n' optnl
+ |
+ ;
+
+ipspec : ANY { $$ = NULL; }
+ | xhost { $$ = $1; }
+ | '{' optnl host_list '}' { $$ = $3; }
+ ;
+
+toipspec : TO ipspec { $$ = $2; }
+ | /* empty */ { $$ = NULL; }
+ ;
+
+host_list : ipspec optnl { $$ = $1; }
+ | host_list comma ipspec optnl {
+ if ($3 == NULL)
+ $$ = $1;
+ else if ($1 == NULL)
+ $$ = $3;
+ else {
+ $1->tail->next = $3;
+ $1->tail = $3->tail;
+ $$ = $1;
+ }
+ }
+ ;
+
+xhost : not host {
+ struct node_host *n;
+
+ for (n = $2; n != NULL; n = n->next)
+ n->not = $1;
+ $$ = $2;
+ }
+ | not NOROUTE {
+ $$ = calloc(1, sizeof(struct node_host));
+ if ($$ == NULL)
+ err(1, "xhost: calloc");
+ $$->addr.type = PF_ADDR_NOROUTE;
+ $$->next = NULL;
+ $$->not = $1;
+ $$->tail = $$;
+ }
+ | not URPFFAILED {
+ $$ = calloc(1, sizeof(struct node_host));
+ if ($$ == NULL)
+ err(1, "xhost: calloc");
+ $$->addr.type = PF_ADDR_URPFFAILED;
+ $$->next = NULL;
+ $$->not = $1;
+ $$->tail = $$;
+ }
+ ;
+
+host : STRING {
+ if (($$ = host($1)) == NULL) {
+ /* error. "any" is handled elsewhere */
+ free($1);
+ yyerror("could not parse host specification");
+ YYERROR;
+ }
+ free($1);
+
+ }
+ | STRING '-' STRING {
+ struct node_host *b, *e;
+
+ if ((b = host($1)) == NULL || (e = host($3)) == NULL) {
+ free($1);
+ free($3);
+ yyerror("could not parse host specification");
+ YYERROR;
+ }
+ if (b->af != e->af ||
+ b->addr.type != PF_ADDR_ADDRMASK ||
+ e->addr.type != PF_ADDR_ADDRMASK ||
+ unmask(&b->addr.v.a.mask, b->af) !=
+ (b->af == AF_INET ? 32 : 128) ||
+ unmask(&e->addr.v.a.mask, e->af) !=
+ (e->af == AF_INET ? 32 : 128) ||
+ b->next != NULL || b->not ||
+ e->next != NULL || e->not) {
+ free(b);
+ free(e);
+ free($1);
+ free($3);
+ yyerror("invalid address range");
+ YYERROR;
+ }
+ memcpy(&b->addr.v.a.mask, &e->addr.v.a.addr,
+ sizeof(b->addr.v.a.mask));
+ b->addr.type = PF_ADDR_RANGE;
+ $$ = b;
+ free(e);
+ free($1);
+ free($3);
+ }
+ | STRING '/' NUMBER {
+ char *buf;
+
+ if (asprintf(&buf, "%s/%lld", $1, (long long)$3) == -1)
+ err(1, "host: asprintf");
+ free($1);
+ if (($$ = host(buf)) == NULL) {
+ /* error. "any" is handled elsewhere */
+ free(buf);
+ yyerror("could not parse host specification");
+ YYERROR;
+ }
+ free(buf);
+ }
+ | NUMBER '/' NUMBER {
+ char *buf;
+
+ /* ie. for 10/8 parsing */
+#ifdef __FreeBSD__
+ if (asprintf(&buf, "%lld/%lld", (long long)$1, (long long)$3) == -1)
+#else
+ if (asprintf(&buf, "%lld/%lld", $1, $3) == -1)
+#endif
+ err(1, "host: asprintf");
+ if (($$ = host(buf)) == NULL) {
+ /* error. "any" is handled elsewhere */
+ free(buf);
+ yyerror("could not parse host specification");
+ YYERROR;
+ }
+ free(buf);
+ }
+ | dynaddr
+ | dynaddr '/' NUMBER {
+ struct node_host *n;
+
+ if ($3 < 0 || $3 > 128) {
+ yyerror("bit number too big");
+ YYERROR;
+ }
+ $$ = $1;
+ for (n = $1; n != NULL; n = n->next)
+ set_ipmask(n, $3);
+ }
+ | '<' STRING '>' {
+ if (strlen($2) >= PF_TABLE_NAME_SIZE) {
+ yyerror("table name '%s' too long", $2);
+ free($2);
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_host));
+ if ($$ == NULL)
+ err(1, "host: calloc");
+ $$->addr.type = PF_ADDR_TABLE;
+ if (strlcpy($$->addr.v.tblname, $2,
+ sizeof($$->addr.v.tblname)) >=
+ sizeof($$->addr.v.tblname))
+ errx(1, "host: strlcpy");
+ free($2);
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+number : NUMBER
+ | STRING {
+ u_long ulval;
+
+ if (atoul($1, &ulval) == -1) {
+ yyerror("%s is not a number", $1);
+ free($1);
+ YYERROR;
+ } else
+ $$ = ulval;
+ free($1);
+ }
+ ;
+
+dynaddr : '(' STRING ')' {
+ int flags = 0;
+ char *p, *op;
+
+ op = $2;
+ if (!isalpha(op[0])) {
+ yyerror("invalid interface name '%s'", op);
+ free(op);
+ YYERROR;
+ }
+ while ((p = strrchr($2, ':')) != NULL) {
+ if (!strcmp(p+1, "network"))
+ flags |= PFI_AFLAG_NETWORK;
+ else if (!strcmp(p+1, "broadcast"))
+ flags |= PFI_AFLAG_BROADCAST;
+ else if (!strcmp(p+1, "peer"))
+ flags |= PFI_AFLAG_PEER;
+ else if (!strcmp(p+1, "0"))
+ flags |= PFI_AFLAG_NOALIAS;
+ else {
+ yyerror("interface %s has bad modifier",
+ $2);
+ free(op);
+ YYERROR;
+ }
+ *p = '\0';
+ }
+ if (flags & (flags - 1) & PFI_AFLAG_MODEMASK) {
+ free(op);
+ yyerror("illegal combination of "
+ "interface modifiers");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_host));
+ if ($$ == NULL)
+ err(1, "address: calloc");
+ $$->af = 0;
+ set_ipmask($$, 128);
+ $$->addr.type = PF_ADDR_DYNIFTL;
+ $$->addr.iflags = flags;
+ if (strlcpy($$->addr.v.ifname, $2,
+ sizeof($$->addr.v.ifname)) >=
+ sizeof($$->addr.v.ifname)) {
+ free(op);
+ free($$);
+ yyerror("interface name too long");
+ YYERROR;
+ }
+ free(op);
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+portspec : port_item { $$ = $1; }
+ | '{' optnl port_list '}' { $$ = $3; }
+ ;
+
+port_list : port_item optnl { $$ = $1; }
+ | port_list comma port_item optnl {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+port_item : portrange {
+ $$ = calloc(1, sizeof(struct node_port));
+ if ($$ == NULL)
+ err(1, "port_item: calloc");
+ $$->port[0] = $1.a;
+ $$->port[1] = $1.b;
+ if ($1.t)
+ $$->op = PF_OP_RRG;
+ else
+ $$->op = PF_OP_EQ;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | unaryop portrange {
+ if ($2.t) {
+ yyerror("':' cannot be used with an other "
+ "port operator");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_port));
+ if ($$ == NULL)
+ err(1, "port_item: calloc");
+ $$->port[0] = $2.a;
+ $$->port[1] = $2.b;
+ $$->op = $1;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | portrange PORTBINARY portrange {
+ if ($1.t || $3.t) {
+ yyerror("':' cannot be used with an other "
+ "port operator");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_port));
+ if ($$ == NULL)
+ err(1, "port_item: calloc");
+ $$->port[0] = $1.a;
+ $$->port[1] = $3.a;
+ $$->op = $2;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+portplain : numberstring {
+ if (parseport($1, &$$, 0) == -1) {
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+portrange : numberstring {
+ if (parseport($1, &$$, PPORT_RANGE) == -1) {
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+uids : uid_item { $$ = $1; }
+ | '{' optnl uid_list '}' { $$ = $3; }
+ ;
+
+uid_list : uid_item optnl { $$ = $1; }
+ | uid_list comma uid_item optnl {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+uid_item : uid {
+ $$ = calloc(1, sizeof(struct node_uid));
+ if ($$ == NULL)
+ err(1, "uid_item: calloc");
+ $$->uid[0] = $1;
+ $$->uid[1] = $1;
+ $$->op = PF_OP_EQ;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | unaryop uid {
+ if ($2 == UID_MAX && $1 != PF_OP_EQ && $1 != PF_OP_NE) {
+ yyerror("user unknown requires operator = or "
+ "!=");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_uid));
+ if ($$ == NULL)
+ err(1, "uid_item: calloc");
+ $$->uid[0] = $2;
+ $$->uid[1] = $2;
+ $$->op = $1;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | uid PORTBINARY uid {
+ if ($1 == UID_MAX || $3 == UID_MAX) {
+ yyerror("user unknown requires operator = or "
+ "!=");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_uid));
+ if ($$ == NULL)
+ err(1, "uid_item: calloc");
+ $$->uid[0] = $1;
+ $$->uid[1] = $3;
+ $$->op = $2;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+uid : STRING {
+ if (!strcmp($1, "unknown"))
+ $$ = UID_MAX;
+ else {
+ struct passwd *pw;
+
+ if ((pw = getpwnam($1)) == NULL) {
+ yyerror("unknown user %s", $1);
+ free($1);
+ YYERROR;
+ }
+ $$ = pw->pw_uid;
+ }
+ free($1);
+ }
+ | NUMBER {
+ if ($1 < 0 || $1 >= UID_MAX) {
+ yyerror("illegal uid value %lu", $1);
+ YYERROR;
+ }
+ $$ = $1;
+ }
+ ;
+
+gids : gid_item { $$ = $1; }
+ | '{' optnl gid_list '}' { $$ = $3; }
+ ;
+
+gid_list : gid_item optnl { $$ = $1; }
+ | gid_list comma gid_item optnl {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+gid_item : gid {
+ $$ = calloc(1, sizeof(struct node_gid));
+ if ($$ == NULL)
+ err(1, "gid_item: calloc");
+ $$->gid[0] = $1;
+ $$->gid[1] = $1;
+ $$->op = PF_OP_EQ;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | unaryop gid {
+ if ($2 == GID_MAX && $1 != PF_OP_EQ && $1 != PF_OP_NE) {
+ yyerror("group unknown requires operator = or "
+ "!=");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_gid));
+ if ($$ == NULL)
+ err(1, "gid_item: calloc");
+ $$->gid[0] = $2;
+ $$->gid[1] = $2;
+ $$->op = $1;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | gid PORTBINARY gid {
+ if ($1 == GID_MAX || $3 == GID_MAX) {
+ yyerror("group unknown requires operator = or "
+ "!=");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_gid));
+ if ($$ == NULL)
+ err(1, "gid_item: calloc");
+ $$->gid[0] = $1;
+ $$->gid[1] = $3;
+ $$->op = $2;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+gid : STRING {
+ if (!strcmp($1, "unknown"))
+ $$ = GID_MAX;
+ else {
+ struct group *grp;
+
+ if ((grp = getgrnam($1)) == NULL) {
+ yyerror("unknown group %s", $1);
+ free($1);
+ YYERROR;
+ }
+ $$ = grp->gr_gid;
+ }
+ free($1);
+ }
+ | NUMBER {
+ if ($1 < 0 || $1 >= GID_MAX) {
+ yyerror("illegal gid value %lu", $1);
+ YYERROR;
+ }
+ $$ = $1;
+ }
+ ;
+
+flag : STRING {
+ int f;
+
+ if ((f = parse_flags($1)) < 0) {
+ yyerror("bad flags %s", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ $$.b1 = f;
+ }
+ ;
+
+flags : FLAGS flag '/' flag { $$.b1 = $2.b1; $$.b2 = $4.b1; }
+ | FLAGS '/' flag { $$.b1 = 0; $$.b2 = $3.b1; }
+ | FLAGS ANY { $$.b1 = 0; $$.b2 = 0; }
+ ;
+
+icmpspec : ICMPTYPE icmp_item { $$ = $2; }
+ | ICMPTYPE '{' optnl icmp_list '}' { $$ = $4; }
+ | ICMP6TYPE icmp6_item { $$ = $2; }
+ | ICMP6TYPE '{' optnl icmp6_list '}' { $$ = $4; }
+ ;
+
+icmp_list : icmp_item optnl { $$ = $1; }
+ | icmp_list comma icmp_item optnl {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+icmp6_list : icmp6_item optnl { $$ = $1; }
+ | icmp6_list comma icmp6_item optnl {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+icmp_item : icmptype {
+ $$ = calloc(1, sizeof(struct node_icmp));
+ if ($$ == NULL)
+ err(1, "icmp_item: calloc");
+ $$->type = $1;
+ $$->code = 0;
+ $$->proto = IPPROTO_ICMP;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | icmptype CODE STRING {
+ const struct icmpcodeent *p;
+
+ if ((p = geticmpcodebyname($1-1, $3, AF_INET)) == NULL) {
+ yyerror("unknown icmp-code %s", $3);
+ free($3);
+ YYERROR;
+ }
+
+ free($3);
+ $$ = calloc(1, sizeof(struct node_icmp));
+ if ($$ == NULL)
+ err(1, "icmp_item: calloc");
+ $$->type = $1;
+ $$->code = p->code + 1;
+ $$->proto = IPPROTO_ICMP;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | icmptype CODE NUMBER {
+ if ($3 < 0 || $3 > 255) {
+ yyerror("illegal icmp-code %lu", $3);
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_icmp));
+ if ($$ == NULL)
+ err(1, "icmp_item: calloc");
+ $$->type = $1;
+ $$->code = $3 + 1;
+ $$->proto = IPPROTO_ICMP;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+icmp6_item : icmp6type {
+ $$ = calloc(1, sizeof(struct node_icmp));
+ if ($$ == NULL)
+ err(1, "icmp_item: calloc");
+ $$->type = $1;
+ $$->code = 0;
+ $$->proto = IPPROTO_ICMPV6;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | icmp6type CODE STRING {
+ const struct icmpcodeent *p;
+
+ if ((p = geticmpcodebyname($1-1, $3, AF_INET6)) == NULL) {
+ yyerror("unknown icmp6-code %s", $3);
+ free($3);
+ YYERROR;
+ }
+ free($3);
+
+ $$ = calloc(1, sizeof(struct node_icmp));
+ if ($$ == NULL)
+ err(1, "icmp_item: calloc");
+ $$->type = $1;
+ $$->code = p->code + 1;
+ $$->proto = IPPROTO_ICMPV6;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | icmp6type CODE NUMBER {
+ if ($3 < 0 || $3 > 255) {
+ yyerror("illegal icmp-code %lu", $3);
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_icmp));
+ if ($$ == NULL)
+ err(1, "icmp_item: calloc");
+ $$->type = $1;
+ $$->code = $3 + 1;
+ $$->proto = IPPROTO_ICMPV6;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+icmptype : STRING {
+ const struct icmptypeent *p;
+
+ if ((p = geticmptypebyname($1, AF_INET)) == NULL) {
+ yyerror("unknown icmp-type %s", $1);
+ free($1);
+ YYERROR;
+ }
+ $$ = p->type + 1;
+ free($1);
+ }
+ | NUMBER {
+ if ($1 < 0 || $1 > 255) {
+ yyerror("illegal icmp-type %lu", $1);
+ YYERROR;
+ }
+ $$ = $1 + 1;
+ }
+ ;
+
+icmp6type : STRING {
+ const struct icmptypeent *p;
+
+ if ((p = geticmptypebyname($1, AF_INET6)) ==
+ NULL) {
+ yyerror("unknown icmp6-type %s", $1);
+ free($1);
+ YYERROR;
+ }
+ $$ = p->type + 1;
+ free($1);
+ }
+ | NUMBER {
+ if ($1 < 0 || $1 > 255) {
+ yyerror("illegal icmp6-type %lu", $1);
+ YYERROR;
+ }
+ $$ = $1 + 1;
+ }
+ ;
+
+tos : STRING {
+ if (!strcmp($1, "lowdelay"))
+ $$ = IPTOS_LOWDELAY;
+ else if (!strcmp($1, "throughput"))
+ $$ = IPTOS_THROUGHPUT;
+ else if (!strcmp($1, "reliability"))
+ $$ = IPTOS_RELIABILITY;
+ else if ($1[0] == '0' && $1[1] == 'x')
+ $$ = strtoul($1, NULL, 16);
+ else
+ $$ = 0; /* flag bad argument */
+ if (!$$ || $$ > 255) {
+ yyerror("illegal tos value %s", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ | NUMBER {
+ $$ = $1;
+ if (!$$ || $$ > 255) {
+ yyerror("illegal tos value %s", $1);
+ YYERROR;
+ }
+ }
+ ;
+
+sourcetrack : SOURCETRACK { $$ = PF_SRCTRACK; }
+ | SOURCETRACK GLOBAL { $$ = PF_SRCTRACK_GLOBAL; }
+ | SOURCETRACK RULE { $$ = PF_SRCTRACK_RULE; }
+ ;
+
+statelock : IFBOUND {
+ $$ = PFRULE_IFBOUND;
+ }
+ | FLOATING {
+ $$ = 0;
+ }
+ ;
+
+keep : NO STATE {
+ $$.action = 0;
+ $$.options = NULL;
+ }
+ | KEEP STATE state_opt_spec {
+ $$.action = PF_STATE_NORMAL;
+ $$.options = $3;
+ }
+ | MODULATE STATE state_opt_spec {
+ $$.action = PF_STATE_MODULATE;
+ $$.options = $3;
+ }
+ | SYNPROXY STATE state_opt_spec {
+ $$.action = PF_STATE_SYNPROXY;
+ $$.options = $3;
+ }
+ ;
+
+flush : /* empty */ { $$ = 0; }
+ | FLUSH { $$ = PF_FLUSH; }
+ | FLUSH GLOBAL {
+ $$ = PF_FLUSH | PF_FLUSH_GLOBAL;
+ }
+ ;
+
+state_opt_spec : '(' state_opt_list ')' { $$ = $2; }
+ | /* empty */ { $$ = NULL; }
+ ;
+
+state_opt_list : state_opt_item { $$ = $1; }
+ | state_opt_list comma state_opt_item {
+ $1->tail->next = $3;
+ $1->tail = $3;
+ $$ = $1;
+ }
+ ;
+
+state_opt_item : MAXIMUM NUMBER {
+ if ($2 < 0 || $2 > UINT_MAX) {
+ yyerror("only positive values permitted");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_MAX;
+ $$->data.max_states = $2;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | NOSYNC {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_NOSYNC;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | MAXSRCSTATES NUMBER {
+ if ($2 < 0 || $2 > UINT_MAX) {
+ yyerror("only positive values permitted");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_MAX_SRC_STATES;
+ $$->data.max_src_states = $2;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | MAXSRCCONN NUMBER {
+ if ($2 < 0 || $2 > UINT_MAX) {
+ yyerror("only positive values permitted");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_MAX_SRC_CONN;
+ $$->data.max_src_conn = $2;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | MAXSRCCONNRATE NUMBER '/' NUMBER {
+ if ($2 < 0 || $2 > UINT_MAX ||
+ $4 < 0 || $4 > UINT_MAX) {
+ yyerror("only positive values permitted");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_MAX_SRC_CONN_RATE;
+ $$->data.max_src_conn_rate.limit = $2;
+ $$->data.max_src_conn_rate.seconds = $4;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | OVERLOAD '<' STRING '>' flush {
+ if (strlen($3) >= PF_TABLE_NAME_SIZE) {
+ yyerror("table name '%s' too long", $3);
+ free($3);
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ if (strlcpy($$->data.overload.tblname, $3,
+ PF_TABLE_NAME_SIZE) >= PF_TABLE_NAME_SIZE)
+ errx(1, "state_opt_item: strlcpy");
+ free($3);
+ $$->type = PF_STATE_OPT_OVERLOAD;
+ $$->data.overload.flush = $5;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | MAXSRCNODES NUMBER {
+ if ($2 < 0 || $2 > UINT_MAX) {
+ yyerror("only positive values permitted");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_MAX_SRC_NODES;
+ $$->data.max_src_nodes = $2;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | sourcetrack {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_SRCTRACK;
+ $$->data.src_track = $1;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | statelock {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_STATELOCK;
+ $$->data.statelock = $1;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | SLOPPY {
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_SLOPPY;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | STRING NUMBER {
+ int i;
+
+ if ($2 < 0 || $2 > UINT_MAX) {
+ yyerror("only positive values permitted");
+ YYERROR;
+ }
+ for (i = 0; pf_timeouts[i].name &&
+ strcmp(pf_timeouts[i].name, $1); ++i)
+ ; /* nothing */
+ if (!pf_timeouts[i].name) {
+ yyerror("illegal timeout name %s", $1);
+ free($1);
+ YYERROR;
+ }
+ if (strchr(pf_timeouts[i].name, '.') == NULL) {
+ yyerror("illegal state timeout %s", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ $$ = calloc(1, sizeof(struct node_state_opt));
+ if ($$ == NULL)
+ err(1, "state_opt_item: calloc");
+ $$->type = PF_STATE_OPT_TIMEOUT;
+ $$->data.timeout.number = pf_timeouts[i].timeout;
+ $$->data.timeout.seconds = $2;
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ ;
+
+label : LABEL STRING {
+ $$ = $2;
+ }
+ ;
+
+qname : QUEUE STRING {
+ $$.qname = $2;
+ $$.pqname = NULL;
+ }
+ | QUEUE '(' STRING ')' {
+ $$.qname = $3;
+ $$.pqname = NULL;
+ }
+ | QUEUE '(' STRING comma STRING ')' {
+ $$.qname = $3;
+ $$.pqname = $5;
+ }
+ ;
+
+no : /* empty */ { $$ = 0; }
+ | NO { $$ = 1; }
+ ;
+
+portstar : numberstring {
+ if (parseport($1, &$$, PPORT_RANGE|PPORT_STAR) == -1) {
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+redirspec : host { $$ = $1; }
+ | '{' optnl redir_host_list '}' { $$ = $3; }
+ ;
+
+redir_host_list : host optnl { $$ = $1; }
+ | redir_host_list comma host optnl {
+ $1->tail->next = $3;
+ $1->tail = $3->tail;
+ $$ = $1;
+ }
+ ;
+
+redirpool : /* empty */ { $$ = NULL; }
+ | ARROW redirspec {
+ $$ = calloc(1, sizeof(struct redirection));
+ if ($$ == NULL)
+ err(1, "redirection: calloc");
+ $$->host = $2;
+ $$->rport.a = $$->rport.b = $$->rport.t = 0;
+ }
+ | ARROW redirspec PORT portstar {
+ $$ = calloc(1, sizeof(struct redirection));
+ if ($$ == NULL)
+ err(1, "redirection: calloc");
+ $$->host = $2;
+ $$->rport = $4;
+ }
+ ;
+
+hashkey : /* empty */
+ {
+ $$ = calloc(1, sizeof(struct pf_poolhashkey));
+ if ($$ == NULL)
+ err(1, "hashkey: calloc");
+ $$->key32[0] = arc4random();
+ $$->key32[1] = arc4random();
+ $$->key32[2] = arc4random();
+ $$->key32[3] = arc4random();
+ }
+ | string
+ {
+ if (!strncmp($1, "0x", 2)) {
+ if (strlen($1) != 34) {
+ free($1);
+ yyerror("hex key must be 128 bits "
+ "(32 hex digits) long");
+ YYERROR;
+ }
+ $$ = calloc(1, sizeof(struct pf_poolhashkey));
+ if ($$ == NULL)
+ err(1, "hashkey: calloc");
+
+ if (sscanf($1, "0x%8x%8x%8x%8x",
+ &$$->key32[0], &$$->key32[1],
+ &$$->key32[2], &$$->key32[3]) != 4) {
+ free($$);
+ free($1);
+ yyerror("invalid hex key");
+ YYERROR;
+ }
+ } else {
+ MD5_CTX context;
+
+ $$ = calloc(1, sizeof(struct pf_poolhashkey));
+ if ($$ == NULL)
+ err(1, "hashkey: calloc");
+ MD5Init(&context);
+ MD5Update(&context, (unsigned char *)$1,
+ strlen($1));
+ MD5Final((unsigned char *)$$, &context);
+ HTONL($$->key32[0]);
+ HTONL($$->key32[1]);
+ HTONL($$->key32[2]);
+ HTONL($$->key32[3]);
+ }
+ free($1);
+ }
+ ;
+
+pool_opts : { bzero(&pool_opts, sizeof pool_opts); }
+ pool_opts_l
+ { $$ = pool_opts; }
+ | /* empty */ {
+ bzero(&pool_opts, sizeof pool_opts);
+ $$ = pool_opts;
+ }
+ ;
+
+pool_opts_l : pool_opts_l pool_opt
+ | pool_opt
+ ;
+
+pool_opt : BITMASK {
+ if (pool_opts.type) {
+ yyerror("pool type cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.type = PF_POOL_BITMASK;
+ }
+ | RANDOM {
+ if (pool_opts.type) {
+ yyerror("pool type cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.type = PF_POOL_RANDOM;
+ }
+ | SOURCEHASH hashkey {
+ if (pool_opts.type) {
+ yyerror("pool type cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.type = PF_POOL_SRCHASH;
+ pool_opts.key = $2;
+ }
+ | ROUNDROBIN {
+ if (pool_opts.type) {
+ yyerror("pool type cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.type = PF_POOL_ROUNDROBIN;
+ }
+ | STATICPORT {
+ if (pool_opts.staticport) {
+ yyerror("static-port cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.staticport = 1;
+ }
+ | STICKYADDRESS {
+ if (filter_opts.marker & POM_STICKYADDRESS) {
+ yyerror("sticky-address cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.marker |= POM_STICKYADDRESS;
+ pool_opts.opts |= PF_POOL_STICKYADDR;
+ }
+ ;
+
+redirection : /* empty */ { $$ = NULL; }
+ | ARROW host {
+ $$ = calloc(1, sizeof(struct redirection));
+ if ($$ == NULL)
+ err(1, "redirection: calloc");
+ $$->host = $2;
+ $$->rport.a = $$->rport.b = $$->rport.t = 0;
+ }
+ | ARROW host PORT portstar {
+ $$ = calloc(1, sizeof(struct redirection));
+ if ($$ == NULL)
+ err(1, "redirection: calloc");
+ $$->host = $2;
+ $$->rport = $4;
+ }
+ ;
+
+natpasslog : /* empty */ { $$.b1 = $$.b2 = 0; $$.w2 = 0; }
+ | PASS { $$.b1 = 1; $$.b2 = 0; $$.w2 = 0; }
+ | PASS log { $$.b1 = 1; $$.b2 = $2.log; $$.w2 = $2.logif; }
+ | log { $$.b1 = 0; $$.b2 = $1.log; $$.w2 = $1.logif; }
+ ;
+
+nataction : no NAT natpasslog {
+ if ($1 && $3.b1) {
+ yyerror("\"pass\" not valid with \"no\"");
+ YYERROR;
+ }
+ if ($1)
+ $$.b1 = PF_NONAT;
+ else
+ $$.b1 = PF_NAT;
+ $$.b2 = $3.b1;
+ $$.w = $3.b2;
+ $$.w2 = $3.w2;
+ }
+ | no RDR natpasslog {
+ if ($1 && $3.b1) {
+ yyerror("\"pass\" not valid with \"no\"");
+ YYERROR;
+ }
+ if ($1)
+ $$.b1 = PF_NORDR;
+ else
+ $$.b1 = PF_RDR;
+ $$.b2 = $3.b1;
+ $$.w = $3.b2;
+ $$.w2 = $3.w2;
+ }
+ ;
+
+natrule : nataction interface af proto fromto tag tagged rtable
+ redirpool pool_opts
+ {
+ struct pf_rule r;
+
+ if (check_rulestate(PFCTL_STATE_NAT))
+ YYERROR;
+
+ memset(&r, 0, sizeof(r));
+
+ r.action = $1.b1;
+ r.natpass = $1.b2;
+ r.log = $1.w;
+ r.logif = $1.w2;
+ r.af = $3;
+
+ if (!r.af) {
+ if ($5.src.host && $5.src.host->af &&
+ !$5.src.host->ifindex)
+ r.af = $5.src.host->af;
+ else if ($5.dst.host && $5.dst.host->af &&
+ !$5.dst.host->ifindex)
+ r.af = $5.dst.host->af;
+ }
+
+ if ($6 != NULL)
+ if (strlcpy(r.tagname, $6, PF_TAG_NAME_SIZE) >=
+ PF_TAG_NAME_SIZE) {
+ yyerror("tag too long, max %u chars",
+ PF_TAG_NAME_SIZE - 1);
+ YYERROR;
+ }
+
+ if ($7.name)
+ if (strlcpy(r.match_tagname, $7.name,
+ PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+ yyerror("tag too long, max %u chars",
+ PF_TAG_NAME_SIZE - 1);
+ YYERROR;
+ }
+ r.match_tag_not = $7.neg;
+ r.rtableid = $8;
+
+ if (r.action == PF_NONAT || r.action == PF_NORDR) {
+ if ($9 != NULL) {
+ yyerror("translation rule with 'no' "
+ "does not need '->'");
+ YYERROR;
+ }
+ } else {
+ if ($9 == NULL || $9->host == NULL) {
+ yyerror("translation rule requires '-> "
+ "address'");
+ YYERROR;
+ }
+ if (!r.af && ! $9->host->ifindex)
+ r.af = $9->host->af;
+
+ remove_invalid_hosts(&$9->host, &r.af);
+ if (invalid_redirect($9->host, r.af))
+ YYERROR;
+ if (check_netmask($9->host, r.af))
+ YYERROR;
+
+ r.rpool.proxy_port[0] = ntohs($9->rport.a);
+
+ switch (r.action) {
+ case PF_RDR:
+ if (!$9->rport.b && $9->rport.t &&
+ $5.dst.port != NULL) {
+ r.rpool.proxy_port[1] =
+ ntohs($9->rport.a) +
+ (ntohs(
+ $5.dst.port->port[1]) -
+ ntohs(
+ $5.dst.port->port[0]));
+ } else
+ r.rpool.proxy_port[1] =
+ ntohs($9->rport.b);
+ break;
+ case PF_NAT:
+ r.rpool.proxy_port[1] =
+ ntohs($9->rport.b);
+ if (!r.rpool.proxy_port[0] &&
+ !r.rpool.proxy_port[1]) {
+ r.rpool.proxy_port[0] =
+ PF_NAT_PROXY_PORT_LOW;
+ r.rpool.proxy_port[1] =
+ PF_NAT_PROXY_PORT_HIGH;
+ } else if (!r.rpool.proxy_port[1])
+ r.rpool.proxy_port[1] =
+ r.rpool.proxy_port[0];
+ break;
+ default:
+ break;
+ }
+
+ r.rpool.opts = $10.type;
+ if ((r.rpool.opts & PF_POOL_TYPEMASK) ==
+ PF_POOL_NONE && ($9->host->next != NULL ||
+ $9->host->addr.type == PF_ADDR_TABLE ||
+ DYNIF_MULTIADDR($9->host->addr)))
+ r.rpool.opts = PF_POOL_ROUNDROBIN;
+ if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
+ PF_POOL_ROUNDROBIN &&
+ disallow_table($9->host, "tables are only "
+ "supported in round-robin redirection "
+ "pools"))
+ YYERROR;
+ if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
+ PF_POOL_ROUNDROBIN &&
+ disallow_alias($9->host, "interface (%s) "
+ "is only supported in round-robin "
+ "redirection pools"))
+ YYERROR;
+ if ($9->host->next != NULL) {
+ if ((r.rpool.opts & PF_POOL_TYPEMASK) !=
+ PF_POOL_ROUNDROBIN) {
+ yyerror("only round-robin "
+ "valid for multiple "
+ "redirection addresses");
+ YYERROR;
+ }
+ }
+ }
+
+ if ($10.key != NULL)
+ memcpy(&r.rpool.key, $10.key,
+ sizeof(struct pf_poolhashkey));
+
+ if ($10.opts)
+ r.rpool.opts |= $10.opts;
+
+ if ($10.staticport) {
+ if (r.action != PF_NAT) {
+ yyerror("the 'static-port' option is "
+ "only valid with nat rules");
+ YYERROR;
+ }
+ if (r.rpool.proxy_port[0] !=
+ PF_NAT_PROXY_PORT_LOW &&
+ r.rpool.proxy_port[1] !=
+ PF_NAT_PROXY_PORT_HIGH) {
+ yyerror("the 'static-port' option can't"
+ " be used when specifying a port"
+ " range");
+ YYERROR;
+ }
+ r.rpool.proxy_port[0] = 0;
+ r.rpool.proxy_port[1] = 0;
+ }
+
+ expand_rule(&r, $2, $9 == NULL ? NULL : $9->host, $4,
+ $5.src_os, $5.src.host, $5.src.port, $5.dst.host,
+ $5.dst.port, 0, 0, 0, "");
+ free($9);
+ }
+ ;
+
+binatrule : no BINAT natpasslog interface af proto FROM host toipspec tag
+ tagged rtable redirection
+ {
+ struct pf_rule binat;
+ struct pf_pooladdr *pa;
+
+ if (check_rulestate(PFCTL_STATE_NAT))
+ YYERROR;
+ if (disallow_urpf_failed($9, "\"urpf-failed\" is not "
+ "permitted as a binat destination"))
+ YYERROR;
+
+ memset(&binat, 0, sizeof(binat));
+
+ if ($1 && $3.b1) {
+ yyerror("\"pass\" not valid with \"no\"");
+ YYERROR;
+ }
+ if ($1)
+ binat.action = PF_NOBINAT;
+ else
+ binat.action = PF_BINAT;
+ binat.natpass = $3.b1;
+ binat.log = $3.b2;
+ binat.logif = $3.w2;
+ binat.af = $5;
+ if (!binat.af && $8 != NULL && $8->af)
+ binat.af = $8->af;
+ if (!binat.af && $9 != NULL && $9->af)
+ binat.af = $9->af;
+
+ if (!binat.af && $13 != NULL && $13->host)
+ binat.af = $13->host->af;
+ if (!binat.af) {
+ yyerror("address family (inet/inet6) "
+ "undefined");
+ YYERROR;
+ }
+
+ if ($4 != NULL) {
+ memcpy(binat.ifname, $4->ifname,
+ sizeof(binat.ifname));
+ binat.ifnot = $4->not;
+ free($4);
+ }
+
+ if ($10 != NULL)
+ if (strlcpy(binat.tagname, $10,
+ PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+ yyerror("tag too long, max %u chars",
+ PF_TAG_NAME_SIZE - 1);
+ YYERROR;
+ }
+ if ($11.name)
+ if (strlcpy(binat.match_tagname, $11.name,
+ PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
+ yyerror("tag too long, max %u chars",
+ PF_TAG_NAME_SIZE - 1);
+ YYERROR;
+ }
+ binat.match_tag_not = $11.neg;
+ binat.rtableid = $12;
+
+ if ($6 != NULL) {
+ binat.proto = $6->proto;
+ free($6);
+ }
+
+ if ($8 != NULL && disallow_table($8, "invalid use of "
+ "table <%s> as the source address of a binat rule"))
+ YYERROR;
+ if ($8 != NULL && disallow_alias($8, "invalid use of "
+ "interface (%s) as the source address of a binat "
+ "rule"))
+ YYERROR;
+ if ($13 != NULL && $13->host != NULL && disallow_table(
+ $13->host, "invalid use of table <%s> as the "
+ "redirect address of a binat rule"))
+ YYERROR;
+ if ($13 != NULL && $13->host != NULL && disallow_alias(
+ $13->host, "invalid use of interface (%s) as the "
+ "redirect address of a binat rule"))
+ YYERROR;
+
+ if ($8 != NULL) {
+ if ($8->next) {
+ yyerror("multiple binat ip addresses");
+ YYERROR;
+ }
+ if ($8->addr.type == PF_ADDR_DYNIFTL)
+ $8->af = binat.af;
+ if ($8->af != binat.af) {
+ yyerror("binat ip versions must match");
+ YYERROR;
+ }
+ if (check_netmask($8, binat.af))
+ YYERROR;
+ memcpy(&binat.src.addr, &$8->addr,
+ sizeof(binat.src.addr));
+ free($8);
+ }
+ if ($9 != NULL) {
+ if ($9->next) {
+ yyerror("multiple binat ip addresses");
+ YYERROR;
+ }
+ if ($9->af != binat.af && $9->af) {
+ yyerror("binat ip versions must match");
+ YYERROR;
+ }
+ if (check_netmask($9, binat.af))
+ YYERROR;
+ memcpy(&binat.dst.addr, &$9->addr,
+ sizeof(binat.dst.addr));
+ binat.dst.neg = $9->not;
+ free($9);
+ }
+
+ if (binat.action == PF_NOBINAT) {
+ if ($13 != NULL) {
+ yyerror("'no binat' rule does not need"
+ " '->'");
+ YYERROR;
+ }
+ } else {
+ if ($13 == NULL || $13->host == NULL) {
+ yyerror("'binat' rule requires"
+ " '-> address'");
+ YYERROR;
+ }
+
+ remove_invalid_hosts(&$13->host, &binat.af);
+ if (invalid_redirect($13->host, binat.af))
+ YYERROR;
+ if ($13->host->next != NULL) {
+ yyerror("binat rule must redirect to "
+ "a single address");
+ YYERROR;
+ }
+ if (check_netmask($13->host, binat.af))
+ YYERROR;
+
+ if (!PF_AZERO(&binat.src.addr.v.a.mask,
+ binat.af) &&
+ !PF_AEQ(&binat.src.addr.v.a.mask,
+ &$13->host->addr.v.a.mask, binat.af)) {
+ yyerror("'binat' source mask and "
+ "redirect mask must be the same");
+ YYERROR;
+ }
+
+ TAILQ_INIT(&binat.rpool.list);
+ pa = calloc(1, sizeof(struct pf_pooladdr));
+ if (pa == NULL)
+ err(1, "binat: calloc");
+ pa->addr = $13->host->addr;
+ pa->ifname[0] = 0;
+ TAILQ_INSERT_TAIL(&binat.rpool.list,
+ pa, entries);
+
+ free($13);
+ }
+
+ pfctl_add_rule(pf, &binat, "");
+ }
+ ;
+
+tag : /* empty */ { $$ = NULL; }
+ | TAG STRING { $$ = $2; }
+ ;
+
+tagged : /* empty */ { $$.neg = 0; $$.name = NULL; }
+ | not TAGGED string { $$.neg = $1; $$.name = $3; }
+ ;
+
+rtable : /* empty */ { $$ = -1; }
+ | RTABLE NUMBER {
+ if ($2 < 0 || $2 > rt_tableid_max()) {
+ yyerror("invalid rtable id");
+ YYERROR;
+ }
+ $$ = $2;
+ }
+ ;
+
+route_host : STRING {
+ $$ = calloc(1, sizeof(struct node_host));
+ if ($$ == NULL)
+ err(1, "route_host: calloc");
+ $$->ifname = $1;
+ set_ipmask($$, 128);
+ $$->next = NULL;
+ $$->tail = $$;
+ }
+ | '(' STRING host ')' {
+ $$ = $3;
+ $$->ifname = $2;
+ }
+ ;
+
+route_host_list : route_host optnl { $$ = $1; }
+ | route_host_list comma route_host optnl {
+ if ($1->af == 0)
+ $1->af = $3->af;
+ if ($1->af != $3->af) {
+ yyerror("all pool addresses must be in the "
+ "same address family");
+ YYERROR;
+ }
+ $1->tail->next = $3;
+ $1->tail = $3->tail;
+ $$ = $1;
+ }
+ ;
+
+routespec : route_host { $$ = $1; }
+ | '{' optnl route_host_list '}' { $$ = $3; }
+ ;
+
+route : /* empty */ {
+ $$.host = NULL;
+ $$.rt = 0;
+ $$.pool_opts = 0;
+ }
+ | FASTROUTE {
+ $$.host = NULL;
+ $$.rt = PF_FASTROUTE;
+ $$.pool_opts = 0;
+ }
+ | ROUTETO routespec pool_opts {
+ $$.host = $2;
+ $$.rt = PF_ROUTETO;
+ $$.pool_opts = $3.type | $3.opts;
+ if ($3.key != NULL)
+ $$.key = $3.key;
+ }
+ | REPLYTO routespec pool_opts {
+ $$.host = $2;
+ $$.rt = PF_REPLYTO;
+ $$.pool_opts = $3.type | $3.opts;
+ if ($3.key != NULL)
+ $$.key = $3.key;
+ }
+ | DUPTO routespec pool_opts {
+ $$.host = $2;
+ $$.rt = PF_DUPTO;
+ $$.pool_opts = $3.type | $3.opts;
+ if ($3.key != NULL)
+ $$.key = $3.key;
+ }
+ ;
+
+timeout_spec : STRING NUMBER
+ {
+ if (check_rulestate(PFCTL_STATE_OPTION)) {
+ free($1);
+ YYERROR;
+ }
+ if ($2 < 0 || $2 > UINT_MAX) {
+ yyerror("only positive values permitted");
+ YYERROR;
+ }
+ if (pfctl_set_timeout(pf, $1, $2, 0) != 0) {
+ yyerror("unknown timeout %s", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+timeout_list : timeout_list comma timeout_spec optnl
+ | timeout_spec optnl
+ ;
+
+limit_spec : STRING NUMBER
+ {
+ if (check_rulestate(PFCTL_STATE_OPTION)) {
+ free($1);
+ YYERROR;
+ }
+ if ($2 < 0 || $2 > UINT_MAX) {
+ yyerror("only positive values permitted");
+ YYERROR;
+ }
+ if (pfctl_set_limit(pf, $1, $2) != 0) {
+ yyerror("unable to set limit %s %u", $1, $2);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+limit_list : limit_list comma limit_spec optnl
+ | limit_spec optnl
+ ;
+
+comma : ','
+ | /* empty */
+ ;
+
+yesno : NO { $$ = 0; }
+ | STRING {
+ if (!strcmp($1, "yes"))
+ $$ = 1;
+ else {
+ yyerror("invalid value '%s', expected 'yes' "
+ "or 'no'", $1);
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+unaryop : '=' { $$ = PF_OP_EQ; }
+ | '!' '=' { $$ = PF_OP_NE; }
+ | '<' '=' { $$ = PF_OP_LE; }
+ | '<' { $$ = PF_OP_LT; }
+ | '>' '=' { $$ = PF_OP_GE; }
+ | '>' { $$ = PF_OP_GT; }
+ ;
+
+%%
+
+int
+yyerror(const char *fmt, ...)
+{
+ va_list ap;
+
+ file->errors++;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:%d: ", file->name, yylval.lineno);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ return (0);
+}
+
+int
+disallow_table(struct node_host *h, const char *fmt)
+{
+ for (; h != NULL; h = h->next)
+ if (h->addr.type == PF_ADDR_TABLE) {
+ yyerror(fmt, h->addr.v.tblname);
+ return (1);
+ }
+ return (0);
+}
+
+int
+disallow_urpf_failed(struct node_host *h, const char *fmt)
+{
+ for (; h != NULL; h = h->next)
+ if (h->addr.type == PF_ADDR_URPFFAILED) {
+ yyerror(fmt);
+ return (1);
+ }
+ return (0);
+}
+
+int
+disallow_alias(struct node_host *h, const char *fmt)
+{
+ for (; h != NULL; h = h->next)
+ if (DYNIF_MULTIADDR(h->addr)) {
+ yyerror(fmt, h->addr.v.tblname);
+ return (1);
+ }
+ return (0);
+}
+
+int
+rule_consistent(struct pf_rule *r, int anchor_call)
+{
+ int problems = 0;
+
+ switch (r->action) {
+ case PF_PASS:
+ case PF_DROP:
+ case PF_SCRUB:
+ case PF_NOSCRUB:
+ problems = filter_consistent(r, anchor_call);
+ break;
+ case PF_NAT:
+ case PF_NONAT:
+ problems = nat_consistent(r);
+ break;
+ case PF_RDR:
+ case PF_NORDR:
+ problems = rdr_consistent(r);
+ break;
+ case PF_BINAT:
+ case PF_NOBINAT:
+ default:
+ break;
+ }
+ return (problems);
+}
+
+int
+filter_consistent(struct pf_rule *r, int anchor_call)
+{
+ int problems = 0;
+
+ if (r->proto != IPPROTO_TCP && r->proto != IPPROTO_UDP &&
+ (r->src.port_op || r->dst.port_op)) {
+ yyerror("port only applies to tcp/udp");
+ problems++;
+ }
+ if (r->proto != IPPROTO_ICMP && r->proto != IPPROTO_ICMPV6 &&
+ (r->type || r->code)) {
+ yyerror("icmp-type/code only applies to icmp");
+ problems++;
+ }
+ if (!r->af && (r->type || r->code)) {
+ yyerror("must indicate address family with icmp-type/code");
+ problems++;
+ }
+ if (r->overload_tblname[0] &&
+ r->max_src_conn == 0 && r->max_src_conn_rate.seconds == 0) {
+ yyerror("'overload' requires 'max-src-conn' "
+ "or 'max-src-conn-rate'");
+ problems++;
+ }
+ if ((r->proto == IPPROTO_ICMP && r->af == AF_INET6) ||
+ (r->proto == IPPROTO_ICMPV6 && r->af == AF_INET)) {
+ yyerror("proto %s doesn't match address family %s",
+ r->proto == IPPROTO_ICMP ? "icmp" : "icmp6",
+ r->af == AF_INET ? "inet" : "inet6");
+ problems++;
+ }
+ if (r->allow_opts && r->action != PF_PASS) {
+ yyerror("allow-opts can only be specified for pass rules");
+ problems++;
+ }
+ if (r->rule_flag & PFRULE_FRAGMENT && (r->src.port_op ||
+ r->dst.port_op || r->flagset || r->type || r->code)) {
+ yyerror("fragments can be filtered only on IP header fields");
+ problems++;
+ }
+ if (r->rule_flag & PFRULE_RETURNRST && r->proto != IPPROTO_TCP) {
+ yyerror("return-rst can only be applied to TCP rules");
+ problems++;
+ }
+ if (r->max_src_nodes && !(r->rule_flag & PFRULE_RULESRCTRACK)) {
+ yyerror("max-src-nodes requires 'source-track rule'");
+ problems++;
+ }
+ if (r->action == PF_DROP && r->keep_state) {
+ yyerror("keep state on block rules doesn't make sense");
+ problems++;
+ }
+ if (r->rule_flag & PFRULE_STATESLOPPY &&
+ (r->keep_state == PF_STATE_MODULATE ||
+ r->keep_state == PF_STATE_SYNPROXY)) {
+ yyerror("sloppy state matching cannot be used with "
+ "synproxy state or modulate state");
+ problems++;
+ }
+ return (-problems);
+}
+
+int
+nat_consistent(struct pf_rule *r)
+{
+ return (0); /* yeah! */
+}
+
+int
+rdr_consistent(struct pf_rule *r)
+{
+ int problems = 0;
+
+ if (r->proto != IPPROTO_TCP && r->proto != IPPROTO_UDP) {
+ if (r->src.port_op) {
+ yyerror("src port only applies to tcp/udp");
+ problems++;
+ }
+ if (r->dst.port_op) {
+ yyerror("dst port only applies to tcp/udp");
+ problems++;
+ }
+ if (r->rpool.proxy_port[0]) {
+ yyerror("rpool port only applies to tcp/udp");
+ problems++;
+ }
+ }
+ if (r->dst.port_op &&
+ r->dst.port_op != PF_OP_EQ && r->dst.port_op != PF_OP_RRG) {
+ yyerror("invalid port operator for rdr destination port");
+ problems++;
+ }
+ return (-problems);
+}
+
+int
+process_tabledef(char *name, struct table_opts *opts)
+{
+ struct pfr_buffer ab;
+ struct node_tinit *ti;
+
+ bzero(&ab, sizeof(ab));
+ ab.pfrb_type = PFRB_ADDRS;
+ SIMPLEQ_FOREACH(ti, &opts->init_nodes, entries) {
+ if (ti->file)
+ if (pfr_buf_load(&ab, ti->file, 0, append_addr)) {
+ if (errno)
+ yyerror("cannot load \"%s\": %s",
+ ti->file, strerror(errno));
+ else
+ yyerror("file \"%s\" contains bad data",
+ ti->file);
+ goto _error;
+ }
+ if (ti->host)
+ if (append_addr_host(&ab, ti->host, 0, 0)) {
+ yyerror("cannot create address buffer: %s",
+ strerror(errno));
+ goto _error;
+ }
+ }
+ if (pf->opts & PF_OPT_VERBOSE)
+ print_tabledef(name, opts->flags, opts->init_addr,
+ &opts->init_nodes);
+ if (!(pf->opts & PF_OPT_NOACTION) &&
+ pfctl_define_table(name, opts->flags, opts->init_addr,
+ pf->anchor->name, &ab, pf->anchor->ruleset.tticket)) {
+ yyerror("cannot define table %s: %s", name,
+ pfr_strerror(errno));
+ goto _error;
+ }
+ pf->tdirty = 1;
+ pfr_buf_clear(&ab);
+ return (0);
+_error:
+ pfr_buf_clear(&ab);
+ return (-1);
+}
+
+struct keywords {
+ const char *k_name;
+ int k_val;
+};
+
+/* macro gore, but you should've seen the prior indentation nightmare... */
+
+#define FREE_LIST(T,r) \
+ do { \
+ T *p, *node = r; \
+ while (node != NULL) { \
+ p = node; \
+ node = node->next; \
+ free(p); \
+ } \
+ } while (0)
+
+#define LOOP_THROUGH(T,n,r,C) \
+ do { \
+ T *n; \
+ if (r == NULL) { \
+ r = calloc(1, sizeof(T)); \
+ if (r == NULL) \
+ err(1, "LOOP: calloc"); \
+ r->next = NULL; \
+ } \
+ n = r; \
+ while (n != NULL) { \
+ do { \
+ C; \
+ } while (0); \
+ n = n->next; \
+ } \
+ } while (0)
+
+void
+expand_label_str(char *label, size_t len, const char *srch, const char *repl)
+{
+ char *tmp;
+ char *p, *q;
+
+ if ((tmp = calloc(1, len)) == NULL)
+ err(1, "expand_label_str: calloc");
+ p = q = label;
+ while ((q = strstr(p, srch)) != NULL) {
+ *q = '\0';
+ if ((strlcat(tmp, p, len) >= len) ||
+ (strlcat(tmp, repl, len) >= len))
+ errx(1, "expand_label: label too long");
+ q += strlen(srch);
+ p = q;
+ }
+ if (strlcat(tmp, p, len) >= len)
+ errx(1, "expand_label: label too long");
+ strlcpy(label, tmp, len); /* always fits */
+ free(tmp);
+}
+
+void
+expand_label_if(const char *name, char *label, size_t len, const char *ifname)
+{
+ if (strstr(label, name) != NULL) {
+ if (!*ifname)
+ expand_label_str(label, len, name, "any");
+ else
+ expand_label_str(label, len, name, ifname);
+ }
+}
+
+void
+expand_label_addr(const char *name, char *label, size_t len, sa_family_t af,
+ struct node_host *h)
+{
+ char tmp[64], tmp_not[66];
+
+ if (strstr(label, name) != NULL) {
+ switch (h->addr.type) {
+ case PF_ADDR_DYNIFTL:
+ snprintf(tmp, sizeof(tmp), "(%s)", h->addr.v.ifname);
+ break;
+ case PF_ADDR_TABLE:
+ snprintf(tmp, sizeof(tmp), "<%s>", h->addr.v.tblname);
+ break;
+ case PF_ADDR_NOROUTE:
+ snprintf(tmp, sizeof(tmp), "no-route");
+ break;
+ case PF_ADDR_URPFFAILED:
+ snprintf(tmp, sizeof(tmp), "urpf-failed");
+ break;
+ case PF_ADDR_ADDRMASK:
+ if (!af || (PF_AZERO(&h->addr.v.a.addr, af) &&
+ PF_AZERO(&h->addr.v.a.mask, af)))
+ snprintf(tmp, sizeof(tmp), "any");
+ else {
+ char a[48];
+ int bits;
+
+ if (inet_ntop(af, &h->addr.v.a.addr, a,
+ sizeof(a)) == NULL)
+ snprintf(tmp, sizeof(tmp), "?");
+ else {
+ bits = unmask(&h->addr.v.a.mask, af);
+ if ((af == AF_INET && bits < 32) ||
+ (af == AF_INET6 && bits < 128))
+ snprintf(tmp, sizeof(tmp),
+ "%s/%d", a, bits);
+ else
+ snprintf(tmp, sizeof(tmp),
+ "%s", a);
+ }
+ }
+ break;
+ default:
+ snprintf(tmp, sizeof(tmp), "?");
+ break;
+ }
+
+ if (h->not) {
+ snprintf(tmp_not, sizeof(tmp_not), "! %s", tmp);
+ expand_label_str(label, len, name, tmp_not);
+ } else
+ expand_label_str(label, len, name, tmp);
+ }
+}
+
+void
+expand_label_port(const char *name, char *label, size_t len,
+ struct node_port *port)
+{
+ char a1[6], a2[6], op[13] = "";
+
+ if (strstr(label, name) != NULL) {
+ snprintf(a1, sizeof(a1), "%u", ntohs(port->port[0]));
+ snprintf(a2, sizeof(a2), "%u", ntohs(port->port[1]));
+ if (!port->op)
+ ;
+ else if (port->op == PF_OP_IRG)
+ snprintf(op, sizeof(op), "%s><%s", a1, a2);
+ else if (port->op == PF_OP_XRG)
+ snprintf(op, sizeof(op), "%s<>%s", a1, a2);
+ else if (port->op == PF_OP_EQ)
+ snprintf(op, sizeof(op), "%s", a1);
+ else if (port->op == PF_OP_NE)
+ snprintf(op, sizeof(op), "!=%s", a1);
+ else if (port->op == PF_OP_LT)
+ snprintf(op, sizeof(op), "<%s", a1);
+ else if (port->op == PF_OP_LE)
+ snprintf(op, sizeof(op), "<=%s", a1);
+ else if (port->op == PF_OP_GT)
+ snprintf(op, sizeof(op), ">%s", a1);
+ else if (port->op == PF_OP_GE)
+ snprintf(op, sizeof(op), ">=%s", a1);
+ expand_label_str(label, len, name, op);
+ }
+}
+
+void
+expand_label_proto(const char *name, char *label, size_t len, u_int8_t proto)
+{
+ struct protoent *pe;
+ char n[4];
+
+ if (strstr(label, name) != NULL) {
+ pe = getprotobynumber(proto);
+ if (pe != NULL)
+ expand_label_str(label, len, name, pe->p_name);
+ else {
+ snprintf(n, sizeof(n), "%u", proto);
+ expand_label_str(label, len, name, n);
+ }
+ }
+}
+
+void
+expand_label_nr(const char *name, char *label, size_t len)
+{
+ char n[11];
+
+ if (strstr(label, name) != NULL) {
+ snprintf(n, sizeof(n), "%u", pf->anchor->match);
+ expand_label_str(label, len, name, n);
+ }
+}
+
+void
+expand_label(char *label, size_t len, const char *ifname, sa_family_t af,
+ struct node_host *src_host, struct node_port *src_port,
+ struct node_host *dst_host, struct node_port *dst_port,
+ u_int8_t proto)
+{
+ expand_label_if("$if", label, len, ifname);
+ expand_label_addr("$srcaddr", label, len, af, src_host);
+ expand_label_addr("$dstaddr", label, len, af, dst_host);
+ expand_label_port("$srcport", label, len, src_port);
+ expand_label_port("$dstport", label, len, dst_port);
+ expand_label_proto("$proto", label, len, proto);
+ expand_label_nr("$nr", label, len);
+}
+
+int
+expand_altq(struct pf_altq *a, struct node_if *interfaces,
+ struct node_queue *nqueues, struct node_queue_bw bwspec,
+ struct node_queue_opt *opts)
+{
+ struct pf_altq pa, pb;
+ char qname[PF_QNAME_SIZE];
+ struct node_queue *n;
+ struct node_queue_bw bw;
+ int errs = 0;
+
+ if ((pf->loadopt & PFCTL_FLAG_ALTQ) == 0) {
+ FREE_LIST(struct node_if, interfaces);
+ FREE_LIST(struct node_queue, nqueues);
+ return (0);
+ }
+
+ LOOP_THROUGH(struct node_if, interface, interfaces,
+ memcpy(&pa, a, sizeof(struct pf_altq));
+ if (strlcpy(pa.ifname, interface->ifname,
+ sizeof(pa.ifname)) >= sizeof(pa.ifname))
+ errx(1, "expand_altq: strlcpy");
+
+ if (interface->not) {
+ yyerror("altq on ! <interface> is not supported");
+ errs++;
+ } else {
+ if (eval_pfaltq(pf, &pa, &bwspec, opts))
+ errs++;
+ else
+ if (pfctl_add_altq(pf, &pa))
+ errs++;
+
+ if (pf->opts & PF_OPT_VERBOSE) {
+ print_altq(&pf->paltq->altq, 0,
+ &bwspec, opts);
+ if (nqueues && nqueues->tail) {
+ printf("queue { ");
+ LOOP_THROUGH(struct node_queue, queue,
+ nqueues,
+ printf("%s ",
+ queue->queue);
+ );
+ printf("}");
+ }
+ printf("\n");
+ }
+
+ if (pa.scheduler == ALTQT_CBQ ||
+ pa.scheduler == ALTQT_HFSC) {
+ /* now create a root queue */
+ memset(&pb, 0, sizeof(struct pf_altq));
+ if (strlcpy(qname, "root_", sizeof(qname)) >=
+ sizeof(qname))
+ errx(1, "expand_altq: strlcpy");
+ if (strlcat(qname, interface->ifname,
+ sizeof(qname)) >= sizeof(qname))
+ errx(1, "expand_altq: strlcat");
+ if (strlcpy(pb.qname, qname,
+ sizeof(pb.qname)) >= sizeof(pb.qname))
+ errx(1, "expand_altq: strlcpy");
+ if (strlcpy(pb.ifname, interface->ifname,
+ sizeof(pb.ifname)) >= sizeof(pb.ifname))
+ errx(1, "expand_altq: strlcpy");
+ pb.qlimit = pa.qlimit;
+ pb.scheduler = pa.scheduler;
+ bw.bw_absolute = pa.ifbandwidth;
+ bw.bw_percent = 0;
+ if (eval_pfqueue(pf, &pb, &bw, opts))
+ errs++;
+ else
+ if (pfctl_add_altq(pf, &pb))
+ errs++;
+ }
+
+ LOOP_THROUGH(struct node_queue, queue, nqueues,
+ n = calloc(1, sizeof(struct node_queue));
+ if (n == NULL)
+ err(1, "expand_altq: calloc");
+ if (pa.scheduler == ALTQT_CBQ ||
+ pa.scheduler == ALTQT_HFSC)
+ if (strlcpy(n->parent, qname,
+ sizeof(n->parent)) >=
+ sizeof(n->parent))
+ errx(1, "expand_altq: strlcpy");
+ if (strlcpy(n->queue, queue->queue,
+ sizeof(n->queue)) >= sizeof(n->queue))
+ errx(1, "expand_altq: strlcpy");
+ if (strlcpy(n->ifname, interface->ifname,
+ sizeof(n->ifname)) >= sizeof(n->ifname))
+ errx(1, "expand_altq: strlcpy");
+ n->scheduler = pa.scheduler;
+ n->next = NULL;
+ n->tail = n;
+ if (queues == NULL)
+ queues = n;
+ else {
+ queues->tail->next = n;
+ queues->tail = n;
+ }
+ );
+ }
+ );
+ FREE_LIST(struct node_if, interfaces);
+ FREE_LIST(struct node_queue, nqueues);
+
+ return (errs);
+}
+
+int
+expand_queue(struct pf_altq *a, struct node_if *interfaces,
+ struct node_queue *nqueues, struct node_queue_bw bwspec,
+ struct node_queue_opt *opts)
+{
+ struct node_queue *n, *nq;
+ struct pf_altq pa;
+ u_int8_t found = 0;
+ u_int8_t errs = 0;
+
+ if ((pf->loadopt & PFCTL_FLAG_ALTQ) == 0) {
+ FREE_LIST(struct node_queue, nqueues);
+ return (0);
+ }
+
+ if (queues == NULL) {
+ yyerror("queue %s has no parent", a->qname);
+ FREE_LIST(struct node_queue, nqueues);
+ return (1);
+ }
+
+ LOOP_THROUGH(struct node_if, interface, interfaces,
+ LOOP_THROUGH(struct node_queue, tqueue, queues,
+ if (!strncmp(a->qname, tqueue->queue, PF_QNAME_SIZE) &&
+ (interface->ifname[0] == 0 ||
+ (!interface->not && !strncmp(interface->ifname,
+ tqueue->ifname, IFNAMSIZ)) ||
+ (interface->not && strncmp(interface->ifname,
+ tqueue->ifname, IFNAMSIZ)))) {
+ /* found ourself in queues */
+ found++;
+
+ memcpy(&pa, a, sizeof(struct pf_altq));
+
+ if (pa.scheduler != ALTQT_NONE &&
+ pa.scheduler != tqueue->scheduler) {
+ yyerror("exactly one scheduler type "
+ "per interface allowed");
+ return (1);
+ }
+ pa.scheduler = tqueue->scheduler;
+
+ /* scheduler dependent error checking */
+ switch (pa.scheduler) {
+ case ALTQT_PRIQ:
+ if (nqueues != NULL) {
+ yyerror("priq queues cannot "
+ "have child queues");
+ return (1);
+ }
+ if (bwspec.bw_absolute > 0 ||
+ bwspec.bw_percent < 100) {
+ yyerror("priq doesn't take "
+ "bandwidth");
+ return (1);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (strlcpy(pa.ifname, tqueue->ifname,
+ sizeof(pa.ifname)) >= sizeof(pa.ifname))
+ errx(1, "expand_queue: strlcpy");
+ if (strlcpy(pa.parent, tqueue->parent,
+ sizeof(pa.parent)) >= sizeof(pa.parent))
+ errx(1, "expand_queue: strlcpy");
+
+ if (eval_pfqueue(pf, &pa, &bwspec, opts))
+ errs++;
+ else
+ if (pfctl_add_altq(pf, &pa))
+ errs++;
+
+ for (nq = nqueues; nq != NULL; nq = nq->next) {
+ if (!strcmp(a->qname, nq->queue)) {
+ yyerror("queue cannot have "
+ "itself as child");
+ errs++;
+ continue;
+ }
+ n = calloc(1,
+ sizeof(struct node_queue));
+ if (n == NULL)
+ err(1, "expand_queue: calloc");
+ if (strlcpy(n->parent, a->qname,
+ sizeof(n->parent)) >=
+ sizeof(n->parent))
+ errx(1, "expand_queue strlcpy");
+ if (strlcpy(n->queue, nq->queue,
+ sizeof(n->queue)) >=
+ sizeof(n->queue))
+ errx(1, "expand_queue strlcpy");
+ if (strlcpy(n->ifname, tqueue->ifname,
+ sizeof(n->ifname)) >=
+ sizeof(n->ifname))
+ errx(1, "expand_queue strlcpy");
+ n->scheduler = tqueue->scheduler;
+ n->next = NULL;
+ n->tail = n;
+ if (queues == NULL)
+ queues = n;
+ else {
+ queues->tail->next = n;
+ queues->tail = n;
+ }
+ }
+ if ((pf->opts & PF_OPT_VERBOSE) && (
+ (found == 1 && interface->ifname[0] == 0) ||
+ (found > 0 && interface->ifname[0] != 0))) {
+ print_queue(&pf->paltq->altq, 0,
+ &bwspec, interface->ifname[0] != 0,
+ opts);
+ if (nqueues && nqueues->tail) {
+ printf("{ ");
+ LOOP_THROUGH(struct node_queue,
+ queue, nqueues,
+ printf("%s ",
+ queue->queue);
+ );
+ printf("}");
+ }
+ printf("\n");
+ }
+ }
+ );
+ );
+
+ FREE_LIST(struct node_queue, nqueues);
+ FREE_LIST(struct node_if, interfaces);
+
+ if (!found) {
+ yyerror("queue %s has no parent", a->qname);
+ errs++;
+ }
+
+ if (errs)
+ return (1);
+ else
+ return (0);
+}
+
+void
+expand_rule(struct pf_rule *r,
+ struct node_if *interfaces, struct node_host *rpool_hosts,
+ struct node_proto *protos, struct node_os *src_oses,
+ struct node_host *src_hosts, struct node_port *src_ports,
+ struct node_host *dst_hosts, struct node_port *dst_ports,
+ struct node_uid *uids, struct node_gid *gids, struct node_icmp *icmp_types,
+ const char *anchor_call)
+{
+ sa_family_t af = r->af;
+ int added = 0, error = 0;
+ char ifname[IF_NAMESIZE];
+ char label[PF_RULE_LABEL_SIZE];
+ char tagname[PF_TAG_NAME_SIZE];
+ char match_tagname[PF_TAG_NAME_SIZE];
+ struct pf_pooladdr *pa;
+ struct node_host *h;
+ u_int8_t flags, flagset, keep_state;
+
+ if (strlcpy(label, r->label, sizeof(label)) >= sizeof(label))
+ errx(1, "expand_rule: strlcpy");
+ if (strlcpy(tagname, r->tagname, sizeof(tagname)) >= sizeof(tagname))
+ errx(1, "expand_rule: strlcpy");
+ if (strlcpy(match_tagname, r->match_tagname, sizeof(match_tagname)) >=
+ sizeof(match_tagname))
+ errx(1, "expand_rule: strlcpy");
+ flags = r->flags;
+ flagset = r->flagset;
+ keep_state = r->keep_state;
+
+ LOOP_THROUGH(struct node_if, interface, interfaces,
+ LOOP_THROUGH(struct node_proto, proto, protos,
+ LOOP_THROUGH(struct node_icmp, icmp_type, icmp_types,
+ LOOP_THROUGH(struct node_host, src_host, src_hosts,
+ LOOP_THROUGH(struct node_port, src_port, src_ports,
+ LOOP_THROUGH(struct node_os, src_os, src_oses,
+ LOOP_THROUGH(struct node_host, dst_host, dst_hosts,
+ LOOP_THROUGH(struct node_port, dst_port, dst_ports,
+ LOOP_THROUGH(struct node_uid, uid, uids,
+ LOOP_THROUGH(struct node_gid, gid, gids,
+
+ r->af = af;
+ /* for link-local IPv6 address, interface must match up */
+ if ((r->af && src_host->af && r->af != src_host->af) ||
+ (r->af && dst_host->af && r->af != dst_host->af) ||
+ (src_host->af && dst_host->af &&
+ src_host->af != dst_host->af) ||
+ (src_host->ifindex && dst_host->ifindex &&
+ src_host->ifindex != dst_host->ifindex) ||
+ (src_host->ifindex && *interface->ifname &&
+ src_host->ifindex != if_nametoindex(interface->ifname)) ||
+ (dst_host->ifindex && *interface->ifname &&
+ dst_host->ifindex != if_nametoindex(interface->ifname)))
+ continue;
+ if (!r->af && src_host->af)
+ r->af = src_host->af;
+ else if (!r->af && dst_host->af)
+ r->af = dst_host->af;
+
+ if (*interface->ifname)
+ strlcpy(r->ifname, interface->ifname,
+ sizeof(r->ifname));
+ else if (if_indextoname(src_host->ifindex, ifname))
+ strlcpy(r->ifname, ifname, sizeof(r->ifname));
+ else if (if_indextoname(dst_host->ifindex, ifname))
+ strlcpy(r->ifname, ifname, sizeof(r->ifname));
+ else
+ memset(r->ifname, '\0', sizeof(r->ifname));
+
+ if (strlcpy(r->label, label, sizeof(r->label)) >=
+ sizeof(r->label))
+ errx(1, "expand_rule: strlcpy");
+ if (strlcpy(r->tagname, tagname, sizeof(r->tagname)) >=
+ sizeof(r->tagname))
+ errx(1, "expand_rule: strlcpy");
+ if (strlcpy(r->match_tagname, match_tagname,
+ sizeof(r->match_tagname)) >= sizeof(r->match_tagname))
+ errx(1, "expand_rule: strlcpy");
+ expand_label(r->label, PF_RULE_LABEL_SIZE, r->ifname, r->af,
+ src_host, src_port, dst_host, dst_port, proto->proto);
+ expand_label(r->tagname, PF_TAG_NAME_SIZE, r->ifname, r->af,
+ src_host, src_port, dst_host, dst_port, proto->proto);
+ expand_label(r->match_tagname, PF_TAG_NAME_SIZE, r->ifname,
+ r->af, src_host, src_port, dst_host, dst_port,
+ proto->proto);
+
+ error += check_netmask(src_host, r->af);
+ error += check_netmask(dst_host, r->af);
+
+ r->ifnot = interface->not;
+ r->proto = proto->proto;
+ r->src.addr = src_host->addr;
+ r->src.neg = src_host->not;
+ r->src.port[0] = src_port->port[0];
+ r->src.port[1] = src_port->port[1];
+ r->src.port_op = src_port->op;
+ r->dst.addr = dst_host->addr;
+ r->dst.neg = dst_host->not;
+ r->dst.port[0] = dst_port->port[0];
+ r->dst.port[1] = dst_port->port[1];
+ r->dst.port_op = dst_port->op;
+ r->uid.op = uid->op;
+ r->uid.uid[0] = uid->uid[0];
+ r->uid.uid[1] = uid->uid[1];
+ r->gid.op = gid->op;
+ r->gid.gid[0] = gid->gid[0];
+ r->gid.gid[1] = gid->gid[1];
+ r->type = icmp_type->type;
+ r->code = icmp_type->code;
+
+ if ((keep_state == PF_STATE_MODULATE ||
+ keep_state == PF_STATE_SYNPROXY) &&
+ r->proto && r->proto != IPPROTO_TCP)
+ r->keep_state = PF_STATE_NORMAL;
+ else
+ r->keep_state = keep_state;
+
+ if (r->proto && r->proto != IPPROTO_TCP) {
+ r->flags = 0;
+ r->flagset = 0;
+ } else {
+ r->flags = flags;
+ r->flagset = flagset;
+ }
+ if (icmp_type->proto && r->proto != icmp_type->proto) {
+ yyerror("icmp-type mismatch");
+ error++;
+ }
+
+ if (src_os && src_os->os) {
+ r->os_fingerprint = pfctl_get_fingerprint(src_os->os);
+ if ((pf->opts & PF_OPT_VERBOSE2) &&
+ r->os_fingerprint == PF_OSFP_NOMATCH)
+ fprintf(stderr,
+ "warning: unknown '%s' OS fingerprint\n",
+ src_os->os);
+ } else {
+ r->os_fingerprint = PF_OSFP_ANY;
+ }
+
+ TAILQ_INIT(&r->rpool.list);
+ for (h = rpool_hosts; h != NULL; h = h->next) {
+ pa = calloc(1, sizeof(struct pf_pooladdr));
+ if (pa == NULL)
+ err(1, "expand_rule: calloc");
+ pa->addr = h->addr;
+ if (h->ifname != NULL) {
+ if (strlcpy(pa->ifname, h->ifname,
+ sizeof(pa->ifname)) >=
+ sizeof(pa->ifname))
+ errx(1, "expand_rule: strlcpy");
+ } else
+ pa->ifname[0] = 0;
+ TAILQ_INSERT_TAIL(&r->rpool.list, pa, entries);
+ }
+
+ if (rule_consistent(r, anchor_call[0]) < 0 || error)
+ yyerror("skipping rule due to errors");
+ else {
+ r->nr = pf->astack[pf->asd]->match++;
+ pfctl_add_rule(pf, r, anchor_call);
+ added++;
+ }
+
+ ))))))))));
+
+ FREE_LIST(struct node_if, interfaces);
+ FREE_LIST(struct node_proto, protos);
+ FREE_LIST(struct node_host, src_hosts);
+ FREE_LIST(struct node_port, src_ports);
+ FREE_LIST(struct node_os, src_oses);
+ FREE_LIST(struct node_host, dst_hosts);
+ FREE_LIST(struct node_port, dst_ports);
+ FREE_LIST(struct node_uid, uids);
+ FREE_LIST(struct node_gid, gids);
+ FREE_LIST(struct node_icmp, icmp_types);
+ FREE_LIST(struct node_host, rpool_hosts);
+
+ if (!added)
+ yyerror("rule expands to no valid combination");
+}
+
+int
+expand_skip_interface(struct node_if *interfaces)
+{
+ int errs = 0;
+
+ if (!interfaces || (!interfaces->next && !interfaces->not &&
+ !strcmp(interfaces->ifname, "none"))) {
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf("set skip on none\n");
+ errs = pfctl_set_interface_flags(pf, "", PFI_IFLAG_SKIP, 0);
+ return (errs);
+ }
+
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf("set skip on {");
+ LOOP_THROUGH(struct node_if, interface, interfaces,
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf(" %s", interface->ifname);
+ if (interface->not) {
+ yyerror("skip on ! <interface> is not supported");
+ errs++;
+ } else
+ errs += pfctl_set_interface_flags(pf,
+ interface->ifname, PFI_IFLAG_SKIP, 1);
+ );
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf(" }\n");
+
+ FREE_LIST(struct node_if, interfaces);
+
+ if (errs)
+ return (1);
+ else
+ return (0);
+}
+
+#undef FREE_LIST
+#undef LOOP_THROUGH
+
+int
+check_rulestate(int desired_state)
+{
+ if (require_order && (rulestate > desired_state)) {
+ yyerror("Rules must be in order: options, normalization, "
+ "queueing, translation, filtering");
+ return (1);
+ }
+ rulestate = desired_state;
+ return (0);
+}
+
+int
+kw_cmp(const void *k, const void *e)
+{
+ return (strcmp(k, ((const struct keywords *)e)->k_name));
+}
+
+int
+lookup(char *s)
+{
+ /* this has to be sorted always */
+ static const struct keywords keywords[] = {
+ { "all", ALL},
+ { "allow-opts", ALLOWOPTS},
+ { "altq", ALTQ},
+ { "anchor", ANCHOR},
+ { "antispoof", ANTISPOOF},
+ { "any", ANY},
+ { "bandwidth", BANDWIDTH},
+ { "binat", BINAT},
+ { "binat-anchor", BINATANCHOR},
+ { "bitmask", BITMASK},
+ { "block", BLOCK},
+ { "block-policy", BLOCKPOLICY},
+ { "cbq", CBQ},
+ { "code", CODE},
+ { "crop", FRAGCROP},
+ { "debug", DEBUG},
+ { "divert-reply", DIVERTREPLY},
+ { "divert-to", DIVERTTO},
+ { "drop", DROP},
+ { "drop-ovl", FRAGDROP},
+ { "dup-to", DUPTO},
+ { "fastroute", FASTROUTE},
+ { "file", FILENAME},
+ { "fingerprints", FINGERPRINTS},
+ { "flags", FLAGS},
+ { "floating", FLOATING},
+ { "flush", FLUSH},
+ { "for", FOR},
+ { "fragment", FRAGMENT},
+ { "from", FROM},
+ { "global", GLOBAL},
+ { "group", GROUP},
+ { "hfsc", HFSC},
+ { "hostid", HOSTID},
+ { "icmp-type", ICMPTYPE},
+ { "icmp6-type", ICMP6TYPE},
+ { "if-bound", IFBOUND},
+ { "in", IN},
+ { "include", INCLUDE},
+ { "inet", INET},
+ { "inet6", INET6},
+ { "keep", KEEP},
+ { "label", LABEL},
+ { "limit", LIMIT},
+ { "linkshare", LINKSHARE},
+ { "load", LOAD},
+ { "log", LOG},
+ { "loginterface", LOGINTERFACE},
+ { "max", MAXIMUM},
+ { "max-mss", MAXMSS},
+ { "max-src-conn", MAXSRCCONN},
+ { "max-src-conn-rate", MAXSRCCONNRATE},
+ { "max-src-nodes", MAXSRCNODES},
+ { "max-src-states", MAXSRCSTATES},
+ { "min-ttl", MINTTL},
+ { "modulate", MODULATE},
+ { "nat", NAT},
+ { "nat-anchor", NATANCHOR},
+ { "no", NO},
+ { "no-df", NODF},
+ { "no-route", NOROUTE},
+ { "no-sync", NOSYNC},
+ { "on", ON},
+ { "optimization", OPTIMIZATION},
+ { "os", OS},
+ { "out", OUT},
+ { "overload", OVERLOAD},
+ { "pass", PASS},
+ { "port", PORT},
+ { "priority", PRIORITY},
+ { "priq", PRIQ},
+ { "probability", PROBABILITY},
+ { "proto", PROTO},
+ { "qlimit", QLIMIT},
+ { "queue", QUEUE},
+ { "quick", QUICK},
+ { "random", RANDOM},
+ { "random-id", RANDOMID},
+ { "rdr", RDR},
+ { "rdr-anchor", RDRANCHOR},
+ { "realtime", REALTIME},
+ { "reassemble", REASSEMBLE},
+ { "reply-to", REPLYTO},
+ { "require-order", REQUIREORDER},
+ { "return", RETURN},
+ { "return-icmp", RETURNICMP},
+ { "return-icmp6", RETURNICMP6},
+ { "return-rst", RETURNRST},
+ { "round-robin", ROUNDROBIN},
+ { "route", ROUTE},
+ { "route-to", ROUTETO},
+ { "rtable", RTABLE},
+ { "rule", RULE},
+ { "ruleset-optimization", RULESET_OPTIMIZATION},
+ { "scrub", SCRUB},
+ { "set", SET},
+ { "set-tos", SETTOS},
+ { "skip", SKIP},
+ { "sloppy", SLOPPY},
+ { "source-hash", SOURCEHASH},
+ { "source-track", SOURCETRACK},
+ { "state", STATE},
+ { "state-defaults", STATEDEFAULTS},
+ { "state-policy", STATEPOLICY},
+ { "static-port", STATICPORT},
+ { "sticky-address", STICKYADDRESS},
+ { "synproxy", SYNPROXY},
+ { "table", TABLE},
+ { "tag", TAG},
+ { "tagged", TAGGED},
+ { "tbrsize", TBRSIZE},
+ { "timeout", TIMEOUT},
+ { "to", TO},
+ { "tos", TOS},
+ { "ttl", TTL},
+ { "upperlimit", UPPERLIMIT},
+ { "urpf-failed", URPFFAILED},
+ { "user", USER},
+ };
+ const struct keywords *p;
+
+ p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ if (p) {
+ if (debug > 1)
+ fprintf(stderr, "%s: %d\n", s, p->k_val);
+ return (p->k_val);
+ } else {
+ if (debug > 1)
+ fprintf(stderr, "string: %s\n", s);
+ return (STRING);
+ }
+}
+
+#define MAXPUSHBACK 128
+
+char *parsebuf;
+int parseindex;
+char pushback_buffer[MAXPUSHBACK];
+int pushback_index = 0;
+
+int
+lgetc(int quotec)
+{
+ int c, next;
+
+ if (parsebuf) {
+ /* Read character from the parsebuffer instead of input. */
+ if (parseindex >= 0) {
+ c = parsebuf[parseindex++];
+ if (c != '\0')
+ return (c);
+ parsebuf = NULL;
+ } else
+ parseindex++;
+ }
+
+ if (pushback_index)
+ return (pushback_buffer[--pushback_index]);
+
+ if (quotec) {
+ if ((c = getc(file->stream)) == EOF) {
+ yyerror("reached end of file while parsing quoted string");
+ if (popfile() == EOF)
+ return (EOF);
+ return (quotec);
+ }
+ return (c);
+ }
+
+ while ((c = getc(file->stream)) == '\\') {
+ next = getc(file->stream);
+ if (next != '\n') {
+ c = next;
+ break;
+ }
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+
+ while (c == EOF) {
+ if (popfile() == EOF)
+ return (EOF);
+ c = getc(file->stream);
+ }
+ return (c);
+}
+
+int
+lungetc(int c)
+{
+ if (c == EOF)
+ return (EOF);
+ if (parsebuf) {
+ parseindex--;
+ if (parseindex >= 0)
+ return (c);
+ }
+ if (pushback_index < MAXPUSHBACK-1)
+ return (pushback_buffer[pushback_index++] = c);
+ else
+ return (EOF);
+}
+
+int
+findeol(void)
+{
+ int c;
+
+ parsebuf = NULL;
+
+ /* skip to either EOF or the first real EOL */
+ while (1) {
+ if (pushback_index)
+ c = pushback_buffer[--pushback_index];
+ else
+ c = lgetc(0);
+ if (c == '\n') {
+ file->lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ return (ERROR);
+}
+
+int
+yylex(void)
+{
+ char buf[8096];
+ char *p, *val;
+ int quotec, next, c;
+ int token;
+
+top:
+ p = buf;
+ while ((c = lgetc(0)) == ' ' || c == '\t')
+ ; /* nothing */
+
+ yylval.lineno = file->lineno;
+ if (c == '#')
+ while ((c = lgetc(0)) != '\n' && c != EOF)
+ ; /* nothing */
+ if (c == '$' && parsebuf == NULL) {
+ while (1) {
+ if ((c = lgetc(0)) == EOF)
+ return (0);
+
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ if (isalnum(c) || c == '_') {
+ *p++ = (char)c;
+ continue;
+ }
+ *p = '\0';
+ lungetc(c);
+ break;
+ }
+ val = symget(buf);
+ if (val == NULL) {
+ yyerror("macro '%s' not defined", buf);
+ return (findeol());
+ }
+ parsebuf = val;
+ parseindex = 0;
+ goto top;
+ }
+
+ switch (c) {
+ case '\'':
+ case '"':
+ quotec = c;
+ while (1) {
+ if ((c = lgetc(quotec)) == EOF)
+ return (0);
+ if (c == '\n') {
+ file->lineno++;
+ continue;
+ } else if (c == '\\') {
+ if ((next = lgetc(quotec)) == EOF)
+ return (0);
+ if (next == quotec || c == ' ' || c == '\t')
+ c = next;
+ else if (next == '\n')
+ continue;
+ else
+ lungetc(next);
+ } else if (c == quotec) {
+ *p = '\0';
+ break;
+ }
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ *p++ = (char)c;
+ }
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL)
+ err(1, "yylex: strdup");
+ return (STRING);
+ case '<':
+ next = lgetc(0);
+ if (next == '>') {
+ yylval.v.i = PF_OP_XRG;
+ return (PORTBINARY);
+ }
+ lungetc(next);
+ break;
+ case '>':
+ next = lgetc(0);
+ if (next == '<') {
+ yylval.v.i = PF_OP_IRG;
+ return (PORTBINARY);
+ }
+ lungetc(next);
+ break;
+ case '-':
+ next = lgetc(0);
+ if (next == '>')
+ return (ARROW);
+ lungetc(next);
+ break;
+ }
+
+#define allowed_to_end_number(x) \
+ (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
+
+ if (c == '-' || isdigit(c)) {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(0)) != EOF && isdigit(c));
+ lungetc(c);
+ if (p == buf + 1 && buf[0] == '-')
+ goto nodigits;
+ if (c == EOF || allowed_to_end_number(c)) {
+ const char *errstr = NULL;
+
+ *p = '\0';
+ yylval.v.number = strtonum(buf, LLONG_MIN,
+ LLONG_MAX, &errstr);
+ if (errstr) {
+ yyerror("\"%s\" invalid number: %s",
+ buf, errstr);
+ return (findeol());
+ }
+ return (NUMBER);
+ } else {
+nodigits:
+ while (p > buf + 1)
+ lungetc(*--p);
+ c = *--p;
+ if (c == '-')
+ return (c);
+ }
+ }
+
+#define allowed_in_string(x) \
+ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+ x != '{' && x != '}' && x != '<' && x != '>' && \
+ x != '!' && x != '=' && x != '/' && x != '#' && \
+ x != ','))
+
+ if (isalnum(c) || c == ':' || c == '_') {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
+ lungetc(c);
+ *p = '\0';
+ if ((token = lookup(buf)) == STRING)
+ if ((yylval.v.string = strdup(buf)) == NULL)
+ err(1, "yylex: strdup");
+ return (token);
+ }
+ if (c == '\n') {
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
+ if (c == EOF)
+ return (0);
+ return (c);
+}
+
+int
+check_file_secrecy(int fd, const char *fname)
+{
+ struct stat st;
+
+ if (fstat(fd, &st)) {
+ warn("cannot stat %s", fname);
+ return (-1);
+ }
+ if (st.st_uid != 0 && st.st_uid != getuid()) {
+ warnx("%s: owner not root or current user", fname);
+ return (-1);
+ }
+ if (st.st_mode & (S_IRWXG | S_IRWXO)) {
+ warnx("%s: group/world readable/writeable", fname);
+ return (-1);
+ }
+ return (0);
+}
+
+struct file *
+pushfile(const char *name, int secret)
+{
+ struct file *nfile;
+
+ if ((nfile = calloc(1, sizeof(struct file))) == NULL ||
+ (nfile->name = strdup(name)) == NULL) {
+ warn("malloc");
+ return (NULL);
+ }
+ if (TAILQ_FIRST(&files) == NULL && strcmp(nfile->name, "-") == 0) {
+ nfile->stream = stdin;
+ free(nfile->name);
+ if ((nfile->name = strdup("stdin")) == NULL) {
+ warn("strdup");
+ free(nfile);
+ return (NULL);
+ }
+ } else if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
+ warn("%s", nfile->name);
+ free(nfile->name);
+ free(nfile);
+ return (NULL);
+ } else if (secret &&
+ check_file_secrecy(fileno(nfile->stream), nfile->name)) {
+ fclose(nfile->stream);
+ free(nfile->name);
+ free(nfile);
+ return (NULL);
+ }
+ nfile->lineno = 1;
+ TAILQ_INSERT_TAIL(&files, nfile, entry);
+ return (nfile);
+}
+
+int
+popfile(void)
+{
+ struct file *prev;
+
+ if ((prev = TAILQ_PREV(file, files, entry)) != NULL) {
+ prev->errors += file->errors;
+ TAILQ_REMOVE(&files, file, entry);
+ fclose(file->stream);
+ free(file->name);
+ free(file);
+ file = prev;
+ return (0);
+ }
+ return (EOF);
+}
+
+int
+parse_config(char *filename, struct pfctl *xpf)
+{
+ int errors = 0;
+ struct sym *sym;
+
+ pf = xpf;
+ errors = 0;
+ rulestate = PFCTL_STATE_NONE;
+ returnicmpdefault = (ICMP_UNREACH << 8) | ICMP_UNREACH_PORT;
+ returnicmp6default =
+ (ICMP6_DST_UNREACH << 8) | ICMP6_DST_UNREACH_NOPORT;
+ blockpolicy = PFRULE_DROP;
+ require_order = 1;
+
+ if ((file = pushfile(filename, 0)) == NULL) {
+ warn("cannot open the main config file!");
+ return (-1);
+ }
+
+ yyparse();
+ errors = file->errors;
+ popfile();
+
+ /* Free macros and check which have not been used. */
+ while ((sym = TAILQ_FIRST(&symhead))) {
+ if ((pf->opts & PF_OPT_VERBOSE2) && !sym->used)
+ fprintf(stderr, "warning: macro '%s' not "
+ "used\n", sym->nam);
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entry);
+ free(sym);
+ }
+
+ return (errors ? -1 : 0);
+}
+
+int
+symset(const char *nam, const char *val, int persist)
+{
+ struct sym *sym;
+
+ for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
+ sym = TAILQ_NEXT(sym, entry))
+ ; /* nothing */
+
+ if (sym != NULL) {
+ if (sym->persist == 1)
+ return (0);
+ else {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entry);
+ free(sym);
+ }
+ }
+ if ((sym = calloc(1, sizeof(*sym))) == NULL)
+ return (-1);
+
+ sym->nam = strdup(nam);
+ if (sym->nam == NULL) {
+ free(sym);
+ return (-1);
+ }
+ sym->val = strdup(val);
+ if (sym->val == NULL) {
+ free(sym->nam);
+ free(sym);
+ return (-1);
+ }
+ sym->used = 0;
+ sym->persist = persist;
+ TAILQ_INSERT_TAIL(&symhead, sym, entry);
+ return (0);
+}
+
+int
+pfctl_cmdline_symset(char *s)
+{
+ char *sym, *val;
+ int ret;
+
+ if ((val = strrchr(s, '=')) == NULL)
+ return (-1);
+
+ if ((sym = malloc(strlen(s) - strlen(val) + 1)) == NULL)
+ err(1, "pfctl_cmdline_symset: malloc");
+
+ strlcpy(sym, s, strlen(s) - strlen(val) + 1);
+
+ ret = symset(sym, val + 1, 1);
+ free(sym);
+
+ return (ret);
+}
+
+char *
+symget(const char *nam)
+{
+ struct sym *sym;
+
+ TAILQ_FOREACH(sym, &symhead, entry)
+ if (strcmp(nam, sym->nam) == 0) {
+ sym->used = 1;
+ return (sym->val);
+ }
+ return (NULL);
+}
+
+void
+mv_rules(struct pf_ruleset *src, struct pf_ruleset *dst)
+{
+ int i;
+ struct pf_rule *r;
+
+ for (i = 0; i < PF_RULESET_MAX; ++i) {
+ while ((r = TAILQ_FIRST(src->rules[i].active.ptr))
+ != NULL) {
+ TAILQ_REMOVE(src->rules[i].active.ptr, r, entries);
+ TAILQ_INSERT_TAIL(dst->rules[i].active.ptr, r, entries);
+ dst->anchor->match++;
+ }
+ src->anchor->match = 0;
+ while ((r = TAILQ_FIRST(src->rules[i].inactive.ptr))
+ != NULL) {
+ TAILQ_REMOVE(src->rules[i].inactive.ptr, r, entries);
+ TAILQ_INSERT_TAIL(dst->rules[i].inactive.ptr,
+ r, entries);
+ }
+ }
+}
+
+void
+decide_address_family(struct node_host *n, sa_family_t *af)
+{
+ if (*af != 0 || n == NULL)
+ return;
+ *af = n->af;
+ while ((n = n->next) != NULL) {
+ if (n->af != *af) {
+ *af = 0;
+ return;
+ }
+ }
+}
+
+void
+remove_invalid_hosts(struct node_host **nh, sa_family_t *af)
+{
+ struct node_host *n = *nh, *prev = NULL;
+
+ while (n != NULL) {
+ if (*af && n->af && n->af != *af) {
+ /* unlink and free n */
+ struct node_host *next = n->next;
+
+ /* adjust tail pointer */
+ if (n == (*nh)->tail)
+ (*nh)->tail = prev;
+ /* adjust previous node's next pointer */
+ if (prev == NULL)
+ *nh = next;
+ else
+ prev->next = next;
+ /* free node */
+ if (n->ifname != NULL)
+ free(n->ifname);
+ free(n);
+ n = next;
+ } else {
+ if (n->af && !*af)
+ *af = n->af;
+ prev = n;
+ n = n->next;
+ }
+ }
+}
+
+int
+invalid_redirect(struct node_host *nh, sa_family_t af)
+{
+ if (!af) {
+ struct node_host *n;
+
+ /* tables and dyniftl are ok without an address family */
+ for (n = nh; n != NULL; n = n->next) {
+ if (n->addr.type != PF_ADDR_TABLE &&
+ n->addr.type != PF_ADDR_DYNIFTL) {
+ yyerror("address family not given and "
+ "translation address expands to multiple "
+ "address families");
+ return (1);
+ }
+ }
+ }
+ if (nh == NULL) {
+ yyerror("no translation address with matching address family "
+ "found.");
+ return (1);
+ }
+ return (0);
+}
+
+int
+atoul(char *s, u_long *ulvalp)
+{
+ u_long ulval;
+ char *ep;
+
+ errno = 0;
+ ulval = strtoul(s, &ep, 0);
+ if (s[0] == '\0' || *ep != '\0')
+ return (-1);
+ if (errno == ERANGE && ulval == ULONG_MAX)
+ return (-1);
+ *ulvalp = ulval;
+ return (0);
+}
+
+int
+getservice(char *n)
+{
+ struct servent *s;
+ u_long ulval;
+
+ if (atoul(n, &ulval) == 0) {
+ if (ulval > 65535) {
+ yyerror("illegal port value %lu", ulval);
+ return (-1);
+ }
+ return (htons(ulval));
+ } else {
+ s = getservbyname(n, "tcp");
+ if (s == NULL)
+ s = getservbyname(n, "udp");
+ if (s == NULL) {
+ yyerror("unknown port %s", n);
+ return (-1);
+ }
+ return (s->s_port);
+ }
+}
+
+int
+rule_label(struct pf_rule *r, char *s)
+{
+ if (s) {
+ if (strlcpy(r->label, s, sizeof(r->label)) >=
+ sizeof(r->label)) {
+ yyerror("rule label too long (max %d chars)",
+ sizeof(r->label)-1);
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+u_int16_t
+parseicmpspec(char *w, sa_family_t af)
+{
+ const struct icmpcodeent *p;
+ u_long ulval;
+ u_int8_t icmptype;
+
+ if (af == AF_INET)
+ icmptype = returnicmpdefault >> 8;
+ else
+ icmptype = returnicmp6default >> 8;
+
+ if (atoul(w, &ulval) == -1) {
+ if ((p = geticmpcodebyname(icmptype, w, af)) == NULL) {
+ yyerror("unknown icmp code %s", w);
+ return (0);
+ }
+ ulval = p->code;
+ }
+ if (ulval > 255) {
+ yyerror("invalid icmp code %lu", ulval);
+ return (0);
+ }
+ return (icmptype << 8 | ulval);
+}
+
+int
+parseport(char *port, struct range *r, int extensions)
+{
+ char *p = strchr(port, ':');
+
+ if (p == NULL) {
+ if ((r->a = getservice(port)) == -1)
+ return (-1);
+ r->b = 0;
+ r->t = PF_OP_NONE;
+ return (0);
+ }
+ if ((extensions & PPORT_STAR) && !strcmp(p+1, "*")) {
+ *p = 0;
+ if ((r->a = getservice(port)) == -1)
+ return (-1);
+ r->b = 0;
+ r->t = PF_OP_IRG;
+ return (0);
+ }
+ if ((extensions & PPORT_RANGE)) {
+ *p++ = 0;
+ if ((r->a = getservice(port)) == -1 ||
+ (r->b = getservice(p)) == -1)
+ return (-1);
+ if (r->a == r->b) {
+ r->b = 0;
+ r->t = PF_OP_NONE;
+ } else
+ r->t = PF_OP_RRG;
+ return (0);
+ }
+ return (-1);
+}
+
+int
+pfctl_load_anchors(int dev, struct pfctl *pf, struct pfr_buffer *trans)
+{
+ struct loadanchors *la;
+
+ TAILQ_FOREACH(la, &loadanchorshead, entries) {
+ if (pf->opts & PF_OPT_VERBOSE)
+ fprintf(stderr, "\nLoading anchor %s from %s\n",
+ la->anchorname, la->filename);
+ if (pfctl_rules(dev, la->filename, pf->opts, pf->optimize,
+ la->anchorname, trans) == -1)
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+rt_tableid_max(void)
+{
+#ifdef __FreeBSD__
+ int fibs;
+ size_t l = sizeof(fibs);
+
+ if (sysctlbyname("net.fibs", &fibs, &l, NULL, 0) == -1)
+ fibs = 16; /* XXX RT_MAXFIBS, at least limit it some. */
+ /*
+ * As the OpenBSD code only compares > and not >= we need to adjust
+ * here given we only accept values of 0..n and want to avoid #ifdefs
+ * in the grammer.
+ */
+ return (fibs - 1);
+#else
+ return (RT_TABLEID_MAX);
+#endif
+}
diff --git a/sbin/pfctl/pf_print_state.c b/sbin/pfctl/pf_print_state.c
new file mode 100644
index 000000000000..d4a2c1d2939b
--- /dev/null
+++ b/sbin/pfctl/pf_print_state.c
@@ -0,0 +1,362 @@
+/* $OpenBSD: pf_print_state.c,v 1.52 2008/08/12 16:40:18 david Exp $ */
+
+/*
+ * Copyright (c) 2001 Daniel Hartmeier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/endian.h>
+#include <net/if.h>
+#define TCPSTATES
+#include <netinet/tcp_fsm.h>
+#include <net/pfvar.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "pfctl_parser.h"
+#include "pfctl.h"
+
+void print_name(struct pf_addr *, sa_family_t);
+
+void
+print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose)
+{
+ switch (addr->type) {
+ case PF_ADDR_DYNIFTL:
+ printf("(%s", addr->v.ifname);
+ if (addr->iflags & PFI_AFLAG_NETWORK)
+ printf(":network");
+ if (addr->iflags & PFI_AFLAG_BROADCAST)
+ printf(":broadcast");
+ if (addr->iflags & PFI_AFLAG_PEER)
+ printf(":peer");
+ if (addr->iflags & PFI_AFLAG_NOALIAS)
+ printf(":0");
+ if (verbose) {
+ if (addr->p.dyncnt <= 0)
+ printf(":*");
+ else
+ printf(":%d", addr->p.dyncnt);
+ }
+ printf(")");
+ break;
+ case PF_ADDR_TABLE:
+ if (verbose)
+ if (addr->p.tblcnt == -1)
+ printf("<%s:*>", addr->v.tblname);
+ else
+ printf("<%s:%d>", addr->v.tblname,
+ addr->p.tblcnt);
+ else
+ printf("<%s>", addr->v.tblname);
+ return;
+ case PF_ADDR_RANGE: {
+ char buf[48];
+
+ if (inet_ntop(af, &addr->v.a.addr, buf, sizeof(buf)) == NULL)
+ printf("?");
+ else
+ printf("%s", buf);
+ if (inet_ntop(af, &addr->v.a.mask, buf, sizeof(buf)) == NULL)
+ printf(" - ?");
+ else
+ printf(" - %s", buf);
+ break;
+ }
+ case PF_ADDR_ADDRMASK:
+ if (PF_AZERO(&addr->v.a.addr, AF_INET6) &&
+ PF_AZERO(&addr->v.a.mask, AF_INET6))
+ printf("any");
+ else {
+ char buf[48];
+
+ if (inet_ntop(af, &addr->v.a.addr, buf,
+ sizeof(buf)) == NULL)
+ printf("?");
+ else
+ printf("%s", buf);
+ }
+ break;
+ case PF_ADDR_NOROUTE:
+ printf("no-route");
+ return;
+ case PF_ADDR_URPFFAILED:
+ printf("urpf-failed");
+ return;
+ default:
+ printf("?");
+ return;
+ }
+
+ /* mask if not _both_ address and mask are zero */
+ if (addr->type != PF_ADDR_RANGE &&
+ !(PF_AZERO(&addr->v.a.addr, AF_INET6) &&
+ PF_AZERO(&addr->v.a.mask, AF_INET6))) {
+ int bits = unmask(&addr->v.a.mask, af);
+
+ if (bits != (af == AF_INET ? 32 : 128))
+ printf("/%d", bits);
+ }
+}
+
+void
+print_name(struct pf_addr *addr, sa_family_t af)
+{
+ char host[NI_MAXHOST];
+
+ strlcpy(host, "?", sizeof(host));
+ switch (af) {
+ case AF_INET: {
+ struct sockaddr_in sin;
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr = addr->v4;
+ getnameinfo((struct sockaddr *)&sin, sin.sin_len,
+ host, sizeof(host), NULL, 0, NI_NOFQDN);
+ break;
+ }
+ case AF_INET6: {
+ struct sockaddr_in6 sin6;
+
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = addr->v6;
+ getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
+ host, sizeof(host), NULL, 0, NI_NOFQDN);
+ break;
+ }
+ }
+ printf("%s", host);
+}
+
+void
+print_host(struct pf_addr *addr, u_int16_t port, sa_family_t af, int opts)
+{
+ if (opts & PF_OPT_USEDNS)
+ print_name(addr, af);
+ else {
+ struct pf_addr_wrap aw;
+
+ memset(&aw, 0, sizeof(aw));
+ aw.v.a.addr = *addr;
+ if (af == AF_INET)
+ aw.v.a.mask.addr32[0] = 0xffffffff;
+ else {
+ memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask));
+ af = AF_INET6;
+ }
+ print_addr(&aw, af, opts & PF_OPT_VERBOSE2);
+ }
+
+ if (port) {
+ if (af == AF_INET)
+ printf(":%u", ntohs(port));
+ else
+ printf("[%u]", ntohs(port));
+ }
+}
+
+void
+print_seq(struct pfsync_state_peer *p)
+{
+ if (p->seqdiff)
+ printf("[%u + %u](+%u)", ntohl(p->seqlo),
+ ntohl(p->seqhi) - ntohl(p->seqlo), ntohl(p->seqdiff));
+ else
+ printf("[%u + %u]", ntohl(p->seqlo),
+ ntohl(p->seqhi) - ntohl(p->seqlo));
+}
+
+void
+print_state(struct pfsync_state *s, int opts)
+{
+ struct pfsync_state_peer *src, *dst;
+ struct pfsync_state_key *sk, *nk;
+ struct protoent *p;
+ int min, sec;
+
+ if (s->direction == PF_OUT) {
+ src = &s->src;
+ dst = &s->dst;
+ sk = &s->key[PF_SK_STACK];
+ nk = &s->key[PF_SK_WIRE];
+ if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6)
+ sk->port[0] = nk->port[0];
+ } else {
+ src = &s->dst;
+ dst = &s->src;
+ sk = &s->key[PF_SK_WIRE];
+ nk = &s->key[PF_SK_STACK];
+ if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6)
+ sk->port[1] = nk->port[1];
+ }
+ printf("%s ", s->ifname);
+ if ((p = getprotobynumber(s->proto)) != NULL)
+ printf("%s ", p->p_name);
+ else
+ printf("%u ", s->proto);
+
+ print_host(&nk->addr[1], nk->port[1], s->af, opts);
+ if (PF_ANEQ(&nk->addr[1], &sk->addr[1], s->af) ||
+ nk->port[1] != sk->port[1]) {
+ printf(" (");
+ print_host(&sk->addr[1], sk->port[1], s->af, opts);
+ printf(")");
+ }
+ if (s->direction == PF_OUT)
+ printf(" -> ");
+ else
+ printf(" <- ");
+ print_host(&nk->addr[0], nk->port[0], s->af, opts);
+ if (PF_ANEQ(&nk->addr[0], &sk->addr[0], s->af) ||
+ nk->port[0] != sk->port[0]) {
+ printf(" (");
+ print_host(&sk->addr[0], sk->port[0], s->af, opts);
+ printf(")");
+ }
+
+ printf(" ");
+ if (s->proto == IPPROTO_TCP) {
+ if (src->state <= TCPS_TIME_WAIT &&
+ dst->state <= TCPS_TIME_WAIT)
+ printf(" %s:%s\n", tcpstates[src->state],
+ tcpstates[dst->state]);
+ else if (src->state == PF_TCPS_PROXY_SRC ||
+ dst->state == PF_TCPS_PROXY_SRC)
+ printf(" PROXY:SRC\n");
+ else if (src->state == PF_TCPS_PROXY_DST ||
+ dst->state == PF_TCPS_PROXY_DST)
+ printf(" PROXY:DST\n");
+ else
+ printf(" <BAD STATE LEVELS %u:%u>\n",
+ src->state, dst->state);
+ if (opts & PF_OPT_VERBOSE) {
+ printf(" ");
+ print_seq(src);
+ if (src->wscale && dst->wscale)
+ printf(" wscale %u",
+ src->wscale & PF_WSCALE_MASK);
+ printf(" ");
+ print_seq(dst);
+ if (src->wscale && dst->wscale)
+ printf(" wscale %u",
+ dst->wscale & PF_WSCALE_MASK);
+ printf("\n");
+ }
+ } else if (s->proto == IPPROTO_UDP && src->state < PFUDPS_NSTATES &&
+ dst->state < PFUDPS_NSTATES) {
+ const char *states[] = PFUDPS_NAMES;
+
+ printf(" %s:%s\n", states[src->state], states[dst->state]);
+ } else if (s->proto != IPPROTO_ICMP && src->state < PFOTHERS_NSTATES &&
+ dst->state < PFOTHERS_NSTATES) {
+ /* XXX ICMP doesn't really have state levels */
+ const char *states[] = PFOTHERS_NAMES;
+
+ printf(" %s:%s\n", states[src->state], states[dst->state]);
+ } else {
+ printf(" %u:%u\n", src->state, dst->state);
+ }
+
+ if (opts & PF_OPT_VERBOSE) {
+ u_int64_t packets[2];
+ u_int64_t bytes[2];
+ u_int32_t creation = ntohl(s->creation);
+ u_int32_t expire = ntohl(s->expire);
+
+ sec = creation % 60;
+ creation /= 60;
+ min = creation % 60;
+ creation /= 60;
+ printf(" age %.2u:%.2u:%.2u", creation, min, sec);
+ sec = expire % 60;
+ expire /= 60;
+ min = expire % 60;
+ expire /= 60;
+ printf(", expires in %.2u:%.2u:%.2u", expire, min, sec);
+
+ bcopy(s->packets[0], &packets[0], sizeof(u_int64_t));
+ bcopy(s->packets[1], &packets[1], sizeof(u_int64_t));
+ bcopy(s->bytes[0], &bytes[0], sizeof(u_int64_t));
+ bcopy(s->bytes[1], &bytes[1], sizeof(u_int64_t));
+ printf(", %ju:%ju pkts, %ju:%ju bytes",
+ (uintmax_t )be64toh(packets[0]),
+ (uintmax_t )be64toh(packets[1]),
+ (uintmax_t )be64toh(bytes[0]),
+ (uintmax_t )be64toh(bytes[1]));
+ if (ntohl(s->anchor) != -1)
+ printf(", anchor %u", ntohl(s->anchor));
+ if (ntohl(s->rule) != -1)
+ printf(", rule %u", ntohl(s->rule));
+ if (s->state_flags & PFSTATE_SLOPPY)
+ printf(", sloppy");
+ if (s->sync_flags & PFSYNC_FLAG_SRCNODE)
+ printf(", source-track");
+ if (s->sync_flags & PFSYNC_FLAG_NATSRCNODE)
+ printf(", sticky-address");
+ printf("\n");
+ }
+ if (opts & PF_OPT_VERBOSE2) {
+ u_int64_t id;
+
+ bcopy(&s->id, &id, sizeof(u_int64_t));
+ printf(" id: %016jx creatorid: %08x",
+ (uintmax_t )be64toh(id), ntohl(s->creatorid));
+ printf("\n");
+ }
+}
+
+int
+unmask(struct pf_addr *m, sa_family_t af)
+{
+ int i = 31, j = 0, b = 0;
+ u_int32_t tmp;
+
+ while (j < 4 && m->addr32[j] == 0xffffffff) {
+ b += 32;
+ j++;
+ }
+ if (j < 4) {
+ tmp = ntohl(m->addr32[j]);
+ for (i = 31; tmp & (1 << i); --i)
+ b++;
+ }
+ return (b);
+}
diff --git a/sbin/pfctl/pfctl.8 b/sbin/pfctl/pfctl.8
new file mode 100644
index 000000000000..5c0e7b37f4de
--- /dev/null
+++ b/sbin/pfctl/pfctl.8
@@ -0,0 +1,687 @@
+.\" $OpenBSD: pfctl.8,v 1.138 2008/06/10 20:55:02 mcbride Exp $
+.\"
+.\" Copyright (c) 2001 Kjell Wooding. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 21, 2011
+.Dt PFCTL 8
+.Os
+.Sh NAME
+.Nm pfctl
+.Nd control the packet filter (PF) device
+.Sh SYNOPSIS
+.Nm pfctl
+.Bk -words
+.Op Fl AdeghmNnOPqRrvz
+.Op Fl a Ar anchor
+.Oo Fl D Ar macro Ns =
+.Ar value Oc
+.Op Fl F Ar modifier
+.Op Fl f Ar file
+.Op Fl i Ar interface
+.Op Fl K Ar host | network
+.Xo
+.Oo Fl k
+.Ar host | network | label | id
+.Oc Xc
+.Op Fl o Ar level
+.Op Fl p Ar device
+.Op Fl s Ar modifier
+.Xo
+.Oo Fl t Ar table
+.Fl T Ar command
+.Op Ar address ...
+.Oc Xc
+.Op Fl x Ar level
+.Ek
+.Sh DESCRIPTION
+The
+.Nm
+utility communicates with the packet filter device using the
+ioctl interface described in
+.Xr pf 4 .
+It allows ruleset and parameter configuration and retrieval of status
+information from the packet filter.
+.Pp
+Packet filtering restricts the types of packets that pass through
+network interfaces entering or leaving the host based on filter
+rules as described in
+.Xr pf.conf 5 .
+The packet filter can also replace addresses and ports of packets.
+Replacing source addresses and ports of outgoing packets is called
+NAT (Network Address Translation) and is used to connect an internal
+network (usually reserved address space) to an external one (the
+Internet) by making all connections to external hosts appear to
+come from the gateway.
+Replacing destination addresses and ports of incoming packets
+is used to redirect connections to different hosts and/or ports.
+A combination of both translations, bidirectional NAT, is also
+supported.
+Translation rules are described in
+.Xr pf.conf 5 .
+.Pp
+When the variable
+.Va pf
+is set to
+.Dv YES
+in
+.Xr rc.conf 5 ,
+the rule file specified with the variable
+.Va pf_rules
+is loaded automatically by the
+.Xr rc 8
+scripts and the packet filter is enabled.
+.Pp
+The packet filter does not itself forward packets between interfaces.
+Forwarding can be enabled by setting the
+.Xr sysctl 8
+variables
+.Em net.inet.ip.forwarding
+and/or
+.Em net.inet6.ip6.forwarding
+to 1.
+Set them permanently in
+.Xr sysctl.conf 5 .
+.Pp
+The
+.Nm
+utility provides several commands.
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl A
+Load only the queue rules present in the rule file.
+Other rules and options are ignored.
+.It Fl a Ar anchor
+Apply flags
+.Fl f ,
+.Fl F ,
+and
+.Fl s
+only to the rules in the specified
+.Ar anchor .
+In addition to the main ruleset,
+.Nm
+can load and manipulate additional rulesets by name,
+called anchors.
+The main ruleset is the default anchor.
+.Pp
+Anchors are referenced by name and may be nested,
+with the various components of the anchor path separated by
+.Sq /
+characters, similar to how file system hierarchies are laid out.
+The last component of the anchor path is where ruleset operations are
+performed.
+.Pp
+Evaluation of
+.Ar anchor
+rules from the main ruleset is described in
+.Xr pf.conf 5 .
+.Pp
+For example, the following will show all filter rules (see the
+.Fl s
+flag below) inside the anchor
+.Dq authpf/smith(1234) ,
+which would have been created for user
+.Dq smith
+by
+.Xr authpf 8 ,
+PID 1234:
+.Bd -literal -offset indent
+# pfctl -a "authpf/smith(1234)" -s rules
+.Ed
+.Pp
+Private tables can also be put inside anchors, either by having table
+statements in the
+.Xr pf.conf 5
+file that is loaded in the anchor, or by using regular table commands, as in:
+.Bd -literal -offset indent
+# pfctl -a foo/bar -t mytable -T add 1.2.3.4 5.6.7.8
+.Ed
+.Pp
+When a rule referring to a table is loaded in an anchor, the rule will use the
+private table if one is defined, and then fall back to the table defined in the
+main ruleset, if there is one.
+This is similar to C rules for variable scope.
+It is possible to create distinct tables with the same name in the global
+ruleset and in an anchor, but this is often bad design and a warning will be
+issued in that case.
+.Pp
+By default, recursive inline printing of anchors applies only to unnamed
+anchors specified inline in the ruleset.
+If the anchor name is terminated with a
+.Sq *
+character, the
+.Fl s
+flag will recursively print all anchors in a brace delimited block.
+For example the following will print the
+.Dq authpf
+ruleset recursively:
+.Bd -literal -offset indent
+# pfctl -a 'authpf/*' -sr
+.Ed
+.Pp
+To print the main ruleset recursively, specify only
+.Sq *
+as the anchor name:
+.Bd -literal -offset indent
+# pfctl -a '*' -sr
+.Ed
+.It Fl D Ar macro Ns = Ns Ar value
+Define
+.Ar macro
+to be set to
+.Ar value
+on the command line.
+Overrides the definition of
+.Ar macro
+in the ruleset.
+.It Fl d
+Disable the packet filter.
+.It Fl e
+Enable the packet filter.
+.It Fl F Ar modifier
+Flush the filter parameters specified by
+.Ar modifier
+(may be abbreviated):
+.Pp
+.Bl -tag -width xxxxxxxxxxxx -compact
+.It Fl F Cm nat
+Flush the NAT rules.
+.It Fl F Cm queue
+Flush the queue rules.
+.It Fl F Cm rules
+Flush the filter rules.
+.It Fl F Cm states
+Flush the state table (NAT and filter).
+.It Fl F Cm Sources
+Flush the source tracking table.
+.It Fl F Cm info
+Flush the filter information (statistics that are not bound to rules).
+.It Fl F Cm Tables
+Flush the tables.
+.It Fl F Cm osfp
+Flush the passive operating system fingerprints.
+.It Fl F Cm all
+Flush all of the above.
+.El
+.It Fl f Ar file
+Load the rules contained in
+.Ar file .
+This
+.Ar file
+may contain macros, tables, options, and normalization, queueing,
+translation, and filtering rules.
+With the exception of macros and tables, the statements must appear in that
+order.
+.It Fl g
+Include output helpful for debugging.
+.It Fl h
+Help.
+.It Fl i Ar interface
+Restrict the operation to the given
+.Ar interface .
+.It Fl K Ar host | network
+Kill all of the source tracking entries originating from the specified
+.Ar host
+or
+.Ar network .
+A second
+.Fl K Ar host
+or
+.Fl K Ar network
+option may be specified, which will kill all the source tracking
+entries from the first host/network to the second.
+.It Xo
+.Fl k
+.Ar host | network | label | id
+.Xc
+Kill all of the state entries matching the specified
+.Ar host ,
+.Ar network ,
+.Ar label ,
+or
+.Ar id .
+.Pp
+For example, to kill all of the state entries originating from
+.Dq host :
+.Pp
+.Dl # pfctl -k host
+.Pp
+A second
+.Fl k Ar host
+or
+.Fl k Ar network
+option may be specified, which will kill all the state entries
+from the first host/network to the second.
+To kill all of the state entries from
+.Dq host1
+to
+.Dq host2 :
+.Pp
+.Dl # pfctl -k host1 -k host2
+.Pp
+To kill all states originating from 192.168.1.0/24 to 172.16.0.0/16:
+.Pp
+.Dl # pfctl -k 192.168.1.0/24 -k 172.16.0.0/16
+.Pp
+A network prefix length of 0 can be used as a wildcard.
+To kill all states with the target
+.Dq host2 :
+.Pp
+.Dl # pfctl -k 0.0.0.0/0 -k host2
+.Pp
+It is also possible to kill states by rule label or state ID.
+In this mode the first
+.Fl k
+argument is used to specify the type
+of the second argument.
+The following command would kill all states that have been created
+from rules carrying the label
+.Dq foobar :
+.Pp
+.Dl # pfctl -k label -k foobar
+.Pp
+To kill one specific state by its unique state ID
+(as shown by pfctl -s state -vv),
+use the
+.Ar id
+modifier and as a second argument the state ID and optional creator ID.
+To kill a state with ID 4823e84500000003 use:
+.Pp
+.Dl # pfctl -k id -k 4823e84500000003
+.Pp
+To kill a state with ID 4823e84500000018 created from a backup
+firewall with hostid 00000002 use:
+.Pp
+.Dl # pfctl -k id -k 4823e84500000018/2
+.Pp
+.It Fl m
+Merge in explicitly given options without resetting those
+which are omitted.
+Allows single options to be modified without disturbing the others:
+.Bd -literal -offset indent
+# echo "set loginterface fxp0" | pfctl -mf -
+.Ed
+.It Fl N
+Load only the NAT rules present in the rule file.
+Other rules and options are ignored.
+.It Fl n
+Do not actually load rules, just parse them.
+.It Fl O
+Load only the options present in the rule file.
+Other rules and options are ignored.
+.It Fl o Ar level
+Control the ruleset optimizer, overriding any rule file settings.
+.Pp
+.Bl -tag -width xxxxxxxxxxxx -compact
+.It Fl o Cm none
+Disable the ruleset optimizer.
+.It Fl o Cm basic
+Enable basic ruleset optimizations.
+This is the default behaviour.
+.It Fl o Cm profile
+Enable basic ruleset optimizations with profiling.
+.El
+For further information on the ruleset optimizer, see
+.Xr pf.conf 5 .
+.It Fl P
+Do not perform service name lookup for port specific rules,
+instead display the ports numerically.
+.It Fl p Ar device
+Use the device file
+.Ar device
+instead of the default
+.Pa /dev/pf .
+.It Fl q
+Only print errors and warnings.
+.It Fl R
+Load only the filter rules present in the rule file.
+Other rules and options are ignored.
+.It Fl r
+Perform reverse DNS lookups on states when displaying them.
+.It Fl s Ar modifier
+Show the filter parameters specified by
+.Ar modifier
+(may be abbreviated):
+.Pp
+.Bl -tag -width xxxxxxxxxxxxx -compact
+.It Fl s Cm nat
+Show the currently loaded NAT rules.
+.It Fl s Cm queue
+Show the currently loaded queue rules.
+When used together with
+.Fl v ,
+per-queue statistics are also shown.
+When used together with
+.Fl v v ,
+.Nm
+will loop and show updated queue statistics every five seconds, including
+measured bandwidth and packets per second.
+.It Fl s Cm rules
+Show the currently loaded filter rules.
+When used together with
+.Fl v ,
+the per-rule statistics (number of evaluations,
+packets and bytes) are also shown.
+Note that the
+.Dq skip step
+optimization done automatically by the kernel
+will skip evaluation of rules where possible.
+Packets passed statefully are counted in the rule that created the state
+(even though the rule isn't evaluated more than once for the entire
+connection).
+.It Fl s Cm Anchors
+Show the currently loaded anchors directly attached to the main ruleset.
+If
+.Fl a Ar anchor
+is specified as well, the anchors loaded directly below the given
+.Ar anchor
+are shown instead.
+If
+.Fl v
+is specified, all anchors attached under the target anchor will be
+displayed recursively.
+.It Fl s Cm states
+Show the contents of the state table.
+.It Fl s Cm Sources
+Show the contents of the source tracking table.
+.It Fl s Cm info
+Show filter information (statistics and counters).
+When used together with
+.Fl v ,
+source tracking statistics are also shown.
+.It Fl s Cm labels
+Show per-rule statistics (label, evaluations, packets total, bytes total,
+packets in, bytes in, packets out, bytes out, state creations) of
+filter rules with labels, useful for accounting.
+.It Fl s Cm timeouts
+Show the current global timeouts.
+.It Fl s Cm memory
+Show the current pool memory hard limits.
+.It Fl s Cm Tables
+Show the list of tables.
+.It Fl s Cm osfp
+Show the list of operating system fingerprints.
+.It Fl s Cm Interfaces
+Show the list of interfaces and interface drivers available to PF.
+When used together with
+.Fl v ,
+it additionally lists which interfaces have skip rules activated.
+When used together with
+.Fl vv ,
+interface statistics are also shown.
+.Fl i
+can be used to select an interface or a group of interfaces.
+.It Fl s Cm all
+Show all of the above, except for the lists of interfaces and operating
+system fingerprints.
+.El
+.It Fl T Ar command Op Ar address ...
+Specify the
+.Ar command
+(may be abbreviated) to apply to the table.
+Commands include:
+.Pp
+.Bl -tag -width xxxxxxxxxxxx -compact
+.It Fl T Cm kill
+Kill a table.
+.It Fl T Cm flush
+Flush all addresses of a table.
+.It Fl T Cm add
+Add one or more addresses in a table.
+Automatically create a nonexisting table.
+.It Fl T Cm delete
+Delete one or more addresses from a table.
+.It Fl T Cm expire Ar number
+Delete addresses which had their statistics cleared more than
+.Ar number
+seconds ago.
+For entries which have never had their statistics cleared,
+.Ar number
+refers to the time they were added to the table.
+.It Fl T Cm replace
+Replace the addresses of the table.
+Automatically create a nonexisting table.
+.It Fl T Cm show
+Show the content (addresses) of a table.
+.It Fl T Cm test
+Test if the given addresses match a table.
+.It Fl T Cm zero
+Clear all the statistics of a table.
+.It Fl T Cm load
+Load only the table definitions from
+.Xr pf.conf 5 .
+This is used in conjunction with the
+.Fl f
+flag, as in:
+.Bd -literal -offset indent
+# pfctl -Tl -f pf.conf
+.Ed
+.El
+.Pp
+For the
+.Cm add ,
+.Cm delete ,
+.Cm replace ,
+and
+.Cm test
+commands, the list of addresses can be specified either directly on the command
+line and/or in an unformatted text file, using the
+.Fl f
+flag.
+Comments starting with a
+.Sq #
+are allowed in the text file.
+With these commands, the
+.Fl v
+flag can also be used once or twice, in which case
+.Nm
+will print the
+detailed result of the operation for each individual address, prefixed by
+one of the following letters:
+.Pp
+.Bl -tag -width XXX -compact
+.It A
+The address/network has been added.
+.It C
+The address/network has been changed (negated).
+.It D
+The address/network has been deleted.
+.It M
+The address matches
+.Po
+.Cm test
+operation only
+.Pc .
+.It X
+The address/network is duplicated and therefore ignored.
+.It Y
+The address/network cannot be added/deleted due to conflicting
+.Sq \&!
+attributes.
+.It Z
+The address/network has been cleared (statistics).
+.El
+.Pp
+Each table can maintain a set of counters that can be retrieved using the
+.Fl v
+flag of
+.Nm .
+For example, the following commands define a wide open firewall which will keep
+track of packets going to or coming from the
+.Ox
+FTP server.
+The following commands configure the firewall and send 10 pings to the FTP
+server:
+.Bd -literal -offset indent
+# printf "table <test> counters { ftp.openbsd.org }\en \e
+ pass out to <test>\en" | pfctl -f-
+# ping -qc10 ftp.openbsd.org
+.Ed
+.Pp
+We can now use the table
+.Cm show
+command to output, for each address and packet direction, the number of packets
+and bytes that are being passed or blocked by rules referencing the table.
+The time at which the current accounting started is also shown with the
+.Dq Cleared
+line.
+.Bd -literal -offset indent
+# pfctl -t test -vTshow
+ 129.128.5.191
+ Cleared: Thu Feb 13 18:55:18 2003
+ In/Block: [ Packets: 0 Bytes: 0 ]
+ In/Pass: [ Packets: 10 Bytes: 840 ]
+ Out/Block: [ Packets: 0 Bytes: 0 ]
+ Out/Pass: [ Packets: 10 Bytes: 840 ]
+.Ed
+.Pp
+Similarly, it is possible to view global information about the tables
+by using the
+.Fl v
+modifier twice and the
+.Fl s
+.Cm Tables
+command.
+This will display the number of addresses on each table,
+the number of rules which reference the table, and the global
+packet statistics for the whole table:
+.Bd -literal -offset indent
+# pfctl -vvsTables
+--a-r-C test
+ Addresses: 1
+ Cleared: Thu Feb 13 18:55:18 2003
+ References: [ Anchors: 0 Rules: 1 ]
+ Evaluations: [ NoMatch: 3496 Match: 1 ]
+ In/Block: [ Packets: 0 Bytes: 0 ]
+ In/Pass: [ Packets: 10 Bytes: 840 ]
+ In/XPass: [ Packets: 0 Bytes: 0 ]
+ Out/Block: [ Packets: 0 Bytes: 0 ]
+ Out/Pass: [ Packets: 10 Bytes: 840 ]
+ Out/XPass: [ Packets: 0 Bytes: 0 ]
+.Ed
+.Pp
+As we can see here, only one packet \- the initial ping request \- matched the
+table, but all packets passing as the result of the state are correctly
+accounted for.
+Reloading the table(s) or ruleset will not affect packet accounting in any way.
+The two
+.Dq XPass
+counters are incremented instead of the
+.Dq Pass
+counters when a
+.Dq stateful
+packet is passed but doesn't match the table anymore.
+This will happen in our example if someone flushes the table while the
+.Xr ping 8
+command is running.
+.Pp
+When used with a single
+.Fl v ,
+.Nm
+will only display the first line containing the table flags and name.
+The flags are defined as follows:
+.Pp
+.Bl -tag -width XXX -compact
+.It c
+For constant tables, which cannot be altered outside
+.Xr pf.conf 5 .
+.It p
+For persistent tables, which don't get automatically killed when no rules
+refer to them.
+.It a
+For tables which are part of the
+.Em active
+tableset.
+Tables without this flag do not really exist, cannot contain addresses, and are
+only listed if the
+.Fl g
+flag is given.
+.It i
+For tables which are part of the
+.Em inactive
+tableset.
+This flag can only be witnessed briefly during the loading of
+.Xr pf.conf 5 .
+.It r
+For tables which are referenced (used) by rules.
+.It h
+This flag is set when a table in the main ruleset is hidden by one or more
+tables of the same name from anchors attached below it.
+.It C
+This flag is set when per-address counters are enabled on the table.
+.El
+.It Fl t Ar table
+Specify the name of the table.
+.It Fl v
+Produce more verbose output.
+A second use of
+.Fl v
+will produce even more verbose output including ruleset warnings.
+See the previous section for its effect on table commands.
+.It Fl x Ar level
+Set the debug
+.Ar level
+(may be abbreviated) to one of the following:
+.Pp
+.Bl -tag -width xxxxxxxxxxxx -compact
+.It Fl x Cm none
+Don't generate debug messages.
+.It Fl x Cm urgent
+Generate debug messages only for serious errors.
+.It Fl x Cm misc
+Generate debug messages for various errors.
+.It Fl x Cm loud
+Generate debug messages for common conditions.
+.El
+.It Fl z
+Clear per-rule statistics.
+.El
+.Sh FILES
+.Bl -tag -width "/etc/pf.conf" -compact
+.It Pa /etc/pf.conf
+Packet filter rules file.
+.It Pa /etc/pf.os
+Passive operating system fingerprint database.
+.El
+.Sh SEE ALSO
+.Xr pf 4 ,
+.Xr pf.conf 5 ,
+.Xr pf.os 5 ,
+.Xr rc.conf 5 ,
+.Xr services 5 ,
+.Xr sysctl.conf 5 ,
+.Xr authpf 8 ,
+.Xr ftp-proxy 8 ,
+.Xr rc 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+program and the
+.Xr pf 4
+filter mechanism first appeared in
+.Ox 3.0 .
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
new file mode 100644
index 000000000000..90a2bb5b7c7d
--- /dev/null
+++ b/sbin/pfctl/pfctl.c
@@ -0,0 +1,2391 @@
+/* $OpenBSD: pfctl.c,v 1.278 2008/08/31 20:18:17 jmc Exp $ */
+
+/*
+ * Copyright (c) 2001 Daniel Hartmeier
+ * Copyright (c) 2002,2003 Henning Brauer
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#ifdef __FreeBSD__
+#include <sys/endian.h>
+#endif
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <net/pfvar.h>
+#include <arpa/inet.h>
+#include <altq/altq.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pfctl_parser.h"
+#include "pfctl.h"
+
+void usage(void);
+int pfctl_enable(int, int);
+int pfctl_disable(int, int);
+int pfctl_clear_stats(int, int);
+int pfctl_clear_interface_flags(int, int);
+int pfctl_clear_rules(int, int, char *);
+int pfctl_clear_nat(int, int, char *);
+int pfctl_clear_altq(int, int);
+int pfctl_clear_src_nodes(int, int);
+int pfctl_clear_states(int, const char *, int);
+void pfctl_addrprefix(char *, struct pf_addr *);
+int pfctl_kill_src_nodes(int, const char *, int);
+int pfctl_net_kill_states(int, const char *, int);
+int pfctl_label_kill_states(int, const char *, int);
+int pfctl_id_kill_states(int, const char *, int);
+void pfctl_init_options(struct pfctl *);
+int pfctl_load_options(struct pfctl *);
+int pfctl_load_limit(struct pfctl *, unsigned int, unsigned int);
+int pfctl_load_timeout(struct pfctl *, unsigned int, unsigned int);
+int pfctl_load_debug(struct pfctl *, unsigned int);
+int pfctl_load_logif(struct pfctl *, char *);
+int pfctl_load_hostid(struct pfctl *, unsigned int);
+int pfctl_get_pool(int, struct pf_pool *, u_int32_t, u_int32_t, int,
+ char *);
+void pfctl_print_rule_counters(struct pf_rule *, int);
+int pfctl_show_rules(int, char *, int, enum pfctl_show, char *, int);
+int pfctl_show_nat(int, int, char *);
+int pfctl_show_src_nodes(int, int);
+int pfctl_show_states(int, const char *, int);
+int pfctl_show_status(int, int);
+int pfctl_show_timeouts(int, int);
+int pfctl_show_limits(int, int);
+void pfctl_debug(int, u_int32_t, int);
+int pfctl_test_altqsupport(int, int);
+int pfctl_show_anchors(int, int, char *);
+int pfctl_ruleset_trans(struct pfctl *, char *, struct pf_anchor *);
+int pfctl_load_ruleset(struct pfctl *, char *,
+ struct pf_ruleset *, int, int);
+int pfctl_load_rule(struct pfctl *, char *, struct pf_rule *, int);
+const char *pfctl_lookup_option(char *, const char **);
+
+struct pf_anchor_global pf_anchors;
+struct pf_anchor pf_main_anchor;
+
+const char *clearopt;
+char *rulesopt;
+const char *showopt;
+const char *debugopt;
+char *anchoropt;
+const char *optiopt = NULL;
+char *pf_device = "/dev/pf";
+char *ifaceopt;
+char *tableopt;
+const char *tblcmdopt;
+int src_node_killers;
+char *src_node_kill[2];
+int state_killers;
+char *state_kill[2];
+int loadopt;
+int altqsupport;
+
+int dev = -1;
+int first_title = 1;
+int labels = 0;
+
+#define INDENT(d, o) do { \
+ if (o) { \
+ int i; \
+ for (i=0; i < d; i++) \
+ printf(" "); \
+ } \
+ } while (0); \
+
+
+static const struct {
+ const char *name;
+ int index;
+} pf_limits[] = {
+ { "states", PF_LIMIT_STATES },
+ { "src-nodes", PF_LIMIT_SRC_NODES },
+ { "frags", PF_LIMIT_FRAGS },
+ { "table-entries", PF_LIMIT_TABLE_ENTRIES },
+ { NULL, 0 }
+};
+
+struct pf_hint {
+ const char *name;
+ int timeout;
+};
+static const struct pf_hint pf_hint_normal[] = {
+ { "tcp.first", 2 * 60 },
+ { "tcp.opening", 30 },
+ { "tcp.established", 24 * 60 * 60 },
+ { "tcp.closing", 15 * 60 },
+ { "tcp.finwait", 45 },
+ { "tcp.closed", 90 },
+ { "tcp.tsdiff", 30 },
+ { NULL, 0 }
+};
+static const struct pf_hint pf_hint_satellite[] = {
+ { "tcp.first", 3 * 60 },
+ { "tcp.opening", 30 + 5 },
+ { "tcp.established", 24 * 60 * 60 },
+ { "tcp.closing", 15 * 60 + 5 },
+ { "tcp.finwait", 45 + 5 },
+ { "tcp.closed", 90 + 5 },
+ { "tcp.tsdiff", 60 },
+ { NULL, 0 }
+};
+static const struct pf_hint pf_hint_conservative[] = {
+ { "tcp.first", 60 * 60 },
+ { "tcp.opening", 15 * 60 },
+ { "tcp.established", 5 * 24 * 60 * 60 },
+ { "tcp.closing", 60 * 60 },
+ { "tcp.finwait", 10 * 60 },
+ { "tcp.closed", 3 * 60 },
+ { "tcp.tsdiff", 60 },
+ { NULL, 0 }
+};
+static const struct pf_hint pf_hint_aggressive[] = {
+ { "tcp.first", 30 },
+ { "tcp.opening", 5 },
+ { "tcp.established", 5 * 60 * 60 },
+ { "tcp.closing", 60 },
+ { "tcp.finwait", 30 },
+ { "tcp.closed", 30 },
+ { "tcp.tsdiff", 10 },
+ { NULL, 0 }
+};
+
+static const struct {
+ const char *name;
+ const struct pf_hint *hint;
+} pf_hints[] = {
+ { "normal", pf_hint_normal },
+ { "satellite", pf_hint_satellite },
+ { "high-latency", pf_hint_satellite },
+ { "conservative", pf_hint_conservative },
+ { "aggressive", pf_hint_aggressive },
+ { NULL, NULL }
+};
+
+static const char *clearopt_list[] = {
+ "nat", "queue", "rules", "Sources",
+ "states", "info", "Tables", "osfp", "all", NULL
+};
+
+static const char *showopt_list[] = {
+ "nat", "queue", "rules", "Anchors", "Sources", "states", "info",
+ "Interfaces", "labels", "timeouts", "memory", "Tables", "osfp",
+ "all", NULL
+};
+
+static const char *tblcmdopt_list[] = {
+ "kill", "flush", "add", "delete", "load", "replace", "show",
+ "test", "zero", "expire", NULL
+};
+
+static const char *debugopt_list[] = {
+ "none", "urgent", "misc", "loud", NULL
+};
+
+static const char *optiopt_list[] = {
+ "none", "basic", "profile", NULL
+};
+
+void
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-AdeghmNnOPqRrvz] ", __progname);
+ fprintf(stderr, "[-a anchor] [-D macro=value] [-F modifier]\n");
+ fprintf(stderr, "\t[-f file] [-i interface] [-K host | network]\n");
+ fprintf(stderr, "\t[-k host | network | label | id] ");
+ fprintf(stderr, "[-o level] [-p device]\n");
+ fprintf(stderr, "\t[-s modifier] ");
+ fprintf(stderr, "[-t table -T command [address ...]] [-x level]\n");
+ exit(1);
+}
+
+int
+pfctl_enable(int dev, int opts)
+{
+ if (ioctl(dev, DIOCSTART)) {
+ if (errno == EEXIST)
+ errx(1, "pf already enabled");
+#ifdef __FreeBSD__
+ else if (errno == ESRCH)
+ errx(1, "pfil registeration failed");
+#endif
+ else
+ err(1, "DIOCSTART");
+ }
+ if ((opts & PF_OPT_QUIET) == 0)
+ fprintf(stderr, "pf enabled\n");
+
+ if (altqsupport && ioctl(dev, DIOCSTARTALTQ))
+ if (errno != EEXIST)
+ err(1, "DIOCSTARTALTQ");
+
+ return (0);
+}
+
+int
+pfctl_disable(int dev, int opts)
+{
+ if (ioctl(dev, DIOCSTOP)) {
+ if (errno == ENOENT)
+ errx(1, "pf not enabled");
+ else
+ err(1, "DIOCSTOP");
+ }
+ if ((opts & PF_OPT_QUIET) == 0)
+ fprintf(stderr, "pf disabled\n");
+
+ if (altqsupport && ioctl(dev, DIOCSTOPALTQ))
+ if (errno != ENOENT)
+ err(1, "DIOCSTOPALTQ");
+
+ return (0);
+}
+
+int
+pfctl_clear_stats(int dev, int opts)
+{
+ if (ioctl(dev, DIOCCLRSTATUS))
+ err(1, "DIOCCLRSTATUS");
+ if ((opts & PF_OPT_QUIET) == 0)
+ fprintf(stderr, "pf: statistics cleared\n");
+ return (0);
+}
+
+int
+pfctl_clear_interface_flags(int dev, int opts)
+{
+ struct pfioc_iface pi;
+
+ if ((opts & PF_OPT_NOACTION) == 0) {
+ bzero(&pi, sizeof(pi));
+ pi.pfiio_flags = PFI_IFLAG_SKIP;
+
+ if (ioctl(dev, DIOCCLRIFFLAG, &pi))
+ err(1, "DIOCCLRIFFLAG");
+ if ((opts & PF_OPT_QUIET) == 0)
+ fprintf(stderr, "pf: interface flags reset\n");
+ }
+ return (0);
+}
+
+int
+pfctl_clear_rules(int dev, int opts, char *anchorname)
+{
+ struct pfr_buffer t;
+
+ memset(&t, 0, sizeof(t));
+ t.pfrb_type = PFRB_TRANS;
+ if (pfctl_add_trans(&t, PF_RULESET_SCRUB, anchorname) ||
+ pfctl_add_trans(&t, PF_RULESET_FILTER, anchorname) ||
+ pfctl_trans(dev, &t, DIOCXBEGIN, 0) ||
+ pfctl_trans(dev, &t, DIOCXCOMMIT, 0))
+ err(1, "pfctl_clear_rules");
+ if ((opts & PF_OPT_QUIET) == 0)
+ fprintf(stderr, "rules cleared\n");
+ return (0);
+}
+
+int
+pfctl_clear_nat(int dev, int opts, char *anchorname)
+{
+ struct pfr_buffer t;
+
+ memset(&t, 0, sizeof(t));
+ t.pfrb_type = PFRB_TRANS;
+ if (pfctl_add_trans(&t, PF_RULESET_NAT, anchorname) ||
+ pfctl_add_trans(&t, PF_RULESET_BINAT, anchorname) ||
+ pfctl_add_trans(&t, PF_RULESET_RDR, anchorname) ||
+ pfctl_trans(dev, &t, DIOCXBEGIN, 0) ||
+ pfctl_trans(dev, &t, DIOCXCOMMIT, 0))
+ err(1, "pfctl_clear_nat");
+ if ((opts & PF_OPT_QUIET) == 0)
+ fprintf(stderr, "nat cleared\n");
+ return (0);
+}
+
+int
+pfctl_clear_altq(int dev, int opts)
+{
+ struct pfr_buffer t;
+
+ if (!altqsupport)
+ return (-1);
+ memset(&t, 0, sizeof(t));
+ t.pfrb_type = PFRB_TRANS;
+ if (pfctl_add_trans(&t, PF_RULESET_ALTQ, "") ||
+ pfctl_trans(dev, &t, DIOCXBEGIN, 0) ||
+ pfctl_trans(dev, &t, DIOCXCOMMIT, 0))
+ err(1, "pfctl_clear_altq");
+ if ((opts & PF_OPT_QUIET) == 0)
+ fprintf(stderr, "altq cleared\n");
+ return (0);
+}
+
+int
+pfctl_clear_src_nodes(int dev, int opts)
+{
+ if (ioctl(dev, DIOCCLRSRCNODES))
+ err(1, "DIOCCLRSRCNODES");
+ if ((opts & PF_OPT_QUIET) == 0)
+ fprintf(stderr, "source tracking entries cleared\n");
+ return (0);
+}
+
+int
+pfctl_clear_states(int dev, const char *iface, int opts)
+{
+ struct pfioc_state_kill psk;
+
+ memset(&psk, 0, sizeof(psk));
+ if (iface != NULL && strlcpy(psk.psk_ifname, iface,
+ sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname))
+ errx(1, "invalid interface: %s", iface);
+
+ if (ioctl(dev, DIOCCLRSTATES, &psk))
+ err(1, "DIOCCLRSTATES");
+ if ((opts & PF_OPT_QUIET) == 0)
+ fprintf(stderr, "%d states cleared\n", psk.psk_killed);
+ return (0);
+}
+
+void
+pfctl_addrprefix(char *addr, struct pf_addr *mask)
+{
+ char *p;
+ const char *errstr;
+ int prefix, ret_ga, q, r;
+ struct addrinfo hints, *res;
+
+ if ((p = strchr(addr, '/')) == NULL)
+ return;
+
+ *p++ = '\0';
+ prefix = strtonum(p, 0, 128, &errstr);
+ if (errstr)
+ errx(1, "prefix is %s: %s", errstr, p);
+
+ bzero(&hints, sizeof(hints));
+ /* prefix only with numeric addresses */
+ hints.ai_flags |= AI_NUMERICHOST;
+
+ if ((ret_ga = getaddrinfo(addr, NULL, &hints, &res))) {
+ errx(1, "getaddrinfo: %s", gai_strerror(ret_ga));
+ /* NOTREACHED */
+ }
+
+ if (res->ai_family == AF_INET && prefix > 32)
+ errx(1, "prefix too long for AF_INET");
+ else if (res->ai_family == AF_INET6 && prefix > 128)
+ errx(1, "prefix too long for AF_INET6");
+
+ q = prefix >> 3;
+ r = prefix & 7;
+ switch (res->ai_family) {
+ case AF_INET:
+ bzero(&mask->v4, sizeof(mask->v4));
+ mask->v4.s_addr = htonl((u_int32_t)
+ (0xffffffffffULL << (32 - prefix)));
+ break;
+ case AF_INET6:
+ bzero(&mask->v6, sizeof(mask->v6));
+ if (q > 0)
+ memset((void *)&mask->v6, 0xff, q);
+ if (r > 0)
+ *((u_char *)&mask->v6 + q) =
+ (0xff00 >> r) & 0xff;
+ break;
+ }
+ freeaddrinfo(res);
+}
+
+int
+pfctl_kill_src_nodes(int dev, const char *iface, int opts)
+{
+ struct pfioc_src_node_kill psnk;
+ struct addrinfo *res[2], *resp[2];
+ struct sockaddr last_src, last_dst;
+ int killed, sources, dests;
+ int ret_ga;
+
+ killed = sources = dests = 0;
+
+ memset(&psnk, 0, sizeof(psnk));
+ memset(&psnk.psnk_src.addr.v.a.mask, 0xff,
+ sizeof(psnk.psnk_src.addr.v.a.mask));
+ memset(&last_src, 0xff, sizeof(last_src));
+ memset(&last_dst, 0xff, sizeof(last_dst));
+
+ pfctl_addrprefix(src_node_kill[0], &psnk.psnk_src.addr.v.a.mask);
+
+ if ((ret_ga = getaddrinfo(src_node_kill[0], NULL, NULL, &res[0]))) {
+ errx(1, "getaddrinfo: %s", gai_strerror(ret_ga));
+ /* NOTREACHED */
+ }
+ for (resp[0] = res[0]; resp[0]; resp[0] = resp[0]->ai_next) {
+ if (resp[0]->ai_addr == NULL)
+ continue;
+ /* We get lots of duplicates. Catch the easy ones */
+ if (memcmp(&last_src, resp[0]->ai_addr, sizeof(last_src)) == 0)
+ continue;
+ last_src = *(struct sockaddr *)resp[0]->ai_addr;
+
+ psnk.psnk_af = resp[0]->ai_family;
+ sources++;
+
+ if (psnk.psnk_af == AF_INET)
+ psnk.psnk_src.addr.v.a.addr.v4 =
+ ((struct sockaddr_in *)resp[0]->ai_addr)->sin_addr;
+ else if (psnk.psnk_af == AF_INET6)
+ psnk.psnk_src.addr.v.a.addr.v6 =
+ ((struct sockaddr_in6 *)resp[0]->ai_addr)->
+ sin6_addr;
+ else
+ errx(1, "Unknown address family %d", psnk.psnk_af);
+
+ if (src_node_killers > 1) {
+ dests = 0;
+ memset(&psnk.psnk_dst.addr.v.a.mask, 0xff,
+ sizeof(psnk.psnk_dst.addr.v.a.mask));
+ memset(&last_dst, 0xff, sizeof(last_dst));
+ pfctl_addrprefix(src_node_kill[1],
+ &psnk.psnk_dst.addr.v.a.mask);
+ if ((ret_ga = getaddrinfo(src_node_kill[1], NULL, NULL,
+ &res[1]))) {
+ errx(1, "getaddrinfo: %s",
+ gai_strerror(ret_ga));
+ /* NOTREACHED */
+ }
+ for (resp[1] = res[1]; resp[1];
+ resp[1] = resp[1]->ai_next) {
+ if (resp[1]->ai_addr == NULL)
+ continue;
+ if (psnk.psnk_af != resp[1]->ai_family)
+ continue;
+
+ if (memcmp(&last_dst, resp[1]->ai_addr,
+ sizeof(last_dst)) == 0)
+ continue;
+ last_dst = *(struct sockaddr *)resp[1]->ai_addr;
+
+ dests++;
+
+ if (psnk.psnk_af == AF_INET)
+ psnk.psnk_dst.addr.v.a.addr.v4 =
+ ((struct sockaddr_in *)resp[1]->
+ ai_addr)->sin_addr;
+ else if (psnk.psnk_af == AF_INET6)
+ psnk.psnk_dst.addr.v.a.addr.v6 =
+ ((struct sockaddr_in6 *)resp[1]->
+ ai_addr)->sin6_addr;
+ else
+ errx(1, "Unknown address family %d",
+ psnk.psnk_af);
+
+ if (ioctl(dev, DIOCKILLSRCNODES, &psnk))
+ err(1, "DIOCKILLSRCNODES");
+ killed += psnk.psnk_killed;
+ }
+ freeaddrinfo(res[1]);
+ } else {
+ if (ioctl(dev, DIOCKILLSRCNODES, &psnk))
+ err(1, "DIOCKILLSRCNODES");
+ killed += psnk.psnk_killed;
+ }
+ }
+
+ freeaddrinfo(res[0]);
+
+ if ((opts & PF_OPT_QUIET) == 0)
+ fprintf(stderr, "killed %d src nodes from %d sources and %d "
+ "destinations\n", killed, sources, dests);
+ return (0);
+}
+
+int
+pfctl_net_kill_states(int dev, const char *iface, int opts)
+{
+ struct pfioc_state_kill psk;
+ struct addrinfo *res[2], *resp[2];
+ struct sockaddr last_src, last_dst;
+ int killed, sources, dests;
+ int ret_ga;
+
+ killed = sources = dests = 0;
+
+ memset(&psk, 0, sizeof(psk));
+ memset(&psk.psk_src.addr.v.a.mask, 0xff,
+ sizeof(psk.psk_src.addr.v.a.mask));
+ memset(&last_src, 0xff, sizeof(last_src));
+ memset(&last_dst, 0xff, sizeof(last_dst));
+ if (iface != NULL && strlcpy(psk.psk_ifname, iface,
+ sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname))
+ errx(1, "invalid interface: %s", iface);
+
+ pfctl_addrprefix(state_kill[0], &psk.psk_src.addr.v.a.mask);
+
+ if ((ret_ga = getaddrinfo(state_kill[0], NULL, NULL, &res[0]))) {
+ errx(1, "getaddrinfo: %s", gai_strerror(ret_ga));
+ /* NOTREACHED */
+ }
+ for (resp[0] = res[0]; resp[0]; resp[0] = resp[0]->ai_next) {
+ if (resp[0]->ai_addr == NULL)
+ continue;
+ /* We get lots of duplicates. Catch the easy ones */
+ if (memcmp(&last_src, resp[0]->ai_addr, sizeof(last_src)) == 0)
+ continue;
+ last_src = *(struct sockaddr *)resp[0]->ai_addr;
+
+ psk.psk_af = resp[0]->ai_family;
+ sources++;
+
+ if (psk.psk_af == AF_INET)
+ psk.psk_src.addr.v.a.addr.v4 =
+ ((struct sockaddr_in *)resp[0]->ai_addr)->sin_addr;
+ else if (psk.psk_af == AF_INET6)
+ psk.psk_src.addr.v.a.addr.v6 =
+ ((struct sockaddr_in6 *)resp[0]->ai_addr)->
+ sin6_addr;
+ else
+ errx(1, "Unknown address family %d", psk.psk_af);
+
+ if (state_killers > 1) {
+ dests = 0;
+ memset(&psk.psk_dst.addr.v.a.mask, 0xff,
+ sizeof(psk.psk_dst.addr.v.a.mask));
+ memset(&last_dst, 0xff, sizeof(last_dst));
+ pfctl_addrprefix(state_kill[1],
+ &psk.psk_dst.addr.v.a.mask);
+ if ((ret_ga = getaddrinfo(state_kill[1], NULL, NULL,
+ &res[1]))) {
+ errx(1, "getaddrinfo: %s",
+ gai_strerror(ret_ga));
+ /* NOTREACHED */
+ }
+ for (resp[1] = res[1]; resp[1];
+ resp[1] = resp[1]->ai_next) {
+ if (resp[1]->ai_addr == NULL)
+ continue;
+ if (psk.psk_af != resp[1]->ai_family)
+ continue;
+
+ if (memcmp(&last_dst, resp[1]->ai_addr,
+ sizeof(last_dst)) == 0)
+ continue;
+ last_dst = *(struct sockaddr *)resp[1]->ai_addr;
+
+ dests++;
+
+ if (psk.psk_af == AF_INET)
+ psk.psk_dst.addr.v.a.addr.v4 =
+ ((struct sockaddr_in *)resp[1]->
+ ai_addr)->sin_addr;
+ else if (psk.psk_af == AF_INET6)
+ psk.psk_dst.addr.v.a.addr.v6 =
+ ((struct sockaddr_in6 *)resp[1]->
+ ai_addr)->sin6_addr;
+ else
+ errx(1, "Unknown address family %d",
+ psk.psk_af);
+
+ if (ioctl(dev, DIOCKILLSTATES, &psk))
+ err(1, "DIOCKILLSTATES");
+ killed += psk.psk_killed;
+ }
+ freeaddrinfo(res[1]);
+ } else {
+ if (ioctl(dev, DIOCKILLSTATES, &psk))
+ err(1, "DIOCKILLSTATES");
+ killed += psk.psk_killed;
+ }
+ }
+
+ freeaddrinfo(res[0]);
+
+ if ((opts & PF_OPT_QUIET) == 0)
+ fprintf(stderr, "killed %d states from %d sources and %d "
+ "destinations\n", killed, sources, dests);
+ return (0);
+}
+
+int
+pfctl_label_kill_states(int dev, const char *iface, int opts)
+{
+ struct pfioc_state_kill psk;
+
+ if (state_killers != 2 || (strlen(state_kill[1]) == 0)) {
+ warnx("no label specified");
+ usage();
+ }
+ memset(&psk, 0, sizeof(psk));
+ if (iface != NULL && strlcpy(psk.psk_ifname, iface,
+ sizeof(psk.psk_ifname)) >= sizeof(psk.psk_ifname))
+ errx(1, "invalid interface: %s", iface);
+
+ if (strlcpy(psk.psk_label, state_kill[1], sizeof(psk.psk_label)) >=
+ sizeof(psk.psk_label))
+ errx(1, "label too long: %s", state_kill[1]);
+
+ if (ioctl(dev, DIOCKILLSTATES, &psk))
+ err(1, "DIOCKILLSTATES");
+
+ if ((opts & PF_OPT_QUIET) == 0)
+ fprintf(stderr, "killed %d states\n", psk.psk_killed);
+
+ return (0);
+}
+
+int
+pfctl_id_kill_states(int dev, const char *iface, int opts)
+{
+ struct pfioc_state_kill psk;
+
+ if (state_killers != 2 || (strlen(state_kill[1]) == 0)) {
+ warnx("no id specified");
+ usage();
+ }
+
+ memset(&psk, 0, sizeof(psk));
+ if ((sscanf(state_kill[1], "%jx/%x",
+ &psk.psk_pfcmp.id, &psk.psk_pfcmp.creatorid)) == 2)
+ HTONL(psk.psk_pfcmp.creatorid);
+ else if ((sscanf(state_kill[1], "%jx", &psk.psk_pfcmp.id)) == 1) {
+ psk.psk_pfcmp.creatorid = 0;
+ } else {
+ warnx("wrong id format specified");
+ usage();
+ }
+ if (psk.psk_pfcmp.id == 0) {
+ warnx("cannot kill id 0");
+ usage();
+ }
+
+ psk.psk_pfcmp.id = htobe64(psk.psk_pfcmp.id);
+ if (ioctl(dev, DIOCKILLSTATES, &psk))
+ err(1, "DIOCKILLSTATES");
+
+ if ((opts & PF_OPT_QUIET) == 0)
+ fprintf(stderr, "killed %d states\n", psk.psk_killed);
+
+ return (0);
+}
+
+int
+pfctl_get_pool(int dev, struct pf_pool *pool, u_int32_t nr,
+ u_int32_t ticket, int r_action, char *anchorname)
+{
+ struct pfioc_pooladdr pp;
+ struct pf_pooladdr *pa;
+ u_int32_t pnr, mpnr;
+
+ memset(&pp, 0, sizeof(pp));
+ memcpy(pp.anchor, anchorname, sizeof(pp.anchor));
+ pp.r_action = r_action;
+ pp.r_num = nr;
+ pp.ticket = ticket;
+ if (ioctl(dev, DIOCGETADDRS, &pp)) {
+ warn("DIOCGETADDRS");
+ return (-1);
+ }
+ mpnr = pp.nr;
+ TAILQ_INIT(&pool->list);
+ for (pnr = 0; pnr < mpnr; ++pnr) {
+ pp.nr = pnr;
+ if (ioctl(dev, DIOCGETADDR, &pp)) {
+ warn("DIOCGETADDR");
+ return (-1);
+ }
+ pa = calloc(1, sizeof(struct pf_pooladdr));
+ if (pa == NULL)
+ err(1, "calloc");
+ bcopy(&pp.addr, pa, sizeof(struct pf_pooladdr));
+ TAILQ_INSERT_TAIL(&pool->list, pa, entries);
+ }
+
+ return (0);
+}
+
+void
+pfctl_move_pool(struct pf_pool *src, struct pf_pool *dst)
+{
+ struct pf_pooladdr *pa;
+
+ while ((pa = TAILQ_FIRST(&src->list)) != NULL) {
+ TAILQ_REMOVE(&src->list, pa, entries);
+ TAILQ_INSERT_TAIL(&dst->list, pa, entries);
+ }
+}
+
+void
+pfctl_clear_pool(struct pf_pool *pool)
+{
+ struct pf_pooladdr *pa;
+
+ while ((pa = TAILQ_FIRST(&pool->list)) != NULL) {
+ TAILQ_REMOVE(&pool->list, pa, entries);
+ free(pa);
+ }
+}
+
+void
+pfctl_print_rule_counters(struct pf_rule *rule, int opts)
+{
+ if (opts & PF_OPT_DEBUG) {
+ const char *t[PF_SKIP_COUNT] = { "i", "d", "f",
+ "p", "sa", "sp", "da", "dp" };
+ int i;
+
+ printf(" [ Skip steps: ");
+ for (i = 0; i < PF_SKIP_COUNT; ++i) {
+ if (rule->skip[i].nr == rule->nr + 1)
+ continue;
+ printf("%s=", t[i]);
+ if (rule->skip[i].nr == -1)
+ printf("end ");
+ else
+ printf("%u ", rule->skip[i].nr);
+ }
+ printf("]\n");
+
+ printf(" [ queue: qname=%s qid=%u pqname=%s pqid=%u ]\n",
+ rule->qname, rule->qid, rule->pqname, rule->pqid);
+ }
+ if (opts & PF_OPT_VERBOSE) {
+ printf(" [ Evaluations: %-8llu Packets: %-8llu "
+ "Bytes: %-10llu States: %-6u]\n",
+ (unsigned long long)rule->evaluations,
+ (unsigned long long)(rule->packets[0] +
+ rule->packets[1]),
+ (unsigned long long)(rule->bytes[0] +
+ rule->bytes[1]), rule->states_cur);
+ if (!(opts & PF_OPT_DEBUG))
+ printf(" [ Inserted: uid %u pid %u "
+ "State Creations: %-6u]\n",
+ (unsigned)rule->cuid, (unsigned)rule->cpid,
+ rule->states_tot);
+ }
+}
+
+void
+pfctl_print_title(char *title)
+{
+ if (!first_title)
+ printf("\n");
+ first_title = 0;
+ printf("%s\n", title);
+}
+
+int
+pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
+ char *anchorname, int depth)
+{
+ struct pfioc_rule pr;
+ u_int32_t nr, mnr, header = 0;
+ int rule_numbers = opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG);
+ int numeric = opts & PF_OPT_NUMERIC;
+ int len = strlen(path);
+ int brace;
+ char *p;
+
+ if (path[0])
+ snprintf(&path[len], MAXPATHLEN - len, "/%s", anchorname);
+ else
+ snprintf(&path[len], MAXPATHLEN - len, "%s", anchorname);
+
+ memset(&pr, 0, sizeof(pr));
+ memcpy(pr.anchor, path, sizeof(pr.anchor));
+ if (opts & PF_OPT_SHOWALL) {
+ pr.rule.action = PF_PASS;
+ if (ioctl(dev, DIOCGETRULES, &pr)) {
+ warn("DIOCGETRULES");
+ goto error;
+ }
+ header++;
+ }
+ pr.rule.action = PF_SCRUB;
+ if (ioctl(dev, DIOCGETRULES, &pr)) {
+ warn("DIOCGETRULES");
+ goto error;
+ }
+ if (opts & PF_OPT_SHOWALL) {
+ if (format == PFCTL_SHOW_RULES && (pr.nr > 0 || header))
+ pfctl_print_title("FILTER RULES:");
+ else if (format == PFCTL_SHOW_LABELS && labels)
+ pfctl_print_title("LABEL COUNTERS:");
+ }
+ mnr = pr.nr;
+ if (opts & PF_OPT_CLRRULECTRS)
+ pr.action = PF_GET_CLR_CNTR;
+
+ for (nr = 0; nr < mnr; ++nr) {
+ pr.nr = nr;
+ if (ioctl(dev, DIOCGETRULE, &pr)) {
+ warn("DIOCGETRULE");
+ goto error;
+ }
+
+ if (pfctl_get_pool(dev, &pr.rule.rpool,
+ nr, pr.ticket, PF_SCRUB, path) != 0)
+ goto error;
+
+ switch (format) {
+ case PFCTL_SHOW_LABELS:
+ break;
+ case PFCTL_SHOW_RULES:
+ if (pr.rule.label[0] && (opts & PF_OPT_SHOWALL))
+ labels = 1;
+ print_rule(&pr.rule, pr.anchor_call, rule_numbers, numeric);
+ printf("\n");
+ pfctl_print_rule_counters(&pr.rule, opts);
+ break;
+ case PFCTL_SHOW_NOTHING:
+ break;
+ }
+ pfctl_clear_pool(&pr.rule.rpool);
+ }
+ pr.rule.action = PF_PASS;
+ if (ioctl(dev, DIOCGETRULES, &pr)) {
+ warn("DIOCGETRULES");
+ goto error;
+ }
+ mnr = pr.nr;
+ for (nr = 0; nr < mnr; ++nr) {
+ pr.nr = nr;
+ if (ioctl(dev, DIOCGETRULE, &pr)) {
+ warn("DIOCGETRULE");
+ goto error;
+ }
+
+ if (pfctl_get_pool(dev, &pr.rule.rpool,
+ nr, pr.ticket, PF_PASS, path) != 0)
+ goto error;
+
+ switch (format) {
+ case PFCTL_SHOW_LABELS:
+ if (pr.rule.label[0]) {
+ printf("%s %llu %llu %llu %llu"
+ " %llu %llu %llu %llu\n",
+ pr.rule.label,
+ (unsigned long long)pr.rule.evaluations,
+ (unsigned long long)(pr.rule.packets[0] +
+ pr.rule.packets[1]),
+ (unsigned long long)(pr.rule.bytes[0] +
+ pr.rule.bytes[1]),
+ (unsigned long long)pr.rule.packets[0],
+ (unsigned long long)pr.rule.bytes[0],
+ (unsigned long long)pr.rule.packets[1],
+ (unsigned long long)pr.rule.bytes[1],
+ (unsigned long long)pr.rule.states_tot);
+ }
+ break;
+ case PFCTL_SHOW_RULES:
+ brace = 0;
+ if (pr.rule.label[0] && (opts & PF_OPT_SHOWALL))
+ labels = 1;
+ INDENT(depth, !(opts & PF_OPT_VERBOSE));
+ if (pr.anchor_call[0] &&
+ ((((p = strrchr(pr.anchor_call, '_')) != NULL) &&
+ ((void *)p == (void *)pr.anchor_call ||
+ *(--p) == '/')) || (opts & PF_OPT_RECURSE))) {
+ brace++;
+ if ((p = strrchr(pr.anchor_call, '/')) !=
+ NULL)
+ p++;
+ else
+ p = &pr.anchor_call[0];
+ } else
+ p = &pr.anchor_call[0];
+
+ print_rule(&pr.rule, p, rule_numbers, numeric);
+ if (brace)
+ printf(" {\n");
+ else
+ printf("\n");
+ pfctl_print_rule_counters(&pr.rule, opts);
+ if (brace) {
+ pfctl_show_rules(dev, path, opts, format,
+ p, depth + 1);
+ INDENT(depth, !(opts & PF_OPT_VERBOSE));
+ printf("}\n");
+ }
+ break;
+ case PFCTL_SHOW_NOTHING:
+ break;
+ }
+ pfctl_clear_pool(&pr.rule.rpool);
+ }
+ path[len] = '\0';
+ return (0);
+
+ error:
+ path[len] = '\0';
+ return (-1);
+}
+
+int
+pfctl_show_nat(int dev, int opts, char *anchorname)
+{
+ struct pfioc_rule pr;
+ u_int32_t mnr, nr;
+ static int nattype[3] = { PF_NAT, PF_RDR, PF_BINAT };
+ int i, dotitle = opts & PF_OPT_SHOWALL;
+
+ memset(&pr, 0, sizeof(pr));
+ memcpy(pr.anchor, anchorname, sizeof(pr.anchor));
+ for (i = 0; i < 3; i++) {
+ pr.rule.action = nattype[i];
+ if (ioctl(dev, DIOCGETRULES, &pr)) {
+ warn("DIOCGETRULES");
+ return (-1);
+ }
+ mnr = pr.nr;
+ for (nr = 0; nr < mnr; ++nr) {
+ pr.nr = nr;
+ if (ioctl(dev, DIOCGETRULE, &pr)) {
+ warn("DIOCGETRULE");
+ return (-1);
+ }
+ if (pfctl_get_pool(dev, &pr.rule.rpool, nr,
+ pr.ticket, nattype[i], anchorname) != 0)
+ return (-1);
+ if (dotitle) {
+ pfctl_print_title("TRANSLATION RULES:");
+ dotitle = 0;
+ }
+ print_rule(&pr.rule, pr.anchor_call,
+ opts & PF_OPT_VERBOSE2, opts & PF_OPT_NUMERIC);
+ printf("\n");
+ pfctl_print_rule_counters(&pr.rule, opts);
+ pfctl_clear_pool(&pr.rule.rpool);
+ }
+ }
+ return (0);
+}
+
+int
+pfctl_show_src_nodes(int dev, int opts)
+{
+ struct pfioc_src_nodes psn;
+ struct pf_src_node *p;
+ char *inbuf = NULL, *newinbuf = NULL;
+ unsigned int len = 0;
+ int i;
+
+ memset(&psn, 0, sizeof(psn));
+ for (;;) {
+ psn.psn_len = len;
+ if (len) {
+ newinbuf = realloc(inbuf, len);
+ if (newinbuf == NULL)
+ err(1, "realloc");
+ psn.psn_buf = inbuf = newinbuf;
+ }
+ if (ioctl(dev, DIOCGETSRCNODES, &psn) < 0) {
+ warn("DIOCGETSRCNODES");
+ free(inbuf);
+ return (-1);
+ }
+ if (psn.psn_len + sizeof(struct pfioc_src_nodes) < len)
+ break;
+ if (len == 0 && psn.psn_len == 0)
+ goto done;
+ if (len == 0 && psn.psn_len != 0)
+ len = psn.psn_len;
+ if (psn.psn_len == 0)
+ goto done; /* no src_nodes */
+ len *= 2;
+ }
+ p = psn.psn_src_nodes;
+ if (psn.psn_len > 0 && (opts & PF_OPT_SHOWALL))
+ pfctl_print_title("SOURCE TRACKING NODES:");
+ for (i = 0; i < psn.psn_len; i += sizeof(*p)) {
+ print_src_node(p, opts);
+ p++;
+ }
+done:
+ free(inbuf);
+ return (0);
+}
+
+int
+pfctl_show_states(int dev, const char *iface, int opts)
+{
+ struct pfioc_states ps;
+ struct pfsync_state *p;
+ char *inbuf = NULL, *newinbuf = NULL;
+ unsigned int len = 0;
+ int i, dotitle = (opts & PF_OPT_SHOWALL);
+
+ memset(&ps, 0, sizeof(ps));
+ for (;;) {
+ ps.ps_len = len;
+ if (len) {
+ newinbuf = realloc(inbuf, len);
+ if (newinbuf == NULL)
+ err(1, "realloc");
+ ps.ps_buf = inbuf = newinbuf;
+ }
+ if (ioctl(dev, DIOCGETSTATES, &ps) < 0) {
+ warn("DIOCGETSTATES");
+ free(inbuf);
+ return (-1);
+ }
+ if (ps.ps_len + sizeof(struct pfioc_states) < len)
+ break;
+ if (len == 0 && ps.ps_len == 0)
+ goto done;
+ if (len == 0 && ps.ps_len != 0)
+ len = ps.ps_len;
+ if (ps.ps_len == 0)
+ goto done; /* no states */
+ len *= 2;
+ }
+ p = ps.ps_states;
+ for (i = 0; i < ps.ps_len; i += sizeof(*p), p++) {
+ if (iface != NULL && strcmp(p->ifname, iface))
+ continue;
+ if (dotitle) {
+ pfctl_print_title("STATES:");
+ dotitle = 0;
+ }
+ print_state(p, opts);
+ }
+done:
+ free(inbuf);
+ return (0);
+}
+
+int
+pfctl_show_status(int dev, int opts)
+{
+ struct pf_status status;
+
+ if (ioctl(dev, DIOCGETSTATUS, &status)) {
+ warn("DIOCGETSTATUS");
+ return (-1);
+ }
+ if (opts & PF_OPT_SHOWALL)
+ pfctl_print_title("INFO:");
+ print_status(&status, opts);
+ return (0);
+}
+
+int
+pfctl_show_timeouts(int dev, int opts)
+{
+ struct pfioc_tm pt;
+ int i;
+
+ if (opts & PF_OPT_SHOWALL)
+ pfctl_print_title("TIMEOUTS:");
+ memset(&pt, 0, sizeof(pt));
+ for (i = 0; pf_timeouts[i].name; i++) {
+ pt.timeout = pf_timeouts[i].timeout;
+ if (ioctl(dev, DIOCGETTIMEOUT, &pt))
+ err(1, "DIOCGETTIMEOUT");
+ printf("%-20s %10d", pf_timeouts[i].name, pt.seconds);
+ if (pf_timeouts[i].timeout >= PFTM_ADAPTIVE_START &&
+ pf_timeouts[i].timeout <= PFTM_ADAPTIVE_END)
+ printf(" states");
+ else
+ printf("s");
+ printf("\n");
+ }
+ return (0);
+
+}
+
+int
+pfctl_show_limits(int dev, int opts)
+{
+ struct pfioc_limit pl;
+ int i;
+
+ if (opts & PF_OPT_SHOWALL)
+ pfctl_print_title("LIMITS:");
+ memset(&pl, 0, sizeof(pl));
+ for (i = 0; pf_limits[i].name; i++) {
+ pl.index = pf_limits[i].index;
+ if (ioctl(dev, DIOCGETLIMIT, &pl))
+ err(1, "DIOCGETLIMIT");
+ printf("%-13s ", pf_limits[i].name);
+ if (pl.limit == UINT_MAX)
+ printf("unlimited\n");
+ else
+ printf("hard limit %8u\n", pl.limit);
+ }
+ return (0);
+}
+
+/* callbacks for rule/nat/rdr/addr */
+int
+pfctl_add_pool(struct pfctl *pf, struct pf_pool *p, sa_family_t af)
+{
+ struct pf_pooladdr *pa;
+
+ if ((pf->opts & PF_OPT_NOACTION) == 0) {
+ if (ioctl(pf->dev, DIOCBEGINADDRS, &pf->paddr))
+ err(1, "DIOCBEGINADDRS");
+ }
+
+ pf->paddr.af = af;
+ TAILQ_FOREACH(pa, &p->list, entries) {
+ memcpy(&pf->paddr.addr, pa, sizeof(struct pf_pooladdr));
+ if ((pf->opts & PF_OPT_NOACTION) == 0) {
+ if (ioctl(pf->dev, DIOCADDADDR, &pf->paddr))
+ err(1, "DIOCADDADDR");
+ }
+ }
+ return (0);
+}
+
+int
+pfctl_add_rule(struct pfctl *pf, struct pf_rule *r, const char *anchor_call)
+{
+ u_int8_t rs_num;
+ struct pf_rule *rule;
+ struct pf_ruleset *rs;
+ char *p;
+
+ rs_num = pf_get_ruleset_number(r->action);
+ if (rs_num == PF_RULESET_MAX)
+ errx(1, "Invalid rule type %d", r->action);
+
+ rs = &pf->anchor->ruleset;
+
+ if (anchor_call[0] && r->anchor == NULL) {
+ /*
+ * Don't make non-brace anchors part of the main anchor pool.
+ */
+ if ((r->anchor = calloc(1, sizeof(*r->anchor))) == NULL)
+ err(1, "pfctl_add_rule: calloc");
+
+ pf_init_ruleset(&r->anchor->ruleset);
+ r->anchor->ruleset.anchor = r->anchor;
+ if (strlcpy(r->anchor->path, anchor_call,
+ sizeof(rule->anchor->path)) >= sizeof(rule->anchor->path))
+ errx(1, "pfctl_add_rule: strlcpy");
+ if ((p = strrchr(anchor_call, '/')) != NULL) {
+ if (!strlen(p))
+ err(1, "pfctl_add_rule: bad anchor name %s",
+ anchor_call);
+ } else
+ p = (char *)anchor_call;
+ if (strlcpy(r->anchor->name, p,
+ sizeof(rule->anchor->name)) >= sizeof(rule->anchor->name))
+ errx(1, "pfctl_add_rule: strlcpy");
+ }
+
+ if ((rule = calloc(1, sizeof(*rule))) == NULL)
+ err(1, "calloc");
+ bcopy(r, rule, sizeof(*rule));
+ TAILQ_INIT(&rule->rpool.list);
+ pfctl_move_pool(&r->rpool, &rule->rpool);
+
+ TAILQ_INSERT_TAIL(rs->rules[rs_num].active.ptr, rule, entries);
+ return (0);
+}
+
+int
+pfctl_ruleset_trans(struct pfctl *pf, char *path, struct pf_anchor *a)
+{
+ int osize = pf->trans->pfrb_size;
+
+ if ((pf->loadopt & PFCTL_FLAG_NAT) != 0) {
+ if (pfctl_add_trans(pf->trans, PF_RULESET_NAT, path) ||
+ pfctl_add_trans(pf->trans, PF_RULESET_BINAT, path) ||
+ pfctl_add_trans(pf->trans, PF_RULESET_RDR, path))
+ return (1);
+ }
+ if (a == pf->astack[0] && ((altqsupport &&
+ (pf->loadopt & PFCTL_FLAG_ALTQ) != 0))) {
+ if (pfctl_add_trans(pf->trans, PF_RULESET_ALTQ, path))
+ return (2);
+ }
+ if ((pf->loadopt & PFCTL_FLAG_FILTER) != 0) {
+ if (pfctl_add_trans(pf->trans, PF_RULESET_SCRUB, path) ||
+ pfctl_add_trans(pf->trans, PF_RULESET_FILTER, path))
+ return (3);
+ }
+ if (pf->loadopt & PFCTL_FLAG_TABLE)
+ if (pfctl_add_trans(pf->trans, PF_RULESET_TABLE, path))
+ return (4);
+ if (pfctl_trans(pf->dev, pf->trans, DIOCXBEGIN, osize))
+ return (5);
+
+ return (0);
+}
+
+int
+pfctl_load_ruleset(struct pfctl *pf, char *path, struct pf_ruleset *rs,
+ int rs_num, int depth)
+{
+ struct pf_rule *r;
+ int error, len = strlen(path);
+ int brace = 0;
+
+ pf->anchor = rs->anchor;
+
+ if (path[0])
+ snprintf(&path[len], MAXPATHLEN - len, "/%s", pf->anchor->name);
+ else
+ snprintf(&path[len], MAXPATHLEN - len, "%s", pf->anchor->name);
+
+ if (depth) {
+ if (TAILQ_FIRST(rs->rules[rs_num].active.ptr) != NULL) {
+ brace++;
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf(" {\n");
+ if ((pf->opts & PF_OPT_NOACTION) == 0 &&
+ (error = pfctl_ruleset_trans(pf,
+ path, rs->anchor))) {
+ printf("pfctl_load_rulesets: "
+ "pfctl_ruleset_trans %d\n", error);
+ goto error;
+ }
+ } else if (pf->opts & PF_OPT_VERBOSE)
+ printf("\n");
+
+ }
+
+ if (pf->optimize && rs_num == PF_RULESET_FILTER)
+ pfctl_optimize_ruleset(pf, rs);
+
+ while ((r = TAILQ_FIRST(rs->rules[rs_num].active.ptr)) != NULL) {
+ TAILQ_REMOVE(rs->rules[rs_num].active.ptr, r, entries);
+ if ((error = pfctl_load_rule(pf, path, r, depth)))
+ goto error;
+ if (r->anchor) {
+ if ((error = pfctl_load_ruleset(pf, path,
+ &r->anchor->ruleset, rs_num, depth + 1)))
+ goto error;
+ } else if (pf->opts & PF_OPT_VERBOSE)
+ printf("\n");
+ free(r);
+ }
+ if (brace && pf->opts & PF_OPT_VERBOSE) {
+ INDENT(depth - 1, (pf->opts & PF_OPT_VERBOSE));
+ printf("}\n");
+ }
+ path[len] = '\0';
+ return (0);
+
+ error:
+ path[len] = '\0';
+ return (error);
+
+}
+
+int
+pfctl_load_rule(struct pfctl *pf, char *path, struct pf_rule *r, int depth)
+{
+ u_int8_t rs_num = pf_get_ruleset_number(r->action);
+ char *name;
+ struct pfioc_rule pr;
+ int len = strlen(path);
+
+ bzero(&pr, sizeof(pr));
+ /* set up anchor before adding to path for anchor_call */
+ if ((pf->opts & PF_OPT_NOACTION) == 0)
+ pr.ticket = pfctl_get_ticket(pf->trans, rs_num, path);
+ if (strlcpy(pr.anchor, path, sizeof(pr.anchor)) >= sizeof(pr.anchor))
+ errx(1, "pfctl_load_rule: strlcpy");
+
+ if (r->anchor) {
+ if (r->anchor->match) {
+ if (path[0])
+ snprintf(&path[len], MAXPATHLEN - len,
+ "/%s", r->anchor->name);
+ else
+ snprintf(&path[len], MAXPATHLEN - len,
+ "%s", r->anchor->name);
+ name = path;
+ } else
+ name = r->anchor->path;
+ } else
+ name = "";
+
+ if ((pf->opts & PF_OPT_NOACTION) == 0) {
+ if (pfctl_add_pool(pf, &r->rpool, r->af))
+ return (1);
+ pr.pool_ticket = pf->paddr.ticket;
+ memcpy(&pr.rule, r, sizeof(pr.rule));
+ if (r->anchor && strlcpy(pr.anchor_call, name,
+ sizeof(pr.anchor_call)) >= sizeof(pr.anchor_call))
+ errx(1, "pfctl_load_rule: strlcpy");
+ if (ioctl(pf->dev, DIOCADDRULE, &pr))
+ err(1, "DIOCADDRULE");
+ }
+
+ if (pf->opts & PF_OPT_VERBOSE) {
+ INDENT(depth, !(pf->opts & PF_OPT_VERBOSE2));
+ print_rule(r, r->anchor ? r->anchor->name : "",
+ pf->opts & PF_OPT_VERBOSE2,
+ pf->opts & PF_OPT_NUMERIC);
+ }
+ path[len] = '\0';
+ pfctl_clear_pool(&r->rpool);
+ return (0);
+}
+
+int
+pfctl_add_altq(struct pfctl *pf, struct pf_altq *a)
+{
+ if (altqsupport &&
+ (loadopt & PFCTL_FLAG_ALTQ) != 0) {
+ memcpy(&pf->paltq->altq, a, sizeof(struct pf_altq));
+ if ((pf->opts & PF_OPT_NOACTION) == 0) {
+ if (ioctl(pf->dev, DIOCADDALTQ, pf->paltq)) {
+ if (errno == ENXIO)
+ errx(1, "qtype not configured");
+ else if (errno == ENODEV)
+ errx(1, "%s: driver does not support "
+ "altq", a->ifname);
+ else
+ err(1, "DIOCADDALTQ");
+ }
+ }
+ pfaltq_store(&pf->paltq->altq);
+ }
+ return (0);
+}
+
+int
+pfctl_rules(int dev, char *filename, int opts, int optimize,
+ char *anchorname, struct pfr_buffer *trans)
+{
+#define ERR(x) do { warn(x); goto _error; } while(0)
+#define ERRX(x) do { warnx(x); goto _error; } while(0)
+
+ struct pfr_buffer *t, buf;
+ struct pfioc_altq pa;
+ struct pfctl pf;
+ struct pf_ruleset *rs;
+ struct pfr_table trs;
+ char *path;
+ int osize;
+
+ RB_INIT(&pf_anchors);
+ memset(&pf_main_anchor, 0, sizeof(pf_main_anchor));
+ pf_init_ruleset(&pf_main_anchor.ruleset);
+ pf_main_anchor.ruleset.anchor = &pf_main_anchor;
+ if (trans == NULL) {
+ bzero(&buf, sizeof(buf));
+ buf.pfrb_type = PFRB_TRANS;
+ t = &buf;
+ osize = 0;
+ } else {
+ t = trans;
+ osize = t->pfrb_size;
+ }
+
+ memset(&pa, 0, sizeof(pa));
+ memset(&pf, 0, sizeof(pf));
+ memset(&trs, 0, sizeof(trs));
+ if ((path = calloc(1, MAXPATHLEN)) == NULL)
+ ERRX("pfctl_rules: calloc");
+ if (strlcpy(trs.pfrt_anchor, anchorname,
+ sizeof(trs.pfrt_anchor)) >= sizeof(trs.pfrt_anchor))
+ ERRX("pfctl_rules: strlcpy");
+ pf.dev = dev;
+ pf.opts = opts;
+ pf.optimize = optimize;
+ pf.loadopt = loadopt;
+
+ /* non-brace anchor, create without resolving the path */
+ if ((pf.anchor = calloc(1, sizeof(*pf.anchor))) == NULL)
+ ERRX("pfctl_rules: calloc");
+ rs = &pf.anchor->ruleset;
+ pf_init_ruleset(rs);
+ rs->anchor = pf.anchor;
+ if (strlcpy(pf.anchor->path, anchorname,
+ sizeof(pf.anchor->path)) >= sizeof(pf.anchor->path))
+ errx(1, "pfctl_add_rule: strlcpy");
+ if (strlcpy(pf.anchor->name, anchorname,
+ sizeof(pf.anchor->name)) >= sizeof(pf.anchor->name))
+ errx(1, "pfctl_add_rule: strlcpy");
+
+
+ pf.astack[0] = pf.anchor;
+ pf.asd = 0;
+ if (anchorname[0])
+ pf.loadopt &= ~PFCTL_FLAG_ALTQ;
+ pf.paltq = &pa;
+ pf.trans = t;
+ pfctl_init_options(&pf);
+
+ if ((opts & PF_OPT_NOACTION) == 0) {
+ /*
+ * XXX For the time being we need to open transactions for
+ * the main ruleset before parsing, because tables are still
+ * loaded at parse time.
+ */
+ if (pfctl_ruleset_trans(&pf, anchorname, pf.anchor))
+ ERRX("pfctl_rules");
+ if (altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ))
+ pa.ticket =
+ pfctl_get_ticket(t, PF_RULESET_ALTQ, anchorname);
+ if (pf.loadopt & PFCTL_FLAG_TABLE)
+ pf.astack[0]->ruleset.tticket =
+ pfctl_get_ticket(t, PF_RULESET_TABLE, anchorname);
+ }
+
+ if (parse_config(filename, &pf) < 0) {
+ if ((opts & PF_OPT_NOACTION) == 0)
+ ERRX("Syntax error in config file: "
+ "pf rules not loaded");
+ else
+ goto _error;
+ }
+
+ if ((pf.loadopt & PFCTL_FLAG_FILTER &&
+ (pfctl_load_ruleset(&pf, path, rs, PF_RULESET_SCRUB, 0))) ||
+ (pf.loadopt & PFCTL_FLAG_NAT &&
+ (pfctl_load_ruleset(&pf, path, rs, PF_RULESET_NAT, 0) ||
+ pfctl_load_ruleset(&pf, path, rs, PF_RULESET_RDR, 0) ||
+ pfctl_load_ruleset(&pf, path, rs, PF_RULESET_BINAT, 0))) ||
+ (pf.loadopt & PFCTL_FLAG_FILTER &&
+ pfctl_load_ruleset(&pf, path, rs, PF_RULESET_FILTER, 0))) {
+ if ((opts & PF_OPT_NOACTION) == 0)
+ ERRX("Unable to load rules into kernel");
+ else
+ goto _error;
+ }
+
+ if ((altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ) != 0))
+ if (check_commit_altq(dev, opts) != 0)
+ ERRX("errors in altq config");
+
+ /* process "load anchor" directives */
+ if (!anchorname[0])
+ if (pfctl_load_anchors(dev, &pf, t) == -1)
+ ERRX("load anchors");
+
+ if (trans == NULL && (opts & PF_OPT_NOACTION) == 0) {
+ if (!anchorname[0])
+ if (pfctl_load_options(&pf))
+ goto _error;
+ if (pfctl_trans(dev, t, DIOCXCOMMIT, osize))
+ ERR("DIOCXCOMMIT");
+ }
+ return (0);
+
+_error:
+ if (trans == NULL) { /* main ruleset */
+ if ((opts & PF_OPT_NOACTION) == 0)
+ if (pfctl_trans(dev, t, DIOCXROLLBACK, osize))
+ err(1, "DIOCXROLLBACK");
+ exit(1);
+ } else { /* sub ruleset */
+ return (-1);
+ }
+
+#undef ERR
+#undef ERRX
+}
+
+FILE *
+pfctl_fopen(const char *name, const char *mode)
+{
+ struct stat st;
+ FILE *fp;
+
+ fp = fopen(name, mode);
+ if (fp == NULL)
+ return (NULL);
+ if (fstat(fileno(fp), &st)) {
+ fclose(fp);
+ return (NULL);
+ }
+ if (S_ISDIR(st.st_mode)) {
+ fclose(fp);
+ errno = EISDIR;
+ return (NULL);
+ }
+ return (fp);
+}
+
+void
+pfctl_init_options(struct pfctl *pf)
+{
+
+ pf->timeout[PFTM_TCP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL;
+ pf->timeout[PFTM_TCP_OPENING] = PFTM_TCP_OPENING_VAL;
+ pf->timeout[PFTM_TCP_ESTABLISHED] = PFTM_TCP_ESTABLISHED_VAL;
+ pf->timeout[PFTM_TCP_CLOSING] = PFTM_TCP_CLOSING_VAL;
+ pf->timeout[PFTM_TCP_FIN_WAIT] = PFTM_TCP_FIN_WAIT_VAL;
+ pf->timeout[PFTM_TCP_CLOSED] = PFTM_TCP_CLOSED_VAL;
+ pf->timeout[PFTM_UDP_FIRST_PACKET] = PFTM_UDP_FIRST_PACKET_VAL;
+ pf->timeout[PFTM_UDP_SINGLE] = PFTM_UDP_SINGLE_VAL;
+ pf->timeout[PFTM_UDP_MULTIPLE] = PFTM_UDP_MULTIPLE_VAL;
+ pf->timeout[PFTM_ICMP_FIRST_PACKET] = PFTM_ICMP_FIRST_PACKET_VAL;
+ pf->timeout[PFTM_ICMP_ERROR_REPLY] = PFTM_ICMP_ERROR_REPLY_VAL;
+ pf->timeout[PFTM_OTHER_FIRST_PACKET] = PFTM_OTHER_FIRST_PACKET_VAL;
+ pf->timeout[PFTM_OTHER_SINGLE] = PFTM_OTHER_SINGLE_VAL;
+ pf->timeout[PFTM_OTHER_MULTIPLE] = PFTM_OTHER_MULTIPLE_VAL;
+ pf->timeout[PFTM_FRAG] = PFTM_FRAG_VAL;
+ pf->timeout[PFTM_INTERVAL] = PFTM_INTERVAL_VAL;
+ pf->timeout[PFTM_SRC_NODE] = PFTM_SRC_NODE_VAL;
+ pf->timeout[PFTM_TS_DIFF] = PFTM_TS_DIFF_VAL;
+ pf->timeout[PFTM_ADAPTIVE_START] = PFSTATE_ADAPT_START;
+ pf->timeout[PFTM_ADAPTIVE_END] = PFSTATE_ADAPT_END;
+
+ pf->limit[PF_LIMIT_STATES] = PFSTATE_HIWAT;
+ pf->limit[PF_LIMIT_FRAGS] = PFFRAG_FRENT_HIWAT;
+ pf->limit[PF_LIMIT_SRC_NODES] = PFSNODE_HIWAT;
+ pf->limit[PF_LIMIT_TABLE_ENTRIES] = PFR_KENTRY_HIWAT;
+
+ pf->debug = PF_DEBUG_URGENT;
+}
+
+int
+pfctl_load_options(struct pfctl *pf)
+{
+ int i, error = 0;
+
+ if ((loadopt & PFCTL_FLAG_OPTION) == 0)
+ return (0);
+
+ /* load limits */
+ for (i = 0; i < PF_LIMIT_MAX; i++) {
+ if ((pf->opts & PF_OPT_MERGE) && !pf->limit_set[i])
+ continue;
+ if (pfctl_load_limit(pf, i, pf->limit[i]))
+ error = 1;
+ }
+
+ /*
+ * If we've set the limit, but haven't explicitly set adaptive
+ * timeouts, do it now with a start of 60% and end of 120%.
+ */
+ if (pf->limit_set[PF_LIMIT_STATES] &&
+ !pf->timeout_set[PFTM_ADAPTIVE_START] &&
+ !pf->timeout_set[PFTM_ADAPTIVE_END]) {
+ pf->timeout[PFTM_ADAPTIVE_START] =
+ (pf->limit[PF_LIMIT_STATES] / 10) * 6;
+ pf->timeout_set[PFTM_ADAPTIVE_START] = 1;
+ pf->timeout[PFTM_ADAPTIVE_END] =
+ (pf->limit[PF_LIMIT_STATES] / 10) * 12;
+ pf->timeout_set[PFTM_ADAPTIVE_END] = 1;
+ }
+
+ /* load timeouts */
+ for (i = 0; i < PFTM_MAX; i++) {
+ if ((pf->opts & PF_OPT_MERGE) && !pf->timeout_set[i])
+ continue;
+ if (pfctl_load_timeout(pf, i, pf->timeout[i]))
+ error = 1;
+ }
+
+ /* load debug */
+ if (!(pf->opts & PF_OPT_MERGE) || pf->debug_set)
+ if (pfctl_load_debug(pf, pf->debug))
+ error = 1;
+
+ /* load logif */
+ if (!(pf->opts & PF_OPT_MERGE) || pf->ifname_set)
+ if (pfctl_load_logif(pf, pf->ifname))
+ error = 1;
+
+ /* load hostid */
+ if (!(pf->opts & PF_OPT_MERGE) || pf->hostid_set)
+ if (pfctl_load_hostid(pf, pf->hostid))
+ error = 1;
+
+ return (error);
+}
+
+int
+pfctl_set_limit(struct pfctl *pf, const char *opt, unsigned int limit)
+{
+ int i;
+
+
+ for (i = 0; pf_limits[i].name; i++) {
+ if (strcasecmp(opt, pf_limits[i].name) == 0) {
+ pf->limit[pf_limits[i].index] = limit;
+ pf->limit_set[pf_limits[i].index] = 1;
+ break;
+ }
+ }
+ if (pf_limits[i].name == NULL) {
+ warnx("Bad pool name.");
+ return (1);
+ }
+
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf("set limit %s %d\n", opt, limit);
+
+ return (0);
+}
+
+int
+pfctl_load_limit(struct pfctl *pf, unsigned int index, unsigned int limit)
+{
+ struct pfioc_limit pl;
+
+ memset(&pl, 0, sizeof(pl));
+ pl.index = index;
+ pl.limit = limit;
+ if (ioctl(pf->dev, DIOCSETLIMIT, &pl)) {
+ if (errno == EBUSY)
+ warnx("Current pool size exceeds requested hard limit");
+ else
+ warnx("DIOCSETLIMIT");
+ return (1);
+ }
+ return (0);
+}
+
+int
+pfctl_set_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet)
+{
+ int i;
+
+ if ((loadopt & PFCTL_FLAG_OPTION) == 0)
+ return (0);
+
+ for (i = 0; pf_timeouts[i].name; i++) {
+ if (strcasecmp(opt, pf_timeouts[i].name) == 0) {
+ pf->timeout[pf_timeouts[i].timeout] = seconds;
+ pf->timeout_set[pf_timeouts[i].timeout] = 1;
+ break;
+ }
+ }
+
+ if (pf_timeouts[i].name == NULL) {
+ warnx("Bad timeout name.");
+ return (1);
+ }
+
+
+ if (pf->opts & PF_OPT_VERBOSE && ! quiet)
+ printf("set timeout %s %d\n", opt, seconds);
+
+ return (0);
+}
+
+int
+pfctl_load_timeout(struct pfctl *pf, unsigned int timeout, unsigned int seconds)
+{
+ struct pfioc_tm pt;
+
+ memset(&pt, 0, sizeof(pt));
+ pt.timeout = timeout;
+ pt.seconds = seconds;
+ if (ioctl(pf->dev, DIOCSETTIMEOUT, &pt)) {
+ warnx("DIOCSETTIMEOUT");
+ return (1);
+ }
+ return (0);
+}
+
+int
+pfctl_set_optimization(struct pfctl *pf, const char *opt)
+{
+ const struct pf_hint *hint;
+ int i, r;
+
+ if ((loadopt & PFCTL_FLAG_OPTION) == 0)
+ return (0);
+
+ for (i = 0; pf_hints[i].name; i++)
+ if (strcasecmp(opt, pf_hints[i].name) == 0)
+ break;
+
+ hint = pf_hints[i].hint;
+ if (hint == NULL) {
+ warnx("invalid state timeouts optimization");
+ return (1);
+ }
+
+ for (i = 0; hint[i].name; i++)
+ if ((r = pfctl_set_timeout(pf, hint[i].name,
+ hint[i].timeout, 1)))
+ return (r);
+
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf("set optimization %s\n", opt);
+
+ return (0);
+}
+
+int
+pfctl_set_logif(struct pfctl *pf, char *ifname)
+{
+
+ if ((loadopt & PFCTL_FLAG_OPTION) == 0)
+ return (0);
+
+ if (!strcmp(ifname, "none")) {
+ free(pf->ifname);
+ pf->ifname = NULL;
+ } else {
+ pf->ifname = strdup(ifname);
+ if (!pf->ifname)
+ errx(1, "pfctl_set_logif: strdup");
+ }
+ pf->ifname_set = 1;
+
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf("set loginterface %s\n", ifname);
+
+ return (0);
+}
+
+int
+pfctl_load_logif(struct pfctl *pf, char *ifname)
+{
+ struct pfioc_if pi;
+
+ memset(&pi, 0, sizeof(pi));
+ if (ifname && strlcpy(pi.ifname, ifname,
+ sizeof(pi.ifname)) >= sizeof(pi.ifname)) {
+ warnx("pfctl_load_logif: strlcpy");
+ return (1);
+ }
+ if (ioctl(pf->dev, DIOCSETSTATUSIF, &pi)) {
+ warnx("DIOCSETSTATUSIF");
+ return (1);
+ }
+ return (0);
+}
+
+int
+pfctl_set_hostid(struct pfctl *pf, u_int32_t hostid)
+{
+ if ((loadopt & PFCTL_FLAG_OPTION) == 0)
+ return (0);
+
+ HTONL(hostid);
+
+ pf->hostid = hostid;
+ pf->hostid_set = 1;
+
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf("set hostid 0x%08x\n", ntohl(hostid));
+
+ return (0);
+}
+
+int
+pfctl_load_hostid(struct pfctl *pf, u_int32_t hostid)
+{
+ if (ioctl(dev, DIOCSETHOSTID, &hostid)) {
+ warnx("DIOCSETHOSTID");
+ return (1);
+ }
+ return (0);
+}
+
+int
+pfctl_set_debug(struct pfctl *pf, char *d)
+{
+ u_int32_t level;
+
+ if ((loadopt & PFCTL_FLAG_OPTION) == 0)
+ return (0);
+
+ if (!strcmp(d, "none"))
+ pf->debug = PF_DEBUG_NONE;
+ else if (!strcmp(d, "urgent"))
+ pf->debug = PF_DEBUG_URGENT;
+ else if (!strcmp(d, "misc"))
+ pf->debug = PF_DEBUG_MISC;
+ else if (!strcmp(d, "loud"))
+ pf->debug = PF_DEBUG_NOISY;
+ else {
+ warnx("unknown debug level \"%s\"", d);
+ return (-1);
+ }
+
+ pf->debug_set = 1;
+
+ if ((pf->opts & PF_OPT_NOACTION) == 0)
+ if (ioctl(dev, DIOCSETDEBUG, &level))
+ err(1, "DIOCSETDEBUG");
+
+ if (pf->opts & PF_OPT_VERBOSE)
+ printf("set debug %s\n", d);
+
+ return (0);
+}
+
+int
+pfctl_load_debug(struct pfctl *pf, unsigned int level)
+{
+ if (ioctl(pf->dev, DIOCSETDEBUG, &level)) {
+ warnx("DIOCSETDEBUG");
+ return (1);
+ }
+ return (0);
+}
+
+int
+pfctl_set_interface_flags(struct pfctl *pf, char *ifname, int flags, int how)
+{
+ struct pfioc_iface pi;
+
+ if ((loadopt & PFCTL_FLAG_OPTION) == 0)
+ return (0);
+
+ bzero(&pi, sizeof(pi));
+
+ pi.pfiio_flags = flags;
+
+ if (strlcpy(pi.pfiio_name, ifname, sizeof(pi.pfiio_name)) >=
+ sizeof(pi.pfiio_name))
+ errx(1, "pfctl_set_interface_flags: strlcpy");
+
+ if ((pf->opts & PF_OPT_NOACTION) == 0) {
+ if (how == 0) {
+ if (ioctl(pf->dev, DIOCCLRIFFLAG, &pi))
+ err(1, "DIOCCLRIFFLAG");
+ } else {
+ if (ioctl(pf->dev, DIOCSETIFFLAG, &pi))
+ err(1, "DIOCSETIFFLAG");
+ }
+ }
+ return (0);
+}
+
+void
+pfctl_debug(int dev, u_int32_t level, int opts)
+{
+ if (ioctl(dev, DIOCSETDEBUG, &level))
+ err(1, "DIOCSETDEBUG");
+ if ((opts & PF_OPT_QUIET) == 0) {
+ fprintf(stderr, "debug level set to '");
+ switch (level) {
+ case PF_DEBUG_NONE:
+ fprintf(stderr, "none");
+ break;
+ case PF_DEBUG_URGENT:
+ fprintf(stderr, "urgent");
+ break;
+ case PF_DEBUG_MISC:
+ fprintf(stderr, "misc");
+ break;
+ case PF_DEBUG_NOISY:
+ fprintf(stderr, "loud");
+ break;
+ default:
+ fprintf(stderr, "<invalid>");
+ break;
+ }
+ fprintf(stderr, "'\n");
+ }
+}
+
+int
+pfctl_test_altqsupport(int dev, int opts)
+{
+ struct pfioc_altq pa;
+
+ if (ioctl(dev, DIOCGETALTQS, &pa)) {
+ if (errno == ENODEV) {
+ if (!(opts & PF_OPT_QUIET))
+ fprintf(stderr, "No ALTQ support in kernel\n"
+ "ALTQ related functions disabled\n");
+ return (0);
+ } else
+ err(1, "DIOCGETALTQS");
+ }
+ return (1);
+}
+
+int
+pfctl_show_anchors(int dev, int opts, char *anchorname)
+{
+ struct pfioc_ruleset pr;
+ u_int32_t mnr, nr;
+
+ memset(&pr, 0, sizeof(pr));
+ memcpy(pr.path, anchorname, sizeof(pr.path));
+ if (ioctl(dev, DIOCGETRULESETS, &pr)) {
+ if (errno == EINVAL)
+ fprintf(stderr, "Anchor '%s' not found.\n",
+ anchorname);
+ else
+ err(1, "DIOCGETRULESETS");
+ return (-1);
+ }
+ mnr = pr.nr;
+ for (nr = 0; nr < mnr; ++nr) {
+ char sub[MAXPATHLEN];
+
+ pr.nr = nr;
+ if (ioctl(dev, DIOCGETRULESET, &pr))
+ err(1, "DIOCGETRULESET");
+ if (!strcmp(pr.name, PF_RESERVED_ANCHOR))
+ continue;
+ sub[0] = 0;
+ if (pr.path[0]) {
+ strlcat(sub, pr.path, sizeof(sub));
+ strlcat(sub, "/", sizeof(sub));
+ }
+ strlcat(sub, pr.name, sizeof(sub));
+ if (sub[0] != '_' || (opts & PF_OPT_VERBOSE))
+ printf(" %s\n", sub);
+ if ((opts & PF_OPT_VERBOSE) && pfctl_show_anchors(dev, opts, sub))
+ return (-1);
+ }
+ return (0);
+}
+
+const char *
+pfctl_lookup_option(char *cmd, const char **list)
+{
+ if (cmd != NULL && *cmd)
+ for (; *list; list++)
+ if (!strncmp(cmd, *list, strlen(cmd)))
+ return (*list);
+ return (NULL);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int error = 0;
+ int ch;
+ int mode = O_RDONLY;
+ int opts = 0;
+ int optimize = PF_OPTIMIZE_BASIC;
+ char anchorname[MAXPATHLEN];
+ char *path;
+
+ if (argc < 2)
+ usage();
+
+ while ((ch = getopt(argc, argv,
+ "a:AdD:eqf:F:ghi:k:K:mnNOo:Pp:rRs:t:T:vx:z")) != -1) {
+ switch (ch) {
+ case 'a':
+ anchoropt = optarg;
+ break;
+ case 'd':
+ opts |= PF_OPT_DISABLE;
+ mode = O_RDWR;
+ break;
+ case 'D':
+ if (pfctl_cmdline_symset(optarg) < 0)
+ warnx("could not parse macro definition %s",
+ optarg);
+ break;
+ case 'e':
+ opts |= PF_OPT_ENABLE;
+ mode = O_RDWR;
+ break;
+ case 'q':
+ opts |= PF_OPT_QUIET;
+ break;
+ case 'F':
+ clearopt = pfctl_lookup_option(optarg, clearopt_list);
+ if (clearopt == NULL) {
+ warnx("Unknown flush modifier '%s'", optarg);
+ usage();
+ }
+ mode = O_RDWR;
+ break;
+ case 'i':
+ ifaceopt = optarg;
+ break;
+ case 'k':
+ if (state_killers >= 2) {
+ warnx("can only specify -k twice");
+ usage();
+ /* NOTREACHED */
+ }
+ state_kill[state_killers++] = optarg;
+ mode = O_RDWR;
+ break;
+ case 'K':
+ if (src_node_killers >= 2) {
+ warnx("can only specify -K twice");
+ usage();
+ /* NOTREACHED */
+ }
+ src_node_kill[src_node_killers++] = optarg;
+ mode = O_RDWR;
+ break;
+ case 'm':
+ opts |= PF_OPT_MERGE;
+ break;
+ case 'n':
+ opts |= PF_OPT_NOACTION;
+ break;
+ case 'N':
+ loadopt |= PFCTL_FLAG_NAT;
+ break;
+ case 'r':
+ opts |= PF_OPT_USEDNS;
+ break;
+ case 'f':
+ rulesopt = optarg;
+ mode = O_RDWR;
+ break;
+ case 'g':
+ opts |= PF_OPT_DEBUG;
+ break;
+ case 'A':
+ loadopt |= PFCTL_FLAG_ALTQ;
+ break;
+ case 'R':
+ loadopt |= PFCTL_FLAG_FILTER;
+ break;
+ case 'o':
+ optiopt = pfctl_lookup_option(optarg, optiopt_list);
+ if (optiopt == NULL) {
+ warnx("Unknown optimization '%s'", optarg);
+ usage();
+ }
+ opts |= PF_OPT_OPTIMIZE;
+ break;
+ case 'O':
+ loadopt |= PFCTL_FLAG_OPTION;
+ break;
+ case 'p':
+ pf_device = optarg;
+ break;
+ case 'P':
+ opts |= PF_OPT_NUMERIC;
+ break;
+ case 's':
+ showopt = pfctl_lookup_option(optarg, showopt_list);
+ if (showopt == NULL) {
+ warnx("Unknown show modifier '%s'", optarg);
+ usage();
+ }
+ break;
+ case 't':
+ tableopt = optarg;
+ break;
+ case 'T':
+ tblcmdopt = pfctl_lookup_option(optarg, tblcmdopt_list);
+ if (tblcmdopt == NULL) {
+ warnx("Unknown table command '%s'", optarg);
+ usage();
+ }
+ break;
+ case 'v':
+ if (opts & PF_OPT_VERBOSE)
+ opts |= PF_OPT_VERBOSE2;
+ opts |= PF_OPT_VERBOSE;
+ break;
+ case 'x':
+ debugopt = pfctl_lookup_option(optarg, debugopt_list);
+ if (debugopt == NULL) {
+ warnx("Unknown debug level '%s'", optarg);
+ usage();
+ }
+ mode = O_RDWR;
+ break;
+ case 'z':
+ opts |= PF_OPT_CLRRULECTRS;
+ mode = O_RDWR;
+ break;
+ case 'h':
+ /* FALLTHROUGH */
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ if (tblcmdopt != NULL) {
+ argc -= optind;
+ argv += optind;
+ ch = *tblcmdopt;
+ if (ch == 'l') {
+ loadopt |= PFCTL_FLAG_TABLE;
+ tblcmdopt = NULL;
+ } else
+ mode = strchr("acdefkrz", ch) ? O_RDWR : O_RDONLY;
+ } else if (argc != optind) {
+ warnx("unknown command line argument: %s ...", argv[optind]);
+ usage();
+ /* NOTREACHED */
+ }
+ if (loadopt == 0)
+ loadopt = ~0;
+
+ if ((path = calloc(1, MAXPATHLEN)) == NULL)
+ errx(1, "pfctl: calloc");
+ memset(anchorname, 0, sizeof(anchorname));
+ if (anchoropt != NULL) {
+ int len = strlen(anchoropt);
+
+ if (anchoropt[len - 1] == '*') {
+ if (len >= 2 && anchoropt[len - 2] == '/')
+ anchoropt[len - 2] = '\0';
+ else
+ anchoropt[len - 1] = '\0';
+ opts |= PF_OPT_RECURSE;
+ }
+ if (strlcpy(anchorname, anchoropt,
+ sizeof(anchorname)) >= sizeof(anchorname))
+ errx(1, "anchor name '%s' too long",
+ anchoropt);
+ loadopt &= PFCTL_FLAG_FILTER|PFCTL_FLAG_NAT|PFCTL_FLAG_TABLE;
+ }
+
+ if ((opts & PF_OPT_NOACTION) == 0) {
+ dev = open(pf_device, mode);
+ if (dev == -1)
+ err(1, "%s", pf_device);
+ altqsupport = pfctl_test_altqsupport(dev, opts);
+ } else {
+ dev = open(pf_device, O_RDONLY);
+ if (dev >= 0)
+ opts |= PF_OPT_DUMMYACTION;
+ /* turn off options */
+ opts &= ~ (PF_OPT_DISABLE | PF_OPT_ENABLE);
+ clearopt = showopt = debugopt = NULL;
+#if defined(__FreeBSD__) && !defined(ENABLE_ALTQ)
+ altqsupport = 0;
+#else
+ altqsupport = 1;
+#endif
+ }
+
+ if (opts & PF_OPT_DISABLE)
+ if (pfctl_disable(dev, opts))
+ error = 1;
+
+ if (showopt != NULL) {
+ switch (*showopt) {
+ case 'A':
+ pfctl_show_anchors(dev, opts, anchorname);
+ break;
+ case 'r':
+ pfctl_load_fingerprints(dev, opts);
+ pfctl_show_rules(dev, path, opts, PFCTL_SHOW_RULES,
+ anchorname, 0);
+ break;
+ case 'l':
+ pfctl_load_fingerprints(dev, opts);
+ pfctl_show_rules(dev, path, opts, PFCTL_SHOW_LABELS,
+ anchorname, 0);
+ break;
+ case 'n':
+ pfctl_load_fingerprints(dev, opts);
+ pfctl_show_nat(dev, opts, anchorname);
+ break;
+ case 'q':
+ pfctl_show_altq(dev, ifaceopt, opts,
+ opts & PF_OPT_VERBOSE2);
+ break;
+ case 's':
+ pfctl_show_states(dev, ifaceopt, opts);
+ break;
+ case 'S':
+ pfctl_show_src_nodes(dev, opts);
+ break;
+ case 'i':
+ pfctl_show_status(dev, opts);
+ break;
+ case 't':
+ pfctl_show_timeouts(dev, opts);
+ break;
+ case 'm':
+ pfctl_show_limits(dev, opts);
+ break;
+ case 'a':
+ opts |= PF_OPT_SHOWALL;
+ pfctl_load_fingerprints(dev, opts);
+
+ pfctl_show_nat(dev, opts, anchorname);
+ pfctl_show_rules(dev, path, opts, 0, anchorname, 0);
+ pfctl_show_altq(dev, ifaceopt, opts, 0);
+ pfctl_show_states(dev, ifaceopt, opts);
+ pfctl_show_src_nodes(dev, opts);
+ pfctl_show_status(dev, opts);
+ pfctl_show_rules(dev, path, opts, 1, anchorname, 0);
+ pfctl_show_timeouts(dev, opts);
+ pfctl_show_limits(dev, opts);
+ pfctl_show_tables(anchorname, opts);
+ pfctl_show_fingerprints(opts);
+ break;
+ case 'T':
+ pfctl_show_tables(anchorname, opts);
+ break;
+ case 'o':
+ pfctl_load_fingerprints(dev, opts);
+ pfctl_show_fingerprints(opts);
+ break;
+ case 'I':
+ pfctl_show_ifaces(ifaceopt, opts);
+ break;
+ }
+ }
+
+ if ((opts & PF_OPT_CLRRULECTRS) && showopt == NULL)
+ pfctl_show_rules(dev, path, opts, PFCTL_SHOW_NOTHING,
+ anchorname, 0);
+
+ if (clearopt != NULL) {
+ if (anchorname[0] == '_' || strstr(anchorname, "/_") != NULL)
+ errx(1, "anchor names beginning with '_' cannot "
+ "be modified from the command line");
+
+ switch (*clearopt) {
+ case 'r':
+ pfctl_clear_rules(dev, opts, anchorname);
+ break;
+ case 'n':
+ pfctl_clear_nat(dev, opts, anchorname);
+ break;
+ case 'q':
+ pfctl_clear_altq(dev, opts);
+ break;
+ case 's':
+ pfctl_clear_states(dev, ifaceopt, opts);
+ break;
+ case 'S':
+ pfctl_clear_src_nodes(dev, opts);
+ break;
+ case 'i':
+ pfctl_clear_stats(dev, opts);
+ break;
+ case 'a':
+ pfctl_clear_rules(dev, opts, anchorname);
+ pfctl_clear_nat(dev, opts, anchorname);
+ pfctl_clear_tables(anchorname, opts);
+ if (!*anchorname) {
+ pfctl_clear_altq(dev, opts);
+ pfctl_clear_states(dev, ifaceopt, opts);
+ pfctl_clear_src_nodes(dev, opts);
+ pfctl_clear_stats(dev, opts);
+ pfctl_clear_fingerprints(dev, opts);
+ pfctl_clear_interface_flags(dev, opts);
+ }
+ break;
+ case 'o':
+ pfctl_clear_fingerprints(dev, opts);
+ break;
+ case 'T':
+ pfctl_clear_tables(anchorname, opts);
+ break;
+ }
+ }
+ if (state_killers) {
+ if (!strcmp(state_kill[0], "label"))
+ pfctl_label_kill_states(dev, ifaceopt, opts);
+ else if (!strcmp(state_kill[0], "id"))
+ pfctl_id_kill_states(dev, ifaceopt, opts);
+ else
+ pfctl_net_kill_states(dev, ifaceopt, opts);
+ }
+
+ if (src_node_killers)
+ pfctl_kill_src_nodes(dev, ifaceopt, opts);
+
+ if (tblcmdopt != NULL) {
+ error = pfctl_command_tables(argc, argv, tableopt,
+ tblcmdopt, rulesopt, anchorname, opts);
+ rulesopt = NULL;
+ }
+ if (optiopt != NULL) {
+ switch (*optiopt) {
+ case 'n':
+ optimize = 0;
+ break;
+ case 'b':
+ optimize |= PF_OPTIMIZE_BASIC;
+ break;
+ case 'o':
+ case 'p':
+ optimize |= PF_OPTIMIZE_PROFILE;
+ break;
+ }
+ }
+
+ if ((rulesopt != NULL) && (loadopt & PFCTL_FLAG_OPTION) &&
+ !anchorname[0])
+ if (pfctl_clear_interface_flags(dev, opts | PF_OPT_QUIET))
+ error = 1;
+
+ if (rulesopt != NULL && !(opts & (PF_OPT_MERGE|PF_OPT_NOACTION)) &&
+ !anchorname[0] && (loadopt & PFCTL_FLAG_OPTION))
+ if (pfctl_file_fingerprints(dev, opts, PF_OSFP_FILE))
+ error = 1;
+
+ if (rulesopt != NULL) {
+ if (anchorname[0] == '_' || strstr(anchorname, "/_") != NULL)
+ errx(1, "anchor names beginning with '_' cannot "
+ "be modified from the command line");
+ if (pfctl_rules(dev, rulesopt, opts, optimize,
+ anchorname, NULL))
+ error = 1;
+ else if (!(opts & PF_OPT_NOACTION) &&
+ (loadopt & PFCTL_FLAG_TABLE))
+ warn_namespace_collision(NULL);
+ }
+
+ if (opts & PF_OPT_ENABLE)
+ if (pfctl_enable(dev, opts))
+ error = 1;
+
+ if (debugopt != NULL) {
+ switch (*debugopt) {
+ case 'n':
+ pfctl_debug(dev, PF_DEBUG_NONE, opts);
+ break;
+ case 'u':
+ pfctl_debug(dev, PF_DEBUG_URGENT, opts);
+ break;
+ case 'm':
+ pfctl_debug(dev, PF_DEBUG_MISC, opts);
+ break;
+ case 'l':
+ pfctl_debug(dev, PF_DEBUG_NOISY, opts);
+ break;
+ }
+ }
+
+ exit(error);
+}
diff --git a/sbin/pfctl/pfctl.h b/sbin/pfctl/pfctl.h
new file mode 100644
index 000000000000..2c69bc2068b0
--- /dev/null
+++ b/sbin/pfctl/pfctl.h
@@ -0,0 +1,130 @@
+/* $OpenBSD: pfctl.h,v 1.42 2007/12/05 12:01:47 chl Exp $ */
+
+/*
+ * Copyright (c) 2001 Daniel Hartmeier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PFCTL_H_
+#define _PFCTL_H_
+
+enum pfctl_show { PFCTL_SHOW_RULES, PFCTL_SHOW_LABELS, PFCTL_SHOW_NOTHING };
+
+enum { PFRB_TABLES = 1, PFRB_TSTATS, PFRB_ADDRS, PFRB_ASTATS,
+ PFRB_IFACES, PFRB_TRANS, PFRB_MAX };
+struct pfr_buffer {
+ int pfrb_type; /* type of content, see enum above */
+ int pfrb_size; /* number of objects in buffer */
+ int pfrb_msize; /* maximum number of objects in buffer */
+ void *pfrb_caddr; /* malloc'ated memory area */
+};
+#define PFRB_FOREACH(var, buf) \
+ for ((var) = pfr_buf_next((buf), NULL); \
+ (var) != NULL; \
+ (var) = pfr_buf_next((buf), (var)))
+
+int pfr_get_fd(void);
+int pfr_clr_tables(struct pfr_table *, int *, int);
+int pfr_add_tables(struct pfr_table *, int, int *, int);
+int pfr_del_tables(struct pfr_table *, int, int *, int);
+int pfr_get_tables(struct pfr_table *, struct pfr_table *, int *, int);
+int pfr_get_tstats(struct pfr_table *, struct pfr_tstats *, int *, int);
+int pfr_clr_tstats(struct pfr_table *, int, int *, int);
+int pfr_clr_addrs(struct pfr_table *, int *, int);
+int pfr_add_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
+int pfr_del_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
+int pfr_set_addrs(struct pfr_table *, struct pfr_addr *, int, int *,
+ int *, int *, int *, int);
+int pfr_get_addrs(struct pfr_table *, struct pfr_addr *, int *, int);
+int pfr_get_astats(struct pfr_table *, struct pfr_astats *, int *, int);
+int pfr_tst_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
+int pfr_ina_define(struct pfr_table *, struct pfr_addr *, int, int *,
+ int *, int, int);
+void pfr_buf_clear(struct pfr_buffer *);
+int pfr_buf_add(struct pfr_buffer *, const void *);
+void *pfr_buf_next(struct pfr_buffer *, const void *);
+int pfr_buf_grow(struct pfr_buffer *, int);
+int pfr_buf_load(struct pfr_buffer *, char *, int,
+ int (*)(struct pfr_buffer *, char *, int));
+char *pfr_strerror(int);
+int pfi_get_ifaces(const char *, struct pfi_kif *, int *);
+int pfi_clr_istats(const char *, int *, int);
+
+void pfctl_print_title(char *);
+int pfctl_clear_tables(const char *, int);
+int pfctl_show_tables(const char *, int);
+int pfctl_command_tables(int, char *[], char *, const char *, char *,
+ const char *, int);
+int pfctl_show_altq(int, const char *, int, int);
+void warn_namespace_collision(const char *);
+int pfctl_show_ifaces(const char *, int);
+FILE *pfctl_fopen(const char *, const char *);
+
+#ifdef __FreeBSD__
+extern int altqsupport;
+extern int dummynetsupport;
+#define HTONL(x) (x) = htonl((__uint32_t)(x))
+#endif
+
+#ifndef DEFAULT_PRIORITY
+#define DEFAULT_PRIORITY 1
+#endif
+
+#ifndef DEFAULT_QLIMIT
+#define DEFAULT_QLIMIT 50
+#endif
+
+/*
+ * generalized service curve used for admission control
+ */
+struct segment {
+ LIST_ENTRY(segment) _next;
+ double x, y, d, m;
+};
+
+extern int loadopt;
+
+int check_commit_altq(int, int);
+void pfaltq_store(struct pf_altq *);
+struct pf_altq *pfaltq_lookup(const char *);
+char *rate2str(double);
+
+void print_addr(struct pf_addr_wrap *, sa_family_t, int);
+void print_host(struct pf_addr *, u_int16_t p, sa_family_t, int);
+void print_seq(struct pfsync_state_peer *);
+void print_state(struct pfsync_state *, int);
+int unmask(struct pf_addr *, sa_family_t);
+
+int pfctl_cmdline_symset(char *);
+int pfctl_add_trans(struct pfr_buffer *, int, const char *);
+u_int32_t
+ pfctl_get_ticket(struct pfr_buffer *, int, const char *);
+int pfctl_trans(int, struct pfr_buffer *, u_long, int);
+
+#endif /* _PFCTL_H_ */
diff --git a/sbin/pfctl/pfctl_altq.c b/sbin/pfctl/pfctl_altq.c
new file mode 100644
index 000000000000..40e11d5a9eb9
--- /dev/null
+++ b/sbin/pfctl/pfctl_altq.c
@@ -0,0 +1,1258 @@
+/* $OpenBSD: pfctl_altq.c,v 1.93 2007/10/15 02:16:35 deraadt Exp $ */
+
+/*
+ * Copyright (c) 2002
+ * Sony Computer Science Laboratories Inc.
+ * Copyright (c) 2002, 2003 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <net/pfvar.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <altq/altq.h>
+#include <altq/altq_cbq.h>
+#include <altq/altq_priq.h>
+#include <altq/altq_hfsc.h>
+
+#include "pfctl_parser.h"
+#include "pfctl.h"
+
+#define is_sc_null(sc) (((sc) == NULL) || ((sc)->m1 == 0 && (sc)->m2 == 0))
+
+TAILQ_HEAD(altqs, pf_altq) altqs = TAILQ_HEAD_INITIALIZER(altqs);
+LIST_HEAD(gen_sc, segment) rtsc, lssc;
+
+struct pf_altq *qname_to_pfaltq(const char *, const char *);
+u_int32_t qname_to_qid(const char *);
+
+static int eval_pfqueue_cbq(struct pfctl *, struct pf_altq *);
+static int cbq_compute_idletime(struct pfctl *, struct pf_altq *);
+static int check_commit_cbq(int, int, struct pf_altq *);
+static int print_cbq_opts(const struct pf_altq *);
+
+static int eval_pfqueue_priq(struct pfctl *, struct pf_altq *);
+static int check_commit_priq(int, int, struct pf_altq *);
+static int print_priq_opts(const struct pf_altq *);
+
+static int eval_pfqueue_hfsc(struct pfctl *, struct pf_altq *);
+static int check_commit_hfsc(int, int, struct pf_altq *);
+static int print_hfsc_opts(const struct pf_altq *,
+ const struct node_queue_opt *);
+
+static void gsc_add_sc(struct gen_sc *, struct service_curve *);
+static int is_gsc_under_sc(struct gen_sc *,
+ struct service_curve *);
+static void gsc_destroy(struct gen_sc *);
+static struct segment *gsc_getentry(struct gen_sc *, double);
+static int gsc_add_seg(struct gen_sc *, double, double, double,
+ double);
+static double sc_x2y(struct service_curve *, double);
+
+#ifdef __FreeBSD__
+u_int32_t getifspeed(int, char *);
+#else
+u_int32_t getifspeed(char *);
+#endif
+u_long getifmtu(char *);
+int eval_queue_opts(struct pf_altq *, struct node_queue_opt *,
+ u_int32_t);
+u_int32_t eval_bwspec(struct node_queue_bw *, u_int32_t);
+void print_hfsc_sc(const char *, u_int, u_int, u_int,
+ const struct node_hfsc_sc *);
+
+void
+pfaltq_store(struct pf_altq *a)
+{
+ struct pf_altq *altq;
+
+ if ((altq = malloc(sizeof(*altq))) == NULL)
+ err(1, "malloc");
+ memcpy(altq, a, sizeof(struct pf_altq));
+ TAILQ_INSERT_TAIL(&altqs, altq, entries);
+}
+
+struct pf_altq *
+pfaltq_lookup(const char *ifname)
+{
+ struct pf_altq *altq;
+
+ TAILQ_FOREACH(altq, &altqs, entries) {
+ if (strncmp(ifname, altq->ifname, IFNAMSIZ) == 0 &&
+ altq->qname[0] == 0)
+ return (altq);
+ }
+ return (NULL);
+}
+
+struct pf_altq *
+qname_to_pfaltq(const char *qname, const char *ifname)
+{
+ struct pf_altq *altq;
+
+ TAILQ_FOREACH(altq, &altqs, entries) {
+ if (strncmp(ifname, altq->ifname, IFNAMSIZ) == 0 &&
+ strncmp(qname, altq->qname, PF_QNAME_SIZE) == 0)
+ return (altq);
+ }
+ return (NULL);
+}
+
+u_int32_t
+qname_to_qid(const char *qname)
+{
+ struct pf_altq *altq;
+
+ /*
+ * We guarantee that same named queues on different interfaces
+ * have the same qid, so we do NOT need to limit matching on
+ * one interface!
+ */
+
+ TAILQ_FOREACH(altq, &altqs, entries) {
+ if (strncmp(qname, altq->qname, PF_QNAME_SIZE) == 0)
+ return (altq->qid);
+ }
+ return (0);
+}
+
+void
+print_altq(const struct pf_altq *a, unsigned int level,
+ struct node_queue_bw *bw, struct node_queue_opt *qopts)
+{
+ if (a->qname[0] != 0) {
+ print_queue(a, level, bw, 1, qopts);
+ return;
+ }
+
+#ifdef __FreeBSD__
+ if (a->local_flags & PFALTQ_FLAG_IF_REMOVED)
+ printf("INACTIVE ");
+#endif
+
+ printf("altq on %s ", a->ifname);
+
+ switch (a->scheduler) {
+ case ALTQT_CBQ:
+ if (!print_cbq_opts(a))
+ printf("cbq ");
+ break;
+ case ALTQT_PRIQ:
+ if (!print_priq_opts(a))
+ printf("priq ");
+ break;
+ case ALTQT_HFSC:
+ if (!print_hfsc_opts(a, qopts))
+ printf("hfsc ");
+ break;
+ }
+
+ if (bw != NULL && bw->bw_percent > 0) {
+ if (bw->bw_percent < 100)
+ printf("bandwidth %u%% ", bw->bw_percent);
+ } else
+ printf("bandwidth %s ", rate2str((double)a->ifbandwidth));
+
+ if (a->qlimit != DEFAULT_QLIMIT)
+ printf("qlimit %u ", a->qlimit);
+ printf("tbrsize %u ", a->tbrsize);
+}
+
+void
+print_queue(const struct pf_altq *a, unsigned int level,
+ struct node_queue_bw *bw, int print_interface,
+ struct node_queue_opt *qopts)
+{
+ unsigned int i;
+
+#ifdef __FreeBSD__
+ if (a->local_flags & PFALTQ_FLAG_IF_REMOVED)
+ printf("INACTIVE ");
+#endif
+ printf("queue ");
+ for (i = 0; i < level; ++i)
+ printf(" ");
+ printf("%s ", a->qname);
+ if (print_interface)
+ printf("on %s ", a->ifname);
+ if (a->scheduler == ALTQT_CBQ || a->scheduler == ALTQT_HFSC) {
+ if (bw != NULL && bw->bw_percent > 0) {
+ if (bw->bw_percent < 100)
+ printf("bandwidth %u%% ", bw->bw_percent);
+ } else
+ printf("bandwidth %s ", rate2str((double)a->bandwidth));
+ }
+ if (a->priority != DEFAULT_PRIORITY)
+ printf("priority %u ", a->priority);
+ if (a->qlimit != DEFAULT_QLIMIT)
+ printf("qlimit %u ", a->qlimit);
+ switch (a->scheduler) {
+ case ALTQT_CBQ:
+ print_cbq_opts(a);
+ break;
+ case ALTQT_PRIQ:
+ print_priq_opts(a);
+ break;
+ case ALTQT_HFSC:
+ print_hfsc_opts(a, qopts);
+ break;
+ }
+}
+
+/*
+ * eval_pfaltq computes the discipline parameters.
+ */
+int
+eval_pfaltq(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw,
+ struct node_queue_opt *opts)
+{
+ u_int rate, size, errors = 0;
+
+ if (bw->bw_absolute > 0)
+ pa->ifbandwidth = bw->bw_absolute;
+ else
+#ifdef __FreeBSD__
+ if ((rate = getifspeed(pf->dev, pa->ifname)) == 0) {
+#else
+ if ((rate = getifspeed(pa->ifname)) == 0) {
+#endif
+ fprintf(stderr, "interface %s does not know its bandwidth, "
+ "please specify an absolute bandwidth\n",
+ pa->ifname);
+ errors++;
+ } else if ((pa->ifbandwidth = eval_bwspec(bw, rate)) == 0)
+ pa->ifbandwidth = rate;
+
+ errors += eval_queue_opts(pa, opts, pa->ifbandwidth);
+
+ /* if tbrsize is not specified, use heuristics */
+ if (pa->tbrsize == 0) {
+ rate = pa->ifbandwidth;
+ if (rate <= 1 * 1000 * 1000)
+ size = 1;
+ else if (rate <= 10 * 1000 * 1000)
+ size = 4;
+ else if (rate <= 200 * 1000 * 1000)
+ size = 8;
+ else
+ size = 24;
+ size = size * getifmtu(pa->ifname);
+ if (size > 0xffff)
+ size = 0xffff;
+ pa->tbrsize = size;
+ }
+ return (errors);
+}
+
+/*
+ * check_commit_altq does consistency check for each interface
+ */
+int
+check_commit_altq(int dev, int opts)
+{
+ struct pf_altq *altq;
+ int error = 0;
+
+ /* call the discipline check for each interface. */
+ TAILQ_FOREACH(altq, &altqs, entries) {
+ if (altq->qname[0] == 0) {
+ switch (altq->scheduler) {
+ case ALTQT_CBQ:
+ error = check_commit_cbq(dev, opts, altq);
+ break;
+ case ALTQT_PRIQ:
+ error = check_commit_priq(dev, opts, altq);
+ break;
+ case ALTQT_HFSC:
+ error = check_commit_hfsc(dev, opts, altq);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return (error);
+}
+
+/*
+ * eval_pfqueue computes the queue parameters.
+ */
+int
+eval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw,
+ struct node_queue_opt *opts)
+{
+ /* should be merged with expand_queue */
+ struct pf_altq *if_pa, *parent, *altq;
+ u_int32_t bwsum;
+ int error = 0;
+
+ /* find the corresponding interface and copy fields used by queues */
+ if ((if_pa = pfaltq_lookup(pa->ifname)) == NULL) {
+ fprintf(stderr, "altq not defined on %s\n", pa->ifname);
+ return (1);
+ }
+ pa->scheduler = if_pa->scheduler;
+ pa->ifbandwidth = if_pa->ifbandwidth;
+
+ if (qname_to_pfaltq(pa->qname, pa->ifname) != NULL) {
+ fprintf(stderr, "queue %s already exists on interface %s\n",
+ pa->qname, pa->ifname);
+ return (1);
+ }
+ pa->qid = qname_to_qid(pa->qname);
+
+ parent = NULL;
+ if (pa->parent[0] != 0) {
+ parent = qname_to_pfaltq(pa->parent, pa->ifname);
+ if (parent == NULL) {
+ fprintf(stderr, "parent %s not found for %s\n",
+ pa->parent, pa->qname);
+ return (1);
+ }
+ pa->parent_qid = parent->qid;
+ }
+ if (pa->qlimit == 0)
+ pa->qlimit = DEFAULT_QLIMIT;
+
+ if (pa->scheduler == ALTQT_CBQ || pa->scheduler == ALTQT_HFSC) {
+ pa->bandwidth = eval_bwspec(bw,
+ parent == NULL ? 0 : parent->bandwidth);
+
+ if (pa->bandwidth > pa->ifbandwidth) {
+ fprintf(stderr, "bandwidth for %s higher than "
+ "interface\n", pa->qname);
+ return (1);
+ }
+ /* check the sum of the child bandwidth is under parent's */
+ if (parent != NULL) {
+ if (pa->bandwidth > parent->bandwidth) {
+ warnx("bandwidth for %s higher than parent",
+ pa->qname);
+ return (1);
+ }
+ bwsum = 0;
+ TAILQ_FOREACH(altq, &altqs, entries) {
+ if (strncmp(altq->ifname, pa->ifname,
+ IFNAMSIZ) == 0 &&
+ altq->qname[0] != 0 &&
+ strncmp(altq->parent, pa->parent,
+ PF_QNAME_SIZE) == 0)
+ bwsum += altq->bandwidth;
+ }
+ bwsum += pa->bandwidth;
+ if (bwsum > parent->bandwidth) {
+ warnx("the sum of the child bandwidth higher"
+ " than parent \"%s\"", parent->qname);
+ }
+ }
+ }
+
+ if (eval_queue_opts(pa, opts, parent == NULL? 0 : parent->bandwidth))
+ return (1);
+
+ switch (pa->scheduler) {
+ case ALTQT_CBQ:
+ error = eval_pfqueue_cbq(pf, pa);
+ break;
+ case ALTQT_PRIQ:
+ error = eval_pfqueue_priq(pf, pa);
+ break;
+ case ALTQT_HFSC:
+ error = eval_pfqueue_hfsc(pf, pa);
+ break;
+ default:
+ break;
+ }
+ return (error);
+}
+
+/*
+ * CBQ support functions
+ */
+#define RM_FILTER_GAIN 5 /* log2 of gain, e.g., 5 => 31/32 */
+#define RM_NS_PER_SEC (1000000000)
+
+static int
+eval_pfqueue_cbq(struct pfctl *pf, struct pf_altq *pa)
+{
+ struct cbq_opts *opts;
+ u_int ifmtu;
+
+ if (pa->priority >= CBQ_MAXPRI) {
+ warnx("priority out of range: max %d", CBQ_MAXPRI - 1);
+ return (-1);
+ }
+
+ ifmtu = getifmtu(pa->ifname);
+ opts = &pa->pq_u.cbq_opts;
+
+ if (opts->pktsize == 0) { /* use default */
+ opts->pktsize = ifmtu;
+ if (opts->pktsize > MCLBYTES) /* do what TCP does */
+ opts->pktsize &= ~MCLBYTES;
+ } else if (opts->pktsize > ifmtu)
+ opts->pktsize = ifmtu;
+ if (opts->maxpktsize == 0) /* use default */
+ opts->maxpktsize = ifmtu;
+ else if (opts->maxpktsize > ifmtu)
+ opts->pktsize = ifmtu;
+
+ if (opts->pktsize > opts->maxpktsize)
+ opts->pktsize = opts->maxpktsize;
+
+ if (pa->parent[0] == 0)
+ opts->flags |= (CBQCLF_ROOTCLASS | CBQCLF_WRR);
+
+ cbq_compute_idletime(pf, pa);
+ return (0);
+}
+
+/*
+ * compute ns_per_byte, maxidle, minidle, and offtime
+ */
+static int
+cbq_compute_idletime(struct pfctl *pf, struct pf_altq *pa)
+{
+ struct cbq_opts *opts;
+ double maxidle_s, maxidle, minidle;
+ double offtime, nsPerByte, ifnsPerByte, ptime, cptime;
+ double z, g, f, gton, gtom;
+ u_int minburst, maxburst;
+
+ opts = &pa->pq_u.cbq_opts;
+ ifnsPerByte = (1.0 / (double)pa->ifbandwidth) * RM_NS_PER_SEC * 8;
+ minburst = opts->minburst;
+ maxburst = opts->maxburst;
+
+ if (pa->bandwidth == 0)
+ f = 0.0001; /* small enough? */
+ else
+ f = ((double) pa->bandwidth / (double) pa->ifbandwidth);
+
+ nsPerByte = ifnsPerByte / f;
+ ptime = (double)opts->pktsize * ifnsPerByte;
+ cptime = ptime * (1.0 - f) / f;
+
+ if (nsPerByte * (double)opts->maxpktsize > (double)INT_MAX) {
+ /*
+ * this causes integer overflow in kernel!
+ * (bandwidth < 6Kbps when max_pkt_size=1500)
+ */
+ if (pa->bandwidth != 0 && (pf->opts & PF_OPT_QUIET) == 0)
+ warnx("queue bandwidth must be larger than %s",
+ rate2str(ifnsPerByte * (double)opts->maxpktsize /
+ (double)INT_MAX * (double)pa->ifbandwidth));
+ fprintf(stderr, "cbq: queue %s is too slow!\n",
+ pa->qname);
+ nsPerByte = (double)(INT_MAX / opts->maxpktsize);
+ }
+
+ if (maxburst == 0) { /* use default */
+ if (cptime > 10.0 * 1000000)
+ maxburst = 4;
+ else
+ maxburst = 16;
+ }
+ if (minburst == 0) /* use default */
+ minburst = 2;
+ if (minburst > maxburst)
+ minburst = maxburst;
+
+ z = (double)(1 << RM_FILTER_GAIN);
+ g = (1.0 - 1.0 / z);
+ gton = pow(g, (double)maxburst);
+ gtom = pow(g, (double)(minburst-1));
+ maxidle = ((1.0 / f - 1.0) * ((1.0 - gton) / gton));
+ maxidle_s = (1.0 - g);
+ if (maxidle > maxidle_s)
+ maxidle = ptime * maxidle;
+ else
+ maxidle = ptime * maxidle_s;
+ offtime = cptime * (1.0 + 1.0/(1.0 - g) * (1.0 - gtom) / gtom);
+ minidle = -((double)opts->maxpktsize * (double)nsPerByte);
+
+ /* scale parameters */
+ maxidle = ((maxidle * 8.0) / nsPerByte) *
+ pow(2.0, (double)RM_FILTER_GAIN);
+ offtime = (offtime * 8.0) / nsPerByte *
+ pow(2.0, (double)RM_FILTER_GAIN);
+ minidle = ((minidle * 8.0) / nsPerByte) *
+ pow(2.0, (double)RM_FILTER_GAIN);
+
+ maxidle = maxidle / 1000.0;
+ offtime = offtime / 1000.0;
+ minidle = minidle / 1000.0;
+
+ opts->minburst = minburst;
+ opts->maxburst = maxburst;
+ opts->ns_per_byte = (u_int)nsPerByte;
+ opts->maxidle = (u_int)fabs(maxidle);
+ opts->minidle = (int)minidle;
+ opts->offtime = (u_int)fabs(offtime);
+
+ return (0);
+}
+
+static int
+check_commit_cbq(int dev, int opts, struct pf_altq *pa)
+{
+ struct pf_altq *altq;
+ int root_class, default_class;
+ int error = 0;
+
+ /*
+ * check if cbq has one root queue and one default queue
+ * for this interface
+ */
+ root_class = default_class = 0;
+ TAILQ_FOREACH(altq, &altqs, entries) {
+ if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0)
+ continue;
+ if (altq->qname[0] == 0) /* this is for interface */
+ continue;
+ if (altq->pq_u.cbq_opts.flags & CBQCLF_ROOTCLASS)
+ root_class++;
+ if (altq->pq_u.cbq_opts.flags & CBQCLF_DEFCLASS)
+ default_class++;
+ }
+ if (root_class != 1) {
+ warnx("should have one root queue on %s", pa->ifname);
+ error++;
+ }
+ if (default_class != 1) {
+ warnx("should have one default queue on %s", pa->ifname);
+ error++;
+ }
+ return (error);
+}
+
+static int
+print_cbq_opts(const struct pf_altq *a)
+{
+ const struct cbq_opts *opts;
+
+ opts = &a->pq_u.cbq_opts;
+ if (opts->flags) {
+ printf("cbq(");
+ if (opts->flags & CBQCLF_RED)
+ printf(" red");
+ if (opts->flags & CBQCLF_ECN)
+ printf(" ecn");
+ if (opts->flags & CBQCLF_RIO)
+ printf(" rio");
+ if (opts->flags & CBQCLF_CLEARDSCP)
+ printf(" cleardscp");
+ if (opts->flags & CBQCLF_FLOWVALVE)
+ printf(" flowvalve");
+ if (opts->flags & CBQCLF_BORROW)
+ printf(" borrow");
+ if (opts->flags & CBQCLF_WRR)
+ printf(" wrr");
+ if (opts->flags & CBQCLF_EFFICIENT)
+ printf(" efficient");
+ if (opts->flags & CBQCLF_ROOTCLASS)
+ printf(" root");
+ if (opts->flags & CBQCLF_DEFCLASS)
+ printf(" default");
+ printf(" ) ");
+
+ return (1);
+ } else
+ return (0);
+}
+
+/*
+ * PRIQ support functions
+ */
+static int
+eval_pfqueue_priq(struct pfctl *pf, struct pf_altq *pa)
+{
+ struct pf_altq *altq;
+
+ if (pa->priority >= PRIQ_MAXPRI) {
+ warnx("priority out of range: max %d", PRIQ_MAXPRI - 1);
+ return (-1);
+ }
+ /* the priority should be unique for the interface */
+ TAILQ_FOREACH(altq, &altqs, entries) {
+ if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) == 0 &&
+ altq->qname[0] != 0 && altq->priority == pa->priority) {
+ warnx("%s and %s have the same priority",
+ altq->qname, pa->qname);
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+check_commit_priq(int dev, int opts, struct pf_altq *pa)
+{
+ struct pf_altq *altq;
+ int default_class;
+ int error = 0;
+
+ /*
+ * check if priq has one default class for this interface
+ */
+ default_class = 0;
+ TAILQ_FOREACH(altq, &altqs, entries) {
+ if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0)
+ continue;
+ if (altq->qname[0] == 0) /* this is for interface */
+ continue;
+ if (altq->pq_u.priq_opts.flags & PRCF_DEFAULTCLASS)
+ default_class++;
+ }
+ if (default_class != 1) {
+ warnx("should have one default queue on %s", pa->ifname);
+ error++;
+ }
+ return (error);
+}
+
+static int
+print_priq_opts(const struct pf_altq *a)
+{
+ const struct priq_opts *opts;
+
+ opts = &a->pq_u.priq_opts;
+
+ if (opts->flags) {
+ printf("priq(");
+ if (opts->flags & PRCF_RED)
+ printf(" red");
+ if (opts->flags & PRCF_ECN)
+ printf(" ecn");
+ if (opts->flags & PRCF_RIO)
+ printf(" rio");
+ if (opts->flags & PRCF_CLEARDSCP)
+ printf(" cleardscp");
+ if (opts->flags & PRCF_DEFAULTCLASS)
+ printf(" default");
+ printf(" ) ");
+
+ return (1);
+ } else
+ return (0);
+}
+
+/*
+ * HFSC support functions
+ */
+static int
+eval_pfqueue_hfsc(struct pfctl *pf, struct pf_altq *pa)
+{
+ struct pf_altq *altq, *parent;
+ struct hfsc_opts *opts;
+ struct service_curve sc;
+
+ opts = &pa->pq_u.hfsc_opts;
+
+ if (pa->parent[0] == 0) {
+ /* root queue */
+ opts->lssc_m1 = pa->ifbandwidth;
+ opts->lssc_m2 = pa->ifbandwidth;
+ opts->lssc_d = 0;
+ return (0);
+ }
+
+ LIST_INIT(&rtsc);
+ LIST_INIT(&lssc);
+
+ /* if link_share is not specified, use bandwidth */
+ if (opts->lssc_m2 == 0)
+ opts->lssc_m2 = pa->bandwidth;
+
+ if ((opts->rtsc_m1 > 0 && opts->rtsc_m2 == 0) ||
+ (opts->lssc_m1 > 0 && opts->lssc_m2 == 0) ||
+ (opts->ulsc_m1 > 0 && opts->ulsc_m2 == 0)) {
+ warnx("m2 is zero for %s", pa->qname);
+ return (-1);
+ }
+
+ if ((opts->rtsc_m1 < opts->rtsc_m2 && opts->rtsc_m1 != 0) ||
+ (opts->lssc_m1 < opts->lssc_m2 && opts->lssc_m1 != 0) ||
+ (opts->ulsc_m1 < opts->ulsc_m2 && opts->ulsc_m1 != 0)) {
+ warnx("m1 must be zero for convex curve: %s", pa->qname);
+ return (-1);
+ }
+
+ /*
+ * admission control:
+ * for the real-time service curve, the sum of the service curves
+ * should not exceed 80% of the interface bandwidth. 20% is reserved
+ * not to over-commit the actual interface bandwidth.
+ * for the linkshare service curve, the sum of the child service
+ * curve should not exceed the parent service curve.
+ * for the upper-limit service curve, the assigned bandwidth should
+ * be smaller than the interface bandwidth, and the upper-limit should
+ * be larger than the real-time service curve when both are defined.
+ */
+ parent = qname_to_pfaltq(pa->parent, pa->ifname);
+ if (parent == NULL)
+ errx(1, "parent %s not found for %s", pa->parent, pa->qname);
+
+ TAILQ_FOREACH(altq, &altqs, entries) {
+ if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0)
+ continue;
+ if (altq->qname[0] == 0) /* this is for interface */
+ continue;
+
+ /* if the class has a real-time service curve, add it. */
+ if (opts->rtsc_m2 != 0 && altq->pq_u.hfsc_opts.rtsc_m2 != 0) {
+ sc.m1 = altq->pq_u.hfsc_opts.rtsc_m1;
+ sc.d = altq->pq_u.hfsc_opts.rtsc_d;
+ sc.m2 = altq->pq_u.hfsc_opts.rtsc_m2;
+ gsc_add_sc(&rtsc, &sc);
+ }
+
+ if (strncmp(altq->parent, pa->parent, PF_QNAME_SIZE) != 0)
+ continue;
+
+ /* if the class has a linkshare service curve, add it. */
+ if (opts->lssc_m2 != 0 && altq->pq_u.hfsc_opts.lssc_m2 != 0) {
+ sc.m1 = altq->pq_u.hfsc_opts.lssc_m1;
+ sc.d = altq->pq_u.hfsc_opts.lssc_d;
+ sc.m2 = altq->pq_u.hfsc_opts.lssc_m2;
+ gsc_add_sc(&lssc, &sc);
+ }
+ }
+
+ /* check the real-time service curve. reserve 20% of interface bw */
+ if (opts->rtsc_m2 != 0) {
+ /* add this queue to the sum */
+ sc.m1 = opts->rtsc_m1;
+ sc.d = opts->rtsc_d;
+ sc.m2 = opts->rtsc_m2;
+ gsc_add_sc(&rtsc, &sc);
+ /* compare the sum with 80% of the interface */
+ sc.m1 = 0;
+ sc.d = 0;
+ sc.m2 = pa->ifbandwidth / 100 * 80;
+ if (!is_gsc_under_sc(&rtsc, &sc)) {
+ warnx("real-time sc exceeds 80%% of the interface "
+ "bandwidth (%s)", rate2str((double)sc.m2));
+ goto err_ret;
+ }
+ }
+
+ /* check the linkshare service curve. */
+ if (opts->lssc_m2 != 0) {
+ /* add this queue to the child sum */
+ sc.m1 = opts->lssc_m1;
+ sc.d = opts->lssc_d;
+ sc.m2 = opts->lssc_m2;
+ gsc_add_sc(&lssc, &sc);
+ /* compare the sum of the children with parent's sc */
+ sc.m1 = parent->pq_u.hfsc_opts.lssc_m1;
+ sc.d = parent->pq_u.hfsc_opts.lssc_d;
+ sc.m2 = parent->pq_u.hfsc_opts.lssc_m2;
+ if (!is_gsc_under_sc(&lssc, &sc)) {
+ warnx("linkshare sc exceeds parent's sc");
+ goto err_ret;
+ }
+ }
+
+ /* check the upper-limit service curve. */
+ if (opts->ulsc_m2 != 0) {
+ if (opts->ulsc_m1 > pa->ifbandwidth ||
+ opts->ulsc_m2 > pa->ifbandwidth) {
+ warnx("upper-limit larger than interface bandwidth");
+ goto err_ret;
+ }
+ if (opts->rtsc_m2 != 0 && opts->rtsc_m2 > opts->ulsc_m2) {
+ warnx("upper-limit sc smaller than real-time sc");
+ goto err_ret;
+ }
+ }
+
+ gsc_destroy(&rtsc);
+ gsc_destroy(&lssc);
+
+ return (0);
+
+err_ret:
+ gsc_destroy(&rtsc);
+ gsc_destroy(&lssc);
+ return (-1);
+}
+
+static int
+check_commit_hfsc(int dev, int opts, struct pf_altq *pa)
+{
+ struct pf_altq *altq, *def = NULL;
+ int default_class;
+ int error = 0;
+
+ /* check if hfsc has one default queue for this interface */
+ default_class = 0;
+ TAILQ_FOREACH(altq, &altqs, entries) {
+ if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0)
+ continue;
+ if (altq->qname[0] == 0) /* this is for interface */
+ continue;
+ if (altq->parent[0] == 0) /* dummy root */
+ continue;
+ if (altq->pq_u.hfsc_opts.flags & HFCF_DEFAULTCLASS) {
+ default_class++;
+ def = altq;
+ }
+ }
+ if (default_class != 1) {
+ warnx("should have one default queue on %s", pa->ifname);
+ return (1);
+ }
+ /* make sure the default queue is a leaf */
+ TAILQ_FOREACH(altq, &altqs, entries) {
+ if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0)
+ continue;
+ if (altq->qname[0] == 0) /* this is for interface */
+ continue;
+ if (strncmp(altq->parent, def->qname, PF_QNAME_SIZE) == 0) {
+ warnx("default queue is not a leaf");
+ error++;
+ }
+ }
+ return (error);
+}
+
+static int
+print_hfsc_opts(const struct pf_altq *a, const struct node_queue_opt *qopts)
+{
+ const struct hfsc_opts *opts;
+ const struct node_hfsc_sc *rtsc, *lssc, *ulsc;
+
+ opts = &a->pq_u.hfsc_opts;
+ if (qopts == NULL)
+ rtsc = lssc = ulsc = NULL;
+ else {
+ rtsc = &qopts->data.hfsc_opts.realtime;
+ lssc = &qopts->data.hfsc_opts.linkshare;
+ ulsc = &qopts->data.hfsc_opts.upperlimit;
+ }
+
+ if (opts->flags || opts->rtsc_m2 != 0 || opts->ulsc_m2 != 0 ||
+ (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth ||
+ opts->lssc_d != 0))) {
+ printf("hfsc(");
+ if (opts->flags & HFCF_RED)
+ printf(" red");
+ if (opts->flags & HFCF_ECN)
+ printf(" ecn");
+ if (opts->flags & HFCF_RIO)
+ printf(" rio");
+ if (opts->flags & HFCF_CLEARDSCP)
+ printf(" cleardscp");
+ if (opts->flags & HFCF_DEFAULTCLASS)
+ printf(" default");
+ if (opts->rtsc_m2 != 0)
+ print_hfsc_sc("realtime", opts->rtsc_m1, opts->rtsc_d,
+ opts->rtsc_m2, rtsc);
+ if (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth ||
+ opts->lssc_d != 0))
+ print_hfsc_sc("linkshare", opts->lssc_m1, opts->lssc_d,
+ opts->lssc_m2, lssc);
+ if (opts->ulsc_m2 != 0)
+ print_hfsc_sc("upperlimit", opts->ulsc_m1, opts->ulsc_d,
+ opts->ulsc_m2, ulsc);
+ printf(" ) ");
+
+ return (1);
+ } else
+ return (0);
+}
+
+/*
+ * admission control using generalized service curve
+ */
+
+/* add a new service curve to a generalized service curve */
+static void
+gsc_add_sc(struct gen_sc *gsc, struct service_curve *sc)
+{
+ if (is_sc_null(sc))
+ return;
+ if (sc->d != 0)
+ gsc_add_seg(gsc, 0.0, 0.0, (double)sc->d, (double)sc->m1);
+ gsc_add_seg(gsc, (double)sc->d, 0.0, INFINITY, (double)sc->m2);
+}
+
+/*
+ * check whether all points of a generalized service curve have
+ * their y-coordinates no larger than a given two-piece linear
+ * service curve.
+ */
+static int
+is_gsc_under_sc(struct gen_sc *gsc, struct service_curve *sc)
+{
+ struct segment *s, *last, *end;
+ double y;
+
+ if (is_sc_null(sc)) {
+ if (LIST_EMPTY(gsc))
+ return (1);
+ LIST_FOREACH(s, gsc, _next) {
+ if (s->m != 0)
+ return (0);
+ }
+ return (1);
+ }
+ /*
+ * gsc has a dummy entry at the end with x = INFINITY.
+ * loop through up to this dummy entry.
+ */
+ end = gsc_getentry(gsc, INFINITY);
+ if (end == NULL)
+ return (1);
+ last = NULL;
+ for (s = LIST_FIRST(gsc); s != end; s = LIST_NEXT(s, _next)) {
+ if (s->y > sc_x2y(sc, s->x))
+ return (0);
+ last = s;
+ }
+ /* last now holds the real last segment */
+ if (last == NULL)
+ return (1);
+ if (last->m > sc->m2)
+ return (0);
+ if (last->x < sc->d && last->m > sc->m1) {
+ y = last->y + (sc->d - last->x) * last->m;
+ if (y > sc_x2y(sc, sc->d))
+ return (0);
+ }
+ return (1);
+}
+
+static void
+gsc_destroy(struct gen_sc *gsc)
+{
+ struct segment *s;
+
+ while ((s = LIST_FIRST(gsc)) != NULL) {
+ LIST_REMOVE(s, _next);
+ free(s);
+ }
+}
+
+/*
+ * return a segment entry starting at x.
+ * if gsc has no entry starting at x, a new entry is created at x.
+ */
+static struct segment *
+gsc_getentry(struct gen_sc *gsc, double x)
+{
+ struct segment *new, *prev, *s;
+
+ prev = NULL;
+ LIST_FOREACH(s, gsc, _next) {
+ if (s->x == x)
+ return (s); /* matching entry found */
+ else if (s->x < x)
+ prev = s;
+ else
+ break;
+ }
+
+ /* we have to create a new entry */
+ if ((new = calloc(1, sizeof(struct segment))) == NULL)
+ return (NULL);
+
+ new->x = x;
+ if (x == INFINITY || s == NULL)
+ new->d = 0;
+ else if (s->x == INFINITY)
+ new->d = INFINITY;
+ else
+ new->d = s->x - x;
+ if (prev == NULL) {
+ /* insert the new entry at the head of the list */
+ new->y = 0;
+ new->m = 0;
+ LIST_INSERT_HEAD(gsc, new, _next);
+ } else {
+ /*
+ * the start point intersects with the segment pointed by
+ * prev. divide prev into 2 segments
+ */
+ if (x == INFINITY) {
+ prev->d = INFINITY;
+ if (prev->m == 0)
+ new->y = prev->y;
+ else
+ new->y = INFINITY;
+ } else {
+ prev->d = x - prev->x;
+ new->y = prev->d * prev->m + prev->y;
+ }
+ new->m = prev->m;
+ LIST_INSERT_AFTER(prev, new, _next);
+ }
+ return (new);
+}
+
+/* add a segment to a generalized service curve */
+static int
+gsc_add_seg(struct gen_sc *gsc, double x, double y, double d, double m)
+{
+ struct segment *start, *end, *s;
+ double x2;
+
+ if (d == INFINITY)
+ x2 = INFINITY;
+ else
+ x2 = x + d;
+ start = gsc_getentry(gsc, x);
+ end = gsc_getentry(gsc, x2);
+ if (start == NULL || end == NULL)
+ return (-1);
+
+ for (s = start; s != end; s = LIST_NEXT(s, _next)) {
+ s->m += m;
+ s->y += y + (s->x - x) * m;
+ }
+
+ end = gsc_getentry(gsc, INFINITY);
+ for (; s != end; s = LIST_NEXT(s, _next)) {
+ s->y += m * d;
+ }
+
+ return (0);
+}
+
+/* get y-projection of a service curve */
+static double
+sc_x2y(struct service_curve *sc, double x)
+{
+ double y;
+
+ if (x <= (double)sc->d)
+ /* y belongs to the 1st segment */
+ y = x * (double)sc->m1;
+ else
+ /* y belongs to the 2nd segment */
+ y = (double)sc->d * (double)sc->m1
+ + (x - (double)sc->d) * (double)sc->m2;
+ return (y);
+}
+
+/*
+ * misc utilities
+ */
+#define R2S_BUFS 8
+#define RATESTR_MAX 16
+
+char *
+rate2str(double rate)
+{
+ char *buf;
+ static char r2sbuf[R2S_BUFS][RATESTR_MAX]; /* ring bufer */
+ static int idx = 0;
+ int i;
+ static const char unit[] = " KMG";
+
+ buf = r2sbuf[idx++];
+ if (idx == R2S_BUFS)
+ idx = 0;
+
+ for (i = 0; rate >= 1000 && i <= 3; i++)
+ rate /= 1000;
+
+ if ((int)(rate * 100) % 100)
+ snprintf(buf, RATESTR_MAX, "%.2f%cb", rate, unit[i]);
+ else
+ snprintf(buf, RATESTR_MAX, "%d%cb", (int)rate, unit[i]);
+
+ return (buf);
+}
+
+#ifdef __FreeBSD__
+/*
+ * XXX
+ * FreeBSD does not have SIOCGIFDATA.
+ * To emulate this, DIOCGIFSPEED ioctl added to pf.
+ */
+u_int32_t
+getifspeed(int pfdev, char *ifname)
+{
+ struct pf_ifspeed io;
+
+ bzero(&io, sizeof io);
+ if (strlcpy(io.ifname, ifname, IFNAMSIZ) >=
+ sizeof(io.ifname))
+ errx(1, "getifspeed: strlcpy");
+ if (ioctl(pfdev, DIOCGIFSPEED, &io) == -1)
+ err(1, "DIOCGIFSPEED");
+ return ((u_int32_t)io.baudrate);
+}
+#else
+u_int32_t
+getifspeed(char *ifname)
+{
+ int s;
+ struct ifreq ifr;
+ struct if_data ifrdat;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ err(1, "socket");
+ bzero(&ifr, sizeof(ifr));
+ if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >=
+ sizeof(ifr.ifr_name))
+ errx(1, "getifspeed: strlcpy");
+ ifr.ifr_data = (caddr_t)&ifrdat;
+ if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == -1)
+ err(1, "SIOCGIFDATA");
+ if (close(s))
+ err(1, "close");
+ return ((u_int32_t)ifrdat.ifi_baudrate);
+}
+#endif
+
+u_long
+getifmtu(char *ifname)
+{
+ int s;
+ struct ifreq ifr;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ err(1, "socket");
+ bzero(&ifr, sizeof(ifr));
+ if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >=
+ sizeof(ifr.ifr_name))
+ errx(1, "getifmtu: strlcpy");
+ if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == -1)
+#ifdef __FreeBSD__
+ ifr.ifr_mtu = 1500;
+#else
+ err(1, "SIOCGIFMTU");
+#endif
+ if (close(s))
+ err(1, "close");
+ if (ifr.ifr_mtu > 0)
+ return (ifr.ifr_mtu);
+ else {
+ warnx("could not get mtu for %s, assuming 1500", ifname);
+ return (1500);
+ }
+}
+
+int
+eval_queue_opts(struct pf_altq *pa, struct node_queue_opt *opts,
+ u_int32_t ref_bw)
+{
+ int errors = 0;
+
+ switch (pa->scheduler) {
+ case ALTQT_CBQ:
+ pa->pq_u.cbq_opts = opts->data.cbq_opts;
+ break;
+ case ALTQT_PRIQ:
+ pa->pq_u.priq_opts = opts->data.priq_opts;
+ break;
+ case ALTQT_HFSC:
+ pa->pq_u.hfsc_opts.flags = opts->data.hfsc_opts.flags;
+ if (opts->data.hfsc_opts.linkshare.used) {
+ pa->pq_u.hfsc_opts.lssc_m1 =
+ eval_bwspec(&opts->data.hfsc_opts.linkshare.m1,
+ ref_bw);
+ pa->pq_u.hfsc_opts.lssc_m2 =
+ eval_bwspec(&opts->data.hfsc_opts.linkshare.m2,
+ ref_bw);
+ pa->pq_u.hfsc_opts.lssc_d =
+ opts->data.hfsc_opts.linkshare.d;
+ }
+ if (opts->data.hfsc_opts.realtime.used) {
+ pa->pq_u.hfsc_opts.rtsc_m1 =
+ eval_bwspec(&opts->data.hfsc_opts.realtime.m1,
+ ref_bw);
+ pa->pq_u.hfsc_opts.rtsc_m2 =
+ eval_bwspec(&opts->data.hfsc_opts.realtime.m2,
+ ref_bw);
+ pa->pq_u.hfsc_opts.rtsc_d =
+ opts->data.hfsc_opts.realtime.d;
+ }
+ if (opts->data.hfsc_opts.upperlimit.used) {
+ pa->pq_u.hfsc_opts.ulsc_m1 =
+ eval_bwspec(&opts->data.hfsc_opts.upperlimit.m1,
+ ref_bw);
+ pa->pq_u.hfsc_opts.ulsc_m2 =
+ eval_bwspec(&opts->data.hfsc_opts.upperlimit.m2,
+ ref_bw);
+ pa->pq_u.hfsc_opts.ulsc_d =
+ opts->data.hfsc_opts.upperlimit.d;
+ }
+ break;
+ default:
+ warnx("eval_queue_opts: unknown scheduler type %u",
+ opts->qtype);
+ errors++;
+ break;
+ }
+
+ return (errors);
+}
+
+u_int32_t
+eval_bwspec(struct node_queue_bw *bw, u_int32_t ref_bw)
+{
+ if (bw->bw_absolute > 0)
+ return (bw->bw_absolute);
+
+ if (bw->bw_percent > 0)
+ return (ref_bw / 100 * bw->bw_percent);
+
+ return (0);
+}
+
+void
+print_hfsc_sc(const char *scname, u_int m1, u_int d, u_int m2,
+ const struct node_hfsc_sc *sc)
+{
+ printf(" %s", scname);
+
+ if (d != 0) {
+ printf("(");
+ if (sc != NULL && sc->m1.bw_percent > 0)
+ printf("%u%%", sc->m1.bw_percent);
+ else
+ printf("%s", rate2str((double)m1));
+ printf(" %u", d);
+ }
+
+ if (sc != NULL && sc->m2.bw_percent > 0)
+ printf(" %u%%", sc->m2.bw_percent);
+ else
+ printf(" %s", rate2str((double)m2));
+
+ if (d != 0)
+ printf(")");
+}
diff --git a/sbin/pfctl/pfctl_optimize.c b/sbin/pfctl/pfctl_optimize.c
new file mode 100644
index 000000000000..9511720ed67d
--- /dev/null
+++ b/sbin/pfctl/pfctl_optimize.c
@@ -0,0 +1,1655 @@
+/* $OpenBSD: pfctl_optimize.c,v 1.17 2008/05/06 03:45:21 mpf Exp $ */
+
+/*
+ * Copyright (c) 2004 Mike Frantzen <frantzen@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/pfvar.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pfctl_parser.h"
+#include "pfctl.h"
+
+/* The size at which a table becomes faster than individual rules */
+#define TABLE_THRESHOLD 6
+
+
+/* #define OPT_DEBUG 1 */
+#ifdef OPT_DEBUG
+# define DEBUG(str, v...) \
+ printf("%s: " str "\n", __FUNCTION__ , ## v)
+#else
+# define DEBUG(str, v...) ((void)0)
+#endif
+
+
+/*
+ * A container that lets us sort a superblock to optimize the skip step jumps
+ */
+struct pf_skip_step {
+ int ps_count; /* number of items */
+ TAILQ_HEAD( , pf_opt_rule) ps_rules;
+ TAILQ_ENTRY(pf_skip_step) ps_entry;
+};
+
+
+/*
+ * A superblock is a block of adjacent rules of similar action. If there
+ * are five PASS rules in a row, they all become members of a superblock.
+ * Once we have a superblock, we are free to re-order any rules within it
+ * in order to improve performance; if a packet is passed, it doesn't matter
+ * who passed it.
+ */
+struct superblock {
+ TAILQ_HEAD( , pf_opt_rule) sb_rules;
+ TAILQ_ENTRY(superblock) sb_entry;
+ struct superblock *sb_profiled_block;
+ TAILQ_HEAD(skiplist, pf_skip_step) sb_skipsteps[PF_SKIP_COUNT];
+};
+TAILQ_HEAD(superblocks, superblock);
+
+
+/*
+ * Description of the PF rule structure.
+ */
+enum {
+ BARRIER, /* the presence of the field puts the rule in it's own block */
+ BREAK, /* the field may not differ between rules in a superblock */
+ NOMERGE, /* the field may not differ between rules when combined */
+ COMBINED, /* the field may itself be combined with other rules */
+ DC, /* we just don't care about the field */
+ NEVER}; /* we should never see this field set?!? */
+struct pf_rule_field {
+ const char *prf_name;
+ int prf_type;
+ size_t prf_offset;
+ size_t prf_size;
+} pf_rule_desc[] = {
+#define PF_RULE_FIELD(field, ty) \
+ {#field, \
+ ty, \
+ offsetof(struct pf_rule, field), \
+ sizeof(((struct pf_rule *)0)->field)}
+
+
+ /*
+ * The presence of these fields in a rule put the rule in it's own
+ * superblock. Thus it will not be optimized. It also prevents the
+ * rule from being re-ordered at all.
+ */
+ PF_RULE_FIELD(label, BARRIER),
+ PF_RULE_FIELD(prob, BARRIER),
+ PF_RULE_FIELD(max_states, BARRIER),
+ PF_RULE_FIELD(max_src_nodes, BARRIER),
+ PF_RULE_FIELD(max_src_states, BARRIER),
+ PF_RULE_FIELD(max_src_conn, BARRIER),
+ PF_RULE_FIELD(max_src_conn_rate, BARRIER),
+ PF_RULE_FIELD(anchor, BARRIER), /* for now */
+
+ /*
+ * These fields must be the same between all rules in the same superblock.
+ * These rules are allowed to be re-ordered but only among like rules.
+ * For instance we can re-order all 'tag "foo"' rules because they have the
+ * same tag. But we can not re-order between a 'tag "foo"' and a
+ * 'tag "bar"' since that would change the meaning of the ruleset.
+ */
+ PF_RULE_FIELD(tagname, BREAK),
+ PF_RULE_FIELD(keep_state, BREAK),
+ PF_RULE_FIELD(qname, BREAK),
+ PF_RULE_FIELD(pqname, BREAK),
+ PF_RULE_FIELD(rt, BREAK),
+ PF_RULE_FIELD(allow_opts, BREAK),
+ PF_RULE_FIELD(rule_flag, BREAK),
+ PF_RULE_FIELD(action, BREAK),
+ PF_RULE_FIELD(log, BREAK),
+ PF_RULE_FIELD(quick, BREAK),
+ PF_RULE_FIELD(return_ttl, BREAK),
+ PF_RULE_FIELD(overload_tblname, BREAK),
+ PF_RULE_FIELD(flush, BREAK),
+ PF_RULE_FIELD(rpool, BREAK),
+ PF_RULE_FIELD(logif, BREAK),
+
+ /*
+ * Any fields not listed in this structure act as BREAK fields
+ */
+
+
+ /*
+ * These fields must not differ when we merge two rules together but
+ * their difference isn't enough to put the rules in different superblocks.
+ * There are no problems re-ordering any rules with these fields.
+ */
+ PF_RULE_FIELD(af, NOMERGE),
+ PF_RULE_FIELD(ifnot, NOMERGE),
+ PF_RULE_FIELD(ifname, NOMERGE), /* hack for IF groups */
+ PF_RULE_FIELD(match_tag_not, NOMERGE),
+ PF_RULE_FIELD(match_tagname, NOMERGE),
+ PF_RULE_FIELD(os_fingerprint, NOMERGE),
+ PF_RULE_FIELD(timeout, NOMERGE),
+ PF_RULE_FIELD(return_icmp, NOMERGE),
+ PF_RULE_FIELD(return_icmp6, NOMERGE),
+ PF_RULE_FIELD(uid, NOMERGE),
+ PF_RULE_FIELD(gid, NOMERGE),
+ PF_RULE_FIELD(direction, NOMERGE),
+ PF_RULE_FIELD(proto, NOMERGE),
+ PF_RULE_FIELD(type, NOMERGE),
+ PF_RULE_FIELD(code, NOMERGE),
+ PF_RULE_FIELD(flags, NOMERGE),
+ PF_RULE_FIELD(flagset, NOMERGE),
+ PF_RULE_FIELD(tos, NOMERGE),
+ PF_RULE_FIELD(src.port, NOMERGE),
+ PF_RULE_FIELD(dst.port, NOMERGE),
+ PF_RULE_FIELD(src.port_op, NOMERGE),
+ PF_RULE_FIELD(dst.port_op, NOMERGE),
+ PF_RULE_FIELD(src.neg, NOMERGE),
+ PF_RULE_FIELD(dst.neg, NOMERGE),
+
+ /* These fields can be merged */
+ PF_RULE_FIELD(src.addr, COMBINED),
+ PF_RULE_FIELD(dst.addr, COMBINED),
+
+ /* We just don't care about these fields. They're set by the kernel */
+ PF_RULE_FIELD(skip, DC),
+ PF_RULE_FIELD(evaluations, DC),
+ PF_RULE_FIELD(packets, DC),
+ PF_RULE_FIELD(bytes, DC),
+ PF_RULE_FIELD(kif, DC),
+ PF_RULE_FIELD(states_cur, DC),
+ PF_RULE_FIELD(states_tot, DC),
+ PF_RULE_FIELD(src_nodes, DC),
+ PF_RULE_FIELD(nr, DC),
+ PF_RULE_FIELD(entries, DC),
+ PF_RULE_FIELD(qid, DC),
+ PF_RULE_FIELD(pqid, DC),
+ PF_RULE_FIELD(anchor_relative, DC),
+ PF_RULE_FIELD(anchor_wildcard, DC),
+ PF_RULE_FIELD(tag, DC),
+ PF_RULE_FIELD(match_tag, DC),
+ PF_RULE_FIELD(overload_tbl, DC),
+
+ /* These fields should never be set in a PASS/BLOCK rule */
+ PF_RULE_FIELD(natpass, NEVER),
+ PF_RULE_FIELD(max_mss, NEVER),
+ PF_RULE_FIELD(min_ttl, NEVER),
+ PF_RULE_FIELD(set_tos, NEVER),
+};
+
+
+
+int add_opt_table(struct pfctl *, struct pf_opt_tbl **, sa_family_t,
+ struct pf_rule_addr *);
+int addrs_combineable(struct pf_rule_addr *, struct pf_rule_addr *);
+int addrs_equal(struct pf_rule_addr *, struct pf_rule_addr *);
+int block_feedback(struct pfctl *, struct superblock *);
+int combine_rules(struct pfctl *, struct superblock *);
+void comparable_rule(struct pf_rule *, const struct pf_rule *, int);
+int construct_superblocks(struct pfctl *, struct pf_opt_queue *,
+ struct superblocks *);
+void exclude_supersets(struct pf_rule *, struct pf_rule *);
+int interface_group(const char *);
+int load_feedback_profile(struct pfctl *, struct superblocks *);
+int optimize_superblock(struct pfctl *, struct superblock *);
+int pf_opt_create_table(struct pfctl *, struct pf_opt_tbl *);
+void remove_from_skipsteps(struct skiplist *, struct superblock *,
+ struct pf_opt_rule *, struct pf_skip_step *);
+int remove_identical_rules(struct pfctl *, struct superblock *);
+int reorder_rules(struct pfctl *, struct superblock *, int);
+int rules_combineable(struct pf_rule *, struct pf_rule *);
+void skip_append(struct superblock *, int, struct pf_skip_step *,
+ struct pf_opt_rule *);
+int skip_compare(int, struct pf_skip_step *, struct pf_opt_rule *);
+void skip_init(void);
+int skip_cmp_af(struct pf_rule *, struct pf_rule *);
+int skip_cmp_dir(struct pf_rule *, struct pf_rule *);
+int skip_cmp_dst_addr(struct pf_rule *, struct pf_rule *);
+int skip_cmp_dst_port(struct pf_rule *, struct pf_rule *);
+int skip_cmp_ifp(struct pf_rule *, struct pf_rule *);
+int skip_cmp_proto(struct pf_rule *, struct pf_rule *);
+int skip_cmp_src_addr(struct pf_rule *, struct pf_rule *);
+int skip_cmp_src_port(struct pf_rule *, struct pf_rule *);
+int superblock_inclusive(struct superblock *, struct pf_opt_rule *);
+void superblock_free(struct pfctl *, struct superblock *);
+
+
+int (*skip_comparitors[PF_SKIP_COUNT])(struct pf_rule *, struct pf_rule *);
+const char *skip_comparitors_names[PF_SKIP_COUNT];
+#define PF_SKIP_COMPARITORS { \
+ { "ifp", PF_SKIP_IFP, skip_cmp_ifp }, \
+ { "dir", PF_SKIP_DIR, skip_cmp_dir }, \
+ { "af", PF_SKIP_AF, skip_cmp_af }, \
+ { "proto", PF_SKIP_PROTO, skip_cmp_proto }, \
+ { "saddr", PF_SKIP_SRC_ADDR, skip_cmp_src_addr }, \
+ { "sport", PF_SKIP_SRC_PORT, skip_cmp_src_port }, \
+ { "daddr", PF_SKIP_DST_ADDR, skip_cmp_dst_addr }, \
+ { "dport", PF_SKIP_DST_PORT, skip_cmp_dst_port } \
+}
+
+struct pfr_buffer table_buffer;
+int table_identifier;
+
+
+int
+pfctl_optimize_ruleset(struct pfctl *pf, struct pf_ruleset *rs)
+{
+ struct superblocks superblocks;
+ struct pf_opt_queue opt_queue;
+ struct superblock *block;
+ struct pf_opt_rule *por;
+ struct pf_rule *r;
+ struct pf_rulequeue *old_rules;
+
+ DEBUG("optimizing ruleset");
+ memset(&table_buffer, 0, sizeof(table_buffer));
+ skip_init();
+ TAILQ_INIT(&opt_queue);
+
+ old_rules = rs->rules[PF_RULESET_FILTER].active.ptr;
+ rs->rules[PF_RULESET_FILTER].active.ptr =
+ rs->rules[PF_RULESET_FILTER].inactive.ptr;
+ rs->rules[PF_RULESET_FILTER].inactive.ptr = old_rules;
+
+ /*
+ * XXX expanding the pf_opt_rule format throughout pfctl might allow
+ * us to avoid all this copying.
+ */
+ while ((r = TAILQ_FIRST(rs->rules[PF_RULESET_FILTER].inactive.ptr))
+ != NULL) {
+ TAILQ_REMOVE(rs->rules[PF_RULESET_FILTER].inactive.ptr, r,
+ entries);
+ if ((por = calloc(1, sizeof(*por))) == NULL)
+ err(1, "calloc");
+ memcpy(&por->por_rule, r, sizeof(*r));
+ if (TAILQ_FIRST(&r->rpool.list) != NULL) {
+ TAILQ_INIT(&por->por_rule.rpool.list);
+ pfctl_move_pool(&r->rpool, &por->por_rule.rpool);
+ } else
+ bzero(&por->por_rule.rpool,
+ sizeof(por->por_rule.rpool));
+
+
+ TAILQ_INSERT_TAIL(&opt_queue, por, por_entry);
+ }
+
+ TAILQ_INIT(&superblocks);
+ if (construct_superblocks(pf, &opt_queue, &superblocks))
+ goto error;
+
+ if (pf->optimize & PF_OPTIMIZE_PROFILE) {
+ if (load_feedback_profile(pf, &superblocks))
+ goto error;
+ }
+
+ TAILQ_FOREACH(block, &superblocks, sb_entry) {
+ if (optimize_superblock(pf, block))
+ goto error;
+ }
+
+ rs->anchor->refcnt = 0;
+ while ((block = TAILQ_FIRST(&superblocks))) {
+ TAILQ_REMOVE(&superblocks, block, sb_entry);
+
+ while ((por = TAILQ_FIRST(&block->sb_rules))) {
+ TAILQ_REMOVE(&block->sb_rules, por, por_entry);
+ por->por_rule.nr = rs->anchor->refcnt++;
+ if ((r = calloc(1, sizeof(*r))) == NULL)
+ err(1, "calloc");
+ memcpy(r, &por->por_rule, sizeof(*r));
+ TAILQ_INIT(&r->rpool.list);
+ pfctl_move_pool(&por->por_rule.rpool, &r->rpool);
+ TAILQ_INSERT_TAIL(
+ rs->rules[PF_RULESET_FILTER].active.ptr,
+ r, entries);
+ free(por);
+ }
+ free(block);
+ }
+
+ return (0);
+
+error:
+ while ((por = TAILQ_FIRST(&opt_queue))) {
+ TAILQ_REMOVE(&opt_queue, por, por_entry);
+ if (por->por_src_tbl) {
+ pfr_buf_clear(por->por_src_tbl->pt_buf);
+ free(por->por_src_tbl->pt_buf);
+ free(por->por_src_tbl);
+ }
+ if (por->por_dst_tbl) {
+ pfr_buf_clear(por->por_dst_tbl->pt_buf);
+ free(por->por_dst_tbl->pt_buf);
+ free(por->por_dst_tbl);
+ }
+ free(por);
+ }
+ while ((block = TAILQ_FIRST(&superblocks))) {
+ TAILQ_REMOVE(&superblocks, block, sb_entry);
+ superblock_free(pf, block);
+ }
+ return (1);
+}
+
+
+/*
+ * Go ahead and optimize a superblock
+ */
+int
+optimize_superblock(struct pfctl *pf, struct superblock *block)
+{
+#ifdef OPT_DEBUG
+ struct pf_opt_rule *por;
+#endif /* OPT_DEBUG */
+
+ /* We have a few optimization passes:
+ * 1) remove duplicate rules or rules that are a subset of other
+ * rules
+ * 2) combine otherwise identical rules with different IP addresses
+ * into a single rule and put the addresses in a table.
+ * 3) re-order the rules to improve kernel skip steps
+ * 4) re-order the 'quick' rules based on feedback from the
+ * active ruleset statistics
+ *
+ * XXX combine_rules() doesn't combine v4 and v6 rules. would just
+ * have to keep af in the table container, make af 'COMBINE' and
+ * twiddle the af on the merged rule
+ * XXX maybe add a weighting to the metric on skipsteps when doing
+ * reordering. sometimes two sequential tables will be better
+ * that four consecutive interfaces.
+ * XXX need to adjust the skipstep count of everything after PROTO,
+ * since they aren't actually checked on a proto mismatch in
+ * pf_test_{tcp, udp, icmp}()
+ * XXX should i treat proto=0, af=0 or dir=0 special in skepstep
+ * calculation since they are a DC?
+ * XXX keep last skiplist of last superblock to influence this
+ * superblock. '5 inet6 log' should make '3 inet6' come before '4
+ * inet' in the next superblock.
+ * XXX would be useful to add tables for ports
+ * XXX we can also re-order some mutually exclusive superblocks to
+ * try merging superblocks before any of these optimization passes.
+ * for instance a single 'log in' rule in the middle of non-logging
+ * out rules.
+ */
+
+ /* shortcut. there will be a lot of 1-rule superblocks */
+ if (!TAILQ_NEXT(TAILQ_FIRST(&block->sb_rules), por_entry))
+ return (0);
+
+#ifdef OPT_DEBUG
+ printf("--- Superblock ---\n");
+ TAILQ_FOREACH(por, &block->sb_rules, por_entry) {
+ printf(" ");
+ print_rule(&por->por_rule, por->por_rule.anchor ?
+ por->por_rule.anchor->name : "", 1, 0);
+ }
+#endif /* OPT_DEBUG */
+
+
+ if (remove_identical_rules(pf, block))
+ return (1);
+ if (combine_rules(pf, block))
+ return (1);
+ if ((pf->optimize & PF_OPTIMIZE_PROFILE) &&
+ TAILQ_FIRST(&block->sb_rules)->por_rule.quick &&
+ block->sb_profiled_block) {
+ if (block_feedback(pf, block))
+ return (1);
+ } else if (reorder_rules(pf, block, 0)) {
+ return (1);
+ }
+
+ /*
+ * Don't add any optimization passes below reorder_rules(). It will
+ * have divided superblocks into smaller blocks for further refinement
+ * and doesn't put them back together again. What once was a true
+ * superblock might have been split into multiple superblocks.
+ */
+
+#ifdef OPT_DEBUG
+ printf("--- END Superblock ---\n");
+#endif /* OPT_DEBUG */
+ return (0);
+}
+
+
+/*
+ * Optimization pass #1: remove identical rules
+ */
+int
+remove_identical_rules(struct pfctl *pf, struct superblock *block)
+{
+ struct pf_opt_rule *por1, *por2, *por_next, *por2_next;
+ struct pf_rule a, a2, b, b2;
+
+ for (por1 = TAILQ_FIRST(&block->sb_rules); por1; por1 = por_next) {
+ por_next = TAILQ_NEXT(por1, por_entry);
+ for (por2 = por_next; por2; por2 = por2_next) {
+ por2_next = TAILQ_NEXT(por2, por_entry);
+ comparable_rule(&a, &por1->por_rule, DC);
+ comparable_rule(&b, &por2->por_rule, DC);
+ memcpy(&a2, &a, sizeof(a2));
+ memcpy(&b2, &b, sizeof(b2));
+
+ exclude_supersets(&a, &b);
+ exclude_supersets(&b2, &a2);
+ if (memcmp(&a, &b, sizeof(a)) == 0) {
+ DEBUG("removing identical rule nr%d = *nr%d*",
+ por1->por_rule.nr, por2->por_rule.nr);
+ TAILQ_REMOVE(&block->sb_rules, por2, por_entry);
+ if (por_next == por2)
+ por_next = TAILQ_NEXT(por1, por_entry);
+ free(por2);
+ } else if (memcmp(&a2, &b2, sizeof(a2)) == 0) {
+ DEBUG("removing identical rule *nr%d* = nr%d",
+ por1->por_rule.nr, por2->por_rule.nr);
+ TAILQ_REMOVE(&block->sb_rules, por1, por_entry);
+ free(por1);
+ break;
+ }
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * Optimization pass #2: combine similar rules with different addresses
+ * into a single rule and a table
+ */
+int
+combine_rules(struct pfctl *pf, struct superblock *block)
+{
+ struct pf_opt_rule *p1, *p2, *por_next;
+ int src_eq, dst_eq;
+
+ if ((pf->loadopt & PFCTL_FLAG_TABLE) == 0) {
+ warnx("Must enable table loading for optimizations");
+ return (1);
+ }
+
+ /* First we make a pass to combine the rules. O(n log n) */
+ TAILQ_FOREACH(p1, &block->sb_rules, por_entry) {
+ for (p2 = TAILQ_NEXT(p1, por_entry); p2; p2 = por_next) {
+ por_next = TAILQ_NEXT(p2, por_entry);
+
+ src_eq = addrs_equal(&p1->por_rule.src,
+ &p2->por_rule.src);
+ dst_eq = addrs_equal(&p1->por_rule.dst,
+ &p2->por_rule.dst);
+
+ if (src_eq && !dst_eq && p1->por_src_tbl == NULL &&
+ p2->por_dst_tbl == NULL &&
+ p2->por_src_tbl == NULL &&
+ rules_combineable(&p1->por_rule, &p2->por_rule) &&
+ addrs_combineable(&p1->por_rule.dst,
+ &p2->por_rule.dst)) {
+ DEBUG("can combine rules nr%d = nr%d",
+ p1->por_rule.nr, p2->por_rule.nr);
+ if (p1->por_dst_tbl == NULL &&
+ add_opt_table(pf, &p1->por_dst_tbl,
+ p1->por_rule.af, &p1->por_rule.dst))
+ return (1);
+ if (add_opt_table(pf, &p1->por_dst_tbl,
+ p1->por_rule.af, &p2->por_rule.dst))
+ return (1);
+ p2->por_dst_tbl = p1->por_dst_tbl;
+ if (p1->por_dst_tbl->pt_rulecount >=
+ TABLE_THRESHOLD) {
+ TAILQ_REMOVE(&block->sb_rules, p2,
+ por_entry);
+ free(p2);
+ }
+ } else if (!src_eq && dst_eq && p1->por_dst_tbl == NULL
+ && p2->por_src_tbl == NULL &&
+ p2->por_dst_tbl == NULL &&
+ rules_combineable(&p1->por_rule, &p2->por_rule) &&
+ addrs_combineable(&p1->por_rule.src,
+ &p2->por_rule.src)) {
+ DEBUG("can combine rules nr%d = nr%d",
+ p1->por_rule.nr, p2->por_rule.nr);
+ if (p1->por_src_tbl == NULL &&
+ add_opt_table(pf, &p1->por_src_tbl,
+ p1->por_rule.af, &p1->por_rule.src))
+ return (1);
+ if (add_opt_table(pf, &p1->por_src_tbl,
+ p1->por_rule.af, &p2->por_rule.src))
+ return (1);
+ p2->por_src_tbl = p1->por_src_tbl;
+ if (p1->por_src_tbl->pt_rulecount >=
+ TABLE_THRESHOLD) {
+ TAILQ_REMOVE(&block->sb_rules, p2,
+ por_entry);
+ free(p2);
+ }
+ }
+ }
+ }
+
+
+ /*
+ * Then we make a final pass to create a valid table name and
+ * insert the name into the rules.
+ */
+ for (p1 = TAILQ_FIRST(&block->sb_rules); p1; p1 = por_next) {
+ por_next = TAILQ_NEXT(p1, por_entry);
+ assert(p1->por_src_tbl == NULL || p1->por_dst_tbl == NULL);
+
+ if (p1->por_src_tbl && p1->por_src_tbl->pt_rulecount >=
+ TABLE_THRESHOLD) {
+ if (p1->por_src_tbl->pt_generated) {
+ /* This rule is included in a table */
+ TAILQ_REMOVE(&block->sb_rules, p1, por_entry);
+ free(p1);
+ continue;
+ }
+ p1->por_src_tbl->pt_generated = 1;
+
+ if ((pf->opts & PF_OPT_NOACTION) == 0 &&
+ pf_opt_create_table(pf, p1->por_src_tbl))
+ return (1);
+
+ pf->tdirty = 1;
+
+ if (pf->opts & PF_OPT_VERBOSE)
+ print_tabledef(p1->por_src_tbl->pt_name,
+ PFR_TFLAG_CONST, 1,
+ &p1->por_src_tbl->pt_nodes);
+
+ memset(&p1->por_rule.src.addr, 0,
+ sizeof(p1->por_rule.src.addr));
+ p1->por_rule.src.addr.type = PF_ADDR_TABLE;
+ strlcpy(p1->por_rule.src.addr.v.tblname,
+ p1->por_src_tbl->pt_name,
+ sizeof(p1->por_rule.src.addr.v.tblname));
+
+ pfr_buf_clear(p1->por_src_tbl->pt_buf);
+ free(p1->por_src_tbl->pt_buf);
+ p1->por_src_tbl->pt_buf = NULL;
+ }
+ if (p1->por_dst_tbl && p1->por_dst_tbl->pt_rulecount >=
+ TABLE_THRESHOLD) {
+ if (p1->por_dst_tbl->pt_generated) {
+ /* This rule is included in a table */
+ TAILQ_REMOVE(&block->sb_rules, p1, por_entry);
+ free(p1);
+ continue;
+ }
+ p1->por_dst_tbl->pt_generated = 1;
+
+ if ((pf->opts & PF_OPT_NOACTION) == 0 &&
+ pf_opt_create_table(pf, p1->por_dst_tbl))
+ return (1);
+ pf->tdirty = 1;
+
+ if (pf->opts & PF_OPT_VERBOSE)
+ print_tabledef(p1->por_dst_tbl->pt_name,
+ PFR_TFLAG_CONST, 1,
+ &p1->por_dst_tbl->pt_nodes);
+
+ memset(&p1->por_rule.dst.addr, 0,
+ sizeof(p1->por_rule.dst.addr));
+ p1->por_rule.dst.addr.type = PF_ADDR_TABLE;
+ strlcpy(p1->por_rule.dst.addr.v.tblname,
+ p1->por_dst_tbl->pt_name,
+ sizeof(p1->por_rule.dst.addr.v.tblname));
+
+ pfr_buf_clear(p1->por_dst_tbl->pt_buf);
+ free(p1->por_dst_tbl->pt_buf);
+ p1->por_dst_tbl->pt_buf = NULL;
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * Optimization pass #3: re-order rules to improve skip steps
+ */
+int
+reorder_rules(struct pfctl *pf, struct superblock *block, int depth)
+{
+ struct superblock *newblock;
+ struct pf_skip_step *skiplist;
+ struct pf_opt_rule *por;
+ int i, largest, largest_list, rule_count = 0;
+ TAILQ_HEAD( , pf_opt_rule) head;
+
+ /*
+ * Calculate the best-case skip steps. We put each rule in a list
+ * of other rules with common fields
+ */
+ for (i = 0; i < PF_SKIP_COUNT; i++) {
+ TAILQ_FOREACH(por, &block->sb_rules, por_entry) {
+ TAILQ_FOREACH(skiplist, &block->sb_skipsteps[i],
+ ps_entry) {
+ if (skip_compare(i, skiplist, por) == 0)
+ break;
+ }
+ if (skiplist == NULL) {
+ if ((skiplist = calloc(1, sizeof(*skiplist))) ==
+ NULL)
+ err(1, "calloc");
+ TAILQ_INIT(&skiplist->ps_rules);
+ TAILQ_INSERT_TAIL(&block->sb_skipsteps[i],
+ skiplist, ps_entry);
+ }
+ skip_append(block, i, skiplist, por);
+ }
+ }
+
+ TAILQ_FOREACH(por, &block->sb_rules, por_entry)
+ rule_count++;
+
+ /*
+ * Now we're going to ignore any fields that are identical between
+ * all of the rules in the superblock and those fields which differ
+ * between every rule in the superblock.
+ */
+ largest = 0;
+ for (i = 0; i < PF_SKIP_COUNT; i++) {
+ skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]);
+ if (skiplist->ps_count == rule_count) {
+ DEBUG("(%d) original skipstep '%s' is all rules",
+ depth, skip_comparitors_names[i]);
+ skiplist->ps_count = 0;
+ } else if (skiplist->ps_count == 1) {
+ skiplist->ps_count = 0;
+ } else {
+ DEBUG("(%d) original skipstep '%s' largest jump is %d",
+ depth, skip_comparitors_names[i],
+ skiplist->ps_count);
+ if (skiplist->ps_count > largest)
+ largest = skiplist->ps_count;
+ }
+ }
+ if (largest == 0) {
+ /* Ugh. There is NO commonality in the superblock on which
+ * optimize the skipsteps optimization.
+ */
+ goto done;
+ }
+
+ /*
+ * Now we're going to empty the superblock rule list and re-create
+ * it based on a more optimal skipstep order.
+ */
+ TAILQ_INIT(&head);
+ while ((por = TAILQ_FIRST(&block->sb_rules))) {
+ TAILQ_REMOVE(&block->sb_rules, por, por_entry);
+ TAILQ_INSERT_TAIL(&head, por, por_entry);
+ }
+
+
+ while (!TAILQ_EMPTY(&head)) {
+ largest = 1;
+
+ /*
+ * Find the most useful skip steps remaining
+ */
+ for (i = 0; i < PF_SKIP_COUNT; i++) {
+ skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]);
+ if (skiplist->ps_count > largest) {
+ largest = skiplist->ps_count;
+ largest_list = i;
+ }
+ }
+
+ if (largest <= 1) {
+ /*
+ * Nothing useful left. Leave remaining rules in order.
+ */
+ DEBUG("(%d) no more commonality for skip steps", depth);
+ while ((por = TAILQ_FIRST(&head))) {
+ TAILQ_REMOVE(&head, por, por_entry);
+ TAILQ_INSERT_TAIL(&block->sb_rules, por,
+ por_entry);
+ }
+ } else {
+ /*
+ * There is commonality. Extract those common rules
+ * and place them in the ruleset adjacent to each
+ * other.
+ */
+ skiplist = TAILQ_FIRST(&block->sb_skipsteps[
+ largest_list]);
+ DEBUG("(%d) skipstep '%s' largest jump is %d @ #%d",
+ depth, skip_comparitors_names[largest_list],
+ largest, TAILQ_FIRST(&TAILQ_FIRST(&block->
+ sb_skipsteps [largest_list])->ps_rules)->
+ por_rule.nr);
+ TAILQ_REMOVE(&block->sb_skipsteps[largest_list],
+ skiplist, ps_entry);
+
+
+ /*
+ * There may be further commonality inside these
+ * rules. So we'll split them off into they're own
+ * superblock and pass it back into the optimizer.
+ */
+ if (skiplist->ps_count > 2) {
+ if ((newblock = calloc(1, sizeof(*newblock)))
+ == NULL) {
+ warn("calloc");
+ return (1);
+ }
+ TAILQ_INIT(&newblock->sb_rules);
+ for (i = 0; i < PF_SKIP_COUNT; i++)
+ TAILQ_INIT(&newblock->sb_skipsteps[i]);
+ TAILQ_INSERT_BEFORE(block, newblock, sb_entry);
+ DEBUG("(%d) splitting off %d rules from superblock @ #%d",
+ depth, skiplist->ps_count,
+ TAILQ_FIRST(&skiplist->ps_rules)->
+ por_rule.nr);
+ } else {
+ newblock = block;
+ }
+
+ while ((por = TAILQ_FIRST(&skiplist->ps_rules))) {
+ TAILQ_REMOVE(&head, por, por_entry);
+ TAILQ_REMOVE(&skiplist->ps_rules, por,
+ por_skip_entry[largest_list]);
+ TAILQ_INSERT_TAIL(&newblock->sb_rules, por,
+ por_entry);
+
+ /* Remove this rule from all other skiplists */
+ remove_from_skipsteps(&block->sb_skipsteps[
+ largest_list], block, por, skiplist);
+ }
+ free(skiplist);
+ if (newblock != block)
+ if (reorder_rules(pf, newblock, depth + 1))
+ return (1);
+ }
+ }
+
+done:
+ for (i = 0; i < PF_SKIP_COUNT; i++) {
+ while ((skiplist = TAILQ_FIRST(&block->sb_skipsteps[i]))) {
+ TAILQ_REMOVE(&block->sb_skipsteps[i], skiplist,
+ ps_entry);
+ free(skiplist);
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * Optimization pass #4: re-order 'quick' rules based on feedback from the
+ * currently running ruleset
+ */
+int
+block_feedback(struct pfctl *pf, struct superblock *block)
+{
+ TAILQ_HEAD( , pf_opt_rule) queue;
+ struct pf_opt_rule *por1, *por2;
+ u_int64_t total_count = 0;
+ struct pf_rule a, b;
+
+
+ /*
+ * Walk through all of the profiled superblock's rules and copy
+ * the counters onto our rules.
+ */
+ TAILQ_FOREACH(por1, &block->sb_profiled_block->sb_rules, por_entry) {
+ comparable_rule(&a, &por1->por_rule, DC);
+ total_count += por1->por_rule.packets[0] +
+ por1->por_rule.packets[1];
+ TAILQ_FOREACH(por2, &block->sb_rules, por_entry) {
+ if (por2->por_profile_count)
+ continue;
+ comparable_rule(&b, &por2->por_rule, DC);
+ if (memcmp(&a, &b, sizeof(a)) == 0) {
+ por2->por_profile_count =
+ por1->por_rule.packets[0] +
+ por1->por_rule.packets[1];
+ break;
+ }
+ }
+ }
+ superblock_free(pf, block->sb_profiled_block);
+ block->sb_profiled_block = NULL;
+
+ /*
+ * Now we pull all of the rules off the superblock and re-insert them
+ * in sorted order.
+ */
+
+ TAILQ_INIT(&queue);
+ while ((por1 = TAILQ_FIRST(&block->sb_rules)) != NULL) {
+ TAILQ_REMOVE(&block->sb_rules, por1, por_entry);
+ TAILQ_INSERT_TAIL(&queue, por1, por_entry);
+ }
+
+ while ((por1 = TAILQ_FIRST(&queue)) != NULL) {
+ TAILQ_REMOVE(&queue, por1, por_entry);
+/* XXX I should sort all of the unused rules based on skip steps */
+ TAILQ_FOREACH(por2, &block->sb_rules, por_entry) {
+ if (por1->por_profile_count > por2->por_profile_count) {
+ TAILQ_INSERT_BEFORE(por2, por1, por_entry);
+ break;
+ }
+ }
+#ifdef __FreeBSD__
+ if (por2 == NULL)
+#else
+ if (por2 == TAILQ_END(&block->sb_rules))
+#endif
+ TAILQ_INSERT_TAIL(&block->sb_rules, por1, por_entry);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Load the current ruleset from the kernel and try to associate them with
+ * the ruleset we're optimizing.
+ */
+int
+load_feedback_profile(struct pfctl *pf, struct superblocks *superblocks)
+{
+ struct superblock *block, *blockcur;
+ struct superblocks prof_superblocks;
+ struct pf_opt_rule *por;
+ struct pf_opt_queue queue;
+ struct pfioc_rule pr;
+ struct pf_rule a, b;
+ int nr, mnr;
+
+ TAILQ_INIT(&queue);
+ TAILQ_INIT(&prof_superblocks);
+
+ memset(&pr, 0, sizeof(pr));
+ pr.rule.action = PF_PASS;
+ if (ioctl(pf->dev, DIOCGETRULES, &pr)) {
+ warn("DIOCGETRULES");
+ return (1);
+ }
+ mnr = pr.nr;
+
+ DEBUG("Loading %d active rules for a feedback profile", mnr);
+ for (nr = 0; nr < mnr; ++nr) {
+ struct pf_ruleset *rs;
+ if ((por = calloc(1, sizeof(*por))) == NULL) {
+ warn("calloc");
+ return (1);
+ }
+ pr.nr = nr;
+ if (ioctl(pf->dev, DIOCGETRULE, &pr)) {
+ warn("DIOCGETRULES");
+ return (1);
+ }
+ memcpy(&por->por_rule, &pr.rule, sizeof(por->por_rule));
+ rs = pf_find_or_create_ruleset(pr.anchor_call);
+ por->por_rule.anchor = rs->anchor;
+ if (TAILQ_EMPTY(&por->por_rule.rpool.list))
+ memset(&por->por_rule.rpool, 0,
+ sizeof(por->por_rule.rpool));
+ TAILQ_INSERT_TAIL(&queue, por, por_entry);
+
+ /* XXX pfctl_get_pool(pf->dev, &pr.rule.rpool, nr, pr.ticket,
+ * PF_PASS, pf->anchor) ???
+ * ... pfctl_clear_pool(&pr.rule.rpool)
+ */
+ }
+
+ if (construct_superblocks(pf, &queue, &prof_superblocks))
+ return (1);
+
+
+ /*
+ * Now we try to associate the active ruleset's superblocks with
+ * the superblocks we're compiling.
+ */
+ block = TAILQ_FIRST(superblocks);
+ blockcur = TAILQ_FIRST(&prof_superblocks);
+ while (block && blockcur) {
+ comparable_rule(&a, &TAILQ_FIRST(&block->sb_rules)->por_rule,
+ BREAK);
+ comparable_rule(&b, &TAILQ_FIRST(&blockcur->sb_rules)->por_rule,
+ BREAK);
+ if (memcmp(&a, &b, sizeof(a)) == 0) {
+ /* The two superblocks lined up */
+ block->sb_profiled_block = blockcur;
+ } else {
+ DEBUG("superblocks don't line up between #%d and #%d",
+ TAILQ_FIRST(&block->sb_rules)->por_rule.nr,
+ TAILQ_FIRST(&blockcur->sb_rules)->por_rule.nr);
+ break;
+ }
+ block = TAILQ_NEXT(block, sb_entry);
+ blockcur = TAILQ_NEXT(blockcur, sb_entry);
+ }
+
+
+
+ /* Free any superblocks we couldn't link */
+ while (blockcur) {
+ block = TAILQ_NEXT(blockcur, sb_entry);
+ superblock_free(pf, blockcur);
+ blockcur = block;
+ }
+ return (0);
+}
+
+
+/*
+ * Compare a rule to a skiplist to see if the rule is a member
+ */
+int
+skip_compare(int skipnum, struct pf_skip_step *skiplist,
+ struct pf_opt_rule *por)
+{
+ struct pf_rule *a, *b;
+ if (skipnum >= PF_SKIP_COUNT || skipnum < 0)
+ errx(1, "skip_compare() out of bounds");
+ a = &por->por_rule;
+ b = &TAILQ_FIRST(&skiplist->ps_rules)->por_rule;
+
+ return ((skip_comparitors[skipnum])(a, b));
+}
+
+
+/*
+ * Add a rule to a skiplist
+ */
+void
+skip_append(struct superblock *superblock, int skipnum,
+ struct pf_skip_step *skiplist, struct pf_opt_rule *por)
+{
+ struct pf_skip_step *prev;
+
+ skiplist->ps_count++;
+ TAILQ_INSERT_TAIL(&skiplist->ps_rules, por, por_skip_entry[skipnum]);
+
+ /* Keep the list of skiplists sorted by whichever is larger */
+ while ((prev = TAILQ_PREV(skiplist, skiplist, ps_entry)) &&
+ prev->ps_count < skiplist->ps_count) {
+ TAILQ_REMOVE(&superblock->sb_skipsteps[skipnum],
+ skiplist, ps_entry);
+ TAILQ_INSERT_BEFORE(prev, skiplist, ps_entry);
+ }
+}
+
+
+/*
+ * Remove a rule from the other skiplist calculations.
+ */
+void
+remove_from_skipsteps(struct skiplist *head, struct superblock *block,
+ struct pf_opt_rule *por, struct pf_skip_step *active_list)
+{
+ struct pf_skip_step *sk, *next;
+ struct pf_opt_rule *p2;
+ int i, found;
+
+ for (i = 0; i < PF_SKIP_COUNT; i++) {
+ sk = TAILQ_FIRST(&block->sb_skipsteps[i]);
+ if (sk == NULL || sk == active_list || sk->ps_count <= 1)
+ continue;
+ found = 0;
+ do {
+ TAILQ_FOREACH(p2, &sk->ps_rules, por_skip_entry[i])
+ if (p2 == por) {
+ TAILQ_REMOVE(&sk->ps_rules, p2,
+ por_skip_entry[i]);
+ found = 1;
+ sk->ps_count--;
+ break;
+ }
+ } while (!found && (sk = TAILQ_NEXT(sk, ps_entry)));
+ if (found && sk) {
+ /* Does this change the sorting order? */
+ while ((next = TAILQ_NEXT(sk, ps_entry)) &&
+ next->ps_count > sk->ps_count) {
+ TAILQ_REMOVE(head, sk, ps_entry);
+ TAILQ_INSERT_AFTER(head, next, sk, ps_entry);
+ }
+#ifdef OPT_DEBUG
+ next = TAILQ_NEXT(sk, ps_entry);
+ assert(next == NULL || next->ps_count <= sk->ps_count);
+#endif /* OPT_DEBUG */
+ }
+ }
+}
+
+
+/* Compare two rules AF field for skiplist construction */
+int
+skip_cmp_af(struct pf_rule *a, struct pf_rule *b)
+{
+ if (a->af != b->af || a->af == 0)
+ return (1);
+ return (0);
+}
+
+/* Compare two rules DIRECTION field for skiplist construction */
+int
+skip_cmp_dir(struct pf_rule *a, struct pf_rule *b)
+{
+ if (a->direction == 0 || a->direction != b->direction)
+ return (1);
+ return (0);
+}
+
+/* Compare two rules DST Address field for skiplist construction */
+int
+skip_cmp_dst_addr(struct pf_rule *a, struct pf_rule *b)
+{
+ if (a->dst.neg != b->dst.neg ||
+ a->dst.addr.type != b->dst.addr.type)
+ return (1);
+ /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0
+ * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP ||
+ * a->proto == IPPROTO_ICMP
+ * return (1);
+ */
+ switch (a->dst.addr.type) {
+ case PF_ADDR_ADDRMASK:
+ if (memcmp(&a->dst.addr.v.a.addr, &b->dst.addr.v.a.addr,
+ sizeof(a->dst.addr.v.a.addr)) ||
+ memcmp(&a->dst.addr.v.a.mask, &b->dst.addr.v.a.mask,
+ sizeof(a->dst.addr.v.a.mask)) ||
+ (a->dst.addr.v.a.addr.addr32[0] == 0 &&
+ a->dst.addr.v.a.addr.addr32[1] == 0 &&
+ a->dst.addr.v.a.addr.addr32[2] == 0 &&
+ a->dst.addr.v.a.addr.addr32[3] == 0))
+ return (1);
+ return (0);
+ case PF_ADDR_DYNIFTL:
+ if (strcmp(a->dst.addr.v.ifname, b->dst.addr.v.ifname) != 0 ||
+ a->dst.addr.iflags != a->dst.addr.iflags ||
+ memcmp(&a->dst.addr.v.a.mask, &b->dst.addr.v.a.mask,
+ sizeof(a->dst.addr.v.a.mask)))
+ return (1);
+ return (0);
+ case PF_ADDR_NOROUTE:
+ case PF_ADDR_URPFFAILED:
+ return (0);
+ case PF_ADDR_TABLE:
+ return (strcmp(a->dst.addr.v.tblname, b->dst.addr.v.tblname));
+ }
+ return (1);
+}
+
+/* Compare two rules DST port field for skiplist construction */
+int
+skip_cmp_dst_port(struct pf_rule *a, struct pf_rule *b)
+{
+ /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0
+ * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP ||
+ * a->proto == IPPROTO_ICMP
+ * return (1);
+ */
+ if (a->dst.port_op == PF_OP_NONE || a->dst.port_op != b->dst.port_op ||
+ a->dst.port[0] != b->dst.port[0] ||
+ a->dst.port[1] != b->dst.port[1])
+ return (1);
+ return (0);
+}
+
+/* Compare two rules IFP field for skiplist construction */
+int
+skip_cmp_ifp(struct pf_rule *a, struct pf_rule *b)
+{
+ if (strcmp(a->ifname, b->ifname) || a->ifname[0] == '\0')
+ return (1);
+ return (a->ifnot != b->ifnot);
+}
+
+/* Compare two rules PROTO field for skiplist construction */
+int
+skip_cmp_proto(struct pf_rule *a, struct pf_rule *b)
+{
+ return (a->proto != b->proto || a->proto == 0);
+}
+
+/* Compare two rules SRC addr field for skiplist construction */
+int
+skip_cmp_src_addr(struct pf_rule *a, struct pf_rule *b)
+{
+ if (a->src.neg != b->src.neg ||
+ a->src.addr.type != b->src.addr.type)
+ return (1);
+ /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0
+ * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP ||
+ * a->proto == IPPROTO_ICMP
+ * return (1);
+ */
+ switch (a->src.addr.type) {
+ case PF_ADDR_ADDRMASK:
+ if (memcmp(&a->src.addr.v.a.addr, &b->src.addr.v.a.addr,
+ sizeof(a->src.addr.v.a.addr)) ||
+ memcmp(&a->src.addr.v.a.mask, &b->src.addr.v.a.mask,
+ sizeof(a->src.addr.v.a.mask)) ||
+ (a->src.addr.v.a.addr.addr32[0] == 0 &&
+ a->src.addr.v.a.addr.addr32[1] == 0 &&
+ a->src.addr.v.a.addr.addr32[2] == 0 &&
+ a->src.addr.v.a.addr.addr32[3] == 0))
+ return (1);
+ return (0);
+ case PF_ADDR_DYNIFTL:
+ if (strcmp(a->src.addr.v.ifname, b->src.addr.v.ifname) != 0 ||
+ a->src.addr.iflags != a->src.addr.iflags ||
+ memcmp(&a->src.addr.v.a.mask, &b->src.addr.v.a.mask,
+ sizeof(a->src.addr.v.a.mask)))
+ return (1);
+ return (0);
+ case PF_ADDR_NOROUTE:
+ case PF_ADDR_URPFFAILED:
+ return (0);
+ case PF_ADDR_TABLE:
+ return (strcmp(a->src.addr.v.tblname, b->src.addr.v.tblname));
+ }
+ return (1);
+}
+
+/* Compare two rules SRC port field for skiplist construction */
+int
+skip_cmp_src_port(struct pf_rule *a, struct pf_rule *b)
+{
+ if (a->src.port_op == PF_OP_NONE || a->src.port_op != b->src.port_op ||
+ a->src.port[0] != b->src.port[0] ||
+ a->src.port[1] != b->src.port[1])
+ return (1);
+ /* XXX if (a->proto != b->proto && a->proto != 0 && b->proto != 0
+ * && (a->proto == IPPROTO_TCP || a->proto == IPPROTO_UDP ||
+ * a->proto == IPPROTO_ICMP
+ * return (1);
+ */
+ return (0);
+}
+
+
+void
+skip_init(void)
+{
+ struct {
+ char *name;
+ int skipnum;
+ int (*func)(struct pf_rule *, struct pf_rule *);
+ } comps[] = PF_SKIP_COMPARITORS;
+ int skipnum, i;
+
+ for (skipnum = 0; skipnum < PF_SKIP_COUNT; skipnum++) {
+ for (i = 0; i < sizeof(comps)/sizeof(*comps); i++)
+ if (comps[i].skipnum == skipnum) {
+ skip_comparitors[skipnum] = comps[i].func;
+ skip_comparitors_names[skipnum] = comps[i].name;
+ }
+ }
+ for (skipnum = 0; skipnum < PF_SKIP_COUNT; skipnum++)
+ if (skip_comparitors[skipnum] == NULL)
+ errx(1, "Need to add skip step comparitor to pfctl?!");
+}
+
+/*
+ * Add a host/netmask to a table
+ */
+int
+add_opt_table(struct pfctl *pf, struct pf_opt_tbl **tbl, sa_family_t af,
+ struct pf_rule_addr *addr)
+{
+#ifdef OPT_DEBUG
+ char buf[128];
+#endif /* OPT_DEBUG */
+ static int tablenum = 0;
+ struct node_host node_host;
+
+ if (*tbl == NULL) {
+ if ((*tbl = calloc(1, sizeof(**tbl))) == NULL ||
+ ((*tbl)->pt_buf = calloc(1, sizeof(*(*tbl)->pt_buf))) ==
+ NULL)
+ err(1, "calloc");
+ (*tbl)->pt_buf->pfrb_type = PFRB_ADDRS;
+ SIMPLEQ_INIT(&(*tbl)->pt_nodes);
+
+ /* This is just a temporary table name */
+ snprintf((*tbl)->pt_name, sizeof((*tbl)->pt_name), "%s%d",
+ PF_OPT_TABLE_PREFIX, tablenum++);
+ DEBUG("creating table <%s>", (*tbl)->pt_name);
+ }
+
+ memset(&node_host, 0, sizeof(node_host));
+ node_host.af = af;
+ node_host.addr = addr->addr;
+
+#ifdef OPT_DEBUG
+ DEBUG("<%s> adding %s/%d", (*tbl)->pt_name, inet_ntop(af,
+ &node_host.addr.v.a.addr, buf, sizeof(buf)),
+ unmask(&node_host.addr.v.a.mask, af));
+#endif /* OPT_DEBUG */
+
+ if (append_addr_host((*tbl)->pt_buf, &node_host, 0, 0)) {
+ warn("failed to add host");
+ return (1);
+ }
+ if (pf->opts & PF_OPT_VERBOSE) {
+ struct node_tinit *ti;
+
+ if ((ti = calloc(1, sizeof(*ti))) == NULL)
+ err(1, "malloc");
+ if ((ti->host = malloc(sizeof(*ti->host))) == NULL)
+ err(1, "malloc");
+ memcpy(ti->host, &node_host, sizeof(*ti->host));
+ SIMPLEQ_INSERT_TAIL(&(*tbl)->pt_nodes, ti, entries);
+ }
+
+ (*tbl)->pt_rulecount++;
+ if ((*tbl)->pt_rulecount == TABLE_THRESHOLD)
+ DEBUG("table <%s> now faster than skip steps", (*tbl)->pt_name);
+
+ return (0);
+}
+
+
+/*
+ * Do the dirty work of choosing an unused table name and creating it.
+ * (be careful with the table name, it might already be used in another anchor)
+ */
+int
+pf_opt_create_table(struct pfctl *pf, struct pf_opt_tbl *tbl)
+{
+ static int tablenum;
+ struct pfr_table *t;
+
+ if (table_buffer.pfrb_type == 0) {
+ /* Initialize the list of tables */
+ table_buffer.pfrb_type = PFRB_TABLES;
+ for (;;) {
+ pfr_buf_grow(&table_buffer, table_buffer.pfrb_size);
+ table_buffer.pfrb_size = table_buffer.pfrb_msize;
+ if (pfr_get_tables(NULL, table_buffer.pfrb_caddr,
+ &table_buffer.pfrb_size, PFR_FLAG_ALLRSETS))
+ err(1, "pfr_get_tables");
+ if (table_buffer.pfrb_size <= table_buffer.pfrb_msize)
+ break;
+ }
+ table_identifier = arc4random();
+ }
+
+ /* XXX would be *really* nice to avoid duplicating identical tables */
+
+ /* Now we have to pick a table name that isn't used */
+again:
+ DEBUG("translating temporary table <%s> to <%s%x_%d>", tbl->pt_name,
+ PF_OPT_TABLE_PREFIX, table_identifier, tablenum);
+ snprintf(tbl->pt_name, sizeof(tbl->pt_name), "%s%x_%d",
+ PF_OPT_TABLE_PREFIX, table_identifier, tablenum);
+ PFRB_FOREACH(t, &table_buffer) {
+ if (strcasecmp(t->pfrt_name, tbl->pt_name) == 0) {
+ /* Collision. Try again */
+ DEBUG("wow, table <%s> in use. trying again",
+ tbl->pt_name);
+ table_identifier = arc4random();
+ goto again;
+ }
+ }
+ tablenum++;
+
+
+ if (pfctl_define_table(tbl->pt_name, PFR_TFLAG_CONST, 1,
+ pf->astack[0]->name, tbl->pt_buf, pf->astack[0]->ruleset.tticket)) {
+ warn("failed to create table %s in %s",
+ tbl->pt_name, pf->astack[0]->name);
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Partition the flat ruleset into a list of distinct superblocks
+ */
+int
+construct_superblocks(struct pfctl *pf, struct pf_opt_queue *opt_queue,
+ struct superblocks *superblocks)
+{
+ struct superblock *block = NULL;
+ struct pf_opt_rule *por;
+ int i;
+
+ while (!TAILQ_EMPTY(opt_queue)) {
+ por = TAILQ_FIRST(opt_queue);
+ TAILQ_REMOVE(opt_queue, por, por_entry);
+ if (block == NULL || !superblock_inclusive(block, por)) {
+ if ((block = calloc(1, sizeof(*block))) == NULL) {
+ warn("calloc");
+ return (1);
+ }
+ TAILQ_INIT(&block->sb_rules);
+ for (i = 0; i < PF_SKIP_COUNT; i++)
+ TAILQ_INIT(&block->sb_skipsteps[i]);
+ TAILQ_INSERT_TAIL(superblocks, block, sb_entry);
+ }
+ TAILQ_INSERT_TAIL(&block->sb_rules, por, por_entry);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Compare two rule addresses
+ */
+int
+addrs_equal(struct pf_rule_addr *a, struct pf_rule_addr *b)
+{
+ if (a->neg != b->neg)
+ return (0);
+ return (memcmp(&a->addr, &b->addr, sizeof(a->addr)) == 0);
+}
+
+
+/*
+ * The addresses are not equal, but can we combine them into one table?
+ */
+int
+addrs_combineable(struct pf_rule_addr *a, struct pf_rule_addr *b)
+{
+ if (a->addr.type != PF_ADDR_ADDRMASK ||
+ b->addr.type != PF_ADDR_ADDRMASK)
+ return (0);
+ if (a->neg != b->neg || a->port_op != b->port_op ||
+ a->port[0] != b->port[0] || a->port[1] != b->port[1])
+ return (0);
+ return (1);
+}
+
+
+/*
+ * Are we allowed to combine these two rules
+ */
+int
+rules_combineable(struct pf_rule *p1, struct pf_rule *p2)
+{
+ struct pf_rule a, b;
+
+ comparable_rule(&a, p1, COMBINED);
+ comparable_rule(&b, p2, COMBINED);
+ return (memcmp(&a, &b, sizeof(a)) == 0);
+}
+
+
+/*
+ * Can a rule be included inside a superblock
+ */
+int
+superblock_inclusive(struct superblock *block, struct pf_opt_rule *por)
+{
+ struct pf_rule a, b;
+ int i, j;
+
+ /* First check for hard breaks */
+ for (i = 0; i < sizeof(pf_rule_desc)/sizeof(*pf_rule_desc); i++) {
+ if (pf_rule_desc[i].prf_type == BARRIER) {
+ for (j = 0; j < pf_rule_desc[i].prf_size; j++)
+ if (((char *)&por->por_rule)[j +
+ pf_rule_desc[i].prf_offset] != 0)
+ return (0);
+ }
+ }
+
+ /* per-rule src-track is also a hard break */
+ if (por->por_rule.rule_flag & PFRULE_RULESRCTRACK)
+ return (0);
+
+ /*
+ * Have to handle interface groups separately. Consider the following
+ * rules:
+ * block on EXTIFS to any port 22
+ * pass on em0 to any port 22
+ * (where EXTIFS is an arbitrary interface group)
+ * The optimizer may decide to re-order the pass rule in front of the
+ * block rule. But what if EXTIFS includes em0??? Such a reordering
+ * would change the meaning of the ruleset.
+ * We can't just lookup the EXTIFS group and check if em0 is a member
+ * because the user is allowed to add interfaces to a group during
+ * runtime.
+ * Ergo interface groups become a defacto superblock break :-(
+ */
+ if (interface_group(por->por_rule.ifname) ||
+ interface_group(TAILQ_FIRST(&block->sb_rules)->por_rule.ifname)) {
+ if (strcasecmp(por->por_rule.ifname,
+ TAILQ_FIRST(&block->sb_rules)->por_rule.ifname) != 0)
+ return (0);
+ }
+
+ comparable_rule(&a, &TAILQ_FIRST(&block->sb_rules)->por_rule, NOMERGE);
+ comparable_rule(&b, &por->por_rule, NOMERGE);
+ if (memcmp(&a, &b, sizeof(a)) == 0)
+ return (1);
+
+#ifdef OPT_DEBUG
+ for (i = 0; i < sizeof(por->por_rule); i++) {
+ int closest = -1;
+ if (((u_int8_t *)&a)[i] != ((u_int8_t *)&b)[i]) {
+ for (j = 0; j < sizeof(pf_rule_desc) /
+ sizeof(*pf_rule_desc); j++) {
+ if (i >= pf_rule_desc[j].prf_offset &&
+ i < pf_rule_desc[j].prf_offset +
+ pf_rule_desc[j].prf_size) {
+ DEBUG("superblock break @ %d due to %s",
+ por->por_rule.nr,
+ pf_rule_desc[j].prf_name);
+ return (0);
+ }
+ if (i > pf_rule_desc[j].prf_offset) {
+ if (closest == -1 ||
+ i-pf_rule_desc[j].prf_offset <
+ i-pf_rule_desc[closest].prf_offset)
+ closest = j;
+ }
+ }
+
+ if (closest >= 0)
+ DEBUG("superblock break @ %d on %s+%xh",
+ por->por_rule.nr,
+ pf_rule_desc[closest].prf_name,
+ i - pf_rule_desc[closest].prf_offset -
+ pf_rule_desc[closest].prf_size);
+ else
+ DEBUG("superblock break @ %d on field @ %d",
+ por->por_rule.nr, i);
+ return (0);
+ }
+ }
+#endif /* OPT_DEBUG */
+
+ return (0);
+}
+
+
+/*
+ * Figure out if an interface name is an actual interface or actually a
+ * group of interfaces.
+ */
+int
+interface_group(const char *ifname)
+{
+ if (ifname == NULL || !ifname[0])
+ return (0);
+
+ /* Real interfaces must end in a number, interface groups do not */
+ if (isdigit(ifname[strlen(ifname) - 1]))
+ return (0);
+ else
+ return (1);
+}
+
+
+/*
+ * Make a rule that can directly compared by memcmp()
+ */
+void
+comparable_rule(struct pf_rule *dst, const struct pf_rule *src, int type)
+{
+ int i;
+ /*
+ * To simplify the comparison, we just zero out the fields that are
+ * allowed to be different and then do a simple memcmp()
+ */
+ memcpy(dst, src, sizeof(*dst));
+ for (i = 0; i < sizeof(pf_rule_desc)/sizeof(*pf_rule_desc); i++)
+ if (pf_rule_desc[i].prf_type >= type) {
+#ifdef OPT_DEBUG
+ assert(pf_rule_desc[i].prf_type != NEVER ||
+ *(((char *)dst) + pf_rule_desc[i].prf_offset) == 0);
+#endif /* OPT_DEBUG */
+ memset(((char *)dst) + pf_rule_desc[i].prf_offset, 0,
+ pf_rule_desc[i].prf_size);
+ }
+}
+
+
+/*
+ * Remove superset information from two rules so we can directly compare them
+ * with memcmp()
+ */
+void
+exclude_supersets(struct pf_rule *super, struct pf_rule *sub)
+{
+ if (super->ifname[0] == '\0')
+ memset(sub->ifname, 0, sizeof(sub->ifname));
+ if (super->direction == PF_INOUT)
+ sub->direction = PF_INOUT;
+ if ((super->proto == 0 || super->proto == sub->proto) &&
+ super->flags == 0 && super->flagset == 0 && (sub->flags ||
+ sub->flagset)) {
+ sub->flags = super->flags;
+ sub->flagset = super->flagset;
+ }
+ if (super->proto == 0)
+ sub->proto = 0;
+
+ if (super->src.port_op == 0) {
+ sub->src.port_op = 0;
+ sub->src.port[0] = 0;
+ sub->src.port[1] = 0;
+ }
+ if (super->dst.port_op == 0) {
+ sub->dst.port_op = 0;
+ sub->dst.port[0] = 0;
+ sub->dst.port[1] = 0;
+ }
+
+ if (super->src.addr.type == PF_ADDR_ADDRMASK && !super->src.neg &&
+ !sub->src.neg && super->src.addr.v.a.mask.addr32[0] == 0 &&
+ super->src.addr.v.a.mask.addr32[1] == 0 &&
+ super->src.addr.v.a.mask.addr32[2] == 0 &&
+ super->src.addr.v.a.mask.addr32[3] == 0)
+ memset(&sub->src.addr, 0, sizeof(sub->src.addr));
+ else if (super->src.addr.type == PF_ADDR_ADDRMASK &&
+ sub->src.addr.type == PF_ADDR_ADDRMASK &&
+ super->src.neg == sub->src.neg &&
+ super->af == sub->af &&
+ unmask(&super->src.addr.v.a.mask, super->af) <
+ unmask(&sub->src.addr.v.a.mask, sub->af) &&
+ super->src.addr.v.a.addr.addr32[0] ==
+ (sub->src.addr.v.a.addr.addr32[0] &
+ super->src.addr.v.a.mask.addr32[0]) &&
+ super->src.addr.v.a.addr.addr32[1] ==
+ (sub->src.addr.v.a.addr.addr32[1] &
+ super->src.addr.v.a.mask.addr32[1]) &&
+ super->src.addr.v.a.addr.addr32[2] ==
+ (sub->src.addr.v.a.addr.addr32[2] &
+ super->src.addr.v.a.mask.addr32[2]) &&
+ super->src.addr.v.a.addr.addr32[3] ==
+ (sub->src.addr.v.a.addr.addr32[3] &
+ super->src.addr.v.a.mask.addr32[3])) {
+ /* sub->src.addr is a subset of super->src.addr/mask */
+ memcpy(&sub->src.addr, &super->src.addr, sizeof(sub->src.addr));
+ }
+
+ if (super->dst.addr.type == PF_ADDR_ADDRMASK && !super->dst.neg &&
+ !sub->dst.neg && super->dst.addr.v.a.mask.addr32[0] == 0 &&
+ super->dst.addr.v.a.mask.addr32[1] == 0 &&
+ super->dst.addr.v.a.mask.addr32[2] == 0 &&
+ super->dst.addr.v.a.mask.addr32[3] == 0)
+ memset(&sub->dst.addr, 0, sizeof(sub->dst.addr));
+ else if (super->dst.addr.type == PF_ADDR_ADDRMASK &&
+ sub->dst.addr.type == PF_ADDR_ADDRMASK &&
+ super->dst.neg == sub->dst.neg &&
+ super->af == sub->af &&
+ unmask(&super->dst.addr.v.a.mask, super->af) <
+ unmask(&sub->dst.addr.v.a.mask, sub->af) &&
+ super->dst.addr.v.a.addr.addr32[0] ==
+ (sub->dst.addr.v.a.addr.addr32[0] &
+ super->dst.addr.v.a.mask.addr32[0]) &&
+ super->dst.addr.v.a.addr.addr32[1] ==
+ (sub->dst.addr.v.a.addr.addr32[1] &
+ super->dst.addr.v.a.mask.addr32[1]) &&
+ super->dst.addr.v.a.addr.addr32[2] ==
+ (sub->dst.addr.v.a.addr.addr32[2] &
+ super->dst.addr.v.a.mask.addr32[2]) &&
+ super->dst.addr.v.a.addr.addr32[3] ==
+ (sub->dst.addr.v.a.addr.addr32[3] &
+ super->dst.addr.v.a.mask.addr32[3])) {
+ /* sub->dst.addr is a subset of super->dst.addr/mask */
+ memcpy(&sub->dst.addr, &super->dst.addr, sizeof(sub->dst.addr));
+ }
+
+ if (super->af == 0)
+ sub->af = 0;
+}
+
+
+void
+superblock_free(struct pfctl *pf, struct superblock *block)
+{
+ struct pf_opt_rule *por;
+ while ((por = TAILQ_FIRST(&block->sb_rules))) {
+ TAILQ_REMOVE(&block->sb_rules, por, por_entry);
+ if (por->por_src_tbl) {
+ if (por->por_src_tbl->pt_buf) {
+ pfr_buf_clear(por->por_src_tbl->pt_buf);
+ free(por->por_src_tbl->pt_buf);
+ }
+ free(por->por_src_tbl);
+ }
+ if (por->por_dst_tbl) {
+ if (por->por_dst_tbl->pt_buf) {
+ pfr_buf_clear(por->por_dst_tbl->pt_buf);
+ free(por->por_dst_tbl->pt_buf);
+ }
+ free(por->por_dst_tbl);
+ }
+ free(por);
+ }
+ if (block->sb_profiled_block)
+ superblock_free(pf, block->sb_profiled_block);
+ free(block);
+}
+
diff --git a/sbin/pfctl/pfctl_osfp.c b/sbin/pfctl/pfctl_osfp.c
new file mode 100644
index 000000000000..df789811ddb5
--- /dev/null
+++ b/sbin/pfctl/pfctl_osfp.c
@@ -0,0 +1,1108 @@
+/* $OpenBSD: pfctl_osfp.c,v 1.14 2006/04/08 02:13:14 ray Exp $ */
+
+/*
+ * Copyright (c) 2003 Mike Frantzen <frantzen@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/pfvar.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pfctl_parser.h"
+#include "pfctl.h"
+
+#ifndef MIN
+# define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif /* MIN */
+#ifndef MAX
+# define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif /* MAX */
+
+
+#if 0
+# define DEBUG(fp, str, v...) \
+ fprintf(stderr, "%s:%s:%s " str "\n", (fp)->fp_os.fp_class_nm, \
+ (fp)->fp_os.fp_version_nm, (fp)->fp_os.fp_subtype_nm , ## v);
+#else
+# define DEBUG(fp, str, v...) ((void)0)
+#endif
+
+
+struct name_entry;
+LIST_HEAD(name_list, name_entry);
+struct name_entry {
+ LIST_ENTRY(name_entry) nm_entry;
+ int nm_num;
+ char nm_name[PF_OSFP_LEN];
+
+ struct name_list nm_sublist;
+ int nm_sublist_num;
+};
+struct name_list classes = LIST_HEAD_INITIALIZER(&classes);
+int class_count;
+int fingerprint_count;
+
+void add_fingerprint(int, int, struct pf_osfp_ioctl *);
+struct name_entry *fingerprint_name_entry(struct name_list *, char *);
+void pfctl_flush_my_fingerprints(struct name_list *);
+char *get_field(char **, size_t *, int *);
+int get_int(char **, size_t *, int *, int *, const char *,
+ int, int, const char *, int);
+int get_str(char **, size_t *, char **, const char *, int,
+ const char *, int);
+int get_tcpopts(const char *, int, const char *,
+ pf_tcpopts_t *, int *, int *, int *, int *, int *,
+ int *);
+void import_fingerprint(struct pf_osfp_ioctl *);
+const char *print_ioctl(struct pf_osfp_ioctl *);
+void print_name_list(int, struct name_list *, const char *);
+void sort_name_list(int, struct name_list *);
+struct name_entry *lookup_name_list(struct name_list *, const char *);
+
+/* Load fingerprints from a file */
+int
+pfctl_file_fingerprints(int dev, int opts, const char *fp_filename)
+{
+ FILE *in;
+ char *line;
+ size_t len;
+ int i, lineno = 0;
+ int window, w_mod, ttl, df, psize, p_mod, mss, mss_mod, wscale,
+ wscale_mod, optcnt, ts0;
+ pf_tcpopts_t packed_tcpopts;
+ char *class, *version, *subtype, *desc, *tcpopts;
+ struct pf_osfp_ioctl fp;
+
+ pfctl_flush_my_fingerprints(&classes);
+
+ if ((in = pfctl_fopen(fp_filename, "r")) == NULL) {
+ warn("%s", fp_filename);
+ return (1);
+ }
+ class = version = subtype = desc = tcpopts = NULL;
+
+ if ((opts & PF_OPT_NOACTION) == 0)
+ pfctl_clear_fingerprints(dev, opts);
+
+ while ((line = fgetln(in, &len)) != NULL) {
+ lineno++;
+ if (class)
+ free(class);
+ if (version)
+ free(version);
+ if (subtype)
+ free(subtype);
+ if (desc)
+ free(desc);
+ if (tcpopts)
+ free(tcpopts);
+ class = version = subtype = desc = tcpopts = NULL;
+ memset(&fp, 0, sizeof(fp));
+
+ /* Chop off comment */
+ for (i = 0; i < len; i++)
+ if (line[i] == '#') {
+ len = i;
+ break;
+ }
+ /* Chop off whitespace */
+ while (len > 0 && isspace(line[len - 1]))
+ len--;
+ while (len > 0 && isspace(line[0])) {
+ len--;
+ line++;
+ }
+ if (len == 0)
+ continue;
+
+#define T_DC 0x01 /* Allow don't care */
+#define T_MSS 0x02 /* Allow MSS multiple */
+#define T_MTU 0x04 /* Allow MTU multiple */
+#define T_MOD 0x08 /* Allow modulus */
+
+#define GET_INT(v, mod, n, ty, mx) \
+ get_int(&line, &len, &v, mod, n, ty, mx, fp_filename, lineno)
+#define GET_STR(v, n, mn) \
+ get_str(&line, &len, &v, n, mn, fp_filename, lineno)
+
+ if (GET_INT(window, &w_mod, "window size", T_DC|T_MSS|T_MTU|
+ T_MOD, 0xffff) ||
+ GET_INT(ttl, NULL, "ttl", 0, 0xff) ||
+ GET_INT(df, NULL, "don't fragment frag", 0, 1) ||
+ GET_INT(psize, &p_mod, "overall packet size", T_MOD|T_DC,
+ 8192) ||
+ GET_STR(tcpopts, "TCP Options", 1) ||
+ GET_STR(class, "OS class", 1) ||
+ GET_STR(version, "OS version", 0) ||
+ GET_STR(subtype, "OS subtype", 0) ||
+ GET_STR(desc, "OS description", 2))
+ continue;
+ if (get_tcpopts(fp_filename, lineno, tcpopts, &packed_tcpopts,
+ &optcnt, &mss, &mss_mod, &wscale, &wscale_mod, &ts0))
+ continue;
+ if (len != 0) {
+ fprintf(stderr, "%s:%d excess field\n", fp_filename,
+ lineno);
+ continue;
+ }
+
+ fp.fp_ttl = ttl;
+ if (df)
+ fp.fp_flags |= PF_OSFP_DF;
+ switch (w_mod) {
+ case 0:
+ break;
+ case T_DC:
+ fp.fp_flags |= PF_OSFP_WSIZE_DC;
+ break;
+ case T_MSS:
+ fp.fp_flags |= PF_OSFP_WSIZE_MSS;
+ break;
+ case T_MTU:
+ fp.fp_flags |= PF_OSFP_WSIZE_MTU;
+ break;
+ case T_MOD:
+ fp.fp_flags |= PF_OSFP_WSIZE_MOD;
+ break;
+ }
+ fp.fp_wsize = window;
+
+ switch (p_mod) {
+ case T_DC:
+ fp.fp_flags |= PF_OSFP_PSIZE_DC;
+ break;
+ case T_MOD:
+ fp.fp_flags |= PF_OSFP_PSIZE_MOD;
+ }
+ fp.fp_psize = psize;
+
+
+ switch (wscale_mod) {
+ case T_DC:
+ fp.fp_flags |= PF_OSFP_WSCALE_DC;
+ break;
+ case T_MOD:
+ fp.fp_flags |= PF_OSFP_WSCALE_MOD;
+ }
+ fp.fp_wscale = wscale;
+
+ switch (mss_mod) {
+ case T_DC:
+ fp.fp_flags |= PF_OSFP_MSS_DC;
+ break;
+ case T_MOD:
+ fp.fp_flags |= PF_OSFP_MSS_MOD;
+ break;
+ }
+ fp.fp_mss = mss;
+
+ fp.fp_tcpopts = packed_tcpopts;
+ fp.fp_optcnt = optcnt;
+ if (ts0)
+ fp.fp_flags |= PF_OSFP_TS0;
+
+ if (class[0] == '@')
+ fp.fp_os.fp_enflags |= PF_OSFP_GENERIC;
+ if (class[0] == '*')
+ fp.fp_os.fp_enflags |= PF_OSFP_NODETAIL;
+
+ if (class[0] == '@' || class[0] == '*')
+ strlcpy(fp.fp_os.fp_class_nm, class + 1,
+ sizeof(fp.fp_os.fp_class_nm));
+ else
+ strlcpy(fp.fp_os.fp_class_nm, class,
+ sizeof(fp.fp_os.fp_class_nm));
+ strlcpy(fp.fp_os.fp_version_nm, version,
+ sizeof(fp.fp_os.fp_version_nm));
+ strlcpy(fp.fp_os.fp_subtype_nm, subtype,
+ sizeof(fp.fp_os.fp_subtype_nm));
+
+ add_fingerprint(dev, opts, &fp);
+
+ fp.fp_flags |= (PF_OSFP_DF | PF_OSFP_INET6);
+ fp.fp_psize += sizeof(struct ip6_hdr) - sizeof(struct ip);
+ add_fingerprint(dev, opts, &fp);
+ }
+
+ if (class)
+ free(class);
+ if (version)
+ free(version);
+ if (subtype)
+ free(subtype);
+ if (desc)
+ free(desc);
+ if (tcpopts)
+ free(tcpopts);
+
+ fclose(in);
+
+ if (opts & PF_OPT_VERBOSE2)
+ printf("Loaded %d passive OS fingerprints\n",
+ fingerprint_count);
+ return (0);
+}
+
+/* flush the kernel's fingerprints */
+void
+pfctl_clear_fingerprints(int dev, int opts)
+{
+ if (ioctl(dev, DIOCOSFPFLUSH))
+ err(1, "DIOCOSFPFLUSH");
+}
+
+/* flush pfctl's view of the fingerprints */
+void
+pfctl_flush_my_fingerprints(struct name_list *list)
+{
+ struct name_entry *nm;
+
+ while ((nm = LIST_FIRST(list)) != NULL) {
+ LIST_REMOVE(nm, nm_entry);
+ pfctl_flush_my_fingerprints(&nm->nm_sublist);
+ free(nm);
+ }
+ fingerprint_count = 0;
+ class_count = 0;
+}
+
+/* Fetch the active fingerprints from the kernel */
+int
+pfctl_load_fingerprints(int dev, int opts)
+{
+ struct pf_osfp_ioctl io;
+ int i;
+
+ pfctl_flush_my_fingerprints(&classes);
+
+ for (i = 0; i >= 0; i++) {
+ memset(&io, 0, sizeof(io));
+ io.fp_getnum = i;
+ if (ioctl(dev, DIOCOSFPGET, &io)) {
+ if (errno == EBUSY)
+ break;
+ warn("DIOCOSFPGET");
+ return (1);
+ }
+ import_fingerprint(&io);
+ }
+ return (0);
+}
+
+/* List the fingerprints */
+void
+pfctl_show_fingerprints(int opts)
+{
+ if (LIST_FIRST(&classes) != NULL) {
+ if (opts & PF_OPT_SHOWALL) {
+ pfctl_print_title("OS FINGERPRINTS:");
+ printf("%u fingerprints loaded\n", fingerprint_count);
+ } else {
+ printf("Class\tVersion\tSubtype(subversion)\n");
+ printf("-----\t-------\t-------------------\n");
+ sort_name_list(opts, &classes);
+ print_name_list(opts, &classes, "");
+ }
+ }
+}
+
+/* Lookup a fingerprint */
+pf_osfp_t
+pfctl_get_fingerprint(const char *name)
+{
+ struct name_entry *nm, *class_nm, *version_nm, *subtype_nm;
+ pf_osfp_t ret = PF_OSFP_NOMATCH;
+ int class, version, subtype;
+ int unp_class, unp_version, unp_subtype;
+ int wr_len, version_len, subtype_len;
+ char *ptr, *wr_name;
+
+ if (strcasecmp(name, "unknown") == 0)
+ return (PF_OSFP_UNKNOWN);
+
+ /* Try most likely no version and no subtype */
+ if ((nm = lookup_name_list(&classes, name))) {
+ class = nm->nm_num;
+ version = PF_OSFP_ANY;
+ subtype = PF_OSFP_ANY;
+ goto found;
+ } else {
+
+ /* Chop it up into class/version/subtype */
+
+ if ((wr_name = strdup(name)) == NULL)
+ err(1, "malloc");
+ if ((ptr = strchr(wr_name, ' ')) == NULL) {
+ free(wr_name);
+ return (PF_OSFP_NOMATCH);
+ }
+ *ptr++ = '\0';
+
+ /* The class is easy to find since it is delimited by a space */
+ if ((class_nm = lookup_name_list(&classes, wr_name)) == NULL) {
+ free(wr_name);
+ return (PF_OSFP_NOMATCH);
+ }
+ class = class_nm->nm_num;
+
+ /* Try no subtype */
+ if ((version_nm = lookup_name_list(&class_nm->nm_sublist, ptr)))
+ {
+ version = version_nm->nm_num;
+ subtype = PF_OSFP_ANY;
+ free(wr_name);
+ goto found;
+ }
+
+
+ /*
+ * There must be a version and a subtype.
+ * We'll do some fuzzy matching to pick up things like:
+ * Linux 2.2.14 (version=2.2 subtype=14)
+ * FreeBSD 4.0-STABLE (version=4.0 subtype=STABLE)
+ * Windows 2000 SP2 (version=2000 subtype=SP2)
+ */
+#define CONNECTOR(x) ((x) == '.' || (x) == ' ' || (x) == '\t' || (x) == '-')
+ wr_len = strlen(ptr);
+ LIST_FOREACH(version_nm, &class_nm->nm_sublist, nm_entry) {
+ version_len = strlen(version_nm->nm_name);
+ if (wr_len < version_len + 2 ||
+ !CONNECTOR(ptr[version_len]))
+ continue;
+ /* first part of the string must be version */
+ if (strncasecmp(ptr, version_nm->nm_name,
+ version_len))
+ continue;
+
+ LIST_FOREACH(subtype_nm, &version_nm->nm_sublist,
+ nm_entry) {
+ subtype_len = strlen(subtype_nm->nm_name);
+ if (wr_len != version_len + subtype_len + 1)
+ continue;
+
+ /* last part of the string must be subtype */
+ if (strcasecmp(&ptr[version_len+1],
+ subtype_nm->nm_name) != 0)
+ continue;
+
+ /* Found it!! */
+ version = version_nm->nm_num;
+ subtype = subtype_nm->nm_num;
+ free(wr_name);
+ goto found;
+ }
+ }
+
+ free(wr_name);
+ return (PF_OSFP_NOMATCH);
+ }
+
+found:
+ PF_OSFP_PACK(ret, class, version, subtype);
+ if (ret != PF_OSFP_NOMATCH) {
+ PF_OSFP_UNPACK(ret, unp_class, unp_version, unp_subtype);
+ if (class != unp_class) {
+ fprintf(stderr, "warning: fingerprint table overflowed "
+ "classes\n");
+ return (PF_OSFP_NOMATCH);
+ }
+ if (version != unp_version) {
+ fprintf(stderr, "warning: fingerprint table overflowed "
+ "versions\n");
+ return (PF_OSFP_NOMATCH);
+ }
+ if (subtype != unp_subtype) {
+ fprintf(stderr, "warning: fingerprint table overflowed "
+ "subtypes\n");
+ return (PF_OSFP_NOMATCH);
+ }
+ }
+ if (ret == PF_OSFP_ANY) {
+ /* should never happen */
+ fprintf(stderr, "warning: fingerprint packed to 'any'\n");
+ return (PF_OSFP_NOMATCH);
+ }
+
+ return (ret);
+}
+
+/* Lookup a fingerprint name by ID */
+char *
+pfctl_lookup_fingerprint(pf_osfp_t fp, char *buf, size_t len)
+{
+ int class, version, subtype;
+ struct name_list *list;
+ struct name_entry *nm;
+
+ char *class_name, *version_name, *subtype_name;
+ class_name = version_name = subtype_name = NULL;
+
+ if (fp == PF_OSFP_UNKNOWN) {
+ strlcpy(buf, "unknown", len);
+ return (buf);
+ }
+ if (fp == PF_OSFP_ANY) {
+ strlcpy(buf, "any", len);
+ return (buf);
+ }
+
+ PF_OSFP_UNPACK(fp, class, version, subtype);
+ if (class >= (1 << _FP_CLASS_BITS) ||
+ version >= (1 << _FP_VERSION_BITS) ||
+ subtype >= (1 << _FP_SUBTYPE_BITS)) {
+ warnx("PF_OSFP_UNPACK(0x%x) failed!!", fp);
+ strlcpy(buf, "nomatch", len);
+ return (buf);
+ }
+
+ LIST_FOREACH(nm, &classes, nm_entry) {
+ if (nm->nm_num == class) {
+ class_name = nm->nm_name;
+ if (version == PF_OSFP_ANY)
+ goto found;
+ list = &nm->nm_sublist;
+ LIST_FOREACH(nm, list, nm_entry) {
+ if (nm->nm_num == version) {
+ version_name = nm->nm_name;
+ if (subtype == PF_OSFP_ANY)
+ goto found;
+ list = &nm->nm_sublist;
+ LIST_FOREACH(nm, list, nm_entry) {
+ if (nm->nm_num == subtype) {
+ subtype_name =
+ nm->nm_name;
+ goto found;
+ }
+ } /* foreach subtype */
+ strlcpy(buf, "nomatch", len);
+ return (buf);
+ }
+ } /* foreach version */
+ strlcpy(buf, "nomatch", len);
+ return (buf);
+ }
+ } /* foreach class */
+
+ strlcpy(buf, "nomatch", len);
+ return (buf);
+
+found:
+ snprintf(buf, len, "%s", class_name);
+ if (version_name) {
+ strlcat(buf, " ", len);
+ strlcat(buf, version_name, len);
+ if (subtype_name) {
+ if (strchr(version_name, ' '))
+ strlcat(buf, " ", len);
+ else if (strchr(version_name, '.') &&
+ isdigit(*subtype_name))
+ strlcat(buf, ".", len);
+ else
+ strlcat(buf, " ", len);
+ strlcat(buf, subtype_name, len);
+ }
+ }
+ return (buf);
+}
+
+/* lookup a name in a list */
+struct name_entry *
+lookup_name_list(struct name_list *list, const char *name)
+{
+ struct name_entry *nm;
+ LIST_FOREACH(nm, list, nm_entry)
+ if (strcasecmp(name, nm->nm_name) == 0)
+ return (nm);
+
+ return (NULL);
+}
+
+
+void
+add_fingerprint(int dev, int opts, struct pf_osfp_ioctl *fp)
+{
+ struct pf_osfp_ioctl fptmp;
+ struct name_entry *nm_class, *nm_version, *nm_subtype;
+ int class, version, subtype;
+
+/* We expand #-# or #.#-#.# version/subtypes into multiple fingerprints */
+#define EXPAND(field) do { \
+ int _dot = -1, _start = -1, _end = -1, _i = 0; \
+ /* pick major version out of #.# */ \
+ if (isdigit(fp->field[_i]) && fp->field[_i+1] == '.') { \
+ _dot = fp->field[_i] - '0'; \
+ _i += 2; \
+ } \
+ if (isdigit(fp->field[_i])) \
+ _start = fp->field[_i++] - '0'; \
+ else \
+ break; \
+ if (isdigit(fp->field[_i])) \
+ _start = (_start * 10) + fp->field[_i++] - '0'; \
+ if (fp->field[_i++] != '-') \
+ break; \
+ if (isdigit(fp->field[_i]) && fp->field[_i+1] == '.' && \
+ fp->field[_i] - '0' == _dot) \
+ _i += 2; \
+ else if (_dot != -1) \
+ break; \
+ if (isdigit(fp->field[_i])) \
+ _end = fp->field[_i++] - '0'; \
+ else \
+ break; \
+ if (isdigit(fp->field[_i])) \
+ _end = (_end * 10) + fp->field[_i++] - '0'; \
+ if (isdigit(fp->field[_i])) \
+ _end = (_end * 10) + fp->field[_i++] - '0'; \
+ if (fp->field[_i] != '\0') \
+ break; \
+ memcpy(&fptmp, fp, sizeof(fptmp)); \
+ for (;_start <= _end; _start++) { \
+ memset(fptmp.field, 0, sizeof(fptmp.field)); \
+ fptmp.fp_os.fp_enflags |= PF_OSFP_EXPANDED; \
+ if (_dot == -1) \
+ snprintf(fptmp.field, sizeof(fptmp.field), \
+ "%d", _start); \
+ else \
+ snprintf(fptmp.field, sizeof(fptmp.field), \
+ "%d.%d", _dot, _start); \
+ add_fingerprint(dev, opts, &fptmp); \
+ } \
+} while(0)
+
+ /* We allow "#-#" as a version or subtype and we'll expand it */
+ EXPAND(fp_os.fp_version_nm);
+ EXPAND(fp_os.fp_subtype_nm);
+
+ if (strcasecmp(fp->fp_os.fp_class_nm, "nomatch") == 0)
+ errx(1, "fingerprint class \"nomatch\" is reserved");
+
+ version = PF_OSFP_ANY;
+ subtype = PF_OSFP_ANY;
+
+ nm_class = fingerprint_name_entry(&classes, fp->fp_os.fp_class_nm);
+ if (nm_class->nm_num == 0)
+ nm_class->nm_num = ++class_count;
+ class = nm_class->nm_num;
+
+ nm_version = fingerprint_name_entry(&nm_class->nm_sublist,
+ fp->fp_os.fp_version_nm);
+ if (nm_version) {
+ if (nm_version->nm_num == 0)
+ nm_version->nm_num = ++nm_class->nm_sublist_num;
+ version = nm_version->nm_num;
+ nm_subtype = fingerprint_name_entry(&nm_version->nm_sublist,
+ fp->fp_os.fp_subtype_nm);
+ if (nm_subtype) {
+ if (nm_subtype->nm_num == 0)
+ nm_subtype->nm_num =
+ ++nm_version->nm_sublist_num;
+ subtype = nm_subtype->nm_num;
+ }
+ }
+
+
+ DEBUG(fp, "\tsignature %d:%d:%d %s", class, version, subtype,
+ print_ioctl(fp));
+
+ PF_OSFP_PACK(fp->fp_os.fp_os, class, version, subtype);
+ fingerprint_count++;
+
+#ifdef FAKE_PF_KERNEL
+ /* Linked to the sys/net/pf_osfp.c. Call pf_osfp_add() */
+ if ((errno = pf_osfp_add(fp)))
+#else
+ if ((opts & PF_OPT_NOACTION) == 0 && ioctl(dev, DIOCOSFPADD, fp))
+#endif /* FAKE_PF_KERNEL */
+ {
+ if (errno == EEXIST) {
+ warn("Duplicate signature for %s %s %s",
+ fp->fp_os.fp_class_nm,
+ fp->fp_os.fp_version_nm,
+ fp->fp_os.fp_subtype_nm);
+
+ } else {
+ err(1, "DIOCOSFPADD");
+ }
+ }
+}
+
+/* import a fingerprint from the kernel */
+void
+import_fingerprint(struct pf_osfp_ioctl *fp)
+{
+ struct name_entry *nm_class, *nm_version, *nm_subtype;
+ int class, version, subtype;
+
+ PF_OSFP_UNPACK(fp->fp_os.fp_os, class, version, subtype);
+
+ nm_class = fingerprint_name_entry(&classes, fp->fp_os.fp_class_nm);
+ if (nm_class->nm_num == 0) {
+ nm_class->nm_num = class;
+ class_count = MAX(class_count, class);
+ }
+
+ nm_version = fingerprint_name_entry(&nm_class->nm_sublist,
+ fp->fp_os.fp_version_nm);
+ if (nm_version) {
+ if (nm_version->nm_num == 0) {
+ nm_version->nm_num = version;
+ nm_class->nm_sublist_num = MAX(nm_class->nm_sublist_num,
+ version);
+ }
+ nm_subtype = fingerprint_name_entry(&nm_version->nm_sublist,
+ fp->fp_os.fp_subtype_nm);
+ if (nm_subtype) {
+ if (nm_subtype->nm_num == 0) {
+ nm_subtype->nm_num = subtype;
+ nm_version->nm_sublist_num =
+ MAX(nm_version->nm_sublist_num, subtype);
+ }
+ }
+ }
+
+
+ fingerprint_count++;
+ DEBUG(fp, "import signature %d:%d:%d", class, version, subtype);
+}
+
+/* Find an entry for a fingerprints class/version/subtype */
+struct name_entry *
+fingerprint_name_entry(struct name_list *list, char *name)
+{
+ struct name_entry *nm_entry;
+
+ if (name == NULL || strlen(name) == 0)
+ return (NULL);
+
+ LIST_FOREACH(nm_entry, list, nm_entry) {
+ if (strcasecmp(nm_entry->nm_name, name) == 0) {
+ /* We'll move this to the front of the list later */
+ LIST_REMOVE(nm_entry, nm_entry);
+ break;
+ }
+ }
+ if (nm_entry == NULL) {
+ nm_entry = calloc(1, sizeof(*nm_entry));
+ if (nm_entry == NULL)
+ err(1, "calloc");
+ LIST_INIT(&nm_entry->nm_sublist);
+ strlcpy(nm_entry->nm_name, name, sizeof(nm_entry->nm_name));
+ }
+ LIST_INSERT_HEAD(list, nm_entry, nm_entry);
+ return (nm_entry);
+}
+
+
+void
+print_name_list(int opts, struct name_list *nml, const char *prefix)
+{
+ char newprefix[32];
+ struct name_entry *nm;
+
+ LIST_FOREACH(nm, nml, nm_entry) {
+ snprintf(newprefix, sizeof(newprefix), "%s%s\t", prefix,
+ nm->nm_name);
+ printf("%s\n", newprefix);
+ print_name_list(opts, &nm->nm_sublist, newprefix);
+ }
+}
+
+void
+sort_name_list(int opts, struct name_list *nml)
+{
+ struct name_list new;
+ struct name_entry *nm, *nmsearch, *nmlast;
+
+ /* yes yes, it's a very slow sort. so sue me */
+
+ LIST_INIT(&new);
+
+ while ((nm = LIST_FIRST(nml)) != NULL) {
+ LIST_REMOVE(nm, nm_entry);
+ nmlast = NULL;
+ LIST_FOREACH(nmsearch, &new, nm_entry) {
+ if (strcasecmp(nmsearch->nm_name, nm->nm_name) > 0) {
+ LIST_INSERT_BEFORE(nmsearch, nm, nm_entry);
+ break;
+ }
+ nmlast = nmsearch;
+ }
+ if (nmsearch == NULL) {
+ if (nmlast)
+ LIST_INSERT_AFTER(nmlast, nm, nm_entry);
+ else
+ LIST_INSERT_HEAD(&new, nm, nm_entry);
+ }
+
+ sort_name_list(opts, &nm->nm_sublist);
+ }
+ nmlast = NULL;
+ while ((nm = LIST_FIRST(&new)) != NULL) {
+ LIST_REMOVE(nm, nm_entry);
+ if (nmlast == NULL)
+ LIST_INSERT_HEAD(nml, nm, nm_entry);
+ else
+ LIST_INSERT_AFTER(nmlast, nm, nm_entry);
+ nmlast = nm;
+ }
+}
+
+/* parse the next integer in a formatted config file line */
+int
+get_int(char **line, size_t *len, int *var, int *mod,
+ const char *name, int flags, int max, const char *filename, int lineno)
+{
+ int fieldlen, i;
+ char *field;
+ long val = 0;
+
+ if (mod)
+ *mod = 0;
+ *var = 0;
+
+ field = get_field(line, len, &fieldlen);
+ if (field == NULL)
+ return (1);
+ if (fieldlen == 0) {
+ fprintf(stderr, "%s:%d empty %s\n", filename, lineno, name);
+ return (1);
+ }
+
+ i = 0;
+ if ((*field == '%' || *field == 'S' || *field == 'T' || *field == '*')
+ && fieldlen >= 1) {
+ switch (*field) {
+ case 'S':
+ if (mod && (flags & T_MSS))
+ *mod = T_MSS;
+ if (fieldlen == 1)
+ return (0);
+ break;
+ case 'T':
+ if (mod && (flags & T_MTU))
+ *mod = T_MTU;
+ if (fieldlen == 1)
+ return (0);
+ break;
+ case '*':
+ if (fieldlen != 1) {
+ fprintf(stderr, "%s:%d long '%c' %s\n",
+ filename, lineno, *field, name);
+ return (1);
+ }
+ if (mod && (flags & T_DC)) {
+ *mod = T_DC;
+ return (0);
+ }
+ case '%':
+ if (mod && (flags & T_MOD))
+ *mod = T_MOD;
+ if (fieldlen == 1) {
+ fprintf(stderr, "%s:%d modulus %s must have a "
+ "value\n", filename, lineno, name);
+ return (1);
+ }
+ break;
+ }
+ if (mod == NULL || *mod == 0) {
+ fprintf(stderr, "%s:%d does not allow %c' %s\n",
+ filename, lineno, *field, name);
+ return (1);
+ }
+ i++;
+ }
+
+ for (; i < fieldlen; i++) {
+ if (field[i] < '0' || field[i] > '9') {
+ fprintf(stderr, "%s:%d non-digit character in %s\n",
+ filename, lineno, name);
+ return (1);
+ }
+ val = val * 10 + field[i] - '0';
+ if (val < 0) {
+ fprintf(stderr, "%s:%d %s overflowed\n", filename,
+ lineno, name);
+ return (1);
+ }
+ }
+
+ if (val > max) {
+ fprintf(stderr, "%s:%d %s value %ld > %d\n", filename, lineno,
+ name, val, max);
+ return (1);
+ }
+ *var = (int)val;
+
+ return (0);
+}
+
+/* parse the next string in a formatted config file line */
+int
+get_str(char **line, size_t *len, char **v, const char *name, int minlen,
+ const char *filename, int lineno)
+{
+ int fieldlen;
+ char *ptr;
+
+ ptr = get_field(line, len, &fieldlen);
+ if (ptr == NULL)
+ return (1);
+ if (fieldlen < minlen) {
+ fprintf(stderr, "%s:%d too short %s\n", filename, lineno, name);
+ return (1);
+ }
+ if ((*v = malloc(fieldlen + 1)) == NULL) {
+ perror("malloc()");
+ return (1);
+ }
+ memcpy(*v, ptr, fieldlen);
+ (*v)[fieldlen] = '\0';
+
+ return (0);
+}
+
+/* Parse out the TCP opts */
+int
+get_tcpopts(const char *filename, int lineno, const char *tcpopts,
+ pf_tcpopts_t *packed, int *optcnt, int *mss, int *mss_mod, int *wscale,
+ int *wscale_mod, int *ts0)
+{
+ int i, opt;
+
+ *packed = 0;
+ *optcnt = 0;
+ *wscale = 0;
+ *wscale_mod = T_DC;
+ *mss = 0;
+ *mss_mod = T_DC;
+ *ts0 = 0;
+ if (strcmp(tcpopts, ".") == 0)
+ return (0);
+
+ for (i = 0; tcpopts[i] && *optcnt < PF_OSFP_MAX_OPTS;) {
+ switch ((opt = toupper(tcpopts[i++]))) {
+ case 'N': /* FALLTHROUGH */
+ case 'S':
+ *packed = (*packed << PF_OSFP_TCPOPT_BITS) |
+ (opt == 'N' ? PF_OSFP_TCPOPT_NOP :
+ PF_OSFP_TCPOPT_SACK);
+ break;
+ case 'W': /* FALLTHROUGH */
+ case 'M': {
+ int *this_mod, *this;
+
+ if (opt == 'W') {
+ this = wscale;
+ this_mod = wscale_mod;
+ } else {
+ this = mss;
+ this_mod = mss_mod;
+ }
+ *this = 0;
+ *this_mod = 0;
+
+ *packed = (*packed << PF_OSFP_TCPOPT_BITS) |
+ (opt == 'W' ? PF_OSFP_TCPOPT_WSCALE :
+ PF_OSFP_TCPOPT_MSS);
+ if (tcpopts[i] == '*' && (tcpopts[i + 1] == '\0' ||
+ tcpopts[i + 1] == ',')) {
+ *this_mod = T_DC;
+ i++;
+ break;
+ }
+
+ if (tcpopts[i] == '%') {
+ *this_mod = T_MOD;
+ i++;
+ }
+ do {
+ if (!isdigit(tcpopts[i])) {
+ fprintf(stderr, "%s:%d unknown "
+ "character '%c' in %c TCP opt\n",
+ filename, lineno, tcpopts[i], opt);
+ return (1);
+ }
+ *this = (*this * 10) + tcpopts[i++] - '0';
+ } while(tcpopts[i] != ',' && tcpopts[i] != '\0');
+ break;
+ }
+ case 'T':
+ if (tcpopts[i] == '0') {
+ *ts0 = 1;
+ i++;
+ }
+ *packed = (*packed << PF_OSFP_TCPOPT_BITS) |
+ PF_OSFP_TCPOPT_TS;
+ break;
+ }
+ (*optcnt) ++;
+ if (tcpopts[i] == '\0')
+ break;
+ if (tcpopts[i] != ',') {
+ fprintf(stderr, "%s:%d unknown option to %c TCP opt\n",
+ filename, lineno, opt);
+ return (1);
+ }
+ i++;
+ }
+
+ return (0);
+}
+
+/* rip the next field ouf of a formatted config file line */
+char *
+get_field(char **line, size_t *len, int *fieldlen)
+{
+ char *ret, *ptr = *line;
+ size_t plen = *len;
+
+
+ while (plen && isspace(*ptr)) {
+ plen--;
+ ptr++;
+ }
+ ret = ptr;
+ *fieldlen = 0;
+
+ for (; plen > 0 && *ptr != ':'; plen--, ptr++)
+ (*fieldlen)++;
+ if (plen) {
+ *line = ptr + 1;
+ *len = plen - 1;
+ } else {
+ *len = 0;
+ }
+ while (*fieldlen && isspace(ret[*fieldlen - 1]))
+ (*fieldlen)--;
+ return (ret);
+}
+
+
+const char *
+print_ioctl(struct pf_osfp_ioctl *fp)
+{
+ static char buf[1024];
+ char tmp[32];
+ int i, opt;
+
+ *buf = '\0';
+ if (fp->fp_flags & PF_OSFP_WSIZE_DC)
+ strlcat(buf, "*", sizeof(buf));
+ else if (fp->fp_flags & PF_OSFP_WSIZE_MSS)
+ strlcat(buf, "S", sizeof(buf));
+ else if (fp->fp_flags & PF_OSFP_WSIZE_MTU)
+ strlcat(buf, "T", sizeof(buf));
+ else {
+ if (fp->fp_flags & PF_OSFP_WSIZE_MOD)
+ strlcat(buf, "%", sizeof(buf));
+ snprintf(tmp, sizeof(tmp), "%d", fp->fp_wsize);
+ strlcat(buf, tmp, sizeof(buf));
+ }
+ strlcat(buf, ":", sizeof(buf));
+
+ snprintf(tmp, sizeof(tmp), "%d", fp->fp_ttl);
+ strlcat(buf, tmp, sizeof(buf));
+ strlcat(buf, ":", sizeof(buf));
+
+ if (fp->fp_flags & PF_OSFP_DF)
+ strlcat(buf, "1", sizeof(buf));
+ else
+ strlcat(buf, "0", sizeof(buf));
+ strlcat(buf, ":", sizeof(buf));
+
+ if (fp->fp_flags & PF_OSFP_PSIZE_DC)
+ strlcat(buf, "*", sizeof(buf));
+ else {
+ if (fp->fp_flags & PF_OSFP_PSIZE_MOD)
+ strlcat(buf, "%", sizeof(buf));
+ snprintf(tmp, sizeof(tmp), "%d", fp->fp_psize);
+ strlcat(buf, tmp, sizeof(buf));
+ }
+ strlcat(buf, ":", sizeof(buf));
+
+ if (fp->fp_optcnt == 0)
+ strlcat(buf, ".", sizeof(buf));
+ for (i = fp->fp_optcnt - 1; i >= 0; i--) {
+ opt = fp->fp_tcpopts >> (i * PF_OSFP_TCPOPT_BITS);
+ opt &= (1 << PF_OSFP_TCPOPT_BITS) - 1;
+ switch (opt) {
+ case PF_OSFP_TCPOPT_NOP:
+ strlcat(buf, "N", sizeof(buf));
+ break;
+ case PF_OSFP_TCPOPT_SACK:
+ strlcat(buf, "S", sizeof(buf));
+ break;
+ case PF_OSFP_TCPOPT_TS:
+ strlcat(buf, "T", sizeof(buf));
+ if (fp->fp_flags & PF_OSFP_TS0)
+ strlcat(buf, "0", sizeof(buf));
+ break;
+ case PF_OSFP_TCPOPT_MSS:
+ strlcat(buf, "M", sizeof(buf));
+ if (fp->fp_flags & PF_OSFP_MSS_DC)
+ strlcat(buf, "*", sizeof(buf));
+ else {
+ if (fp->fp_flags & PF_OSFP_MSS_MOD)
+ strlcat(buf, "%", sizeof(buf));
+ snprintf(tmp, sizeof(tmp), "%d", fp->fp_mss);
+ strlcat(buf, tmp, sizeof(buf));
+ }
+ break;
+ case PF_OSFP_TCPOPT_WSCALE:
+ strlcat(buf, "W", sizeof(buf));
+ if (fp->fp_flags & PF_OSFP_WSCALE_DC)
+ strlcat(buf, "*", sizeof(buf));
+ else {
+ if (fp->fp_flags & PF_OSFP_WSCALE_MOD)
+ strlcat(buf, "%", sizeof(buf));
+ snprintf(tmp, sizeof(tmp), "%d", fp->fp_wscale);
+ strlcat(buf, tmp, sizeof(buf));
+ }
+ break;
+ }
+
+ if (i != 0)
+ strlcat(buf, ",", sizeof(buf));
+ }
+ strlcat(buf, ":", sizeof(buf));
+
+ strlcat(buf, fp->fp_os.fp_class_nm, sizeof(buf));
+ strlcat(buf, ":", sizeof(buf));
+ strlcat(buf, fp->fp_os.fp_version_nm, sizeof(buf));
+ strlcat(buf, ":", sizeof(buf));
+ strlcat(buf, fp->fp_os.fp_subtype_nm, sizeof(buf));
+ strlcat(buf, ":", sizeof(buf));
+
+ snprintf(tmp, sizeof(tmp), "TcpOpts %d 0x%llx", fp->fp_optcnt,
+ (long long int)fp->fp_tcpopts);
+ strlcat(buf, tmp, sizeof(buf));
+
+ return (buf);
+}
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
new file mode 100644
index 000000000000..f2489956daa4
--- /dev/null
+++ b/sbin/pfctl/pfctl_parser.c
@@ -0,0 +1,1746 @@
+/* $OpenBSD: pfctl_parser.c,v 1.240 2008/06/10 20:55:02 mcbride Exp $ */
+
+/*
+ * Copyright (c) 2001 Daniel Hartmeier
+ * Copyright (c) 2002,2003 Henning Brauer
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <sys/proc.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/icmp6.h>
+#include <net/pfvar.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <err.h>
+#include <ifaddrs.h>
+#include <unistd.h>
+
+#include "pfctl_parser.h"
+#include "pfctl.h"
+
+void print_op (u_int8_t, const char *, const char *);
+void print_port (u_int8_t, u_int16_t, u_int16_t, const char *, int);
+void print_ugid (u_int8_t, unsigned, unsigned, const char *, unsigned);
+void print_flags (u_int8_t);
+void print_fromto(struct pf_rule_addr *, pf_osfp_t,
+ struct pf_rule_addr *, u_int8_t, u_int8_t, int, int);
+int ifa_skip_if(const char *filter, struct node_host *p);
+
+struct node_host *ifa_grouplookup(const char *, int);
+struct node_host *host_if(const char *, int);
+struct node_host *host_v4(const char *, int);
+struct node_host *host_v6(const char *, int);
+struct node_host *host_dns(const char *, int, int);
+
+const char *tcpflags = "FSRPAUEW";
+
+static const struct icmptypeent icmp_type[] = {
+ { "echoreq", ICMP_ECHO },
+ { "echorep", ICMP_ECHOREPLY },
+ { "unreach", ICMP_UNREACH },
+ { "squench", ICMP_SOURCEQUENCH },
+ { "redir", ICMP_REDIRECT },
+ { "althost", ICMP_ALTHOSTADDR },
+ { "routeradv", ICMP_ROUTERADVERT },
+ { "routersol", ICMP_ROUTERSOLICIT },
+ { "timex", ICMP_TIMXCEED },
+ { "paramprob", ICMP_PARAMPROB },
+ { "timereq", ICMP_TSTAMP },
+ { "timerep", ICMP_TSTAMPREPLY },
+ { "inforeq", ICMP_IREQ },
+ { "inforep", ICMP_IREQREPLY },
+ { "maskreq", ICMP_MASKREQ },
+ { "maskrep", ICMP_MASKREPLY },
+ { "trace", ICMP_TRACEROUTE },
+ { "dataconv", ICMP_DATACONVERR },
+ { "mobredir", ICMP_MOBILE_REDIRECT },
+ { "ipv6-where", ICMP_IPV6_WHEREAREYOU },
+ { "ipv6-here", ICMP_IPV6_IAMHERE },
+ { "mobregreq", ICMP_MOBILE_REGREQUEST },
+ { "mobregrep", ICMP_MOBILE_REGREPLY },
+ { "skip", ICMP_SKIP },
+ { "photuris", ICMP_PHOTURIS }
+};
+
+static const struct icmptypeent icmp6_type[] = {
+ { "unreach", ICMP6_DST_UNREACH },
+ { "toobig", ICMP6_PACKET_TOO_BIG },
+ { "timex", ICMP6_TIME_EXCEEDED },
+ { "paramprob", ICMP6_PARAM_PROB },
+ { "echoreq", ICMP6_ECHO_REQUEST },
+ { "echorep", ICMP6_ECHO_REPLY },
+ { "groupqry", ICMP6_MEMBERSHIP_QUERY },
+ { "listqry", MLD_LISTENER_QUERY },
+ { "grouprep", ICMP6_MEMBERSHIP_REPORT },
+ { "listenrep", MLD_LISTENER_REPORT },
+ { "groupterm", ICMP6_MEMBERSHIP_REDUCTION },
+ { "listendone", MLD_LISTENER_DONE },
+ { "routersol", ND_ROUTER_SOLICIT },
+ { "routeradv", ND_ROUTER_ADVERT },
+ { "neighbrsol", ND_NEIGHBOR_SOLICIT },
+ { "neighbradv", ND_NEIGHBOR_ADVERT },
+ { "redir", ND_REDIRECT },
+ { "routrrenum", ICMP6_ROUTER_RENUMBERING },
+ { "wrureq", ICMP6_WRUREQUEST },
+ { "wrurep", ICMP6_WRUREPLY },
+ { "fqdnreq", ICMP6_FQDN_QUERY },
+ { "fqdnrep", ICMP6_FQDN_REPLY },
+ { "niqry", ICMP6_NI_QUERY },
+ { "nirep", ICMP6_NI_REPLY },
+ { "mtraceresp", MLD_MTRACE_RESP },
+ { "mtrace", MLD_MTRACE }
+};
+
+static const struct icmpcodeent icmp_code[] = {
+ { "net-unr", ICMP_UNREACH, ICMP_UNREACH_NET },
+ { "host-unr", ICMP_UNREACH, ICMP_UNREACH_HOST },
+ { "proto-unr", ICMP_UNREACH, ICMP_UNREACH_PROTOCOL },
+ { "port-unr", ICMP_UNREACH, ICMP_UNREACH_PORT },
+ { "needfrag", ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG },
+ { "srcfail", ICMP_UNREACH, ICMP_UNREACH_SRCFAIL },
+ { "net-unk", ICMP_UNREACH, ICMP_UNREACH_NET_UNKNOWN },
+ { "host-unk", ICMP_UNREACH, ICMP_UNREACH_HOST_UNKNOWN },
+ { "isolate", ICMP_UNREACH, ICMP_UNREACH_ISOLATED },
+ { "net-prohib", ICMP_UNREACH, ICMP_UNREACH_NET_PROHIB },
+ { "host-prohib", ICMP_UNREACH, ICMP_UNREACH_HOST_PROHIB },
+ { "net-tos", ICMP_UNREACH, ICMP_UNREACH_TOSNET },
+ { "host-tos", ICMP_UNREACH, ICMP_UNREACH_TOSHOST },
+ { "filter-prohib", ICMP_UNREACH, ICMP_UNREACH_FILTER_PROHIB },
+ { "host-preced", ICMP_UNREACH, ICMP_UNREACH_HOST_PRECEDENCE },
+ { "cutoff-preced", ICMP_UNREACH, ICMP_UNREACH_PRECEDENCE_CUTOFF },
+ { "redir-net", ICMP_REDIRECT, ICMP_REDIRECT_NET },
+ { "redir-host", ICMP_REDIRECT, ICMP_REDIRECT_HOST },
+ { "redir-tos-net", ICMP_REDIRECT, ICMP_REDIRECT_TOSNET },
+ { "redir-tos-host", ICMP_REDIRECT, ICMP_REDIRECT_TOSHOST },
+ { "normal-adv", ICMP_ROUTERADVERT, ICMP_ROUTERADVERT_NORMAL },
+ { "common-adv", ICMP_ROUTERADVERT, ICMP_ROUTERADVERT_NOROUTE_COMMON },
+ { "transit", ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS },
+ { "reassemb", ICMP_TIMXCEED, ICMP_TIMXCEED_REASS },
+ { "badhead", ICMP_PARAMPROB, ICMP_PARAMPROB_ERRATPTR },
+ { "optmiss", ICMP_PARAMPROB, ICMP_PARAMPROB_OPTABSENT },
+ { "badlen", ICMP_PARAMPROB, ICMP_PARAMPROB_LENGTH },
+ { "unknown-ind", ICMP_PHOTURIS, ICMP_PHOTURIS_UNKNOWN_INDEX },
+ { "auth-fail", ICMP_PHOTURIS, ICMP_PHOTURIS_AUTH_FAILED },
+ { "decrypt-fail", ICMP_PHOTURIS, ICMP_PHOTURIS_DECRYPT_FAILED }
+};
+
+static const struct icmpcodeent icmp6_code[] = {
+ { "admin-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN },
+ { "noroute-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE },
+ { "notnbr-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOTNEIGHBOR },
+ { "beyond-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE },
+ { "addr-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR },
+ { "port-unr", ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT },
+ { "transit", ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT },
+ { "reassemb", ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_REASSEMBLY },
+ { "badhead", ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER },
+ { "nxthdr", ICMP6_PARAM_PROB, ICMP6_PARAMPROB_NEXTHEADER },
+ { "redironlink", ND_REDIRECT, ND_REDIRECT_ONLINK },
+ { "redirrouter", ND_REDIRECT, ND_REDIRECT_ROUTER }
+};
+
+const struct pf_timeout pf_timeouts[] = {
+ { "tcp.first", PFTM_TCP_FIRST_PACKET },
+ { "tcp.opening", PFTM_TCP_OPENING },
+ { "tcp.established", PFTM_TCP_ESTABLISHED },
+ { "tcp.closing", PFTM_TCP_CLOSING },
+ { "tcp.finwait", PFTM_TCP_FIN_WAIT },
+ { "tcp.closed", PFTM_TCP_CLOSED },
+ { "tcp.tsdiff", PFTM_TS_DIFF },
+ { "udp.first", PFTM_UDP_FIRST_PACKET },
+ { "udp.single", PFTM_UDP_SINGLE },
+ { "udp.multiple", PFTM_UDP_MULTIPLE },
+ { "icmp.first", PFTM_ICMP_FIRST_PACKET },
+ { "icmp.error", PFTM_ICMP_ERROR_REPLY },
+ { "other.first", PFTM_OTHER_FIRST_PACKET },
+ { "other.single", PFTM_OTHER_SINGLE },
+ { "other.multiple", PFTM_OTHER_MULTIPLE },
+ { "frag", PFTM_FRAG },
+ { "interval", PFTM_INTERVAL },
+ { "adaptive.start", PFTM_ADAPTIVE_START },
+ { "adaptive.end", PFTM_ADAPTIVE_END },
+ { "src.track", PFTM_SRC_NODE },
+ { NULL, 0 }
+};
+
+const struct icmptypeent *
+geticmptypebynumber(u_int8_t type, sa_family_t af)
+{
+ unsigned int i;
+
+ if (af != AF_INET6) {
+ for (i=0; i < (sizeof (icmp_type) / sizeof(icmp_type[0]));
+ i++) {
+ if (type == icmp_type[i].type)
+ return (&icmp_type[i]);
+ }
+ } else {
+ for (i=0; i < (sizeof (icmp6_type) /
+ sizeof(icmp6_type[0])); i++) {
+ if (type == icmp6_type[i].type)
+ return (&icmp6_type[i]);
+ }
+ }
+ return (NULL);
+}
+
+const struct icmptypeent *
+geticmptypebyname(char *w, sa_family_t af)
+{
+ unsigned int i;
+
+ if (af != AF_INET6) {
+ for (i=0; i < (sizeof (icmp_type) / sizeof(icmp_type[0]));
+ i++) {
+ if (!strcmp(w, icmp_type[i].name))
+ return (&icmp_type[i]);
+ }
+ } else {
+ for (i=0; i < (sizeof (icmp6_type) /
+ sizeof(icmp6_type[0])); i++) {
+ if (!strcmp(w, icmp6_type[i].name))
+ return (&icmp6_type[i]);
+ }
+ }
+ return (NULL);
+}
+
+const struct icmpcodeent *
+geticmpcodebynumber(u_int8_t type, u_int8_t code, sa_family_t af)
+{
+ unsigned int i;
+
+ if (af != AF_INET6) {
+ for (i=0; i < (sizeof (icmp_code) / sizeof(icmp_code[0]));
+ i++) {
+ if (type == icmp_code[i].type &&
+ code == icmp_code[i].code)
+ return (&icmp_code[i]);
+ }
+ } else {
+ for (i=0; i < (sizeof (icmp6_code) /
+ sizeof(icmp6_code[0])); i++) {
+ if (type == icmp6_code[i].type &&
+ code == icmp6_code[i].code)
+ return (&icmp6_code[i]);
+ }
+ }
+ return (NULL);
+}
+
+const struct icmpcodeent *
+geticmpcodebyname(u_long type, char *w, sa_family_t af)
+{
+ unsigned int i;
+
+ if (af != AF_INET6) {
+ for (i=0; i < (sizeof (icmp_code) / sizeof(icmp_code[0]));
+ i++) {
+ if (type == icmp_code[i].type &&
+ !strcmp(w, icmp_code[i].name))
+ return (&icmp_code[i]);
+ }
+ } else {
+ for (i=0; i < (sizeof (icmp6_code) /
+ sizeof(icmp6_code[0])); i++) {
+ if (type == icmp6_code[i].type &&
+ !strcmp(w, icmp6_code[i].name))
+ return (&icmp6_code[i]);
+ }
+ }
+ return (NULL);
+}
+
+void
+print_op(u_int8_t op, const char *a1, const char *a2)
+{
+ if (op == PF_OP_IRG)
+ printf(" %s >< %s", a1, a2);
+ else if (op == PF_OP_XRG)
+ printf(" %s <> %s", a1, a2);
+ else if (op == PF_OP_EQ)
+ printf(" = %s", a1);
+ else if (op == PF_OP_NE)
+ printf(" != %s", a1);
+ else if (op == PF_OP_LT)
+ printf(" < %s", a1);
+ else if (op == PF_OP_LE)
+ printf(" <= %s", a1);
+ else if (op == PF_OP_GT)
+ printf(" > %s", a1);
+ else if (op == PF_OP_GE)
+ printf(" >= %s", a1);
+ else if (op == PF_OP_RRG)
+ printf(" %s:%s", a1, a2);
+}
+
+void
+print_port(u_int8_t op, u_int16_t p1, u_int16_t p2, const char *proto, int numeric)
+{
+ char a1[6], a2[6];
+ struct servent *s;
+
+ if (!numeric)
+ s = getservbyport(p1, proto);
+ else
+ s = NULL;
+ p1 = ntohs(p1);
+ p2 = ntohs(p2);
+ snprintf(a1, sizeof(a1), "%u", p1);
+ snprintf(a2, sizeof(a2), "%u", p2);
+ printf(" port");
+ if (s != NULL && (op == PF_OP_EQ || op == PF_OP_NE))
+ print_op(op, s->s_name, a2);
+ else
+ print_op(op, a1, a2);
+}
+
+void
+print_ugid(u_int8_t op, unsigned u1, unsigned u2, const char *t, unsigned umax)
+{
+ char a1[11], a2[11];
+
+ snprintf(a1, sizeof(a1), "%u", u1);
+ snprintf(a2, sizeof(a2), "%u", u2);
+ printf(" %s", t);
+ if (u1 == umax && (op == PF_OP_EQ || op == PF_OP_NE))
+ print_op(op, "unknown", a2);
+ else
+ print_op(op, a1, a2);
+}
+
+void
+print_flags(u_int8_t f)
+{
+ int i;
+
+ for (i = 0; tcpflags[i]; ++i)
+ if (f & (1 << i))
+ printf("%c", tcpflags[i]);
+}
+
+void
+print_fromto(struct pf_rule_addr *src, pf_osfp_t osfp, struct pf_rule_addr *dst,
+ sa_family_t af, u_int8_t proto, int verbose, int numeric)
+{
+ char buf[PF_OSFP_LEN*3];
+ if (src->addr.type == PF_ADDR_ADDRMASK &&
+ dst->addr.type == PF_ADDR_ADDRMASK &&
+ PF_AZERO(&src->addr.v.a.addr, AF_INET6) &&
+ PF_AZERO(&src->addr.v.a.mask, AF_INET6) &&
+ PF_AZERO(&dst->addr.v.a.addr, AF_INET6) &&
+ PF_AZERO(&dst->addr.v.a.mask, AF_INET6) &&
+ !src->neg && !dst->neg &&
+ !src->port_op && !dst->port_op &&
+ osfp == PF_OSFP_ANY)
+ printf(" all");
+ else {
+ printf(" from ");
+ if (src->neg)
+ printf("! ");
+ print_addr(&src->addr, af, verbose);
+ if (src->port_op)
+ print_port(src->port_op, src->port[0],
+ src->port[1],
+ proto == IPPROTO_TCP ? "tcp" : "udp",
+ numeric);
+ if (osfp != PF_OSFP_ANY)
+ printf(" os \"%s\"", pfctl_lookup_fingerprint(osfp, buf,
+ sizeof(buf)));
+
+ printf(" to ");
+ if (dst->neg)
+ printf("! ");
+ print_addr(&dst->addr, af, verbose);
+ if (dst->port_op)
+ print_port(dst->port_op, dst->port[0],
+ dst->port[1],
+ proto == IPPROTO_TCP ? "tcp" : "udp",
+ numeric);
+ }
+}
+
+void
+print_pool(struct pf_pool *pool, u_int16_t p1, u_int16_t p2,
+ sa_family_t af, int id)
+{
+ struct pf_pooladdr *pooladdr;
+
+ if ((TAILQ_FIRST(&pool->list) != NULL) &&
+ TAILQ_NEXT(TAILQ_FIRST(&pool->list), entries) != NULL)
+ printf("{ ");
+ TAILQ_FOREACH(pooladdr, &pool->list, entries){
+ switch (id) {
+ case PF_NAT:
+ case PF_RDR:
+ case PF_BINAT:
+ print_addr(&pooladdr->addr, af, 0);
+ break;
+ case PF_PASS:
+ if (PF_AZERO(&pooladdr->addr.v.a.addr, af))
+ printf("%s", pooladdr->ifname);
+ else {
+ printf("(%s ", pooladdr->ifname);
+ print_addr(&pooladdr->addr, af, 0);
+ printf(")");
+ }
+ break;
+ default:
+ break;
+ }
+ if (TAILQ_NEXT(pooladdr, entries) != NULL)
+ printf(", ");
+ else if (TAILQ_NEXT(TAILQ_FIRST(&pool->list), entries) != NULL)
+ printf(" }");
+ }
+ switch (id) {
+ case PF_NAT:
+ if ((p1 != PF_NAT_PROXY_PORT_LOW ||
+ p2 != PF_NAT_PROXY_PORT_HIGH) && (p1 != 0 || p2 != 0)) {
+ if (p1 == p2)
+ printf(" port %u", p1);
+ else
+ printf(" port %u:%u", p1, p2);
+ }
+ break;
+ case PF_RDR:
+ if (p1) {
+ printf(" port %u", p1);
+ if (p2 && (p2 != p1))
+ printf(":%u", p2);
+ }
+ break;
+ default:
+ break;
+ }
+ switch (pool->opts & PF_POOL_TYPEMASK) {
+ case PF_POOL_NONE:
+ break;
+ case PF_POOL_BITMASK:
+ printf(" bitmask");
+ break;
+ case PF_POOL_RANDOM:
+ printf(" random");
+ break;
+ case PF_POOL_SRCHASH:
+ printf(" source-hash 0x%08x%08x%08x%08x",
+ pool->key.key32[0], pool->key.key32[1],
+ pool->key.key32[2], pool->key.key32[3]);
+ break;
+ case PF_POOL_ROUNDROBIN:
+ printf(" round-robin");
+ break;
+ }
+ if (pool->opts & PF_POOL_STICKYADDR)
+ printf(" sticky-address");
+ if (id == PF_NAT && p1 == 0 && p2 == 0)
+ printf(" static-port");
+}
+
+const char *pf_reasons[PFRES_MAX+1] = PFRES_NAMES;
+const char *pf_lcounters[LCNT_MAX+1] = LCNT_NAMES;
+const char *pf_fcounters[FCNT_MAX+1] = FCNT_NAMES;
+const char *pf_scounters[FCNT_MAX+1] = FCNT_NAMES;
+
+void
+print_status(struct pf_status *s, int opts)
+{
+ char statline[80], *running;
+ time_t runtime;
+ int i;
+ char buf[PF_MD5_DIGEST_LENGTH * 2 + 1];
+ static const char hex[] = "0123456789abcdef";
+
+ runtime = time(NULL) - s->since;
+ running = s->running ? "Enabled" : "Disabled";
+
+ if (s->since) {
+ unsigned int sec, min, hrs, day = runtime;
+
+ sec = day % 60;
+ day /= 60;
+ min = day % 60;
+ day /= 60;
+ hrs = day % 24;
+ day /= 24;
+ snprintf(statline, sizeof(statline),
+ "Status: %s for %u days %.2u:%.2u:%.2u",
+ running, day, hrs, min, sec);
+ } else
+ snprintf(statline, sizeof(statline), "Status: %s", running);
+ printf("%-44s", statline);
+ switch (s->debug) {
+ case PF_DEBUG_NONE:
+ printf("%15s\n\n", "Debug: None");
+ break;
+ case PF_DEBUG_URGENT:
+ printf("%15s\n\n", "Debug: Urgent");
+ break;
+ case PF_DEBUG_MISC:
+ printf("%15s\n\n", "Debug: Misc");
+ break;
+ case PF_DEBUG_NOISY:
+ printf("%15s\n\n", "Debug: Loud");
+ break;
+ }
+
+ if (opts & PF_OPT_VERBOSE) {
+ printf("Hostid: 0x%08x\n", ntohl(s->hostid));
+
+ for (i = 0; i < PF_MD5_DIGEST_LENGTH; i++) {
+ buf[i + i] = hex[s->pf_chksum[i] >> 4];
+ buf[i + i + 1] = hex[s->pf_chksum[i] & 0x0f];
+ }
+ buf[i + i] = '\0';
+ printf("Checksum: 0x%s\n\n", buf);
+ }
+
+ if (s->ifname[0] != 0) {
+ printf("Interface Stats for %-16s %5s %16s\n",
+ s->ifname, "IPv4", "IPv6");
+ printf(" %-25s %14llu %16llu\n", "Bytes In",
+ (unsigned long long)s->bcounters[0][0],
+ (unsigned long long)s->bcounters[1][0]);
+ printf(" %-25s %14llu %16llu\n", "Bytes Out",
+ (unsigned long long)s->bcounters[0][1],
+ (unsigned long long)s->bcounters[1][1]);
+ printf(" Packets In\n");
+ printf(" %-23s %14llu %16llu\n", "Passed",
+ (unsigned long long)s->pcounters[0][0][PF_PASS],
+ (unsigned long long)s->pcounters[1][0][PF_PASS]);
+ printf(" %-23s %14llu %16llu\n", "Blocked",
+ (unsigned long long)s->pcounters[0][0][PF_DROP],
+ (unsigned long long)s->pcounters[1][0][PF_DROP]);
+ printf(" Packets Out\n");
+ printf(" %-23s %14llu %16llu\n", "Passed",
+ (unsigned long long)s->pcounters[0][1][PF_PASS],
+ (unsigned long long)s->pcounters[1][1][PF_PASS]);
+ printf(" %-23s %14llu %16llu\n\n", "Blocked",
+ (unsigned long long)s->pcounters[0][1][PF_DROP],
+ (unsigned long long)s->pcounters[1][1][PF_DROP]);
+ }
+ printf("%-27s %14s %16s\n", "State Table", "Total", "Rate");
+ printf(" %-25s %14u %14s\n", "current entries", s->states, "");
+ for (i = 0; i < FCNT_MAX; i++) {
+ printf(" %-25s %14llu ", pf_fcounters[i],
+ (unsigned long long)s->fcounters[i]);
+ if (runtime > 0)
+ printf("%14.1f/s\n",
+ (double)s->fcounters[i] / (double)runtime);
+ else
+ printf("%14s\n", "");
+ }
+ if (opts & PF_OPT_VERBOSE) {
+ printf("Source Tracking Table\n");
+ printf(" %-25s %14u %14s\n", "current entries",
+ s->src_nodes, "");
+ for (i = 0; i < SCNT_MAX; i++) {
+ printf(" %-25s %14lld ", pf_scounters[i],
+#ifdef __FreeBSD__
+ (long long)s->scounters[i]);
+#else
+ s->scounters[i]);
+#endif
+ if (runtime > 0)
+ printf("%14.1f/s\n",
+ (double)s->scounters[i] / (double)runtime);
+ else
+ printf("%14s\n", "");
+ }
+ }
+ printf("Counters\n");
+ for (i = 0; i < PFRES_MAX; i++) {
+ printf(" %-25s %14llu ", pf_reasons[i],
+ (unsigned long long)s->counters[i]);
+ if (runtime > 0)
+ printf("%14.1f/s\n",
+ (double)s->counters[i] / (double)runtime);
+ else
+ printf("%14s\n", "");
+ }
+ if (opts & PF_OPT_VERBOSE) {
+ printf("Limit Counters\n");
+ for (i = 0; i < LCNT_MAX; i++) {
+ printf(" %-25s %14lld ", pf_lcounters[i],
+#ifdef __FreeBSD__
+ (unsigned long long)s->lcounters[i]);
+#else
+ s->lcounters[i]);
+#endif
+ if (runtime > 0)
+ printf("%14.1f/s\n",
+ (double)s->lcounters[i] / (double)runtime);
+ else
+ printf("%14s\n", "");
+ }
+ }
+}
+
+void
+print_src_node(struct pf_src_node *sn, int opts)
+{
+ struct pf_addr_wrap aw;
+ int min, sec;
+
+ memset(&aw, 0, sizeof(aw));
+ if (sn->af == AF_INET)
+ aw.v.a.mask.addr32[0] = 0xffffffff;
+ else
+ memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask));
+
+ aw.v.a.addr = sn->addr;
+ print_addr(&aw, sn->af, opts & PF_OPT_VERBOSE2);
+ printf(" -> ");
+ aw.v.a.addr = sn->raddr;
+ print_addr(&aw, sn->af, opts & PF_OPT_VERBOSE2);
+ printf(" ( states %u, connections %u, rate %u.%u/%us )\n", sn->states,
+ sn->conn, sn->conn_rate.count / 1000,
+ (sn->conn_rate.count % 1000) / 100, sn->conn_rate.seconds);
+ if (opts & PF_OPT_VERBOSE) {
+ sec = sn->creation % 60;
+ sn->creation /= 60;
+ min = sn->creation % 60;
+ sn->creation /= 60;
+ printf(" age %.2u:%.2u:%.2u", sn->creation, min, sec);
+ if (sn->states == 0) {
+ sec = sn->expire % 60;
+ sn->expire /= 60;
+ min = sn->expire % 60;
+ sn->expire /= 60;
+ printf(", expires in %.2u:%.2u:%.2u",
+ sn->expire, min, sec);
+ }
+ printf(", %llu pkts, %llu bytes",
+#ifdef __FreeBSD__
+ (unsigned long long)(sn->packets[0] + sn->packets[1]),
+ (unsigned long long)(sn->bytes[0] + sn->bytes[1]));
+#else
+ sn->packets[0] + sn->packets[1],
+ sn->bytes[0] + sn->bytes[1]);
+#endif
+ switch (sn->ruletype) {
+ case PF_NAT:
+ if (sn->rule.nr != -1)
+ printf(", nat rule %u", sn->rule.nr);
+ break;
+ case PF_RDR:
+ if (sn->rule.nr != -1)
+ printf(", rdr rule %u", sn->rule.nr);
+ break;
+ case PF_PASS:
+ if (sn->rule.nr != -1)
+ printf(", filter rule %u", sn->rule.nr);
+ break;
+ }
+ printf("\n");
+ }
+}
+
+void
+print_rule(struct pf_rule *r, const char *anchor_call, int verbose, int numeric)
+{
+ static const char *actiontypes[] = { "pass", "block", "scrub",
+ "no scrub", "nat", "no nat", "binat", "no binat", "rdr", "no rdr" };
+ static const char *anchortypes[] = { "anchor", "anchor", "anchor",
+ "anchor", "nat-anchor", "nat-anchor", "binat-anchor",
+ "binat-anchor", "rdr-anchor", "rdr-anchor" };
+ int i, opts;
+
+ if (verbose)
+ printf("@%d ", r->nr);
+ if (r->action > PF_NORDR)
+ printf("action(%d)", r->action);
+ else if (anchor_call[0]) {
+ if (anchor_call[0] == '_') {
+ printf("%s", anchortypes[r->action]);
+ } else
+ printf("%s \"%s\"", anchortypes[r->action],
+ anchor_call);
+ } else {
+ printf("%s", actiontypes[r->action]);
+ if (r->natpass)
+ printf(" pass");
+ }
+ if (r->action == PF_DROP) {
+ if (r->rule_flag & PFRULE_RETURN)
+ printf(" return");
+ else if (r->rule_flag & PFRULE_RETURNRST) {
+ if (!r->return_ttl)
+ printf(" return-rst");
+ else
+ printf(" return-rst(ttl %d)", r->return_ttl);
+ } else if (r->rule_flag & PFRULE_RETURNICMP) {
+ const struct icmpcodeent *ic, *ic6;
+
+ ic = geticmpcodebynumber(r->return_icmp >> 8,
+ r->return_icmp & 255, AF_INET);
+ ic6 = geticmpcodebynumber(r->return_icmp6 >> 8,
+ r->return_icmp6 & 255, AF_INET6);
+
+ switch (r->af) {
+ case AF_INET:
+ printf(" return-icmp");
+ if (ic == NULL)
+ printf("(%u)", r->return_icmp & 255);
+ else
+ printf("(%s)", ic->name);
+ break;
+ case AF_INET6:
+ printf(" return-icmp6");
+ if (ic6 == NULL)
+ printf("(%u)", r->return_icmp6 & 255);
+ else
+ printf("(%s)", ic6->name);
+ break;
+ default:
+ printf(" return-icmp");
+ if (ic == NULL)
+ printf("(%u, ", r->return_icmp & 255);
+ else
+ printf("(%s, ", ic->name);
+ if (ic6 == NULL)
+ printf("%u)", r->return_icmp6 & 255);
+ else
+ printf("%s)", ic6->name);
+ break;
+ }
+ } else
+ printf(" drop");
+ }
+ if (r->direction == PF_IN)
+ printf(" in");
+ else if (r->direction == PF_OUT)
+ printf(" out");
+ if (r->log) {
+ printf(" log");
+ if (r->log & ~PF_LOG || r->logif) {
+ int count = 0;
+
+ printf(" (");
+ if (r->log & PF_LOG_ALL)
+ printf("%sall", count++ ? ", " : "");
+ if (r->log & PF_LOG_SOCKET_LOOKUP)
+ printf("%suser", count++ ? ", " : "");
+ if (r->logif)
+ printf("%sto pflog%u", count++ ? ", " : "",
+ r->logif);
+ printf(")");
+ }
+ }
+ if (r->quick)
+ printf(" quick");
+ if (r->ifname[0]) {
+ if (r->ifnot)
+ printf(" on ! %s", r->ifname);
+ else
+ printf(" on %s", r->ifname);
+ }
+ if (r->rt) {
+ if (r->rt == PF_ROUTETO)
+ printf(" route-to");
+ else if (r->rt == PF_REPLYTO)
+ printf(" reply-to");
+ else if (r->rt == PF_DUPTO)
+ printf(" dup-to");
+ else if (r->rt == PF_FASTROUTE)
+ printf(" fastroute");
+ if (r->rt != PF_FASTROUTE) {
+ printf(" ");
+ print_pool(&r->rpool, 0, 0, r->af, PF_PASS);
+ }
+ }
+ if (r->af) {
+ if (r->af == AF_INET)
+ printf(" inet");
+ else
+ printf(" inet6");
+ }
+ if (r->proto) {
+ struct protoent *p;
+
+ if ((p = getprotobynumber(r->proto)) != NULL)
+ printf(" proto %s", p->p_name);
+ else
+ printf(" proto %u", r->proto);
+ }
+ print_fromto(&r->src, r->os_fingerprint, &r->dst, r->af, r->proto,
+ verbose, numeric);
+ if (r->uid.op)
+ print_ugid(r->uid.op, r->uid.uid[0], r->uid.uid[1], "user",
+ UID_MAX);
+ if (r->gid.op)
+ print_ugid(r->gid.op, r->gid.gid[0], r->gid.gid[1], "group",
+ GID_MAX);
+ if (r->flags || r->flagset) {
+ printf(" flags ");
+ print_flags(r->flags);
+ printf("/");
+ print_flags(r->flagset);
+ } else if (r->action == PF_PASS &&
+ (!r->proto || r->proto == IPPROTO_TCP) &&
+ !(r->rule_flag & PFRULE_FRAGMENT) &&
+ !anchor_call[0] && r->keep_state)
+ printf(" flags any");
+ if (r->type) {
+ const struct icmptypeent *it;
+
+ it = geticmptypebynumber(r->type-1, r->af);
+ if (r->af != AF_INET6)
+ printf(" icmp-type");
+ else
+ printf(" icmp6-type");
+ if (it != NULL)
+ printf(" %s", it->name);
+ else
+ printf(" %u", r->type-1);
+ if (r->code) {
+ const struct icmpcodeent *ic;
+
+ ic = geticmpcodebynumber(r->type-1, r->code-1, r->af);
+ if (ic != NULL)
+ printf(" code %s", ic->name);
+ else
+ printf(" code %u", r->code-1);
+ }
+ }
+ if (r->tos)
+ printf(" tos 0x%2.2x", r->tos);
+ if (!r->keep_state && r->action == PF_PASS && !anchor_call[0])
+ printf(" no state");
+ else if (r->keep_state == PF_STATE_NORMAL)
+ printf(" keep state");
+ else if (r->keep_state == PF_STATE_MODULATE)
+ printf(" modulate state");
+ else if (r->keep_state == PF_STATE_SYNPROXY)
+ printf(" synproxy state");
+ if (r->prob) {
+ char buf[20];
+
+ snprintf(buf, sizeof(buf), "%f", r->prob*100.0/(UINT_MAX+1.0));
+ for (i = strlen(buf)-1; i > 0; i--) {
+ if (buf[i] == '0')
+ buf[i] = '\0';
+ else {
+ if (buf[i] == '.')
+ buf[i] = '\0';
+ break;
+ }
+ }
+ printf(" probability %s%%", buf);
+ }
+ opts = 0;
+ if (r->max_states || r->max_src_nodes || r->max_src_states)
+ opts = 1;
+ if (r->rule_flag & PFRULE_NOSYNC)
+ opts = 1;
+ if (r->rule_flag & PFRULE_SRCTRACK)
+ opts = 1;
+ if (r->rule_flag & PFRULE_IFBOUND)
+ opts = 1;
+ if (r->rule_flag & PFRULE_STATESLOPPY)
+ opts = 1;
+ for (i = 0; !opts && i < PFTM_MAX; ++i)
+ if (r->timeout[i])
+ opts = 1;
+ if (opts) {
+ printf(" (");
+ if (r->max_states) {
+ printf("max %u", r->max_states);
+ opts = 0;
+ }
+ if (r->rule_flag & PFRULE_NOSYNC) {
+ if (!opts)
+ printf(", ");
+ printf("no-sync");
+ opts = 0;
+ }
+ if (r->rule_flag & PFRULE_SRCTRACK) {
+ if (!opts)
+ printf(", ");
+ printf("source-track");
+ if (r->rule_flag & PFRULE_RULESRCTRACK)
+ printf(" rule");
+ else
+ printf(" global");
+ opts = 0;
+ }
+ if (r->max_src_states) {
+ if (!opts)
+ printf(", ");
+ printf("max-src-states %u", r->max_src_states);
+ opts = 0;
+ }
+ if (r->max_src_conn) {
+ if (!opts)
+ printf(", ");
+ printf("max-src-conn %u", r->max_src_conn);
+ opts = 0;
+ }
+ if (r->max_src_conn_rate.limit) {
+ if (!opts)
+ printf(", ");
+ printf("max-src-conn-rate %u/%u",
+ r->max_src_conn_rate.limit,
+ r->max_src_conn_rate.seconds);
+ opts = 0;
+ }
+ if (r->max_src_nodes) {
+ if (!opts)
+ printf(", ");
+ printf("max-src-nodes %u", r->max_src_nodes);
+ opts = 0;
+ }
+ if (r->overload_tblname[0]) {
+ if (!opts)
+ printf(", ");
+ printf("overload <%s>", r->overload_tblname);
+ if (r->flush)
+ printf(" flush");
+ if (r->flush & PF_FLUSH_GLOBAL)
+ printf(" global");
+ }
+ if (r->rule_flag & PFRULE_IFBOUND) {
+ if (!opts)
+ printf(", ");
+ printf("if-bound");
+ opts = 0;
+ }
+ if (r->rule_flag & PFRULE_STATESLOPPY) {
+ if (!opts)
+ printf(", ");
+ printf("sloppy");
+ opts = 0;
+ }
+ for (i = 0; i < PFTM_MAX; ++i)
+ if (r->timeout[i]) {
+ int j;
+
+ if (!opts)
+ printf(", ");
+ opts = 0;
+ for (j = 0; pf_timeouts[j].name != NULL;
+ ++j)
+ if (pf_timeouts[j].timeout == i)
+ break;
+ printf("%s %u", pf_timeouts[j].name == NULL ?
+ "inv.timeout" : pf_timeouts[j].name,
+ r->timeout[i]);
+ }
+ printf(")");
+ }
+ if (r->rule_flag & PFRULE_FRAGMENT)
+ printf(" fragment");
+ if (r->rule_flag & PFRULE_NODF)
+ printf(" no-df");
+ if (r->rule_flag & PFRULE_RANDOMID)
+ printf(" random-id");
+ if (r->min_ttl)
+ printf(" min-ttl %d", r->min_ttl);
+ if (r->max_mss)
+ printf(" max-mss %d", r->max_mss);
+ if (r->rule_flag & PFRULE_SET_TOS)
+ printf(" set-tos 0x%2.2x", r->set_tos);
+ if (r->allow_opts)
+ printf(" allow-opts");
+ if (r->action == PF_SCRUB) {
+ if (r->rule_flag & PFRULE_REASSEMBLE_TCP)
+ printf(" reassemble tcp");
+
+ if (r->rule_flag & PFRULE_FRAGDROP)
+ printf(" fragment drop-ovl");
+ else if (r->rule_flag & PFRULE_FRAGCROP)
+ printf(" fragment crop");
+ else
+ printf(" fragment reassemble");
+ }
+ if (r->label[0])
+ printf(" label \"%s\"", r->label);
+ if (r->qname[0] && r->pqname[0])
+ printf(" queue(%s, %s)", r->qname, r->pqname);
+ else if (r->qname[0])
+ printf(" queue %s", r->qname);
+ if (r->tagname[0])
+ printf(" tag %s", r->tagname);
+ if (r->match_tagname[0]) {
+ if (r->match_tag_not)
+ printf(" !");
+ printf(" tagged %s", r->match_tagname);
+ }
+ if (r->rtableid != -1)
+ printf(" rtable %u", r->rtableid);
+ if (r->divert.port) {
+#ifdef __FreeBSD__
+ printf(" divert-to %u", ntohs(r->divert.port));
+#else
+ if (PF_AZERO(&r->divert.addr, r->af)) {
+ printf(" divert-reply");
+ } else {
+ /* XXX cut&paste from print_addr */
+ char buf[48];
+
+ printf(" divert-to ");
+ if (inet_ntop(r->af, &r->divert.addr, buf,
+ sizeof(buf)) == NULL)
+ printf("?");
+ else
+ printf("%s", buf);
+ printf(" port %u", ntohs(r->divert.port));
+ }
+#endif
+ }
+ if (!anchor_call[0] && (r->action == PF_NAT ||
+ r->action == PF_BINAT || r->action == PF_RDR)) {
+ printf(" -> ");
+ print_pool(&r->rpool, r->rpool.proxy_port[0],
+ r->rpool.proxy_port[1], r->af, r->action);
+ }
+}
+
+void
+print_tabledef(const char *name, int flags, int addrs,
+ struct node_tinithead *nodes)
+{
+ struct node_tinit *ti, *nti;
+ struct node_host *h;
+
+ printf("table <%s>", name);
+ if (flags & PFR_TFLAG_CONST)
+ printf(" const");
+ if (flags & PFR_TFLAG_PERSIST)
+ printf(" persist");
+ if (flags & PFR_TFLAG_COUNTERS)
+ printf(" counters");
+ SIMPLEQ_FOREACH(ti, nodes, entries) {
+ if (ti->file) {
+ printf(" file \"%s\"", ti->file);
+ continue;
+ }
+ printf(" {");
+ for (;;) {
+ for (h = ti->host; h != NULL; h = h->next) {
+ printf(h->not ? " !" : " ");
+ print_addr(&h->addr, h->af, 0);
+ }
+ nti = SIMPLEQ_NEXT(ti, entries);
+ if (nti != NULL && nti->file == NULL)
+ ti = nti; /* merge lists */
+ else
+ break;
+ }
+ printf(" }");
+ }
+ if (addrs && SIMPLEQ_EMPTY(nodes))
+ printf(" { }");
+ printf("\n");
+}
+
+int
+parse_flags(char *s)
+{
+ char *p, *q;
+ u_int8_t f = 0;
+
+ for (p = s; *p; p++) {
+ if ((q = strchr(tcpflags, *p)) == NULL)
+ return -1;
+ else
+ f |= 1 << (q - tcpflags);
+ }
+ return (f ? f : PF_TH_ALL);
+}
+
+void
+set_ipmask(struct node_host *h, u_int8_t b)
+{
+ struct pf_addr *m, *n;
+ int i, j = 0;
+
+ m = &h->addr.v.a.mask;
+ memset(m, 0, sizeof(*m));
+
+ while (b >= 32) {
+ m->addr32[j++] = 0xffffffff;
+ b -= 32;
+ }
+ for (i = 31; i > 31-b; --i)
+ m->addr32[j] |= (1 << i);
+ if (b)
+ m->addr32[j] = htonl(m->addr32[j]);
+
+ /* Mask off bits of the address that will never be used. */
+ n = &h->addr.v.a.addr;
+ if (h->addr.type == PF_ADDR_ADDRMASK)
+ for (i = 0; i < 4; i++)
+ n->addr32[i] = n->addr32[i] & m->addr32[i];
+}
+
+int
+check_netmask(struct node_host *h, sa_family_t af)
+{
+ struct node_host *n = NULL;
+ struct pf_addr *m;
+
+ for (n = h; n != NULL; n = n->next) {
+ if (h->addr.type == PF_ADDR_TABLE)
+ continue;
+ m = &h->addr.v.a.mask;
+ /* fix up netmask for dynaddr */
+ if (af == AF_INET && h->addr.type == PF_ADDR_DYNIFTL &&
+ unmask(m, AF_INET6) > 32)
+ set_ipmask(n, 32);
+ /* netmasks > 32 bit are invalid on v4 */
+ if (af == AF_INET &&
+ (m->addr32[1] || m->addr32[2] || m->addr32[3])) {
+ fprintf(stderr, "netmask %u invalid for IPv4 address\n",
+ unmask(m, AF_INET6));
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/* interface lookup routines */
+
+struct node_host *iftab;
+
+void
+ifa_load(void)
+{
+ struct ifaddrs *ifap, *ifa;
+ struct node_host *n = NULL, *h = NULL;
+
+ if (getifaddrs(&ifap) < 0)
+ err(1, "getifaddrs");
+
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (!(ifa->ifa_addr->sa_family == AF_INET ||
+ ifa->ifa_addr->sa_family == AF_INET6 ||
+ ifa->ifa_addr->sa_family == AF_LINK))
+ continue;
+ n = calloc(1, sizeof(struct node_host));
+ if (n == NULL)
+ err(1, "address: calloc");
+ n->af = ifa->ifa_addr->sa_family;
+ n->ifa_flags = ifa->ifa_flags;
+#ifdef __KAME__
+ if (n->af == AF_INET6 &&
+ IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)
+ ifa->ifa_addr)->sin6_addr) &&
+ ((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_scope_id ==
+ 0) {
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+ sin6->sin6_scope_id = sin6->sin6_addr.s6_addr[2] << 8 |
+ sin6->sin6_addr.s6_addr[3];
+ sin6->sin6_addr.s6_addr[2] = 0;
+ sin6->sin6_addr.s6_addr[3] = 0;
+ }
+#endif
+ n->ifindex = 0;
+ if (n->af == AF_INET) {
+ memcpy(&n->addr.v.a.addr, &((struct sockaddr_in *)
+ ifa->ifa_addr)->sin_addr.s_addr,
+ sizeof(struct in_addr));
+ memcpy(&n->addr.v.a.mask, &((struct sockaddr_in *)
+ ifa->ifa_netmask)->sin_addr.s_addr,
+ sizeof(struct in_addr));
+ if (ifa->ifa_broadaddr != NULL)
+ memcpy(&n->bcast, &((struct sockaddr_in *)
+ ifa->ifa_broadaddr)->sin_addr.s_addr,
+ sizeof(struct in_addr));
+ if (ifa->ifa_dstaddr != NULL)
+ memcpy(&n->peer, &((struct sockaddr_in *)
+ ifa->ifa_dstaddr)->sin_addr.s_addr,
+ sizeof(struct in_addr));
+ } else if (n->af == AF_INET6) {
+ memcpy(&n->addr.v.a.addr, &((struct sockaddr_in6 *)
+ ifa->ifa_addr)->sin6_addr.s6_addr,
+ sizeof(struct in6_addr));
+ memcpy(&n->addr.v.a.mask, &((struct sockaddr_in6 *)
+ ifa->ifa_netmask)->sin6_addr.s6_addr,
+ sizeof(struct in6_addr));
+ if (ifa->ifa_broadaddr != NULL)
+ memcpy(&n->bcast, &((struct sockaddr_in6 *)
+ ifa->ifa_broadaddr)->sin6_addr.s6_addr,
+ sizeof(struct in6_addr));
+ if (ifa->ifa_dstaddr != NULL)
+ memcpy(&n->peer, &((struct sockaddr_in6 *)
+ ifa->ifa_dstaddr)->sin6_addr.s6_addr,
+ sizeof(struct in6_addr));
+ n->ifindex = ((struct sockaddr_in6 *)
+ ifa->ifa_addr)->sin6_scope_id;
+ }
+ if ((n->ifname = strdup(ifa->ifa_name)) == NULL)
+ err(1, "ifa_load: strdup");
+ n->next = NULL;
+ n->tail = n;
+ if (h == NULL)
+ h = n;
+ else {
+ h->tail->next = n;
+ h->tail = n;
+ }
+ }
+
+ iftab = h;
+ freeifaddrs(ifap);
+}
+
+struct node_host *
+ifa_exists(const char *ifa_name)
+{
+ struct node_host *n;
+ struct ifgroupreq ifgr;
+ int s;
+
+ if (iftab == NULL)
+ ifa_load();
+
+ /* check wether this is a group */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ err(1, "socket");
+ bzero(&ifgr, sizeof(ifgr));
+ strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name));
+ if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == 0) {
+ /* fake a node_host */
+ if ((n = calloc(1, sizeof(*n))) == NULL)
+ err(1, "calloc");
+ if ((n->ifname = strdup(ifa_name)) == NULL)
+ err(1, "strdup");
+ close(s);
+ return (n);
+ }
+ close(s);
+
+ for (n = iftab; n; n = n->next) {
+ if (n->af == AF_LINK && !strncmp(n->ifname, ifa_name, IFNAMSIZ))
+ return (n);
+ }
+
+ return (NULL);
+}
+
+struct node_host *
+ifa_grouplookup(const char *ifa_name, int flags)
+{
+ struct ifg_req *ifg;
+ struct ifgroupreq ifgr;
+ int s, len;
+ struct node_host *n, *h = NULL;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ err(1, "socket");
+ bzero(&ifgr, sizeof(ifgr));
+ strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name));
+ if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) {
+ close(s);
+ return (NULL);
+ }
+
+ len = ifgr.ifgr_len;
+ if ((ifgr.ifgr_groups = calloc(1, len)) == NULL)
+ err(1, "calloc");
+ if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1)
+ err(1, "SIOCGIFGMEMB");
+
+ for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(struct ifg_req);
+ ifg++) {
+ len -= sizeof(struct ifg_req);
+ if ((n = ifa_lookup(ifg->ifgrq_member, flags)) == NULL)
+ continue;
+ if (h == NULL)
+ h = n;
+ else {
+ h->tail->next = n;
+ h->tail = n->tail;
+ }
+ }
+ free(ifgr.ifgr_groups);
+ close(s);
+
+ return (h);
+}
+
+struct node_host *
+ifa_lookup(const char *ifa_name, int flags)
+{
+ struct node_host *p = NULL, *h = NULL, *n = NULL;
+ int got4 = 0, got6 = 0;
+ const char *last_if = NULL;
+
+ if ((h = ifa_grouplookup(ifa_name, flags)) != NULL)
+ return (h);
+
+ if (!strncmp(ifa_name, "self", IFNAMSIZ))
+ ifa_name = NULL;
+
+ if (iftab == NULL)
+ ifa_load();
+
+ for (p = iftab; p; p = p->next) {
+ if (ifa_skip_if(ifa_name, p))
+ continue;
+ if ((flags & PFI_AFLAG_BROADCAST) && p->af != AF_INET)
+ continue;
+ if ((flags & PFI_AFLAG_BROADCAST) &&
+ !(p->ifa_flags & IFF_BROADCAST))
+ continue;
+ if ((flags & PFI_AFLAG_PEER) &&
+ !(p->ifa_flags & IFF_POINTOPOINT))
+ continue;
+ if ((flags & PFI_AFLAG_NETWORK) && p->ifindex > 0)
+ continue;
+ if (last_if == NULL || strcmp(last_if, p->ifname))
+ got4 = got6 = 0;
+ last_if = p->ifname;
+ if ((flags & PFI_AFLAG_NOALIAS) && p->af == AF_INET && got4)
+ continue;
+ if ((flags & PFI_AFLAG_NOALIAS) && p->af == AF_INET6 && got6)
+ continue;
+ if (p->af == AF_INET)
+ got4 = 1;
+ else
+ got6 = 1;
+ n = calloc(1, sizeof(struct node_host));
+ if (n == NULL)
+ err(1, "address: calloc");
+ n->af = p->af;
+ if (flags & PFI_AFLAG_BROADCAST)
+ memcpy(&n->addr.v.a.addr, &p->bcast,
+ sizeof(struct pf_addr));
+ else if (flags & PFI_AFLAG_PEER)
+ memcpy(&n->addr.v.a.addr, &p->peer,
+ sizeof(struct pf_addr));
+ else
+ memcpy(&n->addr.v.a.addr, &p->addr.v.a.addr,
+ sizeof(struct pf_addr));
+ if (flags & PFI_AFLAG_NETWORK)
+ set_ipmask(n, unmask(&p->addr.v.a.mask, n->af));
+ else {
+ if (n->af == AF_INET) {
+ if (p->ifa_flags & IFF_LOOPBACK &&
+ p->ifa_flags & IFF_LINK1)
+ memcpy(&n->addr.v.a.mask,
+ &p->addr.v.a.mask,
+ sizeof(struct pf_addr));
+ else
+ set_ipmask(n, 32);
+ } else
+ set_ipmask(n, 128);
+ }
+ n->ifindex = p->ifindex;
+
+ n->next = NULL;
+ n->tail = n;
+ if (h == NULL)
+ h = n;
+ else {
+ h->tail->next = n;
+ h->tail = n;
+ }
+ }
+ return (h);
+}
+
+int
+ifa_skip_if(const char *filter, struct node_host *p)
+{
+ int n;
+
+ if (p->af != AF_INET && p->af != AF_INET6)
+ return (1);
+ if (filter == NULL || !*filter)
+ return (0);
+ if (!strcmp(p->ifname, filter))
+ return (0); /* exact match */
+ n = strlen(filter);
+ if (n < 1 || n >= IFNAMSIZ)
+ return (1); /* sanity check */
+ if (filter[n-1] >= '0' && filter[n-1] <= '9')
+ return (1); /* only do exact match in that case */
+ if (strncmp(p->ifname, filter, n))
+ return (1); /* prefix doesn't match */
+ return (p->ifname[n] < '0' || p->ifname[n] > '9');
+}
+
+
+struct node_host *
+host(const char *s)
+{
+ struct node_host *h = NULL;
+ int mask, v4mask, v6mask, cont = 1;
+ char *p, *q, *ps;
+
+ if ((p = strrchr(s, '/')) != NULL) {
+ mask = strtol(p+1, &q, 0);
+ if (!q || *q || mask > 128 || q == (p+1)) {
+ fprintf(stderr, "invalid netmask '%s'\n", p);
+ return (NULL);
+ }
+ if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL)
+ err(1, "host: malloc");
+ strlcpy(ps, s, strlen(s) - strlen(p) + 1);
+ v4mask = v6mask = mask;
+ } else {
+ if ((ps = strdup(s)) == NULL)
+ err(1, "host: strdup");
+ v4mask = 32;
+ v6mask = 128;
+ mask = -1;
+ }
+
+ /* interface with this name exists? */
+ if (cont && (h = host_if(ps, mask)) != NULL)
+ cont = 0;
+
+ /* IPv4 address? */
+ if (cont && (h = host_v4(s, mask)) != NULL)
+ cont = 0;
+
+ /* IPv6 address? */
+ if (cont && (h = host_v6(ps, v6mask)) != NULL)
+ cont = 0;
+
+ /* dns lookup */
+ if (cont && (h = host_dns(ps, v4mask, v6mask)) != NULL)
+ cont = 0;
+ free(ps);
+
+ if (h == NULL || cont == 1) {
+ fprintf(stderr, "no IP address found for %s\n", s);
+ return (NULL);
+ }
+ return (h);
+}
+
+struct node_host *
+host_if(const char *s, int mask)
+{
+ struct node_host *n, *h = NULL;
+ char *p, *ps;
+ int flags = 0;
+
+ if ((ps = strdup(s)) == NULL)
+ err(1, "host_if: strdup");
+ while ((p = strrchr(ps, ':')) != NULL) {
+ if (!strcmp(p+1, "network"))
+ flags |= PFI_AFLAG_NETWORK;
+ else if (!strcmp(p+1, "broadcast"))
+ flags |= PFI_AFLAG_BROADCAST;
+ else if (!strcmp(p+1, "peer"))
+ flags |= PFI_AFLAG_PEER;
+ else if (!strcmp(p+1, "0"))
+ flags |= PFI_AFLAG_NOALIAS;
+ else {
+ free(ps);
+ return (NULL);
+ }
+ *p = '\0';
+ }
+ if (flags & (flags - 1) & PFI_AFLAG_MODEMASK) { /* Yep! */
+ fprintf(stderr, "illegal combination of interface modifiers\n");
+ free(ps);
+ return (NULL);
+ }
+ if ((flags & (PFI_AFLAG_NETWORK|PFI_AFLAG_BROADCAST)) && mask > -1) {
+ fprintf(stderr, "network or broadcast lookup, but "
+ "extra netmask given\n");
+ free(ps);
+ return (NULL);
+ }
+ if (ifa_exists(ps) || !strncmp(ps, "self", IFNAMSIZ)) {
+ /* interface with this name exists */
+ h = ifa_lookup(ps, flags);
+ for (n = h; n != NULL && mask > -1; n = n->next)
+ set_ipmask(n, mask);
+ }
+
+ free(ps);
+ return (h);
+}
+
+struct node_host *
+host_v4(const char *s, int mask)
+{
+ struct node_host *h = NULL;
+ struct in_addr ina;
+ int bits = 32;
+
+ memset(&ina, 0, sizeof(struct in_addr));
+ if (strrchr(s, '/') != NULL) {
+ if ((bits = inet_net_pton(AF_INET, s, &ina, sizeof(ina))) == -1)
+ return (NULL);
+ } else {
+ if (inet_pton(AF_INET, s, &ina) != 1)
+ return (NULL);
+ }
+
+ h = calloc(1, sizeof(struct node_host));
+ if (h == NULL)
+ err(1, "address: calloc");
+ h->ifname = NULL;
+ h->af = AF_INET;
+ h->addr.v.a.addr.addr32[0] = ina.s_addr;
+ set_ipmask(h, bits);
+ h->next = NULL;
+ h->tail = h;
+
+ return (h);
+}
+
+struct node_host *
+host_v6(const char *s, int mask)
+{
+ struct addrinfo hints, *res;
+ struct node_host *h = NULL;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_flags = AI_NUMERICHOST;
+ if (getaddrinfo(s, "0", &hints, &res) == 0) {
+ h = calloc(1, sizeof(struct node_host));
+ if (h == NULL)
+ err(1, "address: calloc");
+ h->ifname = NULL;
+ h->af = AF_INET6;
+ memcpy(&h->addr.v.a.addr,
+ &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
+ sizeof(h->addr.v.a.addr));
+ h->ifindex =
+ ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
+ set_ipmask(h, mask);
+ freeaddrinfo(res);
+ h->next = NULL;
+ h->tail = h;
+ }
+
+ return (h);
+}
+
+struct node_host *
+host_dns(const char *s, int v4mask, int v6mask)
+{
+ struct addrinfo hints, *res0, *res;
+ struct node_host *n, *h = NULL;
+ int error, noalias = 0;
+ int got4 = 0, got6 = 0;
+ char *p, *ps;
+
+ if ((ps = strdup(s)) == NULL)
+ err(1, "host_dns: strdup");
+ if ((p = strrchr(ps, ':')) != NULL && !strcmp(p, ":0")) {
+ noalias = 1;
+ *p = '\0';
+ }
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM; /* DUMMY */
+ error = getaddrinfo(ps, NULL, &hints, &res0);
+ if (error) {
+ free(ps);
+ return (h);
+ }
+
+ for (res = res0; res; res = res->ai_next) {
+ if (res->ai_family != AF_INET &&
+ res->ai_family != AF_INET6)
+ continue;
+ if (noalias) {
+ if (res->ai_family == AF_INET) {
+ if (got4)
+ continue;
+ got4 = 1;
+ } else {
+ if (got6)
+ continue;
+ got6 = 1;
+ }
+ }
+ n = calloc(1, sizeof(struct node_host));
+ if (n == NULL)
+ err(1, "host_dns: calloc");
+ n->ifname = NULL;
+ n->af = res->ai_family;
+ if (res->ai_family == AF_INET) {
+ memcpy(&n->addr.v.a.addr,
+ &((struct sockaddr_in *)
+ res->ai_addr)->sin_addr.s_addr,
+ sizeof(struct in_addr));
+ set_ipmask(n, v4mask);
+ } else {
+ memcpy(&n->addr.v.a.addr,
+ &((struct sockaddr_in6 *)
+ res->ai_addr)->sin6_addr.s6_addr,
+ sizeof(struct in6_addr));
+ n->ifindex =
+ ((struct sockaddr_in6 *)
+ res->ai_addr)->sin6_scope_id;
+ set_ipmask(n, v6mask);
+ }
+ n->next = NULL;
+ n->tail = n;
+ if (h == NULL)
+ h = n;
+ else {
+ h->tail->next = n;
+ h->tail = n;
+ }
+ }
+ freeaddrinfo(res0);
+ free(ps);
+
+ return (h);
+}
+
+/*
+ * convert a hostname to a list of addresses and put them in the given buffer.
+ * test:
+ * if set to 1, only simple addresses are accepted (no netblock, no "!").
+ */
+int
+append_addr(struct pfr_buffer *b, char *s, int test)
+{
+ char *r;
+ struct node_host *h, *n;
+ int rv, not = 0;
+
+ for (r = s; *r == '!'; r++)
+ not = !not;
+ if ((n = host(r)) == NULL) {
+ errno = 0;
+ return (-1);
+ }
+ rv = append_addr_host(b, n, test, not);
+ do {
+ h = n;
+ n = n->next;
+ free(h);
+ } while (n != NULL);
+ return (rv);
+}
+
+/*
+ * same as previous function, but with a pre-parsed input and the ability
+ * to "negate" the result. Does not free the node_host list.
+ * not:
+ * setting it to 1 is equivalent to adding "!" in front of parameter s.
+ */
+int
+append_addr_host(struct pfr_buffer *b, struct node_host *n, int test, int not)
+{
+ int bits;
+ struct pfr_addr addr;
+
+ do {
+ bzero(&addr, sizeof(addr));
+ addr.pfra_not = n->not ^ not;
+ addr.pfra_af = n->af;
+ addr.pfra_net = unmask(&n->addr.v.a.mask, n->af);
+ switch (n->af) {
+ case AF_INET:
+ addr.pfra_ip4addr.s_addr = n->addr.v.a.addr.addr32[0];
+ bits = 32;
+ break;
+ case AF_INET6:
+ memcpy(&addr.pfra_ip6addr, &n->addr.v.a.addr.v6,
+ sizeof(struct in6_addr));
+ bits = 128;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+ if ((test && (not || addr.pfra_net != bits)) ||
+ addr.pfra_net > bits) {
+ errno = EINVAL;
+ return (-1);
+ }
+ if (pfr_buf_add(b, &addr))
+ return (-1);
+ } while ((n = n->next) != NULL);
+
+ return (0);
+}
+
+int
+pfctl_add_trans(struct pfr_buffer *buf, int rs_num, const char *anchor)
+{
+ struct pfioc_trans_e trans;
+
+ bzero(&trans, sizeof(trans));
+ trans.rs_num = rs_num;
+ if (strlcpy(trans.anchor, anchor,
+ sizeof(trans.anchor)) >= sizeof(trans.anchor))
+ errx(1, "pfctl_add_trans: strlcpy");
+
+ return pfr_buf_add(buf, &trans);
+}
+
+u_int32_t
+pfctl_get_ticket(struct pfr_buffer *buf, int rs_num, const char *anchor)
+{
+ struct pfioc_trans_e *p;
+
+ PFRB_FOREACH(p, buf)
+ if (rs_num == p->rs_num && !strcmp(anchor, p->anchor))
+ return (p->ticket);
+ errx(1, "pfctl_get_ticket: assertion failed");
+}
+
+int
+pfctl_trans(int dev, struct pfr_buffer *buf, u_long cmd, int from)
+{
+ struct pfioc_trans trans;
+
+ bzero(&trans, sizeof(trans));
+ trans.size = buf->pfrb_size - from;
+ trans.esize = sizeof(struct pfioc_trans_e);
+ trans.array = ((struct pfioc_trans_e *)buf->pfrb_caddr) + from;
+ return ioctl(dev, cmd, &trans);
+}
diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h
new file mode 100644
index 000000000000..4560d66bb0ed
--- /dev/null
+++ b/sbin/pfctl/pfctl_parser.h
@@ -0,0 +1,305 @@
+/* $OpenBSD: pfctl_parser.h,v 1.86 2006/10/31 23:46:25 mcbride Exp $ */
+
+/*
+ * Copyright (c) 2001 Daniel Hartmeier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PFCTL_PARSER_H_
+#define _PFCTL_PARSER_H_
+
+#define PF_OSFP_FILE "/etc/pf.os"
+
+#define PF_OPT_DISABLE 0x0001
+#define PF_OPT_ENABLE 0x0002
+#define PF_OPT_VERBOSE 0x0004
+#define PF_OPT_NOACTION 0x0008
+#define PF_OPT_QUIET 0x0010
+#define PF_OPT_CLRRULECTRS 0x0020
+#define PF_OPT_USEDNS 0x0040
+#define PF_OPT_VERBOSE2 0x0080
+#define PF_OPT_DUMMYACTION 0x0100
+#define PF_OPT_DEBUG 0x0200
+#define PF_OPT_SHOWALL 0x0400
+#define PF_OPT_OPTIMIZE 0x0800
+#define PF_OPT_NUMERIC 0x1000
+#define PF_OPT_MERGE 0x2000
+#define PF_OPT_RECURSE 0x4000
+
+#define PF_TH_ALL 0xFF
+
+#define PF_NAT_PROXY_PORT_LOW 50001
+#define PF_NAT_PROXY_PORT_HIGH 65535
+
+#define PF_OPTIMIZE_BASIC 0x0001
+#define PF_OPTIMIZE_PROFILE 0x0002
+
+#define FCNT_NAMES { \
+ "searches", \
+ "inserts", \
+ "removals", \
+ NULL \
+}
+
+struct pfr_buffer; /* forward definition */
+
+
+struct pfctl {
+ int dev;
+ int opts;
+ int optimize;
+ int loadopt;
+ int asd; /* anchor stack depth */
+ int bn; /* brace number */
+ int brace;
+ int tdirty; /* kernel dirty */
+#define PFCTL_ANCHOR_STACK_DEPTH 64
+ struct pf_anchor *astack[PFCTL_ANCHOR_STACK_DEPTH];
+ struct pfioc_pooladdr paddr;
+ struct pfioc_altq *paltq;
+ struct pfioc_queue *pqueue;
+ struct pfr_buffer *trans;
+ struct pf_anchor *anchor, *alast;
+ const char *ruleset;
+
+ /* 'set foo' options */
+ u_int32_t timeout[PFTM_MAX];
+ u_int32_t limit[PF_LIMIT_MAX];
+ u_int32_t debug;
+ u_int32_t hostid;
+ char *ifname;
+
+ u_int8_t timeout_set[PFTM_MAX];
+ u_int8_t limit_set[PF_LIMIT_MAX];
+ u_int8_t debug_set;
+ u_int8_t hostid_set;
+ u_int8_t ifname_set;
+};
+
+struct node_if {
+ char ifname[IFNAMSIZ];
+ u_int8_t not;
+ u_int8_t dynamic; /* antispoof */
+ u_int ifa_flags;
+ struct node_if *next;
+ struct node_if *tail;
+};
+
+struct node_host {
+ struct pf_addr_wrap addr;
+ struct pf_addr bcast;
+ struct pf_addr peer;
+ sa_family_t af;
+ u_int8_t not;
+ u_int32_t ifindex; /* link-local IPv6 addrs */
+ char *ifname;
+ u_int ifa_flags;
+ struct node_host *next;
+ struct node_host *tail;
+};
+
+struct node_os {
+ char *os;
+ pf_osfp_t fingerprint;
+ struct node_os *next;
+ struct node_os *tail;
+};
+
+struct node_queue_bw {
+ u_int32_t bw_absolute;
+ u_int16_t bw_percent;
+};
+
+struct node_hfsc_sc {
+ struct node_queue_bw m1; /* slope of 1st segment; bps */
+ u_int d; /* x-projection of m1; msec */
+ struct node_queue_bw m2; /* slope of 2nd segment; bps */
+ u_int8_t used;
+};
+
+struct node_hfsc_opts {
+ struct node_hfsc_sc realtime;
+ struct node_hfsc_sc linkshare;
+ struct node_hfsc_sc upperlimit;
+ int flags;
+};
+
+struct node_queue_opt {
+ int qtype;
+ union {
+ struct cbq_opts cbq_opts;
+ struct priq_opts priq_opts;
+ struct node_hfsc_opts hfsc_opts;
+ } data;
+};
+
+#ifdef __FreeBSD__
+/*
+ * XXX
+ * Absolutely this is not correct location to define this.
+ * Should we use an another sperate header file?
+ */
+#define SIMPLEQ_HEAD STAILQ_HEAD
+#define SIMPLEQ_HEAD_INITIALIZER STAILQ_HEAD_INITIALIZER
+#define SIMPLEQ_ENTRY STAILQ_ENTRY
+#define SIMPLEQ_FIRST STAILQ_FIRST
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY STAILQ_EMPTY
+#define SIMPLEQ_NEXT STAILQ_NEXT
+/*#define SIMPLEQ_FOREACH STAILQ_FOREACH*/
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for((var) = SIMPLEQ_FIRST(head); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = SIMPLEQ_NEXT(var, field))
+#define SIMPLEQ_INIT STAILQ_INIT
+#define SIMPLEQ_INSERT_HEAD STAILQ_INSERT_HEAD
+#define SIMPLEQ_INSERT_TAIL STAILQ_INSERT_TAIL
+#define SIMPLEQ_INSERT_AFTER STAILQ_INSERT_AFTER
+#define SIMPLEQ_REMOVE_HEAD STAILQ_REMOVE_HEAD
+#endif
+SIMPLEQ_HEAD(node_tinithead, node_tinit);
+struct node_tinit { /* table initializer */
+ SIMPLEQ_ENTRY(node_tinit) entries;
+ struct node_host *host;
+ char *file;
+};
+
+
+/* optimizer created tables */
+struct pf_opt_tbl {
+ char pt_name[PF_TABLE_NAME_SIZE];
+ int pt_rulecount;
+ int pt_generated;
+ struct node_tinithead pt_nodes;
+ struct pfr_buffer *pt_buf;
+};
+#define PF_OPT_TABLE_PREFIX "__automatic_"
+
+/* optimizer pf_rule container */
+struct pf_opt_rule {
+ struct pf_rule por_rule;
+ struct pf_opt_tbl *por_src_tbl;
+ struct pf_opt_tbl *por_dst_tbl;
+ u_int64_t por_profile_count;
+ TAILQ_ENTRY(pf_opt_rule) por_entry;
+ TAILQ_ENTRY(pf_opt_rule) por_skip_entry[PF_SKIP_COUNT];
+};
+
+TAILQ_HEAD(pf_opt_queue, pf_opt_rule);
+
+int pfctl_rules(int, char *, int, int, char *, struct pfr_buffer *);
+int pfctl_optimize_ruleset(struct pfctl *, struct pf_ruleset *);
+
+int pfctl_add_rule(struct pfctl *, struct pf_rule *, const char *);
+int pfctl_add_altq(struct pfctl *, struct pf_altq *);
+int pfctl_add_pool(struct pfctl *, struct pf_pool *, sa_family_t);
+void pfctl_move_pool(struct pf_pool *, struct pf_pool *);
+void pfctl_clear_pool(struct pf_pool *);
+
+int pfctl_set_timeout(struct pfctl *, const char *, int, int);
+int pfctl_set_optimization(struct pfctl *, const char *);
+int pfctl_set_limit(struct pfctl *, const char *, unsigned int);
+int pfctl_set_logif(struct pfctl *, char *);
+int pfctl_set_hostid(struct pfctl *, u_int32_t);
+int pfctl_set_debug(struct pfctl *, char *);
+int pfctl_set_interface_flags(struct pfctl *, char *, int, int);
+
+int parse_config(char *, struct pfctl *);
+int parse_flags(char *);
+int pfctl_load_anchors(int, struct pfctl *, struct pfr_buffer *);
+
+void print_pool(struct pf_pool *, u_int16_t, u_int16_t, sa_family_t, int);
+void print_src_node(struct pf_src_node *, int);
+void print_rule(struct pf_rule *, const char *, int, int);
+void print_tabledef(const char *, int, int, struct node_tinithead *);
+void print_status(struct pf_status *, int);
+
+int eval_pfaltq(struct pfctl *, struct pf_altq *, struct node_queue_bw *,
+ struct node_queue_opt *);
+int eval_pfqueue(struct pfctl *, struct pf_altq *, struct node_queue_bw *,
+ struct node_queue_opt *);
+
+void print_altq(const struct pf_altq *, unsigned, struct node_queue_bw *,
+ struct node_queue_opt *);
+void print_queue(const struct pf_altq *, unsigned, struct node_queue_bw *,
+ int, struct node_queue_opt *);
+
+int pfctl_define_table(char *, int, int, const char *, struct pfr_buffer *,
+ u_int32_t);
+
+void pfctl_clear_fingerprints(int, int);
+int pfctl_file_fingerprints(int, int, const char *);
+pf_osfp_t pfctl_get_fingerprint(const char *);
+int pfctl_load_fingerprints(int, int);
+char *pfctl_lookup_fingerprint(pf_osfp_t, char *, size_t);
+void pfctl_show_fingerprints(int);
+
+
+struct icmptypeent {
+ const char *name;
+ u_int8_t type;
+};
+
+struct icmpcodeent {
+ const char *name;
+ u_int8_t type;
+ u_int8_t code;
+};
+
+const struct icmptypeent *geticmptypebynumber(u_int8_t, u_int8_t);
+const struct icmptypeent *geticmptypebyname(char *, u_int8_t);
+const struct icmpcodeent *geticmpcodebynumber(u_int8_t, u_int8_t, u_int8_t);
+const struct icmpcodeent *geticmpcodebyname(u_long, char *, u_int8_t);
+
+struct pf_timeout {
+ const char *name;
+ int timeout;
+};
+
+#define PFCTL_FLAG_FILTER 0x02
+#define PFCTL_FLAG_NAT 0x04
+#define PFCTL_FLAG_OPTION 0x08
+#define PFCTL_FLAG_ALTQ 0x10
+#define PFCTL_FLAG_TABLE 0x20
+
+extern const struct pf_timeout pf_timeouts[];
+
+void set_ipmask(struct node_host *, u_int8_t);
+int check_netmask(struct node_host *, sa_family_t);
+int unmask(struct pf_addr *, sa_family_t);
+void ifa_load(void);
+struct node_host *ifa_exists(const char *);
+struct node_host *ifa_lookup(const char *, int);
+struct node_host *host(const char *);
+
+int append_addr(struct pfr_buffer *, char *, int);
+int append_addr_host(struct pfr_buffer *,
+ struct node_host *, int, int);
+
+#endif /* _PFCTL_PARSER_H_ */
diff --git a/sbin/pfctl/pfctl_qstats.c b/sbin/pfctl/pfctl_qstats.c
new file mode 100644
index 000000000000..95371e47af44
--- /dev/null
+++ b/sbin/pfctl/pfctl_qstats.c
@@ -0,0 +1,449 @@
+/* $OpenBSD: pfctl_qstats.c,v 1.30 2004/04/27 21:47:32 kjc Exp $ */
+
+/*
+ * Copyright (c) Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <net/pfvar.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <altq/altq.h>
+#include <altq/altq_cbq.h>
+#include <altq/altq_priq.h>
+#include <altq/altq_hfsc.h>
+
+#include "pfctl.h"
+#include "pfctl_parser.h"
+
+union class_stats {
+ class_stats_t cbq_stats;
+ struct priq_classstats priq_stats;
+ struct hfsc_classstats hfsc_stats;
+};
+
+#define AVGN_MAX 8
+#define STAT_INTERVAL 5
+
+struct queue_stats {
+ union class_stats data;
+ int avgn;
+ double avg_bytes;
+ double avg_packets;
+ u_int64_t prev_bytes;
+ u_int64_t prev_packets;
+};
+
+struct pf_altq_node {
+ struct pf_altq altq;
+ struct pf_altq_node *next;
+ struct pf_altq_node *children;
+ struct queue_stats qstats;
+};
+
+int pfctl_update_qstats(int, struct pf_altq_node **);
+void pfctl_insert_altq_node(struct pf_altq_node **,
+ const struct pf_altq, const struct queue_stats);
+struct pf_altq_node *pfctl_find_altq_node(struct pf_altq_node *,
+ const char *, const char *);
+void pfctl_print_altq_node(int, const struct pf_altq_node *,
+ unsigned, int);
+void print_cbqstats(struct queue_stats);
+void print_priqstats(struct queue_stats);
+void print_hfscstats(struct queue_stats);
+void pfctl_free_altq_node(struct pf_altq_node *);
+void pfctl_print_altq_nodestat(int,
+ const struct pf_altq_node *);
+
+void update_avg(struct pf_altq_node *);
+
+int
+pfctl_show_altq(int dev, const char *iface, int opts, int verbose2)
+{
+ struct pf_altq_node *root = NULL, *node;
+ int nodes, dotitle = (opts & PF_OPT_SHOWALL);
+
+#ifdef __FreeBSD__
+ if (!altqsupport)
+ return (-1);
+#endif
+
+ if ((nodes = pfctl_update_qstats(dev, &root)) < 0)
+ return (-1);
+
+ if (nodes == 0)
+ printf("No queue in use\n");
+ for (node = root; node != NULL; node = node->next) {
+ if (iface != NULL && strcmp(node->altq.ifname, iface))
+ continue;
+ if (dotitle) {
+ pfctl_print_title("ALTQ:");
+ dotitle = 0;
+ }
+ pfctl_print_altq_node(dev, node, 0, opts);
+ }
+
+ while (verbose2 && nodes > 0) {
+ printf("\n");
+ fflush(stdout);
+ sleep(STAT_INTERVAL);
+ if ((nodes = pfctl_update_qstats(dev, &root)) == -1)
+ return (-1);
+ for (node = root; node != NULL; node = node->next) {
+ if (iface != NULL && strcmp(node->altq.ifname, iface))
+ continue;
+#ifdef __FreeBSD__
+ if (node->altq.local_flags & PFALTQ_FLAG_IF_REMOVED)
+ continue;
+#endif
+ pfctl_print_altq_node(dev, node, 0, opts);
+ }
+ }
+ pfctl_free_altq_node(root);
+ return (0);
+}
+
+int
+pfctl_update_qstats(int dev, struct pf_altq_node **root)
+{
+ struct pf_altq_node *node;
+ struct pfioc_altq pa;
+ struct pfioc_qstats pq;
+ u_int32_t mnr, nr;
+ struct queue_stats qstats;
+ static u_int32_t last_ticket;
+
+ memset(&pa, 0, sizeof(pa));
+ memset(&pq, 0, sizeof(pq));
+ memset(&qstats, 0, sizeof(qstats));
+ if (ioctl(dev, DIOCGETALTQS, &pa)) {
+ warn("DIOCGETALTQS");
+ return (-1);
+ }
+
+ /* if a new set is found, start over */
+ if (pa.ticket != last_ticket && *root != NULL) {
+ pfctl_free_altq_node(*root);
+ *root = NULL;
+ }
+ last_ticket = pa.ticket;
+
+ mnr = pa.nr;
+ for (nr = 0; nr < mnr; ++nr) {
+ pa.nr = nr;
+ if (ioctl(dev, DIOCGETALTQ, &pa)) {
+ warn("DIOCGETALTQ");
+ return (-1);
+ }
+#ifdef __FreeBSD__
+ if (pa.altq.qid > 0 &&
+ !(pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED)) {
+#else
+ if (pa.altq.qid > 0) {
+#endif
+ pq.nr = nr;
+ pq.ticket = pa.ticket;
+ pq.buf = &qstats.data;
+ pq.nbytes = sizeof(qstats.data);
+ if (ioctl(dev, DIOCGETQSTATS, &pq)) {
+ warn("DIOCGETQSTATS");
+ return (-1);
+ }
+ if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
+ pa.altq.ifname)) != NULL) {
+ memcpy(&node->qstats.data, &qstats.data,
+ sizeof(qstats.data));
+ update_avg(node);
+ } else {
+ pfctl_insert_altq_node(root, pa.altq, qstats);
+ }
+ }
+#ifdef __FreeBSD__
+ else if (pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED) {
+ memset(&qstats.data, 0, sizeof(qstats.data));
+ if ((node = pfctl_find_altq_node(*root, pa.altq.qname,
+ pa.altq.ifname)) != NULL) {
+ memcpy(&node->qstats.data, &qstats.data,
+ sizeof(qstats.data));
+ update_avg(node);
+ } else {
+ pfctl_insert_altq_node(root, pa.altq, qstats);
+ }
+ }
+#endif
+ }
+ return (mnr);
+}
+
+void
+pfctl_insert_altq_node(struct pf_altq_node **root,
+ const struct pf_altq altq, const struct queue_stats qstats)
+{
+ struct pf_altq_node *node;
+
+ node = calloc(1, sizeof(struct pf_altq_node));
+ if (node == NULL)
+ err(1, "pfctl_insert_altq_node: calloc");
+ memcpy(&node->altq, &altq, sizeof(struct pf_altq));
+ memcpy(&node->qstats, &qstats, sizeof(qstats));
+ node->next = node->children = NULL;
+
+ if (*root == NULL)
+ *root = node;
+ else if (!altq.parent[0]) {
+ struct pf_altq_node *prev = *root;
+
+ while (prev->next != NULL)
+ prev = prev->next;
+ prev->next = node;
+ } else {
+ struct pf_altq_node *parent;
+
+ parent = pfctl_find_altq_node(*root, altq.parent, altq.ifname);
+ if (parent == NULL)
+ errx(1, "parent %s not found", altq.parent);
+ if (parent->children == NULL)
+ parent->children = node;
+ else {
+ struct pf_altq_node *prev = parent->children;
+
+ while (prev->next != NULL)
+ prev = prev->next;
+ prev->next = node;
+ }
+ }
+ update_avg(node);
+}
+
+struct pf_altq_node *
+pfctl_find_altq_node(struct pf_altq_node *root, const char *qname,
+ const char *ifname)
+{
+ struct pf_altq_node *node, *child;
+
+ for (node = root; node != NULL; node = node->next) {
+ if (!strcmp(node->altq.qname, qname)
+ && !(strcmp(node->altq.ifname, ifname)))
+ return (node);
+ if (node->children != NULL) {
+ child = pfctl_find_altq_node(node->children, qname,
+ ifname);
+ if (child != NULL)
+ return (child);
+ }
+ }
+ return (NULL);
+}
+
+void
+pfctl_print_altq_node(int dev, const struct pf_altq_node *node,
+ unsigned int level, int opts)
+{
+ const struct pf_altq_node *child;
+
+ if (node == NULL)
+ return;
+
+ print_altq(&node->altq, level, NULL, NULL);
+
+ if (node->children != NULL) {
+ printf("{");
+ for (child = node->children; child != NULL;
+ child = child->next) {
+ printf("%s", child->altq.qname);
+ if (child->next != NULL)
+ printf(", ");
+ }
+ printf("}");
+ }
+ printf("\n");
+
+ if (opts & PF_OPT_VERBOSE)
+ pfctl_print_altq_nodestat(dev, node);
+
+ if (opts & PF_OPT_DEBUG)
+ printf(" [ qid=%u ifname=%s ifbandwidth=%s ]\n",
+ node->altq.qid, node->altq.ifname,
+ rate2str((double)(node->altq.ifbandwidth)));
+
+ for (child = node->children; child != NULL;
+ child = child->next)
+ pfctl_print_altq_node(dev, child, level + 1, opts);
+}
+
+void
+pfctl_print_altq_nodestat(int dev, const struct pf_altq_node *a)
+{
+ if (a->altq.qid == 0)
+ return;
+
+#ifdef __FreeBSD__
+ if (a->altq.local_flags & PFALTQ_FLAG_IF_REMOVED)
+ return;
+#endif
+ switch (a->altq.scheduler) {
+ case ALTQT_CBQ:
+ print_cbqstats(a->qstats);
+ break;
+ case ALTQT_PRIQ:
+ print_priqstats(a->qstats);
+ break;
+ case ALTQT_HFSC:
+ print_hfscstats(a->qstats);
+ break;
+ }
+}
+
+void
+print_cbqstats(struct queue_stats cur)
+{
+ printf(" [ pkts: %10llu bytes: %10llu "
+ "dropped pkts: %6llu bytes: %6llu ]\n",
+ (unsigned long long)cur.data.cbq_stats.xmit_cnt.packets,
+ (unsigned long long)cur.data.cbq_stats.xmit_cnt.bytes,
+ (unsigned long long)cur.data.cbq_stats.drop_cnt.packets,
+ (unsigned long long)cur.data.cbq_stats.drop_cnt.bytes);
+ printf(" [ qlength: %3d/%3d borrows: %6u suspends: %6u ]\n",
+ cur.data.cbq_stats.qcnt, cur.data.cbq_stats.qmax,
+ cur.data.cbq_stats.borrows, cur.data.cbq_stats.delays);
+
+ if (cur.avgn < 2)
+ return;
+
+ printf(" [ measured: %7.1f packets/s, %s/s ]\n",
+ cur.avg_packets / STAT_INTERVAL,
+ rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
+}
+
+void
+print_priqstats(struct queue_stats cur)
+{
+ printf(" [ pkts: %10llu bytes: %10llu "
+ "dropped pkts: %6llu bytes: %6llu ]\n",
+ (unsigned long long)cur.data.priq_stats.xmitcnt.packets,
+ (unsigned long long)cur.data.priq_stats.xmitcnt.bytes,
+ (unsigned long long)cur.data.priq_stats.dropcnt.packets,
+ (unsigned long long)cur.data.priq_stats.dropcnt.bytes);
+ printf(" [ qlength: %3d/%3d ]\n",
+ cur.data.priq_stats.qlength, cur.data.priq_stats.qlimit);
+
+ if (cur.avgn < 2)
+ return;
+
+ printf(" [ measured: %7.1f packets/s, %s/s ]\n",
+ cur.avg_packets / STAT_INTERVAL,
+ rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
+}
+
+void
+print_hfscstats(struct queue_stats cur)
+{
+ printf(" [ pkts: %10llu bytes: %10llu "
+ "dropped pkts: %6llu bytes: %6llu ]\n",
+ (unsigned long long)cur.data.hfsc_stats.xmit_cnt.packets,
+ (unsigned long long)cur.data.hfsc_stats.xmit_cnt.bytes,
+ (unsigned long long)cur.data.hfsc_stats.drop_cnt.packets,
+ (unsigned long long)cur.data.hfsc_stats.drop_cnt.bytes);
+ printf(" [ qlength: %3d/%3d ]\n",
+ cur.data.hfsc_stats.qlength, cur.data.hfsc_stats.qlimit);
+
+ if (cur.avgn < 2)
+ return;
+
+ printf(" [ measured: %7.1f packets/s, %s/s ]\n",
+ cur.avg_packets / STAT_INTERVAL,
+ rate2str((8 * cur.avg_bytes) / STAT_INTERVAL));
+}
+
+void
+pfctl_free_altq_node(struct pf_altq_node *node)
+{
+ while (node != NULL) {
+ struct pf_altq_node *prev;
+
+ if (node->children != NULL)
+ pfctl_free_altq_node(node->children);
+ prev = node;
+ node = node->next;
+ free(prev);
+ }
+}
+
+void
+update_avg(struct pf_altq_node *a)
+{
+ struct queue_stats *qs;
+ u_int64_t b, p;
+ int n;
+
+ if (a->altq.qid == 0)
+ return;
+
+ qs = &a->qstats;
+ n = qs->avgn;
+
+ switch (a->altq.scheduler) {
+ case ALTQT_CBQ:
+ b = qs->data.cbq_stats.xmit_cnt.bytes;
+ p = qs->data.cbq_stats.xmit_cnt.packets;
+ break;
+ case ALTQT_PRIQ:
+ b = qs->data.priq_stats.xmitcnt.bytes;
+ p = qs->data.priq_stats.xmitcnt.packets;
+ break;
+ case ALTQT_HFSC:
+ b = qs->data.hfsc_stats.xmit_cnt.bytes;
+ p = qs->data.hfsc_stats.xmit_cnt.packets;
+ break;
+ default:
+ b = 0;
+ p = 0;
+ break;
+ }
+
+ if (n == 0) {
+ qs->prev_bytes = b;
+ qs->prev_packets = p;
+ qs->avgn++;
+ return;
+ }
+
+ if (b >= qs->prev_bytes)
+ qs->avg_bytes = ((qs->avg_bytes * (n - 1)) +
+ (b - qs->prev_bytes)) / n;
+
+ if (p >= qs->prev_packets)
+ qs->avg_packets = ((qs->avg_packets * (n - 1)) +
+ (p - qs->prev_packets)) / n;
+
+ qs->prev_bytes = b;
+ qs->prev_packets = p;
+ if (n < AVGN_MAX)
+ qs->avgn++;
+}
diff --git a/sbin/pfctl/pfctl_radix.c b/sbin/pfctl/pfctl_radix.c
new file mode 100644
index 000000000000..38f16c4b72a9
--- /dev/null
+++ b/sbin/pfctl/pfctl_radix.c
@@ -0,0 +1,585 @@
+/* $OpenBSD: pfctl_radix.c,v 1.27 2005/05/21 21:03:58 henning Exp $ */
+
+/*
+ * Copyright (c) 2002 Cedric Berger
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/pfvar.h>
+
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <err.h>
+
+#include "pfctl.h"
+
+#define BUF_SIZE 256
+
+extern int dev;
+
+static int pfr_next_token(char buf[], FILE *);
+
+
+int
+pfr_clr_tables(struct pfr_table *filter, int *ndel, int flags)
+{
+ struct pfioc_table io;
+
+ bzero(&io, sizeof io);
+ io.pfrio_flags = flags;
+ if (filter != NULL)
+ io.pfrio_table = *filter;
+ if (ioctl(dev, DIOCRCLRTABLES, &io))
+ return (-1);
+ if (ndel != NULL)
+ *ndel = io.pfrio_ndel;
+ return (0);
+}
+
+int
+pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags)
+{
+ struct pfioc_table io;
+
+ if (size < 0 || (size && tbl == NULL)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ bzero(&io, sizeof io);
+ io.pfrio_flags = flags;
+ io.pfrio_buffer = tbl;
+ io.pfrio_esize = sizeof(*tbl);
+ io.pfrio_size = size;
+ if (ioctl(dev, DIOCRADDTABLES, &io))
+ return (-1);
+ if (nadd != NULL)
+ *nadd = io.pfrio_nadd;
+ return (0);
+}
+
+int
+pfr_del_tables(struct pfr_table *tbl, int size, int *ndel, int flags)
+{
+ struct pfioc_table io;
+
+ if (size < 0 || (size && tbl == NULL)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ bzero(&io, sizeof io);
+ io.pfrio_flags = flags;
+ io.pfrio_buffer = tbl;
+ io.pfrio_esize = sizeof(*tbl);
+ io.pfrio_size = size;
+ if (ioctl(dev, DIOCRDELTABLES, &io))
+ return (-1);
+ if (ndel != NULL)
+ *ndel = io.pfrio_ndel;
+ return (0);
+}
+
+int
+pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size,
+ int flags)
+{
+ struct pfioc_table io;
+
+ if (size == NULL || *size < 0 || (*size && tbl == NULL)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ bzero(&io, sizeof io);
+ io.pfrio_flags = flags;
+ if (filter != NULL)
+ io.pfrio_table = *filter;
+ io.pfrio_buffer = tbl;
+ io.pfrio_esize = sizeof(*tbl);
+ io.pfrio_size = *size;
+ if (ioctl(dev, DIOCRGETTABLES, &io))
+ return (-1);
+ *size = io.pfrio_size;
+ return (0);
+}
+
+int
+pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size,
+ int flags)
+{
+ struct pfioc_table io;
+
+ if (size == NULL || *size < 0 || (*size && tbl == NULL)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ bzero(&io, sizeof io);
+ io.pfrio_flags = flags;
+ if (filter != NULL)
+ io.pfrio_table = *filter;
+ io.pfrio_buffer = tbl;
+ io.pfrio_esize = sizeof(*tbl);
+ io.pfrio_size = *size;
+ if (ioctl(dev, DIOCRGETTSTATS, &io))
+ return (-1);
+ *size = io.pfrio_size;
+ return (0);
+}
+
+int
+pfr_clr_addrs(struct pfr_table *tbl, int *ndel, int flags)
+{
+ struct pfioc_table io;
+
+ if (tbl == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+ bzero(&io, sizeof io);
+ io.pfrio_flags = flags;
+ io.pfrio_table = *tbl;
+ if (ioctl(dev, DIOCRCLRADDRS, &io))
+ return (-1);
+ if (ndel != NULL)
+ *ndel = io.pfrio_ndel;
+ return (0);
+}
+
+int
+pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
+ int *nadd, int flags)
+{
+ struct pfioc_table io;
+
+ if (tbl == NULL || size < 0 || (size && addr == NULL)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ bzero(&io, sizeof io);
+ io.pfrio_flags = flags;
+ io.pfrio_table = *tbl;
+ io.pfrio_buffer = addr;
+ io.pfrio_esize = sizeof(*addr);
+ io.pfrio_size = size;
+ if (ioctl(dev, DIOCRADDADDRS, &io))
+ return (-1);
+ if (nadd != NULL)
+ *nadd = io.pfrio_nadd;
+ return (0);
+}
+
+int
+pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
+ int *ndel, int flags)
+{
+ struct pfioc_table io;
+
+ if (tbl == NULL || size < 0 || (size && addr == NULL)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ bzero(&io, sizeof io);
+ io.pfrio_flags = flags;
+ io.pfrio_table = *tbl;
+ io.pfrio_buffer = addr;
+ io.pfrio_esize = sizeof(*addr);
+ io.pfrio_size = size;
+ if (ioctl(dev, DIOCRDELADDRS, &io))
+ return (-1);
+ if (ndel != NULL)
+ *ndel = io.pfrio_ndel;
+ return (0);
+}
+
+int
+pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
+ int *size2, int *nadd, int *ndel, int *nchange, int flags)
+{
+ struct pfioc_table io;
+
+ if (tbl == NULL || size < 0 || (size && addr == NULL)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ bzero(&io, sizeof io);
+ io.pfrio_flags = flags;
+ io.pfrio_table = *tbl;
+ io.pfrio_buffer = addr;
+ io.pfrio_esize = sizeof(*addr);
+ io.pfrio_size = size;
+ io.pfrio_size2 = (size2 != NULL) ? *size2 : 0;
+ if (ioctl(dev, DIOCRSETADDRS, &io))
+ return (-1);
+ if (nadd != NULL)
+ *nadd = io.pfrio_nadd;
+ if (ndel != NULL)
+ *ndel = io.pfrio_ndel;
+ if (nchange != NULL)
+ *nchange = io.pfrio_nchange;
+ if (size2 != NULL)
+ *size2 = io.pfrio_size2;
+ return (0);
+}
+
+int
+pfr_get_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int *size,
+ int flags)
+{
+ struct pfioc_table io;
+
+ if (tbl == NULL || size == NULL || *size < 0 ||
+ (*size && addr == NULL)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ bzero(&io, sizeof io);
+ io.pfrio_flags = flags;
+ io.pfrio_table = *tbl;
+ io.pfrio_buffer = addr;
+ io.pfrio_esize = sizeof(*addr);
+ io.pfrio_size = *size;
+ if (ioctl(dev, DIOCRGETADDRS, &io))
+ return (-1);
+ *size = io.pfrio_size;
+ return (0);
+}
+
+int
+pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size,
+ int flags)
+{
+ struct pfioc_table io;
+
+ if (tbl == NULL || size == NULL || *size < 0 ||
+ (*size && addr == NULL)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ bzero(&io, sizeof io);
+ io.pfrio_flags = flags;
+ io.pfrio_table = *tbl;
+ io.pfrio_buffer = addr;
+ io.pfrio_esize = sizeof(*addr);
+ io.pfrio_size = *size;
+ if (ioctl(dev, DIOCRGETASTATS, &io))
+ return (-1);
+ *size = io.pfrio_size;
+ return (0);
+}
+
+int
+pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags)
+{
+ struct pfioc_table io;
+
+ if (size < 0 || (size && !tbl)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ bzero(&io, sizeof io);
+ io.pfrio_flags = flags;
+ io.pfrio_buffer = tbl;
+ io.pfrio_esize = sizeof(*tbl);
+ io.pfrio_size = size;
+ if (ioctl(dev, DIOCRCLRTSTATS, &io))
+ return (-1);
+ if (nzero)
+ *nzero = io.pfrio_nzero;
+ return (0);
+}
+
+int
+pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
+ int *nmatch, int flags)
+{
+ struct pfioc_table io;
+
+ if (tbl == NULL || size < 0 || (size && addr == NULL)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ bzero(&io, sizeof io);
+ io.pfrio_flags = flags;
+ io.pfrio_table = *tbl;
+ io.pfrio_buffer = addr;
+ io.pfrio_esize = sizeof(*addr);
+ io.pfrio_size = size;
+ if (ioctl(dev, DIOCRTSTADDRS, &io))
+ return (-1);
+ if (nmatch)
+ *nmatch = io.pfrio_nmatch;
+ return (0);
+}
+
+int
+pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size,
+ int *nadd, int *naddr, int ticket, int flags)
+{
+ struct pfioc_table io;
+
+ if (tbl == NULL || size < 0 || (size && addr == NULL)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ bzero(&io, sizeof io);
+ io.pfrio_flags = flags;
+ io.pfrio_table = *tbl;
+ io.pfrio_buffer = addr;
+ io.pfrio_esize = sizeof(*addr);
+ io.pfrio_size = size;
+ io.pfrio_ticket = ticket;
+ if (ioctl(dev, DIOCRINADEFINE, &io))
+ return (-1);
+ if (nadd != NULL)
+ *nadd = io.pfrio_nadd;
+ if (naddr != NULL)
+ *naddr = io.pfrio_naddr;
+ return (0);
+}
+
+/* interface management code */
+
+int
+pfi_get_ifaces(const char *filter, struct pfi_kif *buf, int *size)
+{
+ struct pfioc_iface io;
+
+ if (size == NULL || *size < 0 || (*size && buf == NULL)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ bzero(&io, sizeof io);
+ if (filter != NULL)
+ if (strlcpy(io.pfiio_name, filter, sizeof(io.pfiio_name)) >=
+ sizeof(io.pfiio_name)) {
+ errno = EINVAL;
+ return (-1);
+ }
+ io.pfiio_buffer = buf;
+ io.pfiio_esize = sizeof(*buf);
+ io.pfiio_size = *size;
+ if (ioctl(dev, DIOCIGETIFACES, &io))
+ return (-1);
+ *size = io.pfiio_size;
+ return (0);
+}
+
+/* buffer management code */
+
+size_t buf_esize[PFRB_MAX] = { 0,
+ sizeof(struct pfr_table), sizeof(struct pfr_tstats),
+ sizeof(struct pfr_addr), sizeof(struct pfr_astats),
+ sizeof(struct pfi_kif), sizeof(struct pfioc_trans_e)
+};
+
+/*
+ * add one element to the buffer
+ */
+int
+pfr_buf_add(struct pfr_buffer *b, const void *e)
+{
+ size_t bs;
+
+ if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX ||
+ e == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+ bs = buf_esize[b->pfrb_type];
+ if (b->pfrb_size == b->pfrb_msize)
+ if (pfr_buf_grow(b, 0))
+ return (-1);
+ memcpy(((caddr_t)b->pfrb_caddr) + bs * b->pfrb_size, e, bs);
+ b->pfrb_size++;
+ return (0);
+}
+
+/*
+ * return next element of the buffer (or first one if prev is NULL)
+ * see PFRB_FOREACH macro
+ */
+void *
+pfr_buf_next(struct pfr_buffer *b, const void *prev)
+{
+ size_t bs;
+
+ if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX)
+ return (NULL);
+ if (b->pfrb_size == 0)
+ return (NULL);
+ if (prev == NULL)
+ return (b->pfrb_caddr);
+ bs = buf_esize[b->pfrb_type];
+ if ((((caddr_t)prev)-((caddr_t)b->pfrb_caddr)) / bs >= b->pfrb_size-1)
+ return (NULL);
+ return (((caddr_t)prev) + bs);
+}
+
+/*
+ * minsize:
+ * 0: make the buffer somewhat bigger
+ * n: make room for "n" entries in the buffer
+ */
+int
+pfr_buf_grow(struct pfr_buffer *b, int minsize)
+{
+ caddr_t p;
+ size_t bs;
+
+ if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX) {
+ errno = EINVAL;
+ return (-1);
+ }
+ if (minsize != 0 && minsize <= b->pfrb_msize)
+ return (0);
+ bs = buf_esize[b->pfrb_type];
+ if (!b->pfrb_msize) {
+ if (minsize < 64)
+ minsize = 64;
+ b->pfrb_caddr = calloc(bs, minsize);
+ if (b->pfrb_caddr == NULL)
+ return (-1);
+ b->pfrb_msize = minsize;
+ } else {
+ if (minsize == 0)
+ minsize = b->pfrb_msize * 2;
+ if (minsize < 0 || minsize >= SIZE_T_MAX / bs) {
+ /* msize overflow */
+ errno = ENOMEM;
+ return (-1);
+ }
+ p = realloc(b->pfrb_caddr, minsize * bs);
+ if (p == NULL)
+ return (-1);
+ bzero(p + b->pfrb_msize * bs, (minsize - b->pfrb_msize) * bs);
+ b->pfrb_caddr = p;
+ b->pfrb_msize = minsize;
+ }
+ return (0);
+}
+
+/*
+ * reset buffer and free memory.
+ */
+void
+pfr_buf_clear(struct pfr_buffer *b)
+{
+ if (b == NULL)
+ return;
+ if (b->pfrb_caddr != NULL)
+ free(b->pfrb_caddr);
+ b->pfrb_caddr = NULL;
+ b->pfrb_size = b->pfrb_msize = 0;
+}
+
+int
+pfr_buf_load(struct pfr_buffer *b, char *file, int nonetwork,
+ int (*append_addr)(struct pfr_buffer *, char *, int))
+{
+ FILE *fp;
+ char buf[BUF_SIZE];
+ int rv;
+
+ if (file == NULL)
+ return (0);
+ if (!strcmp(file, "-"))
+ fp = stdin;
+ else {
+ fp = pfctl_fopen(file, "r");
+ if (fp == NULL)
+ return (-1);
+ }
+ while ((rv = pfr_next_token(buf, fp)) == 1)
+ if (append_addr(b, buf, nonetwork)) {
+ rv = -1;
+ break;
+ }
+ if (fp != stdin)
+ fclose(fp);
+ return (rv);
+}
+
+int
+pfr_next_token(char buf[BUF_SIZE], FILE *fp)
+{
+ static char next_ch = ' ';
+ int i = 0;
+
+ for (;;) {
+ /* skip spaces */
+ while (isspace(next_ch) && !feof(fp))
+ next_ch = fgetc(fp);
+ /* remove from '#' until end of line */
+ if (next_ch == '#')
+ while (!feof(fp)) {
+ next_ch = fgetc(fp);
+ if (next_ch == '\n')
+ break;
+ }
+ else
+ break;
+ }
+ if (feof(fp)) {
+ next_ch = ' ';
+ return (0);
+ }
+ do {
+ if (i < BUF_SIZE)
+ buf[i++] = next_ch;
+ next_ch = fgetc(fp);
+ } while (!feof(fp) && !isspace(next_ch));
+ if (i >= BUF_SIZE) {
+ errno = EINVAL;
+ return (-1);
+ }
+ buf[i] = '\0';
+ return (1);
+}
+
+char *
+pfr_strerror(int errnum)
+{
+ switch (errnum) {
+ case ESRCH:
+ return "Table does not exist";
+ case ENOENT:
+ return "Anchor or Ruleset does not exist";
+ default:
+ return strerror(errnum);
+ }
+}
diff --git a/sbin/pfctl/pfctl_table.c b/sbin/pfctl/pfctl_table.c
new file mode 100644
index 000000000000..f3a1efdd06ba
--- /dev/null
+++ b/sbin/pfctl/pfctl_table.c
@@ -0,0 +1,634 @@
+/* $OpenBSD: pfctl_table.c,v 1.67 2008/06/10 20:55:02 mcbride Exp $ */
+
+/*
+ * Copyright (c) 2002 Cedric Berger
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/pfvar.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "pfctl_parser.h"
+#include "pfctl.h"
+
+extern void usage(void);
+static int pfctl_table(int, char *[], char *, const char *, char *,
+ const char *, int);
+static void print_table(struct pfr_table *, int, int);
+static void print_tstats(struct pfr_tstats *, int);
+static int load_addr(struct pfr_buffer *, int, char *[], char *, int);
+static void print_addrx(struct pfr_addr *, struct pfr_addr *, int);
+static void print_astats(struct pfr_astats *, int);
+static void radix_perror(void);
+static void xprintf(int, const char *, ...);
+static void print_iface(struct pfi_kif *, int);
+
+static const char *stats_text[PFR_DIR_MAX][PFR_OP_TABLE_MAX] = {
+ { "In/Block:", "In/Pass:", "In/XPass:" },
+ { "Out/Block:", "Out/Pass:", "Out/XPass:" }
+};
+
+static const char *istats_text[2][2][2] = {
+ { { "In4/Pass:", "In4/Block:" }, { "Out4/Pass:", "Out4/Block:" } },
+ { { "In6/Pass:", "In6/Block:" }, { "Out6/Pass:", "Out6/Block:" } }
+};
+
+#define RVTEST(fct) do { \
+ if ((!(opts & PF_OPT_NOACTION) || \
+ (opts & PF_OPT_DUMMYACTION)) && \
+ (fct)) { \
+ radix_perror(); \
+ goto _error; \
+ } \
+ } while (0)
+
+#define CREATE_TABLE do { \
+ table.pfrt_flags |= PFR_TFLAG_PERSIST; \
+ if ((!(opts & PF_OPT_NOACTION) || \
+ (opts & PF_OPT_DUMMYACTION)) && \
+ (pfr_add_tables(&table, 1, &nadd, flags)) && \
+ (errno != EPERM)) { \
+ radix_perror(); \
+ goto _error; \
+ } \
+ if (nadd) { \
+ warn_namespace_collision(table.pfrt_name); \
+ xprintf(opts, "%d table created", nadd); \
+ if (opts & PF_OPT_NOACTION) \
+ return (0); \
+ } \
+ table.pfrt_flags &= ~PFR_TFLAG_PERSIST; \
+ } while(0)
+
+int
+pfctl_clear_tables(const char *anchor, int opts)
+{
+ return pfctl_table(0, NULL, NULL, "-F", NULL, anchor, opts);
+}
+
+int
+pfctl_show_tables(const char *anchor, int opts)
+{
+ return pfctl_table(0, NULL, NULL, "-s", NULL, anchor, opts);
+}
+
+int
+pfctl_command_tables(int argc, char *argv[], char *tname,
+ const char *command, char *file, const char *anchor, int opts)
+{
+ if (tname == NULL || command == NULL)
+ usage();
+ return pfctl_table(argc, argv, tname, command, file, anchor, opts);
+}
+
+int
+pfctl_table(int argc, char *argv[], char *tname, const char *command,
+ char *file, const char *anchor, int opts)
+{
+ struct pfr_table table;
+ struct pfr_buffer b, b2;
+ struct pfr_addr *a, *a2;
+ int nadd = 0, ndel = 0, nchange = 0, nzero = 0;
+ int rv = 0, flags = 0, nmatch = 0;
+ void *p;
+
+ if (command == NULL)
+ usage();
+ if (opts & PF_OPT_NOACTION)
+ flags |= PFR_FLAG_DUMMY;
+
+ bzero(&b, sizeof(b));
+ bzero(&b2, sizeof(b2));
+ bzero(&table, sizeof(table));
+ if (tname != NULL) {
+ if (strlen(tname) >= PF_TABLE_NAME_SIZE)
+ usage();
+ if (strlcpy(table.pfrt_name, tname,
+ sizeof(table.pfrt_name)) >= sizeof(table.pfrt_name))
+ errx(1, "pfctl_table: strlcpy");
+ }
+ if (strlcpy(table.pfrt_anchor, anchor,
+ sizeof(table.pfrt_anchor)) >= sizeof(table.pfrt_anchor))
+ errx(1, "pfctl_table: strlcpy");
+
+ if (!strcmp(command, "-F")) {
+ if (argc || file != NULL)
+ usage();
+ RVTEST(pfr_clr_tables(&table, &ndel, flags));
+ xprintf(opts, "%d tables deleted", ndel);
+ } else if (!strcmp(command, "-s")) {
+ b.pfrb_type = (opts & PF_OPT_VERBOSE2) ?
+ PFRB_TSTATS : PFRB_TABLES;
+ if (argc || file != NULL)
+ usage();
+ for (;;) {
+ pfr_buf_grow(&b, b.pfrb_size);
+ b.pfrb_size = b.pfrb_msize;
+ if (opts & PF_OPT_VERBOSE2)
+ RVTEST(pfr_get_tstats(&table,
+ b.pfrb_caddr, &b.pfrb_size, flags));
+ else
+ RVTEST(pfr_get_tables(&table,
+ b.pfrb_caddr, &b.pfrb_size, flags));
+ if (b.pfrb_size <= b.pfrb_msize)
+ break;
+ }
+
+ if ((opts & PF_OPT_SHOWALL) && b.pfrb_size > 0)
+ pfctl_print_title("TABLES:");
+
+ PFRB_FOREACH(p, &b)
+ if (opts & PF_OPT_VERBOSE2)
+ print_tstats(p, opts & PF_OPT_DEBUG);
+ else
+ print_table(p, opts & PF_OPT_VERBOSE,
+ opts & PF_OPT_DEBUG);
+ } else if (!strcmp(command, "kill")) {
+ if (argc || file != NULL)
+ usage();
+ RVTEST(pfr_del_tables(&table, 1, &ndel, flags));
+ xprintf(opts, "%d table deleted", ndel);
+ } else if (!strcmp(command, "flush")) {
+ if (argc || file != NULL)
+ usage();
+ RVTEST(pfr_clr_addrs(&table, &ndel, flags));
+ xprintf(opts, "%d addresses deleted", ndel);
+ } else if (!strcmp(command, "add")) {
+ b.pfrb_type = PFRB_ADDRS;
+ if (load_addr(&b, argc, argv, file, 0))
+ goto _error;
+ CREATE_TABLE;
+ if (opts & PF_OPT_VERBOSE)
+ flags |= PFR_FLAG_FEEDBACK;
+ RVTEST(pfr_add_addrs(&table, b.pfrb_caddr, b.pfrb_size,
+ &nadd, flags));
+ xprintf(opts, "%d/%d addresses added", nadd, b.pfrb_size);
+ if (opts & PF_OPT_VERBOSE)
+ PFRB_FOREACH(a, &b)
+ if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
+ print_addrx(a, NULL,
+ opts & PF_OPT_USEDNS);
+ } else if (!strcmp(command, "delete")) {
+ b.pfrb_type = PFRB_ADDRS;
+ if (load_addr(&b, argc, argv, file, 0))
+ goto _error;
+ if (opts & PF_OPT_VERBOSE)
+ flags |= PFR_FLAG_FEEDBACK;
+ RVTEST(pfr_del_addrs(&table, b.pfrb_caddr, b.pfrb_size,
+ &ndel, flags));
+ xprintf(opts, "%d/%d addresses deleted", ndel, b.pfrb_size);
+ if (opts & PF_OPT_VERBOSE)
+ PFRB_FOREACH(a, &b)
+ if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
+ print_addrx(a, NULL,
+ opts & PF_OPT_USEDNS);
+ } else if (!strcmp(command, "replace")) {
+ b.pfrb_type = PFRB_ADDRS;
+ if (load_addr(&b, argc, argv, file, 0))
+ goto _error;
+ CREATE_TABLE;
+ if (opts & PF_OPT_VERBOSE)
+ flags |= PFR_FLAG_FEEDBACK;
+ for (;;) {
+ int sz2 = b.pfrb_msize;
+
+ RVTEST(pfr_set_addrs(&table, b.pfrb_caddr, b.pfrb_size,
+ &sz2, &nadd, &ndel, &nchange, flags));
+ if (sz2 <= b.pfrb_msize) {
+ b.pfrb_size = sz2;
+ break;
+ } else
+ pfr_buf_grow(&b, sz2);
+ }
+ if (nadd)
+ xprintf(opts, "%d addresses added", nadd);
+ if (ndel)
+ xprintf(opts, "%d addresses deleted", ndel);
+ if (nchange)
+ xprintf(opts, "%d addresses changed", nchange);
+ if (!nadd && !ndel && !nchange)
+ xprintf(opts, "no changes");
+ if (opts & PF_OPT_VERBOSE)
+ PFRB_FOREACH(a, &b)
+ if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
+ print_addrx(a, NULL,
+ opts & PF_OPT_USEDNS);
+ } else if (!strcmp(command, "expire")) {
+ const char *errstr;
+ u_int lifetime;
+
+ b.pfrb_type = PFRB_ASTATS;
+ b2.pfrb_type = PFRB_ADDRS;
+ if (argc != 1 || file != NULL)
+ usage();
+ lifetime = strtonum(*argv, 0, UINT_MAX, &errstr);
+ if (errstr)
+ errx(1, "expiry time: %s", errstr);
+ for (;;) {
+ pfr_buf_grow(&b, b.pfrb_size);
+ b.pfrb_size = b.pfrb_msize;
+ RVTEST(pfr_get_astats(&table, b.pfrb_caddr,
+ &b.pfrb_size, flags));
+ if (b.pfrb_size <= b.pfrb_msize)
+ break;
+ }
+ PFRB_FOREACH(p, &b) {
+ ((struct pfr_astats *)p)->pfras_a.pfra_fback = 0;
+ if (time(NULL) - ((struct pfr_astats *)p)->pfras_tzero >
+ lifetime)
+ if (pfr_buf_add(&b2,
+ &((struct pfr_astats *)p)->pfras_a))
+ err(1, "duplicate buffer");
+ }
+
+ if (opts & PF_OPT_VERBOSE)
+ flags |= PFR_FLAG_FEEDBACK;
+ RVTEST(pfr_del_addrs(&table, b2.pfrb_caddr, b2.pfrb_size,
+ &ndel, flags));
+ xprintf(opts, "%d/%d addresses expired", ndel, b2.pfrb_size);
+ if (opts & PF_OPT_VERBOSE)
+ PFRB_FOREACH(a, &b2)
+ if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
+ print_addrx(a, NULL,
+ opts & PF_OPT_USEDNS);
+ } else if (!strcmp(command, "show")) {
+ b.pfrb_type = (opts & PF_OPT_VERBOSE) ?
+ PFRB_ASTATS : PFRB_ADDRS;
+ if (argc || file != NULL)
+ usage();
+ for (;;) {
+ pfr_buf_grow(&b, b.pfrb_size);
+ b.pfrb_size = b.pfrb_msize;
+ if (opts & PF_OPT_VERBOSE)
+ RVTEST(pfr_get_astats(&table, b.pfrb_caddr,
+ &b.pfrb_size, flags));
+ else
+ RVTEST(pfr_get_addrs(&table, b.pfrb_caddr,
+ &b.pfrb_size, flags));
+ if (b.pfrb_size <= b.pfrb_msize)
+ break;
+ }
+ PFRB_FOREACH(p, &b)
+ if (opts & PF_OPT_VERBOSE)
+ print_astats(p, opts & PF_OPT_USEDNS);
+ else
+ print_addrx(p, NULL, opts & PF_OPT_USEDNS);
+ } else if (!strcmp(command, "test")) {
+ b.pfrb_type = PFRB_ADDRS;
+ b2.pfrb_type = PFRB_ADDRS;
+
+ if (load_addr(&b, argc, argv, file, 1))
+ goto _error;
+ if (opts & PF_OPT_VERBOSE2) {
+ flags |= PFR_FLAG_REPLACE;
+ PFRB_FOREACH(a, &b)
+ if (pfr_buf_add(&b2, a))
+ err(1, "duplicate buffer");
+ }
+ RVTEST(pfr_tst_addrs(&table, b.pfrb_caddr, b.pfrb_size,
+ &nmatch, flags));
+ xprintf(opts, "%d/%d addresses match", nmatch, b.pfrb_size);
+ if ((opts & PF_OPT_VERBOSE) && !(opts & PF_OPT_VERBOSE2))
+ PFRB_FOREACH(a, &b)
+ if (a->pfra_fback == PFR_FB_MATCH)
+ print_addrx(a, NULL,
+ opts & PF_OPT_USEDNS);
+ if (opts & PF_OPT_VERBOSE2) {
+ a2 = NULL;
+ PFRB_FOREACH(a, &b) {
+ a2 = pfr_buf_next(&b2, a2);
+ print_addrx(a2, a, opts & PF_OPT_USEDNS);
+ }
+ }
+ if (nmatch < b.pfrb_size)
+ rv = 2;
+ } else if (!strcmp(command, "zero")) {
+ if (argc || file != NULL)
+ usage();
+ flags |= PFR_FLAG_ADDRSTOO;
+ RVTEST(pfr_clr_tstats(&table, 1, &nzero, flags));
+ xprintf(opts, "%d table/stats cleared", nzero);
+ } else
+ warnx("pfctl_table: unknown command '%s'", command);
+ goto _cleanup;
+
+_error:
+ rv = -1;
+_cleanup:
+ pfr_buf_clear(&b);
+ pfr_buf_clear(&b2);
+ return (rv);
+}
+
+void
+print_table(struct pfr_table *ta, int verbose, int debug)
+{
+ if (!debug && !(ta->pfrt_flags & PFR_TFLAG_ACTIVE))
+ return;
+ if (verbose) {
+ printf("%c%c%c%c%c%c%c\t%s",
+ (ta->pfrt_flags & PFR_TFLAG_CONST) ? 'c' : '-',
+ (ta->pfrt_flags & PFR_TFLAG_PERSIST) ? 'p' : '-',
+ (ta->pfrt_flags & PFR_TFLAG_ACTIVE) ? 'a' : '-',
+ (ta->pfrt_flags & PFR_TFLAG_INACTIVE) ? 'i' : '-',
+ (ta->pfrt_flags & PFR_TFLAG_REFERENCED) ? 'r' : '-',
+ (ta->pfrt_flags & PFR_TFLAG_REFDANCHOR) ? 'h' : '-',
+ (ta->pfrt_flags & PFR_TFLAG_COUNTERS) ? 'C' : '-',
+ ta->pfrt_name);
+ if (ta->pfrt_anchor[0])
+ printf("\t%s", ta->pfrt_anchor);
+ puts("");
+ } else
+ puts(ta->pfrt_name);
+}
+
+void
+print_tstats(struct pfr_tstats *ts, int debug)
+{
+ time_t time = ts->pfrts_tzero;
+ int dir, op;
+
+ if (!debug && !(ts->pfrts_flags & PFR_TFLAG_ACTIVE))
+ return;
+ print_table(&ts->pfrts_t, 1, debug);
+ printf("\tAddresses: %d\n", ts->pfrts_cnt);
+ printf("\tCleared: %s", ctime(&time));
+ printf("\tReferences: [ Anchors: %-18d Rules: %-18d ]\n",
+ ts->pfrts_refcnt[PFR_REFCNT_ANCHOR],
+ ts->pfrts_refcnt[PFR_REFCNT_RULE]);
+ printf("\tEvaluations: [ NoMatch: %-18llu Match: %-18llu ]\n",
+ (unsigned long long)ts->pfrts_nomatch,
+ (unsigned long long)ts->pfrts_match);
+ for (dir = 0; dir < PFR_DIR_MAX; dir++)
+ for (op = 0; op < PFR_OP_TABLE_MAX; op++)
+ printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
+ stats_text[dir][op],
+ (unsigned long long)ts->pfrts_packets[dir][op],
+ (unsigned long long)ts->pfrts_bytes[dir][op]);
+}
+
+int
+load_addr(struct pfr_buffer *b, int argc, char *argv[], char *file,
+ int nonetwork)
+{
+ while (argc--)
+ if (append_addr(b, *argv++, nonetwork)) {
+ if (errno)
+ warn("cannot decode %s", argv[-1]);
+ return (-1);
+ }
+ if (pfr_buf_load(b, file, nonetwork, append_addr)) {
+ warn("cannot load %s", file);
+ return (-1);
+ }
+ return (0);
+}
+
+void
+print_addrx(struct pfr_addr *ad, struct pfr_addr *rad, int dns)
+{
+ char ch, buf[256] = "{error}";
+ char fb[] = { ' ', 'M', 'A', 'D', 'C', 'Z', 'X', ' ', 'Y', ' ' };
+ unsigned int fback, hostnet;
+
+ fback = (rad != NULL) ? rad->pfra_fback : ad->pfra_fback;
+ ch = (fback < sizeof(fb)/sizeof(*fb)) ? fb[fback] : '?';
+ hostnet = (ad->pfra_af == AF_INET6) ? 128 : 32;
+ inet_ntop(ad->pfra_af, &ad->pfra_u, buf, sizeof(buf));
+ printf("%c %c%s", ch, (ad->pfra_not?'!':' '), buf);
+ if (ad->pfra_net < hostnet)
+ printf("/%d", ad->pfra_net);
+ if (rad != NULL && fback != PFR_FB_NONE) {
+ if (strlcpy(buf, "{error}", sizeof(buf)) >= sizeof(buf))
+ errx(1, "print_addrx: strlcpy");
+ inet_ntop(rad->pfra_af, &rad->pfra_u, buf, sizeof(buf));
+ printf("\t%c%s", (rad->pfra_not?'!':' '), buf);
+ if (rad->pfra_net < hostnet)
+ printf("/%d", rad->pfra_net);
+ }
+ if (rad != NULL && fback == PFR_FB_NONE)
+ printf("\t nomatch");
+ if (dns && ad->pfra_net == hostnet) {
+ char host[NI_MAXHOST];
+ union sockaddr_union sa;
+
+ strlcpy(host, "?", sizeof(host));
+ bzero(&sa, sizeof(sa));
+ sa.sa.sa_family = ad->pfra_af;
+ if (sa.sa.sa_family == AF_INET) {
+ sa.sa.sa_len = sizeof(sa.sin);
+ sa.sin.sin_addr = ad->pfra_ip4addr;
+ } else {
+ sa.sa.sa_len = sizeof(sa.sin6);
+ sa.sin6.sin6_addr = ad->pfra_ip6addr;
+ }
+ if (getnameinfo(&sa.sa, sa.sa.sa_len, host, sizeof(host),
+ NULL, 0, NI_NAMEREQD) == 0)
+ printf("\t(%s)", host);
+ }
+ printf("\n");
+}
+
+void
+print_astats(struct pfr_astats *as, int dns)
+{
+ time_t time = as->pfras_tzero;
+ int dir, op;
+
+ print_addrx(&as->pfras_a, NULL, dns);
+ printf("\tCleared: %s", ctime(&time));
+ if (as->pfras_a.pfra_fback == PFR_FB_NOCOUNT)
+ return;
+ for (dir = 0; dir < PFR_DIR_MAX; dir++)
+ for (op = 0; op < PFR_OP_ADDR_MAX; op++)
+ printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
+ stats_text[dir][op],
+ (unsigned long long)as->pfras_packets[dir][op],
+ (unsigned long long)as->pfras_bytes[dir][op]);
+}
+
+void
+radix_perror(void)
+{
+ extern char *__progname;
+ fprintf(stderr, "%s: %s.\n", __progname, pfr_strerror(errno));
+}
+
+int
+pfctl_define_table(char *name, int flags, int addrs, const char *anchor,
+ struct pfr_buffer *ab, u_int32_t ticket)
+{
+ struct pfr_table tbl;
+
+ bzero(&tbl, sizeof(tbl));
+ if (strlcpy(tbl.pfrt_name, name, sizeof(tbl.pfrt_name)) >=
+ sizeof(tbl.pfrt_name) || strlcpy(tbl.pfrt_anchor, anchor,
+ sizeof(tbl.pfrt_anchor)) >= sizeof(tbl.pfrt_anchor))
+ errx(1, "pfctl_define_table: strlcpy");
+ tbl.pfrt_flags = flags;
+
+ return pfr_ina_define(&tbl, ab->pfrb_caddr, ab->pfrb_size, NULL,
+ NULL, ticket, addrs ? PFR_FLAG_ADDRSTOO : 0);
+}
+
+void
+warn_namespace_collision(const char *filter)
+{
+ struct pfr_buffer b;
+ struct pfr_table *t;
+ const char *name = NULL, *lastcoll;
+ int coll = 0;
+
+ bzero(&b, sizeof(b));
+ b.pfrb_type = PFRB_TABLES;
+ for (;;) {
+ pfr_buf_grow(&b, b.pfrb_size);
+ b.pfrb_size = b.pfrb_msize;
+ if (pfr_get_tables(NULL, b.pfrb_caddr,
+ &b.pfrb_size, PFR_FLAG_ALLRSETS))
+ err(1, "pfr_get_tables");
+ if (b.pfrb_size <= b.pfrb_msize)
+ break;
+ }
+ PFRB_FOREACH(t, &b) {
+ if (!(t->pfrt_flags & PFR_TFLAG_ACTIVE))
+ continue;
+ if (filter != NULL && strcmp(filter, t->pfrt_name))
+ continue;
+ if (!t->pfrt_anchor[0])
+ name = t->pfrt_name;
+ else if (name != NULL && !strcmp(name, t->pfrt_name)) {
+ coll++;
+ lastcoll = name;
+ name = NULL;
+ }
+ }
+ if (coll == 1)
+ warnx("warning: namespace collision with <%s> global table.",
+ lastcoll);
+ else if (coll > 1)
+ warnx("warning: namespace collisions with %d global tables.",
+ coll);
+ pfr_buf_clear(&b);
+}
+
+void
+xprintf(int opts, const char *fmt, ...)
+{
+ va_list args;
+
+ if (opts & PF_OPT_QUIET)
+ return;
+
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+
+ if (opts & PF_OPT_DUMMYACTION)
+ fprintf(stderr, " (dummy).\n");
+ else if (opts & PF_OPT_NOACTION)
+ fprintf(stderr, " (syntax only).\n");
+ else
+ fprintf(stderr, ".\n");
+}
+
+
+/* interface stuff */
+
+int
+pfctl_show_ifaces(const char *filter, int opts)
+{
+ struct pfr_buffer b;
+ struct pfi_kif *p;
+ int i = 0;
+
+ bzero(&b, sizeof(b));
+ b.pfrb_type = PFRB_IFACES;
+ for (;;) {
+ pfr_buf_grow(&b, b.pfrb_size);
+ b.pfrb_size = b.pfrb_msize;
+ if (pfi_get_ifaces(filter, b.pfrb_caddr, &b.pfrb_size)) {
+ radix_perror();
+ return (1);
+ }
+ if (b.pfrb_size <= b.pfrb_msize)
+ break;
+ i++;
+ }
+ if (opts & PF_OPT_SHOWALL)
+ pfctl_print_title("INTERFACES:");
+ PFRB_FOREACH(p, &b)
+ print_iface(p, opts);
+ return (0);
+}
+
+void
+print_iface(struct pfi_kif *p, int opts)
+{
+ time_t tzero = p->pfik_tzero;
+ int i, af, dir, act;
+
+ printf("%s", p->pfik_name);
+ if (opts & PF_OPT_VERBOSE) {
+ if (p->pfik_flags & PFI_IFLAG_SKIP)
+ printf(" (skip)");
+ }
+ printf("\n");
+
+ if (!(opts & PF_OPT_VERBOSE2))
+ return;
+ printf("\tCleared: %s", ctime(&tzero));
+ printf("\tReferences: %-18d\n", p->pfik_rulerefs);
+ for (i = 0; i < 8; i++) {
+ af = (i>>2) & 1;
+ dir = (i>>1) &1;
+ act = i & 1;
+ printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
+ istats_text[af][dir][act],
+ (unsigned long long)p->pfik_packets[af][dir][act],
+ (unsigned long long)p->pfik_bytes[af][dir][act]);
+ }
+}
diff --git a/sbin/ping/ping.c b/sbin/ping/ping.c
index baf7faaf4d5b..85d03483a5a3 100644
--- a/sbin/ping/ping.c
+++ b/sbin/ping/ping.c
@@ -255,7 +255,8 @@ main(int argc, char *const *argv)
s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
sockerrno = errno;
- setuid(getuid());
+ if (setuid(getuid()) != 0)
+ err(EX_NOPERM, "setuid() failed");
uid = getuid();
alarmtimeout = df = preload = tos = 0;
@@ -832,7 +833,7 @@ main(int argc, char *const *argv)
timeout.tv_sec++;
}
if (timeout.tv_sec < 0)
- timeout.tv_sec = timeout.tv_usec = 0;
+ timerclear(&timeout);
n = select(s + 1, &rfds, NULL, NULL, &timeout);
if (n < 0)
continue; /* Must be EINTR. */
diff --git a/sbin/ping6/ping6.c b/sbin/ping6/ping6.c
index 711561ebb9ac..8c3e16bbdaa0 100644
--- a/sbin/ping6/ping6.c
+++ b/sbin/ping6/ping6.c
@@ -702,8 +702,10 @@ main(int argc, char *argv[])
}
/* revoke root privilege */
- seteuid(getuid());
- setuid(getuid());
+ if (seteuid(getuid()) != 0)
+ err(1, "seteuid() failed");
+ if (setuid(getuid()) != 0)
+ err(1, "setuid() failed");
if ((options & F_FLOOD) && (options & F_INTERVAL))
errx(1, "-f and -i incompatible options");
diff --git a/sbin/quotacheck/quotacheck.c b/sbin/quotacheck/quotacheck.c
index ed9ce753c553..a32ac841207d 100644
--- a/sbin/quotacheck/quotacheck.c
+++ b/sbin/quotacheck/quotacheck.c
@@ -63,6 +63,7 @@ __FBSDID("$FreeBSD$");
#include <grp.h>
#include <libutil.h>
#include <pwd.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -383,9 +384,9 @@ chkquota(char *specname, struct quotafile *qfu, struct quotafile *qfg)
if (vflag) {
if (aflag)
(void)printf("%s: ", mntpt);
- (void)printf("out of range UID/GID (%u/%u) ino=%u\n",
+ (void)printf("out of range UID/GID (%u/%u) ino=%ju\n",
DIP(dp, di_uid), DIP(dp,di_gid),
- ino);
+ (uintmax_t)ino);
}
continue;
}
@@ -601,7 +602,8 @@ getnextinode(ino_t inumber)
static caddr_t nextinop;
if (inumber != nextino++ || inumber > lastvalidinum)
- errx(1, "bad inode number %d to nextinode", inumber);
+ errx(1, "bad inode number %ju to nextinode",
+ (uintmax_t)inumber);
if (inumber >= lastinum) {
readcnt++;
dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum));
@@ -635,7 +637,7 @@ setinodebuf(ino_t inum)
{
if (inum % sblock.fs_ipg != 0)
- errx(1, "bad inode number %d to setinodebuf", inum);
+ errx(1, "bad inode number %ju to setinodebuf", (uintmax_t)inum);
lastvalidinum = inum + sblock.fs_ipg - 1;
nextino = inum;
lastinum = inum;
diff --git a/sbin/rcorder/rcorder.c b/sbin/rcorder/rcorder.c
index c92d06339928..83f6df839682 100644
--- a/sbin/rcorder/rcorder.c
+++ b/sbin/rcorder/rcorder.c
@@ -84,7 +84,7 @@ typedef bool flag;
#define SET TRUE
#define RESET FALSE
-Hash_Table provide_hash_s, *provide_hash;
+static Hash_Table provide_hash_s, *provide_hash;
typedef struct provnode provnode;
typedef struct filenode filenode;
diff --git a/sbin/restore/dirs.c b/sbin/restore/dirs.c
index 203d97702f14..b2cd8fd28869 100644
--- a/sbin/restore/dirs.c
+++ b/sbin/restore/dirs.c
@@ -637,7 +637,8 @@ setdirmodes(int flags)
continue;
}
if (ep == NULL) {
- panic("cannot find directory inode %d\n", node.ino);
+ panic("cannot find directory inode %ju\n",
+ (uintmax_t)node.ino);
continue;
}
cp = myname(ep);
@@ -678,7 +679,8 @@ genliteraldir(char *name, ino_t ino)
itp = inotablookup(ino);
if (itp == NULL)
- panic("Cannot find directory inode %d named %s\n", ino, name);
+ panic("Cannot find directory inode %ju named %s\n",
+ (uintmax_t)ino, name);
if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
fprintf(stderr, "%s: ", name);
(void) fflush(stderr);
@@ -691,15 +693,15 @@ genliteraldir(char *name, ino_t ino)
size = i < BUFSIZ ? i : BUFSIZ;
if (read(dp, buf, (int) size) == -1) {
fprintf(stderr,
- "write error extracting inode %d, name %s\n",
- curfile.ino, curfile.name);
+ "write error extracting inode %ju, name %s\n",
+ (uintmax_t)curfile.ino, curfile.name);
fprintf(stderr, "read: %s\n", strerror(errno));
done(1);
}
if (!Nflag && write(ofile, buf, (int) size) == -1) {
fprintf(stderr,
- "write error extracting inode %d, name %s\n",
- curfile.ino, curfile.name);
+ "write error extracting inode %ju, name %s\n",
+ (uintmax_t)curfile.ino, curfile.name);
fprintf(stderr, "write: %s\n", strerror(errno));
done(1);
}
diff --git a/sbin/restore/interactive.c b/sbin/restore/interactive.c
index 3cd311daca97..05d1a5806a14 100644
--- a/sbin/restore/interactive.c
+++ b/sbin/restore/interactive.c
@@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
#include <glob.h>
#include <limits.h>
#include <setjmp.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -675,7 +676,8 @@ formatf(struct afile *list, int nentry)
for (j = 0; j < columns; j++) {
fp = &list[j * lines + i];
if (vflag) {
- fprintf(stderr, "%*d ", precision, fp->fnum);
+ fprintf(stderr, "%*ju ",
+ precision, (uintmax_t)fp->fnum);
fp->len += precision + 1;
}
if (haveprefix) {
diff --git a/sbin/restore/restore.c b/sbin/restore/restore.c
index e494cdb69a7d..fbc24ece7c49 100644
--- a/sbin/restore/restore.c
+++ b/sbin/restore/restore.c
@@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <limits.h>
+#include <stdint.h>
#include <stdio.h>
#include <string.h>
@@ -61,7 +62,7 @@ listfile(char *name, ino_t ino, int type)
if (TSTINO(ino, dumpmap) == 0)
return (descend);
vprintf(stdout, "%s", type == LEAF ? "leaf" : "dir ");
- fprintf(stdout, "%10d\t%s\n", ino, name);
+ fprintf(stdout, "%10ju\t%s\n", (uintmax_t)ino, name);
return (descend);
}
@@ -83,7 +84,7 @@ addfile(char *name, ino_t ino, int type)
if (ino == WINO && command == 'i' && !vflag)
return (descend);
if (!mflag) {
- (void) sprintf(buf, "./%u", ino);
+ (void) sprintf(buf, "./%ju", (uintmax_t)ino);
name = buf;
if (type == NODE) {
(void) genliteraldir(name, ino);
@@ -457,8 +458,8 @@ nodeupdates(char *name, ino_t ino, int type)
* next incremental tape.
*/
case 0:
- fprintf(stderr, "%s: (inode %d) not found on tape\n",
- name, ino);
+ fprintf(stderr, "%s: (inode %ju) not found on tape\n",
+ name, (uintmax_t)ino);
break;
/*
@@ -612,7 +613,7 @@ createleaves(char *symtabfile)
while (first < curfile.ino) {
ep = lookupino(first);
if (ep == NULL)
- panic("%d: bad first\n", first);
+ panic("%ju: bad first\n", (uintmax_t)first);
fprintf(stderr, "%s: not found on tape\n", myname(ep));
ep->e_flags &= ~(NEW|EXTRACT);
first = lowerbnd(first);
@@ -625,8 +626,8 @@ createleaves(char *symtabfile)
* on the next incremental tape.
*/
if (first != curfile.ino) {
- fprintf(stderr, "expected next file %d, got %d\n",
- first, curfile.ino);
+ fprintf(stderr, "expected next file %ju, got %ju\n",
+ (uintmax_t)first, (uintmax_t)curfile.ino);
skipfile();
goto next;
}
@@ -852,7 +853,7 @@ verifyfile(char *name, ino_t ino, int type)
if (np == ep)
break;
if (np == NULL)
- panic("missing inumber %d\n", ino);
+ panic("missing inumber %ju\n", (uintmax_t)ino);
if (ep->e_type == LEAF && type != LEAF)
badentry(ep, "type should be LEAF");
return (descend);
diff --git a/sbin/restore/symtab.c b/sbin/restore/symtab.c
index 3e523351924e..9d0313dfb083 100644
--- a/sbin/restore/symtab.c
+++ b/sbin/restore/symtab.c
@@ -52,6 +52,7 @@ static const char rcsid[] =
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -100,7 +101,7 @@ addino(ino_t inum, struct entry *np)
struct entry **epp;
if (inum < WINO || inum >= maxino)
- panic("addino: out of range %d\n", inum);
+ panic("addino: out of range %ju\n", (uintmax_t)inum);
epp = &entry[inum % entrytblsize];
np->e_ino = inum;
np->e_next = *epp;
@@ -121,7 +122,7 @@ deleteino(ino_t inum)
struct entry **prev;
if (inum < WINO || inum >= maxino)
- panic("deleteino: out of range %d\n", inum);
+ panic("deleteino: out of range %ju\n", (uintmax_t)inum);
prev = &entry[inum % entrytblsize];
for (next = *prev; next != NULL; next = next->e_next) {
if (next->e_ino == inum) {
@@ -131,7 +132,7 @@ deleteino(ino_t inum)
}
prev = &next->e_next;
}
- panic("deleteino: %d not found\n", inum);
+ panic("deleteino: %ju not found\n", (uintmax_t)inum);
}
/*
diff --git a/sbin/restore/tape.c b/sbin/restore/tape.c
index a566c5acf339..4f3454901104 100644
--- a/sbin/restore/tape.c
+++ b/sbin/restore/tape.c
@@ -164,7 +164,11 @@ setinput(char *source, int ispipecommand)
}
pipein++;
}
- setuid(getuid()); /* no longer need or want root privileges */
+ /* no longer need or want root privileges */
+ if (setuid(getuid()) != 0) {
+ fprintf(stderr, "setuid failed\n");
+ done(1);
+ }
magtape = strdup(source);
if (magtape == NULL) {
fprintf(stderr, "Cannot allocate space for magtape buffer\n");
@@ -274,7 +278,7 @@ setup(void)
done(1);
}
maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1;
- dprintf(stdout, "maxino = %d\n", maxino);
+ dprintf(stdout, "maxino = %ju\n", (uintmax_t)maxino);
map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY));
if (map == NULL)
panic("no memory for active inode map\n");
@@ -1050,8 +1054,9 @@ setupextattr(int extsize)
}
extbufsize = 0;
extbuf = NULL;
- fprintf(stderr, "Cannot extract %d bytes %s for inode %d, name %s\n",
- extsize, "of extended attributes", curfile.ino, curfile.name);
+ fprintf(stderr, "Cannot extract %d bytes %s for inode %ju, name %s\n",
+ extsize, "of extended attributes", (uintmax_t)curfile.ino,
+ curfile.name);
return (NULL);
}
@@ -1079,8 +1084,8 @@ xtrfile(char *buf, long size)
return;
if (write(ofile, buf, (int) size) == -1) {
fprintf(stderr,
- "write error extracting inode %d, name %s\nwrite: %s\n",
- curfile.ino, curfile.name, strerror(errno));
+ "write error extracting inode %ju, name %s\nwrite: %s\n",
+ (uintmax_t)curfile.ino, curfile.name, strerror(errno));
}
}
@@ -1094,8 +1099,8 @@ xtrskip(char *buf, long size)
if (lseek(ofile, size, SEEK_CUR) == -1) {
fprintf(stderr,
- "seek error extracting inode %d, name %s\nlseek: %s\n",
- curfile.ino, curfile.name, strerror(errno));
+ "seek error extracting inode %ju, name %s\nlseek: %s\n",
+ (uintmax_t)curfile.ino, curfile.name, strerror(errno));
done(1);
}
}
@@ -1243,8 +1248,8 @@ getmore:
fprintf(stderr, "restoring %s\n", curfile.name);
break;
case SKIP:
- fprintf(stderr, "skipping over inode %d\n",
- curfile.ino);
+ fprintf(stderr, "skipping over inode %ju\n",
+ (uintmax_t)curfile.ino);
break;
}
if (!yflag && !reply("continue"))
@@ -1477,10 +1482,11 @@ accthdr(struct s_spcl *header)
fprintf(stderr, "Used inodes map header");
break;
case TS_INODE:
- fprintf(stderr, "File header, ino %d", previno);
+ fprintf(stderr, "File header, ino %ju", (uintmax_t)previno);
break;
case TS_ADDR:
- fprintf(stderr, "File continuation header, ino %d", previno);
+ fprintf(stderr, "File continuation header, ino %ju",
+ (uintmax_t)previno);
break;
case TS_END:
fprintf(stderr, "End of tape header");
@@ -1631,8 +1637,8 @@ checksum(int *buf)
}
if (i != CHECKSUM) {
- fprintf(stderr, "Checksum error %o, inode %d file %s\n", i,
- curfile.ino, curfile.name);
+ fprintf(stderr, "Checksum error %o, inode %ju file %s\n", i,
+ (uintmax_t)curfile.ino, curfile.name);
return(FAIL);
}
return(GOOD);
diff --git a/sbin/setkey/setkey.8 b/sbin/setkey/setkey.8
index 469040eccea2..8f7bae139c08 100644
--- a/sbin/setkey/setkey.8
+++ b/sbin/setkey/setkey.8
@@ -442,7 +442,7 @@ protocols other than TCP, UDP and ICMP may not be suitable to use with IPsec.
.Ar policy
is expressed in one of the following three formats:
.Pp
-.Bl -tag -compact
+.Bl -tag -width 2n -compact
.It Fl P Ar direction Li discard
.It Fl P Ar direction Li none
.It Xo Fl P Ar direction Li ipsec
@@ -553,7 +553,9 @@ For
example, if an IP header was followed by an AH header followed by an
ESP header followed by an upper layer protocol header, the rule would
be:
+.Pp
.Dl esp/transport//require ah/transport//require ;
+.Pp
The rule order is very important.
.Pp
Note that
diff --git a/sbin/shutdown/shutdown.c b/sbin/shutdown/shutdown.c
index 05d73a97a7ac..f53b3d8c98f9 100644
--- a/sbin/shutdown/shutdown.c
+++ b/sbin/shutdown/shutdown.c
@@ -93,7 +93,7 @@ static char mbuf[BUFSIZ];
static const char *nosync, *whom;
static void badtime(void);
-static void perform_shutdown(void);
+static void die_you_gravy_sucking_pig_dog(void);
static void finish(int);
static void getoffset(char *);
static void loop(void);
@@ -282,7 +282,7 @@ loop(void)
if (!tp->timeleft)
break;
}
- perform_shutdown();
+ die_you_gravy_sucking_pig_dog();
}
static jmp_buf alarmbuf;
@@ -349,7 +349,7 @@ timeout(int signo __unused)
}
static void
-perform_shutdown(void)
+die_you_gravy_sucking_pig_dog(void)
{
char *empty_environ[] = { NULL };
diff --git a/sbin/tunefs/tunefs.c b/sbin/tunefs/tunefs.c
index de3db1261779..688952fc790a 100644
--- a/sbin/tunefs/tunefs.c
+++ b/sbin/tunefs/tunefs.c
@@ -70,14 +70,16 @@ __FBSDID("$FreeBSD$");
/* the optimization warning string template */
#define OPTWARN "should optimize for %s with minfree %s %d%%"
+static int blocks;
+static char clrbuf[MAXBSIZE];
static struct uufsd disk;
#define sblock disk.d_fs
-void usage(void);
-void printfs(void);
-int journal_alloc(int64_t size);
-void journal_clear(void);
-void sbdirty(void);
+static void usage(void);
+static void printfs(void);
+static int journal_alloc(int64_t size);
+static void journal_clear(void);
+static void sbdirty(void);
int
main(int argc, char *argv[])
@@ -545,16 +547,13 @@ err:
err(12, "%s", special);
}
-void
+static void
sbdirty(void)
{
disk.d_fs.fs_flags |= FS_UNCLEAN | FS_NEEDSFSCK;
disk.d_fs.fs_clean = 0;
}
-static int blocks;
-static char clrbuf[MAXBSIZE];
-
static ufs2_daddr_t
journal_balloc(void)
{
@@ -880,7 +879,7 @@ indir_fill(ufs2_daddr_t blk, int level, int *resid)
/*
* Clear the flag bits so the journal can be removed.
*/
-void
+static void
journal_clear(void)
{
struct ufs1_dinode *dp1;
@@ -894,7 +893,7 @@ journal_clear(void)
warnx("Journal file does not exist");
return;
}
- printf("Clearing journal flags from inode %d\n", ino);
+ printf("Clearing journal flags from inode %ju\n", (uintmax_t)ino);
if (getino(&disk, &ip, ino, &mode) != 0) {
warn("Failed to get journal inode");
return;
@@ -911,7 +910,7 @@ journal_clear(void)
}
}
-int
+static int
journal_alloc(int64_t size)
{
struct ufs1_dinode *dp1;
@@ -970,8 +969,8 @@ journal_alloc(int64_t size)
ino = cgialloc(&disk);
if (ino <= 0)
break;
- printf("Using inode %d in cg %d for %jd byte journal\n",
- ino, cgp->cg_cgx, size);
+ printf("Using inode %ju in cg %d for %jd byte journal\n",
+ (uintmax_t)ino, cgp->cg_cgx, size);
if (getino(&disk, &ip, ino, &mode) != 0) {
warn("Failed to get allocated inode");
sbdirty();
@@ -1060,7 +1059,7 @@ out:
return (-1);
}
-void
+static void
usage(void)
{
fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n",
@@ -1073,7 +1072,7 @@ usage(void)
exit(2);
}
-void
+static void
printfs(void)
{
warnx("POSIX.1e ACLs: (-a) %s",