aboutsummaryrefslogtreecommitdiff
path: root/sbin
diff options
context:
space:
mode:
Diffstat (limited to 'sbin')
-rw-r--r--sbin/comcontrol/comcontrol.818
-rw-r--r--sbin/comcontrol/comcontrol.c30
-rw-r--r--sbin/devd/Makefile5
-rw-r--r--sbin/devd/devd.cc48
-rw-r--r--sbin/devd/devd.conf.516
-rw-r--r--sbin/devd/hyperv.conf1
-rw-r--r--sbin/devd/moused.conf11
-rw-r--r--sbin/devd/snd.conf26
-rw-r--r--sbin/dhclient/dhclient.c2
-rw-r--r--sbin/dhclient/packet.c53
-rw-r--r--sbin/fdisk/fdisk.84
-rw-r--r--sbin/fdisk/fdisk.c2
-rw-r--r--sbin/fsck_ffs/setup.c47
-rw-r--r--sbin/geom/Makefile2
-rw-r--r--sbin/geom/core/geom.c241
-rw-r--r--sbin/hastd/nv.c9
-rw-r--r--sbin/hastd/subr.c11
-rw-r--r--sbin/ifconfig/af_inet6.c6
-rw-r--r--sbin/ifconfig/af_nd6.c1
-rw-r--r--sbin/ifconfig/ifbridge.c335
-rw-r--r--sbin/ifconfig/ifconfig.8231
-rw-r--r--sbin/ifconfig/ifconfig.c37
-rw-r--r--sbin/ifconfig/ifconfig.h11
-rw-r--r--sbin/ifconfig/ifgif.c3
-rw-r--r--sbin/ifconfig/tests/inet6.sh30
-rw-r--r--sbin/init/init.c14
-rw-r--r--sbin/ipf/libipf/printdstl_live.c3
-rw-r--r--sbin/ipf/libipf/printdstlist.c2
-rw-r--r--sbin/ipf/libipf/printdstlistdata.c5
-rw-r--r--sbin/ipf/libipf/printhash_live.c8
-rw-r--r--sbin/ipf/libipf/printhashdata.c19
-rw-r--r--sbin/ipf/libipf/printpooldata.c2
-rw-r--r--sbin/ipfw/Makefile3
-rw-r--r--sbin/ipfw/dummynet.c4
-rw-r--r--sbin/ipfw/nptv6.c49
-rw-r--r--sbin/ipfw/tables.c3
-rw-r--r--sbin/ipfw/tests/Makefile1
-rw-r--r--sbin/ipfw/tests/ipfw_test.sh107
-rwxr-xr-xsbin/ipfw/tests/test_add_rule.py25
-rw-r--r--sbin/kldstat/kldstat.c4
-rwxr-xr-xsbin/mdconfig/tests/mdconfig_test.sh15
-rw-r--r--sbin/mksnap_ffs/mksnap_ffs.c2
-rw-r--r--sbin/mount/mount.86
-rw-r--r--sbin/mount_fusefs/Makefile2
-rw-r--r--sbin/mount_nullfs/mount_nullfs.828
-rw-r--r--sbin/nvmecontrol/modules/Makefile5
-rw-r--r--sbin/nvmecontrol/modules/intel/intel.c15
-rw-r--r--sbin/nvmecontrol/modules/micron/Makefile6
-rw-r--r--sbin/nvmecontrol/modules/micron/micron.c129
-rw-r--r--sbin/nvmecontrol/nvmecontrol.87
-rw-r--r--sbin/pfctl/parse.y399
-rw-r--r--sbin/pfctl/pf_print_state.c13
-rw-r--r--sbin/pfctl/pfctl.8118
-rw-r--r--sbin/pfctl/pfctl.c235
-rw-r--r--sbin/pfctl/pfctl.h43
-rw-r--r--sbin/pfctl/pfctl_optimize.c3
-rw-r--r--sbin/pfctl/pfctl_parser.c115
-rw-r--r--sbin/pfctl/pfctl_parser.h19
-rw-r--r--sbin/pfctl/pfctl_radix.c22
-rw-r--r--sbin/pfctl/pfctl_table.c104
-rw-r--r--sbin/pfctl/tests/files/pf0088.in2
-rw-r--r--sbin/pfctl/tests/files/pf0088.ok2
-rw-r--r--sbin/pfctl/tests/files/pf1072.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1072.in1
-rw-r--r--sbin/pfctl/tests/files/pf1073.in1
-rw-r--r--sbin/pfctl/tests/files/pf1073.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1074.fail1
-rw-r--r--sbin/pfctl/tests/files/pf1074.in1
-rw-r--r--sbin/pfctl/tests/files/pf1075.in1
-rw-r--r--sbin/pfctl/tests/files/pf1075.ok1
-rw-r--r--sbin/pfctl/tests/pfctl_test_list.inc4
-rw-r--r--sbin/ping/Makefile2
-rw-r--r--sbin/ping/tests/Makefile3
-rw-r--r--sbin/rcorder/rcorder.c8
-rw-r--r--sbin/reboot/reboot.89
-rw-r--r--sbin/reboot/reboot.c7
-rw-r--r--sbin/recoverdisk/recoverdisk.1265
-rw-r--r--sbin/recoverdisk/recoverdisk.c796
-rw-r--r--sbin/route/route_netlink.c1
-rw-r--r--sbin/routed/Makefile2
-rw-r--r--sbin/routed/Makefile.inc2
-rw-r--r--sbin/savecore/savecore.86
-rwxr-xr-xsbin/swapon/tests/swapon_test.sh60
-rw-r--r--sbin/veriexec/veriexec.82
-rw-r--r--sbin/zfsbootcfg/zfsbootcfg.88
85 files changed, 2781 insertions, 1121 deletions
diff --git a/sbin/comcontrol/comcontrol.8 b/sbin/comcontrol/comcontrol.8
index bee0fdab102b..f51a1f011167 100644
--- a/sbin/comcontrol/comcontrol.8
+++ b/sbin/comcontrol/comcontrol.8
@@ -1,13 +1,17 @@
-.Dd May 15, 1994
+.Dd August 31, 2025
.Dt COMCONTROL 8
.Os
.Sh NAME
.Nm comcontrol
.Nd control a special tty device
+.Sh DEPRECATION NOTICE
+The
+.Nm
+utility is deprecated and will be removed in
+.Fx 16.0 .
.Sh SYNOPSIS
.Nm
.Ar special_device
-.Op dtrwait Ar number
.Op drainwait Ar number
.Sh DESCRIPTION
The
@@ -22,13 +26,6 @@ Only the superuser can change the settings.
.Pp
The following options are available:
.Bl -tag -width indent
-.It Cm dtrwait Ar number
-Set the time to wait after dropping DTR
-to the given number.
-The units are hundredths of a second.
-The default is 300 hundredths, i.e., 3 seconds.
-This option needed mainly to set proper recover time after
-modem reset.
.It Cm drainwait Ar number
Set the time to wait for output drain
to the given number.
@@ -57,7 +54,6 @@ dialout devices
Originally part of cgd's com package patches, version 0.2.1, to
.Bx 386 0.1 .
Once controlled bidirectional capabilities.
-Little is left to control now
-that these capabilities are standard.
+Little is left to control now that these capabilities are standard.
.Sh AUTHORS
.An Christopher G. Demetriou
diff --git a/sbin/comcontrol/comcontrol.c b/sbin/comcontrol/comcontrol.c
index 7a03b3a569cf..d6d24e8acab8 100644
--- a/sbin/comcontrol/comcontrol.c
+++ b/sbin/comcontrol/comcontrol.c
@@ -46,7 +46,7 @@ static void
usage(void)
{
fprintf(stderr,
- "usage: comcontrol <filename> [dtrwait <n>] [drainwait <n>]\n");
+ "usage: comcontrol <filename> [drainwait <n>]\n");
exit(1);
}
@@ -55,8 +55,8 @@ main(int argc, char *argv[])
{
int fd;
int res = 0;
- int print_dtrwait = 1, print_drainwait = 1;
- int dtrwait = -1, drainwait = -1;
+ int print_drainwait = 1;
+ int drainwait = -1;
if (argc < 2)
usage();
@@ -71,13 +71,6 @@ main(int argc, char *argv[])
}
}
if (argc == 2) {
- if (ioctl(fd, TIOCMGDTRWAIT, &dtrwait) < 0) {
- print_dtrwait = 0;
- if (errno != ENOTTY) {
- res = 1;
- warn("TIOCMGDTRWAIT");
- }
- }
if (ioctl(fd, TIOCGDRAINWAIT, &drainwait) < 0) {
print_drainwait = 0;
if (errno != ENOTTY) {
@@ -85,21 +78,12 @@ main(int argc, char *argv[])
warn("TIOCGDRAINWAIT");
}
}
- if (print_dtrwait)
- printf("dtrwait %d ", dtrwait);
if (print_drainwait)
printf("drainwait %d ", drainwait);
printf("\n");
} else {
while (argv[2] != NULL) {
- if (!strcmp(argv[2],"dtrwait")) {
- if (dtrwait >= 0)
- usage();
- if (argv[3] == NULL || !isdigit(argv[3][0]))
- usage();
- dtrwait = atoi(argv[3]);
- argv += 2;
- } else if (!strcmp(argv[2],"drainwait")) {
+ if (!strcmp(argv[2],"drainwait")) {
if (drainwait >= 0)
usage();
if (argv[3] == NULL || !isdigit(argv[3][0]))
@@ -109,12 +93,6 @@ main(int argc, char *argv[])
} else
usage();
}
- if (dtrwait >= 0) {
- if (ioctl(fd, TIOCMSDTRWAIT, &dtrwait) < 0) {
- res = 1;
- warn("TIOCMSDTRWAIT");
- }
- }
if (drainwait >= 0) {
if (ioctl(fd, TIOCSDRAINWAIT, &drainwait) < 0) {
res = 1;
diff --git a/sbin/devd/Makefile b/sbin/devd/Makefile
index 5d5721d16884..553aecf4ee88 100644
--- a/sbin/devd/Makefile
+++ b/sbin/devd/Makefile
@@ -32,6 +32,11 @@ CONSOLEDIR= ${DEVDDIR}
CONSOLE+= moused.conf syscons.conf
CONSOLEPACKAGE= console-tools
+CONFGROUPS+= SND
+SNDDIR= ${DEVDDIR}
+SND= snd.conf
+SNDPACKAGE= sound
+
.if ${MK_BLUETOOTH} != "no"
CONFGROUPS+= BLUETOOTH
BLUETOOTHDIR= ${DEVDDIR}
diff --git a/sbin/devd/devd.cc b/sbin/devd/devd.cc
index d7a3fee57870..ee38fbb2ccee 100644
--- a/sbin/devd/devd.cc
+++ b/sbin/devd/devd.cc
@@ -153,6 +153,8 @@ static volatile sig_atomic_t romeo_must_die = 0;
static const char *configfile = CF;
+static char vm_guest[80];
+
static void devdlog(int priority, const char* message, ...)
__printflike(2, 3);
static void event_loop(void);
@@ -867,6 +869,8 @@ process_event(char *buffer)
cfg.set_variable("timestamp", timestr);
free(timestr);
+ cfg.set_variable("vm_guest", vm_guest);
+
// Match doesn't have a device, and the format is a little
// different, so handle it separately.
switch (type) {
@@ -1107,6 +1111,14 @@ event_loop(void)
err(1, "select");
} else if (rv == 0)
check_clients();
+ /*
+ * Aside from the socket type, both sockets use the same
+ * protocol, so we can process clients the same way.
+ */
+ if (FD_ISSET(stream_fd, &fds))
+ new_client(stream_fd, SOCK_STREAM);
+ if (FD_ISSET(seqpacket_fd, &fds))
+ new_client(seqpacket_fd, SOCK_SEQPACKET);
if (FD_ISSET(fd, &fds)) {
rv = read(fd, buffer, sizeof(buffer) - 1);
if (rv > 0) {
@@ -1135,14 +1147,6 @@ event_loop(void)
break;
}
}
- if (FD_ISSET(stream_fd, &fds))
- new_client(stream_fd, SOCK_STREAM);
- /*
- * Aside from the socket type, both sockets use the same
- * protocol, so we can process clients the same way.
- */
- if (FD_ISSET(seqpacket_fd, &fds))
- new_client(seqpacket_fd, SOCK_SEQPACKET);
}
cfg.remove_pidfile();
close(seqpacket_fd);
@@ -1204,27 +1208,6 @@ new_action(const char *cmd)
eps *
new_match(const char *var, const char *re)
{
- /*
- * In FreeBSD 14, we changed the system=kern to system=kernel for the
- * resume message to match all the other 'kernel' messages. Generate a
- * warning for the life of 14.x that we've 'fixed' the file on the fly,
- * but make it a fatal error in 15.x and newer.
- */
- if (strcmp(var, "kern") == 0) {
-#if __FreeBSD_version < 1500000
- devdlog(LOG_WARNING,
- "Changing deprecated system='kern' to new name 'kernel' in %s line %d.",
- curr_cf, lineno);
- free(const_cast<char *>(var));
- var = strdup("kernel");
-#elif __FreeBSD_version < 1600000
- errx(1, "Encountered deprecated system=\"kern\" rule in %s line %d",
- curr_cf, lineno);
-#else
-#error "Remove this gross hack"
-#endif
- }
-
eps *e = new match(cfg, var, re);
free(const_cast<char *>(var));
free(const_cast<char *>(re));
@@ -1322,6 +1305,7 @@ int
main(int argc, char **argv)
{
int ch;
+ size_t len;
check_devd_enabled();
while ((ch = getopt(argc, argv, "df:l:nq")) != -1) {
@@ -1346,6 +1330,12 @@ main(int argc, char **argv)
}
}
+ len = sizeof(vm_guest);
+ if (sysctlbyname("kern.vm_guest", vm_guest, &len, NULL, 0) < 0) {
+ devdlog(LOG_ERR,
+ "sysctlbyname(kern.vm_guest) failed: %d\n", errno);
+ }
+
cfg.parse();
if (!no_daemon && daemonize_quick) {
cfg.open_pidfile();
diff --git a/sbin/devd/devd.conf.5 b/sbin/devd/devd.conf.5
index baf4b9d3a183..8df3e910e076 100644
--- a/sbin/devd/devd.conf.5
+++ b/sbin/devd/devd.conf.5
@@ -652,6 +652,22 @@ and
for details.
.El
.Pp
+.Bl -column "System" "Subsystem" "1234567" -compact
+.Sy "System" Ta Sy "Subsystem" Ta Sy "Type" Ta Sy "Description"
+.It Li SND Ta Ta Ta
+Events related to the
+.Xr sound 4
+driver.
+.It Li SND Ta Li CONN Ta Li IN Ta
+Connected input device specified in
+.Pa cdev
+variable.
+.It Li SND Ta Li CONN Ta Li OUT Ta
+Connected output device specified in
+.Pa cdev
+variable.
+.El
+.Pp
.\"
.\" End of tables
.\"
diff --git a/sbin/devd/hyperv.conf b/sbin/devd/hyperv.conf
index 13695a0c75b6..70108ac36e54 100644
--- a/sbin/devd/hyperv.conf
+++ b/sbin/devd/hyperv.conf
@@ -103,5 +103,6 @@ notify 10 {
notify 10 {
match "system" "ETHERNET";
match "type" "IFATTACH";
+ match "vm_guest" "hv";
action "/usr/libexec/hyperv/hyperv_vfattach $subsystem 0";
};
diff --git a/sbin/devd/moused.conf b/sbin/devd/moused.conf
index 002edad9a8a9..8821c2bb8375 100644
--- a/sbin/devd/moused.conf
+++ b/sbin/devd/moused.conf
@@ -31,5 +31,14 @@ notify 100 {
match "type" "DESTROY";
match "cdev" "ums[0-9]+";
- action "service moused stop $cdev";
+ action "service moused quietstop $cdev";
+};
+
+notify 100 {
+ match "system" "DEVFS";
+ match "subsystem" "CDEV";
+ match "type" "CREATE";
+ match "cdev" "input/event[0-9]+";
+
+ action "service moused quietstart $cdev";
};
diff --git a/sbin/devd/snd.conf b/sbin/devd/snd.conf
new file mode 100644
index 000000000000..a45f427f6c79
--- /dev/null
+++ b/sbin/devd/snd.conf
@@ -0,0 +1,26 @@
+# Audio redirection
+notify 0 {
+ match "system" "SND";
+ match "subsystem" "CONN";
+ match "type" "IN";
+ match "cdev" "dsp[0-9]+";
+
+ # Other audio servers or device switching commands can be used here
+ # instead of virtual_oss(8).
+ #
+ # FIXME: We are hardcoding /dev/vdsp.ctl here, simply because it is a
+ # common virtual_oss control device name. Until we find a proper way to
+ # define control devices here, /dev/vdsp.ctl can be changed to the
+ # control device of choice.
+ action "/usr/sbin/virtual_oss_cmd /dev/vdsp.ctl -R /dev/$cdev";
+};
+
+notify 0 {
+ match "system" "SND";
+ match "subsystem" "CONN";
+ match "type" "OUT";
+ match "cdev" "dsp[0-9]+";
+
+ # See comment above.
+ action "/usr/sbin/virtual_oss_cmd /dev/vdsp.ctl -P /dev/$cdev";
+};
diff --git a/sbin/dhclient/dhclient.c b/sbin/dhclient/dhclient.c
index cbab3fa2973c..5d2a7453578b 100644
--- a/sbin/dhclient/dhclient.c
+++ b/sbin/dhclient/dhclient.c
@@ -539,7 +539,7 @@ main(int argc, char *argv[])
setproctitle("%s", ifi->name);
/* setgroups(2) is not permitted in capability mode. */
- if (setgroups(1, &pw->pw_gid) != 0)
+ if (setgroups(0, NULL) != 0)
error("can't restrict groups: %m");
if (caph_enter_casper() < 0)
diff --git a/sbin/dhclient/packet.c b/sbin/dhclient/packet.c
index 3d7390c06ee0..fc0305a8cb0c 100644
--- a/sbin/dhclient/packet.c
+++ b/sbin/dhclient/packet.c
@@ -135,11 +135,14 @@ assemble_udp_ip_header(unsigned char *buf, int *bufix, u_int32_t from,
udp.uh_ulen = htons(sizeof(udp) + len);
memset(&udp.uh_sum, 0, sizeof(udp.uh_sum));
- udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp),
- checksum(data, len, checksum((unsigned char *)&ip.ip_src,
+ udp.uh_sum = wrapsum(checksum(data, len, checksum((unsigned char *)&udp,
+ sizeof(udp), checksum((unsigned char *)&ip.ip_src,
2 * sizeof(ip.ip_src),
IPPROTO_UDP + (u_int32_t)ntohs(udp.uh_ulen)))));
+ if (udp.uh_sum == htons(0))
+ udp.uh_sum = htons(0xffff);
+
memcpy(&buf[*bufix], &udp, sizeof(udp));
*bufix += sizeof(udp);
}
@@ -166,7 +169,7 @@ decode_udp_ip_header(unsigned char *buf, int bufix, struct sockaddr_in *from,
struct ip *ip;
struct udphdr *udp;
u_int32_t ip_len = (buf[bufix] & 0xf) << 2;
- u_int32_t sum, usum;
+ u_int32_t sum, usum, pseudo_sum;
static int ip_packets_seen;
static int ip_packets_bad_checksum;
static int udp_packets_seen;
@@ -224,23 +227,37 @@ decode_udp_ip_header(unsigned char *buf, int bufix, struct sockaddr_in *from,
}
usum = udp->uh_sum;
- udp->uh_sum = 0;
-
- sum = wrapsum(checksum((unsigned char *)udp, sizeof(*udp),
- checksum(data, len, checksum((unsigned char *)&ip->ip_src,
- 2 * sizeof(ip->ip_src),
- IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen)))));
-
udp_packets_seen++;
- if (usum && usum != sum) {
- udp_packets_bad_checksum++;
- if (udp_packets_seen > 4 && udp_packets_bad_checksum != 0 &&
- (udp_packets_seen / udp_packets_bad_checksum) < 2) {
- note("%d bad udp checksums in %d packets",
- udp_packets_bad_checksum, udp_packets_seen);
- udp_packets_seen = udp_packets_bad_checksum = 0;
+
+ if (usum != htons(0)) {
+ udp->uh_sum = 0;
+
+ pseudo_sum = checksum((unsigned char *)&ip->ip_src,
+ 2 * sizeof(ip->ip_src),
+ IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen));
+ sum = wrapsum(checksum(data, len,
+ checksum((unsigned char *)udp, sizeof(*udp), pseudo_sum)));
+ if (sum == htons(0))
+ sum = htons(0xffff);
+
+ /*
+ * In addition to accepting UDP packets with the correct
+ * checksum in the checksum field, accept also the ones which
+ * have the correct pseudo header checksum in the checksum
+ * field. This allows to process UDP packets, which have been
+ * marked for transmit checksum offloading by the sender side.
+ */
+ if (usum != sum && usum != htons(pseudo_sum & 0x0000ffff)) {
+ udp_packets_bad_checksum++;
+ if (udp_packets_seen > 4 &&
+ udp_packets_bad_checksum != 0 &&
+ (udp_packets_seen / udp_packets_bad_checksum) < 2) {
+ note("%d bad udp checksums in %d packets",
+ udp_packets_bad_checksum, udp_packets_seen);
+ udp_packets_seen = udp_packets_bad_checksum = 0;
+ }
+ return (-1);
}
- return (-1);
}
memcpy(&from->sin_port, &udp->uh_sport, sizeof(udp->uh_sport));
diff --git a/sbin/fdisk/fdisk.8 b/sbin/fdisk/fdisk.8
index 09933acf57df..f26e6bb67d6e 100644
--- a/sbin/fdisk/fdisk.8
+++ b/sbin/fdisk/fdisk.8
@@ -1,5 +1,5 @@
.\"
-.Dd October 5, 2016
+.Dd October 24, 2025
.Dt FDISK 8
.Os
.Sh NAME
@@ -18,7 +18,7 @@
.Sh DEPRECATION NOTICE
.Nm
is deprecated and is not available in
-.Fx 15.0
+.Fx 16.0
or later.
Use
.Xr gpart 8
diff --git a/sbin/fdisk/fdisk.c b/sbin/fdisk/fdisk.c
index 925cc68932d7..28230191bb16 100644
--- a/sbin/fdisk/fdisk.c
+++ b/sbin/fdisk/fdisk.c
@@ -266,7 +266,7 @@ main(int argc, char *argv[])
struct dos_partition *partp;
fprintf(stderr,
- "WARNING: fdisk is deprecated and is not available in FreeBSD 15 or later.\n"
+ "WARNING: fdisk is deprecated and is not available in FreeBSD 16 or later.\n"
"Please use gpart instead.\n\n");
while ((c = getopt(argc, argv, "BIab:f:ipqstuv1234")) != -1)
diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c
index f10f02d159c3..41b4a5336350 100644
--- a/sbin/fsck_ffs/setup.c
+++ b/sbin/fsck_ffs/setup.c
@@ -58,7 +58,6 @@ char *copybuf; /* buffer to copy snapshot blocks */
static int sbhashfailed;
#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
-static int calcsb(char *dev, int devfd, struct fs *fs);
static void saverecovery(int readfd, int writefd);
static int chkrecovery(int devfd);
static int getlbnblkno(struct inodesc *);
@@ -501,52 +500,6 @@ sblock_init(void)
}
/*
- * Calculate a prototype superblock based on information in the boot area.
- * When done the cgsblock macro can be calculated and the fs_ncg field
- * can be used. Do NOT attempt to use other macros without verifying that
- * their needed information is available!
- */
-static int
-calcsb(char *dev, int devfd, struct fs *fs)
-{
- struct fsrecovery *fsr;
- char *fsrbuf;
- u_int secsize;
-
- /*
- * We need fragments-per-group and the partition-size.
- *
- * Newfs stores these details at the end of the boot block area
- * at the start of the filesystem partition. If they have been
- * overwritten by a boot block, we fail. But usually they are
- * there and we can use them.
- */
- if (ioctl(devfd, DIOCGSECTORSIZE, &secsize) == -1)
- return (0);
- fsrbuf = Balloc(secsize);
- if (fsrbuf == NULL)
- errx(EEXIT, "calcsb: cannot allocate recovery buffer");
- if (blread(devfd, fsrbuf,
- (SBLOCK_UFS2 - secsize) / dev_bsize, secsize) != 0) {
- free(fsrbuf);
- return (0);
- }
- fsr = (struct fsrecovery *)&fsrbuf[secsize - sizeof *fsr];
- if (fsr->fsr_magic != FS_UFS2_MAGIC) {
- free(fsrbuf);
- return (0);
- }
- memset(fs, 0, sizeof(struct fs));
- fs->fs_fpg = fsr->fsr_fpg;
- fs->fs_fsbtodb = fsr->fsr_fsbtodb;
- fs->fs_sblkno = fsr->fsr_sblkno;
- fs->fs_magic = fsr->fsr_magic;
- fs->fs_ncg = fsr->fsr_ncg;
- free(fsrbuf);
- return (1);
-}
-
-/*
* Check to see if recovery information exists.
* Return 1 if it exists or cannot be created.
* Return 0 if it does not exist and can be created.
diff --git a/sbin/geom/Makefile b/sbin/geom/Makefile
index 078503d3ae67..61561ef1ff1b 100644
--- a/sbin/geom/Makefile
+++ b/sbin/geom/Makefile
@@ -9,7 +9,7 @@ MAN= geom.8
CFLAGS+= -I${.CURDIR} -I${.CURDIR}/core
CFLAGS+= -DGEOM_CLASS_DIR=\"${GEOM_CLASS_DIR}\"
-LIBADD= geom util
+LIBADD= geom util xo
.if defined(RESCUE)
.PATH: ${SRCTOP}/lib/geom/part \
diff --git a/sbin/geom/core/geom.c b/sbin/geom/core/geom.c
index 950f6790b1a8..2de696ce6a43 100644
--- a/sbin/geom/core/geom.c
+++ b/sbin/geom/core/geom.c
@@ -49,9 +49,12 @@
#include <assert.h>
#include <libgeom.h>
#include <geom.h>
+#include <libxo/xo.h>
#include "misc/subr.h"
+#define GEOM_XO_VERSION "1"
+
#ifdef STATIC_GEOM_CLASSES
extern uint32_t gpart_version;
extern struct g_command gpart_class_commands[];
@@ -249,7 +252,7 @@ static void
set_option(struct gctl_req *req, struct g_option *opt, const char *val)
{
const char *optname;
- uint64_t number;
+ int64_t number;
void *ptr;
if (G_OPT_ISMULTI(opt)) {
@@ -513,6 +516,7 @@ run_command(int argc, char *argv[])
gctl_free(req);
if (verbose)
printf("Done.\n");
+ xo_finish();
exit(EXIT_SUCCESS);
}
@@ -810,6 +814,10 @@ main(int argc, char *argv[])
provider_name = NULL;
tflag = false;
+ argc = xo_parse_args(argc, argv);
+ if (argc < 0)
+ return (argc);
+
if (strcmp(getprogname(), "geom") == 0) {
while ((ch = getopt(argc, argv, "hp:t")) != -1) {
switch (ch) {
@@ -831,6 +839,7 @@ main(int argc, char *argv[])
* Don't adjust argc and argv, it would break get_class().
*/
}
+ xo_set_version(GEOM_XO_VERSION);
if (tflag && provider_name != NULL) {
errx(EXIT_FAILURE,
@@ -839,6 +848,7 @@ main(int argc, char *argv[])
if (provider_name != NULL) {
list_one_geom_by_provider(provider_name);
+ xo_finish();
return (0);
}
@@ -882,29 +892,33 @@ find_geom(struct gclass *classp, const char *name)
}
static void
-list_one_provider(struct gprovider *pp, const char *prefix)
+list_one_provider(struct gprovider *pp, const char *padding)
{
struct gconfig *conf;
char buf[5];
- printf("Name: %s\n", pp->lg_name);
+ xo_emit("{Lcw:Name}{:Name}\n", pp->lg_name);
humanize_number(buf, sizeof(buf), (int64_t)pp->lg_mediasize, "",
HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
- printf("%sMediasize: %jd (%s)\n", prefix, (intmax_t)pp->lg_mediasize,
- buf);
- printf("%sSectorsize: %u\n", prefix, pp->lg_sectorsize);
+ xo_emit("{P:/%s}{Lcw:Mediasize}{:Mediasize/%jd} ({N:/%s})\n",
+ padding, (intmax_t)pp->lg_mediasize, buf);
+ xo_emit("{P:/%s}{Lcw:Sectorsize}{:Sectorsize/%u} \n",
+ padding, pp->lg_sectorsize);
if (pp->lg_stripesize > 0 || pp->lg_stripeoffset > 0) {
- printf("%sStripesize: %ju\n", prefix, pp->lg_stripesize);
- printf("%sStripeoffset: %ju\n", prefix, pp->lg_stripeoffset);
+ xo_emit("{P:/%s}{Lcw:Stripesize}{Stripesize/%ju}\n",
+ padding, pp->lg_stripesize);
+ xo_emit("{P:/%s}{Lcw:Stripeoffset}{Stripeoffset/%ju}\n",
+ padding, pp->lg_stripeoffset);
}
- printf("%sMode: %s\n", prefix, pp->lg_mode);
+ xo_emit("{P:/%s}{Lcw:Mode}{Mode}\n", padding, pp->lg_mode);
LIST_FOREACH(conf, &pp->lg_config, lg_config) {
- printf("%s%s: %s\n", prefix, conf->lg_name, conf->lg_val);
+ xo_emit("{P:/%s}{Lcwa:}{a:}\n", padding, conf->lg_name,
+ conf->lg_name, conf->lg_val);
}
}
static void
-list_one_consumer(struct gconsumer *cp, const char *prefix)
+list_one_consumer(struct gconsumer *cp, const char *padding)
{
struct gprovider *pp;
struct gconfig *conf;
@@ -915,20 +929,24 @@ list_one_consumer(struct gconsumer *cp, const char *prefix)
else {
char buf[5];
- printf("Name: %s\n", pp->lg_name);
+ xo_emit("{Lcw:Name}{:Name}\n", pp->lg_name);
humanize_number(buf, sizeof(buf), (int64_t)pp->lg_mediasize, "",
HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
- printf("%sMediasize: %jd (%s)\n", prefix,
- (intmax_t)pp->lg_mediasize, buf);
- printf("%sSectorsize: %u\n", prefix, pp->lg_sectorsize);
+ xo_emit("{P:/%s}{Lcw:Mediasize}{:Mediasize/%jd} ({N:/%s})\n",
+ padding, (intmax_t)pp->lg_mediasize, buf);
+ xo_emit("{P:/%s}{Lcw:Sectorsize}{:Sectorsize/%u}\n",
+ padding, pp->lg_sectorsize);
if (pp->lg_stripesize > 0 || pp->lg_stripeoffset > 0) {
- printf("%sStripesize: %ju\n", prefix, pp->lg_stripesize);
- printf("%sStripeoffset: %ju\n", prefix, pp->lg_stripeoffset);
+ xo_emit("{P:/%s}{Lcw:Stripesize}{:Stripesize/%ju}\n",
+ padding, pp->lg_stripesize);
+ xo_emit("{P:/%s}{Lcw:Stripeoffset}{:Stripesize/%ju}\n",
+ padding, pp->lg_stripeoffset);
}
- printf("%sMode: %s\n", prefix, cp->lg_mode);
+ xo_emit("{P:/%s}{Lcw:Mode}{:Mode}\n", padding, pp->lg_mode);
}
LIST_FOREACH(conf, &cp->lg_config, lg_config) {
- printf("%s%s: %s\n", prefix, conf->lg_name, conf->lg_val);
+ xo_emit("{P:/%s}{Lcwa:}{a:}\n", padding, conf->lg_name,
+ conf->lg_name, conf->lg_val);
}
}
@@ -940,27 +958,36 @@ list_one_geom(struct ggeom *gp)
struct gconfig *conf;
unsigned n;
- printf("Geom name: %s\n", gp->lg_name);
+ xo_emit("{Lcw:Geom name}{:Name}\n", gp->lg_name);
LIST_FOREACH(conf, &gp->lg_config, lg_config) {
- printf("%s: %s\n", conf->lg_name, conf->lg_val);
+ xo_emit("{Lcwa:}{a:}\n", conf->lg_name, conf->lg_name,
+ conf->lg_val);
}
if (!LIST_EMPTY(&gp->lg_provider)) {
- printf("Providers:\n");
+ xo_open_list("Providers");
+ xo_emit("{Tc:Providers}\n");
n = 1;
LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
- printf("%u. ", n++);
+ xo_emit("{T:/%u}. ", n++);
+ xo_open_instance("provider");
list_one_provider(pp, " ");
+ xo_close_instance("provider");
}
+ xo_close_list("Providers");
}
if (!LIST_EMPTY(&gp->lg_consumer)) {
- printf("Consumers:\n");
+ xo_open_list("Consumers");
+ xo_emit("{Tc:Consumers}\n");
n = 1;
LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
- printf("%u. ", n++);
+ xo_emit("{T:/%u}. ", n++);
+ xo_open_instance("consumer");
list_one_consumer(cp, " ");
+ xo_close_instance("consumer");
}
+ xo_close_list("Consumers");
}
- printf("\n");
+ xo_emit("\n");
}
static void
@@ -978,8 +1005,10 @@ list_one_geom_by_provider(const char *provider_name)
if (gp == NULL)
errx(EXIT_FAILURE, "Cannot find provider '%s'.", provider_name);
- printf("Geom class: %s\n", gp->lg_class->lg_name);
+ xo_open_container("Geom");
+ xo_emit("{Lwc:Geom class}{:Class}\n", gp->lg_class->lg_name);
list_one_geom(gp);
+ xo_close_container("Geom");
}
static void
@@ -1038,14 +1067,20 @@ std_list(struct gctl_req *req, unsigned flags __unused)
"an instance named '%s'.",
gclass_name, name);
}
+ xo_open_container("Geom");
list_one_geom(gp);
+ xo_close_container("Geom");
}
} else {
+ xo_open_list("Geoms");
LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
if (LIST_EMPTY(&gp->lg_provider) && !all)
continue;
+ xo_open_instance("geom");
list_one_geom(gp);
+ xo_close_instance("geom");
}
+ xo_close_list("Geoms");
}
geom_deletetree(&mesh);
}
@@ -1115,34 +1150,24 @@ status_update_len_prs(struct ggeom *gp, int *name_len, int *status_len)
}
static char *
-status_one_consumer(struct gconsumer *cp)
+status_one_consumer(struct gconsumer *cp, const char *value)
{
- static char buf[256];
struct gprovider *pp;
struct gconfig *conf;
- const char *state, *syncr;
+ char *ret;
pp = cp->lg_provider;
if (pp == NULL)
return (NULL);
- state = NULL;
- syncr = NULL;
+ ret = NULL;
LIST_FOREACH(conf, &cp->lg_config, lg_config) {
- if (strcasecmp(conf->lg_name, "state") == 0)
- state = conf->lg_val;
- if (strcasecmp(conf->lg_name, "synchronized") == 0)
- syncr = conf->lg_val;
- }
- if (state == NULL && syncr == NULL)
- snprintf(buf, sizeof(buf), "%s", pp->lg_name);
- else if (state != NULL && syncr != NULL) {
- snprintf(buf, sizeof(buf), "%s (%s, %s)", pp->lg_name,
- state, syncr);
- } else {
- snprintf(buf, sizeof(buf), "%s (%s)", pp->lg_name,
- state ? state : syncr);
+ if (strcasecmp(conf->lg_name, value) == 0)
+ ret = conf->lg_val;
}
- return (buf);
+
+ if (ret == NULL)
+ return (NULL);
+ return (ret);
}
static void
@@ -1150,8 +1175,8 @@ status_one_geom(struct ggeom *gp, int script, int name_len, int status_len)
{
struct gconsumer *cp;
struct gconfig *conf;
- const char *name, *status, *component;
- int gotone;
+ const char *name, *status, *cstate, *csyncr;
+ int gotone, len;
name = gp->lg_name;
status = "N/A";
@@ -1161,21 +1186,53 @@ status_one_geom(struct ggeom *gp, int script, int name_len, int status_len)
break;
}
}
- gotone = 0;
+ gotone = len = 0;
+ xo_open_instance("status");
LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
- component = status_one_consumer(cp);
- if (component == NULL)
+ if (cp->lg_provider == NULL)
continue;
+
+ cstate = status_one_consumer(cp, "state");
+ csyncr = status_one_consumer(cp, "synchronized");
+ if (!gotone || script) {
+ if (!gotone) {
+ xo_emit("{:name/%*s} {:status/%*s} ",
+ name_len, name, status_len, status);
+ } else {
+ xo_emit("{d:name/%*s} {d:status/%*s} ",
+ name_len, name, status_len, status);
+ }
+ xo_open_list("components");
+ }
+
+ xo_open_instance("components");
+ if (cstate != NULL && csyncr != NULL) {
+ xo_emit("{P:/%*s}{:component} ({:state}, {:synchronized})\n",
+ len, "", cp->lg_provider->lg_name, cstate, csyncr);
+ } else if (cstate != NULL) {
+ xo_emit("{P:/%*s}{:component} ({:state})\n",
+ len, "", cp->lg_provider->lg_name, cstate);
+ } else if (csyncr != NULL) {
+ xo_emit("{P:/%*s}{:component} ({:synchronized})\n",
+ len, "", cp->lg_provider->lg_name, csyncr);
+ } else {
+ xo_emit("{P:/%*s}{:component}\n",
+ len, "", cp->lg_provider->lg_name);
+ }
+ xo_close_instance("components");
gotone = 1;
- printf("%*s %*s %s\n", name_len, name, status_len, status,
- component);
- if (!script)
- name = status = "";
+ if (!len && !script)
+ len = name_len + status_len + 4;
}
if (!gotone) {
- printf("%*s %*s %s\n", name_len, name, status_len, status,
- "N/A");
+ xo_emit("{:name/%*s} {:status/%*s} ", name_len, name, status_len, status);
+ xo_open_list("components");
+ xo_open_instance("components");
+ xo_emit("{P:/%*s}{d:component}\n", len, "", "N/A");
+ xo_close_instance("components");
}
+ xo_close_list("components");
+ xo_close_instance("status");
}
static void
@@ -1184,9 +1241,10 @@ status_one_geom_prs(struct ggeom *gp, int script, int name_len, int status_len)
struct gprovider *pp;
struct gconsumer *cp;
struct gconfig *conf;
- const char *name, *status, *component;
- int gotone;
+ const char *name, *status, *cstate, *csyncr;
+ int gotone, len;
+ xo_open_instance("status");
LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
name = pp->lg_name;
status = "N/A";
@@ -1202,22 +1260,53 @@ status_one_geom_prs(struct ggeom *gp, int script, int name_len, int status_len)
break;
}
}
- gotone = 0;
+ gotone = len = 0;
LIST_FOREACH(cp, &gp->lg_consumer, lg_consumer) {
- component = status_one_consumer(cp);
- if (component == NULL)
+ if (cp->lg_provider == NULL)
continue;
+
+ cstate = status_one_consumer(cp, "state");
+ csyncr = status_one_consumer(cp, "synchronized");
+ if (!gotone || script) {
+ if (!gotone) {
+ xo_emit("{:name/%*s} {:status/%*s} ",
+ name_len, name, status_len, status);
+ } else {
+ xo_emit("{d:name/%*s} {d:status/%*s} ",
+ name_len, name, status_len, status);
+ }
+ xo_open_list("components");
+ }
+
+ xo_open_instance("component");
+ if (cstate != NULL && csyncr != NULL) {
+ xo_emit("{P:/%*s}{:component} ({:state}, {:synchronized})\n",
+ len, "", cp->lg_provider->lg_name, cstate, csyncr);
+ } else if (cstate != NULL) {
+ xo_emit("{P:/%*s}{:component} ({:state})\n",
+ len, "", cp->lg_provider->lg_name, cstate);
+ } else if (csyncr != NULL) {
+ xo_emit("{P:/%*s}{:component} ({:synchronized})\n",
+ len, "", cp->lg_provider->lg_name, csyncr);
+ } else {
+ xo_emit("{P:/%*s}{:component}\n",
+ len, "", cp->lg_provider->lg_name);
+ }
+ xo_close_instance("component");
gotone = 1;
- printf("%*s %*s %s\n", name_len, name,
- status_len, status, component);
- if (!script)
- name = status = "";
+ if (!len && !script)
+ len = name_len + status_len + 4;
}
if (!gotone) {
- printf("%*s %*s %s\n", name_len, name,
- status_len, status, "N/A");
+ xo_emit("{:name/%*s} {:status/%*s} ", name_len, name, status_len, status);
+ xo_open_list("components");
+ xo_open_instance("components");
+ xo_emit("{P:/%*s}{d:component}\n", len, "", "N/A");
+ xo_close_instance("components");
}
+ xo_close_list("components");
}
+ xo_close_instance("status");
}
static void
@@ -1240,13 +1329,9 @@ std_status(struct gctl_req *req, unsigned flags __unused)
all = gctl_get_int(req, "all");
geoms = gctl_get_int(req, "geoms");
script = gctl_get_int(req, "script");
- if (script) {
- name_len = 0;
- status_len = 0;
- } else {
- name_len = strlen("Name");
- status_len = strlen("Status");
- }
+ name_len = strlen("Name");
+ status_len = strlen("Status");
+
if (nargs > 0) {
for (i = 0, n = 0; i < nargs; i++) {
name = gctl_get_ascii(req, "arg%d", i);
@@ -1282,9 +1367,10 @@ std_status(struct gctl_req *req, unsigned flags __unused)
goto end;
}
if (!script) {
- printf("%*s %*s %s\n", name_len, "Name", status_len, "Status",
- "Components");
+ xo_emit("{T:/%*s} {T:/%*s} {T:Components}\n",
+ name_len, "Name", status_len, "Status");
}
+ xo_open_list("status");
if (nargs > 0) {
for (i = 0; i < nargs; i++) {
name = gctl_get_ascii(req, "arg%d", i);
@@ -1312,6 +1398,7 @@ std_status(struct gctl_req *req, unsigned flags __unused)
}
}
}
+ xo_close_list("status");
end:
geom_deletetree(&mesh);
}
diff --git a/sbin/hastd/nv.c b/sbin/hastd/nv.c
index 0730e4f2a794..16ab95cf0dc6 100644
--- a/sbin/hastd/nv.c
+++ b/sbin/hastd/nv.c
@@ -97,7 +97,7 @@ struct nvhdr {
} __packed;
#define NVH_DATA(nvh) ((unsigned char *)nvh + NVH_HSIZE(nvh))
#define NVH_HSIZE(nvh) \
- (sizeof(struct nvhdr) + roundup2((nvh)->nvh_namesize, 8))
+ (sizeof(struct nvhdr) + roundup2((size_t)(nvh)->nvh_namesize, 8))
#define NVH_DSIZE(nvh) \
(((nvh)->nvh_type & NV_ORDER_MASK) == NV_ORDER_HOST ? \
(nvh)->nvh_dsize : \
@@ -247,11 +247,8 @@ nv_validate(struct nv *nv, size_t *extrap)
break;
}
dsize = NVH_DSIZE(nvh);
- if (dsize == 0) {
- error = EINVAL;
- break;
- }
- if (size < NVH_SIZE(nvh)) {
+ if (roundup2(dsize, 8) == 0 ||
+ roundup2(dsize, 8) > size - NVH_HSIZE(nvh)) {
error = EINVAL;
break;
}
diff --git a/sbin/hastd/subr.c b/sbin/hastd/subr.c
index 2a26482b3727..084ea50dae7c 100644
--- a/sbin/hastd/subr.c
+++ b/sbin/hastd/subr.c
@@ -156,7 +156,6 @@ drop_privs(const struct hast_resource *res)
struct passwd *pw;
uid_t ruid, euid, suid;
gid_t rgid, egid, sgid;
- gid_t gidset[1];
bool capsicum, jailed;
/*
@@ -207,10 +206,8 @@ drop_privs(const struct hast_resource *res)
}
}
PJDLOG_VERIFY(chdir("/") == 0);
- gidset[0] = pw->pw_gid;
- if (setgroups(1, gidset) == -1) {
- pjdlog_errno(LOG_ERR, "Unable to set groups to gid %u",
- (unsigned int)pw->pw_gid);
+ if (setgroups(0, NULL) == -1) {
+ pjdlog_errno(LOG_ERR, "Unable to drop supplementary groups");
return (-1);
}
if (setgid(pw->pw_gid) == -1) {
@@ -286,9 +283,7 @@ drop_privs(const struct hast_resource *res)
PJDLOG_VERIFY(rgid == pw->pw_gid);
PJDLOG_VERIFY(egid == pw->pw_gid);
PJDLOG_VERIFY(sgid == pw->pw_gid);
- PJDLOG_VERIFY(getgroups(0, NULL) == 1);
- PJDLOG_VERIFY(getgroups(1, gidset) == 1);
- PJDLOG_VERIFY(gidset[0] == pw->pw_gid);
+ PJDLOG_VERIFY(getgroups(0, NULL) == 0);
pjdlog_debug(1,
"Privileges successfully dropped using %s%s+setgid+setuid.",
diff --git a/sbin/ifconfig/af_inet6.c b/sbin/ifconfig/af_inet6.c
index 17dc068ee875..9386f5eaf513 100644
--- a/sbin/ifconfig/af_inet6.c
+++ b/sbin/ifconfig/af_inet6.c
@@ -726,6 +726,8 @@ static struct cmd inet6_cmds[] = {
DEF_CMD_ARG("pltime", setip6pltime),
DEF_CMD_ARG("vltime", setip6vltime),
DEF_CMD("eui64", 0, setip6eui64),
+ DEF_CMD("stableaddr", ND6_IFF_STABLEADDR, setnd6flags),
+ DEF_CMD("-stableaddr", -ND6_IFF_STABLEADDR, setnd6flags),
#ifdef EXPERIMENTAL
DEF_CMD("ipv6_only", ND6_IFF_IPV6_ONLY_MANUAL,setnd6flags),
DEF_CMD("-ipv6_only", -ND6_IFF_IPV6_ONLY_MANUAL,setnd6flags),
@@ -753,13 +755,13 @@ static struct afswtch af_inet6 = {
#ifdef WITHOUT_NETLINK
.af_difaddr = SIOCDIFADDR_IN6,
.af_aifaddr = SIOCAIFADDR_IN6,
- .af_ridreq = &in6_addreq,
+ .af_ridreq = &in6_ridreq,
.af_addreq = &in6_addreq,
.af_exec = af_exec_ioctl,
#else
.af_difaddr = NL_RTM_DELADDR,
.af_aifaddr = NL_RTM_NEWADDR,
- .af_ridreq = &in6_add,
+ .af_ridreq = &in6_del,
.af_addreq = &in6_add,
.af_exec = in6_exec_nl,
#endif
diff --git a/sbin/ifconfig/af_nd6.c b/sbin/ifconfig/af_nd6.c
index 2899ad6a0778..fb7e72028e2e 100644
--- a/sbin/ifconfig/af_nd6.c
+++ b/sbin/ifconfig/af_nd6.c
@@ -66,6 +66,7 @@ static const char *ND6BITS[] = {
[9] = "IPV6_ONLY",
[10] = "IPV6_ONLY_MANUAL",
#endif
+ [11] = "STABLEADDR",
[15] = "DEFAULTIF",
};
diff --git a/sbin/ifconfig/ifbridge.c b/sbin/ifconfig/ifbridge.c
index ce5d2f4894fa..eff443447c13 100644
--- a/sbin/ifconfig/ifbridge.c
+++ b/sbin/ifconfig/ifbridge.c
@@ -60,6 +60,10 @@
#include "ifconfig.h"
+static int parse_vlans(ifbvlan_set_t *set, const char *str);
+static int get_val(const char *cp, u_long *valp);
+static int get_vlan_id(const char *cp, ether_vlanid_t *valp);
+
static const char *stpstates[] = { STP_STATES };
static const char *stpproto[] = { STP_PROTOS };
static const char *stproles[] = { STP_ROLES };
@@ -80,6 +84,20 @@ get_val(const char *cp, u_long *valp)
}
static int
+get_vlan_id(const char *cp, ether_vlanid_t *valp)
+{
+ u_long val;
+
+ if (get_val(cp, &val) == -1)
+ return (-1);
+ if (val < DOT1Q_VID_MIN || val > DOT1Q_VID_MAX)
+ return (-1);
+
+ *valp = (ether_vlanid_t)val;
+ return (0);
+}
+
+static int
do_cmd(if_ctx *ctx, u_long op, void *arg, size_t argsize, int set)
{
struct ifdrv ifd = {};
@@ -176,6 +194,21 @@ print_vlans(ifbvlan_set_t *vlans)
}
}
+static char const *
+vlan_proto_name(uint16_t proto)
+{
+ switch (proto) {
+ case 0:
+ return "none";
+ case ETHERTYPE_VLAN:
+ return "802.1q";
+ case ETHERTYPE_QINQ:
+ return "802.1ad";
+ default:
+ return "unknown";
+ }
+}
+
static void
bridge_status(if_ctx *ctx)
{
@@ -209,6 +242,11 @@ bridge_status(if_ctx *ctx)
params->ifbop_root_path_cost,
params->ifbop_root_port & 0xfff);
+ printb("\tbridge flags", bridge->flags, IFBRFBITS);
+ if (bridge->defpvid)
+ printf(" defuntagged=%u", (unsigned) bridge->defpvid);
+ printf("\n");
+
prefix = "\tmember: ";
pad = "\t ";
for (size_t i = 0; i < bridge->members_count; ++i) {
@@ -217,8 +255,9 @@ bridge_status(if_ctx *ctx)
printf("%s%s ", prefix, member->ifbr_ifsname);
printb("flags", member->ifbr_ifsflags, IFBIFBITS);
printf("\n%s", pad);
- printf("ifmaxaddr %u port %u priority %u path cost %u",
- member->ifbr_addrmax,
+ if (member->ifbr_addrmax != 0)
+ printf("ifmaxaddr %u ", member->ifbr_addrmax);
+ printf("port %u priority %u path cost %u",
member->ifbr_portno,
member->ifbr_priority,
member->ifbr_path_cost);
@@ -241,8 +280,11 @@ bridge_status(if_ctx *ctx)
else
printf(" <unknown state %d>", state);
}
- if (member->ifbr_untagged != 0)
- printf(" untagged %u", (unsigned)member->ifbr_untagged);
+ if (member->ifbr_vlanproto != 0)
+ printf(" vlan protocol %s",
+ vlan_proto_name(member->ifbr_vlanproto));
+ if (member->ifbr_pvid != 0)
+ printf(" untagged %u", (unsigned)member->ifbr_pvid);
print_vlans(&bridge->member_vlans[i]);
printf("\n");
}
@@ -250,15 +292,62 @@ bridge_status(if_ctx *ctx)
ifconfig_bridge_free_bridge_status(bridge);
}
-static void
-setbridge_add(if_ctx *ctx, const char *val, int dummy __unused)
+static int
+setbridge_add(if_ctx *ctx, int argc, const char *const *argv)
{
struct ifbreq req;
+ struct ifbif_vlan_req vlreq;
+ int oargc = argc;
memset(&req, 0, sizeof(req));
- strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname));
+ memset(&vlreq, 0, sizeof(vlreq));
+
+ if (argc < 1)
+ errx(1, "usage: addm <interface> [opts ...]");
+
+ strlcpy(req.ifbr_ifsname, argv[0], sizeof(req.ifbr_ifsname));
+ --argc; ++argv;
+
+ while (argc) {
+ if (strcmp(argv[0], "untagged") == 0) {
+ if (argc < 2)
+ errx(1, "usage: untagged <vlan id>");
+
+ if (get_vlan_id(argv[1], &req.ifbr_pvid) < 0)
+ errx(1, "invalid VLAN identifier: %s", argv[1]);
+
+ argc -= 2;
+ argv += 2;
+ } else if (strcmp(argv[0], "tagged") == 0) {
+ if (argc < 2)
+ errx(1, "usage: tagged <vlan set>");
+
+ vlreq.bv_op = BRDG_VLAN_OP_SET;
+ strlcpy(vlreq.bv_ifname, req.ifbr_ifsname,
+ sizeof(vlreq.bv_ifname));
+ if (parse_vlans(&vlreq.bv_set, argv[1]) != 0)
+ errx(1, "invalid vlan set: %s", argv[1]);
+
+ argc -= 2;
+ argv += 2;
+ } else {
+ break;
+ }
+ }
+
if (do_cmd(ctx, BRDGADD, &req, sizeof(req), 1) < 0)
- err(1, "BRDGADD %s", val);
+ err(1, "BRDGADD %s", req.ifbr_ifsname);
+
+ if (req.ifbr_pvid != 0 &&
+ do_cmd(ctx, BRDGSIFPVID, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGSIFPVID %s %u", req.ifbr_ifsname,
+ (unsigned)req.ifbr_pvid);
+
+ if (vlreq.bv_op != 0 &&
+ do_cmd(ctx, BRDGSIFVLANSET, &vlreq, sizeof(vlreq), 1) < 0)
+ err(1, "BRDGSIFVLANSET %s", req.ifbr_ifsname);
+
+ return (oargc - argc);
}
static void
@@ -420,49 +509,85 @@ setbridge_flushall(if_ctx *ctx, const char *val __unused, int dummy __unused)
err(1, "BRDGFLUSH");
}
-static void
-setbridge_static(if_ctx *ctx, const char *val, const char *mac)
+static int
+setbridge_static(if_ctx *ctx, int argc, const char *const *argv)
{
struct ifbareq req;
struct ether_addr *ea;
+ int arg;
+
+ if (argc < 2)
+ errx(1, "usage: static <interface> <address> [vlan <id>]");
+ arg = 0;
memset(&req, 0, sizeof(req));
- strlcpy(req.ifba_ifsname, val, sizeof(req.ifba_ifsname));
+ req.ifba_flags = IFBAF_STATIC;
- ea = ether_aton(mac);
- if (ea == NULL)
- errx(1, "%s: invalid address: %s", val, mac);
+ strlcpy(req.ifba_ifsname, argv[arg], sizeof(req.ifba_ifsname));
+ ++arg;
+ ea = ether_aton(argv[arg]);
+ if (ea == NULL)
+ errx(1, "invalid address: %s", argv[arg]);
memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst));
- req.ifba_flags = IFBAF_STATIC;
- req.ifba_vlan = 0; /* XXX allow user to specify */
+ ++arg;
+
+ req.ifba_vlan = 0;
+ if (argc > 2 && strcmp(argv[arg], "vlan") == 0) {
+ if (argc < 3)
+ errx(1, "usage: static <interface> <address> "
+ "[vlan <id>]");
+ ++arg;
+
+ if (get_vlan_id(argv[arg], &req.ifba_vlan) < 0)
+ errx(1, "invalid vlan id: %s", argv[arg]);
+ ++arg;
+ }
if (do_cmd(ctx, BRDGSADDR, &req, sizeof(req), 1) < 0)
- err(1, "BRDGSADDR %s", val);
+ err(1, "BRDGSADDR");
+ return arg;
}
-static void
-setbridge_deladdr(if_ctx *ctx, const char *val, int dummy __unused)
+static int
+setbridge_deladdr(if_ctx *ctx, int argc, const char *const *argv)
{
struct ifbareq req;
struct ether_addr *ea;
+ int arg;
+
+ if (argc < 1)
+ errx(1, "usage: deladdr <address> [vlan <id>]");
+ arg = 0;
memset(&req, 0, sizeof(req));
- ea = ether_aton(val);
+ ea = ether_aton(argv[arg]);
if (ea == NULL)
- errx(1, "invalid address: %s", val);
-
+ errx(1, "invalid address: %s", argv[arg]);
memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst));
+ ++arg;
+
+ req.ifba_vlan = 0;
+ if (argc >= 2 && strcmp(argv[arg], "vlan") == 0) {
+ if (argc < 3)
+ errx(1, "usage: deladdr <address> [vlan <id>]");
+ ++arg;
+
+ if (get_vlan_id(argv[arg], &req.ifba_vlan) < 0)
+ errx(1, "invalid vlan id: %s", argv[arg]);
+ ++arg;
+ }
if (do_cmd(ctx, BRDGDADDR, &req, sizeof(req), 1) < 0)
- err(1, "BRDGDADDR %s", val);
+ err(1, "BRDGDADDR");
+
+ return arg;
}
static void
setbridge_addr(if_ctx *ctx, const char *val __unused, int dummy __unused)
{
-
bridge_addresses(ctx, "");
}
@@ -610,42 +735,32 @@ setbridge_ifpathcost(if_ctx *ctx, const char *ifn, const char *cost)
}
static void
-setbridge_untagged(if_ctx *ctx, const char *ifn, const char *vlanid)
+setbridge_ifuntagged(if_ctx *ctx, const char *ifn, const char *vlanid)
{
struct ifbreq req;
- u_long val;
memset(&req, 0, sizeof(req));
+ strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
- if (get_val(vlanid, &val) < 0)
+ if (get_vlan_id(vlanid, &req.ifbr_pvid) < 0)
errx(1, "invalid VLAN identifier: %s", vlanid);
- /*
- * Reject vlan 0, since it's not a valid vlan identifier and has a
- * special meaning in the kernel interface.
- */
- if (val == 0)
- errx(1, "invalid VLAN identifier: %lu", val);
-
- strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
- req.ifbr_untagged = val;
-
- if (do_cmd(ctx, BRDGSIFUNTAGGED, &req, sizeof(req), 1) < 0)
- err(1, "BRDGSIFUNTAGGED %s", vlanid);
+ if (do_cmd(ctx, BRDGSIFPVID, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGSIFPVID %s", vlanid);
}
static void
-unsetbridge_untagged(if_ctx *ctx, const char *ifn, int dummy __unused)
+unsetbridge_ifuntagged(if_ctx *ctx, const char *ifn, int dummy __unused)
{
struct ifbreq req;
memset(&req, 0, sizeof(req));
strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname));
- req.ifbr_untagged = 0;
+ req.ifbr_pvid = 0;
- if (do_cmd(ctx, BRDGSIFUNTAGGED, &req, sizeof(req), 1) < 0)
- err(1, "BRDGSIFUNTAGGED");
+ if (do_cmd(ctx, BRDGSIFPVID, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGSIFPVID");
}
static void
@@ -693,18 +808,6 @@ unsetbridge_private(if_ctx *ctx, const char *val, int dummy __unused)
do_bridgeflag(ctx, val, IFBIF_PRIVATE, 0);
}
-static void
-setbridge_vlanfilter(if_ctx *ctx, const char *val, int dummy __unused)
-{
- do_bridgeflag(ctx, val, IFBIF_VLANFILTER, 1);
-}
-
-static void
-unsetbridge_vlanfilter(if_ctx *ctx, const char *val, int dummy __unused)
-{
- do_bridgeflag(ctx, val, IFBIF_VLANFILTER, 0);
-}
-
static int
parse_vlans(ifbvlan_set_t *set, const char *str)
{
@@ -779,25 +882,109 @@ set_bridge_vlanset(if_ctx *ctx, const char *ifn, const char *vlans, int op)
}
static void
-setbridge_tagged(if_ctx *ctx, const char *ifn, const char *vlans)
+setbridge_iftagged(if_ctx *ctx, const char *ifn, const char *vlans)
{
set_bridge_vlanset(ctx, ifn, vlans, BRDG_VLAN_OP_SET);
}
static void
-addbridge_tagged(if_ctx *ctx, const char *ifn, const char *vlans)
+addbridge_iftagged(if_ctx *ctx, const char *ifn, const char *vlans)
{
set_bridge_vlanset(ctx, ifn, vlans, BRDG_VLAN_OP_ADD);
}
static void
-delbridge_tagged(if_ctx *ctx, const char *ifn, const char *vlans)
+delbridge_iftagged(if_ctx *ctx, const char *ifn, const char *vlans)
{
set_bridge_vlanset(ctx, ifn, vlans, BRDG_VLAN_OP_DEL);
}
+static void
+setbridge_flags(if_ctx *ctx, const char *val __unused, int newflags)
+{
+ struct ifbrparam req;
+
+ if (do_cmd(ctx, BRDGGFLAGS, &req, sizeof(req), 0) < 0)
+ err(1, "BRDGGFLAGS");
+
+ req.ifbrp_flags |= (uint32_t)newflags;
+
+ if (do_cmd(ctx, BRDGSFLAGS, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGSFLAGS");
+}
+
+static void
+unsetbridge_flags(if_ctx *ctx, const char *val __unused, int newflags)
+{
+ struct ifbrparam req;
+
+ if (do_cmd(ctx, BRDGGFLAGS, &req, sizeof(req), 0) < 0)
+ err(1, "BRDGGFLAGS");
+
+ req.ifbrp_flags &= ~(uint32_t)newflags;
+
+ if (do_cmd(ctx, BRDGSFLAGS, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGSFLAGS");
+}
+
+static void
+setbridge_defuntagged(if_ctx *ctx, const char *arg, int dummy __unused)
+{
+ struct ifbrparam req;
+
+ memset(&req, 0, sizeof(req));
+ if (get_vlan_id(arg, &req.ifbrp_defpvid) < 0)
+ errx(1, "invalid vlan id: %s", arg);
+
+ if (do_cmd(ctx, BRDGSDEFPVID, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGSDEFPVID");
+}
+
+static void
+unsetbridge_defuntagged(if_ctx *ctx, const char *val __unused, int dummy __unused)
+{
+ struct ifbrparam req;
+
+ memset(&req, 0, sizeof(req));
+ req.ifbrp_defpvid = 0;
+
+ if (do_cmd(ctx, BRDGSDEFPVID, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGSDEFPVID");
+}
+
+static void
+setbridge_qinq(if_ctx *ctx, const char *val, int dummy __unused)
+{
+ do_bridgeflag(ctx, val, IFBIF_QINQ, 1);
+}
+
+static void
+unsetbridge_qinq(if_ctx *ctx, const char *val, int dummy __unused)
+{
+ do_bridgeflag(ctx, val, IFBIF_QINQ, 0);
+}
+
+static void
+setbridge_ifvlanproto(if_ctx *ctx, const char *ifname, const char *proto)
+{
+ struct ifbreq req;
+
+ memset(&req, 0, sizeof(req));
+ strlcpy(req.ifbr_ifsname, ifname, sizeof(req.ifbr_ifsname));
+
+ if (strcmp(proto, "802.1q") == 0)
+ req.ifbr_vlanproto = ETHERTYPE_VLAN;
+ else if (strcmp(proto, "802.1ad") == 0)
+ req.ifbr_vlanproto = ETHERTYPE_QINQ;
+ else
+ errx(1, "unrecognised VLAN protocol: %s", proto);
+
+ if (do_cmd(ctx, BRDGSIFVLANPROTO, &req, sizeof(req), 1) < 0)
+ err(1, "BRDGSIFVLANPROTO");
+}
+
static struct cmd bridge_cmds[] = {
- DEF_CMD_ARG("addm", setbridge_add),
+ DEF_CMD_VARG("addm", setbridge_add),
DEF_CMD_ARG("deletem", setbridge_delete),
DEF_CMD_ARG("discover", setbridge_discover),
DEF_CMD_ARG("-discover", unsetbridge_discover),
@@ -819,8 +1006,8 @@ static struct cmd bridge_cmds[] = {
DEF_CMD_ARG("-autoptp", unsetbridge_autoptp),
DEF_CMD("flush", 0, setbridge_flush),
DEF_CMD("flushall", 0, setbridge_flushall),
- DEF_CMD_ARG2("static", setbridge_static),
- DEF_CMD_ARG("deladdr", setbridge_deladdr),
+ DEF_CMD_VARG("static", setbridge_static),
+ DEF_CMD_VARG("deladdr", setbridge_deladdr),
DEF_CMD("addr", 1, setbridge_addr),
DEF_CMD_ARG("maxaddr", setbridge_maxaddr),
DEF_CMD_ARG("hellotime", setbridge_hellotime),
@@ -832,17 +1019,29 @@ static struct cmd bridge_cmds[] = {
DEF_CMD_ARG2("ifpriority", setbridge_ifpriority),
DEF_CMD_ARG2("ifpathcost", setbridge_ifpathcost),
DEF_CMD_ARG2("ifmaxaddr", setbridge_ifmaxaddr),
- DEF_CMD_ARG("vlanfilter", setbridge_vlanfilter),
- DEF_CMD_ARG("-vlanfilter", unsetbridge_vlanfilter),
- DEF_CMD_ARG2("untagged", setbridge_untagged),
- DEF_CMD_ARG("-untagged", unsetbridge_untagged),
- DEF_CMD_ARG2("tagged", setbridge_tagged),
- DEF_CMD_ARG2("+tagged", addbridge_tagged),
- DEF_CMD_ARG2("-tagged", delbridge_tagged),
+ DEF_CMD_ARG2("ifuntagged", setbridge_ifuntagged),
+ DEF_CMD_ARG("-ifuntagged", unsetbridge_ifuntagged),
+ DEF_CMD_ARG2("iftagged", setbridge_iftagged),
+ DEF_CMD_ARG2("+iftagged", addbridge_iftagged),
+ DEF_CMD_ARG2("-iftagged", delbridge_iftagged),
+ DEF_CMD_ARG2("ifvlanproto", setbridge_ifvlanproto),
DEF_CMD_ARG("timeout", setbridge_timeout),
DEF_CMD_ARG("private", setbridge_private),
DEF_CMD_ARG("-private", unsetbridge_private),
+ DEF_CMD("vlanfilter", (int32_t)IFBRF_VLANFILTER,
+ setbridge_flags),
+ DEF_CMD("-vlanfilter", (int32_t)IFBRF_VLANFILTER,
+ unsetbridge_flags),
+ DEF_CMD_ARG("defuntagged", setbridge_defuntagged),
+ DEF_CMD("-defuntagged", 0, unsetbridge_defuntagged),
+ DEF_CMD("defqinq", (int32_t)IFBRF_DEFQINQ,
+ setbridge_flags),
+ DEF_CMD("-defqinq", (int32_t)IFBRF_DEFQINQ,
+ unsetbridge_flags),
+ DEF_CMD_ARG("qinq", setbridge_qinq),
+ DEF_CMD_ARG("-qinq", unsetbridge_qinq),
};
+
static struct afswtch af_bridge = {
.af_name = "af_bridge",
.af_af = AF_UNSPEC,
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8
index 6c61af48abec..d4f8d2b5747a 100644
--- a/sbin/ifconfig/ifconfig.8
+++ b/sbin/ifconfig/ifconfig.8
@@ -28,7 +28,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd July 11, 2025
+.Dd October 12, 2025
.Dt IFCONFIG 8
.Os
.Sh NAME
@@ -36,7 +36,7 @@
.Nd configure network interface parameters
.Sh SYNOPSIS
.Nm
-.Op Fl j Ar jail
+.Op Fl j Ar jid
.Op Fl DkLmn
.Op Fl f Ar type Ns Cm \&: Ns Ar format
.Ar interface
@@ -50,11 +50,11 @@
.Oc
.Op Ar parameters
.Nm
-.Op Fl j Ar jail
+.Op Fl j Ar jid
.Ar interface
.Cm destroy
.Nm
-.Op Fl j Ar jail
+.Op Fl j Ar jid
.Fl a
.Op Fl dDkLmuv
.Op Fl f Ar type Ns Cm \&: Ns Ar format
@@ -64,16 +64,16 @@
.Nm
.Fl C
.Nm
-.Op Fl j Ar jail
+.Op Fl j Ar jid
.Fl g Ar groupname
.Nm
-.Op Fl j Ar jail
+.Op Fl j Ar jid
.Fl l
.Op Fl du
.Op Fl g Ar groupname
.Op Ar address_family
.Nm
-.Op Fl j Ar jail
+.Op Fl j Ar jid
.Op Fl dkLmuv
.Op Fl f Ar type Ns Cm \&: Ns Ar format
.Sh DESCRIPTION
@@ -257,22 +257,22 @@ Setting
to
.Cm all
selects all interfaces.
-.It Fl j Ar jail
-Perform the actions inside the
-.Ar jail .
+.It Fl j Ar jid
+Perform the actions inside the jail specified by
+.Ar jid ,
+which may be either a jail name or a numeric jail ID.
.Pp
The
-.Cm ifconfig
-will first attach to the
-.Ar jail
-(by jail id or jail name) before performing the effects.
-.Pp
-This allow network interfaces of
-.Ar jail
-to be configured even if the
-.Cm ifconfig
-binary is not available in
-.Ar jail .
+.Nm
+utility will attach to the specified jail immediately upon
+encountering the option on the command line.
+The option may be specified multiple times to attach to a nested jail
+(jail within a jail).
+.Pp
+This makes it possible to configure network interfaces within a vnet
+jail even if the
+.Nm
+binary is not available inside the jail.
.It Fl k
Print keying information for the
.Ar interface ,
@@ -1004,6 +1004,36 @@ Set a flag to disable Duplicate Address Detection.
.It Cm -no_dad
Clear a flag
.Cm no_dad .
+.It Cm stableaddr
+Set a flag to create SLAAC addresses using a stable algorithm according to RFC 7217
+The
+.Xr sysctl 8
+variable
+.Va net.inet6.ip6.use_stableaddr
+controls whether this flag is set by default or not for newly created interfaces.
+To get consistent defaults for interfaces created at boot it should be set as a tunable via loader.conf(8).
+The
+.Xr sysctl 8
+variable
+.Va net.inet6.ip6.stableaddr_maxretries
+sets the maximum number of retries to generate a unique IPv6 address to be performed in case of DAD failures.
+This defaults to 3 which is also the reccommended minimum value.
+The interface ID source can be configured using the
+.Xr sysctl 8
+variable
+.Va net.inet6.ip6.stableaddr_netifsource:
+.Bl -tag -compact
+.It Cm 0
+uses the interface name string (the default)
+.It Cm 1
+uses the interface ID
+.It Cm 2
+uses the MAC address of the interface (if one can be obtained for it)
+.El
+.Pp
+.It Cm -stableaddr
+Clear the flag
+.Cm stableaddr .
.El
.Ss IPv6 Parameters
The following parameters are specific for IPv6 addresses.
@@ -2494,12 +2524,27 @@ compatibility.
.Ss Bridge Interface Parameters
The following parameters are specific to bridge interfaces:
.Bl -tag -width indent
-.It Cm addm Ar interface
+.It Cm addm Ar interface Op Ar options ...
Add the interface named by
.Ar interface
as a member of the bridge.
The interface is put into promiscuous mode
so that it can receive every packet sent on the network.
+.Pp
+The interface name may be followed by one or more of the following
+.Ar options :
+.Bl -tag -width ".Cm untagged Ar vlan-id"
+.It Cm untagged Ar vlan-id
+Set the untagged VLAN identifier for the interface.
+This is equivalent to the
+.Cm ifuntagged
+command.
+.It Cm tagged Ar vlan-set
+Set the allowed VLAN list for the interface.
+This is equivalent to the
+.Cm iftagged
+command.
+.El
.It Cm deletem Ar interface
Remove the interface named by
.Ar interface
@@ -2520,15 +2565,23 @@ is zero, then address cache entries will not be expired.
The default is 1200 seconds.
.It Cm addr
Display the addresses that have been learned by the bridge.
-.It Cm static Ar interface-name Ar address
-Add a static entry into the address cache pointing to
+.It Cm static Ar interface-name Ar address Op Cm vlan Ar vlan-id
+Add a static entry into the address cache for pointing to
.Ar interface-name .
+If
+.Ar vlan-id
+is specified, the entry is added for that VLAN, otherwise it is added
+for VLAN 0.
+.Pp
Static entries are never aged out of the cache or re-placed, even if the
address is seen on a different interface.
-.It Cm deladdr Ar address
+.It Cm deladdr Ar address Op Cm vlan Ar vlan-id
Delete
.Ar address
-from the address cache.
+from the address cache. If
+.Ar vlan-id
+is specified, the entry is deleted from that VLAN's address table,
+otherwise it is deleted from the VLAN 0 address table.
.It Cm flush
Delete all dynamically-learned addresses from the address cache.
.It Cm flushall
@@ -2695,26 +2748,18 @@ Set the maximum number of hosts allowed from an interface, packets with unknown
source addresses are dropped until an existing host cache entry expires or is
removed.
Set to 0 to disable.
-.El
-.Ss Bridge VLAN Filtering Parameters
-The behaviour of these options is described in the
-.Dq VLAN SUPPORT
-section of
-.Xr bridge 4 .
-.Bl -tag -width indent
-.It Cm vlanfilter Ar interface
-Enable VLAN filtering on an interface.
-.It Cm -vlanfilter Ar interface
-Disable VLAN filtering on an interface.
-.It Cm untagged Ar interface Ar vlan-id
-Set the untagged VLAN identifier for an interface.
-.Pp
-Setting
-.Cm untagged
-will automatically enable VLAN filtering on the interface.
-.It Cm -untagged Ar interface Ar vlan-id
-Clear the untagged VLAN identifier for an interface.
-.It Cm tagged Ar interface Ar vlan-list
+.It Cm vlanfilter
+Enable VLAN filtering on the bridge.
+Incoming frames on member interfaces will be dropped unless the frame
+is explicitly permitted by the interface's
+.Cm ifuntagged
+or
+.Cm iftagged
+configuration.
+.It Cm -vlanfilter
+Disable VLAN filtering on the bridge.
+This is the default.
+.It Cm iftagged Ar interface Ar vlan-list
Set the interface's VLAN access list to the provided list of VLANs.
The list should be a comma-separated list of one or more VLAN IDs
or ranges formatted as
@@ -2726,26 +2771,80 @@ or the value
.Dq all
meaning all VLANs (1-4094).
.Pp
-Setting
-.Cm tagged
-will automatically enable VLAN filtering on the interface.
-.It Cm +tagged Ar interface Ar vlan-list
+This option is only meaningful if the
+.Cm vlanfilter
+option is enabled for the bridge;
+otherwise, all VLANs will be permitted.
+.It Cm +iftagged Ar interface Ar vlan-list
Add the provided list of VLAN IDs to the interface's VLAN access list.
The list should be formatted as described for
-.Cm tagged .
+.Cm iftagged .
.Pp
-Setting
-.Cm +tagged
-will automatically enable VLAN filtering on the interface.
-.It Cm -tagged Ar interface Ar vlan-list
+This option is only meaningful if the
+.Cm vlanfilter
+option is enabled for the bridge;
+otherwise, all VLANs will be permitted.
+.It Cm -iftagged Ar interface Ar vlan-list
Remove the provided list of VLAN IDs from the interface's VLAN access
list.
The list should be formatted as described for
-.Cm tagged .
+.Cm iftagged .
.Pp
-Setting
-.Cm -tagged
-will automatically enable VLAN filtering on the interface.
+This option is only meaningful if the
+.Cm vlanfilter
+option is enabled for the bridge;
+otherwise, all VLANs will be permitted.
+.It Cm ifuntagged Ar interface Ar vlan-id
+Set the untagged VLAN identifier for an interface.
+Frames received on this interface without an 802.1Q tag will be assigned
+to this VLAN instead of the default VLAN 0,
+and outgoing frames on this VLAN will have their 802.1Q tag removed.
+.It Cm -ifuntagged Ar interface
+Clear the untagged VLAN identifier for an interface.
+.It Cm defuntagged Ar vlan-id
+Enable the
+.Cm untagged
+option by default on newly added members.
+.It Cm -defuntagged
+Do not enable the
+.Cm untagged
+option by default on newly added members.
+This is the default.
+.It Cm qinq Ar interface
+Allow this interface to send 802.1ad
+.Dq Q-in-Q
+frames.
+This option is only meaningful if the
+.Cm vlanfilter
+option is enabled for the bridge;
+otherwise, Q-in-Q frames are always allowed.
+.It Cm -qinq Ar interface
+Do not allow this interface to send 802.1ad
+.Dq Q-in-Q
+frames.
+This is the default if the
+.Cm vlanfilter
+option is enabled.
+.It Cm defqinq
+Enable the
+.Cm qinq
+option by default on newly added members.
+.It Cm -defqinq
+Do not enable the
+.Cm qinq
+option by default on newly added members.
+This is the default.
+.It Cm ifvlanproto Ar interface Ar proto
+Set the VLAN encapsulation protocol on
+.Ar interface
+to
+.Ar proto ,
+which must be either
+.Dq 802.1q
+or
+.Dq 802.1ad .
+The default is
+.Dq 802.1q .
.El
.Ss Link Aggregation and Link Failover Parameters
The following parameters are specific to lagg interfaces:
@@ -2878,13 +2977,25 @@ interfaces previously configured with
Another name for the
.Fl tunnel
parameter.
+.It Cm noclamp
+This flag prevents the MTU from being clamped to 1280 bytes, the
+minimum MTU for IPv6, when the outer protocol is IPv6. When the
+flag is set, the MTU value configured on the interface will be
+used instead of the fixed length of 1280 bytes. For more details,
+please refer to the
+.Ar MTU Configuration and Path MTU Discovery
+section in
+.Xr gif 4 .
+.It Cm -noclamp
+Clear the flag
+.Cm noclamp .
.It Cm ignore_source
Set a flag to accept encapsulated packets destined to this host
independently from source address.
This may be useful for hosts, that receive encapsulated packets
from the load balancers.
.It Cm -ignore_source
-Clear a flag
+Clear the flag
.Cm ignore_source .
.El
.Ss GRE Tunnel Parameters
diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c
index a0680d09e54c..9aeb4a09ef49 100644
--- a/sbin/ifconfig/ifconfig.c
+++ b/sbin/ifconfig/ifconfig.c
@@ -463,6 +463,9 @@ args_parse(struct ifconfig_args *args, int argc, char *argv[])
{
char options[1024];
struct option *p;
+#ifdef JAIL
+ int jid;
+#endif
int c;
/* Parse leading line options */
@@ -494,7 +497,11 @@ args_parse(struct ifconfig_args *args, int argc, char *argv[])
#ifdef JAIL
if (optarg == NULL)
usage();
- args->jail_name = optarg;
+ jid = jail_getid(optarg);
+ if (jid == -1)
+ Perror("jail not found");
+ if (jail_attach(jid) != 0)
+ Perror("cannot attach to jail");
#else
Perror("not built with jail support");
#endif
@@ -611,9 +618,6 @@ main(int ac, char *av[])
{
char *envformat;
int flags;
-#ifdef JAIL
- int jid;
-#endif
struct ifconfig_args _args = {};
struct ifconfig_args *args = &_args;
@@ -638,16 +642,6 @@ main(int ac, char *av[])
args_parse(args, ac, av);
-#ifdef JAIL
- if (args->jail_name) {
- jid = jail_getid(args->jail_name);
- if (jid == -1)
- Perror("jail not found");
- if (jail_attach(jid) != 0)
- Perror("cannot attach to jail");
- }
-#endif
-
if (!args->all && !args->namesonly) {
/* not listing, need an argument */
args->ifname = args_pop(args);
@@ -1209,6 +1203,13 @@ top:
argc -= 2, argv += 2;
} else if (p->c_parameter == SPARAM && p->c_u.c_func3) {
p->c_u.c_func3(ctx, *argv, p->c_sparameter);
+ } else if (p->c_parameter == ARGVECTOR && p->c_u.c_funcv) {
+ int argsdone;
+
+ argsdone = p->c_u.c_funcv(ctx, argc - 1,
+ (const char *const *)argv + 1);
+ argc -= argsdone;
+ argv += argsdone;
} else if (p->c_u.c_func)
p->c_u.c_func(ctx, *argv, p->c_parameter);
argc--, argv++;
@@ -1662,9 +1663,10 @@ static const char *IFCAPBITS[] = {
[20] = "NETMAP",
[21] = "RXCSUM_IPV6",
[22] = "TXCSUM_IPV6",
+ [23] = "HWSTATS",
[24] = "TXRTLMT",
[25] = "HWRXTSTMP",
- [26] = "NOMAP",
+ [26] = "MEXTPG",
[27] = "TXTLS4",
[28] = "TXTLS6",
[29] = "VXLAN_HWCSUM",
@@ -2087,11 +2089,6 @@ static struct cmd basic_cmds[] = {
DEF_CMD("-alias", -IFF_UP, notealias),
DEF_CMD("delete", -IFF_UP, notealias),
DEF_CMD("remove", -IFF_UP, notealias),
-#ifdef notdef
-#define EN_SWABIPS 0x1000
- DEF_CMD("swabips", EN_SWABIPS, setifflags),
- DEF_CMD("-swabips", EN_SWABIPS, clearifflags),
-#endif
DEF_CMD_ARG("netmask", setifnetmask),
DEF_CMD_ARG("metric", setifmetric),
DEF_CMD_ARG("broadcast", setifbroadaddr),
diff --git a/sbin/ifconfig/ifconfig.h b/sbin/ifconfig/ifconfig.h
index 555e7dbaca64..672020443b8c 100644
--- a/sbin/ifconfig/ifconfig.h
+++ b/sbin/ifconfig/ifconfig.h
@@ -67,6 +67,7 @@ typedef struct ifconfig_context if_ctx;
typedef void c_func(if_ctx *ctx, const char *cmd, int arg);
typedef void c_func2(if_ctx *ctx, const char *arg1, const char *arg2);
typedef void c_func3(if_ctx *ctx, const char *cmd, const char *arg);
+typedef int c_funcv(if_ctx *ctx, int argc, const char *const *argv);
struct cmd {
const char *c_name;
@@ -75,11 +76,13 @@ struct cmd {
#define NEXTARG2 0xfffffe /* has 2 following args */
#define OPTARG 0xfffffd /* has optional following arg */
#define SPARAM 0xfffffc /* parameter is string c_sparameter */
+#define ARGVECTOR 0xfffffb /* takes argument vector */
const char *c_sparameter;
union {
c_func *c_func;
c_func2 *c_func2;
c_func3 *c_func3;
+ c_funcv *c_funcv;
} c_u;
int c_iscloneop;
struct cmd *c_next;
@@ -121,6 +124,13 @@ void callback_register(callback_func *, void *);
.c_iscloneop = 0, \
.c_next = NULL, \
}
+#define DEF_CMD_VARG(name, func) { \
+ .c_name = (name), \
+ .c_parameter = ARGVECTOR, \
+ .c_u = { .c_funcv = (func) }, \
+ .c_iscloneop = 0, \
+ .c_next = NULL, \
+}
#define DEF_CMD_SARG(name, sparam, func) { \
.c_name = (name), \
.c_parameter = SPARAM, \
@@ -239,7 +249,6 @@ struct ifconfig_args {
const char *matchgroup; /* Group name to match */
const char *nogroup; /* Group name to exclude */
const struct afswtch *afp; /* AF we're operating on */
- const char *jail_name; /* Jail name or jail id specified */
};
struct option {
diff --git a/sbin/ifconfig/ifgif.c b/sbin/ifconfig/ifgif.c
index 991cf110678f..9b8be210a59e 100644
--- a/sbin/ifconfig/ifgif.c
+++ b/sbin/ifconfig/ifgif.c
@@ -49,6 +49,7 @@
#include "ifconfig.h"
static const char *GIFBITS[] = {
+ [0] = "NOCLAMP",
[1] = "IGNORE_SOURCE",
};
@@ -90,6 +91,8 @@ setgifopts(if_ctx *ctx, const char *val __unused, int d)
}
static struct cmd gif_cmds[] = {
+ DEF_CMD("noclamp", GIF_NOCLAMP, setgifopts),
+ DEF_CMD("-noclamp", -GIF_NOCLAMP, setgifopts),
DEF_CMD("ignore_source", GIF_IGNORE_SOURCE, setgifopts),
DEF_CMD("-ignore_source", -GIF_IGNORE_SOURCE, setgifopts),
};
diff --git a/sbin/ifconfig/tests/inet6.sh b/sbin/ifconfig/tests/inet6.sh
index edfd88d93af7..22399915a64d 100644
--- a/sbin/ifconfig/tests/inet6.sh
+++ b/sbin/ifconfig/tests/inet6.sh
@@ -76,8 +76,38 @@ broadcast_cleanup()
vnet_cleanup
}
+atf_test_case "delete6" "cleanup"
+delete6_head()
+{
+ atf_set descr 'Test removing IPv6 addresses'
+ atf_set require.user root
+}
+
+delete6_body()
+{
+ vnet_init
+
+ ep=$(vnet_mkepair)
+
+ atf_check -s exit:0 \
+ ifconfig ${ep}a inet6 fe80::42/64
+ atf_check -s exit:0 -o match:"fe80::42%${ep}" \
+ ifconfig ${ep}a inet6
+
+ atf_check -s exit:0 \
+ ifconfig ${ep}a inet6 -alias fe80::42
+ atf_check -s exit:0 -o not-match:"fe80::42%${ep}" \
+ ifconfig ${ep}a inet6
+}
+
+delete6_cleanup()
+{
+ vnet_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case netmask
atf_add_test_case broadcast
+ atf_add_test_case delete6
}
diff --git a/sbin/init/init.c b/sbin/init/init.c
index b345c8fa219a..d28501053c7f 100644
--- a/sbin/init/init.c
+++ b/sbin/init/init.c
@@ -851,9 +851,9 @@ single_user(void)
const char *shell;
char *argv[2];
struct timeval tv, tn;
+ struct passwd *pp;
#ifdef SECURE
struct ttyent *typ;
- struct passwd *pp;
static const char banner[] =
"Enter root password, or ^D to go multi-user\n";
char *clear, *password;
@@ -885,6 +885,7 @@ single_user(void)
*/
open_console();
+ pp = getpwnam("root");
#ifdef SECURE
/*
* Check the root password.
@@ -892,7 +893,6 @@ single_user(void)
* it's the only tty that can be 'off' and 'secure'.
*/
typ = getttynam("console");
- pp = getpwnam("root");
if (typ && (typ->ty_status & TTY_SECURE) == 0 &&
pp && *pp->pw_passwd) {
write_stderr(banner);
@@ -909,7 +909,6 @@ single_user(void)
}
}
endttyent();
- endpwent();
#endif /* SECURE */
#ifdef DEBUGSHELL
@@ -930,6 +929,15 @@ single_user(void)
}
#endif /* DEBUGSHELL */
+ if (pp != NULL && pp->pw_dir != NULL && *pp->pw_dir != '\0' &&
+ chdir(pp->pw_dir) == 0) {
+ setenv("HOME", pp->pw_dir, 1);
+ } else {
+ chdir("/");
+ setenv("HOME", "/", 1);
+ }
+ endpwent();
+
/*
* Unblock signals.
* We catch all the interesting ones,
diff --git a/sbin/ipf/libipf/printdstl_live.c b/sbin/ipf/libipf/printdstl_live.c
index 088448e6656d..72cb75a832c9 100644
--- a/sbin/ipf/libipf/printdstl_live.c
+++ b/sbin/ipf/libipf/printdstl_live.c
@@ -40,6 +40,9 @@ printdstl_live( ippool_dst_t *d, int fd, char *name, int opts,
if ((d->ipld_flags & IPHASH_DELETE) != 0)
PRINTF("# ");
+ if (opts & OPT_SAVEOUT)
+ PRINTF("{\n");
+
if ((opts & OPT_DEBUG) == 0)
PRINTF("\t{");
diff --git a/sbin/ipf/libipf/printdstlist.c b/sbin/ipf/libipf/printdstlist.c
index 2cf41ffe414c..497d7004c94c 100644
--- a/sbin/ipf/libipf/printdstlist.c
+++ b/sbin/ipf/libipf/printdstlist.c
@@ -42,6 +42,8 @@ printdstlist( ippool_dst_t *pp, copyfunc_t copyfunc, char *name, int opts,
return (NULL);
}
+ if (opts & OPT_SAVEOUT)
+ PRINTF("\t");
node = printdstlistnode(n, bcopywrap, opts, fields);
free(n);
diff --git a/sbin/ipf/libipf/printdstlistdata.c b/sbin/ipf/libipf/printdstlistdata.c
index 7940d2ae021b..546bf35cabf6 100644
--- a/sbin/ipf/libipf/printdstlistdata.c
+++ b/sbin/ipf/libipf/printdstlistdata.c
@@ -11,8 +11,7 @@
void
printdstlistdata( ippool_dst_t *pool, int opts)
{
-
- if ((opts & OPT_DEBUG) == 0) {
+ if ((opts & OPT_DEBUG) == 0 || opts & OPT_SAVEOUT) {
if ((pool->ipld_flags & IPDST_DELETE) != 0)
PRINTF("# ");
PRINTF("pool ");
@@ -24,7 +23,7 @@ printdstlistdata( ippool_dst_t *pool, int opts)
printunit(pool->ipld_unit);
- if ((opts & OPT_DEBUG) == 0) {
+ if ((opts & OPT_DEBUG) == 0 || opts & OPT_SAVEOUT) {
PRINTF("/dstlist (name %s;", pool->ipld_name);
if (pool->ipld_policy != IPLDP_NONE) {
PRINTF(" policy ");
diff --git a/sbin/ipf/libipf/printhash_live.c b/sbin/ipf/libipf/printhash_live.c
index b8ee31b27597..427daa18316b 100644
--- a/sbin/ipf/libipf/printhash_live.c
+++ b/sbin/ipf/libipf/printhash_live.c
@@ -26,7 +26,9 @@ printhash_live(iphtable_t *hp, int fd, char *name, int opts, wordtab_t *fields)
if ((hp->iph_flags & IPHASH_DELETE) != 0)
PRINTF("# ");
- if ((opts & OPT_DEBUG) == 0)
+ if (opts & OPT_SAVEOUT)
+ PRINTF("{\n");
+ else if ((opts & OPT_DEBUG) == 0)
PRINTF("\t{");
obj.ipfo_rev = IPFILTER_VERSION;
@@ -50,6 +52,8 @@ printhash_live(iphtable_t *hp, int fd, char *name, int opts, wordtab_t *fields)
last = 1;
if (bcmp(&zero, &entry, sizeof(zero)) == 0)
break;
+ if (opts & OPT_SAVEOUT)
+ PRINTF("\t");
(void) printhashnode(hp, &entry, bcopywrap, opts, fields);
printed++;
}
@@ -59,7 +63,7 @@ printhash_live(iphtable_t *hp, int fd, char *name, int opts, wordtab_t *fields)
if (printed == 0)
putchar(';');
- if ((opts & OPT_DEBUG) == 0)
+ if ((opts & OPT_DEBUG) == 0 || (opts & OPT_SAVEOUT))
PRINTF(" };\n");
(void) ioctl(fd,SIOCIPFDELTOK, &iter.ili_key);
diff --git a/sbin/ipf/libipf/printhashdata.c b/sbin/ipf/libipf/printhashdata.c
index 690243d63f1e..6fa62e67556d 100644
--- a/sbin/ipf/libipf/printhashdata.c
+++ b/sbin/ipf/libipf/printhashdata.c
@@ -12,7 +12,11 @@ void
printhashdata(iphtable_t *hp, int opts)
{
- if ((opts & OPT_DEBUG) == 0) {
+ if (opts & OPT_SAVEOUT) {
+ if ((hp->iph_flags & IPHASH_DELETE) == IPHASH_DELETE)
+ PRINTF("# ");
+ PRINTF("pool ");
+ } else if ((opts & OPT_DEBUG) == 0) {
if ((hp->iph_type & IPHASH_ANON) == IPHASH_ANON)
PRINTF("# 'anonymous' table refs %d\n", hp->iph_ref);
if ((hp->iph_flags & IPHASH_DELETE) == IPHASH_DELETE)
@@ -37,6 +41,8 @@ printhashdata(iphtable_t *hp, int opts)
}
PRINTF(" role=");
} else {
+ if ((hp->iph_flags & IPHASH_DELETE) == IPHASH_DELETE)
+ PRINTF("# ");
PRINTF("Hash Table %s: %s",
ISDIGIT(*hp->iph_name) ? "Number" : "Name",
hp->iph_name);
@@ -48,7 +54,16 @@ printhashdata(iphtable_t *hp, int opts)
printunit(hp->iph_unit);
- if ((opts & OPT_DEBUG) == 0) {
+ if ((opts & OPT_SAVEOUT)) {
+ if ((hp->iph_type & ~IPHASH_ANON) == IPHASH_LOOKUP)
+ PRINTF("/hash");
+ PRINTF("(%s \"%s\"; size %lu;",
+ ISDIGIT(*hp->iph_name) ? "number" : "name",
+ hp->iph_name, (u_long)hp->iph_size);
+ if (hp->iph_seed != 0)
+ PRINTF(" seed %lu;", hp->iph_seed);
+ PRINTF(")\n", hp->iph_seed);
+ } else if ((opts & OPT_DEBUG) == 0) {
if ((hp->iph_type & ~IPHASH_ANON) == IPHASH_LOOKUP)
PRINTF(" type=hash");
PRINTF(" %s=%s size=%lu",
diff --git a/sbin/ipf/libipf/printpooldata.c b/sbin/ipf/libipf/printpooldata.c
index bd5af316eb19..b203522734be 100644
--- a/sbin/ipf/libipf/printpooldata.c
+++ b/sbin/ipf/libipf/printpooldata.c
@@ -13,6 +13,8 @@ printpooldata(ip_pool_t *pool, int opts)
{
if (opts & OPT_SAVEOUT) {
+ if ((pool->ipo_flags & IPOOL_DELETE) != 0)
+ PRINTF("# ");
PRINTF("pool ");
} else if ((opts & OPT_DEBUG) == 0) {
if ((pool->ipo_flags & IPOOL_ANON) != 0)
diff --git a/sbin/ipfw/Makefile b/sbin/ipfw/Makefile
index bfbe70130de7..418c0f613741 100644
--- a/sbin/ipfw/Makefile
+++ b/sbin/ipfw/Makefile
@@ -17,6 +17,9 @@ CFLAGS+=-DPF
LIBADD= jail util
MAN= ipfw.8
+HAS_TESTS=
+SUBDIR.${MK_TESTS}= tests
+
.include <bsd.prog.mk>
CWARNFLAGS+= -Wno-cast-align
diff --git a/sbin/ipfw/dummynet.c b/sbin/ipfw/dummynet.c
index 6714b2af3124..a0cefcffd183 100644
--- a/sbin/ipfw/dummynet.c
+++ b/sbin/ipfw/dummynet.c
@@ -680,7 +680,7 @@ ipfw_delete_pipe(int do_pipe, int i)
i = do_cmd(IP_DUMMYNET3, &cmd, cmd.oid.len);
if (i) {
i = 1;
- warn("rule %u: setsockopt(IP_DUMMYNET_DEL)", i);
+ warn("rule %u: setsockopt(IP_DUMMYNET3)", i);
}
return i;
}
@@ -1863,7 +1863,7 @@ end_mask:
i = do_cmd(IP_DUMMYNET3, base, (char *)buf - (char *)base);
if (i)
- err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
+ err(1, "setsockopt(%s)", "IP_DUMMYNET3");
}
void
diff --git a/sbin/ipfw/nptv6.c b/sbin/ipfw/nptv6.c
index 83bf4c768fd9..eee6109a3d9e 100644
--- a/sbin/ipfw/nptv6.c
+++ b/sbin/ipfw/nptv6.c
@@ -153,10 +153,10 @@ static struct _s_x nptv6newcmds[] = {
{ NULL, 0 }
};
-
static void
nptv6_parse_prefix(const char *arg, struct in6_addr *prefix, int *len)
{
+ long plen;
char *p, *l;
p = strdup(arg);
@@ -167,13 +167,15 @@ nptv6_parse_prefix(const char *arg, struct in6_addr *prefix, int *len)
if (inet_pton(AF_INET6, p, prefix) != 1)
errx(EX_USAGE, "Bad prefix: %s", p);
if (l != NULL) {
- *len = (int)strtol(l, &l, 10);
- if (*l != '\0' || *len <= 0 || *len > 64)
+ plen = strtol(l, &l, 10);
+ if (*l != '\0' || plen < 8 || plen > 64)
errx(EX_USAGE, "Bad prefix length: %s", arg);
+ *len = plen;
} else
*len = 0;
free(p);
}
+
/*
* Creates new nptv6 instance
* ipfw nptv6 <NAME> create int_prefix <prefix> ext_prefix <prefix>
@@ -189,10 +191,10 @@ nptv6_create(const char *name, uint8_t set, int ac, char *av[])
struct in6_addr mask;
ipfw_nptv6_cfg *cfg;
ipfw_obj_lheader *olh;
- int tcmd, flags, plen;
+ int tcmd, flags, iplen, eplen, pplen;
char *p;
- plen = 0;
+ iplen = eplen = pplen = 0;
memset(buf, 0, sizeof(buf));
olh = (ipfw_obj_lheader *)buf;
cfg = (ipfw_nptv6_cfg *)(olh + 1);
@@ -205,10 +207,8 @@ nptv6_create(const char *name, uint8_t set, int ac, char *av[])
switch (tcmd) {
case TOK_INTPREFIX:
NEED1("IPv6 prefix required");
- nptv6_parse_prefix(*av, &cfg->internal, &plen);
+ nptv6_parse_prefix(*av, &cfg->internal, &iplen);
flags |= NPTV6_HAS_INTPREFIX;
- if (plen > 0)
- goto check_prefix;
ac--; av++;
break;
case TOK_EXTPREFIX:
@@ -216,10 +216,8 @@ nptv6_create(const char *name, uint8_t set, int ac, char *av[])
errx(EX_USAGE,
"Only one ext_prefix or ext_if allowed");
NEED1("IPv6 prefix required");
- nptv6_parse_prefix(*av, &cfg->external, &plen);
+ nptv6_parse_prefix(*av, &cfg->external, &eplen);
flags |= NPTV6_HAS_EXTPREFIX;
- if (plen > 0)
- goto check_prefix;
ac--; av++;
break;
case TOK_EXTIF:
@@ -236,24 +234,29 @@ nptv6_create(const char *name, uint8_t set, int ac, char *av[])
break;
case TOK_PREFIXLEN:
NEED1("IPv6 prefix length required");
- plen = strtol(*av, &p, 10);
-check_prefix:
- if (*p != '\0' || plen < 8 || plen > 64)
+ pplen = strtol(*av, &p, 10);
+ if (*p != '\0' || pplen < 8 || pplen > 64)
errx(EX_USAGE, "wrong prefix length: %s", *av);
- /* RFC 6296 Sec. 3.1 */
- if (cfg->plen > 0 && cfg->plen != plen) {
- warnx("Prefix length mismatch (%d vs %d). "
- "It was extended up to %d",
- cfg->plen, plen, MAX(plen, cfg->plen));
- plen = MAX(plen, cfg->plen);
- }
- cfg->plen = plen;
- flags |= NPTV6_HAS_PREFIXLEN;
ac--; av++;
break;
}
}
+ /* RFC 6296 Sec. 3.1 */
+ if (pplen != 0) {
+ if ((eplen != 0 && eplen != pplen) ||
+ (iplen != 0 && iplen != pplen))
+ errx(EX_USAGE, "prefix length mismatch");
+ cfg->plen = pplen;
+ flags |= NPTV6_HAS_PREFIXLEN;
+ } else if (eplen != 0 || iplen != 0) {
+ if (eplen != 0 && iplen != 0 && eplen != iplen)
+ errx(EX_USAGE, "prefix length mismatch");
+ warnx("use prefixlen instead");
+ cfg->plen = eplen ? eplen : iplen;
+ flags |= NPTV6_HAS_PREFIXLEN;
+ }
+
/* Check validness */
if ((flags & NPTV6_HAS_INTPREFIX) != NPTV6_HAS_INTPREFIX)
errx(EX_USAGE, "int_prefix required");
diff --git a/sbin/ipfw/tables.c b/sbin/ipfw/tables.c
index 7c3b1bb35a01..245c0c9e0399 100644
--- a/sbin/ipfw/tables.c
+++ b/sbin/ipfw/tables.c
@@ -1037,9 +1037,6 @@ table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add,
}
}
- /* Get real OS error */
- error = errno;
-
/* Report results back */
ptent = tent_buf;
for (i = 0; i < count; ptent++, i++) {
diff --git a/sbin/ipfw/tests/Makefile b/sbin/ipfw/tests/Makefile
index 987410f5d710..e2d4dab2729a 100644
--- a/sbin/ipfw/tests/Makefile
+++ b/sbin/ipfw/tests/Makefile
@@ -1,5 +1,6 @@
PACKAGE= tests
ATF_TESTS_PYTEST+= test_add_rule.py
+ATF_TESTS_SH+= ipfw_test
.include <bsd.test.mk>
diff --git a/sbin/ipfw/tests/ipfw_test.sh b/sbin/ipfw/tests/ipfw_test.sh
new file mode 100644
index 000000000000..c7993c430a3d
--- /dev/null
+++ b/sbin/ipfw/tests/ipfw_test.sh
@@ -0,0 +1,107 @@
+#
+# Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+. $(atf_get_srcdir)/../../sys/common/vnet.subr
+
+atf_test_case nptv6 cleanup
+nptv6_head()
+{
+ atf_set "descr" "Test creation of NPTv6 rules"
+ atf_set "require.user" "root"
+ atf_set "require.kmods" "ipfw_nptv6"
+}
+nptv6_body()
+{
+ vnet_init
+ local jail=ipfw_$(atf_get ident)
+ local epair=$(vnet_mkepair)
+ vnet_mkjail ${jail} ${epair}a
+
+ local rule="xyzzy"
+ local int="2001:db8:1::"
+ local ext="2001:db8:2::"
+
+ atf_check jexec ${jail} \
+ ifconfig "${epair}"a inet6 ${ext}1/64 up
+
+ # This is how it's supposed to be used
+ atf_check jexec ${jail} ipfw nptv6 ${rule} create \
+ int_prefix ${int} ext_prefix ${ext} prefixlen 64
+ atf_check -o inline:\
+"nptv6 $rule int_prefix $int ext_prefix $ext prefixlen 64\n" \
+ jexec ${jail} ipfw nptv6 all list
+ atf_check jexec ${jail} ipfw nptv6 all destroy
+
+ # Specify external interface rather than network
+ atf_check jexec ${jail} ipfw nptv6 ${rule} create \
+ int_prefix ${int} ext_if ${epair}a prefixlen 64
+ atf_check -o inline:\
+"nptv6 $rule int_prefix $int ext_if ${epair}a prefixlen 64\n" \
+ jexec ${jail} ipfw nptv6 all list
+ atf_check jexec ${jail} ipfw nptv6 all destroy
+
+ # This should also work
+ atf_check jexec ${jail} ipfw nptv6 ${rule} create \
+ int_prefix ${int}/64 ext_prefix ${ext}/64 prefixlen 64
+ atf_check -o inline:\
+"nptv6 $rule int_prefix $int ext_prefix $ext prefixlen 64\n" \
+ jexec ${jail} ipfw nptv6 all list
+ atf_check jexec ${jail} ipfw nptv6 all destroy
+
+ # This should also work, although it's not encouraged
+ atf_check -e match:"use prefixlen instead" \
+ jexec ${jail} ipfw nptv6 ${rule} create \
+ int_prefix ${int}/64 ext_prefix ${ext}/64
+ atf_check -o inline:\
+"nptv6 $rule int_prefix $int ext_prefix $ext prefixlen 64\n" \
+ jexec ${jail} ipfw nptv6 all list
+ atf_check jexec ${jail} ipfw nptv6 all destroy
+
+ # These should all fail
+ atf_check -s not-exit:0 -e match:"one ext_prefix or ext_if" \
+ jexec ${jail} ipfw nptv6 ${rule} create \
+ int_prefix ${int} ext_prefix ${ext} ext_if ${epair}a
+ atf_check -o empty jexec ${jail} ipfw nptv6 all list
+
+ atf_check -s not-exit:0 -e match:"one ext_prefix or ext_if" \
+ jexec ${jail} ipfw nptv6 ${rule} create \
+ int_prefix ${int} ext_if ${epair}a ext_prefix ${ext}
+ atf_check -o empty jexec ${jail} ipfw nptv6 all list
+
+ atf_check -s not-exit:0 -e match:"prefix length mismatch" \
+ jexec ${jail} ipfw nptv6 ${rule} create \
+ int_prefix ${int}/48 ext_prefix ${ext}/64
+ atf_check -o empty jexec ${jail} ipfw nptv6 all list
+
+ atf_check -s not-exit:0 -e match:"prefix length mismatch" \
+ jexec ${jail} ipfw nptv6 ${rule} create \
+ int_prefix ${int}/64 ext_prefix ${ext}/64 prefixlen 48
+ atf_check -o empty jexec ${jail} ipfw nptv6 all list
+
+ atf_check -s not-exit:0 -e match:"prefix length mismatch" \
+ jexec ${jail} ipfw nptv6 ${rule} create \
+ int_prefix ${int}/64 ext_prefix ${ext} prefixlen 48
+ atf_check -o empty jexec ${jail} ipfw nptv6 all list
+
+ atf_check -s not-exit:0 -e match:"prefix length mismatch" \
+ jexec ${jail} ipfw nptv6 ${rule} create \
+ int_prefix ${int} ext_prefix ${ext}/64 prefixlen 48
+ atf_check -o empty jexec ${jail} ipfw nptv6 all list
+
+ atf_check -s not-exit:0 -e match:"prefix length mismatch" \
+ jexec ${jail} ipfw nptv6 ${rule} create \
+ int_prefix ${int}/64 ext_if ${epair}a prefixlen 48
+ atf_check -o empty jexec ${jail} ipfw nptv6 all list
+}
+nptv6_cleanup()
+{
+ vnet_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case nptv6
+}
diff --git a/sbin/ipfw/tests/test_add_rule.py b/sbin/ipfw/tests/test_add_rule.py
index 60c8cebaceaa..c2c4bf0b360c 100755
--- a/sbin/ipfw/tests/test_add_rule.py
+++ b/sbin/ipfw/tests/test_add_rule.py
@@ -36,6 +36,7 @@ from atf_python.sys.netpfil.ipfw.insns import InsnProb
from atf_python.sys.netpfil.ipfw.insns import InsnProto
from atf_python.sys.netpfil.ipfw.insns import InsnReject
from atf_python.sys.netpfil.ipfw.insns import InsnTable
+from atf_python.sys.netpfil.ipfw.insns import InsnU32
from atf_python.sys.netpfil.ipfw.insns import IpFwOpcode
from atf_python.sys.netpfil.ipfw.ioctl import CTlv
from atf_python.sys.netpfil.ipfw.ioctl import CTlvRule
@@ -152,8 +153,8 @@ class TestAddRule(BaseTest):
NTlv(IpFwTlvType.IPFW_TLV_TBL_NAME, idx=2, name="BBB"),
],
"insns": [
- InsnTable(IpFwOpcode.O_IP_SRC_LOOKUP, arg1=1),
- InsnTable(IpFwOpcode.O_IP_DST_LOOKUP, arg1=2),
+ InsnU32(IpFwOpcode.O_IP_SRC_LOOKUP, u32=1),
+ InsnU32(IpFwOpcode.O_IP_DST_LOOKUP, u32=2),
InsnEmpty(IpFwOpcode.O_ACCEPT),
],
},
@@ -182,7 +183,7 @@ class TestAddRule(BaseTest):
],
"insns": [
InsnIp(IpFwOpcode.O_IP_DST, ip="1.2.3.4"),
- Insn(IpFwOpcode.O_EXTERNAL_ACTION, arg1=1),
+ InsnU32(IpFwOpcode.O_EXTERNAL_ACTION, u32=1),
Insn(IpFwOpcode.O_EXTERNAL_DATA, arg1=123),
],
},
@@ -199,8 +200,8 @@ class TestAddRule(BaseTest):
],
"insns": [
InsnIp(IpFwOpcode.O_IP_DST, ip="1.2.3.4"),
- Insn(IpFwOpcode.O_EXTERNAL_ACTION, arg1=1),
- Insn(IpFwOpcode.O_EXTERNAL_INSTANCE, arg1=2),
+ InsnU32(IpFwOpcode.O_EXTERNAL_ACTION, u32=1),
+ InsnU32(IpFwOpcode.O_EXTERNAL_INSTANCE, u32=2),
],
},
},
@@ -227,7 +228,7 @@ class TestAddRule(BaseTest):
],
"insns": [
InsnComment(comment="test comment"),
- Insn(IpFwOpcode.O_CHECK_STATE, arg1=1),
+ InsnU32(IpFwOpcode.O_CHECK_STATE, u32=1),
],
},
},
@@ -241,9 +242,9 @@ class TestAddRule(BaseTest):
NTlv(IpFwTlvType.IPFW_TLV_STATE_NAME, idx=1, name="OUT"),
],
"insns": [
- Insn(IpFwOpcode.O_PROBE_STATE, arg1=1),
+ InsnU32(IpFwOpcode.O_PROBE_STATE, u32=1),
Insn(IpFwOpcode.O_PROTO, arg1=6),
- Insn(IpFwOpcode.O_KEEP_STATE, arg1=1),
+ InsnU32(IpFwOpcode.O_KEEP_STATE, u32=1),
InsnEmpty(IpFwOpcode.O_ACCEPT),
],
},
@@ -259,7 +260,7 @@ class TestAddRule(BaseTest):
],
"insns": [
Insn(IpFwOpcode.O_PROTO, arg1=6),
- Insn(IpFwOpcode.O_KEEP_STATE, arg1=1),
+ InsnU32(IpFwOpcode.O_KEEP_STATE, u32=1),
InsnEmpty(IpFwOpcode.O_ACCEPT),
],
},
@@ -370,7 +371,7 @@ class TestAddRule(BaseTest):
),
pytest.param(("pipe 42", Insn(IpFwOpcode.O_PIPE, arg1=42)), id="pipe_42"),
pytest.param(
- ("skipto 42", Insn(IpFwOpcode.O_SKIPTO, arg1=42)), id="skipto_42"
+ ("skipto 42", InsnU32(IpFwOpcode.O_SKIPTO, u32=42)), id="skipto_42"
),
pytest.param(
("netgraph 42", Insn(IpFwOpcode.O_NETGRAPH, arg1=42)), id="netgraph_42"
@@ -386,7 +387,7 @@ class TestAddRule(BaseTest):
),
pytest.param(("tee 42", Insn(IpFwOpcode.O_TEE, arg1=42)), id="tee_42"),
pytest.param(
- ("call 420", Insn(IpFwOpcode.O_CALLRETURN, arg1=420)), id="call_420"
+ ("call 420", InsnU32(IpFwOpcode.O_CALLRETURN, u32=420)), id="call_420"
),
# TOK_FORWARD
pytest.param(
@@ -400,7 +401,7 @@ class TestAddRule(BaseTest):
),
pytest.param(("reass", InsnEmpty(IpFwOpcode.O_REASS)), id="reass"),
pytest.param(
- ("return", InsnEmpty(IpFwOpcode.O_CALLRETURN, is_not=True)), id="return"
+ ("return", InsnU32(IpFwOpcode.O_CALLRETURN, is_not=True)), id="return"
),
],
)
diff --git a/sbin/kldstat/kldstat.c b/sbin/kldstat/kldstat.c
index 79c647576440..3a90f1c97eb4 100644
--- a/sbin/kldstat/kldstat.c
+++ b/sbin/kldstat/kldstat.c
@@ -35,7 +35,7 @@
#include <libutil.h>
#include <stdio.h>
#include <stdlib.h>
-#include <strings.h>
+#include <string.h>
#include <unistd.h>
#define PTR_WIDTH ((int)(sizeof(void *) * 2 + 2))
@@ -51,7 +51,7 @@ printmod(int modid)
{
struct module_stat stat;
- bzero(&stat, sizeof(stat));
+ memset(&stat, 0, sizeof(stat));
stat.version = sizeof(struct module_stat);
if (modstat(modid, &stat) < 0) {
warn("can't stat module id %d", modid);
diff --git a/sbin/mdconfig/tests/mdconfig_test.sh b/sbin/mdconfig/tests/mdconfig_test.sh
index ea87ff5d542d..cc29c188cbd8 100755
--- a/sbin/mdconfig/tests/mdconfig_test.sh
+++ b/sbin/mdconfig/tests/mdconfig_test.sh
@@ -274,22 +274,23 @@ attach_size_rounddown()
attach_size_rounddown_body()
{
local md
- local ss=8192
- local ms=$(($ss + 4096))
- local ms2=$((2 * $ss + 4096))
+ local pgsz=$(pagesize)
+ local ss=$(($pgsz * 2))
+ local ms=$(($ss + $pgsz))
+ local ms2=$((2 * $ss + $pgsz))
- # Use a sector size that's a likely multiple of PAGE_SIZE, as md(4)
+ # Use a sector size that's a multiple of the kernel page size, as md(4)
# expects that for swap MDs.
atf_check -s exit:0 -o save:mdconfig.out -e empty \
-x "mdconfig -a -t swap -S $ss -s ${ms}b"
md=$(cat mdconfig.out)
- # 12288 bytes should be rounded down to one sector.
- check_diskinfo "$md" 8192 1 $ss
+ # one sector plus one page should be rounded down to one sector.
+ check_diskinfo "$md" $ss 1 $ss
# Resize and verify that the new size was also rounded down.
atf_check -s exit:0 -o empty -e empty \
-x "mdconfig -r -u ${md#md} -s ${ms2}b"
- check_diskinfo "$md" 16384 2 $ss
+ check_diskinfo "$md" $((2 * $ss)) 2 $ss
}
attach_size_rounddown_cleanup()
{
diff --git a/sbin/mksnap_ffs/mksnap_ffs.c b/sbin/mksnap_ffs/mksnap_ffs.c
index 0d8e32a15ab3..58939cc69029 100644
--- a/sbin/mksnap_ffs/mksnap_ffs.c
+++ b/sbin/mksnap_ffs/mksnap_ffs.c
@@ -150,7 +150,7 @@ main(int argc, char **argv)
errx(1, "%s: Not a mount point", stfsbuf.f_mntonname);
}
if (cp != stfsbuf.f_mntonname)
- strlcpy(stfsbuf.f_mntonname, cp, sizeof(stfsbuf.f_mntonname));
+ memmove(stfsbuf.f_mntonname, cp, strlen(cp) + 1);
/*
* Having verified access to the directory in which the
diff --git a/sbin/mount/mount.8 b/sbin/mount/mount.8
index b584d71ea567..7bfc21ea41d5 100644
--- a/sbin/mount/mount.8
+++ b/sbin/mount/mount.8
@@ -28,7 +28,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd April 30, 2025
+.Dd July 16, 2025
.Dt MOUNT 8
.Os
.Sh NAME
@@ -80,7 +80,7 @@ Generate output via
.Xr libxo 3
in a selection of different human and machine readable formats.
See
-.Xr xo_parse_args 3
+.Xr xo_options 7
for details on command line arguments.
.It Fl a
All the file systems described in
@@ -573,7 +573,7 @@ support for a particular file system might be provided either on a static
.Xr acl 3 ,
.Xr getmntinfo 3 ,
.Xr libxo 3 ,
-.Xr xo_parse_args 3 ,
+.Xr xo_options 7 ,
.Xr cd9660 4 ,
.Xr devfs 4 ,
.Xr ext2fs 4 ,
diff --git a/sbin/mount_fusefs/Makefile b/sbin/mount_fusefs/Makefile
index e683b35f0c8a..a237ba99eb6b 100644
--- a/sbin/mount_fusefs/Makefile
+++ b/sbin/mount_fusefs/Makefile
@@ -20,7 +20,7 @@ DEBUG_FLAGS+= -DFUSE4BSD_VERSION="\"${F4BVERS}\""
PACKAGE=runtime
PROG= mount_fusefs
-MAN8= mount_fusefs.8
+MAN= mount_fusefs.8
LIBADD= util
.include <bsd.prog.mk>
diff --git a/sbin/mount_nullfs/mount_nullfs.8 b/sbin/mount_nullfs/mount_nullfs.8
index 17b1f45f5e42..b3cf57fd9dea 100644
--- a/sbin/mount_nullfs/mount_nullfs.8
+++ b/sbin/mount_nullfs/mount_nullfs.8
@@ -90,7 +90,7 @@ See the
.Xr mount 8
man page for possible options and their meanings.
Additionally the following option is supported:
-.Bl -tag -width nocache
+.Bl -tag -width nounixbypass
.It Cm nocache
Disable metadata caching in the null layer.
Some lower-layer file systems may force this option.
@@ -98,6 +98,32 @@ Depending on the access pattern,
this may result in increased lock contention.
.It Cm cache
Force enable metadata caching.
+.It Cm nounixbypass
+Disable bypassing
+.Xr unix 4
+socket files used for
+.Xr bind 2
+and
+.Xr connect 2 ,
+to the lower (mounted-from) filesystem layer.
+.Pp
+The effect is that lower and upper (bypassed) unix sockets
+are separate.
+.It Cm unixbypass
+Enable the bypass of unix socket file to lower filesystem layer.
+This is default.
+.Pp
+The effect is that
+.Xr bind 2
+and
+.Xr connect 2
+operations on a unix socket done from either the upper (nullfs) or lower
+layer path are performed on same unix socket.
+For instance, if a server
+.Xr bind 2
+is done on a socket in the lower layer, then
+.Xr connect 2
+on the socket file accessed via the nullfs mount, connects to the server.
.El
.El
.Pp
diff --git a/sbin/nvmecontrol/modules/Makefile b/sbin/nvmecontrol/modules/Makefile
index 70d1ba40a1e2..f3c3572acb34 100644
--- a/sbin/nvmecontrol/modules/Makefile
+++ b/sbin/nvmecontrol/modules/Makefile
@@ -1,3 +1,6 @@
-SUBDIR= intel wdc samsung
+SUBDIR= intel
+SUBDIR+=micron
+SUBDIR+=samsung
+SUBDIR+=wdc
.include <bsd.subdir.mk>
diff --git a/sbin/nvmecontrol/modules/intel/intel.c b/sbin/nvmecontrol/modules/intel/intel.c
index 4229a48e4153..6ffe2c4c1563 100644
--- a/sbin/nvmecontrol/modules/intel/intel.c
+++ b/sbin/nvmecontrol/modules/intel/intel.c
@@ -195,6 +195,18 @@ print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *b
}
}
+static void
+print_intel_drive_marketing_name(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
+{
+ const char *p = buf;
+
+ printf("Intel Drive Marketing Name Log\n");
+ printf("=======================\n");
+ printf("%.*s\n", 29, p);
+}
+
+#define INTEL_LOG_DRIVE_MARKETING_NAME 0xdd
+
NVME_LOGPAGE(intel_temp,
INTEL_LOG_TEMP_STATS, "intel", "Temperature Stats",
print_intel_temp_stats, sizeof(struct intel_log_temp_stats));
@@ -207,3 +219,6 @@ NVME_LOGPAGE(intel_wlat,
NVME_LOGPAGE(intel_smart, /* Note: Samsung and Micron also use this */
INTEL_LOG_ADD_SMART, "intel", "Extra Health/SMART Data",
print_intel_add_smart, DEFAULT_SIZE);
+NVME_LOGPAGE(intel_dmn,
+ INTEL_LOG_DRIVE_MARKETING_NAME, "intel", "Drive Marketing Name Log",
+ print_intel_drive_marketing_name, DEFAULT_SIZE);
diff --git a/sbin/nvmecontrol/modules/micron/Makefile b/sbin/nvmecontrol/modules/micron/Makefile
new file mode 100644
index 000000000000..3cefd455f711
--- /dev/null
+++ b/sbin/nvmecontrol/modules/micron/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+LIB= micron
+SRCS= micron.c
+
+.include <bsd.lib.mk>
diff --git a/sbin/nvmecontrol/modules/micron/micron.c b/sbin/nvmecontrol/modules/micron/micron.c
new file mode 100644
index 000000000000..2d4731e7da47
--- /dev/null
+++ b/sbin/nvmecontrol/modules/micron/micron.c
@@ -0,0 +1,129 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2022 Wanpeng Qian <wanpengqian@gmail.com>
+ *
+ * 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 <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/endian.h>
+
+#include "nvmecontrol.h"
+
+static void
+print_micron_unique_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
+{
+ uint8_t *walker = buf;
+ uint8_t *end = walker + 150;
+ const char *name;
+ uint64_t raw;
+ uint8_t normalized;
+
+ static struct kv_name kv[] =
+ {
+ { 0xf9, "NAND Writes 1GiB" },
+ { 0xfa, "NAND Reads 1GiB" },
+ { 0xea, "Thermal Throttle Status" },
+ { 0xe7, "Temperature" },
+ { 0xe8, "Power Consumption" },
+ { 0xaf, "Power Loss Protection" },
+ };
+
+ printf("Vendor Unique SMART Information\n");
+ printf("=========================\n");
+ /*
+ * walker[0] = Key
+ * walker[1,2] = reserved
+ * walker[3] = Normalized Value
+ * walker[4] = reserved
+ * walker[5..10] = Little Endian Raw value
+ * (or other represenations)
+ * walker[11] = reserved
+ */
+ while (walker < end) {
+ name = kv_lookup(kv, nitems(kv), *walker);
+ normalized = walker[3];
+ raw = le48dec(walker + 5);
+ switch (*walker){
+ case 0:
+ break;
+ case 0xf9:
+ /* FALLTHOUGH */
+ case 0xfa:
+ printf("%2X %-24s: %ju GiB\n", *walker, name, (uintmax_t)raw);
+ break;
+ case 0xea:
+ printf("%2X %-24s:", *walker, name);
+ if (*(walker + 5) == 0)
+ printf(" inactive\n");
+ if (*(walker + 5) == 1)
+ printf(" active, total throttling time %u mins\n", le32dec(walker + 6));
+ break;
+ case 0xe7:
+ printf("%2X %-24s: max ", *walker, name);
+ print_temp_C(le16dec(walker + 5));
+ printf(" : min ");
+ print_temp_C(le16dec(walker + 7));
+ printf(" : cur ");
+ print_temp_C(le16dec(walker + 9));
+ break;
+ case 0xe8:
+ printf("%2X %-24s: max %u W, min %u W, ave %u W\n",
+ *walker, name, le16dec(walker + 5), le16dec(walker + 7), le16dec(walker + 9));
+ break;
+ case 0xaf:
+ printf("%2X %-24s:", *walker, name);
+ if (normalized == 100)
+ printf(" success");
+ if (normalized == 0)
+ printf(" failed");
+ printf(" %3d\n", normalized);
+ break;
+ default:
+ printf("%2X %-24s: %3d %ju\n",
+ *walker, name, normalized, (uintmax_t)raw);
+ break;
+ }
+ walker += 12;
+ }
+}
+
+#define MICRON_LOG_UNIQUE_SMART 0xca
+
+NVME_LOGPAGE(micron_smart,
+ MICRON_LOG_UNIQUE_SMART, "micron", "Vendor Unique SMART Information",
+ print_micron_unique_smart, DEFAULT_SIZE);
diff --git a/sbin/nvmecontrol/nvmecontrol.8 b/sbin/nvmecontrol/nvmecontrol.8
index 624a0c93719b..dc757bcf90c3 100644
--- a/sbin/nvmecontrol/nvmecontrol.8
+++ b/sbin/nvmecontrol/nvmecontrol.8
@@ -303,7 +303,8 @@ data associated with that drive.
.El
.Ss logpage
The logpage command knows how to print log pages of various types.
-It also knows about vendor specific log pages from hgst/wdc, samsung and intel.
+It also knows about vendor specific log pages from HGST/WDC, Samsung,
+Micron and Intel.
Note that some vendors use the same log page numbers for different data.
.Pp
.Bl -tag -compact -width "Page 0x00"
@@ -328,13 +329,15 @@ Advanced SMART information (WDC/HGST)
.It Dv Page 0xc1
Read latency stats (Intel)
.It Dv Page 0xc2
-Wite latency stats (Intel)
+Write latency stats (Intel)
.It Dv Page 0xc5
Temperature stats (Intel)
.It Dv Page 0xca
Advanced SMART information (Intel)
.It Dv Page 0xca
Extended SMART information (Samsung)
+.It Dv Page 0xca
+Vendor Unique SMART information (Micron)
.El
.Pp
Specifying
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index 9a917d1d8464..0f7702fc4630 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -166,8 +166,8 @@ struct node_gid {
};
struct node_icmp {
- u_int8_t code;
- u_int8_t type;
+ uint16_t code;
+ uint16_t type;
u_int8_t proto;
struct node_icmp *next;
struct node_icmp *tail;
@@ -238,6 +238,7 @@ static struct pool_opts {
#define POM_TYPE 0x01
#define POM_STICKYADDRESS 0x02
#define POM_ENDPI 0x04
+#define POM_IPV6NH 0x08
u_int8_t opts;
int type;
int staticport;
@@ -249,7 +250,7 @@ struct redirspec {
struct node_host *host;
struct range rport;
struct pool_opts pool_opts;
- int af;
+ sa_family_t af;
bool binat;
};
@@ -266,7 +267,7 @@ static struct filter_opts {
#define FOM_SETTOS 0x0100
#define FOM_SCRUB_TCP 0x0200
#define FOM_SETPRIO 0x0400
-#define FOM_ONCE 0x1000 /* not yet implemmented */
+#define FOM_ONCE 0x1000
#define FOM_PRIO 0x2000
#define FOM_SETDELAY 0x4000
#define FOM_FRAGCACHE 0x8000 /* does not exist in OpenBSD */
@@ -367,11 +368,12 @@ static struct node_fairq_opts fairq_opts;
static struct node_state_opt *keep_state_defaults = NULL;
static struct pfctl_watermarks syncookie_opts;
+int validate_range(uint8_t, uint16_t, uint16_t);
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 pfctl_rule *, int);
-int filter_consistent(struct pfctl_rule *, int);
+int rule_consistent(struct pfctl_rule *);
+int filter_consistent(struct pfctl_rule *);
int nat_consistent(struct pfctl_rule *);
int rdr_consistent(struct pfctl_rule *);
int process_tabledef(char *, struct table_opts *, int);
@@ -392,7 +394,7 @@ void expand_eth_rule(struct pfctl_eth_rule *,
int apply_rdr_ports(struct pfctl_rule *r, struct pfctl_pool *, struct redirspec *);
int apply_nat_ports(struct pfctl_pool *, struct redirspec *);
int apply_redirspec(struct pfctl_pool *, struct redirspec *);
-int check_binat_redirspec(struct node_host *, struct pfctl_rule *, int);
+int check_binat_redirspec(struct node_host *, struct pfctl_rule *, sa_family_t);
void add_binat_rdr_rule(struct pfctl_rule *, struct redirspec *,
struct node_host *, struct pfctl_rule *, struct redirspec **,
struct node_host **);
@@ -401,7 +403,7 @@ void expand_rule(struct pfctl_rule *, bool, struct node_if *,
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_if *,
- struct node_icmp *, const char *);
+ struct node_icmp *);
int expand_altq(struct pf_altq *, struct node_if *,
struct node_queue *, struct node_queue_bw bwspec,
struct node_queue_opt *);
@@ -418,6 +420,8 @@ int rt_tableid_max(void);
void mv_rules(struct pfctl_ruleset *, struct pfctl_ruleset *);
void mv_eth_rules(struct pfctl_eth_ruleset *, struct pfctl_eth_ruleset *);
+void mv_tables(struct pfctl *, struct pfr_ktablehead *,
+ struct pfctl_anchor *, struct pfctl_anchor *);
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);
@@ -428,6 +432,7 @@ int filteropts_to_rule(struct pfctl_rule *, struct filter_opts *);
struct node_mac* node_mac_from_string(const char *);
struct node_mac* node_mac_from_string_masklen(const char *, int);
struct node_mac* node_mac_from_string_mask(const char *, const char *);
+static bool pfctl_setup_anchor(struct pfctl_rule *, struct pfctl *, char *);
static TAILQ_HEAD(loadanchorshead, loadanchors)
loadanchorshead = TAILQ_HEAD_INITIALIZER(loadanchorshead);
@@ -537,12 +542,12 @@ int parseport(char *, struct range *r, int);
%token ALTQ CBQ CODEL PRIQ HFSC FAIRQ BANDWIDTH TBRSIZE LINKSHARE REALTIME
%token UPPERLIMIT QUEUE PRIORITY QLIMIT HOGS BUCKETS RTABLE TARGET INTERVAL
%token DNPIPE DNQUEUE RIDENTIFIER
-%token LOAD RULESET_OPTIMIZATION PRIO
+%token LOAD RULESET_OPTIMIZATION PRIO ONCE
%token STICKYADDRESS ENDPI MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW ALLOW_RELATED
%token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS
%token DIVERTTO DIVERTREPLY BRIDGE_TO RECEIVEDON NE LE GE AFTO NATTO RDRTO
-%token BINATTO MAXPKTRATE MAXPKTSIZE
+%token BINATTO MAXPKTRATE MAXPKTSIZE IPV6NH
%token <v.string> STRING
%token <v.number> NUMBER
%token <v.i> PORTBINARY
@@ -947,6 +952,7 @@ anchorname : STRING {
pfa_anchorlist : /* empty */
| pfa_anchorlist '\n'
+ | pfa_anchorlist tabledef '\n'
| pfa_anchorlist pfrule '\n'
| pfa_anchorlist anchorrule '\n'
| pfa_anchorlist include '\n'
@@ -972,7 +978,7 @@ pfa_anchor : '{'
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");
+ err(1, "pfa_anchor: pf_find_or_create_ruleset (%s)", ta);
pf->astack[pf->asd] = rs->anchor;
pf->anchor = rs->anchor;
} '\n' pfa_anchorlist '}'
@@ -997,43 +1003,9 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
}
pfctl_init_rule(&r);
+ if (! pfctl_setup_anchor(&r, pf, $2))
+ YYERROR;
- if (pf->astack[pf->asd + 1]) {
- if ($2 && strchr($2, '/') != NULL) {
- free($2);
- yyerror("anchor paths containing '/' "
- "cannot be used for inline anchors.");
- YYERROR;
- }
-
- /* Move inline rules into relative location. */
- pfctl_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;
@@ -1069,8 +1041,7 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
expand_rule(&r, false, $5, NULL, NULL, NULL,
$7, $8.src_os, $8.src.host, $8.src.port, $8.dst.host,
- $8.dst.port, $9.uid, $9.gid, $9.rcv, $9.icmpspec,
- pf->astack[pf->asd + 1] ? pf->alast->name : $2);
+ $8.dst.port, $9.uid, $9.gid, $9.rcv, $9.icmpspec);
free($2);
pf->astack[pf->asd + 1] = NULL;
}
@@ -1083,6 +1054,8 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
}
pfctl_init_rule(&r);
+ if (! pfctl_setup_anchor(&r, pf, $2))
+ YYERROR;
r.action = PF_NAT;
r.af = $4;
@@ -1093,7 +1066,7 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
expand_rule(&r, false, $3, NULL, NULL, NULL,
$5, $6.src_os, $6.src.host, $6.src.port, $6.dst.host,
- $6.dst.port, 0, 0, 0, 0, $2);
+ $6.dst.port, 0, 0, 0, 0);
free($2);
}
| RDRANCHOR string interface af proto fromto rtable {
@@ -1105,6 +1078,8 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
}
pfctl_init_rule(&r);
+ if (! pfctl_setup_anchor(&r, pf, $2))
+ YYERROR;
r.action = PF_RDR;
r.af = $4;
@@ -1136,7 +1111,7 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
expand_rule(&r, false, $3, NULL, NULL, NULL,
$5, $6.src_os, $6.src.host, $6.src.port, $6.dst.host,
- $6.dst.port, 0, 0, 0, 0, $2);
+ $6.dst.port, 0, 0, 0, 0);
free($2);
}
| BINATANCHOR string interface af proto fromto rtable {
@@ -1148,6 +1123,8 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
}
pfctl_init_rule(&r);
+ if (! pfctl_setup_anchor(&r, pf, $2))
+ YYERROR;
r.action = PF_BINAT;
r.af = $4;
@@ -1172,7 +1149,7 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto
decide_address_family($6.src.host, &r.af);
decide_address_family($6.dst.host, &r.af);
- pfctl_append_rule(pf, &r, $2);
+ pfctl_append_rule(pf, &r);
free($2);
}
;
@@ -1459,7 +1436,7 @@ scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts
expand_rule(&r, false, $4, NULL, NULL, NULL,
$6, $7.src_os, $7.src.host, $7.src.port, $7.dst.host,
- $7.dst.port, NULL, NULL, NULL, NULL, "");
+ $7.dst.port, NULL, NULL, NULL, NULL);
}
;
@@ -1624,7 +1601,7 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts {
if (h != NULL)
expand_rule(&r, false, j, NULL, NULL,
NULL, NULL, NULL, h, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, "");
+ NULL, NULL, NULL, NULL, NULL);
if ((i->ifa_flags & IFF_LOOPBACK) == 0) {
bzero(&r, sizeof(r));
@@ -1647,7 +1624,7 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts {
expand_rule(&r, false, NULL,
NULL, NULL, NULL, NULL,
NULL, h, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, "");
+ NULL, NULL, NULL, NULL);
} else
free(hh);
}
@@ -2647,13 +2624,16 @@ pfrule : action dir logquick interface route af proto fromto
YYERROR;
}
r.rt = $5.rt;
- decide_address_family($5.redirspec->host, &r.af);
- if (!(r.rule_flag & PFRULE_AFTO))
- remove_invalid_hosts(&($5.redirspec->host), &r.af);
- if ($5.redirspec->host == NULL) {
- yyerror("no routing address with "
- "matching address family found.");
- YYERROR;
+
+ if (!($5.redirspec->pool_opts.opts & PF_POOL_IPV6NH)) {
+ decide_address_family($5.redirspec->host, &r.af);
+ if (!(r.rule_flag & PFRULE_AFTO))
+ remove_invalid_hosts(&($5.redirspec->host), &r.af);
+ if ($5.redirspec->host == NULL) {
+ yyerror("no routing address with "
+ "matching address family found.");
+ YYERROR;
+ }
}
}
#ifdef __FreeBSD__
@@ -2727,7 +2707,7 @@ pfrule : action dir logquick interface route af proto fromto
expand_rule(&r, false, $4, $9.nat, $9.rdr, $5.redirspec,
$7, $8.src_os, $8.src.host, $8.src.port, $8.dst.host,
- $8.dst.port, $9.uid, $9.gid, $9.rcv, $9.icmpspec, "");
+ $8.dst.port, $9.uid, $9.gid, $9.rcv, $9.icmpspec);
}
;
@@ -2977,7 +2957,8 @@ filter_opt : USER uids {
filter_opts.nat = $4;
filter_opts.nat->af = $2;
- if ($4->af && $4->af != $2) {
+ remove_invalid_hosts(&($4->host), &(filter_opts.nat->af));
+ if ($4->host == NULL) {
yyerror("af-to addresses must be in the "
"target address family");
YYERROR;
@@ -2997,8 +2978,9 @@ filter_opt : USER uids {
filter_opts.nat->af = $2;
filter_opts.rdr = $6;
filter_opts.rdr->af = $2;
- if (($4->af && $4->host->af != $2) ||
- ($6->af && $6->host->af != $2)) {
+ remove_invalid_hosts(&($4->host), &(filter_opts.nat->af));
+ remove_invalid_hosts(&($6->host), &(filter_opts.rdr->af));
+ if ($4->host == NULL || $6->host == NULL) {
yyerror("af-to addresses must be in the "
"target address family");
YYERROR;
@@ -3025,6 +3007,9 @@ filter_opt : USER uids {
}
filter_opts.max_pkt_size = $2;
}
+ | ONCE {
+ filter_opts.marker |= FOM_ONCE;
+ }
| filter_sets
;
@@ -3231,8 +3216,7 @@ logopts : logopt { $$ = $1; }
logopt : ALL { $$.log = PF_LOG_ALL; $$.logif = 0; }
| MATCHES { $$.log = PF_LOG_MATCHES; $$.logif = 0; }
- | USER { $$.log = PF_LOG_SOCKET_LOOKUP; $$.logif = 0; }
- | GROUP { $$.log = PF_LOG_SOCKET_LOOKUP; $$.logif = 0; }
+ | USER { $$.log = PF_LOG_USER; $$.logif = 0; }
| TO string {
const char *errstr;
u_int i;
@@ -3825,9 +3809,14 @@ port_item : portrange {
err(1, "port_item: calloc");
$$->port[0] = $1.a;
$$->port[1] = $1.b;
- if ($1.t)
+ if ($1.t) {
$$->op = PF_OP_RRG;
- else
+ if (validate_range($$->op, $$->port[0],
+ $$->port[1])) {
+ yyerror("invalid port range");
+ YYERROR;
+ }
+ } else
$$->op = PF_OP_EQ;
$$->next = NULL;
$$->tail = $$;
@@ -3844,6 +3833,10 @@ port_item : portrange {
$$->port[0] = $2.a;
$$->port[1] = $2.b;
$$->op = $1;
+ if (validate_range($$->op, $$->port[0], $$->port[1])) {
+ yyerror("invalid port range");
+ YYERROR;
+ }
$$->next = NULL;
$$->tail = $$;
}
@@ -3859,6 +3852,10 @@ port_item : portrange {
$$->port[0] = $1.a;
$$->port[1] = $3.a;
$$->op = $2;
+ if (validate_range($$->op, $$->port[0], $$->port[1])) {
+ yyerror("invalid port range");
+ YYERROR;
+ }
$$->next = NULL;
$$->tail = $$;
}
@@ -4661,6 +4658,14 @@ pool_opt : BITMASK {
pool_opts.marker |= POM_ENDPI;
pool_opts.opts |= PF_POOL_ENDPI;
}
+ | IPV6NH {
+ if (pool_opts.marker & POM_IPV6NH) {
+ yyerror("prefer-ipv6-nexthop cannot be redefined");
+ YYERROR;
+ }
+ pool_opts.marker |= POM_IPV6NH;
+ pool_opts.opts |= PF_POOL_IPV6NH;
+ }
| MAPEPORTSET number '/' number '/' number {
if (pool_opts.mape.offset) {
yyerror("map-e-portset cannot be redefined");
@@ -4800,6 +4805,12 @@ natrule : nataction interface af proto fromto tag tagged rtable
"address'");
YYERROR;
}
+ if ($9->pool_opts.opts & PF_POOL_IPV6NH) {
+ yyerror("The prefer-ipv6-nexthop option "
+ "can't be used for nat/rdr/binat pools"
+ );
+ YYERROR;
+ }
if (!r.af && ! $9->host->ifindex)
r.af = $9->host->af;
@@ -4831,7 +4842,7 @@ natrule : nataction interface af proto fromto tag tagged rtable
expand_rule(&r, false, $2, NULL, $9, NULL, $4,
$5.src_os, $5.src.host, $5.src.port, $5.dst.host,
- $5.dst.port, 0, 0, 0, 0, "");
+ $5.dst.port, 0, 0, 0, 0);
}
;
@@ -4839,7 +4850,7 @@ binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag
tagged rtable binat_redirspec
{
struct pfctl_rule binat;
- struct pf_pooladdr *pa;
+ struct pfctl_pooladdr *pa;
if (check_rulestate(PFCTL_STATE_NAT))
YYERROR;
@@ -4998,18 +5009,19 @@ binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag
YYERROR;
}
- pa = calloc(1, sizeof(struct pf_pooladdr));
+ pa = calloc(1, sizeof(struct pfctl_pooladdr));
if (pa == NULL)
err(1, "binat: calloc");
pa->addr = $13->host->addr;
pa->ifname[0] = 0;
+ pa->af = $13->host->af;
TAILQ_INSERT_TAIL(&binat.rdr.list,
pa, entries);
free($13);
}
- pfctl_append_rule(pf, &binat, "");
+ pfctl_append_rule(pf, &binat);
}
;
@@ -5060,13 +5072,6 @@ route_host : STRING {
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;
@@ -5197,6 +5202,19 @@ yyerror(const char *fmt, ...)
}
int
+validate_range(uint8_t op, uint16_t p1, uint16_t p2)
+{
+ uint16_t a = ntohs(p1);
+ uint16_t b = ntohs(p2);
+
+ if ((op == PF_OP_RRG && a > b) || /* 34:12, i.e. none */
+ (op == PF_OP_IRG && a >= b) || /* 34><12, i.e. none */
+ (op == PF_OP_XRG && a > b)) /* 34<>22, i.e. all */
+ return 1;
+ return 0;
+}
+
+int
disallow_table(struct node_host *h, const char *fmt)
{
for (; h != NULL; h = h->next)
@@ -5230,7 +5248,7 @@ disallow_alias(struct node_host *h, const char *fmt)
}
int
-rule_consistent(struct pfctl_rule *r, int anchor_call)
+rule_consistent(struct pfctl_rule *r)
{
int problems = 0;
@@ -5240,7 +5258,7 @@ rule_consistent(struct pfctl_rule *r, int anchor_call)
case PF_DROP:
case PF_SCRUB:
case PF_NOSCRUB:
- problems = filter_consistent(r, anchor_call);
+ problems = filter_consistent(r);
break;
case PF_NAT:
case PF_NONAT:
@@ -5259,7 +5277,7 @@ rule_consistent(struct pfctl_rule *r, int anchor_call)
}
int
-filter_consistent(struct pfctl_rule *r, int anchor_call)
+filter_consistent(struct pfctl_rule *r)
{
int problems = 0;
@@ -5324,6 +5342,10 @@ filter_consistent(struct pfctl_rule *r, int anchor_call)
"synproxy state or modulate state");
problems++;
}
+ if ((r->keep_state == PF_STATE_SYNPROXY) && (r->direction != PF_IN))
+ fprintf(stderr, "%s:%d: warning: "
+ "synproxy used for inbound rules only, "
+ "ignored for outbound\n", file->name, yylval.lineno);
if (r->rule_flag & PFRULE_AFTO && r->rt) {
if (r->rt != PF_ROUTETO && r->rt != PF_REPLYTO) {
yyerror("dup-to "
@@ -5412,6 +5434,7 @@ process_tabledef(char *name, struct table_opts *opts, int popts)
{
struct pfr_buffer ab;
struct node_tinit *ti;
+ struct pfr_uktable *ukt;
unsigned long maxcount;
size_t s = sizeof(maxcount);
@@ -5444,9 +5467,23 @@ process_tabledef(char *name, struct table_opts *opts, int popts)
else if (pf->opts & PF_OPT_VERBOSE)
fprintf(stderr, "%s:%d: skipping duplicate table checks"
" for <%s>\n", file->name, yylval.lineno, name);
+ /*
+ * postpone definition of non-root tables to moment
+ * when path is fully resolved.
+ */
+ if (pf->asd > 0) {
+ ukt = calloc(1, sizeof(struct pfr_uktable));
+ if (ukt == NULL) {
+ DBGPRINT(
+ "%s:%d: not enough memory for <%s>\n", file->name,
+ yylval.lineno, name);
+ goto _error;
+ }
+ } else
+ ukt = NULL;
if (!(pf->opts & PF_OPT_NOACTION) &&
pfctl_define_table(name, opts->flags, opts->init_addr,
- pf->anchor->path, &ab, pf->anchor->ruleset.tticket)) {
+ pf->anchor->path, &ab, pf->anchor->ruleset.tticket, ukt)) {
if (sysctlbyname("net.pf.request_maxcount", &maxcount, &s,
NULL, 0) == -1)
@@ -5462,6 +5499,28 @@ process_tabledef(char *name, struct table_opts *opts, int popts)
goto _error;
}
+
+ if (ukt != NULL) {
+ ukt->pfrukt_init_addr = opts->init_addr;
+ if (RB_INSERT(pfr_ktablehead, &pfr_ktables,
+ &ukt->pfrukt_kt) != NULL) {
+ /*
+ * I think this should not happen, because
+ * pfctl_define_table() above does the same check
+ * effectively.
+ */
+ DBGPRINT(
+ "%s:%d table %s already exists in %s\n",
+ file->name, yylval.lineno,
+ ukt->pfrukt_name, pf->anchor->path);
+ free(ukt);
+ goto _error;
+ }
+ DBGPRINT("%s %s@%s inserted to tree\n",
+ __func__, ukt->pfrukt_name, pf->anchor->path);
+ } else
+ DBGPRINT("%s ukt is null\n", __func__);
+
pf->tdirty = 1;
pfr_buf_clear(&ab);
return (0);
@@ -6014,8 +6073,14 @@ apply_rdr_ports(struct pfctl_rule *r, struct pfctl_pool *rpool, struct redirspec
if (!rs->rport.b && rs->rport.t) {
rpool->proxy_port[1] = ntohs(rs->rport.a) +
(ntohs(r->dst.port[1]) - ntohs(r->dst.port[0]));
- } else
+ } else {
+ if (validate_range(rs->rport.t, rs->rport.a,
+ rs->rport.b)) {
+ yyerror("invalid rdr-to port range");
+ return (1);
+ }
r->rdr.proxy_port[1] = ntohs(rs->rport.b);
+ }
if (rs->pool_opts.staticport) {
yyerror("the 'static-port' option is only valid with nat rules");
@@ -6079,7 +6144,7 @@ int
apply_redirspec(struct pfctl_pool *rpool, struct redirspec *rs)
{
struct node_host *h;
- struct pf_pooladdr *pa;
+ struct pfctl_pooladdr *pa;
if (rs == NULL)
return 0;
@@ -6119,10 +6184,11 @@ apply_redirspec(struct pfctl_pool *rpool, struct redirspec *rs)
sizeof(struct pf_poolhashkey));
for (h = rs->host; h != NULL; h = h->next) {
- pa = calloc(1, sizeof(struct pf_pooladdr));
+ pa = calloc(1, sizeof(struct pfctl_pooladdr));
if (pa == NULL)
err(1, "%s: calloc", __func__);
pa->addr = h->addr;
+ pa->af = h->af;
if (h->ifname != NULL) {
if (strlcpy(pa->ifname, h->ifname,
sizeof(pa->ifname)) >= sizeof(pa->ifname))
@@ -6136,9 +6202,10 @@ apply_redirspec(struct pfctl_pool *rpool, struct redirspec *rs)
}
int
-check_binat_redirspec(struct node_host *src_host, struct pfctl_rule *r, int af)
+check_binat_redirspec(struct node_host *src_host, struct pfctl_rule *r,
+ sa_family_t af)
{
- struct pf_pooladdr *nat_pool = TAILQ_FIRST(&(r->nat.list));
+ struct pfctl_pooladdr *nat_pool = TAILQ_FIRST(&(r->nat.list));
int error = 0;
/* XXX: FreeBSD allows syntax like "{ host1 host2 }" for redirection
@@ -6211,7 +6278,7 @@ add_binat_rdr_rule(
/*
* We're copying the whole rule, but we must re-init redir pools.
- * FreeBSD uses lists of pf_pooladdr, we can't just overwrite them.
+ * FreeBSD uses lists of pfctl_pooladdr, we can't just overwrite them.
*/
bcopy(binat_rule, rdr_rule, sizeof(struct pfctl_rule));
TAILQ_INIT(&(rdr_rule->rdr.list));
@@ -6250,7 +6317,7 @@ expand_rule(struct pfctl_rule *r, bool keeprule,
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_if *rcv, struct node_icmp *icmp_types, const char *anchor_call)
+ struct node_if *rcv, struct node_icmp *icmp_types)
{
sa_family_t af = r->af;
int added = 0, error = 0;
@@ -6417,11 +6484,11 @@ expand_rule(struct pfctl_rule *r, bool keeprule,
error += check_binat_redirspec(src_host, r, af);
}
- if (rule_consistent(r, anchor_call[0]) < 0 || error)
+ if (rule_consistent(r) < 0 || error)
yyerror("skipping rule due to errors");
else {
r->nr = pf->astack[pf->asd]->match++;
- pfctl_append_rule(pf, r, anchor_call);
+ pfctl_append_rule(pf, r);
added++;
}
@@ -6437,8 +6504,7 @@ expand_rule(struct pfctl_rule *r, bool keeprule,
expand_rule(&rdr_rule, true, interface, NULL, rdr_redirspec,
NULL, proto, src_os, dst_host, dst_port,
- rdr_dst_host, src_port, uid, gid, rcv, icmp_type,
- "");
+ rdr_dst_host, src_port, uid, gid, rcv, icmp_type);
}
if (osrch && src_host->addr.type == PF_ADDR_DYNIFTL) {
@@ -6632,6 +6698,7 @@ lookup(char *s)
{ "no-route", NOROUTE},
{ "no-sync", NOSYNC},
{ "on", ON},
+ { "once", ONCE},
{ "optimization", OPTIMIZATION},
{ "os", OS},
{ "out", OUT},
@@ -6639,6 +6706,7 @@ lookup(char *s)
{ "pass", PASS},
{ "pflow", PFLOW},
{ "port", PORT},
+ { "prefer-ipv6-nexthop", IPV6NH},
{ "prio", PRIO},
{ "priority", PRIORITY},
{ "priq", PRIQ},
@@ -7038,7 +7106,7 @@ pushfile(const char *name, int secret)
free(nfile);
return (NULL);
}
- } else if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
+ } else if ((nfile->stream = pfctl_fopen(nfile->name, "r")) == NULL) {
warn("%s: %s", __func__, nfile->name);
free(nfile->name);
free(nfile);
@@ -7224,6 +7292,61 @@ mv_eth_rules(struct pfctl_eth_ruleset *src, struct pfctl_eth_ruleset *dst)
}
void
+mv_tables(struct pfctl *pf, struct pfr_ktablehead *ktables,
+ struct pfctl_anchor *a, struct pfctl_anchor *alast)
+{
+ struct pfr_ktable *kt, *kt_safe;
+ char new_path[PF_ANCHOR_MAXPATH];
+ char *path_cut;
+ int sz;
+ struct pfr_uktable *ukt;
+ SLIST_HEAD(, pfr_uktable) ukt_list;
+
+ /*
+ * Here we need to rename anchor path from temporal names such as
+ * _1/_2/foo to _1/bar/foo etc.
+ *
+ * This also means we need to remove and insert table to ktables
+ * tree as anchor path is being updated.
+ */
+ SLIST_INIT(&ukt_list);
+ DBGPRINT("%s [ %s ] (%s)\n", __func__, a->path, alast->path);
+ RB_FOREACH_SAFE(kt, pfr_ktablehead, ktables, kt_safe) {
+ path_cut = strstr(kt->pfrkt_anchor, alast->path);
+ if (path_cut != NULL) {
+ path_cut += strlen(alast->path);
+ if (*path_cut)
+ sz = snprintf(new_path, sizeof (new_path),
+ "%s%s", a->path, path_cut);
+ else
+ sz = snprintf(new_path, sizeof (new_path),
+ "%s", a->path);
+ if (sz >= sizeof (new_path))
+ errx(1, "new path is too long for %s@%s\n",
+ kt->pfrkt_name, kt->pfrkt_anchor);
+
+ DBGPRINT("%s %s@%s -> %s@%s\n", __func__,
+ kt->pfrkt_name, kt->pfrkt_anchor,
+ kt->pfrkt_name, new_path);
+ RB_REMOVE(pfr_ktablehead, ktables, kt);
+ strlcpy(kt->pfrkt_anchor, new_path,
+ sizeof(kt->pfrkt_anchor));
+ SLIST_INSERT_HEAD(&ukt_list, (struct pfr_uktable *)kt,
+ pfrukt_entry);
+ }
+ }
+
+ while ((ukt = SLIST_FIRST(&ukt_list)) != NULL) {
+ SLIST_REMOVE_HEAD(&ukt_list, pfrukt_entry);
+ if (RB_INSERT(pfr_ktablehead, ktables,
+ (struct pfr_ktable *)ukt) != NULL)
+ errx(1, "%s@%s exists already\n",
+ ukt->pfrukt_name,
+ ukt->pfrukt_anchor);
+ }
+}
+
+void
decide_address_family(struct node_host *n, sa_family_t *af)
{
if (*af != 0 || n == NULL)
@@ -7432,7 +7555,7 @@ parseport(char *port, struct range *r, int extensions)
}
int
-pfctl_load_anchors(int dev, struct pfctl *pf, struct pfr_buffer *trans)
+pfctl_load_anchors(int dev, struct pfctl *pf)
{
struct loadanchors *la;
@@ -7441,7 +7564,7 @@ pfctl_load_anchors(int dev, struct pfctl *pf, struct pfr_buffer *trans)
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)
+ la->anchorname, pf->trans) == -1)
return (-1);
}
@@ -7589,6 +7712,14 @@ node_mac_from_string_mask(const char *str, const char *mask)
int
filteropts_to_rule(struct pfctl_rule *r, struct filter_opts *opts)
{
+ if (opts->marker & FOM_ONCE) {
+ if ((r->action != PF_PASS && r->action != PF_DROP) || r->anchor) {
+ yyerror("'once' only applies to pass/block rules");
+ return (1);
+ }
+ r->rule_flag |= PFRULE_ONCE;
+ }
+
r->keep_state = opts->keep.action;
r->pktrate.limit = opts->pktrate.limit;
r->pktrate.seconds = opts->pktrate.seconds;
@@ -7676,3 +7807,73 @@ filteropts_to_rule(struct pfctl_rule *r, struct filter_opts *opts)
return (0);
}
+
+static bool
+pfctl_setup_anchor(struct pfctl_rule *r, struct pfctl *pf, char *anchorname)
+{
+ char *p;
+
+ if (pf->astack[pf->asd + 1]) {
+ if (anchorname && strchr(anchorname, '/') != NULL) {
+ free(anchorname);
+ yyerror("anchor paths containing '/' "
+ "cannot be used for inline anchors.");
+ return (false);
+ }
+
+ /* Move inline rules into relative location. */
+ pfctl_anchor_setup(r,
+ &pf->astack[pf->asd]->ruleset,
+ anchorname ? anchorname : 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);
+ return (false);
+ }
+ mv_rules(&pf->alast->ruleset,
+ &r->anchor->ruleset);
+ mv_tables(pf, &pfr_ktables, r->anchor, pf->alast);
+ }
+ pf_remove_if_empty_ruleset(&pf->alast->ruleset);
+ pf->alast = r->anchor;
+ } else {
+ if (! anchorname) {
+ yyerror("anchors without explicit "
+ "rules must specify a name");
+ return (false);
+ }
+ /*
+ * Don't make non-brace anchors part of the main anchor pool.
+ */
+ if ((r->anchor = calloc(1, sizeof(*r->anchor))) == NULL) {
+ err(1, "anchorrule: calloc");
+ }
+ pf_init_ruleset(&r->anchor->ruleset);
+ r->anchor->ruleset.anchor = r->anchor;
+ if (strlcpy(r->anchor->path, anchorname,
+ sizeof(r->anchor->path)) >= sizeof(r->anchor->path)) {
+ errx(1, "anchorrule: strlcpy");
+ }
+ if ((p = strrchr(anchorname, '/')) != NULL) {
+ if (strlen(p) == 1) {
+ yyerror("anchorrule: bad anchor name %s",
+ anchorname);
+ return (false);
+ }
+ } else
+ p = anchorname;
+ if (strlcpy(r->anchor->name, p,
+ sizeof(r->anchor->name)) >= sizeof(r->anchor->name)) {
+ errx(1, "anchorrule: strlcpy");
+ }
+ }
+
+ return (true);
+}
diff --git a/sbin/pfctl/pf_print_state.c b/sbin/pfctl/pf_print_state.c
index 5abdc2de419d..417ff70de975 100644
--- a/sbin/pfctl/pf_print_state.c
+++ b/sbin/pfctl/pf_print_state.c
@@ -113,10 +113,11 @@ print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose)
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);
-
- if (bits < (af == AF_INET ? 32 : 128))
- printf("/%d", bits);
+ if (af == AF_INET || af == AF_INET6) {
+ int bits = unmask(&addr->v.a.mask);
+ if (bits < (af == AF_INET ? 32 : 128))
+ printf("/%d", bits);
+ }
}
}
@@ -228,7 +229,6 @@ print_state(struct pfctl_state *s, int opts)
struct pfctl_state_key *key, *sk, *nk;
const char *protoname;
int min, sec;
- sa_family_t af;
uint8_t proto;
int afto = (s->key[PF_SK_STACK].af != s->key[PF_SK_WIRE].af);
int idx;
@@ -242,7 +242,6 @@ print_state(struct pfctl_state *s, int opts)
key = s->key;
#endif
- af = s->key[PF_SK_WIRE].af;
proto = s->key[PF_SK_WIRE].proto;
if (s->direction == PF_OUT) {
@@ -430,7 +429,7 @@ print_state(struct pfctl_state *s, int opts)
default:
printf(" gateway: ");
}
- print_host(&s->rt_addr, 0, af, opts);
+ print_host(&s->rt_addr, 0, s->rt_af, opts);
if (s->rt_ifname[0])
printf("@%s", s->rt_ifname);
}
diff --git a/sbin/pfctl/pfctl.8 b/sbin/pfctl/pfctl.8
index f582c6301124..58de54cdf923 100644
--- a/sbin/pfctl/pfctl.8
+++ b/sbin/pfctl/pfctl.8
@@ -24,7 +24,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd July 7, 2025
+.Dd August 28, 2025
.Dt PFCTL 8
.Os
.Sh NAME
@@ -115,8 +115,9 @@ Apply flags
.Fl f ,
.Fl F ,
.Fl s ,
+.Fl T ,
and
-.Fl T
+.Fl z
only to the rules in the specified
.Ar anchor .
In addition to the main ruleset,
@@ -211,31 +212,31 @@ Flush the filter parameters specified by
.Ar modifier
(may be abbreviated):
.Pp
-.Bl -tag -width xxxxxxxxxxxx -compact
-.It Fl F Cm nat
+.Bl -tag -width xxxxxxxxx -compact
+.It Cm nat
Flush the NAT rules.
-.It Fl F Cm queue
+.It Cm queue
Flush the queue rules.
-.It Fl F Cm ethernet
+.It Cm ethernet
Flush the Ethernet filter rules.
-.It Fl F Cm rules
+.It Cm rules
Flush the filter rules.
-.It Fl F Cm states
+.It Cm states
Flush the state table (NAT and filter).
-.It Fl F Cm Sources
+.It Cm Sources
Flush the source tracking table.
-.It Fl F Cm info
+.It Cm info
Flush the filter information (statistics that are not bound to rules).
-.It Fl F Cm Tables
+.It Cm Tables
Flush the tables.
-.It Fl F Cm osfp
+.It Cm osfp
Flush the passive operating system fingerprints.
-.It Fl F Cm Reset
+.It Cm Reset
Reset limits, timeouts and other options back to default settings.
See the OPTIONS section in
.Xr pf.conf 5
for details.
-.It Fl F Cm all
+.It Cm all
Flush all of the above.
.El
.Pp
@@ -401,15 +402,16 @@ 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
+.Bl -tag -width xxxxxxxxx -compact
+.It Cm none
Disable the ruleset optimizer.
-.It Fl o Cm basic
+.It Cm basic
Enable basic ruleset optimizations.
This is the default behaviour.
-.It Fl o Cm profile
+.It Cm profile
Enable basic ruleset optimizations with profiling.
.El
+.Pp
For further information on the ruleset optimizer, see
.Xr pf.conf 5 .
.It Fl P
@@ -431,15 +433,15 @@ Perform reverse DNS lookups on states and tables when displaying them.
and
.Fl r
are mutually exclusive.
-.It Fl s Ar modifier
+.It Fl s Ar modifier Op Fl R Ar id
Show the filter parameters specified by
.Ar modifier
(may be abbreviated):
.Pp
-.Bl -tag -width xxxxxxxxxxxxx -compact
-.It Fl s Cm nat
+.Bl -tag -width xxxxxxxxxxx -compact
+.It Cm nat
Show the currently loaded NAT rules.
-.It Fl s Cm queue
+.It Cm queue
Show the currently loaded queue rules.
When used together with
.Fl v ,
@@ -449,18 +451,25 @@ When used together with
.Nm
will loop and show updated queue statistics every five seconds, including
measured bandwidth and packets per second.
-.It Fl s Cm ether
+.It Cm ether
Show the currently loaded Ethernet rules.
When used together with
.Fl v ,
the per-rule statistics (number of evaluations,
packets, and bytes) are also shown.
-.It Fl s Cm rules
+.It 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.
+When used together with
+.Fl g
+or
+.Fl vv ,
+expired rules
+.Pq marked as Dq # expired
+are also shown.
Note that the
.Dq skip step
optimization done automatically by the kernel
@@ -468,7 +477,7 @@ will skip evaluation of rules where possible.
Packets passed statefully are counted in the rule that created the state
(even though the rule is not evaluated more than once for the entire
connection).
-.It Fl s Cm Anchors
+.It Cm Anchors
Show the currently loaded anchors directly attached to the main ruleset.
If
.Fl a Ar anchor
@@ -479,11 +488,11 @@ If
.Fl v
is specified, all anchors attached under the target anchor will be
displayed recursively.
-.It Fl s Cm states
+.It Cm states
Show the contents of the state table.
-.It Fl s Cm Sources
+.It Cm Sources
Show the contents of the source tracking table.
-.It Fl s Cm info
+.It Cm info
Show filter information (statistics and counters).
When used together with
.Fl v ,
@@ -491,21 +500,21 @@ source tracking statistics, the firewall's 32-bit hostid number and the
main ruleset's MD5 checksum for use with
.Xr pfsync 4
are also shown.
-.It Fl s Cm Running
+.It Cm Running
Show the running status and provide a non-zero exit status when disabled.
-.It Fl s Cm labels
+.It 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
+.It Cm timeouts
Show the current global timeouts.
-.It Fl s Cm memory
+.It Cm memory
Show the current pool memory hard limits.
-.It Fl s Cm Tables
+.It Cm Tables
Show the list of tables.
-.It Fl s Cm osfp
+.It Cm osfp
Show the list of operating system fingerprints.
-.It Fl s Cm Interfaces
+.It Cm Interfaces
Show the list of interfaces and interface groups available to PF.
When used together with
.Fl v ,
@@ -515,7 +524,7 @@ When used together with
interface statistics are also shown.
.Fl i
can be used to select an interface or a group of interfaces.
-.It Fl s Cm all
+.It Cm all
Show all of the above, except for the lists of interfaces and operating
system fingerprints.
.El
@@ -563,44 +572,45 @@ no free ports in translation port range
.It Fl S
Do not perform domain name resolution.
If a name cannot be resolved without DNS, an error will be reported.
-.It Fl T Ar command Op Ar address ...
+.It Fl t Ar table Fl T Ar command Op Ar address ...
Specify the
.Ar command
-(may be abbreviated) to apply to the table.
+(may be abbreviated) to apply to
+.Ar 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.
+.Bl -tag -width "expire number" -compact
+.It Cm add
+Add one or more addresses to a table.
Automatically create a persistent table if it does not exist.
-.It Fl T Cm delete
+.It Cm delete
Delete one or more addresses from a table.
-.It Fl T Cm expire Ar number
+.It 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
+.It Cm flush
+Flush all addresses in a table.
+.It Cm kill
+Kill a table.
+.It Cm replace
Replace the addresses of the table.
Automatically create a persistent table if it does not exist.
-.It Fl T Cm show
+.It Cm show
Show the content (addresses) of a table.
-.It Fl T Cm test
+.It Cm test
Test if the given addresses match a table.
-.It Fl T Cm zero Op Ar address ...
+.It Cm zero Op Ar address ...
Clear all the statistics of a table, or only for specified addresses.
-.It Fl T Cm reset
+.It Cm reset
Clear statistics only for addresses with non-zero statistics. Addresses
with counter values at zero and their
.Dq Cleared
timestamp are left untouched.
-.It Fl T Cm load
+.It Cm load
Load only the table definitions from
.Xr pf.conf 5 .
This is used in conjunction with the
@@ -765,8 +775,6 @@ 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
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 271286deeda7..ed317495c2e0 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -110,6 +110,8 @@ int pfctl_show_status(int, int);
int pfctl_show_running(int);
int pfctl_show_timeouts(int, int);
int pfctl_show_limits(int, int);
+void pfctl_read_limits(struct pfctl_handle *);
+void pfctl_restore_limits(void);
void pfctl_debug(int, u_int32_t, int);
int pfctl_test_altqsupport(int, int);
int pfctl_show_anchors(int, int, char *);
@@ -131,12 +133,13 @@ int pfctl_walk_get(int, struct pfioc_ruleset *, void *);
int pfctl_walk_anchors(int, int, const char *,
int(*)(int, struct pfioc_ruleset *, void *), void *);
struct pfr_anchors *
- pfctl_get_anchors(int, char *, int);
-int pfctl_recurse(int, int, char *,
+ pfctl_get_anchors(int, const char *, int);
+int pfctl_recurse(int, int, const char *,
int(*)(int, int, struct pfr_anchoritem *));
int pfctl_call_clearrules(int, int, struct pfr_anchoritem *);
int pfctl_call_cleartables(int, int, struct pfr_anchoritem *);
int pfctl_call_clearanchors(int, int, struct pfr_anchoritem *);
+int pfctl_call_showtables(int, int, struct pfr_anchoritem *);
static struct pfctl_anchor_global pf_anchors;
struct pfctl_anchor pf_main_anchor;
@@ -183,9 +186,13 @@ static const struct {
{ "src-nodes", PF_LIMIT_SRC_NODES },
{ "frags", PF_LIMIT_FRAGS },
{ "table-entries", PF_LIMIT_TABLE_ENTRIES },
+ { "anchors", PF_LIMIT_ANCHORS },
+ { "eth-anchors", PF_LIMIT_ETH_ANCHORS },
{ NULL, 0 }
};
+static unsigned int limit_curr[PF_LIMIT_MAX];
+
struct pf_hint {
const char *name;
int timeout;
@@ -698,7 +705,7 @@ pfctl_kill_src_nodes(int dev, int opts)
dests++;
- copy_satopfaddr(&psnk.psnk_src.addr.v.a.addr,
+ copy_satopfaddr(&psnk.psnk_dst.addr.v.a.addr,
resp[1]->ai_addr);
if (ioctl(dev, DIOCKILLSRCNODES, &psnk))
err(1, "DIOCKILLSRCNODES");
@@ -787,7 +794,7 @@ pfctl_net_kill_states(int dev, const char *iface, int opts)
dests++;
- copy_satopfaddr(&kill.src.addr.v.a.addr,
+ copy_satopfaddr(&kill.dst.addr.v.a.addr,
resp[1]->ai_addr);
if ((ret = pfctl_kill_states_h(pfh, &kill, &newkilled)) != 0)
@@ -1038,7 +1045,7 @@ pfctl_get_pool(int dev, struct pfctl_pool *pool, u_int32_t nr,
u_int32_t ticket, int r_action, const char *anchorname, int which)
{
struct pfioc_pooladdr pp;
- struct pf_pooladdr *pa;
+ struct pfctl_pooladdr *pa;
u_int32_t pnr, mpnr;
int ret;
@@ -1054,10 +1061,11 @@ pfctl_get_pool(int dev, struct pfctl_pool *pool, u_int32_t nr,
warnc(ret, "DIOCGETADDR");
return (-1);
}
- pa = calloc(1, sizeof(struct pf_pooladdr));
+ pa = calloc(1, sizeof(struct pfctl_pooladdr));
if (pa == NULL)
err(1, "calloc");
- bcopy(&pp.addr, pa, sizeof(struct pf_pooladdr));
+ bcopy(&pp.addr, pa, sizeof(struct pfctl_pooladdr));
+ pa->af = pp.af;
TAILQ_INSERT_TAIL(&pool->list, pa, entries);
}
@@ -1067,7 +1075,7 @@ pfctl_get_pool(int dev, struct pfctl_pool *pool, u_int32_t nr,
void
pfctl_move_pool(struct pfctl_pool *src, struct pfctl_pool *dst)
{
- struct pf_pooladdr *pa;
+ struct pfctl_pooladdr *pa;
while ((pa = TAILQ_FIRST(&src->list)) != NULL) {
TAILQ_REMOVE(&src->list, pa, entries);
@@ -1078,7 +1086,7 @@ pfctl_move_pool(struct pfctl_pool *src, struct pfctl_pool *dst)
void
pfctl_clear_pool(struct pfctl_pool *pool)
{
- struct pf_pooladdr *pa;
+ struct pfctl_pooladdr *pa;
while ((pa = TAILQ_FIRST(&pool->list)) != NULL) {
TAILQ_REMOVE(&pool->list, pa, entries);
@@ -1134,6 +1142,9 @@ pfctl_print_rule_counters(struct pfctl_rule *rule, int opts)
printf(" [ queue: qname=%s qid=%u pqname=%s pqid=%u ]\n",
rule->qname, rule->qid, rule->pqname, rule->pqid);
+ if (rule->rule_flag & PFRULE_EXPIRED)
+ printf(" [ Expired: %lld secs ago ]\n",
+ (long long)(time(NULL) - rule->exptime));
}
if (opts & PF_OPT_VERBOSE) {
printf(" [ Evaluations: %-8llu Packets: %-8llu "
@@ -1304,7 +1315,6 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
struct pfctl_rule rule;
char anchor_call[MAXPATHLEN];
u_int32_t nr, header = 0;
- int rule_numbers = opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG);
int numeric = opts & PF_OPT_NUMERIC;
int len = strlen(path), ret = 0;
char *npath, *p;
@@ -1402,8 +1412,14 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
case PFCTL_SHOW_RULES:
if (rule.label[0][0] && (opts & PF_OPT_SHOWALL))
labels = 1;
- print_rule(&rule, anchor_call, rule_numbers, numeric);
- printf("\n");
+ print_rule(&rule, anchor_call, opts, numeric);
+ /*
+ * Do not print newline, when we have not
+ * printed expired rule.
+ */
+ if (!(rule.rule_flag & PFRULE_EXPIRED) ||
+ (opts & (PF_OPT_VERBOSE2|PF_OPT_DEBUG)))
+ printf("\n");
pfctl_print_rule_counters(&rule, opts);
break;
case PFCTL_SHOW_NOTHING:
@@ -1475,7 +1491,7 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
if (rule.label[0][0] && (opts & PF_OPT_SHOWALL))
labels = 1;
INDENT(depth, !(opts & PF_OPT_VERBOSE));
- print_rule(&rule, anchor_call, rule_numbers, numeric);
+ print_rule(&rule, anchor_call, opts, numeric);
/*
* If this is a 'unnamed' brace notation
@@ -1777,6 +1793,31 @@ pfctl_show_limits(int dev, int opts)
}
void
+pfctl_read_limits(struct pfctl_handle *h)
+{
+ int i;
+
+ for (i = 0; pf_limits[i].name; i++) {
+ if (pfctl_get_limit(h, i, &limit_curr[i]))
+ err(1, "DIOCGETLIMIT");
+ }
+}
+
+void
+pfctl_restore_limits(void)
+{
+ int i;
+
+ if (pfh == NULL)
+ return;
+
+ for (i = 0; pf_limits[i].name; i++) {
+ if (pfctl_set_limit(pfh, i, limit_curr[i]))
+ warn("DIOCSETLIMIT (%s)", pf_limits[i].name);
+ }
+}
+
+void
pfctl_show_creators(int opts)
{
int ret;
@@ -1794,14 +1835,14 @@ pfctl_show_creators(int opts)
/* callbacks for rule/nat/rdr/addr */
int
-pfctl_add_pool(struct pfctl *pf, struct pfctl_pool *p, sa_family_t af, int which)
+pfctl_add_pool(struct pfctl *pf, struct pfctl_pool *p, int which)
{
- struct pf_pooladdr *pa;
+ struct pfctl_pooladdr *pa;
int ret;
- pf->paddr.af = af;
TAILQ_FOREACH(pa, &p->list, entries) {
- memcpy(&pf->paddr.addr, pa, sizeof(struct pf_pooladdr));
+ memcpy(&pf->paddr.addr, pa, sizeof(struct pfctl_pooladdr));
+ pf->paddr.af = pa->af;
if ((pf->opts & PF_OPT_NOACTION) == 0) {
if ((ret = pfctl_add_addr(pf->h, &pf->paddr, which)) != 0)
errc(1, ret, "DIOCADDADDR");
@@ -1819,14 +1860,12 @@ pfctl_init_rule(struct pfctl_rule *r)
TAILQ_INIT(&(r->route.list));
}
-int
-pfctl_append_rule(struct pfctl *pf, struct pfctl_rule *r,
- const char *anchor_call)
+void
+pfctl_append_rule(struct pfctl *pf, struct pfctl_rule *r)
{
u_int8_t rs_num;
struct pfctl_rule *rule;
struct pfctl_ruleset *rs;
- char *p;
rs_num = pf_get_ruleset_number(r->action);
if (rs_num == PF_RULESET_MAX)
@@ -1834,29 +1873,6 @@ pfctl_append_rule(struct pfctl *pf, struct pfctl_rule *r,
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_append_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_append_rule: strlcpy");
- if ((p = strrchr(anchor_call, '/')) != NULL) {
- if (!strlen(p))
- err(1, "pfctl_append_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_append_rule: strlcpy");
- }
-
if ((rule = calloc(1, sizeof(*rule))) == NULL)
err(1, "calloc");
bcopy(r, rule, sizeof(*rule));
@@ -1868,7 +1884,6 @@ pfctl_append_rule(struct pfctl *pf, struct pfctl_rule *r,
pfctl_move_pool(&r->route, &rule->route);
TAILQ_INSERT_TAIL(rs->rules[rs_num].active.ptr, rule, entries);
- return (0);
}
int
@@ -2061,6 +2076,41 @@ pfctl_load_eth_rule(struct pfctl *pf, char *path, struct pfctl_eth_rule *r,
return (0);
}
+static int
+pfctl_load_tables(struct pfctl *pf, char *path, struct pfctl_anchor *a,
+ int rs_num)
+{
+ struct pfr_ktable *kt, *ktw;
+ struct pfr_uktable *ukt;
+ char anchor_path[PF_ANCHOR_MAXPATH];
+ int e;
+
+ RB_FOREACH_SAFE(kt, pfr_ktablehead, &pfr_ktables, ktw) {
+ if (strcmp(kt->pfrkt_anchor, a->path) != 0)
+ continue;
+
+ if (path != NULL && *path) {
+ strlcpy(anchor_path, kt->pfrkt_anchor,
+ sizeof(anchor_path));
+ snprintf(kt->pfrkt_anchor, PF_ANCHOR_MAXPATH, "%s/%s",
+ path, anchor_path);
+ }
+ ukt = (struct pfr_uktable *)kt;
+ e = pfr_ina_define(&ukt->pfrukt_t, ukt->pfrukt_addrs.pfrb_caddr,
+ ukt->pfrukt_addrs.pfrb_size, NULL, NULL,
+ pf->anchor->ruleset.tticket,
+ ukt->pfrukt_init_addr ? PFR_FLAG_ADDRSTOO : 0);
+ if (e != 0)
+ err(1, "%s pfr_ina_define() %s@%s", __func__,
+ kt->pfrkt_name, kt->pfrkt_anchor);
+ RB_REMOVE(pfr_ktablehead, &pfr_ktables, kt);
+ pfr_buf_clear(&ukt->pfrukt_addrs);
+ free(ukt);
+ }
+
+ return (0);
+}
+
int
pfctl_load_ruleset(struct pfctl *pf, char *path, struct pfctl_ruleset *rs,
int rs_num, int depth)
@@ -2109,6 +2159,8 @@ pfctl_load_ruleset(struct pfctl *pf, char *path, struct pfctl_ruleset *rs,
if ((error = pfctl_load_ruleset(pf, path,
&r->anchor->ruleset, rs_num, depth + 1)))
goto error;
+ if ((error = pfctl_load_tables(pf, path, r->anchor, rs_num)))
+ goto error;
} else if (pf->opts & PF_OPT_VERBOSE)
printf("\n");
free(r);
@@ -2131,15 +2183,20 @@ pfctl_load_rule(struct pfctl *pf, char *path, struct pfctl_rule *r, int depth)
{
u_int8_t rs_num = pf_get_ruleset_number(r->action);
char *name;
- u_int32_t ticket;
+ uint32_t ticket;
char anchor[PF_ANCHOR_NAME_SIZE];
int len = strlen(path);
int error;
bool was_present;
/* set up anchor before adding to path for anchor_call */
- if ((pf->opts & PF_OPT_NOACTION) == 0)
+ if ((pf->opts & PF_OPT_NOACTION) == 0) {
+ if (pf->trans == NULL)
+ errx(1, "pfctl_load_rule: no transaction");
ticket = pfctl_get_ticket(pf->trans, rs_num, path);
+ if (rs_num == PF_RULESET_FILTER)
+ pf->anchor->ruleset.tticket = ticket;
+ }
if (strlcpy(anchor, path, sizeof(anchor)) >= sizeof(anchor))
errx(1, "pfctl_load_rule: strlcpy");
@@ -2165,11 +2222,11 @@ pfctl_load_rule(struct pfctl *pf, char *path, struct pfctl_rule *r, int depth)
errc(1, error, "DIOCBEGINADDRS");
}
- if (pfctl_add_pool(pf, &r->rdr, r->af, PF_RDR))
+ if (pfctl_add_pool(pf, &r->rdr, PF_RDR))
return (1);
- if (pfctl_add_pool(pf, &r->nat, r->naf ? r->naf : r->af, PF_NAT))
+ if (pfctl_add_pool(pf, &r->nat, PF_NAT))
return (1);
- if (pfctl_add_pool(pf, &r->route, r->af, PF_RT))
+ if (pfctl_add_pool(pf, &r->route, PF_RT))
return (1);
error = pfctl_add_rule_h(pf->h, r, anchor, name, ticket,
pf->paddr.ticket);
@@ -2241,6 +2298,8 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
RB_INIT(&pf_anchors);
memset(&pf_main_anchor, 0, sizeof(pf_main_anchor));
pf_init_ruleset(&pf_main_anchor.ruleset);
+ memset(&pf, 0, sizeof(pf));
+ memset(&trs, 0, sizeof(trs));
pf_main_anchor.ruleset.anchor = &pf_main_anchor;
memset(&pf_eth_main_anchor, 0, sizeof(pf_eth_main_anchor));
@@ -2250,6 +2309,7 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
if (trans == NULL) {
bzero(&buf, sizeof(buf));
buf.pfrb_type = PFRB_TRANS;
+ pf.trans = &buf;
t = &buf;
osize = 0;
} else {
@@ -2360,7 +2420,7 @@ pfctl_rules(int dev, char *filename, int opts, int optimize,
if (trans == NULL) {
/* process "load anchor" directives */
- if (pfctl_load_anchors(dev, &pf, t) == -1)
+ if (pfctl_load_anchors(dev, &pf) == -1)
ERRX("load anchors");
if ((opts & PF_OPT_NOACTION) == 0) {
@@ -2441,8 +2501,14 @@ pfctl_init_options(struct pfctl *pf)
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->limit[PF_LIMIT_SRC_NODES] = (limit_curr[PF_LIMIT_SRC_NODES] == 0) ?
+ PFSNODE_HIWAT : limit_curr[PF_LIMIT_SRC_NODES];
+ pf->limit[PF_LIMIT_TABLE_ENTRIES] =
+ (limit_curr[PF_LIMIT_TABLE_ENTRIES] == 0) ?
+ PFR_KENTRY_HIWAT : limit_curr[PF_LIMIT_TABLE_ENTRIES];
+ pf->limit[PF_LIMIT_ANCHORS] = (limit_curr[PF_LIMIT_ANCHORS] == 0) ?
+ PF_ANCHOR_HIWAT : limit_curr[PF_LIMIT_ANCHORS];
pf->debug = PF_DEBUG_URGENT;
pf->reassemble = 0;
@@ -2543,6 +2609,9 @@ pfctl_apply_limit(struct pfctl *pf, const char *opt, unsigned int limit)
if (pf->opts & PF_OPT_VERBOSE)
printf("set limit %s %d\n", opt, limit);
+ if ((pf->opts & PF_OPT_NOACTION) == 0)
+ pfctl_load_options(pf);
+
return (0);
}
@@ -2988,20 +3057,23 @@ pfctl_show_anchors(int dev, int opts, char *anchor)
}
struct pfr_anchors *
-pfctl_get_anchors(int dev, char *anchor, int opts)
+pfctl_get_anchors(int dev, const char *anchor, int opts)
{
struct pfioc_ruleset pr;
static struct pfr_anchors anchors;
+ char anchorbuf[PATH_MAX];
char *n;
SLIST_INIT(&anchors);
memset(&pr, 0, sizeof(pr));
if (*anchor != '\0') {
- n = dirname(anchor);
+ strlcpy(anchorbuf, anchor, sizeof(anchorbuf));
+ n = dirname(anchorbuf);
if (n[0] != '.' && n[1] != '\0')
strlcpy(pr.path, n, sizeof(pr.path));
- n = basename(anchor);
+ strlcpy(anchorbuf, anchor, sizeof(anchorbuf));
+ n = basename(anchorbuf);
if (n != NULL)
strlcpy(pr.name, n, sizeof(pr.name));
}
@@ -3051,7 +3123,14 @@ pfctl_call_clearanchors(int dev, int opts, struct pfr_anchoritem *pfra)
}
int
-pfctl_recurse(int dev, int opts, char *anchorname,
+pfctl_call_showtables(int dev, int opts, struct pfr_anchoritem *pfra)
+{
+ pfctl_show_tables(pfra->pfra_anchorname, opts);
+ return (0);
+}
+
+int
+pfctl_recurse(int dev, int opts, const char *anchorname,
int(*walkf)(int, int, struct pfr_anchoritem *))
{
int rv = 0;
@@ -3064,11 +3143,13 @@ pfctl_recurse(int dev, int opts, char *anchorname,
* so that failures on one anchor do not prevent clearing others.
*/
opts |= PF_OPT_IGNFAIL;
- printf("Removing:\n");
+ if ((opts & PF_OPT_CALLSHOW) == 0)
+ printf("Removing:\n");
SLIST_FOREACH_SAFE(pfra, anchors, pfra_sle, pfra_save) {
- printf(" %s\n",
- (*pfra->pfra_anchorname == '\0') ? "/" :
- pfra->pfra_anchorname);
+ if ((opts & PF_OPT_CALLSHOW) == 0)
+ printf(" %s\n",
+ (*pfra->pfra_anchorname == '\0') ? "/" :
+ pfra->pfra_anchorname);
rv |= walkf(dev, opts, pfra);
SLIST_REMOVE(anchors, pfra, pfr_anchoritem, pfra_sle);
free(pfra->pfra_anchorname);
@@ -3133,6 +3214,7 @@ pfctl_reset(int dev, int opts)
struct pfr_buffer t;
int i;
+ memset(&pf, 0, sizeof(pf));
pf.dev = dev;
pf.h = pfh;
pfctl_init_options(&pf);
@@ -3393,6 +3475,11 @@ main(int argc, char *argv[])
if (pfh == NULL)
err(1, "Failed to open netlink");
+ if ((opts & PF_OPT_NOACTION) == 0) {
+ pfctl_read_limits(pfh);
+ atexit(pfctl_restore_limits);
+ }
+
if (opts & PF_OPT_DISABLE)
if (pfctl_disable(dev, opts))
exit_val = 1;
@@ -3470,7 +3557,12 @@ main(int argc, char *argv[])
pfctl_show_fingerprints(opts);
break;
case 'T':
- pfctl_show_tables(anchorname, opts);
+ if (opts & PF_OPT_RECURSE) {
+ opts |= PF_OPT_CALLSHOW;
+ pfctl_recurse(dev, opts, anchorname,
+ pfctl_call_showtables);
+ } else
+ pfctl_show_tables(anchorname, opts);
break;
case 'o':
pfctl_load_fingerprints(dev, opts);
@@ -3493,6 +3585,12 @@ main(int argc, char *argv[])
}
if (clearopt != NULL) {
+ int mnr;
+
+ /* Check if anchor exists. */
+ if ((pfctl_get_rulesets(pfh, anchorname, &mnr)) == ENOENT)
+ errx(1, "No such anchor %s", anchorname);
+
switch (*clearopt) {
case 'e':
pfctl_flush_eth_rules(dev, opts, anchorname);
@@ -3631,7 +3729,18 @@ main(int argc, char *argv[])
}
}
- exit(exit_val);
+ /*
+ * prevent pfctl_restore_limits() exit handler from restoring
+ * pf(4) options settings on successful exit.
+ */
+ if (exit_val == 0) {
+ close(dev);
+ dev = -1;
+ pfctl_close(pfh);
+ pfh = NULL;
+ }
+
+ return (exit_val);
}
char *
diff --git a/sbin/pfctl/pfctl.h b/sbin/pfctl/pfctl.h
index afecc78086e0..136f51ea08f9 100644
--- a/sbin/pfctl/pfctl.h
+++ b/sbin/pfctl/pfctl.h
@@ -36,6 +36,12 @@
#include <libpfctl.h>
+#ifdef PFCTL_DEBUG
+#define DBGPRINT(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define DBGPRINT(...) (void)(0)
+#endif
+
extern struct pfctl_handle *pfh;
struct pfctl;
@@ -55,6 +61,41 @@ struct pfr_buffer {
(var) != NULL; \
(var) = pfr_buf_next((buf), (var)))
+RB_HEAD(pfr_ktablehead, pfr_ktable);
+struct pfr_ktable {
+ struct pfr_tstats pfrkt_ts;
+ RB_ENTRY(pfr_ktable) pfrkt_tree;
+ SLIST_ENTRY(pfr_ktable) pfrkt_workq;
+ struct radix_node_head *pfrkt_ip4;
+ struct radix_node_head *pfrkt_ip6;
+ struct pfr_ktable *pfrkt_shadow;
+ struct pfr_ktable *pfrkt_root;
+ struct pf_kruleset *pfrkt_rs;
+ long pfrkt_larg;
+ int pfrkt_nflags;
+};
+#define pfrkt_t pfrkt_ts.pfrts_t
+#define pfrkt_name pfrkt_t.pfrt_name
+#define pfrkt_anchor pfrkt_t.pfrt_anchor
+#define pfrkt_ruleset pfrkt_t.pfrt_ruleset
+#define pfrkt_flags pfrkt_t.pfrt_flags
+#define pfrkt_cnt pfrkt_kts.pfrkts_cnt
+#define pfrkt_refcnt pfrkt_kts.pfrkts_refcnt
+#define pfrkt_tzero pfrkt_kts.pfrkts_tzero
+
+struct pfr_uktable {
+ struct pfr_ktable pfrukt_kt;
+ struct pfr_buffer pfrukt_addrs;
+ int pfrukt_init_addr;
+ SLIST_ENTRY(pfr_uktable) pfrukt_entry;
+};
+
+#define pfrukt_t pfrukt_kt.pfrkt_ts.pfrts_t
+#define pfrukt_name pfrukt_kt.pfrkt_t.pfrt_name
+#define pfrukt_anchor pfrukt_kt.pfrkt_t.pfrt_anchor
+
+extern struct pfr_ktablehead pfr_ktables;
+
struct pfr_anchoritem {
SLIST_ENTRY(pfr_anchoritem) pfra_sle;
char *pfra_anchorname;
@@ -62,7 +103,6 @@ struct pfr_anchoritem {
SLIST_HEAD(pfr_anchors, pfr_anchoritem);
-int pfr_get_fd(void);
int pfr_add_table(struct pfr_table *, int *, int);
int pfr_del_table(struct pfr_table *, int *, int);
int pfr_get_tables(struct pfr_table *, struct pfr_table *, int *, int);
@@ -85,7 +125,6 @@ int pfr_buf_load(struct pfr_buffer *, char *, int,
int (*)(struct pfr_buffer *, char *, int, int), int);
char *pf_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_do_clear_tables(const char *, int);
diff --git a/sbin/pfctl/pfctl_optimize.c b/sbin/pfctl/pfctl_optimize.c
index 1d2a60555f19..2d16bbd22b39 100644
--- a/sbin/pfctl/pfctl_optimize.c
+++ b/sbin/pfctl/pfctl_optimize.c
@@ -1331,7 +1331,8 @@ again:
if (pfctl_define_table(tbl->pt_name, PFR_TFLAG_CONST, 1,
- pf->astack[0]->path, tbl->pt_buf, pf->astack[0]->ruleset.tticket)) {
+ pf->astack[0]->path, tbl->pt_buf, pf->astack[0]->ruleset.tticket,
+ NULL)) {
warn("failed to create table %s in %s",
tbl->pt_name, pf->astack[0]->name);
return (1);
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
index bd2c10c8080f..b8531067d3f6 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -242,7 +242,7 @@ copy_satopfaddr(struct pf_addr *pfa, struct sockaddr *sa)
const struct icmptypeent *
geticmptypebynumber(u_int8_t type, sa_family_t af)
{
- unsigned int i;
+ size_t i;
if (af != AF_INET6) {
for (i=0; i < nitems(icmp_type); i++) {
@@ -261,7 +261,7 @@ geticmptypebynumber(u_int8_t type, sa_family_t af)
const struct icmptypeent *
geticmptypebyname(char *w, sa_family_t af)
{
- unsigned int i;
+ size_t i;
if (af != AF_INET6) {
for (i=0; i < nitems(icmp_type); i++) {
@@ -280,7 +280,7 @@ geticmptypebyname(char *w, sa_family_t af)
const struct icmpcodeent *
geticmpcodebynumber(u_int8_t type, u_int8_t code, sa_family_t af)
{
- unsigned int i;
+ size_t i;
if (af != AF_INET6) {
for (i=0; i < nitems(icmp_code); i++) {
@@ -301,7 +301,7 @@ geticmpcodebynumber(u_int8_t type, u_int8_t code, sa_family_t af)
const struct icmpcodeent *
geticmpcodebyname(u_long type, char *w, sa_family_t af)
{
- unsigned int i;
+ size_t i;
if (af != AF_INET6) {
for (i=0; i < nitems(icmp_code); i++) {
@@ -389,9 +389,11 @@ print_flags(uint16_t f)
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)
+ sa_family_t af, u_int8_t proto, int opts, int numeric)
{
char buf[PF_OSFP_LEN*3];
+ int verbose = opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG);
+
if (src->addr.type == PF_ADDR_ADDRMASK &&
dst->addr.type == PF_ADDR_ADDRMASK &&
PF_AZERO(&src->addr.v.a.addr, AF_INET6) &&
@@ -429,10 +431,9 @@ print_fromto(struct pf_rule_addr *src, pf_osfp_t osfp, struct pf_rule_addr *dst,
}
void
-print_pool(struct pfctl_pool *pool, u_int16_t p1, u_int16_t p2,
- sa_family_t af, int id)
+print_pool(struct pfctl_pool *pool, u_int16_t p1, u_int16_t p2, int id)
{
- struct pf_pooladdr *pooladdr;
+ struct pfctl_pooladdr *pooladdr;
if ((TAILQ_FIRST(&pool->list) != NULL) &&
TAILQ_NEXT(TAILQ_FIRST(&pool->list), entries) != NULL)
@@ -442,15 +443,15 @@ print_pool(struct pfctl_pool *pool, u_int16_t p1, u_int16_t p2,
case PF_NAT:
case PF_RDR:
case PF_BINAT:
- print_addr(&pooladdr->addr, af, 0);
+ print_addr(&pooladdr->addr, pooladdr->af, 0);
break;
case PF_PASS:
case PF_MATCH:
- if (PF_AZERO(&pooladdr->addr.v.a.addr, af))
+ if (PF_AZERO(&pooladdr->addr.v.a.addr, pooladdr->af))
printf("%s", pooladdr->ifname);
else {
printf("(%s ", pooladdr->ifname);
- print_addr(&pooladdr->addr, af, 0);
+ print_addr(&pooladdr->addr, pooladdr->af, 0);
printf(")");
}
break;
@@ -509,6 +510,8 @@ print_pool(struct pfctl_pool *pool, u_int16_t p1, u_int16_t p2,
if (pool->mape.offset > 0)
printf(" map-e-portset %u/%u/%u",
pool->mape.offset, pool->mape.psidlen, pool->mape.psid);
+ if (pool->opts & PF_POOL_IPV6NH)
+ printf(" prefer-ipv6-nexthop");
}
void
@@ -613,6 +616,20 @@ print_status(struct pfctl_status *s, struct pfctl_syncookies *cookies, int opts)
printf("%14s\n", "");
}
}
+ if (opts & PF_OPT_VERBOSE) {
+ printf("Fragments\n");
+ printf(" %-25s %14ju %14s\n", "current entries",
+ s->fragments, "");
+ TAILQ_FOREACH(c, &s->ncounters, entry) {
+ printf(" %-25s %14ju ", c->name,
+ c->counter);
+ if (runtime > 0)
+ printf("%14.1f/s\n",
+ (double)c->counter / (double)runtime);
+ else
+ printf("%14s\n", "");
+ }
+ }
printf("Counters\n");
TAILQ_FOREACH(c, &s->counters, entry) {
printf(" %-25s %14ju ", c->name, c->counter);
@@ -674,7 +691,7 @@ print_src_node(struct pfctl_src_node *sn, int opts)
print_addr(&aw, sn->af, opts & PF_OPT_VERBOSE2);
printf(" -> ");
aw.v.a.addr = sn->raddr;
- print_addr(&aw, sn->naf ? sn->naf : sn->af, opts & PF_OPT_VERBOSE2);
+ print_addr(&aw, sn->raf, 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);
@@ -839,34 +856,39 @@ print_eth_rule(struct pfctl_eth_rule *r, const char *anchor_call,
}
void
-print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numeric)
+print_rule(struct pfctl_rule *r, const char *anchor_call, int opts, int numeric)
{
static const char *actiontypes[] = { "pass", "block", "scrub",
"no scrub", "nat", "no nat", "binat", "no binat", "rdr", "no rdr",
- "", "", "match"};
+ "synproxy drop", "defer", "match", "af-rt", "route-to" };
static const char *anchortypes[] = { "anchor", "anchor", "anchor",
"anchor", "nat-anchor", "nat-anchor", "binat-anchor",
"binat-anchor", "rdr-anchor", "rdr-anchor" };
- int i, ropts;
+ int i, ropts;
+ int verbose = opts & (PF_OPT_VERBOSE2 | PF_OPT_DEBUG);
char *p;
+ if ((r->rule_flag & PFRULE_EXPIRED) && (!verbose))
+ return;
+
if (verbose)
printf("@%d ", r->nr);
- if (r->action == PF_MATCH)
- printf("match");
- else if (r->action > PF_NORDR)
- printf("action(%d)", r->action);
- else if (anchor_call[0]) {
- p = strrchr(anchor_call, '/');
- if (p ? p[1] == '_' : anchor_call[0] == '_')
- printf("%s", anchortypes[r->action]);
- else
- printf("%s \"%s\"", anchortypes[r->action],
- anchor_call);
+ if (anchor_call[0]) {
+ if (r->action >= nitems(anchortypes)) {
+ printf("anchor(%d)", r->action);
+ } else {
+ p = strrchr(anchor_call, '/');
+ if (p ? p[1] == '_' : 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 >= nitems(actiontypes))
+ printf("action(%d)", r->action);
+ else
+ printf("%s", actiontypes[r->action]);
}
if (r->action == PF_DROP) {
if (r->rule_flag & PFRULE_RETURN)
@@ -928,7 +950,7 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
printf("%sall", count++ ? ", " : "");
if (r->log & PF_LOG_MATCHES)
printf("%smatches", count++ ? ", " : "");
- if (r->log & PF_LOG_SOCKET_LOOKUP)
+ if (r->log & PF_LOG_USER)
printf("%suser", count++ ? ", " : "");
if (r->logif)
printf("%sto pflog%u", count++ ? ", " : "",
@@ -952,10 +974,7 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
else if (r->rt == PF_DUPTO)
printf(" dup-to");
printf(" ");
- print_pool(&r->rdr, 0, 0, r->af, PF_PASS);
- print_pool(&r->route, 0, 0,
- r->rule_flag & PFRULE_AFTO && r->rt != PF_REPLYTO ? r->naf : r->af,
- PF_PASS);
+ print_pool(&r->route, 0, 0, PF_PASS);
}
if (r->af) {
if (r->af == AF_INET)
@@ -972,7 +991,7 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
printf(" proto %u", r->proto);
}
print_fromto(&r->src, r->os_fingerprint, &r->dst, r->af, r->proto,
- verbose, numeric);
+ opts, numeric);
if (r->rcv_ifname[0])
printf(" %sreceived-on %s", r->rcvifnot ? "!" : "",
r->rcv_ifname);
@@ -1221,6 +1240,8 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
printf(" %s %d",
r->free_flags & PFRULE_DN_IS_PIPE ? "dnpipe" : "dnqueue",
r->dnpipe);
+ if (r->rule_flag & PFRULE_ONCE)
+ printf(" once");
if (r->qname[0] && r->pqname[0])
printf(" queue(%s, %s)", r->qname, r->pqname);
else if (r->qname[0])
@@ -1252,17 +1273,16 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
if (r->action == PF_NAT || r->action == PF_BINAT || r->action == PF_RDR) {
printf(" -> ");
print_pool(&r->rdr, r->rdr.proxy_port[0],
- r->rdr.proxy_port[1], r->af, r->action);
+ r->rdr.proxy_port[1], r->action);
} else {
if (!TAILQ_EMPTY(&r->nat.list)) {
if (r->rule_flag & PFRULE_AFTO) {
- printf(" af-to %s from ", r->naf == AF_INET ? "inet" : "inet6");
+ printf(" af-to %s from ", r->naf == AF_INET ? "inet" : (r->naf == AF_INET6 ? "inet6" : "? "));
} else {
printf(" nat-to ");
}
print_pool(&r->nat, r->nat.proxy_port[0],
- r->nat.proxy_port[1], r->naf ? r->naf : r->af,
- PF_NAT);
+ r->nat.proxy_port[1], PF_NAT);
}
if (!TAILQ_EMPTY(&r->rdr.list)) {
if (r->rule_flag & PFRULE_AFTO) {
@@ -1271,10 +1291,16 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
printf(" rdr-to ");
}
print_pool(&r->rdr, r->rdr.proxy_port[0],
- r->rdr.proxy_port[1], r->naf ? r->naf : r->af,
- PF_RDR);
+ r->rdr.proxy_port[1], PF_RDR);
}
}
+
+ if (r->rule_flag & PFRULE_EXPIRED) {
+ printf(" # expired");
+
+ if (r->exptime != 0)
+ printf(" %s", ctime(&r->exptime));
+ }
}
void
@@ -1444,7 +1470,7 @@ ifa_add_groups_to_map(char *ifa_name)
ENTRY item;
ENTRY *ret_item;
int *answer;
-
+
item.key = ifg->ifgrq_group;
if (hsearch_r(item, FIND, &ret_item, &isgroup_map) == 0) {
struct ifgroupreq ifgr2;
@@ -1483,7 +1509,8 @@ ifa_load(void)
err(1, "getifaddrs");
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
- if (!(ifa->ifa_addr->sa_family == AF_INET ||
+ if (ifa->ifa_addr == NULL ||
+ !(ifa->ifa_addr->sa_family == AF_INET ||
ifa->ifa_addr->sa_family == AF_INET6 ||
ifa->ifa_addr->sa_family == AF_LINK))
continue;
@@ -1585,7 +1612,7 @@ is_a_group(char *name)
{
ENTRY item;
ENTRY *ret_item;
-
+
item.key = name;
if (hsearch_r(item, FIND, &ret_item, &isgroup_map) == 0)
return (0);
diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h
index 7a3c0c2a523f..44ddfb45fbe1 100644
--- a/sbin/pfctl/pfctl_parser.h
+++ b/sbin/pfctl/pfctl_parser.h
@@ -36,6 +36,8 @@
#include <libpfctl.h>
+#include <pfctl.h>
+
#define PF_OSFP_FILE "/etc/pf.os"
#define PF_OPT_DISABLE 0x00001
@@ -56,6 +58,7 @@
#define PF_OPT_KILLMATCH 0x08000
#define PF_OPT_NODNS 0x10000
#define PF_OPT_IGNFAIL 0x20000
+#define PF_OPT_CALLSHOW 0x40000
#define PF_NAT_PROXY_PORT_LOW 50001
#define PF_NAT_PROXY_PORT_HIGH 65535
@@ -89,6 +92,7 @@ struct pfctl {
struct pfioc_queue *pqueue;
struct pfr_buffer *trans;
struct pfctl_anchor *anchor, *alast;
+ struct pfr_ktablehead pfr_ktlast;
int eth_nr;
struct pfctl_eth_anchor *eanchor, *ealast;
struct pfctl_eth_anchor *eastack[PFCTL_ANCHOR_STACK_DEPTH];
@@ -276,17 +280,19 @@ struct pf_opt_rule {
TAILQ_HEAD(pf_opt_queue, pf_opt_rule);
+struct pfr_uktable;
+
void copy_satopfaddr(struct pf_addr *, struct sockaddr *);
int pfctl_rules(int, char *, int, int, char *, struct pfr_buffer *);
int pfctl_optimize_ruleset(struct pfctl *, struct pfctl_ruleset *);
void pfctl_init_rule(struct pfctl_rule *r);
-int pfctl_append_rule(struct pfctl *, struct pfctl_rule *, const char *);
+void pfctl_append_rule(struct pfctl *, struct pfctl_rule *);
int pfctl_append_eth_rule(struct pfctl *, struct pfctl_eth_rule *,
const char *);
int pfctl_add_altq(struct pfctl *, struct pf_altq *);
-int pfctl_add_pool(struct pfctl *, struct pfctl_pool *, sa_family_t, int);
+int pfctl_add_pool(struct pfctl *, struct pfctl_pool *, int);
void pfctl_move_pool(struct pfctl_pool *, struct pfctl_pool *);
void pfctl_clear_pool(struct pfctl_pool *);
@@ -302,9 +308,9 @@ int pfctl_cfg_syncookies(struct pfctl *, uint8_t, struct pfctl_watermarks *);
int parse_config(char *, struct pfctl *);
int parse_flags(char *);
-int pfctl_load_anchors(int, struct pfctl *, struct pfr_buffer *);
+int pfctl_load_anchors(int, struct pfctl *);
-void print_pool(struct pfctl_pool *, u_int16_t, u_int16_t, sa_family_t, int);
+void print_pool(struct pfctl_pool *, u_int16_t, u_int16_t, int);
void print_src_node(struct pfctl_src_node *, int);
void print_eth_rule(struct pfctl_eth_rule *, const char *, int);
void print_rule(struct pfctl_rule *, const char *, int, int);
@@ -323,7 +329,7 @@ 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);
+ u_int32_t, struct pfr_uktable *);
void pfctl_clear_fingerprints(int, int);
int pfctl_file_fingerprints(int, int, const char *);
@@ -379,5 +385,8 @@ struct node_host *host(const char *, int);
int append_addr(struct pfr_buffer *, char *, int, int);
int append_addr_host(struct pfr_buffer *,
struct node_host *, int, int);
+int pfr_ktable_compare(struct pfr_ktable *,
+ struct pfr_ktable *);
+RB_PROTOTYPE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare);
#endif /* _PFCTL_PARSER_H_ */
diff --git a/sbin/pfctl/pfctl_radix.c b/sbin/pfctl/pfctl_radix.c
index 00e4207d377b..98f907738d95 100644
--- a/sbin/pfctl/pfctl_radix.c
+++ b/sbin/pfctl/pfctl_radix.c
@@ -48,6 +48,7 @@
#include <err.h>
#include "pfctl.h"
+#include "pfctl_parser.h"
#define BUF_SIZE 256
@@ -55,6 +56,19 @@ extern int dev;
static int pfr_next_token(char buf[BUF_SIZE], FILE *);
+struct pfr_ktablehead pfr_ktables = { 0 };
+RB_GENERATE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare);
+
+int
+pfr_ktable_compare(struct pfr_ktable *p, struct pfr_ktable *q)
+{
+ int d;
+
+ if ((d = strncmp(p->pfrkt_name, q->pfrkt_name, PF_TABLE_NAME_SIZE)))
+ return (d);
+ return (strcmp(p->pfrkt_anchor, q->pfrkt_anchor));
+}
+
static void
pfr_report_error(struct pfr_table *tbl, struct pfioc_table *io,
const char *err)
@@ -122,7 +136,10 @@ pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
{
int ret;
- ret = pfctl_table_add_addrs(dev, tbl, addr, size, nadd, flags);
+ if (*nadd)
+ *nadd = 0;
+
+ ret = pfctl_table_add_addrs_h(pfh, tbl, addr, size, nadd, flags);
if (ret) {
errno = ret;
return (-1);
@@ -136,7 +153,7 @@ pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
{
int ret;
- ret = pfctl_table_del_addrs(dev, tbl, addr, size, ndel, flags);
+ ret = pfctl_table_del_addrs_h(pfh, tbl, addr, size, ndel, flags);
if (ret) {
errno = ret;
return (-1);
@@ -253,6 +270,7 @@ pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size,
struct pfioc_table io;
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
+ DBGPRINT("%s %p %d %p\n", __func__, tbl, size, addr);
errno = EINVAL;
return (-1);
}
diff --git a/sbin/pfctl/pfctl_table.c b/sbin/pfctl/pfctl_table.c
index f583f5ef8e79..4955e1791fd7 100644
--- a/sbin/pfctl/pfctl_table.c
+++ b/sbin/pfctl/pfctl_table.c
@@ -417,34 +417,39 @@ print_table(const 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",
+ if (verbose)
+ printf("%c%c%c%c%c%c%c\t",
(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);
+ (ta->pfrt_flags & PFR_TFLAG_COUNTERS) ? 'C' : '-');
+
+ printf("%s", ta->pfrt_name);
+ if (ta->pfrt_anchor[0] != '\0')
+ printf("@%s", ta->pfrt_anchor);
+
+ printf("\n");
}
int
print_tstats(const struct pfr_tstats *ts, int debug)
{
- time_t time = ts->pfrts_tzero;
- int dir, op;
+ time_t time = ts->pfrts_tzero;
+ int dir, op;
+ char *ct;
if (!debug && !(ts->pfrts_flags & PFR_TFLAG_ACTIVE))
return (0);
+ ct = ctime(&time);
print_table(&ts->pfrts_t, 1, debug);
printf("\tAddresses: %d\n", ts->pfrts_cnt);
- printf("\tCleared: %s", ctime(&time));
+ if (ct)
+ printf("\tCleared: %s", ct);
+ else
+ printf("\tCleared: %lld\n", (long long)time);
printf("\tReferences: [ Anchors: %-18d Rules: %-18d ]\n",
ts->pfrts_refcnt[PFR_REFCNT_ANCHOR],
ts->pfrts_refcnt[PFR_REFCNT_RULE]);
@@ -543,12 +548,17 @@ nonzero_astats(struct pfr_astats *as)
void
print_astats(struct pfr_astats *as, int dns)
{
- time_t time = as->pfras_tzero;
- int dir, op;
+ time_t time = as->pfras_tzero;
+ int dir, op;
+ char *ct;
+ ct = ctime(&time);
print_addrx(&as->pfras_a, NULL, dns);
- printf("\tCleared: %s", ctime(&time));
- if (as->pfras_a.pfra_fback == PFR_FB_NOCOUNT)
+ if (ct)
+ printf("\tCleared: %s", ct);
+ else
+ printf("\tCleared: %lld\n", (long long)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++)
@@ -560,19 +570,50 @@ print_astats(struct pfr_astats *as, int dns)
int
pfctl_define_table(char *name, int flags, int addrs, const char *anchor,
- struct pfr_buffer *ab, u_int32_t ticket)
+ struct pfr_buffer *ab, u_int32_t ticket, struct pfr_uktable *ukt)
{
- struct pfr_table tbl;
+ struct pfr_table tbl_buf;
+ struct pfr_table *tbl;
+
+ if (ukt == NULL) {
+ bzero(&tbl_buf, sizeof(tbl_buf));
+ tbl = &tbl_buf;
+ } else {
+ if (ab->pfrb_size != 0) {
+ /*
+ * copy IP addresses which come with table from
+ * temporal buffer to buffer attached to table.
+ */
+ ukt->pfrukt_addrs = *ab;
+ ab->pfrb_size = 0;
+ ab->pfrb_msize = 0;
+ ab->pfrb_caddr = NULL;
+ } else
+ memset(&ukt->pfrukt_addrs, 0,
+ sizeof(struct pfr_buffer));
+
+ tbl = &ukt->pfrukt_t;
+ }
- 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;
+ 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, "%s: strlcpy", __func__);
+ tbl->pfrt_flags = flags;
+ DBGPRINT("%s %s@%s [%x]\n", __func__, tbl->pfrt_name, tbl->pfrt_anchor,
+ tbl->pfrt_flags);
+
+ /*
+ * non-root anchors processed by parse.y are loaded to kernel later.
+ * Here we load tables, which are either created for root anchor
+ * or by 'pfctl -t ... -T ...' command.
+ */
+ if (ukt != NULL)
+ return (0);
- return pfr_ina_define(&tbl, ab->pfrb_caddr, ab->pfrb_size, NULL,
- NULL, ticket, addrs ? PFR_FLAG_ADDRSTOO : 0);
+ return (pfr_ina_define(tbl, ab->pfrb_caddr, ab->pfrb_size, NULL, NULL,
+ ticket, addrs ? PFR_FLAG_ADDRSTOO : 0));
}
void
@@ -653,8 +694,9 @@ pfctl_show_ifaces(const char *filter, int opts)
void
print_iface(struct pfi_kif *p, int opts)
{
- time_t tzero = p->pfik_tzero;
- int i, af, dir, act;
+ time_t tzero = p->pfik_tzero;
+ int i, af, dir, act;
+ char *ct;
printf("%s", p->pfik_name);
if (opts & PF_OPT_VERBOSE) {
@@ -665,7 +707,11 @@ print_iface(struct pfi_kif *p, int opts)
if (!(opts & PF_OPT_VERBOSE2))
return;
- printf("\tCleared: %s", ctime(&tzero));
+ ct = ctime(&tzero);
+ if (ct)
+ printf("\tCleared: %s", ct);
+ else
+ printf("\tCleared: %lld\n", (long long)tzero);
printf("\tReferences: %-18d\n", p->pfik_rulerefs);
for (i = 0; i < 8; i++) {
af = (i>>2) & 1;
diff --git a/sbin/pfctl/tests/files/pf0088.in b/sbin/pfctl/tests/files/pf0088.in
index 4700b6916b7e..a85aa84a30bb 100644
--- a/sbin/pfctl/tests/files/pf0088.in
+++ b/sbin/pfctl/tests/files/pf0088.in
@@ -16,7 +16,7 @@ pass to 10.0.0.2 keep state
block from 10.0.0.3 to 10.0.0.2
pass to 10.0.0.2 modulate state
block from 10.0.0.3 to 10.0.0.2
-pass to 10.0.0.2 synproxy state
+pass in to 10.0.0.2 synproxy state
pass out proto tcp from 10.0.0.4 to 10.0.0.5 keep state
diff --git a/sbin/pfctl/tests/files/pf0088.ok b/sbin/pfctl/tests/files/pf0088.ok
index 47251a4503dd..801056a4ab46 100644
--- a/sbin/pfctl/tests/files/pf0088.ok
+++ b/sbin/pfctl/tests/files/pf0088.ok
@@ -11,7 +11,7 @@ pass inet from any to 10.0.0.2 flags S/SA keep state
block drop inet from 10.0.0.3 to 10.0.0.2
pass inet from any to 10.0.0.2 flags S/SA modulate state
block drop inet from 10.0.0.3 to 10.0.0.2
-pass inet from any to 10.0.0.2 flags S/SA synproxy state
+pass in inet from any to 10.0.0.2 flags S/SA synproxy state
pass out inet proto tcp from 10.0.0.4 to 10.0.0.5 flags S/SA keep state
pass out inet proto tcp from 10.0.0.4 to 10.0.0.5 port = http flags S/SA keep state
pass out all flags S/SA keep state
diff --git a/sbin/pfctl/tests/files/pf1072.fail b/sbin/pfctl/tests/files/pf1072.fail
new file mode 100644
index 000000000000..06ef5ae457e5
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1072.fail
@@ -0,0 +1 @@
+invalid port range
diff --git a/sbin/pfctl/tests/files/pf1072.in b/sbin/pfctl/tests/files/pf1072.in
new file mode 100644
index 000000000000..e09e92388ce1
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1072.in
@@ -0,0 +1 @@
+pass in proto tcp from any port 500:100 to any
diff --git a/sbin/pfctl/tests/files/pf1073.in b/sbin/pfctl/tests/files/pf1073.in
new file mode 100644
index 000000000000..477995893ac3
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1073.in
@@ -0,0 +1 @@
+pass in on vtnet0 route-to ( vtnet1 2001:db8::1 ) prefer-ipv6-nexthop inet
diff --git a/sbin/pfctl/tests/files/pf1073.ok b/sbin/pfctl/tests/files/pf1073.ok
new file mode 100644
index 000000000000..f34867508c75
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1073.ok
@@ -0,0 +1 @@
+pass in on vtnet0 route-to (vtnet1 2001:db8::1) prefer-ipv6-nexthop inet all flags S/SA keep state
diff --git a/sbin/pfctl/tests/files/pf1074.fail b/sbin/pfctl/tests/files/pf1074.fail
new file mode 100644
index 000000000000..afe8ee3c458f
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1074.fail
@@ -0,0 +1 @@
+no routing address with matching address family found.
diff --git a/sbin/pfctl/tests/files/pf1074.in b/sbin/pfctl/tests/files/pf1074.in
new file mode 100644
index 000000000000..5d285bc5d6e8
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1074.in
@@ -0,0 +1 @@
+pass in on vtnet0 route-to ( vtnet1 2001:db8::1 ) inet
diff --git a/sbin/pfctl/tests/files/pf1075.in b/sbin/pfctl/tests/files/pf1075.in
new file mode 100644
index 000000000000..835a31a25c6a
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1075.in
@@ -0,0 +1 @@
+pass inet from (lo0)/24 once
diff --git a/sbin/pfctl/tests/files/pf1075.ok b/sbin/pfctl/tests/files/pf1075.ok
new file mode 100644
index 000000000000..2369c9410cda
--- /dev/null
+++ b/sbin/pfctl/tests/files/pf1075.ok
@@ -0,0 +1 @@
+pass inet from (lo0)/24 to any flags S/SA keep state once
diff --git a/sbin/pfctl/tests/pfctl_test_list.inc b/sbin/pfctl/tests/pfctl_test_list.inc
index 51729bc9adad..9dd4a590ad8f 100644
--- a/sbin/pfctl/tests/pfctl_test_list.inc
+++ b/sbin/pfctl/tests/pfctl_test_list.inc
@@ -180,3 +180,7 @@ PFCTL_TEST(1068, "max-pkt-rate")
PFCTL_TEST(1069, "max-pkt-size")
PFCTL_TEST_FAIL(1070, "include line number")
PFCTL_TEST(1071, "mask length on (lo0)")
+PFCTL_TEST_FAIL(1072, "Invalid port range")
+PFCTL_TEST(1073, "Filter AF different than route-to AF, with prefer-ipv6-nexthop")
+PFCTL_TEST_FAIL(1074, "Filter AF different than route-to AF, without prefer-ipv6-nexthop")
+PFCTL_TEST(1075, "One shot rule")
diff --git a/sbin/ping/Makefile b/sbin/ping/Makefile
index b4e3f115b245..30c68cbaba52 100644
--- a/sbin/ping/Makefile
+++ b/sbin/ping/Makefile
@@ -32,8 +32,6 @@ CFLAGS+=-DWITH_CASPER
CFLAGS+=-DIPSEC
LIBADD+= ipsec
-CFLAGS+= -Wno-error=unused-but-set-variable
-
HAS_TESTS=
SUBDIR.${MK_TESTS}+= tests
diff --git a/sbin/ping/tests/Makefile b/sbin/ping/tests/Makefile
index 0520b1d634cf..7d3ab02b9a86 100644
--- a/sbin/ping/tests/Makefile
+++ b/sbin/ping/tests/Makefile
@@ -1,5 +1,6 @@
ATF_TESTS_C+= in_cksum_test
-SRCS.in_cksum_test= in_cksum_test.c ../utils.c
+.PATH: ${.CURDIR:H}
+SRCS.in_cksum_test= in_cksum_test.c utils.c
PACKAGE= tests
diff --git a/sbin/rcorder/rcorder.c b/sbin/rcorder/rcorder.c
index 87b0e873c7ae..3d2a67c82a5a 100644
--- a/sbin/rcorder/rcorder.c
+++ b/sbin/rcorder/rcorder.c
@@ -980,9 +980,11 @@ do_file(filenode *fnode, strnodelist *stack_ptr)
fnode->last->next = fnode->next;
}
- if (fnode->issues_count)
- warnx("`%s' was seen in circular dependencies for %d times.",
- fnode->filename, fnode->issues_count);
+ if (fnode->issues_count) {
+ warnx("`%s' was seen in circular dependencies %d time%s.",
+ fnode->filename, fnode->issues_count,
+ fnode->issues_count > 1 ? "s" : "");
+ }
DPRINTF((stderr, "nuking %s\n", fnode->filename));
}
diff --git a/sbin/reboot/reboot.8 b/sbin/reboot/reboot.8
index 0ddcee643244..1bbc39d52be4 100644
--- a/sbin/reboot/reboot.8
+++ b/sbin/reboot/reboot.8
@@ -110,6 +110,15 @@ Care should be taken if
.Va value
contains any characters that are special to the shell or loader's configuration
parsing code.
+.It Fl f
+Force reboot.
+Normally,
+.Nm
+checks for the presence of the next kernel,
+and absence of the
+.Pa /var/run/noshutdown
+file.
+Without this flag, reboot is denied if one of the conditions failed.
.It Fl k Ar kname
Boot the specified kernel
.Ar kname
diff --git a/sbin/reboot/reboot.c b/sbin/reboot/reboot.c
index 9825d4f96319..f6065e80fb66 100644
--- a/sbin/reboot/reboot.c
+++ b/sbin/reboot/reboot.c
@@ -40,6 +40,7 @@
#include <err.h>
#include <errno.h>
#include <fcntl.h>
+#include <paths.h>
#include <pwd.h>
#include <signal.h>
#include <spawn.h>
@@ -222,6 +223,7 @@ main(int argc, char *argv[])
{
struct utmpx utx;
const struct passwd *pw;
+ struct stat st;
int ch, howto = 0, i, sverrno;
bool Dflag, fflag, lflag, Nflag, nflag, qflag;
uint64_t pageins;
@@ -294,6 +296,11 @@ main(int argc, char *argv[])
if (argc != 0)
usage();
+ if (!donextboot && !fflag && stat(_PATH_NOSHUTDOWN, &st) == 0) {
+ errx(1, "Reboot cannot be done, " _PATH_NOSHUTDOWN
+ " is present");
+ }
+
if (Dflag && ((howto & ~RB_HALT) != 0 || kernel != NULL))
errx(1, "cannot delete existing nextboot config and do anything else");
if ((howto & (RB_DUMP | RB_HALT)) == (RB_DUMP | RB_HALT))
diff --git a/sbin/recoverdisk/recoverdisk.1 b/sbin/recoverdisk/recoverdisk.1
index 2999ac6ec409..76085f3ceb41 100644
--- a/sbin/recoverdisk/recoverdisk.1
+++ b/sbin/recoverdisk/recoverdisk.1
@@ -27,10 +27,11 @@
.Os
.Sh NAME
.Nm recoverdisk
-.Nd recover data from hard disk or optical media
+.Nd recover data from disk-like devices.
.Sh SYNOPSIS
.Nm
.Op Fl b Ar bigsize
+.Op Fl i Ar interval
.Op Fl r Ar readlist
.Op Fl s Ar interval
.Op Fl u Ar pattern
@@ -41,79 +42,109 @@
.Sh DESCRIPTION
The
.Nm
-utility reads data from the
+utility reads all data from the
.Ar source
-file until all blocks could be successfully read.
+and retries read operations until they succeed.
If
.Ar destination
-was specified all data is being written to that file.
-It starts reading in multiples of the sector size.
-Whenever a block fails, it is put to the end of the working queue and will be
-read again, possibly with a smaller read size.
+is specified all data read be written there.
.Pp
-By default it uses block sizes of roughly 1 MB, 32kB, and the native
-sector size (usually 512 bytes).
-These figures are adjusted slightly, for devices whose sectorsize is not a
-power of 2, e.g., audio CDs with a sector size of 2352 bytes.
+The internal work-list can be saved and loaded so that
+.Nm
+sessions can be resumed, for instance when a marginal
+source hard-disk shuts down.
+.Pp
+The work-list is initialized with a single item which covers the entire
+.Ar source
+and
+.Nm
+always chips away at the first item on the work-list.
+
+When a read succeeds, that part of the current chunk is eliminated
+from the work-list.
+
+When a read fails, that part of the item is appended to the worklist
+as a separate item, and will be retried in due order.
+If
+.Ar destination
+is specified, the corresponding range is filled with '_UNREAD_'.
+.Pp
+The first pass attempts to read everything in "big-size" chunks,
+the second pass reads in "medium-size" chunks and third and subsequent
+passes read in "small-size" chunks.
+This three stage process is an attempt to optimize the case where only
+a few bad blocks exist on
+.Ar source .
+If too many read-errors are encountered,
+.Nm
+will fall back to smaller sizes sooner.
+.Pp
+The three sizes default to 128kB (or less if the sector size does
+not divide 128kB cleanly, for instance audio CD media), and the
+reported
+.Dv DIOCGSTRIPESIZE
+and
+.Dv DIOCGSECTORSIZE
+respectively.
.Pp
The options are as follows:
.Bl -tag -width indent
.It Fl b Ar bigsize
-The size of reads attempted first.
-The middle pass is roughly the logarithmic average of the bigsize and
-the sectorsize.
-.It Fl r Ar readlist
-Read the list of blocks and block sizes to read from the specified file.
-.It Fl s Ar interval
-How often we should update the writelist file while things go OK.
-The default is 60 and the unit is "progress messages" so if things
-go well, this is the same as once per minute.
+The size of reads attempted in first pass.
+.It Fl m Ar mediumsize
+The size of reads attempted in second pass.
+.It Fl s Ar smallsize
+The size of reads attempted in third and subsequent passes.
+.It Fl r Ar work-list-file
+Read the work-list from a file.
+.It Fl w Ar work-list-file
+Write the work-list to a file when a read succeed, but at most once
+every minute.
+.It Fl l Ar log-file
+Each successful read is logged with timestamp, offset and length.
+.It Fl t Ar totalsize
+How many bytes should be recovered.
+The default is what
+.Dv DIOCGMEDIASIZE
+reports for character and block devices or
+.Dv st_size
+if
+.Ar source
+is a regular file.
+.It Fl i Ar pause
+.Xr sleep 3
+this long between reads. This reduces the load on the
+.Ar source
+device and the system in general.
+.It Fl p Ar pause
+.Xr sleep 3
+this long whenever a read fails.
+This makes the
+.Ar source
+device look less sick to the operating system.
.It Fl u Ar pattern
-By default blocks which encounter read errors will be filled with
-the pattern
+By default blocks which cannot be read are filled with the pattern
.Ql _UNREAD_
in the output file.
-This option can be
-used to specify another pattern.
-Nothing gets written if the string is empty.
+This option can be used to specify a different pattern.
+If the pattern is the empty string, nothing is written.
.It Fl v
-Enables nicer status report using ANSI escapes and UTF-8.
-.It Fl w Ar writelist
-Write the list of remaining blocks to read to the specified file if
-.Nm
-is aborted via
-.Dv SIGINT .
+Produce a detailed progress report with ANSI escapes and UTF-8.
.El
.Pp
-The
-.Fl r
-and
-.Fl w
-options can be specified together.
-Especially, they can point to the same file, which will be updated on abort.
-.Sh OUTPUT
-The
.Nm
-utility
-prints several columns, detailing the progress
-.Bl -tag -width remaining
-.It Va start
-Starting offset of the current block.
-.It Va size
-Read size of the current block.
-.It Va len
-Length of the current block.
-.It Va state
-Is increased for every failed read.
-.It Va done
-Number of bytes already read.
-.It Va remaining
-Number of bytes remaining.
-.It Va "% done"
-Percent complete.
-.El
+can be aborted with
+.Dv SIGINT ,
+but with a sick
+.Ar source
+it may take up to several minutes before the current read operation
+returns from the kernel.
+.Pp
.Sh EXAMPLES
.Bd -literal
+# check if all sectors can be read on a USB stick:
+recoverdisk /dev/da0
+
# recover data from failing hard drive ada3
recoverdisk /dev/ada3 /data/disk.img
@@ -129,10 +160,72 @@ recoverdisk -r worklist -w worklist /dev/cd0 /data/cd.iso
# recover a single file from the unreadable media
recoverdisk /cdrom/file.avi file.avi
-# If the disk hangs the system on read-errors try:
-recoverdisk -b 0 /dev/ada3 /somewhere
-
.Ed
+.Sh PRACTICAL ADVICE
+In Datamuseum.dk
+.Nm
+has been used to recover all sorts of data-media for two decades,
+here are some things we have learned:
+.Bl -bullet
+.It
+Interacting with failing hardware has a tendency to crash machines,
+so it is always a good idea to use the
+.Fl -w work-list-file
+so that it is possible to continue.
+.It
+When attempting to recover hard to read data from failing hard disks,
+it pays to pamper the drive as much as possible:
+.It
+It is generally best to keep the drive in it's usual physical orientation,
+but it can also help to try other orientations.
+.It
+Insulate the drive from external vibrations.
+.It
+Keep the drive cool with a fan.
+.It
+If possible, power the drive from a laboratory power supply.
+.It
+Do not lose patience: Let
+.Nm
+run as long as possible.
+.It
+(S)ATA controllers do not handle failing disks well, if this
+is a problem, use a USB-(S)ATA adapter instead.
+.It
+The
+.Nm
+source code is deliberately written to be easily portable to
+older versions of
+.Fx
+and to other operating systems.
+.It
+If you need to read ST-506, RLL or ESDI drives
+.Fx 3.5.1
+is a good compromise.
+.It
+Sometimes forcing the disk to step between reads helps.
+Since
+.Nm
+process the work-list in the order it is read, this
+can be accomplished by sorting the work-list with
+something like:
+.Dl % sort +0.5
+.It
+By default the
+.Xr CAM
+layer will retry failing read operations, but that
+will get stuck on the bad sectors for long time
+and delay recovering what actually can be read from
+a rapidly failing drive.
+In that situation, set the appropriate
+.Dl kern.cam.*.retry_count
+sysctl to zero.
+.It
+For floppies and un-zoned hard disks (ST-506 to
+early IDE) set
+.Fl b Ar bigsize
+to the size of a track.
+.El
.Sh SEE ALSO
.Xr dd 1 ,
.Xr ada 4 ,
@@ -143,7 +236,8 @@ recoverdisk -b 0 /dev/ada3 /somewhere
The
.Nm
utility first appeared in
-.Fx 7.0 .
+.Fx 7.0
+because Somebodyâ„¢ forgot to make a backup copy.
.Sh AUTHORS
.An -nosplit
The original implementation was done by
@@ -151,34 +245,29 @@ The original implementation was done by
with minor improvements from
.An Ulrich Sp\(:orlein Aq Mt uqs@FreeBSD.org .
.Pp
-This manual page was written by
+This manual page was originally written by
.An Ulrich Sp\(:orlein .
.Sh BUGS
-Reading from media where the sectorsize is not a power of 2 will make all
-1 MB reads fail.
-This is due to the DMA reads being split up into blocks of at most 128kB.
-These reads then fail if the sectorsize is not a divisor of 128kB.
-When reading a full raw audio CD, this leads to roughly 700 error messages
-flying by.
-This is harmless and can be avoided by setting
-.Fl b
-to no more than 128kB.
+If a failing device causes the machine to crash, there is
+a risk that a chunk might have been successfully read
+and removed from the work-list, but not yet flushed to
+the
+.Ar destination .
.Pp
.Nm
-needs to know about read errors as fast as possible, i.e., retries by lower
-layers will usually slow down the operation.
-When using
-.Xr cam 4
-attached drives, you may want to set kern.cam.XX.retry_count to zero, e.g.:
-.Bd -literal
-# sysctl kern.cam.ada.retry_count=0
-# sysctl kern.cam.cd.retry_count=0
-# sysctl kern.cam.da.retry_count=0
-.Ed
-.\".Pp
-.\"When reading from optical media, a bug in the GEOM framework will
-.\"prevent it from seeing that the media has been removed.
-.\"The device can still be opened, but all reads will fail.
-.\"This is usually harmless, but will send
-.\".Nm
-.\"into an infinite loop.
+calls
+.Xr fdatasync 3
+on the destination before writing the work-list to a
+temporary file, and calls it again on the temporary
+file before renaming it to the specified
+.Fl w Ar work-file-list
+filename.
+But even then things dont always work out.
+.Pp
+.Nm
+should have an option for reconstructing the work-list
+from the
+.Ar destination
+by enumerating the
+.Fl u Ar pattern
+filled ranges.
diff --git a/sbin/recoverdisk/recoverdisk.c b/sbin/recoverdisk/recoverdisk.c
index 446266c36d50..5971f78738ac 100644
--- a/sbin/recoverdisk/recoverdisk.c
+++ b/sbin/recoverdisk/recoverdisk.c
@@ -8,6 +8,7 @@
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
* ----------------------------------------------------------------------------
*/
+
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/disk.h>
@@ -27,18 +28,15 @@
#include <time.h>
#include <unistd.h>
-/* Safe printf into a fixed-size buffer */
-#define bprintf(buf, fmt, ...) \
- do { \
- int ibprintf; \
- ibprintf = snprintf(buf, sizeof buf, fmt, __VA_ARGS__); \
- assert(ibprintf >= 0 && ibprintf < (int)sizeof buf); \
- } while (0)
+/*
+ * This is a compromise between speed and wasted effort
+ */
+#define COMPROMISE_SIZE (128<<10)
struct lump {
- off_t start;
- off_t len;
- int state;
+ uint64_t start;
+ uint64_t len;
+ unsigned pass;
TAILQ_ENTRY(lump) list;
};
@@ -46,25 +44,34 @@ struct period {
time_t t0;
time_t t1;
char str[20];
- off_t bytes_read;
+ uint64_t bytes_read;
TAILQ_ENTRY(period) list;
};
TAILQ_HEAD(period_head, period);
static volatile sig_atomic_t aborting = 0;
static int verbose = 0;
-static size_t bigsize = 1024 * 1024;
-static size_t medsize;
-static size_t minsize = 512;
-static off_t tot_size;
-static off_t done_size;
+static uint64_t big_read;
+static uint64_t medium_read;
+static uint64_t small_read;
+static uint64_t total_size;
+static uint64_t done_size;
+static uint64_t wasted_size;
static char *input;
-static char *wworklist = NULL;
-static char *rworklist = NULL;
+static char *write_worklist_file = NULL;
+static char *read_worklist_file = NULL;
static const char *unreadable_pattern = "_UNREAD_";
-static const int write_errors_are_fatal = 1;
-static int fdr, fdw;
-
+static int write_errors_are_fatal = 1;
+static int read_fd, write_fd;
+static FILE *log_file = NULL;
+static char *work_buf;
+static char *pattern_buf;
+static double error_pause;
+static double interval;
+
+static unsigned nlumps;
+static double n_reads, n_good_reads;
+static time_t t_first;
static TAILQ_HEAD(, lump) lumps = TAILQ_HEAD_INITIALIZER(lumps);
static struct period_head minute = TAILQ_HEAD_INITIALIZER(minute);
static struct period_head quarter = TAILQ_HEAD_INITIALIZER(quarter);
@@ -74,7 +81,8 @@ static struct period_head day = TAILQ_HEAD_INITIALIZER(quarter);
/**********************************************************************/
static void
-report_good_read2(time_t now, size_t bytes, struct period_head *ph, time_t dt)
+account_good_read_period(time_t now, uint64_t bytes,
+ struct period_head *ph, time_t dt)
{
struct period *pp;
const char *fmt;
@@ -82,7 +90,7 @@ report_good_read2(time_t now, size_t bytes, struct period_head *ph, time_t dt)
pp = TAILQ_FIRST(ph);
if (pp == NULL || pp->t1 < now) {
- pp = calloc(1, sizeof(*pp));
+ pp = calloc(1UL, sizeof(*pp));
assert(pp != NULL);
pp->t0 = (now / dt) * dt;
pp->t1 = (now / dt + 1) * dt;
@@ -98,13 +106,13 @@ report_good_read2(time_t now, size_t bytes, struct period_head *ph, time_t dt)
}
static void
-report_good_read(time_t now, size_t bytes)
+account_good_read(time_t now, uint64_t bytes)
{
- report_good_read2(now, bytes, &minute, 60L);
- report_good_read2(now, bytes, &quarter, 900L);
- report_good_read2(now, bytes, &hour, 3600L);
- report_good_read2(now, bytes, &day, 86400L);
+ account_good_read_period(now, bytes, &minute, 60L);
+ account_good_read_period(now, bytes, &quarter, 900L);
+ account_good_read_period(now, bytes, &hour, 3600L);
+ account_good_read_period(now, bytes, &day, 86400L);
}
static void
@@ -114,20 +122,18 @@ report_one_period(const char *period, struct period_head *ph)
int n;
n = 0;
- printf("%s \xe2\x94\x82", period);
+ printf("%s ", period);
TAILQ_FOREACH(pp, ph, list) {
- if (n == 3) {
+ if (++n == 4) {
TAILQ_REMOVE(ph, pp, list);
free(pp);
break;
}
- if (n++)
- printf(" \xe2\x94\x82");
- printf(" %s %14jd", pp->str, pp->bytes_read);
+ printf("\xe2\x94\x82 %s %14ju ",
+ pp->str, (uintmax_t)pp->bytes_read);
}
for (; n < 3; n++) {
- printf(" \xe2\x94\x82");
- printf(" %5s %14s", "", "");
+ printf("\xe2\x94\x82 %5s %14s ", "", "");
}
printf("\x1b[K\n");
}
@@ -146,27 +152,23 @@ report_periods(void)
static void
set_verbose(void)
{
- struct winsize wsz;
- if (!isatty(STDIN_FILENO) || ioctl(STDIN_FILENO, TIOCGWINSZ, &wsz))
- return;
verbose = 1;
}
static void
-report_header(int eol)
+report_header(const char *term)
{
- printf("%13s %7s %13s %5s %13s %13s %9s",
+ printf("%13s %7s %13s %5s %13s %13s %9s%s",
"start",
"size",
"block-len",
"pass",
"done",
"remaining",
- "% done");
- if (eol)
- printf("\x1b[K");
- putchar('\n');
+ "% done",
+ term
+ );
}
#define REPORTWID 79
@@ -186,20 +188,20 @@ report_hline(const char *how)
printf("\x1b[K\n");
}
-static off_t hist[REPORTWID];
-static off_t last_done = -1;
+static uint64_t hist[REPORTWID];
+static uint64_t prev_done = ~0UL;
static void
-report_histogram(const struct lump *lp)
+report_histogram(uint64_t start)
{
- off_t j, bucket, fp, fe, k, now;
+ uint64_t j, bucket, fp, fe, k, now;
double a;
struct lump *lp2;
- bucket = tot_size / REPORTWID;
- if (tot_size > bucket * REPORTWID)
+ bucket = total_size / REPORTWID;
+ if (total_size > bucket * REPORTWID)
bucket += 1;
- if (done_size != last_done) {
+ if (done_size != prev_done) {
memset(hist, 0, sizeof hist);
TAILQ_FOREACH(lp2, &lumps, list) {
fp = lp2->start;
@@ -213,9 +215,9 @@ report_histogram(const struct lump *lp)
fp += k;
}
}
- last_done = done_size;
+ prev_done = done_size;
}
- now = lp->start / bucket;
+ now = start / bucket;
for (j = 0; j < REPORTWID; j++) {
a = round(8 * (double)hist[j] / bucket);
assert (a >= 0 && a < 9);
@@ -228,7 +230,7 @@ report_histogram(const struct lump *lp)
} else {
putchar(0xe2);
putchar(0x96);
- putchar(0x80 + (int)a);
+ putchar(0x80 + (char)a);
}
if (j == now)
printf("\x1b[0m");
@@ -237,34 +239,40 @@ report_histogram(const struct lump *lp)
}
static void
-report(const struct lump *lp, size_t sz)
+report(uint64_t sz)
{
struct winsize wsz;
+ const struct lump *lp = TAILQ_FIRST(&lumps);
int j;
-
- assert(lp != NULL);
+ unsigned pass = 0;
+ uintmax_t start = 0, length = 0;
+ time_t t_now = time(NULL);
+
+ if (lp != NULL) {
+ pass = lp->pass;
+ start = lp->start;
+ length = lp->len;
+ }
if (verbose) {
printf("\x1b[H%s\x1b[K\n", input);
- report_header(1);
- } else {
- putchar('\r');
+ report_header("\x1b[K\n");
}
- printf("%13jd %7zu %13jd %5d %13jd %13jd %9.4f",
- (intmax_t)lp->start,
- sz,
- (intmax_t)lp->len,
- lp->state,
- (intmax_t)done_size,
- (intmax_t)(tot_size - done_size),
- 100*(double)done_size/(double)tot_size
+ printf("%13ju %7ju %13ju %5u %13ju %13ju %9.4f",
+ start,
+ (uintmax_t)sz,
+ length,
+ pass,
+ (uintmax_t)done_size,
+ (uintmax_t)(total_size - done_size),
+ 100*(double)done_size/(double)total_size
);
if (verbose) {
printf("\x1b[K\n");
report_hline(NULL);
- report_histogram(lp);
+ report_histogram(start);
if (TAILQ_EMPTY(&minute)) {
report_hline(NULL);
} else {
@@ -272,27 +280,36 @@ report(const struct lump *lp, size_t sz)
report_periods();
report_hline("\xe2\x94\xb4");
}
+ printf("Missing: %u", nlumps);
+ printf(" Success: %.0f/%.0f =", n_good_reads, n_reads);
+ printf(" %.4f%%", 100 * n_good_reads / n_reads);
+ printf(" Duration: %.3fs", (t_now - t_first) / n_reads);
+ printf("\x1b[K\n");
+ report_hline(NULL);
j = ioctl(STDIN_FILENO, TIOCGWINSZ, &wsz);
if (!j)
printf("\x1b[%d;1H", wsz.ws_row);
+ } else {
+ printf("\n");
}
- fflush(stdout);
}
/**********************************************************************/
static void
-new_lump(off_t start, off_t len, int state)
+new_lump(uint64_t start, uint64_t len, unsigned pass)
{
struct lump *lp;
+ assert(len > 0);
lp = malloc(sizeof *lp);
if (lp == NULL)
err(1, "Malloc failed");
lp->start = start;
lp->len = len;
- lp->state = state;
+ lp->pass = pass;
TAILQ_INSERT_TAIL(&lumps, lp, list);
+ nlumps += 1;
}
/**********************************************************************
@@ -306,98 +323,100 @@ save_worklist(void)
struct lump *llp;
char buf[PATH_MAX];
- if (fdw >= 0 && fdatasync(fdw))
+ if (write_fd >= 0 && fdatasync(write_fd))
err(1, "Write error, probably disk full");
- if (wworklist != NULL) {
- bprintf(buf, "%s.tmp", wworklist);
- (void)fprintf(stderr, "\nSaving worklist ...");
- (void)fflush(stderr);
+ if (write_worklist_file != NULL) {
+ snprintf(buf, sizeof(buf), "%s.tmp", write_worklist_file);
+ fprintf(stderr, "\nSaving worklist ...");
file = fopen(buf, "w");
if (file == NULL)
err(1, "Error opening file %s", buf);
- TAILQ_FOREACH(llp, &lumps, list)
- fprintf(file, "%jd %jd %d\n",
- (intmax_t)llp->start, (intmax_t)llp->len,
- llp->state);
- (void)fflush(file);
+ TAILQ_FOREACH(llp, &lumps, list) {
+ assert (llp->len > 0);
+ fprintf(file, "%ju %ju %u\n",
+ (uintmax_t)llp->start,
+ (uintmax_t)llp->len,
+ llp->pass);
+ }
+ fflush(file);
if (ferror(file) || fdatasync(fileno(file)) || fclose(file))
err(1, "Error writing file %s", buf);
- if (rename(buf, wworklist))
- err(1, "Error renaming %s to %s", buf, wworklist);
- (void)fprintf(stderr, " done.\n");
+ if (rename(buf, write_worklist_file))
+ err(1, "Error renaming %s to %s",
+ buf, write_worklist_file);
+ fprintf(stderr, " done.\n");
}
}
/* Read the worklist if -r was given */
-static off_t
-read_worklist(off_t t)
+static uint64_t
+read_worklist(void)
{
- off_t s, l, d;
- int state, lines;
+ uintmax_t start, length;
+ uint64_t missing = 0;
+ unsigned pass, lines;
FILE *file;
- (void)fprintf(stderr, "Reading worklist ...");
- (void)fflush(stderr);
- file = fopen(rworklist, "r");
+ fprintf(stderr, "Reading worklist ...");
+ file = fopen(read_worklist_file, "r");
if (file == NULL)
- err(1, "Error opening file %s", rworklist);
+ err(1, "Error opening file %s", read_worklist_file);
lines = 0;
- d = t;
for (;;) {
++lines;
- if (3 != fscanf(file, "%jd %jd %d\n", &s, &l, &state)) {
+ if (3 != fscanf(file, "%ju %ju %u\n", &start, &length, &pass)) {
if (!feof(file))
- err(1, "Error parsing file %s at line %d",
- rworklist, lines);
+ err(1, "Error parsing file %s at line %u",
+ read_worklist_file, lines);
else
break;
}
- new_lump(s, l, state);
- d -= l;
+ if (length > 0) {
+ new_lump(start, length, pass);
+ missing += length;
+ }
}
if (fclose(file))
- err(1, "Error closing file %s", rworklist);
- (void)fprintf(stderr, " done.\n");
+ err(1, "Error closing file %s", read_worklist_file);
+ fprintf(stderr, " done.\n");
/*
- * Return the number of bytes already read
- * (at least not in worklist).
+ * Return the number of bytes outstanding
*/
- return (d);
+ return (missing);
}
/**********************************************************************/
static void
-write_buf(int fd, const void *buf, ssize_t len, off_t where)
+write_buf(int fd, const void *buf, uint64_t length, uint64_t where)
{
- ssize_t i;
+ int64_t i;
- i = pwrite(fd, buf, len, where);
- if (i == len)
+ i = pwrite(fd, buf, length, (off_t)where);
+ if (i > 0 && (uint64_t)i == length)
return;
- printf("\nWrite error at %jd/%zu\n\t%s\n",
- where, i, strerror(errno));
+ printf("\nWrite error at %ju/%ju: %jd (%s)\n",
+ (uintmax_t)where,
+ (uintmax_t)length,
+ (intmax_t)i, strerror(errno));
save_worklist();
if (write_errors_are_fatal)
exit(3);
}
static void
-fill_buf(char *buf, ssize_t len, const char *pattern)
+fill_buf(char *buf, int64_t len, const char *pattern)
{
- ssize_t sz = strlen(pattern);
- ssize_t i, j;
+ int64_t sz = strlen(pattern);
+ int64_t i;
for (i = 0; i < len; i += sz) {
- j = len - i;
- if (j > sz)
- j = sz;
- memcpy(buf + i, pattern, j);
+ memcpy(buf + i, pattern, MIN(len - i, sz));
}
}
@@ -406,45 +425,356 @@ fill_buf(char *buf, ssize_t len, const char *pattern)
static void
usage(void)
{
- (void)fprintf(stderr, "usage: recoverdisk [-b bigsize] [-r readlist] "
+ fprintf(stderr, "usage: recoverdisk "
+ "[-b big_read] [-i interval ] [-r readlist] "
"[-s interval] [-w writelist] source [destination]\n");
/* XXX update */
exit(1);
}
static void
-sighandler(__unused int sig)
+sighandler(int sig)
{
+ (void)sig;
aborting = 1;
}
+/**********************************************************************/
+
+static int64_t
+attempt_one_lump(time_t t_now)
+{
+ struct lump *lp;
+ uint64_t sz;
+ int64_t retval;
+ int error;
+
+ lp = TAILQ_FIRST(&lumps);
+ if (lp == NULL)
+ return(0);
+
+ if (lp->pass == 0) {
+ sz = MIN(lp->len, big_read);
+ } else if (lp->pass == 1) {
+ sz = MIN(lp->len, medium_read);
+ } else {
+ sz = MIN(lp->len, small_read);
+ }
+
+ assert(sz != 0);
+
+ n_reads += 1;
+ retval = pread(read_fd, work_buf, sz, lp->start);
+
+#if 0 /* enable this when testing */
+ if (!(random() & 0xf)) {
+ retval = -1;
+ errno = EIO;
+ usleep(20000);
+ } else {
+ usleep(2000);
+ }
+#endif
+
+ error = errno;
+ if (retval > 0) {
+ n_good_reads += 1;
+ sz = retval;
+ done_size += sz;
+ if (write_fd >= 0) {
+ write_buf(write_fd, work_buf, sz, lp->start);
+ }
+ if (log_file != NULL) {
+ fprintf(log_file, "%jd %ju %ju\n",
+ (intmax_t)t_now,
+ (uintmax_t)lp->start,
+ (uintmax_t)sz
+ );
+ fflush(log_file);
+ }
+ } else {
+ wasted_size += sz;
+ printf("%14ju %7ju read error %d: (%s)",
+ (uintmax_t)lp->start,
+ (uintmax_t)sz, error, strerror(error));
+ if (error_pause > 1) {
+ printf(" (Pausing %g s)", error_pause);
+ }
+ printf("\n");
+
+ if (write_fd >= 0 && pattern_buf != NULL) {
+ write_buf(write_fd, pattern_buf, sz, lp->start);
+ }
+ new_lump(lp->start, sz, lp->pass + 1);
+ retval = -sz;
+ }
+ lp->start += sz;
+ lp->len -= sz;
+ if (lp->len == 0) {
+ TAILQ_REMOVE(&lumps, lp, list);
+ nlumps -= 1;
+ free(lp);
+ }
+ errno = error;
+ return (retval);
+}
+
+
+/**********************************************************************/
+
+static void
+determine_total_size(void)
+{
+ struct stat sb;
+ int error;
+
+ if (total_size != 0)
+ return;
+
+ error = fstat(read_fd, &sb);
+ if (error < 0)
+ err(1, "fstat failed");
+
+ if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) {
+#ifdef DIOCGMEDIASIZE
+ off_t mediasize;
+ error = ioctl(read_fd, DIOCGMEDIASIZE, &mediasize);
+ if (error == 0 && mediasize > 0) {
+ total_size = mediasize;
+ printf("# Got total_size from DIOCGMEDIASIZE: %ju\n",
+ (uintmax_t)total_size);
+ return;
+ }
+#endif
+ } else if (S_ISREG(sb.st_mode) && sb.st_size > 0) {
+ total_size = sb.st_size;
+ printf("# Got total_size from stat(2): %ju\n",
+ (uintmax_t)total_size);
+ return;
+ } else {
+ errx(1, "Input must be device or regular file");
+ }
+ fprintf(stderr, "Specify total size with -t option\n");
+ exit(1);
+}
+
+static void
+determine_read_sizes(void)
+{
+ int error;
+ u_int sectorsize;
+ off_t stripesize;
+
+#ifdef DIOCGSECTORSIZE
+ if (small_read == 0) {
+ error = ioctl(read_fd, DIOCGSECTORSIZE, &sectorsize);
+ if (error >= 0 && sectorsize > 0) {
+ small_read = sectorsize;
+ printf("# Got small_read from DIOCGSECTORSIZE: %ju\n",
+ (uintmax_t)small_read
+ );
+ }
+ }
+#endif
+
+ if (small_read == 0) {
+ small_read = 512;
+ printf("# Defaulting small_read to %ju\n", (uintmax_t)small_read);
+ }
+
+ if (medium_read && (medium_read % small_read)) {
+ errx(1,
+ "medium_read (%ju) is not a multiple of small_read (%ju)\n",
+ (uintmax_t)medium_read, (uintmax_t)small_read
+ );
+ }
+
+ if (big_read != 0 && (big_read % small_read)) {
+ errx(1,
+ "big_read (%ju) is not a multiple of small_read (%ju)\n",
+ (uintmax_t)big_read, (uintmax_t)small_read
+ );
+ }
+
+#ifdef DIOCGSTRIPESIZE
+ if (medium_read == 0) {
+ error = ioctl(read_fd, DIOCGSTRIPESIZE, &stripesize);
+ if (error < 0 || stripesize <= 0) {
+ // nope
+ } else if ((uint64_t)stripesize < small_read) {
+ // nope
+ } else if (stripesize % small_read) {
+ // nope
+ } else if (stripesize <= COMPROMISE_SIZE) {
+ medium_read = stripesize;
+ printf("# Got medium_read from DIOCGSTRIPESIZE: %ju\n",
+ (uintmax_t)medium_read
+ );
+ }
+ }
+#endif
+
+#if defined(DIOCGFWSECTORS) && defined(DIOCGFWHEADS)
+ if (medium_read == 0) {
+ u_int fwsectors = 0, fwheads = 0;
+ error = ioctl(read_fd, DIOCGFWSECTORS, &fwsectors);
+ if (error)
+ fwsectors = 0;
+ error = ioctl(read_fd, DIOCGFWHEADS, &fwheads);
+ if (error)
+ fwheads = 0;
+ if (fwsectors * fwheads * small_read <= COMPROMISE_SIZE) {
+ medium_read = fwsectors * fwheads * small_read;
+ printf(
+ "# Got medium_read from DIOCGFW{SECTORS*HEADS}: %ju\n",
+ (uintmax_t)medium_read
+ );
+ } else if (fwsectors * small_read <= COMPROMISE_SIZE) {
+ medium_read = fwsectors * small_read;
+ printf(
+ "# Got medium_read from DIOCGFWSECTORS: %ju\n",
+ (uintmax_t)medium_read
+ );
+ }
+ }
+#endif
+
+ if (big_read == 0 && medium_read != 0) {
+ if (medium_read * 2 > COMPROMISE_SIZE) {
+ big_read = medium_read;
+ medium_read = 0;
+ } else {
+ big_read = COMPROMISE_SIZE;
+ big_read -= big_read % medium_read;
+ }
+ printf("# Got big_read from medium_read: %ju\n",
+ (uintmax_t)big_read
+ );
+ }
+
+ if (big_read == 0) {
+ big_read = COMPROMISE_SIZE;
+ big_read -= big_read % small_read;
+ printf("# Defaulting big_read to %ju\n",
+ (uintmax_t)big_read
+ );
+ }
+
+ if (medium_read >= big_read)
+ medium_read = 0;
+
+ if (medium_read == 0) {
+ /*
+ * We do not want to go directly to single sectors, but
+ * we also dont want to waste time doing multi-sector
+ * reads with high failure probability.
+ */
+ uint64_t h = big_read;
+ uint64_t l = small_read;
+ while (h > l) {
+ h >>= 2;
+ l <<= 1;
+ }
+ medium_read = h;
+ printf("# Got medium_read from small_read & big_read: %ju\n",
+ (uintmax_t)medium_read
+ );
+ }
+ printf("# Bigsize = %ju, medium_read = %ju, small_read = %ju\n",
+ (uintmax_t)big_read, (uintmax_t)medium_read, (uintmax_t)small_read);
+
+ assert(0 < small_read);
+
+ assert(0 < medium_read);
+ assert(medium_read >= small_read);
+ assert(medium_read <= big_read);
+ assert(medium_read % small_read == 0);
+
+ assert(0 < big_read);
+ assert(big_read >= medium_read);
+ assert(big_read % small_read == 0);
+}
+
+/**********************************************************************/
+
+static void
+monitor_read_sizes(uint64_t failed_size)
+{
+
+ if (failed_size == big_read && medium_read != small_read) {
+ if (n_reads < n_good_reads + 3)
+ return;
+ fprintf(
+ stderr,
+ "Too many failures for big reads."
+ " (%.0f bad of %.0f)"
+ " Shifting to medium_reads.\n",
+ n_reads - n_good_reads, n_reads
+ );
+ big_read = medium_read;
+ medium_read = small_read;
+ wasted_size = 0;
+ return;
+ }
+
+ if (big_read > small_read && wasted_size / small_read > 200) {
+ fprintf(
+ stderr,
+ "Too much wasted effort."
+ " (%.0f bad of %.0f)"
+ " Shifting to small_reads.\n",
+ n_reads - n_good_reads, n_reads
+ );
+ big_read = small_read;
+ medium_read = small_read;
+ return;
+ }
+}
+
+/**********************************************************************/
+
int
main(int argc, char * const argv[])
{
int ch;
- size_t sz, j;
+ int64_t sz;
int error;
- char *buf;
- u_int sectorsize;
- off_t stripesize;
- time_t t1, t2;
- struct stat sb;
- u_int n, snapshot = 60;
- static struct lump *lp;
+ time_t t_now, t_report, t_save;
+ time_t snapshot = 60, unsaved;
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
- while ((ch = getopt(argc, argv, "b:r:w:s:u:v")) != -1) {
+ while ((ch = getopt(argc, argv, "b:i:l:p:m:r:w:s:t:u:v")) != -1) {
switch (ch) {
case 'b':
- bigsize = strtoul(optarg, NULL, 0);
+ big_read = strtoul(optarg, NULL, 0);
+ break;
+ case 'i':
+ interval = strtod(optarg, NULL);
+ break;
+ case 'l':
+ log_file = fopen(optarg, "a");
+ if (log_file == NULL) {
+ err(1, "Could not open logfile for append");
+ }
+ break;
+ case 'p':
+ error_pause = strtod(optarg, NULL);
+ break;
+ case 'm':
+ medium_read = strtoul(optarg, NULL, 0);
break;
case 'r':
- rworklist = strdup(optarg);
- if (rworklist == NULL)
+ read_worklist_file = strdup(optarg);
+ if (read_worklist_file == NULL)
err(1, "Cannot allocate enough memory");
break;
case 's':
- snapshot = strtoul(optarg, NULL, 0);
+ small_read = strtoul(optarg, NULL, 0);
+ break;
+ case 't':
+ total_size = strtoul(optarg, NULL, 0);
break;
case 'u':
unreadable_pattern = optarg;
@@ -453,8 +783,8 @@ main(int argc, char * const argv[])
set_verbose();
break;
case 'w':
- wworklist = strdup(optarg);
- if (wworklist == NULL)
+ write_worklist_file = strdup(optarg);
+ if (write_worklist_file == NULL)
err(1, "Cannot allocate enough memory");
break;
default:
@@ -469,149 +799,111 @@ main(int argc, char * const argv[])
usage();
input = argv[0];
- fdr = open(argv[0], O_RDONLY);
- if (fdr < 0)
+ read_fd = open(argv[0], O_RDONLY);
+ if (read_fd < 0)
err(1, "Cannot open read descriptor %s", argv[0]);
- error = fstat(fdr, &sb);
- if (error < 0)
- err(1, "fstat failed");
- if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) {
- error = ioctl(fdr, DIOCGSECTORSIZE, &sectorsize);
- if (error < 0)
- err(1, "DIOCGSECTORSIZE failed");
+ determine_total_size();
- error = ioctl(fdr, DIOCGSTRIPESIZE, &stripesize);
- if (error == 0 && stripesize < sectorsize)
- sectorsize = stripesize;
+ determine_read_sizes();
- minsize = sectorsize;
- bigsize = rounddown(bigsize, sectorsize);
+ work_buf = malloc(big_read);
+ assert (work_buf != NULL);
- error = ioctl(fdr, DIOCGMEDIASIZE, &tot_size);
- if (error < 0)
- err(1, "DIOCGMEDIASIZE failed");
+ if (argc > 1) {
+ write_fd = open(argv[1], O_WRONLY | O_CREAT, DEFFILEMODE);
+ if (write_fd < 0)
+ err(1, "Cannot open write descriptor %s", argv[1]);
+ if (ftruncate(write_fd, (off_t)total_size) < 0)
+ err(1, "Cannot truncate output %s to %ju bytes",
+ argv[1], (uintmax_t)total_size);
} else {
- tot_size = sb.st_size;
+ write_fd = -1;
}
- if (bigsize < minsize)
- bigsize = minsize;
-
- for (ch = 0; (bigsize >> ch) > minsize; ch++)
- continue;
- medsize = bigsize >> (ch / 2);
- medsize = rounddown(medsize, minsize);
-
- fprintf(stderr, "Bigsize = %zu, medsize = %zu, minsize = %zu\n",
- bigsize, medsize, minsize);
-
- buf = malloc(bigsize);
- if (buf == NULL)
- err(1, "Cannot allocate %zu bytes buffer", bigsize);
+ if (strlen(unreadable_pattern)) {
+ pattern_buf = malloc(big_read);
+ assert(pattern_buf != NULL);
+ fill_buf(pattern_buf, big_read, unreadable_pattern);
+ }
- if (argc > 1) {
- fdw = open(argv[1], O_WRONLY | O_CREAT, DEFFILEMODE);
- if (fdw < 0)
- err(1, "Cannot open write descriptor %s", argv[1]);
- if (ftruncate(fdw, tot_size) < 0)
- err(1, "Cannot truncate output %s to %jd bytes",
- argv[1], (intmax_t)tot_size);
- } else
- fdw = -1;
-
- if (rworklist != NULL) {
- done_size = read_worklist(tot_size);
+ if (read_worklist_file != NULL) {
+ done_size = total_size - read_worklist();
} else {
- new_lump(0, tot_size, 0);
+ new_lump(0UL, total_size, 0UL);
done_size = 0;
}
- if (wworklist != NULL)
+ if (write_worklist_file != NULL)
signal(SIGINT, sighandler);
- t1 = time(NULL);
sz = 0;
if (!verbose)
- report_header(0);
+ report_header("\n");
else
printf("\x1b[2J");
- n = 0;
- for (;;) {
- lp = TAILQ_FIRST(&lumps);
- if (lp == NULL)
- break;
- while (lp->len > 0) {
- if (lp->state == 0)
- sz = MIN(lp->len, (off_t)bigsize);
- else if (lp->state == 1)
- sz = MIN(lp->len, (off_t)medsize);
- else
- sz = MIN(lp->len, (off_t)minsize);
- assert(sz != 0);
-
- t2 = time(NULL);
- if (t1 != t2 || lp->len < (off_t)bigsize) {
- t1 = t2;
- if (++n == snapshot) {
- save_worklist();
- n = 0;
- }
- report(lp, sz);
- }
+ t_first = time(NULL);
+ t_report = t_first;
+ t_save = t_first;
+ unsaved = 0;
+ while (!aborting) {
+ if (interval > 0) {
+ usleep((unsigned long)(1e6 * interval));
+ }
+ t_now = time(NULL);
+ sz = attempt_one_lump(t_now);
+ error = errno;
- j = pread(fdr, buf, sz, lp->start);
-#if 0
-if (!(random() & 0xf)) {
- j = -1;
- errno = EIO;
-}
-#endif
- if (j == sz) {
- done_size += sz;
- if (fdw >= 0)
- write_buf(fdw, buf, sz, lp->start);
- lp->start += sz;
- lp->len -= sz;
- if (verbose && lp->state > 2)
- report_good_read(t2, sz);
- continue;
- }
- error = errno;
-
- printf("%jd %zu %d read error (%s)\n",
- lp->start, sz, lp->state, strerror(error));
- if (verbose)
- report(lp, sz);
- if (fdw >= 0 && strlen(unreadable_pattern)) {
- fill_buf(buf, sz, unreadable_pattern);
- write_buf(fdw, buf, sz, lp->start);
+ if (sz == 0) {
+ break;
+ }
+
+ if (sz > 0) {
+ unsaved += 1;
+ }
+ if (unsaved && (t_save + snapshot) < t_now) {
+ save_worklist();
+ unsaved = 0;
+ t_save = t_now;
+ if (!verbose) {
+ report_header("\n");
+ t_report = t_now;
}
- new_lump(lp->start, sz, lp->state + 1);
- lp->start += sz;
- lp->len -= sz;
- if (error == EINVAL) {
- printf("Try with -b 131072 or lower ?\n");
- aborting = 1;
- break;
+ }
+ if (sz > 0) {
+ if (verbose) {
+ account_good_read(t_now, sz);
}
- if (error == ENXIO) {
- printf("Input device probably detached...\n");
- aborting = 1;
- break;
+ if (t_report != t_now) {
+ report(sz);
+ t_report = t_now;
}
+ continue;
}
- if (aborting)
- save_worklist();
- if (aborting || !TAILQ_NEXT(lp, list))
- report(lp, sz);
- if (aborting)
+
+ monitor_read_sizes(-sz);
+
+ if (error == EINVAL) {
+ printf("Try with -b 131072 or lower ?\n");
+ aborting = 1;
break;
- assert(lp->len == 0);
- TAILQ_REMOVE(&lumps, lp, list);
- free(lp);
+ }
+ if (error == ENXIO) {
+ printf("Input device probably detached...\n");
+ aborting = 1;
+ break;
+ }
+ report(-sz);
+ t_report = t_now;
+ if (error_pause > 0) {
+ usleep((unsigned long)(1e6 * error_pause));
+ }
}
+ save_worklist();
+ free(work_buf);
+ if (pattern_buf != NULL)
+ free(pattern_buf);
printf("%s", aborting ? "Aborted\n" : "Completed\n");
- free(buf);
- return (0);
+ report(0UL);
+ return (0); // XXX
}
diff --git a/sbin/route/route_netlink.c b/sbin/route/route_netlink.c
index 631c2860b547..ba22a2ec1e22 100644
--- a/sbin/route/route_netlink.c
+++ b/sbin/route/route_netlink.c
@@ -738,6 +738,7 @@ print_nlmsg(struct nl_helper *h, struct nlmsghdr *hdr, struct snl_msg_info *cinf
print_nlmsg_generic(h, hdr, cinfo);
}
+ fflush(stdout);
snl_clear_lb(&h->ss_cmd);
}
diff --git a/sbin/routed/Makefile b/sbin/routed/Makefile
index 643f790049ac..b88bf17efffc 100644
--- a/sbin/routed/Makefile
+++ b/sbin/routed/Makefile
@@ -1,6 +1,6 @@
# Make `routed` for FreeBSD
-PACKAGE=runtime
+PACKAGE=rip
PROG= routed
MAN= routed.8
SRCS= if.c input.c main.c output.c parms.c radix.c rdisc.c table.c trace.c
diff --git a/sbin/routed/Makefile.inc b/sbin/routed/Makefile.inc
index 01b5f23410c8..e09337e43724 100644
--- a/sbin/routed/Makefile.inc
+++ b/sbin/routed/Makefile.inc
@@ -1 +1,3 @@
+PACKAGE= rip
+
.include "../Makefile.inc"
diff --git a/sbin/savecore/savecore.8 b/sbin/savecore/savecore.8
index 53d2360719dd..1fb79c51f98d 100644
--- a/sbin/savecore/savecore.8
+++ b/sbin/savecore/savecore.8
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd April 4, 2022
+.Dd July 16, 2025
.Dt SAVECORE 8
.Os
.Sh NAME
@@ -69,7 +69,7 @@ Generate output via
.Xr libxo 3
in a selection of different human and machine readable formats.
See
-.Xr xo_parse_args 3
+.Xr xo_options 7
for details on command line arguments.
.It Fl C
Check to see if a dump exists,
@@ -193,7 +193,7 @@ is meant to be called near the end of the initialization file
.Xr zstd 1 ,
.Xr getbootfile 3 ,
.Xr libxo 3 ,
-.Xr xo_parse_args 3 ,
+.Xr xo_options 7 ,
.Xr mem 4 ,
.Xr textdump 4 ,
.Xr tar 5 ,
diff --git a/sbin/swapon/tests/swapon_test.sh b/sbin/swapon/tests/swapon_test.sh
index b6d31ecaeed0..a04bb36cc49e 100755
--- a/sbin/swapon/tests/swapon_test.sh
+++ b/sbin/swapon/tests/swapon_test.sh
@@ -31,7 +31,10 @@ attach_mdX_head()
attach_mdX_body()
{
# if the swapfile is too small (like 1k) then mdconfig hangs looking up the md
- atf_check -s exit:0 -x "truncate -s 10k swapfile"
+ # but need a swapfile bigger than one page kernel page size
+ pagesize=$(sysctl -n hw.pagesize)
+ minsize=$(( pagesize * 2 ))
+ atf_check -s exit:0 -x "truncate -s $minsize swapfile"
atf_check -s exit:0 -o save:fstab.out -x "echo 'md31 none swap sw,file=swapfile 0 0'"
atf_check -s exit:0 -o match:"swapon: adding /dev/md31 as swap device" -x "swapon -F fstab.out -a"
}
@@ -49,7 +52,10 @@ attach_dev_mdX_head()
attach_dev_mdX_body()
{
# if the swapfile is too small (like 1k) then mdconfig hangs looking up the md
- atf_check -s exit:0 -x "truncate -s 10k swapfile"
+ # but need a swapfile bigger than one page kernel page size
+ pagesize=$(sysctl -n hw.pagesize)
+ minsize=$(( pagesize * 2 ))
+ atf_check -s exit:0 -x "truncate -s $minsize swapfile"
atf_check -s exit:0 -o save:fstab.out -x "echo '/dev/md32 none swap sw,file=swapfile 0 0'"
atf_check -s exit:0 -o match:"swapon: adding /dev/md32 as swap device" -x "swapon -F fstab.out -a"
}
@@ -67,7 +73,10 @@ attach_md_head()
attach_md_body()
{
# if the swapfile is too small (like 1k) then mdconfig hangs looking up the md
- atf_check -s exit:0 -x "truncate -s 10k swapfile"
+ # but need a swapfile bigger than one page kernel page size
+ pagesize=$(sysctl -n hw.pagesize)
+ minsize=$(( pagesize * 2 ))
+ atf_check -s exit:0 -x "truncate -s $minsize swapfile"
atf_check -s exit:0 -o save:fstab.out -x "echo 'md none swap sw,file=swapfile 0 0'"
atf_check -s exit:0 -o match:"swapon: adding /dev/md[0-9][0-9]* as swap device" -x "swapon -F fstab.out -a"
}
@@ -85,7 +94,10 @@ attach_dev_md_head()
attach_dev_md_body()
{
# if the swapfile is too small (like 1k) then mdconfig hangs looking up the md
- atf_check -s exit:0 -x "truncate -s 10k swapfile"
+ # but need a swapfile bigger than one page kernel page size
+ pagesize=$(sysctl -n hw.pagesize)
+ minsize=$(( pagesize * 2 ))
+ atf_check -s exit:0 -x "truncate -s $minsize swapfile"
atf_check -s exit:0 -o save:fstab.out -x "echo '/dev/md none swap sw,file=swapfile 0 0'"
atf_check -s exit:0 -o match:"swapon: adding /dev/md[0-9][0-9]* as swap device" -x "swapon -F fstab.out -a"
}
@@ -103,7 +115,10 @@ attach_mdX_eli_head()
attach_mdX_eli_body()
{
# if the swapfile is too small (like 1k) then mdconfig hangs looking up the md
- atf_check -s exit:0 -x "truncate -s 10k swapfile"
+ # but need a swapfile bigger than one page kernel page size
+ pagesize=$(sysctl -n hw.pagesize)
+ minsize=$(( pagesize * 2 ))
+ atf_check -s exit:0 -x "truncate -s $minsize swapfile"
atf_check -s exit:0 -o save:fstab.out -x "echo 'md33.eli none swap sw,file=swapfile 0 0'"
atf_check -s exit:0 -o match:"swapon: adding /dev/md33.eli as swap device" -x "swapon -F fstab.out -a"
}
@@ -121,7 +136,10 @@ attach_dev_mdX_eli_head()
attach_dev_mdX_eli_body()
{
# if the swapfile is too small (like 1k) then mdconfig hangs looking up the md
- atf_check -s exit:0 -x "truncate -s 10k swapfile"
+ # but need a swapfile bigger than one page kernel page size
+ pagesize=$(sysctl -n hw.pagesize)
+ minsize=$(( pagesize * 2 ))
+ atf_check -s exit:0 -x "truncate -s $minsize swapfile"
atf_check -s exit:0 -o save:fstab.out -x "echo '/dev/md34.eli none swap sw,file=swapfile 0 0'"
atf_check -s exit:0 -o match:"swapon: adding /dev/md34.eli as swap device" -x "swapon -F fstab.out -a"
}
@@ -139,7 +157,10 @@ attach_md_eli_head()
attach_md_eli_body()
{
# if the swapfile is too small (like 1k) then mdconfig hangs looking up the md
- atf_check -s exit:0 -x "truncate -s 10k swapfile"
+ # but need a swapfile bigger than one page kernel page size
+ pagesize=$(sysctl -n hw.pagesize)
+ minsize=$(( pagesize * 2 ))
+ atf_check -s exit:0 -x "truncate -s $minsize swapfile"
atf_check -s exit:0 -o save:fstab.out -x "echo 'md.eli none swap sw,file=swapfile 0 0'"
atf_check -s exit:0 -o match:"swapon: adding /dev/md[0-9][0-9]*.eli as swap device" -x "swapon -F fstab.out -a"
}
@@ -157,7 +178,10 @@ attach_dev_md_eli_head()
attach_dev_md_eli_body()
{
# if the swapfile is too small (like 1k) then mdconfig hangs looking up the md
- atf_check -s exit:0 -x "truncate -s 10k swapfile"
+ # but need a swapfile bigger than one page kernel page size
+ pagesize=$(sysctl -n hw.pagesize)
+ minsize=$(( pagesize * 2 ))
+ atf_check -s exit:0 -x "truncate -s $minsize swapfile"
atf_check -s exit:0 -o save:fstab.out -x "echo '/dev/md.eli none swap sw,file=swapfile 0 0'"
atf_check -s exit:0 -o match:"swapon: adding /dev/md[0-9][0-9]*.eli as swap device" -x "swapon -F fstab.out -a"
}
@@ -167,6 +191,24 @@ attach_dev_md_eli_cleanup()
}
###
+
+atf_test_case attach_too_small
+attach_too_small_head()
+{
+ atf_set "descr" "should refuse to attach if smaller than one kernel page size"
+}
+attach_too_small_body()
+{
+ # Need to use smaller than kernel page size
+ pagesize=$(sysctl -n hw.pagesize)
+ minsize=$(( pagesize / 2 ))
+ atf_check -s exit:0 -x "truncate -s $minsize swapfile"
+ atf_check -s exit:0 -o save:fstab.out -x "echo 'md35 none swap sw,file=swapfile 0 0'"
+ atf_check -s exit:1 -e match:"swapon: /dev/md35: NSWAPDEV limit reached" -x "swapon -F fstab.out -a"
+ atf_check -s exit:0 -x "mdconfig -d -u 35"
+}
+
+###
atf_init_test_cases()
{
atf_add_test_case attach_mdX
@@ -178,4 +220,6 @@ atf_init_test_cases()
atf_add_test_case attach_dev_mdX_eli
atf_add_test_case attach_md_eli
atf_add_test_case attach_dev_md_eli
+
+ atf_add_test_case attach_too_small
}
diff --git a/sbin/veriexec/veriexec.8 b/sbin/veriexec/veriexec.8
index 8e99f1d61faf..8352dd8e5e49 100644
--- a/sbin/veriexec/veriexec.8
+++ b/sbin/veriexec/veriexec.8
@@ -195,7 +195,7 @@ and be strict about enforcing certificate validity:
.Ed
.Nm
-will look for a detatched signature that it recognizes, such as
+will look for a detached signature that it recognizes, such as
.Pa manifest.asc
(OpenPGP) or
.Pa manifest.*sig
diff --git a/sbin/zfsbootcfg/zfsbootcfg.8 b/sbin/zfsbootcfg/zfsbootcfg.8
index 5e7f02b2578c..3831adfc81bd 100644
--- a/sbin/zfsbootcfg/zfsbootcfg.8
+++ b/sbin/zfsbootcfg/zfsbootcfg.8
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd July 22, 2020
+.Dd July 28, 2025
.Dt ZFSBOOTCFG 8
.Os
.Sh NAME
@@ -44,14 +44,11 @@
is used to set
.Xr boot.config 5 Ns -style
options to be used by
-.Xr zfsboot 8 ,
.Xr gptzfsboot 8
or
.Xr loader 8
the next time the machine is booted.
Once
-.Xr zfsboot 8
-or
.Xr gptzfsboot 8
or
.Xr loader 8
@@ -130,8 +127,7 @@ To clear the boot options:
.Xr boot.config 5 ,
.Xr bectl 8 ,
.Xr gptzfsboot 8 ,
-.Xr loader 8 ,
-.Xr zfsboot 8
+.Xr loader 8
.Sh HISTORY
.Nm
appeared in