diff options
Diffstat (limited to 'sbin')
261 files changed, 6223 insertions, 3655 deletions
diff --git a/sbin/bectl/bectl.8 b/sbin/bectl/bectl.8 index 3014576cad4c..0e08b3383e9a 100644 --- a/sbin/bectl/bectl.8 +++ b/sbin/bectl/bectl.8 @@ -1,24 +1,14 @@ .\" -.\" SPDX-License-Identifier: BSD-2-Clause -.\" .\" Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in> .\" -.\" 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. -.\" +.\" SPDX-License-Identifier: BSD-2-Clause .\" -.Dd April 9, 2024 +.Dd June 13, 2025 .Dt BECTL 8 .Os .Sh NAME .Nm bectl -.Nd Utility to manage boot environments on ZFS +.Nd manage ZFS boot environments .Sh SYNOPSIS .Nm .Op Fl h @@ -90,34 +80,31 @@ .Sh DESCRIPTION The .Nm -command is used to setup and interact with ZFS boot environments, which are -bootable clones of datasets. -.Pp -A boot environment allows the system to be upgraded, while preserving the -pre-upgrade system environment. -.Pp -.Nm -itself accepts an -.Fl r -flag specified before the command to indicate the -.Ar beroot -that should be used as the boot environment root, or the dataset whose children -are all boot environments. -Normally this information is derived from the bootfs property of the pool that -is mounted at -.Pa / , -but it is useful when the system has not been booted into a ZFS root or a -different pool should be operated on. -For instance, booting into the recovery media and manually importing a pool from -one of the system's resident disks will require the -.Fl r -flag to work. +utility manages bootable ZFS clones called boot environments. +Boot envionments allow system changes to be tested safely, +as they are selectable directly from the boot +.Xr loader 8 . +This utility can +.Cm create , +.Cm list , +.Cm mount , +or +.Cm jail +boot environments. +Once the changes have been tested, the boot environment can be +.Cm unmount Ns ed , +.Cm activate Ns d , +.Cm rename Ns d , +and +.Cm destroy Ns ed . .Ss Supported Subcommands and Flags -.Bl -tag -width activate -.It Xo -.Fl h -.Xc +.Bl -tag -width indent +.It Fl h Print usage information and exit. +.It Fl r Ar beroot Sy Ar subcommand +Specify a parent dataset for the boot environment to use for +.Ar subcommand +for operation on manually imported pools or unusual layouts. .It Xo .Cm activate .Op Fl t | Fl T @@ -132,19 +119,19 @@ flag is given, this takes effect only for the next boot. Flag .Fl T removes temporary boot once configuration. -Without temporary configuration, the next boot will use zfs dataset specified -in boot pool +Without temporary configuration, +the next boot will use zfs dataset specified in boot pool .Ar bootfs property. .It Xo .Cm check .Xc -Performs a silent sanity check on the current system. +Perform a check to see if the current system can use boot environments. If boot environments are supported and used, .Nm will exit with a status code of 0. -Any other status code is not currently defined and may, in the future, grow -special meaning for different degrees of sanity check failures. +Any other status code is not currently defined and may, in the future, +grow special meaning for different degrees of sanity check failures. .It Xo .Cm create .Op Fl r @@ -172,8 +159,8 @@ environment. .Pp If .Nm -is creating from another boot environment, a snapshot of that boot environment -will be created to clone from. +is creating from another boot environment, +a snapshot of that boot environment will be created to clone from. .It Xo .Cm create .Op Fl r @@ -184,8 +171,10 @@ Create a snapshot of the boot environment named .Pp If the .Fl r -flag is given, a recursive snapshot of the boot environment will be created. -A snapshot is created for each descendant dataset of the boot environment. +flag is given, +a recursive snapshot of the boot environment will be created. +A snapshot is created for each descendant dataset +of the boot environment. See .Sx Boot Environment Structures for a discussion on different layouts. @@ -251,8 +240,8 @@ If .Ar utility is specified, it will be executed instead of .Pa /bin/sh . -The jail will be destroyed and the boot environment unmounted when the command -finishes executing, unless the +The jail will be destroyed and the boot environment unmounted +when the command finishes executing, unless the .Fl U argument is specified. .Pp @@ -279,11 +268,11 @@ The following default parameters are provided: .It Va allow.mount Ta Cm true .It Va allow.mount.devfs Ta Cm true .It Va enforce_statfs Ta Cm 1 -.It Va name Ta Set to jail ID. +.It Va name Ta set to jail ID .It Va host.hostname Ta Va bootenv -.It Va path Ta Set to a path in Pa /tmp +.It Va path Ta set to a path in Pa /tmp generated by -.Xr libbe 3 . +.Xr libbe 3 .El .Pp All default parameters may be overwritten. @@ -308,8 +297,8 @@ or combination of .It Fl a Display all datasets. .It Fl D -Display the full space usage for each boot environment, assuming all -other boot environments were destroyed. +Display the full space usage for each boot environment, +assuming all other boot environments were destroyed. .It Fl H Used for scripting. Do not print headers and separate fields by a single tab instead of @@ -361,8 +350,8 @@ will make a directory such as .Pa be_mount.c6Sf in .Pa /tmp . -Randomness in the last four characters of the directory name will prevent -mount point conflicts. +Randomness in the last four characters of the directory name +will prevent mount point conflicts. Unmount of an environment, followed by mount of the same environment without giving a .Ar mountpoint , @@ -372,7 +361,7 @@ Rename the given .Ar origBeName to the given .Ar newBeName . -The boot environment will not be unmounted in order for this rename to occur. +The boot environment will not be unmounted for this rename to occur. .It Cm ujail Brq Ar jailId | jailName | beName .It Cm unjail Brq Ar jailId | jailName | beName Destroy the jail created from the given boot environment. @@ -400,8 +389,8 @@ boot environment layout, as created by the Auto ZFS option to .Xr bsdinstall 8 , is a .Dq shallow -boot environment structure, where boot environment datasets do not have any -directly subordinate datasets. +boot environment structure, where boot environment datasets +do not have any directly subordinate datasets. Instead, they're organized off in .Pa zroot/ROOT , and they rely on datasets elsewhere in the pool having @@ -429,7 +418,8 @@ set to .Dv off , thus files in .Pa /usr -typically fall into the boot environment because this dataset is not mounted. +typically fall into the boot environment +because this dataset is not mounted. .Pa zroot/usr/src is mounted, thus files in .Pa /usr/src @@ -455,8 +445,8 @@ Note that the subordinate datasets now have .Dv canmount set to .Dv noauto . -These are more obviously a part of the boot environment, as indicated by their -positioning in the layout. +These are more obviously a part of the boot environment, +as indicated by their positioning in the layout. These subordinate datasets will be mounted by the .Dv zfsbe .Xr rc 8 @@ -478,16 +468,25 @@ A future version of may default to handling both styles and deprecate the various .Fl r flags. -.\" .Sh EXAMPLES -.\" .Bl -bullet -.\" .It +.Sh EXAMPLES +Create a boot environment, named with today's date, +containing snapshots of the root dataset and of all child datasets: +.Pp +.Dl bectl create -r `date +%Y%m%d` +.Pp +Mount a previous boot environment, +.Ar yesterdaysbe , +to +.Pa /mnt : +.Pp +.Dl bectl mount yesterdaysbe /mnt .\" To fill in with jail upgrade example when behavior is firm. -.\" .El .Sh SEE ALSO .Xr libbe 3 , .Xr zfsprops 7 , .Xr beinstall.sh 8 , .Xr jail 8 , +.Xr loader 8 , .Xr zfs 8 , .Xr zpool 8 .Sh HISTORY diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index f4359750a8d7..95715b34336b 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -1,28 +1,7 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * +/* * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in> * - * 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * SPDX-License-Identifier: BSD-2-Clause */ #include <sys/param.h> @@ -179,13 +158,15 @@ bectl_cmd_activate(int argc, char *argv[]) /* activate logic goes here */ if ((err = be_activate(be, argv[0], temp)) != 0) /* XXX TODO: more specific error msg based on err */ - printf("Did not successfully activate boot environment %s\n", + printf("Did not successfully activate boot environment %s", argv[0]); else - printf("Successfully activated boot environment %s\n", argv[0]); + printf("Successfully activated boot environment %s", argv[0]); if (temp) - printf("for next boot\n"); + printf(" for next boot"); + + printf("\n"); return (err); } diff --git a/sbin/bectl/bectl.h b/sbin/bectl/bectl.h index b8b824b18010..355110bcd6d3 100644 --- a/sbin/bectl/bectl.h +++ b/sbin/bectl/bectl.h @@ -1,28 +1,7 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * +/* * Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org> * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * SPDX-License-Identifier: BSD-2-Clause */ int usage(bool explicit); diff --git a/sbin/bectl/bectl_jail.c b/sbin/bectl/bectl_jail.c index fa063962dada..050d46b627b4 100644 --- a/sbin/bectl/bectl_jail.c +++ b/sbin/bectl/bectl_jail.c @@ -1,28 +1,7 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * +/* * Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org> * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * SPDX-License-Identifier: BSD-2-Clause */ #include <sys/param.h> diff --git a/sbin/bectl/bectl_list.c b/sbin/bectl/bectl_list.c index c158aa9b1600..bd724b7bf012 100644 --- a/sbin/bectl/bectl_list.c +++ b/sbin/bectl/bectl_list.c @@ -1,28 +1,7 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * +/* * Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org> * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * SPDX-License-Identifier: BSD-2-Clause */ #include <sys/param.h> diff --git a/sbin/bectl/tests/bectl_test.sh b/sbin/bectl/tests/bectl_test.sh index 00c71e11de09..8084b0a173f4 100755 --- a/sbin/bectl/tests/bectl_test.sh +++ b/sbin/bectl/tests/bectl_test.sh @@ -1,28 +1,7 @@ # -# SPDX-License-Identifier: BSD-2-Clause -# # Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org> # -# 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. +# SPDX-License-Identifier: BSD-2-Clause # ZPOOL_NAME_FILE=zpool_name diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c index a2e65055fcaa..15a5d42a2ba5 100644 --- a/sbin/camcontrol/camcontrol.c +++ b/sbin/camcontrol/camcontrol.c @@ -5400,6 +5400,19 @@ cts_print(struct cam_device *device, struct ccb_trans_settings *cts) nvmf_transport_type(nvmf->trtype)); } } + if (cts->transport == XPORT_UFSHCI) { + struct ccb_trans_settings_ufshci *ufshci = + &cts->xport_specific.ufshci; + + if (ufshci->valid & CTS_UFSHCI_VALID_LINK) { + fprintf(stdout, "%sHigh Speed Gear: %d (%d max)\n", + pathstr, ufshci->hs_gear, ufshci->max_hs_gear); + fprintf(stdout, "%sUnipro TX lanes: %d (%d max)\n", pathstr, + ufshci->tx_lanes, ufshci->max_tx_lanes); + fprintf(stdout, "%sUnipro RX lanes: %d (%d max)\n", pathstr, + ufshci->rx_lanes, ufshci->max_rx_lanes); + } + } if (cts->protocol == PROTO_ATA) { struct ccb_trans_settings_ata *ata= &cts->proto_specific.ata; 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 4ff0187a5a22..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} @@ -46,6 +51,11 @@ HYPERV+= hyperv.conf HYPERVPACKAGE= hyperv-tools .endif +CONFGROUPS+= NVME +NVMEDIR= ${DEVDDIR} +NVME+= nvmf.conf +NVMEPACKAGE= nvme-tools + .if ${MK_USB} != "no" DEVD+= uath.conf ulpt.conf .endif diff --git a/sbin/devd/devd.8 b/sbin/devd/devd.8 index f7e19df438ed..f4e8e173399c 100644 --- a/sbin/devd/devd.8 +++ b/sbin/devd/devd.8 @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd October 19, 2021 +.Dd May 7, 2025 .Dt DEVD 8 .Os .Sh NAME @@ -154,6 +154,12 @@ to communicate with its clients. .It Pa /var/run/devd.pipe A deprecated socket retained for use with old clients. .El +.Sh EXAMPLES +Use +.Xr cat 1 +to monitor kernel events: +.Pp +.Dl cat /var/run/devd.seqpacket.pipe .Sh SEE ALSO .Xr devctl 4 , .Xr devd.conf 5 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 5027ce78eae0..8df3e910e076 100644 --- a/sbin/devd/devd.conf.5 +++ b/sbin/devd/devd.conf.5 @@ -38,7 +38,7 @@ .\" ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS .\" SOFTWARE. .\" -.Dd December 20, 2024 +.Dd July 9, 2025 .Dt DEVD.CONF 5 .Os .Sh NAME @@ -322,7 +322,7 @@ mechanism. .\" for each of the tables so that things line up in columns nicely. .\" Please do not omit the type column for notifiers that omit it. .Pp -.Bl -column "System" "Subsystem" "1234567" -compact +.Bl -column "SYSTEM" "SUBSYSTEM" "12345678" -compact .Sy "System" Ta Sy "Subsystem" Ta Sy "Type" Ta Sy "Description" .It Li ACPI Ta Ta Ta Events related to the ACPI Subsystem. @@ -346,13 +346,13 @@ Suspend notification. Thermal zone events. .El .Pp -.Bl -column "System" "Subsystem" "1234567" -compact +.Bl -column "SYSTEM" "SUBSYSTEM" "12345678" -compact .Sy "System" Ta Sy "Subsystem" Ta Sy "Type" Ta Sy "Description" .It Li AEON Ta Li power Ta Li press Ta The power button on an Amiga has been pressed. .El .Pp -.Bl -column "System" "Subsystem" "1234567" -compact +.Bl -column "SYSTEM" "SUBSYSTEM" "12345678" -compact .Sy "System" Ta Sy "Subsystem" Ta Sy "Type" Ta Sy "Description" .It Li CAM Ta Ta Ta Events related to the @@ -366,24 +366,24 @@ Generic errors. Command timeouts. .El .Pp -.Bl -column "System" "Subsystem" "1234567" -compact +.Bl -column "SYSTEM" "SUBSYSTEM" "12345678" -compact .Sy "System" Ta Sy "Subsystem" Ta Sy "Type" Ta Sy "Description" .It Li CARP Ta Ta Ta Events related to the .Xr carp 4 protocol. -.It CARP Ta Ar vhid@inet Ta Ta +.It Li CARP Ta Ar vhid@inet Ta Ta The .Dq subsystem contains the actual CARP vhid and the name of the network interface on which the event took place. -.It CARP Ta Ar vhid@inet Ta MASTER Ta +.It Li CARP Ta Ar vhid@inet Ta Li MASTER Ta Node become the master for a virtual host. -.It CARP Ta Ar vhid@inet Ta BACKUP Ta +.It Li CARP Ta Ar vhid@inet Ta Li BACKUP Ta Node become the backup for a virtual host. .El .Pp -.Bl -column "System" "Subsystem" "1234567" -compact +.Bl -column "CORETEMP" "SUBSYSTEM" "TEMPERATURE" -compact .Sy "System" Ta Sy "Subsystem" Ta Sy "Type" Ta Sy "Description" .It Li coretemp Ta Ta Ta Events related to the @@ -395,7 +395,7 @@ Notification that the CPU core has reached critical temperature. String containing the temperature of the core that has become too hot. .El .Pp -.Bl -column "System" "Subsystem" "1234567" -compact +.Bl -column "SYSTEM" "SUBSYSTEM" "12345678" -compact .Sy "System" Ta Sy "Subsystem" Ta Sy "Type" Ta Sy "Description" .It Li DEVFS .It Li DEVFS Ta Li CDEV Ta Li CREATE Ta @@ -408,7 +408,7 @@ The node is destroyed. .El .Pp -.Bl -column "System" "Subsystem" "1234567" -compact +.Bl -column "SYSTEM" "SUBSYSTEM" "12345678" -compact .Sy "System" Ta Sy "Subsystem" Ta Sy "Type" Ta Sy "Description" .It Li ETHERNET Ta Ar inet Ta IFATTACH Ta Notification when the default VNET instance of the @@ -416,7 +416,7 @@ Notification when the default VNET instance of the interface is attached. .El .Pp -.Bl -column "System" "Subsystem" "1234567" -compact +.Bl -column "SYSTEM" "SUBSYSTEM" "GEOM::ROTATION_RATE" -compact .Sy "System" Ta Sy "Subsystem" Ta Sy "Type" Ta Sy "Description" .It Li GEOM Ta Ta Ta Events related to the @@ -447,7 +447,7 @@ A provider size has changed. .El .Pp -.Bl -column "System" "Subsystem" "1234567" -compact +.Bl -column "SYSTEM" "SUBSYSTEM" "LINK_DOWN" -compact .Sy "System" Ta Sy "Subsystem" Ta Sy "Type" Ta Sy "Description" .It Li IFNET .It Li IFNET Ta Em inet Ta Ta @@ -471,7 +471,7 @@ The network interface address added. The network interface address removed. .El .Pp -.Bl -column "System" "Subsystem" "1234567" -compact +.Bl -column "SYSTEM" "SUBSYSTEM" "12345678" -compact .Sy "System" Ta Sy "Subsystem" Ta Sy "Type" Ta Sy "Description" .It Li kernel Ta Li signal Ta Li coredump Ta Notification that a process has crashed and dumped core. @@ -479,21 +479,33 @@ Notification that a process has crashed and dumped core. Notification that the system has woken from the suspended state. .El .Pp -.Bl -column "System" "Subsystem" "1234567" -compact +.Bl -column "SYSTEM" "SUBSYSTEM" "SMART_ERROR" -compact .Sy "System" Ta Sy "Subsystem" Ta Sy "Type" Ta Sy "Description" +.It Li nvme Ta Li controller Ta Ta +Controller events provide the controller name +.Pq for example, Li nvme0 +in $name. .It Li nvme Ta Li controller Ta Li SMART_ERROR Ta A SMART Critical Warning State change has happened. $state has a hex bitmask of the bits that changed, as defined in the NVMe Standard for Critical Warning field of log page 2 .Dq SMART / Health Information Log : -.Bl -column "Bit Value" "Meaning" -compact -.Sy "Bit Value" Ta Sy "Meaning" -.It 0x1 Ta Spare capacity below threshold -.It 0x2 Ta Temperature outside acceptable range -.It 0x4 Ta Reliability of media degraded -.It 0x8 Ta Media placed into read-only mode -.It 0x10 Ta Volatime memory backup failure -.It 0x20 Ta Persistent memrory read-only or degraded +.Pp +.Bl -tag -width "Bit Value" -compact +.It Sy "Bit Value" +.Sy Meaning +.It 0x1 +Spare capacity below threshold +.It 0x2 +Temperature outside acceptable range +.It 0x4 +Reliability of media degraded +.It 0x8 +Media placed into read-only mode +.It 0x10 +Volatime memory backup failure +.It 0x20 +Persistent memory read-only or degraded .El .It Li nvme Ta Li controller Ta Li RESET Ta A controller reset event has happened. @@ -503,11 +515,13 @@ $event is one of and .Dq timed_out representing the start of a controller reset, the successful completion of a -controller reset, and a timeout while waiting for the controller to reset +controller reset, or a timeout while waiting for the controller to reset, respectively. +.It Li nvme Ta Li controller Ta Li RECONNECT Ta +An NVMe over Fabrics host has disconnected and is requesting a reconnect. .El .Pp -.Bl -column "System" "Subsystem" "1234567" -compact +.Bl -column "SYSTEM" "SUBSYSTEM" "SHUTDOWN-THRESHOLD" -compact .Sy "System" Ta Sy "Subsystem" Ta Sy "Type" Ta Sy "Description" .It Li PMU Ta Ta Ta Notification of events from various types of Power Management Units. @@ -520,25 +534,25 @@ Power has been applied to the AC power line. .It Li PMU Ta Li "AC" Ta Li unplugged Ta Power has been removed from the AC power line. .It Li PMU Ta Li Battery Ta Ta -.It Li PMU Ta Li Battery Ta absent Ta +.It Li PMU Ta Li Battery Ta Li absent Ta Battery is no longer absent. -.It Li PMU Ta Li Battery Ta charged Ta +.It Li PMU Ta Li Battery Ta Li charged Ta The battery has become charged. -.It Li PMU Ta Li Battery Ta charging Ta +.It Li PMU Ta Li Battery Ta Li charging Ta The battery has started charging. -.It Li PMU Ta Li Battery Ta disconnected Ta +.It Li PMU Ta Li Battery Ta Li disconnected Ta The battery has been disconnected. -.It Li PMU Ta Li Battery Ta high-temp Ta +.It Li PMU Ta Li Battery Ta Li high-temp Ta The battery reported a temperature over the limit. -.It Li PMU Ta Li Battery Ta low-temp Ta +.It Li PMU Ta Li Battery Ta Li low-temp Ta The battery reported a temperature under the limit. -.It Li PMU Ta Li Battery Ta plugged Ta +.It Li PMU Ta Li Battery Ta Li plugged Ta The battery has become plugged (eg connected). -.It Li PMU Ta Li Battery Ta shutdown-threshold Ta +.It Li PMU Ta Li Battery Ta Li shutdown-threshold Ta The power in the battery has fallen below the shutdown threshold. -.It Li PMU Ta Li Battery Ta warning-threshold Ta +.It Li PMU Ta Li Battery Ta Li warning-threshold Ta The power in the battery has fallen below the warn the user threshold. -.It Li PMU Ta Li Button Ta pressed Ta +.It Li PMU Ta Li Button Ta Li pressed Ta A button on a .Xr adb 4 or @@ -548,39 +562,39 @@ has been pressed. One of the keys on the .Xr adb 4 keyboard has been pressed. -.It Li PMU Ta Li keys Ta brightness Ta +.It Li PMU Ta Li keys Ta Li brightness Ta A brightness level change has been requested. Direction is in the $notify variable. -.It Li PMU Ta Li keys Ta mute Ta +.It Li PMU Ta Li keys Ta Li mute Ta The mute key -.It Li PMU Ta Li keys Ta volume Ta +.It Li PMU Ta Li keys Ta Li volume Ta A volume level change has been requested. Direction is in the $notify variable. -.It Li PMU Ta Li keys Ta eject Ta +.It Li PMU Ta Li keys Ta Li eject Ta An ejection has been requested. -.It Li PMU Ta Li lid Ta close Ta +.It Li PMU Ta Li lid Ta Li close Ta The .Xr pmc 4 device has detected the lid closing. -.It Li PMU Ta Li lid Ta open Ta +.It Li PMU Ta Li lid Ta Li open Ta The .Xr pmc 4 device has detected the lid openinging. -.It Li PMU Ta Li POWER Ta ACLINE Ta +.It Li PMU Ta Li POWER Ta Li ACLINE Ta The .Xr pmc 4 device has detected an AC line state ($notify=0x00 is offline, 0x01 is online). -.It Li PMU Ta Li USB Ta overvoltage Ta +.It Li PMU Ta Li USB Ta Li overvoltage Ta An over-voltage condition on the power lines for the USB power pins. -.It Li PMU Ta Li USB Ta plugged Ta +.It Li PMU Ta Li USB Ta Li plugged Ta A device has been plugged into a USB device. -.It Li PMU Ta Li USB Ta undervoltage Ta +.It Li PMU Ta Li USB Ta Li undervoltage Ta An under-voltage condition on the power lines for the USB power pins. -.It Li PMU Ta Li USB Ta unplugged Ta -A device has been unplugged into a USB device. +.It Li PMU Ta Li USB Ta Li unplugged Ta +A device has been unplugged from a USB device. .El .Pp -.Bl -column "System" "Subsystem" "1234567" -compact +.Bl -column "SYSTEM" "SUBSYSTEM" "12345678" -compact .Sy "System" Ta Sy "Subsystem" Ta Sy "Type" Ta Sy "Description" .It Li RCTL Ta Ta Ta Events related to the @@ -590,7 +604,7 @@ framework. A rule with action specified as "devctl" was triggered. .El .Pp -.Bl -column "System" "Subsystem" "1234567" -compact +.Bl -column "SYSTEM" "SUBSYSTEM" "12345678" -compact .Sy "System" Ta Sy "Subsystem" Ta Sy "Type" Ta Sy "Description" .It Li USB Ta Ta Ta Events related to the USB subsystem. @@ -604,7 +618,7 @@ USB interface is attached to a device. USB interface is detached from a device. .El .Pp -.Bl -column "System" "Subsystem" "1234567" -compact +.Bl -column "SYSTEM" "SUBSYSTEM" "12345678" -compact .Sy "System" Ta Sy "Subsystem" Ta Sy "Type" Ta Sy "Description" .It Li VFS Ta Ta Ta Events from the vfs system. @@ -618,18 +632,18 @@ Notification of a filesystem is remounted (whether or not the options actually c Notification of a filesystem being unmounted. .El .Pp -.Bl -column "System" "Subsystem" "1234567" -compact +.Bl -column "SYSTEM" "SUBSYSTEM" "12345678" -compact .Sy "System" Ta Sy "Subsystem" Ta Sy "Type" Ta Sy "Description" -.It Li VT Ta BELL Ta RING Ta +.It Li VT Ta Li BELL Ta Li RING Ta Notification that the console bell has rung. See .Xr vt 4 for details. .El .Pp -.Bl -column "System" "Subsystem" "1234567" -compact +.Bl -column "SYSTEM" "SUBSYSTEM" "12345678" -compact .Sy "System" Ta Sy "Subsystem" Ta Sy "Type" Ta Sy "Description" -.It Li ZFS Ta ZFS Ta Ta +.It Li ZFS Ta Li ZFS Ta Ta Events about the ZFS subsystem. See .Xr zfsd 8 @@ -638,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/nvmf.conf b/sbin/devd/nvmf.conf new file mode 100644 index 000000000000..eaf3ebe86cec --- /dev/null +++ b/sbin/devd/nvmf.conf @@ -0,0 +1,7 @@ +# Attempt to reconnect NVMeoF host devices when requested +notify 100 { + match "system" "nvme"; + match "subsystem" "controller"; + match "type" "RECONNECT"; + action "nvmecontrol reconnect $name"; +}; diff --git a/sbin/devd/snd.conf b/sbin/devd/snd.conf new file mode 100644 index 000000000000..cf9cd9e94191 --- /dev/null +++ b/sbin/devd/snd.conf @@ -0,0 +1,23 @@ +# Audio redirection +notify 0 { + match "system" "SND"; + match "subsystem" "CONN"; + match "type" "IN"; + match "cdev" "dsp[0-9]+"; + + # 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]+"; + + # FIXME: 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 93988d5ce7a4..5d2a7453578b 100644 --- a/sbin/dhclient/dhclient.c +++ b/sbin/dhclient/dhclient.c @@ -90,8 +90,8 @@ cap_channel_t *capsyslog; -time_t cur_time; -struct timespec time_now; +time_t cur_time; /* Seconds since epoch. */ +struct timespec time_now; /* CLOCK_MONOTONIC. */ static time_t default_lease_time = 43200; /* 12 hours... */ const char *path_dhclient_conf = _PATH_DHCLIENT_CONF; @@ -450,7 +450,7 @@ main(int argc, char *argv[]) tzset(); clock_gettime(CLOCK_MONOTONIC, &time_now); - cur_time = time_now.tv_sec; + cur_time = time(NULL); inaddr_broadcast.s_addr = INADDR_BROADCAST; inaddr_any.s_addr = INADDR_ANY; @@ -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) @@ -1030,13 +1030,13 @@ dhcpoffer(struct packet *packet) struct client_lease *lease, *lp; int i; struct timespec arp_timeout_needed; - struct timespec stop_selecting = { .tv_sec = 0, .tv_nsec = 0 }; - time_now.tv_sec = cur_time; - time_now.tv_nsec = 0; - + time_t stop_selecting; + struct timespec stop_time; const char *name = packet->options[DHO_DHCP_MESSAGE_TYPE].len ? "DHCPOFFER" : "BOOTREPLY"; + clock_gettime(CLOCK_MONOTONIC, &time_now); + /* If we're not receptive to an offer right now, or if the offer has an unrecognizable transaction id, then just drop it. */ if (ip->client->state != S_SELECTING || @@ -1095,7 +1095,7 @@ dhcpoffer(struct packet *packet) arp_timeout_needed = arp_timeout; /* Figure out when we're supposed to stop selecting. */ - stop_selecting.tv_sec = + stop_selecting = ip->client->first_sending + ip->client->config->select_interval; /* If this is the lease we asked for, put it at the head of the @@ -1116,7 +1116,7 @@ dhcpoffer(struct packet *packet) timespecadd(&time_now, &arp_timeout_needed, &interm_struct); if (ip->client->offered_leases && - timespeccmp(&interm_struct, &stop_selecting, >)) + interm_struct.tv_sec >= stop_selecting) arp_timeout_needed = zero_timespec; /* Put the lease at the end of the list. */ @@ -1131,27 +1131,21 @@ dhcpoffer(struct packet *packet) } } - /* If we're supposed to stop selecting before we've had time - to wait for the ARPREPLY, add some delay to wait for - the ARPREPLY. */ - struct timespec time_left; - timespecsub(&stop_selecting, &time_now, &time_left); - + /* + * Wait until stop_selecting seconds past the epoch, or until + * arp_timeout_needed past now, whichever is longer. Note that + * the first case only occurs if select-timeout is set to nonzero + * in dhclient.conf. + */ + struct timespec time_left = + {.tv_sec = stop_selecting - cur_time, .tv_nsec = 0}; if (timespeccmp(&time_left, &arp_timeout_needed, <)) { - timespecadd(&time_now, &arp_timeout_needed, &stop_selecting); - } - - /* If the selecting interval has expired, go immediately to - state_selecting(). Otherwise, time out into - state_selecting at the select interval. */ - - - if (timespeccmp(&stop_selecting, &zero_timespec, <=)) - state_selecting(ip); - else { - add_timeout_timespec(stop_selecting, state_selecting, ip); - cancel_timeout(send_discover, ip); + timespecadd(&time_now, &arp_timeout_needed, &stop_time); + } else { + timespecadd(&time_now, &time_left, &stop_time); } + add_timeout_timespec(stop_time, state_selecting, ip); + cancel_timeout(send_discover, ip); } /* Allocate a client_lease structure and initialize it from the parameters diff --git a/sbin/dhclient/dhcpd.h b/sbin/dhclient/dhcpd.h index 6f1f6679875c..c61564067598 100644 --- a/sbin/dhclient/dhcpd.h +++ b/sbin/dhclient/dhcpd.h @@ -361,8 +361,8 @@ char *piaddr(struct iaddr); extern cap_channel_t *capsyslog; extern const char *path_dhclient_conf; extern char *path_dhclient_db; -extern struct timespec time_now; -extern time_t cur_time; +extern struct timespec time_now; /* CLOCK_MONOTONIC */ +extern time_t cur_time; /* Seconds since epoch */ extern int log_priority; extern int log_perror; diff --git a/sbin/dhclient/dispatch.c b/sbin/dhclient/dispatch.c index 72230c01a491..06bd31e5ec82 100644 --- a/sbin/dhclient/dispatch.c +++ b/sbin/dhclient/dispatch.c @@ -159,8 +159,8 @@ dispatch(void) struct protocol *l; struct pollfd *fds; struct timespec howlong; - time_now.tv_sec = cur_time; - time_now.tv_nsec = 0; + + clock_gettime(CLOCK_MONOTONIC, &time_now); for (l = protocols; l; l = l->next) nfds++; @@ -224,7 +224,7 @@ another: if (count == -1) { if (errno == EAGAIN || errno == EINTR) { clock_gettime(CLOCK_MONOTONIC, &time_now); - cur_time = time_now.tv_sec; + cur_time = time(NULL); continue; } else error("poll: %m"); @@ -232,7 +232,7 @@ another: /* Get the current time... */ clock_gettime(CLOCK_MONOTONIC, &time_now); - cur_time = time_now.tv_sec; + cur_time = time(NULL); i = 0; for (l = protocols; l; l = l->next) { @@ -365,7 +365,11 @@ active: void add_timeout(time_t when_s, void (*where)(void *), void *what) { - struct timespec when = { .tv_sec = when_s, .tv_nsec = 0 }; + struct timespec when; + + cur_time = time(NULL); + clock_gettime(CLOCK_MONOTONIC, &when); + when.tv_sec += when_s - cur_time; add_timeout_timespec(when, where, what); } 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/fsck/Makefile b/sbin/fsck/Makefile index 3273ea730dc8..33ff4784a92b 100644 --- a/sbin/fsck/Makefile +++ b/sbin/fsck/Makefile @@ -3,10 +3,7 @@ PACKAGE=runtime PROG= fsck SRCS= fsck.c fsutil.c preen.c -SRCS+= getmntopts.c MAN= fsck.8 -MOUNT= ${SRCTOP}/sbin/mount -CFLAGS+= -I${MOUNT} -.PATH: ${MOUNT} +LIBADD= util .include <bsd.prog.mk> diff --git a/sbin/fsck_ffs/Makefile b/sbin/fsck_ffs/Makefile index 7b0f0ed822bf..c6676007db2a 100644 --- a/sbin/fsck_ffs/Makefile +++ b/sbin/fsck_ffs/Makefile @@ -5,12 +5,12 @@ LINKS+= ${BINDIR}/fsck_ffs ${BINDIR}/fsck_4.2bsd MAN= fsck_ffs.8 MLINKS= fsck_ffs.8 fsck_ufs.8 fsck_ffs.8 fsck_4.2bsd.8 SRCS= dir.c ea.c fsutil.c inode.c main.c pass1.c pass1b.c pass2.c pass3.c \ - pass4.c pass5.c setup.c suj.c utilities.c gjournal.c getmntopts.c \ + pass4.c pass5.c setup.c suj.c utilities.c gjournal.c \ globs.c -LIBADD= ufs +LIBADD= ufs util WARNS?= 2 -CFLAGS+= -I${.CURDIR} -I${.CURDIR:H}/mount +CFLAGS+= -I${.CURDIR} -.PATH: ${SRCTOP}/sys/ufs/ffs ${.CURDIR:H}/mount +.PATH: ${SRCTOP}/sys/ufs/ffs .include <bsd.prog.mk> diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h index 32d1d93e05c8..aaf02850f29a 100644 --- a/sbin/fsck_ffs/fsck.h +++ b/sbin/fsck_ffs/fsck.h @@ -153,7 +153,7 @@ struct bufarea { int b_flags; /* B_ flags below */ int b_type; /* BT_ type below */ int b_refcnt; /* ref count of users */ - int b_index; /* for BT_LEVEL, ptr index */ + uint64_t b_index; /* for BT_LEVEL, ptr index */ /* for BT_INODES, first inum */ union { char *b_buf; /* buffer space */ diff --git a/sbin/fsck_msdosfs/fsck_msdosfs.8 b/sbin/fsck_msdosfs/fsck_msdosfs.8 index a74649e0c47e..7c90c8b11b5d 100644 --- a/sbin/fsck_msdosfs/fsck_msdosfs.8 +++ b/sbin/fsck_msdosfs/fsck_msdosfs.8 @@ -125,9 +125,8 @@ to assume as the answer to all operator questions. .El .Sh SEE ALSO -.Xr fsck 8 , -.Xr fsck_ffs 8 , -.Xr mount_msdosfs 8 +.Xr msdosfs 4 , +.Xr fsck 8 .Sh HISTORY The .Nm diff --git a/sbin/geom/core/geom.c b/sbin/geom/core/geom.c index 950f6790b1a8..b78021194ddd 100644 --- a/sbin/geom/core/geom.c +++ b/sbin/geom/core/geom.c @@ -249,7 +249,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)) { diff --git a/sbin/growfs/Makefile b/sbin/growfs/Makefile index 6fb1ba51db00..b5b3d99f287d 100644 --- a/sbin/growfs/Makefile +++ b/sbin/growfs/Makefile @@ -3,13 +3,10 @@ .include <src.opts.mk> -.PATH: ${.CURDIR:H}/mount - PACKAGE=ufs PROG= growfs -SRCS= growfs.c getmntopts.c +SRCS= growfs.c MAN= growfs.8 -CFLAGS+=-I${.CURDIR:H}/mount .if defined(GFSDBG) SRCS+= debug.c diff --git a/sbin/growfs/tests/legacy_test.pl b/sbin/growfs/tests/legacy_test.pl index 7ae2e59422d8..e2b145fa7f35 100755 --- a/sbin/growfs/tests/legacy_test.pl +++ b/sbin/growfs/tests/legacy_test.pl @@ -30,9 +30,9 @@ sub fsck_md { sub setsize { my ($partszMB, $unitszMB) = @_; - open my $fd, "|-", "bsdlabel -R md$unit /dev/stdin" or die; - print $fd "a: ", ($partszMB * BLKS_PER_MB), " 0 4.2BSD 1024 8192\n"; - print $fd "c: ", ($unitszMB * BLKS_PER_MB), " 0 unused 0 0\n"; + open my $fd, "|-", "gpart restore -F md$unit" or die; + print $fd "BSD 8\n"; + print $fd "1 freebsd-ufs 0 ", ($partszMB * BLKS_PER_MB), "\n"; close $fd; } 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 fcd04139a8c1..9386f5eaf513 100644 --- a/sbin/ifconfig/af_inet6.c +++ b/sbin/ifconfig/af_inet6.c @@ -428,6 +428,11 @@ in6_getaddr(const char *addr_str, int which) { struct in6_px *px = sin6tab_nl[which]; + if (which == MASK) + errx(1, "netmask: invalid option for inet6"); + if (which == BRDADDR) + errx(1, "broadcast: invalid option for inet6"); + px->set = true; px->plen = 128; if (which == ADDR) { @@ -721,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), @@ -748,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_link.c b/sbin/ifconfig/af_link.c index ab35d04a8709..55b75d847c16 100644 --- a/sbin/ifconfig/af_link.c +++ b/sbin/ifconfig/af_link.c @@ -210,7 +210,8 @@ link_getaddr(const char *addr, int which) temp[0] = ':'; strcpy(temp + 1, addr); sdl.sdl_len = sizeof(sdl); - link_addr(temp, &sdl); + if (link_addr(temp, &sdl) == -1) + errx(1, "malformed link-level address"); free(temp); } if (sdl.sdl_alen > sizeof(sa->sa_data)) 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 2d0af1255a73..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 = {}; @@ -147,6 +165,51 @@ bridge_addresses(if_ctx *ctx, const char *prefix) } static void +print_vlans(ifbvlan_set_t *vlans) +{ + unsigned printed = 0; + + for (unsigned vlan = DOT1Q_VID_MIN; vlan <= DOT1Q_VID_MAX;) { + unsigned last; + + if (!BRVLAN_TEST(vlans, vlan)) { + ++vlan; + continue; + } + + last = vlan; + while (last < DOT1Q_VID_MAX && BRVLAN_TEST(vlans, last + 1)) + ++last; + + if (printed == 0) + printf(" tagged "); + else + printf(","); + + printf("%u", vlan); + if (last != vlan) + printf("-%u", last); + ++printed; + vlan = last + 1; + } +} + +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) { struct ifconfig_bridge_status *bridge; @@ -179,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) { @@ -187,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); @@ -211,21 +280,74 @@ bridge_status(if_ctx *ctx) else printf(" <unknown state %d>", state); } + 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"); } 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 @@ -387,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, ""); } @@ -577,6 +735,35 @@ setbridge_ifpathcost(if_ctx *ctx, const char *ifn, const char *cost) } static void +setbridge_ifuntagged(if_ctx *ctx, const char *ifn, const char *vlanid) +{ + struct ifbreq req; + + memset(&req, 0, sizeof(req)); + strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); + + if (get_vlan_id(vlanid, &req.ifbr_pvid) < 0) + errx(1, "invalid VLAN identifier: %s", vlanid); + + if (do_cmd(ctx, BRDGSIFPVID, &req, sizeof(req), 1) < 0) + err(1, "BRDGSIFPVID %s", vlanid); +} + +static void +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_pvid = 0; + + if (do_cmd(ctx, BRDGSIFPVID, &req, sizeof(req), 1) < 0) + err(1, "BRDGSIFPVID"); +} + +static void setbridge_ifmaxaddr(if_ctx *ctx, const char *ifn, const char *arg) { struct ifbreq req; @@ -612,19 +799,192 @@ setbridge_timeout(if_ctx *ctx, const char *arg, int dummy __unused) static void setbridge_private(if_ctx *ctx, const char *val, int dummy __unused) { - do_bridgeflag(ctx, val, IFBIF_PRIVATE, 1); } static void unsetbridge_private(if_ctx *ctx, const char *val, int dummy __unused) { - do_bridgeflag(ctx, val, IFBIF_PRIVATE, 0); } +static int +parse_vlans(ifbvlan_set_t *set, const char *str) +{ + char *s, *token; + + /* "none" means the empty vlan set */ + if (strcmp(str, "none") == 0) { + __BIT_ZERO(BRVLAN_SETSIZE, set); + return (0); + } + + /* "all" means all vlans, except for 0 and 4095 which are reserved */ + if (strcmp(str, "all") == 0) { + __BIT_FILL(BRVLAN_SETSIZE, set); + BRVLAN_CLR(set, DOT1Q_VID_NULL); + BRVLAN_CLR(set, DOT1Q_VID_RSVD_IMPL); + return (0); + } + + if ((s = strdup(str)) == NULL) + return (-1); + + while ((token = strsep(&s, ",")) != NULL) { + unsigned long first, last; + char *p, *lastp; + + if ((lastp = strchr(token, '-')) != NULL) + *lastp++ = '\0'; + + first = last = strtoul(token, &p, 10); + if (*p != '\0') + goto err; + if (first < DOT1Q_VID_MIN || first > DOT1Q_VID_MAX) + goto err; + + if (lastp) { + last = strtoul(lastp, &p, 10); + if (*p != '\0') + goto err; + if (last < DOT1Q_VID_MIN || last > DOT1Q_VID_MAX || + last < first) + goto err; + } + + for (unsigned vlan = first; vlan <= last; ++vlan) + BRVLAN_SET(set, vlan); + } + + free(s); + return (0); + +err: + free(s); + return (-1); +} + +static void +set_bridge_vlanset(if_ctx *ctx, const char *ifn, const char *vlans, int op) +{ + struct ifbif_vlan_req req; + + memset(&req, 0, sizeof(req)); + + if (parse_vlans(&req.bv_set, vlans) != 0) + errx(1, "invalid vlan set: %s", vlans); + + strlcpy(req.bv_ifname, ifn, sizeof(req.bv_ifname)); + req.bv_op = op; + + if (do_cmd(ctx, BRDGSIFVLANSET, &req, sizeof(req), 1) < 0) + err(1, "BRDGSIFVLANSET %s", vlans); +} + +static void +setbridge_iftagged(if_ctx *ctx, const char *ifn, const char *vlans) +{ + set_bridge_vlanset(ctx, ifn, vlans, BRDG_VLAN_OP_SET); +} + +static void +addbridge_iftagged(if_ctx *ctx, const char *ifn, const char *vlans) +{ + set_bridge_vlanset(ctx, ifn, vlans, BRDG_VLAN_OP_ADD); +} + +static void +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), @@ -646,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), @@ -659,10 +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_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 dfea59dfd229..fafb77e1ca6c 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 June 6, 2024 +.Dd September 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 , @@ -448,11 +448,10 @@ of specifying the host portion, removing all NS addresses will allow you to respecify the host portion. .It Cm anycast (Inet6 only.) -Specify that the address configured is an anycast address. -Based on the current specification, -only routers may configure anycast addresses. -Anycast address will not be used as source address of any of outgoing -IPv6 packets. +Specify that the address configured is an anycast address, +as described in RFC 4291 section 2.6. +Anycast addresses will not be used as source address of any outgoing +IPv6 packets unless an application explicitly binds to the address. .It Cm arp Enable the use of the Address Resolution Protocol .Pq Xr arp 4 @@ -1005,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. @@ -2495,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 @@ -2521,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 @@ -2696,6 +2748,103 @@ 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. +.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 +.Ar first-last , +the value +.Dq none +meaning the empty set, +or the value +.Dq all +meaning all VLANs (1-4094). +.Pp +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 iftagged . +.Pp +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 iftagged . +.Pp +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 Ar vlan-id +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: @@ -2828,34 +2977,26 @@ interfaces previously configured with Another name for the .Fl tunnel parameter. -.It Cm accept_rev_ethip_ver -Set a flag to accept both correct EtherIP packets and ones -with reversed version field. -Enabled by default. -This is for backward compatibility with -.Fx 6.1 , -6.2, 6.3, 7.0, and 7.1. -.It Cm -accept_rev_ethip_ver -Clear a flag -.Cm accept_rev_ethip_ver . +.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 . -.It Cm send_rev_ethip_ver -Set a flag to send EtherIP packets with reversed version -field intentionally. -Disabled by default. -This is for backward compatibility with -.Fx 6.1 , -6.2, 6.3, 7.0, and 7.1. -.It Cm -send_rev_ethip_ver -Clear a flag -.Cm send_rev_ethip_ver . .El .Ss GRE Tunnel Parameters The following parameters apply to GRE tunnel interfaces, @@ -3326,6 +3467,16 @@ tried to alter an interface's configuration. .Xr rc 8 , .Xr routed 8 , .Xr sysctl 8 +.Rs +.%R RFC 3484 +.%D February 2003 +.%T "Default Address Selection for Internet Protocol version 6 (IPv6)" +.Re +.Rs +.%R RFC 4291 +.%D February 2006 +.%T "IP Version 6 Addressing Architecture" +.Re .Sh HISTORY The .Nm 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/ifconfig_netlink.c b/sbin/ifconfig/ifconfig_netlink.c index 5a986e840d7f..b5badfd585b8 100644 --- a/sbin/ifconfig/ifconfig_netlink.c +++ b/sbin/ifconfig/ifconfig_netlink.c @@ -25,6 +25,8 @@ * SUCH DAMAGE. */ +#define _WANT_IFCAP_BIT_NAMES + #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -139,7 +141,7 @@ struct ifmap { * Memory is allocated using snl temporary buffers */ static struct ifmap * -prepare_ifmap(struct snl_state *ss) +prepare_ifmap(struct snl_state *ss, const char *ifname) { struct snl_writer nw = {}; @@ -147,6 +149,8 @@ prepare_ifmap(struct snl_state *ss) struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK); hdr->nlmsg_flags |= NLM_F_DUMP; snl_reserve_msg_object(&nw, struct ifinfomsg); + if (ifname != NULL) + snl_add_msg_attr_string(&nw, IFLA_IFNAME, ifname); if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return (NULL); @@ -453,7 +457,7 @@ list_interfaces_nl(struct ifconfig_args *args) nl_init_socket(&ss); - struct ifmap *ifmap = prepare_ifmap(&ss); + struct ifmap *ifmap = prepare_ifmap(&ss, args->ifname); struct iface **sorted_ifaces = snl_allocz(&ss, ifmap->count * sizeof(void *)); for (uint32_t i = 0, num = 0; i < ifmap->size; i++) { if (ifmap->ifaces[i] != NULL) { 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/ifieee80211.c b/sbin/ifconfig/ifieee80211.c index 656e71e325cd..e73dcc2c4f24 100644 --- a/sbin/ifconfig/ifieee80211.c +++ b/sbin/ifconfig/ifieee80211.c @@ -2779,6 +2779,177 @@ printwmeinfo(if_ctx *ctx, const char *tag, const u_int8_t *ie) } static void +printhecap(if_ctx *ctx, const char *tag, const uint8_t *ie) +{ + const struct ieee80211_he_cap_elem *hecap; + const struct ieee80211_he_mcs_nss_supp *mcsnss; + unsigned int i; + uint8_t chw; + + printf("%s", tag); + if (!ctx->args->verbose) + return; + + /* Check that the right size. */ + if (ie[1] < 1 + sizeof(*hecap) + 4) { + printf("<err: he_cap inval. length %#0x>", ie[1]); + return; + } + /* Skip Element ID, Length, EID Extension. */ + hecap = (const struct ieee80211_he_cap_elem *)(ie + 3); + + /* XXX-BZ we need to somehow decode each field? */ + printf("<mac_cap"); + for (i = 0; i < nitems(hecap->mac_cap_info); i++) + printf(" %#04x", hecap->mac_cap_info[i]); + printf(" phy_cap"); + for (i = 0; i < nitems(hecap->phy_cap_info); i++) + printf(" %#04x", hecap->phy_cap_info[i]); + + chw = hecap->phy_cap_info[0]; + ie = (const uint8_t *)(const void *)(hecap + 1); + mcsnss = (const struct ieee80211_he_mcs_nss_supp *)ie; + /* Cannot use <= as < is a delimiter. */ + printf(" rx/tx_he_mcs map: loweq80 %#06x/%#06x", + mcsnss->rx_mcs_80, mcsnss->tx_mcs_80); + ie += 2; + if ((chw & (1<<2)) != 0) { + printf(" 160 %#06x/%#06x", + mcsnss->rx_mcs_160, mcsnss->tx_mcs_160); + ie += 2; + } + if ((chw & (1<<3)) != 0) { + printf(" 80+80 %#06x/%#06x", + mcsnss->rx_mcs_80p80, mcsnss->tx_mcs_80p80); + ie += 2; + } + /* TODO: ppet = (struct ... *)ie; */ + + printf(">"); +} + +static void +printheoper(if_ctx *ctx, const char *tag, const uint8_t *ie) +{ + printf("%s", tag); + if (ctx->args->verbose) { + const struct ieee80211_he_operation *heoper; + uint32_t params; + + /* Check that the right size. */ + if (ie[1] < 1 + sizeof(*heoper)) { + printf("<err: he_oper inval. length %#0x>", ie[1]); + return; + } + /* Skip Element ID, Length, EID Extension. */ + heoper = (const struct ieee80211_he_operation *)(ie + 3); + + /* XXX-BZ we need to somehow decode each field? */ + params = heoper->he_oper_params & 0x00ffffff; + printf("<params %#08x", params); + printf(" bss_col %#04x", (heoper->he_oper_params & 0xff000000) >> 24); + printf(" mcs_nss %#06x", heoper->he_mcs_nss_set); + if ((params & (1 << 14)) != 0) { + printf(" vht_op 0-3"); + } + if ((params & (1 << 15)) != 0) { + printf(" max_coh_bssid 0-1"); + } + if ((params & (1 << 17)) != 0) { + printf(" 6ghz_op 0-5"); + } + printf(">"); + } +} + +static void +printmuedcaparamset(if_ctx *ctx, const char *tag, const uint8_t *ie) +{ + static const char *acnames[] = { "BE", "BK", "VO", "VI" }; + const struct ieee80211_mu_edca_param_set *mu_edca; + int i; + + printf("%s", tag); + if (!ctx->args->verbose) + return; + + /* Check that the right size. */ + if (ie[1] != 1 + sizeof(*mu_edca)) { + printf("<err: mu_edca inval. length %#04x>", ie[1]); + return; + } + /* Skip Element ID, Length, EID Extension. */ + mu_edca = (const struct ieee80211_mu_edca_param_set *)(ie + 3); + + printf("<qosinfo 0x%x", mu_edca->mu_qos_info); + ie++; + for (i = 0; i < WME_NUM_AC; i++) { + const struct ieee80211_he_mu_edca_param_ac_rec *ac = + &mu_edca->param_ac_recs[i]; + + printf(" %s[aifsn %u ecwmin %u ecwmax %u timer %u]", acnames[i], + ac->aifsn, + _IEEE80211_MASKSHIFT(ac->ecw_min_max, WME_PARAM_LOGCWMIN), + _IEEE80211_MASKSHIFT(ac->ecw_min_max, WME_PARAM_LOGCWMAX), + ac->mu_edca_timer); + } + printf(">"); +} + +static void +printsupopclass(if_ctx *ctx, const char *tag, const u_int8_t *ie) +{ + uint8_t len, i; + + printf("%s", tag); + if (!ctx->args->verbose) + return; + + /* Check that the right size. */ + len = ie[1]; + if (len < 2) { + printf("<err: sup_op_class inval. length %#04x>", ie[1]); + return; + } + + ie += 2; + i = 0; + printf("<cur op class %u", *ie); + i++; + if (i < len && *(ie + i) != 130) + printf(" op classes"); + while (i < len && *(ie + i) != 130) { + printf(" %u", *(ie + i)); + i++; + } + if (i > 1 && i < len && *(ie + i) != 130) { + printf(" parsing error at %#0x>", i); + return; + } + /* Skip OneHundredAndThirty Delimiter. */ + i++; + if (i < len && *(ie + i) != 0) + printf(" ext seq"); + while (i < len && *(ie + i) != 0) { + printf(" %u", *(ie + i)); + i++; + } + if (i > 1 && i < len && *(ie + i) != 0) { + printf(" parsing error at %#0x>", i); + return; + } + /* Skip Zero Delimiter. */ + i++; + if ((i + 1) < len) + printf(" duple seq"); + while ((i + 1) < len) { + printf(" %u/%u", *(ie + i), *(ie + i + 1)); + i += 2; + } + printf(">"); +} + +static void printvhtcap(if_ctx *ctx, const char *tag, const u_int8_t *ie) { printf("%s", tag); @@ -3138,6 +3309,10 @@ rsn_cipher(const u_int8_t *sel) return "AES-OCB"; case RSN_SEL(RSN_CSE_GCMP_128): return "AES-GCMP"; + case RSN_SEL(RSN_CSE_CCMP_256): + return "AES-CCMP-256"; + case RSN_SEL(RSN_CSE_GCMP_256): + return "AES-GCMP-256"; } return "?"; #undef WPA_SEL @@ -3154,6 +3329,10 @@ rsn_keymgmt(const u_int8_t *sel) return "8021X-UNSPEC"; case RSN_SEL(RSN_ASE_8021X_PSK): return "8021X-PSK"; + case RSN_SEL(RSN_ASE_8021X_UNSPEC_SHA256): + return "8021X-UNSPEC-SHA256"; + case RSN_SEL(RSN_ASE_8021X_PSK_SHA256): + return "8021X-PSK-256"; case RSN_SEL(RSN_ASE_NONE): return "NONE"; } @@ -3204,6 +3383,33 @@ printrsnie(if_ctx *ctx, const char *tag, const u_int8_t *ie, size_t ielen) } } +static void +printrsnxe(if_ctx *ctx, const char *tag, const u_int8_t *ie, size_t ielen) +{ + size_t n; + + printf("%s", tag); + if (!ctx->args->verbose) + return; + + ie += 2, ielen -= 2; + + n = (*ie & 0x0f); + printf("<%zu", n + 1); + + /* We do not yet know about more than n=1 (0). */ + if (n != 0) + goto end; + + if (*ie & 0x10) + printf(" PTWTOPS"); + if (*ie & 0x20) + printf(" SAE h-t-e"); + +end: + printf(">"); +} + #define BE_READ_2(p) \ ((u_int16_t) \ ((((const u_int8_t *)(p))[1] ) | \ @@ -3581,7 +3787,22 @@ iswpsoui(const uint8_t *frm) } static const char * -iename(int elemid) +ie_ext_name(uint8_t ext_elemid) +{ + static char iename_buf[32]; + + switch (ext_elemid) { + case IEEE80211_ELEMID_EXT_HE_CAPA: return " HECAP"; + case IEEE80211_ELEMID_EXT_HE_OPER: return " HEOPER"; + case IEEE80211_ELEMID_EXT_MU_EDCA_PARAM_SET: return " MU_EDCA_PARAM_SET"; + } + snprintf(iename_buf, sizeof(iename_buf), " ELEMID_EXT_%d", + ext_elemid & 0xff); + return (const char *) iename_buf; +} + +static const char * +iename(uint8_t elemid, const u_int8_t *vp) { static char iename_buf[64]; switch (elemid) { @@ -3603,6 +3824,8 @@ iename(int elemid) case IEEE80211_ELEMID_IBSSDFS: return " IBSSDFS"; case IEEE80211_ELEMID_RESERVED_47: return " RESERVED_47"; + case IEEE80211_ELEMID_SUP_OP_CLASS: + return " SUP_OP_CLASS"; case IEEE80211_ELEMID_MOBILITY_DOMAIN: return " MOBILITY_DOMAIN"; case IEEE80211_ELEMID_RRM_ENACAPS: @@ -3612,13 +3835,43 @@ iename(int elemid) case IEEE80211_ELEMID_TPC: return " TPC"; case IEEE80211_ELEMID_CCKM: return " CCKM"; case IEEE80211_ELEMID_EXTCAP: return " EXTCAP"; + case IEEE80211_ELEMID_RSN_EXT: return " RSNXE"; + case IEEE80211_ELEMID_EXTFIELD: + if (vp[1] >= 1) + return ie_ext_name(vp[2]); + break; } - snprintf(iename_buf, sizeof(iename_buf), " UNKNOWN_ELEMID_%d", + snprintf(iename_buf, sizeof(iename_buf), " ELEMID_%d", elemid); return (const char *) iename_buf; } static void +printexties(if_ctx *ctx, const u_int8_t *vp, unsigned int maxcols) +{ + const int verbose = ctx->args->verbose; + + if (vp[1] < 1) + return; + + switch (vp[2]) { + case IEEE80211_ELEMID_EXT_HE_CAPA: + printhecap(ctx, " HECAP", vp); + break; + case IEEE80211_ELEMID_EXT_HE_OPER: + printheoper(ctx, " HEOPER", vp); + break; + case IEEE80211_ELEMID_EXT_MU_EDCA_PARAM_SET: + printmuedcaparamset(ctx, " MU_EDCA_PARAM_SET", vp); + break; + default: + if (verbose) + printie(ctx, iename(vp[0], vp), vp, 2+vp[1], maxcols); + break; + } +} + +static void printies(if_ctx *ctx, const u_int8_t *vp, int ielen, unsigned int maxcols) { const int verbose = ctx->args->verbose; @@ -3669,6 +3922,9 @@ printies(if_ctx *ctx, const u_int8_t *vp, int ielen, unsigned int maxcols) case IEEE80211_ELEMID_HTCAP: printhtcap(ctx, " HTCAP", vp); break; + case IEEE80211_ELEMID_SUP_OP_CLASS: + printsupopclass(ctx, " SUP_OP_CLASS", vp); + break; case IEEE80211_ELEMID_HTINFO: if (verbose) printhtinfo(ctx, " HTINFO", vp); @@ -3695,9 +3951,15 @@ printies(if_ctx *ctx, const u_int8_t *vp, int ielen, unsigned int maxcols) case IEEE80211_ELEMID_APCHANREP: printapchanrep(ctx, " APCHANREP", vp, 2+vp[1]); break; + case IEEE80211_ELEMID_RSN_EXT: + printrsnxe(ctx, " RSNXE", vp, 2+vp[1]); + break; + case IEEE80211_ELEMID_EXTFIELD: + printexties(ctx, vp, maxcols); + break; default: if (verbose) - printie(ctx, iename(vp[0]), vp, 2+vp[1], maxcols); + printie(ctx, iename(vp[0], vp), vp, 2+vp[1], maxcols); break; } ielen -= 2+vp[1]; @@ -4749,6 +5011,9 @@ printcipher(int s, struct ieee80211req *ireq, int keylenop) case IEEE80211_CIPHER_AES_CCM: printf("AES-CCM"); break; + case IEEE80211_CIPHER_AES_GCM_128: + printf("AES-GCM"); + break; case IEEE80211_CIPHER_CKIP: printf("CKIP"); break; @@ -4763,6 +5028,17 @@ printcipher(int s, struct ieee80211req *ireq, int keylenop) #endif static void +printkey_index(uint16_t keyix, char *buf, size_t buflen) +{ + buf[0] = '\0'; + if (keyix == IEEE80211_KEYIX_NONE) { + snprintf(buf, buflen, "ucast"); + } else { + snprintf(buf, buflen, "%u", keyix+1); + } +} + +static void printkey(if_ctx *ctx, const struct ieee80211req_key *ik) { static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE]; @@ -4770,38 +5046,43 @@ printkey(if_ctx *ctx, const struct ieee80211req_key *ik) int printcontents; const int verbose = ctx->args->verbose; const bool printkeys = ctx->args->printkeys; + char keyix[16]; printcontents = printkeys && (memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose); if (printcontents) LINE_BREAK(); + printkey_index(ik->ik_keyix, keyix, sizeof(keyix)); switch (ik->ik_type) { case IEEE80211_CIPHER_WEP: /* compatibility */ - LINE_CHECK("wepkey %u:%s", ik->ik_keyix+1, + LINE_CHECK("wepkey %s:%s", keyix, keylen <= 5 ? "40-bit" : keylen <= 13 ? "104-bit" : "128-bit"); break; case IEEE80211_CIPHER_TKIP: if (keylen > 128/8) keylen -= 128/8; /* ignore MIC for now */ - LINE_CHECK("TKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen); + LINE_CHECK("TKIP %s:%u-bit", keyix, 8*keylen); break; case IEEE80211_CIPHER_AES_OCB: - LINE_CHECK("AES-OCB %u:%u-bit", ik->ik_keyix+1, 8*keylen); + LINE_CHECK("AES-OCB %s:%u-bit", keyix, 8*keylen); break; case IEEE80211_CIPHER_AES_CCM: - LINE_CHECK("AES-CCM %u:%u-bit", ik->ik_keyix+1, 8*keylen); + LINE_CHECK("AES-CCM %s:%u-bit", keyix, 8*keylen); + break; + case IEEE80211_CIPHER_AES_GCM_128: + LINE_CHECK("AES-GCM %s:%u-bit", keyix, 8*keylen); break; case IEEE80211_CIPHER_CKIP: - LINE_CHECK("CKIP %u:%u-bit", ik->ik_keyix+1, 8*keylen); + LINE_CHECK("CKIP %s:%u-bit", keyix, 8*keylen); break; case IEEE80211_CIPHER_NONE: - LINE_CHECK("NULL %u:%u-bit", ik->ik_keyix+1, 8*keylen); + LINE_CHECK("NULL %s:%u-bit", keyix, 8*keylen); break; default: - LINE_CHECK("UNKNOWN (0x%x) %u:%u-bit", - ik->ik_type, ik->ik_keyix+1, 8*keylen); + LINE_CHECK("UNKNOWN (0x%x) %s:%u-bit", + ik->ik_type, keyix, 8*keylen); break; } if (printcontents) { @@ -4884,6 +5165,7 @@ ieee80211_status(if_ctx *ctx) { int s = ctx->io_s; static const uint8_t zerobssid[IEEE80211_ADDR_LEN]; + uint8_t bssid[IEEE80211_ADDR_LEN]; enum ieee80211_opmode opmode = get80211opmode(ctx); int i, num, wpa, wme, bgscan, bgscaninterval, val, len, wepmode; uint8_t data[32]; @@ -4934,10 +5216,10 @@ ieee80211_status(if_ctx *ctx) } else if (verbose) printf(" channel UNDEF"); - if (get80211(ctx, IEEE80211_IOC_BSSID, data, IEEE80211_ADDR_LEN) >= 0 && - (memcmp(data, zerobssid, sizeof(zerobssid)) != 0 || verbose)) { - printf(" bssid %s", ether_ntoa((struct ether_addr *)data)); - printbssidname((struct ether_addr *)data); + if (get80211(ctx, IEEE80211_IOC_BSSID, bssid, IEEE80211_ADDR_LEN) >= 0 && + (memcmp(bssid, zerobssid, sizeof(zerobssid)) != 0 || verbose)) { + printf(" bssid %s", ether_ntoa((struct ether_addr *)bssid)); + printbssidname((struct ether_addr *)bssid); } if (get80211len(ctx, IEEE80211_IOC_STATIONNAME, data, sizeof(data), &len) != -1) { @@ -5084,7 +5366,7 @@ ieee80211_status(if_ctx *ctx) memset(&ik, 0, sizeof(ik)); ik.ik_keyix = i; if (get80211(ctx, IEEE80211_IOC_WPAKEY, &ik, sizeof(ik)) < 0) { - warn("WEP support, but can get keys!"); + warn("WEP support, but cannot get keys!"); goto end; } if (ik.ik_keylen != 0) { @@ -5093,6 +5375,21 @@ ieee80211_status(if_ctx *ctx) printkey(ctx, &ik); } } + if (opmode == IEEE80211_M_STA && wpa >= 2) { + struct ieee80211req_key ik; + int error; + + memset(&ik, 0, sizeof(ik)); + ik.ik_keyix = IEEE80211_KEYIX_NONE; + memcpy(ik.ik_macaddr, bssid, sizeof(ik.ik_macaddr)); + error = get80211(ctx, IEEE80211_IOC_WPAKEY, &ik, sizeof(ik)); + if (error == 0 && ik.ik_keylen != 0) { + if (verbose) + LINE_BREAK(); + printkey(ctx, &ik); + i++; + } + } if (i > 0 && verbose) LINE_BREAK(); end: diff --git a/sbin/ifconfig/tests/Makefile b/sbin/ifconfig/tests/Makefile index ff545f603085..e902f262552a 100644 --- a/sbin/ifconfig/tests/Makefile +++ b/sbin/ifconfig/tests/Makefile @@ -1,5 +1,8 @@ -NETBSD_ATF_TESTS_SH= nonexistent_test +NETBSD_ATF_TESTS_SH= nonexistent_test +ATF_TESTS_SH+= inet6 -.include <netbsd-tests.test.mk> +TEST_METADATA+= execenv="jail" +TEST_METADATA+= execenv_jail_params="vnet allow.raw_sockets" +.include <netbsd-tests.test.mk> .include <bsd.test.mk> diff --git a/sbin/ifconfig/tests/inet6.sh b/sbin/ifconfig/tests/inet6.sh new file mode 100644 index 000000000000..22399915a64d --- /dev/null +++ b/sbin/ifconfig/tests/inet6.sh @@ -0,0 +1,113 @@ +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2025 Lexi Winter + +. $(atf_get_srcdir)/../../sys/common/vnet.subr + +# Bug 286910: adding 'netmask' or 'broadcast' to an IPv6 address crashed +# ifconfig. + +atf_test_case "netmask" "cleanup" +netmask_head() +{ + atf_set descr "Test invalid 'netmask' option" + atf_set require.user root +} + +netmask_body() +{ + vnet_init + + ep=$(vnet_mkepair) + vnet_mkjail ifcjail ${ep}a + + # Add the address the wrong way + atf_check -s exit:1 \ + -e match:"ifconfig: netmask: invalid option for inet6" \ + jexec ifcjail ifconfig ${ep}a inet6 2001:db8:1::1 netmask 64 + + # Add the address the correct way + atf_check -s exit:0 \ + jexec ifcjail ifconfig ${ep}a inet6 2001:db8:1::1/64 + atf_check -s exit:0 -o match:"2001:db8:1::1 prefixlen 64" \ + jexec ifcjail ifconfig ${ep}a + + # Remove the address the wrong way + atf_check -s exit:1 \ + -e match:"ifconfig: netmask: invalid option for inet6" \ + jexec ifcjail ifconfig ${ep}a inet6 2001:db8:1::1 netmask 64 -alias +} + +netmask_cleanup() +{ + vnet_cleanup +} + +atf_test_case "broadcast" "cleanup" +broadcast_head() +{ + atf_set descr "Test invalid 'broadcast' option" + atf_set require.user root +} + +broadcast_body() +{ + vnet_init + + ep=$(vnet_mkepair) + vnet_mkjail ifcjail ${ep}a + + atf_check -s exit:1 \ + -e match:"ifconfig: broadcast: invalid option for inet6" \ + jexec ifcjail ifconfig ${ep}a \ + inet6 2001:db8:1::1 broadcast 2001:db8:1::ffff + + atf_check -s exit:0 \ + jexec ifcjail ifconfig ${ep}a inet6 2001:db8:1::1/64 + + atf_check -s exit:1 \ + -e match:"ifconfig: broadcast: invalid option for inet6" \ + jexec ifcjail ifconfig ${ep}a \ + inet6 2001:db8:1::1 broadcast 2001:db:1::ffff -alias +} + +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/Makefile b/sbin/init/Makefile index 124a9f77dede..1fc9b633f664 100644 --- a/sbin/init/Makefile +++ b/sbin/init/Makefile @@ -1,7 +1,6 @@ CONFGROUPS= CONFTTYS PACKAGE=runtime PROG= init -SRCS= init.c getmntopts.c MAN= init.8 PRECIOUSPROG= INSTALLFLAGS=-b -B.bak @@ -11,11 +10,6 @@ LIBADD= util crypt CONFTTYSNAME= ttys CONFTTYS+= ttys -# Needed for getmntopts.c -MOUNT= ${SRCTOP}/sbin/mount -CFLAGS+=-I${MOUNT} -.PATH: ${MOUNT} - NO_SHARED?= YES .include <bsd.prog.mk> diff --git a/sbin/init/init.c b/sbin/init/init.c index b6908f19fd82..d28501053c7f 100644 --- a/sbin/init/init.c +++ b/sbin/init/init.c @@ -49,6 +49,7 @@ #include <fcntl.h> #include <kenv.h> #include <libutil.h> +#include <mntopts.h> #include <paths.h> #include <signal.h> #include <stdarg.h> @@ -69,7 +70,6 @@ #include <login_cap.h> #endif -#include "mntopts.h" #include "pathnames.h" /* @@ -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/ipsend/iptests.c b/sbin/ipf/ipsend/iptests.c index 6f95970a83aa..6a72a0adfffd 100644 --- a/sbin/ipf/ipsend/iptests.c +++ b/sbin/ipf/ipsend/iptests.c @@ -74,7 +74,6 @@ typedef int boolean_t; # include <netinet/in_pcb.h> # endif #include "ipsend.h" -# include <netinet/tcp_timer.h> # include <netinet/tcp_var.h> #if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 106000000) # define USE_NANOSLEEP diff --git a/sbin/ipf/ipsend/sock.c b/sbin/ipf/ipsend/sock.c index b42f56f848da..7ffacc950d22 100644 --- a/sbin/ipf/ipsend/sock.c +++ b/sbin/ipf/ipsend/sock.c @@ -58,7 +58,6 @@ typedef int boolean_t; #include <netinet/ip_var.h> #define _WANT_INPCB #include <netinet/in_pcb.h> -#include <netinet/tcp_timer.h> #define _WANT_TCPCB #include <netinet/tcp_var.h> #include <stdio.h> 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 3caaa5e022fe..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,10 @@ 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); + return (hp->iph_next); } 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/ipfw.8 b/sbin/ipfw/ipfw.8 index 348e9a58f2ce..ddfdc35ce651 100644 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -1,11 +1,11 @@ .\" -.Dd March 3, 2025 +.Dd April 18, 2025 .Dt IPFW 8 .Os .Sh NAME .Nm ipfw , dnctl .Nd User interface for firewall, traffic shaper, packet scheduler, -in-kernel NAT. +in-kernel NAT.\& .Sh SYNOPSIS .Ss FIREWALL CONFIGURATION .Nm @@ -171,6 +171,8 @@ in-kernel NAT. .Nm .Cm internal iflist .Nm +.Cm internal monitor Op Ar filter-comment +.Nm .Cm internal talist .Nm .Cm internal vlist @@ -331,8 +333,8 @@ When listing, show dynamic rules in addition to static ones. When listing, show only dynamic states. When deleting, delete only dynamic states. .It Fl f -Run without prompting for confirmation for commands that can cause problems if misused, -i.e., +Run without prompting for confirmation for commands that can cause problems +if misused, i.e., .Cm flush . If there is no tty associated with the process, this is implied. The @@ -577,7 +579,7 @@ The format of firewall rules is the following: .Op Cm set Ar set_number .Op Cm prob Ar match_probability .Ar action -.Op Cm log Op Cm logamount Ar number +.Op Cm log Op log_opts .Op Cm altq Ar queue .Oo .Bro Cm tag | untag @@ -694,8 +696,10 @@ side effects. .It Cm log Op Cm logamount Ar number Packets matching a rule with the .Cm log -keyword will be made available for logging in two ways: -if the sysctl variable +keyword will be made available for logging. +Unless per-rule log destination is specified by +.Cm logdst Ar logdst_spec +option (see below), packets are logged in two ways: if the sysctl variable .Va net.inet.ip.fw.verbose is set to 0 (default), one can use .Xr bpf 4 @@ -743,6 +747,47 @@ command. Note: logging is done after all other packet matching conditions have been successfully verified, and before performing the final action (accept, deny, etc.) on the packet. +.It Cm log Oo +.Cm logamount Ar number +.Oc Cm logdst Ar logdst_spec +.Ar logdst_spec +is a comma-separated list of log destinations for logging +packets matching the rule. +Destinations supported are: +.Bl -tag -width indent +.It Ar syslog +Logs a packet to +.Xr syslogd 8 +with a +.Dv LOG_SECURITY +facility. +.It Ar ipfw0 +Logs a packet to the +.Li ipfw0 +pseudo interface. +.It Ar rtsock +Logs a packet to the +.Xr route 4 +socket. +See the comments of +.Fn ipfw_log_rtsock +in ipfw source code for more +information on the message's structure. +.El +.Pp +Note: +.Cm logamount +limits a number of logging events rather than packets being logged. +I.e. A packet matching a rule with +.Bd -ragged -offset indent + ... +.Cm log logamount +100 +.Cm logdst +syslog,ipfw0 ... +.Ed +.Pp +will log upto 50 packets. .It Cm tag Ar number When a packet matches a rule with the .Cm tag @@ -951,7 +996,7 @@ Pass packet to a nat instance (for network address translation, address redirect, etc.): see the -.Sx NETWORK ADDRESS TRANSLATION (NAT) +.Sx NETWORK ADDRESS TRANSLATION (NAT)\& Section for further information. .It Cm nat64lsn Ar name Pass packet to a stateful NAT64 instance (for IPv6/IPv4 network address and @@ -964,14 +1009,14 @@ protocol translation): see the .Sx IPv6/IPv4 NETWORK ADDRESS AND PROTOCOL TRANSLATION Section for further information. .It Cm nat64clat Ar name -Pass packet to a CLAT NAT64 instance (for client-side IPv6/IPv4 network address and -protocol translation): see the +Pass packet to a CLAT NAT64 instance (for client-side IPv6/IPv4 network address +and protocol translation): see the .Sx IPv6/IPv4 NETWORK ADDRESS AND PROTOCOL TRANSLATION Section for further information. .It Cm nptv6 Ar name Pass packet to a NPTv6 instance (for IPv6-to-IPv6 network prefix translation): see the -.Sx IPv6-to-IPv6 NETWORK PREFIX TRANSLATION (NPTv6) +.Sx IPv6-to-IPv6 NETWORK PREFIX TRANSLATION (NPTv6)\& Section for further information. .It Cm pipe Ar pipe_nr Pass packet to a @@ -1286,7 +1331,7 @@ protocol options, incoming or outgoing interfaces, etc.) that the packet must match in order to be recognised. In general, the patterns are connected by (implicit) .Cm and -operators -- i.e., all must match in order for the +operators \(em i.e., all must match in order for the rule to match. Individual patterns can be prefixed by the .Cm not @@ -1409,7 +1454,7 @@ See the .Sx LOOKUP TABLES section below for more information on lookup tables. .El -.It Ar addr-list : ip-addr Ns Op Ns , Ns Ar addr-list +.It Ar addr-list : ip-addr Ns Op , Ns Ar addr-list .It Ar ip-addr : A host or subnet address specified in one of the following ways: .Bl -tag -width indent @@ -1425,7 +1470,7 @@ and mask width of bits. As an example, 1.2.3.4/25 or 1.2.3.0/25 will match all IP numbers from 1.2.3.0 to 1.2.3.127 . -.It Ar addr Ns : Ns Ar mask +.It Ar addr : Ns Ar mask Matches all addresses with base .Ar addr (specified as an IP address, a network number, or a hostname) @@ -1442,7 +1487,7 @@ format for contiguous masks, which is more compact and less error-prone. .El .It Ar addr-set : addr Ns Oo Ns / Ns Ar masklen Oc Ns Cm { Ns Ar list Ns Cm } -.It Ar list : Bro Ar num | num-num Brc Ns Op Ns , Ns Ar list +.It Ar list : Bro Ar num | num-num Brc Ns Op , Ns Ar list Matches all addresses with base address .Ar addr (specified as an IP address, a network number, or a hostname) @@ -1469,7 +1514,7 @@ or 1.2.3.0/24{128,35-55,89} will match the following IP addresses: .br 1.2.3.128, 1.2.3.35 to 1.2.3.55, 1.2.3.89 . -.It Ar addr6-list : ip6-addr Ns Op Ns , Ns Ar addr6-list +.It Ar addr6-list : ip6-addr Ns Op , Ns Ar addr6-list .It Ar ip6-addr : A host or subnet specified one of the following ways: .Bl -tag -width indent @@ -3075,10 +3120,10 @@ This has the associated probabilities .Po Ar K and .Ar H Pc -for the loss probability. This is different from the literature, -where this model is described with probabilities of successful -transmission k and h. However, converting from literature is -easy: +for the loss probability. +This is different from the literature, where this model is described with +probabilities of successful transmission k and h. +However, converting from literature is easy: .Pp K = 1 - k ; H = 1 - h .Pp @@ -3204,8 +3249,8 @@ delay low. At regular time intervals of .Cm tupdate .Ar time -(15ms by default) a background process (re)calculates the probability based on queue delay -deviations from +(15ms by default) a background process (re)calculates the probability based on +queue delay deviations from .Cm target .Ar time (15ms by default) and queue delay trends. @@ -3224,8 +3269,8 @@ delay deviations that is used in drop probability calculation. 0.125 is the default. .It Cm beta Ar n .Ar n -is a floating point number between 0 and 7 which specifies is the weight of queue -delay trend that is used in drop probability calculation. +is a floating point number between 0 and 7 which specifies is the weight of +queue delay trend that is used in drop probability calculation. 1.25 is the default. .It Cm max_burst Ar time The maximum period of time that PIE does not drop/mark packets. @@ -3584,7 +3629,8 @@ The NAT64 instance will determine a destination IPv4 address from prefix .It Cm states_chunks Ar number The number of states chunks in single ports group. Each ports group by default can keep 64 state entries in single chunk. -The above value affects the maximum number of states that can be associated with single IPv4 alias address and port. +The above value affects the maximum number of states that can be associated with +a single IPv4 alias address and port. The value must be power of 2, and up to 128. .It Cm host_del_age Ar seconds The number of seconds until the host entry for a IPv6 client will be deleted @@ -4288,6 +4334,15 @@ sub-options: Lists all interface which are currently tracked by .Nm with their in-kernel status. +.It Cm monitor Op Ar filter-comment +Capture messages from +.Xr route 4 +socket, that were logged using rules with +.Cm log Cm logdst Ar rtsock +opcode. Optional +.Ar filter-comment +can be specified to show only those messages, that were logged +by rules with specific rule comment. .It Cm talist List all table lookup algorithms currently available. .El @@ -4460,7 +4515,7 @@ and .Cm defer-action can be used to precisely control creation and checking of dynamic rules. Example of usage of these options are provided in -.Sx NETWORK ADDRESS TRANSLATION (NAT) +.Sx NETWORK ADDRESS TRANSLATION (NAT)\& Section. .Pp To limit the number of connections a user can open @@ -4949,7 +5004,7 @@ The syntax has grown over the years and sometimes it might be confusing. Unfortunately, backward compatibility prevents cleaning up mistakes made in the definition of the syntax. .Pp -.Em !!! WARNING !!! +.Em !!! WARNING !!!\& .Pp Misconfiguring the firewall can put your computer in an unusable state, possibly shutting down network services and requiring console access to diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index be796808380f..2addc0295f0f 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -47,6 +47,8 @@ #include <net/ethernet.h> #include <net/if.h> /* only IFNAMSIZ */ +#include <net/if_dl.h> +#include <net/route.h> #include <netinet/in.h> #include <netinet/in_systm.h> /* only n_short, n_long */ #include <netinet/ip.h> @@ -2009,7 +2011,7 @@ print_logdst(struct buf_pr *bp, uint16_t arg1) { char const *comma = ""; - bprintf(bp, " logdst ", arg1); + bprintf(bp, " logdst "); if (arg1 & IPFW_LOG_SYSLOG) { bprintf(bp, "%ssyslog", comma); comma = ","; @@ -2180,7 +2182,7 @@ print_action_instruction(struct buf_pr *bp, const struct format_opts *fo, bprintf(bp, " %u", cmd->arg1); else bprintf(bp, " %ubytes", - cmd->len * sizeof(uint32_t)); + (unsigned)(cmd->len * sizeof(uint32_t))); break; case O_SETDSCP: if (cmd->arg1 == IP_FW_TARG) { @@ -5956,6 +5958,7 @@ static struct _s_x intcmds[] = { { "iflist", TOK_IFLIST }, { "olist", TOK_OLIST }, { "vlist", TOK_VLIST }, + { "monitor", TOK_MONITOR }, { NULL, 0 } }; @@ -6030,6 +6033,134 @@ ipfw_list_objects(int ac __unused, char *av[] __unused) free(olh); } +static void +bprint_sa(struct buf_pr *bp, const struct sockaddr *sa) +{ + struct sockaddr_storage ss; + char buf[INET6_ADDRSTRLEN]; + + memset(&ss, 0, sizeof(ss)); + if (sa->sa_len == 0) + ss.ss_family = sa->sa_family; + else + memcpy(&ss, sa, sa->sa_len); + + /* set ss_len in case it was shortened */ + switch (sa->sa_family) { + case AF_INET: + ss.ss_len = sizeof(struct sockaddr_in); + break; + default: + ss.ss_len = sizeof(struct sockaddr_in6); + } + if (getnameinfo((const struct sockaddr *)&ss, ss.ss_len, + buf, sizeof(buf), NULL, 0, NI_NUMERICHOST) != 0) { + bprintf(bp, "bad-addr"); + return; + } + bprintf(bp, "%s", buf); +} + +static void +ipfw_rtsock_monitor(const char *filter) +{ + char msg[2048], buf[32]; + struct timespec tp; + struct tm tm; + struct buf_pr bp; + struct rt_msghdr *hdr; + struct sockaddr *sa; + struct sockaddr_dl *sdl; + ipfwlog_rtsock_hdr_v2 *loghdr; + ssize_t nread; + size_t msglen; + int rtsock; + + rtsock = socket(PF_ROUTE, SOCK_RAW, AF_IPFWLOG); + if (rtsock < 0) + err(EX_UNAVAILABLE, "socket(AF_IPFWLOG)"); + bp_alloc(&bp, 4096); + for (;;) { + nread = read(rtsock, msg, sizeof(msg)); + if (nread < 0) { + warn("read()"); + continue; + } + msglen = nread; + if (msglen < sizeof(*hdr)) + continue; + + hdr = (struct rt_msghdr *)msg; + if (hdr->rtm_version != RTM_VERSION || + hdr->rtm_type != RTM_IPFWLOG || + (hdr->rtm_addrs & (1 << RTAX_DST)) == 0 || + (hdr->rtm_addrs & (1 << RTAX_GATEWAY)) == 0 || + (hdr->rtm_addrs & (1 << RTAX_NETMASK)) == 0) + continue; + + msglen -= sizeof(*hdr); + sdl = (struct sockaddr_dl *)(hdr + 1); + if (msglen < sizeof(*sdl) || msglen < SA_SIZE(sdl) || + sdl->sdl_family != AF_IPFWLOG || + sdl->sdl_type != 2 /* version */ || + sdl->sdl_alen != sizeof(*loghdr)) + continue; + + msglen -= SA_SIZE(sdl); + loghdr = (ipfwlog_rtsock_hdr_v2 *)sdl->sdl_data; + /* filter by rule comment. MAX_COMMENT_LEN = 80 */ + if (filter != NULL && + strncmp(filter, loghdr->comment, 80) != 0) + continue; + + sa = (struct sockaddr *)((char *)sdl + SA_SIZE(sdl)); + if (msglen < SA_SIZE(sa)) + continue; + + msglen -= SA_SIZE(sa); + bp_flush(&bp); + + clock_gettime(CLOCK_REALTIME, &tp); + localtime_r(&tp.tv_sec, &tm); + strftime(buf, sizeof(buf), "%T", &tm); + bprintf(&bp, "%s.%03ld AF %s", buf, tp.tv_nsec / 1000000, + sa->sa_family == AF_INET ? "IPv4" : "IPv6"); + + bprintf(&bp, " %s >", + ether_ntoa((const struct ether_addr *)loghdr->ether_shost)); + bprintf(&bp, " %s, ", + ether_ntoa((const struct ether_addr *)loghdr->ether_dhost)); + bprint_sa(&bp, sa); + + sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa)); + if (msglen < SA_SIZE(sa)) + continue; + + msglen -= SA_SIZE(sa); + bprintf(&bp, " > "); + bprint_sa(&bp, sa); + bprintf(&bp, ", set %u, rulenum %u, targ 0x%08x, " + "%scmd[op %d, len %d, arg1 0x%04x], mark 0x%08x", + sdl->sdl_index, loghdr->rulenum, loghdr->tablearg, + (loghdr->cmd.len & F_NOT) ? "!": "", + loghdr->cmd.opcode, F_LEN(&loghdr->cmd), + loghdr->cmd.arg1, loghdr->mark); + + sa = (struct sockaddr *)((char *)sa + SA_SIZE(sa)); + if ((hdr->rtm_addrs & (1 << RTAX_GENMASK)) != 0 && + msglen >= SA_SIZE(sa)) { + msglen -= SA_SIZE(sa); + bprintf(&bp, ", nh "); + bprint_sa(&bp, sa); + } + if (sdl->sdl_nlen > 0) + bprintf(&bp, " // %s", loghdr->comment); + printf("%s\n", bp.buf); + } + bp_free(&bp); + close(rtsock); +} + void ipfw_internal_handler(int ac, char *av[]) { @@ -6054,6 +6185,10 @@ ipfw_internal_handler(int ac, char *av[]) case TOK_VLIST: ipfw_list_values(ac, av); break; + case TOK_MONITOR: + av++; + ipfw_rtsock_monitor(*av); + break; } } diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h index 788e9fe365f3..f19ea59c1bb4 100644 --- a/sbin/ipfw/ipfw2.h +++ b/sbin/ipfw/ipfw2.h @@ -275,6 +275,7 @@ enum tokens { TOK_UNLOCK, TOK_VLIST, TOK_OLIST, + TOK_MONITOR, TOK_MISSING, TOK_ORFLUSH, @@ -347,7 +348,7 @@ struct buf_pr { int pr_u64(struct buf_pr *bp, void *pd, int width); int bp_alloc(struct buf_pr *b, size_t size); void bp_free(struct buf_pr *b); -int bprintf(struct buf_pr *b, const char *format, ...); +int bprintf(struct buf_pr *b, const char *format, ...) __printflike(2, 3); /* memory allocation support */ 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/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/mksnap_ffs/Makefile b/sbin/mksnap_ffs/Makefile index 591fd6adcc22..cf5079fdfa8e 100644 --- a/sbin/mksnap_ffs/Makefile +++ b/sbin/mksnap_ffs/Makefile @@ -2,11 +2,10 @@ PACKAGE=ufs PROG= mksnap_ffs -SRCS= mksnap_ffs.c getmntopts.c +LIBADD= util MAN= mksnap_ffs.8 WARNS?= 2 -CFLAGS+=-I${.CURDIR:H}/mount .if defined(NOSUID) BINMODE=554 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/Makefile b/sbin/mount/Makefile index 9b5c9b9e593d..e2a3098813c4 100644 --- a/sbin/mount/Makefile +++ b/sbin/mount/Makefile @@ -1,15 +1,7 @@ PACKAGE=runtime PROG= mount -SRCS= mount.c mount_fs.c getmntopts.c vfslist.c -MAN= mntopts.3 mount.8 -MLINKS+= mntopts.3 getmntopts.3 -MLINKS+= mntopts.3 getmntpoint.3 -MLINKS+= mntopts.3 chkdoreload.3 -MLINKS+= mntopts.3 build_iovec.3 -MLINKS+= mntopts.3 build_iovec_argf.3 -MLINKS+= mntopts.3 free_iovec.3 -MLINKS+= mntopts.3 checkpath.3 -MLINKS+= mntopts.3 rmslashes.3 +SRCS= mount.c mount_fs.c vfslist.c +MAN= mount.8 LIBADD= util xo diff --git a/sbin/mount/getmntopts.c b/sbin/mount/getmntopts.c deleted file mode 100644 index 0912b1e1a306..000000000000 --- a/sbin/mount/getmntopts.c +++ /dev/null @@ -1,301 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * Copyright (c) 1994 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/param.h> -#include <sys/mount.h> -#include <sys/stat.h> -#include <sys/uio.h> - -#include <err.h> -#include <errno.h> -#include <paths.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "mntopts.h" - -int getmnt_silent = 0; - -void -getmntopts(const char *options, const struct mntopt *m0, int *flagp, - int *altflagp) -{ - const struct mntopt *m; - int negative, len; - char *opt, *optbuf, *p; - int *thisflagp; - - /* Copy option string, since it is about to be torn asunder... */ - if ((optbuf = strdup(options)) == NULL) - err(1, NULL); - - for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) { - /* Check for "no" prefix. */ - if (opt[0] == 'n' && opt[1] == 'o') { - negative = 1; - opt += 2; - } else - negative = 0; - - /* - * for options with assignments in them (ie. quotas) - * ignore the assignment as it's handled elsewhere - */ - p = strchr(opt, '='); - if (p != NULL) - *++p = '\0'; - - /* Scan option table. */ - for (m = m0; m->m_option != NULL; ++m) { - len = strlen(m->m_option); - if (strncasecmp(opt, m->m_option, len) == 0) - if (opt[len] == '\0' || opt[len] == '=') - break; - } - - /* Save flag, or fail if option is not recognized. */ - if (m->m_option) { - thisflagp = m->m_altloc ? altflagp : flagp; - if (negative == m->m_inverse) - *thisflagp |= m->m_flag; - else - *thisflagp &= ~m->m_flag; - } else if (!getmnt_silent) { - errx(1, "-o %s: option not supported", opt); - } - } - - free(optbuf); -} - -void -rmslashes(char *rrpin, char *rrpout) -{ - char *rrpoutstart; - - *rrpout = *rrpin; - for (rrpoutstart = rrpout; *rrpin != '\0'; *rrpout++ = *rrpin++) { - - /* skip all double slashes */ - while (*rrpin == '/' && *(rrpin + 1) == '/') - rrpin++; - } - - /* remove trailing slash if necessary */ - if (rrpout - rrpoutstart > 1 && *(rrpout - 1) == '/') - *(rrpout - 1) = '\0'; - else - *rrpout = '\0'; -} - -int -checkpath(const char *path, char *resolved) -{ - struct stat sb; - - if (realpath(path, resolved) == NULL || stat(resolved, &sb) != 0) - return (1); - if (!S_ISDIR(sb.st_mode)) { - errno = ENOTDIR; - return (1); - } - return (0); -} - -int -checkpath_allow_file(const char *path, char *resolved) -{ - struct stat sb; - - if (realpath(path, resolved) == NULL || stat(resolved, &sb) != 0) - return (1); - if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode)) { - errno = ENOTDIR; - return (1); - } - return (0); -} - -/* - * Get the mount point information for name. Name may be mount point name - * or device name (with or without /dev/ preprended). - */ -struct statfs * -getmntpoint(const char *name) -{ - struct stat devstat, mntdevstat; - char device[sizeof(_PATH_DEV) - 1 + MNAMELEN]; - char *ddevname; - struct statfs *mntbuf, *statfsp; - int i, mntsize, isdev; - u_long len; - - if (stat(name, &devstat) != 0) - return (NULL); - if (S_ISCHR(devstat.st_mode) || S_ISBLK(devstat.st_mode)) - isdev = 1; - else - isdev = 0; - mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); - for (i = 0; i < mntsize; i++) { - statfsp = &mntbuf[i]; - if (isdev == 0) { - if (strcmp(name, statfsp->f_mntonname)) - continue; - return (statfsp); - } - ddevname = statfsp->f_mntfromname; - if (*ddevname != '/') { - if ((len = strlen(_PATH_DEV) + strlen(ddevname) + 1) > - sizeof(statfsp->f_mntfromname) || - len > sizeof(device)) - continue; - strncpy(device, _PATH_DEV, len); - strncat(device, ddevname, len); - if (stat(device, &mntdevstat) == 0) - strncpy(statfsp->f_mntfromname, device, len); - } - if (stat(ddevname, &mntdevstat) == 0 && - mntdevstat.st_rdev == devstat.st_rdev) - return (statfsp); - } - return (NULL); -} - -/* - * If possible reload a mounted filesystem. - * When prtmsg != NULL print a warning if a reload is attempted, but fails. - * Return 0 on success, 1 on failure. - */ -int -chkdoreload(struct statfs *mntp, - void (*prtmsg)(const char *, ...) __printflike(1,2)) -{ - struct iovec *iov; - int iovlen, error; - char errmsg[255]; - - /* - * If the filesystem is not mounted it does not need to be reloaded. - * If it is mounted for writing, then it could not have been opened - * for writing by a utility, so does not need to be reloaded. - */ - if (mntp == NULL || (mntp->f_flags & MNT_RDONLY) == 0) - return (0); - - /* - * We modified a mounted file system. Do a mount update on - * it so we can continue using it as safely as possible. - */ - iov = NULL; - iovlen = 0; - errmsg[0] = '\0'; - build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "ffs"), 4); - build_iovec(&iov, &iovlen, "from", mntp->f_mntfromname, (size_t)-1); - build_iovec(&iov, &iovlen, "fspath", mntp->f_mntonname, (size_t)-1); - build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg)); - build_iovec(&iov, &iovlen, "update", NULL, 0); - build_iovec(&iov, &iovlen, "reload", NULL, 0); - /* - * XX: We need the following line until we clean up - * nmount parsing of root mounts and NFS root mounts. - */ - build_iovec(&iov, &iovlen, "ro", NULL, 0); - error = nmount(iov, iovlen, mntp->f_flags); - free_iovec(&iov, &iovlen); - if (error == 0) - return (0); - if (prtmsg != NULL) - prtmsg("mount reload of '%s' failed: %s %s\n\n", - mntp->f_mntonname, strerror(errno), errmsg); - return (1); -} - -void -build_iovec(struct iovec **iov, int *iovlen, const char *name, void *val, - size_t len) -{ - int i; - - if (*iovlen < 0) - return; - i = *iovlen; - *iov = realloc(*iov, sizeof **iov * (i + 2)); - if (*iov == NULL) { - *iovlen = -1; - return; - } - (*iov)[i].iov_base = strdup(name); - (*iov)[i].iov_len = strlen(name) + 1; - i++; - (*iov)[i].iov_base = val; - if (len == (size_t)-1) { - if (val != NULL) - len = strlen(val) + 1; - else - len = 0; - } - (*iov)[i].iov_len = (int)len; - *iovlen = ++i; -} - -/* - * This function is needed for compatibility with parameters - * which used to use the mount_argf() command for the old mount() syscall. - */ -void -build_iovec_argf(struct iovec **iov, int *iovlen, const char *name, - const char *fmt, ...) -{ - va_list ap; - char val[255] = { 0 }; - - va_start(ap, fmt); - vsnprintf(val, sizeof(val), fmt, ap); - va_end(ap); - build_iovec(iov, iovlen, name, strdup(val), (size_t)-1); -} - -/* - * Free the iovec and reset to NULL with zero length. Useful for calling - * nmount in a loop. - */ -void -free_iovec(struct iovec **iov, int *iovlen) -{ - int i; - - for (i = 0; i < *iovlen; i += 2) - free((*iov)[i].iov_base); - free(*iov); -} diff --git a/sbin/mount/mntopts.3 b/sbin/mount/mntopts.3 deleted file mode 100644 index 74fca612479f..000000000000 --- a/sbin/mount/mntopts.3 +++ /dev/null @@ -1,379 +0,0 @@ -.\" Copyright (c) 2023 Marshall Kirk McKusick -.\" Copyright (c) 1994 The Regents of the University of California. -.\" -.\" 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 AUTHORS 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 AUTHORS 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. -.\" -.Dd January 19, 2023 -.Dt MNTOPTS 3 -.Os -.Sh NAME -.Nm getmntopts , -.Nm getmntpoint , -.Nm chkdoreload , -.Nm build_iovec , -.Nm build_iovec_argf , -.Nm free_iovec , -.Nm checkpath , -.Nm rmslashes -.Nd "mount point operations" -.Sh SYNOPSIS -.In mntopts.h -.Ft void -.Fo getmntopts -.Fa "const char *options" "const struct mntopt *mopts" -.Fa "int *flagp" "int *altflagp" -.Fc -.Ft struct statfs * -.Fn getmntpoint "const char *name" -.Ft int -.Fo chkdoreload -.Fa "struct statfs *mntp" -.Fa "void (*prtmsg)(const char *fmt, ...)" -.Fc -.Ft void -.Fo build_iovec -.Fa "struct iovec **iov" "int *iovlen" "const char *name" "void *val" -.Fa "size_t len" -.Fc -.Ft void -.Fo build_iovec_argf -.Fa "struct iovec **iov" "int *iovlen" "const char *name" -.Fa "const char *fmt" "..." -.Fc -.Ft void -.Fn free_iovec "struct iovec **iov" "int *iovlen" -.Ft int -.Fn checkpath "const char *path" "char *resolved" -.Ft void -.Fn rmslashes "char *rrpin" "char *rrpout" -.Sh DESCRIPTION -The -.Nm mntopts -functions support operations associated with a mount point. -For historic reasons are in a file in the sources for the -.Xr mount 8 -program. -Thus, to access them the following lines need to be added to the -.Nm Makefile -of the program wanting to use them: -.Bd -literal -SRCS+= getmntopts.c -MOUNT= ${SRCTOP}/sbin/mount -CFLAGS+= -I${MOUNT} -\&.PATH: ${MOUNT} -.Ed -.Pp -The -.Fn getmntopts -function takes a comma separated option list and a list -of valid option names, and computes the bitmask -corresponding to the requested set of options. -.Pp -The string -.Fa options -is broken down into a sequence of comma separated tokens. -Each token is looked up in the table described by -.Fa mopts -and the bits in -the word referenced by either -.Fa flagp -or -.Fa altflagp -(depending on the -.Va m_altloc -field of the option's table entry) -are updated. -The flag words are not initialized by -.Fn getmntopts . -The table, -.Fa mopts , -has the following format: -.Bd -literal -struct mntopt { - char *m_option; /* option name */ - int m_inverse; /* is this a negative option, e.g., "dev" */ - int m_flag; /* bit to set, e.g., MNT_RDONLY */ - int m_altloc; /* non-zero to use altflagp rather than flagp */ -}; -.Ed -.Pp -The members of this structure are: -.Bl -tag -width m_inverse -.It Va m_option -the option name, -for example -.Dq Li suid . -.It Va m_inverse -tells -.Fn getmntopts -that the name has the inverse meaning of the -bit. -For example, -.Dq Li suid -is the string, whereas the -mount flag is -.Dv MNT_NOSUID . -In this case, the sense of the string and the flag -are inverted, so the -.Va m_inverse -flag should be set. -.It Va m_flag -the value of the bit to be set or cleared in -the flag word when the option is recognized. -The bit is set when the option is discovered, -but cleared if the option name was preceded -by the letters -.Dq Li no . -The -.Va m_inverse -flag causes these two operations to be reversed. -.It Va m_altloc -the bit should be set or cleared in -.Fa altflagp -rather than -.Fa flagp . -.El -.Pp -Each of the user visible -.Dv MNT_ -flags has a corresponding -.Dv MOPT_ -macro which defines an appropriate -.Vt "struct mntopt" -entry. -To simplify the program interface and ensure consistency across all -programs, a general purpose macro, -.Dv MOPT_STDOPTS , -is defined which -contains an entry for all the generic VFS options. -In addition, the macros -.Dv MOPT_FORCE -and -.Dv MOPT_UPDATE -exist to enable the -.Dv MNT_FORCE -and -.Dv MNT_UPDATE -flags to be set. -Finally, the table must be terminated by an entry with a -.Dv NULL -first element. -.Pp -The -.Fn getmntpoint -function takes the pathname of a possible mount point -or of a device (with or without -.Pa /dev/ -prepended to it). -If the pathname is a directory or a file, -.Fn getmntpoint -checks to see if the mount point currently has a filesystem -mounted on it. -If the pathname is a device, -.Fn getmntpoint -checks to see if it is currently mounted. -If there is an associated mount, a pointer to a -.Vt "struct statfs" -is returned. -The returned result is stored in a static buffer that is over-written -each time the -.Fn getmntpoint -function or the -.Xr getmntinfo 3 -library routine is called. -If no mount is found, NULL is returned. -.Pp -The -.Fn chkdoreload -function takes a pointer to a -.Vt "struct statfs" . -If the filesystem associated with the mount point is mounted read-only, -.Fn chkdoreload -requests the filesystem to reload all of its metadata from its backing store. -The second parameter is the function to call to print an error message -if the reload fails. -If no error message is desired, a -.Dv NULL -can be passed as the second argument. -The -.Fn chkdoreload -function returns zero on success or non-zero on failure. -.Pp -The -.Fn build_iovec -function adds a parameter to a list of parameters to be passed to the -.Xr nmount 2 -system call. -The parameter list is built up in -.Va iov -and its length is kept in -.Va iovlen . -Before the first call to -.Fn build_iovec , -.Va iov -should be set to -.Dv NULL -and -.Va iovlen -should be set to 0. -The parameter name is passed in -.Va name . -The value of the parameter name is pointed to by -.Va val . -The size of the value is passed in -.Va len . -If the value is a string, a -.Va len -of -1 is passed to indicate that the length should be determined using -.Xr strlen 3 . -If the parameter has no value, -.Va name -should be -.Dv NULL -and -.Va len -should be 0. -.Pp -The -.Fn build_iovec_argf -function adds a formatted parameter to a list of parameters to be passed -to the -.Xr nmount 2 -system call. -The parameter list is built up in -.Va iov -and its length is kept in -.Va iovlen . -Before the first call to -.Fn build_iovec_argf , -.Va iov -should be set to -.Dv NULL -and -.Va iovlen -should be set to 0. -The parameter name is passed in -.Va name . -The value of the parameter name is described by a format string pointed to by -.Va fmt . -If the parameter has no value, -.Va name -should be -.Dv NULL . -.Pp -The -.Fn free_iovec -function frees the memory in the -.Va iov -vector of the length specified in -.Va iovlen -that was previously allocated by the -.Fn build_iovec -and / or -.Fn build_iovec_argf -functions. -The -.Va iov -is set to -.Dv NULL -and the -.Va iovlen -is set to 0 to indicate that the space has been freed. -.Pp -The -.Fn checkpath -function uses -.Xr realpath 3 -to verify that its -.Va path -argument is valid and references a directory. -The -.Fn checkpath -function returns zero on success or non-zero on failure. -.Pp -The -.Fn rmslashes -function removes all double slashes and trailing slashes from its -.Va rrpin -pathname parameter and returns the resulting pathname in its -.Va rrpout -parameter. -.Sh EXAMPLES -Most commands will use the standard option set. -Local file systems which support the -.Dv MNT_UPDATE -flag, would also have an -.Dv MOPT_UPDATE -entry. -This can be declared and used as follows: -.Bd -literal -#include "mntopts.h" - -struct mntopt mopts[] = { - MOPT_STDOPTS, - MOPT_UPDATE, - { NULL } -}; - - ... - mntflags = mntaltflags = 0; - ... - getmntopts(options, mopts, &mntflags, &mntaltflags); - ... -.Ed -.Sh DIAGNOSTICS -If the external integer variable -.Va getmnt_silent -is zero, then the -.Fn getmntopts -function displays an error message and exits if an -unrecognized option is encountered. -Otherwise unrecognized options are silently ignored. -By default -.Va getmnt_silent -is zero. -.Sh SEE ALSO -.Xr err 3 , -.Xr mount 8 , -.Xr nmount 8 -.Sh HISTORY -The -.Fn getmntopts -function appeared in -.Bx 4.4 . -The -.Fn build_iovec , -.Fn build_iovec_argf , -.Fn free_iovec , -.Fn checkpath , -and -.Fn rmslashes -functions were added with -.Xr nmount 8 -in -.Fx 5.0 . -The -.Fn getmntpoint -and -.Fn chkdoreload -functions were added in -.Fx 13.2 . diff --git a/sbin/mount/mntopts.h b/sbin/mount/mntopts.h deleted file mode 100644 index dc1452097ed8..000000000000 --- a/sbin/mount/mntopts.h +++ /dev/null @@ -1,109 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * Copyright (c) 1994 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -struct mntopt { - const char *m_option; /* option name */ - int m_inverse; /* if a negative option, e.g. "atime" */ - long long m_flag; /* bit to set, e.g. MNT_RDONLY */ - int m_altloc; /* 1 => set bit in altflags */ -}; - -/* User-visible MNT_ flags. */ -#define MOPT_ASYNC { "async", 0, MNT_ASYNC, 0 } -#define MOPT_NOATIME { "atime", 1, MNT_NOATIME, 0 } -#define MOPT_NOEXEC { "exec", 1, MNT_NOEXEC, 0 } -#define MOPT_NOSUID { "suid", 1, MNT_NOSUID, 0 } -#define MOPT_NOSYMFOLLOW { "symfollow", 1, MNT_NOSYMFOLLOW, 0 } -#define MOPT_RDONLY { "rdonly", 0, MNT_RDONLY, 0 } -#define MOPT_SYNC { "sync", 0, MNT_SYNCHRONOUS, 0 } -#define MOPT_UNION { "union", 0, MNT_UNION, 0 } -#define MOPT_USERQUOTA { "userquota", 0, 0, 0 } -#define MOPT_GROUPQUOTA { "groupquota", 0, 0, 0 } -#define MOPT_NOCLUSTERR { "clusterr", 1, MNT_NOCLUSTERR, 0 } -#define MOPT_NOCLUSTERW { "clusterw", 1, MNT_NOCLUSTERW, 0 } -#define MOPT_SUIDDIR { "suiddir", 0, MNT_SUIDDIR, 0 } -#define MOPT_SNAPSHOT { "snapshot", 0, MNT_SNAPSHOT, 0 } -#define MOPT_MULTILABEL { "multilabel", 0, MNT_MULTILABEL, 0 } -#define MOPT_ACLS { "acls", 0, MNT_ACLS, 0 } -#define MOPT_NFS4ACLS { "nfsv4acls", 0, MNT_NFS4ACLS, 0 } -#define MOPT_AUTOMOUNTED { "automounted",0, MNT_AUTOMOUNTED, 0 } -#define MOPT_UNTRUSTED { "untrusted", 0, MNT_UNTRUSTED, 0 } - -/* Control flags. */ -#define MOPT_FORCE { "force", 0, MNT_FORCE, 0 } -#define MOPT_UPDATE { "update", 0, MNT_UPDATE, 0 } -#define MOPT_RO { "ro", 0, MNT_RDONLY, 0 } -#define MOPT_RW { "rw", 1, MNT_RDONLY, 0 } -#define MOPT_NOCOVER { "cover", 1, MNT_NOCOVER, 0 } -#define MOPT_EMPTYDIR { "emptydir", 0, MNT_EMPTYDIR, 0 } -/* This is parsed by mount(8), but is ignored by specific mount_*(8)s. */ -#define MOPT_AUTO { "auto", 0, 0, 0 } - -/* A handy macro as terminator of MNT_ array. */ -#define MOPT_END { NULL, 0, 0, 0 } - -#define MOPT_FSTAB_COMPAT \ - MOPT_RO, \ - MOPT_RW, \ - MOPT_AUTO - -/* Standard options which all mounts can understand. */ -#define MOPT_STDOPTS \ - MOPT_USERQUOTA, \ - MOPT_GROUPQUOTA, \ - MOPT_FSTAB_COMPAT, \ - MOPT_NOATIME, \ - MOPT_NOEXEC, \ - MOPT_SUIDDIR, /* must be before MOPT_NOSUID */ \ - MOPT_NOSUID, \ - MOPT_NOSYMFOLLOW, \ - MOPT_RDONLY, \ - MOPT_UNION, \ - MOPT_NOCLUSTERR, \ - MOPT_NOCLUSTERW, \ - MOPT_MULTILABEL, \ - MOPT_ACLS, \ - MOPT_NFS4ACLS, \ - MOPT_AUTOMOUNTED, \ - MOPT_UNTRUSTED, \ - MOPT_NOCOVER, \ - MOPT_EMPTYDIR - -void getmntopts(const char *, const struct mntopt *, int *, int *); -void rmslashes(char *, char *); -int checkpath(const char *, char resolved_path[]); -int checkpath_allow_file(const char *, char resolved_path[]); -struct statfs *getmntpoint(const char *); -int chkdoreload(struct statfs *, void (*)(const char *, ...) __printflike(1,2)); -extern int getmnt_silent; -void build_iovec(struct iovec **iov, int *iovlen, const char *name, void *val, size_t len); -void build_iovec_argf(struct iovec **iov, int *iovlen, const char *name, const char *fmt, ...); -void free_iovec(struct iovec **iovec, int *iovlen); diff --git a/sbin/mount/mount.8 b/sbin/mount/mount.8 index 7bb4eefe03ff..7bfc21ea41d5 100644 --- a/sbin/mount/mount.8 +++ b/sbin/mount/mount.8 @@ -1,3 +1,6 @@ +.\" +.\" SPDX-License-Identifier: BSD-3-Clause +.\" .\" Copyright (c) 1980, 1989, 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" @@ -25,7 +28,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd November 19, 2024 +.Dd July 16, 2025 .Dt MOUNT 8 .Os .Sh NAME @@ -37,7 +40,7 @@ .Op Fl adflpruvw .Op Fl F Ar fstab .Op Fl o Ar options -.Op Fl t Oo Cm no Oc Ns Cm Ar type Ns Op Cm , Ns Ar type ... +.Op Fl t Oo Cm no Oc Ns Ar type Ns Op , Ns Ar type ... .Nm .Op Fl -libxo .Op Fl dfpruvw @@ -46,7 +49,7 @@ .Op Fl -libxo .Op Fl dfpruvw .Op Fl o Ar options -.Op Fl t Oo Cm no Oc Ns Cm Ar type Ns Op Cm , Ns Ar type ... +.Op Fl t Oo Cm no Oc Ns Ar type Ns Op , Ns Ar type ... .Ar special node .Sh DESCRIPTION The @@ -77,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 @@ -270,9 +273,11 @@ It is set automatically when the user does not have super-user privileges. Do not follow symlinks on the mounted file system. .It Cm ro -The same as -.Fl r ; -mount the file system read-only (even the super-user may not write it). +Mount the filesystem read-only, even the super-user may not write it. +Equivalent to +.Fl r . +.It Cm rw +Mount the filesystem read-write. .It Cm snapshot Take a snapshot of the specified filesystem. When this option is used, all other options are ignored. @@ -432,7 +437,7 @@ The same as the argument to the .Fl o option. -.It Fl t Oo Cm no Oc Ns Cm Ar type Ns Op Cm , Ns Ar type ... +.It Fl t Oo Cm no Oc Ns Ar type Ns Op , Ns Ar type ... The argument following the .Fl t is used to indicate the file system type. @@ -547,6 +552,10 @@ for more information.) .It Pa /etc/fstab file system table .El +.Sh EXAMPLES +Remount the root filesystem with read-write permissions: +.Pp +.Dl mount -uw / .Sh DIAGNOSTICS Various, most of them are self-explanatory. .Pp @@ -564,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/mount.c b/sbin/mount/mount.c index cf603a63e394..496d71b57e4c 100644 --- a/sbin/mount/mount.c +++ b/sbin/mount/mount.c @@ -1,4 +1,4 @@ -/*- +/* * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1989, 1993, 1994 @@ -39,6 +39,7 @@ #include <errno.h> #include <fstab.h> +#include <mntopts.h> #include <paths.h> #include <pwd.h> #include <signal.h> @@ -51,7 +52,6 @@ #include <libxo/xo.h> #include "extern.h" -#include "mntopts.h" #include "pathnames.h" #define EXIT(a) { \ @@ -882,9 +882,9 @@ usage(void) { xo_error("%s\n%s\n%s\n", -"usage: mount [-adflpruvw] [-F fstab] [-o options] [-t ufs | external_type]", +"usage: mount [-adflpruvw] [-F fstab] [-o options] [-t [no]type[,type ...]]", " mount [-dfpruvw] special | node", -" mount [-dfpruvw] [-o options] [-t ufs | external_type] special node"); +" mount [-dfpruvw] [-o options] [-t [no]type[,type ...]] special node"); EXIT(EXIT_FAILURE); } diff --git a/sbin/mount/mount_fs.c b/sbin/mount/mount_fs.c index 6035857578ae..30c34ae32f31 100644 --- a/sbin/mount/mount_fs.c +++ b/sbin/mount/mount_fs.c @@ -38,13 +38,13 @@ #include <err.h> #include <getopt.h> #include <libgen.h> +#include <mntopts.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "extern.h" -#include "mntopts.h" static struct mntopt mopts[] = { MOPT_STDOPTS, diff --git a/sbin/mount_cd9660/Makefile b/sbin/mount_cd9660/Makefile index 3a8f9cd45cb4..9fa8e7436169 100644 --- a/sbin/mount_cd9660/Makefile +++ b/sbin/mount_cd9660/Makefile @@ -1,16 +1,10 @@ PACKAGE=runtime PROG= mount_cd9660 -SRCS= mount_cd9660.c getmntopts.c MAN= mount_cd9660.8 -LIBADD= kiconv - -MOUNT= ${.CURDIR:H}/mount -CFLAGS+= -I${MOUNT} +LIBADD= kiconv util # Needs to be dynamically linked for optional dlopen() access to # userland libiconv NO_SHARED?= NO -.PATH: ${MOUNT} - .include <bsd.prog.mk> diff --git a/sbin/mount_cd9660/mount_cd9660.c b/sbin/mount_cd9660/mount_cd9660.c index d8d64eb5a656..3eddbefb9a03 100644 --- a/sbin/mount_cd9660/mount_cd9660.c +++ b/sbin/mount_cd9660/mount_cd9660.c @@ -48,6 +48,7 @@ #include <err.h> #include <errno.h> #include <grp.h> +#include <mntopts.h> #include <pwd.h> #include <stdlib.h> #include <stdio.h> @@ -55,8 +56,6 @@ #include <sysexits.h> #include <unistd.h> -#include "mntopts.h" - static struct mntopt mopts[] = { MOPT_STDOPTS, MOPT_UPDATE, diff --git a/sbin/mount_fusefs/Makefile b/sbin/mount_fusefs/Makefile index 0c173bf1b687..a237ba99eb6b 100644 --- a/sbin/mount_fusefs/Makefile +++ b/sbin/mount_fusefs/Makefile @@ -20,12 +20,7 @@ DEBUG_FLAGS+= -DFUSE4BSD_VERSION="\"${F4BVERS}\"" PACKAGE=runtime PROG= mount_fusefs -SRCS= mount_fusefs.c getmntopts.c -MAN8= mount_fusefs.8 - -MOUNT= ${.CURDIR:H}/mount -CFLAGS+= -I${MOUNT} - -.PATH: ${MOUNT} +MAN= mount_fusefs.8 +LIBADD= util .include <bsd.prog.mk> diff --git a/sbin/mount_fusefs/mount_fusefs.c b/sbin/mount_fusefs/mount_fusefs.c index 8b1797747a25..2d65494421ef 100644 --- a/sbin/mount_fusefs/mount_fusefs.c +++ b/sbin/mount_fusefs/mount_fusefs.c @@ -49,11 +49,10 @@ #include <signal.h> #include <getopt.h> #include <limits.h> +#include <mntopts.h> #include <osreldate.h> #include <paths.h> -#include "mntopts.h" - #ifndef FUSE4BSD_VERSION #define FUSE4BSD_VERSION "0.3.9-pre1" #endif diff --git a/sbin/mount_msdosfs/Makefile b/sbin/mount_msdosfs/Makefile index e01755b2fce2..50f07f175001 100644 --- a/sbin/mount_msdosfs/Makefile +++ b/sbin/mount_msdosfs/Makefile @@ -1,19 +1,10 @@ -# -# - PACKAGE=runtime PROG= mount_msdosfs -SRCS= mount_msdosfs.c getmntopts.c MAN= mount_msdosfs.8 -LIBADD= kiconv - -MOUNT= ${.CURDIR:H}/mount -CFLAGS+= -I${MOUNT} +LIBADD= kiconv util # Needs to be dynamically linked for optional dlopen() access to # userland libiconv NO_SHARED?= NO -.PATH: ${MOUNT} - .include <bsd.prog.mk> diff --git a/sbin/mount_msdosfs/mount_msdosfs.8 b/sbin/mount_msdosfs/mount_msdosfs.8 index 3a97089e220c..4ae3e6960122 100644 --- a/sbin/mount_msdosfs/mount_msdosfs.8 +++ b/sbin/mount_msdosfs/mount_msdosfs.8 @@ -33,7 +33,7 @@ .Os .Sh NAME .Nm mount_msdosfs -.Nd mount an MS-DOS file system +.Nd mount an MS-DOS (FAT) file system .Sh SYNOPSIS .Nm .Op Fl 9ls diff --git a/sbin/mount_msdosfs/mount_msdosfs.c b/sbin/mount_msdosfs/mount_msdosfs.c index 9128914bc2b0..36e1c524922d 100644 --- a/sbin/mount_msdosfs/mount_msdosfs.c +++ b/sbin/mount_msdosfs/mount_msdosfs.c @@ -44,6 +44,7 @@ #include <errno.h> #include <grp.h> #include <locale.h> +#include <mntopts.h> #include <pwd.h> #include <stdio.h> /* must be after stdio to declare fparseln */ @@ -53,8 +54,6 @@ #include <sysexits.h> #include <unistd.h> -#include "mntopts.h" - static gid_t a_gid(char *); static uid_t a_uid(char *); static mode_t a_mask(char *); diff --git a/sbin/mount_nfs/Makefile b/sbin/mount_nfs/Makefile index 3e61a9f680e0..fb425106dd81 100644 --- a/sbin/mount_nfs/Makefile +++ b/sbin/mount_nfs/Makefile @@ -1,12 +1,12 @@ PACKAGE=nfs PROG= mount_nfs -SRCS= mount_nfs.c getmntopts.c mounttab.c +SRCS= mount_nfs.c mounttab.c MAN= mount_nfs.8 +LIBADD= util -MOUNT= ${.CURDIR:H}/mount UMNTALL= ${SRCTOP}/usr.sbin/rpc.umntall -CFLAGS+= -DNFS -I${MOUNT} -I${UMNTALL} +CFLAGS+= -DNFS -I${UMNTALL} -.PATH: ${MOUNT} ${UMNTALL} +.PATH: ${UMNTALL} .include <bsd.prog.mk> diff --git a/sbin/mount_nfs/mount_nfs.c b/sbin/mount_nfs/mount_nfs.c index 189bdd70b398..55adb01fc1da 100644 --- a/sbin/mount_nfs/mount_nfs.c +++ b/sbin/mount_nfs/mount_nfs.c @@ -58,6 +58,7 @@ #include <err.h> #include <errno.h> #include <fcntl.h> +#include <mntopts.h> #include <netdb.h> #include <stdbool.h> #include <stdio.h> @@ -67,7 +68,6 @@ #include <sysexits.h> #include <unistd.h> -#include "mntopts.h" #include "mounttab.h" /* Table for af,sotype -> netid conversions. */ @@ -587,6 +587,7 @@ getnfsargs(char **specp, char **hostpp, struct iovec **iov, int *iovlen) char *hostp, *delimp, *errstr, *spec; size_t len; static char nam[MNAMELEN + 1], pname[MAXHOSTNAMELEN + 5]; + bool resolved; spec = *specp; if (*spec == '[' && (delimp = strchr(spec + 1, ']')) != NULL && @@ -643,30 +644,7 @@ getnfsargs(char **specp, char **hostpp, struct iovec **iov, int *iovlen) else if (nfsproto == IPPROTO_UDP) hints.ai_socktype = SOCK_DGRAM; - if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) { - hints.ai_flags = AI_CANONNAME; - if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs)) - != 0) { - if (portspec == NULL) - errx(1, "%s: %s", hostp, gai_strerror(ecode)); - else - errx(1, "%s:%s: %s", hostp, portspec, - gai_strerror(ecode)); - return (0); - } - - /* - * For a Kerberized nfs mount where the "principal" - * argument has not been set, add it here. - */ - if (got_principal == 0 && secflavor != AUTH_SYS && - ai_nfs->ai_canonname != NULL) { - snprintf(pname, sizeof (pname), "nfs@%s", - ai_nfs->ai_canonname); - build_iovec(iov, iovlen, "principal", pname, - strlen(pname) + 1); - } - } + resolved = (getaddrinfo(hostp, portspec, &hints, &ai_nfs) == 0); if ((opflags & (BGRNDNOW | ISBGRND)) == BGRNDNOW) { warnx("Mount %s:%s, backgrounding", @@ -678,6 +656,37 @@ getnfsargs(char **specp, char **hostpp, struct iovec **iov, int *iovlen) ret = TRYRET_LOCALERR; for (;;) { + if (!resolved) { + hints.ai_flags = AI_CANONNAME; + if ((ecode = getaddrinfo(hostp, portspec, &hints, + &ai_nfs)) != 0) { + if (portspec == NULL) + warnx("%s: %s", hostp, + gai_strerror(ecode)); + else + warnx("%s:%s: %s", hostp, portspec, + gai_strerror(ecode)); + if (ecode == EAI_AGAIN && + (opflags & (BGRNDNOW | BGRND))) + goto retry; + else + exit(1); + } + resolved = true; + /* + * For a Kerberized nfs mount where the + * "principal" argument has not been set, add + * it here. + */ + if (got_principal == 0 && secflavor != AUTH_SYS && + ai_nfs->ai_canonname != NULL) { + snprintf(pname, sizeof (pname), "nfs@%s", + ai_nfs->ai_canonname); + build_iovec(iov, iovlen, "principal", pname, + strlen(pname) + 1); + } + } + /* * Try each entry returned by getaddrinfo(). Note the * occurrence of remote errors by setting `remoteerr'. @@ -705,7 +714,7 @@ getnfsargs(char **specp, char **hostpp, struct iovec **iov, int *iovlen) /* Exit if all errors were local. */ if (!remoteerr) exit(1); - +retry: /* * If retrycnt == 0, we are to keep retrying forever. * Otherwise decrement it, and exit if it hits zero. diff --git a/sbin/mount_nullfs/Makefile b/sbin/mount_nullfs/Makefile index 12e87308ed13..1844aa14b24f 100644 --- a/sbin/mount_nullfs/Makefile +++ b/sbin/mount_nullfs/Makefile @@ -1,11 +1,6 @@ PACKAGE=runtime PROG= mount_nullfs -SRCS= mount_nullfs.c getmntopts.c MAN= mount_nullfs.8 - -MOUNT= ${.CURDIR:H}/mount -CFLAGS+=-I${MOUNT} - -.PATH: ${MOUNT} +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/mount_nullfs/mount_nullfs.c b/sbin/mount_nullfs/mount_nullfs.c index b5782dc24e45..fc04961e6247 100644 --- a/sbin/mount_nullfs/mount_nullfs.c +++ b/sbin/mount_nullfs/mount_nullfs.c @@ -38,14 +38,13 @@ #include <sys/uio.h> #include <err.h> +#include <mntopts.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sysexits.h> #include <unistd.h> -#include "mntopts.h" - static void usage(void) __dead2; static int diff --git a/sbin/mount_udf/Makefile b/sbin/mount_udf/Makefile index c41c051fc315..6506f6ba3985 100644 --- a/sbin/mount_udf/Makefile +++ b/sbin/mount_udf/Makefile @@ -1,12 +1,7 @@ PACKAGE=runtime PROG= mount_udf -SRCS= mount_udf.c getmntopts.c MAN= mount_udf.8 -LIBADD= kiconv - -MOUNT= ${.CURDIR:H}/mount -CFLAGS+= -I${MOUNT} -I${SRCTOP}/sys -.PATH: ${MOUNT} +LIBADD= kiconv util # Needs to be dynamically linked for optional dlopen() access to # userland libiconv diff --git a/sbin/mount_udf/mount_udf.c b/sbin/mount_udf/mount_udf.c index 2805872d41ad..0425c6a1378c 100644 --- a/sbin/mount_udf/mount_udf.c +++ b/sbin/mount_udf/mount_udf.c @@ -53,14 +53,13 @@ #include <err.h> #include <errno.h> +#include <mntopts.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sysexits.h> #include <unistd.h> -#include "mntopts.h" - static struct mntopt mopts[] = { MOPT_STDOPTS, MOPT_UPDATE, diff --git a/sbin/mount_unionfs/Makefile b/sbin/mount_unionfs/Makefile index 2096386a3784..35e7e8fa75b7 100644 --- a/sbin/mount_unionfs/Makefile +++ b/sbin/mount_unionfs/Makefile @@ -1,11 +1,6 @@ PACKAGE=runtime PROG= mount_unionfs -SRCS= mount_unionfs.c getmntopts.c MAN= mount_unionfs.8 - -MOUNT= ${.CURDIR:H}/mount -CFLAGS+=-I${MOUNT} - -.PATH: ${MOUNT} +LIBADD= util .include <bsd.prog.mk> diff --git a/sbin/mount_unionfs/mount_unionfs.c b/sbin/mount_unionfs/mount_unionfs.c index 28f224ae0f68..d207e5581745 100644 --- a/sbin/mount_unionfs/mount_unionfs.c +++ b/sbin/mount_unionfs/mount_unionfs.c @@ -41,6 +41,7 @@ #include <sys/errno.h> #include <err.h> +#include <mntopts.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -49,8 +50,6 @@ #include <grp.h> #include <pwd.h> -#include "mntopts.h" - static int subdir(const char *p, const char *dir) { diff --git a/sbin/newfs/newfs.c b/sbin/newfs/newfs.c index 49bd8b7dc4b6..418319d1cd3a 100644 --- a/sbin/newfs/newfs.c +++ b/sbin/newfs/newfs.c @@ -105,7 +105,6 @@ struct uufsd disk; /* libufs disk structure */ static char device[MAXPATHLEN]; static u_char bootarea[BBSIZE]; static int is_file; /* work on a file, not a device */ -static char *dkname; static char *disktype; static void getfssize(intmax_t *, const char *p, intmax_t, intmax_t); @@ -328,9 +327,7 @@ main(int argc, char *argv[]) if (fstat(disk.d_fd, &st) < 0) err(1, "%s", special); if ((st.st_mode & S_IFMT) != S_IFCHR) { - warn("%s: not a character-special device", special); is_file = 1; /* assume it is a file */ - dkname = special; if (sectorsize == 0) sectorsize = 512; mediasize = st.st_size; @@ -344,6 +341,11 @@ main(int argc, char *argv[]) } pp = NULL; lp = getdisklabel(); + /* + * set filesystem size from file size when a bsdlabel isn't present + */ + if (lp == NULL && is_file) + fssize = mediasize / sectorsize; if (lp != NULL) { if (!is_file) /* already set for files */ part_name = special[strlen(special) - 1]; @@ -433,7 +435,7 @@ getdisklabel(void) bootarea + (0 /* labeloffset */ + 1 /* labelsoffset */ * sectorsize), &lab, MAXPARTITIONS)) - errx(1, "no valid label found"); + return (NULL); lp = &lab; return &lab; diff --git a/sbin/newfs_msdos/newfs_msdos.8 b/sbin/newfs_msdos/newfs_msdos.8 index 92e407ab81af..03dfbfced51f 100644 --- a/sbin/newfs_msdos/newfs_msdos.8 +++ b/sbin/newfs_msdos/newfs_msdos.8 @@ -252,6 +252,7 @@ Create a 30MB image file, with the FAT partition starting newfs_msdos -C 30M -@63s ./somefile .Ed .Sh SEE ALSO +.Xr msdosfs 4 , .Xr gpart 8 , .Xr newfs 8 .Sh HISTORY diff --git a/sbin/nvmecontrol/Makefile b/sbin/nvmecontrol/Makefile index 2abcd72dedfa..cf9c23548e36 100644 --- a/sbin/nvmecontrol/Makefile +++ b/sbin/nvmecontrol/Makefile @@ -30,7 +30,7 @@ SRCS+= telemetry.c CFLAGS+= -I${SRCTOP}/lib/libnvmf MAN= nvmecontrol.8 LDFLAGS+= -rdynamic -LIBADD+= nvmf util +LIBADD+= nvmf sbuf util SUBDIR= modules HAS_TESTS= SUBDIR.${MK_TESTS}+= tests diff --git a/sbin/nvmecontrol/connect.c b/sbin/nvmecontrol/connect.c index c1d5d2cbaf5a..3d6d12bf2c48 100644 --- a/sbin/nvmecontrol/connect.c +++ b/sbin/nvmecontrol/connect.c @@ -31,6 +31,8 @@ static struct options { const char *subnqn; const char *hostnqn; uint32_t kato; + uint32_t reconnect_delay; + uint32_t controller_loss_timeout; uint16_t num_io_queues; uint16_t queue_size; bool data_digests; @@ -43,6 +45,8 @@ static struct options { .subnqn = NULL, .hostnqn = NULL, .kato = NVMF_KATO_DEFAULT / 1000, + .reconnect_delay = NVMF_DEFAULT_RECONNECT_DELAY, + .controller_loss_timeout = NVMF_DEFAULT_CONTROLLER_LOSS, .num_io_queues = 1, .queue_size = 0, .data_digests = false, @@ -107,7 +111,7 @@ connect_nvm_controller(enum nvmf_trtype trtype, int adrfam, const char *address, } error = nvmf_handoff_host(dle, hostnqn, admin, opt.num_io_queues, io, - &cdata); + &cdata, opt.reconnect_delay, opt.controller_loss_timeout); if (error != 0) { warnc(error, "Failed to handoff queues to kernel"); free(io); @@ -259,6 +263,11 @@ static const struct opts connect_opts[] = { "Number of entries in each I/O queue"), OPT("keep-alive-tmo", 'k', arg_uint32, opt, kato, "Keep Alive timeout (in seconds)"), + OPT("reconnect-delay", 'r', arg_uint32, opt, reconnect_delay, + "Delay between reconnect attempts after connection loss " + "(in seconds)"), + OPT("ctrl-loss-tmo", 'l', arg_uint32, opt, controller_loss_timeout, + "Controller loss timeout after connection loss (in seconds)"), OPT("hostnqn", 'q', arg_string, opt, hostnqn, "Host NQN"), OPT("flow_control", 'F', arg_none, opt, flow_control, diff --git a/sbin/nvmecontrol/identify.c b/sbin/nvmecontrol/identify.c index ea81f23d3aab..98a3141bf9ad 100644 --- a/sbin/nvmecontrol/identify.c +++ b/sbin/nvmecontrol/identify.c @@ -267,7 +267,7 @@ identify(const struct cmd *f, int argc, char *argv[]) static const struct opts identify_opts[] = { #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } OPT("hex", 'x', arg_none, opt, hex, - "Print identiy information in hex"), + "Print identity information in hex"), OPT("verbose", 'v', arg_none, opt, verbose, "More verbosity: print entire identify table"), OPT("nsid", 'n', arg_uint32, opt, nsid, 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/ns.c b/sbin/nvmecontrol/ns.c index 5e2a50df4ba1..c538338eb6c3 100644 --- a/sbin/nvmecontrol/ns.c +++ b/sbin/nvmecontrol/ns.c @@ -333,7 +333,7 @@ static struct identify_options { static const struct opts identify_opts[] = { OPT("hex", 'x', arg_none, identify_opt, hex, - "Print identiy information in hex"), + "Print identity information in hex"), OPT("verbose", 'v', arg_none, identify_opt, verbose, "More verbosity: print entire identify table"), OPT("nsid", 'n', arg_uint32, identify_opt, nsid, diff --git a/sbin/nvmecontrol/nvmecontrol.8 b/sbin/nvmecontrol/nvmecontrol.8 index a9f4552fbdc6..dc757bcf90c3 100644 --- a/sbin/nvmecontrol/nvmecontrol.8 +++ b/sbin/nvmecontrol/nvmecontrol.8 @@ -33,7 +33,7 @@ .\" .\" Author: Jim Harris <jimharris@FreeBSD.org> .\" -.Dd May 10, 2024 +.Dd July 9, 2025 .Dt NVMECONTROL 8 .Os .Sh NAME @@ -216,6 +216,8 @@ .Op Fl c Ar cntl-id .Op Fl i Ar queues .Op Fl k Ar seconds +.Op Fl l Ar seconds +.Op Fl r Ar seconds .Op Fl t Ar transport .Op Fl q Ar HostNQN .Op Fl Q Ar entries @@ -226,6 +228,8 @@ .Op Fl FGg .Op Fl i Ar queues .Op Fl k Ar seconds +.Op Fl l Ar seconds +.Op Fl r Ar seconds .Op Fl t Ar transport .Op Fl q Ar HostNQN .Op Fl Q Ar entries @@ -241,6 +245,8 @@ .Op Fl FGg .Op Fl i Ar queues .Op Fl k Ar seconds +.Op Fl l Ar seconds +.Op Fl r Ar seconds .Op Fl t Ar transport .Op Fl q Ar HostNQN .Op Fl Q Ar entries @@ -297,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" @@ -322,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 @@ -739,10 +748,10 @@ List the remote controllers advertised by a remote Discovery Controller: .It Fl t Ar transport Transport to use. The default is +.Ar tcp . .It Fl q Ar HostNQN NVMe Qualified Name to use for this host. By default an NQN is auto-generated from the current host's UUID. -.Ar tcp . .It Fl v Display the .Dv IDENTIFY_CONTROLLER @@ -786,6 +795,29 @@ The default is 1. .It Fl k Ar seconds Keep Alive timer duration in seconds. The default is 120. +.It Fl l Ar seconds +Controller Loss timer duration in seconds. +The default is 600. +.Pp +This timer starts when an association is lost with a remote I/O controller +and is cancelled when a new association is established. +If the timer expires, the controller device is deleted. +A setting of zero disables this timer. +.It Fl r Ar seconds +Reconnect timer duration in seconds. +The default is 10. +.Pp +When an association is lost with a remote I/O controller, +the controller device will request reconnection via periodic +.Xr devctl 4 +notifications until either a new association is established or the controller +device is deleted. +This timer sets the interval between each +.Xr devctl 4 +notification. +Note that the first notification is triggered immediately after an association +is lost. +A setting of zero disables this timer. .It Fl t Ar transport Transport to use. The default is diff --git a/sbin/nvmecontrol/reconnect.c b/sbin/nvmecontrol/reconnect.c index adf1edac662b..06af40624177 100644 --- a/sbin/nvmecontrol/reconnect.c +++ b/sbin/nvmecontrol/reconnect.c @@ -27,6 +27,8 @@ static struct options { const char *transport; const char *hostnqn; uint32_t kato; + uint32_t reconnect_delay; + uint32_t controller_loss_timeout; uint16_t num_io_queues; uint16_t queue_size; bool data_digests; @@ -37,6 +39,8 @@ static struct options { .transport = "tcp", .hostnqn = NULL, .kato = NVMF_KATO_DEFAULT / 1000, + .reconnect_delay = NVMF_DEFAULT_RECONNECT_DELAY, + .controller_loss_timeout = NVMF_DEFAULT_CONTROLLER_LOSS, .num_io_queues = 1, .queue_size = 0, .data_digests = false, @@ -59,6 +63,7 @@ static int reconnect_nvm_controller(int fd, const struct nvmf_association_params *aparams, enum nvmf_trtype trtype, int adrfam, const char *address, const char *port, uint16_t cntlid, const char *subnqn, const char *hostnqn, uint32_t kato, + uint32_t reconnect_delay, uint32_t controller_loss_timeout, u_int num_io_queues, u_int queue_size, const struct nvme_discovery_log_entry *dle) { @@ -88,7 +93,7 @@ reconnect_nvm_controller(int fd, const struct nvmf_association_params *aparams, } error = nvmf_reconnect_host(fd, dle, hostnqn, admin, num_io_queues, io, - &cdata); + &cdata, reconnect_delay, controller_loss_timeout); if (error != 0) { warnc(error, "Failed to handoff queues to kernel"); free(io); @@ -137,7 +142,8 @@ reconnect_by_address(int fd, const nvlist_t *rparams, const char *addr) error = reconnect_nvm_controller(fd, &aparams, trtype, AF_UNSPEC, address, port, le16toh(dle->cntlid), subnqn, hostnqn, - opt.kato * 1000, opt.num_io_queues, opt.queue_size, NULL); + opt.kato * 1000, opt.reconnect_delay, opt.controller_loss_timeout, + opt.num_io_queues, opt.queue_size, NULL); free(subnqn); free(tofree); return (error); @@ -196,6 +202,8 @@ reconnect_by_params(int fd, const nvlist_t *rparams) address, port, le16toh(dle->cntlid), dle->subnqn, nvlist_get_string(rparams, "hostnqn"), dnvlist_get_number(rparams, "kato", 0), + dnvlist_get_number(rparams, "reconnect_delay", 0), + dnvlist_get_number(rparams, "controller_loss_timeout", 0), nvlist_get_number(rparams, "num_io_queues"), nvlist_get_number(rparams, "io_qsize"), dle); free(subnqn); @@ -291,6 +299,11 @@ static const struct opts reconnect_opts[] = { "Number of entries in each I/O queue"), OPT("keep-alive-tmo", 'k', arg_uint32, opt, kato, "Keep Alive timeout (in seconds)"), + OPT("reconnect-delay", 'r', arg_uint32, opt, reconnect_delay, + "Delay between reconnect attempts after connection loss " + "(in seconds)"), + OPT("ctrl-loss-tmo", 'l', arg_uint32, opt, controller_loss_timeout, + "Controller loss timeout after connection loss (in seconds)"), OPT("hostnqn", 'q', arg_string, opt, hostnqn, "Host NQN"), OPT("flow_control", 'F', arg_none, opt, flow_control, diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index 8f732bdf268a..0f7702fc4630 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -89,9 +89,13 @@ static struct file { TAILQ_ENTRY(file) entry; FILE *stream; char *name; + size_t ungetpos; + size_t ungetsize; + u_char *ungetbuf; + int eof_reached; int lineno; int errors; -} *file; +} *file, *topfile; struct file *pushfile(const char *, int); int popfile(void); int check_file_secrecy(int, const char *); @@ -100,8 +104,9 @@ int yylex(void); int yyerror(const char *, ...); int kw_cmp(const void *, const void *); int lookup(char *); +int igetc(void); int lgetc(int); -int lungetc(int); +void lungetc(int); int findeol(void); static TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); @@ -161,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; @@ -227,16 +232,13 @@ struct range { int b; int t; }; -struct redirection { - struct node_host *host; - struct range rport; -}; static struct pool_opts { int marker; #define POM_TYPE 0x01 #define POM_STICKYADDRESS 0x02 #define POM_ENDPI 0x04 +#define POM_IPV6NH 0x08 u_int8_t opts; int type; int staticport; @@ -245,9 +247,11 @@ static struct pool_opts { } pool_opts; struct redirspec { - struct redirection *rdr; + struct node_host *host; + struct range rport; struct pool_opts pool_opts; - int af; + sa_family_t af; + bool binat; }; static struct filter_opts { @@ -263,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 */ @@ -302,15 +306,19 @@ static struct filter_opts { struct node_host *addr; u_int16_t port; } divert; - struct redirspec nat; - struct redirspec rdr; - struct redirspec rroute; + struct redirspec *nat; + struct redirspec *rdr; /* new-style scrub opts */ int nodf; int minttl; int settos; int randomid; int max_mss; + struct { + uint32_t limit; + uint32_t seconds; + } pktrate; + int max_pkt_size; } filter_opts; static struct antispoof_opts { @@ -360,14 +368,15 @@ 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 process_tabledef(char *, struct table_opts *, int); void expand_label_str(char *, size_t, const char *, const char *); void expand_label_if(const char *, char *, size_t, const char *); void expand_label_addr(const char *, char *, size_t, sa_family_t, @@ -382,13 +391,19 @@ void expand_eth_rule(struct pfctl_eth_rule *, struct node_mac *, struct node_mac *, struct node_host *, struct node_host *, const char *, const char *); -void expand_rule(struct pfctl_rule *, struct node_if *, +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 *, sa_family_t); +void add_binat_rdr_rule(struct pfctl_rule *, struct redirspec *, + struct node_host *, struct pfctl_rule *, struct redirspec **, + struct node_host **); +void expand_rule(struct pfctl_rule *, bool, struct node_if *, struct redirspec *, struct redirspec *, struct redirspec *, - struct node_host *, struct node_host *, struct node_host *, struct node_proto *, struct node_os *, struct node_host *, struct node_port *, struct node_host *, struct node_port *, struct node_uid *, struct node_gid *, struct node_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 *); @@ -405,15 +420,19 @@ 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); u_int16_t parseicmpspec(char *, sa_family_t); int kw_casecmp(const void *, const void *); int map_tos(char *string, int *); +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); @@ -463,13 +482,10 @@ typedef struct { } etheraddr; char *bridge_to; struct { - struct node_host *host; + struct redirspec *redirspec; u_int8_t rt; - u_int8_t pool_opts; - sa_family_t af; - struct pf_poolhashkey *key; } route; - struct redirection *redirection; + struct redirspec *redirspec; struct { int action; struct node_state_opt *options; @@ -526,11 +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 +%token DIVERTTO DIVERTREPLY BRIDGE_TO RECEIVEDON NE LE GE AFTO NATTO RDRTO +%token BINATTO MAXPKTRATE MAXPKTSIZE IPV6NH %token <v.string> STRING %token <v.number> NUMBER %token <v.i> PORTBINARY @@ -554,14 +571,15 @@ int parseport(char *, struct range *r, int); %type <v.fromto> fromto l3fromto %type <v.peer> ipportspec from to %type <v.host> ipspec toipspec xhost host dynaddr host_list -%type <v.host> redir_host_list redirspec -%type <v.host> route_host route_host_list routespec +%type <v.host> redir_host redir_host_list routespec +%type <v.host> route_host route_host_list %type <v.os> os xos os_list %type <v.port> portspec port_list port_item %type <v.uid> uids uid_list uid_item %type <v.gid> gids gid_list gid_item %type <v.route> route -%type <v.redirection> redirection redirpool +%type <v.redirspec> no_port_redirspec port_redirspec route_redirspec +%type <v.redirspec> binat_redirspec nat_redirspec %type <v.string> label stringall tag anchorname %type <v.string> string varstring numberstring %type <v.keep_state> keep @@ -896,6 +914,8 @@ varset : STRING '=' varstring { if (isspace((unsigned char)*s)) { yyerror("macro name cannot contain " "whitespace"); + free($1); + free($3); YYERROR; } } @@ -906,14 +926,36 @@ varset : STRING '=' varstring { } ; -anchorname : STRING { $$ = $1; } +anchorname : STRING { + if ($1[0] == '\0') { + free($1); + yyerror("anchor name must not be empty"); + YYERROR; + } + if (strlen(pf->anchor->path) + 1 + + strlen($1) >= PATH_MAX) { + free($1); + yyerror("anchor name is longer than %u", + PATH_MAX - 1); + YYERROR; + } + if ($1[0] == '_' || strstr($1, "/_") != NULL) { + free($1); + yyerror("anchor names beginning with '_' " + "are reserved for internal use"); + YYERROR; + } + $$ = $1; + } | /* empty */ { $$ = NULL; } ; pfa_anchorlist : /* empty */ | pfa_anchorlist '\n' + | pfa_anchorlist tabledef '\n' | pfa_anchorlist pfrule '\n' | pfa_anchorlist anchorrule '\n' + | pfa_anchorlist include '\n' ; pfa_anchor : '{' @@ -922,6 +964,8 @@ pfa_anchor : '{' struct pfctl_ruleset *rs; /* stepping into a brace anchor */ + if (pf->asd >= PFCTL_ANCHOR_STACK_DEPTH) + errx(1, "pfa_anchor: anchors too deep"); pf->asd++; pf->bn++; @@ -934,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 '}' @@ -958,82 +1002,14 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto YYERROR; } - if ($2 && ($2[0] == '_' || strstr($2, "/_") != NULL)) { - free($2); - yyerror("anchor names beginning with '_' " - "are reserved for internal use"); + pfctl_init_rule(&r); + if (! pfctl_setup_anchor(&r, pf, $2)) YYERROR; - } - memset(&r, 0, sizeof(r)); - 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; - r.prob = $9.prob; - r.rtableid = $9.rtableid; - r.ridentifier = $9.ridentifier; - if ($9.tag) - if (strlcpy(r.tagname, $9.tag, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - if ($9.match_tag) - if (strlcpy(r.match_tagname, $9.match_tag, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - r.match_tag_not = $9.match_tag_not; - if (rule_label(&r, $9.label)) - YYERROR; - for (int i = 0; i < PF_RULE_MAX_LABEL_COUNT; i++) - free($9.label[i]); - r.flags = $9.flags.b1; - r.flagset = $9.flags.b2; - if (($9.flags.b1 & $9.flags.b2) != $9.flags.b1) { - yyerror("flags always false"); - YYERROR; - } if ($9.flags.b1 || $9.flags.b2 || $8.src_os) { for (proto = $7; proto != NULL && proto->proto != IPPROTO_TCP; @@ -1051,7 +1027,8 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto } } - r.tos = $9.tos; + if (filteropts_to_rule(&r, &$9)) + YYERROR; if ($9.keep.action) { yyerror("cannot specify state handling " @@ -1059,33 +1036,12 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto YYERROR; } - if ($9.match_tag) - if (strlcpy(r.match_tagname, $9.match_tag, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - r.match_tag_not = $9.match_tag_not; - if ($9.marker & FOM_PRIO) { - if ($9.prio == 0) - r.prio = PF_PRIO_ZERO; - else - r.prio = $9.prio; - } - if ($9.marker & FOM_SETPRIO) { - r.set_prio[0] = $9.set_prio[0]; - r.set_prio[1] = $9.set_prio[1]; - r.scrub_flags |= PFSTATE_SETPRIO; - } - decide_address_family($8.src.host, &r.af); decide_address_family($8.dst.host, &r.af); - expand_rule(&r, $5, NULL, NULL, NULL, NULL, NULL, NULL, + 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; } @@ -1097,7 +1053,10 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto YYERROR; } - memset(&r, 0, sizeof(r)); + pfctl_init_rule(&r); + if (! pfctl_setup_anchor(&r, pf, $2)) + YYERROR; + r.action = PF_NAT; r.af = $4; r.rtableid = $7; @@ -1105,9 +1064,9 @@ 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); - expand_rule(&r, $3, NULL, NULL, NULL, NULL, NULL, NULL, + 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 { @@ -1118,7 +1077,10 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto YYERROR; } - memset(&r, 0, sizeof(r)); + pfctl_init_rule(&r); + if (! pfctl_setup_anchor(&r, pf, $2)) + YYERROR; + r.action = PF_RDR; r.af = $4; r.rtableid = $7; @@ -1147,9 +1109,9 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto r.dst.port_op = $6.dst.port->op; } - expand_rule(&r, $3, NULL, NULL, NULL, NULL, NULL, NULL, + 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 { @@ -1160,7 +1122,10 @@ anchorrule : ANCHOR anchorname dir quick interface af proto fromto YYERROR; } - memset(&r, 0, sizeof(r)); + pfctl_init_rule(&r); + if (! pfctl_setup_anchor(&r, pf, $2)) + YYERROR; + r.action = PF_BINAT; r.af = $4; r.rtableid = $7; @@ -1184,19 +1149,16 @@ 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); } ; -loadrule : LOAD ANCHOR string FROM string { +loadrule : LOAD ANCHOR anchorname FROM string { struct loadanchors *loadanchor; - if (strlen(pf->anchor->name) + 1 + - strlen($3) >= MAXPATHLEN) { - yyerror("anchorname %s too long, max %u\n", - $3, MAXPATHLEN - 1); - free($3); + if ($3 == NULL) { + yyerror("anchor name is missing"); YYERROR; } loadanchor = calloc(1, sizeof(struct loadanchors)); @@ -1207,7 +1169,7 @@ loadrule : LOAD ANCHOR string FROM string { err(1, "loadrule: malloc"); if (pf->anchor->name[0]) snprintf(loadanchor->anchorname, MAXPATHLEN, - "%s/%s", pf->anchor->name, $3); + "%s/%s", pf->anchor->path, $3); else strlcpy(loadanchor->anchorname, $3, MAXPATHLEN); if ((loadanchor->filename = strdup($5)) == NULL) @@ -1278,6 +1240,8 @@ etherpfa_anchor : '{' struct pfctl_eth_ruleset *rs; /* steping into a brace anchor */ + if (pf->asd >= PFCTL_ANCHOR_STACK_DEPTH) + errx(1, "pfa_anchor: anchors too deep"); pf->asd++; pf->bn++; @@ -1425,7 +1389,7 @@ scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts if (check_rulestate(PFCTL_STATE_SCRUB)) YYERROR; - memset(&r, 0, sizeof(r)); + pfctl_init_rule(&r); r.action = $1.b1; r.direction = $2; @@ -1470,9 +1434,9 @@ scrubrule : scrubaction dir logquick interface af proto fromto scrub_opts r.match_tag_not = $8.match_tag_not; r.rtableid = $8.rtableid; - expand_rule(&r, $4, NULL, NULL, NULL, NULL, NULL, NULL, + 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); } ; @@ -1587,7 +1551,7 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { YYERROR; for (i = $3; i; i = i->next) { - bzero(&r, sizeof(r)); + pfctl_init_rule(&r); r.action = PF_DROP; r.direction = PF_IN; @@ -1635,9 +1599,9 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { } if (h != NULL) - expand_rule(&r, j, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, h, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, ""); + expand_rule(&r, false, j, NULL, NULL, + NULL, NULL, NULL, h, NULL, NULL, + NULL, NULL, NULL, NULL, NULL); if ((i->ifa_flags & IFF_LOOPBACK) == 0) { bzero(&r, sizeof(r)); @@ -1657,9 +1621,10 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { else h = ifa_lookup(i->ifname, 0); if (h != NULL) - expand_rule(&r, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, h, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, ""); + expand_rule(&r, false, NULL, + NULL, NULL, NULL, NULL, + NULL, h, NULL, NULL, NULL, + NULL, NULL, NULL, NULL); } else free(hh); } @@ -1738,7 +1703,7 @@ tabledef : TABLE '<' STRING '>' table_opts { YYERROR; } if (pf->loadopt & PFCTL_FLAG_TABLE) - if (process_tabledef($3, &$5)) { + if (process_tabledef($3, &$5, pf->opts)) { free($3); YYERROR; } @@ -2371,7 +2336,7 @@ pfrule : action dir logquick interface route af proto fromto if (check_rulestate(PFCTL_STATE_FILTER)) YYERROR; - memset(&r, 0, sizeof(r)); + pfctl_init_rule(&r); r.action = $1.b1; switch ($1.b2) { @@ -2394,66 +2359,11 @@ pfrule : action dir logquick interface route af proto fromto r.log = $3.log; r.logif = $3.logif; r.quick = $3.quick; - r.prob = $9.prob; - r.rtableid = $9.rtableid; - - if ($9.nodf) - r.scrub_flags |= PFSTATE_NODF; - if ($9.randomid) - r.scrub_flags |= PFSTATE_RANDOMID; - if ($9.minttl) - r.min_ttl = $9.minttl; - if ($9.max_mss) - r.max_mss = $9.max_mss; - if ($9.marker & FOM_SETTOS) { - r.scrub_flags |= PFSTATE_SETTOS; - r.set_tos = $9.settos; - } - if ($9.marker & FOM_SCRUB_TCP) - r.scrub_flags |= PFSTATE_SCRUB_TCP; - - if ($9.marker & FOM_PRIO) { - if ($9.prio == 0) - r.prio = PF_PRIO_ZERO; - else - r.prio = $9.prio; - } - if ($9.marker & FOM_SETPRIO) { - r.set_prio[0] = $9.set_prio[0]; - r.set_prio[1] = $9.set_prio[1]; - r.scrub_flags |= PFSTATE_SETPRIO; - } - - if ($9.marker & FOM_AFTO) - r.rule_flag |= PFRULE_AFTO; - r.af = $6; - if ($9.tag) - if (strlcpy(r.tagname, $9.tag, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - if ($9.match_tag) - if (strlcpy(r.match_tagname, $9.match_tag, - PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { - yyerror("tag too long, max %u chars", - PF_TAG_NAME_SIZE - 1); - YYERROR; - } - r.match_tag_not = $9.match_tag_not; - if (rule_label(&r, $9.label)) - YYERROR; - for (int i = 0; i < PF_RULE_MAX_LABEL_COUNT; i++) - free($9.label[i]); - r.ridentifier = $9.ridentifier; - r.flags = $9.flags.b1; - r.flagset = $9.flags.b2; - if (($9.flags.b1 & $9.flags.b2) != $9.flags.b1) { - yyerror("flags always false"); + + if (filteropts_to_rule(&r, &$9)) YYERROR; - } + if ($9.flags.b1 || $9.flags.b2 || $8.src_os) { for (proto = $7; proto != NULL && proto->proto != IPPROTO_TCP; @@ -2469,18 +2379,8 @@ pfrule : action dir logquick interface route af proto fromto "apply to tcp"); YYERROR; } -#if 0 - if (($9.flags.b1 & parse_flags("S")) == 0 && - $8.src_os) { - yyerror("OS fingerprinting requires " - "the SYN TCP flag (flags S/SA)"); - YYERROR; - } -#endif } - r.tos = $9.tos; - r.keep_state = $9.keep.action; o = $9.keep.options; /* 'keep state' by default on pass rules. */ @@ -2714,10 +2614,6 @@ pfrule : action dir logquick interface route af proto fromto if (r.keep_state && !statelock) r.rule_flag |= default_statelock; - if ($9.fragment) - r.rule_flag |= PFRULE_FRAGMENT; - r.allow_opts = $9.allowopts; - decide_address_family($8.src.host, &r.af); decide_address_family($8.dst.host, &r.af); @@ -2728,63 +2624,18 @@ pfrule : action dir logquick interface route af proto fromto YYERROR; } r.rt = $5.rt; - r.route.opts = $5.pool_opts; - if ($5.key != NULL) - memcpy(&r.route.key, $5.key, - sizeof(struct pf_poolhashkey)); - } - if (r.rt) { - decide_address_family($5.host, &r.af); - if (!(r.rule_flag & PFRULE_AFTO)) - remove_invalid_hosts(&$5.host, &r.af); - if ($5.host == NULL) { - yyerror("no routing address with " - "matching address family found."); - YYERROR; - } - if ((r.route.opts & PF_POOL_TYPEMASK) == - PF_POOL_NONE && ($5.host->next != NULL || - $5.host->addr.type == PF_ADDR_TABLE || - DYNIF_MULTIADDR($5.host->addr))) - r.route.opts |= PF_POOL_ROUNDROBIN; - if ($5.host->next != NULL && - !PF_POOL_DYNTYPE(r.route.opts)) { - yyerror("address pool option " - "not supported by type"); - } - if (!PF_POOL_DYNTYPE(r.route.opts) && - (disallow_table($5.host, - "tables are not supported by pool type") || - disallow_alias($5.host, - "interface (%s) is not supported by pool type"))) - YYERROR; - if ($5.host->next != NULL) { - if ((r.route.opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN) { - yyerror("r.route.opts must " - "be PF_POOL_ROUNDROBIN"); + + 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; } } } - if ($9.queues.qname != NULL) { - if (strlcpy(r.qname, $9.queues.qname, - sizeof(r.qname)) >= sizeof(r.qname)) { - yyerror("rule qname too long (max " - "%d chars)", sizeof(r.qname)-1); - YYERROR; - } - free($9.queues.qname); - } - if ($9.queues.pqname != NULL) { - if (strlcpy(r.pqname, $9.queues.pqname, - sizeof(r.pqname)) >= sizeof(r.pqname)) { - yyerror("rule pqname too long (max " - "%d chars)", sizeof(r.pqname)-1); - YYERROR; - } - free($9.queues.pqname); - } #ifdef __FreeBSD__ r.divert.port = $9.divert.port; #else @@ -2824,21 +2675,39 @@ pfrule : action dir logquick interface route af proto fromto } if ($9.marker & FOM_AFTO) { - r.naf = $9.nat.af; - - r.nat.opts = $9.nat.pool_opts.type; - r.nat.opts |= $9.nat.pool_opts.opts; - - if (!PF_POOL_DYNTYPE(r.nat.opts) && - disallow_table($9.nat.rdr->host, "tables are not " - "supported by pool type")) - YYERROR; + r.naf = $9.nat->af; + } else { + if ($9.nat) { + if (!r.af && ! $9.nat->host->ifindex) + r.af = $9.nat->host->af; + remove_invalid_hosts(&($9.nat->host), &r.af); + if (invalid_redirect($9.nat->host, r.af)) + YYERROR; + if ($9.nat->host->addr.type == PF_ADDR_DYNIFTL) { + if (($9.nat->host = gen_dynnode($9.nat->host, r.af)) == NULL) + err(1, "calloc"); + } + if (check_netmask($9.nat->host, r.af)) + YYERROR; + } + if ($9.rdr) { + if (!r.af && ! $9.rdr->host->ifindex) + r.af = $9.rdr->host->af; + remove_invalid_hosts(&($9.rdr->host), &r.af); + if (invalid_redirect($9.rdr->host, r.af)) + YYERROR; + if ($9.rdr->host->addr.type == PF_ADDR_DYNIFTL) { + if (($9.rdr->host = gen_dynnode($9.rdr->host, r.af)) == NULL) + err(1, "calloc"); + } + if (check_netmask($9.rdr->host, r.af)) + YYERROR; + } } - expand_rule(&r, $4, &$9.nat, &$9.rdr, &$9.rroute, - NULL, $9.nat.rdr ? $9.nat.rdr->host : NULL, $5.host, + 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); } ; @@ -3011,7 +2880,7 @@ filter_opt : USER uids { } | DIVERTTO STRING PORT portplain { #ifndef __FreeBSD__ - if ((filter_opts.divert.addr = host($2)) == NULL) { + if ((filter_opts.divert.addr = host($2, pf->opts)) == NULL) { yyerror("could not parse divert address: %s", $2); free($2); @@ -3053,8 +2922,31 @@ filter_opt : USER uids { filter_opts.marker |= FOM_SCRUB_TCP; filter_opts.marker |= $3.marker; } - | AFTO af FROM redirspec pool_opts { - if (filter_opts.nat.rdr) { + | NATTO port_redirspec { + if (filter_opts.nat) { + yyerror("cannot respecify nat-to/binat-to"); + YYERROR; + } + filter_opts.nat = $2; + } + | RDRTO port_redirspec { + if (filter_opts.rdr) { + yyerror("cannot respecify rdr-to"); + YYERROR; + } + filter_opts.rdr = $2; + } + | BINATTO port_redirspec { + if (filter_opts.nat) { + yyerror("cannot respecify nat-to/binat-to"); + YYERROR; + } + filter_opts.nat = $2; + filter_opts.nat->binat = 1; + filter_opts.nat->pool_opts.staticport = 1; + } + | AFTO af FROM port_redirspec { + if (filter_opts.nat) { yyerror("cannot respecify af-to"); YYERROR; } @@ -3062,26 +2954,19 @@ filter_opt : USER uids { yyerror("no address family specified"); YYERROR; } - if ($4->af && $4->af != $2) { + + filter_opts.nat = $4; + filter_opts.nat->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; } - filter_opts.nat.af = $2; - filter_opts.nat.rdr = calloc(1, sizeof(struct redirection)); - if (filter_opts.nat.rdr == NULL) - err(1, "af-to: calloc"); - filter_opts.nat.rdr->host = $4; - memcpy(&filter_opts.nat.pool_opts, &$5, - sizeof(filter_opts.nat.pool_opts)); - filter_opts.rdr.rdr = - calloc(1, sizeof(struct redirection)); - bzero(&filter_opts.rdr.pool_opts, - sizeof(filter_opts.rdr.pool_opts)); filter_opts.marker |= FOM_AFTO; } - | AFTO af FROM redirspec pool_opts TO redirspec pool_opts { - if (filter_opts.nat.rdr) { + | AFTO af FROM port_redirspec TO port_redirspec { + if (filter_opts.nat) { yyerror("cannot respecify af-to"); YYERROR; } @@ -3089,28 +2974,42 @@ filter_opt : USER uids { yyerror("no address family specified"); YYERROR; } - if (($4->af && $4->af != $2) || - ($7->af && $7->af != $2)) { + filter_opts.nat = $4; + filter_opts.nat->af = $2; + filter_opts.rdr = $6; + filter_opts.rdr->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; } - filter_opts.nat.af = $2; - filter_opts.nat.rdr = calloc(1, sizeof(struct redirection)); - if (filter_opts.nat.rdr == NULL) - err(1, "af-to: calloc"); - filter_opts.nat.rdr->host = $4; - memcpy(&filter_opts.nat.pool_opts, &$5, - sizeof(filter_opts.nat.pool_opts)); - filter_opts.rdr.af = $2; - filter_opts.rdr.rdr = calloc(1, sizeof(struct redirection)); - if (filter_opts.rdr.rdr == NULL) - err(1, "af-to: calloc"); - filter_opts.rdr.rdr->host = $7; - memcpy(&filter_opts.nat.pool_opts, &$8, - sizeof(filter_opts.nat.pool_opts)); filter_opts.marker |= FOM_AFTO; } + | MAXPKTRATE NUMBER '/' NUMBER { + if ($2 < 0 || $2 > UINT_MAX || + $4 < 0 || $4 > UINT_MAX) { + yyerror("only positive values permitted"); + YYERROR; + } + if (filter_opts.pktrate.limit) { + yyerror("cannot respecify max-pkt-rate"); + YYERROR; + } + filter_opts.pktrate.limit = $2; + filter_opts.pktrate.seconds = $4; + } + | MAXPKTSIZE NUMBER { + if ($2 < 0 || $2 > UINT16_MAX) { + yyerror("only positive values permitted"); + YYERROR; + } + filter_opts.max_pkt_size = $2; + } + | ONCE { + filter_opts.marker |= FOM_ONCE; + } | filter_sets ; @@ -3317,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; @@ -3718,7 +3616,7 @@ xhost : not host { ; host : STRING { - if (($$ = host($1)) == NULL) { + if (($$ = host($1, pf->opts)) == NULL) { /* error. "any" is handled elsewhere */ free($1); yyerror("could not parse host specification"); @@ -3730,7 +3628,8 @@ host : STRING { | STRING '-' STRING { struct node_host *b, *e; - if ((b = host($1)) == NULL || (e = host($3)) == NULL) { + if ((b = host($1, pf->opts)) == NULL || + (e = host($3, pf->opts)) == NULL) { free($1); free($3); yyerror("could not parse host specification"); @@ -3739,9 +3638,9 @@ host : STRING { if (b->af != e->af || b->addr.type != PF_ADDR_ADDRMASK || e->addr.type != PF_ADDR_ADDRMASK || - unmask(&b->addr.v.a.mask, b->af) != + unmask(&b->addr.v.a.mask) != (b->af == AF_INET ? 32 : 128) || - unmask(&e->addr.v.a.mask, e->af) != + unmask(&e->addr.v.a.mask) != (e->af == AF_INET ? 32 : 128) || b->next != NULL || b->not || e->next != NULL || e->not) { @@ -3766,7 +3665,7 @@ host : STRING { if (asprintf(&buf, "%s/%lld", $1, (long long)$3) == -1) err(1, "host: asprintf"); free($1); - if (($$ = host(buf)) == NULL) { + if (($$ = host(buf, pf->opts)) == NULL) { /* error. "any" is handled elsewhere */ free(buf); yyerror("could not parse host specification"); @@ -3784,7 +3683,7 @@ host : STRING { if (asprintf(&buf, "%lld/%lld", $1, $3) == -1) #endif err(1, "host: asprintf"); - if (($$ = host(buf)) == NULL) { + if (($$ = host(buf, pf->opts)) == NULL) { /* error. "any" is handled elsewhere */ free(buf); yyerror("could not parse host specification"); @@ -3910,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 = $$; @@ -3929,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 = $$; } @@ -3944,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 = $$; } @@ -3990,7 +3902,7 @@ uid_item : uid { $$->tail = $$; } | unaryop uid { - if ($2 == UID_MAX && $1 != PF_OP_EQ && $1 != PF_OP_NE) { + if ($2 == -1 && $1 != PF_OP_EQ && $1 != PF_OP_NE) { yyerror("user unknown requires operator = or " "!="); YYERROR; @@ -4005,7 +3917,7 @@ uid_item : uid { $$->tail = $$; } | uid PORTBINARY uid { - if ($1 == UID_MAX || $3 == UID_MAX) { + if ($1 == -1 || $3 == -1) { yyerror("user unknown requires operator = or " "!="); YYERROR; @@ -4023,16 +3935,16 @@ uid_item : uid { uid : STRING { if (!strcmp($1, "unknown")) - $$ = UID_MAX; + $$ = -1; else { - struct passwd *pw; + uid_t uid; - if ((pw = getpwnam($1)) == NULL) { + if (uid_from_user($1, &uid) == -1) { yyerror("unknown user %s", $1); free($1); YYERROR; } - $$ = pw->pw_uid; + $$ = uid; } free($1); } @@ -4068,7 +3980,7 @@ gid_item : gid { $$->tail = $$; } | unaryop gid { - if ($2 == GID_MAX && $1 != PF_OP_EQ && $1 != PF_OP_NE) { + if ($2 == -1 && $1 != PF_OP_EQ && $1 != PF_OP_NE) { yyerror("group unknown requires operator = or " "!="); YYERROR; @@ -4083,7 +3995,7 @@ gid_item : gid { $$->tail = $$; } | gid PORTBINARY gid { - if ($1 == GID_MAX || $3 == GID_MAX) { + if ($1 == -1 || $3 == -1) { yyerror("group unknown requires operator = or " "!="); YYERROR; @@ -4101,16 +4013,16 @@ gid_item : gid { gid : STRING { if (!strcmp($1, "unknown")) - $$ = GID_MAX; + $$ = -1; else { - struct group *grp; + gid_t gid; - if ((grp = getgrnam($1)) == NULL) { + if (gid_from_group($1, &gid) == -1) { yyerror("unknown group %s", $1); free($1); YYERROR; } - $$ = grp->gr_gid; + $$ = gid; } free($1); } @@ -4580,7 +4492,7 @@ portstar : numberstring { } ; -redirspec : host { $$ = $1; } +redir_host : host { $$ = $1; } | '{' optnl redir_host_list '}' { $$ = $3; } ; @@ -4592,20 +4504,42 @@ redir_host_list : host optnl { $$ = $1; } } ; -redirpool : /* empty */ { $$ = NULL; } - | ARROW redirspec { - $$ = calloc(1, sizeof(struct redirection)); +/* Redirection without port */ +no_port_redirspec: redir_host pool_opts { + $$ = calloc(1, sizeof(struct redirspec)); if ($$ == NULL) - err(1, "redirection: calloc"); - $$->host = $2; + err(1, "redirspec: calloc"); + $$->host = $1; + $$->pool_opts = $2; $$->rport.a = $$->rport.b = $$->rport.t = 0; } - | ARROW redirspec PORT portstar { - $$ = calloc(1, sizeof(struct redirection)); + ; + +/* Redirection with optional port */ +port_redirspec : no_port_redirspec; + | redir_host PORT portstar pool_opts { + $$ = calloc(1, sizeof(struct redirspec)); if ($$ == NULL) - err(1, "redirection: calloc"); - $$->host = $2; - $$->rport = $4; + err(1, "redirspec: calloc"); + $$->host = $1; + $$->rport = $3; + $$->pool_opts = $4; + } + +/* Redirection with an arrow and an optional port: FreeBSD NAT rules */ +nat_redirspec : /* empty */ { $$ = NULL; } + | ARROW port_redirspec { + $$ = $2; + } + ; + +/* Redirection with interfaces and without ports: route-to rules */ +route_redirspec : routespec pool_opts { + $$ = calloc(1, sizeof(struct redirspec)); + if ($$ == NULL) + err(1, "redirspec: calloc"); + $$->host = $1; + $$->pool_opts = $2; } ; @@ -4724,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"); @@ -4756,18 +4698,18 @@ pool_opt : BITMASK { } ; -redirection : /* empty */ { $$ = NULL; } +binat_redirspec : /* empty */ { $$ = NULL; } | ARROW host { - $$ = calloc(1, sizeof(struct redirection)); + $$ = calloc(1, sizeof(struct redirspec)); if ($$ == NULL) - err(1, "redirection: calloc"); + err(1, "redirspec: calloc"); $$->host = $2; $$->rport.a = $$->rport.b = $$->rport.t = 0; } | ARROW host PORT portstar { - $$ = calloc(1, sizeof(struct redirection)); + $$ = calloc(1, sizeof(struct redirspec)); if ($$ == NULL) - err(1, "redirection: calloc"); + err(1, "redirspec: calloc"); $$->host = $2; $$->rport = $4; } @@ -4808,7 +4750,7 @@ nataction : no NAT natpasslog { ; natrule : nataction interface af proto fromto tag tagged rtable - redirpool pool_opts + nat_redirspec { struct pfctl_rule r; struct node_state_opt *o; @@ -4816,7 +4758,7 @@ natrule : nataction interface af proto fromto tag tagged rtable if (check_rulestate(PFCTL_STATE_NAT)) YYERROR; - memset(&r, 0, sizeof(r)); + pfctl_init_rule(&r); r.action = $1.b1; r.natpass = $1.b2; @@ -4863,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; @@ -4875,110 +4823,6 @@ natrule : nataction interface af proto fromto tag tagged rtable } if (check_netmask($9->host, r.af)) YYERROR; - - r.rdr.proxy_port[0] = ntohs($9->rport.a); - - switch (r.action) { - case PF_RDR: - if (!$9->rport.b && $9->rport.t && - $5.dst.port != NULL) { - r.rdr.proxy_port[1] = - ntohs($9->rport.a) + - (ntohs( - $5.dst.port->port[1]) - - ntohs( - $5.dst.port->port[0])); - } else - r.rdr.proxy_port[1] = - ntohs($9->rport.b); - break; - case PF_NAT: - r.rdr.proxy_port[1] = - ntohs($9->rport.b); - if (!r.rdr.proxy_port[0] && - !r.rdr.proxy_port[1]) { - r.rdr.proxy_port[0] = - PF_NAT_PROXY_PORT_LOW; - r.rdr.proxy_port[1] = - PF_NAT_PROXY_PORT_HIGH; - } else if (!r.rdr.proxy_port[1]) - r.rdr.proxy_port[1] = - r.rdr.proxy_port[0]; - break; - default: - break; - } - - r.rdr.opts = $10.type; - if ((r.rdr.opts & PF_POOL_TYPEMASK) == - PF_POOL_NONE && ($9->host->next != NULL || - $9->host->addr.type == PF_ADDR_TABLE || - DYNIF_MULTIADDR($9->host->addr))) - r.rdr.opts = PF_POOL_ROUNDROBIN; - if (!PF_POOL_DYNTYPE(r.rdr.opts) && - (disallow_table($9->host, - "tables are not supported by pool type") || - disallow_alias($9->host, - "interface (%s) is not supported by pool type"))) - YYERROR; - if ($9->host->next != NULL) { - if ((r.rdr.opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN) { - yyerror("only round-robin " - "valid for multiple " - "redirection addresses"); - YYERROR; - } - } - } - - if ($10.key != NULL) - memcpy(&r.rdr.key, $10.key, - sizeof(struct pf_poolhashkey)); - - if ($10.opts) - r.rdr.opts |= $10.opts; - - if ($10.staticport) { - if (r.action != PF_NAT) { - yyerror("the 'static-port' option is " - "only valid with nat rules"); - YYERROR; - } - if (r.rdr.proxy_port[0] != - PF_NAT_PROXY_PORT_LOW && - r.rdr.proxy_port[1] != - PF_NAT_PROXY_PORT_HIGH) { - yyerror("the 'static-port' option can't" - " be used when specifying a port" - " range"); - YYERROR; - } - r.rdr.proxy_port[0] = 0; - r.rdr.proxy_port[1] = 0; - } - - if ($10.mape.offset) { - if (r.action != PF_NAT) { - yyerror("the 'map-e-portset' option is" - " only valid with nat rules"); - YYERROR; - } - if ($10.staticport) { - yyerror("the 'map-e-portset' option" - " can't be used 'static-port'"); - YYERROR; - } - if (r.rdr.proxy_port[0] != - PF_NAT_PROXY_PORT_LOW && - r.rdr.proxy_port[1] != - PF_NAT_PROXY_PORT_HIGH) { - yyerror("the 'map-e-portset' option" - " can't be used when specifying" - " a port range"); - YYERROR; - } - r.rdr.mape = $10.mape; } o = keep_state_defaults; @@ -4996,19 +4840,17 @@ natrule : nataction interface af proto fromto tag tagged rtable o = o->next; } - expand_rule(&r, $2, NULL, NULL, NULL, - $9 == NULL ? NULL : $9->host, NULL, NULL, $4, + 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, ""); - free($9); + $5.dst.port, 0, 0, 0, 0); } ; binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag - tagged rtable redirection + tagged rtable binat_redirspec { struct pfctl_rule binat; - struct pf_pooladdr *pa; + struct pfctl_pooladdr *pa; if (check_rulestate(PFCTL_STATE_NAT)) YYERROR; @@ -5016,7 +4858,7 @@ binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag "permitted as a binat destination")) YYERROR; - memset(&binat, 0, sizeof(binat)); + pfctl_init_rule(&binat); if ($1 && $3.b1) { yyerror("\"pass\" not valid with \"no\""); @@ -5167,20 +5009,19 @@ binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag YYERROR; } - TAILQ_INIT(&binat.rdr.list); - TAILQ_INIT(&binat.nat.list); - 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); } ; @@ -5231,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; @@ -5249,36 +5083,23 @@ routespec : route_host { $$ = $1; } ; route : /* empty */ { - $$.host = NULL; - $$.rt = 0; - $$.pool_opts = 0; + $$.rt = PF_NOPFROUTE; } | FASTROUTE { /* backwards-compat */ - $$.host = NULL; - $$.rt = 0; - $$.pool_opts = 0; + $$.rt = PF_NOPFROUTE; } - | ROUTETO routespec pool_opts { - $$.host = $2; + | ROUTETO route_redirspec { $$.rt = PF_ROUTETO; - $$.pool_opts = $3.type | $3.opts; - if ($3.key != NULL) - $$.key = $3.key; + $$.redirspec = $2; } - | REPLYTO routespec pool_opts { - $$.host = $2; + | REPLYTO route_redirspec { $$.rt = PF_REPLYTO; - $$.pool_opts = $3.type | $3.opts; - if ($3.key != NULL) - $$.key = $3.key; + $$.redirspec = $2; } - | DUPTO routespec pool_opts { - $$.host = $2; + | DUPTO route_redirspec { $$.rt = PF_DUPTO; - $$.pool_opts = $3.type | $3.opts; - if ($3.key != NULL) - $$.key = $3.key; + $$.redirspec = $2; } ; @@ -5381,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) @@ -5414,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; @@ -5424,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: @@ -5443,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; @@ -5479,8 +5313,9 @@ filter_consistent(struct pfctl_rule *r, int anchor_call) r->af == AF_INET ? "inet" : "inet6"); problems++; } - if (r->allow_opts && r->action != PF_PASS) { - yyerror("allow-opts can only be specified for pass rules"); + if (r->allow_opts && r->action != PF_PASS && r->action != PF_MATCH) { + yyerror("allow-opts can only be specified for pass or " + "match rules"); problems++; } if (r->rule_flag & PFRULE_FRAGMENT && (r->src.port_op || @@ -5507,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 " @@ -5514,8 +5353,9 @@ filter_consistent(struct pfctl_rule *r, int anchor_call) problems++; } } - /* match rules rules */ - if (r->action == PF_MATCH) { + /* Basic rule sanity check. */ + switch (r->action) { + case PF_MATCH: if (r->divert.port) { yyerror("divert is not supported on match rules"); problems++; @@ -5529,10 +5369,28 @@ filter_consistent(struct pfctl_rule *r, int anchor_call) yyerror("af-to is not supported on match rules"); problems++; } + break; + case PF_DROP: + if (r->rt) { + yyerror("route-to, reply-to and dup-to " + "are not supported on block rules"); + problems++; + } + break; + default:; } - if (r->rdr.opts & PF_POOL_STICKYADDR && !r->keep_state) { + if (!TAILQ_EMPTY(&(r->nat.list)) || !TAILQ_EMPTY(&(r->rdr.list))) { + if (r->action != PF_MATCH && !r->keep_state) { + yyerror("nat-to and rdr-to require keep state"); + problems++; + } + if (r->direction == PF_INOUT) { + yyerror("nat-to and rdr-to require a direction"); + problems++; + } + } + if (r->route.opts & PF_POOL_STICKYADDR && !r->keep_state) { yyerror("'sticky-address' requires 'keep state'"); - problems++; } return (-problems); } @@ -5572,10 +5430,11 @@ rdr_consistent(struct pfctl_rule *r) } int -process_tabledef(char *name, struct table_opts *opts) +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); @@ -5583,7 +5442,7 @@ process_tabledef(char *name, struct table_opts *opts) ab.pfrb_type = PFRB_ADDRS; SIMPLEQ_FOREACH(ti, &opts->init_nodes, entries) { if (ti->file) - if (pfr_buf_load(&ab, ti->file, 0, append_addr)) { + if (pfr_buf_load(&ab, ti->file, 0, append_addr, popts)) { if (errno) yyerror("cannot load \"%s\": %s", ti->file, strerror(errno)); @@ -5602,9 +5461,29 @@ process_tabledef(char *name, struct table_opts *opts) if (pf->opts & PF_OPT_VERBOSE) print_tabledef(name, opts->flags, opts->init_addr, &opts->init_nodes); + if (!(pf->opts & PF_OPT_NOACTION) || + (pf->opts & PF_OPT_DUMMYACTION)) + warn_duplicate_tables(name, pf->anchor->path); + 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->name, &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) @@ -5616,10 +5495,32 @@ process_tabledef(char *name, struct table_opts *opts) name); else yyerror("cannot define table %s: %s", name, - pfr_strerror(errno)); + pf_strerror(errno)); 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); @@ -5670,18 +5571,18 @@ expand_label_str(char *label, size_t len, const char *srch, const char *repl) char *p, *q; if ((tmp = calloc(1, len)) == NULL) - err(1, "expand_label_str: calloc"); + err(1, "%s: calloc", __func__); p = q = label; while ((q = strstr(p, srch)) != NULL) { *q = '\0'; if ((strlcat(tmp, p, len) >= len) || (strlcat(tmp, repl, len) >= len)) - errx(1, "expand_label: label too long"); + errx(1, "%s: label too long", __func__); q += strlen(srch); p = q; } if (strlcat(tmp, p, len) >= len) - errx(1, "expand_label: label too long"); + errx(1, "%s: label too long", __func__); strlcpy(label, tmp, len); /* always fits */ free(tmp); } @@ -5729,7 +5630,7 @@ expand_label_addr(const char *name, char *label, size_t len, sa_family_t af, sizeof(a)) == NULL) snprintf(tmp, sizeof(tmp), "?"); else { - bits = unmask(&addr->addr.v.a.mask, af); + bits = unmask(&addr->addr.v.a.mask); if ((af == AF_INET && bits < 32) || (af == AF_INET6 && bits < 128)) snprintf(tmp, sizeof(tmp), @@ -5847,7 +5748,7 @@ expand_altq(struct pf_altq *a, struct node_if *interfaces, memcpy(&pa, a, sizeof(struct pf_altq)); if (strlcpy(pa.ifname, interface->ifname, sizeof(pa.ifname)) >= sizeof(pa.ifname)) - errx(1, "expand_altq: strlcpy"); + errx(1, "%s: strlcpy", __func__); if (interface->not) { yyerror("altq on ! <interface> is not supported"); @@ -5881,16 +5782,16 @@ expand_altq(struct pf_altq *a, struct node_if *interfaces, memset(&pb, 0, sizeof(struct pf_altq)); if (strlcpy(qname, "root_", sizeof(qname)) >= sizeof(qname)) - errx(1, "expand_altq: strlcpy"); + errx(1, "%s: strlcpy", __func__); if (strlcat(qname, interface->ifname, sizeof(qname)) >= sizeof(qname)) - errx(1, "expand_altq: strlcat"); + errx(1, "%s: strlcat", __func__); if (strlcpy(pb.qname, qname, sizeof(pb.qname)) >= sizeof(pb.qname)) - errx(1, "expand_altq: strlcpy"); + errx(1, "%s: strlcpy", __func__); if (strlcpy(pb.ifname, interface->ifname, sizeof(pb.ifname)) >= sizeof(pb.ifname)) - errx(1, "expand_altq: strlcpy"); + errx(1, "%s: strlcpy", __func__); pb.qlimit = pa.qlimit; pb.scheduler = pa.scheduler; bw.bw_absolute = pa.ifbandwidth; @@ -5905,20 +5806,20 @@ expand_altq(struct pf_altq *a, struct node_if *interfaces, LOOP_THROUGH(struct node_queue, queue, nqueues, n = calloc(1, sizeof(struct node_queue)); if (n == NULL) - err(1, "expand_altq: calloc"); + err(1, "%s: calloc", __func__); if (pa.scheduler == ALTQT_CBQ || pa.scheduler == ALTQT_HFSC || pa.scheduler == ALTQT_FAIRQ) if (strlcpy(n->parent, qname, sizeof(n->parent)) >= sizeof(n->parent)) - errx(1, "expand_altq: strlcpy"); + errx(1, "%s: strlcpy", __func__); if (strlcpy(n->queue, queue->queue, sizeof(n->queue)) >= sizeof(n->queue)) - errx(1, "expand_altq: strlcpy"); + errx(1, "%s: strlcpy", __func__); if (strlcpy(n->ifname, interface->ifname, sizeof(n->ifname)) >= sizeof(n->ifname)) - errx(1, "expand_altq: strlcpy"); + errx(1, "%s: strlcpy", __func__); n->scheduler = pa.scheduler; n->next = NULL; n->tail = n; @@ -6001,10 +5902,10 @@ expand_queue(struct pf_altq *a, struct node_if *interfaces, if (strlcpy(pa.ifname, tqueue->ifname, sizeof(pa.ifname)) >= sizeof(pa.ifname)) - errx(1, "expand_queue: strlcpy"); + errx(1, "%s: strlcpy", __func__); if (strlcpy(pa.parent, tqueue->parent, sizeof(pa.parent)) >= sizeof(pa.parent)) - errx(1, "expand_queue: strlcpy"); + errx(1, "%s: strlcpy", __func__); if (eval_pfqueue(pf, &pa, &bwspec, opts)) errs++; @@ -6022,19 +5923,19 @@ expand_queue(struct pf_altq *a, struct node_if *interfaces, n = calloc(1, sizeof(struct node_queue)); if (n == NULL) - err(1, "expand_queue: calloc"); + err(1, "%s: calloc", __func__); if (strlcpy(n->parent, a->qname, sizeof(n->parent)) >= sizeof(n->parent)) - errx(1, "expand_queue strlcpy"); + errx(1, "%s strlcpy", __func__); if (strlcpy(n->queue, nq->queue, sizeof(n->queue)) >= sizeof(n->queue)) - errx(1, "expand_queue strlcpy"); + errx(1, "%s strlcpy", __func__); if (strlcpy(n->ifname, tqueue->ifname, sizeof(n->ifname)) >= sizeof(n->ifname)) - errx(1, "expand_queue strlcpy"); + errx(1, "%s strlcpy", __func__); n->scheduler = tqueue->scheduler; n->next = NULL; n->tail = n; @@ -6103,12 +6004,12 @@ expand_eth_rule(struct pfctl_eth_rule *r, char qname[PF_QNAME_SIZE]; if (strlcpy(tagname, r->tagname, sizeof(tagname)) >= sizeof(tagname)) - errx(1, "expand_eth_rule: tagname"); + errx(1, "%s: tagname", __func__); if (strlcpy(match_tagname, r->match_tagname, sizeof(match_tagname)) >= sizeof(match_tagname)) - errx(1, "expand_eth_rule: match_tagname"); + errx(1, "%s: match_tagname", __func__); if (strlcpy(qname, r->qname, sizeof(qname)) >= sizeof(qname)) - errx(1, "expand_eth_rule: qname"); + errx(1, "%s: qname", __func__); LOOP_THROUGH(struct node_if, interface, interfaces, LOOP_THROUGH(struct node_etherproto, proto, protos, @@ -6140,12 +6041,12 @@ expand_eth_rule(struct pfctl_eth_rule *r, if (strlcpy(r->tagname, tagname, sizeof(r->tagname)) >= sizeof(r->tagname)) - errx(1, "expand_eth_rule: r->tagname"); + errx(1, "%s: r->tagname", __func__); if (strlcpy(r->match_tagname, match_tagname, sizeof(r->match_tagname)) >= sizeof(r->match_tagname)) - errx(1, "expand_eth_rule: r->match_tagname"); + errx(1, "%s: r->match_tagname", __func__); if (strlcpy(r->qname, qname, sizeof(r->qname)) >= sizeof(r->qname)) - errx(1, "expand_eth_rule: r->qname"); + errx(1, "%s: r->qname", __func__); if (bridge_to) strlcpy(r->bridge_to, bridge_to, sizeof(r->bridge_to)); @@ -6161,16 +6062,262 @@ expand_eth_rule(struct pfctl_eth_rule *r, FREE_LIST(struct node_host, ipdsts); } +int +apply_rdr_ports(struct pfctl_rule *r, struct pfctl_pool *rpool, struct redirspec *rs) +{ + if (rs == NULL) + return 0; + + rpool->proxy_port[0] = ntohs(rs->rport.a); + + 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 { + 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"); + return 1; + } + + if (rs->pool_opts.mape.offset) { + yyerror("the 'map-e-portset' option is only valid with nat rules"); + return 1; + } + + return 0; +} + +int +apply_nat_ports(struct pfctl_pool *rpool, struct redirspec *rs) +{ + if (rs == NULL) + return 0; + + rpool->proxy_port[0] = ntohs(rs->rport.a); + rpool->proxy_port[1] = ntohs(rs->rport.b); + if (!rpool->proxy_port[0] && !rpool->proxy_port[1]) { + rpool->proxy_port[0] = PF_NAT_PROXY_PORT_LOW; + rpool->proxy_port[1] = PF_NAT_PROXY_PORT_HIGH; + } else if (!rpool->proxy_port[1]) + rpool->proxy_port[1] = rpool->proxy_port[0]; + + if (rs->pool_opts.staticport) { + if (rpool->proxy_port[0] != PF_NAT_PROXY_PORT_LOW && + rpool->proxy_port[1] != PF_NAT_PROXY_PORT_HIGH) { + yyerror("the 'static-port' option can't" + " be used when specifying a port" + " range"); + return 1; + } + rpool->proxy_port[0] = 0; + rpool->proxy_port[1] = 0; + } + + if (rs->pool_opts.mape.offset) { + if (rs->pool_opts.staticport) { + yyerror("the 'map-e-portset' option" + " can't be used 'static-port'"); + return 1; + } + if (rpool->proxy_port[0] != PF_NAT_PROXY_PORT_LOW && + rpool->proxy_port[1] != PF_NAT_PROXY_PORT_HIGH) { + yyerror("the 'map-e-portset' option" + " can't be used when specifying" + " a port range"); + return 1; + } + rpool->mape = rs->pool_opts.mape; + } + + return 0; +} + +int +apply_redirspec(struct pfctl_pool *rpool, struct redirspec *rs) +{ + struct node_host *h; + struct pfctl_pooladdr *pa; + + if (rs == NULL) + return 0; + + rpool->opts = rs->pool_opts.type; + + if ((rpool->opts & PF_POOL_TYPEMASK) == PF_POOL_NONE && + (rs->host->next != NULL || + rs->host->addr.type == PF_ADDR_TABLE || + DYNIF_MULTIADDR(rs->host->addr))) + rpool->opts = PF_POOL_ROUNDROBIN; + + if (!PF_POOL_DYNTYPE(rpool->opts) && + (disallow_table(rs->host, "tables are not supported by pool type") || + disallow_alias(rs->host, "interface (%s) is not supported by pool type"))) + return 1; + + if (rs->host->next != NULL && + ((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN)) { + yyerror("r.route.opts must be PF_POOL_ROUNDROBIN"); + return 1; + } + + if (rs->host->next != NULL) { + if ((rpool->opts & PF_POOL_TYPEMASK) != + PF_POOL_ROUNDROBIN) { + yyerror("only round-robin valid for multiple " + "redirection addresses"); + return 1; + } + } + + rpool->opts |= rs->pool_opts.opts; + + if (rs->pool_opts.key != NULL) + memcpy(&(rpool->key), rs->pool_opts.key, + sizeof(struct pf_poolhashkey)); + + for (h = rs->host; h != NULL; h = h->next) { + 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)) + errx(1, "%s: strlcpy", __func__); + } else + pa->ifname[0] = 0; + TAILQ_INSERT_TAIL(&(rpool->list), pa, entries); + } + + return 0; +} + +int +check_binat_redirspec(struct node_host *src_host, struct pfctl_rule *r, + sa_family_t af) +{ + struct pfctl_pooladdr *nat_pool = TAILQ_FIRST(&(r->nat.list)); + int error = 0; + + /* XXX: FreeBSD allows syntax like "{ host1 host2 }" for redirection + * pools but does not covert them to tables automatically, because + * syntax "{ (iface1 host1), (iface2 iface2) }" is allowed for route-to + * redirection. Add a FreeBSD-specific guard against using multiple + * hosts for source and redirection. + */ + if (src_host->next) { + yyerror("invalid use of table as the source address " + "of a binat-to rule"); + error++; + } + if (TAILQ_NEXT(nat_pool, entries)) { + yyerror ("tables cannot be used as the redirect " + "address of a binat-to rule"); + error++; + } + + if (disallow_table(src_host, "invalid use of table " + "<%s> as the source address of a binat-to rule") || + disallow_alias(src_host, "invalid use of interface " + "(%s) as the source address of a binat-to rule")) { + error++; + } else if ((r->src.addr.type != PF_ADDR_ADDRMASK && + r->src.addr.type != PF_ADDR_DYNIFTL) || + (nat_pool->addr.type != PF_ADDR_ADDRMASK && + nat_pool->addr.type != PF_ADDR_DYNIFTL)) { + yyerror("binat-to requires a specified " + "source and redirect address"); + error++; + } + if (DYNIF_MULTIADDR(r->src.addr) || + DYNIF_MULTIADDR(nat_pool->addr)) { + yyerror ("dynamic interfaces must be " + "used with:0 in a binat-to rule"); + error++; + } + if (PF_AZERO(&r->src.addr.v.a.mask, af) || + PF_AZERO(&(nat_pool->addr.v.a.mask), af)) { + yyerror ("source and redir addresess must have " + "a matching network mask in binat-rule"); + error++; + } + if (nat_pool->addr.type == PF_ADDR_TABLE) { + yyerror ("tables cannot be used as the redirect " + "address of a binat-to rule"); + error++; + } + if (r->direction != PF_INOUT) { + yyerror("binat-to cannot be specified " + "with a direction"); + error++; + } + + /* first specify outbound NAT rule */ + r->direction = PF_OUT; + + return (error); +} + void -expand_rule(struct pfctl_rule *r, +add_binat_rdr_rule( + struct pfctl_rule *binat_rule, + struct redirspec *binat_nat_redirspec, struct node_host *binat_src_host, + struct pfctl_rule *rdr_rule, struct redirspec **rdr_redirspec, + struct node_host **rdr_dst_host) +{ + struct node_host *rdr_src_host; + + /* + * We're copying the whole rule, but we must re-init redir pools. + * 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)); + TAILQ_INIT(&(rdr_rule->nat.list)); + + /* now specify inbound rdr rule */ + rdr_rule->direction = PF_IN; + + if ((rdr_src_host = calloc(1, sizeof(*rdr_src_host))) == NULL) + err(1, "%s", __func__); + bcopy(binat_src_host, rdr_src_host, sizeof(*rdr_src_host)); + rdr_src_host->ifname = NULL; + rdr_src_host->next = NULL; + rdr_src_host->tail = NULL; + + if (((*rdr_dst_host) = calloc(1, sizeof(**rdr_dst_host))) == NULL) + err(1, "%s", __func__); + bcopy(&(binat_nat_redirspec->host->addr), &((*rdr_dst_host)->addr), + sizeof((*rdr_dst_host)->addr)); + (*rdr_dst_host)->ifname = NULL; + (*rdr_dst_host)->next = NULL; + (*rdr_dst_host)->tail = NULL; + + if (((*rdr_redirspec) = calloc(1, sizeof(**rdr_redirspec))) == NULL) + err(1, "%s", __func__); + bcopy(binat_nat_redirspec, (*rdr_redirspec), sizeof(**rdr_redirspec)); + (*rdr_redirspec)->pool_opts.staticport = 0; + (*rdr_redirspec)->host = rdr_src_host; +} + +void +expand_rule(struct pfctl_rule *r, bool keeprule, struct node_if *interfaces, struct redirspec *nat, struct redirspec *rdr, struct redirspec *route, - struct node_host *rdr_hosts, struct node_host *nat_hosts, - struct node_host *route_hosts, struct node_proto *protos, + struct node_proto *protos, struct node_os *src_oses, struct node_host *src_hosts, struct node_port *src_ports, struct node_host *dst_hosts, struct node_port *dst_ports, struct node_uid *uids, struct node_gid *gids, - struct node_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; @@ -6178,17 +6325,16 @@ expand_rule(struct pfctl_rule *r, char label[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE]; char tagname[PF_TAG_NAME_SIZE]; char match_tagname[PF_TAG_NAME_SIZE]; - struct pf_pooladdr *pa; - struct node_host *h, *osrch, *odsth; + struct node_host *osrch, *odsth; u_int8_t flags, flagset, keep_state; memcpy(label, r->label, sizeof(r->label)); assert(sizeof(r->label) == sizeof(label)); if (strlcpy(tagname, r->tagname, sizeof(tagname)) >= sizeof(tagname)) - errx(1, "expand_rule: strlcpy"); + errx(1, "%s: strlcpy", __func__); if (strlcpy(match_tagname, r->match_tagname, sizeof(match_tagname)) >= sizeof(match_tagname)) - errx(1, "expand_rule: strlcpy"); + errx(1, "%s: strlcpy", __func__); flags = r->flags; flagset = r->flagset; keep_state = r->keep_state; @@ -6241,21 +6387,21 @@ expand_rule(struct pfctl_rule *r, memcpy(r->label, label, sizeof(r->label)); if (strlcpy(r->tagname, tagname, sizeof(r->tagname)) >= sizeof(r->tagname)) - errx(1, "expand_rule: strlcpy"); + errx(1, "%s: strlcpy", __func__); if (strlcpy(r->match_tagname, match_tagname, sizeof(r->match_tagname)) >= sizeof(r->match_tagname)) - errx(1, "expand_rule: strlcpy"); + errx(1, "%s: strlcpy", __func__); osrch = odsth = NULL; if (src_host->addr.type == PF_ADDR_DYNIFTL) { osrch = src_host; if ((src_host = gen_dynnode(src_host, r->af)) == NULL) - err(1, "expand_rule: calloc"); + err(1, "%s: calloc", __func__); } if (dst_host->addr.type == PF_ADDR_DYNIFTL) { odsth = dst_host; if ((dst_host = gen_dynnode(dst_host, r->af)) == NULL) - err(1, "expand_rule: calloc"); + err(1, "%s: calloc", __func__); } error += check_netmask(src_host, r->af); @@ -6317,63 +6463,50 @@ expand_rule(struct pfctl_rule *r, r->os_fingerprint = PF_OSFP_ANY; } - TAILQ_INIT(&r->rdr.list); - for (h = rdr_hosts; h != NULL; h = h->next) { - pa = calloc(1, sizeof(struct pf_pooladdr)); - if (pa == NULL) - err(1, "expand_rule: calloc"); - pa->addr = h->addr; - if (h->ifname != NULL) { - if (strlcpy(pa->ifname, h->ifname, - sizeof(pa->ifname)) >= - sizeof(pa->ifname)) - errx(1, "expand_rule: strlcpy"); - } else - pa->ifname[0] = 0; - TAILQ_INSERT_TAIL(&r->rdr.list, pa, entries); - } - TAILQ_INIT(&r->nat.list); - for (h = nat_hosts; h != NULL; h = h->next) { - pa = calloc(1, sizeof(struct pf_pooladdr)); - if (pa == NULL) - err(1, "expand_rule: calloc"); - pa->addr = h->addr; - if (h->ifname != NULL) { - if (strlcpy(pa->ifname, h->ifname, - sizeof(pa->ifname)) >= - sizeof(pa->ifname)) - errx(1, "expand_rule: strlcpy"); - } else - pa->ifname[0] = 0; - TAILQ_INSERT_TAIL(&r->nat.list, pa, entries); - } - TAILQ_INIT(&r->route.list); - for (h = route_hosts; h != NULL; h = h->next) { - pa = calloc(1, sizeof(struct pf_pooladdr)); - if (pa == NULL) - err(1, "expand_rule: calloc"); - pa->addr = h->addr; - if (h->ifname != NULL) { - if (strlcpy(pa->ifname, h->ifname, - sizeof(pa->ifname)) >= - sizeof(pa->ifname)) - errx(1, "expand_rule: strlcpy"); - } else - pa->ifname[0] = 0; - TAILQ_INSERT_TAIL(&r->route.list, pa, entries); - } + if (r->action == PF_RDR) { + /* Pre-FreeBSD 15 "rdr" rule */ + error += apply_rdr_ports(r, &(r->rdr), rdr); + error += apply_redirspec(&(r->rdr), rdr); + } else if (r->action == PF_NAT) { + /* Pre-FreeBSD 15 "nat" rule */ + error += apply_nat_ports(&(r->rdr), rdr); + error += apply_redirspec(&(r->rdr), rdr); + } else { + /* Modern rule with optional NAT, BINAT, RDR or ROUTE*/ + error += apply_redirspec(&(r->route), route); - r->nat.proxy_port[0] = PF_NAT_PROXY_PORT_LOW; - r->nat.proxy_port[1] = PF_NAT_PROXY_PORT_HIGH; + error += apply_nat_ports(&(r->nat), nat); + error += apply_redirspec(&(r->nat), nat); + error += apply_rdr_ports(r, &(r->rdr), rdr); + error += apply_redirspec(&(r->rdr), rdr); - if (rule_consistent(r, anchor_call[0]) < 0 || error) + if (nat && nat->binat) + error += check_binat_redirspec(src_host, r, af); + } + + 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++; } + /* Generate binat's matching inbound rule */ + if (!error && nat && nat->binat) { + struct pfctl_rule rdr_rule; + struct redirspec *rdr_redirspec; + struct node_host *rdr_dst_host; + + add_binat_rdr_rule( + r, nat, src_hosts, + &rdr_rule, &rdr_redirspec, &rdr_dst_host); + + 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); + } + if (osrch && src_host->addr.type == PF_ADDR_DYNIFTL) { free(src_host); src_host = osrch; @@ -6385,18 +6518,30 @@ expand_rule(struct pfctl_rule *r, )))))))))); - FREE_LIST(struct node_if, interfaces); - FREE_LIST(struct node_proto, protos); - FREE_LIST(struct node_host, src_hosts); - FREE_LIST(struct node_port, src_ports); - FREE_LIST(struct node_os, src_oses); - FREE_LIST(struct node_host, dst_hosts); - FREE_LIST(struct node_port, dst_ports); - FREE_LIST(struct node_uid, uids); - FREE_LIST(struct node_gid, gids); - FREE_LIST(struct node_icmp, icmp_types); - FREE_LIST(struct node_host, rdr_hosts); - FREE_LIST(struct node_host, nat_hosts); + if (!keeprule) { + FREE_LIST(struct node_if, interfaces); + FREE_LIST(struct node_proto, protos); + FREE_LIST(struct node_host, src_hosts); + FREE_LIST(struct node_port, src_ports); + FREE_LIST(struct node_os, src_oses); + FREE_LIST(struct node_host, dst_hosts); + FREE_LIST(struct node_port, dst_ports); + FREE_LIST(struct node_uid, uids); + FREE_LIST(struct node_gid, gids); + FREE_LIST(struct node_icmp, icmp_types); + if (nat) { + FREE_LIST(struct node_host, nat->host); + free(nat); + } + if (rdr) { + FREE_LIST(struct node_host, rdr->host); + free(rdr); + } + if (route) { + FREE_LIST(struct node_host, route->host); + free(route); + } + } if (!added) yyerror("rule expands to no valid combination"); @@ -6481,6 +6626,7 @@ lookup(char *s) { "bandwidth", BANDWIDTH}, { "binat", BINAT}, { "binat-anchor", BINATANCHOR}, + { "binat-to", BINATTO}, { "bitmask", BITMASK}, { "block", BLOCK}, { "block-policy", BLOCKPOLICY}, @@ -6536,6 +6682,8 @@ lookup(char *s) { "matches", MATCHES}, { "max", MAXIMUM}, { "max-mss", MAXMSS}, + { "max-pkt-rate", MAXPKTRATE}, + { "max-pkt-size", MAXPKTSIZE}, { "max-src-conn", MAXSRCCONN}, { "max-src-conn-rate", MAXSRCCONNRATE}, { "max-src-nodes", MAXSRCNODES}, @@ -6544,11 +6692,13 @@ lookup(char *s) { "modulate", MODULATE}, { "nat", NAT}, { "nat-anchor", NATANCHOR}, + { "nat-to", NATTO}, { "no", NO}, { "no-df", NODF}, { "no-route", NOROUTE}, { "no-sync", NOSYNC}, { "on", ON}, + { "once", ONCE}, { "optimization", OPTIMIZATION}, { "os", OS}, { "out", OUT}, @@ -6556,6 +6706,7 @@ lookup(char *s) { "pass", PASS}, { "pflow", PFLOW}, { "port", PORT}, + { "prefer-ipv6-nexthop", IPV6NH}, { "prio", PRIO}, { "priority", PRIORITY}, { "priq", PRIQ}, @@ -6568,6 +6719,7 @@ lookup(char *s) { "random-id", RANDOMID}, { "rdr", RDR}, { "rdr-anchor", RDRANCHOR}, + { "rdr-to", RDRTO}, { "realtime", REALTIME}, { "reassemble", REASSEMBLE}, { "received-on", RECEIVEDON}, @@ -6627,44 +6779,47 @@ lookup(char *s) } } -#define MAXPUSHBACK 128 +#define START_EXPAND 1 +#define DONE_EXPAND 2 -static char *parsebuf; -static int parseindex; -static char pushback_buffer[MAXPUSHBACK]; -static int pushback_index = 0; +static int expanding; int -lgetc(int quotec) +igetc(void) { - int c, next; - - if (parsebuf) { - /* Read character from the parsebuffer instead of input. */ - if (parseindex >= 0) { - c = parsebuf[parseindex++]; - if (c != '\0') - return (c); - parsebuf = NULL; - } else - parseindex++; + int c; + while (1) { + if (file->ungetpos > 0) + c = file->ungetbuf[--file->ungetpos]; + else + c = getc(file->stream); + if (c == START_EXPAND) + expanding = 1; + else if (c == DONE_EXPAND) + expanding = 0; + else + break; } + return (c); +} - if (pushback_index) - return (pushback_buffer[--pushback_index]); +int +lgetc(int quotec) +{ + int c, next; if (quotec) { - if ((c = getc(file->stream)) == EOF) { + if ((c = igetc()) == EOF) { yyerror("reached end of file while parsing quoted string"); - if (popfile() == EOF) + if (file == topfile || popfile() == EOF) return (EOF); return (quotec); } return (c); } - while ((c = getc(file->stream)) == '\\') { - next = getc(file->stream); + while ((c = igetc()) == '\\') { + next = igetc(); if (next != '\n') { c = next; break; @@ -6673,28 +6828,38 @@ lgetc(int quotec) file->lineno++; } - while (c == EOF) { - if (popfile() == EOF) - return (EOF); - c = getc(file->stream); + if (c == EOF) { + /* + * Fake EOL when hit EOF for the first time. This gets line + * count right if last line in included file is syntactically + * invalid and has no newline. + */ + if (file->eof_reached == 0) { + file->eof_reached = 1; + return ('\n'); + } + while (c == EOF) { + if (file == topfile || popfile() == EOF) + return (EOF); + c = igetc(); + } } return (c); } -int +void lungetc(int c) { if (c == EOF) - return (EOF); - if (parsebuf) { - parseindex--; - if (parseindex >= 0) - return (c); + return; + if (file->ungetpos >= file->ungetsize) { + void *p = reallocarray(file->ungetbuf, file->ungetsize, 2); + if (p == NULL) + err(1, "%s", __func__); + file->ungetbuf = p; + file->ungetsize *= 2; } - if (pushback_index < MAXPUSHBACK-1) - return (pushback_buffer[pushback_index++] = c); - else - return (EOF); + file->ungetbuf[file->ungetpos++] = c; } int @@ -6702,14 +6867,9 @@ findeol(void) { int c; - parsebuf = NULL; - /* skip to either EOF or the first real EOL */ while (1) { - if (pushback_index) - c = pushback_buffer[--pushback_index]; - else - c = lgetc(0); + c = lgetc(0); if (c == '\n') { file->lineno++; break; @@ -6737,7 +6897,7 @@ top: if (c == '#') while ((c = lgetc(0)) != '\n' && c != EOF) ; /* nothing */ - if (c == '$' && parsebuf == NULL) { + if (c == '$' && !expanding) { while (1) { if ((c = lgetc(0)) == EOF) return (0); @@ -6759,8 +6919,13 @@ top: yyerror("macro '%s' not defined", buf); return (findeol()); } - parsebuf = val; - parseindex = 0; + p = val + strlen(val) - 1; + lungetc(DONE_EXPAND); + while (p >= val) { + lungetc(*p); + p--; + } + lungetc(START_EXPAND); goto top; } @@ -6777,7 +6942,8 @@ top: } else if (c == '\\') { if ((next = lgetc(quotec)) == EOF) return (0); - if (next == quotec || c == ' ' || c == '\t') + if (next == quotec || next == ' ' || + next == '\t') c = next; else if (next == '\n') { file->lineno++; @@ -6800,7 +6966,7 @@ top: } yylval.v.string = strdup(buf); if (yylval.v.string == NULL) - err(1, "yylex: strdup"); + err(1, "%s: strdup", __func__); return (STRING); case '!': next = lgetc(0); @@ -6840,7 +7006,7 @@ top: if (c == '-' || isdigit(c)) { do { *p++ = c; - if ((unsigned)(p-buf) >= sizeof(buf)) { + if ((size_t)(p-buf) >= sizeof(buf)) { yyerror("string too long"); return (findeol()); } @@ -6879,7 +7045,7 @@ nodigits: if (isalnum(c) || c == ':' || c == '_') { do { *p++ = c; - if ((unsigned)(p-buf) >= sizeof(buf)) { + if ((size_t)(p-buf) >= sizeof(buf)) { yyerror("string too long"); return (findeol()); } @@ -6888,7 +7054,7 @@ nodigits: *p = '\0'; if ((token = lookup(buf)) == STRING) if ((yylval.v.string = strdup(buf)) == NULL) - err(1, "yylex: strdup"); + err(1, "%s: strdup", __func__); return (token); } if (c == '\n') { @@ -6927,19 +7093,21 @@ pushfile(const char *name, int secret) if ((nfile = calloc(1, sizeof(struct file))) == NULL || (nfile->name = strdup(name)) == NULL) { - warn("malloc"); + warn("%s", __func__); + if (nfile) + free(nfile); return (NULL); } if (TAILQ_FIRST(&files) == NULL && strcmp(nfile->name, "-") == 0) { nfile->stream = stdin; free(nfile->name); if ((nfile->name = strdup("stdin")) == NULL) { - warn("strdup"); + warn("%s", __func__); free(nfile); return (NULL); } - } else if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { - warn("%s", nfile->name); + } else if ((nfile->stream = pfctl_fopen(nfile->name, "r")) == NULL) { + warn("%s: %s", __func__, nfile->name); free(nfile->name); free(nfile); return (NULL); @@ -6950,7 +7118,16 @@ pushfile(const char *name, int secret) free(nfile); return (NULL); } - nfile->lineno = 1; + nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0; + nfile->ungetsize = 16; + nfile->ungetbuf = malloc(nfile->ungetsize); + if (nfile->ungetbuf == NULL) { + warn("%s", __func__); + fclose(nfile->stream); + free(nfile->name); + free(nfile); + return (NULL); + } TAILQ_INSERT_TAIL(&files, nfile, entry); return (nfile); } @@ -6960,16 +7137,17 @@ popfile(void) { struct file *prev; - if ((prev = TAILQ_PREV(file, files, entry)) != NULL) { + if ((prev = TAILQ_PREV(file, files, entry)) != NULL) prev->errors += file->errors; - TAILQ_REMOVE(&files, file, entry); - fclose(file->stream); - free(file->name); - free(file); - file = prev; - return (0); - } - return (EOF); + + TAILQ_REMOVE(&files, file, entry); + fclose(file->stream); + free(file->name); + free(file->ungetbuf); + free(file); + file = prev; + + return (file ? 0 : EOF); } int @@ -6992,6 +7170,7 @@ parse_config(char *filename, struct pfctl *xpf) warn("cannot open the main config file!"); return (-1); } + topfile = file; yyparse(); errors = file->errors; @@ -7016,9 +7195,10 @@ symset(const char *nam, const char *val, int persist) { struct sym *sym; - for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); - sym = TAILQ_NEXT(sym, entry)) - ; /* nothing */ + TAILQ_FOREACH(sym, &symhead, entry) { + if (strcmp(nam, sym->nam) == 0) + break; + } if (sym != NULL) { if (sym->persist == 1) @@ -7059,10 +7239,9 @@ pfctl_cmdline_symset(char *s) if ((val = strrchr(s, '=')) == NULL) return (-1); - if ((sym = malloc(strlen(s) - strlen(val) + 1)) == NULL) - err(1, "pfctl_cmdline_symset: malloc"); - - strlcpy(sym, s, strlen(s) - strlen(val) + 1); + sym = strndup(s, val - s); + if (sym == NULL) + err(1, "%s: malloc", __func__); ret = symset(sym, val + 1, 1); free(sym); @@ -7075,11 +7254,12 @@ symget(const char *nam) { struct sym *sym; - TAILQ_FOREACH(sym, &symhead, entry) + TAILQ_FOREACH(sym, &symhead, entry) { if (strcmp(nam, sym->nam) == 0) { sym->used = 1; return (sym->val); } + } return (NULL); } @@ -7090,19 +7270,11 @@ mv_rules(struct pfctl_ruleset *src, struct pfctl_ruleset *dst) struct pfctl_rule *r; for (i = 0; i < PF_RULESET_MAX; ++i) { - while ((r = TAILQ_FIRST(src->rules[i].active.ptr)) - != NULL) { - TAILQ_REMOVE(src->rules[i].active.ptr, r, entries); - TAILQ_INSERT_TAIL(dst->rules[i].active.ptr, r, entries); + TAILQ_FOREACH(r, src->rules[i].active.ptr, entries) dst->anchor->match++; - } + TAILQ_CONCAT(dst->rules[i].active.ptr, src->rules[i].active.ptr, entries); src->anchor->match = 0; - while ((r = TAILQ_FIRST(src->rules[i].inactive.ptr)) - != NULL) { - TAILQ_REMOVE(src->rules[i].inactive.ptr, r, entries); - TAILQ_INSERT_TAIL(dst->rules[i].inactive.ptr, - r, entries); - } + TAILQ_CONCAT(dst->rules[i].inactive.ptr, src->rules[i].inactive.ptr, entries); } } @@ -7120,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) @@ -7328,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; @@ -7337,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); } @@ -7423,7 +7650,7 @@ node_mac_from_string(const char *str) m = calloc(1, sizeof(struct node_mac)); if (m == NULL) - err(1, "mac: calloc"); + err(1, "%s: calloc", __func__); if (sscanf(str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", &m->mac[0], &m->mac[1], &m->mac[2], &m->mac[3], &m->mac[4], @@ -7481,3 +7708,172 @@ node_mac_from_string_mask(const char *str, const char *mask) return (m); } + +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; + r->prob = opts->prob; + r->rtableid = opts->rtableid; + r->ridentifier = opts->ridentifier; + r->max_pkt_size = opts->max_pkt_size; + r->tos = opts->tos; + + if (opts->nodf) + r->scrub_flags |= PFSTATE_NODF; + if (opts->randomid) + r->scrub_flags |= PFSTATE_RANDOMID; + if (opts->minttl) + r->min_ttl = opts->minttl; + if (opts->max_mss) + r->max_mss = opts->max_mss; + + if (opts->tag) + if (strlcpy(r->tagname, opts->tag, + PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { + yyerror("tag too long, max %u chars", + PF_TAG_NAME_SIZE - 1); + return (1); + } + if (opts->match_tag) + if (strlcpy(r->match_tagname, opts->match_tag, + PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { + yyerror("tag too long, max %u chars", + PF_TAG_NAME_SIZE - 1); + return (1); + } + r->match_tag_not = opts->match_tag_not; + + if (rule_label(r, opts->label)) + return (1); + for (int i = 0; i < PF_RULE_MAX_LABEL_COUNT; i++) + free(opts->label[i]); + + if (opts->marker & FOM_AFTO) + r->rule_flag |= PFRULE_AFTO; + if (opts->marker & FOM_SCRUB_TCP) + r->scrub_flags |= PFSTATE_SCRUB_TCP; + if (opts->marker & FOM_PRIO) + r->prio = opts->prio ? opts->prio : PF_PRIO_ZERO; + if (opts->marker & FOM_SETPRIO) { + r->set_prio[0] = opts->set_prio[0]; + r->set_prio[1] = opts->set_prio[1]; + r->scrub_flags |= PFSTATE_SETPRIO; + } + if (opts->marker & FOM_SETTOS) { + r->scrub_flags |= PFSTATE_SETTOS; + r->set_tos = opts->settos; + } + + r->flags = opts->flags.b1; + r->flagset = opts->flags.b2; + if ((opts->flags.b1 & opts->flags.b2) != opts->flags.b1) { + yyerror("flags always false"); + return (1); + } + + if (opts->queues.qname != NULL) { + if (strlcpy(r->qname, opts->queues.qname, + sizeof(r->qname)) >= sizeof(r->qname)) { + yyerror("rule qname too long (max " + "%d chars)", sizeof(r->qname)-1); + return (1); + } + free(opts->queues.qname); + } + if (opts->queues.pqname != NULL) { + if (strlcpy(r->pqname, opts->queues.pqname, + sizeof(r->pqname)) >= sizeof(r->pqname)) { + yyerror("rule pqname too long (max " + "%d chars)", sizeof(r->pqname)-1); + return (1); + } + free(opts->queues.pqname); + } + + if (opts->fragment) + r->rule_flag |= PFRULE_FRAGMENT; + r->allow_opts = opts->allowopts; + + 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 1d2fa45cd9d7..417ff70de975 100644 --- a/sbin/pfctl/pf_print_state.c +++ b/sbin/pfctl/pf_print_state.c @@ -85,31 +85,18 @@ print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose) printf("<%s>", addr->v.tblname); return; case PF_ADDR_RANGE: { - char buf[48]; + print_addr_str(af, &addr->v.a.addr); + printf(" - "); + print_addr_str(af, &addr->v.a.mask); - if (inet_ntop(af, &addr->v.a.addr, buf, sizeof(buf)) == NULL) - printf("?"); - else - printf("%s", buf); - if (inet_ntop(af, &addr->v.a.mask, buf, sizeof(buf)) == NULL) - printf(" - ?"); - else - printf(" - %s", buf); break; } case PF_ADDR_ADDRMASK: if (PF_AZERO(&addr->v.a.addr, AF_INET6) && PF_AZERO(&addr->v.a.mask, AF_INET6)) printf("any"); - else { - char buf[48]; - - if (inet_ntop(af, &addr->v.a.addr, buf, - sizeof(buf)) == NULL) - printf("?"); - else - printf("%s", buf); - } + else + print_addr_str(af, &addr->v.a.addr); break; case PF_ADDR_NOROUTE: printf("no-route"); @@ -126,62 +113,63 @@ 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, af); - - 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); + } } } void +print_addr_str(sa_family_t af, struct pf_addr *addr) +{ + static char buf[48]; + + if (inet_ntop(af, addr, buf, sizeof(buf)) == NULL) + printf("?"); + else + printf("%s", buf); +} + +void print_name(struct pf_addr *addr, sa_family_t af) { - char host[NI_MAXHOST]; - - strlcpy(host, "?", sizeof(host)); - switch (af) { - case AF_INET: { - struct sockaddr_in sin; - - memset(&sin, 0, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; - sin.sin_addr = addr->v4; - getnameinfo((struct sockaddr *)&sin, sin.sin_len, - host, sizeof(host), NULL, 0, NI_NOFQDN); - break; - } - case AF_INET6: { - struct sockaddr_in6 sin6; - - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_len = sizeof(sin6); - sin6.sin6_family = AF_INET6; - sin6.sin6_addr = addr->v6; - getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, - host, sizeof(host), NULL, 0, NI_NOFQDN); - break; - } + struct sockaddr_storage ss; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + char host[NI_MAXHOST]; + + memset(&ss, 0, sizeof(ss)); + ss.ss_family = af; + if (ss.ss_family == AF_INET) { + sin = (struct sockaddr_in *)&ss; + sin->sin_len = sizeof(*sin); + sin->sin_addr = addr->v4; + } else { + sin6 = (struct sockaddr_in6 *)&ss; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_addr = addr->v6; } - printf("%s", host); + + if (getnameinfo((struct sockaddr *)&ss, ss.ss_len, host, sizeof(host), + NULL, 0, NI_NOFQDN) != 0) + printf("?"); + else + printf("%s", host); } void print_host(struct pf_addr *addr, u_int16_t port, sa_family_t af, int opts) { + struct pf_addr_wrap aw; + if (opts & PF_OPT_USEDNS) print_name(addr, af); else { - struct pf_addr_wrap aw; - memset(&aw, 0, sizeof(aw)); aw.v.a.addr = *addr; - if (af == AF_INET) - aw.v.a.mask.addr32[0] = 0xffffffff; - else { - memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask)); - af = AF_INET6; - } + memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask)); print_addr(&aw, af, opts & PF_OPT_VERBOSE2); } @@ -241,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; @@ -255,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) { @@ -443,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); } @@ -457,7 +443,7 @@ print_state(struct pfctl_state *s, int opts) } int -unmask(struct pf_addr *m, sa_family_t af) +unmask(struct pf_addr *m) { int i = 31, j = 0, b = 0; u_int32_t tmp; diff --git a/sbin/pfctl/pfctl.8 b/sbin/pfctl/pfctl.8 index 565cd8f7fd64..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 February 10, 2025 +.Dd August 28, 2025 .Dt PFCTL 8 .Os .Sh NAME @@ -82,7 +82,7 @@ Translation rules are described in .Xr pf.conf 5 . .Pp When the variable -.Va pf +.Va pf_enable is set to .Dv YES in @@ -114,8 +114,10 @@ Other rules and options are ignored. Apply flags .Fl f , .Fl F , +.Fl s , +.Fl T , and -.Fl s +.Fl z only to the rules in the specified .Ar anchor . In addition to the main ruleset, @@ -185,6 +187,13 @@ as the anchor name: .Bd -literal -offset indent # pfctl -a '*' -sr .Ed +.Pp +To flush all rulesets and tables recursively, specify only +.Sq * +as the anchor name: +.Bd -literal -offset indent +# pfctl -a '*' -Fa +.Ed .It Fl D Ar macro Ns = Ns Ar value Define .Ar macro @@ -203,28 +212,46 @@ 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 all +.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 Cm all Flush all of the above. .El +.Pp +If +.Fl a +is specified as well and +.Ar anchor +is terminated with a +.Sq * +character, +.Cm rules , +.Cm Tables +and +.Cm all +flush the given anchor recursively. .It Fl f Ar file Load the rules contained in .Ar file . @@ -254,13 +281,14 @@ option may be specified, which will kill all the source tracking entries from the first host/network to the second. .It Xo .Fl k -.Ar host | network | label | id | gateway | nat +.Ar host | network | label | id | key | gateway | nat .Xc Kill all of the state entries matching the specified .Ar host , .Ar network , .Ar label , .Ar id , +.Ar key , .Ar gateway, or .Ar nat. @@ -293,7 +321,7 @@ To kill all states with the target .Pp .Dl # pfctl -k 0.0.0.0/0 -k host2 .Pp -It is also possible to kill states by rule label or state ID. +It is also possible to kill states by rule label, state key or state ID. In this mode the first .Fl k argument is used to specify the type @@ -304,6 +332,17 @@ from rules carrying the label .Pp .Dl # pfctl -k label -k foobar .Pp +To kill one specific state by its key +(protocol, host1, port1, direction, host2 and port2 in the same format +of pfctl -s state), +use the +.Ar key +modifier and as a second argument the state key. +To kill a state whose protocol is TCP and originating from +10.0.0.101:32123 to 10.0.0.1:80 use: +.Pp +.Dl # pfctl -k key -k 'tcp 10.0.0.1:80 <- 10.0.0.101:32123' +.Pp To kill one specific state by its unique state ID (as shown by pfctl -s state -vv), use the @@ -363,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 @@ -388,16 +428,20 @@ Only print errors and warnings. Load only the filter rules present in the rule file. Other rules and options are ignored. .It Fl r -Perform reverse DNS lookups on states when displaying them. -.It Fl s Ar modifier +Perform reverse DNS lookups on states and tables when displaying them. +.Fl N +and +.Fl r +are mutually exclusive. +.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 , @@ -407,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 @@ -426,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 @@ -437,31 +488,34 @@ 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 , -source tracking statistics are also shown. -.It Fl s Cm Running +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 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 -Show the list of interfaces and interface drivers available to PF. +.It Cm Interfaces +Show the list of interfaces and interface groups available to PF. When used together with .Fl v , it additionally lists which interfaces have skip rules activated. @@ -470,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 @@ -515,44 +569,48 @@ address mapping failed .It translate no free ports in translation port range .El -.It Fl T Ar command Op Ar address ... +.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 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. -Automatically create a nonexisting table. -.It Fl T Cm delete +.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 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 nonexisting table. -.It Fl T Cm show +Automatically create a persistent table if it does not exist. +.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 @@ -717,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 fd76a1926457..ed317495c2e0 100644 --- a/sbin/pfctl/pfctl.c +++ b/sbin/pfctl/pfctl.c @@ -59,6 +59,8 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <stdarg.h> +#include <libgen.h> #include "pfctl_parser.h" #include "pfctl.h" @@ -66,23 +68,26 @@ void usage(void); int pfctl_enable(int, int); int pfctl_disable(int, int); -int pfctl_clear_stats(struct pfctl_handle *, int); -int pfctl_get_skip_ifaces(void); -int pfctl_check_skip_ifaces(char *); -int pfctl_adjust_skip_ifaces(struct pfctl *); -int pfctl_clear_interface_flags(int, int); -int pfctl_flush_eth_rules(int, int, char *); +void pfctl_clear_stats(struct pfctl_handle *, int); +void pfctl_get_skip_ifaces(void); +void pfctl_check_skip_ifaces(char *); +void pfctl_adjust_skip_ifaces(struct pfctl *); +void pfctl_clear_interface_flags(int, int); +void pfctl_flush_eth_rules(int, int, char *); int pfctl_flush_rules(int, int, char *); -int pfctl_flush_nat(int, int, char *); +void pfctl_flush_nat(int, int, char *); int pfctl_clear_altq(int, int); -int pfctl_clear_src_nodes(int, int); -int pfctl_clear_iface_states(int, const char *, int); -void pfctl_addrprefix(char *, struct pf_addr *); -int pfctl_kill_src_nodes(int, const char *, int); -int pfctl_net_kill_states(int, const char *, int); -int pfctl_gateway_kill_states(int, const char *, int); -int pfctl_label_kill_states(int, const char *, int); -int pfctl_id_kill_states(int, const char *, int); +void pfctl_clear_src_nodes(int, int); +void pfctl_clear_iface_states(int, const char *, int); +struct addrinfo * + pfctl_addrprefix(char *, struct pf_addr *, int); +void pfctl_kill_src_nodes(int, int); +void pfctl_net_kill_states(int, const char *, int); +void pfctl_gateway_kill_states(int, const char *, int); +void pfctl_label_kill_states(int, const char *, int); +void pfctl_id_kill_states(int, const char *, int); +void pfctl_key_kill_states(int, const char *, int); +int pfctl_parse_host(char *, struct pf_rule_addr *); void pfctl_init_options(struct pfctl *); int pfctl_load_options(struct pfctl *); int pfctl_load_limit(struct pfctl *, unsigned int, unsigned int); @@ -93,18 +98,20 @@ int pfctl_load_hostid(struct pfctl *, u_int32_t); int pfctl_load_reassembly(struct pfctl *, u_int32_t); int pfctl_load_syncookies(struct pfctl *, u_int8_t); int pfctl_get_pool(int, struct pfctl_pool *, u_int32_t, u_int32_t, int, - char *, int); + const char *, int); void pfctl_print_eth_rule_counters(struct pfctl_eth_rule *, int); void pfctl_print_rule_counters(struct pfctl_rule *, int); int pfctl_show_eth_rules(int, char *, int, enum pfctl_show, char *, int, int); int pfctl_show_rules(int, char *, int, enum pfctl_show, char *, int, int); -int pfctl_show_nat(int, char *, int, char *, int, int); +int pfctl_show_nat(int, const char *, int, char *, int, int); int pfctl_show_src_nodes(int, int); int pfctl_show_states(int, const char *, int); 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 *); @@ -120,6 +127,19 @@ int pfctl_load_ruleset(struct pfctl *, char *, struct pfctl_ruleset *, int, int); int pfctl_load_rule(struct pfctl *, char *, struct pfctl_rule *, int); const char *pfctl_lookup_option(char *, const char * const *); +void pfctl_reset(int, int); +int pfctl_walk_show(int, struct pfioc_ruleset *, void *); +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, 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; @@ -147,6 +167,7 @@ int dev = -1; struct pfctl_handle *pfh = NULL; static int first_title = 1; static int labels = 0; +static int exit_val = 0; #define INDENT(d, o) do { \ if (o) { \ @@ -165,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; @@ -228,7 +253,7 @@ static const struct { static const char * const clearopt_list[] = { "nat", "queue", "rules", "Sources", "states", "info", "Tables", "osfp", "all", - "ethernet", NULL + "ethernet", "Reset", NULL }; static const char * const showopt_list[] = { @@ -256,7 +281,7 @@ usage(void) extern char *__progname; fprintf(stderr, -"usage: %s [-AdeghMmNnOPqRrvz] [-a anchor] [-D macro=value] [-F modifier]\n" +"usage: %s [-AdeghMmNnOPqRSrvz] [-a anchor] [-D macro=value] [-F modifier]\n" "\t[-f file] [-i interface] [-K host | network]\n" "\t[-k host | network | gateway | label | id] [-o level] [-p device]\n" "\t[-s modifier] [-t table -T command [address ...]] [-x level]\n", @@ -265,6 +290,40 @@ usage(void) exit(1); } +void +pfctl_err(int opts, int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + + if ((opts & PF_OPT_IGNFAIL) == 0) + verr(eval, fmt, ap); + else + vwarn(fmt, ap); + + va_end(ap); + + exit_val = eval; +} + +void +pfctl_errx(int opts, int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + + if ((opts & PF_OPT_IGNFAIL) == 0) + verrx(eval, fmt, ap); + else + vwarnx(fmt, ap); + + va_end(ap); + + exit_val = eval; +} + /* * Cache protocol number to name translations. * @@ -352,18 +411,17 @@ pfctl_disable(int dev, int opts) return (0); } -int +void pfctl_clear_stats(struct pfctl_handle *h, int opts) { int ret; if ((ret = pfctl_clear_status(h)) != 0) - errc(1, ret, "DIOCCLRSTATUS"); + pfctl_err(opts, 1, "DIOCCLRSTATUS"); if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "pf: statistics cleared\n"); - return (0); } -int +void pfctl_get_skip_ifaces(void) { bzero(&skip_b, sizeof(skip_b)); @@ -376,10 +434,9 @@ pfctl_get_skip_ifaces(void) if (skip_b.pfrb_size <= skip_b.pfrb_msize) break; } - return (0); } -int +void pfctl_check_skip_ifaces(char *ifname) { struct pfi_kif *p; @@ -401,10 +458,9 @@ pfctl_check_skip_ifaces(char *ifname) } } } - return (0); } -int +void pfctl_adjust_skip_ifaces(struct pfctl *pf) { struct pfi_kif *p, *pp; @@ -437,11 +493,9 @@ pfctl_adjust_skip_ifaces(struct pfctl *pf) pfctl_set_interface_flags(pf, p->pfik_name, PFI_IFLAG_SKIP, 0); } - - return (0); } -int +void pfctl_clear_interface_flags(int dev, int opts) { struct pfioc_iface pi; @@ -455,10 +509,9 @@ pfctl_clear_interface_flags(int dev, int opts) if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "pf: interface flags reset\n"); } - return (0); } -int +void pfctl_flush_eth_rules(int dev, int opts, char *anchorname) { int ret; @@ -469,8 +522,6 @@ pfctl_flush_eth_rules(int dev, int opts, char *anchorname) if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "Ethernet rules cleared\n"); - - return (ret); } int @@ -479,14 +530,16 @@ pfctl_flush_rules(int dev, int opts, char *anchorname) int ret; ret = pfctl_clear_rules(dev, anchorname); - if (ret != 0) - err(1, "pfctl_clear_rules"); - if ((opts & PF_OPT_QUIET) == 0) + if (ret != 0) { + pfctl_err(opts, 1, "%s", __func__); + return (1); + } else if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "rules cleared\n"); + return (0); } -int +void pfctl_flush_nat(int dev, int opts, char *anchorname) { int ret; @@ -496,7 +549,6 @@ pfctl_flush_nat(int dev, int opts, char *anchorname) err(1, "pfctl_clear_nat"); if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "nat cleared\n"); - return (0); } int @@ -517,17 +569,16 @@ pfctl_clear_altq(int dev, int opts) return (0); } -int +void pfctl_clear_src_nodes(int dev, int opts) { if (ioctl(dev, DIOCCLRSRCNODES)) - err(1, "DIOCCLRSRCNODES"); + pfctl_err(opts, 1, "DIOCCLRSRCNODES"); if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "source tracking entries cleared\n"); - return (0); } -int +void pfctl_clear_iface_states(int dev, const char *iface, int opts) { struct pfctl_kill kill; @@ -537,47 +588,47 @@ pfctl_clear_iface_states(int dev, const char *iface, int opts) memset(&kill, 0, sizeof(kill)); if (iface != NULL && strlcpy(kill.ifname, iface, sizeof(kill.ifname)) >= sizeof(kill.ifname)) - errx(1, "invalid interface: %s", iface); + pfctl_errx(opts, 1, "invalid interface: %s", iface); if (opts & PF_OPT_KILLMATCH) kill.kill_match = true; if ((ret = pfctl_clear_states_h(pfh, &kill, &killed)) != 0) - errc(1, ret, "DIOCCLRSTATES"); + pfctl_err(opts, 1, "DIOCCLRSTATUS"); if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "%d states cleared\n", killed); - return (0); } -void -pfctl_addrprefix(char *addr, struct pf_addr *mask) +struct addrinfo * +pfctl_addrprefix(char *addr, struct pf_addr *mask, int numeric) { char *p; const char *errstr; int prefix, ret_ga, q, r; struct addrinfo hints, *res; - if ((p = strchr(addr, '/')) == NULL) - return; - - *p++ = '\0'; - prefix = strtonum(p, 0, 128, &errstr); - if (errstr) - errx(1, "prefix is %s: %s", errstr, p); - bzero(&hints, sizeof(hints)); - /* prefix only with numeric addresses */ - hints.ai_flags |= AI_NUMERICHOST; + hints.ai_socktype = SOCK_DGRAM; /* dummy */ + if (numeric) + hints.ai_flags = AI_NUMERICHOST; + + if ((p = strchr(addr, '/')) != NULL) { + *p++ = '\0'; + /* prefix only with numeric addresses */ + hints.ai_flags |= AI_NUMERICHOST; + } if ((ret_ga = getaddrinfo(addr, NULL, &hints, &res))) { errx(1, "getaddrinfo: %s", gai_strerror(ret_ga)); /* NOTREACHED */ } - if (res->ai_family == AF_INET && prefix > 32) - errx(1, "prefix too long for AF_INET"); - else if (res->ai_family == AF_INET6 && prefix > 128) - errx(1, "prefix too long for AF_INET6"); + if (p == NULL) + return (res); + + prefix = strtonum(p, 0, res->ai_family == AF_INET6 ? 128 : 32, &errstr); + if (errstr) + errx(1, "prefix is %s: %s", errstr, p); q = prefix >> 3; r = prefix & 7; @@ -596,17 +647,17 @@ pfctl_addrprefix(char *addr, struct pf_addr *mask) (0xff00 >> r) & 0xff; break; } - freeaddrinfo(res); + + return (res); } -int -pfctl_kill_src_nodes(int dev, const char *iface, int opts) +void +pfctl_kill_src_nodes(int dev, int opts) { struct pfioc_src_node_kill psnk; struct addrinfo *res[2], *resp[2]; struct sockaddr last_src, last_dst; int killed, sources, dests; - int ret_ga; killed = sources = dests = 0; @@ -616,12 +667,9 @@ pfctl_kill_src_nodes(int dev, const char *iface, int opts) memset(&last_src, 0xff, sizeof(last_src)); memset(&last_dst, 0xff, sizeof(last_dst)); - pfctl_addrprefix(src_node_kill[0], &psnk.psnk_src.addr.v.a.mask); + res[0] = pfctl_addrprefix(src_node_kill[0], + &psnk.psnk_src.addr.v.a.mask, (opts & PF_OPT_NODNS)); - if ((ret_ga = getaddrinfo(src_node_kill[0], NULL, NULL, &res[0]))) { - errx(1, "getaddrinfo: %s", gai_strerror(ret_ga)); - /* NOTREACHED */ - } for (resp[0] = res[0]; resp[0]; resp[0] = resp[0]->ai_next) { if (resp[0]->ai_addr == NULL) continue; @@ -633,29 +681,16 @@ pfctl_kill_src_nodes(int dev, const char *iface, int opts) psnk.psnk_af = resp[0]->ai_family; sources++; - if (psnk.psnk_af == AF_INET) - psnk.psnk_src.addr.v.a.addr.v4 = - ((struct sockaddr_in *)resp[0]->ai_addr)->sin_addr; - else if (psnk.psnk_af == AF_INET6) - psnk.psnk_src.addr.v.a.addr.v6 = - ((struct sockaddr_in6 *)resp[0]->ai_addr)-> - sin6_addr; - else - errx(1, "Unknown address family %d", psnk.psnk_af); + copy_satopfaddr(&psnk.psnk_src.addr.v.a.addr, resp[0]->ai_addr); if (src_node_killers > 1) { dests = 0; memset(&psnk.psnk_dst.addr.v.a.mask, 0xff, sizeof(psnk.psnk_dst.addr.v.a.mask)); memset(&last_dst, 0xff, sizeof(last_dst)); - pfctl_addrprefix(src_node_kill[1], - &psnk.psnk_dst.addr.v.a.mask); - if ((ret_ga = getaddrinfo(src_node_kill[1], NULL, NULL, - &res[1]))) { - errx(1, "getaddrinfo: %s", - gai_strerror(ret_ga)); - /* NOTREACHED */ - } + res[1] = pfctl_addrprefix(src_node_kill[1], + &psnk.psnk_dst.addr.v.a.mask, + (opts & PF_OPT_NODNS)); for (resp[1] = res[1]; resp[1]; resp[1] = resp[1]->ai_next) { if (resp[1]->ai_addr == NULL) @@ -670,18 +705,8 @@ pfctl_kill_src_nodes(int dev, const char *iface, int opts) dests++; - if (psnk.psnk_af == AF_INET) - psnk.psnk_dst.addr.v.a.addr.v4 = - ((struct sockaddr_in *)resp[1]-> - ai_addr)->sin_addr; - else if (psnk.psnk_af == AF_INET6) - psnk.psnk_dst.addr.v.a.addr.v6 = - ((struct sockaddr_in6 *)resp[1]-> - ai_addr)->sin6_addr; - else - errx(1, "Unknown address family %d", - psnk.psnk_af); - + copy_satopfaddr(&psnk.psnk_dst.addr.v.a.addr, + resp[1]->ai_addr); if (ioctl(dev, DIOCKILLSRCNODES, &psnk)) err(1, "DIOCKILLSRCNODES"); killed += psnk.psnk_killed; @@ -699,10 +724,9 @@ pfctl_kill_src_nodes(int dev, const char *iface, int opts) if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "killed %d src nodes from %d sources and %d " "destinations\n", killed, sources, dests); - return (0); } -int +void pfctl_net_kill_states(int dev, const char *iface, int opts) { struct pfctl_kill kill; @@ -710,7 +734,7 @@ pfctl_net_kill_states(int dev, const char *iface, int opts) struct sockaddr last_src, last_dst; unsigned int newkilled; int killed, sources, dests; - int ret_ga, ret; + int ret; killed = sources = dests = 0; @@ -721,7 +745,7 @@ pfctl_net_kill_states(int dev, const char *iface, int opts) memset(&last_dst, 0xff, sizeof(last_dst)); if (iface != NULL && strlcpy(kill.ifname, iface, sizeof(kill.ifname)) >= sizeof(kill.ifname)) - errx(1, "invalid interface: %s", iface); + pfctl_errx(opts, 1, "invalid interface: %s", iface); if (state_killers == 2 && (strcmp(state_kill[0], "nat") == 0)) { kill.nat = true; @@ -729,15 +753,12 @@ pfctl_net_kill_states(int dev, const char *iface, int opts) state_killers = 1; } - pfctl_addrprefix(state_kill[0], &kill.src.addr.v.a.mask); + res[0] = pfctl_addrprefix(state_kill[0], + &kill.src.addr.v.a.mask, (opts & PF_OPT_NODNS)); if (opts & PF_OPT_KILLMATCH) kill.kill_match = true; - if ((ret_ga = getaddrinfo(state_kill[0], NULL, NULL, &res[0]))) { - errx(1, "getaddrinfo: %s", gai_strerror(ret_ga)); - /* NOTREACHED */ - } for (resp[0] = res[0]; resp[0]; resp[0] = resp[0]->ai_next) { if (resp[0]->ai_addr == NULL) continue; @@ -749,29 +770,16 @@ pfctl_net_kill_states(int dev, const char *iface, int opts) kill.af = resp[0]->ai_family; sources++; - if (kill.af == AF_INET) - kill.src.addr.v.a.addr.v4 = - ((struct sockaddr_in *)resp[0]->ai_addr)->sin_addr; - else if (kill.af == AF_INET6) - kill.src.addr.v.a.addr.v6 = - ((struct sockaddr_in6 *)resp[0]->ai_addr)-> - sin6_addr; - else - errx(1, "Unknown address family %d", kill.af); + copy_satopfaddr(&kill.src.addr.v.a.addr, resp[0]->ai_addr); if (state_killers > 1) { dests = 0; memset(&kill.dst.addr.v.a.mask, 0xff, sizeof(kill.dst.addr.v.a.mask)); memset(&last_dst, 0xff, sizeof(last_dst)); - pfctl_addrprefix(state_kill[1], - &kill.dst.addr.v.a.mask); - if ((ret_ga = getaddrinfo(state_kill[1], NULL, NULL, - &res[1]))) { - errx(1, "getaddrinfo: %s", - gai_strerror(ret_ga)); - /* NOTREACHED */ - } + res[1] = pfctl_addrprefix(state_kill[1], + &kill.dst.addr.v.a.mask, + (opts & PF_OPT_NODNS)); for (resp[1] = res[1]; resp[1]; resp[1] = resp[1]->ai_next) { if (resp[1]->ai_addr == NULL) @@ -786,26 +794,17 @@ pfctl_net_kill_states(int dev, const char *iface, int opts) dests++; - if (kill.af == AF_INET) - kill.dst.addr.v.a.addr.v4 = - ((struct sockaddr_in *)resp[1]-> - ai_addr)->sin_addr; - else if (kill.af == AF_INET6) - kill.dst.addr.v.a.addr.v6 = - ((struct sockaddr_in6 *)resp[1]-> - ai_addr)->sin6_addr; - else - errx(1, "Unknown address family %d", - kill.af); + copy_satopfaddr(&kill.dst.addr.v.a.addr, + resp[1]->ai_addr); if ((ret = pfctl_kill_states_h(pfh, &kill, &newkilled)) != 0) - errc(1, ret, "DIOCKILLSTATES"); + pfctl_errx(opts, 1, "DIOCKILLSTATES"); killed += newkilled; } freeaddrinfo(res[1]); } else { if ((ret = pfctl_kill_states_h(pfh, &kill, &newkilled)) != 0) - errc(1, ret, "DIOCKILLSTATES"); + pfctl_errx(opts, 1, "DIOCKILLSTATES"); killed += newkilled; } } @@ -815,10 +814,9 @@ pfctl_net_kill_states(int dev, const char *iface, int opts) if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "killed %d states from %d sources and %d " "destinations\n", killed, sources, dests); - return (0); } -int +void pfctl_gateway_kill_states(int dev, const char *iface, int opts) { struct pfctl_kill kill; @@ -826,7 +824,6 @@ pfctl_gateway_kill_states(int dev, const char *iface, int opts) struct sockaddr last_src; unsigned int newkilled; int killed = 0; - int ret_ga; if (state_killers != 2 || (strlen(state_kill[1]) == 0)) { warnx("no gateway specified"); @@ -839,17 +836,14 @@ pfctl_gateway_kill_states(int dev, const char *iface, int opts) memset(&last_src, 0xff, sizeof(last_src)); if (iface != NULL && strlcpy(kill.ifname, iface, sizeof(kill.ifname)) >= sizeof(kill.ifname)) - errx(1, "invalid interface: %s", iface); + pfctl_errx(opts, 1, "invalid interface: %s", iface); if (opts & PF_OPT_KILLMATCH) kill.kill_match = true; - pfctl_addrprefix(state_kill[1], &kill.rt_addr.addr.v.a.mask); + res = pfctl_addrprefix(state_kill[1], &kill.rt_addr.addr.v.a.mask, + (opts & PF_OPT_NODNS)); - if ((ret_ga = getaddrinfo(state_kill[1], NULL, NULL, &res))) { - errx(1, "getaddrinfo: %s", gai_strerror(ret_ga)); - /* NOTREACHED */ - } for (resp = res; resp; resp = resp->ai_next) { if (resp->ai_addr == NULL) continue; @@ -860,18 +854,10 @@ pfctl_gateway_kill_states(int dev, const char *iface, int opts) kill.af = resp->ai_family; - if (kill.af == AF_INET) - kill.rt_addr.addr.v.a.addr.v4 = - ((struct sockaddr_in *)resp->ai_addr)->sin_addr; - else if (kill.af == AF_INET6) - kill.rt_addr.addr.v.a.addr.v6 = - ((struct sockaddr_in6 *)resp->ai_addr)-> - sin6_addr; - else - errx(1, "Unknown address family %d", kill.af); - + copy_satopfaddr(&kill.rt_addr.addr.v.a.addr, + resp->ai_addr); if (pfctl_kill_states_h(pfh, &kill, &newkilled)) - err(1, "DIOCKILLSTATES"); + pfctl_errx(opts, 1, "DIOCKILLSTATES"); killed += newkilled; } @@ -879,10 +865,9 @@ pfctl_gateway_kill_states(int dev, const char *iface, int opts) if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "killed %d states\n", killed); - return (0); } -int +void pfctl_label_kill_states(int dev, const char *iface, int opts) { struct pfctl_kill kill; @@ -896,7 +881,7 @@ pfctl_label_kill_states(int dev, const char *iface, int opts) memset(&kill, 0, sizeof(kill)); if (iface != NULL && strlcpy(kill.ifname, iface, sizeof(kill.ifname)) >= sizeof(kill.ifname)) - errx(1, "invalid interface: %s", iface); + pfctl_errx(opts, 1, "invalid interface: %s", iface); if (opts & PF_OPT_KILLMATCH) kill.kill_match = true; @@ -906,15 +891,13 @@ pfctl_label_kill_states(int dev, const char *iface, int opts) errx(1, "label too long: %s", state_kill[1]); if ((ret = pfctl_kill_states_h(pfh, &kill, &killed)) != 0) - errc(1, ret, "DIOCKILLSTATES"); + pfctl_errx(opts, 1, "DIOCKILLSTATES"); if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "killed %d states\n", killed); - - return (0); } -int +void pfctl_id_kill_states(int dev, const char *iface, int opts) { struct pfctl_kill kill; @@ -946,20 +929,123 @@ pfctl_id_kill_states(int dev, const char *iface, int opts) } if ((ret = pfctl_kill_states_h(pfh, &kill, &killed)) != 0) - errc(1, ret, "DIOCKILLSTATES"); + pfctl_errx(opts, 1, "DIOCKILLSTATES"); + + if ((opts & PF_OPT_QUIET) == 0) + fprintf(stderr, "killed %d states\n", killed); +} + +void +pfctl_key_kill_states(int dev, const char *iface, int opts) +{ + struct pfctl_kill kill; + char *s, *token, *tokens[4]; + struct protoent *p; + u_int i, sidx, didx; + int ret, killed; + + if (state_killers != 2 || (strlen(state_kill[1]) == 0)) { + warnx("no key specified"); + usage(); + } + memset(&kill, 0, sizeof(kill)); + + if (iface != NULL && + strlcpy(kill.ifname, iface, sizeof(kill.ifname)) >= + sizeof(kill.ifname)) + pfctl_errx(opts, 1, "invalid interface: %s", iface); + + s = strdup(state_kill[1]); + if (!s) + errx(1, "%s: strdup", __func__); + i = 0; + while ((token = strsep(&s, " \t")) != NULL) + if (*token != '\0') { + if (i < 4) + tokens[i] = token; + i++; + } + if (i != 4) + errx(1, "%s: key must be " + "\"protocol host1:port1 direction host2:port2\" format", + __func__); + + if ((p = getprotobyname(tokens[0])) == NULL) + errx(1, "invalid protocol: %s", tokens[0]); + kill.proto = p->p_proto; + + if (strcmp(tokens[2], "->") == 0) { + sidx = 1; + didx = 3; + } else if (strcmp(tokens[2], "<-") == 0) { + sidx = 3; + didx = 1; + } else + errx(1, "invalid direction: %s", tokens[2]); + + if (pfctl_parse_host(tokens[sidx], &kill.src) == -1) + errx(1, "invalid host: %s", tokens[sidx]); + if (pfctl_parse_host(tokens[didx], &kill.dst) == -1) + errx(1, "invalid host: %s", tokens[didx]); + + if ((ret = pfctl_kill_states_h(pfh, &kill, &killed)) != 0) + pfctl_errx(opts, 1, "DIOCKILLSTATES"); if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "killed %d states\n", killed); +} + +int +pfctl_parse_host(char *str, struct pf_rule_addr *addr) +{ + char *s = NULL, *sbs, *sbe; + struct addrinfo hints, *ai; + + s = strdup(str); + if (!s) + errx(1, "pfctl_parse_host: strdup"); + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_DGRAM; /* dummy */ + hints.ai_flags = AI_NUMERICHOST; + + if ((sbs = strchr(s, '[')) != NULL && (sbe = strrchr(s, ']')) != NULL) { + hints.ai_family = AF_INET6; + *(sbs++) = *sbe = '\0'; + } else if ((sbs = strchr(s, ':')) != NULL) { + hints.ai_family = AF_INET; + *(sbs++) = '\0'; + } else { + /* Assume that no ':<number>' means port 0 */ + } + + if (getaddrinfo(s, sbs, &hints, &ai) != 0) + goto error; + + copy_satopfaddr(&addr->addr.v.a.addr, ai->ai_addr); + addr->port[0] = ai->ai_family == AF_INET6 ? + ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port : + ((struct sockaddr_in *)ai->ai_addr)->sin_port; + freeaddrinfo(ai); + free(s); + + memset(&addr->addr.v.a.mask, 0xff, sizeof(struct pf_addr)); + addr->port_op = PF_OP_EQ; + addr->addr.type = PF_ADDR_ADDRMASK; return (0); + +error: + free(s); + return (-1); } int pfctl_get_pool(int dev, struct pfctl_pool *pool, u_int32_t nr, - u_int32_t ticket, int r_action, char *anchorname, int which) + 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; @@ -975,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); } @@ -988,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); @@ -999,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); @@ -1055,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 " @@ -1126,7 +1216,7 @@ pfctl_show_eth_rules(int dev, char *path, int opts, enum pfctl_show format, if (anchorname[0] == '/') { if ((npath = calloc(1, MAXPATHLEN)) == NULL) - errx(1, "pfctl_rules: calloc"); + errx(1, "calloc"); snprintf(npath, MAXPATHLEN, "%s", anchorname); } else { if (path[0]) @@ -1225,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; @@ -1242,7 +1331,7 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format, if (anchorname[0] == '/') { if ((npath = calloc(1, MAXPATHLEN)) == NULL) - errx(1, "pfctl_rules: calloc"); + errx(1, "calloc"); strlcpy(npath, anchorname, MAXPATHLEN); } else { if (path[0]) @@ -1261,18 +1350,12 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format, u_int32_t mnr, nr; memset(&prs, 0, sizeof(prs)); - if ((ret = pfctl_get_rulesets(pfh, npath, &mnr)) != 0) { - if (ret == EINVAL) - fprintf(stderr, "Anchor '%s' " - "not found.\n", anchorname); - else - errc(1, ret, "DIOCGETRULESETS"); - } + if ((ret = pfctl_get_rulesets(pfh, npath, &mnr)) != 0) + errx(1, "%s", pf_strerror(ret)); - pfctl_print_rule_counters(&rule, opts); for (nr = 0; nr < mnr; ++nr) { if ((ret = pfctl_get_ruleset(pfh, npath, nr, &prs)) != 0) - errc(1, ret, "DIOCGETRULESET"); + errx(1, "%s", pf_strerror(ret)); INDENT(depth, !(opts & PF_OPT_VERBOSE)); printf("anchor \"%s\" all {\n", prs.name); pfctl_show_rules(dev, npath, opts, @@ -1287,14 +1370,14 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format, if (opts & PF_OPT_SHOWALL) { ret = pfctl_get_rules_info_h(pfh, &ri, PF_PASS, path); if (ret != 0) { - warnc(ret, "DIOCGETRULES"); + warnx("%s", pf_strerror(ret)); goto error; } header++; } ret = pfctl_get_rules_info_h(pfh, &ri, PF_SCRUB, path); if (ret != 0) { - warnc(ret, "DIOCGETRULES"); + warnx("%s", pf_strerror(ret)); goto error; } if (opts & PF_OPT_SHOWALL) { @@ -1329,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: @@ -1402,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 @@ -1438,7 +1527,7 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format, } int -pfctl_show_nat(int dev, char *path, int opts, char *anchorname, int depth, +pfctl_show_nat(int dev, const char *path, int opts, char *anchorname, int depth, int wildcard) { struct pfctl_rules_info ri; @@ -1461,16 +1550,17 @@ pfctl_show_nat(int dev, char *path, int opts, char *anchorname, int depth, p[0] = '\0'; } + if ((npath = calloc(1, MAXPATHLEN)) == NULL) + errx(1, "calloc"); + if (anchorname[0] == '/') { - if ((npath = calloc(1, MAXPATHLEN)) == NULL) - errx(1, "pfctl_rules: calloc"); snprintf(npath, MAXPATHLEN, "%s", anchorname); } else { - if (path[0]) - snprintf(&path[len], MAXPATHLEN - len, "/%s", anchorname); + snprintf(npath, MAXPATHLEN, "%s", path); + if (npath[0]) + snprintf(&npath[len], MAXPATHLEN - len, "/%s", anchorname); else - snprintf(&path[len], MAXPATHLEN - len, "%s", anchorname); - npath = path; + snprintf(&npath[len], MAXPATHLEN - len, "%s", anchorname); } /* @@ -1486,13 +1576,12 @@ pfctl_show_nat(int dev, char *path, int opts, char *anchorname, int depth, fprintf(stderr, "NAT anchor '%s' " "not found.\n", anchorname); else - errc(1, ret, "DIOCGETRULESETS"); + errx(1, "%s", pf_strerror(ret)); } - pfctl_print_rule_counters(&rule, opts); for (nr = 0; nr < mnr; ++nr) { if ((ret = pfctl_get_ruleset(pfh, npath, nr, &prs)) != 0) - errc(1, ret, "DIOCGETRULESET"); + errx(1, "%s", pf_strerror(ret)); INDENT(depth, !(opts & PF_OPT_VERBOSE)); printf("nat-anchor \"%s\" all {\n", prs.name); pfctl_show_nat(dev, npath, opts, @@ -1500,12 +1589,12 @@ pfctl_show_nat(int dev, char *path, int opts, char *anchorname, int depth, INDENT(depth, !(opts & PF_OPT_VERBOSE)); printf("}\n"); } - path[len] = '\0'; + npath[len] = '\0'; return (0); } for (i = 0; i < 3; i++) { - ret = pfctl_get_rules_info_h(pfh, &ri, nattype[i], path); + ret = pfctl_get_rules_info_h(pfh, &ri, nattype[i], npath); if (ret != 0) { warnc(ret, "DIOCGETRULES"); return (-1); @@ -1513,19 +1602,19 @@ pfctl_show_nat(int dev, char *path, int opts, char *anchorname, int depth, for (nr = 0; nr < ri.nr; ++nr) { INDENT(depth, !(opts & PF_OPT_VERBOSE)); - if ((ret = pfctl_get_rule_h(pfh, nr, ri.ticket, path, + if ((ret = pfctl_get_rule_h(pfh, nr, ri.ticket, npath, nattype[i], &rule, anchor_call)) != 0) { warnc(ret, "DIOCGETRULE"); return (-1); } if (pfctl_get_pool(dev, &rule.rdr, nr, - ri.ticket, nattype[i], path, PF_RDR) != 0) + ri.ticket, nattype[i], npath, PF_RDR) != 0) return (-1); if (pfctl_get_pool(dev, &rule.nat, nr, - ri.ticket, nattype[i], path, PF_NAT) != 0) + ri.ticket, nattype[i], npath, PF_NAT) != 0) return (-1); if (pfctl_get_pool(dev, &rule.route, nr, - ri.ticket, nattype[i], path, PF_RT) != 0) + ri.ticket, nattype[i], npath, PF_RT) != 0) return (-1); if (dotitle) { @@ -1605,7 +1694,7 @@ pfctl_show_states(int dev, const char *iface, int opts) struct pfctl_state_filter filter = {}; if (iface != NULL) - strncpy(filter.ifname, iface, IFNAMSIZ); + strlcpy(filter.ifname, iface, IFNAMSIZ); arg.opts = opts; arg.dotitle = opts & PF_OPT_SHOWALL; @@ -1704,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; @@ -1721,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"); @@ -1737,14 +1851,21 @@ pfctl_add_pool(struct pfctl *pf, struct pfctl_pool *p, sa_family_t af, int which return (0); } -int -pfctl_append_rule(struct pfctl *pf, struct pfctl_rule *r, - const char *anchor_call) +void +pfctl_init_rule(struct pfctl_rule *r) +{ + memset(r, 0, sizeof(struct pfctl_rule)); + TAILQ_INIT(&(r->rdr.list)); + TAILQ_INIT(&(r->nat.list)); + TAILQ_INIT(&(r->route.list)); +} + +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) @@ -1752,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)); @@ -1786,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 @@ -1979,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) @@ -2002,13 +2134,12 @@ pfctl_load_ruleset(struct pfctl *pf, char *path, struct pfctl_ruleset *rs, if ((pf->opts & PF_OPT_NOACTION) == 0 && (error = pfctl_ruleset_trans(pf, path, rs->anchor, false))) { - printf("pfctl_load_rulesets: " - "pfctl_ruleset_trans %d\n", error); + printf("%s: " + "pfctl_ruleset_trans %d\n", __func__, error); goto error; } } else if (pf->opts & PF_OPT_VERBOSE) printf("\n"); - } if (pf->optimize && rs_num == PF_RULESET_FILTER) @@ -2028,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); @@ -2050,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"); @@ -2084,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); @@ -2145,8 +2283,8 @@ int pfctl_rules(int dev, char *filename, int opts, int optimize, char *anchorname, struct pfr_buffer *trans) { -#define ERR(x) do { warn(x); goto _error; } while(0) -#define ERRX(x) do { warnx(x); goto _error; } while(0) +#define ERR(...) do { warn(__VA_ARGS__); goto _error; } while(0) +#define ERRX(...) do { warnx(__VA_ARGS__); goto _error; } while(0) struct pfr_buffer *t, buf; struct pfioc_altq pa; @@ -2154,12 +2292,14 @@ pfctl_rules(int dev, char *filename, int opts, int optimize, struct pfctl_ruleset *rs; struct pfctl_eth_ruleset *ethrs; struct pfr_table trs; - char *path; + char *path = NULL; int osize; 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)); @@ -2169,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 { @@ -2181,10 +2322,10 @@ pfctl_rules(int dev, char *filename, int opts, int optimize, memset(&pf, 0, sizeof(pf)); memset(&trs, 0, sizeof(trs)); if ((path = calloc(1, MAXPATHLEN)) == NULL) - ERRX("pfctl_rules: calloc"); + ERRX("%s: calloc", __func__); if (strlcpy(trs.pfrt_anchor, anchorname, sizeof(trs.pfrt_anchor)) >= sizeof(trs.pfrt_anchor)) - ERRX("pfctl_rules: strlcpy"); + ERRX("%s: strlcpy", __func__); pf.dev = dev; pf.h = pfh; pf.opts = opts; @@ -2193,16 +2334,16 @@ pfctl_rules(int dev, char *filename, int opts, int optimize, /* non-brace anchor, create without resolving the path */ if ((pf.anchor = calloc(1, sizeof(*pf.anchor))) == NULL) - ERRX("pfctl_rules: calloc"); + ERRX("%s: calloc", __func__); rs = &pf.anchor->ruleset; pf_init_ruleset(rs); rs->anchor = pf.anchor; if (strlcpy(pf.anchor->path, anchorname, sizeof(pf.anchor->path)) >= sizeof(pf.anchor->path)) - errx(1, "pfctl_rules: strlcpy"); + errx(1, "%s: strlcpy", __func__); if (strlcpy(pf.anchor->name, anchorname, sizeof(pf.anchor->name)) >= sizeof(pf.anchor->name)) - errx(1, "pfctl_rules: strlcpy"); + errx(1, "%s: strlcpy", __func__); pf.astack[0] = pf.anchor; @@ -2215,14 +2356,14 @@ pfctl_rules(int dev, char *filename, int opts, int optimize, /* Set up ethernet anchor */ if ((pf.eanchor = calloc(1, sizeof(*pf.eanchor))) == NULL) - ERRX("pfctl_rules: calloc"); + ERRX("%s: calloc", __func__); if (strlcpy(pf.eanchor->path, anchorname, sizeof(pf.eanchor->path)) >= sizeof(pf.eanchor->path)) - errx(1, "pfctl_rules: strlcpy"); + errx(1, "%s: strlcpy", __func__); if (strlcpy(pf.eanchor->name, anchorname, sizeof(pf.eanchor->name)) >= sizeof(pf.eanchor->name)) - errx(1, "pfctl_rules: strlcpy"); + errx(1, "%s: strlcpy", __func__); ethrs = &pf.eanchor->ruleset; pf_init_eth_ruleset(ethrs); @@ -2236,7 +2377,7 @@ pfctl_rules(int dev, char *filename, int opts, int optimize, * loaded at parse time. */ if (pfctl_ruleset_trans(&pf, anchorname, pf.anchor, true)) - ERRX("pfctl_rules"); + ERRX("%s", __func__); if (pf.loadopt & PFCTL_FLAG_ETH) pf.eth_ticket = pfctl_get_ticket(t, PF_RULESET_ETH, anchorname); if (altqsupport && (pf.loadopt & PFCTL_FLAG_ALTQ)) @@ -2277,17 +2418,17 @@ pfctl_rules(int dev, char *filename, int opts, int optimize, if (check_commit_altq(dev, opts) != 0) ERRX("errors in altq config"); - /* process "load anchor" directives */ - if (!anchorname[0]) - if (pfctl_load_anchors(dev, &pf, t) == -1) + if (trans == NULL) { + /* process "load anchor" directives */ + if (pfctl_load_anchors(dev, &pf) == -1) ERRX("load anchors"); - if (trans == NULL && (opts & PF_OPT_NOACTION) == 0) { - if (!anchorname[0]) - if (pfctl_load_options(&pf)) + if ((opts & PF_OPT_NOACTION) == 0) { + if (!anchorname[0] && pfctl_load_options(&pf)) goto _error; - if (pfctl_trans(dev, t, DIOCXCOMMIT, osize)) - ERR("DIOCXCOMMIT"); + if (pfctl_trans(dev, t, DIOCXCOMMIT, osize)) + ERR("DIOCXCOMMIT"); + } } free(path); return (0); @@ -2360,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; @@ -2388,7 +2535,7 @@ pfctl_load_options(struct pfctl *pf) } /* - * If we've set the limit, but haven't explicitly set adaptive + * If we've set the states limit, but haven't explicitly set adaptive * timeouts, do it now with a start of 60% and end of 120%. */ if (pf->limit_set[PF_LIMIT_STATES] && @@ -2462,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); } @@ -2770,7 +2920,7 @@ pfctl_set_interface_flags(struct pfctl *pf, char *ifname, int flags, int how) if ((pf->opts & PF_OPT_NOACTION) == 0) { if (how == 0) { if (ioctl(pf->dev, DIOCCLRIFFLAG, &pi)) - err(1, "DIOCCLRIFFLAG"); + pfctl_err(pf->opts, 1, "DIOCCLRIFFLAG"); } else { if (ioctl(pf->dev, DIOCSETIFFLAG, &pi)) err(1, "DIOCSETIFFLAG"); @@ -2829,43 +2979,187 @@ pfctl_test_altqsupport(int dev, int opts) } int -pfctl_show_anchors(int dev, int opts, char *anchorname) +pfctl_walk_show(int opts, struct pfioc_ruleset *pr, void *warg) +{ + if (pr->path[0]) { + if (pr->path[0] != '_' || (opts & PF_OPT_VERBOSE)) + printf(" %s/%s\n", pr->path, pr->name); + } else if (pr->name[0] != '_' || (opts & PF_OPT_VERBOSE)) + printf(" %s\n", pr->name); + + return (0); +} + +int +pfctl_walk_get(int opts, struct pfioc_ruleset *pr, void *warg) +{ + struct pfr_anchoritem *pfra; + struct pfr_anchors *anchors; + int e; + + anchors = (struct pfr_anchors *)warg; + + pfra = malloc(sizeof(*pfra)); + if (pfra == NULL) + err(1, "%s", __func__); + + if (pr->path[0]) + e = asprintf(&pfra->pfra_anchorname, "%s/%s", pr->path, + pr->name); + else + e = asprintf(&pfra->pfra_anchorname, "%s", pr->name); + + if (e == -1) + err(1, "%s", __func__); + + SLIST_INSERT_HEAD(anchors, pfra, pfra_sle); + + return (0); +} + +int +pfctl_walk_anchors(int dev, int opts, const char *anchor, + int(walkf)(int, struct pfioc_ruleset *, void *), void *warg) { struct pfioc_ruleset pr; u_int32_t mnr, nr; int ret; memset(&pr, 0, sizeof(pr)); - if ((ret = pfctl_get_rulesets(pfh, anchorname, &mnr)) != 0) { - if (ret == EINVAL) - fprintf(stderr, "Anchor '%s' not found.\n", - anchorname); - else - errc(1, ret, "DIOCGETRULESETS"); - return (-1); - } + if ((ret = pfctl_get_rulesets(pfh, anchor, &mnr)) != 0) + errx(1, "%s", pf_strerror(ret)); for (nr = 0; nr < mnr; ++nr) { char sub[MAXPATHLEN]; - if ((ret = pfctl_get_ruleset(pfh, anchorname, nr, &pr)) != 0) + if ((ret = pfctl_get_ruleset(pfh, anchor, nr, &pr)) != 0) errc(1, ret, "DIOCGETRULESET"); if (!strcmp(pr.name, PF_RESERVED_ANCHOR)) continue; - sub[0] = 0; - if (pr.path[0]) { - strlcat(sub, pr.path, sizeof(sub)); - strlcat(sub, "/", sizeof(sub)); - } - strlcat(sub, pr.name, sizeof(sub)); - if (sub[0] != '_' || (opts & PF_OPT_VERBOSE)) - printf(" %s\n", sub); - if ((opts & PF_OPT_VERBOSE) && pfctl_show_anchors(dev, opts, sub)) + sub[0] = '\0'; + if (walkf(opts, &pr, warg)) + return (-1); + + if (pr.path[0]) + snprintf(sub, sizeof(sub), "%s/%s", pr.path, pr.name); + else + snprintf(sub, sizeof(sub), "%s", pr.name); + if (pfctl_walk_anchors(dev, opts, sub, walkf, warg)) return (-1); } return (0); } int +pfctl_show_anchors(int dev, int opts, char *anchor) +{ + return ( + pfctl_walk_anchors(dev, opts, anchor, pfctl_walk_show, NULL)); +} + +struct pfr_anchors * +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') { + strlcpy(anchorbuf, anchor, sizeof(anchorbuf)); + n = dirname(anchorbuf); + if (n[0] != '.' && n[1] != '\0') + strlcpy(pr.path, n, sizeof(pr.path)); + strlcpy(anchorbuf, anchor, sizeof(anchorbuf)); + n = basename(anchorbuf); + if (n != NULL) + strlcpy(pr.name, n, sizeof(pr.name)); + } + + /* insert a root anchor first. */ + pfctl_walk_get(opts, &pr, &anchors); + + if (pfctl_walk_anchors(dev, opts, anchor, pfctl_walk_get, &anchors)) + errx(1, "%s failed to retrieve list of anchors, can't continue", + __func__); + + return (&anchors); +} + +int +pfctl_call_cleartables(int dev, int opts, struct pfr_anchoritem *pfra) +{ + /* + * PF_OPT_QUIET makes pfctl_clear_tables() to stop printing number of + * tables cleared for given anchor. + */ + opts |= PF_OPT_QUIET; + return ((pfctl_do_clear_tables(pfra->pfra_anchorname, opts) == -1) ? + 1 : 0); +} + +int +pfctl_call_clearrules(int dev, int opts, struct pfr_anchoritem *pfra) +{ + /* + * PF_OPT_QUIET makes pfctl_clear_rules() to stop printing a 'rules + * cleared' message for every anchor it deletes. + */ + opts |= PF_OPT_QUIET; + return (pfctl_flush_rules(dev, opts, pfra->pfra_anchorname)); +} + +int +pfctl_call_clearanchors(int dev, int opts, struct pfr_anchoritem *pfra) +{ + int rv = 0; + + rv |= pfctl_call_cleartables(dev, opts, pfra); + rv |= pfctl_call_clearrules(dev, opts, pfra); + + return (rv); +} + +int +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; + struct pfr_anchors *anchors; + struct pfr_anchoritem *pfra, *pfra_save; + + anchors = pfctl_get_anchors(dev, anchorname, opts); + /* + * While traversing the list, pfctl_clear_*() must always return + * so that failures on one anchor do not prevent clearing others. + */ + opts |= PF_OPT_IGNFAIL; + if ((opts & PF_OPT_CALLSHOW) == 0) + printf("Removing:\n"); + SLIST_FOREACH_SAFE(pfra, anchors, pfra_sle, pfra_save) { + 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); + free(pfra); + } + + return (rv); +} + +int pfctl_show_eth_anchors(int dev, int opts, char *anchorname) { struct pfctl_eth_rulesets_info ri; @@ -2913,10 +3207,49 @@ pfctl_lookup_option(char *cmd, const char * const *list) return (NULL); } +void +pfctl_reset(int dev, int opts) +{ + struct pfctl pf; + struct pfr_buffer t; + int i; + + memset(&pf, 0, sizeof(pf)); + pf.dev = dev; + pf.h = pfh; + pfctl_init_options(&pf); + + /* Force reset upon pfctl_load_options() */ + pf.debug_set = 1; + pf.reass_set = 1; + pf.syncookieswat_set = 1; + pf.ifname = strdup("none"); + if (pf.ifname == NULL) + err(1, "%s: strdup", __func__); + pf.ifname_set = 1; + + memset(&t, 0, sizeof(t)); + t.pfrb_type = PFRB_TRANS; + if (pfctl_trans(dev, &t, DIOCXBEGIN, 0)) + err(1, "%s: DIOCXBEGIN", __func__); + + for (i = 0; pf_limits[i].name; i++) + pf.limit_set[pf_limits[i].index] = 1; + + for (i = 0; pf_timeouts[i].name; i++) + pf.timeout_set[pf_timeouts[i].timeout] = 1; + + pfctl_load_options(&pf); + + if (pfctl_trans(dev, &t, DIOCXCOMMIT, 0)) + err(1, "%s: DIOCXCOMMIT", __func__); + + pfctl_clear_interface_flags(dev, opts); +} + int main(int argc, char *argv[]) { - int error = 0; int ch; int mode = O_RDONLY; int opts = 0; @@ -2928,7 +3261,7 @@ main(int argc, char *argv[]) usage(); while ((ch = getopt(argc, argv, - "a:AdD:eqf:F:ghi:k:K:mMnNOo:Pp:rRs:t:T:vx:z")) != -1) { + "a:AdD:eqf:F:ghi:k:K:mMnNOo:Pp:rRs:St:T:vx:z")) != -1) { switch (ch) { case 'a': anchoropt = optarg; @@ -3030,6 +3363,9 @@ main(int argc, char *argv[]) usage(); } break; + case 'S': + opts |= PF_OPT_NODNS; + break; case 't': tableopt = optarg; break; @@ -3065,6 +3401,12 @@ main(int argc, char *argv[]) } } + if ((opts & PF_OPT_NODNS) && (opts & PF_OPT_USEDNS)) + errx(1, "-N and -r are mutually exclusive"); + + if ((tblcmdopt == NULL) ^ (tableopt == NULL)) + usage(); + if (tblcmdopt != NULL) { argc -= optind; argv += optind; @@ -3073,7 +3415,7 @@ main(int argc, char *argv[]) loadopt |= PFCTL_FLAG_TABLE; tblcmdopt = NULL; } else - mode = strchr("acdefkrz", ch) ? O_RDWR : O_RDONLY; + mode = strchr("st", ch) ? O_RDONLY : O_RDWR; } else if (argc != optind) { warnx("unknown command line argument: %s ...", argv[optind]); usage(); @@ -3082,12 +3424,21 @@ main(int argc, char *argv[]) if (loadopt == 0) loadopt = ~0; - if ((path = calloc(1, MAXPATHLEN)) == NULL) - errx(1, "pfctl: calloc"); memset(anchorname, 0, sizeof(anchorname)); if (anchoropt != NULL) { int len = strlen(anchoropt); + if (anchoropt[0] == '\0') + errx(1, "anchor name must not be empty"); + if (mode == O_RDONLY && showopt == NULL && tblcmdopt == NULL) { + warnx("anchors apply to -f, -F, -s, and -T only"); + usage(); + } + if (mode == O_RDWR && tblcmdopt == NULL && + (anchoropt[0] == '_' || strstr(anchoropt, "/_") != NULL)) + errx(1, "anchor names beginning with '_' cannot " + "be modified from the command line"); + if (len >= 1 && anchoropt[len - 1] == '*') { if (len >= 2 && anchoropt[len - 2] == '/') anchoropt[len - 2] = '\0'; @@ -3124,9 +3475,17 @@ 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)) - error = 1; + exit_val = 1; + + if ((path = calloc(1, MAXPATHLEN)) == NULL) + errx(1, "%s: calloc", __func__); if (showopt != NULL) { switch (*showopt) { @@ -3164,7 +3523,7 @@ main(int argc, char *argv[]) pfctl_show_status(dev, opts); break; case 'R': - error = pfctl_show_running(dev); + exit_val = pfctl_show_running(dev); break; case 't': pfctl_show_timeouts(dev, opts); @@ -3184,19 +3543,26 @@ main(int argc, char *argv[]) 0); pfctl_show_nat(dev, path, opts, anchorname, 0, 0); - pfctl_show_rules(dev, path, opts, 0, anchorname, 0, 0); + pfctl_show_rules(dev, path, opts, PFCTL_SHOW_RULES, + anchorname, 0, 0); pfctl_show_altq(dev, ifaceopt, opts, 0); pfctl_show_states(dev, ifaceopt, opts); pfctl_show_src_nodes(dev, opts); pfctl_show_status(dev, opts); - pfctl_show_rules(dev, path, opts, 1, anchorname, 0, 0); + pfctl_show_rules(dev, path, opts, PFCTL_SHOW_LABELS, + anchorname, 0, 0); pfctl_show_timeouts(dev, opts); pfctl_show_limits(dev, opts); pfctl_show_tables(anchorname, opts); pfctl_show_fingerprints(opts); break; case 'T': - pfctl_show_tables(anchorname, opts); + 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); @@ -3219,16 +3585,22 @@ main(int argc, char *argv[]) } if (clearopt != NULL) { - if (anchorname[0] == '_' || strstr(anchorname, "/_") != NULL) - errx(1, "anchor names beginning with '_' cannot " - "be modified from the command line"); + 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); break; case 'r': - pfctl_flush_rules(dev, opts, anchorname); + if (opts & PF_OPT_RECURSE) + pfctl_recurse(dev, opts, anchorname, + pfctl_call_clearrules); + else + pfctl_flush_rules(dev, opts, anchorname); break; case 'n': pfctl_flush_nat(dev, opts, anchorname); @@ -3246,24 +3618,42 @@ main(int argc, char *argv[]) pfctl_clear_stats(pfh, opts); break; case 'a': + if (ifaceopt) { + warnx("don't specify an interface with -Fall"); + usage(); + /* NOTREACHED */ + } pfctl_flush_eth_rules(dev, opts, anchorname); pfctl_flush_rules(dev, opts, anchorname); pfctl_flush_nat(dev, opts, anchorname); - pfctl_do_clear_tables(anchorname, opts); + if (opts & PF_OPT_RECURSE) + pfctl_recurse(dev, opts, anchorname, + pfctl_call_clearanchors); + else { + pfctl_do_clear_tables(anchorname, opts); + pfctl_flush_rules(dev, opts, anchorname); + } if (!*anchorname) { pfctl_clear_altq(dev, opts); pfctl_clear_iface_states(dev, ifaceopt, opts); pfctl_clear_src_nodes(dev, opts); pfctl_clear_stats(pfh, opts); pfctl_clear_fingerprints(dev, opts); - pfctl_clear_interface_flags(dev, opts); + pfctl_reset(dev, opts); } break; case 'o': pfctl_clear_fingerprints(dev, opts); break; case 'T': - pfctl_do_clear_tables(anchorname, opts); + if ((opts & PF_OPT_RECURSE) == 0) + pfctl_do_clear_tables(anchorname, opts); + else + pfctl_recurse(dev, opts, anchorname, + pfctl_call_cleartables); + break; + case 'R': + pfctl_reset(dev, opts); break; } } @@ -3274,15 +3664,17 @@ main(int argc, char *argv[]) pfctl_id_kill_states(dev, ifaceopt, opts); else if (!strcmp(state_kill[0], "gateway")) pfctl_gateway_kill_states(dev, ifaceopt, opts); + else if (!strcmp(state_kill[0], "key")) + pfctl_key_kill_states(dev, ifaceopt, opts); else pfctl_net_kill_states(dev, ifaceopt, opts); } if (src_node_killers) - pfctl_kill_src_nodes(dev, ifaceopt, opts); + pfctl_kill_src_nodes(dev, opts); if (tblcmdopt != NULL) { - error = pfctl_command_tables(argc, argv, tableopt, + exit_val = pfctl_table(argc, argv, tableopt, tblcmdopt, rulesopt, anchorname, opts); rulesopt = NULL; } @@ -3303,29 +3695,22 @@ main(int argc, char *argv[]) if ((rulesopt != NULL) && (loadopt & PFCTL_FLAG_OPTION) && !anchorname[0] && !(opts & PF_OPT_NOACTION)) - if (pfctl_get_skip_ifaces()) - error = 1; + pfctl_get_skip_ifaces(); if (rulesopt != NULL && !(opts & PF_OPT_MERGE) && !anchorname[0] && (loadopt & PFCTL_FLAG_OPTION)) if (pfctl_file_fingerprints(dev, opts, PF_OSFP_FILE)) - error = 1; + exit_val = 1; if (rulesopt != NULL) { - if (anchorname[0] == '_' || strstr(anchorname, "/_") != NULL) - errx(1, "anchor names beginning with '_' cannot " - "be modified from the command line"); if (pfctl_rules(dev, rulesopt, opts, optimize, anchorname, NULL)) - error = 1; - else if (!(opts & PF_OPT_NOACTION) && - (loadopt & PFCTL_FLAG_TABLE)) - warn_namespace_collision(NULL); + exit_val = 1; } if (opts & PF_OPT_ENABLE) if (pfctl_enable(dev, opts)) - error = 1; + exit_val = 1; if (debugopt != NULL) { switch (*debugopt) { @@ -3344,5 +3729,30 @@ main(int argc, char *argv[]) } } - exit(error); + /* + * 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 * +pf_strerror(int errnum) +{ + switch (errnum) { + case ESRCH: + return "Table does not exist."; + case EINVAL: + case ENOENT: + return "Anchor does not exist."; + default: + return strerror(errnum); + } } diff --git a/sbin/pfctl/pfctl.h b/sbin/pfctl/pfctl.h index ffd37cf023a6..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,12 +61,51 @@ struct pfr_buffer { (var) != NULL; \ (var) = pfr_buf_next((buf), (var))) -int pfr_get_fd(void); +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; +}; + +SLIST_HEAD(pfr_anchors, pfr_anchoritem); + 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); -int pfr_get_tstats(struct pfr_table *, struct pfr_tstats *, int *, int); -int pfr_clr_tstats(struct pfr_table *, int, int *, int); int pfr_clr_astats(struct pfr_table *, struct pfr_addr *, int, int *, int); int pfr_clr_addrs(struct pfr_table *, int *, int); int pfr_add_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int); @@ -77,19 +122,18 @@ int pfr_buf_add(struct pfr_buffer *, const void *); void *pfr_buf_next(struct pfr_buffer *, const void *); int pfr_buf_grow(struct pfr_buffer *, int); int pfr_buf_load(struct pfr_buffer *, char *, int, - int (*)(struct pfr_buffer *, char *, int)); -char *pfr_strerror(int); + int (*)(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); -int pfctl_show_tables(const char *, int); -int pfctl_command_tables(int, char *[], char *, const char *, char *, +void pfctl_show_tables(const char *, int); +int pfctl_table(int, char *[], char *, const char *, char *, const char *, int); int pfctl_show_altq(int, const char *, int, int); -void warn_namespace_collision(const char *); -int pfctl_show_ifaces(const char *, int); +void warn_duplicate_tables(const char *, const char *); +void pfctl_show_ifaces(const char *, int); void pfctl_show_creators(int); FILE *pfctl_fopen(const char *, const char *); @@ -122,10 +166,10 @@ void pfaltq_store(struct pf_altq *); char *rate2str(double); void print_addr(struct pf_addr_wrap *, sa_family_t, int); +void print_addr_str(sa_family_t, struct pf_addr *); void print_host(struct pf_addr *, u_int16_t p, sa_family_t, int); void print_seq(struct pfctl_state_peer *); void print_state(struct pfctl_state *, int); -int unmask(struct pf_addr *, sa_family_t); int pfctl_cmdline_symset(char *); int pfctl_add_trans(struct pfr_buffer *, int, const char *); @@ -152,4 +196,7 @@ void expand_label(char *, size_t, struct pfctl_rule *); const char *pfctl_proto2name(int); +void pfctl_err(int, int, const char *, ...); +void pfctl_errx(int, int, const char *, ...); + #endif /* _PFCTL_H_ */ diff --git a/sbin/pfctl/pfctl_optimize.c b/sbin/pfctl/pfctl_optimize.c index 7817bcfd284a..2d16bbd22b39 100644 --- a/sbin/pfctl/pfctl_optimize.c +++ b/sbin/pfctl/pfctl_optimize.c @@ -241,6 +241,8 @@ int skip_cmp_src_addr(struct pfctl_rule *, struct pfctl_rule *); int skip_cmp_src_port(struct pfctl_rule *, struct pfctl_rule *); int superblock_inclusive(struct superblock *, struct pf_opt_rule *); void superblock_free(struct pfctl *, struct superblock *); +struct pf_opt_tbl *pf_opt_table_ref(struct pf_opt_tbl *); +void pf_opt_table_unref(struct pf_opt_tbl *); static int (*skip_comparitors[PF_SKIP_COUNT])(struct pfctl_rule *, @@ -271,7 +273,10 @@ pfctl_optimize_ruleset(struct pfctl *pf, struct pfctl_ruleset *rs) struct pfctl_rule *r; struct pfctl_rulequeue *old_rules; - DEBUG("optimizing ruleset"); + if (TAILQ_EMPTY(rs->rules[PF_RULESET_FILTER].active.ptr)) + return (0); + + DEBUG("optimizing ruleset \"%s\"", rs->anchor->path); memset(&table_buffer, 0, sizeof(table_buffer)); skip_init(); TAILQ_INIT(&opt_queue); @@ -345,9 +350,11 @@ pfctl_optimize_ruleset(struct pfctl *pf, struct pfctl_ruleset *rs) TAILQ_INSERT_TAIL( rs->rules[PF_RULESET_FILTER].active.ptr, r, entries); + pf_opt_table_unref(por->por_src_tbl); + pf_opt_table_unref(por->por_dst_tbl); free(por); } - free(block); + superblock_free(pf, block); } return (0); @@ -355,16 +362,8 @@ pfctl_optimize_ruleset(struct pfctl *pf, struct pfctl_ruleset *rs) error: while ((por = TAILQ_FIRST(&opt_queue))) { TAILQ_REMOVE(&opt_queue, por, por_entry); - if (por->por_src_tbl) { - pfr_buf_clear(por->por_src_tbl->pt_buf); - free(por->por_src_tbl->pt_buf); - free(por->por_src_tbl); - } - if (por->por_dst_tbl) { - pfr_buf_clear(por->por_dst_tbl->pt_buf); - free(por->por_dst_tbl->pt_buf); - free(por->por_dst_tbl); - } + pf_opt_table_unref(por->por_src_tbl); + pf_opt_table_unref(por->por_dst_tbl); free(por); } while ((block = TAILQ_FIRST(&superblocks))) { @@ -537,12 +536,14 @@ combine_rules(struct pfctl *pf, struct superblock *block) if (add_opt_table(pf, &p1->por_dst_tbl, p1->por_rule.af, &p2->por_rule.dst)) return (1); - p2->por_dst_tbl = p1->por_dst_tbl; if (p1->por_dst_tbl->pt_rulecount >= TABLE_THRESHOLD) { TAILQ_REMOVE(&block->sb_rules, p2, por_entry); free(p2); + } else { + p2->por_dst_tbl = + pf_opt_table_ref(p1->por_dst_tbl); } } else if (!src_eq && dst_eq && p1->por_dst_tbl == NULL && p2->por_src_tbl == NULL && @@ -559,12 +560,14 @@ combine_rules(struct pfctl *pf, struct superblock *block) if (add_opt_table(pf, &p1->por_src_tbl, p1->por_rule.af, &p2->por_rule.src)) return (1); - p2->por_src_tbl = p1->por_src_tbl; if (p1->por_src_tbl->pt_rulecount >= TABLE_THRESHOLD) { TAILQ_REMOVE(&block->sb_rules, p2, por_entry); free(p2); + } else { + p2->por_src_tbl = + pf_opt_table_ref(p1->por_src_tbl); } } } @@ -720,11 +723,7 @@ reorder_rules(struct pfctl *pf, struct superblock *block, int depth) * it based on a more optimal skipstep order. */ TAILQ_INIT(&head); - while ((por = TAILQ_FIRST(&block->sb_rules))) { - TAILQ_REMOVE(&block->sb_rules, por, por_entry); - TAILQ_INSERT_TAIL(&head, por, por_entry); - } - + TAILQ_CONCAT(&head, &block->sb_rules, por_entry); while (!TAILQ_EMPTY(&head)) { largest = 1; @@ -745,11 +744,7 @@ reorder_rules(struct pfctl *pf, struct superblock *block, int depth) * Nothing useful left. Leave remaining rules in order. */ DEBUG("(%d) no more commonality for skip steps", depth); - while ((por = TAILQ_FIRST(&head))) { - TAILQ_REMOVE(&head, por, por_entry); - TAILQ_INSERT_TAIL(&block->sb_rules, por, - por_entry); - } + TAILQ_CONCAT(&block->sb_rules, &head, por_entry); } else { /* * There is commonality. Extract those common rules @@ -860,10 +855,7 @@ block_feedback(struct pfctl *pf, struct superblock *block) */ TAILQ_INIT(&queue); - while ((por1 = TAILQ_FIRST(&block->sb_rules)) != NULL) { - TAILQ_REMOVE(&block->sb_rules, por1, por_entry); - TAILQ_INSERT_TAIL(&queue, por1, por_entry); - } + TAILQ_CONCAT(&queue, &block->sb_rules, por_entry); while ((por1 = TAILQ_FIRST(&queue)) != NULL) { TAILQ_REMOVE(&queue, por1, por_entry); @@ -900,13 +892,13 @@ load_feedback_profile(struct pfctl *pf, struct superblocks *superblocks) struct pf_opt_queue queue; struct pfctl_rules_info rules; struct pfctl_rule a, b, rule; - int nr, mnr; + int nr, mnr, ret; TAILQ_INIT(&queue); TAILQ_INIT(&prof_superblocks); - if (pfctl_get_rules_info_h(pf->h, &rules, PF_PASS, "")) { - warn("DIOCGETRULES"); + if ((ret = pfctl_get_rules_info_h(pf->h, &rules, PF_PASS, "")) != 0) { + warnx("%s", pf_strerror(ret)); return (1); } mnr = rules.nr; @@ -921,7 +913,8 @@ load_feedback_profile(struct pfctl *pf, struct superblocks *superblocks) if (pfctl_get_rule_h(pf->h, nr, rules.ticket, "", PF_PASS, &rule, anchor_call)) { - warn("DIOCGETRULENV"); + warnx("%s", pf_strerror(ret)); + free(por); return (1); } memcpy(&por->por_rule, &rule, sizeof(por->por_rule)); @@ -1249,12 +1242,13 @@ add_opt_table(struct pfctl *pf, struct pf_opt_tbl **tbl, sa_family_t af, ((*tbl)->pt_buf = calloc(1, sizeof(*(*tbl)->pt_buf))) == NULL) err(1, "calloc"); + (*tbl)->pt_refcnt = 1; (*tbl)->pt_buf->pfrb_type = PFRB_ADDRS; SIMPLEQ_INIT(&(*tbl)->pt_nodes); /* This is just a temporary table name */ snprintf((*tbl)->pt_name, sizeof((*tbl)->pt_name), "%s%d", - PF_OPT_TABLE_PREFIX, tablenum++); + PF_OPTIMIZER_TABLE_PFX, tablenum++); DEBUG("creating table <%s>", (*tbl)->pt_name); } @@ -1265,7 +1259,7 @@ add_opt_table(struct pfctl *pf, struct pf_opt_tbl **tbl, sa_family_t af, #ifdef OPT_DEBUG DEBUG("<%s> adding %s/%d", (*tbl)->pt_name, inet_ntop(af, &node_host.addr.v.a.addr, buf, sizeof(buf)), - unmask(&node_host.addr.v.a.mask, af)); + unmask(&node_host.addr.v.a.mask)); #endif /* OPT_DEBUG */ if (append_addr_host((*tbl)->pt_buf, &node_host, 0, 0)) { @@ -1321,9 +1315,9 @@ pf_opt_create_table(struct pfctl *pf, struct pf_opt_tbl *tbl) /* Now we have to pick a table name that isn't used */ again: DEBUG("translating temporary table <%s> to <%s%x_%d>", tbl->pt_name, - PF_OPT_TABLE_PREFIX, table_identifier, tablenum); + PF_OPTIMIZER_TABLE_PFX, table_identifier, tablenum); snprintf(tbl->pt_name, sizeof(tbl->pt_name), "%s%x_%d", - PF_OPT_TABLE_PREFIX, table_identifier, tablenum); + PF_OPTIMIZER_TABLE_PFX, table_identifier, tablenum); PFRB_FOREACH(t, &table_buffer) { if (strcasecmp(t->pfrt_name, tbl->pt_name) == 0) { /* Collision. Try again */ @@ -1337,7 +1331,8 @@ again: if (pfctl_define_table(tbl->pt_name, PFR_TFLAG_CONST, 1, - pf->astack[0]->name, 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); @@ -1600,8 +1595,8 @@ exclude_supersets(struct pfctl_rule *super, struct pfctl_rule *sub) sub->src.addr.type == PF_ADDR_ADDRMASK && super->src.neg == sub->src.neg && super->af == sub->af && - unmask(&super->src.addr.v.a.mask, super->af) < - unmask(&sub->src.addr.v.a.mask, sub->af) && + unmask(&super->src.addr.v.a.mask) < + unmask(&sub->src.addr.v.a.mask) && super->src.addr.v.a.addr.addr32[0] == (sub->src.addr.v.a.addr.addr32[0] & super->src.addr.v.a.mask.addr32[0]) && @@ -1628,8 +1623,8 @@ exclude_supersets(struct pfctl_rule *super, struct pfctl_rule *sub) sub->dst.addr.type == PF_ADDR_ADDRMASK && super->dst.neg == sub->dst.neg && super->af == sub->af && - unmask(&super->dst.addr.v.a.mask, super->af) < - unmask(&sub->dst.addr.v.a.mask, sub->af) && + unmask(&super->dst.addr.v.a.mask) < + unmask(&sub->dst.addr.v.a.mask) && super->dst.addr.v.a.addr.addr32[0] == (sub->dst.addr.v.a.addr.addr32[0] & super->dst.addr.v.a.mask.addr32[0]) && @@ -1657,20 +1652,8 @@ superblock_free(struct pfctl *pf, struct superblock *block) struct pf_opt_rule *por; while ((por = TAILQ_FIRST(&block->sb_rules))) { TAILQ_REMOVE(&block->sb_rules, por, por_entry); - if (por->por_src_tbl) { - if (por->por_src_tbl->pt_buf) { - pfr_buf_clear(por->por_src_tbl->pt_buf); - free(por->por_src_tbl->pt_buf); - } - free(por->por_src_tbl); - } - if (por->por_dst_tbl) { - if (por->por_dst_tbl->pt_buf) { - pfr_buf_clear(por->por_dst_tbl->pt_buf); - free(por->por_dst_tbl->pt_buf); - } - free(por->por_dst_tbl); - } + pf_opt_table_unref(por->por_src_tbl); + pf_opt_table_unref(por->por_dst_tbl); free(por); } if (block->sb_profiled_block) @@ -1678,3 +1661,24 @@ superblock_free(struct pfctl *pf, struct superblock *block) free(block); } +struct pf_opt_tbl * +pf_opt_table_ref(struct pf_opt_tbl *pt) +{ + /* parser does not run concurrently, we don't need atomic ops. */ + if (pt != NULL) + pt->pt_refcnt++; + + return (pt); +} + +void +pf_opt_table_unref(struct pf_opt_tbl *pt) +{ + if ((pt != NULL) && ((--pt->pt_refcnt) == 0)) { + if (pt->pt_buf != NULL) { + pfr_buf_clear(pt->pt_buf); + free(pt->pt_buf); + } + free(pt); + } +} diff --git a/sbin/pfctl/pfctl_osfp.c b/sbin/pfctl/pfctl_osfp.c index 649c1e8a2c3b..5770c8343a46 100644 --- a/sbin/pfctl/pfctl_osfp.c +++ b/sbin/pfctl/pfctl_osfp.c @@ -112,16 +112,11 @@ pfctl_file_fingerprints(int dev, int opts, const char *fp_filename) while ((line = fgetln(in, &len)) != NULL) { lineno++; - if (class) - free(class); - if (version) - free(version); - if (subtype) - free(subtype); - if (desc) - free(desc); - if (tcpopts) - free(tcpopts); + free(class); + free(version); + free(subtype); + free(desc); + free(tcpopts); class = version = subtype = desc = tcpopts = NULL; memset(&fp, 0, sizeof(fp)); @@ -250,16 +245,11 @@ pfctl_file_fingerprints(int dev, int opts, const char *fp_filename) add_fingerprint(dev, opts, &fp); } - if (class) - free(class); - if (version) - free(version); - if (subtype) - free(subtype); - if (desc) - free(desc); - if (tcpopts) - free(tcpopts); + free(class); + free(version); + free(subtype); + free(desc); + free(tcpopts); fclose(in); @@ -274,7 +264,7 @@ void pfctl_clear_fingerprints(int dev, int opts) { if (ioctl(dev, DIOCOSFPFLUSH)) - err(1, "DIOCOSFPFLUSH"); + pfctl_err(opts, 1, "DIOCOSFPFLUSH"); } /* flush pfctl's view of the fingerprints */ diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c index eb3a0826578e..b8531067d3f6 100644 --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -68,15 +68,14 @@ void print_op (u_int8_t, const char *, const char *); void print_port (u_int8_t, u_int16_t, u_int16_t, const char *, int); -void print_ugid (u_int8_t, unsigned, unsigned, const char *, unsigned); +void print_ugid (u_int8_t, id_t, id_t, const char *); void print_flags (uint16_t); void print_fromto(struct pf_rule_addr *, pf_osfp_t, struct pf_rule_addr *, sa_family_t, u_int8_t, int, int); int ifa_skip_if(const char *filter, struct node_host *p); -struct node_host *host_if(const char *, int, int *); -struct node_host *host_v4(const char *, int); -struct node_host *host_v6(const char *, int); +struct node_host *host_if(const char *, int); +struct node_host *host_ip(const char *, int); struct node_host *host_dns(const char *, int, int); const char * const tcpflags = "FSRPAUEWe"; @@ -135,7 +134,8 @@ static const struct icmptypeent icmp6_type[] = { { "niqry", ICMP6_NI_QUERY }, { "nirep", ICMP6_NI_REPLY }, { "mtraceresp", MLD_MTRACE_RESP }, - { "mtrace", MLD_MTRACE } + { "mtrace", MLD_MTRACE }, + { "listenrepv2", MLDV2_LISTENER_REPORT }, }; static const struct icmpcodeent icmp_code[] = { @@ -228,10 +228,21 @@ pfctl_parser_init(void) err(1, "Failed to create interface group query response map"); } +void +copy_satopfaddr(struct pf_addr *pfa, struct sockaddr *sa) +{ + if (sa->sa_family == AF_INET6) + pfa->v6 = ((struct sockaddr_in6 *)sa)->sin6_addr; + else if (sa->sa_family == AF_INET) + pfa->v4 = ((struct sockaddr_in *)sa)->sin_addr; + else + warnx("unhandled af %d", sa->sa_family); +} + 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++) { @@ -250,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++) { @@ -269,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++) { @@ -290,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++) { @@ -353,14 +364,14 @@ print_port(u_int8_t op, u_int16_t p1, u_int16_t p2, const char *proto, int numer } void -print_ugid(u_int8_t op, unsigned u1, unsigned u2, const char *t, unsigned umax) +print_ugid(u_int8_t op, id_t i1, id_t i2, const char *t) { char a1[11], a2[11]; - snprintf(a1, sizeof(a1), "%u", u1); - snprintf(a2, sizeof(a2), "%u", u2); + snprintf(a1, sizeof(a1), "%ju", (uintmax_t)i1); + snprintf(a2, sizeof(a2), "%ju", (uintmax_t)i2); printf(" %s", t); - if (u1 == umax && (op == PF_OP_EQ || op == PF_OP_NE)) + if (i1 == -1 && (op == PF_OP_EQ || op == PF_OP_NE)) print_op(op, "unknown", a2); else print_op(op, a1, a2); @@ -378,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) && @@ -418,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) @@ -431,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; @@ -498,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 @@ -602,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); @@ -663,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); @@ -828,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) @@ -917,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++ ? ", " : "", @@ -941,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) @@ -961,16 +991,14 @@ 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); if (r->uid.op) - print_ugid(r->uid.op, r->uid.uid[0], r->uid.uid[1], "user", - UID_MAX); + print_ugid(r->uid.op, r->uid.uid[0], r->uid.uid[1], "user"); if (r->gid.op) - print_ugid(r->gid.op, r->gid.gid[0], r->gid.gid[1], "group", - GID_MAX); + print_ugid(r->gid.op, r->gid.gid[0], r->gid.gid[1], "group"); if (r->flags || r->flagset) { printf(" flags "); print_flags(r->flags); @@ -1007,6 +1035,11 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer printf(" tos 0x%2.2x", r->tos); if (r->prio) printf(" prio %u", r->prio == PF_PRIO_ZERO ? 0 : r->prio); + if (r->pktrate.limit) + printf(" max-pkt-rate %u/%u", r->pktrate.limit, + r->pktrate.seconds); + if (r->max_pkt_size) + printf( " max-pkt-size %u", r->max_pkt_size); if (r->scrub_flags & PFSTATE_SETMASK) { char *comma = ""; printf(" set ("); @@ -1207,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]) @@ -1227,37 +1262,44 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer if (PF_AZERO(&r->divert.addr, r->af)) { printf(" divert-reply"); } else { - /* XXX cut&paste from print_addr */ - char buf[48]; - printf(" divert-to "); - if (inet_ntop(r->af, &r->divert.addr, buf, - sizeof(buf)) == NULL) - printf("?"); - else - printf("%s", buf); + print_addr_str(r->af, &r->divert.addr); printf(" port %u", ntohs(r->divert.port)); } #endif } - if (!anchor_call[0] && ! TAILQ_EMPTY(&r->nat.list) && - r->rule_flag & PFRULE_AFTO) { - printf(" af-to %s from ", r->naf == AF_INET ? "inet" : "inet6"); - print_pool(&r->nat, r->nat.proxy_port[0], r->nat.proxy_port[1], - r->naf ? r->naf : r->af, PF_NAT); + if (anchor_call[0]) + return; + 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->action); + } else { + if (!TAILQ_EMPTY(&r->nat.list)) { + if (r->rule_flag & PFRULE_AFTO) { + 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], PF_NAT); + } if (!TAILQ_EMPTY(&r->rdr.list)) { - printf(" to "); + if (r->rule_flag & PFRULE_AFTO) { + printf(" to "); + } else { + 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 (!anchor_call[0] && - (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); + + if (r->rule_flag & PFRULE_EXPIRED) { + printf(" # expired"); + + if (r->exptime != 0) + printf(" %s", ctime(&r->exptime)); } } @@ -1315,14 +1357,20 @@ parse_flags(char *s) } void -set_ipmask(struct node_host *h, u_int8_t b) +set_ipmask(struct node_host *h, int bb) { struct pf_addr *m, *n; int i, j = 0; + uint8_t b; m = &h->addr.v.a.mask; memset(m, 0, sizeof(*m)); + if (bb == -1) + b = h->af == AF_INET ? 32 : 128; + else + b = bb; + while (b >= 32) { m->addr32[j++] = 0xffffffff; b -= 32; @@ -1353,7 +1401,7 @@ check_netmask(struct node_host *h, sa_family_t af) if (af == AF_INET && (m->addr32[1] || m->addr32[2] || m->addr32[3])) { fprintf(stderr, "netmask %u invalid for IPv4 address\n", - unmask(m, AF_INET6)); + unmask(m)); return (1); } } @@ -1364,7 +1412,6 @@ struct node_host * gen_dynnode(struct node_host *h, sa_family_t af) { struct node_host *n; - struct pf_addr *m; if (h->addr.type != PF_ADDR_DYNIFTL) return (NULL); @@ -1377,8 +1424,7 @@ gen_dynnode(struct node_host *h, sa_family_t af) n->tail = NULL; /* fix up netmask */ - m = &n->addr.v.a.mask; - if (af == AF_INET && unmask(m, AF_INET6) > 32) + if (af == AF_INET && unmask(&n->addr.v.a.mask) > 32) set_ipmask(n, 32); return (n); @@ -1424,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; @@ -1463,13 +1509,14 @@ 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; n = calloc(1, sizeof(struct node_host)); if (n == NULL) - err(1, "address: calloc"); + err(1, "%s: calloc", __func__); n->af = ifa->ifa_addr->sa_family; n->ifa_flags = ifa->ifa_flags; #ifdef __KAME__ @@ -1488,45 +1535,28 @@ ifa_load(void) } #endif n->ifindex = 0; - if (n->af == AF_INET) { - memcpy(&n->addr.v.a.addr, &((struct sockaddr_in *) - ifa->ifa_addr)->sin_addr.s_addr, - sizeof(struct in_addr)); - memcpy(&n->addr.v.a.mask, &((struct sockaddr_in *) - ifa->ifa_netmask)->sin_addr.s_addr, - sizeof(struct in_addr)); - if (ifa->ifa_broadaddr != NULL) - memcpy(&n->bcast, &((struct sockaddr_in *) - ifa->ifa_broadaddr)->sin_addr.s_addr, - sizeof(struct in_addr)); - if (ifa->ifa_dstaddr != NULL) - memcpy(&n->peer, &((struct sockaddr_in *) - ifa->ifa_dstaddr)->sin_addr.s_addr, - sizeof(struct in_addr)); - } else if (n->af == AF_INET6) { - memcpy(&n->addr.v.a.addr, &((struct sockaddr_in6 *) - ifa->ifa_addr)->sin6_addr.s6_addr, - sizeof(struct in6_addr)); - memcpy(&n->addr.v.a.mask, &((struct sockaddr_in6 *) - ifa->ifa_netmask)->sin6_addr.s6_addr, - sizeof(struct in6_addr)); - if (ifa->ifa_broadaddr != NULL) - memcpy(&n->bcast, &((struct sockaddr_in6 *) - ifa->ifa_broadaddr)->sin6_addr.s6_addr, - sizeof(struct in6_addr)); - if (ifa->ifa_dstaddr != NULL) - memcpy(&n->peer, &((struct sockaddr_in6 *) - ifa->ifa_dstaddr)->sin6_addr.s6_addr, - sizeof(struct in6_addr)); - n->ifindex = ((struct sockaddr_in6 *) - ifa->ifa_addr)->sin6_scope_id; - } else if (n->af == AF_LINK) { - ifa_add_groups_to_map(ifa->ifa_name); + if (n->af == AF_LINK) { n->ifindex = ((struct sockaddr_dl *) ifa->ifa_addr)->sdl_index; + ifa_add_groups_to_map(ifa->ifa_name); + } else { + copy_satopfaddr(&n->addr.v.a.addr, ifa->ifa_addr); + ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family; + copy_satopfaddr(&n->addr.v.a.mask, ifa->ifa_netmask); + if (ifa->ifa_broadaddr != NULL) { + ifa->ifa_broadaddr->sa_family = ifa->ifa_addr->sa_family; + copy_satopfaddr(&n->bcast, ifa->ifa_broadaddr); + } + if (ifa->ifa_dstaddr != NULL) { + ifa->ifa_dstaddr->sa_family = ifa->ifa_addr->sa_family; + copy_satopfaddr(&n->peer, ifa->ifa_dstaddr); + } + if (n->af == AF_INET6) + n->ifindex = ((struct sockaddr_in6 *) + ifa->ifa_addr) ->sin6_scope_id; } if ((n->ifname = strdup(ifa->ifa_name)) == NULL) - err(1, "ifa_load: strdup"); + err(1, "%s: strdup", __func__); n->next = NULL; n->tail = n; if (h == NULL) @@ -1582,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); @@ -1706,6 +1736,8 @@ ifa_lookup(char *ifa_name, int flags) if ((flags & PFI_AFLAG_BROADCAST) && !(p->ifa_flags & IFF_BROADCAST)) continue; + if ((flags & PFI_AFLAG_BROADCAST) && p->bcast.v4.s_addr == 0) + continue; if ((flags & PFI_AFLAG_PEER) && !(p->ifa_flags & IFF_POINTOPOINT)) continue; @@ -1727,7 +1759,7 @@ ifa_lookup(char *ifa_name, int flags) got6 = 1; n = calloc(1, sizeof(struct node_host)); if (n == NULL) - err(1, "address: calloc"); + err(1, "%s: calloc", __func__); n->af = p->af; if (flags & PFI_AFLAG_BROADCAST) memcpy(&n->addr.v.a.addr, &p->bcast, @@ -1739,19 +1771,9 @@ ifa_lookup(char *ifa_name, int flags) memcpy(&n->addr.v.a.addr, &p->addr.v.a.addr, sizeof(struct pf_addr)); if (flags & PFI_AFLAG_NETWORK) - set_ipmask(n, unmask(&p->addr.v.a.mask, n->af)); - else { - if (n->af == AF_INET) { - if (p->ifa_flags & IFF_LOOPBACK && - p->ifa_flags & IFF_LINK1) - memcpy(&n->addr.v.a.mask, - &p->addr.v.a.mask, - sizeof(struct pf_addr)); - else - set_ipmask(n, 32); - } else - set_ipmask(n, 128); - } + set_ipmask(n, unmask(&p->addr.v.a.mask)); + else + set_ipmask(n, -1); n->ifindex = p->ifindex; n->ifname = strdup(p->ifname); @@ -1790,57 +1812,41 @@ ifa_skip_if(const char *filter, struct node_host *p) struct node_host * -host(const char *s) +host(const char *s, int opts) { struct node_host *h = NULL; - int mask, v4mask, v6mask, cont = 1; - char *p, *q, *ps; - - if ((p = strrchr(s, '/')) != NULL) { - mask = strtol(p+1, &q, 0); - if (!q || *q || mask > 128 || q == (p+1)) { - fprintf(stderr, "invalid netmask '%s'\n", p); - return (NULL); + int mask = -1; + char *p, *ps; + const char *errstr; + + if ((p = strchr(s, '/')) != NULL) { + mask = strtonum(p+1, 0, 128, &errstr); + if (errstr) { + fprintf(stderr, "netmask is %s: %s\n", errstr, p); + goto error; } if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL) - err(1, "host: malloc"); + err(1, "%s: malloc", __func__); strlcpy(ps, s, strlen(s) - strlen(p) + 1); - v4mask = v6mask = mask; } else { if ((ps = strdup(s)) == NULL) - err(1, "host: strdup"); - v4mask = 32; - v6mask = 128; - mask = -1; + err(1, "%s: strdup", __func__); } - /* IPv4 address? */ - if (cont && (h = host_v4(s, mask)) != NULL) - cont = 0; - - /* IPv6 address? */ - if (cont && (h = host_v6(ps, v6mask)) != NULL) - cont = 0; - - /* interface with this name exists? */ - /* expensive with thousands of interfaces - prioritze IPv4/6 check */ - if (cont && (h = host_if(ps, mask, &cont)) != NULL) - cont = 0; - - /* dns lookup */ - if (cont && (h = host_dns(ps, v4mask, v6mask)) != NULL) - cont = 0; - free(ps); - - if (h == NULL || cont == 1) { + if ((h = host_ip(ps, mask)) == NULL && + (h = host_if(ps, mask)) == NULL && + (h = host_dns(ps, mask, (opts & PF_OPT_NODNS))) == NULL) { fprintf(stderr, "no IP address found for %s\n", s); - return (NULL); + goto error; } + +error: + free(ps); return (h); } struct node_host * -host_if(const char *s, int mask, int *cont) +host_if(const char *s, int mask) { struct node_host *n, *h = NULL; char *p, *ps; @@ -1857,101 +1863,79 @@ host_if(const char *s, int mask, int *cont) flags |= PFI_AFLAG_PEER; else if (!strcmp(p+1, "0")) flags |= PFI_AFLAG_NOALIAS; - else { - free(ps); - return (NULL); - } + else + goto error; *p = '\0'; - *cont = 0; } if (flags & (flags - 1) & PFI_AFLAG_MODEMASK) { /* Yep! */ fprintf(stderr, "illegal combination of interface modifiers\n"); - free(ps); - return (NULL); + goto error; } if ((flags & (PFI_AFLAG_NETWORK|PFI_AFLAG_BROADCAST)) && mask > -1) { fprintf(stderr, "network or broadcast lookup, but " "extra netmask given\n"); - free(ps); - return (NULL); + goto error; } if (ifa_exists(ps) || !strncmp(ps, "self", IFNAMSIZ)) { /* interface with this name exists */ h = ifa_lookup(ps, flags); - for (n = h; n != NULL && mask > -1; n = n->next) - set_ipmask(n, mask); + if (mask > -1) + for (n = h; n != NULL; n = n->next) + set_ipmask(n, mask); } +error: free(ps); return (h); } struct node_host * -host_v4(const char *s, int mask) +host_ip(const char *s, int mask) { + struct addrinfo hints, *res; struct node_host *h = NULL; - struct in_addr ina; - int bits = 32; - - memset(&ina, 0, sizeof(struct in_addr)); - if (strrchr(s, '/') != NULL) { - if ((bits = inet_net_pton(AF_INET, s, &ina, sizeof(ina))) == -1) - return (NULL); - } else { - if (inet_pton(AF_INET, s, &ina) != 1) - return (NULL); - } - h = calloc(1, sizeof(struct node_host)); + h = calloc(1, sizeof(*h)); if (h == NULL) - err(1, "address: calloc"); - h->ifname = NULL; - h->af = AF_INET; - h->addr.v.a.addr.addr32[0] = ina.s_addr; - set_ipmask(h, bits); - h->next = NULL; - h->tail = h; - - return (h); -} - -struct node_host * -host_v6(const char *s, int mask) -{ - struct addrinfo hints, *res; - struct node_host *h = NULL; + err(1, "%s: calloc", __func__); + if (mask != -1) { + /* Try to parse 10/8 */ + h->af = AF_INET; + if (inet_net_pton(AF_INET, s, &h->addr.v.a.addr.v4, + sizeof(h->addr.v.a.addr.v4)) != -1) + goto out; + } memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET6; + hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; /*dummy*/ hints.ai_flags = AI_NUMERICHOST; - if (getaddrinfo(s, "0", &hints, &res) == 0) { - h = calloc(1, sizeof(struct node_host)); - if (h == NULL) - err(1, "address: calloc"); - h->ifname = NULL; - h->af = AF_INET6; - memcpy(&h->addr.v.a.addr, - &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, - sizeof(h->addr.v.a.addr)); - h->ifindex = - ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; - set_ipmask(h, mask); + if (getaddrinfo(s, NULL, &hints, &res) == 0) { + h->af = res->ai_family; + copy_satopfaddr(&h->addr.v.a.addr, res->ai_addr); + if (h->af == AF_INET6) + h->ifindex = + ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; freeaddrinfo(res); - h->next = NULL; - h->tail = h; + } else { + free(h); + return (NULL); } +out: + set_ipmask(h, mask); + h->ifname = NULL; + h->next = NULL; + h->tail = h; return (h); } struct node_host * -host_dns(const char *s, int v4mask, int v6mask) +host_dns(const char *s, int mask, int numeric) { struct addrinfo hints, *res0, *res; struct node_host *n, *h = NULL; - int error, noalias = 0; - int got4 = 0, got6 = 0; + int noalias = 0, got4 = 0, got6 = 0; char *p, *ps; if ((ps = strdup(s)) == NULL) @@ -1963,11 +1947,10 @@ host_dns(const char *s, int v4mask, int v6mask) memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; /* DUMMY */ - error = getaddrinfo(ps, NULL, &hints, &res0); - if (error) { - free(ps); - return (h); - } + if (numeric) + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(ps, NULL, &hints, &res0) != 0) + goto error; for (res = res0; res; res = res->ai_next) { if (res->ai_family != AF_INET && @@ -1989,22 +1972,11 @@ host_dns(const char *s, int v4mask, int v6mask) err(1, "host_dns: calloc"); n->ifname = NULL; n->af = res->ai_family; - if (res->ai_family == AF_INET) { - memcpy(&n->addr.v.a.addr, - &((struct sockaddr_in *) - res->ai_addr)->sin_addr.s_addr, - sizeof(struct in_addr)); - set_ipmask(n, v4mask); - } else { - memcpy(&n->addr.v.a.addr, - &((struct sockaddr_in6 *) - res->ai_addr)->sin6_addr.s6_addr, - sizeof(struct in6_addr)); + copy_satopfaddr(&n->addr.v.a.addr, res->ai_addr); + if (res->ai_family == AF_INET6) n->ifindex = - ((struct sockaddr_in6 *) - res->ai_addr)->sin6_scope_id; - set_ipmask(n, v6mask); - } + ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; + set_ipmask(n, mask); n->next = NULL; n->tail = n; if (h == NULL) @@ -2015,6 +1987,7 @@ host_dns(const char *s, int v4mask, int v6mask) } } freeaddrinfo(res0); +error: free(ps); return (h); @@ -2026,7 +1999,7 @@ host_dns(const char *s, int v4mask, int v6mask) * if set to 1, only simple addresses are accepted (no netblock, no "!"). */ int -append_addr(struct pfr_buffer *b, char *s, int test) +append_addr(struct pfr_buffer *b, char *s, int test, int opts) { char *r; struct node_host *h, *n; @@ -2034,7 +2007,7 @@ append_addr(struct pfr_buffer *b, char *s, int test) for (r = s; *r == '!'; r++) not = !not; - if ((n = host(r)) == NULL) { + if ((n = host(r, opts)) == NULL) { errno = 0; return (-1); } @@ -2063,7 +2036,7 @@ append_addr_host(struct pfr_buffer *b, struct node_host *n, int test, int not) bzero(&addr, sizeof(addr)); addr.pfra_not = n->not ^ not; addr.pfra_af = n->af; - addr.pfra_net = unmask(&n->addr.v.a.mask, n->af); + addr.pfra_net = unmask(&n->addr.v.a.mask); switch (n->af) { case AF_INET: addr.pfra_ip4addr.s_addr = n->addr.v.a.addr.addr32[0]; diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h index e916f5e7c100..44ddfb45fbe1 100644 --- a/sbin/pfctl/pfctl_parser.h +++ b/sbin/pfctl/pfctl_parser.h @@ -36,24 +36,29 @@ #include <libpfctl.h> +#include <pfctl.h> + #define PF_OSFP_FILE "/etc/pf.os" -#define PF_OPT_DISABLE 0x0001 -#define PF_OPT_ENABLE 0x0002 -#define PF_OPT_VERBOSE 0x0004 -#define PF_OPT_NOACTION 0x0008 -#define PF_OPT_QUIET 0x0010 -#define PF_OPT_CLRRULECTRS 0x0020 -#define PF_OPT_USEDNS 0x0040 -#define PF_OPT_VERBOSE2 0x0080 -#define PF_OPT_DUMMYACTION 0x0100 -#define PF_OPT_DEBUG 0x0200 -#define PF_OPT_SHOWALL 0x0400 -#define PF_OPT_OPTIMIZE 0x0800 -#define PF_OPT_NUMERIC 0x1000 -#define PF_OPT_MERGE 0x2000 -#define PF_OPT_RECURSE 0x4000 -#define PF_OPT_KILLMATCH 0x8000 +#define PF_OPT_DISABLE 0x00001 +#define PF_OPT_ENABLE 0x00002 +#define PF_OPT_VERBOSE 0x00004 +#define PF_OPT_NOACTION 0x00008 +#define PF_OPT_QUIET 0x00010 +#define PF_OPT_CLRRULECTRS 0x00020 +#define PF_OPT_USEDNS 0x00040 +#define PF_OPT_VERBOSE2 0x00080 +#define PF_OPT_DUMMYACTION 0x00100 +#define PF_OPT_DEBUG 0x00200 +#define PF_OPT_SHOWALL 0x00400 +#define PF_OPT_OPTIMIZE 0x00800 +#define PF_OPT_NUMERIC 0x01000 +#define PF_OPT_MERGE 0x02000 +#define PF_OPT_RECURSE 0x04000 +#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 @@ -87,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]; @@ -257,10 +263,10 @@ struct pf_opt_tbl { char pt_name[PF_TABLE_NAME_SIZE]; int pt_rulecount; int pt_generated; + uint32_t pt_refcnt; struct node_tinithead pt_nodes; struct pfr_buffer *pt_buf; }; -#define PF_OPT_TABLE_PREFIX "__automatic_" /* optimizer pf_rule container */ struct pf_opt_rule { @@ -274,14 +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 *); -int pfctl_append_rule(struct pfctl *, struct pfctl_rule *, const char *); +void pfctl_init_rule(struct pfctl_rule *r); +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 *); @@ -297,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); @@ -318,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 *); @@ -358,9 +369,9 @@ struct pf_timeout { extern const struct pf_timeout pf_timeouts[]; -void set_ipmask(struct node_host *, u_int8_t); +void set_ipmask(struct node_host *, int); int check_netmask(struct node_host *, sa_family_t); -int unmask(struct pf_addr *, sa_family_t); +int unmask(struct pf_addr *); struct node_host *gen_dynnode(struct node_host *, sa_family_t); void ifa_load(void); unsigned int ifa_nametoindex(const char *); @@ -369,10 +380,13 @@ int get_query_socket(void); struct node_host *ifa_exists(char *); struct node_host *ifa_grouplookup(char *ifa_name, int flags); struct node_host *ifa_lookup(char *, int); -struct node_host *host(const char *); +struct node_host *host(const char *, int); -int append_addr(struct pfr_buffer *, 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 3b0cc615e5a2..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) @@ -113,20 +127,7 @@ pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size, int pfr_clr_addrs(struct pfr_table *tbl, int *ndel, int flags) { - struct pfioc_table io; - - if (tbl == NULL) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_table = *tbl; - if (ioctl(dev, DIOCRCLRADDRS, &io)) - return (-1); - if (ndel != NULL) - *ndel = io.pfrio_ndel; - return (0); + return (pfctl_clear_addrs(pfh, tbl, ndel, flags)); } int @@ -135,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); @@ -149,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); @@ -235,29 +239,6 @@ pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size, } int -pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags) -{ - struct pfioc_table io; - - if (size < 0 || (size && !tbl)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_buffer = tbl; - io.pfrio_esize = sizeof(*tbl); - io.pfrio_size = size; - if (ioctl(dev, DIOCRCLRTSTATS, &io)) { - pfr_report_error(tbl, &io, "clear tstats from"); - return (-1); - } - if (nzero) - *nzero = io.pfrio_nzero; - return (0); -} - -int pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size, int *nmatch, int flags) { @@ -289,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); } @@ -429,15 +411,14 @@ pfr_buf_clear(struct pfr_buffer *b) { if (b == NULL) return; - if (b->pfrb_caddr != NULL) - free(b->pfrb_caddr); + free(b->pfrb_caddr); b->pfrb_caddr = NULL; b->pfrb_size = b->pfrb_msize = 0; } int pfr_buf_load(struct pfr_buffer *b, char *file, int nonetwork, - int (*append_addr)(struct pfr_buffer *, char *, int)) + int (*append_addr)(struct pfr_buffer *, char *, int, int), int opts) { FILE *fp; char buf[BUF_SIZE]; @@ -453,7 +434,7 @@ pfr_buf_load(struct pfr_buffer *b, char *file, int nonetwork, return (-1); } while ((rv = pfr_next_token(buf, fp)) == 1) - if (append_addr(b, buf, nonetwork)) { + if (append_addr(b, buf, nonetwork, opts)) { rv = -1; break; } @@ -498,16 +479,3 @@ pfr_next_token(char buf[BUF_SIZE], FILE *fp) buf[i] = '\0'; return (1); } - -char * -pfr_strerror(int errnum) -{ - switch (errnum) { - case ESRCH: - return "Table does not exist"; - case ENOENT: - return "Anchor or Ruleset does not exist"; - default: - return strerror(errnum); - } -} diff --git a/sbin/pfctl/pfctl_table.c b/sbin/pfctl/pfctl_table.c index 57f7354b0172..4955e1791fd7 100644 --- a/sbin/pfctl/pfctl_table.c +++ b/sbin/pfctl/pfctl_table.c @@ -55,15 +55,12 @@ #include "pfctl.h" extern void usage(void); -static int pfctl_table(int, char *[], char *, const char *, char *, - const char *, int); static void print_table(const struct pfr_table *, int, int); static int print_tstats(const struct pfr_tstats *, int); -static int load_addr(struct pfr_buffer *, int, char *[], char *, int); +static int load_addr(struct pfr_buffer *, int, char *[], char *, int, int); static void print_addrx(struct pfr_addr *, struct pfr_addr *, int); static int nonzero_astats(struct pfr_astats *); static void print_astats(struct pfr_astats *, int); -static void radix_perror(void); static void xprintf(int, const char *, ...); static void print_iface(struct pfi_kif *, int); @@ -77,26 +74,28 @@ static const char *istats_text[2][2][2] = { { { "In6/Pass:", "In6/Block:" }, { "Out6/Pass:", "Out6/Block:" } } }; -#define RVTEST(fct) do { \ - if ((!(opts & PF_OPT_NOACTION) || \ - (opts & PF_OPT_DUMMYACTION)) && \ - (fct)) { \ - radix_perror(); \ - goto _error; \ - } \ +#define RVTEST(fct) do { \ + if ((!(opts & PF_OPT_NOACTION) || \ + (opts & PF_OPT_DUMMYACTION)) && \ + (fct)) { \ + if ((opts & PF_OPT_RECURSE) == 0) \ + warnx("%s", pf_strerror(errno)); \ + goto _error; \ + } \ } while (0) #define CREATE_TABLE do { \ + warn_duplicate_tables(table.pfrt_name, \ + table.pfrt_anchor); \ table.pfrt_flags |= PFR_TFLAG_PERSIST; \ if ((!(opts & PF_OPT_NOACTION) || \ (opts & PF_OPT_DUMMYACTION)) && \ (pfr_add_table(&table, &nadd, flags)) && \ (errno != EPERM)) { \ - radix_perror(); \ + warnx("%s", pf_strerror(errno)); \ goto _error; \ } \ if (nadd) { \ - warn_namespace_collision(table.pfrt_name); \ xprintf(opts, "%d table created", nadd); \ if (opts & PF_OPT_NOACTION) \ return (0); \ @@ -107,22 +106,21 @@ static const char *istats_text[2][2][2] = { int pfctl_do_clear_tables(const char *anchor, int opts) { - return pfctl_table(0, NULL, NULL, "-F", NULL, anchor, opts); -} + int rv; -int -pfctl_show_tables(const char *anchor, int opts) -{ - return pfctl_table(0, NULL, NULL, "-s", NULL, anchor, opts); + if ((rv = pfctl_table(0, NULL, NULL, "-F", NULL, anchor, opts)) == -1) { + if ((opts & PF_OPT_IGNFAIL) == 0) + exit(1); + } + + return (rv); } -int -pfctl_command_tables(int argc, char *argv[], char *tname, - const char *command, char *file, const char *anchor, int opts) +void +pfctl_show_tables(const char *anchor, int opts) { - if (tname == NULL || command == NULL) - usage(); - return pfctl_table(argc, argv, tname, command, file, anchor, opts); + if (pfctl_table(0, NULL, NULL, "-s", NULL, anchor, opts)) + exit(1); } int @@ -202,7 +200,7 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command, xprintf(opts, "%d addresses deleted", ndel); } else if (!strcmp(command, "add")) { b.pfrb_type = PFRB_ADDRS; - if (load_addr(&b, argc, argv, file, 0)) + if (load_addr(&b, argc, argv, file, 0, opts)) goto _error; CREATE_TABLE; if (opts & PF_OPT_VERBOSE) @@ -212,12 +210,13 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command, xprintf(opts, "%d/%d addresses added", nadd, b.pfrb_size); if (opts & PF_OPT_VERBOSE) PFRB_FOREACH(a, &b) - if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback) + if ((opts & PF_OPT_VERBOSE2) || + a->pfra_fback != PFR_FB_NONE) print_addrx(a, NULL, opts & PF_OPT_USEDNS); } else if (!strcmp(command, "delete")) { b.pfrb_type = PFRB_ADDRS; - if (load_addr(&b, argc, argv, file, 0)) + if (load_addr(&b, argc, argv, file, 0, opts)) goto _error; if (opts & PF_OPT_VERBOSE) flags |= PFR_FLAG_FEEDBACK; @@ -226,12 +225,13 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command, xprintf(opts, "%d/%d addresses deleted", ndel, b.pfrb_size); if (opts & PF_OPT_VERBOSE) PFRB_FOREACH(a, &b) - if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback) + if ((opts & PF_OPT_VERBOSE2) || + a->pfra_fback != PFR_FB_NONE) print_addrx(a, NULL, opts & PF_OPT_USEDNS); } else if (!strcmp(command, "replace")) { b.pfrb_type = PFRB_ADDRS; - if (load_addr(&b, argc, argv, file, 0)) + if (load_addr(&b, argc, argv, file, 0, opts)) goto _error; CREATE_TABLE; if (opts & PF_OPT_VERBOSE) @@ -257,7 +257,8 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command, xprintf(opts, "no changes"); if (opts & PF_OPT_VERBOSE) PFRB_FOREACH(a, &b) - if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback) + if ((opts & PF_OPT_VERBOSE2) || + a->pfra_fback != PFR_FB_NONE) print_addrx(a, NULL, opts & PF_OPT_USEDNS); } else if (!strcmp(command, "expire")) { @@ -280,7 +281,7 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command, break; } PFRB_FOREACH(p, &b) { - ((struct pfr_astats *)p)->pfras_a.pfra_fback = 0; + ((struct pfr_astats *)p)->pfras_a.pfra_fback = PFR_FB_NONE; if (time(NULL) - ((struct pfr_astats *)p)->pfras_tzero > lifetime) if (pfr_buf_add(&b2, @@ -295,7 +296,8 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command, xprintf(opts, "%d/%d addresses expired", ndel, b2.pfrb_size); if (opts & PF_OPT_VERBOSE) PFRB_FOREACH(a, &b2) - if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback) + if ((opts & PF_OPT_VERBOSE2) || + a->pfra_fback != PFR_FB_NONE) print_addrx(a, NULL, opts & PF_OPT_USEDNS); } else if (!strcmp(command, "reset")) { @@ -354,7 +356,7 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command, b.pfrb_type = PFRB_ADDRS; b2.pfrb_type = PFRB_ADDRS; - if (load_addr(&b, argc, argv, file, 1)) + if (load_addr(&b, argc, argv, file, 1, opts)) goto _error; if (opts & PF_OPT_VERBOSE2) { flags |= PFR_FLAG_REPLACE; @@ -381,7 +383,7 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command, rv = 2; } else if (!strcmp(command, "zero") && (argc || file != NULL)) { b.pfrb_type = PFRB_ADDRS; - if (load_addr(&b, argc, argv, file, 0)) + if (load_addr(&b, argc, argv, file, 0, opts)) goto _error; if (opts & PF_OPT_VERBOSE) flags |= PFR_FLAG_FEEDBACK; @@ -396,7 +398,7 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command, opts & PF_OPT_USEDNS); } else if (!strcmp(command, "zero")) { flags |= PFR_FLAG_ADDRSTOO; - RVTEST(pfr_clr_tstats(&table, 1, &nzero, flags)); + RVTEST(pfctl_clear_tstats(pfh, &table, &nzero, flags)); xprintf(opts, "%d table/stats cleared", nzero); } else warnx("pfctl_table: unknown command '%s'", command); @@ -415,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]); @@ -461,15 +468,15 @@ print_tstats(const struct pfr_tstats *ts, int debug) int load_addr(struct pfr_buffer *b, int argc, char *argv[], char *file, - int nonetwork) + int nonetwork, int opts) { while (argc--) - if (append_addr(b, *argv++, nonetwork)) { + if (append_addr(b, *argv++, nonetwork, opts)) { if (errno) warn("cannot decode %s", argv[-1]); return (-1); } - if (pfr_buf_load(b, file, nonetwork, append_addr)) { + if (pfr_buf_load(b, file, nonetwork, append_addr, opts)) { warn("cannot load %s", file); return (-1); } @@ -541,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++) @@ -556,37 +568,59 @@ print_astats(struct pfr_astats *as, int dns) (unsigned long long)as->pfras_bytes[dir][op]); } -void -radix_perror(void) -{ - extern char *__progname; - fprintf(stderr, "%s: %s.\n", __progname, pfr_strerror(errno)); -} - int pfctl_define_table(char *name, int flags, int addrs, const char *anchor, - struct pfr_buffer *ab, u_int32_t ticket) + struct pfr_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 -warn_namespace_collision(const char *filter) +warn_duplicate_tables(const char *tablename, const char *anchorname) { struct pfr_buffer b; struct pfr_table *t; - const char *name = NULL, *lastcoll; - int coll = 0; bzero(&b, sizeof(b)); b.pfrb_type = PFRB_TABLES; @@ -602,22 +636,13 @@ warn_namespace_collision(const char *filter) PFRB_FOREACH(t, &b) { if (!(t->pfrt_flags & PFR_TFLAG_ACTIVE)) continue; - if (filter != NULL && strcmp(filter, t->pfrt_name)) + if (!strcmp(anchorname, t->pfrt_anchor)) continue; - if (!t->pfrt_anchor[0]) - name = t->pfrt_name; - else if (name != NULL && !strcmp(name, t->pfrt_name)) { - coll++; - lastcoll = name; - name = NULL; - } + if (!strcmp(tablename, t->pfrt_name)) + warnx("warning: table <%s> already defined" + " in anchor \"%s\"", tablename, + t->pfrt_anchor[0] ? t->pfrt_anchor : "/"); } - if (coll == 1) - warnx("warning: namespace collision with <%s> global table.", - lastcoll); - else if (coll > 1) - warnx("warning: namespace collisions with %d global tables.", - coll); pfr_buf_clear(&b); } @@ -644,7 +669,7 @@ xprintf(int opts, const char *fmt, ...) /* interface stuff */ -int +void pfctl_show_ifaces(const char *filter, int opts) { struct pfr_buffer b; @@ -655,10 +680,8 @@ pfctl_show_ifaces(const char *filter, int opts) for (;;) { pfr_buf_grow(&b, b.pfrb_size); b.pfrb_size = b.pfrb_msize; - if (pfi_get_ifaces(filter, b.pfrb_caddr, &b.pfrb_size)) { - radix_perror(); - return (1); - } + if (pfi_get_ifaces(filter, b.pfrb_caddr, &b.pfrb_size)) + errx(1, "%s", pf_strerror(errno)); if (b.pfrb_size <= b.pfrb_msize) break; } @@ -666,14 +689,14 @@ pfctl_show_ifaces(const char *filter, int opts) pfctl_print_title("INTERFACES:"); PFRB_FOREACH(p, &b) print_iface(p, opts); - return (0); } void print_iface(struct pfi_kif *p, int opts) { - time_t tzero = p->pfik_tzero; - int i, af, dir, act; + time_t tzero = p->pfik_tzero; + int i, af, dir, act; + char *ct; printf("%s", p->pfik_name); if (opts & PF_OPT_VERBOSE) { @@ -684,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/Makefile b/sbin/pfctl/tests/files/Makefile index 27cf512d81c5..fc52b1db3c30 100644 --- a/sbin/pfctl/tests/files/Makefile +++ b/sbin/pfctl/tests/files/Makefile @@ -4,6 +4,6 @@ TESTSDIR= ${TESTSBASE}/sbin/pfctl/files BINDIR= ${TESTSDIR} # We use ${.CURDIR} as workaround so that the glob patterns work. -FILES!= echo ${.CURDIR}/pf????.in ${.CURDIR}/pf????.include ${.CURDIR}/pf????.ok +FILES!= echo ${.CURDIR}/pf????.in ${.CURDIR}/pf????.include ${.CURDIR}/pf????.ok ${.CURDIR}/pf????.fail .include <bsd.progs.mk> diff --git a/sbin/pfctl/tests/files/pf0016.in b/sbin/pfctl/tests/files/pf0016.in index 738bfb664395..7dbc53aa6a21 100644 --- a/sbin/pfctl/tests/files/pf0016.in +++ b/sbin/pfctl/tests/files/pf0016.in @@ -1,5 +1,5 @@ # Test rule order processing: should fail unless nat -> filter -#match out on lo0 from 192.168.1.1 to any nat-to 10.0.0.1 -#match in on lo0 proto tcp from any to 1.2.3.4/32 port 2222 rdr-to 10.0.0.10 port 22 -#match on lo0 from 192.168.1.1 to any binat-to 10.0.0.1 +match out on lo0 from 192.168.1.1 to any nat-to 10.0.0.1 +match in on lo0 proto tcp from any to 1.2.3.4/32 port 2222 rdr-to 10.0.0.10 port 22 +match on lo0 from 192.168.1.1 to any binat-to 10.0.0.1 pass in on lo1000000 from any to any no state diff --git a/sbin/pfctl/tests/files/pf0016.ok b/sbin/pfctl/tests/files/pf0016.ok index 6f0c211a5b8a..d65374a16475 100644 --- a/sbin/pfctl/tests/files/pf0016.ok +++ b/sbin/pfctl/tests/files/pf0016.ok @@ -1 +1,5 @@ +match out on lo0 inet from 192.168.1.1 to any nat-to 10.0.0.1 +match in on lo0 inet proto tcp from any to 1.2.3.4 port = 2222 rdr-to 10.0.0.10 port 22 +match out on lo0 inet from 192.168.1.1 to any nat-to 10.0.0.1 static-port +match in on lo0 inet from any to 10.0.0.1 rdr-to 192.168.1.1 pass in on lo1000000 all no state diff --git a/sbin/pfctl/tests/files/pf0018.in b/sbin/pfctl/tests/files/pf0018.in index 46606b476d79..ab3c81f86c5f 100644 --- a/sbin/pfctl/tests/files/pf0018.in +++ b/sbin/pfctl/tests/files/pf0018.in @@ -3,17 +3,17 @@ TEST_LIST1 = "{ 192.168.1.5, 192.168.1.6, 192.168.1.7 }" TEST_LIST2 = "{ 172.6.1.1, 172.14.1.2/32, 172.16.2.0/24 }" -#match out on lo0 from 192.168.1.1 to any nat-to 10.0.0.1 -#match out on lo0 proto tcp from 192.168.1.2 to any nat-to 10.0.0.2 -#match out on lo0 proto udp from 192.168.1.3 to any nat-to 10.0.0.3 -#match out on lo0 proto icmp from 192.168.1.4 to any nat-to 10.0.0.4 +match out on lo0 from 192.168.1.1 to any nat-to 10.0.0.1 +match out on lo0 proto tcp from 192.168.1.2 to any nat-to 10.0.0.2 +match out on lo0 proto udp from 192.168.1.3 to any nat-to 10.0.0.3 +match out on lo0 proto icmp from 192.168.1.4 to any nat-to 10.0.0.4 -#match out on lo0 inet from $TEST_LIST1 to $TEST_LIST2 nat-to lo0 +match out on lo0 inet from $TEST_LIST1 to $TEST_LIST2 nat-to lo0 -#match out on lo0 inet from 192.168.0.1/24 to any nat-to (lo0) +match out on lo0 inet from 192.168.0.1/24 to any nat-to (lo0) -#match out on lo0 from 192.168.1.8 to ! 172.17.0.0/16 nat-to 10.0.0.8 +match out on lo0 from 192.168.1.8 to ! 172.17.0.0/16 nat-to 10.0.0.8 -#match out on ! lo0 proto { udp, tcp } from any to any nat-to 10.0.0.8 static-port +match out on ! lo0 proto { udp, tcp } from any to any nat-to 10.0.0.8 static-port -#match out on { lo0, tun1000000 } from any to any nat-to 10.0.0.8 +match out on { lo0, tun1000000 } from any to any nat-to 10.0.0.8 diff --git a/sbin/pfctl/tests/files/pf0018.ok b/sbin/pfctl/tests/files/pf0018.ok index c19ead6da1f0..6ba137ae84f8 100644 --- a/sbin/pfctl/tests/files/pf0018.ok +++ b/sbin/pfctl/tests/files/pf0018.ok @@ -1,2 +1,21 @@ TEST_LIST1 = "{ 192.168.1.5, 192.168.1.6, 192.168.1.7 }" TEST_LIST2 = "{ 172.6.1.1, 172.14.1.2/32, 172.16.2.0/24 }" +match out on lo0 inet from 192.168.1.1 to any nat-to 10.0.0.1 +match out on lo0 inet proto tcp from 192.168.1.2 to any nat-to 10.0.0.2 +match out on lo0 inet proto udp from 192.168.1.3 to any nat-to 10.0.0.3 +match out on lo0 inet proto icmp from 192.168.1.4 to any nat-to 10.0.0.4 +match out on lo0 inet from 192.168.1.5 to 172.6.1.1 nat-to 127.0.0.1 +match out on lo0 inet from 192.168.1.5 to 172.14.1.2 nat-to 127.0.0.1 +match out on lo0 inet from 192.168.1.5 to 172.16.2.0/24 nat-to 127.0.0.1 +match out on lo0 inet from 192.168.1.6 to 172.6.1.1 nat-to 127.0.0.1 +match out on lo0 inet from 192.168.1.6 to 172.14.1.2 nat-to 127.0.0.1 +match out on lo0 inet from 192.168.1.6 to 172.16.2.0/24 nat-to 127.0.0.1 +match out on lo0 inet from 192.168.1.7 to 172.6.1.1 nat-to 127.0.0.1 +match out on lo0 inet from 192.168.1.7 to 172.14.1.2 nat-to 127.0.0.1 +match out on lo0 inet from 192.168.1.7 to 172.16.2.0/24 nat-to 127.0.0.1 +match out on lo0 inet from 192.168.0.0/24 to any nat-to (lo0) round-robin +match out on lo0 inet from 192.168.1.8 to ! 172.17.0.0/16 nat-to 10.0.0.8 +match out on ! lo0 inet proto udp all nat-to 10.0.0.8 static-port +match out on ! lo0 inet proto tcp all nat-to 10.0.0.8 static-port +match out on lo0 inet all nat-to 10.0.0.8 +match out on tun1000000 inet all nat-to 10.0.0.8 diff --git a/sbin/pfctl/tests/files/pf0019.in b/sbin/pfctl/tests/files/pf0019.in index 0b1456e6fd03..e2bedbb64bd0 100644 --- a/sbin/pfctl/tests/files/pf0019.in +++ b/sbin/pfctl/tests/files/pf0019.in @@ -3,7 +3,7 @@ GOOD = "{ lo0, lo1000000 }" GOOD_NET = "{ 127.0.0.0/24, 10.0.1.0/24 }" DEST_NET = "{ 1.2.3.4/25, 2.4.6.8/30 }" -#match in on lo0 proto tcp from any to 1.2.3.4/32 port 2222 rdr-to 10.0.0.10 port 22 +match in on lo0 proto tcp from any to 1.2.3.4/32 port 2222 rdr-to 10.0.0.10 port 22 # Test list processing -#match in on $GOOD proto tcp from $GOOD_NET to $DEST_NET port 21 rdr-to 127.0.0.1 port 8021 +match in on $GOOD proto tcp from $GOOD_NET to $DEST_NET port 21 rdr-to 127.0.0.1 port 8021 diff --git a/sbin/pfctl/tests/files/pf0019.ok b/sbin/pfctl/tests/files/pf0019.ok index 16c845aa2cd6..a5afc374d19f 100644 --- a/sbin/pfctl/tests/files/pf0019.ok +++ b/sbin/pfctl/tests/files/pf0019.ok @@ -2,3 +2,12 @@ EVIL = "lo0" GOOD = "{ lo0, lo1000000 }" GOOD_NET = "{ 127.0.0.0/24, 10.0.1.0/24 }" DEST_NET = "{ 1.2.3.4/25, 2.4.6.8/30 }" +match in on lo0 inet proto tcp from any to 1.2.3.4 port = 2222 rdr-to 10.0.0.10 port 22 +match in on lo0 inet proto tcp from 127.0.0.0/24 to 1.2.3.0/25 port = ftp rdr-to 127.0.0.1 port 8021 +match in on lo0 inet proto tcp from 127.0.0.0/24 to 2.4.6.8/30 port = ftp rdr-to 127.0.0.1 port 8021 +match in on lo0 inet proto tcp from 10.0.1.0/24 to 1.2.3.0/25 port = ftp rdr-to 127.0.0.1 port 8021 +match in on lo0 inet proto tcp from 10.0.1.0/24 to 2.4.6.8/30 port = ftp rdr-to 127.0.0.1 port 8021 +match in on lo1000000 inet proto tcp from 127.0.0.0/24 to 1.2.3.0/25 port = ftp rdr-to 127.0.0.1 port 8021 +match in on lo1000000 inet proto tcp from 127.0.0.0/24 to 2.4.6.8/30 port = ftp rdr-to 127.0.0.1 port 8021 +match in on lo1000000 inet proto tcp from 10.0.1.0/24 to 1.2.3.0/25 port = ftp rdr-to 127.0.0.1 port 8021 +match in on lo1000000 inet proto tcp from 10.0.1.0/24 to 2.4.6.8/30 port = ftp rdr-to 127.0.0.1 port 8021 diff --git a/sbin/pfctl/tests/files/pf0020.in b/sbin/pfctl/tests/files/pf0020.in index b00125bbcdb8..c973785bc9c5 100644 --- a/sbin/pfctl/tests/files/pf0020.in +++ b/sbin/pfctl/tests/files/pf0020.in @@ -5,5 +5,5 @@ GOOD = "{ lo0, lo1000000 }" GOOD_NET = "{ 127.0.0.0/24, 10.0.1.0/24 }" DEST_NET = "{ 1.2.3.4/25, 2.4.6.8/30 }" -#match out on $EVIL inet from $GOOD_NET to $DEST_NET nat-to $EVIL -#match in on $GOOD proto tcp from $GOOD_NET to $DEST_NET port 21 rdr-to 127.0.0.1 port 8021 +match out on $EVIL inet from $GOOD_NET to $DEST_NET nat-to $EVIL +match in on $GOOD proto tcp from $GOOD_NET to $DEST_NET port 21 rdr-to 127.0.0.1 port 8021 diff --git a/sbin/pfctl/tests/files/pf0020.ok b/sbin/pfctl/tests/files/pf0020.ok index 16c845aa2cd6..bd2c6cf2055d 100644 --- a/sbin/pfctl/tests/files/pf0020.ok +++ b/sbin/pfctl/tests/files/pf0020.ok @@ -2,3 +2,15 @@ EVIL = "lo0" GOOD = "{ lo0, lo1000000 }" GOOD_NET = "{ 127.0.0.0/24, 10.0.1.0/24 }" DEST_NET = "{ 1.2.3.4/25, 2.4.6.8/30 }" +match out on lo0 inet from 127.0.0.0/24 to 1.2.3.0/25 nat-to 127.0.0.1 +match out on lo0 inet from 127.0.0.0/24 to 2.4.6.8/30 nat-to 127.0.0.1 +match out on lo0 inet from 10.0.1.0/24 to 1.2.3.0/25 nat-to 127.0.0.1 +match out on lo0 inet from 10.0.1.0/24 to 2.4.6.8/30 nat-to 127.0.0.1 +match in on lo0 inet proto tcp from 127.0.0.0/24 to 1.2.3.0/25 port = ftp rdr-to 127.0.0.1 port 8021 +match in on lo0 inet proto tcp from 127.0.0.0/24 to 2.4.6.8/30 port = ftp rdr-to 127.0.0.1 port 8021 +match in on lo0 inet proto tcp from 10.0.1.0/24 to 1.2.3.0/25 port = ftp rdr-to 127.0.0.1 port 8021 +match in on lo0 inet proto tcp from 10.0.1.0/24 to 2.4.6.8/30 port = ftp rdr-to 127.0.0.1 port 8021 +match in on lo1000000 inet proto tcp from 127.0.0.0/24 to 1.2.3.0/25 port = ftp rdr-to 127.0.0.1 port 8021 +match in on lo1000000 inet proto tcp from 127.0.0.0/24 to 2.4.6.8/30 port = ftp rdr-to 127.0.0.1 port 8021 +match in on lo1000000 inet proto tcp from 10.0.1.0/24 to 1.2.3.0/25 port = ftp rdr-to 127.0.0.1 port 8021 +match in on lo1000000 inet proto tcp from 10.0.1.0/24 to 2.4.6.8/30 port = ftp rdr-to 127.0.0.1 port 8021 diff --git a/sbin/pfctl/tests/files/pf0048.in b/sbin/pfctl/tests/files/pf0048.in index e97a819de945..a0dd143c8dd2 100644 --- a/sbin/pfctl/tests/files/pf0048.in +++ b/sbin/pfctl/tests/files/pf0048.in @@ -1,12 +1,12 @@ table < regress > { 1.2.3.4 !5.6.7.8 10/8 lo0 } table <regress.1> const { ::1 fe80::/64 } table <regress.a> { 1.2.3.4 !5.6.7.8 } { ::1 ::2 ::3 } file "/dev/null" const { 4.3.2.1 } -#match out on lo0 inet from < regress.1> to <regress.2> nat-to lo0:0 -#match out on !lo0 inet from !<regress.1 > to <regress.2> nat-to lo0:0 -#match in on lo0 inet6 from <regress.1> to <regress.2> rdr-to lo0:0 -#match in on !lo0 inet6 from !< regress.1 > to <regress.2> rdr-to lo0:0 -#match in from { <regress.1> !<regress.2> } to any -#match out from any to { !<regress.1>, <regress.2> } +match out on lo0 inet from < regress.1> to <regress.2> nat-to lo0:0 +match out on !lo0 inet from !<regress.1 > to <regress.2> nat-to lo0:0 +match in on lo0 inet6 from <regress.1> to <regress.2> rdr-to lo0:0 +match in on !lo0 inet6 from !< regress.1 > to <regress.2> rdr-to lo0:0 +match in from { <regress.1> !<regress.2> } to any +match out from any to { !<regress.1>, <regress.2> } pass in from <regress> to any pass out from any to <regress > pass in from { <regress.1> <regress.2> } to any diff --git a/sbin/pfctl/tests/files/pf0048.ok b/sbin/pfctl/tests/files/pf0048.ok index f3536f566d35..89569fb4f8ba 100644 --- a/sbin/pfctl/tests/files/pf0048.ok +++ b/sbin/pfctl/tests/files/pf0048.ok @@ -1,6 +1,14 @@ table <regress> { 1.2.3.4 !5.6.7.8 10.0.0.0/8 ::1 fe80::1 127.0.0.1 } table <regress.1> const { ::1 fe80::/64 } table <regress.a> const { 1.2.3.4 !5.6.7.8 ::1 ::2 ::3 } file "/dev/null" { 4.3.2.1 } +match out on lo0 inet from <regress.1> to <regress.2> nat-to 127.0.0.1 +match out on ! lo0 inet from ! <regress.1> to <regress.2> nat-to 127.0.0.1 +match in on lo0 inet6 from <regress.1> to <regress.2> rdr-to ::1 +match in on ! lo0 inet6 from ! <regress.1> to <regress.2> rdr-to ::1 +match in from <regress.1> to any +match in from ! <regress.2> to any +match out from any to ! <regress.1> +match out from any to <regress.2> pass in from <regress> to any flags S/SA keep state pass out from any to <regress> flags S/SA keep state pass in from <regress.1> to any flags S/SA keep state diff --git a/sbin/pfctl/tests/files/pf0069.in b/sbin/pfctl/tests/files/pf0069.in index 1298954bbeda..85847b9bd6b2 100644 --- a/sbin/pfctl/tests/files/pf0069.in +++ b/sbin/pfctl/tests/files/pf0069.in @@ -1,3 +1,2 @@ -#match out on lo0 inet all tag regress nat-to lo0 +match out on lo0 inet all tag regress nat-to lo0 pass out quick on lo0 keep state tagged regress - diff --git a/sbin/pfctl/tests/files/pf0069.ok b/sbin/pfctl/tests/files/pf0069.ok index 33e0519645fc..2bf34c04baa7 100644 --- a/sbin/pfctl/tests/files/pf0069.ok +++ b/sbin/pfctl/tests/files/pf0069.ok @@ -1 +1,2 @@ +match out on lo0 inet all tag regress nat-to 127.0.0.1 pass out quick on lo0 all flags S/SA keep state tagged regress diff --git a/sbin/pfctl/tests/files/pf0070.in b/sbin/pfctl/tests/files/pf0070.in index 8d5e34a13ff8..1ccec9302436 100644 --- a/sbin/pfctl/tests/files/pf0070.in +++ b/sbin/pfctl/tests/files/pf0070.in @@ -1,3 +1,2 @@ -#match out on lo0 from 10.0.0.0/8 to any nat-to lo0 +match out on lo0 from 10.0.0.0/8 to any nat-to lo0 block out on lo0 tagged regress - diff --git a/sbin/pfctl/tests/files/pf0070.ok b/sbin/pfctl/tests/files/pf0070.ok index d30b70ff3e5a..cf79485b40c1 100644 --- a/sbin/pfctl/tests/files/pf0070.ok +++ b/sbin/pfctl/tests/files/pf0070.ok @@ -1 +1,2 @@ +match out on lo0 inet from 10.0.0.0/8 to any nat-to 127.0.0.1 block drop out on lo0 all tagged regress diff --git a/sbin/pfctl/tests/files/pf0071.in b/sbin/pfctl/tests/files/pf0071.in index 48976b61ed3d..8975a8ebc943 100644 --- a/sbin/pfctl/tests/files/pf0071.in +++ b/sbin/pfctl/tests/files/pf0071.in @@ -1,3 +1,2 @@ -#match in on lo0 proto tcp from 10.0.0.0/8 to port 80 rdr-to lo0 +match in on lo0 proto tcp from 10.0.0.0/8 to port 80 rdr-to lo0 block out on lo0 tagged regress - diff --git a/sbin/pfctl/tests/files/pf0071.ok b/sbin/pfctl/tests/files/pf0071.ok index d30b70ff3e5a..2bae94fc8fac 100644 --- a/sbin/pfctl/tests/files/pf0071.ok +++ b/sbin/pfctl/tests/files/pf0071.ok @@ -1 +1,2 @@ +match in on lo0 inet proto tcp from 10.0.0.0/8 to any port = http rdr-to 127.0.0.1 block drop out on lo0 all tagged regress diff --git a/sbin/pfctl/tests/files/pf0072.in b/sbin/pfctl/tests/files/pf0072.in index fd037f31ef27..d23843b799d5 100644 --- a/sbin/pfctl/tests/files/pf0072.in +++ b/sbin/pfctl/tests/files/pf0072.in @@ -1,4 +1,3 @@ # test binat tagging -#match on lo0 from 192.168.1.1 to any tag regress binat-to 10.0.0.1 +match on lo0 from 192.168.1.1 to any tag regress binat-to 10.0.0.1 block out on lo0 tagged regress - diff --git a/sbin/pfctl/tests/files/pf0072.ok b/sbin/pfctl/tests/files/pf0072.ok index d30b70ff3e5a..02e676dadc06 100644 --- a/sbin/pfctl/tests/files/pf0072.ok +++ b/sbin/pfctl/tests/files/pf0072.ok @@ -1 +1,3 @@ +match out on lo0 inet from 192.168.1.1 to any tag regress nat-to 10.0.0.1 static-port +match in on lo0 inet from any to 10.0.0.1 tag regress rdr-to 192.168.1.1 block drop out on lo0 all tagged regress diff --git a/sbin/pfctl/tests/files/pf0084.in b/sbin/pfctl/tests/files/pf0084.in index c0390df889e3..17140a786d73 100644 --- a/sbin/pfctl/tests/files/pf0084.in +++ b/sbin/pfctl/tests/files/pf0084.in @@ -1,9 +1,9 @@ -#match out on tun1000000 from 10.0.0.0/24 to any \ -# nat-to { 10.0.1.1, 10.0.1.2 } round-robin sticky-address -#match in on tun1000000 from any to 10.0.1.1 \ -# rdr-to { 10.0.0.0/24 } sticky-address random -#match in on tun1000000 from any to 10.0.1.2 \ -# rdr-to { 10.0.0.1, 10.0.0.2 } sticky-address +match out on tun1000000 from 10.0.0.0/24 to any \ + nat-to { 10.0.1.1, 10.0.1.2 } round-robin sticky-address +match in on tun1000000 from any to 10.0.1.1 \ + rdr-to { 10.0.0.0/24 } sticky-address random +match in on tun1000000 from any to 10.0.1.2 \ + rdr-to { 10.0.0.1, 10.0.0.2 } sticky-address pass in proto tcp from any to any port 22 \ keep state (source-track) diff --git a/sbin/pfctl/tests/files/pf0084.ok b/sbin/pfctl/tests/files/pf0084.ok index 272fd6052023..1ca89e515a3d 100644 --- a/sbin/pfctl/tests/files/pf0084.ok +++ b/sbin/pfctl/tests/files/pf0084.ok @@ -1,3 +1,6 @@ +match out on tun1000000 inet from 10.0.0.0/24 to any nat-to { 10.0.1.1, 10.0.1.2 } round-robin sticky-address +match in on tun1000000 inet from any to 10.0.1.1 rdr-to 10.0.0.0/24 random sticky-address +match in on tun1000000 inet from any to 10.0.1.2 rdr-to { 10.0.0.1, 10.0.0.2 } round-robin sticky-address pass in proto tcp from any to any port = ssh flags S/SA keep state (source-track global) pass in proto tcp from any to any port = smtp flags S/SA keep state (source-track global) pass in proto tcp from any to any port = http flags S/SA keep state (source-track rule, max-src-states 3, max-src-nodes 1000) 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/pf0098.in b/sbin/pfctl/tests/files/pf0098.in index b2b642be2026..c26f0fcfe4d3 100644 --- a/sbin/pfctl/tests/files/pf0098.in +++ b/sbin/pfctl/tests/files/pf0098.in @@ -1,4 +1,3 @@ # Test rule order processing should pass (require-order no longer required) pass in on lo1000000 all -#match out on lo0 inet6 all nat-to lo0 - +match out on lo0 inet6 all nat-to lo0 diff --git a/sbin/pfctl/tests/files/pf0098.ok b/sbin/pfctl/tests/files/pf0098.ok index 62016c91d60b..105bb46b4ae5 100644 --- a/sbin/pfctl/tests/files/pf0098.ok +++ b/sbin/pfctl/tests/files/pf0098.ok @@ -1 +1,2 @@ pass in on lo1000000 all flags S/SA keep state +match out on lo0 inet6 all nat-to { ::1, fe80::1 } round-robin diff --git a/sbin/pfctl/tests/files/pf1026.ok b/sbin/pfctl/tests/files/pf1026.ok index 5b849fe80bd3..323036f2b800 100644 --- a/sbin/pfctl/tests/files/pf1026.ok +++ b/sbin/pfctl/tests/files/pf1026.ok @@ -1 +1 @@ -pass in on epair2b route-to (epair0a 192.0.2.2) inet6 from any to 64:ff9b::/96 flags S/SA keep state af-to inet from (epair0a) +pass in on epair2b route-to (epair0a 192.0.2.2) inet6 from any to 64:ff9b::/96 flags S/SA keep state af-to inet from (epair0a) round-robin diff --git a/sbin/pfctl/tests/files/pf1027.ok b/sbin/pfctl/tests/files/pf1027.ok index 5a3f30ae1592..b50f1e216837 100644 --- a/sbin/pfctl/tests/files/pf1027.ok +++ b/sbin/pfctl/tests/files/pf1027.ok @@ -1 +1 @@ -pass in on epair2b reply-to (epair0a 2001:db8::1) inet6 from any to 64:ff9b::/96 flags S/SA keep state af-to inet from (epair0a) +pass in on epair2b reply-to (epair0a 2001:db8::1) inet6 from any to 64:ff9b::/96 flags S/SA keep state af-to inet from (epair0a) round-robin diff --git a/sbin/pfctl/tests/files/pf1028.in b/sbin/pfctl/tests/files/pf1028.in new file mode 100644 index 000000000000..2386fcb52249 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1028.in @@ -0,0 +1 @@ +rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 diff --git a/sbin/pfctl/tests/files/pf1028.ok b/sbin/pfctl/tests/files/pf1028.ok new file mode 100644 index 000000000000..07be890f4e05 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1028.ok @@ -0,0 +1 @@ +rdr on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 diff --git a/sbin/pfctl/tests/files/pf1029.in b/sbin/pfctl/tests/files/pf1029.in new file mode 100644 index 000000000000..73815839aadd --- /dev/null +++ b/sbin/pfctl/tests/files/pf1029.in @@ -0,0 +1 @@ +rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1002 diff --git a/sbin/pfctl/tests/files/pf1029.ok b/sbin/pfctl/tests/files/pf1029.ok new file mode 100644 index 000000000000..6e9083bf856a --- /dev/null +++ b/sbin/pfctl/tests/files/pf1029.ok @@ -0,0 +1 @@ +rdr on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1002 diff --git a/sbin/pfctl/tests/files/pf1030.in b/sbin/pfctl/tests/files/pf1030.in new file mode 100644 index 000000000000..b6f891998a71 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1030.in @@ -0,0 +1 @@ +rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 50001:65535 diff --git a/sbin/pfctl/tests/files/pf1030.ok b/sbin/pfctl/tests/files/pf1030.ok new file mode 100644 index 000000000000..4f6b2eba2f39 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1030.ok @@ -0,0 +1 @@ +rdr on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 50001:65535 diff --git a/sbin/pfctl/tests/files/pf1031.in b/sbin/pfctl/tests/files/pf1031.in new file mode 100644 index 000000000000..7cad4ae64000 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1031.in @@ -0,0 +1 @@ +rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 port 1004:2004 -> 192.0.2.3 port 1004 diff --git a/sbin/pfctl/tests/files/pf1031.ok b/sbin/pfctl/tests/files/pf1031.ok new file mode 100644 index 000000000000..8dd7fe027716 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1031.ok @@ -0,0 +1 @@ +rdr on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 port 1004:2004 -> 192.0.2.3 port 1004 diff --git a/sbin/pfctl/tests/files/pf1032.in b/sbin/pfctl/tests/files/pf1032.in new file mode 100644 index 000000000000..a2eec78da045 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1032.in @@ -0,0 +1 @@ +rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 port 1005:2005 -> 192.0.2.3 port 3004:* diff --git a/sbin/pfctl/tests/files/pf1032.ok b/sbin/pfctl/tests/files/pf1032.ok new file mode 100644 index 000000000000..3b3f124efc33 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1032.ok @@ -0,0 +1 @@ +rdr on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 port 1005:2005 -> 192.0.2.3 port 3004:4004 diff --git a/sbin/pfctl/tests/files/pf1033.fail b/sbin/pfctl/tests/files/pf1033.fail new file mode 100644 index 000000000000..d9fbfe4296e3 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1033.fail @@ -0,0 +1 @@ +the 'static-port' option is only valid with nat rules diff --git a/sbin/pfctl/tests/files/pf1033.in b/sbin/pfctl/tests/files/pf1033.in new file mode 100644 index 000000000000..76f33e7e8f0e --- /dev/null +++ b/sbin/pfctl/tests/files/pf1033.in @@ -0,0 +1 @@ +rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 static-port diff --git a/sbin/pfctl/tests/files/pf1034.fail b/sbin/pfctl/tests/files/pf1034.fail new file mode 100644 index 000000000000..e407996a8fa3 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1034.fail @@ -0,0 +1 @@ +the 'map-e-portset' option is only valid with nat rules diff --git a/sbin/pfctl/tests/files/pf1034.in b/sbin/pfctl/tests/files/pf1034.in new file mode 100644 index 000000000000..be847a8af241 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1034.in @@ -0,0 +1 @@ +rdr on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 map-e-portset 6/8/0x34 diff --git a/sbin/pfctl/tests/files/pf1035.in b/sbin/pfctl/tests/files/pf1035.in new file mode 100644 index 000000000000..9382ffedc8c9 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1035.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 diff --git a/sbin/pfctl/tests/files/pf1035.ok b/sbin/pfctl/tests/files/pf1035.ok new file mode 100644 index 000000000000..be573ef460f5 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1035.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 diff --git a/sbin/pfctl/tests/files/pf1036.in b/sbin/pfctl/tests/files/pf1036.in new file mode 100644 index 000000000000..81718c908303 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1036.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 50001:65535 diff --git a/sbin/pfctl/tests/files/pf1036.ok b/sbin/pfctl/tests/files/pf1036.ok new file mode 100644 index 000000000000..be573ef460f5 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1036.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 diff --git a/sbin/pfctl/tests/files/pf1037.in b/sbin/pfctl/tests/files/pf1037.in new file mode 100644 index 000000000000..a30f6c0e7bbe --- /dev/null +++ b/sbin/pfctl/tests/files/pf1037.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1003 diff --git a/sbin/pfctl/tests/files/pf1037.ok b/sbin/pfctl/tests/files/pf1037.ok new file mode 100644 index 000000000000..020e2de28dec --- /dev/null +++ b/sbin/pfctl/tests/files/pf1037.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1003 diff --git a/sbin/pfctl/tests/files/pf1038.in b/sbin/pfctl/tests/files/pf1038.in new file mode 100644 index 000000000000..532060e56494 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1038.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1004:2004 diff --git a/sbin/pfctl/tests/files/pf1038.ok b/sbin/pfctl/tests/files/pf1038.ok new file mode 100644 index 000000000000..a4021db7b1b2 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1038.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1004:2004 diff --git a/sbin/pfctl/tests/files/pf1039.in b/sbin/pfctl/tests/files/pf1039.in new file mode 100644 index 000000000000..dba14b0625de --- /dev/null +++ b/sbin/pfctl/tests/files/pf1039.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 static-port diff --git a/sbin/pfctl/tests/files/pf1039.ok b/sbin/pfctl/tests/files/pf1039.ok new file mode 100644 index 000000000000..80cfbe742865 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1039.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 static-port diff --git a/sbin/pfctl/tests/files/pf1040.fail b/sbin/pfctl/tests/files/pf1040.fail new file mode 100644 index 000000000000..5b9afc22b441 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1040.fail @@ -0,0 +1 @@ +the 'static-port' option can't be used when specifying a port range diff --git a/sbin/pfctl/tests/files/pf1040.in b/sbin/pfctl/tests/files/pf1040.in new file mode 100644 index 000000000000..38d7292a560a --- /dev/null +++ b/sbin/pfctl/tests/files/pf1040.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1006 static-port diff --git a/sbin/pfctl/tests/files/pf1040.ok b/sbin/pfctl/tests/files/pf1040.ok new file mode 100644 index 000000000000..ffe2e023f77c --- /dev/null +++ b/sbin/pfctl/tests/files/pf1040.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 map-e-portset 6/8/52 diff --git a/sbin/pfctl/tests/files/pf1041.in b/sbin/pfctl/tests/files/pf1041.in new file mode 100644 index 000000000000..4c384ac70e05 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1041.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 map-e-portset 6/8/0x34 diff --git a/sbin/pfctl/tests/files/pf1041.ok b/sbin/pfctl/tests/files/pf1041.ok new file mode 100644 index 000000000000..ffe2e023f77c --- /dev/null +++ b/sbin/pfctl/tests/files/pf1041.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 map-e-portset 6/8/52 diff --git a/sbin/pfctl/tests/files/pf1042.fail b/sbin/pfctl/tests/files/pf1042.fail new file mode 100644 index 000000000000..56e174a5ece5 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1042.fail @@ -0,0 +1 @@ +the 'map-e-portset' option can't be used 'static-port' diff --git a/sbin/pfctl/tests/files/pf1042.in b/sbin/pfctl/tests/files/pf1042.in new file mode 100644 index 000000000000..906f637b6a0a --- /dev/null +++ b/sbin/pfctl/tests/files/pf1042.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 static-port map-e-portset 6/8/0x34 diff --git a/sbin/pfctl/tests/files/pf1043.fail b/sbin/pfctl/tests/files/pf1043.fail new file mode 100644 index 000000000000..cdfab00916a2 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1043.fail @@ -0,0 +1 @@ +the 'map-e-portset' option can't be used when specifying a port range diff --git a/sbin/pfctl/tests/files/pf1043.in b/sbin/pfctl/tests/files/pf1043.in new file mode 100644 index 000000000000..15428a9e54bc --- /dev/null +++ b/sbin/pfctl/tests/files/pf1043.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 port 1007 map-e-portset 6/8/0x34 diff --git a/sbin/pfctl/tests/files/pf1044.in b/sbin/pfctl/tests/files/pf1044.in new file mode 100644 index 000000000000..6a927b66b83f --- /dev/null +++ b/sbin/pfctl/tests/files/pf1044.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> <targets> sticky-address diff --git a/sbin/pfctl/tests/files/pf1044.ok b/sbin/pfctl/tests/files/pf1044.ok new file mode 100644 index 000000000000..a68b1daaa73a --- /dev/null +++ b/sbin/pfctl/tests/files/pf1044.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> <targets> round-robin sticky-address diff --git a/sbin/pfctl/tests/files/pf1045.in b/sbin/pfctl/tests/files/pf1045.in new file mode 100644 index 000000000000..38f708ce19b8 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1045.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 bitmask diff --git a/sbin/pfctl/tests/files/pf1045.ok b/sbin/pfctl/tests/files/pf1045.ok new file mode 100644 index 000000000000..5388db7e58a4 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1045.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 bitmask diff --git a/sbin/pfctl/tests/files/pf1046.fail b/sbin/pfctl/tests/files/pf1046.fail new file mode 100644 index 000000000000..b152f9063241 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1046.fail @@ -0,0 +1 @@ +tables are not supported by pool type diff --git a/sbin/pfctl/tests/files/pf1046.in b/sbin/pfctl/tests/files/pf1046.in new file mode 100644 index 000000000000..e4a9f79efd6f --- /dev/null +++ b/sbin/pfctl/tests/files/pf1046.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> <targets> bitmask diff --git a/sbin/pfctl/tests/files/pf1047.fail b/sbin/pfctl/tests/files/pf1047.fail new file mode 100644 index 000000000000..239b96b2fed4 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1047.fail @@ -0,0 +1 @@ +interface \(vtnet1\) is not supported by pool type diff --git a/sbin/pfctl/tests/files/pf1047.in b/sbin/pfctl/tests/files/pf1047.in new file mode 100644 index 000000000000..369bfcb0fb26 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1047.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> (vtnet1) bitmask diff --git a/sbin/pfctl/tests/files/pf1048.in b/sbin/pfctl/tests/files/pf1048.in new file mode 100644 index 000000000000..01232a33b5d8 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1048.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 random diff --git a/sbin/pfctl/tests/files/pf1048.ok b/sbin/pfctl/tests/files/pf1048.ok new file mode 100644 index 000000000000..35e86fc676fc --- /dev/null +++ b/sbin/pfctl/tests/files/pf1048.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 random diff --git a/sbin/pfctl/tests/files/pf1049.in b/sbin/pfctl/tests/files/pf1049.in new file mode 100644 index 000000000000..3f2e5acf8265 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1049.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> { 192.0.2.3 } diff --git a/sbin/pfctl/tests/files/pf1049.ok b/sbin/pfctl/tests/files/pf1049.ok new file mode 100644 index 000000000000..be573ef460f5 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1049.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 192.0.2.3 diff --git a/sbin/pfctl/tests/files/pf1050.in b/sbin/pfctl/tests/files/pf1050.in new file mode 100644 index 000000000000..69ccaf445c3b --- /dev/null +++ b/sbin/pfctl/tests/files/pf1050.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> <targets> diff --git a/sbin/pfctl/tests/files/pf1050.ok b/sbin/pfctl/tests/files/pf1050.ok new file mode 100644 index 000000000000..24ca9b459bb7 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1050.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> <targets> round-robin diff --git a/sbin/pfctl/tests/files/pf1051.in b/sbin/pfctl/tests/files/pf1051.in new file mode 100644 index 000000000000..734da64a372c --- /dev/null +++ b/sbin/pfctl/tests/files/pf1051.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.1 203.0.113.2 } diff --git a/sbin/pfctl/tests/files/pf1051.ok b/sbin/pfctl/tests/files/pf1051.ok new file mode 100644 index 000000000000..86f23488be41 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1051.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.1, 203.0.113.2 } round-robin diff --git a/sbin/pfctl/tests/files/pf1052.in b/sbin/pfctl/tests/files/pf1052.in new file mode 100644 index 000000000000..2ea770f3c06e --- /dev/null +++ b/sbin/pfctl/tests/files/pf1052.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.1 <targets> } diff --git a/sbin/pfctl/tests/files/pf1052.ok b/sbin/pfctl/tests/files/pf1052.ok new file mode 100644 index 000000000000..b71d105eb77a --- /dev/null +++ b/sbin/pfctl/tests/files/pf1052.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.1, <targets> } round-robin diff --git a/sbin/pfctl/tests/files/pf1053.in b/sbin/pfctl/tests/files/pf1053.in new file mode 100644 index 000000000000..f0cced0b64a2 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1053.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 diff --git a/sbin/pfctl/tests/files/pf1053.ok b/sbin/pfctl/tests/files/pf1053.ok new file mode 100644 index 000000000000..de321b8c738f --- /dev/null +++ b/sbin/pfctl/tests/files/pf1053.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 diff --git a/sbin/pfctl/tests/files/pf1054.in b/sbin/pfctl/tests/files/pf1054.in new file mode 100644 index 000000000000..9e66bb2a81d6 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1054.in @@ -0,0 +1,3 @@ +# XXX: it causes just the 0th address to be used without cycling +# Probably a bug +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 round-robin diff --git a/sbin/pfctl/tests/files/pf1054.ok b/sbin/pfctl/tests/files/pf1054.ok new file mode 100644 index 000000000000..3d7ab7974d87 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1054.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 round-robin diff --git a/sbin/pfctl/tests/files/pf1055.in b/sbin/pfctl/tests/files/pf1055.in new file mode 100644 index 000000000000..c116ef5fd43e --- /dev/null +++ b/sbin/pfctl/tests/files/pf1055.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 source-hash 0x42424242424242424242424242424242 diff --git a/sbin/pfctl/tests/files/pf1055.ok b/sbin/pfctl/tests/files/pf1055.ok new file mode 100644 index 000000000000..468e47012169 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1055.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.0/24 source-hash 0x42424242424242424242424242424242 diff --git a/sbin/pfctl/tests/files/pf1056.in b/sbin/pfctl/tests/files/pf1056.in new file mode 100644 index 000000000000..bd2af077fc3f --- /dev/null +++ b/sbin/pfctl/tests/files/pf1056.in @@ -0,0 +1 @@ +pass in on vtnet0 inet6 from any to 64:ff9b::/96 af-to inet from 203.0.113.1 to 203.0.113.2 diff --git a/sbin/pfctl/tests/files/pf1056.ok b/sbin/pfctl/tests/files/pf1056.ok new file mode 100644 index 000000000000..0397570dbce0 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1056.ok @@ -0,0 +1 @@ +pass in on vtnet0 inet6 from any to 64:ff9b::/96 flags S/SA keep state af-to inet from 203.0.113.1 to 203.0.113.2 diff --git a/sbin/pfctl/tests/files/pf1057.in b/sbin/pfctl/tests/files/pf1057.in new file mode 100644 index 000000000000..0e26976e5a0d --- /dev/null +++ b/sbin/pfctl/tests/files/pf1057.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> vlan1057 diff --git a/sbin/pfctl/tests/files/pf1057.ok b/sbin/pfctl/tests/files/pf1057.ok new file mode 100644 index 000000000000..7626951e138c --- /dev/null +++ b/sbin/pfctl/tests/files/pf1057.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> 203.0.113.5 diff --git a/sbin/pfctl/tests/files/pf1058.in b/sbin/pfctl/tests/files/pf1058.in new file mode 100644 index 000000000000..27c0ef1d69b3 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1058.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.1 vlan1058 } diff --git a/sbin/pfctl/tests/files/pf1058.ok b/sbin/pfctl/tests/files/pf1058.ok new file mode 100644 index 000000000000..b1d2b07a58b4 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1058.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.1, 203.0.113.5 } round-robin diff --git a/sbin/pfctl/tests/files/pf1059.in b/sbin/pfctl/tests/files/pf1059.in new file mode 100644 index 000000000000..92ed5c50656b --- /dev/null +++ b/sbin/pfctl/tests/files/pf1059.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> (vlan1059) diff --git a/sbin/pfctl/tests/files/pf1059.ok b/sbin/pfctl/tests/files/pf1059.ok new file mode 100644 index 000000000000..6b028f18ee60 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1059.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> (vlan1059) round-robin diff --git a/sbin/pfctl/tests/files/pf1060.in b/sbin/pfctl/tests/files/pf1060.in new file mode 100644 index 000000000000..85cdd19f2897 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1060.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.0 (vlan1060) } diff --git a/sbin/pfctl/tests/files/pf1060.ok b/sbin/pfctl/tests/files/pf1060.ok new file mode 100644 index 000000000000..3364b3cbdcc5 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1060.ok @@ -0,0 +1 @@ +nat on vtnet0 inet proto tcp from 192.0.2.1 to 192.0.2.2 -> { 203.0.113.0, (vlan1060) } round-robin diff --git a/sbin/pfctl/tests/files/pf1061.in b/sbin/pfctl/tests/files/pf1061.in new file mode 100644 index 000000000000..32eb8272db8b --- /dev/null +++ b/sbin/pfctl/tests/files/pf1061.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 2001:db8::1 to 2001:db8::2 -> vlan1061:0 diff --git a/sbin/pfctl/tests/files/pf1061.ok b/sbin/pfctl/tests/files/pf1061.ok new file mode 100644 index 000000000000..d2e6d969cb11 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1061.ok @@ -0,0 +1 @@ +nat on vtnet0 inet6 proto tcp from 2001:db8::1 to 2001:db8::2 -> 2001:db8::cb00:7105 diff --git a/sbin/pfctl/tests/files/pf1062.in b/sbin/pfctl/tests/files/pf1062.in new file mode 100644 index 000000000000..4d6a0ecc2e92 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1062.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 2001:db8::1 to 2001:db8::2 -> { 2001:db8::3 vlan1062:0 } diff --git a/sbin/pfctl/tests/files/pf1062.ok b/sbin/pfctl/tests/files/pf1062.ok new file mode 100644 index 000000000000..cb5db62ded1d --- /dev/null +++ b/sbin/pfctl/tests/files/pf1062.ok @@ -0,0 +1 @@ +nat on vtnet0 inet6 proto tcp from 2001:db8::1 to 2001:db8::2 -> { 2001:db8::3, 2001:db8::cb00:7105 } round-robin diff --git a/sbin/pfctl/tests/files/pf1063.in b/sbin/pfctl/tests/files/pf1063.in new file mode 100644 index 000000000000..3d164538640d --- /dev/null +++ b/sbin/pfctl/tests/files/pf1063.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 2001:db8::1 to 2001:db8::2 -> (vlan1063) diff --git a/sbin/pfctl/tests/files/pf1063.ok b/sbin/pfctl/tests/files/pf1063.ok new file mode 100644 index 000000000000..13189e00cc8a --- /dev/null +++ b/sbin/pfctl/tests/files/pf1063.ok @@ -0,0 +1 @@ +nat on vtnet0 inet6 proto tcp from 2001:db8::1 to 2001:db8::2 -> (vlan1063) round-robin diff --git a/sbin/pfctl/tests/files/pf1064.in b/sbin/pfctl/tests/files/pf1064.in new file mode 100644 index 000000000000..78d04135154f --- /dev/null +++ b/sbin/pfctl/tests/files/pf1064.in @@ -0,0 +1 @@ +nat on vtnet0 proto tcp from 2001:db8::1 to 2001:db8::2 -> { fe80::2 (vlan1064) } diff --git a/sbin/pfctl/tests/files/pf1064.ok b/sbin/pfctl/tests/files/pf1064.ok new file mode 100644 index 000000000000..ed15d054ab34 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1064.ok @@ -0,0 +1 @@ +nat on vtnet0 inet6 proto tcp from 2001:db8::1 to 2001:db8::2 -> { fe80::2, (vlan1064) } round-robin diff --git a/sbin/pfctl/tests/files/pf1065.in b/sbin/pfctl/tests/files/pf1065.in new file mode 100644 index 000000000000..690045befee6 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1065.in @@ -0,0 +1 @@ +no nat on vtnet0 proto tcp from 2001:db8::1 to 2001:db8::2 diff --git a/sbin/pfctl/tests/files/pf1065.ok b/sbin/pfctl/tests/files/pf1065.ok new file mode 100644 index 000000000000..651a2fa0ae09 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1065.ok @@ -0,0 +1 @@ +no nat on vtnet0 inet6 proto tcp from 2001:db8::1 to 2001:db8::2 diff --git a/sbin/pfctl/tests/files/pf1066.in b/sbin/pfctl/tests/files/pf1066.in new file mode 100644 index 000000000000..e81461c470ab --- /dev/null +++ b/sbin/pfctl/tests/files/pf1066.in @@ -0,0 +1 @@ +no rdr on vtnet0 proto tcp from 2001:db8::1 to 2001:db8::2 diff --git a/sbin/pfctl/tests/files/pf1066.ok b/sbin/pfctl/tests/files/pf1066.ok new file mode 100644 index 000000000000..5ff596fa0158 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1066.ok @@ -0,0 +1 @@ +no rdr on vtnet0 inet6 proto tcp from 2001:db8::1 to 2001:db8::2 diff --git a/sbin/pfctl/tests/files/pf1067.fail b/sbin/pfctl/tests/files/pf1067.fail new file mode 100644 index 000000000000..23ac1daad64f --- /dev/null +++ b/sbin/pfctl/tests/files/pf1067.fail @@ -0,0 +1 @@ +route-to, reply-to and dup-to are not supported on block rules diff --git a/sbin/pfctl/tests/files/pf1067.in b/sbin/pfctl/tests/files/pf1067.in new file mode 100644 index 000000000000..47f3bf6285dd --- /dev/null +++ b/sbin/pfctl/tests/files/pf1067.in @@ -0,0 +1 @@ +block in route-to (if0 127.0.0.1/8) diff --git a/sbin/pfctl/tests/files/pf1068.in b/sbin/pfctl/tests/files/pf1068.in new file mode 100644 index 000000000000..993cfa37f8f9 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1068.in @@ -0,0 +1 @@ +pass in proto icmp max-pkt-rate 100/10 diff --git a/sbin/pfctl/tests/files/pf1068.ok b/sbin/pfctl/tests/files/pf1068.ok new file mode 100644 index 000000000000..bd36043207f9 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1068.ok @@ -0,0 +1 @@ +pass in proto icmp all max-pkt-rate 100/10 keep state diff --git a/sbin/pfctl/tests/files/pf1069.in b/sbin/pfctl/tests/files/pf1069.in new file mode 100644 index 000000000000..3a69158fff7e --- /dev/null +++ b/sbin/pfctl/tests/files/pf1069.in @@ -0,0 +1 @@ +pass in proto icmp max-pkt-size 128 diff --git a/sbin/pfctl/tests/files/pf1069.ok b/sbin/pfctl/tests/files/pf1069.ok new file mode 100644 index 000000000000..b79228266156 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1069.ok @@ -0,0 +1 @@ +pass in proto icmp all max-pkt-size 128 keep state diff --git a/sbin/pfctl/tests/files/pf1070.fail b/sbin/pfctl/tests/files/pf1070.fail new file mode 100644 index 000000000000..60b56d9da2b9 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1070.fail @@ -0,0 +1 @@ +pf1070.include:2: syntax error diff --git a/sbin/pfctl/tests/files/pf1070.in b/sbin/pfctl/tests/files/pf1070.in new file mode 100644 index 000000000000..42b874d4d6f4 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1070.in @@ -0,0 +1,2 @@ +pass in +include pf1070.include diff --git a/sbin/pfctl/tests/files/pf1070.include b/sbin/pfctl/tests/files/pf1070.include new file mode 100644 index 000000000000..09c3755dbe28 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1070.include @@ -0,0 +1,2 @@ +block out +invalidline diff --git a/sbin/pfctl/tests/files/pf1071.in b/sbin/pfctl/tests/files/pf1071.in new file mode 100644 index 000000000000..9e6c2abc0621 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1071.in @@ -0,0 +1 @@ +pass inet from (lo0)/24 diff --git a/sbin/pfctl/tests/files/pf1071.ok b/sbin/pfctl/tests/files/pf1071.ok new file mode 100644 index 000000000000..409b5dc4b068 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1071.ok @@ -0,0 +1 @@ +pass inet from (lo0)/24 to any 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/macro.sh b/sbin/pfctl/tests/macro.sh index 9c48dbbc69f0..071c6cb4f426 100755 --- a/sbin/pfctl/tests/macro.sh +++ b/sbin/pfctl/tests/macro.sh @@ -3,6 +3,7 @@ atf_test_case "space" cleanup space_head() { atf_set descr "Test macros with spaces" + atf_set require.kmods "pf" } space_body() diff --git a/sbin/pfctl/tests/pfctl_test.c b/sbin/pfctl/tests/pfctl_test.c index cc69ed4e002a..5f0aa7826bb4 100644 --- a/sbin/pfctl/tests/pfctl_test.c +++ b/sbin/pfctl/tests/pfctl_test.c @@ -65,24 +65,6 @@ * Copied from OpenBSD. */ -static bool -check_pf_module_available(void) -{ - int modid; - struct module_stat stat; - - if ((modid = modfind("pf")) < 0) { - warn("pf module not found"); - return false; - } - stat.version = sizeof(struct module_stat); - if (modstat(modid, &stat) < 0) { - warn("can't stat pf module id %d", modid); - return false; - } - return (true); -} - extern char **environ; static struct sbuf * @@ -119,27 +101,14 @@ read_file(const char *filename) } static void -run_pfctl_test(const char *input_path, const char *expected_path, - const atf_tc_t *tc) +run_command_pipe(const char *argv[], struct sbuf **output) { - int status; + posix_spawn_file_actions_t action; pid_t pid; int pipefds[2]; - char input_files_path[PATH_MAX]; - struct sbuf *expected_output; - struct sbuf *real_output; - posix_spawn_file_actions_t action; - - if (!check_pf_module_available()) - atf_tc_skip("pf(4) is not loaded"); - - /* The test inputs need to be able to use relative includes. */ - snprintf(input_files_path, sizeof(input_files_path), "%s/files", - atf_tc_get_config_var(tc, "srcdir")); - ATF_REQUIRE_ERRNO(0, chdir(input_files_path) == 0); + int status; ATF_REQUIRE_ERRNO(0, pipe(pipefds) == 0); - expected_output = read_file(expected_path); posix_spawn_file_actions_init(&action); posix_spawn_file_actions_addclose(&action, STDIN_FILENO); @@ -147,27 +116,115 @@ run_pfctl_test(const char *input_path, const char *expected_path, posix_spawn_file_actions_adddup2(&action, pipefds[0], STDOUT_FILENO); posix_spawn_file_actions_adddup2(&action, pipefds[0], STDERR_FILENO); - const char *argv[] = { "pfctl", "-o", "none", "-nvf", input_path, - NULL }; - printf("Running %s %s %s %s %s\n", argv[0], argv[1], argv[2], argv[3], - argv[4]); + printf("Running "); + for (int i=0; argv[i] != NULL; i++) + printf("%s ", argv[i]); + printf("\n"); + status = posix_spawnp( - &pid, "pfctl", &action, NULL, __DECONST(char **, argv), environ); + &pid, argv[0], &action, NULL, __DECONST(char **, argv), environ); ATF_REQUIRE_EQ_MSG( status, 0, "posix_spawn failed: %s", strerror(errno)); posix_spawn_file_actions_destroy(&action); close(pipefds[0]); - real_output = read_fd(pipefds[1], 0); - printf("---\n%s---\n", sbuf_data(real_output)); + (*output) = read_fd(pipefds[1], 0); + printf("---\n%s---\n", sbuf_data(*output)); ATF_REQUIRE_EQ(waitpid(pid, &status, 0), pid); ATF_REQUIRE_MSG(WIFEXITED(status), - "pfctl returned non-zero! Output:\n %s", sbuf_data(real_output)); + "%s returned non-zero! Output:\n %s", argv[0], sbuf_data(*output)); + close(pipefds[1]); +} + +static void +run_command(const char *argv[]) +{ + posix_spawn_file_actions_t action; + pid_t pid; + int status; + + posix_spawn_file_actions_init(&action); + posix_spawn_file_actions_addopen(&action, STDOUT_FILENO, "/dev/null", O_WRONLY, 0); + posix_spawn_file_actions_addopen(&action, STDERR_FILENO, "/dev/null", O_WRONLY, 0); + posix_spawn_file_actions_addopen(&action, STDIN_FILENO, "/dev/zero", O_RDONLY, 0); + + printf("Running "); + for (int i=0; argv[i] != NULL; i++) + printf("%s ", argv[i]); + printf("\n"); + + status = posix_spawnp( + &pid, argv[0], &action, NULL, __DECONST(char **, argv), environ); + posix_spawn_file_actions_destroy(&action); + waitpid(pid, &status, 0); +} + +static void +run_pfctl_test(const char *input_path, const char *output_path, + const atf_tc_t *tc, bool test_failure) +{ + char input_files_path[PATH_MAX]; + struct sbuf *expected_output; + struct sbuf *real_output; + + /* The test inputs need to be able to use relative includes. */ + snprintf(input_files_path, sizeof(input_files_path), "%s/files", + atf_tc_get_config_var(tc, "srcdir")); + ATF_REQUIRE_ERRNO(0, chdir(input_files_path) == 0); + expected_output = read_file(output_path); + + const char *argv[] = { "pfctl", "-o", "none", "-nvf", input_path, + NULL }; + run_command_pipe(argv, &real_output); + + if (test_failure) { + /* + * Error output contains additional strings like line number + * or "skipping rule due to errors", so use regexp to see + * if the expected error message is there somewhere. + */ + ATF_CHECK_MATCH(sbuf_data(expected_output), sbuf_data(real_output)); + sbuf_delete(expected_output); + } else { + ATF_CHECK_STREQ(sbuf_data(expected_output), sbuf_data(real_output)); + sbuf_delete(expected_output); + } - ATF_CHECK_STREQ(sbuf_data(expected_output), sbuf_data(real_output)); - sbuf_delete(expected_output); sbuf_delete(real_output); - close(pipefds[1]); +} + +static void +do_pf_test_iface_create(const char *number) +{ + struct sbuf *ifconfig_output; + char ifname[16] = {0}; + + snprintf(ifname, sizeof(ifname), "vlan%s", number); + const char *argv[] = { "ifconfig", ifname, "create", NULL}; + run_command_pipe(argv, &ifconfig_output); + sbuf_delete(ifconfig_output); + + const char *argv_inet[] = { "ifconfig", ifname, "inet", "203.0.113.5/30", NULL}; + run_command_pipe(argv_inet, &ifconfig_output); + sbuf_delete(ifconfig_output); + + const char *argv_inet6[] = { "ifconfig", ifname, "inet6", "2001:db8::203.0.113.5/126", NULL}; + run_command_pipe(argv_inet6, &ifconfig_output); + sbuf_delete(ifconfig_output); + + const char *argv_show[] = { "ifconfig", ifname, NULL}; + run_command_pipe(argv_show, &ifconfig_output); + sbuf_delete(ifconfig_output); +} + +static void +do_pf_test_iface_remove(const char *number) +{ + char ifname[16] = {0}; + + snprintf(ifname, sizeof(ifname), "vlan%s", number); + const char *argv[] = { "ifconfig", ifname, "destroy", NULL}; + run_command(argv); } static void @@ -179,7 +236,21 @@ do_pf_test(const char *number, const atf_tc_t *tc) atf_tc_get_config_var(tc, "srcdir"), number); asprintf(&expected_path, "%s/files/pf%s.ok", atf_tc_get_config_var(tc, "srcdir"), number); - run_pfctl_test(input_path, expected_path, tc); + run_pfctl_test(input_path, expected_path, tc, false); + free(input_path); + free(expected_path); +} + +static void +do_pf_test_fail(const char *number, const atf_tc_t *tc) +{ + char *input_path; + char *expected_path; + asprintf(&input_path, "%s/files/pf%s.in", + atf_tc_get_config_var(tc, "srcdir"), number); + asprintf(&expected_path, "%s/files/pf%s.fail", + atf_tc_get_config_var(tc, "srcdir"), number); + run_pfctl_test(input_path, expected_path, tc, true); free(input_path); free(expected_path); } @@ -190,15 +261,17 @@ do_selfpf_test(const char *number, const atf_tc_t *tc) char *expected_path; asprintf(&expected_path, "%s/files/pf%s.ok", atf_tc_get_config_var(tc, "srcdir"), number); - run_pfctl_test(expected_path, expected_path, tc); + run_pfctl_test(expected_path, expected_path, tc, false); free(expected_path); } +/* Standard tests perform the normal test and then the selfpf test */ #define PFCTL_TEST(number, descr) \ ATF_TC(pf##number); \ ATF_TC_HEAD(pf##number, tc) \ { \ atf_tc_set_md_var(tc, "descr", descr); \ + atf_tc_set_md_var(tc, "require.kmods", "pf"); \ } \ ATF_TC_BODY(pf##number, tc) \ { \ @@ -208,21 +281,61 @@ do_selfpf_test(const char *number, const atf_tc_t *tc) ATF_TC_HEAD(selfpf##number, tc) \ { \ atf_tc_set_md_var(tc, "descr", "Self " descr); \ + atf_tc_set_md_var(tc, "require.kmods", "pf"); \ } \ ATF_TC_BODY(selfpf##number, tc) \ { \ do_selfpf_test(#number, tc); \ } +/* Tests for failure perform only the normal test */ +#define PFCTL_TEST_FAIL(number, descr) \ + ATF_TC(pf##number); \ + ATF_TC_HEAD(pf##number, tc) \ + { \ + atf_tc_set_md_var(tc, "descr", descr); \ + atf_tc_set_md_var(tc, "require.kmods", "pf"); \ + } \ + ATF_TC_BODY(pf##number, tc) \ + { \ + do_pf_test_fail(#number, tc); \ + } +/* Tests with interface perform only the normal test */ +#define PFCTL_TEST_IFACE(number, descr) \ + ATF_TC_WITH_CLEANUP(pf##number); \ + ATF_TC_HEAD(pf##number, tc) \ + { \ + atf_tc_set_md_var(tc, "descr", descr); \ + atf_tc_set_md_var(tc, "execenv", "jail"); \ + atf_tc_set_md_var(tc, "execenv.jail.params", "vnet"); \ + atf_tc_set_md_var(tc, "require.kmods", "pf"); \ + } \ + ATF_TC_BODY(pf##number, tc) \ + { \ + do_pf_test_iface_create(#number); \ + do_pf_test(#number, tc); \ + } \ + ATF_TC_CLEANUP(pf##number, tc) \ + { \ + do_pf_test_iface_remove(#number); \ + } #include "pfctl_test_list.inc" #undef PFCTL_TEST +#undef PFCTL_TEST_FAIL +#undef PFCTL_TEST_IFACE ATF_TP_ADD_TCS(tp) { #define PFCTL_TEST(number, descr) \ ATF_TP_ADD_TC(tp, pf##number); \ ATF_TP_ADD_TC(tp, selfpf##number); +#define PFCTL_TEST_FAIL(number, descr) \ + ATF_TP_ADD_TC(tp, pf##number); +#define PFCTL_TEST_IFACE(number, descr) \ + ATF_TP_ADD_TC(tp, pf##number); #include "pfctl_test_list.inc" #undef PFCTL_TEST +#undef PFCTL_TEST_FAIL +#undef PFCTL_TEST_IFACE return atf_no_error(); } diff --git a/sbin/pfctl/tests/pfctl_test_list.inc b/sbin/pfctl/tests/pfctl_test_list.inc index 7dd3a2af0eea..9dd4a590ad8f 100644 --- a/sbin/pfctl/tests/pfctl_test_list.inc +++ b/sbin/pfctl/tests/pfctl_test_list.inc @@ -136,3 +136,51 @@ PFCTL_TEST(1024, "nat64") PFCTL_TEST(1025, "nat64 with implicit address family") PFCTL_TEST(1026, "nat64 with route-to") PFCTL_TEST(1027, "nat64 with reply-to") +PFCTL_TEST(1028, "RDR pool: For RDR rules no port specified means keep port") +PFCTL_TEST(1029, "RDR pool: A single port is shown") +PFCTL_TEST(1030, "RDR pool: The default values are shown for RDR rules") +PFCTL_TEST(1031, "RDR pool: Multiple ports redirected to a single port") +PFCTL_TEST(1032, "RDR pool: Multiple ports redirected to a port range") +PFCTL_TEST_FAIL(1033, "RDR pool: static-port can't be used with RDR rules") +PFCTL_TEST_FAIL(1034, "RDR pool: MAP-E port can't be used with RDR rules") +PFCTL_TEST(1035, "NAT pool: For NAT rules no port specified means default values") +PFCTL_TEST(1036, "NAT pool: Default port numbers are not shown, even if explicitly applied") +PFCTL_TEST(1037, "NAT pool: Single port") +PFCTL_TEST(1038, "NAT pool: Two ports") +PFCTL_TEST(1039, "NAT pool: Static port") +PFCTL_TEST_FAIL(1040, "NAT pool: Static port can't be used with port numbers") +PFCTL_TEST(1041, "NAT pool: MAP-E is displayed using decimal system") +PFCTL_TEST_FAIL(1042, "NAT pool: MAP-E port can't be used with static port") +PFCTL_TEST_FAIL(1043, "NAT pool: MAP-E port can't be used with port numbers") +PFCTL_TEST(1044, "pool: sticky-address is applied on top of round-robin") +PFCTL_TEST(1045, "pool: bitmask is allowed for prefixes") +PFCTL_TEST_FAIL(1046, "pool: bitmask is not allowed for tables") +PFCTL_TEST_FAIL(1047, "pool: bitmask is not allowed for interfaces in brackets") +PFCTL_TEST(1048, "pool: random is allowed for prefixes") +PFCTL_TEST(1049, "pool: round-robin is not set for a single host, even if it looks like a table") +PFCTL_TEST(1050, "pool: round-robin is set automatically for tables") +PFCTL_TEST(1051, "pool: round-robin is set automatically for multiple targets") +PFCTL_TEST(1052, "pool: hosts and table are allowed, round-robin is automatically set") +PFCTL_TEST(1053, "pool: round-robin is not set automatically for prefixes") +PFCTL_TEST(1054, "pool: round-robin is allowed for prefixes") +PFCTL_TEST(1055, "pool: source hash") +PFCTL_TEST(1056, "af-to: from and to") +PFCTL_TEST_IFACE(1057, "Interface translation: IPv4 rule, interface without brackets is translated") +PFCTL_TEST_IFACE(1058, "Interface translation: IPv4 rule, interface without brackets is translated, extra host, round-robin is applied") +PFCTL_TEST_IFACE(1059, "Interface translation: IPv4 rule, interface with brackets is not translated, round-robin is applied") +PFCTL_TEST_IFACE(1060, "Interface translation: IPv4 rule, interface with brackets is not translated, extra host, round-robin is applied") +PFCTL_TEST_IFACE(1061, "Interface translation: IPv6 rule, interface without brackets is translated") +PFCTL_TEST_IFACE(1062, "Interface translation: IPv6 rule, interface without brackets is translated, extra host, round-robin is applied") +PFCTL_TEST_IFACE(1063, "Interface translation: IPv6 rule, interface with brackets is not translated, round-robin is applied") +PFCTL_TEST_IFACE(1064, "Interface translation: IPv6 rule, interface with brackets is not translated, extra host, round robin is applied") +PFCTL_TEST(1065, "no nat") +PFCTL_TEST(1066, "no rdr") +PFCTL_TEST_FAIL(1067, "route-to can't be used on block rules") +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/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/boot_i386.8 b/sbin/reboot/boot_i386.8 index 5e97a7594684..26dd588fb437 100644 --- a/sbin/reboot/boot_i386.8 +++ b/sbin/reboot/boot_i386.8 @@ -32,7 +32,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd July 11, 2020 +.Dd April 7, 2025 .Dt BOOT 8 i386 .Os .Sh NAME @@ -45,14 +45,14 @@ An automatic consistency check of the file systems will be performed, and unless this fails, the system will resume multi-user operations. .Pp .Sy Cold starts . -Most i386 PCs attempt to boot first from floppy disk drive 0 (sometimes +Legacy i386 PCs attempt to boot first from floppy disk drive 0 (sometimes known as drive A:) and, failing that, from hard disk drive 0 (sometimes known as drive C:, or as drive 0x80 to the BIOS). Some BIOSes allow you to change this default sequence, and may also include a CD-ROM drive as a boot device. .Pp -Some newer PCs boot using UEFI firmware, not BIOS. +Contemporary PCs boot using UEFI firmware, not BIOS. That process is described in .Xr uefi 8 . diff --git a/sbin/reboot/reboot.8 b/sbin/reboot/reboot.8 index 59cbd9de7519..1bbc39d52be4 100644 --- a/sbin/reboot/reboot.8 +++ b/sbin/reboot/reboot.8 @@ -25,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd February 8, 2024 +.Dd April 12, 2025 .Dt REBOOT 8 .Os .Sh NAME @@ -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 @@ -213,6 +222,7 @@ reboot -r .Ed .Sh SEE ALSO .Xr kenv 1 , +.Xr reboot 2 , .Xr getutxent 3 , .Xr ipmi 4 , .Xr boot 8 , 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, §orsize); + 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, §orsize); - 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/routed/routed.8 b/sbin/routed/routed.8 index 8cf12d7b60e1..334c828b943e 100644 --- a/sbin/routed/routed.8 +++ b/sbin/routed/routed.8 @@ -27,13 +27,20 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd June 27, 2022 +.Dd May 20, 2025 .Dt ROUTED 8 .Os .Sh NAME .Nm routed , .Nm rdisc .Nd network RIP and router discovery routing daemon +.Sh DEPRECATION NOTICE +The +.Nm routed +and +.Nm rdisc +utilities are deprecated and will be removed in +.Fx 16.0 . .Sh SYNOPSIS .Nm .Op Fl isqdghmpAtv diff --git a/sbin/routed/rtquery/rtquery.8 b/sbin/routed/rtquery/rtquery.8 index de5e1fc7cf96..ff46a3414dcf 100644 --- a/sbin/routed/rtquery/rtquery.8 +++ b/sbin/routed/rtquery/rtquery.8 @@ -1,11 +1,16 @@ .\" $Revision: 2.27 $ .\" -.Dd June 1, 1996 +.Dd May 20, 2025 .Dt RTQUERY 8 .Os .Sh NAME .Nm rtquery .Nd query routing daemons for their routing tables +.Sh DEPRECATION NOTICE +The +.Nm +utility is deprecated and will be removed in +.Fx 16.0 . .Sh SYNOPSIS .Nm .Op Fl np1 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/savecore/tests/Makefile b/sbin/savecore/tests/Makefile index 81e6b8ce2667..7ec3c9aeedcc 100644 --- a/sbin/savecore/tests/Makefile +++ b/sbin/savecore/tests/Makefile @@ -1,3 +1,6 @@ ATF_TESTS_SH= livedump_test log_test +# The test fails if any kernel modules are loaded while the test is running. +TEST_METADATA.livedump_test+= is_exclusive=true + .include <bsd.test.mk> diff --git a/sbin/shutdown/shutdown.8 b/sbin/shutdown/shutdown.8 index ab90af6244e9..ed44ac36aef6 100644 --- a/sbin/shutdown/shutdown.8 +++ b/sbin/shutdown/shutdown.8 @@ -36,7 +36,7 @@ .Nm .Op Fl .Oo -.Fl c | Fl h | Fl p | +.Fl c | Fl f | Fl h | Fl p | .Fl r | Fl k .Oc .Oo @@ -71,6 +71,12 @@ At the present time, only systems with BMC supported by the driver that implement this functionality support this flag. The amount of time the system is off is dependent on the device that implements this feature. +.It Fl f +The +.Nm +command ignores the presence of the +.Pa /var/run/noshutdown +file. .It Fl h The system is halted at the specified .Ar time . @@ -206,6 +212,12 @@ file that .Nm created will be removed automatically. .Pp +If the +.Pa /var/run/noshutdown +file is present, +.Nm +exits without executing any action on the system. +.Pp When run without options, the .Nm utility will place the system into single user mode at the @@ -219,11 +231,18 @@ is equivalent to running: shutdown -p now .Ed .Sh FILES -.Bl -tag -width /var/run/nologin -compact +.Bl -tag -width /var/run/noshutdown -compact .It Pa /var/run/nologin tells .Xr login 1 not to let anyone log in +.It Pa /var/run/noshutdown +prevents +.Nm +from initiating an action on the system. +Can be overridden with the +.Fl f +option. .El .Sh EXAMPLES Reboot the system in 30 minutes and display a warning message on the terminals diff --git a/sbin/shutdown/shutdown.c b/sbin/shutdown/shutdown.c index e92d41220a20..762b23ab6bd9 100644 --- a/sbin/shutdown/shutdown.c +++ b/sbin/shutdown/shutdown.c @@ -32,6 +32,7 @@ #include <sys/param.h> #include <sys/boottrace.h> #include <sys/resource.h> +#include <sys/stat.h> #include <sys/syslog.h> #include <sys/time.h> @@ -79,7 +80,8 @@ static struct interval { #undef S static time_t offset, shuttime; -static int docycle, dohalt, dopower, doreboot, killflg, mbuflen, oflag; +static int docycle, dohalt, dopower, doreboot, ign_noshutdown, + killflg, mbuflen, oflag; static char mbuf[BUFSIZ]; static const char *nosync, *whom; @@ -100,6 +102,7 @@ main(int argc, char **argv) { char *p, *endp; struct passwd *pw; + struct stat st; int arglen, ch, len, readstdin; bool dowarn; @@ -133,7 +136,7 @@ main(int argc, char **argv) goto poweroff; } - while ((ch = getopt(argc, argv, "-chknopqr")) != -1) + while ((ch = getopt(argc, argv, "-cfhknopqr")) != -1) switch (ch) { case '-': readstdin = 1; @@ -141,6 +144,9 @@ main(int argc, char **argv) case 'c': docycle = 1; break; + case 'f': + ign_noshutdown = 1; + break; case 'h': dohalt = 1; break; @@ -216,6 +222,12 @@ poweroff: } mbuflen = strlen(mbuf); + if (!ign_noshutdown && stat(_PATH_NOSHUTDOWN, &st) == 0) { + (void)printf("Shutdown cannot be done, " _PATH_NOSHUTDOWN + " is present\n"); + exit(2); + } + if (offset) { BOOTTRACE("Shutdown at %s", ctime(&shuttime)); (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); @@ -593,7 +605,7 @@ usage(const char *cp) if (cp != NULL) warnx("%s", cp); (void)fprintf(stderr, - "usage: shutdown [-] [-c | -h | -p | -r | -k] [-o [-n]] [-q] time [warning-message ...]\n" + "usage: shutdown [-] [-c | -f | -h | -p | -r | -k] [-o [-n]] [-q] time [warning-message ...]\n" " poweroff\n"); exit(1); } diff --git a/sbin/swapon/Makefile b/sbin/swapon/Makefile index ef87c9668dd7..0c034338a533 100644 --- a/sbin/swapon/Makefile +++ b/sbin/swapon/Makefile @@ -8,4 +8,9 @@ MLINKS+=swapon.8 swapctl.8 LIBADD= util +.include <src.opts.mk> + +HAS_TESTS= +SUBDIR.${MK_TESTS}+= tests + .include <bsd.prog.mk> diff --git a/sbin/swapon/swapon.c b/sbin/swapon/swapon.c index a09f2598e2ac..3dff4df5e63f 100644 --- a/sbin/swapon/swapon.c +++ b/sbin/swapon/swapon.c @@ -54,6 +54,8 @@ #include <string.h> #include <unistd.h> +#define DOT_ELI ".eli" + static void usage(void) __dead2; static const char *swap_on_off(const char *, int, char *); static const char *swap_on_off_geli(const char *, char *, int); @@ -230,15 +232,18 @@ swap_on_off(const char *name, int doingall, char *mntops) (fnmatch(_PATH_DEV MD_NAME "[0-9]*", name, 0) == 0 || fnmatch(MD_NAME "[0-9]*", name, 0) == 0 || strncmp(_PATH_DEV MD_NAME, name, - sizeof(_PATH_DEV) + sizeof(MD_NAME)) == 0 || - strncmp(MD_NAME, name, sizeof(MD_NAME)) == 0)) + sizeof(_PATH_DEV MD_NAME)) == 0 || + strncmp(MD_NAME, name, sizeof(MD_NAME)) == 0 || + strncmp(_PATH_DEV MD_NAME DOT_ELI, name, + sizeof(_PATH_DEV MD_NAME DOT_ELI)) == 0 || + strncmp(MD_NAME DOT_ELI, name, sizeof(MD_NAME DOT_ELI)) == 0)) return (swap_on_off_md(name, mntops, doingall)); basebuf = strdup(name); base = basename(basebuf); /* Swap on encrypted device by GEOM_ELI. */ - if (fnmatch("*.eli", base, 0) == 0) { + if (fnmatch("*" DOT_ELI, base, 0) == 0) { free(basebuf); return (swap_on_off_geli(name, mntops, doingall)); } @@ -327,6 +332,8 @@ swap_on_geli_args(const char *mntops) return (NULL); } Tflag = " -T "; + } else if ((p = strstr(token, "file=")) == token) { + /* ignore known option */ } else if (strcmp(token, "late") == 0) { /* ignore known option */ } else if (strcmp(token, "noauto") == 0) { @@ -416,24 +423,38 @@ swap_on_off_md(const char *name, char *mntops, int doingall) char *p, *vnodefile; size_t linelen; u_long ul; + const char *suffix; + char *devbuf, *dname; + int name_len; fd = -1; sfd = NULL; - if (strlen(name) == (sizeof(MD_NAME) - 1)) + devbuf = strdup(name); + name_len = strlen(name) - strlen(DOT_ELI); + if (name_len > 0 && strcmp(suffix = &name[name_len], DOT_ELI) == 0) { + suffix++; + devbuf[name_len] = '\0'; + } else + suffix = NULL; + /* dname will be name without /dev/ prefix and .eli suffix */ + dname = basename(devbuf); + if (strlen(dname) == (sizeof(MD_NAME) - 1)) mdunit = -1; else { errno = 0; - ul = strtoul(name + 2, &p, 10); + ul = strtoul(dname + 2, &p, 10); if (errno == 0) { if (*p != '\0' || ul > INT_MAX) errno = EINVAL; } if (errno) { - warn("Bad device unit: %s", name); + warn("Bad device unit: %s", dname); + free(devbuf); return (NULL); } mdunit = (int)ul; } + free(devbuf); vnodefile = NULL; if ((p = strstr(mntops, "file=")) != NULL) { @@ -573,10 +594,19 @@ swap_on_off_md(const char *name, char *mntops, int doingall) } } } - snprintf(mdpath, sizeof(mdpath), "%s%s%d", _PATH_DEV, - MD_NAME, mdunit); - mdpath[sizeof(mdpath) - 1] = '\0'; - ret = swap_on_off_sfile(mdpath, doingall); + + if (suffix != NULL && strcmp("eli", suffix) == 0) { + /* Swap on encrypted device by GEOM_ELI. */ + snprintf(mdpath, sizeof(mdpath), "%s%s%d" DOT_ELI, _PATH_DEV, + MD_NAME, mdunit); + mdpath[sizeof(mdpath) - 1] = '\0'; + ret = swap_on_off_geli(mdpath, mntops, doingall); + } else { + snprintf(mdpath, sizeof(mdpath), "%s%s%d", _PATH_DEV, + MD_NAME, mdunit); + mdpath[sizeof(mdpath) - 1] = '\0'; + ret = swap_on_off_sfile(mdpath, doingall); + } if (which_prog == SWAPOFF) { if (ret != NULL) { diff --git a/sbin/swapon/tests/Makefile b/sbin/swapon/tests/Makefile new file mode 100644 index 000000000000..aa0c9cf202d6 --- /dev/null +++ b/sbin/swapon/tests/Makefile @@ -0,0 +1,5 @@ +ATF_TESTS_SH= swapon_test + +TEST_METADATA.swapon_test+= required_user="root" + +.include <bsd.test.mk> diff --git a/sbin/swapon/tests/swapon_test.sh b/sbin/swapon/tests/swapon_test.sh new file mode 100755 index 000000000000..a04bb36cc49e --- /dev/null +++ b/sbin/swapon/tests/swapon_test.sh @@ -0,0 +1,225 @@ +# Copyright (c) 2025 Ronald Klop <ronald@FreeBSD.org> +# +# 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. +# +# + +atf_test_case attach_mdX cleanup +attach_mdX_head() +{ + atf_set "descr" "mdX device should attach" +} +attach_mdX_body() +{ + # if the swapfile is too small (like 1k) then mdconfig hangs looking up the md + # 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" +} +attach_mdX_cleanup() +{ + swapoff -F fstab.out -a +} + +### +atf_test_case attach_dev_mdX cleanup +attach_dev_mdX_head() +{ + atf_set "descr" "/dev/mdX device should attach" +} +attach_dev_mdX_body() +{ + # if the swapfile is too small (like 1k) then mdconfig hangs looking up the md + # 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" +} +attach_dev_mdX_cleanup() +{ + swapoff -F fstab.out -a +} + +### +atf_test_case attach_md cleanup +attach_md_head() +{ + atf_set "descr" "md device should attach" +} +attach_md_body() +{ + # if the swapfile is too small (like 1k) then mdconfig hangs looking up the md + # 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" +} +attach_md_cleanup() +{ + swapoff -F fstab.out -a +} + +### +atf_test_case attach_dev_md cleanup +attach_dev_md_head() +{ + atf_set "descr" "/dev/md device should attach" +} +attach_dev_md_body() +{ + # if the swapfile is too small (like 1k) then mdconfig hangs looking up the md + # 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" +} +attach_dev_md_cleanup() +{ + swapoff -F fstab.out -a +} + +### +atf_test_case attach_mdX_eli cleanup +attach_mdX_eli_head() +{ + atf_set "descr" "mdX.eli device should attach" +} +attach_mdX_eli_body() +{ + # if the swapfile is too small (like 1k) then mdconfig hangs looking up the md + # 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" +} +attach_mdX_eli_cleanup() +{ + swapoff -F fstab.out -a +} + +### +atf_test_case attach_dev_mdX_eli cleanup +attach_dev_mdX_eli_head() +{ + atf_set "descr" "/dev/mdX.eli device should attach" +} +attach_dev_mdX_eli_body() +{ + # if the swapfile is too small (like 1k) then mdconfig hangs looking up the md + # 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" +} +attach_dev_mdX_eli_cleanup() +{ + swapoff -F fstab.out -a +} + +### +atf_test_case attach_md_eli cleanup +attach_md_eli_head() +{ + atf_set "descr" "md.eli device should attach" +} +attach_md_eli_body() +{ + # if the swapfile is too small (like 1k) then mdconfig hangs looking up the md + # 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" +} +attach_md_eli_cleanup() +{ + swapoff -F fstab.out -a +} + +### +atf_test_case attach_dev_md_eli cleanup +attach_dev_md_eli_head() +{ + atf_set "descr" "/dev/md.eli device should attach" +} +attach_dev_md_eli_body() +{ + # if the swapfile is too small (like 1k) then mdconfig hangs looking up the md + # 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" +} +attach_dev_md_eli_cleanup() +{ + swapoff -F fstab.out -a +} + +### + +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 + atf_add_test_case attach_dev_mdX + atf_add_test_case attach_md + atf_add_test_case attach_dev_md + + atf_add_test_case attach_mdX_eli + 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/sysctl/tests/sysctl_test.sh b/sbin/sysctl/tests/sysctl_test.sh index dfc32a87b212..b4cc7180a0f9 100644 --- a/sbin/sysctl/tests/sysctl_test.sh +++ b/sbin/sysctl/tests/sysctl_test.sh @@ -41,6 +41,7 @@ sysctl_aflag_body() # output and it would all otherwise be saved. sysctl -ao >/dev/null 2>stderr if [ $? -ne 0 ]; then + cat stderr atf_fail "sysctl -ao failed" elif [ -s stderr ]; then cat stderr diff --git a/sbin/tunefs/Makefile b/sbin/tunefs/Makefile index e236525557e1..4ea2219c8e03 100644 --- a/sbin/tunefs/Makefile +++ b/sbin/tunefs/Makefile @@ -1,13 +1,8 @@ PACKAGE=ufs PROG= tunefs -SRCS= tunefs.c getmntopts.c -LIBADD= ufs +LIBADD= ufs util MAN= tunefs.8 -MOUNT= ${SRCTOP}/sbin/mount -CFLAGS+= -I${MOUNT} -.PATH: ${MOUNT} - WARNS?= 3 .include <bsd.prog.mk> diff --git a/sbin/veriexec/veriexec.8 b/sbin/veriexec/veriexec.8 index 3c85957357f5..8352dd8e5e49 100644 --- a/sbin/veriexec/veriexec.8 +++ b/sbin/veriexec/veriexec.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 August 1, 2024 +.Dd April 19, 2025 .Dt VERIEXEC 8 .Os .Sh NAME @@ -109,7 +109,7 @@ state. .It Ar enforce .Xr mac_veriexec 4 will fail attempts to -.Xr exec 2 +.Xr execve 2 or .Xr open 2 files with @@ -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 |