diff options
Diffstat (limited to 'sbin')
731 files changed, 15916 insertions, 14638 deletions
diff --git a/sbin/Makefile b/sbin/Makefile index c125075c6696..ef953e80ba1e 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.5 (Berkeley) 3/31/94 - .include <src.opts.mk> SUBDIR=adjkerntz \ @@ -23,11 +21,9 @@ SUBDIR=adjkerntz \ fsck_msdosfs \ fsdb \ fsirand \ - gbde \ geom \ ggate \ growfs \ - gvinum \ ifconfig \ init \ kldconfig \ @@ -52,6 +48,7 @@ SUBDIR=adjkerntz \ newfs_msdos \ nfsiod \ nos-tun \ + nvmecontrol \ pfilctl \ rcorder \ reboot \ @@ -65,6 +62,7 @@ SUBDIR=adjkerntz \ swapon \ sysctl \ tunefs \ + umbctl \ umount .if ${MK_INET} != "no" || ${MK_INET6} != "no" @@ -78,10 +76,10 @@ SUBDIR.${MK_INET6}+= rtsol SUBDIR.${MK_IPFILTER}+= ipf SUBDIR.${MK_IPFW}+= ipfw SUBDIR.${MK_IPFW}+= natd -SUBDIR.${MK_NVME}+= nvmecontrol SUBDIR.${MK_OPENSSL}+= decryptcore SUBDIR.${MK_PF}+= pfctl SUBDIR.${MK_PF}+= pflogd +SUBDIR.${MK_PF}+= pflowctl SUBDIR.${MK_QUOTAS}+= quotacheck SUBDIR.${MK_ROUTED}+= routed SUBDIR.${MK_VERIEXEC}+= veriexec diff --git a/sbin/Makefile.amd64 b/sbin/Makefile.amd64 index 4904b2ebfe87..a7cb39976324 100644 --- a/sbin/Makefile.amd64 +++ b/sbin/Makefile.amd64 @@ -1,3 +1,2 @@ - SUBDIR += bsdlabel SUBDIR += fdisk diff --git a/sbin/Makefile.arm b/sbin/Makefile.arm index 4904b2ebfe87..a7cb39976324 100644 --- a/sbin/Makefile.arm +++ b/sbin/Makefile.arm @@ -1,3 +1,2 @@ - SUBDIR += bsdlabel SUBDIR += fdisk diff --git a/sbin/Makefile.i386 b/sbin/Makefile.i386 index 4904b2ebfe87..a7cb39976324 100644 --- a/sbin/Makefile.i386 +++ b/sbin/Makefile.i386 @@ -1,3 +1,2 @@ - SUBDIR += bsdlabel SUBDIR += fdisk diff --git a/sbin/Makefile.inc b/sbin/Makefile.inc index d59f70fe1ef6..7034747d22e7 100644 --- a/sbin/Makefile.inc +++ b/sbin/Makefile.inc @@ -1,5 +1,3 @@ -# @(#)Makefile.inc 8.1 (Berkeley) 6/8/93 - .include <src.opts.mk> BINDIR?= /sbin diff --git a/sbin/Makefile.powerpc64 b/sbin/Makefile.powerpc64 index 4904b2ebfe87..a7cb39976324 100644 --- a/sbin/Makefile.powerpc64 +++ b/sbin/Makefile.powerpc64 @@ -1,3 +1,2 @@ - SUBDIR += bsdlabel SUBDIR += fdisk diff --git a/sbin/adjkerntz/Makefile b/sbin/adjkerntz/Makefile index 1fa9b74b90bc..54980c3a57fb 100644 --- a/sbin/adjkerntz/Makefile +++ b/sbin/adjkerntz/Makefile @@ -1,4 +1,3 @@ - PACKAGE=runtime PROG= adjkerntz MAN= adjkerntz.8 diff --git a/sbin/adjkerntz/adjkerntz.8 b/sbin/adjkerntz/adjkerntz.8 index 787282348a91..b87981fd19f4 100644 --- a/sbin/adjkerntz/adjkerntz.8 +++ b/sbin/adjkerntz/adjkerntz.8 @@ -1,4 +1,7 @@ -.\" Copyright (C) 1993-1998 by Andrey A. Chernov, Moscow, Russia. +.\"- +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.\" Copyright (c) 1993-1998 by Andrey A. Chernov, Moscow, Russia. .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -22,12 +25,12 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd March 8, 2012 +.Dd July 29, 2024 .Dt ADJKERNTZ 8 .Os .Sh NAME .Nm adjkerntz -.Nd adjust the local time CMOS clock to reflect time zone changes and keep the current timezone offset for the kernel +.Nd adjust real-time clock (rtc) and kernel timezone offset .Sh SYNOPSIS .Nm .Fl i @@ -37,7 +40,7 @@ The .Nm utility maintains the proper relationship between the kernel clock, which -is always set to UTC and the CMOS clock, which may be set to local time. +is always set to UTC and the real-time clock, which may be set to local time. The .Nm utility also informs the kernel about machine timezone shifts in order to @@ -51,13 +54,13 @@ installed on the same system rather than fixing broken MS-DOS file timestamps. If the file .Pa /etc/wall_cmos_clock -exists, it means that the CMOS clock keeps local time (MS-DOS and MS-Windows -compatible mode). -If that file does not exist, it means that the CMOS clock keeps UTC time. +exists, it means that the real-time clock keeps local time +.Pq MS-DOS and MS-Windows compatible mode . +If that file does not exist, it means that the real-time clock keeps UTC time. The .Nm utility passes this state to the -.Pa machdep.wall_cmos_clock +.Va machdep.wall_cmos_clock kernel variable. .Pp Adjustments may be needed at system startup and shutdown, and @@ -65,7 +68,7 @@ whenever a time zone change occurs. To handle these different situations, .Nm is invoked in two ways: -.Bl -tag -width 4n +.Bl -tag -width "-a [-s]" .It Fl i This form handles system startups and shutdowns. The @@ -76,51 +79,51 @@ on entry to multi-user mode, before any other daemons have been started. The .Nm utility puts itself into the background. -Then, for a local time CMOS clock, +Then, for a local time real-time clock, .Nm reads the local time from it and sets the kernel clock to the corresponding UTC time. The .Nm utility also stores the local time zone offset in the -.Pa machdep.adjkerntz +.Va machdep.adjkerntz kernel variable, for use by subsequent invocations of -.Em "'adjkerntz -a'" +.Ql adjkerntz -a and by local time file systems. .Pp -For a local time CMOS clock -.Em "'adjkerntz -i'" +For a local time real-time clock +.Ql adjkerntz -i pauses and remains inactive as a background daemon until it receives a SIGTERM. The SIGTERM will normally be sent by .Xr init 8 -when the system leaves multi-user mode (usually, because the system -is being shut down). +when the system leaves multi-user mode +.Pq usually, because the system is being shut down . After receiving the SIGTERM, .Nm -reads the UTC kernel clock and updates the CMOS clock, if necessary, +reads the UTC kernel clock and updates the real-time clock, if necessary, to ensure that it reflects the current local time zone. Then .Nm exits. .It Fl a Op Fl s -This form is used to update the local time CMOS clock and kernel -.Pa machdep.adjkerntz -variable when time zone changes occur, +This form is used to update the local time real-time clock and +.Va machdep.adjkerntz +kernel variable when time zone changes occur, e.g., when entering or leaving daylight savings time. The .Nm utility uses the kernel clock's UTC time, -the previously stored -time zone offset, and the changed time zone rule to -calculate a new time zone offset. +the previously stored time zone offset, +and the changed time zone rule to calculate a new time zone offset. It stores the new offset into the -.Pa machdep.adjkerntz -kernel variable and updates the wall CMOS clock to the new local time. +.Va machdep.adjkerntz +kernel variable and updates the wall real-time clock to the new local time. If -.Em "'adjkerntz -a'" -was started at a nonexistent time (during a timezone change), it exits -with a warning diagnostic unless the +.Ql adjkerntz -a +was started at a nonexistent time +.Pq during a timezone change , +it exits with a warning diagnostic unless the .Fl s option was used, in which case .Nm @@ -128,14 +131,14 @@ sleeps 30 minutes and tries again. .Pp This form should be invoked from root's .Xr crontab 5 -every half hour between midnight and 5am, when most modern time -zone changes occur. +every half hour between midnight and 5am, +when most modern time zone changes occur. Warning: do not use the .Fl s option in a .Xr crontab 5 command line, or multiple -.Em "'adjkerntz -a'" +.Ql adjkerntz -a instances could conflict with each other. .El .Pp @@ -145,7 +148,7 @@ utility clears the kernel timezone structure and makes the kernel clock run in the UTC time zone. Super-user privileges are required for all operations. .Sh ENVIRONMENT -.Bl -tag -width Fl +.Bl -tag -width /etc/wall_cmos_clock .It Ev TZ Time zone change rule, see .Xr tzset 3 ; @@ -164,8 +167,8 @@ and .Xr zic 8 . .It Pa /etc/wall_cmos_clock Empty file. -Its presence indicates that the machine's CMOS clock is set to local -time, while its absence indicates a UTC CMOS clock. +Its presence indicates that the machine's real-time clock is set to local +time, while its absence indicates a UTC real-time clock. .El .Sh DIAGNOSTICS No diagnostics. @@ -178,6 +181,7 @@ and exits with a nonzero return code. .Xr tzset 3 , .Xr crontab 5 , .Xr mount_msdosfs 8 , +.Xr ntpd 8 , .Xr rc 8 , .Xr sysctl 8 , .Xr tzsetup 8 , diff --git a/sbin/adjkerntz/adjkerntz.c b/sbin/adjkerntz/adjkerntz.c index 7ed46f9c4d85..b231e1647b33 100644 --- a/sbin/adjkerntz/adjkerntz.c +++ b/sbin/adjkerntz/adjkerntz.c @@ -26,14 +26,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#)Copyright (C) 1993-1996 by Andrey A. Chernov, Moscow, Russia.\n\ - All rights reserved.\n"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> /* * Andrey A. Chernov <ache@astral.msk.su> Dec 20 1993 * diff --git a/sbin/bectl/Makefile b/sbin/bectl/Makefile index d640de33bf72..c11426c57e45 100644 --- a/sbin/bectl/Makefile +++ b/sbin/bectl/Makefile @@ -1,4 +1,3 @@ - .include <src.opts.mk> PACKAGE=zfs @@ -22,7 +21,7 @@ CFLAGS+= -I${SRCTOP}/sys CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/module/icp/include CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h -CFLAGS+= -DHAVE_ISSETUGID +CFLAGS+= -DHAVE_ISSETUGID -DHAVE_STRLCAT -DHAVE_STRLCPY CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h HAS_TESTS= yes diff --git a/sbin/bectl/Makefile.depend b/sbin/bectl/Makefile.depend index f04ca630e059..bcc4f32f88da 100644 --- a/sbin/bectl/Makefile.depend +++ b/sbin/bectl/Makefile.depend @@ -1,28 +1,17 @@ # Autogenerated - do NOT edit! DIRDEPS = \ - cddl/lib/libavl \ cddl/lib/libnvpair \ cddl/lib/libspl \ - cddl/lib/libumem \ - cddl/lib/libuutil \ - cddl/lib/libzfs \ - cddl/lib/libzfs_core \ + cddl/lib/libzfsbootenv \ include \ include/xlocale \ lib/${CSU_DIR} \ lib/libbe \ lib/libc \ lib/libcompiler_rt \ - lib/libexpat \ - lib/libgeom \ lib/libjail \ - lib/libmd \ - lib/libsbuf \ - lib/libthr \ lib/libutil \ - lib/libz \ - lib/msun \ .include <dirdeps.mk> diff --git a/sbin/bectl/bectl.8 b/sbin/bectl/bectl.8 index 718f4e2597ae..0e08b3383e9a 100644 --- a/sbin/bectl/bectl.8 +++ b/sbin/bectl/bectl.8 @@ -1,29 +1,17 @@ .\" -.\" 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. -.\" -.\" -.\" @(#)be.1 +.\" SPDX-License-Identifier: BSD-2-Clause .\" -.Dd June 28, 2023 +.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\&? +.Op Fl h .Nm .Op Fl r Ar beroot .Cm activate @@ -92,35 +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. -.Pp +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 | -.Fl \&? -Print usage information. +.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 @@ -135,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 @@ -175,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 @@ -187,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. @@ -204,7 +190,7 @@ Destroy the given boot environment or .Ar beName Ns Cm @ Ns Ar snapshot snapshot without confirmation, unlike in -.Xr beadm 1 . +.Xr beadm 8 . Specifying .Fl F will automatically unmount without confirmation. @@ -254,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 @@ -282,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. @@ -311,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 @@ -364,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 , @@ -375,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. @@ -396,7 +382,6 @@ will force the unmount if busy. .Pp Unmount will not remove the mount point. .El -.Pp .Ss Boot Environment Structures The traditional .Fx @@ -404,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 @@ -433,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 @@ -459,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 @@ -482,34 +468,50 @@ A future version of may default to handling both styles and deprecate the various .Fl r flags. -\" .Sh EXAMPLES -\" .Bl -bullet -\" .It -\" To fill in with jail upgrade example when behavior is firm. -\" .El +.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. .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 .Nm -is based on -.Xr beadm 1 -and was implemented as a project for the 2017 Summer of Code, along with -.Xr libbe 3 . -.Sh AUTHORS -.Nm -was written by -.An Kyle Kneitinger (kneitinger) Aq Mt kyle@kneit.in . -.Pp -.Xr beadm 1 -was written and is maintained by -.An Slawomir Wojciech Wojtczak (vermaden) Aq Mt vermaden@interia.pl . +and +.Xr libbe 3 +were written by +.An Kyle Kneitinger (kneitinger) Aq Mt kyle@kneit.in +as a 2017 Google Summer of Code project, with +.An Allan Jude (allanjude) Aq Mt allanjude@freebsd.org +as mentor. .Pp +.Nm +and this manual page were derived from +.Xr beadm 8 . +.Sh AUTHORS +.An Slawomir Wojciech Wojtczak (vermaden) Aq Mt vermaden@interia.pl +is the creator and maintainer of +.Xr beadm 8 . .An Bryan Drewery (bdrewery) Aq Mt bryan@shatow.net -wrote the original -.Xr beadm 1 -manual page that this one is derived from. +contributed child dataset fixes, and wrote the +.Xr beadm 8 +manual page. +.Pp +Most later changes to +.Nm , +and to this page, were written by +.An Kyle Evans (kevans) Aq Mt kevans@freebsd.org . diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index 287eeaf29fab..95715b34336b 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -1,31 +1,9 @@ -/*- - * 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/cdefs.h> #include <sys/param.h> #include <sys/mount.h> #include <errno.h> @@ -65,7 +43,7 @@ usage(bool explicit) fp = explicit ? stdout : stderr; fprintf(fp, "%s", - "Usage:\tbectl {-h | -? | subcommand [args...]}\n" + "Usage:\tbectl {-h | subcommand [args...]}\n" #if SOON "\tbectl [-r beroot] add (path)*\n" #endif @@ -115,7 +93,9 @@ static struct command_map_entry command_map[] = { "mount", bectl_cmd_mount, false }, { "rename", bectl_cmd_rename, false }, { "unjail", bectl_cmd_unjail, false }, + { "ujail", bectl_cmd_unjail, false }, { "unmount", bectl_cmd_unmount, false }, + { "umount", bectl_cmd_unmount, false }, { "check", bectl_cmd_check, true }, }; @@ -178,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); } @@ -440,7 +422,7 @@ bectl_cmd_mount(int argc, char *argv[]) switch (err) { case BE_ERR_SUCCESS: - printf("Successfully mounted %s at %s\n", bootenv, result_loc); + printf("%s\n", result_loc); break; default: fprintf(stderr, @@ -546,36 +528,30 @@ main(int argc, char *argv[]) { struct command_map_entry *cmd; const char *command; - char *root; - int rc; - - cmd = NULL; - root = NULL; - if (argc < 2) - return (usage(false)); + char *root = NULL; + int opt, rc; - if (strcmp(argv[1], "-r") == 0) { - if (argc < 4) - return (usage(false)); - root = strdup(argv[2]); - command = argv[3]; - argc -= 3; - argv += 3; - } else { - command = argv[1]; - argc -= 1; - argv += 1; + while ((opt = getopt(argc, argv, "hr:")) != -1) { + switch (opt) { + case 'h': + exit(usage(true)); + case 'r': + root = strdup(optarg); + break; + default: + exit(usage(false)); + } } - /* Handle command aliases */ - if (strcmp(command, "umount") == 0) - command = "unmount"; + argc -= optind; + argv += optind; - if (strcmp(command, "ujail") == 0) - command = "unjail"; + if (argc == 0) + exit(usage(false)); - if ((strcmp(command, "-?") == 0) || (strcmp(command, "-h") == 0)) - return (usage(true)); + command = *argv; + optreset = 1; + optind = 1; if ((cmd = get_cmd_info(command)) == NULL) { fprintf(stderr, "Unknown command: %s\n", command); 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 ae48202163de..050d46b627b4 100644 --- a/sbin/bectl/bectl_jail.c +++ b/sbin/bectl/bectl_jail.c @@ -1,31 +1,9 @@ -/*- - * 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/cdefs.h> #include <sys/param.h> #include <sys/jail.h> #include <sys/mount.h> diff --git a/sbin/bectl/bectl_list.c b/sbin/bectl/bectl_list.c index 0fcb5deb37f3..bd724b7bf012 100644 --- a/sbin/bectl/bectl_list.c +++ b/sbin/bectl/bectl_list.c @@ -1,31 +1,9 @@ -/*- - * 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/cdefs.h> #include <sys/param.h> #include <stdbool.h> #include <stdio.h> diff --git a/sbin/bectl/tests/Makefile b/sbin/bectl/tests/Makefile index 5da29657bea1..3a35a5263a69 100644 --- a/sbin/bectl/tests/Makefile +++ b/sbin/bectl/tests/Makefile @@ -1,4 +1,3 @@ - PACKAGE= tests ATF_TESTS_SH+= bectl_test diff --git a/sbin/bectl/tests/Makefile.depend b/sbin/bectl/tests/Makefile.depend new file mode 100644 index 000000000000..11aba52f82cf --- /dev/null +++ b/sbin/bectl/tests/Makefile.depend @@ -0,0 +1,10 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/sbin/bectl/tests/bectl_test.sh b/sbin/bectl/tests/bectl_test.sh index 0f167829cf46..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 @@ -93,7 +72,6 @@ bectl_cleanup() atf_test_case bectl_create cleanup bectl_create_head() { - atf_set "descr" "Check the various forms of bectl create" atf_set "require.user" root } @@ -157,7 +135,6 @@ bectl_create_cleanup() atf_test_case bectl_destroy cleanup bectl_destroy_head() { - atf_set "descr" "Check bectl destroy" atf_set "require.user" root } @@ -240,14 +217,12 @@ bectl_destroy_body() } bectl_destroy_cleanup() { - bectl_cleanup $(get_zpool_name) } atf_test_case bectl_export_import cleanup bectl_export_import_head() { - atf_set "descr" "Check bectl export and import" atf_set "require.user" root } @@ -278,14 +253,12 @@ bectl_export_import_body() } bectl_export_import_cleanup() { - bectl_cleanup $(get_zpool_name) } atf_test_case bectl_list cleanup bectl_list_head() { - atf_set "descr" "Check bectl list" atf_set "require.user" root } @@ -323,14 +296,12 @@ bectl_list_body() } bectl_list_cleanup() { - bectl_cleanup $(get_zpool_name) } atf_test_case bectl_mount cleanup bectl_mount_head() { - atf_set "descr" "Check bectl mount/unmount" atf_set "require.user" root } @@ -367,14 +338,12 @@ bectl_mount_body() } bectl_mount_cleanup() { - bectl_cleanup $(get_zpool_name) } atf_test_case bectl_rename cleanup bectl_rename_head() { - atf_set "descr" "Check bectl rename" atf_set "require.user" root } @@ -403,14 +372,12 @@ bectl_rename_body() } bectl_rename_cleanup() { - bectl_cleanup $(get_zpool_name) } atf_test_case bectl_jail cleanup bectl_jail_head() { - atf_set "descr" "Check bectl rename" atf_set "require.user" root atf_set "require.progs" jail @@ -577,6 +544,94 @@ bectl_promotion_cleanup() bectl_cleanup $(get_zpool_name) } +atf_test_case bectl_destroy_bootonce cleanup +bectl_destroy_bootonce_head() +{ + atf_set "descr" "Check bectl destroy (bootonce)" + atf_set "require.user" root +} +bectl_destroy_bootonce_body() +{ + if [ "$(atf_config_get ci false)" = "true" ] && \ + [ "$(uname -p)" = "i386" ]; then + atf_skip "https://bugs.freebsd.org/249055" + fi + + if [ "$(atf_config_get ci false)" = "true" ] && \ + [ "$(uname -p)" = "armv7" ]; then + atf_skip "https://bugs.freebsd.org/249229" + fi + + cwd=$(realpath .) + zpool=$(make_zpool_name) + disk=${cwd}/disk.img + mount=${cwd}/mnt + root=${mount}/root + + be=default2 + + bectl_create_setup ${zpool} ${disk} ${mount} + atf_check -s exit:0 -o empty bectl -r ${zpool}/ROOT create -e default ${be} + + # Create boot environment and bootonce activate it + atf_check -s exit:0 -o ignore bectl -r ${zpool}/ROOT activate -t ${be} + atf_check -s exit:0 -o inline:"zfs:${zpool}/ROOT/${be}:\n" zfsbootcfg -z ${zpool} + + # Destroy it + atf_check -s exit:0 -o ignore bectl -r ${zpool}/ROOT destroy ${be} + + # Should be empty + atf_check -s exit:0 -o empty zfsbootcfg -z ${zpool} +} +bectl_destroy_bootonce_cleanup() +{ + bectl_cleanup $(get_zpool_name) +} + +atf_test_case bectl_rename_bootonce cleanup +bectl_rename_bootonce_head() +{ + atf_set "descr" "Check bectl destroy (bootonce)" + atf_set "require.user" root +} +bectl_rename_bootonce_body() +{ + if [ "$(atf_config_get ci false)" = "true" ] && \ + [ "$(uname -p)" = "i386" ]; then + atf_skip "https://bugs.freebsd.org/249055" + fi + + if [ "$(atf_config_get ci false)" = "true" ] && \ + [ "$(uname -p)" = "armv7" ]; then + atf_skip "https://bugs.freebsd.org/249229" + fi + + cwd=$(realpath .) + zpool=$(make_zpool_name) + disk=${cwd}/disk.img + mount=${cwd}/mnt + root=${mount}/root + + be=default2 + + bectl_create_setup ${zpool} ${disk} ${mount} + atf_check -s exit:0 -o empty bectl -r ${zpool}/ROOT create -e default ${be} + + # Create boot environment and bootonce activate it + atf_check -s exit:0 -o ignore bectl -r ${zpool}/ROOT activate -t ${be} + atf_check -s exit:0 -o inline:"zfs:${zpool}/ROOT/${be}:\n" zfsbootcfg -z ${zpool} + + # Rename it + atf_check -s exit:0 -o ignore bectl -r ${zpool}/ROOT rename ${be} ${be}_renamed + + # Should be renamed + atf_check -s exit:0 -o inline:"zfs:${zpool}/ROOT/${be}_renamed:\n" zfsbootcfg -z ${zpool} +} +bectl_rename_bootonce_cleanup() +{ + bectl_cleanup $(get_zpool_name) +} + atf_init_test_cases() { atf_add_test_case bectl_create @@ -587,4 +642,6 @@ atf_init_test_cases() atf_add_test_case bectl_rename atf_add_test_case bectl_jail atf_add_test_case bectl_promotion + atf_add_test_case bectl_destroy_bootonce + atf_add_test_case bectl_rename_bootonce } diff --git a/sbin/bsdlabel/Makefile b/sbin/bsdlabel/Makefile index f1d6461beaef..d2be16efa544 100644 --- a/sbin/bsdlabel/Makefile +++ b/sbin/bsdlabel/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.2 (Berkeley) 3/17/94 - .PATH: ${SRCTOP}/sys/geom CONFS= disktab diff --git a/sbin/bsdlabel/bsdlabel.8 b/sbin/bsdlabel/bsdlabel.8 index 9228bba78d4b..abea59756aea 100644 --- a/sbin/bsdlabel/bsdlabel.8 +++ b/sbin/bsdlabel/bsdlabel.8 @@ -28,9 +28,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)disklabel.8 8.2 (Berkeley) 4/19/94 -.\" -.Dd October 5, 2016 +.Dd January 23, 2025 .Dt BSDLABEL 8 .Os .Sh NAME @@ -61,6 +59,22 @@ .Op Fl f .Ar disk | Fl f Ar file .Ar protofile +.Sh DEPRECATION NOTICE +.Nm +is deprecated and is not available in +.Fx 15.0 +or later. +Use +.Xr gpart 8 +instead +.Po +with the +.Cm BSD +partitioning scheme +.Pc , +or install the +.Pa freebsd-bsdlabel +port or package. .Sh DESCRIPTION The .Nm @@ -310,8 +324,6 @@ file systems and .Xr ccd 4 partitions, use type .Cm 4.2BSD . -For Vinum drives, use type -.Cm vinum . Other common types are .Cm swap and @@ -504,3 +516,8 @@ The .Nm disklabel utility appeared in .Bx 4.3 Tahoe . +.Sh BUGS +.\" PR276517 +The disklabel scheme supports up to 20 partitions, but +.Nm +supports only 8 partitions. diff --git a/sbin/bsdlabel/bsdlabel.c b/sbin/bsdlabel/bsdlabel.c index 766def59ffe9..a68ee377a97c 100644 --- a/sbin/bsdlabel/bsdlabel.c +++ b/sbin/bsdlabel/bsdlabel.c @@ -42,19 +42,6 @@ * from: $NetBSD: disksubr.c,v 1.13 2000/12/17 22:39:18 pk $ */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1987, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)disklabel.c 8.2 (Berkeley) 1/7/94"; -/* from static char sccsid[] = "@(#)disklabel.c 1.2 (Symmetric) 11/28/85"; */ -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #include <sys/param.h> #include <stdint.h> #include <sys/file.h> @@ -63,7 +50,7 @@ static char sccsid[] = "@(#)disklabel.c 8.2 (Berkeley) 1/7/94"; #include <sys/disk.h> #define DKTYPENAMES #define FSTYPENAMES -#define MAXPARTITIONS 20 +#define MAXPARTITIONS 8 /* XXX should be 20, but see PR276517 */ #include <sys/disklabel.h> #include <unistd.h> @@ -148,6 +135,10 @@ main(int argc, char *argv[]) error = 0; name = NULL; + fprintf(stderr, + "WARNING: bsdlabel is deprecated and is not available in FreeBSD 15 or later.\n" + "Please use gpart instead.\n\n"); + while ((ch = getopt(argc, argv, "ABb:efm:nRrw")) != -1) switch (ch) { case 'A': diff --git a/sbin/bsdlabel/pathnames.h b/sbin/bsdlabel/pathnames.h index f28593e71e55..4d215eb72e33 100644 --- a/sbin/bsdlabel/pathnames.h +++ b/sbin/bsdlabel/pathnames.h @@ -27,8 +27,6 @@ * 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. - * - * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 */ #include <paths.h> diff --git a/sbin/camcontrol/Makefile b/sbin/camcontrol/Makefile index d47dbdc43bd7..2b0c2c9dbc14 100644 --- a/sbin/camcontrol/Makefile +++ b/sbin/camcontrol/Makefile @@ -1,22 +1,30 @@ - .include <src.opts.mk> PACKAGE=runtime PROG= camcontrol -SRCS= camcontrol.c util.c -SRCS+= attrib.c depop.c epc.c fwdownload.c modeedit.c persist.c progress.c timestamp.c zone.c -.if ${MK_NVME} != "no" +SRCS= camcontrol.c +SRCS+= attrib.c +SRCS+= depop.c +SRCS+= epc.c +SRCS+= fwdownload.c +SRCS+= modeedit.c +SRCS+= persist.c +SRCS+= progress.c +SRCS+= timestamp.c +SRCS+= util.c +SRCS+= zone.c .PATH: ${SRCTOP}/sbin/nvmecontrol -CFLAGS+= -I${SRCTOP}/sbin/nvmecontrol -DWITH_NVME -SRCS+= identify_ext.c nc_util.c +CFLAGS+= -I${SRCTOP}/sbin/nvmecontrol +SRCS+= identify_ext.c +SRCS+= nc_util.c .PATH: ${SRCTOP}/sys/dev/nvme SRCS+= nvme_util.c -.endif # This is verboten .if ${MACHINE_CPUARCH} == "arm" WARNS?= 3 .endif -LIBADD= cam sbuf util +CFLAGS+= -I${SRCTOP}/lib/libnvmf +LIBADD= cam nvmf sbuf util MAN= camcontrol.8 .include <bsd.prog.mk> diff --git a/sbin/camcontrol/Makefile.depend b/sbin/camcontrol/Makefile.depend index 0ef6390836dd..bef65a3c03d5 100644 --- a/sbin/camcontrol/Makefile.depend +++ b/sbin/camcontrol/Makefile.depend @@ -7,6 +7,7 @@ DIRDEPS = \ lib/libc \ lib/libcam \ lib/libcompiler_rt \ + lib/libnvmf \ lib/libsbuf \ lib/libutil \ diff --git a/sbin/camcontrol/attrib.c b/sbin/camcontrol/attrib.c index 7e89dbc3d48e..4cf9832756b4 100644 --- a/sbin/camcontrol/attrib.c +++ b/sbin/camcontrol/attrib.c @@ -36,7 +36,7 @@ #include <sys/cdefs.h> #include <sys/ioctl.h> #include <sys/stdint.h> -#include <sys/types.h> +#include <sys/param.h> #include <sys/endian.h> #include <sys/sbuf.h> #include <sys/queue.h> @@ -265,7 +265,7 @@ scsiattrib(struct cam_device *device, int argc, char **argv, char *combinedopt, int entry_num = 0; status = scsi_get_nv(elem_type_map, - sizeof(elem_type_map) / sizeof(elem_type_map[0]), + nitems(elem_type_map), optarg, &entry_num, SCSI_NV_FLAG_IG_CASE); if (status == SCSI_NV_FOUND) element_type = elem_type_map[entry_num].value; diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8 index 3bbd81dab033..bdda1828abf5 100644 --- a/sbin/camcontrol/camcontrol.8 +++ b/sbin/camcontrol/camcontrol.8 @@ -25,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd June 1, 2023 +.Dd December 28, 2023 .Dt CAMCONTROL 8 .Os .Sh NAME @@ -51,6 +51,12 @@ .Op device id .Op generic args .Nm +.Ic sense +.Op device id +.Op generic args +.Op Fl D +.Op Fl x +.Nm .Ic inquiry .Op device id .Op generic args @@ -488,6 +494,15 @@ Send the SCSI test unit ready (0x00) command to the given device. The .Nm utility will report whether the device is ready or not. +.It Ic sense +Send a SCSI REQUEST SENSE command (0x03) to a device. +The decoded sense (or hexdump) is printed to stdout. +.Bl -tag -width 4n +.It Fl D +Request descriptor sense instead of fixed sense. +.It Fl x +Do a hexdump of the returned sense data. +.El .It Ic inquiry Send a SCSI inquiry command (0x12) to a device. By default, @@ -509,7 +524,7 @@ This is to aid in script writing. Print out transfer rate information. .El .It Ic identify -Send a ATA identify command (0xec) to a device. +Send an ATA identify command (0xec) to a device. .It Ic reportluns Send the SCSI REPORT LUNS (0xA0) command to the given device. By default, @@ -745,7 +760,7 @@ Disable block descriptors for mode sense. Display/edit block descriptors instead of mode page. .It Fl L Use long LBA block descriptors. -Allows number of LBAs bigger then 2^^32. +Allows number of LBAs bigger than 2^^32. .It Fl b Displays mode page data in binary format. .It Fl e @@ -762,7 +777,7 @@ The editor will be invoked if detects that standard input is terminal. .It Fl l Lists all available mode pages. -If specified more then once, also lists subpages. +If specified more than once, also lists subpages. .It Fl m Ar page[,subpage] This specifies the number of the mode page and optionally subpage the user would like to view and/or edit. @@ -1685,6 +1700,8 @@ Tested with Constellation ES (ST32000444SS), ES.2 (ST33000651SS) and ES.3 (ST1000NM0023) drives. .It SmrtStor Tested with 400GB Optimus SSDs (TXA2D20400GA6001). +.It TOSHIBA +Tested with 22TB MG10SFA22TE SAS drives. .El .Pp .Em WARNING! WARNING! WARNING! @@ -2313,7 +2330,7 @@ For the Report Zones command, specify a subset of zones to report. .It all Report all zones. This is the default. -.It emtpy +.It empty Report only empty zones. .It imp_open Report zones that are implicitly open. diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c index 9417943afcb3..15a5d42a2ba5 100644 --- a/sbin/camcontrol/camcontrol.c +++ b/sbin/camcontrol/camcontrol.c @@ -44,6 +44,7 @@ #include <fcntl.h> #include <ctype.h> #include <err.h> +#include <libnvmf.h> #include <libutil.h> #include <limits.h> #include <inttypes.h> @@ -60,9 +61,7 @@ #include <cam/mmc/mmc_all.h> #include <camlib.h> #include "camcontrol.h" -#ifdef WITH_NVME #include "nvmecontrol_ext.h" -#endif typedef enum { CAM_CMD_NONE, @@ -111,6 +110,7 @@ typedef enum { CAM_CMD_DEVTYPE, CAM_CMD_AMA, CAM_CMD_DEPOP, + CAM_CMD_REQSENSE } cam_cmd; typedef enum { @@ -233,6 +233,7 @@ static struct camcontrol_opts option_table[] = { {"epc", CAM_CMD_EPC, CAM_ARG_NONE, "c:dDeHp:Pr:sS:T:"}, {"timestamp", CAM_CMD_TIMESTAMP, CAM_ARG_NONE, "f:mrsUT:"}, {"depop", CAM_CMD_DEPOP, CAM_ARG_NONE, "ac:de:ls"}, + {"sense", CAM_CMD_REQSENSE, CAM_ARG_NONE, "Dx"}, {"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL}, {"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL}, {"-h", CAM_CMD_USAGE, CAM_ARG_NONE, NULL}, @@ -276,9 +277,10 @@ static int print_dev_ata(struct device_match_result *dev_result, char *tmpstr); static int print_dev_semb(struct device_match_result *dev_result, char *tmpstr); static int print_dev_mmcsd(struct device_match_result *dev_result, char *tmpstr); -#ifdef WITH_NVME static int print_dev_nvme(struct device_match_result *dev_result, char *tmpstr); -#endif +static int requestsense(struct cam_device *device, int argc, char **argv, + char *combinedopt, int task_attr, int retry_count, + int timeout); static int testunitready(struct cam_device *device, int task_attr, int retry_count, int timeout, int quiet); static int scsistart(struct cam_device *device, int startstop, int loadeject, @@ -595,14 +597,12 @@ getdevtree(int argc, char **argv, char *combinedopt) skip_device = 1; break; } -#ifdef WITH_NVME } else if (dev_result->protocol == PROTO_NVME) { if (print_dev_nvme(dev_result, &tmpstr[0]) != 0) { skip_device = 1; break; } -#endif } else { sprintf(tmpstr, "<>"); } @@ -653,6 +653,7 @@ getdevtree(int argc, char **argv, char *combinedopt) if (need_close) fprintf(stdout, ")\n"); + free(ccb.cdm.matches); close(fd); return (error); @@ -776,7 +777,6 @@ print_dev_mmcsd(struct device_match_result *dev_result, char *tmpstr) return (0); } -#ifdef WITH_NVME static int nvme_get_cdata(struct cam_device *dev, struct nvme_controller_data *cdata) { @@ -838,7 +838,114 @@ print_dev_nvme(struct device_match_result *dev_result, char *tmpstr) cam_close_device(dev); return (0); } -#endif + +static int +requestsense(struct cam_device *device, int argc, char **argv, + char *combinedopt, int task_attr, int retry_count, int timeout) +{ + int c; + int descriptor_sense = 0; + int do_hexdump = 0; + struct scsi_sense_data sense; + union ccb *ccb = NULL; + int error = 0; + size_t returned_bytes; + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'D': + descriptor_sense = 1; + break; + case 'x': + do_hexdump = 1; + break; + default: + break; + } + } + + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("couldn't allocate CCB"); + return (1); + } + + /* cam_getccb cleans up the header, caller has to zero the payload */ + CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio); + + bzero(&sense, sizeof(sense)); + + scsi_request_sense(&ccb->csio, + /*retries*/ retry_count, + /*cbfcnp*/ NULL, + /*data_ptr*/ (void *)&sense, + /*dxfer_len*/ sizeof(sense), + /*tag_action*/ task_attr, + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ timeout ? timeout : 60000); + + if (descriptor_sense != 0) { + struct scsi_request_sense *cdb; + + cdb = (struct scsi_request_sense *)&ccb->csio.cdb_io.cdb_bytes; + cdb->byte2 |= SRS_DESC; + } + + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + + if (arglist & CAM_ARG_ERR_RECOVER) + ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; + + if (cam_send_ccb(device, ccb) < 0) { + warn("error sending REQUEST SENSE command"); + cam_freeccb(ccb); + error = 1; + goto bailout; + } + + /* + * REQUEST SENSE is not generally supposed to fail. But there can + * be transport or other errors that might cause it to fail. It + * may also fail if the user asks for descriptor sense and the + * device doesn't support it. So we check the CCB status here to see. + */ + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + warnx("REQUEST SENSE failed"); + cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); + error = 1; + goto bailout; + } + + returned_bytes = ccb->csio.dxfer_len - ccb->csio.resid; + + if (do_hexdump != 0) { + hexdump(&sense, returned_bytes, NULL, 0); + } else { + char path_str[80]; + struct sbuf *sb; + + cam_path_string(device, path_str, sizeof(path_str)); + sb = sbuf_new_auto(); + if (sb == NULL) { + warnx("%s: cannot allocate sbuf", __func__); + error = 1; + goto bailout; + } + + scsi_sense_only_sbuf(&sense, returned_bytes, sb, path_str, + &device->inq_data, scsiio_cdb_ptr(&ccb->csio), + ccb->csio.cdb_len); + + sbuf_finish(sb); + printf("%s", sbuf_data(sb)); + sbuf_delete(sb); + } +bailout: + if (ccb != NULL) + cam_freeccb(ccb); + + return (error); +} static int testunitready(struct cam_device *device, int task_attr, int retry_count, @@ -1995,7 +2102,7 @@ ata_read_native_max(struct cam_device *device, int retry_count, /*sector_count*/0, /*data_ptr*/NULL, /*dxfer_len*/0, - timeout ? timeout : 5000, + timeout ? timeout : 10 * 1000, is48bit); if (error) @@ -2376,7 +2483,6 @@ ataidentify(struct cam_device *device, int retry_count, int timeout) return (0); } -#ifdef WITH_NVME static int nvmeidentify(struct cam_device *device, int retry_count __unused, int timeout __unused) { @@ -2388,12 +2494,10 @@ nvmeidentify(struct cam_device *device, int retry_count __unused, int timeout __ return (0); } -#endif static int identify(struct cam_device *device, int retry_count, int timeout) { -#ifdef WITH_NVME struct ccb_pathinq cpi; if (get_cpi(device, &cpi) != 0) { @@ -2404,7 +2508,6 @@ identify(struct cam_device *device, int retry_count, int timeout) if (cpi.protocol == PROTO_NVME) { return (nvmeidentify(device, retry_count, timeout)); } -#endif return (ataidentify(device, retry_count, timeout)); } @@ -5277,6 +5380,39 @@ cts_print(struct cam_device *device, struct ccb_trans_settings *cts) sata->caps); } } + if (cts->transport == XPORT_NVME) { + struct ccb_trans_settings_nvme *nvme = + &cts->xport_specific.nvme; + + if (nvme->valid & CTS_NVME_VALID_LINK) { + fprintf(stdout, "%sPCIe lanes: %d (%d max)\n", pathstr, + nvme->lanes, nvme->max_lanes); + fprintf(stdout, "%sPCIe Generation: %d (%d max)\n", pathstr, + nvme->speed, nvme->max_speed); + } + } + if (cts->transport == XPORT_NVMF) { + struct ccb_trans_settings_nvmf *nvmf = + &cts->xport_specific.nvmf; + + if (nvmf->valid & CTS_NVMF_VALID_TRTYPE) { + fprintf(stdout, "%sTransport: %s\n", pathstr, + 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; @@ -5297,24 +5433,16 @@ cts_print(struct cam_device *device, struct ccb_trans_settings *cts) "enabled" : "disabled"); } } -#ifdef WITH_NVME if (cts->protocol == PROTO_NVME) { - struct ccb_trans_settings_nvme *nvmex = - &cts->xport_specific.nvme; + struct ccb_trans_settings_nvme *nvme = + &cts->proto_specific.nvme; - if (nvmex->valid & CTS_NVME_VALID_SPEC) { + if (nvme->valid & CTS_NVME_VALID_SPEC) { fprintf(stdout, "%sNVMe Spec: %d.%d\n", pathstr, - NVME_MAJOR(nvmex->spec), - NVME_MINOR(nvmex->spec)); - } - if (nvmex->valid & CTS_NVME_VALID_LINK) { - fprintf(stdout, "%sPCIe lanes: %d (%d max)\n", pathstr, - nvmex->lanes, nvmex->max_lanes); - fprintf(stdout, "%sPCIe Generation: %d (%d max)\n", pathstr, - nvmex->speed, nvmex->max_speed); + NVME_MAJOR(nvme->spec), + NVME_MINOR(nvme->spec)); } } -#endif } /* @@ -9869,6 +9997,7 @@ usage(int printlong) " camcontrol devlist [-b] [-v]\n" " camcontrol periphlist [dev_id][-n dev_name] [-u unit]\n" " camcontrol tur [dev_id][generic args]\n" +" camcontrol sense [dev_id][generic args][-D][-x]\n" " camcontrol inquiry [dev_id][generic args] [-D] [-S] [-R]\n" " camcontrol identify [dev_id][generic args] [-v]\n" " camcontrol reportluns [dev_id][generic args] [-c] [-l] [-r report]\n" @@ -9957,6 +10086,7 @@ usage(int printlong) "Specify one of the following options:\n" "devlist list all CAM devices\n" "periphlist list all CAM peripheral drivers attached to a device\n" +"sense send a request sense command to the named device\n" "tur send a test unit ready to the named device\n" "inquiry send a SCSI inquiry command to the named device\n" "identify send a ATA identify command to the named device\n" @@ -10021,6 +10151,9 @@ usage(int printlong) "-f format specify defect list format (block, bfi or phys)\n" "-G get the grown defect list\n" "-P get the permanent defect list\n" +"sense arguments:\n" +"-D request descriptor sense data\n" +"-x do a hexdump of the sense data\n" "inquiry arguments:\n" "-D get the standard inquiry data\n" "-S get the serial number\n" @@ -10491,6 +10624,10 @@ main(int argc, char **argv) case CAM_CMD_DEVTYPE: error = getdevtype(cam_dev); break; + case CAM_CMD_REQSENSE: + error = requestsense(cam_dev, argc, argv, combinedopt, + task_attr, retry_count, timeout); + break; case CAM_CMD_TUR: error = testunitready(cam_dev, task_attr, retry_count, timeout, 0); diff --git a/sbin/camcontrol/depop.c b/sbin/camcontrol/depop.c index 3be67650cdae..4aff0284abe1 100644 --- a/sbin/camcontrol/depop.c +++ b/sbin/camcontrol/depop.c @@ -50,7 +50,6 @@ * 'depop' provides a wrapper around all these functions. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <err.h> diff --git a/sbin/camcontrol/epc.c b/sbin/camcontrol/epc.c index 206b34594f4f..4273ad19047c 100644 --- a/sbin/camcontrol/epc.c +++ b/sbin/camcontrol/epc.c @@ -33,10 +33,9 @@ * ATA Extended Power Conditions (EPC) support */ -#include <sys/cdefs.h> +#include <sys/param.h> #include <sys/ioctl.h> #include <sys/stdint.h> -#include <sys/types.h> #include <sys/endian.h> #include <sys/sbuf.h> #include <sys/queue.h> @@ -151,7 +150,7 @@ epc_print_pcl_desc(struct ata_power_cond_log_desc *desc, const char *prefix) max_chars = 75; num_printed = printf("%sFlags: ", prefix); - for (i = 0; i < (sizeof(epc_flags) / sizeof(epc_flags[0])); i++) { + for (i = 0; i < nitems(epc_flags); i++) { if ((desc->flags & epc_flags[i].value) == 0) continue; if (first == 0) { @@ -466,7 +465,7 @@ check_power_mode: } mode_name = scsi_nv_to_str(epc_power_cond_map, - sizeof(epc_power_cond_map) / sizeof(epc_power_cond_map[0]), count); + nitems(epc_power_cond_map), count); printf("Current power state: "); /* Note: ident can be null in power_only mode */ if ((ident == NULL) @@ -638,7 +637,7 @@ epc(struct cam_device *device, int argc, char **argv, char *combinedopt, int entry_num; status = scsi_get_nv(epc_cmd_map, - (sizeof(epc_cmd_map) / sizeof(epc_cmd_map[0])), + nitems(epc_cmd_map), optarg, &entry_num, SCSI_NV_FLAG_IG_CASE); if (status == SCSI_NV_FOUND) action = epc_cmd_map[entry_num].value; @@ -715,7 +714,7 @@ epc(struct cam_device *device, int argc, char **argv, char *combinedopt, int entry_num; status = scsi_get_nv(epc_ps_map, - (sizeof(epc_ps_map) / sizeof(epc_ps_map[0])), + nitems(epc_ps_map), optarg, &entry_num, SCSI_NV_FLAG_IG_CASE); if (status == SCSI_NV_FOUND) power_src = epc_ps_map[entry_num].value; diff --git a/sbin/camcontrol/fwdownload.c b/sbin/camcontrol/fwdownload.c index 25b98c804e3e..569021c2bfc1 100644 --- a/sbin/camcontrol/fwdownload.c +++ b/sbin/camcontrol/fwdownload.c @@ -49,7 +49,6 @@ * specified device. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/stat.h> @@ -84,6 +83,7 @@ typedef enum { VENDOR_SAMSUNG, VENDOR_SEAGATE, VENDOR_SMART, + VENDOR_TOSHIBA, VENDOR_ATA, VENDOR_UNKNOWN } fw_vendor_t; @@ -213,6 +213,8 @@ static struct fw_vendor vendors_list[] = { 0x8000, 0x07, 0x07, 0, 1, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT}, {VENDOR_SMART, "SmrtStor", T_DIRECT, 0x8000, 0x07, 0x07, 0, 1, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT}, + {VENDOR_TOSHIBA, "TOSHIBA", T_DIRECT, + 0x8000, 0x07, 0x07, 0, 1, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT}, {VENDOR_HGST, "WD", T_DIRECT, 0x1000, 0x07, 0x07, 1, 0, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT}, {VENDOR_HGST, "WDC", T_DIRECT, diff --git a/sbin/camcontrol/timestamp.c b/sbin/camcontrol/timestamp.c index 403c126c95d9..3130dec188dc 100644 --- a/sbin/camcontrol/timestamp.c +++ b/sbin/camcontrol/timestamp.c @@ -35,7 +35,6 @@ * SCSI tape drive timestamp support */ -#include <sys/cdefs.h> #include <sys/types.h> #include <assert.h> diff --git a/sbin/camcontrol/zone.c b/sbin/camcontrol/zone.c index d651ca0e2acf..dc87cd9a9570 100644 --- a/sbin/camcontrol/zone.c +++ b/sbin/camcontrol/zone.c @@ -34,10 +34,9 @@ * This is an implementation of the SCSI ZBC and ATA ZAC specs. */ -#include <sys/cdefs.h> +#include <sys/param.h> #include <sys/ioctl.h> #include <sys/stdint.h> -#include <sys/types.h> #include <sys/endian.h> #include <sys/sbuf.h> #include <sys/queue.h> @@ -353,7 +352,7 @@ zone(struct cam_device *device, int argc, char **argv, char *combinedopt, int entry_num; status = scsi_get_nv(zone_cmd_map, - (sizeof(zone_cmd_map) / sizeof(zone_cmd_map[0])), + nitems(zone_cmd_map), optarg, &entry_num, SCSI_NV_FLAG_IG_CASE); if (status == SCSI_NV_FOUND) action = zone_cmd_map[entry_num].value; @@ -387,7 +386,7 @@ zone(struct cam_device *device, int argc, char **argv, char *combinedopt, int entry_num; status = scsi_get_nv(zone_rep_opts, - (sizeof(zone_rep_opts) /sizeof(zone_rep_opts[0])), + nitems(zone_rep_opts), optarg, &entry_num, SCSI_NV_FLAG_IG_CASE); if (status == SCSI_NV_FOUND) rep_option = zone_rep_opts[entry_num].value; diff --git a/sbin/ccdconfig/Makefile b/sbin/ccdconfig/Makefile index 6a27bb7e7cf4..8972945ed072 100644 --- a/sbin/ccdconfig/Makefile +++ b/sbin/ccdconfig/Makefile @@ -1,4 +1,3 @@ - PACKAGE=ccdconfig PROG= ccdconfig MAN= ccdconfig.8 diff --git a/sbin/ccdconfig/ccdconfig.8 b/sbin/ccdconfig/ccdconfig.8 index 2c7460f291f6..52bbfef20af6 100644 --- a/sbin/ccdconfig/ccdconfig.8 +++ b/sbin/ccdconfig/ccdconfig.8 @@ -27,7 +27,7 @@ .\" .\" $NetBSD: ccdconfig.8,v 1.4 1996/02/28 01:01:17 thorpej Exp $ .\" -.Dd March 17, 2022 +.Dd January 23, 2025 .Dt CCDCONFIG 8 .Os .Sh NAME @@ -122,13 +122,6 @@ argument is optional. ccd0 16 none /dev/da2s1 /dev/da3s1 .Ed .Pp -The component devices need to name partitions of type -.Li FS_BSDFFS -(or -.Dq 4.2BSD -as shown by -.Xr disklabel 8 ) . -.Pp If you want to use the .Tn Linux .Xr md 4 @@ -152,7 +145,7 @@ are exactly the same as you might place in the configuration file. The first example creates a 4-disk stripe out of four scsi disk partitions. -The stripe uses a 64 sector interleave. +The stripe uses a 64-sector interleave. The second example is an example of a complex stripe/mirror combination. It reads as a two disk stripe of da4 and da5 which is mirrored to a two disk stripe of da6 and da7. @@ -183,34 +176,9 @@ ccdconfig -c /dev/ccd0 32 linux /dev/ada0s1 /dev/ada0s2 .Ed .Pp When you create a new ccd disk you generally want to -.Xr fdisk 8 -and -.Xr disklabel 8 +partition it using +.Xr gpart 8 it before doing anything else. -Once you create the initial label you can -edit it, adding additional partitions. -The label itself takes up the first -16 sectors of the ccd disk. -If all you are doing is creating file systems -with newfs, you do not have to worry about this as newfs will skip the -label area. -However, if you intend to -.Xr dd 1 -to or from a ccd partition it is usually a good idea to construct the -partition such that it does not overlap the label area. -For example, if -you have A ccd disk with 10000 sectors you might create a 'd' partition -with offset 16 and size 9984. -.Bd -literal -# disklabel ccd0 > /tmp/disklabel.ccd0 -# disklabel -R ccd0 /tmp/disklabel.ccd0 -# disklabel -e ccd0 -.Ed -.Pp -The disklabeling of a ccd disk is usually a one-time affair. -If you reboot the machine and reconfigure the ccd disk, -the disklabel you -had created before will still be there and not require reinitialization. Beware that changing any ccd parameters: interleave, flags, or the device list making up the ccd disk, will usually destroy any prior data on that ccd disk. @@ -231,27 +199,18 @@ If you need more than this you should look into external hardware RAID SCSI boxes, RAID controllers (see GENERIC), or software RAID systems such as -.Xr geom 8 -and -.Xr gvinum 8 . +.Xr graid 8 +or +.Xr zfs 8 . .Sh SEE ALSO .Xr dd 1 , .Xr ccd 4 , -.Xr disklabel 8 , -.Xr fdisk 8 , -.Xr gvinum 8 , -.Xr rc 8 +.Xr gpart 8 , +.Xr graid 8 , +.Xr rc 8 , +.Xr zfs 8 .Sh HISTORY The .Nm utility first appeared in .Nx 1.1 . -.Sh BUGS -The initial disklabel returned by -.Xr ccd 4 -specifies only 3 partitions. -One needs to change the number of partitions to 8 using -.Dq Nm disklabel Fl e -to get the usual -.Bx -expectations. diff --git a/sbin/ccdconfig/ccdconfig.c b/sbin/ccdconfig/ccdconfig.c index fd62c459a78f..6b8086ef0213 100644 --- a/sbin/ccdconfig/ccdconfig.c +++ b/sbin/ccdconfig/ccdconfig.c @@ -32,7 +32,6 @@ * NetBSD: ccdconfig.c,v 1.6 1996/05/16 07:11:18 thorpej Exp $ */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/linker.h> #include <sys/module.h> diff --git a/sbin/clri/Makefile b/sbin/clri/Makefile index 726adbf8871c..51f4e9a914dc 100644 --- a/sbin/clri/Makefile +++ b/sbin/clri/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.1 (Berkeley) 6/5/93 - PACKAGE=ufs PROG= clri MAN= clri.8 diff --git a/sbin/clri/clri.8 b/sbin/clri/clri.8 index f9a86fe659ec..9d1fb92e22ba 100644 --- a/sbin/clri/clri.8 +++ b/sbin/clri/clri.8 @@ -25,8 +25,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)clri.8 8.2 (Berkeley) 4/19/94 -.\" .Dd October 5, 2016 .Dt CLRI 8 .Os diff --git a/sbin/clri/clri.c b/sbin/clri/clri.c index 7144490869cd..331975b4cdce 100644 --- a/sbin/clri/clri.c +++ b/sbin/clri/clri.c @@ -32,19 +32,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1990, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)clri.c 8.2 (Berkeley) 9/23/93"; -#endif /* not lint */ -#endif - -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/disklabel.h> diff --git a/sbin/comcontrol/Makefile b/sbin/comcontrol/Makefile index 2ddb60218039..6c868dcfb1ae 100644 --- a/sbin/comcontrol/Makefile +++ b/sbin/comcontrol/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 5.4 (Berkeley) 6/5/91 - PACKAGE=runtime PROG= comcontrol MAN= comcontrol.8 diff --git a/sbin/comcontrol/comcontrol.8 b/sbin/comcontrol/comcontrol.8 index 46c7f987d774..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,20 +26,13 @@ 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. The units are seconds. The default is 5 minutes, 0 means waiting forever. -This option needed mainly to specify upper limit of minutes +This option is needed mainly to specify an upper limit of minutes to prevent modem hanging. .El .Pp @@ -52,12 +49,11 @@ dialin devices, hardwired terminals dialout devices .El .Sh SEE ALSO -.Xr stty 1 , +.Xr stty 1 .Sh HISTORY 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/conscontrol/Makefile b/sbin/conscontrol/Makefile index 072edf04a5c4..33665536167c 100644 --- a/sbin/conscontrol/Makefile +++ b/sbin/conscontrol/Makefile @@ -1,4 +1,3 @@ - PACKAGE=runtime PROG= conscontrol MAN= conscontrol.8 diff --git a/sbin/conscontrol/conscontrol.8 b/sbin/conscontrol/conscontrol.8 index 54c89d5e5c1b..c11a1f7db5d9 100644 --- a/sbin/conscontrol/conscontrol.8 +++ b/sbin/conscontrol/conscontrol.8 @@ -1,3 +1,5 @@ +.\"- +.\" SPDX-License-Identifer: BSD-2-Clause .\" .\" Copyright (c) 2001 Jonathan Lemon <jlemon@FreeBSD.org> .\" All rights reserved. @@ -23,12 +25,12 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd April 14, 2011 +.Dd July 7, 2024 .Dt CONSCONTROL 8 .Os .Sh NAME .Nm conscontrol -.Nd control physical console devices +.Nd control physical system video console devices .Sh SYNOPSIS .Nm .Op Cm list diff --git a/sbin/conscontrol/conscontrol.c b/sbin/conscontrol/conscontrol.c index b8890cf22030..9c7ec9175910 100644 --- a/sbin/conscontrol/conscontrol.c +++ b/sbin/conscontrol/conscontrol.c @@ -26,7 +26,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/sysctl.h> #include <sys/ioctl.h> diff --git a/sbin/ddb/Makefile b/sbin/ddb/Makefile index d8d67602c3ef..8d527ad5259a 100644 --- a/sbin/ddb/Makefile +++ b/sbin/ddb/Makefile @@ -1,4 +1,3 @@ - CONFS= ddb.conf PACKAGE=runtime PROG= ddb diff --git a/sbin/ddb/ddb_capture.c b/sbin/ddb/ddb_capture.c index fcff16e292ea..ab8b2c1ed0bb 100644 --- a/sbin/ddb/ddb_capture.c +++ b/sbin/ddb/ddb_capture.c @@ -26,7 +26,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/sysctl.h> diff --git a/sbin/ddb/ddb_script.c b/sbin/ddb/ddb_script.c index c40b8b96b21f..76724df8d772 100644 --- a/sbin/ddb/ddb_script.c +++ b/sbin/ddb/ddb_script.c @@ -26,7 +26,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/sysctl.h> diff --git a/sbin/decryptcore/Makefile b/sbin/decryptcore/Makefile index 885766f5cfc6..97421fe5cd3c 100644 --- a/sbin/decryptcore/Makefile +++ b/sbin/decryptcore/Makefile @@ -1,4 +1,3 @@ - PROG= decryptcore CFLAGS+=-DOPENSSL_API_COMPAT=0x10100000L diff --git a/sbin/decryptcore/decryptcore.c b/sbin/decryptcore/decryptcore.c index adeccfac8d47..1e7cf4070e51 100644 --- a/sbin/decryptcore/decryptcore.c +++ b/sbin/decryptcore/decryptcore.c @@ -24,7 +24,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/capsicum.h> #include <sys/endian.h> diff --git a/sbin/devd/Makefile b/sbin/devd/Makefile index f1a5f12ac478..553aecf4ee88 100644 --- a/sbin/devd/Makefile +++ b/sbin/devd/Makefile @@ -1,7 +1,6 @@ - .include <src.opts.mk> -WARNS?= 3 +WARNS?= 6 PACKAGE=devd CONFGROUPS= CONFS DEVD CONFS= devd.conf @@ -11,6 +10,13 @@ DEVDDIR= /etc/devd DEVD+= asus.conf .endif +.if ${MK_AUTOFS} != "no" +CONFGROUPS+= AUTOFS +AUTOFSDIR= ${DEVDDIR} +AUTOFS+= autofs.conf +AUTOFSPACKAGE= autofs +.endif + CONFGROUPS+= DHCLIENT DHCLIENTDIR= ${DEVDDIR} DHCLIENT+= dhclient.conf @@ -26,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} @@ -40,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/autofs.conf b/sbin/devd/autofs.conf new file mode 100644 index 000000000000..2671687ecd83 --- /dev/null +++ b/sbin/devd/autofs.conf @@ -0,0 +1,9 @@ +# +# autofs(4) specific devd events + +# Discard autofs caches, useful for the -media special map. +notify 100 { + match "system" "GEOM"; + match "subsystem" "DEV"; + action "/usr/sbin/automount -c"; +}; 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 05715c18708f..ee38fbb2ccee 100644 --- a/sbin/devd/devd.cc +++ b/sbin/devd/devd.cc @@ -63,7 +63,6 @@ // - devd needs to document the unix domain socket // - devd.conf needs more details on the supported statements. -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/socket.h> #include <sys/stat.h> @@ -154,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); @@ -868,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) { @@ -939,7 +942,7 @@ create_socket(const char *name, int socktype) return (fd); } -static unsigned int max_clients = 10; /* Default, can be overridden on cmdline. */ +static unsigned int max_clients = 50; /* Default, can be overridden on cmdline. */ static unsigned int num_clients; static list<client_t> clients; @@ -1108,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) { @@ -1136,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); @@ -1205,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)); @@ -1323,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) { @@ -1347,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 b/sbin/devd/devd.conf index ea8421e76d61..86faf2f30722 100644 --- a/sbin/devd/devd.conf +++ b/sbin/devd/devd.conf @@ -18,7 +18,7 @@ options { # Setup some shorthand for regex that we use later in the file. #XXX Yes, this is gross -- imp set wifi-driver-regex - "(ath|ath[0-9]+k|bwi|bwn|ipw|iwlwifi|iwi|iwm|iwn|malo|mwl|mt79|otus|\ + "(ath|ath[0-9]+k|bwi|bwn|ipw|iwlwifi|iwi|iwm|iwn|malo|mwl|mt79|mtw|otus|\ ral|rsu|rtw|rtwn|rum|run|uath|upgt|ural|urtw|wpi|wtap|zyd)[0-9]+"; }; @@ -75,29 +75,6 @@ detach 100 { # action "sleep 2 && /usr/sbin/ath3kfw -d $device-name -f /usr/local/etc/ath3k-1.fw"; #}; -# Don't even try to second guess what to do about drivers that don't -# match here. Instead, pass it off to syslog. Commented out for the -# moment, as the pnpinfo variable isn't set in devd yet. Individual -# variables within the bus supplied pnpinfo are set. -nomatch 0 { -# action "logger Unknown device: $pnpinfo $location $bus"; -}; - -# Various logging of unknown devices. -nomatch 10 { - match "bus" "uhub[0-9]+"; - action "logger Unknown USB device: vendor $vendor product $product \ - bus $bus"; -}; - -# Some Cardbus cards don't offer numerical manufacturer/product IDs, just -# show the CIS info there. -nomatch 10 { - match "bus" "cardbus[0-9]+"; - action "logger Unknown Cardbus device: device $device class $class \ - vendor $vendor bus $bus"; -}; - # Notify all users before beginning emergency shutdown when we get # a _CRT or _HOT thermal event and we're going to power down the system # very soon. @@ -159,13 +136,6 @@ notify 0 { action "service postgresql restart"; }; -# Discard autofs caches, useful for the -media special map. -notify 100 { - match "system" "GEOM"; - match "subsystem" "DEV"; - action "/usr/sbin/automount -c"; -}; - # Handle userland coredumps. # This commented out handler makes it possible to run an # automated debugging session after the core dump is generated. diff --git a/sbin/devd/devd.conf.5 b/sbin/devd/devd.conf.5 index c9c421090ab9..8df3e910e076 100644 --- a/sbin/devd/devd.conf.5 +++ b/sbin/devd/devd.conf.5 @@ -38,12 +38,12 @@ .\" ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS .\" SOFTWARE. .\" -.Dd December 1, 2022 +.Dd July 9, 2025 .Dt DEVD.CONF 5 .Os .Sh NAME .Nm devd.conf -.Nd configuration file for +.Nd configuration file format for .Xr devd 8 .Sh DESCRIPTION .Ss General Syntax @@ -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,7 +479,49 @@ 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 : +.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. +$event is one of +.Dq start , +.Dq success , +and +.Dq timed_out +representing the start of a controller reset, the successful completion of a +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" "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. @@ -492,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 @@ -520,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 @@ -562,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. @@ -576,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. @@ -590,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 @@ -610,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 .\" @@ -666,6 +724,26 @@ For example: // part of the previous comment. .Ed .Ss Notes on Variable Expansion +Variables are expanded by preceding them by a +.Dq $ +sign. +Any text immediately following that sign, starting with a letter, a minus sign +.Dq - , +an underscore +.Dq _ , +or an asterisk +.Dq * +is considered an internal variable, and expanded accordingly. +If that variable does not exist, it silently expands to an empty string. +Consequently, if the intention is to pass a +.Xr sh 1 +variable, it must be surrounded by braces to prevent it from being +considered an internal variable. +.Pp +See +.Sx EXAMPLES +for a detailed example. +.Pp To prevent issues with special shell characters, the following happens for each variable .Ic $foo . @@ -683,6 +761,10 @@ The value of the .Ic foo variable is inserted into the buffer with all single quote characters prefixed by a backslash. +.It +A final +.Dq ' +is inserted. .El .Pp See @@ -724,6 +806,14 @@ regardless of the value of the variable. The .Xr devd 8 configuration file. +.It Pa /etc/devd +A directory that +.Nm devd +searches for additional configuration files. +.It Pa /usr/local/etc/devd +A directory that +.Nm devd +searches for additional configuration files. .El .Sh EXAMPLES .Bd -literal @@ -786,6 +876,37 @@ detach 0 { }; .Ed .Pp +The following example illustrates the difference betwen internal and shell variables: +.Bd -literal +attach 20 { + device-name "umodem[0-9]+"; + match "vendor" "0x2047"; + match "product" "0x001(0|3|4)"; + match "interface" "0"; + action "cd /dev; p=$product; dn=$device-name; \\ + un=$(sysctl -n dev.umodem.${dn#umodem}.ttyname); \\ + chmod 666 cua${un}; ln -sf cua${un} mspfet${p#0x}"; +}; +.Ed +.Pp +.Dq product , +and +.Dq device-name +are internal variables. +Their contents are initially assigned to shell variables +.Dq p , +and +.Dq dn , +respectively. +Then, variable +.Dq dn +is used inside a shell command substitution, assigning to shell variable +.Dq un . +Finally, this shell variable is used in two other shell statements, where +it needs to be wrapped in braces in order to prevent it from being +considered in internal variable to +.Nm devd . +.Pp The installed .Pa /etc/devd.conf has many additional examples. diff --git a/sbin/devd/devmatch.conf b/sbin/devd/devmatch.conf index 085338eabca4..a7c27857c0bd 100644 --- a/sbin/devd/devmatch.conf +++ b/sbin/devd/devmatch.conf @@ -1,14 +1,32 @@ +# Implement the run-time component of devmatch by reacting to nomatch events. + # +# Ignore those devices that can't possibly match. When there's neither a +# location, nor a pnpinfo string, we know that there's nothing devmatch can +# match on. When it's only a location, it'd debateable, but for nomatch +# events, we can't disambiguate between the two reliably. # +nomatch 101 { + match "_" " +at +on .*"; +}; # -# Example devd configuration file for automatically -# loading what modules we can based on nomatch -# events. +# Ignore ACPI devices whose _HID is none. These cannot tell us what to load, +# since 'none' is not a valid id. There's no need to call devvmatch for these either. +# +nomatch 101 { + match "_HID" "none"; + match "bus" "acpi[0-9]+"; +}; + # # Generic NOMATCH event +# +# Note: It would be better to have some internal-to-devd action that will do +# what devmatch does without re-parsing loader.hints for each invocation +# nomatch 100 { - action "service devmatch quietstart '?'$_"; + action "service devmatch quietstart $*"; }; # Add the following to devd.conf to prevent this from running: 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/devd/tests/Makefile b/sbin/devd/tests/Makefile index 7a51f488ac68..fba7176d5ae1 100644 --- a/sbin/devd/tests/Makefile +++ b/sbin/devd/tests/Makefile @@ -1,4 +1,3 @@ - ATF_TESTS_C= client_test TEST_METADATA.client_test= required_files="/var/run/devd.pid" TEST_METADATA.client_test+= required_programs="devd" diff --git a/sbin/devd/tests/client_test.c b/sbin/devd/tests/client_test.c index 729c7a2f8bad..fdac3c98b3c6 100644 --- a/sbin/devd/tests/client_test.c +++ b/sbin/devd/tests/client_test.c @@ -22,15 +22,14 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> -#include <stdbool.h> -#include <stdio.h> - #include <sys/param.h> -#include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + #include <atf-c.h> const char create_pat[] = "!system=DEVFS subsystem=CDEV type=CREATE cdev=md"; @@ -134,29 +133,37 @@ ATF_TC_BODY(seqpacket, tc) ATF_TC_WITHOUT_HEAD(stream); ATF_TC_BODY(stream, tc) { + char *event; int s; bool got_create_event = false; bool got_destroy_event = false; - ssize_t len = 0; + size_t len = 0, sz; s = common_setup(SOCK_STREAM, "/var/run/devd.pipe"); + + /* + * Use a large buffer: we're reading from a stream socket so can't rely + * on record boundaries. Instead, we just keep appending to the buffer. + */ + sz = 1024 * 1024; + event = malloc(sz); + ATF_REQUIRE(event != NULL); + /* * Loop until both events are detected on the same or different reads. * There may be extra events due to unrelated system activity. * If we never get both events, then the test will timeout. */ - while (!(got_create_event && got_destroy_event)) { - char event[1024]; + while (!(got_create_event && got_destroy_event) && len < sz - 1) { ssize_t newlen; char *create_pos, *destroy_pos; /* Read 1 less than sizeof(event) to allow space for NULL */ - newlen = read(s, &event[len], sizeof(event) - len - 1); - ATF_REQUIRE(newlen != -1); + newlen = read(s, &event[len], sz - len - 1); + ATF_REQUIRE(newlen > 0); len += newlen; /* NULL terminate the result */ event[len] = '\0'; - printf("%s", event); create_pos = strstr(event, create_pat); if (create_pos != NULL) @@ -166,7 +173,11 @@ ATF_TC_BODY(stream, tc) if (destroy_pos != NULL) got_destroy_event = true; } + printf("%s", event); + if (len >= sz - 1) + atf_tc_fail("Event buffer overflowed"); + free(event); close(s); } diff --git a/sbin/devfs/Makefile b/sbin/devfs/Makefile index dc823abcaf18..0a20cee8241c 100644 --- a/sbin/devfs/Makefile +++ b/sbin/devfs/Makefile @@ -1,4 +1,3 @@ - CONFS= devfs.conf devfs.rules CONFSDIR_devfs.rules= /etc/defaults CONFSMODE_devfs.rules= 600 diff --git a/sbin/devfs/devfs.8 b/sbin/devfs/devfs.8 index af777fe8947f..bbe4825b8bb2 100644 --- a/sbin/devfs/devfs.8 +++ b/sbin/devfs/devfs.8 @@ -23,7 +23,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd December 1, 2020 +.Dd October 18, 2023 .Dt DEVFS 8 .Os .Sh NAME @@ -38,7 +38,7 @@ The .Nm utility provides an interface to manipulate properties of -.Xr devfs 5 +.Xr devfs 4 mounts. .Pp The rules, by default as configured by @@ -64,7 +64,7 @@ The following flags are common to all keywords: Operate on .Ar mount-point , which is expected to be a -.Xr devfs 5 +.Xr devfs 4 mount. If this option is not specified, .Nm @@ -73,7 +73,7 @@ operates on .El .Ss Rule Subsystem The -.Xr devfs 5 +.Xr devfs 4 rule subsystem provides a way for the administrator of a system to control the attributes of DEVFS nodes. .\" XXX devfs node? entry? what? @@ -248,7 +248,7 @@ will return the same information regardless of the mount-point specified with The mount-point is only relevant when changing what its current ruleset is or when using one of the apply commands. .Sh FILES -.Bl -tag -width "Pa /usr/share/examples/etc/devfs.conf" -compact +.Bl -tag -width "Pa /etc/defaults/devfs.rules" -compact .It Pa /etc/defaults/devfs.rules Default .Nm @@ -264,10 +264,6 @@ with the same ruleset number, otherwise the two files are effectively merged. Boot-time .Nm configuration file. -.It Pa /usr/share/examples/etc/devfs.conf -Example boot-time -.Nm -configuration file. .El .Sh EXAMPLES When the system boots, @@ -376,7 +372,7 @@ this feature can be used to copy rulesets: .Xr chmod 1 , .Xr jail 2 , .Xr glob 3 , -.Xr devfs 5 , +.Xr devfs 4 , .Xr devfs.conf 5 , .Xr devfs.rules 5 , .Xr chown 8 , diff --git a/sbin/devfs/devfs.c b/sbin/devfs/devfs.c index 72feed7fb857..ae8954e9d88b 100644 --- a/sbin/devfs/devfs.c +++ b/sbin/devfs/devfs.c @@ -30,7 +30,6 @@ * DEVFS control. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/queue.h> diff --git a/sbin/devfs/devfs.rules b/sbin/devfs/devfs.rules index f85940aaa7dd..c51672815e64 100644 --- a/sbin/devfs/devfs.rules +++ b/sbin/devfs/devfs.rules @@ -1,5 +1,5 @@ # -# The following are some default rules for devfs(5) mounts. +# The following are some default rules for devfs(4) mounts. # The format is very simple. Empty lines and lines beginning # with a hash '#' are ignored. If the hash mark occurs anywhere # other than the beginning of a line, it and any subsequent diff --git a/sbin/devfs/rule.c b/sbin/devfs/rule.c index b9b493ae3742..d07104d1de13 100644 --- a/sbin/devfs/rule.c +++ b/sbin/devfs/rule.c @@ -30,7 +30,6 @@ * Rule subsystem manipulation. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/conf.h> #include <sys/ioctl.h> diff --git a/sbin/devmatch/Makefile b/sbin/devmatch/Makefile index bc0b2a7cba55..20da7231206f 100644 --- a/sbin/devmatch/Makefile +++ b/sbin/devmatch/Makefile @@ -1,4 +1,3 @@ - PACKAGE= devmatch PROG= devmatch MAN= devmatch.8 diff --git a/sbin/devmatch/devmatch.8 b/sbin/devmatch/devmatch.8 index f5e3a5154a32..71bd7365d645 100644 --- a/sbin/devmatch/devmatch.8 +++ b/sbin/devmatch/devmatch.8 @@ -97,7 +97,7 @@ The term PNP is overloaded in .Fx . It means, generically, the identifying data the bus provides about a device. -While this include old ISA PNP identifiers, it also includes the +While this includes old ISA PNP identifiers, it also includes the logical equivalent in USB, PCI, and others. .Pp Many drivers currently lack proper PNP table decorations and need to diff --git a/sbin/devmatch/devmatch.c b/sbin/devmatch/devmatch.c index 2485120c57b6..4a5a300ca313 100644 --- a/sbin/devmatch/devmatch.c +++ b/sbin/devmatch/devmatch.c @@ -23,7 +23,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <ctype.h> #include <devinfo.h> @@ -31,6 +30,7 @@ #include <errno.h> #include <fcntl.h> #include <getopt.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -55,13 +55,13 @@ static struct option longopts[] = { #define DEVMATCH_MAX_HITS 256 -static int all_flag; -static int dump_flag; +static bool all_flag; +static bool dump_flag; static char *linker_hints; static char *nomatch_str; -static int quiet_flag; -static int unbound_flag; -static int verbose_flag; +static bool quiet_flag; +static bool unbound_flag; +static bool verbose_flag; static void *hints; static void *hints_end; @@ -127,6 +127,12 @@ read_linker_hints(void) err(1, "Can't open %s for reading", fn); } + if (len < sizeof(int)) { + warnx("Linker hints file too short."); + free(hints); + hints = NULL; + return; + } if (*(int *)(intptr_t)hints != LINKER_HINTS_VERSION) { warnx("Linker hints version %d doesn't match expected %d.", *(int *)(intptr_t)hints, LINKER_HINTS_VERSION); @@ -407,7 +413,7 @@ search_hints(const char *bus, const char *dev, const char *pnpinfo) else if (!notme) { if (!unbound_flag) { if (all_flag) - printf("%s: %s", *dev ? dev : "unattached", lastmod); + printf("%s: %s\n", *dev ? dev : "unattached", lastmod); else printf("%s\n", lastmod); if (verbose_flag) @@ -446,7 +452,7 @@ find_unmatched(struct devinfo_dev *dev, void *arg) break; if (!(dev->dd_flags & DF_ENABLED)) break; - if (dev->dd_flags & DF_ATTACHED_ONCE) + if (!all_flag && dev->dd_flags & DF_ATTACHED_ONCE) break; parent = devinfo_handle_to_device(dev->dd_parent); bus = strdup(parent->dd_name); @@ -574,10 +580,10 @@ main(int argc, char **argv) longopts, NULL)) != -1) { switch (ch) { case 'a': - all_flag++; + all_flag = true; break; case 'd': - dump_flag++; + dump_flag = true; break; case 'h': linker_hints = optarg; @@ -586,13 +592,13 @@ main(int argc, char **argv) nomatch_str = optarg; break; case 'q': - quiet_flag++; + quiet_flag = true; break; case 'u': - unbound_flag++; + unbound_flag = true; break; case 'v': - verbose_flag++; + verbose_flag = true; break; default: usage(); diff --git a/sbin/dhclient/dhclient.8 b/sbin/dhclient/dhclient.8 index a7328304fa68..7f0b418128ca 100644 --- a/sbin/dhclient/dhclient.8 +++ b/sbin/dhclient/dhclient.8 @@ -36,7 +36,7 @@ .\" see ``http://www.isc.org/isc''. To learn more about Vixie .\" Enterprises, see ``http://www.vix.com''. .\" -.Dd August 4, 2018 +.Dd August 1, 2024 .Dt DHCLIENT 8 .Os .Sh NAME @@ -82,6 +82,10 @@ will revert to running in the background. Specify an alternate location, .Ar file , for the leases file. +.It Fl n +Make +.Nm +not wait for ARP resolution. .It Fl p Ar file Specify an alternate location for the PID file. The default is diff --git a/sbin/dhclient/dhclient.c b/sbin/dhclient/dhclient.c index 8179a5c34209..5d2a7453578b 100644 --- a/sbin/dhclient/dhclient.c +++ b/sbin/dhclient/dhclient.c @@ -90,7 +90,8 @@ cap_channel_t *capsyslog; -time_t cur_time; +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; @@ -120,6 +121,8 @@ struct pidfh *pidfile; */ #define TIME_MAX ((((time_t) 1 << (sizeof(time_t) * CHAR_BIT - 2)) - 1) * 2 + 1) +static struct timespec arp_timeout = { .tv_sec = 0, .tv_nsec = 250 * 1000 * 1000 }; +static const struct timespec zero_timespec = { .tv_sec = 0, .tv_nsec = 0 }; int log_priority; static int no_daemon; static int unknown_ok = 1; @@ -383,7 +386,7 @@ main(int argc, char *argv[]) cap_openlog(capsyslog, getprogname(), LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY); cap_setlogmask(capsyslog, LOG_UPTO(LOG_DEBUG)); - while ((ch = getopt(argc, argv, "bc:dl:p:qu")) != -1) + while ((ch = getopt(argc, argv, "bc:dl:np:qu")) != -1) switch (ch) { case 'b': immediate_daemon = 1; @@ -397,6 +400,9 @@ main(int argc, char *argv[]) case 'l': path_dhclient_db = optarg; break; + case 'n': + arp_timeout = zero_timespec; + break; case 'p': path_dhclient_pidfile = optarg; break; @@ -443,7 +449,8 @@ main(int argc, char *argv[]) log_perror = 0; tzset(); - time(&cur_time); + clock_gettime(CLOCK_MONOTONIC, &time_now); + cur_time = time(NULL); inaddr_broadcast.s_addr = INADDR_BROADCAST; inaddr_any.s_addr = INADDR_ANY; @@ -532,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) @@ -572,7 +579,7 @@ void usage(void) { - fprintf(stderr, "usage: %s [-bdqu] ", getprogname()); + fprintf(stderr, "usage: %s [-bdnqu] ", getprogname()); fprintf(stderr, "[-c conffile] [-l leasefile] interface\n"); exit(1); } @@ -1022,10 +1029,14 @@ dhcpoffer(struct packet *packet) struct interface_info *ip = packet->interface; struct client_lease *lease, *lp; int i; - int arp_timeout_needed, stop_selecting; + struct timespec arp_timeout_needed; + 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 || @@ -1078,9 +1089,10 @@ dhcpoffer(struct packet *packet) /* If the script can't send an ARP request without waiting, we'll be waiting when we do the ARPCHECK, so don't wait now. */ if (script_go()) - arp_timeout_needed = 0; + arp_timeout_needed = zero_timespec; + else - arp_timeout_needed = 2; + arp_timeout_needed = arp_timeout; /* Figure out when we're supposed to stop selecting. */ stop_selecting = @@ -1099,9 +1111,13 @@ dhcpoffer(struct packet *packet) offer would take us past the selection timeout, then don't extend the timeout - just hope for the best. */ + + struct timespec interm_struct; + timespecadd(&time_now, &arp_timeout_needed, &interm_struct); + if (ip->client->offered_leases && - (cur_time + arp_timeout_needed) > stop_selecting) - arp_timeout_needed = 0; + interm_struct.tv_sec >= stop_selecting) + arp_timeout_needed = zero_timespec; /* Put the lease at the end of the list. */ lease->next = NULL; @@ -1115,21 +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. */ - if (stop_selecting - cur_time < arp_timeout_needed) - stop_selecting = cur_time + arp_timeout_needed; - - /* If the selecting interval has expired, go immediately to - state_selecting(). Otherwise, time out into - state_selecting at the select interval. */ - if (stop_selecting <= 0) - state_selecting(ip); - else { - add_timeout(stop_selecting, state_selecting, ip); - cancel_timeout(send_discover, ip); + /* + * 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_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 @@ -2618,6 +2634,9 @@ check_option(struct client_lease *l, int option) case DHO_BOOTFILE_NAME: case DHO_DHCP_USER_CLASS_ID: case DHO_URL: + case DHO_SIP_SERVERS: + case DHO_V_I_VENDOR_CLASS: + case DHO_V_I_VENDOR_OPTS: case DHO_END: return (1); case DHO_CLASSLESS_ROUTES: diff --git a/sbin/dhclient/dhcp.h b/sbin/dhclient/dhcp.h index 300deb464238..02ea42a66079 100644 --- a/sbin/dhclient/dhcp.h +++ b/sbin/dhclient/dhcp.h @@ -172,7 +172,10 @@ struct dhcp_packet { #define DHO_DHCP_USER_CLASS_ID 77 #define DHO_URL 114 #define DHO_DOMAIN_SEARCH 119 +#define DHO_SIP_SERVERS 120 #define DHO_CLASSLESS_ROUTES 121 +#define DHO_V_I_VENDOR_CLASS 124 +#define DHO_V_I_VENDOR_OPTS 125 #define DHO_END 255 /* DHCP message types. */ diff --git a/sbin/dhclient/dhcpd.h b/sbin/dhclient/dhcpd.h index 399b5c1ecf6c..c61564067598 100644 --- a/sbin/dhclient/dhcpd.h +++ b/sbin/dhclient/dhcpd.h @@ -199,7 +199,6 @@ struct client_state { struct interface_info { struct interface_info *next; struct hardware hw_address; - struct in_addr primary_address; char name[IFNAMSIZ]; int rfdesc; int wfdesc; @@ -219,7 +218,7 @@ struct interface_info { struct timeout { struct timeout *next; - time_t when; + struct timespec when; void (*func)(void *); void *what; }; @@ -321,6 +320,7 @@ void reinitialize_interfaces(void); void dispatch(void); void got_one(struct protocol *); void add_timeout(time_t, void (*)(void *), void *); +void add_timeout_timespec(struct timespec, void (*)(void *), void *); void cancel_timeout(void (*)(void *), void *); void add_protocol(const char *, int, void (*)(struct protocol *), void *); void remove_protocol(struct protocol *); @@ -361,7 +361,8 @@ char *piaddr(struct iaddr); extern cap_channel_t *capsyslog; extern const char *path_dhclient_conf; extern char *path_dhclient_db; -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 310f477f8a4f..06bd31e5ec82 100644 --- a/sbin/dhclient/dispatch.c +++ b/sbin/dhclient/dispatch.c @@ -56,6 +56,10 @@ #define assert_aligned(p, align) assert((((uintptr_t)p) & ((align) - 1)) == 0) static struct protocol *protocols; +static const struct timespec timespec_intmax_ms = { + .tv_sec = INT_MAX / 1000, + .tv_nsec = (INT_MAX % 1000) * 1000000 +}; static struct timeout *timeouts; static struct timeout *free_timeouts; static int interfaces_invalidated; @@ -76,7 +80,6 @@ discover_interfaces(struct interface_info *iface) { struct ifaddrs *ifap, *ifa; struct ifreq *tif; - int len = IFNAMSIZ + sizeof(struct sockaddr_storage); if (getifaddrs(&ifap) != 0) error("getifaddrs failed"); @@ -119,7 +122,7 @@ discover_interfaces(struct interface_info *iface) LLADDR(foo), foo->sdl_alen); } if (!iface->ifp) { - if ((tif = calloc(1, len)) == NULL) + if ((tif = calloc(1, sizeof(struct ifreq))) == NULL) error("no space to remember ifp"); strlcpy(tif->ifr_name, ifa->ifa_name, IFNAMSIZ); iface->ifp = tif; @@ -155,7 +158,9 @@ dispatch(void) int count, live_interfaces, i, to_msec, nfds = 0; struct protocol *l; struct pollfd *fds; - time_t howlong; + struct timespec howlong; + + clock_gettime(CLOCK_MONOTONIC, &time_now); for (l = protocols; l; l = l->next) nfds++; @@ -173,7 +178,7 @@ another: if (timeouts) { struct timeout *t; - if (timeouts->when <= cur_time) { + if (timespeccmp(&timeouts->when, &time_now, <=)) { t = timeouts; timeouts = timeouts->next; (*(t->func))(t->what); @@ -188,10 +193,10 @@ another: * int for poll, while not polling with a * negative timeout and blocking indefinitely. */ - howlong = timeouts->when - cur_time; - if (howlong > INT_MAX / 1000) - howlong = INT_MAX / 1000; - to_msec = howlong * 1000; + timespecsub(&timeouts->when, &time_now, &howlong); + if (timespeccmp(&howlong, ×pec_intmax_ms, >)) + howlong = timespec_intmax_ms; + to_msec = howlong.tv_sec * 1000 + howlong.tv_nsec / 1000000; } else to_msec = -1; @@ -218,14 +223,16 @@ another: /* Not likely to be transitory... */ if (count == -1) { if (errno == EAGAIN || errno == EINTR) { - time(&cur_time); + clock_gettime(CLOCK_MONOTONIC, &time_now); + cur_time = time(NULL); continue; } else error("poll: %m"); } /* Get the current time... */ - time(&cur_time); + clock_gettime(CLOCK_MONOTONIC, &time_now); + cur_time = time(NULL); i = 0; for (l = protocols; l; l = l->next) { @@ -356,7 +363,18 @@ active: } void -add_timeout(time_t when, void (*where)(void *), void *what) +add_timeout(time_t when_s, void (*where)(void *), void *what) +{ + 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); +} + +void +add_timeout_timespec(struct timespec when, void (*where)(void *), void *what) { struct timeout *t, *q; @@ -395,7 +413,7 @@ add_timeout(time_t when, void (*where)(void *), void *what) /* Now sort this timeout into the timeout list. */ /* Beginning of list? */ - if (!timeouts || timeouts->when > q->when) { + if (!timeouts || timespeccmp(&timeouts->when, &q->when, >)) { q->next = timeouts; timeouts = q; return; @@ -403,7 +421,7 @@ add_timeout(time_t when, void (*where)(void *), void *what) /* Middle of list? */ for (t = timeouts; t->next; t = t->next) { - if (t->next->when > q->when) { + if (timespeccmp(&t->next->when, &q->when, >)) { q->next = t->next; t->next = q; return; 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/dhclient/tests/Makefile b/sbin/dhclient/tests/Makefile index 7d4359d4b762..2cca8635fa13 100644 --- a/sbin/dhclient/tests/Makefile +++ b/sbin/dhclient/tests/Makefile @@ -1,4 +1,3 @@ - .PATH: ${.CURDIR:H} ATF_TESTS_SH= pcp diff --git a/sbin/dmesg/Makefile b/sbin/dmesg/Makefile index 4214988b7c71..b0bd864fd09d 100644 --- a/sbin/dmesg/Makefile +++ b/sbin/dmesg/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.1 (Berkeley) 6/5/93 - PACKAGE=runtime PROG= dmesg MAN= dmesg.8 diff --git a/sbin/dmesg/dmesg.8 b/sbin/dmesg/dmesg.8 index cbd7d66f9e98..0dca0ff5da89 100644 --- a/sbin/dmesg/dmesg.8 +++ b/sbin/dmesg/dmesg.8 @@ -25,8 +25,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)dmesg.8 8.1 (Berkeley) 6/5/93 -.\" .Dd May 7, 2022 .Dt DMESG 8 .Os @@ -76,7 +74,7 @@ variables control how the kernel timestamps entries in the message buffer: The default value is shown next to each variable. .Bl -tag -width indent .It kern.msgbuf_show_timestamp : No 0 -If set to 0, no timetamps are added. +If set to 0, no timestamps are added. If set to 1, then a 1-second granularity timestamp will be added to most lines in the message buffer. If set to 2, then a microsecond granularity timestamp will be added. diff --git a/sbin/dmesg/dmesg.c b/sbin/dmesg/dmesg.c index 9b744aee7d2e..65005a903154 100644 --- a/sbin/dmesg/dmesg.c +++ b/sbin/dmesg/dmesg.c @@ -29,18 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1991, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static const char sccsid[] = "@(#)dmesg.c 8.1 (Berkeley) 6/5/93"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/msgbuf.h> #include <sys/sysctl.h> diff --git a/sbin/dump/Makefile b/sbin/dump/Makefile index 5cb5b9f27a47..6f0dbf3f478f 100644 --- a/sbin/dump/Makefile +++ b/sbin/dump/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.1 (Berkeley) 6/5/93 - # dump.h header file # itime.c reads /etc/dumpdates # main.c driver diff --git a/sbin/dump/dump.8 b/sbin/dump/dump.8 index 00cfef444c9b..08b3c2e574d7 100644 --- a/sbin/dump/dump.8 +++ b/sbin/dump/dump.8 @@ -26,8 +26,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)dump.8 8.3 (Berkeley) 5/1/95 -.\" .Dd December 28, 2020 .Dt DUMP 8 .Os diff --git a/sbin/dump/dump.h b/sbin/dump/dump.h index bd133fc4c68e..a776c8209d0b 100644 --- a/sbin/dump/dump.h +++ b/sbin/dump/dump.h @@ -27,8 +27,6 @@ * 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. - * - * @(#)dump.h 8.2 (Berkeley) 4/28/95 */ /* @@ -99,7 +97,7 @@ void quit(const char *fmt, ...) __printflike(1, 2); void timeest(void); time_t unctime(char *str); -/* mapping rouintes */ +/* mapping routines */ union dinode; int mapfiles(ino_t maxino, long *tapesize); int mapdirs(ino_t maxino, long *tapesize); diff --git a/sbin/dump/dumprmt.c b/sbin/dump/dumprmt.c index d1769faa3daf..4f61905eb60e 100644 --- a/sbin/dump/dumprmt.c +++ b/sbin/dump/dumprmt.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -#if 0 -static char sccsid[] = "@(#)dumprmt.c 8.3 (Berkeley) 4/28/95"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/mtio.h> #include <sys/socket.h> diff --git a/sbin/dump/itime.c b/sbin/dump/itime.c index cb6d55625e6d..ba6ccaf47f04 100644 --- a/sbin/dump/itime.c +++ b/sbin/dump/itime.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -#if 0 -static char sccsid[] = "@(#)itime.c 8.1 (Berkeley) 6/5/93"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/queue.h> diff --git a/sbin/dump/main.c b/sbin/dump/main.c index 779db5fb4b43..0ff622125fdd 100644 --- a/sbin/dump/main.c +++ b/sbin/dump/main.c @@ -29,20 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1980, 1991, 1993, 1994\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/1/95"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/stat.h> #include <sys/mount.h> diff --git a/sbin/dump/optr.c b/sbin/dump/optr.c index 8eb163516c72..f263f48374ae 100644 --- a/sbin/dump/optr.c +++ b/sbin/dump/optr.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -#if 0 -static char sccsid[] = "@(#)optr.c 8.2 (Berkeley) 1/6/94"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/queue.h> #include <sys/wait.h> diff --git a/sbin/dump/pathnames.h b/sbin/dump/pathnames.h index 3ee966a55ff7..e309643473a7 100644 --- a/sbin/dump/pathnames.h +++ b/sbin/dump/pathnames.h @@ -27,8 +27,6 @@ * 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. - * - * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 */ #include <paths.h> diff --git a/sbin/dump/tape.c b/sbin/dump/tape.c index c123f7fa9404..e448bacf61b6 100644 --- a/sbin/dump/tape.c +++ b/sbin/dump/tape.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -#if 0 -static char sccsid[] = "@(#)tape.c 8.4 (Berkeley) 5/1/95"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/socket.h> #include <sys/wait.h> diff --git a/sbin/dump/traverse.c b/sbin/dump/traverse.c index 08e902667759..d3bb671644e3 100644 --- a/sbin/dump/traverse.c +++ b/sbin/dump/traverse.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -#if 0 -static char sccsid[] = "@(#)traverse.c 8.7 (Berkeley) 6/15/95"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/stat.h> @@ -59,10 +51,6 @@ static const char rcsid[] = #include "dump.h" -union dinode { - struct ufs1_dinode dp1; - struct ufs2_dinode dp2; -}; #define DIP(dp, field) \ ((sblock->fs_magic == FS_UFS1_MAGIC) ? \ (dp)->dp1.field : (dp)->dp2.field) diff --git a/sbin/dump/unctime.c b/sbin/dump/unctime.c index 3e03a65f1a7c..82ee12597c05 100644 --- a/sbin/dump/unctime.c +++ b/sbin/dump/unctime.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -#if 0 -static char sccsid[] = "@(#)unctime.c 8.2 (Berkeley) 6/14/94"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <time.h> /* diff --git a/sbin/dumpfs/Makefile b/sbin/dumpfs/Makefile index d7f768ad4c3d..1e7fa4f150d3 100644 --- a/sbin/dumpfs/Makefile +++ b/sbin/dumpfs/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.1 (Berkeley) 6/5/93 - PACKAGE=ufs PROG= dumpfs WARNS?= 2 diff --git a/sbin/dumpfs/dumpfs.8 b/sbin/dumpfs/dumpfs.8 index 63da8e59aa46..768329315bc3 100644 --- a/sbin/dumpfs/dumpfs.8 +++ b/sbin/dumpfs/dumpfs.8 @@ -25,8 +25,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)dumpfs.8 8.1 (Berkeley) 6/5/93 -.\" .Dd July 2, 2021 .Dt DUMPFS 8 .Os diff --git a/sbin/dumpfs/dumpfs.c b/sbin/dumpfs/dumpfs.c index 739f281feb7f..c4988ded88d8 100644 --- a/sbin/dumpfs/dumpfs.c +++ b/sbin/dumpfs/dumpfs.c @@ -44,20 +44,6 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1983, 1992, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)dumpfs.c 8.5 (Berkeley) 4/29/95"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/time.h> #include <sys/disklabel.h> diff --git a/sbin/dumpon/Makefile b/sbin/dumpon/Makefile index 346951f12b3c..03a72bcba41f 100644 --- a/sbin/dumpon/Makefile +++ b/sbin/dumpon/Makefile @@ -1,4 +1,3 @@ - .include <src.opts.mk> PACKAGE=runtime diff --git a/sbin/dumpon/dumpon.8 b/sbin/dumpon/dumpon.8 index 59d199a2de17..a57dfef7096d 100644 --- a/sbin/dumpon/dumpon.8 +++ b/sbin/dumpon/dumpon.8 @@ -25,8 +25,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" From: @(#)swapon.8 8.1 (Berkeley) 6/5/93 -.\" .Dd April 23, 2020 .Dt DUMPON 8 .Os diff --git a/sbin/dumpon/dumpon.c b/sbin/dumpon/dumpon.c index 81144e33bc71..e6c1634ff6fe 100644 --- a/sbin/dumpon/dumpon.c +++ b/sbin/dumpon/dumpon.c @@ -29,18 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1980, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "From: @(#)swapon.c 8.1 (Berkeley) 6/5/93"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/capsicum.h> #include <sys/disk.h> @@ -82,7 +70,7 @@ static char sccsid[] = "From: @(#)swapon.c 8.1 (Berkeley) 6/5/93"; static int verbose; -static void _Noreturn +static _Noreturn void usage(void) { fprintf(stderr, @@ -475,20 +463,23 @@ main(int argc, char *argv[]) struct diocskerneldump_arg ndconf, *kdap; struct addrinfo hints, *res; const char *dev, *pubkeyfile, *server, *client, *gateway; - int ch, error, fd, cipher; + int ch, error, fd; bool gzip, list, netdump, zstd, insert, rflag; uint8_t ins_idx; +#ifdef HAVE_CRYPTO + int cipher = KERNELDUMP_ENC_NONE; +#endif gzip = list = netdump = zstd = insert = rflag = false; kdap = NULL; pubkeyfile = NULL; server = client = gateway = NULL; ins_idx = KDA_APPEND; - cipher = KERNELDUMP_ENC_NONE; while ((ch = getopt(argc, argv, "C:c:g:i:k:lrs:vZz")) != -1) switch ((char)ch) { case 'C': +#ifdef HAVE_CRYPTO if (strcasecmp(optarg, "chacha") == 0 || strcasecmp(optarg, "chacha20") == 0) cipher = KERNELDUMP_ENC_CHACHA20; @@ -499,6 +490,11 @@ main(int argc, char *argv[]) errx(EX_USAGE, "Unrecognized cipher algorithm " "'%s'", optarg); break; +#else + errx(EX_USAGE, + "Built without crypto support, -C is unhandled."); + break; +#endif case 'c': client = optarg; break; diff --git a/sbin/etherswitchcfg/Makefile b/sbin/etherswitchcfg/Makefile index 12a5c01e9ed7..00f9c3089323 100644 --- a/sbin/etherswitchcfg/Makefile +++ b/sbin/etherswitchcfg/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 5.4 (Berkeley) 6/5/91 - PACKAGE=runtime PROG= etherswitchcfg MAN= etherswitchcfg.8 diff --git a/sbin/fdisk/Makefile b/sbin/fdisk/Makefile index 322dfa33043c..85cea684a883 100644 --- a/sbin/fdisk/Makefile +++ b/sbin/fdisk/Makefile @@ -1,4 +1,3 @@ - PACKAGE=runtime PROG= fdisk SRCS= fdisk.c fdisk_mbr_enc.c diff --git a/sbin/fdisk/fdisk.8 b/sbin/fdisk/fdisk.8 index 3c6a73318271..09933acf57df 100644 --- a/sbin/fdisk/fdisk.8 +++ b/sbin/fdisk/fdisk.8 @@ -15,6 +15,22 @@ .Fl f Ar configfile .Op Fl itv .Op Ar disk +.Sh DEPRECATION NOTICE +.Nm +is deprecated and is not available in +.Fx 15.0 +or later. +Use +.Xr gpart 8 +instead +.Po +with the +.Cm MBR +partitioning scheme +.Pc , +or install the +.Pa freebsd-fdisk +port or package. .Sh PROLOGUE In order for the BIOS to boot the kernel, certain conventions must be adhered to. @@ -456,7 +472,7 @@ downwards to correspond to head and cylinder boundaries): Example: to set slices 1, 2 and 4 to .Fx slices, the first being 2 Gigabytes, the second being 10 Gigabytes and the -forth being the remainder of the disk (again, numbers will be rounded +fourth being the remainder of the disk (again, numbers will be rounded appropriately): .Pp .Dl "p 1 165 63 2G" diff --git a/sbin/fdisk/fdisk.c b/sbin/fdisk/fdisk.c index 6cfa2b510f22..925cc68932d7 100644 --- a/sbin/fdisk/fdisk.c +++ b/sbin/fdisk/fdisk.c @@ -265,6 +265,10 @@ main(int argc, char *argv[]) int partition = -1; struct dos_partition *partp; + fprintf(stderr, + "WARNING: fdisk is deprecated and is not available in FreeBSD 15 or later.\n" + "Please use gpart instead.\n\n"); + while ((c = getopt(argc, argv, "BIab:f:ipqstuv1234")) != -1) switch (c) { case 'B': @@ -380,7 +384,8 @@ main(int argc, char *argv[]) printf("******* Working on device %s *******\n",disk); if (I_flag) { - read_s0(); + if (read_s0()) + warnx("Ignoring bad existing MBR."); reset_boot(); partp = &mboot.parts[0]; partp->dp_typ = DOSPTYP_386BSD; @@ -410,8 +415,10 @@ main(int argc, char *argv[]) else print_params(); - if (read_s0()) + if (read_s0()) { + printf("Will replace existing bad MBR\n"); init_sector0(dos_sectors); + } printf("Media sector size is %d\n", secsize); printf("Warning: BIOS sector numbering starts with sector 1\n"); @@ -1465,7 +1472,6 @@ sanitize_partition(struct dos_partition *partp) * The following choices are considered: * /dev/ad0s1a => /dev/ad0 * /dev/da0a => /dev/da0 - * /dev/vinum/root => /dev/vinum/root * A ".eli" part is removed if it exists (see geli(8)). * A ".journal" ending is removed if it exists (see gjournal(8)). */ @@ -1482,7 +1488,7 @@ get_rootdisk(void) if (statfs("/", &rootfs) == -1) err(1, "statfs(\"/\")"); - if ((rv = regcomp(&re, "^(/dev/[a-z/]+[0-9]*)([sp][0-9]+)?[a-h]?(\\.journal)?$", + if ((rv = regcomp(&re, "^(/dev/[a-z/]+[0-9]+)([sp][0-9]+)?[a-h]?(\\.journal)?$", REG_EXTENDED)) != 0) errx(1, "regcomp() failed (%d)", rv); strlcpy(dev, rootfs.f_mntfromname, sizeof (dev)); diff --git a/sbin/fdisk/fdisk_mbr_enc.c b/sbin/fdisk/fdisk_mbr_enc.c index 6fe99bbba1b3..d621124c7a85 100644 --- a/sbin/fdisk/fdisk_mbr_enc.c +++ b/sbin/fdisk/fdisk_mbr_enc.c @@ -33,7 +33,6 @@ * NB! This file must be usable both in kernel and userland. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/diskmbr.h> #include <sys/endian.h> diff --git a/sbin/ffsinfo/Makefile b/sbin/ffsinfo/Makefile index 043be5743ab1..cc03ac9d08ca 100644 --- a/sbin/ffsinfo/Makefile +++ b/sbin/ffsinfo/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.8 (Berkeley) 6/21/2000 -# # $TSHeader: src/sbin/ffsinfo/Makefile,v 1.3 2000/12/05 19:45:10 tomsoft Exp $ # diff --git a/sbin/ffsinfo/ffsinfo.8 b/sbin/ffsinfo/ffsinfo.8 index 4d76acd0ba0a..2221b2fb63b6 100644 --- a/sbin/ffsinfo/ffsinfo.8 +++ b/sbin/ffsinfo/ffsinfo.8 @@ -36,7 +36,7 @@ .\" .\" $TSHeader: src/sbin/ffsinfo/ffsinfo.8,v 1.3 2000/12/12 19:30:55 tomsoft Exp $ .\" -.Dd September 8, 2000 +.Dd January 23, 2025 .Dt FFSINFO 8 .Os .Sh NAME @@ -120,11 +120,11 @@ to .Pa /var/tmp/ffsinfo with all available information. .Sh SEE ALSO +.Xr ffs 4 , .Xr dumpfs 8 , .Xr fsck 8 , .Xr gpart 8 , .Xr growfs 8 , -.Xr gvinum 8 , .Xr newfs 8 , .Xr tunefs 8 .Sh HISTORY diff --git a/sbin/ffsinfo/ffsinfo.c b/sbin/ffsinfo/ffsinfo.c index 9d447d209ffd..842d5c20bad0 100644 --- a/sbin/ffsinfo/ffsinfo.c +++ b/sbin/ffsinfo/ffsinfo.c @@ -41,18 +41,6 @@ * */ -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz\n\ -Copyright (c) 1980, 1989, 1993 The Regents of the University of California.\n\ -All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - /* ********************************************************** INCLUDES ***** */ #include <sys/param.h> #include <sys/disklabel.h> @@ -198,27 +186,18 @@ main(int argc, char **argv) * No path prefix was given, so try in this order: * /dev/r%s * /dev/%s - * /dev/vinum/r%s - * /dev/vinum/%s. * - * FreeBSD now doesn't distinguish between raw and block + * FreeBSD now doesn't distinguish between raw and block * devices any longer, but it should still work this way. */ - len = strlen(device) + strlen(_PATH_DEV) + 2 + strlen("vinum/"); + len = strlen(device) + strlen(_PATH_DEV) + 2; special = (char *)malloc(len); if (special == NULL) errx(1, "malloc failed"); snprintf(special, len, "%sr%s", _PATH_DEV, device); if (stat(special, &st) == -1) { + /* For now this is the 'last resort' */ snprintf(special, len, "%s%s", _PATH_DEV, device); - if (stat(special, &st) == -1) { - snprintf(special, len, "%svinum/r%s", - _PATH_DEV, device); - if (stat(special, &st) == -1) - /* For now this is the 'last resort' */ - snprintf(special, len, "%svinum/%s", - _PATH_DEV, device); - } } device = special; } 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/fsck.c b/sbin/fsck/fsck.c index 3757ed062ba5..4c24cc9813e1 100644 --- a/sbin/fsck/fsck.c +++ b/sbin/fsck/fsck.c @@ -30,13 +30,10 @@ * 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. - * - * From: @(#)mount.c 8.19 (Berkeley) 4/19/94 * From: $NetBSD: mount.c,v 1.24 1995/11/18 03:34:29 cgd Exp * $NetBSD: fsck.c,v 1.30 2003/08/07 10:04:15 agc Exp $ */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/mount.h> #include <sys/queue.h> diff --git a/sbin/fsck/preen.c b/sbin/fsck/preen.c index 219b2a91e0e2..574826f4d8f0 100644 --- a/sbin/fsck/preen.c +++ b/sbin/fsck/preen.c @@ -31,14 +31,7 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)preen.c 8.5 (Berkeley) 4/28/95"; -#else -__RCSID("$NetBSD: preen.c,v 1.18 1998/07/26 20:02:36 mycroft Exp $"); -#endif -#endif /* not lint */ +/* $NetBSD: preen.c,v 1.18 1998/07/26 20:02:36 mycroft Exp $ */ #include <sys/param.h> #include <sys/stat.h> diff --git a/sbin/fsck_ffs/Makefile b/sbin/fsck_ffs/Makefile index de8913c1ecb3..c6676007db2a 100644 --- a/sbin/fsck_ffs/Makefile +++ b/sbin/fsck_ffs/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.2 (Berkeley) 4/27/95 - PACKAGE=ufs PROG= fsck_ffs LINKS+= ${BINDIR}/fsck_ffs ${BINDIR}/fsck_ufs @@ -7,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/dir.c b/sbin/fsck_ffs/dir.c index 0551ba4521c3..3eb0b63c0988 100644 --- a/sbin/fsck_ffs/dir.c +++ b/sbin/fsck_ffs/dir.c @@ -29,12 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)dir.c 8.8 (Berkeley) 4/28/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/time.h> #include <sys/types.h> @@ -258,7 +252,7 @@ fsck_readdir(struct inodesc *idesc) dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); /* * Only need to check current entry if it is the first in the - * the block, as later entries will have been checked in the + * block, as later entries will have been checked in the * previous call to this function. */ if (idesc->id_loc % DIRBLKSIZ != 0 || dircheck(idesc, bp, dp) != 0) { diff --git a/sbin/fsck_ffs/ea.c b/sbin/fsck_ffs/ea.c index 8e5e8ebe8aa0..f6ebc5c072fc 100644 --- a/sbin/fsck_ffs/ea.c +++ b/sbin/fsck_ffs/ea.c @@ -35,7 +35,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/time.h> #include <sys/stdint.h> diff --git a/sbin/fsck_ffs/fsck.h b/sbin/fsck_ffs/fsck.h index 1864ea19f4ca..aaf02850f29a 100644 --- a/sbin/fsck_ffs/fsck.h +++ b/sbin/fsck_ffs/fsck.h @@ -57,8 +57,6 @@ * 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. - * - * @(#)fsck.h 8.4 (Berkeley) 5/9/95 */ #ifndef _FSCK_H_ @@ -67,6 +65,7 @@ #include <unistd.h> #include <stdlib.h> #include <stdio.h> +#include <libufs.h> #include <sys/queue.h> @@ -76,10 +75,6 @@ #define INOBUFSIZE 64*1024 /* size of buffer to read inodes in pass1 */ #define ZEROBUFSIZE (dev_bsize * 128) /* size of zero buffer used by -Z */ -union dinode { - struct ufs1_dinode dp1; - struct ufs2_dinode dp2; -}; #define DIP(dp, field) \ ((sblock.fs_magic == FS_UFS1_MAGIC) ? \ (dp)->dp1.field : (dp)->dp2.field) @@ -158,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 */ @@ -376,7 +371,6 @@ extern long secsize; /* actual disk sector size */ extern char skipclean; /* skip clean file systems if preening */ extern int snapcnt; /* number of active snapshots */ extern struct inode snaplist[FSMAXSNAP + 1]; /* list of active snapshots */ -extern char snapname[BUFSIZ]; /* when doing snapshots, the name of the file */ extern int sujrecovery; /* 1 => doing check using the journal */ extern int surrender; /* Give up if reads fail */ extern char usedsoftdep; /* just fix soft dependency inconsistencies */ @@ -425,6 +419,20 @@ Malloc(size_t size) break; return (retval); } +/* + * Allocate a block of memory to be used as an I/O buffer. + * Ensure that the buffer is aligned to the I/O subsystem requirements. + */ +static inline void* +Balloc(size_t size) +{ + void *retval; + + while ((retval = aligned_alloc(LIBUFS_BUFALIGN, size)) == NULL) + if (flushentry() == 0) + break; + return (retval); +} /* * Wrapper for calloc() that flushes the cylinder group cache to try diff --git a/sbin/fsck_ffs/fsck_ffs.8 b/sbin/fsck_ffs/fsck_ffs.8 index f100686e70e8..8df5e684b963 100644 --- a/sbin/fsck_ffs/fsck_ffs.8 +++ b/sbin/fsck_ffs/fsck_ffs.8 @@ -26,9 +26,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)fsck.8 8.4 (Berkeley) 5/9/95 -.\" -.Dd May 3, 2019 +.Dd November 17, 2023 .Dt FSCK_FFS 8 .Os .Sh NAME @@ -423,6 +421,7 @@ are fully enumerated and explained in Appendix A of .%T "Fsck \- The UNIX File System Check Program" .Re .Sh SEE ALSO +.Xr ffs 4 , .Xr fs 5 , .Xr fstab 5 , .Xr fsck 8 , diff --git a/sbin/fsck_ffs/fsutil.c b/sbin/fsck_ffs/fsutil.c index 5cf17e22fe80..2acf825b159f 100644 --- a/sbin/fsck_ffs/fsutil.c +++ b/sbin/fsck_ffs/fsutil.c @@ -29,12 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)utilities.c 8.6 (Berkeley) 5/19/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/time.h> #include <sys/types.h> @@ -58,7 +52,6 @@ static const char sccsid[] = "@(#)utilities.c 8.6 (Berkeley) 5/19/95"; #include <stdlib.h> #include <time.h> #include <unistd.h> -#include <libufs.h> #include "fsck.h" @@ -189,7 +182,7 @@ bufinit(void) initbarea(&failedbuf, BT_UNKNOWN); failedbuf.b_errs = -1; failedbuf.b_un.b_buf = NULL; - if ((cgblk.b_un.b_buf = Malloc((unsigned int)sblock.fs_bsize)) == NULL) + if ((cgblk.b_un.b_buf = Balloc((unsigned int)sblock.fs_bsize)) == NULL) errx(EEXIT, "Initial malloc(%d) failed", sblock.fs_bsize); initbarea(&cgblk, BT_CYLGRP); numbufs = cachelookups = cachereads = 0; @@ -211,7 +204,7 @@ allocbuf(const char *failreason) char *bufp; bp = (struct bufarea *)Malloc(sizeof(struct bufarea)); - bufp = Malloc((unsigned int)sblock.fs_bsize); + bufp = Balloc((unsigned int)sblock.fs_bsize); if (bp == NULL || bufp == NULL) { errx(EEXIT, "%s", failreason); /* NOTREACHED */ @@ -241,7 +234,7 @@ cglookup(int cg) if ((unsigned) cg >= sblock.fs_ncg) errx(EEXIT, "cglookup: out of range cylinder group %d", cg); if (cgbufs == NULL) { - cgbufs = calloc(sblock.fs_ncg, sizeof(struct bufarea)); + cgbufs = Calloc(sblock.fs_ncg, sizeof(struct bufarea)); if (cgbufs == NULL) errx(EEXIT, "Cannot allocate cylinder group buffers"); } @@ -250,7 +243,7 @@ cglookup(int cg) return (cgbp); cgp = NULL; if (flushtries == 0) - cgp = Malloc((unsigned int)sblock.fs_cgsize); + cgp = Balloc((unsigned int)sblock.fs_cgsize); if (cgp == NULL) { if (sujrecovery) errx(EEXIT,"Ran out of memory during journal recovery"); @@ -616,7 +609,6 @@ ckfini(int markclean) int ofsmodified, cnt, cg; if (bkgrdflag) { - unlink(snapname); if ((!(sblock.fs_flags & FS_UNCLEAN)) != markclean) { cmd.value = FS_UNCLEAN; cmd.size = markclean ? -1 : 1; @@ -967,7 +959,7 @@ blzero(int fd, ufs2_daddr_t blk, long size) if (fd < 0) return; if (zero == NULL) { - zero = calloc(ZEROBUFSIZE, 1); + zero = Balloc(ZEROBUFSIZE); if (zero == NULL) errx(EEXIT, "cannot allocate buffer pool"); } @@ -1025,7 +1017,6 @@ check_cgmagic(int cg, struct bufarea *cgbp) CHK(cgp->cg_ndblk, >, sblock.fs_fpg, "%jd"); if (sblock.fs_magic == FS_UFS1_MAGIC) { CHK(cgp->cg_old_niblk, !=, sblock.fs_ipg, "%jd"); - CHK(cgp->cg_old_ncyl, >, sblock.fs_old_cpg, "%jd"); } else if (sblock.fs_magic == FS_UFS2_MAGIC) { CHK(cgp->cg_niblk, !=, sblock.fs_ipg, "%jd"); CHK(cgp->cg_initediblk, >, sblock.fs_ipg, "%jd"); @@ -1042,7 +1033,6 @@ check_cgmagic(int cg, struct bufarea *cgbp) } else if (sblock.fs_magic == FS_UFS1_MAGIC) { CHK(cgp->cg_niblk, !=, 0, "%jd"); CHK(cgp->cg_initediblk, !=, 0, "%jd"); - CHK(cgp->cg_old_ncyl, !=, sblock.fs_old_cpg, "%jd"); CHK(cgp->cg_old_niblk, !=, sblock.fs_ipg, "%jd"); CHK(cgp->cg_old_btotoff, !=, start, "%jd"); CHK(cgp->cg_old_boff, !=, cgp->cg_old_btotoff + diff --git a/sbin/fsck_ffs/globs.c b/sbin/fsck_ffs/globs.c index 038fc3475742..1f04311e2e5c 100644 --- a/sbin/fsck_ffs/globs.c +++ b/sbin/fsck_ffs/globs.c @@ -29,18 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1980, 1986, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/14/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #include <sys/param.h> #include <ufs/ufs/dinode.h> #include <ufs/ffs/fs.h> @@ -71,7 +59,6 @@ int freefiles[MIBSIZE]; /* MIB cmd to free a set of files */ int freedirs[MIBSIZE]; /* MIB cmd to free a set of directories */ int freeblks[MIBSIZE]; /* MIB cmd to free a set of data blocks */ struct fsck_cmd cmd; /* sysctl file system update commands */ -char snapname[BUFSIZ]; /* when doing snapshots, the name of the file */ char *cdevname; /* name of device being checked */ long dev_bsize; /* computed value of DEV_BSIZE */ long secsize; /* actual disk sector size */ @@ -144,7 +131,6 @@ fsckinit(void) bzero(freedirs, sizeof(int) * MIBSIZE); bzero(freeblks, sizeof(int) * MIBSIZE); bzero(&cmd, sizeof(struct fsck_cmd)); - bzero(snapname, sizeof(char) * BUFSIZ); cdevname = NULL; dev_bsize = 0; secsize = 0; diff --git a/sbin/fsck_ffs/inode.c b/sbin/fsck_ffs/inode.c index 3db8a5e5c23d..b30e3aa5068b 100644 --- a/sbin/fsck_ffs/inode.c +++ b/sbin/fsck_ffs/inode.c @@ -29,12 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)inode.c 8.8 (Berkeley) 4/28/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/stat.h> #include <sys/stdint.h> @@ -48,11 +42,11 @@ static const char sccsid[] = "@(#)inode.c 8.8 (Berkeley) 4/28/95"; #include <pwd.h> #include <string.h> #include <time.h> -#include <libufs.h> #include "fsck.h" struct bufarea *icachebp; /* inode cache buffer */ +static time_t now; /* current time of day */ static int iblock(struct inodesc *, off_t isize, int type); static ufs2_daddr_t indir_blkatoff(ufs2_daddr_t, ino_t, ufs_lbn_t, ufs_lbn_t, @@ -436,6 +430,7 @@ void ginode(ino_t inumber, struct inode *ip) { ufs2_daddr_t iblk; + union dinodep dpp; struct ufs2_dinode *dp; if (inumber < UFS_ROOTINO || inumber >= maxino) @@ -473,11 +468,14 @@ ginode(ino_t inumber, struct inode *ip) if (sblock.fs_magic == FS_UFS1_MAGIC) { ip->i_dp = (union dinode *) &ip->i_bp->b_un.b_dinode1[inumber - ip->i_bp->b_index]; + dpp.dp1 = (struct ufs1_dinode *)ip->i_dp; + if (ffs_oldfscompat_inode_read(&sblock, dpp, now)) + inodirty(ip); return; } ip->i_dp = (union dinode *) &ip->i_bp->b_un.b_dinode2[inumber - ip->i_bp->b_index]; - dp = (struct ufs2_dinode *)ip->i_dp; + dpp.dp2 = dp = (struct ufs2_dinode *)ip->i_dp; /* Do not check hash of inodes being created */ if (dp->di_mode != 0 && ffs_verify_dinode_ckhash(&sblock, dp)) { pwarn("INODE CHECK-HASH FAILED"); @@ -489,6 +487,8 @@ ginode(ino_t inumber, struct inode *ip) inodirty(ip); } } + if (ffs_oldfscompat_inode_read(&sblock, dpp, now)) + inodirty(ip); } /* @@ -527,6 +527,7 @@ getnextinode(ino_t inumber, int rebuiltcg) mode_t mode; ufs2_daddr_t ndb, blk; union dinode *dp; + union dinodep dpp; struct inode ip; static caddr_t nextinop; @@ -557,10 +558,13 @@ getnextinode(ino_t inumber, int rebuiltcg) nextinop = inobuf.b_un.b_buf; } dp = (union dinode *)nextinop; - if (sblock.fs_magic == FS_UFS1_MAGIC) + if (sblock.fs_magic == FS_UFS1_MAGIC) { nextinop += sizeof(struct ufs1_dinode); - else + dpp.dp1 = (struct ufs1_dinode *)dp; + } else { nextinop += sizeof(struct ufs2_dinode); + dpp.dp2 = (struct ufs2_dinode *)dp; + } if ((ckhashadd & CK_INODE) != 0) { ffs_update_dinode_ckhash(&sblock, (struct ufs2_dinode *)dp); dirty(&inobuf); @@ -579,6 +583,8 @@ getnextinode(ino_t inumber, int rebuiltcg) dirty(&inobuf); } } + if (ffs_oldfscompat_inode_read(&sblock, dpp, now)) + dirty(&inobuf); if (rebuiltcg && (char *)dp == inobuf.b_un.b_buf) { /* * Try to determine if we have reached the end of the @@ -632,8 +638,19 @@ getnextinode(ino_t inumber, int rebuiltcg) void setinodebuf(int cg, ino_t inosused) { + struct timespec time; ino_t inum; + /* + * Get the current value of the present time. + * This will happen before each cylinder group is scanned. + * If for some reason getting the time fails, we will use + * the last time that the superblock was updated. + */ + if (clock_gettime(CLOCK_REALTIME_FAST, &time) == 0) + now = time.tv_sec; + else + now = sblock.fs_time; inum = cg * sblock.fs_ipg; lastvalidinum = inum + inosused - 1; nextinum = inum; @@ -646,7 +663,7 @@ setinodebuf(int cg, ino_t inosused) inobufsize = blkroundup(&sblock, MAX(INOBUFSIZE, sblock.fs_bsize)); initbarea(&inobuf, BT_INODES); - if ((inobuf.b_un.b_buf = Malloc((unsigned)inobufsize)) == NULL) + if ((inobuf.b_un.b_buf = Balloc((unsigned)inobufsize)) == NULL) errx(EEXIT, "cannot allocate space for inode buffer"); } fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ? diff --git a/sbin/fsck_ffs/main.c b/sbin/fsck_ffs/main.c index 58e184f3581f..aabab489573e 100644 --- a/sbin/fsck_ffs/main.c +++ b/sbin/fsck_ffs/main.c @@ -29,18 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1980, 1986, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/14/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #define _WANT_P_OSREL #include <sys/param.h> #include <sys/file.h> @@ -59,7 +47,6 @@ static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/14/95"; #include <fstab.h> #include <grp.h> #include <inttypes.h> -#include <libufs.h> #include <mntopts.h> #include <paths.h> #include <stdint.h> @@ -68,7 +55,8 @@ static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/14/95"; #include "fsck.h" -static int restarts; +static int restarts; +static char snapname[BUFSIZ]; /* when doing snapshots, the name of the file */ static void usage(void) __dead2; static intmax_t argtoimax(int flag, const char *req, const char *str, int base); @@ -664,8 +652,7 @@ setup_bkgrdchk(struct statfs *mntp, int sbreadfailed, char **filesys) "SUPPORT\n"); } /* Find or create the snapshot directory */ - snprintf(snapname, sizeof snapname, "%s/.snap", - mntp->f_mntonname); + snprintf(snapname, sizeof snapname, "%s/.snap", mntp->f_mntonname); if (stat(snapname, &snapdir) < 0) { if (errno != ENOENT) { pwarn("CANNOT FIND SNAPSHOT DIRECTORY %s: %s, CANNOT " @@ -713,6 +700,8 @@ setup_bkgrdchk(struct statfs *mntp, int sbreadfailed, char **filesys) "BACKGROUND\n", snapname, strerror(errno)); return (0); } + /* Immediately unlink snapshot so that it will be deleted when closed */ + unlink(snapname); free(sblock.fs_csp); free(sblock.fs_si); if (readsb() == 0) { diff --git a/sbin/fsck_ffs/pass1.c b/sbin/fsck_ffs/pass1.c index bed9d547f637..f4f97a38097b 100644 --- a/sbin/fsck_ffs/pass1.c +++ b/sbin/fsck_ffs/pass1.c @@ -29,12 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)pass1.c 8.6 (Berkeley) 4/28/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/stat.h> #include <sys/sysctl.h> diff --git a/sbin/fsck_ffs/pass1b.c b/sbin/fsck_ffs/pass1b.c index c1af6158f92c..3fb9cab89078 100644 --- a/sbin/fsck_ffs/pass1b.c +++ b/sbin/fsck_ffs/pass1b.c @@ -29,12 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)pass1b.c 8.4 (Berkeley) 4/28/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #include <sys/param.h> #include <ufs/ufs/dinode.h> diff --git a/sbin/fsck_ffs/pass2.c b/sbin/fsck_ffs/pass2.c index 977ee1b1c61e..1755a1295a9e 100644 --- a/sbin/fsck_ffs/pass2.c +++ b/sbin/fsck_ffs/pass2.c @@ -29,12 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)pass2.c 8.9 (Berkeley) 4/28/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/sysctl.h> diff --git a/sbin/fsck_ffs/pass3.c b/sbin/fsck_ffs/pass3.c index 9a01bb02ccc8..853f5d62382f 100644 --- a/sbin/fsck_ffs/pass3.c +++ b/sbin/fsck_ffs/pass3.c @@ -29,12 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)pass3.c 8.2 (Berkeley) 4/27/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #include <sys/param.h> #include <ufs/ufs/dinode.h> diff --git a/sbin/fsck_ffs/pass4.c b/sbin/fsck_ffs/pass4.c index b4cf59bc97dc..a261051c432a 100644 --- a/sbin/fsck_ffs/pass4.c +++ b/sbin/fsck_ffs/pass4.c @@ -29,12 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)pass4.c 8.4 (Berkeley) 4/28/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/stat.h> diff --git a/sbin/fsck_ffs/pass5.c b/sbin/fsck_ffs/pass5.c index 58de6791903f..528a6956c4a1 100644 --- a/sbin/fsck_ffs/pass5.c +++ b/sbin/fsck_ffs/pass5.c @@ -29,12 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)pass5.c 8.9 (Berkeley) 4/28/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/sysctl.h> @@ -45,7 +39,6 @@ static const char sccsid[] = "@(#)pass5.c 8.9 (Berkeley) 4/28/95"; #include <inttypes.h> #include <limits.h> #include <string.h> -#include <libufs.h> #include "fsck.h" diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c index 49e89f9221b0..f10f02d159c3 100644 --- a/sbin/fsck_ffs/setup.c +++ b/sbin/fsck_ffs/setup.c @@ -29,12 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)setup.c 8.10 (Berkeley) 5/9/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/disk.h> #include <sys/stat.h> @@ -52,7 +46,6 @@ static const char sccsid[] = "@(#)setup.c 8.10 (Berkeley) 5/9/95"; #include <limits.h> #include <stdint.h> #include <string.h> -#include <libufs.h> #include "fsck.h" @@ -214,7 +207,7 @@ setup(char *dev) sbdirty(); } if (snapcnt > 0 && copybuf == NULL) { - copybuf = Malloc(sblock.fs_bsize); + copybuf = Balloc(sblock.fs_bsize); if (copybuf == NULL) errx(EEXIT, "cannot allocate space for snapshot " "copy buffer"); @@ -299,6 +292,8 @@ checksnapinfo(struct inode *snapip) size = fragroundup(fs, DIP(snapip->i_dp, di_size) - lblktosize(fs, lbn)); bp = getdatablk(idesc.id_parent, size, BT_DATA); + if (bp->b_errs != 0) + return (0); snapblklist = (daddr_t *)bp->b_un.b_buf; /* * snapblklist[0] is the size of the list @@ -378,14 +373,14 @@ openfilesys(char *dev) if ((statb.st_mode & S_IFMT) != S_IFCHR && (statb.st_mode & S_IFMT) != S_IFBLK) { if (bkgrdflag != 0 && (statb.st_flags & SF_SNAPSHOT) == 0) { - pfatal("BACKGROUND FSCK LACKS A SNAPSHOT\n"); - exit(EEXIT); + pwarn("BACKGROUND FSCK LACKS A SNAPSHOT\n"); + return (0); } if (bkgrdflag != 0) { cursnapshot = statb.st_ino; } else { - pfatal("%s IS NOT A DISK DEVICE\n", dev); - if (reply("CONTINUE") == 0) + pwarn("%s IS NOT A DISK DEVICE\n", dev); + if (preen || reply("CONTINUE") == 0) return (0); } } @@ -499,7 +494,7 @@ sblock_init(void) fsmodified = 0; lfdir = 0; initbarea(&sblk, BT_SUPERBLK); - sblk.b_un.b_buf = Malloc(SBLOCKSIZE); + sblk.b_un.b_buf = Balloc(SBLOCKSIZE); if (sblk.b_un.b_buf == NULL) errx(EEXIT, "cannot allocate space for superblock"); dev_bsize = secsize = DEV_BSIZE; @@ -528,7 +523,7 @@ calcsb(char *dev, int devfd, struct fs *fs) */ if (ioctl(devfd, DIOCGSECTORSIZE, &secsize) == -1) return (0); - fsrbuf = Malloc(secsize); + fsrbuf = Balloc(secsize); if (fsrbuf == NULL) errx(EEXIT, "calcsb: cannot allocate recovery buffer"); if (blread(devfd, fsrbuf, @@ -571,7 +566,7 @@ chkrecovery(int devfd) rdsize = sblock.fs_fsize; if (ioctl(devfd, DIOCGSECTORSIZE, &secsize) == -1 || rdsize % secsize != 0 || - (fsrbuf = Malloc(rdsize)) == NULL || + (fsrbuf = Balloc(rdsize)) == NULL || blread(devfd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize, rdsize) != 0) { free(fsrbuf); @@ -610,7 +605,7 @@ saverecovery(int readfd, int writefd) if (sblock.fs_magic != FS_UFS2_MAGIC || ioctl(readfd, DIOCGSECTORSIZE, &secsize) == -1 || rdsize % secsize != 0 || - (fsrbuf = Malloc(rdsize)) == NULL || + (fsrbuf = Balloc(rdsize)) == NULL || blread(readfd, fsrbuf, (SBLOCK_UFS2 - rdsize) / dev_bsize, rdsize) != 0) { printf("RECOVERY DATA COULD NOT BE CREATED\n"); diff --git a/sbin/fsck_ffs/suj.c b/sbin/fsck_ffs/suj.c index c66b605bd69d..73be60c5e5b4 100644 --- a/sbin/fsck_ffs/suj.c +++ b/sbin/fsck_ffs/suj.c @@ -26,7 +26,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/disk.h> #include <sys/disklabel.h> @@ -47,7 +46,6 @@ #include <stdio.h> #include <stdlib.h> #include <stdint.h> -#include <libufs.h> #include <string.h> #include <strings.h> #include <sysexits.h> @@ -2274,7 +2272,7 @@ suj_add_block(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags) static void suj_read(void) { - uint8_t block[1 * 1024 * 1024]; + uint8_t block[1 * 1024 * 1024] __aligned(LIBUFS_BUFALIGN); struct suj_seg *seg; struct jsegrec *recn; struct jsegrec *rec; diff --git a/sbin/fsck_ffs/utilities.c b/sbin/fsck_ffs/utilities.c index 45bbcc9d6612..56655089b9d6 100644 --- a/sbin/fsck_ffs/utilities.c +++ b/sbin/fsck_ffs/utilities.c @@ -29,12 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)utilities.c 8.6 (Berkeley) 5/19/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/types.h> #include <sys/stat.h> diff --git a/sbin/fsck_msdosfs/boot.c b/sbin/fsck_msdosfs/boot.c index 3d1657ad66f3..f91609470ad7 100644 --- a/sbin/fsck_msdosfs/boot.c +++ b/sbin/fsck_msdosfs/boot.c @@ -29,8 +29,6 @@ #include <sys/cdefs.h> #ifndef lint __RCSID("$NetBSD: boot.c,v 1.22 2020/01/11 16:29:07 christos Exp $"); -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include <sys/param.h> diff --git a/sbin/fsck_msdosfs/check.c b/sbin/fsck_msdosfs/check.c index 654ceeb9c5ca..f672a2ac515c 100644 --- a/sbin/fsck_msdosfs/check.c +++ b/sbin/fsck_msdosfs/check.c @@ -29,8 +29,6 @@ #include <sys/cdefs.h> #ifndef lint __RCSID("$NetBSD: check.c,v 1.14 2006/06/05 16:51:18 christos Exp $"); -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #ifdef HAVE_LIBUTIL_H diff --git a/sbin/fsck_msdosfs/dir.c b/sbin/fsck_msdosfs/dir.c index 02fe07880e0e..19516d882e2a 100644 --- a/sbin/fsck_msdosfs/dir.c +++ b/sbin/fsck_msdosfs/dir.c @@ -32,8 +32,6 @@ #include <sys/cdefs.h> #ifndef lint __RCSID("$NetBSD: dir.c,v 1.20 2006/06/05 16:51:18 christos Exp $"); -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include <assert.h> @@ -997,7 +995,7 @@ readDosDirSection(struct fat_descriptor *fat, struct dosDirEntry *dir) n->next = pendingDirectories; n->dir = d; pendingDirectories = n; - } else { + } else if (!(mod & FSERROR)) { mod |= k = checksize(fat, p, &dirent); if (k & FSDIRMOD) mod |= THISMOD; diff --git a/sbin/fsck_msdosfs/fat.c b/sbin/fsck_msdosfs/fat.c index e35e2f27d305..567bfcd428cb 100644 --- a/sbin/fsck_msdosfs/fat.c +++ b/sbin/fsck_msdosfs/fat.c @@ -30,8 +30,6 @@ #include <sys/cdefs.h> #ifndef lint __RCSID("$NetBSD: fat.c,v 1.18 2006/06/05 16:51:18 christos Exp $"); -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include <sys/endian.h> diff --git a/sbin/fsck_msdosfs/fsck_msdosfs.8 b/sbin/fsck_msdosfs/fsck_msdosfs.8 index 963818ea8bd7..7c90c8b11b5d 100644 --- a/sbin/fsck_msdosfs/fsck_msdosfs.8 +++ b/sbin/fsck_msdosfs/fsck_msdosfs.8 @@ -23,7 +23,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 January 6, 2020 +.Dd December 18, 2024 .Dt FSCK_MSDOSFS 8 .Os .Sh NAME @@ -32,10 +32,10 @@ .Sh SYNOPSIS .Nm .Fl p -.Op Fl Cf +.Op Fl BCf .Ar filesystem ... .Nm -.Op Fl CMny +.Op Fl BCMny .Ar filesystem ... .Sh DESCRIPTION The @@ -71,10 +71,14 @@ making any changes. .Pp The options are as follows: .Bl -tag -width indent +.It Fl B +Ignored for +.Xr fsck 8 +compatibility. .It Fl C -Compatibility with the corresponding +Ignored for .Xr fsck 8 -option (skip check if clean), defined to no-op. +compatibility. .It Fl F Compatibility with the wrapper .Xr fsck 8 @@ -121,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/fsck_msdosfs/main.c b/sbin/fsck_msdosfs/main.c index de54cd18eae7..49a811964ab9 100644 --- a/sbin/fsck_msdosfs/main.c +++ b/sbin/fsck_msdosfs/main.c @@ -29,8 +29,6 @@ #include <sys/cdefs.h> #ifndef lint __RCSID("$NetBSD: main.c,v 1.10 1997/10/01 02:18:14 enami Exp $"); -static const char rcsid[] = - "$FreeBSD$"; #endif /* not lint */ #include <stdlib.h> @@ -70,9 +68,10 @@ main(int argc, char **argv) skipclean = 1; allow_mmap = 1; - while ((ch = getopt(argc, argv, "CfFnpyM")) != -1) { + while ((ch = getopt(argc, argv, "BCfFnpyM")) != -1) { switch (ch) { - case 'C': /* for fsck_ffs compatibility */ + case 'B': /* for fsck_ffs compatibility */ + case 'C': break; case 'f': skipclean = 0; diff --git a/sbin/fsdb/Makefile b/sbin/fsdb/Makefile index 5a71210dc365..28016b4ccdfc 100644 --- a/sbin/fsdb/Makefile +++ b/sbin/fsdb/Makefile @@ -1,5 +1,4 @@ # $NetBSD: Makefile,v 1.1.1.1 1995/10/08 23:08:36 thorpej Exp $ -# @(#)Makefile 8.1 (Berkeley) 6/5/93 PACKAGE=ufs PROG= fsdb diff --git a/sbin/fsdb/fsdb.c b/sbin/fsdb/fsdb.c index 2d8c75cce5fe..48526ad4044b 100644 --- a/sbin/fsdb/fsdb.c +++ b/sbin/fsdb/fsdb.c @@ -30,11 +30,6 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <ctype.h> #include <err.h> diff --git a/sbin/fsdb/fsdbutil.c b/sbin/fsdb/fsdbutil.c index c8a3a8a525e3..737dabba643f 100644 --- a/sbin/fsdb/fsdbutil.c +++ b/sbin/fsdb/fsdbutil.c @@ -30,11 +30,6 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <ctype.h> #include <err.h> diff --git a/sbin/fsirand/fsirand.c b/sbin/fsirand/fsirand.c index cd60093e3642..2a5eb0c5136d 100644 --- a/sbin/fsirand/fsirand.c +++ b/sbin/fsirand/fsirand.c @@ -32,11 +32,6 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/resource.h> diff --git a/sbin/gbde/Makefile b/sbin/gbde/Makefile deleted file mode 100644 index 8c84781fc4ed..000000000000 --- a/sbin/gbde/Makefile +++ /dev/null @@ -1,31 +0,0 @@ - -PACKAGE=geom -PROG= gbde -SRCS= gbde.c template.c -SRCS+= rijndael-alg-fst.c -SRCS+= rijndael-api-fst.c -SRCS+= g_bde_lock.c - -# rijndael-fst.c does evil casting things which can results in warnings, -# the test-vectors check out however, so it works right. -NO_WCAST_ALIGN= -NO_WMISSING_VARIABLE_DECLARATIONS= - -CFLAGS+= -I${SRCTOP}/sys -.PATH: ${SRCTOP}/sys/geom/bde \ - ${SRCTOP}/sys/crypto/rijndael \ - ${SRCTOP}/sys/crypto/sha2 - -CLEANFILES+= template.c - -MAN= gbde.8 -LIBADD= md util geom - -template.c: template.txt - file2c 'const char template[] = {' ',0};' \ - < ${.CURDIR}/template.txt > template.c - -test: ${PROG} - sh ${.CURDIR}/test.sh ${.CURDIR} - -.include <bsd.prog.mk> diff --git a/sbin/gbde/gbde.8 b/sbin/gbde/gbde.8 deleted file mode 100644 index 1f3d41017307..000000000000 --- a/sbin/gbde/gbde.8 +++ /dev/null @@ -1,271 +0,0 @@ -.\" -.\" Copyright (c) 2002 Poul-Henning Kamp -.\" Copyright (c) 2002 Networks Associates Technology, Inc. -.\" All rights reserved. -.\" -.\" This software was developed for the FreeBSD Project by Poul-Henning Kamp -.\" and NAI Labs, the Security Research Division of Network Associates, Inc. -.\" under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the -.\" DARPA CHATS research program. -.\" -.\" 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. -.\" -.Dd October 3, 2016 -.Dt GBDE 8 -.Os -.Sh NAME -.Nm gbde -.Nd operation and management utility for Geom Based Disk Encryption -.Sh SYNOPSIS -.Nm -.Cm attach -.Ar destination -.Op Fl k Ar keyfile -.Op Fl l Ar lockfile -.Op Fl p Ar pass-phrase -.Nm -.Cm detach -.Ar destination -.Nm -.Cm init -.Ar destination -.Op Fl i -.Op Fl f Ar filename -.Op Fl K Ar new-keyfile -.Op Fl L Ar new-lockfile -.Op Fl P Ar new-pass-phrase -.Nm -.Cm setkey -.Ar destination -.Op Fl n Ar key -.Op Fl k Ar keyfile -.Op Fl l Ar lockfile -.Op Fl p Ar pass-phrase -.Op Fl K Ar new-keyfile -.Op Fl L Ar new-lockfile -.Op Fl P Ar new-pass-phrase -.Nm -.Cm nuke -.Ar destination -.Op Fl n Ar key -.Op Fl k Ar keyfile -.Op Fl l Ar lockfile -.Op Fl p Ar pass-phrase -.Nm -.Cm destroy -.Ar destination -.Op Fl k Ar keyfile -.Op Fl l Ar lockfile -.Op Fl p Ar pass-phrase -.Sh DESCRIPTION -.Bf -symbolic -NOTICE: -Please be aware that this code has not yet received much review -and analysis by qualified cryptographers and therefore should be considered -a slightly suspect experimental facility. -.Pp -We cannot at this point guarantee that the on-disk format will not change -in response to reviews or bug-fixes, so potential users are advised to -be prepared that -.Xr dump 8 Ns / Ns -.Xr restore 8 -based migrations may be called for in the future. -.Ef -.Pp -The -.Nm -utility is the only official operation and management interface for the -.Xr gbde 4 -.Tn GEOM -based disk encryption kernel facility. -The interaction between the -.Nm -utility and the kernel part is not a published interface. -.Pp -The operational aspect consists of two subcommands: -one to open and attach -a device to the in-kernel cryptographic -.Nm -module -.Pq Cm attach , -and one to close and detach a device -.Pq Cm detach . -.Pp -The management part allows initialization of the master key and lock sectors -on a device -.Pq Cm init , -initialization and replacement of pass-phrases -.Pq Cm setkey , -and key invalidation -.Pq Cm nuke -and blackening -.Pq Cm destroy -functions. -.Pp -The -.Fl l Ar lockfile -argument is used to supply the lock selector data. -If no -.Fl l -option is specified, the first sector is used for this purpose. -.Pp -The -.Fl L Ar new-lockfile -argument -specifies the lock selector file for the key -initialized with the -.Cm init -subcommand -or modified with the -.Cm setkey -subcommand. -.Pp -The -.Fl n Ar key -argument can be used to specify to which of the four keys -the operation applies. -A value of 1 to 4 selects the specified key, a value of 0 (the default) -means -.Dq "this key" -(i.e., the key used to gain access to the device) -and a value of \-1 means -.Dq "all keys" . -.Pp -The -.Fl f Ar filename -specifies an optional parameter file for use under initialization. -.Pp -Alternatively, the -.Fl i -option toggles an interactive mode where a template file with descriptions -of the parameters can be interactively edited. -.Pp -The -.Fl p Ar pass-phrase -argument -specifies the pass-phrase used for opening the device. -If not specified, the controlling terminal will be used to prompt the user -for the pass-phrase. -Be aware that using this option may expose the pass-phrase to other -users who happen to run -.Xr ps 1 -or similar while the command is running. -.Pp -The -.Fl P Ar new-pass-phrase -argument -can be used to specify the new pass-phrase to the -.Cm init -and -.Cm setkey -subcommands. -If not specified, the user is prompted for the new pass-phrase on the -controlling terminal. -Be aware that using this option may expose the pass-phrase to other -users who happen to run -.Xr ps 1 -or similar while the command is running. -.Pp -The -.Fl k Ar keyfile -argument specifies a key file to be used in combination with the -pass-phrase (whether the pass-phrase is specified on the command line -or entered from the terminal) for opening the device. -The device will only be opened if the contents of the key file and the -pass-phrase are both correct. -.Pp -The -.Fl K Ar new-keyfile -argument can be used to specify a new key file to the -.Cm init -and -.Cm setkey -subcommands. -If not specified, no key file will be used (even if one was previously -used). -.Sh EXAMPLES -To initialize a device, using default parameters: -.Pp -.Dl "gbde init /dev/ada0s1f -L /etc/ada0s1f.lock" -.Pp -To attach an encrypted device: -.Pp -.Dl "gbde attach ada0s1f -l /etc/ada0s1f.lock" -.Pp -The encrypted device has the suffix -.Pa .bde -so a typical -command to create and mount a file system would be: -.Pp -.Dl "newfs /dev/ada0s1f.bde" -.Dl "mount /dev/ada0s1f.bde /secret" -.Pp -To detach an encrypted device: -.Pp -.Dl "gbde detach ada0s1f" -.Pp -Please notice that detaching an encrypted device corresponds to -physically removing it, do not forget to unmount the file system first. -.Pp -To initialize the second key using a detached lockfile and a trivial -pass-phrase: -.Pp -.Dl "gbde setkey ada0s1f -n 2 -P foo -L key2.lockfile" -.Pp -To invalidate your own masterkey: -.Pp -.Dl "gbde nuke ada0s1f" -.Pp -This will overwrite your masterkey sector with zeros, and results in -a diagnostic if you try to use the key again. -You can also destroy the other three copies of the masterkey with the --n argument. -.Pp -You can also invalidate your masterkey without leaving a tell-tale sector -full of zeros: -.Pp -.Dl "gbde destroy ada0s1f" -.Pp -This will overwrite the information fields in your masterkey sector, -encrypt it and write it back. -You get a (different) diagnostic if you try to use it. -.Sh SEE ALSO -.Xr gbde 4 , -.Xr geom 4 -.Sh HISTORY -This software was developed for the -.Fx -Project by -.An Poul-Henning Kamp -and NAI Labs, the Security Research Division of Network Associates, Inc.\& -under DARPA/SPAWAR contract N66001-01-C-8035 -.Pq Dq CBOSS , -as part of the -DARPA CHATS research program. -.Nm -first appeared in -.Fx 5.0 . -.Sh AUTHORS -.An Poul-Henning Kamp Aq Mt phk@FreeBSD.org -.Sh BUGS -The cryptographic algorithms and the overall design have not been -attacked mercilessly for over 10 years by a gang of cryptoanalysts. diff --git a/sbin/gbde/gbde.c b/sbin/gbde/gbde.c deleted file mode 100644 index e173bb78ad90..000000000000 --- a/sbin/gbde/gbde.c +++ /dev/null @@ -1,895 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2002 Poul-Henning Kamp - * Copyright (c) 2002 Networks Associates Technology, Inc. - * All rights reserved. - * - * This software was developed for the FreeBSD Project by Poul-Henning Kamp - * and NAI Labs, the Security Research Division of Network Associates, Inc. - * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the - * DARPA CHATS research program. - * - * 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. - * - * XXX: Future stuff - * - * Replace the template file options (-i & -f) with command-line variables - * "-v property=foo" - * - * Introduce -e, extra entropy source (XOR with /dev/random) - * - * Introduce -E, alternate entropy source (instead of /dev/random) - * - * Introduce -i take IV from keyboard or - * - * Introduce -I take IV from file/cmd - * - * Introduce -m/-M store encrypted+encoded masterkey in file - * - * Introduce -k/-K get pass-phrase part from file/cmd - * - * Introduce -d add more dest-devices to worklist. - * - * Add key-option: selfdestruct bit. - * - * New/changed verbs: - * "onetime" attach with onetime nonstored locksector - * "key"/"unkey" to blast memory copy of key without orphaning - * "nuke" blow away everything attached, crash/halt/power-off if possible. - * "blast" destroy all copies of the masterkey - * "destroy" destroy one copy of the masterkey - * "backup"/"restore" of masterkey sectors. - * - * Make all verbs work on both attached/detached devices. - * - */ - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/mutex.h> -#include <md5.h> -#include <readpassphrase.h> -#include <string.h> -#include <stdint.h> -#include <unistd.h> -#include <fcntl.h> -#include <paths.h> -#include <strings.h> -#include <stdlib.h> -#include <err.h> -#include <stdio.h> -#include <libutil.h> -#include <libgeom.h> -#include <sys/errno.h> -#include <sys/disk.h> -#include <sys/stat.h> -#include <crypto/rijndael/rijndael-api-fst.h> -#include <crypto/sha2/sha512.h> -#include <sys/param.h> -#include <sys/linker.h> - -#define GBDEMOD "geom_bde" -#define KASSERT(foo, bar) do { if(!(foo)) { warn bar ; exit (1); } } while (0) - -#include <geom/geom.h> -#include <geom/bde/g_bde.h> - -extern const char template[]; - - -#if 0 -static void -g_hexdump(void *ptr, int length) -{ - int i, j, k; - unsigned char *cp; - - cp = ptr; - for (i = 0; i < length; i+= 16) { - printf("%04x ", i); - for (j = 0; j < 16; j++) { - k = i + j; - if (k < length) - printf(" %02x", cp[k]); - else - printf(" "); - } - printf(" |"); - for (j = 0; j < 16; j++) { - k = i + j; - if (k >= length) - printf(" "); - else if (cp[k] >= ' ' && cp[k] <= '~') - printf("%c", cp[k]); - else - printf("."); - } - printf("|\n"); - } -} -#endif - -static void __dead2 -usage(void) -{ - - (void)fprintf(stderr, -"usage: gbde attach destination [-k keyfile] [-l lockfile] [-p pass-phrase]\n" -" gbde detach destination\n" -" gbde init destination [-i] [-f filename] [-K new-keyfile]\n" -" [-L new-lockfile] [-P new-pass-phrase]\n" -" gbde setkey destination [-n key]\n" -" [-k keyfile] [-l lockfile] [-p pass-phrase]\n" -" [-K new-keyfile] [-L new-lockfile] [-P new-pass-phrase]\n" -" gbde nuke destination [-n key]\n" -" [-k keyfile] [-l lockfile] [-p pass-phrase]\n" -" gbde destroy destination [-k keyfile] [-l lockfile] [-p pass-phrase]\n"); - exit(1); -} - -void * -g_read_data(struct g_consumer *cp, off_t offset, off_t length, int *error) -{ - void *p; - int fd, i; - off_t o2; - - p = malloc(length); - if (p == NULL) - err(1, "malloc"); - fd = *(int *)cp; - o2 = lseek(fd, offset, SEEK_SET); - if (o2 != offset) - err(1, "lseek"); - i = read(fd, p, length); - if (i != length) - err(1, "read"); - if (error != NULL) - error = 0; - return (p); -} - -static void -random_bits(void *p, u_int len) -{ - arc4random_buf(p, len); -} - -/* XXX: not nice */ -static u_char sha2[SHA512_DIGEST_LENGTH]; - -static void -reset_passphrase(struct g_bde_softc *sc) -{ - - memcpy(sc->sha2, sha2, SHA512_DIGEST_LENGTH); -} - -static void -setup_passphrase(struct g_bde_softc *sc, int sure, const char *input, - const char *keyfile) -{ - char buf1[BUFSIZ + SHA512_DIGEST_LENGTH]; - char buf2[BUFSIZ + SHA512_DIGEST_LENGTH]; - char *p; - int kfd, klen, bpos = 0; - - if (keyfile != NULL) { - /* Read up to BUFSIZ bytes from keyfile */ - kfd = open(keyfile, O_RDONLY, 0); - if (kfd < 0) - err(1, "%s", keyfile); - klen = read(kfd, buf1, BUFSIZ); - if (klen == -1) - err(1, "%s", keyfile); - close(kfd); - - /* Prepend the passphrase with the hash of the key read */ - g_bde_hash_pass(sc, buf1, klen); - memcpy(buf1, sc->sha2, SHA512_DIGEST_LENGTH); - memcpy(buf2, sc->sha2, SHA512_DIGEST_LENGTH); - bpos = SHA512_DIGEST_LENGTH; - } - - if (input != NULL) { - if (strlen(input) >= BUFSIZ) - errx(1, "Passphrase too long"); - strcpy(buf1 + bpos, input); - - g_bde_hash_pass(sc, buf1, strlen(buf1 + bpos) + bpos); - memcpy(sha2, sc->sha2, SHA512_DIGEST_LENGTH); - return; - } - for (;;) { - p = readpassphrase( - sure ? "Enter new passphrase:" : "Enter passphrase: ", - buf1 + bpos, sizeof buf1 - bpos, - RPP_ECHO_OFF | RPP_REQUIRE_TTY); - if (p == NULL) - err(1, "readpassphrase"); - - if (sure) { - p = readpassphrase("Reenter new passphrase: ", - buf2 + bpos, sizeof buf2 - bpos, - RPP_ECHO_OFF | RPP_REQUIRE_TTY); - if (p == NULL) - err(1, "readpassphrase"); - - if (strcmp(buf1 + bpos, buf2 + bpos)) { - printf("They didn't match.\n"); - continue; - } - } - if (strlen(buf1 + bpos) < 3) { - printf("Too short passphrase.\n"); - continue; - } - break; - } - g_bde_hash_pass(sc, buf1, strlen(buf1 + bpos) + bpos); - memcpy(sha2, sc->sha2, SHA512_DIGEST_LENGTH); -} - -static void -encrypt_sector(void *d, int len, int klen, void *key) -{ - keyInstance ki; - cipherInstance ci; - int error; - - error = rijndael_cipherInit(&ci, MODE_CBC, NULL); - if (error <= 0) - errx(1, "rijndael_cipherInit=%d", error); - error = rijndael_makeKey(&ki, DIR_ENCRYPT, klen, key); - if (error <= 0) - errx(1, "rijndael_makeKeY=%d", error); - error = rijndael_blockEncrypt(&ci, &ki, d, len * 8, d); - if (error <= 0) - errx(1, "rijndael_blockEncrypt=%d", error); -} - -static void -cmd_attach(const struct g_bde_softc *sc, const char *dest, const char *lfile) -{ - int ffd; - u_char buf[16]; - struct gctl_req *r; - const char *errstr; - - r = gctl_get_handle(); - gctl_ro_param(r, "verb", -1, "create geom"); - gctl_ro_param(r, "class", -1, "BDE"); - gctl_ro_param(r, "provider", -1, dest); - gctl_ro_param(r, "pass", SHA512_DIGEST_LENGTH, sc->sha2); - if (lfile != NULL) { - ffd = open(lfile, O_RDONLY, 0); - if (ffd < 0) - err(1, "%s", lfile); - read(ffd, buf, 16); - gctl_ro_param(r, "key", 16, buf); - close(ffd); - } - errstr = gctl_issue(r); - if (errstr != NULL) - errx(1, "Attach to %s failed: %s", dest, errstr); - - exit (0); -} - -static void -cmd_detach(const char *dest) -{ - struct gctl_req *r; - const char *errstr; - char buf[BUFSIZ]; - - r = gctl_get_handle(); - gctl_ro_param(r, "verb", -1, "destroy geom"); - gctl_ro_param(r, "class", -1, "BDE"); - sprintf(buf, "%s.bde", dest); - gctl_ro_param(r, "geom", -1, buf); - /* gctl_dump(r, stdout); */ - errstr = gctl_issue(r); - if (errstr != NULL) - errx(1, "Detach of %s failed: %s", dest, errstr); - exit (0); -} - -static void -cmd_open(struct g_bde_softc *sc, int dfd , const char *l_opt, u_int *nkey) -{ - int error; - int ffd; - u_char keyloc[16]; - u_int sectorsize; - off_t mediasize; - struct stat st; - - error = ioctl(dfd, DIOCGSECTORSIZE, §orsize); - if (error) - sectorsize = 512; - error = ioctl(dfd, DIOCGMEDIASIZE, &mediasize); - if (error) { - error = fstat(dfd, &st); - if (error == 0 && S_ISREG(st.st_mode)) - mediasize = st.st_size; - else - error = ENOENT; - } - if (error) - mediasize = (off_t)-1; - if (l_opt != NULL) { - ffd = open(l_opt, O_RDONLY, 0); - if (ffd < 0) - err(1, "%s", l_opt); - read(ffd, keyloc, sizeof keyloc); - close(ffd); - } else { - memset(keyloc, 0, sizeof keyloc); - } - - error = g_bde_decrypt_lock(sc, sc->sha2, keyloc, mediasize, - sectorsize, nkey); - if (error == ENOENT) - errx(1, "Lock was destroyed."); - if (error == ESRCH) - errx(1, "Lock was nuked."); - if (error == ENOTDIR) - errx(1, "Lock not found"); - if (error != 0) - errx(1, "Error %d decrypting lock", error); - if (nkey) - printf("Opened with key %u\n", 1 + *nkey); - return; -} - -static void -cmd_nuke(struct g_bde_key *gl, int dfd , int key) -{ - int i; - u_char *sbuf; - off_t offset, offset2; - - sbuf = malloc(gl->sectorsize); - memset(sbuf, 0, gl->sectorsize); - offset = (gl->lsector[key] & ~(gl->sectorsize - 1)); - offset2 = lseek(dfd, offset, SEEK_SET); - if (offset2 != offset) - err(1, "lseek"); - i = write(dfd, sbuf, gl->sectorsize); - free(sbuf); - if (i != (int)gl->sectorsize) - err(1, "write"); - printf("Nuked key %d\n", 1 + key); -} - -static void -cmd_write(struct g_bde_key *gl, struct g_bde_softc *sc, int dfd , int key, const char *l_opt) -{ - int i, ffd; - uint64_t off[2]; - u_char keyloc[16]; - u_char *sbuf, *q; - off_t offset, offset2; - - sbuf = malloc(gl->sectorsize); - /* - * Find the byte-offset in the lock sector where we will put the lock - * data structure. We can put it any random place as long as the - * structure fits. - */ - for(;;) { - random_bits(off, sizeof off); - off[0] &= (gl->sectorsize - 1); - if (off[0] + G_BDE_LOCKSIZE > gl->sectorsize) - continue; - break; - } - - /* Add the sector offset in bytes */ - off[0] += (gl->lsector[key] & ~(gl->sectorsize - 1)); - gl->lsector[key] = off[0]; - - i = g_bde_keyloc_encrypt(sc->sha2, off[0], off[1], keyloc); - if (i) - errx(1, "g_bde_keyloc_encrypt()"); - if (l_opt != NULL) { - ffd = open(l_opt, O_WRONLY | O_CREAT | O_TRUNC, 0600); - if (ffd < 0) - err(1, "%s", l_opt); - write(ffd, keyloc, sizeof keyloc); - close(ffd); - } else if (gl->flags & GBDE_F_SECT0) { - offset2 = lseek(dfd, 0, SEEK_SET); - if (offset2 != 0) - err(1, "lseek"); - i = read(dfd, sbuf, gl->sectorsize); - if (i != (int)gl->sectorsize) - err(1, "read"); - memcpy(sbuf + key * 16, keyloc, sizeof keyloc); - offset2 = lseek(dfd, 0, SEEK_SET); - if (offset2 != 0) - err(1, "lseek"); - i = write(dfd, sbuf, gl->sectorsize); - if (i != (int)gl->sectorsize) - err(1, "write"); - } else { - errx(1, "No -L option and no space in sector 0 for lockfile"); - } - - /* Allocate a sectorbuffer and fill it with random junk */ - if (sbuf == NULL) - err(1, "malloc"); - random_bits(sbuf, gl->sectorsize); - - /* Fill random bits in the spare field */ - random_bits(gl->spare, sizeof(gl->spare)); - - /* Encode the structure where we want it */ - q = sbuf + (off[0] % gl->sectorsize); - i = g_bde_encode_lock(sc->sha2, gl, q); - if (i < 0) - errx(1, "programming error encoding lock"); - - encrypt_sector(q, G_BDE_LOCKSIZE, 256, sc->sha2 + 16); - offset = gl->lsector[key] & ~(gl->sectorsize - 1); - offset2 = lseek(dfd, offset, SEEK_SET); - if (offset2 != offset) - err(1, "lseek"); - i = write(dfd, sbuf, gl->sectorsize); - if (i != (int)gl->sectorsize) - err(1, "write"); - free(sbuf); -#if 0 - printf("Wrote key %d at %jd\n", key, (intmax_t)offset); - printf("s0 = %jd\n", (intmax_t)gl->sector0); - printf("sN = %jd\n", (intmax_t)gl->sectorN); - printf("l[0] = %jd\n", (intmax_t)gl->lsector[0]); - printf("l[1] = %jd\n", (intmax_t)gl->lsector[1]); - printf("l[2] = %jd\n", (intmax_t)gl->lsector[2]); - printf("l[3] = %jd\n", (intmax_t)gl->lsector[3]); - printf("k = %jd\n", (intmax_t)gl->keyoffset); - printf("ss = %jd\n", (intmax_t)gl->sectorsize); -#endif -} - -static void -cmd_destroy(struct g_bde_key *gl, int nkey) -{ - int i; - - bzero(&gl->sector0, sizeof gl->sector0); - bzero(&gl->sectorN, sizeof gl->sectorN); - bzero(&gl->keyoffset, sizeof gl->keyoffset); - gl->flags &= GBDE_F_SECT0; - bzero(gl->mkey, sizeof gl->mkey); - for (i = 0; i < G_BDE_MAXKEYS; i++) - if (i != nkey) - gl->lsector[i] = ~0; -} - -static int -sorthelp(const void *a, const void *b) -{ - const uint64_t *oa, *ob; - - oa = a; - ob = b; - if (*oa > *ob) - return 1; - if (*oa < *ob) - return -1; - return 0; -} - -static void -cmd_init(struct g_bde_key *gl, int dfd, const char *f_opt, int i_opt, const char *l_opt) -{ - int i; - u_char *buf; - unsigned sector_size; - uint64_t first_sector; - uint64_t last_sector; - uint64_t total_sectors; - off_t off, off2; - unsigned nkeys; - const char *p; - char *q, cbuf[BUFSIZ]; - unsigned u, u2; - uint64_t o; - properties params; - - bzero(gl, sizeof *gl); - if (f_opt != NULL) { - i = open(f_opt, O_RDONLY); - if (i < 0) - err(1, "%s", f_opt); - params = properties_read(i); - close (i); - } else if (i_opt) { - /* XXX: Polish */ - asprintf(&q, "%stemp.XXXXXXXXXX", _PATH_TMP); - if (q == NULL) - err(1, "asprintf"); - i = mkstemp(q); - if (i < 0) - err(1, "%s", q); - write(i, template, strlen(template)); - close (i); - p = getenv("EDITOR"); - if (p == NULL) - p = "vi"; - if (snprintf(cbuf, sizeof(cbuf), "%s %s\n", p, q) >= - (ssize_t)sizeof(cbuf)) { - unlink(q); - errx(1, "EDITOR is too long"); - } - system(cbuf); - i = open(q, O_RDONLY); - if (i < 0) - err(1, "%s", f_opt); - params = properties_read(i); - close (i); - unlink(q); - free(q); - } else { - /* XXX: Hack */ - i = open(_PATH_DEVNULL, O_RDONLY); - if (i < 0) - err(1, "%s", _PATH_DEVNULL); - params = properties_read(i); - close (i); - } - - /* <sector_size> */ - p = property_find(params, "sector_size"); - i = ioctl(dfd, DIOCGSECTORSIZE, &u); - if (p != NULL) { - sector_size = strtoul(p, &q, 0); - if (!*p || *q) - errx(1, "sector_size not a proper number"); - } else if (i == 0) { - sector_size = u; - } else { - errx(1, "Missing sector_size property"); - } - if (sector_size & (sector_size - 1)) - errx(1, "sector_size not a power of 2"); - if (sector_size < 512) - errx(1, "sector_size is smaller than 512"); - buf = malloc(sector_size); - if (buf == NULL) - err(1, "Failed to malloc sector buffer"); - gl->sectorsize = sector_size; - - i = ioctl(dfd, DIOCGMEDIASIZE, &off); - if (i == 0) { - first_sector = 0; - total_sectors = off / sector_size; - last_sector = total_sectors - 1; - } else { - first_sector = 0; - last_sector = 0; - total_sectors = 0; - } - - /* <first_sector> */ - p = property_find(params, "first_sector"); - if (p != NULL) { - first_sector = strtoul(p, &q, 0); - if (!*p || *q) - errx(1, "first_sector not a proper number"); - } - - /* <last_sector> */ - p = property_find(params, "last_sector"); - if (p != NULL) { - last_sector = strtoul(p, &q, 0); - if (!*p || *q) - errx(1, "last_sector not a proper number"); - if (last_sector <= first_sector) - errx(1, "last_sector not larger than first_sector"); - total_sectors = last_sector + 1; - } - - /* <total_sectors> */ - p = property_find(params, "total_sectors"); - if (p != NULL) { - total_sectors = strtoul(p, &q, 0); - if (!*p || *q) - errx(1, "total_sectors not a proper number"); - if (last_sector == 0) - last_sector = first_sector + total_sectors - 1; - } - - if (l_opt == NULL && first_sector != 0) - errx(1, "No -L new-lockfile argument and first_sector != 0"); - else if (l_opt == NULL) { - first_sector++; - total_sectors--; - gl->flags |= GBDE_F_SECT0; - } - gl->sector0 = first_sector * gl->sectorsize; - - if (total_sectors != (last_sector - first_sector) + 1) - errx(1, "total_sectors disagree with first_sector and last_sector"); - if (total_sectors == 0) - errx(1, "missing last_sector or total_sectors"); - - gl->sectorN = (last_sector + 1) * gl->sectorsize; - - /* Find a random keyoffset */ - random_bits(&o, sizeof o); - o %= (gl->sectorN - gl->sector0); - o &= ~(gl->sectorsize - 1); - gl->keyoffset = o; - - /* <number_of_keys> */ - p = property_find(params, "number_of_keys"); - if (p != NULL) { - nkeys = strtoul(p, &q, 0); - if (!*p || *q) - errx(1, "number_of_keys not a proper number"); - if (nkeys < 1 || nkeys > G_BDE_MAXKEYS) - errx(1, "number_of_keys out of range"); - } else { - nkeys = 4; - } - for (u = 0; u < nkeys; u++) { - for(;;) { - do { - random_bits(&o, sizeof o); - o %= gl->sectorN; - o &= ~(gl->sectorsize - 1); - } while(o < gl->sector0); - for (u2 = 0; u2 < u; u2++) - if (o == gl->lsector[u2]) - break; - if (u2 < u) - continue; - break; - } - gl->lsector[u] = o; - } - for (; u < G_BDE_MAXKEYS; u++) { - do - random_bits(&o, sizeof o); - while (o < gl->sectorN); - gl->lsector[u] = o; - } - qsort(gl->lsector, G_BDE_MAXKEYS, sizeof gl->lsector[0], sorthelp); - - /* Flush sector zero if we use it for lockfile data */ - if (gl->flags & GBDE_F_SECT0) { - off2 = lseek(dfd, 0, SEEK_SET); - if (off2 != 0) - err(1, "lseek(2) to sector 0"); - random_bits(buf, sector_size); - i = write(dfd, buf, sector_size); - if (i != (int)sector_size) - err(1, "write sector 0"); - } - - /* <random_flush> */ - p = property_find(params, "random_flush"); - if (p != NULL) { - off = first_sector * sector_size; - off2 = lseek(dfd, off, SEEK_SET); - if (off2 != off) - err(1, "lseek(2) to first_sector"); - off2 = last_sector * sector_size; - while (off <= off2) { - random_bits(buf, sector_size); - i = write(dfd, buf, sector_size); - if (i != (int)sector_size) - err(1, "write to $device_name"); - off += sector_size; - } - } - - random_bits(gl->mkey, sizeof gl->mkey); - random_bits(gl->salt, sizeof gl->salt); - - return; -} - -static enum action { - ACT_HUH, - ACT_ATTACH, ACT_DETACH, - ACT_INIT, ACT_SETKEY, ACT_DESTROY, ACT_NUKE -} action; - -int -main(int argc, char **argv) -{ - const char *opts; - const char *k_opt, *K_opt; - const char *l_opt, *L_opt; - const char *p_opt, *P_opt; - const char *f_opt; - char *dest; - int i_opt, n_opt, ch, dfd, doopen; - u_int nkey; - int i; - char *q, buf[BUFSIZ]; - struct g_bde_key *gl; - struct g_bde_softc sc; - - fprintf(stderr, "GBDE disk-encryption is deprecated,\n"); - fprintf(stderr, "and will be removed in FreeBSD 15.0\n"); - fprintf(stderr, "(continuing in 5 seconds)\n"); - sleep(5); - - if (argc < 3) - usage(); - - if (modfind("g_bde") < 0) { - /* need to load the gbde module */ - if (kldload(GBDEMOD) < 0 || modfind("g_bde") < 0) - err(1, GBDEMOD ": Kernel module not available"); - } - doopen = 0; - if (!strcmp(argv[1], "attach")) { - action = ACT_ATTACH; - opts = "k:l:p:"; - } else if (!strcmp(argv[1], "detach")) { - action = ACT_DETACH; - opts = ""; - } else if (!strcmp(argv[1], "init")) { - action = ACT_INIT; - doopen = 1; - opts = "f:iK:L:P:"; - } else if (!strcmp(argv[1], "setkey")) { - action = ACT_SETKEY; - doopen = 1; - opts = "k:K:l:L:n:p:P:"; - } else if (!strcmp(argv[1], "destroy")) { - action = ACT_DESTROY; - doopen = 1; - opts = "k:l:p:"; - } else if (!strcmp(argv[1], "nuke")) { - action = ACT_NUKE; - doopen = 1; - opts = "k:l:n:p:"; - } else { - usage(); - } - argc--; - argv++; - - dest = strdup(argv[1]); - argc--; - argv++; - - p_opt = NULL; - P_opt = NULL; - k_opt = NULL; - K_opt = NULL; - l_opt = NULL; - L_opt = NULL; - f_opt = NULL; - n_opt = 0; - i_opt = 0; - - while((ch = getopt(argc, argv, opts)) != -1) - switch (ch) { - case 'f': - f_opt = optarg; - break; - case 'i': - i_opt = !i_opt; - break; - case 'k': - k_opt = optarg; - break; - case 'K': - K_opt = optarg; - break; - case 'l': - l_opt = optarg; - break; - case 'L': - L_opt = optarg; - break; - case 'n': - n_opt = strtoul(optarg, &q, 0); - if (!*optarg || *q) - errx(1, "-n argument not numeric"); - if (n_opt < -1 || n_opt > G_BDE_MAXKEYS) - errx(1, "-n argument out of range"); - break; - case 'p': - p_opt = optarg; - break; - case 'P': - P_opt = optarg; - break; - default: - usage(); - } - - if (doopen) { - dfd = open(dest, O_RDWR); - if (dfd < 0 && dest[0] != '/') { - if (snprintf(buf, sizeof(buf), "%s%s", - _PATH_DEV, dest) >= (ssize_t)sizeof(buf)) - errno = ENAMETOOLONG; - else - dfd = open(buf, O_RDWR); - } - if (dfd < 0) - err(1, "%s", dest); - } else { - if (!memcmp(dest, _PATH_DEV, strlen(_PATH_DEV))) - strcpy(dest, dest + strlen(_PATH_DEV)); - } - - memset(&sc, 0, sizeof sc); - sc.consumer = (void *)&dfd; - gl = &sc.key; - switch(action) { - case ACT_ATTACH: - setup_passphrase(&sc, 0, p_opt, k_opt); - cmd_attach(&sc, dest, l_opt); - break; - case ACT_DETACH: - cmd_detach(dest); - break; - case ACT_INIT: - cmd_init(gl, dfd, f_opt, i_opt, L_opt); - setup_passphrase(&sc, 1, P_opt, K_opt); - cmd_write(gl, &sc, dfd, 0, L_opt); - break; - case ACT_SETKEY: - setup_passphrase(&sc, 0, p_opt, k_opt); - cmd_open(&sc, dfd, l_opt, &nkey); - if (n_opt == 0) - n_opt = nkey + 1; - setup_passphrase(&sc, 1, P_opt, K_opt); - cmd_write(gl, &sc, dfd, n_opt - 1, L_opt); - break; - case ACT_DESTROY: - setup_passphrase(&sc, 0, p_opt, k_opt); - cmd_open(&sc, dfd, l_opt, &nkey); - cmd_destroy(gl, nkey); - reset_passphrase(&sc); - cmd_write(gl, &sc, dfd, nkey, l_opt); - break; - case ACT_NUKE: - setup_passphrase(&sc, 0, p_opt, k_opt); - cmd_open(&sc, dfd, l_opt, &nkey); - if (n_opt == 0) - n_opt = nkey + 1; - if (n_opt == -1) { - for(i = 0; i < G_BDE_MAXKEYS; i++) - cmd_nuke(gl, dfd, i); - } else { - cmd_nuke(gl, dfd, n_opt - 1); - } - break; - default: - errx(1, "internal error"); - } - - return(0); -} diff --git a/sbin/gbde/image.uu b/sbin/gbde/image.uu deleted file mode 100644 index 1c06d84f1e45..000000000000 --- a/sbin/gbde/image.uu +++ /dev/null @@ -1,3304 +0,0 @@ - -begin 644 gbde.image.bz2 -M0EIH.3%!629364"&9H\`Y1=_____________________________________ -M________X7"_"W7I7N^[5>OM;NXTY!ZK'VZ]]MS?=??>>OM=[?/I-WMSSFM; -MO>[&RU[OMNKV=>\:^>K>]N;5WN];V*7F[O6[[[>G;WWRKZ]ZK7?7UZ7=[WN= -M[?<5WWR[97V[M=U[>UAYYW'WNW<^^WUM??7U]UZ=>MZ?7KOOK[;WM[O>]'O; -MKWU?;[Z^=*YZYNLNKYQ7KWO=Z]W.>OOO/HR>KV^N>L]>>Z\O;ZWWN[O=IOK* -MT]>WUUS=V]:Y+[/OON]>[?9[GWS>][OK27KI]UK.N]]]WOJW=URR]L[[LU>\ -M77VSN]YM\]Z*]NW>S[GWONYONZ^[SI>^^YWN[6Z^YUF:Y]Z^[[=]=\NNY]Y; -MO>^NEW>S=GGKW>^^[;?.[[7?;[?'7N.^YZ^UW/K[VY./:N\M]S:]WWV>^I>W -M./3MVW>]GKR^]5K-=GG>^\MW'NWO>^^]?/7O7WO/?;O7UU[O?=]OO=<9@7V] -M<\V=W5OOMS7>;[[Z^O/OO=ES[>7VON]Z^WE>]OI\^]ZE??+,[WG5FW33V;4/ -M:]O-[M:SUWWOENY[S*]>[/0]]O!=];G>MWEGOO:MW>K?7NW6NVHO6NMWOJ\^ -MOO?;K;UUM;>VV:Z>YV]T]ON9MCWGMN[WGNCWWW=[Z[5/O?5=??;WINOMG>TO -MO+??;U?;XS=]VK;<W>UYYYSNOGWO/I[WTT=KV[JI[:]]WU[?:NFNO=W>GNZ] -MFV]W>WGJ+S;OO>T=+Q[7UP[WKNOHR;C>M5?=SMNU==O>Y3[[?55WW;U\\U>= -M[>^;NUU?5VNWGON^^>W3ZUKNNN[-WW>NVC4\QUO;N&77;>W>>=\Y].^^[[=] -MZWW?;YTJ]BFW??=O3M[V[[Z^=Y?>^\L];;O?/OM\]MON[N.]Y[KSV[PWN]W= -M+ZK=]/M]SO>]GN^[>]O??=U-Z=[OGWMQ][ZP3[[;OK[V=]W?:ZZ^]>]SO`.) -M[?7>[=U7O>OOOEM]]W=?>VUE:-;,ON]Y][O5V^^][OK[[[V]Y]WVV]M>@Z.V -M?7OFU\???=[[E=WM-/MRV/.9MJ)6>[[UMNWSOGQ]?/ON>]]OGE/'K;?=J[ZU -M]?9=U\[O<T]H9OM]WJVN?7>ENZ[A[M[O.2]U[O<S-[7V^[YT^OI]W??/HO?< -MZSXWWJ7N?>YW(^?;[?;S[)OO=][SV8O?<GS.]7OO?>WW9:WUWWWOGN>^^]/? -M7GK>7N5[VRSYNVIWVU>YNU;KSP9=9][+;[VOMMZ^^C[WO?.GIWJ^NN^ON]WN -M[V]OO;Z^J[UMNOGFN[WM4]>OO=UVOM]X8]MNO>\9&^N^WU]WG;CJ[NMW=O8. -MZ5?9U?==R^;?6N]O>M:VUF]N]N[,VY'K*WSW=XZN[O>K[M\Z:?7>][IUK%[= -MV.]N[O6O?77=OIWMFGOG>:?.^YWWS[SX5>ON^VY]3WW=[K[7O6^^]?7>M]\V -M/<UUQ]:^WOMC>1TDZVWF[U>[IO;"[[NVR]:]GKWON^\S'>]]=O/?>W>K[[O> -M6O7K[V^CO/NUY?=U[O/7GSVKG;[OJX^[S?=;W5TSO>[Z^USMN[O?=WWVUZWN -MWNS[[O>];KQE>MWS:US8^[=[ZWM>G??/=>^9W=?;OO'CW;[[X'S/<YW???== -MSNY"J?X`3``!&```IL````5/P`3$P``)@`````````3```"8``-&F@``:&@: -M`JB%5/_`````!4_%4_P```````````F``````3```--`#$``````"8`!,3*> -M!51*I_@`)@```-)@`$P`F`3`3``5/P`F``3`T-!IH:#30R!IH%-@!,$TP``$ -MP3`)B8)@C$P*HA53_P`$P``````````!4_#0,0#0#0T-`T``)@`!#``E3_3! -M38`"&```F``3```542J?X`!,"8`3`F`&1HT9!IH,AB-"8`3$R,`)@$P``3)A -M-,3)@$R8"9J4_31@3"8%/``3`3``&@*H90TR&@T#0#$:9-&A,!,3`)@`$PF` -M$:81FB:8``F``)B8`3*>!&3`1B81IDP3``!,IX!,J?J>`":4["(B(J<C``(@ -M"(`B]!&1```$0`'41D1$`1``$0!$!L$8```!$1$0`&(1D0``!$``TD9$`1`` -M$1$1>@C``(@`"(``!)"$G*%(@"(`B`(B(B`(C\C`(@1&1`$1$``1$1$`1/OL -MKOMIH+QS3GG]CAF%<+B]I?#`V7-_"-Y#LDSH9(@]7VLR`[HWF)TW`6KW'/:[ -M(XX%BKB][$SP'KU?K:_(1N9_H^2@JA3P.J%L@];AP_!DU0#Q+M]J`25MW8VF -M*XL679-BSK:$@%7+58\#PQMY/2W?@M+-W)\E?]B$TIE`=%,17/[11-(;\I/# -M3K0@:YL-M_DJH(H!9LQB\>C6A#Z#2)'V&%ST*32:^<7Z,WD!2Y^/JR(;=ORJ -M>,<*PVQ,R&#QC8-[$9MU[)EC?'-!R@=(2:X0\CG?++DK!YO[LOU*6-G(56S[ -MML*@CU_P?SNY*I`D2CK+X+*<D33>[&5,NIT^H*%`ZF8Q<'K7)XIWZ8=1A[<' -M;TS"6Q^U[E3BL90'SR0^'D=A@&E@R::PDR3?^0@H869KI`T;Z3$XA/LY\]1E -M/C0"&C\V"&S3KB$?U0>XUFH\Q[N+OQNKX'*LW]</]KK<$L][=G,6P<.>*#OQ -MPCD->$>?-13U@<R>8#8*D.'R)W#N8IFE^^*R@S%:&O"R%#-'W?!YNM3N5PZ` -M/#!F3E9,(+E-0&:61NBQ11&;HL-7T,4#Z:MA]`E:==_$3](_^+,-FT4#KAN+ -MBE/&6*K[.Y6IUY,+C%C*7"\Z&7P!BW("$L:%`G#&<?&$\=LA)""T$O078Q1Q -MMTR<,YL&6.H!.P$NQ4C\5:*(-AQ?@B2`II;>'-[R2,L8NQD^'8NKDO6%V-S] -M.XJ9#YRP6=JD;F2*J'B=3QEU]OH\;!N7\Q_OE"0`"(B``(B(B`(D-T3;PKMS -ME!5Z$Y68,"-/Q<GA,D'YFYSB^;*9@X#0S:+&?]I6A(/?N%TE=YWX'"3:")XR -M@>>PAYR72R[0T3+!<W`!BSS#NJP"^QE)#]U8NJO;%H#8@ZFPD5^(XZ5R+S?9 -M,;7[T@)0"OI+_%3*6R$4M)._@@GW,9&OV[[;-#T`VF``;R(4:!QU[=*3AX:Y -M]QUY1>9,F9+<[0LJ,'K!]IX"V'V63B#J53F7=OS`#2?&W!1[9G`*'&?SYV2N -MW5W]E$!P+VH9'#BZ;8[/'<$>1S4//EG80Q8;"06GUQ=I<,<MC0,MGN:3J,A' -M(Z")BMPGM_S&X/51E6LW*\%2%.ZY_$-(C++?8^[IC?>]Y7D(7FTGIUAK$E9@ -MG$JE)3V?>1[&:XGBU&L)E>&W&XYK%=/0]20P38X^,$3A/B+Y`![Z4:ZJ_RZ3 -M5O9QO4@7/4:,]*_@!XU8;!W>8B]V%ZATFI*&3JQU@A;8*IW`S;E&3A9=#W.J -M`H@<XS:A&850DGDFMDVN.17D2I+*HT6*RA4%6L.8R2EE9WHTLWF.JTJ`"I18 -M<2LSCK4:D\M3#S"^@YI(Z?L(9(?EN43_C'54GG!XRLK6*(4D))8LW^RF[G,# -MT?*CW1;UJ+,6E'GXP4F8`K^F]0@\(77%3F)+%RGE>5MZ#+DN\BXYH.2C>"<% -MJF?>2G6_7\\@-/$FU7[5XKM.7&U+XO\E7\+V$,8:`K'Q&"(^ZA[8'4*!.B=9 -M_TQ@Z596F1WTPA:,40MQ5CS*H;5H21VQS6@9P$^U;$\D=LR-1HU^42O(>K+J -M:L@X=VR&SWEZ=!Z.]1PX^+V[E>F>=!PSK.#KV'"G^,MUMU;(HF:<A\21.#;Q -M>QL+H*ZR(TM"9V,/*)3X+*%=HLY<)W^`#[H(PPH(<N./[$:SQ!O7,$8?+:GE -M$X-TB5BUFQ<W'N?X150PB5$'_%R]N?>$?;XL0)TC##3VW2WP(S!3X5H#\($, -M%2D%>[/>W\9=))4F[$:]V]J?9X@\OUKEG;3272J_<IN69W>R(T:R##2:"L;O -M2"SKT7P)Z9$@T"?1B4<_JD--\(C4:'3+J>($7XU]F1[G8;U!R)]'8+M/WAYV -M=TSANI97]6K?,&AG`GA(6*M,JV:*BL<25%3%@Q?0K8E>0Y:U,MY$"J0WSD6M -MB;C;W<?7(\=W=3J9`NQ76BR['PN-'R<W^1[DA.AE["+&3)4%U;,RMP[Z;H.] -M*)=:2B9!)V'%47_&7_F94`TK<#7B7+,&",N*X?5/?KKN,9=PG)>[A*5#-WKZ -M^4*GD`WKYL[5FCI)Q-KE<DFT7L<I0UHML:XC+O#5]<UP_#\+B6N26F([)0Z> -MV\Q\2*G9'!?`BX'Q2.:J*%%LRJ6?D#\-)>B>NA7!,I0#4C-)7J6Z#7YF@L*C -M]KEA9^]?J]LJ'.H-P>\ZY,8RA\7=_1<DN$0:^SGV<EB?.YKH=2$DL=]["KB7 -M.B<UMV0,$B)9\KS6]^:^W*A,B?[VTUP8K3K>97H'F'?-*@<??7:@&1P?V9-S -ME68E_5XV*#)M^*Q:.L6!GK.W0I+A,;^S>VIJ4&-\)4U#613H+]^6-I6%=O:V -MCO7^I];_E<=Q,7U\WB_KZJ>+K2SR`FAD/D$RN'675:N`\RWP\()^OKK():JT -MB-E5L,!,=,GS;)@YE?/^^C[V1V_@"Z.P033VJ*^A!AE5Q_QVMTI'=[0Y^K5E -MA^`0E>6=>&9X=>"XH>%8M6TT2.A_S::_D'=^LWN6.9\UAA,\J7;C//PJ]C)^ -M:\;1(O$K-4W@@=DVC2^'C'U-545^#=I*P.58$<K]L[5VSY#G-NKT_T>+T"G9 -M?LE2U\W^6`M;<IL['I;Z+FP`<W[=6R,4=U?-W\IS_E;`?JEHV#=)PAHWIJ1C -MZ-VX4EWQ$B<OH!0]Z#C,7ND!!^1PSPQT>5C&^]2S'&$)=V502NFDJF^HF'35 -M.\%4(M+Y.VMX)OW?Q1\?-M@8V^L!?6D2M6!SI5*!^A$6L"^"^]L5Z"9E#XI1 -MA=-5"CIK7Q=2/A/V83`RIEWD-TV2!DX^H_U"@MYZC(:O=;W;L>9O=%W=>#U4 -MZ]L=2H3_W"<?"8=Z@A?T&T-$,%]NH]0_NG>N..'ZVK.^%FCDG?!F8S%$\2HO -M?IY+618!N;^FV85%.C0.Q3A+1=ZS+U<(O&G2:!=;$'SW;Y9B$$7<<^.$P-_2 -M92+RQZM'_`>12>R<//V4^^FBAIN/K]$YH+.YZ7P&I<1&$$O3_WU,D^L@RO(: -MT%Y4&,.+51)V5J60-&D_%2\ZER0VB;19<_Y]J[L#D'P9'9:3',PE8*BW%4KI -MJ[&D6%X;X?O=IKY`Z$$SF>'1@N72M<?WBG?Z58T77-PEK2=6.,8&YN]J>-XF -M`8J+I5'F*J(75:2GKH]V&^!LX--5ZXQ20I:+ZK)9>/=SC,IRG7JU#6N/C)Y< -MS=Q(T2=4+T>T_O`JEA6JWN"8!+^))2+?432]+))3O"3T&):8C2>Q&>V&J98. -MY9\\QVE%LKZC=O95UY)^AQW@X?!=]WC'""_B%8<[^TB\8X3#WQ!WI2\;+@L$ -M0HHMOQ)Z4X[[MP7=(>MNO`?E'K--#20D@$A&,;!(?.LDU'[7I91VV#_.BV[H -MP5,5RU@>O)>$[<#H$8V"V0&`T1&XA*F-$Q<Q2"9``GXQL(Z].FXO"[G-46%- -MLBX_=&$T89_0JA>:5A\`.:-[2;/P6NMJLZ:3GP`/.Q.NE(*#:LAXJ4-99LT) -M-ID,,T/2_VS/4.TD%7-$RUDT;UFQFUKRFV7Z]C:<YLNW]L)Z:X5[KS@O9-5/ -M)%H95QON)(".;G*JE3_NO95RQ('G,#T'-:\292P&>E"-MM]6_QCH[T`7!S74 -M3YOR.*:#X;9I+O)J"G1=U].?[.YM3;VAGY&-70TI2(!G2K=RJ!>?AB'8:7_I -M/L+4I=?6%ULTFV**N3]#K$X5:9"ZEH-,/S;H-#S,1CQ.T-PJPDB3@#Z(VD]I -M]L("A/`:@7Q!?U>UG_@Z:/+Z#SN$E21[R')ZR,X<>",[`S'0FR["<G."Z<!R -M@X>Y\^$\#S<!%U`OX1`LRH%-KG6)=,"V!%=WV+Q>Y""@M=04)(OCRT+42_7^ -MS&V]B&#`NB-KE\MW&),/\/DZCN(&PJ<>PL"M)$A1[GS"N<DOIUP[*WUH./@\ -M&]HP?<BT(^TTUW)GMJUK[R?`GIL=4SATC8(\Q^QL>9^)'BI&VQ-)9W?<B7.G -M8'<D6-&'"O-/MO4\<:E1IN8O=*8EJC#.`3)R@##0-FW02,U=MH7?C86Z?KQB -M=!:T[H\I]/?K+/K0@8S#G7I<A%51[T+`C-*>IK,$>N,[K[$)--3"Q<^7"S2V -M5P5=BK%%/<AA=+_BH:G-8BQ0OQ9=!$B;D04@3A'9$JVVR3]$8#0![_Q^U75@ -M796@9?*^M$EY$GS!!UBK_OM"N)M<&5OG5GA?:U;ELC:XX>JC62*6F[)8%2*; -MXTE-2&)%]LA<B$SWEZ2H6/X(`DKW3Y%C/M,.--?.$'!53K$G[[8@P52)8E/N -M"&K*;G"'3!6;G\O/S[W,Z:J,#RPG!*QU7OQ?UR:<K0=!8'44JV<U,UX-&W8@ -MR<HJ>DS\D\_KUW/UQ+4Y3C'YJ@V0>&D?'%&B/["^4C0FWE;+:I:;MPNK:;1/ -MV6=(0V`+H%>:KN%*F>R!F3IRS8%6)8[O17E<>]-/@.$[!-4]>,)Z:>GFGU#2 -M5O_)=\5B*[(0?ZV1$EYRT^J%P'Q;-WLC^TMI[=)=$!$XY^`K0GV6@A+QGM]Z -MQ1;,"[0'!97SB_'S/B[Y>;<50/2&/J*6_AW;0A8&RP?SG[/R`]A?7K`$P5D\ -MKC,M.O(*M3/6MB_FL\<"?II+/35,(Q+X^TJV\NJ*QZ?]^IF3JGH='PB!?H8_ -MM!3FR=HN>P1C;8%NY]W2@%&AQ"9L:9_]0E'\8KOI\7RG8/5\/FHH8N,%#4O\ -MWES>AS5&N!J@/#L:M`)C;6A'!=FC_,W#.<&^R&Z+"`GQT2?K(#KS(L+`Y?)K -MCP#?<J4G5Z9B8EI%]IVG"XF"X<;PC0`(#E?(D.L-!4(A\H^6#$HEHY"J-[G^ -M\SND>C;'2>1&L'G=!`EMUUAH'[&J9K#BVC>6#G[E0T)#7PRV9=7O\*"Z3A;' -M/K^8T"YBF15.'XV)EZ)%!TTY@$42I8=RY5RV.6]L<AF4UN,O"QH(HC?5%93@ -M*_B(]N93V0YW'-:3%E-#@'J#.?J5=.0,-@C@=R<;`&?><_`]T8'"9LQ1+AIV -M_ET<\KE<XY-:468&HV-0K?I7.*1H]3NN079QL3]QR&3$-CBGFEJFO_J0:@5C -MUG=/4JQ-BBNNC22>*G31(1NQS?#<TSR[#_$2-XAO'P+!Y0`N.#O1/ORKZ$,- -M!"V;'=:3=Y<V.O\\H`BH]`TA16X['']L"S:_]K[QQE'_AL:S>W+0R%\EW$P+ -M(;40NK"#=5#>_2J(RP,/;`>8L.=YPK,`LJS.M7TL=`[)HJ4->?>A(,V)$1P; -MC?&;M^C;OS"EIG3EUZRI*'ZE1[6^3G2")'U(1B9%@%O^^@%.&T`@.A,67:O$ -M?U`PSK,E%-M+JN?K/[U=_$HI.8,%8SO0=;HJW?+L^+%5PS8!S"\D<%&/@,"% -M_3:!O6-*4)GKFTTEFN87$H1)/QB>.;=\9D(.JC#8'V55AZ'X2J\<5E#<I;X^ -MV\WKH+G,1P<17)-]OX^,:/CSSB;6PZ0'[NTX?U!_:8^P.%J>EH+-B9B/YUU[ -M_V/&"'D;YX%P6W699`7$F]`B9`'=+RR(,7E-T;S>%0C_:I;::@ZU>N!HV0C` -M$/N33[_WS19QN$(S`U&3H'L;U>_39M[HW4=SWESHD\NZ88-9BO6]U`#0"G!- -M_*I!W.PR<LVA7TNI&O5L>&I"*#F;;1Y`"J4:XV:S`6$>8%M9='ONF`9-L6BA -MFD_(\4P\#V^J6\V;S'IJFU)7CIQJ?3UCD59)4THHV;4[),QJ(Y6#W8$)P-V7 -MO9^^38)Q9;U/Y`6A*?\E)05BC=M-(STFE@+^?)3(#DWZ']9CSJCL\^FYL5?, -MN)D<=^%(!YN60H%B1I+>?T.ZL#.9(CFRMO0V?6-P*!'?VL^V/'U=2NQL36X- -MJ0\E-_-IMVYNJ<]Y;D,F1"DX*3T=;+[/9L#=`<J0L>ER34&`^:F%-F3O#D+Q -M$8:*^I38G_WTW]RN)H:"OPNC+.T7O32F\%G%)?+75LPM:$F4&)=![(&3&GO@ -M&FC(7;_IL@EH'Y@'K15QK<+IGBWKY=O7J\/08C6[B75=ZJXXIU233\^HS$5& -MSWI5H9.%.!^8018ZZ5T1>Q;3X"-<_\V;C!4V8=UI%_D<'-PBE@]9Y?-K6*9) -M?)9P(N&++8LPNNVC-:-\2&FNL>"4P#DU/TP&X')*>IL;T7$SA%R&0[PYR7P" -MT4WG)6?6BXNDA#R)2191XTUDG;<6QM.?Z>-O9+U6V*!L-8$(`&ISZ62I]PC3 -M.LNLY4=D4IKZ9LU5DCW17KK=B+F%#D(Y<QY>%YJF$T*)&`FN=POR'"+UNF=G -ME`B-F^ZU,6E^A1XZAA3-U?6QQ>)B1`'C%Q*<=@IM+LR>"4%)26\YRROK"S8= -M]E08V/V<59::9ZWMS`@_PE1>7-B+_RQV(F(\'(.R;$5:503.3Z2]@$[A_MM4 -MXO#;=A7H+RE\)((TJ[T4"9,(W-GY4&Y227=&CEE_XXAJ$O>%X):'^0?]V"%- -M"S_*HIT)3?"9QN%\LX>+5,C*!!"AKF0KGOB@H72HV<=K(+83-47Y$A9K\SC1 -M+9RD7,/]9!ADS"$&_-Y$07SVM#/Y6>]W$H@,Q_"`4`WPPHU!Y?MFGDUS@9I\ -M9SA/1=*;<RF$Z5#/E>UR<V<>-IFYBFM33W%(?]L^U>-,9H]TP+Z-JP:5ZDA+ -MNC*8)=7)SQYJ5M;*NW^)>3862XJI[''?<WX4LY.B+5@TF$<W)Q,HXH_UD!J' -M@I'+IXVV8%6N,:@J,*0>,)'ZGS%7R@KX/RI*;\3FJ<F>3:`5-DDN?M\GQ:?` -MM&!="%>ULO1U;(\?_8I)+S"%8B_,Z,3:F&X*.V5-55?[$ZD`7PVN80)P=U3Y -M&@EHD0725=0VK:.,$'K!3592LBPUU#;80U7GW!\THD2-=%Y`?<UFU6>!7PQ? -M"UV1SI]&Q:`7IL8H3W&JZ$PIYD?W>*#I:3FJ^+O:,5WQ;)RY6M0$WMB86T]- -M)A3(#!>0BN,A?R+NW#=1P;[>!/(^F6'.=(:B]X_10T:U@X.ULJ;B1N*_->X/ -MG*#;,PE#\DUE?)=57*HC&N`M1#1;Z7@X8L949)[*!V$J_4.N*'A8L(V2KY&@ -M*?Q2'<+W^H'`L6),P90)?ZNGI<F'NNH=E90U"@?HNI`S<+U$*UGHS06.Y-S< -MI44+GH\'\/AYEXV<KL22OQ,8TZ:+A91E\T64`B.NG?C3DF*2R!C^#TUH!YJ: -M)GO<)"QUZ&$NLX`:N^!(KE5Y+$@%KNN+M14:[L0>7%G]!A!E7:\"ZNJDB*\J -M!^_4^"2K3LM<ZGQA8M"5&MD=CM4N:0%#($]Z#=MM:?%;]?3R'+XB2/BTJC.^ -MK<N1CQ"G[XUDNX*FLWVT/)U:!(V9(^-48.]K)2ZMJ@R7))I8D0OOR#.$%TR` -MA`:2&W=ZP,EVMT-DIV$&?C.R#8L"09S.^46X/=?XT_9!$`<!PN5_>'C4YNVL -M3KK4M',5@[SKX7;,=6I8V]URL>G3+J]<3?8U1T41GAFWG=>O$6-A%%1N>Y5' -MAA5C`OGFC&`.QO'K(!N/5TU0MTOS4!.@X6V=R<7+GPK$>/VUZ(]-<B=%N<ED -ML`:,<EKJ*5'(G9"ZLFI1-3]78R%]B%"O'\#=[SKI/?)W<D`\2U0URSC"!>N0 -M,!NU?9'H9B34VC116\#V/%?N3Y`AH8HQ=-.,1I_RY]?L29VQEC*!N0Q)@W"V -M<J((@0?/B+]<ZC.5(BWH,^I'$_"'2=WJZ;\?(XLI\^/BO(G9=H"=>7+ESQEL -MX'416G$O>>UK7J9&\,5/6M.7'1;=IQ,^;$'95%XPFEBHT35LGI6<5.B;1#O[ -MM^WBEM$5*.\1<M>N5\32DU:I5LKE27E*7:9DIPU.XJ74&X3;N_U"FH[9G?:> -MA9EXYF[3NS,32%#UL?NNQ8"Q`MY?UJF\%B??OZN_6[8*<4OG&79=;@0_I2A; -M2A#4BWS5+!^O77#&<,BK":<5]B@VN!59?T_XU!4CR+8>N$"R/?-DT6E;>8;5 -M];61"409>??#$0J/*[R&+*>KU#1CHW%%P#/+,U;O/^[G!/C;H2)&-DWKB%?_ -MRP-7HAN8VZ=)(Y-?2MD9/WDO9BW_BI%BHE[^H6%52<6`WM[&_W^1(U/X9]`Z -MKC!MX(^Y)9(0:SJ?ZL<(D/#0'F$P<8SIC=`@!T%E+-.;E:&.>N]K'W73"A"I -MR/.5P"<_5,.H:%.EQ388AI2UPFI!PY1IHR^=2!A%[SJ[J<;A69C1<6RM-)_H -M$:ZC7*@"&/N<[R:DP8,X]=H7]7,A2F5$#O&?IP#A4OM$VYQ5<\G7SL7<O=57 -M5YKQ^#/S)SZOB?QO$L'Z9<)83=HA,RS8^U11R=$S\\2S^=.RE<I'&8FX\3`; -MJV@R(6[>6?!9):BS4GF[W>\\4`\@=5Y:\.AP?<+:_CSU-4]5CWV\I!I2Q;N> -M,"ACP/KL,G1GT;(I^A7F%'":\X+[+A/@:++ER/\WC$G6SHSS**)ND9-B:O[< -MW4O;4C)P1GAU6MV(JIII=C:MB?`2<2N<6P$0K<HQ6%;.U<WU.<`3$":GVL-8 -M7U'DW##L`*IVB0K!SOW#B?7;'D[N9.?,<S=GF616Y5^"3=RG1"F7P-J"1>T= -MC`FYE>3RU3582+[^_7_3!;QDZ(6-&:]L?EU7%_9&E&UUTXG@+;CU&6H5W%2X -M=,GAZ`VGO]>X2#BB44LM1J<^U=AJF65K\]FO1SAO'K4$4EH[:.7B*"<`<AB& -M0OCB[CPDXW$G1?W&W9],KX*X#"I;->V70/_=UFLQ]C#B0QU<KTF@]TXF`?$" -M&^T%Y<YN'4L%63FNY")LYQ$G,M/Z0-63"7WHQK$5WN%:CIU;6SN4:7?L\,.; -M;!U7MOGX\=*YFK,^@![24V?*M,XB$?+5<X%4CJ'S^`C>3["TN?P2R"2-=M[( -M%*YBR9;'=-^%_)KII?-BX(N^10+=9%]&1G<<(HY'LV4$;HGG!/NC2"K$EC%N -M<!\S!EID>]C/=\A?'F0YA-M)O8LN6?J2P+KC6!G\L/7,Y5J4_C!?F?6?(TNA -M1R-5<CVH](2QA;DUZ^(I0BM2L5ZA)Y@Z(P/FM,0.HW6!0A[R-;O`SU5P/SUQ -MT"N;)Z?1?ENJA3(5E3#2YOD.];X^4[,YG)3YM4^V5]J\6$>"O8MN91<$R8HZ -MOJ/@?&,$DOIYK5T;GF;PDI=-'>HC`F"UKP*(@/K@.M,E\,D6EC<#B:"JT_%! -M,A-L>TQY"%[,J_YHRH<N'M*^4VI;QS62&5>O+3B=!ST^XY0.]CWT_4=4-GKF -M=GTS/Q%3405:P0^"WJ'BFDH#%0!*Q;+^0.NIX&SD7\=G#-8GJ@XI6K]\=J`? -ML4,6G>L$!MV_:DFC@91RW]9:B_.]LXAB+DVZ?:596RPY%D[6UQM6K?#'54GB -M=^*F6L/&LI=V#>RYKLYTB0G5K1>7MEQSQ$3$"OI1VBK?L^LQXKQ=MMLX)E`^ -MSXQYGCY4#`-B;<6?YGX5N;=J>X&U.R1_0Z:_X>Z8G&9KQN@V\Z`.5M7CHUZ& -M6X9-\%O1EH-#8XWHDJURS!*UL,6DQGV)I^E!S-/&GNG'[V#>=]%+`O;.\E_( -M\5A^%VS"=O:-]EJE(:GG-Q5LA)+=%;G4.,R%GBLP]"DW<MMUY$WS%V7>"><L -M\4%ZZFF=6O.W^\EC8O1B9J92<A1X.8)Q7GZK+VER![^H\W8!"SHL'N'TUQ/W -M[QWHA!PA/F*:&PPW?WC]^WC6K;L?_J,9J0>Q*9#8NA>(.#L$[NN#4$[=%V6+ -MJA!"\+SH[+ZDS64G2Z:]\<4(`Z\WQV7_?5KC$,:U,*I.A93MU"5C*/3OWF^N -MY5U\4*=B69='G@U.Z<JX<@=P+LM1[A@362.P/`RV7N40%'I(]/@3FAC8ZCKW -M%BLO3V*[:[DHN_04+%=9)D307=.PRAV'X=@7TC^PA$++WM?-U&S?)2F@N<GE -M>U3-F=Y(9MH;D=;DKI.T7H.LHYK(;SFH#W2TGC&^</"Z?L>^B`3?*)-]<U6Y -M$9?+]KK<?/;4Q;EV\R:'X7.@[_IN$F=`K/EU5FJY:*=/:8+)\HRCXF8R&OON -M9A>&XF0=FN];0\J]&`4,97O_^Y[FX\MA%0HO7ED*@!T-5KZ/='U<!#.]'H/^ -M9J0/U>\"BHX&"+Z-NO2J93U)*;0U21E=4)G(=6_*7)8/8$@;7^'@>6P5U<PB -M-:I4>E?77EBI[7G)Z#RWQ(:GOT.2=6PM53(JK/(Q5M+M5!+N2Z>[QLE^I"3" -M_GM*/%Q_[S?Y5NON&HH-OV<XVQ^SL"<.YK`J>)'R>YINM\W=PS'X?:Z\E![/ -MOYL=YW%4P6'[!B3&-[";FY5:/14I)DJJ^A`$W3?5M/>YVK89%I*,J9++%8[7 -M7,&!*/M<ACOTOLPQNS<.,>?Z=EH"`GH7$[Q[02J8GJ1]+L)G2B):1C:T#BK0 -M34TOI/E<)@BM-$@W1XA;*UBN+SA%AS(6^Q>,,?W4)U+Z+L2S$`W$?]F-4"QK -MRVY!,@?+Z^7A;&:U?-025].,*NQ5ZU@HI&?1C05DM>#,%ZC<>>9C]3=K#4!! -M'Y0=%&]K)[S24Q9%KK)D6F%4LYZ#X2KVE^N''=$O>S=(91#VXEM9ECQUU7X4 -MK^?&$M^W,:%;&V$E[96O^`5^@UG12/.'/Y>SO(NE@RZUI!&D)3^;4A5J![=9 -MX'1^$AMTNB-?;(5*(4,PD1L(!K&KF3,MA.2\@_[@#GQL\ENC>@_1R138&`Z' -M'==(@&?K&Q`A!7J0#IE^A:\,)BONS0CT('>Y@BJL!1O6T%7;'U_V<+;$]R[V -M&_6I6T'[4EG-YR?-@R[4)/P.L99EXE-M6CRNB54_A?L\)4]Z'..=;^_YXH5W -MV=C0P`:34M3LSR.6,S&[F-6PB46=)PD2R_*Y+'5<T9WXMR*C_VY0*SGF.`R. -MJ(+)UY`H(YU:U]4=SQ;&[W![`)O_AF5823/B!#-:+;/?5'3[O',I1=DBI]Q/ -M;FS'%=F]W&(#>@H?$5IZ*QVI%[S*R1];4Z@DBAN_TBK41T.KK@4J=)H\UY#V -M,$2)3%_6+?)EJ@5M,-RTJ(O[U]!"$!5((LKP_7C;O_9"U0?T0G>\;*=F=W#2 -MFG".-87"3VM)<%>2%9F\;9L2W4L;/AN>TU#30W2?9V52QW+YV6*<E$82NR/^ -MKPK%8*P@J6F1A9?<1LUT!48SC[U6=)4.FL#D)ID:C(O0>6Q"4TRU.W/9]%YP -M=UW030;';"VAH"P_D'Y0`FIF47UR1A9FN;IT1E#CU-,O0O9[2X"=VEF"=@TR -MO/RO)UQS"#RW3E]LD&RM8%_Z<$"&6+MX,8J(9KI;;YSZO&+1E[!K%/I?;07G -M6)^-X<2[+6\MS13DJUVQS*@6/!)/=?6<>XTDIBO;OG_P'43I/ICY^Q7G"K!A -MOT9WY#C`^*EO/,[19/DR+6'`',9DN5ZWC!;$;AHK8U;0=TYXM"E(VUR>:^#2 -MI;3.(Z#KJUBXJY?;C;2+C:(`Y<G.O7&*VAC&3B>]/X0_?;/YQX$7($DML/49 -MFK[-.9VW?6=!%D]U)'+RZ@_RB&]TSY_GZE5PKEWZUJ8M7FJ0Q[C`@+OONQ1; -M(S9Y`0&:[7#0%JP1H7C[/]QY=K;'Q58W/;Q/"=%>B/3H,2D\@0"Z9@5A;<4E -MK&@5()-DC(_^++-%Y#E&%]*WI!Z5;F`K)/T;=I7?W`D#P"VR.V="XAQ!WM,' -M4:N+:349^1U@8OYJ0`IU9-.PX=JYZ4S15FJ02L1XCDP%Z']O-'GIX1!1!?7^ -M]^.(G7/=FVS@GOPAD9U>O%U(#2HI<K6:C<^\IXG2:_,[(S:.=?:R)B<FQ84Z -MKO01TY0<MTCJ$S[N\($[T*$,(W8/NJ1J^P.,=>$9FEOE\HA\D?/5ZTSO!]5( -MPA;N]FV3*FAY0Z1\OI\+_P3RT(5T&6&*)VF<!@Q^+&H4^.*VYF)CF][UP1\: -M@BB!#_"D?`E_#FT;M[X^1O8>5V'/%"0QPPJJ"U\-=8LEWO53^=N4!NF3NUKJ -M[0#;QE-=]0X//Q+RD#??<^G]X1C)I)T]>_N_A$Q=U5J+I\"F&<9W-*L]_`Q) -M'(B3*:?B5U.JPV2)[%?)<,;R2^-0+;`\.J+7!Y-YX\9!*NV8'Q9J*7'9_R6U -M&2:T@O>J8#;L(8VFX3`WVU#`7`^>U1Z\PVGL*N1AGSK6KAGQF=$/SRV%[JF3 -M4[%NFI)56E26&-6JG1?VUV_1`AV`L'MCZ@]NG.V\7?P<<J-[.$^=PJ.BB#-H -M&&_\&29ZT7S;F,+ZH"6<(]WL(^>I[I5)>87#&`^?`_!!5\QY9T>.XD3M>&SR -MPX#[.^L`<)(*CF;%G-Z9)-^X;H/_N7`\5GR0Y7E6!S-L-N+M_E)Y,]GVC/OJ -MUT/B<<D+GM%8"ST]Z"'LX(N4,(0K]?:_,R+RTYU[0F&`XT%_8S06<GR.IK`2 -M]=W,%GZ&-X!NV=?*G"&"*WJ"L1\>]A])X"H)?I+GF6`U]?1/6Q^Z-<#C[W<T -M5W!EVU`*M-(SN[FMF;UF1SM/OR>#YN;],NK\^=^[ZY%&+Y)+%+5>W>FN4NR9 -M9<V1J!P#$=[C;<\(,B/?VPR:6)Y[P*C`)_U25(E2JR?)Y8]@Q$6"X$0AJ;U2 -M!EFL[X;4\TY^B1F2SW%<2L3F@5*F\?&-((9*0(];9$09?>=MG,5OAW0"YK?E -M/W^3T%LN36+<YUC/SLB,YNKVI)>..5I:3Z*!Y0&T5?U&%<RY/X'Q'*<T`\#] -M:-M"];!R^](?+F.ZK(V>*:$Q=@X>X7\,BL:9FMAZW$`*<0NH'ZJV?JU-T$6[ -MQ)>$N%)=:V6;9TDFRV:X&BZHHC2.?C&BZS@::&-<_^AXX_1)=Z!B.+N#R4/P -MUEBK7%7YO2D)K(G+$U2J<!XSKI=8>/*V8<@+7CN2N="C*$H^<$VU_$WN^CN` -M,%02KL,@#E2IBC&S,K.JX[;O.4]U>1Y(:++2F+73F&)1H)B]6]\BW(5-Y]`* -M5:XOXM4&G^^&&,:4/3OMQ&(=/-RF]BJ\<T5W_\BP%;)WN:G+<:'I:RO24,EW -MT$]PYSI\/"6+55[NDE@^"V\WB4=$@,IU*,W9PHBYT=#MI;*9WM8U6%\"%EK& -MTNPYS)?MD+%BN$:$#)3%\`KG3KXS8'2MY_I7M.O?'XNX7V.><4L!(W&O"%<) -M,-=H/I>5PA50PH)C(#4$&'0:8XQRH8G]9\WV]A#NO=EVA41V(3V[`G)U?Y/9 -MLE8"ES5Y+^/;$,K"YK#HPA2PDHA%)+7^%*,610#-QT/W`4_JA"/F."Z?>1OA -M^NTC(H+H3PAY^;"O?,!\#Q6QBZVYI9MS3F3'FWQG5+QI+5RABJ':2NO&'C%@ -M^V8Y&"3H3IZPS(W%-""N-C8UNTJ2E7`XR?*>ZPC$G![S_Z<H=%0QY/P&.8TA -MM6R#!C_D&<]\TLC8M9^*X`J%GCK)Z]&,LEKU24CW%:BY@AWSJX)E3LHT*2&O -M,:3+%>%C[,%>[&<Z2O%2K$G.BGM[$L0_A@QF51RP%.^1&9#Y+2S<3;-I/)\G -M/%_QXAF4@Q3$1YFT3[O;L<M0Y7WG9YP%$2V2_$3I?VINTG)4-2\<'7HZ9&G< -M,'ULRD[&C_%PX7"34GIG[W^V%Y9RV-'=$/@*M0<+*>K),L*"IY*N58.:[_U2 -MJP8NG2LCEA=X3_9/K"^:^_`3,?1<KP-^GP*HB@1OLQ@])6WHLSM1$8:EG3O4 -M^JHSRK`8>4">[8#.QW\@NH1`F!D/(*.(JRQ\PV;VM1:6L;=?X2C[\2PA3C7= -ML"?H+HK\2"P,;'3E?S6;W.'[H#YNE)<;,V1M<]2BD+UQ"<M*Q%Q#!J`[9-[! -M/W?A?SN%UQ'8YA4@J`R;[<ZV\)!%LST;4,5.Z8*7>\/+5N(#SS$@C=U"_L=B -M8F.&XEBQ[YJZ?&F%8\=?XIB57+FUIR:V.:]%;D%)5::]'*J?)84"O`BB_TH1 -MM9AW!OJK!%G\ME9';NT1J<YA"=[GK,6/S]EEL"W8"[6VNX]]:8[+;Z25$JAM -M;LMUD)P"4MN&"X%#^!D?41*H#B:'<?WV*V-/)C<R1#A9S:7N"6.@CI*S4@&5 -M-571'-=R!.J-`K+NI"L'P2"X]5TX(2-@I7:V7=BTXE,N0N3Z4)_NQ5GQU&K_ -M%+_IIE"[.^#*AAV#[/[&#PO)M5NQ"P9YB7X_XQWAZJ=XW1U`9A`$\B=M.0[) -MFY)Y>!(#&GIMNX1=]1\YJUQ333'M>"N)9=>%3CPBKJ_B0NFM6XD,GU@=\22) -MOHU$`^,=6"$A';1A'#\D[.?9\7RD'LA`VJ%'H.'^2-<"22J``']V=2/-]#6X -M)LR1*O^<P(3YHP*#IOTXU+:1+J3%]C-)WX]6I3+OM@6R6Z]_Y7_%G\`L;=)& -MF0=3.,1=`0Z?SL5L&T]#<F]`C]FH3T;)1EPQ'6]=:]P$2P$IOU)[+1SL)X=L -M?"M(F/=TLM,.6ZU?K_N\>267#SR1J(B5R^9Y8=SASFEE;^<F'KMH.Z([2\H! -M"WX!P`K&!79[L]2IT71CF,ZT*AW<:$Q78=]AJ^\NK8FBW>\KP^45[RN03!!M -M3Y)/8RBOK#='/<3I<'!/:;3/OD;^Z@UJS:!K<L(G>Y(`<+B7;3^3T\-:^!]8 -M5U'LQ+.]/UGW"S8=(9,F@)?\P+.B+E%>\7G:H'UWGZ'(JHP7,M$EZLH5L#D" -MCDA/M56\4PREU:)VG;<>'J6`CNSHP>`,(EGN1_4:FX@,4(XE+N\R^/$=")6M -MJY<"&/DF,(5Q3BZO4(.3G_K?R,-I9^5'R\JJ*7SC]+>?-0&B/K!(7^@S[S:D -M'\MQ+*@6,>00<I:5M_R5!$X>,(Y5P;Z""F5`3P#.)O/JTS.J-^H9/EF&Z'YP -M$SPIC*Y='$1DD7C;\K50P!L7)#O7DWH[/K]#6<*<(M!2=GLHP!(\56W[[4RL -M07O2RQUS'*^U8*7,\T+F.3GFV1]6.VU?)-N%VBK.]C9([93)-ZT`WMXV;WX= -M[))]+S#['ZY164%7/QMYI4=*!RZTUOR'PVT;Q9..FX7&?'71Z%L>H"M157&F -M++N)3R%8.E8)K`XM(L(:?MQK1H@!`$OK\>O#/Y&!^"6/0IN2O5^8;_GCDD6U -MOA,H"A5-.I0=2[2"?,<0?4_F[@7[LQ8J6A3U@DYXR>;.OETGH50Z@4Y=()(B -MB:?[;!8X2LC4O.ZF[VO5V<B41%&(K[9!`U>B[.!5\V8?*9K=B5_0@*$&RN,* -MW=F4T7=0)/;4+L=[L$SCHQKK^DE0J&<IOF5VM"<$<'QJFUA<5?V6A)T$Y,A_ -MA/T`ZRT<1']K>`63MN<*1'L8Q*2B3#""!DF:9R7:(,K2FK3:+\TPXXP\(?WH -M=B5O554O`UD)26'W+`A<Y%9LSTDVN!NL>K(7BMG>6^.]WKX;!?-8*&1YT@Q5 -MW[<U[9KZF[22'A;UVF^=6DR)G>#LT=;%$GN0NH`:AG]L=*JQ-3D]IRHZ]U.; -MQ@F0'+M4(UIRO!3JMU:SY,P/OH.0SWP*>>;;]M2U[9;4+A""[0;"'H9;P66J -M<X2?LG3`8T>:'?"1J6T`%K_2?XV,LWS^LTS6212(U6LGZ`9`<4I:8QW(4@]H -M2(?R/2/H7B46?I.L04V\=0=GU&T.F4C2+^M_+P6&?-+Y?X=(P8!J;ISZR\]_ -M8]2?"BB;MBWMN?^-O+8J!TZ]R@=1JC_0-L-E18IA3?,[VT69<%VWX.N0<O@G -M/=CDWGWK-98^*2.`K!HKK1#>RC5#\N.:`0NZ1E$SRGT^4S&#W90:R$WWB\#6 -M`$XCSVN8NOC;\T=A4_Z]Y(HP#@/&*%2(.S2*[+PN(`(MTG<A-^`2QHV18O.8 -MN//4DQ/C@=0?'Z&($VD$[3;_<__><"9,4E>"XKF3]$E6X@T(\,%->]GG$1># -MC==G"7N-46<#I,XU6K<^2.\30A-5*U0)OZ2UCS3UHK1:*BH5&U_O&O4<3<F8 -M"+9V2;\:2X?.D:;QX,$`6U5'W?RYR6P2G'5FT-Q-LIJ&;Q=YUG<@"'<\VK\N -M;F%Q(ZJ7-%EF:(YYDA&JW*;/:UN.:G;80KB>,X;A>.57+.,BP<B/_CO_<D`! -M5K]9"A6I%2KXW391:;36H@;;WU?S=8#]/MG0VF!0E&O+?KI3KTZ(18,.?VW= -M+,3241;,<Y7:I<(-R!UV@(OAESTI8'YSAF8DB2VR?V)Q*,<N#^AY4/G`CKJB -M@B%.A\M35%-_8EL:")U^H.CXGQ3UH2**UY'U8X@TU/.BY/0P)''CU$D=T=/Q -M^R:1Q9?+$W53OL)..9Z%H"6R#&I]*F\]ECJ"NE8^VS?,GW0>^//=*1T-M-K: -M&)FU2ZC\*=%JSVY8EPB^*"49%!EG_+H$%!`UL`]@WT;-4TR]3;3O'CK0'A+X -M$Z)X<*RZ[EQ+6$R"2.GXR0R8DUI5<QVF,V3+2+?9=.QA`_\VI!#2W&X_\]GC -MQ09.AD&N>O"\CS^_G&P6^NR+YII6Z_!QYR_CIF8[Q8>`$6*6R8!,AR9B$&&* -MQLR:H_?SI39Y]A@VLN6H(I$H1F[WH;4MUT\9S.[\9"_LEV7(SA7#8T84:F#] -M1+FR3Q\&B9XGY>[/P7*S^,.,C!&V`02(H5VUEAOE6-3I1_CMI<[$&'5Q\%XH -M/"6()*4L?.@(VQ`/9=GSVT]]P%1YD+?=84"-Z$9<INKP:.[HM#;<'4?J;`;- -M3!E%-,0555)5]*H1U`*?K2W`185Y<4CMXAW-^#,M].K"H?%?$T>5"#=\)"[7 -M[L>9OSM#K.J/&Q-'[8WV_?E<^HX&^X-[RVHVE,:F=$V_'@(JRYLS[S##%DO9 -M0>M,%GMC/@"ZO]SZ`FV#'MI64ITLD73(;EXX'D4ZU@LN;**FXLF0L`832#8H -MF[MQ;AN?$7K/7.,=MS1F<<G39`X3F$!`MJ_B'N1C57_;U9?G3F_,/@`#1E/" -MR[\D>1PT:`R\'XQ(/ZOLKC'#%,8L$>.V1Q[AXOC3IS:72)'$_=2'\@,T$P7Z -MJ+&#,>2=71]WC$+SQ[1QM:`EQQ"OBXJU)T<%J\V[6YX6YQU@WN67+?Z:UMF: -M.,+]"GRAI_)R*-9SIPOF?-3E;8O)BMM:]ENG-0=_BFJYI.T&D$?$ST_^C%L" -M@..TVDQ'RFV#24M0LJU:,/)D<6P7+)06[8H]&KJO#7@?`SIA4\Z!+N>*_?P` -M</7+8^;@/5O=^P,.^\V,O"CDG:`HL_%@?(Z7&1%L>Y/!=&HJKJIAW!&(Q*]> -MLH3S2>*IF$G^4,W_I5\<;XV1L3>'3/-6JS$_9Q_*'M!\,*A1!'>HZ&`*IR2@ -M0@H!:I(%"SS3KO`N1D9\)Q8SLJ*;(H(86B)M+!U>8M%%0F:*H+ZCDQ=RQM(9 -MK<,;A$S7_<H_?\\$)[6=5SN=8HW$9SG?JC#X2`_>0)RG+*Q3,T>!=H?M'_C` -MW$>N1.CPT[[A[TH/N8L/`+18TO/2N^^4+Y.PKI"SK.#-D_W198URJ_+'Z8\& -MPSP*:;7]EJ)K%9*\``.Q;Y0Z@EE]\E":^>!@CV2X-OS(<51T'*[#6,M1#F=Y -M5A;+GA8P=0PV[?^O+N-83?P3WRNO?O(`:\C_F_OX-%:6'*F=0B:,NT8+`3?R -M:B.)VX<X;"NW>5@R>[3K,FC'8Y]A@6@E]P1?)KC-%Z;3PJ*^@"DYZH3?5%QA -M:["TZ1\3RM08*=IQ#D_^7'"P_$V=L!IE!;+\`!V__LEE>E>J3"9M$D):Q%6R -M:-.,(S.,YD:+N]48XPH",7#0L)>ZBU=F4R)K^-WWH,<$&:D_,D!<K0"<;1RR -M/[PB\J0;[6LLI=L28OM1T\9>:1?H_.8LOC>STI9^&$01AT-X4M8`W$Q6:KWE -M`A`[$[4-R)2'6S<C1O0^%FVD\(2C=]/!KX]"N++(>?H`_`DUEO@2`$,W/OCE -MYU/IZOR&\/PU82@):QBUD11A_G4$U&*2]4/M8_#*+?-0FY">]NAQA.3ZX6ID -M_61H!>-FF/T@>?LL>B46G.C\%9K3DCL@XLVG4BP:M1_9_/CSL=`A1)R15(09 -M"-8302RRN/VB\.<S<X_KMZDO3#K"=DMA/6[<=S[):K@O%C/-X-(TG\;WN%O\ -MPA*:!IMO[J`I[/T$@PUN7'''H%IG]=J*N>\<^1N*IT8R-4@:BOJ*==`+*)F= -M.)Z@+3_R5)1_DA>/A[6:47[U8*\K=O>!JM4SH.^"X-JYY2H1"6FBBV2""M,U -M0XPKQ;UJ;Z[HTTIRRYG"US/=E*RI'ECK.Y7*)'-*R1C_10;WAID8@+3R.5B` -M8+L#8AQ[8F.!>-\O$7J^V!T(+F6?_0=FAB?%]1^=DP[[Z^,H)10E8)3&:@5I -MH*1\F\V^X$[S4'X033_.._PD!ZN'F`+^44/4$6VN1D2G!!\X0TGG1@]*GF:M -M&T*F1[K\N=-9$[030!L/MO(T<(X1DPN\1T07AY>%R()KV'[,#FT6&$I1R7D< -MJ9ST]W<CU&6_?'S%X*%NP2YIY?MC`-C#4.0/C\3LH[]3.G4S;(Z13*,%J;]4 -MX_U!A444CQ1Y;)NI0$=W`(/\7Z2P,+%-ILU.O,UV?(^C['L=4;@E'465`S:M -M]TX[7'^TW8/\BP4PX*P"E;J!F@0&>D]H!!%F+'I&*O@U8,F*6U?+>AT67=Z] -M'VT50EX._#LXE'7UFO1J-*B%.?&X;4=B\WB_&B?^`GR=C:<#*0<0I%(,]G=& -MM4YR;FJ6@?.?:Y(?)6JC6R*\5UO-L_I6=I!]6%S;R:X-"^5.=:P6<03ND1V> -MND$O'^IC(-_`!5@6_8U;[K5!8;<),K"38U3ZV3CLOI=S8H\_=?'-20QY_`%Q -M:B68X^I&WUC5=&B8,#&VQ-LUZ>I61UUI:J_G*_\F_VR3U4%3LW$C(@9T"Q1% -MT:O[)=J]N_'ZJ7%TWY?MJ!BP_U#89A#*#`(:$^I\C8]V%:[Y!_YG;/5R?`EW -M[?]'".`KIVNL>WZE^R3P]C$^-MZ''^0%=VY_%SS]PH.8/M(%>4+]&?_?6+S: -M[?B!6Z$,VQK<L+=IO<,=(U5.:87N)'WF)<$UN\=62TN!"04+)*+SOOVK3N$\ -M/E+(T;8#X1=K5OHB]@D322NZ0A%%GCR-PKF1Q/3C1Q-3=@[8BIY"Y3)&L.K$ -M0RL"OO>;_J2/H3.'LGL>I53ZG`155Q+'I"HT,FS)K*&M5K$R=R4M!V#OB/A; -M90P1A6LG4'\+D719!H)"DP"='IQGP0F*UYH8R5#`.M7_1P/%BVS5V]>U,:]& -MAW485;G<6-T9U1W7BV8+C^D7L#5U\3"3=1EWM6IB`'7KH&;X;;M`SJ:YW1V8 -M$,$][US]E`U[*405?'<)GQ\."LG3H-;:Z0(>[;4*%;6_>!4_!O_*)_(3$L`; -MO0[P.]3PU=_3_]]3#]E4(M8816X-YTLXMXL0%*^]N+,/.:#$L>:DXU>JHZ6) -MFS[T''%^L9%CL3?P"[SEY,62D'QOG"0+-:".>"6<YG>'#XWGY"8)D9_A$#DR -M2T<'`O5I9+,LN^=#/?`-SF74RLF\LI[2./11BGVH%=$M$(KY2NI#H++9\K'= -MF'GPQHP7NXTR+Z8TO!GD#Z'ECU[B+Q]"F;(Z0KBNM"35W(*!Y[_32J.A.:'> -M6$5SN$8C>L\`?THWA;?'YXP%@I50:_<L"A"@DP7R&U'<N-TZ78*$%C@`[?7! -M$>_J>I^B*IFA@GNY@V==8?K!11-`Z!&QO'Y".X;IR"Q'H1LN%1V)K&6)GID- -MI*]W'94S<'@FHO!U61J-(IMU(/$P/+B>]^K"RJESN^GAMC@;<:W^Z';(B?2Z -M-)XI+$!,[((MJ?&X:YFFPV;JG#=8$Y5KQH)HX/W8J1+;"E#KF9R>?:_^FD0? -M9MYLXS8;3BQ8!)[H'._:E#;VF#YJ0&;U_HD=N)E6`>6?A:L>!;;B]>&>'%J! -M`J^*;V>><0CQ+U9%(H!1=*W;L7+E_'=/*H1!$-I'7<T[.]:UQMB!;NP/'Z0; -M6D#;?9>9Q!(8CT/;SM:QN(P?0DX#&0):MD.V_`7>G%$:K,^`@I(*G>70]+;T -MS#G(N%[?&28QA8*DQ]Q>M`ZV=T)W$Y2@FZF!M5]']F,X"4.Y/>"::XS\8#WT -M*`-=+R$J(&R7"NF:+AF>3=?ID41-+(2A3Y8AZZOD]&)+6"^7-V%I/>!CP"R- -M$9,-1<:G-\]JBIZ&5^MZ&@17!?.>5A4%K>I>"\/N+^RE:M3'#P9?!3#<?D<< -M`[\\Q=HA1'+IQ+\";P&XLDP)92"&M"'8.K2'4!"1=.,K">G]N8/,,!;J%CDJ -M#C^N3K],")70:N`-86TK]D,\6C";1+=,9*<65*R-'?$,OHSZ!@?IC_W,9]+. -MN]XC7]&,+EB"T'*#2(F=E<L^*<J&'--OXJ%+CP/RV('O)_K#`SZBI&R;1"(6 -MHFZ;DD!6-U$*/KU\H0LQ4'O:NJ[B1ESCL$)53^6<7_Z*FFER]"E)I44&:[V3 -M5.7G+IW!!BVF=:T<NA<2=&.Z)\1[99\S/XH+\6.V2`NNR[J#T\*<`[T:#]K3 -MR=E-)*,MG=,&A#F$9P^8T[]G(NA0B6\L\0#WR37T07S)W5&V(YLFDW*E5U4M -MFU_=\`R9TB:-`Y_/T&H]"4OL$:2\G#G/-LGC]8'(74&<(#PR5TP2AQK87UD, -M]WV#%,%:TS'5`)H>>-K.ES&LS9_O@Q)T,L3,'N8Q#)#!-#=GRR1/L,A70VMZ -MI0E`KI;:3`.GVGD$9-L(RZOQ?]@([KT&M`?!U[E![B^)S/@\X=/'Z-&46_%^ -M$E[[-*!LBZI:5.6%8X?%/[.L/!8O<2>/AC%O4U4J5K+';H*`(W+79#LM,8?_ -M9Y4!35H=X?DUR3&/6LT?!"1-HCNV]D(L6*U0GVUKK"D4J//73](J2XQ6;1!I -M]G\G`$2N.C2@/ZE]<'ML$6D$Z!QNV#31&QUM"O"+H!;<_1*C*M;M),\@J6@> -M5*`\SZ^B(4Z!!>I&AZB%FAA%W&J`"85[&$C4A4BKT:T\PC&AOV!%]>Q'2^?J -M;$#"L2OZ^$'C14@4,QQ,D,=@<@E#/*U2@^(5>W;1(&A<^;BGAO+O.3-BR]21 -M3@GDK+96PFG=3)SO.1SM.2[YY-0>`<:XBP9&>_Y\::N'^>!(F%:M%D9!(M3B -MY@LK!AH0'3E<`=%BG,BZC2,=U/']YT;EY%DB#5@M3N<?N^V)D=''HX1[2ZQA -MQ(.'^TNL#)4M)^$]9:X]:[N5!+5D"]O[A.MZ.GYD#2VI:C/J^8^9+9'^#O+$ -M`KLYK<D\@34;PV=Y;L2&B(UU->?-*_\`!^.[6T6<.*6&V452@2@F6&N(\]Q@ -MT;[^C@U)!&;-_;'779\QU(,,P'-;E7:2D<D>^3NX@[J$)A/I[XMC'0E"4K?" -M7A`]D2R\/4B2IQJUSV\K!KB*K=83&)*-AQNI+AMQ_3"24_MDW&ID7[:2:5\< -ME2M.]^AX/!9+H"XVE*EEC'\<GC0_%42ARXV$72^YUGA?+^HX[FXL+8*#@KJJ -ME;60&A*R;4JJKR>#.P/?<X1T?7P>$(0X;I<6D9(G__$)NK--6!P3>JLP\!2/ -MC>Z'^E]D]V;]L+04,FWUVLEB*DH,46:B;"-B*Q]<CV'K%;CWT-L_WMNSH;/= -M"9!B8;)11,.IP'*$/WG`_Z>V1"(7(MS"OP5&:L(K":?*JCOZ`A)"N)!2W3G( -M,SO:6W;Q?+@<BOG#;%3;4>G=\YU]75;/GSCQ7C@I<BIV^49V@MOI'@);084U -M!+;%V`-8M_N#+%/WW(YB<LB@SAHJ(_*2IL,SB6`,M9T\M4FXT$_OZ^6F?9&" -MG<(&9&F`O@W#CB+<6FE@8D2FK@*P<`41(NU>]&^IG'@F*/HI`T#,'&U.XD5: -MR$\KNHXU;'D?K1ZN@D]QO^J,M]M'ZC2<'?58C/[SZ]G,8?%:I]-_93][N!NE -M1O<Z$G3N6;"2P)59XU*%[1J%;_>]@>2^C]9,TX(52]6M6+\B=JO+N.(\R<%@ -MO#I49FN/>NY[I8B<HYI,)6"@I%F'\XL>6%%SNB`R[4!NWT1I/D3C0HG=2Y.$ -M#FOPQI76=/,*Z`)*OHE,NXIMS7_<K?:T]OE"X[[+/*?TV+?\^,1XT/NKT;&M -M7=0#YA;`$?[[!%M`0^5NIGAS;'TS;)+,5TFCYM%7%,P>YL2)SS3HB+FL5/S3 -MVD#+X_I&O7U%^6!GT[C(9&=UO*>DX6(GD9BZ=HAN<9H0^BUKXF"R0^,IO`)M -M,,T!1%GD<!%_BQM>_+\?9HV7D:]'#_3XSI3Q6^T@*==?<9(A6N`4VC:$_)X" -M3XO6M4S`MWUM%ZK5.'"(W!BT-6?2ZSGY%C[V,@\6L4UN+O\-ABF;YPP\H8@S -MKV7`>:RIQIJPYJ^]\+(0_FD/B:BV&@DC68&0-K]*J&C$\JX<&EUK!ZWG@]NN -MW#]Q+_<4Z0^JLM]$+(U.NN.[@J\BRL0-B80Z5?H(D3HML4>QW^$F)/,$NJ3: -MFLY2YE4`J:%$D"'>^:BUWP3_HBE$/ZU")$00:=L$33J"A:?\B81*";L@7=6H -M:\0QB7#^NEGT'%/%NIL#!:^XP3_L@:AKL62T*X9\5Z)=>"]9?IGS"!T?VFIV -MI'+]^KYA`/2I%`Y![4%.5GW%@:Y=:!(AH4>C/3F+!,W@=,45J^QMP`^3S^T, -M;(SMODVKP&%R+NH[M-W#XUPB*694U\!SQ1T5H/B?1:_&^,3ED%-^UTZY<=&E -M%!][/*?&\YH@[S),F/%2NN@CII!_0KS^)7$B<J3_>JT<[N\?EK<0CU^XL$<8 -M&K``$NA033ST(P5M]!78P`=Q>\GS<E8<1N,D0`HND]KPMQTN_H5FWT;2:OW$ -M\^D1Q4Z4"QW+(RQRI?8+8AD.@99H(HKDQ0LBPL).2X+]4WU3Y`?O<SMO4FG4 -M;_S5%`EUU51&$01K>A3"T]9H&]K(0?R)21YS+K0PS/0=/]588@\.]$;:<#AU -M@.?FPZD]ZR^0<@9#,)N(\ZS^=9:6)\-)3+KK21U]0F03I4)U-H\)'5\;VO[7 -M%#6A9!8^Y8`3F);'1U58H.4TUL3HNW0(5">D_W1N8>I62O.^BM%E^O>,U,'< -M\$',CS-U,(+B2IC2"CF:GU7=QO)7%,3PTXU0FE?96/JLD69/U%@B!]])!HA= -MH0,!UW$B,E6<OGC!PZ#.CMEU\Y@EC&(18WL!V'2\<?ZIXN"C/'[]8T'RQG7Q -M06M3AI5TV[0PP69EHG<!Q'+8!:STD2[!E#?>:'=8CT`_X6]-?5EX]9-9361V -MM>;A*U"3,4LFH60HKKT^D4Q8S66A*@\O+5&1=>/G'[Z\X53>@LK;PQS<N'># -M4QWPBA/WXI\J4'=_+>SXJ65/'RCE/$T,<=5.*%W=0U2>N.J[I7H3'GBSB[W: -M`>J78=F_G`F<R2?<S"E&YT+3BGMIRM9'TJ2TT)1^WN326#?G\L.VF3Z?@LV$ -MW5C^*ZT+$*SLO`(B9CR::%^F1(8#?:IE2.2-SAT%0YLFNC;G,[01FG9]CD+E -M9H<6AY&KP_0QX8LT>E0XT@$2U%2;U$,CZ,HISPEJU"CS(C5P/,]/Z.40Q6)- -M1//@%(JS+P1Q,43$[5^8A]O$:2UR]8IHI]CK$KV?61_9"C^F0>S[G='Z5XAE -M\*AQ7L$4CM/J=E'CHKT>BHGY\EM%4</"/Y)OB4)'BC_JG%LK+P.\Q*E`-Z`3 -M7=SZ\%`O3YH6=74'"1XLD6SQ#>LH=1@H2O"GSR/BZ)=*WE[S%[N*/A,^/[_0 -M24J)*RP6^B1F.FG=I\%+;$QBC<K:*8NR4(3&[O?G;OWM1L->QU!48$P/)-)+ -M\^,Z><^F]6#%=9]T?,QE"9\.^"/4E%K5U=;]M,/APJ!:F<P*S_C1*@]^_ZR? -MXY>3@G7Y<S7Q(I\H>^B&$L_#\RFLH]^21"S#<>]WY%/5%M!(;ATDDKQS;ROA -MR9IB"4WWF)BD\8FJ$6ZG&.W\KE'6[ZR/[HR6%MV&-3]''[0B4GUF%R>5?;A@ -M+OWT"8WKH"OU@QDDBHB^?HN7W<Z.P/>Q=8FZ/NO[Y<'*VTE?,ZC&J6F6Z^&> -MZ$F$@5#\$/;48*/U1>8$YW<:"35D<8L';4HUMH`[E'+)J;=_FAAR=('B`,F` -MOXK03CC\65^WV1M&2>J&X)CCZBI/QH_-K#MU=/XS0JGU9T4F`Z&WP]X/=\L6 -MGAQ:C62G"-)<*KUNDEO^]F^)FN&//I<R.3\9WS;;8&6WU(92_L1L,2_D6T^) -ML/C>8QE5"&B1Z9\&?JPUOR50?G%EWR4%JCFEK?^F+_E`Z$7:7>WF7<X>+2CW -MC!,6!6["I@!GSL1.]3GSGO%C'%7MCU5&?292;?3G>LOVD$+&3+TWC!.&\,W. -M4D2XWV=4"K""*J)Z!/$<DZ8(2RV5!L.Q!0,@`W,;%&G9GI9W96?T#SK6Z5!% -MYR&$3WX:$YEX5)J#W*+\JI;*X#O2LL#N/*W4,/2!`$R]W[GC2T$?I(].17^0 -MFZYP7OK](T+A9TE%)M<K>M)KI]@:.4TZ.&*53=.:[NB/^I69F.*3*O$UP8>> -MI;<6(U#M1=_/Y=\\3:B5U49@F<<G%6654E_TM<,EUVIHQ_/-8UX`Z(R_*PJN -MM1-@OSXXMAT[.ZG%:7??N/+67YOW0HK<*0CQS^RV3N%Y"5P"DFP!^A^A($Q4 -M=+:,[X9C.4ZD1%JK?K]#<<133@?87ASJ)8/737^9;J4(F-+^CT<*5Q-$L@)& -M&&N'7R3GC4KA3>Y&E+FRX=1![GVS*H7;KI6N`[WW,4X.ZT^FSR?TH8XLOWSV -M"K$3WV)4)1YM+2U"*?CQ#WS'&K*2F33D``_)#`$FP.XYV\F14<E?#T#RU:XX -M/[APYS6!34N%"+?_.<%=7LS?P=Q6O_?Q.^/5T`:&,$FZ+_J((D.W9*4F3N)Q -MK<L'M#9FQXM@M8[5#3J)9"_H7-A%\W4/K/+LI:B[8CKW96=&3A.X;D;4\:W; -M9=R7%1NE<EKK6/O*(?Z/OMD#)9Q(XJC4N,)OD;^P342A<>;*<M\&UU>$KK\) -MKK51[A5V/F>)P;/G+P?,(3+(M#SWXK7Z";9H5**@Z2)7><I'F1E?Y>Q8B1!R -MA^@2A)*/Y"CWI3Z3KSE^!NN2=V-Y_B[:/N/I*$T^YWKJNVB,+'-/`1:59"<I -M^!CFUKB&OL]UE5"XR+=8W'A898=$&9CW-OH?3.RSSJHF=WN+<BU/YV[04`(2 -M`ST!%M(@L'0:KQ].CN^>9Z$=I7`IMUT2[I?M#\>2A8%EE2K1VKD/`E^.[:AZ -M!R/[HJ+`ISZE1A"`HM@U+Q&V8'P4L9!!RF+%M_+9@?+K(NU9\K=:9F4"T<D< -MKS;?+@>(J>M\LT-IWH7_`7M$JO+RRB:^RHD*V42O8ZGRBGJ+C3T"2W&_?`T7 -M/HDMHJ8)9.]LB=$!G<K(JW0B:<%^.QX_T8/6I\&PN%N:N\XZO>EH#4()[AQO -M+C#CKP;FL;VD''(NU$KX4'.0'"G_RQB``[E@CGA,<PAC;M#<SDB(GGDW4'35 -M[D0=OU%KD'<7D6&;'OS@2GH,8K392(1LQ%V!K,ZZ!;O4-]RR:"]%E="^6I3V -M$ILF4HJ5/$O'5BS:45@7-:*%%A"LC020\Y6&<>++<*-S;V;#:4R=I+,!-YD% -M8%@.U%+VG?YG!M8BV1H>Q@E@R'!JT<67U[S1<TH%SH4*8+`0+CV_!6<$0I"H -M)-JU]5%\&[NS77<AT##M#/)'%9*1NN.]&_8S87!J7KH)V)['YC>J^)Y40T-3 -MUG^9,7G^-?USVF53)`/QWIJ*(%HKG?9K]8*]9DAHJ#.,GN6R/[5Z'\VP`KYP -MO`2@1F6S'WI+.IP[CF%2>8`@O#X<`6](CGE&-WBD$&!O&?3/2>HR27Q[(%<4 -MM=$Q\L>=&`&L)Y?DU`/)*&[`PBG_?Y3@YG8KF<G!#$0J"KV>],2U2(<RO>=" -M7G"&?7JT;TFMN)%H,!?+ET.OY%D%2"BUAJHN[/D*V"USN!PQN1JH;/>\%(Q3 -M.CB&7H_L5#ZD_(/@FKP<@Q43XZEZPS4=H`U%@9/9HB"8&LA0:8KE]?`$4E2< -MO&KS]OJ$^V1.5U5^:1SDI`_-OSTZY?>ZJQA!#'52*\-.^SI?OUZ,Z:O1MT]: -M@T2*VL(7&:CD?R6V28)_^B%(&I^?!5WWOHZ^Z&0D4OK6:/ZT.#<0BA6V=%B$ -M`QB&R4T_T_?E8XWD^7]5RO&RP>98EXCNL&.<^N]YH3XUUZ'KA4M:[4F'O!GM -MTBZ1O/0@AXDPF\;SZ\K`G[(JF;8D&1S4V3R2?\O)4".R/JFXV-0SP;]JCI6+ -MM'8X\6O]:1V0KW;<B!<3+8K)4[5-4(N&(9)2)?VZ@FX\$&?B#/Q$LNB2MQ<7 -M2ZS-(T4K">;_BQPRMQ3IV2RP`B7MFS76'=2]#VKN`.S=+X>.E9][EP?RG@TB -M*<.DYIXA@.=^IR?RH^_D#'UZRF+(;G(HPYGJ=1N+-\J9VE3UD?A2((";4VON -M;??QR(3P<4;WITO^DA\3'Q>1"]8)MD(V4],ANDOW_,VK`$<G$?MZ<?*%8,*X -M[UKR,00R]5<]2X?XMPD196-G%\<AE\+F9EQ^H0)V28K]ZLX"!"5E(=08YD1^ -M,?<.,,23!F:.?+H9#4E3^:UFK^D8O'A<"CW\Y"]9$O7$$;*U][W2"`>$MCY@ -M,JKU85+PAX/Y^E:XE&0$"*4"D_A28GXJW8HB(5VEHUS7*2\_K?J@X5N)=/]N -MH8AXN!+EW!(&6?$)ZWKW>[>HD*H^CEJI[_?>AS>^1<6/>P%BHWHHWF'LQX9# -MJH?P<3L4*@QWMIB.]<25^M6LKKAJSMEC[#WMX_9PA9.L3ALT>D;ST(9;0[YS -M"O9DFEFU73OB+=^,Q%CC7(8X<"4W_?5Y;,S0@0IH6)W\?UQG*+[EL;^-8I\< -M0Y<NV.50#(,TIH8,7L4\W`5#.Y\QL$Z*YP?'1Y1(3*@F,*_RC0[-E82F&G-2 -MG,R!2*.VMKXY.D2KO%:Y=*&%ZOL/1NM<B0$JC5'=MF`]O9>(\V-`75V4TA$$ -MXP\";2;D99CI'@XO2?=3`3T`61'2<3*`</?;K`P'CH9>A6&L.OE?6T3[*N!, -M?GQ'7Y+GCY86WG:2C24(&-%D]]8NG%IV.+!]#(2/E>UJ?X75QU0>'V.R'2/K -MF@XP5DNP5E!3@BTLSA&<B62#XD*D7.A^W%LY)-<Q@_5S,ZEVWJCKVWS#T.S" -M0G*OVS.WM""_943:9AV+P_$+A&IT>0H*OU*DXXT/ZZVK7[7=F7O8Q6-/%IRY -MW*0Z]'"WG@``S0F^329`![>X/U.\)&1GIJBP^U)IRV;S.O<,?,/,I9IMTR?I -M)5,_:WC.%4$[QMM&-N*^:W1^7*U<BFV8\1R-HGW!3NQJ.[_O?ZQF)%U].3ZC -MEY8O<YL!:K<SM0(DJD](BP&#V?CD$]GM]N^W-Q>W&'A=<>6S4ZM'K^[SP9<K -MJ:^BY)?`E(.N??[KF=5J\^YZ2-4=@>7'DZ24>\/!2&S-"BNZ*K?M5D]GA[M3 -MW[M^M'ZT,VW4UCSC?#F>Q\F2OS[#B5+]?8;0>EYTD+5%#->ZN\DIZ]:A@H59 -M01I5;")$NPAA)X>@5Y]2%-A9H,?+OS/AI9QA=3[73]L&/IJU8?5D((BHLD[X -M4>0$$E@K14YAAH:GM;AR>_LB7&**'Y?E"4W(M#E1O;%A)_'NY^@4/#RH+?[_ -M.ATT8WG+=O`Y^7O),BYTOMD,0#''L=%9O2[CNUG)>$!O.?^%O%:BM93IJL[, -M)P@)#A82J<+=#&3TY313J&2L.7$\82AI]$^K/VU-%G4KX]AQSS,69=3]&NO> -MR9FWA!M\<E-P*._RVS8.I"NPD)/@0]>M&G?-_:>PVO@(V=-%;EQ*)Y<@9$P" -MM\<0G_@,D5+;;!`=HQEQ67K+(6*$NC!$:4&.#<`9=>_:(^ES[#BMS*A7Q^*? -M`3H*8$J>IP\KOUQ!>M[U`"#$0@M/4E`-WO]\A$667TJ"C-VQ9_H0@5+/";9] -M!@,+!J8C@*](L4`TZ63[EQH\DG<IG18U&Z/T'[(>RDCPG4OSW%,1)%\1>ZW` -MJ?*?=H[]42+R9=E4!9Q\0QHK;#F[![^3Q9^D#?@+-N7R-AYB,/,+Q#]I2+@A -M,__/IC)HO_G!?I1NPOE\QEGJK=H4]6JTJ"X9^IPT`]755TNU%\_`,8$[B+N: -MU6>;UM*QZC$/RHM&*QU'8_!U?/A;J_I4OHA,!C(9,\IC<'<T`<6H9<OO7TWO -M;/+H-"V-QIG\@=4=2J=0;Y<JEH:;+2!.<MN5M^4NN]`'.5OJ&'!-[97(]!D: -M+YX'M%J@;X]+41)/<3M`^Z8BZ["&S-<.CP0L]*GQ;FKH4@\EM<:$$^5]WLA$ -M8_"ZJN0/&PZW1S2";H0332J!OYDLQ,O=&**&.G'C(;\3O(YW:*IK;+O4(R5N -MB]&;<'?S;BSA%6)G*G\?1^"X`*5G(HIWX!<S\VG&(ZO5L0#7JC\BG$3*@@AP -M%%NU(#U_<W>XL&;,<(T1A;MGT?_%+X8<J^BJ!:[FR7&>V#(O;I8AI)MO[X/J -MJ$LQ`QF/RHH@X!LLV?AXH.MJ]!1X?PR5];!+9M,/AM])\Z.SN`*);A_6J[0` -M2QK>:CEDB=MLS9A"[12&PZ3.N::;+:Q@J82.1,?5AN8[(4>6=]J'6RK%J"2H -MY?)^^M"L*.0(=H,41[=XXX/ZPX0X4Q%H)P24\=B$:;V)Z78;?0#\<;`U9B[I -MV5"':![WFYJ\?J%+*,PAMPIO!DC)DD:VL8/[X;RST)<EN?O638"N)W?%M-RA -M2XBNWN'6DM?)8H&5&[>_JX`9W"U5A>0Z<\3`DY"<WM_QO)Q1MY%!0Q6(]YA[ -MXDW@],A!8H<D(>6)75US7(-)7G&X4HBSBK&64+(6R#$SPD;M]K[YL#*W5[(* -M9^<]ASK#;Z/?#>^#1)_!*]E]K3;B'-;46D+`BG0^;<5D@R2NGA[!$D`MD;Q. -M8.8=5)UQ(LR6!B)F.!7N&W;<P&G,/BQ*803:?+1L]O[WJ"P_`HUT;4KZN7#K -M!%><Y9Y0#OJV]$>C":+#UP03UGWM4Z\I*-KPV'J=_>-!&RVK6!TJF[R$PHGR -M,)0C=*[>\5';F$55"U=#YBJ4<\0$,9"A^O7H>3,T=N2/XD)1%]!X3Q)?7I:M -M]>7KKCJWKBF:.OJ$HDLT#0\L2?&,2?%19G#'QHZ1\ORA<4IN:6L#5[[VBH&V -MGJ/4+VWB"&NP9F-C@G2P$D2A_NJ"`H+4ZA4F9WG!'7`&97Z]:E/WJL`\Z^,< -M!$;D=3_NPA>)4_*!=@P?D#1/@B($<FB.#N\DAQ3G"FGMT/X][+5[:<XZ4ZYD -M&_5@Q.WK2*A6GRL>9R%![7WD(O^4B/`F=L^`YT@LJ\Q4_6%4[8MP6:0Z53X& -MFQ5C9#IK$E.OVWW;L,YNS^9)`_\7(#59++:*_AW>2)D@/HC5K37@"4P$0FMG -MD^M^O4_+7Y30O8XQ*D,LDS>F5X"D@)[Z:'-''?V(QH%A_QL@X*O\NY*4C9BW -M#08MUN/K`S"D?</VIY'\:MDY3T$.72^A1]96Z=,FL-+64H&)2D@$^7;PQ`G2 -MVOX.^NA'NPGD^4:^0X/[0F!X*)7>"QC"S@[)8B&Y:N$<E<2VSW&;3X&J$YFA -MMDXJ9\D<:I(77'7,7VUIK%GY;^Z6SS%!&D0;Q8&A??&)QO8R5EX^$*,-+Q!= -MT@Z59C9\HY9H*!>-%]STH6O_UB=,FGZP)<F9?)%U:YOIY-([\6":TN>2WWZP -M,7UFN:@K65I&RJ5SH+LG?,*TL/?8+OABF@/G'X+?PUV;[L^!B"MP8?1RJDL; -M&,\BUED-QP'30J(!0!Z-'5CJ#\003QOW34521J.TY@8JUKP;9\'FH\.0\?#` -M7WP/=9AP9G9GM?CWD?A5I?#@_K*#L+#EK'%KX"7M5(F@R;?RV6T!Z,J"!2,O -M>M$X3$)U`]4I7,6]M'*_M*G(Y>9G1F+M+M'W:='6@])G]D`#`=I:_F('4%B> -MP,#A7F.%]#57"8O;A9;KK[&(S#<?4^7$'$0%J<C?QA-$5:&`+5^D/'#!X\\X -MK%.F$632$]-;9#M9H1F\^U"+[^%?)+RI7>M^Q)O1V%3\:O*%1>^JHV_7?UK" -M/06($V"0V83$IBF)2V[LF1HDX],/=1]8>\G*N_A'J[L.V$!N)#O9?C-44^^W -MH-RZ]MP8L0.PU#"6=+YI2"JZ6MP&2E^)'6>O'8![6)TD,]BQ\L7JEN.:Z&%# -MNO.X)9,*$5Z$@455>J7B&P7KG+A9HWEVB7'.F#;"MQF6@H%Q'O\XI[!%#%I" -M1/.W5)(XS!1C[O/6)3RF.G5=80@#3IUI7"K>BLI;5<R&YIQ8?O'GW3]Y^<N= -MDGZ-(O#W?-SZ;<:8X7#.'"""R8_H30.DNW`NGD2`)F)JU+F!PV'=5/[R`<K_ -M<3O\+$>*B`MC2Z9W&)13ZRIO4%QI`B7V;I%=$PJM3SL#LW90(4@_)UQ-3SCG -M``Q_I^+[JX4JKXCXS,`L.)48M-7;P&-.?SD:;/8O\[8"1\@/6PCC;>$O5Q'3 -M`AG=.IHGZEWE;!J%1D^)R(DE%D#\(K@2YSZ:#C'$))&AA!%`.P6[YOV^R&C; -M(K*23U6,5Z\:2_*$G@VI#R0!K@6P<I77C$$EPJ)`MS@PST#N3P&9+`&?FZ8S -MY]M@>SWKN\2]Z!"S;L^FV3287)[UY9UAV'KX^"<*-FR</7.$Z'I+G;#@F*H7 -MV[=]48,9RY,G9(D<FL@^<B?S-#RV;EWX:3F[/A!,NPJP?7@%X_$AVB"1X>KL -M^%,>M?RP/R&PJ0>:GOJ:*S]@0[7XT]8!6W123XY`U4<P,]2V9DS79U`TS88> -MHF;M,$%628**O""TX9;R5>5:Y6^)/\6Q()G8-/@+RW@VO)U--#K.#910,7R^ -M?+<.+]>P:$/GU)$"Q0O<2OA$]>BJ+#>=KO]I)0^J#Q*!%4%!D$0B?;$G&9(* -MM[X+O,DDP-C--@/!5A@=\3:/PZ>C'TE[444,!-,3J&_L=K25IYE+^1(PK=25 -MF%Z2M'/G/K.Y[>@UX5-CD4\8B<R.>=E2HSP.,"P2(_)<_`LR)-!>=4YT1'QN -MG0`LPSE/UTO:N0^%"\RR(!G,14^.:.B%S:5R.@TM;@JW*I3>T36`4HX6>5M7 -M10]6X8?(-+FA3)$3!/F5/-&LG*""]`6^Y0YGT<DP=?NN>%<UQP^=<KDS[_Z- -M+R^%DMC#-9`1,VT*8[F-QMS.4:-AI!B6<Z)9O3UV^:Z2;3IQ9TC7AT?BVD<% -M3NAG3#L'(HAA"3=(I,G-.K-/MW5UW)6N!M#AG[(O@S]A1]H/'COY0`:]L`QF -MFD4.(01;@=[2M/GNT*A>KI;P6`^U'H,?C%A87"YGD1>UY)*E@.P_RD]RQ92= -M\A?>\-*\$'$>X4LS0C0*$@(],ZU[J$H`R?3\;/3+*P;334509?J!T".5)RY> -MKS(V:8^=597C4[BYT+<_OFTH1A*"@Q<OD)!CX'4@VO;%JO=Q]M(F1P3/7DGQ -MR2$]D41S^C37@N?TIOX'Y2[;6<VT8)LT:UK,;)U]#(O"&6?.+<[U0R%E("8] -M;_/])Y+)Z4_4NA3:^GV339F`/G5JQ^&<HHYZ=',F[*4RT?JK!:?WGM$37E\H -M`BZ)L.#:KD;%,*7F(-%9TU8J5Q"7]P)I,'32SZ4,IDIH$[>X0:+_[1;DS$$" -MT5D,!I3'G#OO"+KR@*&M)G#R8(C@.VLS)G]0IVT1[IHX:^=9PXO.IA3!>.R> -MJ'S$)0>90Z?CP'JIY:2WBWAHQ9<E2)ZON-O2AM_RH"NY3NM-.$>L8V0_83*) -MCK'P$U2NN%3=B5FBH7#D@BH3[P[60?^Q,CH$VP'`?'I:8+AXZ-;!H^%G%X=[ -M<QC4FP:-H='KE('U2F"@\(56]Z46+71]Z1Y0E@8J9Z08OS=K+O:1?L8N1/PN -MY_`;8F`-AF>`($#,"4NT*G#VYQ>QU%#Z_X2/S68>;;!)9,[FN(S40S$E@_=P -MEEC$U/YV#]MCM1"[%K"?[9X<Z'E-OAP-@0E%.X\8%[L`SX/Y7@AVHQX<<9NW -M,8D>!Y#AI?U4B!:*X/)(WR0(LFT$6:/ZTWT//:-^'/C825Z#J^@ZJU8#K9P" -M96B:W#;N1FA;)&#ZE<XEN6J7X`(V/]QTG!*-*3@[;5SG.HN]H$W^U^B3V"]- -M?Y#"F@!,RCO05__GK44U8IO1^OX8GHWQA!L'G4:4C\N/2I6;')-"-GGN]X(J -MN;%F1OEL8=F.V5<.RO\>H*)CI<>;5+B`M@>P;@)FE1,9$(T:L:16KN>R559Q -MM.B2S;[\R$D)T)EL`Y=X14X:-*^AP';5(8:9T>>K_WI5K=M,N.""+=_P:52$ -MR5H6K4]5SYC([PZ>5>+][#58J5+Q.BCQ4;$R"3H\7RQK;VKG4LWW156@Y3CZ -MOXD.G]?`11B$2GD##F_T14H,RM>3A.L$9UH;V^J@I_06;N%0;R55=\5J9Z=F -M,[,Y\,F<A8Q]/(4,E'&/!H5U1S-CC+^K"[=,J7BD0E=2;D:+A(Z(OD:@]"=! -M9+[KBG?#U>IRGE&_#LV$"Y7D%^LHP;C+HN>!ULT!J%#V/;'>YM_[,<3=[C.F -M)D;B\`'F)UI.N!P<D;\![O#,3KO`';P]<>!8KKC*[&0NK0)OU!1)\M.:6M9+ -M>5I._2@]F]/<1:QV$V!,5$2J@DZFK2#3%AE/&658W?M!L/E:!G5S:)C@Z!.6 -MT;5[M.K/``;KSS"WU&)LT)JO<SX["Y&JD0VNP\DB;\OW6T"].++VYRB>J%DN -M5240N!4;3\JFF+51;(2MW:SM$9:'`B!33//F@@1?O.8S/^L-E"X5^\T#[9)$ -M1RJ)SHSE)*X9YJEYH.T][.+FC:8K\QR^VP;J8]W8E&.U7()&H\Z%JX%[4)%H -MUEOH:XJ!P%'ZD$[</?28H$XJX:(F(D?GE!_^EV>P9)`IYD[@=+CM6FE<5AU3 -M!QQ]*9C9\P=L?O`^UWH94%M_/XW:W/\S',9HJAUSR`Z@V-9B^&LC\:(H[,[= -M\5!LSQ.T1J45%:IU#6&[WQ=`^9*(:M4O2PWI^>/@"TAM)=F-VK)/;^NXFRQ5 -MH7J_:&VC/ABF%9LEIZ&0#JZH)D$]!%UZ,8U$(SE;^BVX\U6B^0`:KSFD:(`0 -M7[9^<VA^GWD*)(U*A/:,;L].J%X!_E4-XHWE/BR<@Y2=9)?29P&K';<<9C[G -M@A5W&C;_D>MA28F8]`'5'CQ>*UE/K9_P5APLFK`$V):]EM,>7C6Q-&VY$L9; -M];85C%<;)AFIXRBIU&`?#0=-(^00@4A7_8/=7/]>C>V++ON2*#K3D6"4R1>D -MX-F4W5H!.&#IM`ZSJ0#L(>4Y]Q^-S9]OOZ"JC:#DA`T:#HQ)3A+<J**`@&C: -MH$99RK8F@[`.*L,F+BT<.CKBY+)SRS?_S-748/_;_1Z0/4O\.I&EW#NP,W<) -M\47C"_O(NK!W6XCP./@VA^G5?V<A65CZSHYGU;@ALL'*A?2V^="H]^]5#V9> -M4QI8+,9+=@CGS=R)11>Z)P[9+2.,M,XK3U]F9%EC-PM3_)76L7SZ;8=D?\&, -MK_D$00UH_]R^DS[^0L'@?WQDS]9U`!]U^]\`D#B[NNG99*ZIHX?`A[$5CE9Y -M=$E(FSXB>Z;""]HA>>C^T&I_AWVA=@3RB`W3NT_Z'*+V#*DFQ.+#UP/@[YM> -M&S4`AHF7CCF527\V;-0=&!S.<J<8RD'BK^?P.G\,?)MA7F\S,\8,RHAY-[;8 -MU'U030RI]P!?&$>WUG`LB9V-2)8TT)A(&QQGBL^'O>VB6-B,R=8EGR4XT):- -M9EMRYJ^.=J7J70F5UCWT5)%6Q@6!9#1PG1^V>KEX1_SS_]49)99`^>K^NYJR -MR?`8*(*A*;0^L_&'`U0H"#!N?%8KT2S0[/:*]F7\):1]7CW0&ISY8C2F;YJU -MB,GR#L<?^%2$[[)9=OYD&YVD5^Q);5P7YX;EQ']S2#"^Z9I;$=-KP/![R'!B -M!OM#\JU)II;:V=U5((Q&O6(U,UL4_;Z+(,)(DR#]%43Y47!Q9F`5K!.)-5SN -M#7F<,>%]L9XM5J-VLUH,WR_%:0"%]-"(EFA\O/-R0J"R0ZLUVZQYS0PE] -M6!QLBC<HNXK@7C"Q5@T]J=UDFS`3(D2-79MMN;%84UN,%8OJRRYG:7T?]_8D -MB`W/?A47L/7;PF16W"W3$#H5+A8QPIQUP")%#I@2,(76QX:G'S_EZV.^;5VG -M.FKA]QD!/>&EYI]!I-^CGN-+$M$YO1P/KS=;UGB+1\>4_P*V_UA;9ZI)A?$A -M!V:`"0S4YC@<F]?EP2UEKH68Z,4ZOCNHG(E]:GN7!&OW2QD]7<WI^F^W^B," -M5X5$[91P`']_G;/!-+K56YW#0^:J.)6Y2I:4P(RJ9<`9:^L>"$7[CGCW"0FN -M%"D<4!@M-$ORVG46CBPHVT!J1>$++KK"P3$,1,7E-UE#8)WT$?!];&J:IL+) -M;V9$49/\*T&8E!F\8\)8"U1I3KE=KZ_>%O5O^"6F=T(SV;^D>PT'!F(VGW?\ -M]52V3:M/K+-P2K;)75XBL-(99:\>;YY2CQ=UC3T_QP1=EB.[/Q6H5W,:1\ON -M9>0D%1C`@-6G$U]WG+7SA@J'YG+>7U):"W499H8?#Y^J<0"_7/K<0T89MYE< -M+ABPHE[O1C=2.O\9_7Q22JHVP20NNV(M6O'#=CK%,ZL131_D\P$8(L"HM?O9 -M5ZV)!O,4^;+>-]>;&"UW*R^FR=8889)EP;A*P!OQ<,!_4:$.VM7^;%<LA.A; -M(T+XT"_T,NOSS\F"I<M,L&1)].@=(X.\A2(<%O:P\A-#FNS,:/#=F4TWMLV] -MW@4ME`73I($\;ZNMHI*0T8*,#(>MK`:?5T`$E?7#++_&/.2CHU"A+RNI/D]< -M2R4Z8S4F!J\AF`-9(']L6=9T-X.1@NI9CSMG]2&>/VEV4-P.:42>7ET//_/$ -M&$@)T_I`3(W@=H?*3TD`/6@N)V916PZ,Z6B_CZD0L\;"IJ-+.XW2WEU-W05N -MTC$]"_#*RP`XR($CKX=]])6"<'/S.*W=LY<\J"5:P=<QR.O";M=K5T,VQ0T; -MVKQ1"8CGJ-"\3>$>F-<1NSOF$:;Z=W?Z:J#6J2UT_8V$AMFA@'5QY\52Y)30 -MAWJ2$6@9D_[`7,0)7/O@?SA-3FA%[!TOLQ4"#$C?.7#R59SGF[$$R]BZSC)3 -M/L11.K1^16X\C\MO14D)8-68NM."H;'=AQ%/L506;NM+2B&C>+(A94OQ\:G7 -MQ]&+&QO0:AG:Z-4IDB<`C&W^@SQ7DJ5$4U>E>[.F8NK(T3!CGI#PPKJ^<H4: -MT-212<U/XG<95A@4U/'E"0I\;=./WYF@`E]W]@T$TK<Q*YHBQNXZ79`/N-PF -M$FD$\Y5&\[5TDI4-_3FBD@`K0IQ#'^&AO=*1G+24"MA7]O1)?E*J])N(.L_I -MO82H`EM/%\$D@/%)3#6?(.X-)8*E'X%/`(4`.#[<@V`26:Y9):(`;U5*QOD' -M7AO_D7>N#1==<-<%E,NHTA3@%LYBT&$8"=I,L$#Q[:5Y24O?.><]CGM[0[[V -MUU@&C]'9K$Q1]R'C$TCJAFR^A(I;G?1&:-`Q&_%>1GI5^,,T&>PKHYU<W.MC -ML3#?I,''?.'E#[M#C]IDG>X"H'"G2R[Z`N[`;6<HB-ZXO21(*TA4;KN40]NT -M&QOM(J@&_,H9N$J=($6KP#(DG>8VW('44=X2^HSW,S/#E)&;7L37',0Q5YZ? -M`=_\X7WU<&:$*.._+S!_#(MEZ;48E2]XEC[;9BNU<#`1"2#]\9?"71=G%D;T -MVE1`-?Z)S$R6UBD&W):N#ZRPRK4YT'H(,JUE6M9X"86,Z\!_Y\1`QR7ZT:J9 -M0]S>6;]X[B?2!,%8%.BJ>(8$9A7DL>\$(JMN4F=J3M#Z7AR1EJP-_&I!#!`" -MF<2(FHD?G8>E;?;0U8-Z#"P<$SI21+IBTO%=)Q[Q<'-?,W+YI0^L>Y2L4@^9 -MK%E_2%8?>P6TXI4P9-PJ1`VN7*[_5+?Z<=\^3&HJ^>X8`+'BM+=^U)8[$S>) -M,A!C^"Z7<TK$<K@1_SXH(X=G7'G1\`I347V/@]49;8,JOO#O?*!$C=4C3DGF -M1$\I>I>;GXZG'XOY%Q`'^.5G:-1R(S2][RG4S^?X'1EXY*T"6,#=]DE<G=Y< -M75KBC9HH7F=#9:6*<3!6=`P*+0+7A@Z7_=[O$#X)VK9`7;.W;)B*39UXU:8> -M+D)U"U4BG&GW_EW28CR[\9X/--A<(D*"-OBI0;P^[U-HT$1[&HB>8;EF;PI: -M`-B23\]2G5)<(K(MHF_0%/ZFC\G[SO+)_.0U#9DG<=@LX*285`G)IQK.-"I< -M4/P-TN($:7QWQ`MM;5&S3,["?PQI%/O[^,:W)V4GL(,RAFUU14$*K#^*$SZS -MBB:91AN1*Y.0A`=/$A*`#=9+KC:3I#+S*A:^6P4HF?RV%X_>!<'/>$`G*9/- -M7*0K/WHU(:VA\AXI4<Q[&!C1=QF\90_,)KG%9K+I*NHJ[0FZ%E`75S>88#^- -MB80#A&)]0O<[R9'Y)V4!T>N,E+\<DJ)%"92-Q>L#^WP73C]]^6";V_E5`:(J -M$PQVM7>:-REY;E$7=G0$&&(EI.K>28`<*6P@:H=&A>65\FIJYMXBFKGIU -MWZ5X31;/`3;1[08G'4#YBQPY>H-SH&*#KD@_^HP)#P^0R_ES\GF_7LL.!^(G -M9\'OC/$3=@_EGSNU%+/'OT`Q;)4=D7*:03.5>@NPATG!U8L"_?-:7F>F>B.( -MJ$:+CF+@$XG:'=%W*W]!Z(&/$'&Q&-B$U8OD;[\#&`-&[I17=,*HOT]/HC@3 -MUYF&+&N&1]3?0I"L5^,/X5^K1E:3>AM6,9(FU-7B2$A0?3[%%`N?:[C[-RZ^ -M5T-#NH=O!G^C7;1'LR?<$C<L>0FZ2[]-K&#^G)KI$S2]LD0O[5IIU^D+-7UE -M5"1N`<^P;9`D=XFVXT?]*>3&,QS"XDH!\[#*L/-S)<PJ;#"H`^19;]Q%#W8N -M`LSI[G,BO4C-P4EA)+/A7+L6W;T/2X\.B&[-R%:F5F49/;A"TA6D%:<)I+IF -M$<>D.NF^UD>#"MR!#V^67H2XX5WQ(/K35J$16PMKRZFVDP1:QY]6WA:>2]EV -MAB@HJR^!<SK^9H^IV7S7NA&KO0\OQG@-Y)356Z\,\XR^D0GJZWO4AR6'F:LW -M6PW^%(S[AEW-./@VGAR1"/9^.M>$6'W`6B,U5'?=&_RWRA&MY0;E*+L:O^O1 -M-^?>O#%N;NI4VF9D'#M_*72T9CCA;2N7\FC4DT?VV&D;2%EB1P8,?SO!Q9H\ -M.7TX[@J0#TVT2A4+6H'2GIU(NO+T7#B_[>VHO`S!R5XQ'*96F"/'/;B;P#P> -M[@90&37?((%B7N\\<<B54)/$ROL&,$79RL="O(ASRU+Q<R"@M5]`9:#G"?6) -M%'9?K97!6>;V/JHD,)03YBF(HL#;BRZ$/GJ+;]_,_`*7N1P4S8[AE%-84UY= -M:\281;W&'6B]U/LO[1;;8"?(S4J@XZ?0V4;Q^1%M-ZR)"/+QPHV_XE9E<'=E -M?R2B3T+DK*WMSA0;]J-D!MK"V>Z#_"6V\I64L>FZ[_PFO'3J?/&4N&4]<P.S -M[[O2QF^4Y[5O-=`4V?\14'&D,3!Q^TF"X]\PCFZ='&+HY]?NJ@%K3%>?R+[\ -M@02Q'=FN`%Q>D2@0\@M6ZD7^!S!R'?68Y$TS:&ZK3-!KO+_BM+T9?*FV\]=V -M87Q7)O'RF@-<CG=SYLY`XV8$9#B@NO0_K=\S<C*OG]^&E84+"Q)'\_C>)V?] -M*^1*MH<4^YC6;^T3T7*WQ)$-..F,DUV_O,^PU-B7&+==(D!L38%=ZZFHBAA0 -MV^/2,VL+:/QY^XS;TS@9/1@\0UBLY"'-2=.AH-6K?'81`7\X?&J^N.R3LCRM -M@88Y.N2O=M.T]I>P\NY%F?<C@[AZI]AFV^&0[.VQP`2[A.?$8MW>1GV./=\H -M=AS:/%@;N@+6[2Y-=X_)GI#YM@[:72K]^ST\5JYJ3F\F-!NND;4<7U\:U6TU -M8,E.6<^3H6#@&+SN(/NTP?!;"L]_J@L.<5R<E/#D!^$T8<RH/)%K,]P,!]B- -M@R+=Q/":C`5A7CN976L#5KW6H!M59@;HC\>`ETD49>:X_5$I3P\4+QQ-14"D -M!M5O4@7[QDQ2WE_(_S#1^.69Y)=/#(1S2S$B<X]$D\[+'E<":7SS.*D@17PS -MJ'BV\K"W21T_^ST8II`V=7Y'X=PB<Y]<<YP!0>AZIVTCI]WME)>JYN+=W46B -M+D)*1/*(ET5C8\YZ_OX2<L$BVS"T*U-;]1+Q`&E1$BXDKSN%[_YNJ=<ZMP<= -MT(:K)F=R$`^43L#B\77>=S?`<\\:&7&$U+N+KG`)T2R']K#81&-K&AI-Y"IM -M7<8-J!H)XZG.F&"X`G07$10Q_,K`F$D2TSZ8M]>3CCTCI]DQN@^Y-UQH?Y>& -M)_FK(J<S5",J-SI&R,6\"D3I=1EVJN6)Y\E$2G(F8H<L!7"#]^\TRM&"DFXX -M6`\,@I(%@-0X/W&2JK_PE+>IBEE3^3,[@TU5`$0,%Y'FV"2RHW"BC6+K`<RE -M:N2-V$\FZB78=YEA@K5O]L`.A^QQ6^UFXA;M(XGY"MK`RC:>>C4S,.Z)V$OY -MT[ZP<4!?!$'&G\[CZ,A[(\KHQ.FCI[-Y=#,[DL!-?($BBLJ42EN)-@MCCET; -MW>W0NGV3:S5BD6/<->,K/8UH#"&17S71_F2&P]T-:D6-!JO;<WK3ETCQ%GW9 -MV,:&<7-]EJ>=.\X6+WWKW0+.P\`[+:SN[HO)\JL96PJ5/EYQ,Y[K**=TC*GM -M.E]@T=Z@QL)!M#?/-AA.?14=/+8@#1I!'QU`VYG1GT>DK&I&0!J\G7>NB_\_ -MC'@;"2A%UO=0@`+[N95Y71P/<%NLE.+L_90^UZ#V<.V#RC*+M"HQS5A9L?*S -MF8R7%QD,2.7B!G0_V:X^_KI5/\>(.;D]_>$_Z-V;$["-U76W7N3SHOCRT!KK -M@TA8MDX4<E3*1G90HLWO[EOW'#SL]*"_`063V1;`$S[=T;&NL;#.0$!JP6!, -M:`;[2ZDNC1FN]?DK:FF&RE0DSAG);SU[O1<41/MK1)ZZ1XR?>MM=.]_1N?4K -MB.K>J'-K(Q#2E\4T;J%Q?>_$>GSD$"1]3D\!R4Q">E&7]._&ZW/.XAJ/!3%G -MC%!U25A8=D(B(8_'7:K2DE5GQIK.4FW7-5)XPUA;O>II1N9R!M]6UO1<O+%_ -M#*J\<<1]N7.=!IVD`Z?"A=U[`%PN3+5>^V(2__'*>T_"8R(4#>C9,/*!IL02 -MN.0=T-N38M%4<ZJL8VAU-TCI!F)^<ERS`?'4(VYS<DZJ%][J\\\"#7!U`QG? -M@!TXOATP!&@(WE4]AB;,.!P1"]Z^ZLQ[1%F>9^)4L1!%9O!A\__VB;-?Y?E: -MYQJ,)"V[S1CCO"=`KI@T)5I#/%`]BP)E3>[B`/P%WGB[JFACJ.OKLEM7!B>Q -M\#%_LL<,%LN'5D)]7A]L%I*E$#4DI)%`TQ3<RM^.+C]G]Y"1^DF4NWOL$`WK -MOSVY>(HKL5/9LE/9$^>7B^)AGS3P,*79%7NK@B$I676LY08(KO\MT$!BPY/C -M,$6K,+A3R!WV:*GXH-4XQ`RZAX]QJHV37(UTMW)MCOI.PZX&P0Q#%Z&+M![8 -M!F3K>F-W:WT]4/?)?VYL3\?<87I$>TB5-I(![3;)*&.HH1.85"^6K-P-PT;D -M;&G$E%AJ>_2!ZG;@,7@\_R!T)*+-C<Q_,E^@F"[NH<QRA-U="\I\HM@8N;AC -M3QDIAKM#2-R$RJB_0.B.?!2!$^7F^C*QO#\V73LH;]*6#B2L\`A6@KT&!B0Q -M'E!WK(307O0DSW<HPRN:54$9,!+NOL<-JV61TLG(HEKK$#8'::YZW$^+.4'$ -MS:[L[YS>\KM?)@OS?=FVLMGIO0<3LQ9^K@$`>;X!WFGG,MI5<.<>X"11+QG. -M>Z>:56XUC_O$W!U]\`"N@ELIJ<G^J"P]L_M':CJBQ:[4!8+(S$'O%"L?"\Q` -MO(5%":XJQ:93%"MH".6T[EKC;^W$PII+?/)65.0O@(X??F&I^C@LB70'OPS9 -MF0:<U847T^5?*.<V?@E,+/401A2@JEU3[Z*@PUQ<9V*)OL>M'_.2FEJE%&W; -M'[45&,.+?@>32;'?;WP!G[^R1'O$GDM6+ZPAC>X`@\1O1D%W!J_#_<'AXH_A -M*20%W(\Y&RV_,ST4EJ*6ETJ$:MJ'N%K8O>@66"O^:&JYNN'5?V%<NKZ-&F$T -ML16(V_2C,LA?)FZ%6AG83\3I8W;>!@&>/U(+$.TV&Q/M-D891*@!FI&BWJA4 -MI6\-DMI4[!Q]M_?QI5V276FKU*F<?`("V!F7:E=1XAD>,"O4)CP-@:3;=3?B -MZ[51**V"+GZX5<[-=C7F*7!^@ULB/.I$^%63Y(T)F)7?C-P3[HNPQC_6X'2Z -MC$B1:?FINX>SHHK@"L]N":J?'"#O2EJ/N?M(;"H`1!NY+.("J>S%'*ZL:#V/ -M5"YP;'(-N$SN9'CFT;?7KW3H$0?<VMK7#P*8FKSU(PGW78"Y\1_9X>*CVU\V -MZ-C:(X77K=G#,21W/5:$K,[2<N\L-"U`J>HWO/RYIH4HJ("4^&1M:'J:[ET[ -M*)C#PIBHH`:&4M*6VQY/4?/&)=SKJDI&+COPZA:`"^I\>1Q"$Q,[CW29=+4) -MB)A8S16L6B2$&HU1<]2HR/!L0WTDG4S+/77;FDO/GW#ULN6F*QK[_'U.X*]C -M7@TVN\_(A"]LJLKSFZD-_\ER@+8R206#H$-4-CP@$9<##+0'+]]LIMW]*5(6 -M$U/WKJT[04KH0>_7^N@1?M9FV.5X5\P17)`,<<>'I#&\!7UB6YMBS577[O9$ -MG3&I4,R/M$/8T2<ZS5&\K=#6R:#7YM%!GV91QAXQ%^:MZ=HY+6FO-N]&DM*` -M6J!@QN2[T9E9)4BD+/0$*;?B59,*[=YGOEZY(+'"P]<F:-]JT+KU01E<PK?C -M&GKW^BS%+9`H+!!-WBRGB[@LYQP0#0.GV>3O.0^+U"G1'^8(_A$&9/]MH+_3 -M2/0'V[[V\_N=8!+OU&DO"QR#\45I,:5\,#>`]L]Z`UH`(*=[^>(\+06=&QR? -M4?*P*S`XXS+/G$V]?;P,D0XRY)EBVT6>.)/!E12)`'5%#L&H"V.Z9MJ.A.;< -MF*I6'!MLS&M7U<$A]US^2[F_(6(1GTV//F#:($=XF#6)!P[@Z#LM^8#PB8GG -M,],G_X_[9;RM_UV76S95+04"H$`<5.TPD<^N.OJ&4B^"5.3!DF8EE`A%W5RD -M58TW'8+T7"4C_V`NN[\H$B$_V7..$NDRR?ZA^[SR"68?%%\8OLB"F"QAFK(4 -M@/:*J<E/Q<U2IYR`M(P;O0,/Z?;-&"7-UH:4*UYY[PLZI<"@$\OUR62"_-B= -MNFOW]I;K7":>129H"E+V*88S-<\)7H4+>-JMFJN'0*N71S/R0C3>V\0R=2I$ -MIL"XZ\8_ILS2++N*^/F<?;SW$LB^`_7`:Y?75L1MQNNPW]]K4E"YP]B.MG5B -M?[SA^*.'3BVA6;^R#XR6,D+V_CB&I9T?4,N$AF<2C71)9]$"W+1H@K`:\8!, -M"Y_YVC&%Y9\`<UB=U^%[N?&6#2L]T33TB=0?HAJ9C^C(E_1E'$Y$_$P&+_1X -M>WK+QPF3@8LJTPG=)>_G2A,8[/8^"U(94@NA*;=F6;KK&9O/DB\9'"A%N2D7 -MO\F!UAW@"B^_RX-_@>`1(14NF[>J7F[;Y:O^T_]=?-T&[%;"72NVK/'%BA$I -M[X009*7`GY:.M0N!*&>+'`\63G(.^\X2X^FL#`8+G8*0Y!*;-?GHZ7#7Z6/. -M]A0>.53O2P^6K-H3$NH`/W(*`BK'61_@B"9_.+V''D0!>KHXORG@#FCT?Z7F -MEKBW;@.4RE0F:MS\37:LEQF0*\W<V2HW4I#`F<#S('$P8:^#:=AHBAMR^2O* -M&O9>Q?#HAROEWJ3F9]+7!Z`%!S/Z3?)ZC5TW.7/(KDO$]>!"/&7?<-;1.6O` -M/4&=`&\D%#WR1T$GH36>.XX(0?Z?;.6OMB*8[4[+0I.:.OVS3V#"2[%HF*!T -M%N0NBI#>I[V\I1!5N5R)=/?W7_>BS3?"KR3@0+OI!YS?3H@RG9#K#G7YW*F? -MF^SR5/W'G/HK$VW*OH_')OR!X%;>52]J@7M8O(WFS!'8Y(%XL0*6>7!\O:"\ -M"CZ4LEJ"1[YQ%^D!R4EO+%\[#>=--],V.KL</+=*[/XXCV^(W2`!B."(^'[O -MX^(7<@4`6MNPKJV2YYKJ@B<Y^BR"[_H&?R6*YQWZ;M<1RICFHW`T\1GJB03/ -M1';[7=Y%[IOY+*DM<1A:="52D@Y?G/!M,,6ZX<\8^\X+7%?YV@E(N!5:FM%C -MK&`.G:1$CG>!7<T_`%@/C@R4F7[;WA6"QOUGQL-_0H.DB)C-XRS71DUYL-'T -M=UI?GGYI!IYA5UIU&1P^;H_.<\D3?GU2;=K0N$=5BE0]9.WQTT>XDPO7!'M- -MRKWETO^HV*Z2#WV"*XUN?RCQHX:G+D+HZX2725D(6I0<X.X7@,RV,]:S?IQI -MHUHJ?G?@XV&_LZ<CJG4R2Q^YA"LW8MWASP>C%D#K_CP_W^H5>B>LW(9T)<X0 -MTL9I,J&.0L<P*=SXM0I]'TIW5&>.(+<!>O=-$3MY.0N;Z_;^C32)9#"$YW_9 -MF?R^_8/=$#7A*P(BS"4A[+^8S#5\92D,4*/_RPR!C5^[H-RQ@I2*!VI@P5R8 -MHZ-E)EYV4-VM4)Z?GOUQIX"SM3(R]_!1'@Y/D']]Z2[^6M^1YT3_7M3.U/C/ -MK6,@.6PGO9LO`E*E.01?!RI91543UW1(6,EC>4C94D$5NY/L.M8="U)CWX-% -MYT.</?W<6F-RO_-_+X<.4#[0%7-ITSXQSCX+%,(.0:3GTEO<$MPJ+HMA19#5 -M^0'4?<<LV>G"+Z6)D&UYAJ3J'<LTB*A6GIR3K/K4G']2=YXGEZU0PX8>)<J/ -M97UH?L;F-YD-3JBYYZUP1DE8Q*[$8"<0MP6QV=,,>\F7%,F:I_K61.1OS2*0 -MT`?VIE,XCM1V7M6SC(I(-(+1(_7N$<U4<E301\QEM/(5\?*\(2FQIVST"J4N -MV</!W#,?B0JN7MUG;%%7%EU"%8E"`M3!/4?N;82(V6&?F9V0@>_LR9MSW"2" -M_#LZE9T&/"/[2.^RK%I1+-XO@7W1S*:W!ST$#U>.)<4WM&IG`5_5)(+R=0R^ -MQ?Z\<6;H;J7@VD8<Z;`KBN43;%F&C*0"GF)&[.$,+CM@]?!"E`L@G^$LHSBL -MO)Y`"NX"$*]WBGI[ZWS4UXYPS:R'RZK-"F2:][L;<BS?52QM"/?-D<!$>2T$ -MI5WB"X<=H[^D8`TCH/L7/!;&`$[0HFZD./1CV(.9AK8X<R+M^IK!]'^JSA\2 -MN/6?^P6C#;-N)KVBH%EK\"X52!F7@8K+$?3"?VG??:8\;5W07_HFRN]+IRX@ -MJI+4:H:B[OGR8:$J&BP7A@JJVSD_VW+L;(SI0'7G,+_KX">YGJ>#Y,LR&SY< -M"+@BEY^T&Y;A#W='!6T#.:#C+08?),B+-:/%1;Q`\K0K(PQ)"+A<DU:5"<EV -M`(OUDJ!+`X3T=XQ*T.?)9`?J(#?2._`>7>'/0[]"8<&5AR'408F*_'K>\EFW -MN;7NVJCI-[G/TJ4?H@:_LY*(+G*H`>L$%KR!8#E\B3J&,`DRP3D6TS?/7(UC -M4WQW'SW?$^II3\J9HBH=A!]=9PMS/>_L=Y_173#31^['%A^:XV$<&WPT-B:: -MPAB*@,OY#,&>H+9%.<=180U.N=:J?:*EB"(X,@8"U%$8].61$"8F[ZL>`IQR -MQ_].ET,K7ZF!9-X<3;4HY85NO2T8S7#;AAT9"#3"B2/;J?X)EZBM#+G[0D96 -MZFLX9[IHYF=G0\JB-4:S70=)TH7L0+DK5?I1`.&J[UE?OC,%'R6:JP+1X"+Y -M6&\:?Q:..WX01//[2CN6,GU[^,!MH!.@E>#4@CI5OB2E,'X76-*JK6$DI+KV -M45C5F.'?%!G`/T6Z3E%4-'<C`7SVD`ZC]!>GMSF_@5&I5-2;F_"^(LT)!L69 -ME08^2K)2U^R5[G1E\#XORXW@)^Q/NG(&;%EWD<C).AU53Q=]>@%Z3Q)?PX&" -MZR=W#4XMF.V^0TSRBLI!\8T.A4E^HT]\E._Z,?)T3QH-7Y:A?\]F]9TN.G6) -MJ3&OAL/&F70ZR.U%D`D-:J_L:/E75E3?H_GF*F8$:)P?^!A$+>'<+G\N.OA$ -M17&?@1@:$=YUO?Y+W'G5`++\-E+`B3"<5)AY1S+1W=7>ET8@-3[1=C8\[_</ -MABDG':Q68CS00"6^=?VS:)4#^.;JT5:+2W(]\O;/&Z&KX0SECM3`JP,?=W64 -M.L:\70`#KCU#AZ+5PHEY4C)3^-^SU[Q4TST/#!H9T]!OJGG!QGTU$96$44GJ -M]T\9;M!J:I5]$F*0*HO>MDQJR$3+RFX4X+P["LNO]2WYJDUA=6E5)=0DL8$E -MAD28,%+:S%B_BICW3V*87VBP%'7QC]17_594]0FGS3ME4V7Q(8XB.'*MY<W] -MV*4#1(:5W__*L9V.\[><UX:_PXG)*V1N$.209=(UX\BR!Q;1I."-GR#$TC_= -MM[6@ZV54M?$BY&?%IPUQ%OP]*XB/`QHG68F:%B:ZTLGY3LY3%/GC+7!)\G)L -MN3YZ^\F<O4!+'$1X$"YY6S-4!9\[=PYZ:N`]2;*0G@WR>/VTA"5PG4(M-4WE -MCX)*FO=2-G27(_.IV:/KB>N_+=RV&AV\T8?R<,@@H"<,4\$4[)A4U4$Y!D4T -M5>D[OMP(LZ`_"H+9+W.N8XYS^/`?V^@9R2&"O:,D;,`/1V4V6GM.D'U+1J77 -MU.;./3=90[U\N.0-J+_O:,8:L[;VG0I6%AD[((V!LSS6%DB$TKRZI)*4(Z)% -M@@SB2WDJ*5;=9(_'WDK..:[_W;^'*S!1>9#*N2_R43*L&:VDKM%D""D-"LXF -MPD*6NO):9O2F']P2&?BI<'CK4^R&3EGMR/C&[:"W@M32P`2O;<#)-(+YOI5[ -M,1RG3=G<6M^[<_7BQD@^THSO5K0YZHB]SO1N-UQ;8ES'3[-7WAT-5C6Y\?6# -MV4"!^_;Y;;8T^P*.U7.*(*RS9!Z%F13>G='?L>9H5U"F:A/'':.^&Y_+=I"V -M9]V]ZW15V8[(^W!8:[POE)_\L@G3=\9%9VA'U5:C$JRP"`?VYU@\>-P)I:0# -M]]#;E&T0\0_7I\36B*_:9`7W[O0JX+X*D0*$Q=H$8[<>L15V2E>H[*V`92_( -M.+5+Z3@@![!.R/WV&*-Y>33FA?JX_I_UO@=%>-9`L<&:,@?]&T&V2H<%.Z:^ -M=O-:T?1J``[5&=50RWD/\^A^N64\Q:@`!!T!#>=JBH=??O_V69VTO/+`Y?,7 -M(U;":RTV^]HEA`^6TM)/.;XH3=<$@(L!4SHA96>=!(VV6(WU@XV@H$124Y<J -M0^$,YXZ7)"AO_4+K0;$"%#HG%26T9\\>B>AR9:#M:(,K_`/*,N/0/EO,IJI? -M>K@YJ.;_7(2^A67_<`)5H/F2\\.2M^1BI1EVZ%8OB$./Q`K$Y6A56LC'/J!+ -MI?8->T7O+IT,75&L\!T?91?Z-)@PMU-P85NV6Y0-[ZDEYM6O7*_6ZE@AH^^? -M3!LN!:22.)W+-FM($U.W0==O+HWKGYBL,4TQ2AI@JE8(.PZ0LM8QI++E8%GW -MDO/)DGCN8[BD)(/B$*-$B=V(X\-?9?<1)M+K)1+F.9#[MB8S/'OULMNG*W<M -MJC_/JZQ/&3C'Z")WJT'+'+U]W/]O!LL[#)UN8HB,?F`A?0O-ZU,PJ7@#[=;1 -M+3&`BBA/=L!76>9#,_^JZ^NN@V<O%FZ-V;#M)Z<XMAW30$.[&I42J&_0R>U% -M$02Y64%4$O4PATHVX*Q2(JD2.T6#WZ<',N.K<^'C@$L=;7.+^G-38XE/*&*I -M;).4?,KOJ5>]RO,NU1F]KW\.CNIN<@H.%P_NS&`@[+8]>#G/KGWENP]U$/&- -M+1YY`7;(VYVZY:8PB]5M;'%=N3:^,,;CF(_L_E@M7I)9NCHEFVBG=)AX:H,6 -M(CZ&8^$K\_8GI8H>CG<ML'%+-@>UXABE%[D,I#<4WCU$#E!@/2E_P@11+*:P -M89V0V#"!^'IB.PL`6G1>E?UCC/N0=`FR+.<8%CG>)^<'2)=DR5D7;1D<M4L) -MJ>KH\3LDL\,!(<]$LFQ"?*U3O%I$,=+#*CR,)6PZ!O+D5M^<MT7D??R<H?8_ -MYHQDIW+<6M':-"O)4:8?\PC6&9$[:KOB['<[H@G85E-1WC5J=!LE+F=N>]IH -M[R*^;P>;:K%T::>&>;JSL=R74/'#1^P`BPR60J.;$(PIE>E7S"D>2^]-NR?S -M%@294U>H1+VNEDZF)OLLFT+>WWA[.E7_G)D^,ZW,94=/@HHF!DX"+8T$P#VL -MX8T!G5B<&QV9Z]:PX\JZ:!QCO3;5^3;S:JUK`PK,FHMLV*#<<BYB[(I5^*JT -M6OSXO5MVQ61Y:/6A;K>C:JRQ0?_I(O`4EXIAJ5U>#(,Z**=#9OOFN=NQ8]=: -M9+<1#ICA]*_F*\GOA_@QCS4&+4U@5OLY7UG%1W(?OT]@H=L2A;&ADR]1T9,2 -M;_IZT%@`M]B(ZA!]NN69B[/6O,H!TQT7RNE46T^`2.NFJ;%-QLX5J@5A5?H' -MG71K_<L<KBZX@/8]<0AO^GNU8MOF^.%\5/0A(&'"'0\=FV,_7P3+!SC3)<'^ -M9\,?*%I=K7'H/?OE@TZE>U;7VR$==_1L%8F`/9,\H#YYM$D9(:T[-=Z58Y>Q -MU78QJB];YGWT1(BJD5]^&0U0-I?([-;HO'7^"?:/7<85[?V>.)-[.J"=^:B2 -MP;H*J$:_3UT>#?.#V!1OTSW>0/39NV7^CQ'(G-!QDPB;)E6H7%FLR2X]2%V* -MUGQ'1?:GK&3A=AR894`(J)OUU[9:D:V;1&>7RT0[>2M'?AWX2-#Y\5_LTZ1B -M43!&4/&_8FXQO>.K!WT5XP??PK]BR8')CH2AX*G[LG5^*Q@M:%-N+9+CL1E@ -MIY`J0?;RVI./EL%IAM>2#/[?93H0@]K7O)O"Z"9[?C:=4I]_*TX;H47^;HH, -M[#1AY@`GUT?1*@T-R2#9[6$W+<L;YK9:O[X_,;<>7?=\I=.</6_=S0'YKP:: -MJKRDP@`P;_R@WQUNM#@]R*\K"<H,EV5>%^2D4Z8TZ;,8KM=BUZYH!L<(.8K1 -M%@`TVY4*J:EI%5`'V\1G;.1F$D:ZC/X'WK)79]ZF(JQ1_QIQ(!'@PN]SGSN\ -M/JY8JR3RFD*'M5;=&O>Z4#D!P>/UC$G#*I"ZY2A-$'3-'_@UK8UPC]7*Q -MU_=LBJU.GVQC#M>4,TLPJIUZ<I]EWST/"$[R/[CK;/IQK5]2B!8Z[F+3Z#69 -M#'$KW>*'XD3$4_SW+@9V[G:9@Z8)AL$!<UHLKY:HML`;OT/2E?[*@PNGR^?/ -M1/.GA0%U1K;(Z_Z3CXY:BG?FEEP9`Q>C=^1$(3LG%#TI9.XM>KE,G*P?:N?% -MXB"D<\K+%)@=YL?P1]XW2W@JZ715[."FV[O37S/<7Z'\8T+1RJ0IVI&[?NR) -M@/R\IR\D.8)W@ZW/Q2V<0FYOY,O5N/!ZC+#(,NE_B>B9F*EFC]R>XU&XX(D] -M6B999P@GOG>V/V+2:ZV3.;E3\V6O;&`77TI00AYUFY."+@:RZ(:.4MNUI9!L -MW<T8+)(KASBL\Q2P4CVQ09Q;U<$94?FO\58:_@,_,W=*!A%%"B]T@ADZVB4. -M*G6TS]FMI\A&)9DJ>3WA]3@L)$-!)%UHD`YYEZ?J3IFNJ`U6L":MTBR"L-Z" -MJE^_``7NC@U\C!N,F4`W,_N2DP.E*HHARZ6P9\[3"PEJ*ZQ0[K^4_\BJ:>F] -MYS#@?[\YKGN;GVXF5.YDS,O6\5ZM`MK6%5/?<$`F8#+UE6YF=.A*$`9]7>'# -MF].GR><Y\LX"I[K?6LJT`;&)4T#L6#0:3$E$^+6%0(\2TDEC0)7\A04P?.IZ -M*'>H#]L]9\-[9L,,SF%6/8.-?7EWO@).NI'OQR;6F0K)-]>:H^0]<C+V=UL%ZE4;H3ZDUQ:UX?IG'P%#8S97*Z<"L -M>;R"!.SD+3/A.D*WQ*.'M5Z+>U_*YXP9.,`[Z]:'1GA%+$S<WFROO6EPNN$@ -MCY!Y!1YD($@5O"WPDE>OO<1\?6FNQ/\+XCP-$+4ZN+6_'7_F5%M"V9V&9@_= -MUV3>G15A'BV=<M"IGTOG')(GT8&R`9'/Y]AD2$:G%4*/H;4`WE,&-"[FO+HA -M5[[TO@4W/#RTH4+'PZK%RP:8\TJGC#+4H:Y/'3<N&2?4RG84/+Z&=S6^N:@` -MV9L&*J:J4O]8_\^BB4=HT]?@F/KY=VVN^Y.E2L"_0!3K84#W@;MEK1\W#&1G -MF/XMD^CU=<GR75[G2WSEEXH/4NI94Y^>V'0ITXYR2!VP,OKM0#$E3>PZX9`> -M-MW^*OGQ200ZX93D^O&!%\5%[!K`]XWIO9PO,B7CSB\5@3Y5*92:S;GMEL#K -M2D1/ZD$)BOO25KOW*K32`Z=*]YQP`7H_9Y"`C$>Y$Q<M^8NW(8EJH6M2E5%_ -MK-N)=^%B![^_`=U7P6*-O_KGYCX.`&+"=7"FY044/L8(U<G,(Y'V>>X*4/]) -MVZD/9VOOZS>'$`?%\]AR%4!C.>!V@"PQL<':J9]T$9G6SLGN)5"XUD<G?B2( -M<Q.@TG+4F7GG?+(V%*JNT?4TWR[.!^1%!4&D_.9.E6\PA$WQ[3Y;\&>,KA#! -M[$R,:-425')$#HH&R+/62*M%VKSAY1V;-$U51VMDN6K`7%=9DJ'S><%"/#KL -M9>\R%"S6$"2O]O,!4)$Z?2$C687"@ZEFBT_R@-X0NNN.RS*RSQCFIW]+@(&^ -MVB[;VJJQMS-A9[-N<;</#7O>0[*S2=_?UH@'OX.`K?OP7*)7WC,@I0*',N-U -M/K`J[[C]0RZA3NF,SF)^I%WDZ!1UVR6L9D4_[G)NAU(WG[6V?PJ"JKP4'[C6 -M]E2QAV9#%A0^[>X[:)'A%D1^9N;EYCHJ@H]'W]9Q76O<70_><C$F0(MR:(@" -MFI2EHD8J'*?SBYN,N'&_E:M%_+W;F.9'7RHAXL!/L_&JEP$R[CY][0?-J5U( -M`6'U./GNR44Y;Y_:=(S<,X5M6"(ZH/[P%T#-XP8"3U<H*[L`[E?CRCM=OTN. -MLQN[PAD/OT[:W?,V#S"SI&/:MGU0/B9;:^OL1ONMO#4*2T=IKW+8B16)(7*9 -MQU)OOIKYZ&]-#%*K8PGOG_7H,V3(UZL9G5]1CR3['LY6E0R8%TG67`A*C]K: -M`K$^=?VC(S=K/>YEY'I,.O0WV['A::#+/CVJ:HI923I7KW6H5'PO"2U(_.1? -MM/B(9KA,ENJU$U+$&Y#XQF6(!7<^F(J$IK;J/",K?([GDR/&4`'*F96+J.^T -MI.!1Z</$(J+5*`6ZS"3^',4::E[N5C@W2]O!)VS4[\`N:Q#]'P^W=$",EJ+( -M+H^<X\GNC":Z3@`GI/[$L-;:$`9196.ADS%X-)(@>6!10?KD^'^*AIVA%\"R -M[.\L%4,\5.DLMBUI=!G85V<Z[S/DJ(DQP';WI&=_'2W.=>=IK>2''TKPP5R= -M.^DP$]NT#.'ELQ6':CMT^>:(*O/9C`-<=Z+B>YO?G+<QY/>ZXF2EB;79Q7K+ -M):3783]I>$7N7=UA)_7-UU8/J^;(=O?REI!<\SG`TT^]*D`E]I^P4W;MR,05 -MU*G@=H4U`IORK),KC@25Y!9>;6O26C)0OR\-,-\\-(EDPGXNL,0CT2M/_(ZK -MI^/J.SR[DGKOD38=:2.Q\S'%(#6XPK8W0#1J[9:DD.>O0VV>S"$IZD5YS1%$ -MQO6@!7S4?[.@`T)#60_[O*T*L6A]*6ZYX9+B#F8R+9M0Q-,E3F(59G\XFD%3 -M<`VG_LMIU2(3V>R:_@Q&:QLJ@V)5=(X9S:Z`X>EH6_34*,G7_\*YJ04Y*Z9V -M^.=W[Q\W%NRA">3'^>CLTNPM>;#5L)D@V8XG*T	SKHUH@"S?<#8NE`A'3A -M[-):69.:MC(\4(M_SK0P\:KNT+0KIWVBQ[PR4I=I%0$'K4E$'\SAASPRO3(; -MS2RJ/HPF9KW?XY=:PL`5ET\K1$<SW-T!'_[H:56;C68X>^/<Y#=VL4&N>GVM -M,/'WA6C:00;K&#X(?'!X_R_I<H7HCGS*8*6)LAE<?:4VYV+`4L8!'<^546#; -M/8M]Q&:UGFO"9T0H[SG'D;*+@.;_V$3(CVZ/BLLH'B.*;Z>!F&48:PNP\69C -M"MU4U4*0)KB_WP65`+07(*UG/HQQS*?6'7:;&5RI#6'ZCK:&9YP%B.ZRV1T6 -MY4Y2=VY`"3$G_ZK!$*E"<TV.B\\]".5IYU-11(RM`H5MZ>_1H6C?I)`3G822 -M<:M$KJ$QD%JGH&+UV0'ZVZ&):GK[KVC3\K;HJD-E*C^P)KG,&+W51Q=<)OOF -M12]DK\P:1FI^0;C@0R>,<&T/'=`"&_#"R;EA]4-TS:`B]*9L#.GV*^8]EJ.M -M^(U&>:#]O\O?5,_,`W]C$13`3Y+DAZJ."49^"&4T,3*-?[<?'9^0B\^$KBCH -MAUA2[%=N8,RG4E!-#P>X4Z:57+T#@3(AC\:Z"8G$>MWJ6%?$;GBNRH6WEWK] -M#LRP;.05S$9:5F=^3#AVJ/(4-^-5X9>F@X3C+`74XLI41SP2<H>*6UXD(\I5 -MOF2,03C;QYY`MOE&`CY<;UF[D,JGK:("+IM*UR]^T$VO/J)KY_AH5"2)1$S` -MCJ)G)3#+!HL[DCX=C4F8YMGEB?3T@,LQHY>/I&#G>94YWB+CM8&=IBC._*7T -MK%7%TZ^D\LJC(25)JA(-7_\W\#'UAXHZ%V\L=(YJJ+Q.S?F37:RZH.UI^^:B -MHP4U\Z_#.L^=//A$W47)7`<-P!=Z%&WH\2,B/JR3>>ST]YH"<36'T9,R'F=9 -MC794=_H*648U\H>]916[I$$Y"XK(&N\V-C-,\7)*R+ECA,\S0`B("JO9(R-G -M7(L8/JW2R(&%71OT@G[5_["<U'$!('4TGZ4V37)3S^'8:_(4BXJ[>R;2%,)/ -M37LC/FQGS*EZ@`@/$=M!?A=+K:T.<6Y)?X^@>!IPI-DK,>85J1C1I[XTCD+0 -M1(Y"F>26S5SSN%6:\*7D*A8J;1N'('Y_"F_([N=/;@SXR8,89SX^<*OV10D: -MM.)?;!(C6HQNU9`0E1OZD84])W0B(H^CW,:UG?X;H%\%]#XJ<+KZBJ!<0QB# -M@0_+6GWU>.U^%GJ0D]%?A5=&ON#>#XQT,%FDR"=!-G1,E]U;)&I/Y^I6PD6T -MF^LLV#YWY842K/;D\K5'8^NM\WMAJ.S550IP0)(*\<"&<^)1>WZ+6-JACA.* -M@1XF\Q#.XUGQ8_>3B74;V)+]M@QNX=)I631^9P*#!5A:M1_^%FF!I^1[U?BQ -M@.7R95/7)FZ9NHPKUNIWJ6HL)"$_:1<4T?;T:1K7?I>R3OS'OH53X-"SP"L- -M`?/X/9/),;^G/OS$/&\#*)?2-PF>,;/5`8I(7DF.Q*,@S+C#(ICR<YR;$)8R -M5/!))C++&P\W.-I,S4P/(6K2E$"_!*6]/&M%_AT2Z,5:UM5V%MJ2O.QP_2Y] -M9B2:[_'>1ZYU(>8K;4G,&]C?!?!L.#$TJ3$MIRO@V9X*)]MSOAJ!;[3K=O(C -M7(Z!Y4UK!@*/`Y[8.S-8FE26D4\RCR%SGOS(T9TP&%VCSTN1@/N)%LX[X"[# -M1?)_N#G!EC)=),,&BB6/9_9!<\-MO+9IJI*Q=\F\=J^(;3TH:,C,YTT'?^]H -M1/#.</)X"R([<GC@HFN-BU<QMI0&8<+D>4\J%G_T0O*3Z/'^H0/#JP/4.,V5 -M/I2PU^9='9S!1HHMK74X9DCG3Q=8<`2#FZP#B"Z_Y'M9;"(8;`Q!A5<:W`?7 -MRG[GR:\3U&)G:V>ML3?[*J':PP8`?R3TOL(M4+STK;2=[^WIX&"')EW-BX3= -MKW=/=3`*[?N:44JFW<V'K9[RD9K]]4%&V.?82_:LXM4W9[D\3+8$^%#8HSX@ -MADN08`+H9^N,D."WYVP/Y'Q,**-/E(C@RSU<W<)-M-%?VGHOH2R4#^HZ:$#_ -M?$6.IU[X:$#5D1&2<>QT>-2_`/84*,FU"V'+;)BMEBY5:3+(J,8/T6C%[/_! -M*L;L.TZCT/3HZ^@/#_TC)_3L"1.[Z+Q(FA,,1VLT:2KUQ*#442*=9Y0X<>S] -M>RLSP/3!%K-0YW/>_M&8HW^8C=CG:.,LI>V$P#MT*B)]^8B8[3B_[DHS#X+- -MOQR3>](1H])E)$BTL5*X-_FVM\M*ZTS5;8>+-=^L\<O`8NY!E+ZI[$+^EV<Y -M[D[HT)XU&SIA`S]SXB?O%'2,E#]3[M?_-SP>;#"N3O\8@-@`$JS[#CC,(ON< -MI<*3?DRS.&$@*>@PZ]9EX""F@^[?,/2@?\%`3&?>"#:(.L:)$`7_0`/">3W: -M+_,Q8?Z-1U:CR#2]M&OP->&AM9%2UV2?M895?3NHH34>_#/$HA&VW&>M+ZV@ -M99YH6]["E.38OULK44>G8_-!'_"LZ1[J58F!IWQ;9NS?)9*II2Q4U>$(D>T9 -M&K9X!MFGX@\$$Z[OH<(%G8=UM,LLJ]$ULV%,8&'_1Q_1?&X0/'>V!NN*^*F? -M84X`=I+@F%$8$04YHA;N,7&/T`[)30/!F86:85?07\BWHVCW6WG<$ZC86BS3 -M5@'O<_0W3LDL5%R4;U[U-S.E?R/\#+$8V/<NQW)3D&FA#+K9VB9_G'M^?<8$ -M+Z.3C&?403O]NHC>SDBF?ZD[AVT-2S4^2UB>['<Y$R+ZOA6=*LWXA,=;H'Y] -M/#SV=VEVPKYP2-=)R)1C#450IP<ZK6S_V"J%H.2'S`^UO]BB^4`\:="PB2E% -MLF?2328%6RM=6QP\-&"UN:,"E30@V)T5*C_/V(0TM(J),=31505`'>0CR,@< -MZT6*&,U/M\[T8X(3JUU^I4-S]E8%L/VVMJI.2@EMW>O:<##A(5?`S7>CO&>8 -MVLJ&C+@A`"G?5P-6K09+L</N,CA40J\)L<HGWQVQ$<J*FSJ+/^&_<LB]'5P4 -M@+U3$SWY/'6D--<]?LAQUZ"\X=E-3R<1#QA<HYUAS:>65I$>=K7..0)HN/4O -ML+>POI?S#CS\("$2R=>"<IJH-UON7I@RJE3I58'0",6/N=YX2_])W'^_V4M! -M8*/P8:WMX>3LIFK$#@L<]2'(98F,,5C^[!`2_+S%7&`L+=E.0/$?NO\+K'6T -M!"#K)DN3R&N4@N*M%!?M'L9>Y1W.?'IJ$?AP->\K"X5Q.'0(&7?P<=JH^-&# -M5+^I;*K@/S>>KT87)W=^RSOPH)"B4'P9"'JJZHN-FMO^/VL1"?4@4Q(CRY$> -M!`ZSW.#^@><8;HG?+HD-2'3\O_4-5!N'%(CKRE5=^6AG@N@DM$O*V4.8E*@M -MUO,HGT_6\87H-6'][[TE0J*N1Y4^9E#WVQOR,^Q;,V/BL(.Z04'VRD,,RF/8 -MFA&+T@[U7@]I;C_ZPT_<(3CLO!)X-ZS_M%H)=P4]4%.`Y4#[<`S2%C[<<#JJ -M]6HH>B97X9U\D==`L8KBS`;$VODE[+NC_%<WS2].][EDGMB'-V(?#08LY%K) -M'ZX%OX2@DZ_;)<-G)KSG!&OR7Y8$`=F*D)DQE*#*9@$4%E&\XL8[RH!#/7"? -M8(CO4W>*]-2YW=_ZPU*^G@O3(4&KV9.Z1,>RD'GM=Z'LGA94:*$U<YT84K), -M9]T`O9<=/:$F;#7JRA*]6X>PG=A&Y\[1FG/JBLRV8KV49RY'>[T!!3M7)W<U -ME(UF'+45\HFZ`^R/(BM-)/,N3:XT:V3Y[>$W+5W?"%J73+]#LF#Q2"\('IN? -M"LB*9/RXKGP>'C36;8.^0KGQ\@6'/R*4^#I]'O'@HM!/9<>V4C3E23CB$Q"; -M>[37(7@IKR#AN(R'+!:(#"F[4<,XJR2E?TL>A7\4[D$NPGW+QENJ\XA@S,6- -MF*G<3W\KP&[XKIMJI<8.1=SL>5ZUAH5LNCGO)G"<CK8HJEA4LRZWG$"NJJSW -MK.ZH7OZMBQ[8I+;X?QU26;,FG^VR1KSO8@BUPS<4DP-2-QBXN(A1CA-4PB(Q -ME8$5T<@EG,`R?\`@S<S'#,#IE4Y$'W!T0#6<6$V"=/8:M8/`WQ6ZE%P?O;LZ -MM+[A8*4"4H)S[U/^T,,S0FH`BHJQ81V,FL<85',>TUWUPG)KZIOWRO2TQD%/ -M&V(I2H)T;G9YOQT]I<7.G`Q,)JHWQY))^2M7F&V`QL3A0DH][%KA(E77]-`L -M6A!,BB3LR5E8_J"[>[[XI9FG%T`A+=H8J?;3%"R![DE?%E/VCV]+2:CX;5(J -MQ/4&(1`&,$?"\CK79=(4&/WA#DG&**S%,+:+\\YJBY+)FTFOBASLJ_\`V+WS -MJ3!74[Y)3B=<KYHT"CCOZIDL^J*K[Z\+&XR!`G&SXSEV/V"1D2?(P>!4ZJ*/ -MUR5W;Z=>%^/F(?'WV<[I>2.M,7^84`\MD*J?;24J@'\\+*YY">Z7[J;XD,ZA -M=S,<9S2][?2O[Q//'J+_C$Q76R6DF\/_#)SE'80'YW_=-[F.SY59TLQ:-MHF -M(U7EAGS]/!CEP^9.Y5Z=0K`F6;_.('2R`\@GG:_WXTJ\.=,S`@7KQ5%HTAG9 -M3HTO,'NKUTPQWA3R(O8$YU>C>BF.X.W%E;MT>QBM,/L#^?B*+R5W:S%8#@P_ -M\XWFZ2%`)(<Q@`41U[;0HHF48/P;N;/!>@8E2T?YWPE#KVHO>V1_:VUF-'`& -M3P<?A9$13+7<WMHP5H>ZK5B;%"3EA0!W,`]JJ#,.C@"7`$P83#B1=-K_(PXK -M$J#28M*H2D`0)'&S/Z5\&HIQ[[2\A!("M5G7+JJILMDAYIM.*=M"S/)>=TV7 -MF`<8>1Q@,-_<RZE:=JAETL'H_I[8`^Z6Q7T:]WO\Q2'!F3IT2"M64'S\ZB]! -M>U?D:&%T+`;#%PJK)AIL^56N2/@]U&O*:*,PV-JA2I$>EB'!"7KS?AFJP02% -M++(UN>*(8N"XBY8&A/7QX_F6!9<@;7K,6>=T/^?F!\H>Z>W9HD7/<6?BUM\: -MC*)3@/@8GU;$R@G,*:S7-P+H(E+5,G0',RK=MCP%/R32MSN'PULI(RMV;@Z" -M;7)2=/&I/&E-3YBM(PE4E)\6GDQ3(R``_E?"^T6!VE`E^3GJ#7$P,HL['^>^ -M%H5YZ1LT_+NQ:0^N%:MA%A:K^!"GJ]I6_=WHT\Y-&9(N9GMIU8X]H^5[M^DK -M/?<M)1(FQN$6P>'8S$0H<E!4/(?3RBA$7>1X'=MB?NI"5)3OG00DQ,TZ6('R -M<;(S79.#4)NT0%;O5+031-=:JQ1K$N4X#N>E?D^WY%J1M#(K#7=H:6;OC1+; -MVE%?Z_%K%YYO;_2;YZ@FJ-9W%:LF0FZ0=W7I+&6O'NX%,_E.@$8>UR@Y\[<Y -ME<7!AC;&`X,\O2,:3X7H5!F6721MPG;PZO(1D+O*(#J#0^&<\>,[S-;#?&EZ -MB+4_AH%Q>A='PFZ:4YN(-A)ASN_@EB=Y"(O%]C]B6Q,UUP3[>'K\;!<3;]5Q -M*_1K^$TFZ<BYH48W-C;6!/\Q+KM))?%7S_U`=#[C^5.K;:6R:O(!N@&V)(_X -MD2]9K6U8%1=*^.)'U^H6)?1HA<DL^=UK%2BS`AAE#:&`L2D/%GGU7\X8Q/P+ -M5&@7NAO'@"Y\[?*=L!?(:W.1A[X8[.\J+L]-'%<%64B'78JJBT[D[UY.`=F2 -M;IM[OD;(C,EC2-Q'QJ+762A/;$#4KZ7VW1$C<=,H!X?`3SF<WF%!3)^:SK\' -MBJC.M2^X*90^@`6&S=6,XD1B.(:=317GZI>BXF(?KO`Q='\16))\5I^$2OA7 -MR9;!2!3>E#7R)`ZMR?W;F'-',J!?<DZK6JB`>YHW=H7!H$K+/U/JJKY\NGP9 -M:&=P7[Z7[D+#6&4N8<II,GVO9P?8TT(6-$<XQ)QQ?:'QC.MN2VCV\$=?:H.W -MD3(6(W=WDS)Q2=YCD;ND1E0NK$OMV&Y,)$)C&TOE%GLJ3$YC_3Q*`[;R<Y>T -M^@9?J(YB],Z52^2,HW+^ZS7">.5)?&\;!./"5M4[>O+9LX\G0\6&:C^A)2@* -M2$BXG\6KJ%Q[LIKTVMBXBI#ST!@V7Y``22HW?VWQ*I"9[E<JP2&DAK@7+5\' -MQ%T8B"!2_0C)[TD:6'8PUU/LZ&V^IY"L$Q'QYE3?2;(9K]DPZ)$Y)H(20W?P -M3N=J*G3."T52V6-1#E(47)#9N`/0.R&<!-`ZKMH^&GI^.]B-2,FE-AO8PC5N -MF0&+M*:@^NF1?5IGH'T>RJ$X+)Z\$F:^EF#Y]VX!,H3U"D;;)V&$=^4`%,-A -M05PKYH1",%%]5`Y;NVZ3J?/IV6/Q_7?KY"<3OHLPQ-JDTND7#4'27^TQUGX" -M#&AM<HLB3STG]1@Y*!-K+^#>_'ET@&9US((#:!TSM]20*M?GHH2FJ@./-SV5 -M-XKD]+:J[]I0J!#$]^G=(_;FU%Y`_P%BR=P:X"3-XK3OF9#9,)I>)J5*O7BT -M3`3S$)^959$V^+G4KG_ECRS+#OK_,NH^@<]L\6QY"G3(XRR+"V1AP7X;VM#' -M_/;H%P/6@F>58-IAN6X0#O\"R5#3<WZ]^3RQ_[E^[T5F-[V"!Z-6Y`'NVR,( -M0:/<SA>BU6!<6W(DEB\=`KCW7-F;DGE6G9^=R?_M0@24^2!`:%XI-(#F4<T= -MK.MK[FI*B8FH3(L@&]27B=9K#T:"A/JC@)D42O[XY:@S8+,B??(R@O<?]#K_ -M`850IPNK24SY(W70;)O&O;>T[J%@2&6\J&&K'G-.5*@_"ZY>MP6IHV912].- -M?9IDL.2/71/2U]A.QY+/)%ASZ$#L_D:&%48F.)(<DFRC>UJ,I5"0"Y,7%4FC -M$$,8XQI60,YE'R6D##)/:2F+E=5#H-V"/G7DB+;WHA9CMUJJW_M=IOP^Y+WU -MZPH<2YQ&L_,Z\D]]@(%FH4JM1!3GN1AXB'<[SVJ'@$04<_2+N<MK.?+LKJ$A -M`'7Y8N-*_G@RF3VZ.[HDI]*8[=?\Y/]?JSH^QQG#:\[_H_-,]:_I:%;@9U)P -M4W<0J!`^?1-9+QX\C`:M7T82L1EFG5Y27_05*CMD`FWKU)WS0D)/[#&SD7K0 -ML5]SY`LBAC._.H,O7C6:YF$/!*M1SF4"H%5F(T-=OGFX<1'Y*RV;UQ89+Y_X -MXY`UP"NL^3Y0[(K&K-_D6&00SQM7<"[V%64?;BGCF,0GE(81AFN6MPOJPHMD -M94L^XK/2,9>;.\)PF#.&1&U792N_\:GBL0U["T]"F:+?9:14W]*F1RZK.]V? -ME,EQJ!%2I36/E-)?=K9%C.O$6!N=B-1M+%)XP1%Y\=FFTQW[_@+>='5&!O!] -ML;9'38D[\N9K4F7>.FD7G+MOB.#GI[]CB6V<<M62^N*\;36KG)94N6=(URN/ -M7D$*(4.E/*2.\0#YKDE-L\WQBZI<'#DXHTH3=\[[[NVW>&8N619!&1(_+B]8 -MOXM<79A/B,2WJ;+W;_=5<'##@LX?H?I1!YANJBHX^CP#SF?8.Z_/LM)J-/SM -M6U7\PK5KT>KYO-!YOY9/("3M4XY_Q'#D;2(03Y-_N!>=N@8A;\(G<)30(%:G -MM<W9/'V@GQ^7"-_T=,2I7IDRZ4CS0N,E?0S%,ACO"R):VZ'_;D,'-PJBR5_' -MZ%`HPHV*%1@X%\Y:A8+A0[1:=6W]EWEZ=/#8UY;.)<GOMV1[(5G37OC4R,A* -MC5Y!=-S788:B@7YDIV24QC!,$';9\:D_JMR]93HU";WGIENWKD)DL92L6]F[ -MP#BA'%1-LGJJ\GVJ/8BKCPZ*S#>(MNCMD'3K<U^5@99$0])=;FMPO?2]F5*8 -M?$(K^Q,/4,87\8"^!#@U>3[KK`G'!%J,#U$V^NX9U@FE7W>>3J]7Y'(X0%PO -MF;2A='"4%N@ES"1`CSJ0"-`/[2M6"+VN\6W/^J7X[/-4F'\Q`R,ZSQQ5(-6Q -M8B"JW?(=O[WK/4Z+37$48H6N'K?4S)`ZM2L[RN^W]FVRS$0*_YV[O5A/3GVM -M=XR-8N6PG"SML[0ZE!T]?9'"`Q`-1R.4%-@OJV$I#7`H[W<E)8+^2.KW3L=G -M5'YOH?`&/C3<].`$5>%U@DM`1Z_^(C/S&)LN)-_#DD05+"_>)7K[8F2,*=F^ -M!GU_@MM0V&(95MQZ/V;H!8"MM>S4P'F`CK#YO_&_O#5POEQB&?T)8C+P)'BC -M:VQVJ`O+PAA+R<"88VZ62X_1?-'3OJBG-6/GI,'4'S6I!ZT;Q;JR<2Z8=EO7 -M=Z0HDBE;0R=_<M5G$LK\\.V'WXE0YTE9*Q8TEQW$K-4N;"<#T@=`?%N_P%-R -MM@NW3<;'T0!O?;^P_#Y8VT#\"U*P[^,_YGJQRDS?<K/^!0(<"2T,YO852CPZ -M7`[Q\@&&C=[W+*;_X[+4<B0<E!$Z.I`$RL:,;P(!X=1GD"74,L'8:.]+;$M^ -MZC-=8X^*L@?!$R3$.#7'GC#_5X[S2E>B]-A:&72#5>1,^0'7V.QS0^&7\ZL* -M-Q8.;[N8DRT:E-1_JTS?GA/\@20)T2U?W`]=EW$(,U[PM@B<4!,+(Z1P`PNT -M=VR?.1RBH/][YP=D[VTVV\VRQ5JB\G%8$4W5.W'M#'T`GXK_H2\MP+A]62;K -MPME!%4C/WJ4W1<PV7[061T1?S]R&5%:(8W]NQH']&N.`2#H[@3@KK8KM,M3_ -M%,U3U#!F=+V>OF#0JM#N.UUD'W'&2R3AI$H%2XD,7!;>DG.]3$><GG7&+HTU -MXQE(AN:C(T@_74KCFXHDH,Z-&!O-5IR9LX^RQ4Q17#SKF_WV]F7CJ.$2-CR= -M1\G]2M'IP+'WR6,_2!<!?#'*XU[I2O3G1Q8NT?:!4'ZF!(?.&@\1A@H1VD+I -M'@OTVW^YS,PCW!0YE0:%633KPN!-YBU<9_TM!&@"3L$F.YH)P"L8AX65SLA# -MC=QMW;.=WNEOMSH)H94TWW9(-QS^I,?IIDIW^;0(0A+'?F$K%G>,N6CFY$UY -M=V!P2DL!6CV6.&8+'P[\_Y>!<\/!9"';;/CO]%MWJ8$'*Y(>*-7;M][IEFY% -MZRWQ%8!E1_"'>MW#.N@@2Z)(A;/,F!7GCQK"+/Q;B[L)?Q6O'M/31:S+5'OF -MR2O\J3Q6$@*>B[$V#@R>'*Q9MQ78]\<<E5-C4Y$<P%`@8J@?AR[V>385L34F -MTFH:R?4'I$`6'5HB/47:;VO4(/!W:C?D47CK"/G?(L82W*/9#7&2<'E[$2^O -MUX6,:S2)&`3,OO'@[1&V&B=-LG4:A]3E%&R464MXG8/LH"]P"R5N=Z`HYJ>[ -M-KCZ#&.R3ID.-R:)YA#/)LVV'74-`2[EZ7%0T@#SOTD07*R#.)+2K^J_7':Q -M@9BSF",15B@VW0U@?"0?T#]=Y';,J2:KK7#X\T[8C]O<GM'.+&/,VX.O7BRD -M#<?9R.Y)1YN8+@QU(F,>(/CA5VW3%NRWC6LT4)W9KE7O1CWFC8#="B%4/H1K -MYS5BH47=%`QFFW7%*'%74:)8V]4**A,#1V)WR8S*70,N$ZSWWU]H*^.N61X> -M;]A3]SJ3)/47MI350ZX=1L,W,ZVK_;H+VP4]I4[G>_^/NM$4#%A2IKK26YJ& -M+`4">/+.':]Q>,\Z:WS#MN7+U;M.,6L\597"7NOI#/I3[GGP6#05F9Z@[1E* -MD!KQH$D2\DH,-&WUH3X!JMK9X9J4,BIVBZ!5SY>W#,X*3X]G$L7-_M5K\IOH -M?(HMP1"5G^,,.@KM"!]1=?ZJ&.G(6;*O&^02J$:AQ'IF<)]O!)A5>1DOH'?W -M:")&[T$>B?`U"7/F12N.YF,G)(BG>+6`@OH[UH/KLY44IM'G,/@%(SO#C4.N -M\Y"QYG*<_`BYE`V2H6K3F/IM'(H-QB+,7843T.T;PRE+6/L_$7[2D'G-*H3! -M?Z=KRTDRLU8@79&`/VI+_O8BNZ[C)B&Q!9RU;(5,XC/'".\_%4Y0MG0X;N<; -MPYF.A10G;KX6OQ&S7$0$VQMLC`0ZK/M4+TT'9/8^!RYOJ$S0S5YT"!><Y_[Y -M:<+3N]/UCV<2U)XB&PJNZ"]UR0H]$GQTH?^;&^21F&,RN,HD5.).EYNICC+< -M&NL!AXDG_C^];R91XDF2G$BVS/#JJ$3@U,=B<CNT-`R,N\RVVV8SA,KOIOP= -M!YU>M))T98:YTFHAU=,\C6?/#,(!]RHH[-Q2M9ED9ZLR?H8):P3SP.6JOCQ* -M#T*'N$6K!=`]-%\*CV,>E]9?T\1+#@;#5<=.D!T(PU"T?'6Z;%/"IF#Q<D"R -M#I[XM()75.J`?1F&NV^CM$O-L)!WJZ#+R^4P0>[//R2]!$81YAAC?_7Q6:L: -M$J_.%S^3W1_,\>4V81*12;%\0>@2/,*OV,9%O9+M26Q=_%.!(A!J3X<#D>RS -M>.]F*A<%PY.8`PC+B!^%D[F'J-G>,DH1Y098!K^$;!7M\-=PAYJSA86CDO;; -M-0M0[R(ZD@/D97$\4XD?--S]20*6FJ)@LOX@,#<R1/SB3>CFF+9NK$I&[GV3 -MC\,W01`E/VW+U,+R2AI)/B_1.[T4R+V&S"BA;9NPZ<@,H_QY0%*AYQR9QE@H -M$*V<?=%$?(](/+B?M4Z_6MZZ]:-F*RA=Q09Y=/$YD)E&?RIG%<P3$,/B2]L_ -ML!0F*7]OV,YC7379>V52"=F)8=_^AD>G9DV-0V7CIX;18!@6`+6XWORG;BVC -M3<@9B*!:]&>P2UX)6`E7<@9*6\IKD;%*V^45"7+LZ18G"VA*7_PM,FR2HJ'8 -MD,VO0!=?I47D,'D!(L",.J]K3R,$:B@TBO#]M#DGG;_\*/:YSA)`XOOD(Q(# -M&88F^Q34CYKU:@0.:)1E9'R2_CY5"+;,.530_#LA+[*>-Z=+!WN?!>'9*7`R -ME;F/F&>E,SLJY*6S0SOF(4*_Z<@S9^\53WQS7)#8LZQ/VWS>?ND*K.;E@SP= -MN;1)Q"S_:0GW3V':"4R+A(]8:R[=T^4F)%B.%'QJ1D_W6S]-!T(K55+@&19P -MN'Y4>QY;6"HO4^"%_9;;N8>9&!-5K"B1?^+K)LZ$8@UJPDE94HYL8MV_K@EK -M>%9(G1D.'9@]-5#(G[*[?-"M/7X_MK/=WD5"`4PAKB58Z3EQ<9T/SV=4V<;< -MQI-.0V$9`DG@Z8@@%"I'$\M>.\6&*Y=U/=[(CIZV#P:$P*9W[MTBV9%$TOY< -M,OXP-(%E_S4O/DJR=)N$'!,KB^@=6557^`IEW&!'7\=7WMA!?G@M\^0]#5YA -MM"1T-)_U<'1&1NWY[4&(K_$.'2?1B+;E%Z4>%`N(IV9Y*K7BVY_Z@8A6TW[* -M7H5I\%]AZ1^X+>J/W/*`7:+%VMY#O>:.JSX&!\[2)G-T;0UWVB,@OH:E>^7O -MBFX`:7]:7.=7Y]$8GR"83--'<4T*9'>4L-S2._0#B..[6V%AU2AT`FRD$=:" -MM7<20UE+\^*:\?&7VY92(^=P+5L>&*'U,>(1M<&(%1N^2/<MN3E8N:J]E]TA -MEJ1^GK&+#`_R2A*]<V;O]"4C,&:\83DS]:?MTU30+P"Q*2*P;ORX50&:4U[/ -M*40QDJ7Z*"U7@2&`'1_C_&81+"!71Y>,"22-[P95;=WQVA(>RR%B5[S.\9X: -M6;;%R)N".K(B38[@5'=PT.-"%LGY&IG70OF(WAI%*^-"-$&Y=[-ZW^ZRAY2R -MDO0-5>"H#/N_'W*ZG"\U;"(A'WZJIBT%K*G5'W8G<8C<F'I%R[*N,DXY@O:+ -M;9>#-GQ89^^CQS_4J#!'FHNB0"_;53Q(GAW]<`"IVB>IJ[/WB"J6LEE:=6+Q -M*V,'4M%L:/HX.#$N&PDX->-H9<`3:48T_M58I^R#*O7F?($L/D`3)1L]85O4 -M@,WN\DEBT&L6[O&)CW^^2*'?[L1)?F_-I,!ROVMGTV%#,E=.-Z.P@CI3NV%^ -M?0P\:>P$RD8JR50/&7.YN1\P>U-6%$/\A9U6B&F3,MH/EH%FX$D1BCP\[.3& -M#1ZU>D`NJ">_A\^AK=3/T:80WAE:O_=1<(/+F\K>RW>:5G>.D,P51</V5F_F -MXB9JVKK#OPP*)P;3IBL]/1C%-X!K5W3%;ACG+FSXMH)OG-O2XTCFO171:SQ7 -M;_U#F=4=-G%?#G.-*5&`\:.3Q!@D:EQYZNQ$.,/OG"8**B?ZSAL0)AJ<-27) -MT=9_LNNPMSJI3J7/T#]AYNFT48C9-HT0+)C2"N.?D_P(_Q9IZ'$^7HA"T)*] -M_YH!Y1NA=CWN\U'V;.&I\(X>[K*E#!DE&_'VJP)]9%\501([L-:K!^KO%JDI -M[I-COIMZ'D70A<IT,?HVGJ/A/2/HF7T-O?N9-[8W#X)03`$?^JURH65?:BO0 -M!_0\A#A'K&-T9+"C]5)?#)"DK6E$2M2=;-/>;VK(-*#^2]-QS"S_SNLBHPDQ -MRH`$#.!D3?4-KB^5D4=J`'0^Z,TB:I^78ZW*+OW*BGL1CCRZ(5!J..&JD^Q< -MXS!W17U&J7*]"E5C)`C@,7H($!1ESTG9R8L))>6_/[Z>G;BGMU@2$Q8=RD$_ -M'Y9/<Q:SWT8@$='^S;%@964-*PSYE5.-RO'YXJ2UD:Z=VC33F;MFBKZ*4=ZY -M/B*4`,5E*<\'Y3X?C<[S-=1=@$#\P+LN=D?J?58C\,]Y`M(?NK5_YN*9/GN. -M@?^Z=<PIP<(UP+6(+F3<KG<A[X2+F9$L?W81)"'V!9V,ZRI;@6O'_6!KD)!? -M>K:IUA0$N:--)AR;T=3X0NK%_.25"O[D\J'/OWY&>$-F'87Z:,KU*/KN$]8Y -MRAS_8BU'LY/:H4>VHQ"SG`U>;6QLVDMDBSFS#XLLO88\>BDDOXW/EC_'UF:6 -MZDD-P56Y5UG(O6[>!E3M_*98R[;ZDB<,+<_)G2ZR')LY`<[+]L4HX5$N`N-\ -MAM\#SM/LS48'&&J6U3=.2FJ:_AP<Q4IZ,-YE.P)@WQ^*FU?=I%:IXU.9;!/L -M)!*_>:8:&_7G180)1.7-)*:5&9#;7%O^.H;XNSX*V&G+^PO=Y10^+'BUISY9 -MT[X78E%50C=4M?#Z-Y<ZN9&/=P_7L#SI.T2_=:=UP*3\T%`<`R>M>4?RO+7M -MQM5?J^DOSCK1_B(OXF(9[!^WIX@VQ)`^>Y10ICYGAV:`N[R;]/D^<]:`@UP- -M<A8^K9AU^E77,:<"L0J%[H.^L^0[%!+TYTE?[6N^3[FG7QJ*90;9JIJ6OQ[V -MJ9ME`GQ*-BSN0MWYX9JSL<_R$X<"?@<TP6)NK!PGPQ)U[\[`&FG^_03>:DX] -M!;KG36`94`H=JWD65.;^A[H#4*<ZBVGS&^W#E3)VJ0E!;RB7G_Z:J94J`;Q^ -M6+)1<B!=G'!DCQ$-%FB;0\*ZO37PQ#`C49F<82E6<'PHEKZX$[F^Y6S\(JM% -M%[O21_T_ZQ)Q1IX_$)AXJ59JSA'SI@:ZE)],(^35-@`Z:?&]0_&D?PG>!R<" -M&X<0?/H(L@OP@""%1)3:T.=8U?*(7[J1WZE6LT:CYF=!DX>1]<K@&=:I=T8B -MK`MO*OMH(H8@YI9ROTOC8RY"4F))F6CT1/#V5K^:0T*I)F1<"W<>%J>OW8I" -MGP)4'8INX*:E54P9S^3_UL0OZE73$ES$6G_FZMN'9QR2F!$;4Z[-Z@N,##S[ -M=J_^5/8F<R"8M-MVD!0B`@B'%+UG<$+"(!9_N]$3IXKWL`^*^!^=Q@M_8/D< -M_/UL@,KX!4'9%*S/S.N.2XC:!6R1H$L!,"T?#4(U#`IEC5_?Z(#*K?X5NQN_ -M\5WE>C>!LD&`?R][OR*FRL:_H%"5C!&"M8+%/Z)YG-YM\2G]:P#2IUL-JGQ5 -M5>:>YCNP^!V1HZ.PX_(,;R;14(]-J0F:%4FVSPUDH'MVG[]7IK=:_^Q?ZLOY -M4O\$H2.`<-2J'$PK&I?`*_^.>(`H[<117@S]'GZ`K^(WIM5I44/2BGTE/PI, -MW31U?1HW6K14EG9W?IT>*419,5#-N\(H+:^13C6LVQ(L@]J&/EZ^85S:%_KW -M$$_[8''Q\K?B5UV89Q37[MA/Y$A%:>""E^Q9U5325Q"IS4OJ27H)$1Z=G"H2 -MBY<H.M/H@5*U#^A&*ZO>@I[21%Q1.`/?945EB,<W`;?_4V_`,GQ8/G0!X"+[ -M-D@](0'4%6Z<\>/5=;UC2>>&>B`>`D^>RRK`@[FP/9SP?_O!][3EB%[Y$9UK -M3>@VOH:[5]#M?38L>$Y2CFN/ES/,7["%,9M-&R7R5$@`XYXUE$L(1>2,&@YY -M2(9?Q+N^76(BOGZ@;G[C=]I7HZ)AD+CLR25G8J;9@FJDRJ(#9VA^37F1.+_@ -M8`(;<$GG#YM34V+?=%N>SL?-;416UOU]XRP^<87/7QS+XX&1I..=D*=<Y&!^ -M_1!A9#=XN<]?_[C)-#).W5VDA1P\]?5/Q!YE"G?,OE:?W9Y<J31=*L4IMM]2 -ME!9V-<MI7!NO6)PL7V1\IN\"YM]_89YAY^";281(V<@V<;L.-?WJTKS)J9"( -M33,*LD<\X9.UC:Q3;?[%17T_.EI/K%DP1Z\!00P]*Z)_=NAAS1S!D(A4QK*K -M:W;"4P']NSB]G5L84FW:Y?:Q($^*V'A+&Z=HJNOY/Y&[913`O0*FL;U*C-/$ -M@52+,:H&M7#.N%DWQ;YVU:_YW.0"D*A,`1+XWB/$`RGB(8>@WAVZ/"3C@?;W -M:/$Q#-.%>Y6`0_J.R;V]-P/>&?^<SI8UH2X:#K!0[CGG"/_<[^BTC?"8E.(` -M8F<U^#B,#A&FM.[<:+3+GS7)\3Y9"/KVZ(&/_4R8QW7L\RMO>PF:R[&H?&H` -M*`C[G`)AN2;NIVP[J$5#/3<<!CVY^M;NFTG[RD9=(&Z"(8K;\5)>4+D:KPVF -M<73KSWAXZ!*TU,CLI^46,C*P-S%ZV'H6JCM1'-?-DU,GEAQM797_I%$J]D#B -MI"W(.,^L5"H6C-CMLQ*JZB\-6S'>R%E]`%=)/$\OI4M*<%I87?CS=K&4O8N$ -MJA."LPUD+0VG;>\]!*Y_U:6;@P`)Q#B5)=.J^:?HV_);X*5R#1J%O.2.\<)! -MTD0@4D43IT[,&&&O.TGJG-RCTAG174KP/$JZ;WOOS/48^I,$NMZ(%`@V]V`3 -MPAY4OS(0/L]1B/67786TXR:CO!L'O_M7UST'W:C>U3FOTKQQI[0FWU]$Z+Z# -MBN(1>D-V6MZFZ_A-:4VD$OK>QU\/\94B`^,A/2-CE2(WZ[DFAYC>9%8%M=[? -MF&0]5(OA_P:?G)`EIS8@&7OE<[W,1DJSV;LJBGG7.+'(JH4^YP==+D_+&/,? -MJ*;\*[J)AT9M-5,\1E7AZE'XN0?/=)UR`('0'P+.:/IEYUA\UB&78T#V!XIG -M''8/J^EQ22E56A>*7JO4KBWEQD8-B8/W7$$#QP,Y:FZW:MK`-Y/G0K`=/"=M -M<3X!Z!U"UNIA!XK;)5BH24*5IP8T\P2+]XD6*9?R^5""*?I'U.F-XNUE/Y+Y -M(=<AXK7COA`J3.!?TWRH-<];KEE*]V+GM.+>%XGTSAD,-22NX2'M,;[[O#,Z -M2ANUP%M#H+\Y4S#26N^09,"BT\<@'+JU"\4@#.2I?&4'I-@-N"TVDE=;GAJ^ -MMQ?''*K^-@10VEHJ^:LJUT^E2MZAE/T5_@/")KVX[KHZ6;>%"85WA+XG&L,4 -M#N$7"2>QN;GM%X]M+<YD.VR)D2+XS1R1WE[II?G6)Z$I=YA\$%03!K*^("!0 -M_?`YGL\%_`P*;'/QD)6B\3CJF(1$)+@3*XQ(YOV]>H`F.,S3(8QF1B"<V!P$ -MB]%;(N-(RM7"NH^E&<3>TR0Q.XPF-*=?O[#!_N97%$/K-"EZE\KU"\ZL,Q1R -M0;(LW'YN:.&DLTR4&<!YH0W"-X2>^L4/5>E-]Y]@PDM93)L1-3HO%UPD&<2B -M,&<7I6`%[:]/7%87M74T<<$W+J=T5+%SUP&"/KI_6FPB+[5ST5#]S:5,CX#: -ME"4P2H4X[-8>[,0;T3AI8WJ%E;;A.%527A608M]Y;MWD;Y>-:7.]@&CX3)!L -MTH(M]#-\2PSU'T'1(OHL@8?P&6MX](#PFT\YOO&(*`6#R[UUD`P=1.]I<:3C -MQ#R@'G`&WQ9;O=TO'(]G5C3K;`T[<DA)##NWXB,"=,E#_*!CK$%`\XM#H(7W -M[H*Z&[GBQYBH:#!WH-03L:1Q9-F^].6;!3";A!H9<U=30]8"KP8<[/=3G9R3 -MX,ZH4WPWL#N:U(L&'7#K^8,AF#4S=JN-CXM"[_XVU:V>B\[I*KND&7V(RKIS -M3..2Y8)(-0,43)M79C*EQ.]/;\J,XQ5[/'W-=H+*Z&*_OCJ_1^5&`3U2Y&A0 -M4F"%,ZFI%;"6]_YT[ZS76H^1P9[&+7E^E\=ON!GA?0,9<PEX8CL-W8N0:U[/ -M:$E<SB57(<Q=_Z@F)\"%[DV+`P;OO>I@H0%5$%F_[R+NRC_9/86X6V.FQK;C -MZF:W)QAM@N/#10*69+<=KM&TK#;6GK5JS^G6<C<.$;I`[W;,FJY)1T2C\Q<O -M%HA\QR9")PKB9O`SC%$WB.X-30V,;T*,N6"N-_);HCI4_II/@/?<UIK3`.0/ -MZ'$D]5<QE5X\MZ$`6=9<,FPR8HH2_LL-)I)&Q/ETA@V]?<R7L[@^&/TS,[&K -MI$6LX]Q71N\>>`T9'LR\UCWO.N##D4(8P=KRV2Q2]JX]\,"M"U'YFWP0%AV5 -M)`5?>RHO]UU_Y[NYH5ZOYPUHR7J2]K4X"^U6_4R/+^_U'J$[0,:OWY&R8Z3/ -M`!K_X<8#`A`/2ZLK1I*_,7/K`WY9FE7]:;Z+`P)WH^#)^FNLN7I$DR9WGS2W -MFMA\#TJ0A,(Q1S5.,HH&24/&U1)=505B/K&4]XMMC7@\X0(AY/$'W'+QPQ[B -M+H9-V:8+'S$GL^_;SL`GW.+GU.A(D]GRO\HE^)/9RP?>\'W2OZW!->_A;/\K -MU:T<#+>#XSUR.D(L#(-CMF(GO";DEQ98.\R7OLU+Y[KXBX7!>$92U-<HQZ.T -M/(5=GF9_OP_(E<[G;?2`8P3XC![XU#0?=-99M]+R(XCU08'MRRN$(12(3?D* -M!X(M_ZN7\($K(*[].IK8_U&)P%%KN;O4M&X+F>>&^5O>,M$GOAEH5]@Y\-U0 -M\]GBOA`&H4,G-J[.GKDZ[ZC4Z=G+"7T)L!J[?A>`:#X)W1U^6N"LOF;.8I=& -MLH+BLQP:4L`B00W6M+]K)0/;22*EO&64IKDRL[X]=2E@.N3I4T67^/"UO+W8 -M#Q77K(18U]GC0M.MM4$)."(%]R@FZ.^D9Y'':-+MR8=MLOZCK.,/YLU7<7YC -M/`R>9$NCGL!R!&[WQR.5Y7_,8TS"^-CH6QNL%&*_L=,03,.G&_DK(&1H'L6; -M?.&>I,#Q.I*O*G61<SD(P.]*<:>7.=O"OJ`JL(];`)3D[3#H=5/M9/$@#:(\ -MY+=3QS4(%J)$/;R.\-8F@Y]DV65^;7MW#Q8IMHN"[;KI:Y+<$[=>!H>1;N`B -M$'K@X;NU^PNJUM-"(M,XD59P,.=4>&0S90ZBHKPHF-VNUNDTT$#'34*=<3#8 -MH3\YV_Q,SS2[AV)=L;+E693>TZIO^IZG1"4:-ZEO-,M8$8"PH9(27Z$$]_TN -M*"7)]AO?2H.K*V(R)\C#3A`P";,=5@3_GK,#H,CQ_1#5C@G-G[NKQC2#WX6= -MNMB&QA@.+>CK?FVU!G*.9SUH".<EF:-Z2\B8]H*_G?0`SK^RK`H^<F"W(&K! -M*[-M#%1^0H&$H].$ZUN/(KJFZ-[!5QIUE*?7+/)?(JZ;YN]XE,O_+M*<G:VL -M,X7GH1'0!J)KT;%_Z#9[<[\'"MUG-YIO?;CT^*D$R5-NNT@/C')L;NM*_T]_ -MJGJH(58$KL1EP4D'`!ENQ-&&*1_@ZR:6@EUPB=3[T]NW,4LI^-1FHU><+MJ+ -M!2D:)7,*!C]#O9*:A%O#QJR&=RGC^(5\3GY=C6`YAJQ-X*'*6?'_5=,_WGLL -M3>M\*%8D%]<-)N?!<^<T=F01L.,`9!8J-W*^].S0]GJDF3$6[-91JQ"A&X;H -M??GQ/NG"<_LX!D[R7V^M^T9S;OW+BKBRVNG5'51YK61/YG#\T3N3UQZ]TG,% -M+8$YS.Z\@;3^"MBA*MW3;E`?I/BP+LM9$O2O!EYCQBE*H%&+6/R8^9LF9!;) -MZ[)GBKM-0VPTUVY9G9".H+N-%$5E.X"XK*)(XTA]",P<6[[OT@!/1%10B(4% -MW?*#@#\620KAB9R!PB-5=+:*?9_PY1O(5ZI.]ETO.<6&S<UY"/.7#HFR%+W: -ME^X-\^QE:W>"#GB9U+8'<,H<B69-N<-"\/>Y*GT3U')@4`H]\(GD'3QS!K6X -M-*H[$-".5'*>]P;1)3T/*DOPPEVS=.Y7&"_JRK_(@4%Q]<W5%VNF<&0Y]:R: -M'8!PGC$/"L5Q09G)WA:`)2&GG3#QXW.9M$`$(@SOVTT@MY[JE0F`".:%Z8#" -M^O>D99QF$,8NA,):#D'9!P>6N)PL4%$DK/``"@`,&<LL,[AI)BB.U!!O:A44 -M)VO6D6H.-\;+LMW1[6$.IJY@(T(_9J'[^21`_\#>1!]:P$1Y-J!?K-G?&NYC -MD?GEPYJ#.A-"VS]VU"3)P2KJ'T4K<[`0*Z_AV*TK+UC[*7KN.U>%*I^OZ1.? -MY1LK6,3JZ82H[8*G,8W5W/-3'1+8`%91>W3_H:];:G',D&;.+6U$"8Z7G_N% -M)T*A9PT@S2/B.KOMUTD">+'FKX=<?>BT!$^;P9M3J%;CVG+[C3-YJ74,=R*U -M3RD2;7SJX$+Y`L8S)W]#*%'^SQZ7;UFU[(ISN2B-2/)"D;V8<<:&<#_I!<PJ -M+KB%-E_X#MZ<]R'-M_[W98J=V6"2WP3/:[P7\?Y<Y?C-?+U,+)<1.TB%BB3\ -ML4$,WF/:4P!JXBE:(S-]QB7@#QG;#L;/86S[*Z8BIK'__P'U($?=H.`,G3+? -MZ9!6_B"-!B`JX+#RV,B'KE][`^P0-URH<G!'UJ/5,&W[V,63;/4J!>:>(%*@ -MWZ=T6F6?=1%\QZ16"-,.[=*D\D=%TAER<%'8"]#V>/P[:(X+%TK15,/"@''. -MLRS,(9>6'2*/R[BEV,ELSZ"S^SJ6C`T+@@8V3N5&>7AJF^ZI'^)M9)I$NX:Q -MEMUGSUB0&*ZY'J<4'[.(51F:DF$P3)Y<)+DUA@;05M9K"^C9\RU-*#4P+LY% -MQYSY9M=5<$%3(`-G=*`.:_G^<#AA!Q$2&7TJ]A;MBC&2_M:#$#(S6+?+3GK9 -MP\<1/ZGXE>B!K:+@AETBQVH]@X^=]4]164@EPM,46#L)&1=1LE6Y%X$XQ*U9 -MK(X'<@K!ED'#=93HJA<%CTLVM29`8S=U\RCFRPU'!$`FAX^?@YUQ^-34/E)V -M?FUT[WMFY$P:XB9JMXWK/1#6<9#F9U9@">@[2*\4Z7#(8N_Q\HL*O8!H[B9] -MYS97;)QSEJR2Z,R)T\#9N=(?5^1IY*4)G.N8C4;;'%`D8#&@3'C>6&R-;LRN -M.!6]SDCZWAEO%MR]0<<C?TX1+T+L%70-ZD);_$PH*XK0M]O8I6QM07<[`)!Q -M[I->4`#$@>7?L3UN<U3S=V^1=\$ONO3JF$.]@8'PL!F#]C'6\E!R:858(?R< -M'-<K>95&.Z8$K?UMG.V3"(JWZ*`A,V=NG>O9&<KDN&@WV@IN9#KWHVE^WX;& -M3RX<:!B2*HZSFYR">ANB2/C%%=O?[NU01`>QFEE9)YD:SCC,M%B;%[W/LEWC -MX4?8\&_ZMQPQH<B]UH1+O<",@;;4@P_FQ02G9,\BNOM$_?YR"7@U2;I<$&$G -MS5D4KG;5\6^^Y<H/L?^2N[@5KN<+6YV*P2-7OX4`K=U:%1`JE>"N$<Z2228C -M).;;F:9/1M94D"8T"\_@R?I05B<I#_=TMJ(0Y$R'J5*Y(%/G6`YBK1'M^ZQ" -MSH!=YL:F35ZBZWAA;$)7+W3>K@0FO[CFS_17G`_GS)+R0$?\O6:6^I%C&$U` -MX)Q&KF+]&U5U9:^[&E/!C@4.P=R'HR]Q9@-TWYLMP6%MZ]PW0'=NA@P!K,J8 -M_IO^]LEK8]\8K:O1(*$7FCBC>+VT.;7,8.#>>'^RJEJ=X_D@=;JB9?BB>4-F -MW>8'A=9J4C.0#XJX<'H+$S:_B/M;)E/-,FXG8$A62@_<TVQT^L3%_'G'!P8J -M!JSV_KA%,S')9R5#5W^4L18FO17;+J,4/.NRM&*9*05"M+'RZVUA;,M&5XT[ -MM_;9D9KG<:L@-ZU_.")UTOS=SA/4,^L[)*]2BDV5EFD;.=WFAK#\1*2-ZO/) -M:XSD76FH%(4GH,P(0A]"?Q4B^8)HFZ4PQK5L\2&YQ8ZE4RJG_&GQO>;5#1%D -M)<AOSP"O0V@S48A5D+@:OH<?CBND9G<-:M)NS>20<ASP,:;EU9_0VG*'O5Q@ -M"G#!(SE^6+Z5I)E%WK??#%5N:X<(%//Y[4\4SWXJ%&C.J5AA134_6G8K)2^^ -M@R$DU8;_:'8WF#9#'I%V^HY6L;14`[XRT[!P<V,2)S!7[X>N4E"MA]`[?/OU -M[-3ZV,FRL,`_&\X&Z3C0X58LUQ7_%PO,(Z-&VZR'5X'[(@R7.'J00"8MAMUM -M'CO;M7CUSZ.'<RFTZ_,-O<.Y9ET5D!,![=C*.1-,DXB60[%<=<V1[O%U&(L+ -M_P2_.X4/.P2#/SC$BF.&B!DN!0$G`'_TWM\5YBA'PC\AM;OMGM->J>KU+*(" -MT]^D0EN]\YK>=2%$QN5W\@``MW2];(,+,Q_HJ(`Q`P;@'"E$QODV>/2<01)F -M6T_=7^1^E:TA'#B\W`+7<:=0HL]AI#2O::_[FH$6CUC7X3H/"W_I[T8R7\06 -MB?=N!#"4DK-'?ONGUU^OP*45=G^08KQ;Y]ULV6Y%==X$3:E=@LUK3N8LL18@ -M7E`0][#62YIFA$BH>X0^A9-RMB#$$D8T]/OWM;JDS+`IOQI==05";!XL[_CK -MVA$0T5V!E4Y1\Y1!6WEAG@?$C`FJ>8)A$^J&Z/:L\^'AI11EU^\2F,Z+&`\7 -M+#DB+.*;]Q>33!]+*9TY?61P;]$8:G-DZ__,]#FI76>HGD,R"#5/#=NEU&FL -M7OYWRBR$TP-;(Q3EA&V"2UPD*2775Z%(]1X%"2BY4>Y"*='P)[WO#*P&<,#W -M]X-T77R*DB80/#"`BE(PIY$!4;1U&S]%@2WWV>2MFE5W'@&YU\F,0TU!YM_@ -ME3J[Y@,1[*A=;4B&@8H7ELH5]LT;.+V6)V@6=B7CHA4Y#1"6`>M_P,C<SSA; -MS9HBIQ?NJ!GO7KD)["%98C/A]SP9S*BVGO%*K!WTJ*6)L2\,\-QW$"],X/Q] -M",]+%\;/<7++.O2.5^!,8=D];Q=`(C0TCM\A^P%N2E3PL1-=9M1`C#U63Y7C -ME)7AKDR>$#TQIA-.<17]ZZZM874%&EZX"JA**6Y]1F%-T^TVXQ4@W1I8(*ZW -MVK#!CICY-ABSGHQ?72M%+/!`0N3J-5<;XGN\@Z1`1$IP1F>S<SF[`9+%RI,' -M'4`C(FF8\\XQ!\\7:3)K?K?X!I7]R@(@QZNTDX2Q-0[2ICNM05K.L-$*"M40 -M,#)!BBD]N\`K#`7;1G!Z(/01"&KR6Z5)H;<3I6JNC5\GI92)&2@]-BO8*05$ -MT&>_D0W63>NO\#>21>J<8JC+,N_,&[*7I,K[\;\=[B#"4]3O5J<;YY8K1HA/ -ML_8!JEH'@U)+:/Z!TFRGH[J44U&%M!EIH$5ZA$L\L:``@27'?:UK89-EZ>,O -M0KHG*O(UXIR-ZOZ0'Y38+FTW425PL+%>!R[1VS_F4F$%0K_O'8UGDZ:D[V'[ -M\UTV10?8R\6M?;^P7Q(75RZ=XA?E7$_/.$^:D:,S)7W95(C[=R-R[4EF+!5E -M,HUVA//CS:&VUW/8\9-"H\04LD7M)^2_>#&!TB$Q@0CE'`1[4N=-EY%VYKCK -M2Y3*&8E_"'FV#8"8V]WVLKA#C?<"&B<%5PH#*;#CUS*QZS)%!LA"=%OE)C.G -M^Y`DM)!NN`+6(7U#14RK1#1C96*;MW3BD2X@S,VY?+D+_VK)_RSY\"TG/LLD -MT*&[,[!6&;?:B$*H'>@`@+Q3DR$)Z/*$2V?_;V[G\:(I6AE#>82U2O=)*TP- -MSJY>+0_FI(A3G/6(K5CO*R1(G<;(J0(.'\K-[';]/N('6$<+2J=X2U^X!_D` -MH(??L?VPDD$W$`O?[5(OCH4D%SJ5D+6$([B#/A][4.P^Q!CG8G$82I,T_`_? -MIH"V7L'(/I2(S#!WTHL_?VIAC#'BH<EJD<Q/O=9:.L"@\U?)X;G(/&$]>%Q7 -M\.O6Q"2Y>#O!5(;;#TH=^9/\D)0%KRNP3?E_U&`Z.K-CG>!]6KK8PY=8A7AF -M\%XQ+MXIT)7R:8!!/@)DG>#<4EC-46$_=7`J<C_RIAXGF<+>OZCZ20+)Y.*' -MM%O"=KL`!T-1H0\&*F)`KEVK2_:,([/$>XT+M9]P3OGQ`O2J6AI==8Y8\$_; -MD40D+'1$IPD$\BRY-O49>S^S&BC0_1&.Z:5KC?F[C3;A"X^J7-V$;=#45O94 -M&/O+/9'?H]*B^.X7)?^BW+XA34\[#B^)S,&MFI:/T?:7^+\'_)<>\,;/+NR% -MD@0ZV38LVLC>+,-K0[GQ@D$]P?VIJ+R_QRE'QPV7R>R\D4>O3IF(,EHS+O/V -M3O!I((?&D=1XSS`@R!%7V`[!AQ=<L_D>\LRW0;K')=$`G,T9!V88GJZ/+%-< -M$*SY%T4'3*-1)/FO$4JUDJ,=I#'Q>_L5<1G8>@REOP`],+G,_%69_@]*N5*X -M:O4H^.MDXVWOS/U`^ZQHPD37!G)UU`AE:5,5E??'GY@;G[?CK^')\A9Z^-CX -M"&(E#P',R%"_:^^&8]1+Z$><VC%@UY4/P;I#*B^[GLZ<ER](5)V,;J/1L5K^ -MT0OJV`PEX.D4N*U"IS+F"<",_IW!F8/>4(%I[`8C\\@XVW7BIR&EI,`9%FV6 -M;&ZQ7!@$J0P7%5XZX#Y?\T!%]G]TN==$LIRZQGE*:Y]V%B?^$W21@Q(YP'%\ -M6X)U!R\@!YRTHO_/#6+$ZNNR4(;=4W5V!>@"%T-#F?]LH*RG<8M2(2SE>LJT -MJN>;AAHK47VWF;XJ.,#T$Q=_9N9?3LJ_FYN,DG-O*P/1ASF@VQG5,Z#<VKP` -MM47K[+VWR!>P*"*$99*"6PBIFW]95HFR]*T6FZ38DW&@GLBZ,;S3+AA1$P6? -M0RYF@+:HZ1VD],KKR;8PQPQ)A;LMURJ7Z'U;\*O\ZW%*W!>&DFABOP-?R*]6 -M\Q'%00RMAC;\W6Z+!M)N0HPHB%&6I9)AV>/(<9.KD56UZ6Y/KU_31W1-%0H] -M\Q2F=*:%Z4%*<(;S:CJF8NPHB'*!:&HG+*N:3Z`\)J6_L/'GW8;;NWX%.5K[ -M)1CIUMK=H!R3P)DSM^%!.%4<Q568OT<!1XNV6I;5G.O):KHR1@C]^P8K@E=^ -M(4BFHCI(WJ0C9'+=XN7V9"QUQ.E?J$?.<'_V\LS/"Q!'V_2%_L&-+L8$W5MZ -MAQC89F?3J/#)6G.L?6S"[/R`$45M,L`D]80+Q@IBBNH^%6%Q)T!AC)/_[YG* -MP,.<HA9TO=!5E8(WU-#O/?:;^%_F4C]0[/WI?0Q<X^V9\:W.6HT*7FU-B$5E -MI61$R`]4EM@$$.4I9Q*8W34$9?H)*>"1>=VS@SST/**G[-R<2F_I<Z"2ZV"7 -M&T9WE!.N`6,8O3@-9TQ-^],'/Z.M#7/F`8NK?%C^0P\CM.[?=PE-;$!A7[)T -M6Z2Y^?0#_Y<9^<>/E`#$E`5TD!T;&1`^I+,I=WP$Z*0>WD?Z!2G*7L1PSM?\ -M**NC#SIL6B<4UV%_RFZX>7),GS8B%:X$00%#39?#X6G8Y%^/U`J<==8XBG1X -MNF:&/,)7B*;MQO=\!CMJF4F487=$&)6#^RCQI9<[K<I+$3/%7SI><C=/?A<9 -M>:H=ER7#Z?HTQRL;(*A3O24<KH6M5ODE-)9$GC@%*^YE5V.QR0V^1=F9:>O< -M<#OH.)`D:#V26C#A^._9BS;H'[?#964+RO043[$EWBR01VOFK>Q4ZJPH..5D -MDCH\W?I:'V+I9KIPL4Y>LC@HT(R^GE]?MH6T)0Q[.!^Z&441Q10;HI6P!Y@W -M+/'(L.:.`HS4[L-V`PI1V/`I+TXAU]5D7_&+^R+?RY31:%M^&=4.Y#9"G>PQ -MS*H?50QJ',(1-'H[.*JN)/;G-4:6@6Z_(6Q#Q)7>A6``=#U!?,8`#L;/1MS- -M!EO0M4<X!+:/ZCD2H0GQO#PXS+0]?N7N?P*I&FPZU&"]@.!4.(Q[D>SP7%./ -MU`IHW.,=<_4>><^4/Q+K"=G\'[MRD,MRS^V4`#/BS])8SK+#*%/@3$0S\+5' -M9Z&UF](VO!*\L$S]!PMX3TCSY%L$W3;\1QU\P$MI3/!>XV.`QH2OV;?O255* -MZ6;YPPMA<S`W%S4<=3F8N3P\@BC#M(P#Y?W-O1MCVA(G0I8*8?#67B4M1$[9 -MGE+3M99:]:1LW4"'F$Q)OVOI:\&:#GJ+/0OP?96=D1X/<G\/;]("9RD9A4DR -M7]GZ+_9$U==:R=IU/Q(IK)=-(^U)>^D:7I`ESE)BN>6B=K>/KA!?;Z0#.&%. -M_]F.N/KYJ6P!!DRSK.:T!)8WP%'.TO9&)1LD";IG`KJ(!D^L=XX\6>Y[<W\] -M#HSO'0E!S,S=#V>EXI][C:1@AN?8W$1Z>5QY`0)0:6-Y5S&P9"F8H?C;:L\B -MRQ4@PVVCZ[U^!?#R=Q.\IG>0X>+K&OY?G2Y0H=#P9V9^!V=Q-8-\,`289CCO -M*)EX1'3=.=98&MRS,O&(JCR^^J1[9;`Z%KO\>OSQ^W2YB+/8,;C:#4EI09U9 -MY]#\Z_+/&%_1HZ&!L-%Y<8IPE%[`X9^9A(Q!*S'3N]ZV[&&,*QL7"EKFY6Q[ -M8I@F;C0CJSC_7$*;2\4![)$]JPB9DG;X<&`\\][W_@I5VC+&O)Z/G00U*&?N -M("[)/C/>R*/+3E$_*9`Y<Y)U]5SI?@P%#_VRM2*EBG&"\K"K,IH-RET5SZ%U -MH/&(3N(]MB$8W>DZQ>NRHN-HF%HS:8Z<]W(VPTR=BZM(6*^CPRSM(B*4E&*3 -M5Y6UQ3KKW2)Q-PMW+Q2,'XC;Y):T,#1,M<AP<)/-7:WL14/4UN*5?O(5Y.RT -M;MCM#4E=-/GVW@/,B^"JU.,;<!)"N#M_D@V?!)C#[E:UNIU)2>'^\B.#$I@3 -M_RJKJ0E%"+3*6_.X$:UT&MX5U`J-B<;X6U-[36T'&!.SGKF@$[;P1KW0I/H$ -MPKXRX;L/SS?1$OE3HV!O!$>S+.*CT;NVS`RLXS9_\O>_`RC5IR``<)U$BPG7 -MNJ-C!%%[`^[6W(,8J/*7A\*75G$EEI$E,6-ZZD/SNW<\5!+/K66^*1U7G%S< -MYB'T61&&ZKZ3%WO:]Q0(SG..[RE3<$2"CE457HR84UEKPSA8&(\MHW29CT75 -M>;SPA!9FLWD2A2B*JT,W((8XS\]1,V3BG5L@YXK!JS<6Y(,$NVL332ZXQ*SE -M@/&[A^)1>T(J\E^!E_U?S8-QU8K5BF2.`YO$G18R(96;([T;0>SNM4R.UF*2 -M2(LKS#V8Y=B`XNY-:2<G4C)`,%>'J7H`'N4ZWI%),3[IT/*8Q"9+VQS2(Z5Q -MX0MZ](%N)9#"_O"WI(J_8J23'MS1\.UNJ)0:^*OA6>,:%YC^,&"_!/B&%/Z^ -M6$,+#(2J"IZ@4RBM6$!TP*K$-]6[XSUL5VQ>)S>U++XWN.$1PMEZ0<*L:FB5 -MNX,N`4S'DF>`V*HM*/SK+PO<[<*4)JA$Z#<>D+G#%X(8-0O;].LE/\&@L.NO -MOSS)*LM/F#UHYEFEP[XT;JZ;H'6OQX]IUXN]Q2'@-^R0N`K'$92!9<W&)G%U -M++`=]0B@P3O37)L4<9,9DQ,F1(0^2;S'@6B@WIJ$.6C*\@EAJ_JE#2#=(S97 -MDXR`9#+<9\X(363=_OS9Z"=^J_NP8(RRKDI3!W_Q40FW`WLM\=]`/I'QW-*5 -M!R3!%%/X3]R29KT*3_64AFE)G&AM'U+,M])W;!>X$FUYR)KKJ^*$B%"3+YQU -MB\"^.":MIN*%RO-G6M9YN.DWU8_U(BU+$O#I2MR%4E37.&?$%N^$/#'<NNV# -M2YQUNN68N.W8;C=8QMN5P]2YT^PE0ZF[*(C":&<'D]G7?(@'M-O1C)U_JAF/ -M:(L17VD!.XO&4*%`V-ZETNS[^-^QTY0S[#\QO6O^&`._3N1&NIUXVW/S'18( -M)YF/J8U:+.\('B]9AM6R@%`HUDZ-^\"QEQ*N./0T!Y2*I$)2&!1^BSR/U"!R -M38QE)A0;@(VF#</3+;.)%N<B4QX]5BT9H;U8X+LJK@9V([[4=.S+6SM3KO1W -M`>5"@3GZKW$*TON"$7KBBO:AW+K%VARVX7P_Z2.DSEJSO[7X=>MKK-:J%[$; -M1,8$GM;NL+Y^GM&%K_H^'BNVXHS97OSHEZ;UVVAX,"J3F1GY`P*/T5$KK1%@ -M+]U8_/,[X3TO<2N2'8\S*%+^&S,',*@F[K([GQT16^!C?8C-WT`>V[K-$D)/ -M8OCAAJH\=1=5A2K$Q/Q$XS6H43#&EH$`?`M^\O;4CU"WIVV/GNMYNH<M*VMD -MO$GBGY+YL8*NVO3P.?@6D6-TRYL_W#7K@UR-Q]<U@^-%((M**19,WX6MI(EW -M?U*@VY=:%OUK*H"JDGQI/YO;]"\^KH$2XFS+4FXPL[$S,*3%H\8,QMZV2*F- -M76\S520ZJPUAUYP32(B-5N#<LPD8P@-##>PL'6,J>$MEC/_!XC'N^R3)'>A) -M:+#J!877OWH8%LM67^]X^RG6!@.4M(-?'85R43'V>ONR9O!=A(=C;>T5FUB4 -M?"`?[/H3ZV*[@WN?*E\;V/54,3>W#4=\G&0@4&@N(Z'DYHSK*0MSRJMV-DO5 -M)3I`%T0<&G<2G[,EMXE"HP?GPR!5QPW*R9Z*M[!;#CE4/Z?8;Q!A[#"\2;!S -M$\QO&1\:NE!_ZB,C-1S$5?<.,K)ENXN2/)]>X[QJ6G`,CUVYZ_FX1`/(&A7_ -MMVK^'&SN`YYZL@)/H`FY]Q2<MPNFJ=+ZR8!$VBT\IN[,2HA$>NQNP!BF'HPK -M7J<IT#Q91V_H9R4R4:NYVDTP(>1+)AQ#DSLR.2VXR+D=^R5JCW[FW:,*='[4 -M5<_)D@$EEG+=TX'97"N06;!&6XO3_SL4!BJ@;C<5DN85:S@WM!?O,BJY#/)W -MW"Z$)C#<SIM.NXQU^%_#9L[PIK/G@3DBC"$!L],I^Y_MS\SB-#:/59RF!C0C -M:[4YXEB(]^J@C__4`H&UQ`?YN.)L!!@PY@E;GYDV^'X_]1P'@!(\$7D3IL[> -M^<KM9T<AC0F9D\)GL$=.`6,PI0V>E=R06ZI/;GS6?E_6PQ*XCP/]'L8]OJSI -MB*3A(E7#(&%KV^-"OBV:NN5#<^0*LX8GRF@@R:V^>[<6ANRR)LQJ`4,4]FI] -M&)+"8:@;+ZQJN+W7!=T\F.MD[GH%A@W]=\"`SC].(S#86@1)'RFI_+[D/#X` -M#[,`-B=:IV:>X+59^Z3;:GQC-J7=N?_;&P@_3X+$['JLSCP?/T,`5R<4X+^! -MSX(C@$B%M\=$.OMQQHGDPPJ<MIG$0D\4BL7P1[$(X*?[2WQK/[_;R9+0!V,E -M=FQ75<I](B&$I-97(N/QW]1L=9D9?A,-ZW^>LH^W[B/@-PB)*)Z!Z;]3O+GB -MZS;8NM)13N[=_NB/3[V*"MD)QYD:WV`XII'<]`YN6/6T6F"%D]Q%.J54?:Z# -MVYIM#%6%D7)G-C,LG2H>XZ!W_[^D#MV6W7_3LA8P3T4YB64':;ZT"/+E.^,@ -M<^TW;^UIXJ;(?&ET"4H'+Y/7,0$`/RL!D/"X4DU;3/A3+M-ZLOQJ,7'RGZO2 -MSB5.<MZ]]D+B4:D%R#N(M6O*]>3>9IZ7]BE!`T+ZR<W1U+;HH5^/H[,7TVZ= -M$GW)QV:E^TO-2Y(P*+9U#%Q:')/W)V[$XMW^V#"OI2-H1J%:4,Y2K9_2;=\3 -M7KI=(^?#O$P>L[74W>,PI`$UW=IT0)EKTD55D9J)AUE7V,NOSHVR+R0(M;UE -MM8LRCG%!;<U_^"3J:)#3#&AT&;FVPNT%:%)`O%*[Z*Y;OM28ILNW1:5E[N]= -M3FM5^J6A^)^L#;^'Y#'R(U$JCS<4VGH5NGQ#[`C(7,128F9"VH=LB7?QN;'T -MWBR[+I\@J>K6U@)!EEIDM![.C6]^>IDVKC700S^P(&JP+`H-*[;QI%,W?J-F -MG1M'/@H<U9>;"98[<1D:QJPB/,<R_2_-%R^0(`+[(D?_JC8./;*BNK$CRAZP -M`IKR#(_P24"-HR:UX4MXS6V[!>'[CQ+9%UHW4^K%GP551%Q%JECG2)11JX-N -M58I*]L94+1>Y@(A$E/.QD&T1/J6>^FNR0\;08+L?C#%M)#T->9M=^;CQ@E88 -M07T=HEF=TI.ZUFK#V*'>%4Q?X:V,HH2ZZJ?\CXLHN^><A0%>G]X1IE--@YSV -M1G[[M(R^M'F+C$BGY`/<`58(M`A`+0F=@SV]L4Q&`,S51U&_D,/*2A_41>OV -MW)I`4;E2>^#8^UL[QB5VKQI%,%?L<)Q'&S(WGGP<U%G34(E;;='Z';PMR[69 -M<LR[]*<-;AQH@7&?.(T4--%?RG\OG25^XM'\1JQ!+#Q^L&.V^3&)$]O@HFC- -M":T5*E5!'#:`86QR+U-17!MQ%./FN8W=/54<W:./G&\C+ZM#N3XO9%'0Y.WV -MC@,_V0(GQ`KL35!$E5D<N"97(H664;T5>:_%4:ZR;8R2A<%#D;S]4A,'ON7> -ML\T?P-)6PF;!?3H&C&=IH)N3]G6S)MM*0[2.PWM6UH.-P-:!AH)-9^5*6@S` -M31IBGSEFKKUNO:PB9;L8%>]H_XQ]%,0N'/.,$*:T^U+=!A;H3\/Y(,T#X)LA -M,/@[E"4%</*Y/ABHM1%W.I-C$-%;^U6WGXB_Y1>Q(4V#[F=_ZURVC'\&QF%H -M(=5/+O"XOX?^H^ZG[<^?9C*G:&FRE-E%TSE'AB6OCKD*8H48A!R(776+A(53 -MJ5=\`A%]NVJ-P0@J0?LB[]-*6Z"FEQ*OZ]1LSE\CU!%(D>7QE9=EUZ3F.M13 -MU)J@?DPY^U\>%$>N]83_`A"0LSQ6D26`9<J`%;UZ$*V8[F7Z93>@C"SKT.2" -M3R)98I\O?3I>_:+O-*[G+?6,-_K]9E[*:S0R>MT@_OI\^P>B9\SM=7$N<=.5 -M2_?I6+5):\M]Y%C0ZF.F0?2=CE]6'`#XO17,'QJ]&4(2K9`EQN\NEL7HJ+0L -MKE`V:AE\A3?,8THE:K>8Z0?_1/\T04H4>SW,Z0UAG0G?XYPAP9SC%T!N@WQA -MFG1&HL&(&MU&<!XF`0-Z6]O'KC@3\ZB]"8NV8YC5=_;GXZ@D0.<)T'!3K\9- -M^N3<+[-;M`#7RTKPUG0)[0QG,ULWKO>@*.C%PY<E6B-IR9[[;/%SGA%K)5YZ -M&.CL`-H^*MD,X/6-5#KHG)U6,C^1[HRFW.YG;[`&F#EWJMD!M*=*/+#$_E6@ -MLNN*G3&?&G>FR]U5E&:U&"5;9!&LB;K:KE1(;DZ7=IO*9LF#<D[SCQ.G^HR& -M@\JI"<"JT^?L"K&QG&U'^-/![A;F\Q&E<+--#K1:6D]W+@):(JYI&>:KPM5> -M8^Q#,\"('4UCN]*)B4"N"KU@^GO",CRFJU)>N_Y7,`2/T'=CR17?5@=0\6L- -M&,C3/N0_)3VH(6UDS[]=#X>^B!SBEL^/LI).,HW8VJC'X,$.^GFL]`YT]QO5 -MQK1XEU:X\?:2K!WBI[*SCVJ63I>8E'>Y61'LX.(XL1LV7N$9-0*U:EN>U'VS -M6=Q2.A$501UPZC0"C?+UCZ\+G-W!:8IQ>_UIH++QJ_7^@P(6G((+E["ND>=5 -M%3XL1,YV/@NYU@2?!7%*5.J1G;*&)8U=#$FRZE[PLF(M-*ONHD&KTFSD1(6R -MB4BA*EF#*1:\;J>X"WU5ONB?$"F^;9L#+YTOOU1X4W?L-*/K1A6<;F\<0.O\ -M_6,JM_]`.7I(B`#D>4?'ZZ_;RCBG=8#6C)K(H35E(LA/>AV)ZRN/UC;2PP], -MT4J&IMD1(+Z)N!).^M8E*LF\(<P:R[?'AF([O#IU<D\/3H'>.#)"A\27*^!E -M8H64E)-'C0ML7X3]=LG49YZ7A]5ZG5/M&!R&ZD4&_\P,%'?QF"FV:&0@[<,= -M/0DNJJ3I\=&5@4=N'DXA-:R:O#-D./1<^U[60PE-B5ZCH,27B@P6TC^(3BEI -M6_.9)"_(HT.!LO]4#SUY/,]G&_#ST;+6U\>3UU;=):^+8)YKOA6=AQR5$?NE -ME[[&'2E\V)^Y>?826Q>,'`%N<BK:5L+EVN5,/:M-Y;*>[<V#F+T+")Y402HZ -MK2Q"+WFX=NS0@3*P>&NFL3TW*YUWFP`X(T0A5G9;9-8SY/6-)/P)/^(24V:2 -M>[:#4>L4KD?Y^D9[5/X`N7Z;+6<>$T(WPTAV?^Y'B&E,OP_;VF?HL9/,2#GG -M*49!*I\&214-E]M(+/O6,NMJCV=FHG+=7%4IC;6-N8DJ!J<!\WYVW<-@5B-A -M)Y/@9YLM&RU^AGHCM]8\9+N=@U26<=D5#WW^0GY4R%GW/FZ5H`F_B9*Y$<>H -MC_ZST/4_5/W=Y5I9/$2>'K&*.T_L=H8`BD^TQY)%7D%X(B658VWEB]G\\L+, -MOPKRIJ@69H3H\HT-+O]@MRQ<!!1=)@6-\^\)/1$?5]:FK*S&IW=&TLP/:0I4 -M6<\<ZD@6N6.2@.DJ4"$?K:02X?>Q-H)5#]5!??BM)^>6@<A;8`WF@('G.J2_ -M$5&]7=$J'8ABE9%K8@3K<B#^\.II>_!8^V\K$Z,.]*WEX95?H8P'_).M?G%< -MW`M0&O2,@@"F@<\P%(`5M8+.5@P+5#ANRPV^/[J_RE`2@?<7NT5]H8A9>&>' -M1DV9]C*!^HJ;5L[]DI:U_4<54Y:S;_K#/K-JU"-5)O;L+OCUH1L+R\3R-UAD -ML%A7A0-P#_E&,M3HE;%G^`]B(K&YL7SX5PNOZB,7?_20KZS`1']4)HWZ/Y(^ -M<JAXMLM"TPR]ABPF<8._G<3JY$KB=:%D82?#!MA')ZEW&W.I0$%966_WJ#AT -MS7AY\DH"QW7>MOV>!A_E\OMI&;KN7S[IN2!;1YI\/MO%;LO"LSLAX0\>6]NN -M]-@VU_7JE:5*C`9HKPF*WQN)U$;V]-MPT(^A>7U$#K=KFEFIF^/3?NM+8.7+ -MUB`K[?!+WN+'Z2#NM#KG0M$JZ.\N`5JE(HH/Y#?EA(=N]T3E]#!6X>SXB-N8 -MA-ZW0`1JX&I@^4,@--7?B?]X'Z_.S<.RD&05G,OT-:$!23UOV).&)TOE$;OB -ML^*9_06SMQV$74?B885J9B64W]'E,OH%8&CAO]U1BVBD]GML`:5E.GA`'#:M -MM'LQSQ6E'TBWQ)F'^<<*C3[:MLY>?RY:_&Z_T9!GF[W7\O]66<Z):+DL7NB0 -M1,I4&@4C<`CQ0-!J.O#R,]]FTJ04C'=3[_%0QA=MEN''I3_5XURC;J"YL\=# -ML0]Y1=2,JTFKX*MW>E0N+&_4(9WJM'+X(/D=&Q;D&_J3*"(G(4P]Y0=;;+E6 -M'VG[$>JF[?Q]#>"8>YY1V=I'EM'E+9B""^S=W6/IC`SN@<,K/'1C2_XS3R:" -M1!M#)H:N8'<:S?2<QS.5K2`#);X,7[;6FS.NT9B-?]_Z+AC3#`'Q6N?W70B4 -M)$;7ETBDD%&L/V$/\*^2OII3&B<NC:J<QV(W\0-;W@%Z(6_QPC2^0S8%L[,M -M4?"6ZV&N:.X#/TO!4GI[2&83;=5;&-`R;SN_Z7>)Y^=@U11KE3QXRX3AF^A@ -M3"!XR39%EWZC-%!JVGB&6)616+O\D\MQ%3;MP(M%Z3&)A#^+!#N#Y4)F0"8( -M@`$E;5E#40_#3R?//9[%;_P#Z%3OQ0Y(#WF19W:G.HHTO^WUDG?EL-/DG*LK -M<,`.6Z=!G7/M.^".)36YKC-;/@6&Z."&W81><98I%4KI;`+TN##V%`!D_5_& -M-,+M-2'?5`%%P^LY=VFN)-LOKQJMZ,"HCB$9IGX`BO\GO;DQ[(/W\8HR,58G -M-WJI."B,32"0FE1-+W@4+=V>LIJ6`SX<(R-JU?K(46#B<XM:6O6[Y&IQ^;?= -M9&IE>0Y0IH14:Z9;MOH/3"JB6"N_GD%H/%L[,@JYU#];5*MK@*HLJCQN5V*@ -M?NG-JC%($L^?#@;4M"T`3&4`@\B#859J]>O$_:H0<HPS4K<SA5:<?Z7Y-.W* -MCBFRFJ2N9-4*(:*=J.<H!G?#S6I.,S^F`,9>(C_(DK,YF(Y54$WFURJ%+'UR -M(#=#F.223EU"V.Y_,5OS[E&V?$WF>^&O><*XE#D0_-JS4<:B*%\!7#;CLDNY -M=0Y$A0O$2*9;Z]^"QH.>-3.FN=6@F\RYI^VH&DU?'&N(#<Y%NVH[\J0X&WSQ -MN+SN5Q56<ITS4Z;Y-=P"W1*K7P)-.ZDD<-]ES^8RC/,2NFABQ;/=$#(<6G;: -M43GE\?@QDWE`:M3=M]7RJ1,.)JV/^+O7FYW&+[80,.:A=',#PO-,YSZ#[)"$ -M`Q)7JZ5NGIPQ?>*&*V"R'):W'9"NU_U)Z7!O'.3".>%%WJN,L74>SQM(4I.@ -M_[%<,)^.S;_K,A8?K*Q$8ZH`F>E!HTZS$9KY-7$VQ!GR/Y.:_:"@606+YWK% -MIU@/XTT\DPF<_Y"(@OIS@34,G/&O78N]@T\IKT1N&#XT03PEID(1@3J<3U64 -MOY5$983R?>#T]Z!=X$=*Y\R:Z*_G;1Z%!VLVH[5E)DS3;2H31#NGZ;!",,[G -M:L5.^$K5N%[=B7;`53)T$G/H_&MSJ=(6#\H6;72!RM+MM1_`A%L@446Z6G3# -M(0D6/+I>K"]B,AH21IX`2-"W6=GY1(M"%U>F_AMJW:H!#TN]F69"LPL#SK-7 -M<^E6@SN3#(GF3TK6G-9.;N@M^91*Y/0<KQ43-KR$U;.LS9S.5H2@46U,ELDL -M-O[B!,AJBV-!*M3H4+?6?->""N!.Y>S?P@S*"KE0O9RUU?NI>W2ZOVHP0W8B -MWZJGE6`^,F6N?VHO@*HJV[:]ZW6Z)27Y3H3>CJ0I]4HF)JJAI<;2<#J$0NPQ -MM7`MP@9OJBS)^)+#NF7!B(`LS?SG)Z<*/T,++KGM_K4YORU/UW^P2&_U8C(N -MH!)3T'F"`L9RF%WLF3",NOJHEJ:U"GVA(J4N!^^%*PMA)'"<?JDK94._4@,F -MN.>\5PY76<C3^48<18ONZ#;+1*$-MHJ\8(RDH^S.A7>#,5)*[IQG\>:)L?F/ -MM9>^5M+M<SL'=[8(%.\B\?NH=`GA-$PN31F8Q/BM4]$_B6!$3UA=@TE0>6A` -M9:-_L9?7!9N(IME!&^V;;(2Y@A#*Q[,=_,7M=6Q]9MM\!^;D$I*<JIOP5G>% -MJCZ,B,*[?"1`@,_7BS&I#KY2<T=`F,'3T:]K6K+I*/IJO!@SSL14F-XN6LJ% -M%GD@_5>@%LAH$;O;(R-9'7W^JZ4M#L@)F@>).2(L2S0'9&PU/">+MAOGHHH: -MZM-B0`5<AW*:^$*>_+?R_OH3R=9*%A<F@F)[6,E&6VH>\JV:?G;_?Z,I;(`= -M!S5RN.>9_!("#*AY:-]VIE:J=/5P^$I4D1?\OSSQ"MR;.*-O2N%>4.V?'<2B -MB8[4M[L?]#:.+H'T0%<LB*3M%.-`RMRW#]GM;-ZZ1@\6'QZX4`^FO!8`<0!N -M=S8\`UOB7":]F2&%!DZ+S_J.<L-F7$(KZX=(6'HIL_QCC+O$0;>!$JO-:E.\ -M,G'4W8V+.<[/:UR<26*Q9C,-^*1U0C[04/:=SD:)K^HYY8P@9&'$-N0O?`"% -MB\=#3!(IJ]G)UMC!C5U;K`Y3]P<9VH/9!+HT><[O;@'XG,_TV!(.:OY\&>/# -M,+0F!P`M+V'0Z<VA0_DJ!&?2-DJ+#C*U#W<.>\H_;M*VUZHT39:EL.[63D.: -M=_=$1N#CBCTA,4]4]9D4*NV4N'*G#BM&V8OD^_Q>::-YV(I9^WW9/@PMQ$K' -M27L_S,T,O>P`<\9+.-D4#71YQ@E5XZ-80J3>UU5H!-K5K1&-W#]^T&.'>E(D -MC7K6J\UU&*#'IBR0&-/G/8+VHPHG$R:`972%6^X4B_4N7'B;:F(?JP_!<X%M -MO=P<U\<+N-V)+$90\WJ;486YF.M8'R;?C2=E)*'FSW>/!\@YI=-JN8%X7UW* -ML/07="&0N+B7L2[C"W#&#GFF'+(GZG2;T74?3$%V@_`?M/Z+6=^4LA<*/5S? -M1^62WTG*O3\YZ-C^F3]*6\],QR4D/Q1T1X(>!Y4#-"]!A%?#_*8;?G`18`_( -ME@RNFJ):@N^-50R^C_5%&+$>5VHZ@9U?-)G$"%065!J::(R7[;JI3R_PD*MX -MF64>JLJU=4OETX?[]M$-=&;RS)W;O^SB.,*%4@Y$!U3DNOLO[`K%Q/I/!]GK -M8X5P20=_#QZR;^8B!0Q(Q/$=#>KW/KY"G!7C0<Y>"`:56N-2'CV=6KM&[^D0 -MHBS8^`>IW:N_N.E\!@Z'.R73/P;]SKF*HNRF&<WT_L.=N@P=9%AYHHK;"_R7 -MHE`9MW]B:>%/X8I4$2)9+W'HBWKYW_QGI`8``I\S+AJOBIBSGK/2R^:;?GLF -M\3B"G-$-@VKGU%34@WQ*<(F`"Y!J@%`V6=%8U'A4^@;^OT/#(D@[6$4@US+6 -MML[%&1A>_`W\_U@E1?D!PB_N2Z3I1H:%3&OB]NRKTZI)%V+LM.[-_E[!L9TS -M/-DQL6D]]UW3QC=\/'D4A=/6:&%?F7YM'2UKN]9">C/ZT'D1?M`:1O'D_[1W -MWW=N6:\2SA7HQ*X@=)FNBM^^+X=Q6U1&8-ZN&C]>U66#O7FW@*%)NN4:5.H* -M8&"5?&-D1\=HY*>G?NP:=6]1X+\`]=+!&`R#FL5*V=78\Z`U)DS=G\=E$Z@- -M/;Y36O5M+@V@>I53FPK\+J)@;AY_EL\,=;.:)&"$-NXGNP7(3*=RBP>S%5"L -MOVBT`OP''VW?Y78S($S<AD\VQBK2&V7V1+5K)^Q';6E]N@?>,)_$>;^S`,O` -MEX67DL`;*L^-BJ]0&I;^5C9M!'/6E26=P*U.S\-!W+8:0V7/>%2'FCOYK/8K -M[`:E6%=S`"C5[]Y$1>=>_@_7[)%K7XSN<N^2&%:=<FH+*4J1A1*D'"*+SB4/ -M[JM/5-CKTDUG\YRKI$32VP3++\#GC3-&G>_SK02&TXEBX4TBR89!H5Y0/"1_ -MI53.!0O8/DN:A(]:%3B]"*`%Z-6+^H4&\A_'AB?P+4TR\8J=O-1&4?_2`D!+ -MB*+M7(7FYIJ<B&*W4DSR]+>^D)82)3$((W46%>;G_:JXXI;;O<\WS0NQ+7P: -MMM]8>W%_*14`KW,H8_@Q;\[9<LQ1R%W"8CATS],R&^.B[:"8M=;W>/HEF7J6 -M/][(8K-,70M0(678W==RCYI(""XFN-KYWYTD@.U>K>)B(O)&;J08#">"F#;, -M9?4R'6"V$M:QQ[1XV]+B*2KD9WGK@3^S]E"'0^7>;X$D7R&^4[Q[AA(QN._, -MZ.&R^R97WGQD14X!0&=*&\+FQ35W%B9&Q:\X)E3(*\$GH5?!.=,Q.@1E$W7^ -M4>`)=[[&,_\8AAC%CI[T0&ML[@7EI3(Q+";/X]7#[S&MQ/8X/]HAB$Y0?@A( -M;*#2S4Q+P"])+V%NADDYU[#P!0I"B-,Y^1DM[*6K'^9T+%4H9-1XL4G$0C`) -M9VA/TR$#C=""N9W17X6V4@L8;6D>1@\6-Y+@M_6M*J6<<MR4`5S9X_Z(1/`6 -MHV^U)_$!8,L!1?!P/L9N30`37M0]:^%-=RDQ!`!M!W%""RG26N>7"F:JZ@KR -M-<G&K/(=:W>(>UQA#(3V]+[:/4ZS&?P+:"=M#S1D4:.G_!1<ITP3EZ"RT;O4 -M<M,&O!/S1?@5/U-3P`G76&+JUXY)&,G>B@R*7#GW3.'I_+;B>*&5G&XC&IQ1 -M&K3(?^IHPO=W06S&$JR-,'!3'!?1O^%94(8[*5$7[:A?+_]&8R#]@!PH=E!S -MP&7!JDIJN:^S].5UTUBHTJ_=5"M95[)DX!G-9UR*%WK!BJ:"LMU<<47E)82> -MTOMJ.L1]-]]PDZ_`MKQ6A,,7V&3E-P/Y^%93=#VD!Z`JXQD#07%L%ZSN0+.X -MG>J'E-%4^'SVTQ7?[<U3D5A^<C;,E"\^/CH.ZR+T#R$H06:\RY$#FJ]VD)"5 -M`(;6!=]2LQ0U&=TW:=A"ZK[)#+"5F44;LU(O"*2='S%'S]"<[K7G8X4SI9QG -MK'!'_BB/9D/)Y1JFU$;C'%ZN.GBU4V8+IU;GOL3]U'5$8"0@X3>5'_M+"+LA -M<`_V@7M'_8$@([&4VF,K+@C_#@4Q.9,(7GK%S:B/2F/:JD?.=PK.<;A;N21= -M1XRO>.'`DP8;FT^LR_%%("X]7WT-_B*7ZFR==V4BS7A_%W$!*LZ/9>@)5\,F -M992CVBJW*LW?I@;P@[JJJN6OC17<J,TG3=Y+DVG#N"-J*56%5-L=9WK_1:'] -M;]UJ?1.87:S]V=XHH9ZYK=FL3#<2JW1=X&YQ;%66K`D?AQJ)?<RQLXHHS!@* -M\)5..NN:`UR,8;H/<\C[\<0D[Y:^X7@0K8Z5W'-MCHI^:(<CH#(BUL7^W_L8 -M,<6HE2QPXN&J.]0SPE_2#Y2IUH\X;I@)Y*((Q?5?=)".OBK9=>:G&;:).R"E -M8QQ[^&Q.8FHY5!A,0[J96"H+#I:SV3`??L)E"!?B]6F`4@.]?`%LST0@%(5. -M6"@LC%Q!P]R/V\E&+X:NW")56]R6IS+*W4V,.^D9[*#0*<47*AZ.\_F[@7E$ -MR/QSHDU?9AX6S2AA)H!+P<=4A)=_2:BO!"78"1I/?4U]](F/$T)VR\N]@7^: -M_0%87E3P^1KWD4US7[7_M7/[Z,EP3'A_E*\DMJ#`F*X5,M6!N@FL6:"2>&,X -M"<$QG;*UIA89VJ:9Q$5<(T5^5\7WMOT)A76CNMF_:2^RW'%'<S0(`CLEM'WL -M*/-:J/D1[F4FCS1I560K(78)GMYE`T4M#X68ELS1O\)46T^/=I,92@79G=^( -M/C2SDK3VSE%E<<J2"3O`"#Y@#W1T3^N"Q\3-=;SUB\R_URNEY01N=DCQ85(] -MW?EK[XN!"C)%J.,+6LL0LE58>5L-==AX.9>XK-AME:RYY">)+^N;0K(6L?@Y -M#889RL>W/F08&XX9KU51TRBPONQ+=4<EL/9+VQ;@8V?<>8RS!^[#%T5[`(;T -M/DG\8_Y$"2F-PM@1,KH.T6?*"?F]?**V!B05:%9$752Z*KH^U^(P7E\H3"F# -M!;@4:R;8N.Z"`MZP'GMDA7TG!00!VE*_`4P(G,8,@)*$79FDGOMB]4+01^>N -M;:NR7>:V[4JC8G#2W_>"7BEN#I@&+7<%+#H_AKN<X[?]`_6TGJ5;S0.9N=^U -M`R8U:JC:<C2VY`"7`\`]],:*/20LK#*<!(P9!^[`@JVJ(8"GS;W=',Z(850] -M,D$#MFE"O0HE?''>#Q1-$!MMARND-$/Q[3TV>;CGU"+Q(\JF&,/OV\W-M3-U -MO0.M0J26"HDO^99Z<I.^ZYTWI90M>:$9,:H/ODIP0P!GHU1K,AQ3WZW?R/RV -MKKK39U6)"%N.O@/I(#Z$(U"KMA4&&,FI(>_(69*5(]%[4O`O-U>_0^BMFP@F -M'"B)]*!/W"OHY#VV%XM\P-O);!42`(C`1`J3&VJ.76,(R[(WKJN7I5#H2)5& -M+V%_<OQ:P7MAQ:RAQ7_B7H?D&M#5+4&<3*'E63WR&5-\SZI?EL@U$:JR^%$I -M1XZD]BA.EYV79KV>5+J<Q$HEQ@@$MYY5P(;BL8(Z03JTBG$2W8VT<)M=B&G# -M1'#8[KWK+$2>'9\V"98!F&SB(*Q--<BTF#18%"[V5=`%]IZ62]43F/J33>(C -MF@0=]:CPAK"L*1??)M/FFE<=`ADQB7G/O?9M&]3`WV/N[[_9#HF,V$/5%!"% -M@YOCOL-T]3P(%,=>$%KQ=U?IJUPO@C7_:H&K$F![7$]7W_?/67F`]=9(YPZ/ -MHQZ#I&%S./9YV+:(0AW5F^UHTM1]A`@&[6K:.0D')85`PYJRW'BLD+/-JGC= -MP#1'+HVLG*U>17ZA:*:(N(BL@@'K>)OK"'Y=?PORYH(3)]^AB:20B"/I:E?Y -M-QLQFM$&MO#8D'SK:=BZ6DEL>05H47'LA=FA)\]>>FC_3O7GLVWO%`-F]9KZ -M"-DC$@K%;NJD41V8G'H/R*"E*MA!TK:?%0"P]I2"Y/&1G:"D8)D35&R+_$C_ -MNEF;F2_'/4^("B@^9"]FSR/7V>N\?7L<+$U!YQ@H>)Z`/**Y017;7W.;GJ^8 -M.R;L"XQ`OR=R(6;K]<:=5O*:::->-?R]\91(^^@R!>//`>"NP1-`^:5MMBJ, -M81LR54_WE_0Z$2F&OZ$-J9Y"Q-BG6I(G4%YJ8HTPIWD;,WF)/I;LM&8.I9ZD -M_XEG+U6!F<*LFID\)8QHM9B.N<P,-F)N,LR"JR&EF54Z>".8ZU7*KQAK+V&# -M78E\FL!,XJ-=U!N:VQ$Q$9!W;O=*6!-I0BB8ZW"T<K"ROD=Z[.<$*/]T9<_L -M`+#S`$K(JS;(PMF343>("FF5Q^D[WV[QIN/Q:VY!HK%)H?2:%L*M)Z^;%PU- -M;+VYML'(\H1"?M+;O[>B4J<'+FI"0+:"7XO%[;JO2G(?7WDE%&1UI.N[+[SQ -M%5=7<_C^_FVL`8VORR(;9)P>ZMX<F12DQ&:"*W-!#<+]B%7O<4M_D*L3R,\* -M<MB:[*2-G)]7XY06<N5[ITA1<:)@K%7;L2!B1=>"\N.2`F"F/]4)5VA$6`&K -M#<$B#!R-9'5>!P*W/7IYPX$>=JN>Y:=F2&NXY],EC&@OBB<.S\J$&OG?9.^L -MA@._I=1]>R>]4)"*$U]XJ9N>&[%/L-&`Z6'/1(3@?T+P*H<;Y.5@=S.MVO== -M<U*%%ZG:6QR[:!F,C<J6E+E8:7]]CX%G2GVLTB\8^#H%E79'-,I`X.4R[P]Y -M!)7\OL/IM_,'V'!<YX"6E@W?K#F7G46$U?=<I*7Z0IVA9,P$8W2`HS$:7G7^ -MIBPH$GF'\Z3E8GUT0F'MJJ.7E['^@'TH<U@8*X&C%CV7F:3?'2-&O+T0`/J= -M]N=9VSG.*Y$B1>%22,P-Q?.[QLP$I;1<K[>Y_#=Z(WD:%U*X2251'W%GT`6S -M+=JXW?$0QOMKZ&"[>1[X^-J)',K]-^.:5LX:2]H;Y.!7'#?>Z_&\Z-'AP^)G -MFHX3B$Q^`9:8DY.DZ:_PIQ:-X29\RB2KU*=N&J*%L/;T'>%_K+=BHN?DI-$Q -MIWNY@FM"_\Q3?X%K\$@)ML'X@&ZD-OTO<^O1,2YAO=)%US=%'J#\M'<N$U_X -M?81-?^D.['3%!O,L3>'MOQHX<QD$GX9TI^G$U890&(#<]QJ?DD`!U+[>JN=7 -MCJVK3J8I4=^MT:7/E$Z@GJ-F@`:]&;0[4VL]%M>"BN:)8`;B/@1,^*_8A#G= -MIZPUYIZCN/>`T3R*N:T0S'7##35&HY[7U/\BCA"[1/NIC<$5!5O>&RC/FYPS -MDTH5]74*KSMQ5,\RIY@SF_"W+^?5"D<C0:*)&3"@+->$G9+VI:@*$$E:#1TG -MYL1])T29I/?2\O5+I8G0!F==W,658@!`6J1GV\!"J4]HL$BHX-$A[A$/],/! -MEZ[IIFTB=$^X<A2'-/X^1>][C1_<TSX!#>E``"SX(8QJQ\^`BYK5+Z6W@A_* -MH&>].-?.F8@C"U3-/>12;2.<-%6N82G<4EEE?AP7YR#:?<KAR<9@SAG!XYY' -M!^[3ZF.6J2`9KE]R[-'TZUC'CL*+%`BDX_BR<F7?LS)5=#C]R\YPV":N"<*! -MT7>)Q8Y<2%*:<^6G4R[(/ZFB(:R"]LEGVO@+B$T=.6A8.ZM=IQ&.'K=9M?W" -M0FH*LDQ;!?U.DJ=(`(/N&?HX+`B[,[A]].&O1`Z!+F6W6P1)\V5A/*P3NR<- -M//AT_+"5F/+5H_A9QW!$MKW^4=#?F1=9*<ZAC^O^@I]P&7GRF`E]PG"J:U<< -MXL(=GRRC,H22&$GC^0G:0=VV<A?**HA<3Z#Y3M'@P^L>;[-\&^A,:Z`Z.N'\ -MPZ;1,"3%M!F&1:;'$EC&OK_4FR&^?PRP'>+4N;MM@"FJ-![5.9G,[82OY6GN -M(NC_I$2"+2:!FO?Y)R^!141&`_`Q"DU%45)^/O;6H'4M7(HR<SKDIOBA<9%, -M$^WK<GYAGJBO$YJ$B3=!%[4[#BHF9(PURVBA=.W*WR(Y]U&_ED!FLI7F6D4I -MVK\D:5M+&\@M&R9'\4=*T.OU`"BL[0N^[FTZE)'@EJ>C?7.67%;+Z)1,PI*1 -MLIGF\=T)`"33!_)(@4F-#\;.#0"H>F%J23$/$AG0JLF$1]G>8G#6E=AREW-\ -M)L?>`T`?(&B_<4T6&)?1"K^#FOM+:'/:!E+SV.*E.)X"64T+Z>5A"D;L@+]G -MDY%J;QH*!<*@>3%G=1)5^"-"QW8Q=FTJN)6/LBJ[>GH[!MO/O[1]0&QU(RIP -M3+T(XZMS2\7(!M?>]>!R8=K_`.V.9<"#6ZF68?'"7%2(6H>!/V1^.S#MCPA, -MLXTU8-\/:=;)APY?57'\7K(Y@\"EPB8RN2N&G&CS,_^I$4%$TX1FQV?\&XA> -M5M0TUA1,S>,UXHY!*'H950578Z;P*V(;-&+$35NI8U'1UF[HIU#.C#25=T(0 -M=$T,P_LVO-:[ZJ]A0Z38E\0.01>@$5B+0)(V,5NYO"F/EN4-F:9\8G#R[8<B -M&C),R-#WW76;4#1!:-C*3`K:Y+N<%Y#-?_NI^!]!0;2-UDED;S.WUGP>S&T< -M.(]EB,-L>O=;KB&.F:5EF>HG8A>IUO^+,,"JJ4EPSG9`PT[X;>)IM@7/O5ZU -MDYT_TB</$!@MX*.`E%T3*=P`A,805XL?,TG8A%,K_&XYH5*LQ;E3Q71XU<_[ -MPK4NE0U64I`X<W;F\EV>KS<JF?J<#L/^K`J%DNON8Y5!9=WOF,,9#>Y=XEJ; -M&H*7.\]V>*Y*++7"(5&`::.A6R)MC0$*WGW84POHLAFQ%1YP+81H'/^)BE?] -M19?*=][INL]*\^>%H"K\YT^5@1;IIC-F2&G6>9:#!2X&=D@Q30X(WJN&?RQK -M_0[":`GBF*N=EVQ\83Z%X>!;Z<#91BF1U!X>:Q1.`=NA-!B(M29QZ]%!1@$T -M7F0:JAEKD_Q@=PR&LWM/X?UB\CS<?>-WP,$0<.2X:^33S"67\[U[*!WG4Y5L -MCO/\)ZO4KRE9R'G3E$;O#,VR^>'CQWG/VI<18M-=#@2V`47[4^6'M97CM9-: -MCKD30!4VK<$I,$%[AQR8K7_,LA340I^&),.9PMTW![@7DQY1E`XN`DRV`)IX -M,)JO58-;A<^"JR&S@G*7NX43!5"0).1(3F'!?P+T?XFA:F9V03A_32^CJ3Y: -M30_O82&1@X:R?2U*)4!2ODQZ'NY);VF1OM@<E5W&-VMC?F1VX\K@-D"]\E:E -M$XEU:;#CQCZE,S0(O7>*9`R1Q]&FC\K?.!KNVY.%:O!U*FT5-$&"^?(E4])# -M[_7"]U?1?A.9+1BSX`?K*%'USHK?>/UF>C=6Y*9O<>'KR)9'BSJ75EY'\+.( -M9E<L2SZ2"Q>:D/J(>M#\M5(AQP09H=Z!3V<&NFPIV)VD*[UC?])F8!8;Z\-G -M2*!O:PRVSG-=1);R+;M`(MLIZY2)9A!>)$7(V7$MMU,0RI>9IO"LZ3PN[+ES -M[V:=]J=$>M\_+/.^PRYMOH0\^=4S=->`Q'BXQY3C#$"-A@:T7J%`';)*<&>( -M`=EID0O4&X9ZGV(@S'G`W[&C&-N7TH%6!T<[9ZT8/TY)^3QV&@G<'VGO""Z2 -MU/!=9V:TY!FJH;Y4ZUC@6W5`3O/X>-<S-5T(SR*]LXD`]9_U':<K"AU;YDHQ -M==1RS77_,J<;ZS'\-C=[/8'Y;),9.=HZ(^6HPJ!P=PK[8`D\J`A9"RHE\W&= -M,_8V4+FI`E#0T*?^?+-Q5]--UC,S(RJF9+/^@>2(77RC5O_B+,+W[-U))O3H -M.K.XUW@"MB5(KL5R=$-`8A"D"S#>;I5\)<,??D4]4YPG]],,IA<!%G]L.TPD -MD$4N1?;8:#MO%\9'_$8R^:;%QWXEP\Q$U\+#-YL0>%#IU*]C*>)CQV=^]\/& -M[J`EQS9'RIU@QX'HD!F6%;8I<[!Q/ES2&UR=)/L+>E_)NFI&T%JT_9G6\4Q# -M&_6MN!:7.-F-F_88$H</D*>R"$1:LQ@4?[`X?2TW0(JT]1]+3/)]\V`"@NWI -MN'PMM^)%`.Y84PX+\J][>U2`Y([$TG8G/WLS+E^O()2B<7:*3JE^5-]PC#4! -M5,W&T]<*<B=2MDE'Z*A)<FRJ,>L':*3T;-.0A!L4;#?U=&W"C`MCF`7BH!=% -M<[X/W4>:,^_Q>FB]Z91F)_3&(%71F6!\AI18M_4V<I7O?$PW4.(3-&UZU5R; -M<IE&A]R]<,W+YHAE2@5>:UX%,R\CTN[!?IUSI?5U:C0&WG_??-TR&K$?;3#F -MV#8Y0.@DDJ)E)+U!$5`K+3@XM-0\Q?3-&*CS%8K$8&"G`A=#((,'/7^O4W'W -M:<,DM]<(=5L08_-';B[?P7Q:FM&?W&A/-\=@WZ672`K:9/S?GZ,]'![]XX(O -M:9RD;H4ACDE'B5+Z1#JG[0_\.&LVO*;IDH^I^/7O&W0+IJN^9@4]JO'N[Y:] -M+4?)8*DDZ#S_T@2V76$+NZQIC(<AI1K"I^-\+J\"2U5[X=A!1H=<0,$+_&,E -M!W'8^^%X$B_2F56./K3T^(3&1)*2N6!-^5R+1@5L?S^A;^>#`$MTZ,6RNUL: -M::F3$2EQ`1\AKD5_"?J#ZTC-TVW?YF2_G*`<<>=15@MQ3B@F.F4`(>CUH5C+ -M?[UK[D_<5GU0]LJ6;RENWT0VQQ$OJ9$"<L;9D$R?E_I]AV'E6`RX%==%PF.F -M8LX&=HB)FN4P*2Y9"B'OA3V*[O23D\%_::-PW=TA?=T3[L4?&.XJ9N.T'"8^ -M[#F2V'0AN:.,0'Y0JERS/Q"%`0,LN]XBS_Z<J4R=*6TBJA!"$>]U_.5F&<3" -M2?A"24HRK>,`4OJ2"HH1(;D4:T*JQTEU_8.T8!68``_,;N?]Y0T:Z?:@6F'> -MZ6".?Z!S35Z`UD1I[>12_O@92ZFD3$D^Q@%@C%TJ"6'_)'Q=U+.PH7L$:8Z8 -MG'W\4959=XT+&>S!`\X.:6#'?3*I_RSMWGB[:0,N_G=[SA/&BBEU-H)0Q0]\ -M(H1P>2Y'8']D)6&`::Z,E,'%\UN5#_T`/#>1QYC_:&38(P);Q3)?H359=M"* -MQ+OG++/TN:HQJQNDR/Z&-TS1C-:A=:H%&+U?>IVIJ^*IFP<U;>SQ(QS637MJ -M'2,#"X5(-D`A!,+EZ2Y3K_GQ9$UZ1IR\G$<#G\!7^+8#J()%%J>Q,74Z]%S5 -MIB)KH83_PP?;YBK\UJK[5K]2>AJ2Y,XK"24N8*^2^8J&JG"4"Q#K!UVJALB4 -M:QC_[M'?VLQ@ZT6BQ:5SRZWU;K6PWIWADOVZ;NRI.FJ*G01,P`<(ME88(P-1 -M[9:/SJ\ZX>KT?KJ74I5ED*3%4:RHC93'()B_H+L?(%R"^YEC)K(;**S&AR)= -ME9=A$E%U^MH!^[]U0CX4R'''VA<9(1U1><;%1%Q.(][!#0EC8[B:J0<S@UW$ -M3\Y%S^_.*Q`)"DR-V,$@!W.?RV'-C\?$)H,GL2``+\:ZMRZ,9P9O.TLU]3NK -MPL=(]'.V>PY$>U_=S^#5+PVF>;ZY9O5(R([\23_YXT>/@LT?M>#0V6<6D:1\ -M%M:D:(%.+A,:J/>77':B.`708[S;==^M59B[9<PW]2[71^D@FM-LI7#U5/8L -MI"/K):+S%H&8I3(O?X2YPI84U!0.6-U\)YSXFD*K]M`MN+YB9:*R0&J2ZO;) -MDGVRG\4\7]''(JJ;5J</:.)2NL7V39A:F3FRX'E5QF$GELJ63P?\5$VKN$<R -M6/I7S.B87^6DV:'F.5)1P3AS$](^15NRLBYB>0$U+M/TJ'HOWR$L0BV%EI_A -M'<X8345&;MGC[)2.+D`,YIQTM6JKQ<*TP,>@+_E2.\F:S8=6&A[3(@?,#&V% -MG$%[5F]M$E)Y#CU\S%'U$^[\8O\W3$YF^/@W80MC#EL<"_Q55DCT9Z06>N4V -M?U]?Y655KE@TU@R"G04W8Q!/.$$\Y5>K8ZD-IZOSE3K'"M;@\6&W91S/YFF6 -MEOV=,PS?1B:DWS""H$_C(NP1KGUB:MM#2L3L.,(_JI\\<+FRRG2A\E&^TOW6 -MP:YX+KY//H7N*23OBK[/(-J&?4X+<?SEE@_4$O3<*9E':P3_&7X6U!L^Q^3( -M@ROTH*O[EZ2MJG7R3RO'$.W$^OSDE?,K1JX:ZYF-JPD\/2M*N+R^QXF'.Y@Q -M:O+'U]ZB6&'Y4MX`Z(>VQ*,*D[)'5).Y*IXP<^[%?"`>BQ+YXQ^/,LR@E(48 -M8)UI#\80`NHV96<[?3<PVX8_B!:_$KUE;"T`W67=Z)MN`ROR%.9%/,OIHC5# -M-A9YZ(V2M//_)WHOHC+8S*`*"REUBW1*$%(\BR3A@A>S&$^8ZIO$(4X9@)%; -M353VOW>%3#W[E)U$VLEL.0>M+'L^)UYS\,&;PIK71D@3K_7<L&C)XB6ZJ7;M -MP?5T5MD5YJ-5PG[><?5P8#F']RY1H_]6'Y@Z40ZUX$IAQR(5J@`3D?3>+\>@ -M^H)%N1AB6AL./F'JXDA+'Q=,\Y%BC"_JXZA<6='OF'TO?1#>DCWI=I/5AYK/ -M%P'!,Y+Z$<N&=!!^UO]G%$/S!S;9[Z5X1NRSC3/&*%[M:%L3J3D/=*$#V$F; -MBH17^4MP@M8-:]X?EXP0;S303F3#5ZOC@*%U8D3B-2/O*G36QS<8U\+IE*4C -MUQ'=S?>7CV!VDR\I\7_9>KG6@&XKM+0-<W,X8V;23)_^)T8ZTY_9)F.23Y#0 -M.&@-J,/BX]33CSS@5]+%_4]PUK\3"(UJ7F4#I8!AV20XF*9,1['GGE^U3M<5 -M)J[$R<^M=?OW^DNR-GN?;#C3B#0NP)^13\'-/&N2)9\K#+J]?2NJ;SIF-72M -MP+DSIR%2$_K#Z7NWIJ5\X5V._3YKE4$)ST=/%HB`AFMC#KE(JZK*;6EM%?`` -M&TC_0-I]GL!]M&L<LLY+H0;'%&&;M5J"C<#F+,2==1=:.5>E2><H>[%X:9K6 -MAX;W>JMLF[.BAT(EI,-_7:,)[Y81OD[1F/&<#BJ#,?O[?\Y%OS9$;4>0V;J( -MLKW5K6D=)H,J?O$XH@$?-79,A,30*!VJJY[*EH)0RE]M`-\]3:>\BK60.<)7 -MTJB]\8"8.]RC)5%@@^NA^)W`7VO*6_-',(!2&1.&?^N,#+]-W]$C+CZ;4!&* -M.BLUAT.22/EU*WYAAI_D">2V&%RSCF3*58355CKW<I'3SA>^DJ#HWK\T0NBQ -MRCY3%)>._^?JCOI<8ZCN,5C&^,E8AXS\8ST5:*=6,OOS?(QD/3,"?9!'.G/1 -MS3I`D([J`G"6AFM<0)T5<10Z(PLFO[4)"6A]\TZ^7,!_EA/'S*TT\.?'(^FA -M?K>VY#[(83*;!1LUBT>5U,+HJ3X,'C&,.,]RH6AE6/_^0]D\/-%F((SXM-V( -MJ0_F9[W=1@N/S#(/MDUV!?S^4@3K;W.JH#VD8\@I',889A2^#+:\/6KO$M^D -M%TAF,PZIO^&/E]9CN<-`FS]8CHRF]ASLZ`R8R*F!70'(`\)=/)B1T'?O[:4D -M;5>#\&^-0O4J">O@K+6?CMR43C(FLE3:J:2))"?!GZMRSG1EV$TFRHAT_/.Y -MZH-$@UP,GTZ3IFUIX?HYB_E[."Q(9_FQ<YRT/`SW,&5GD>G!#K@<9"`"1\FW -M$F0S+%;(?/G\*K;6_NK"(+8T&ZT!.*!V_">#/D54:5FDO:03SNU8X(*F6"#V -M].!.$!`ONSH:/W;6*?1NJH]P4O#J.E,1B;<G(F<BQ"8E%4:T4ONT!F&9SX3_ -M\81U@]VW2_$'ZEI"H&Q(RFK*AU-HN%8OH#;!AM!)9N":7H96L[D7P$_-F70; -M]H.85GE.L)KXG:(_/=_#[%H`B]B\V[!'VB;_34!26ML9]?==4_L9P[QQ+V"T -MW5+ZDB;D/_%K>IP9E.#M)Y7LRT"P&9HXTG7NP]^*+9<7;$!G&?$8I'ZU+N9. -MS=TAT:(>QLM?Y]CG&I!XG\3@,*OJ5)7*]HB/C_D>6YC&JE`,]<!"A3>.$"!? -M(3'\^XB![%:7LA,IV2E(ZJS6^0<JL%*LO_.@8;28P6=$T<,_!!]D3)&RDN-' -M\WH_O#&/3WP"=SMN!K\M@^+1>RQ2E"U/'5L3!O$/#\E,[0"<$Q@S:XVQ86OS -M%W2EPR):8C_J("&<R+2VU/63B=H!:_U2&2XU26W:)&.A,D@_7JC`=7L:4_3/ -M^)3@5VQB5;H*$Y0XO\H&!C4>O;&.?2E+9%"*N>7G%0LF!"!.T'7YYLN!F2@` -M*]RW_%Q`!3$"O9,\]4V5S=29/UH(R'LNKL\JK*ALRW8P,,X=[;OYSQP-W3G) -MLZPZ!3W+8#I/]G"\Q;E\(9"3JWR6@&"D;$,\"P[9$4H!=5'S5TPY[W;O&G;K -MX\"EA/NDLK$TJQ0A+LF-E8HK17^+PP\KWND[FP_S+T."\2;(._MLM<BVORTU -M;;JJ1Y;(]7\KSIP&Y)F>VYU@]^3II7.2^E*^;!\)F55N[$R2HD!A'$TA^($@ -MUGZM_,A:R"#(%/SI&:@3G6;B7CT)-XMX1\>VW@`9KN"2U+)JS54P;"WSMN^+ -ME'[H?1U_TUJ"F.<#B5OG5F)\B5,&2W'D"9>=*3CVF`*_SKGN&GG'\]+JR.L@ -M23\,,`DT(X(\*L]%3@H712.,N["*XEP`+#ZJ8(#6M-\)9!V&8<>DP"OFVFC8 -M[^&(XJ@7QS52Z>@IT08<PR73_*'C\EME'6?=OZ.%,NHFCYX"$H":V?5DNL#% -M#W7B&X7WLZ[:5:SJM[&::"YY*5/'I[3\CRUH*\X0"*]*7KD&F?4OVQVH.]=. -M234RBB",HN?CIEF$2*'=4#U.%N^(\Y:B,>MN>D+FWBJ]=+SGE^BW?:`=.8(M -MCAW;M:0XZ'-68O-OX8<.XXDO^5)31&^QX`P0=)<R6\X3!-T_ZTJ1727GL.FV -M83T!]RQ7LXN_X-$NE00H9?9",FP$?D=SDG(Q5`/R`J%I*#&_G0A$."+_Q"), -MUKIDZ1!0_)K['W9-_%A%>3.I(L0KMH#]A`.<:NAE2O#/]MGE=%0]K)5(-GVZ -M2XS3MJA$]7*DKF[,^`FQMR"5FF8*A?R5Z\XY&\LE67N5I5D"$2#!X,_U(41K -M[[>0!4HT#S!TX$8=$T&\'KCC%&+?B7^MMCIOL1F/E%DA:H!U,U?2L(]ZAO*W -M!67?:4_SEV#]D-5?`;^YH'K$#F))1@9K4\;&E`[5*OLO!'V'C?4%>0Y#P],R -MI,?Q3NT=KS2?O1N=`U[-"?!1.]6@LWYN0,&%9^4C/UK[A+`Y[4IYNR#W@%1% -MTY,?M,JCS%(N4OB_JI5SB"N??K7O3&UZ:*JS.,)E//4&SP:73<ZYT9=GHAA+ -M.\7]F^B*IPM+T\*M>%@L/;:"/'NI<L5ENI3G1M;,?H.Y"/I3&`-AZTE1J.I+ -M%K18$G9\UNCI<+<VO6&M;[U)1KS9%Z52/&!M'BQ_"QXOG+E)<T@J&F2H)7[X -M)W&9J`<2;%8S?FS1ADQ39'K/M#^Q5B.!Y#*`4X13>/.N!@DD"T*'Q#/$QJP. -M9V6Y3SB\.)--!>\!MGFZ?K(7``UB>8R>V1X1BWDR_S$^MY6QD!Y/HR12UMTI -M@U<O,1INS*7YW!7JD_!USQ)A-`RC#PZ?+@E64B5//.6OGE+'<'\7O+'6#D*E -MDC[:185^:J=QI`Z,0W8@_T<DNYP,!!V!VHG>G9'EWI.8\I].;>44]7C>A2AA -M3X5I;/V*NIC'DU.ITRQR?,*(TX$';TM,&?TIZTOD*^^,;2N1!'%"RV]*0R\C -M)+)/3<:]M2("H6*71A*8F2IZO\KX`*VG_AW`30IX:C%S+N!?M59)?GQ^*!</ -MMKGZF]!?C=;A4S(/#L!&XTCWA15SFL&T5>BC^Y(,ZIK+?J6IDQ3__<`2@,,, -MT1PL@_ENYV@[G(<%&IA1*N'[?`:")J.IKA,"!T<8[W_>H$G\]FPEE!-QS2<K -M/BR-(.*WNCI+80"#M=`V\M)/]TASVNH%N\/5&ZF:OWRA9,;+.-/S;<R4L7*> -M]?/U$]2X9<5+"4'O-O!\W46_HX9J%U$3VDX#8@/TJI-?1JG'.O>D#7?RPO\J -MUQ8*I+-C9LN^#9"8WF'\)>E^6H`]/Y>5?&3ZDQUDCR<5XIWE8\Z'I>W747Z> -M<)9K/(N/<G-?Q$YR?WU^[Z7/R.F)<YIZ1!E=E[&P[?ULQ+SGOV%[I+*T6Q/; -M[:&*$M:/L.P%V4B%+?1@"ZZ$`P5'N1?_[SB__0XFO1:Y+3P,[:[$_(DCILG, -M9*8W133/\@;U5RQR;'$P"<Q.U8K<\V6S!ON%X6W?C?/.KD7*)Z\-E"_0X5\> -MU,6U$K)%%%&`OCBMTYL+*JM%:88K9R8G`+275Q!!W<`]%-WNE7K@>?HM9)+E -MO#A/7!8X)P7,41W"+\V6<W6B`;VBQ.V$2,O6(4#;[T+OTN\S_/[J*D_Y["5O -M\519ZBKALKRC)Q-P'*DE,3(#+VR+L:6[,(E[M^=@;<#677J%%RK!QQ:<@6O( -M<T^Y&NYJ5%U-$Q%.?5(RB\F0_+$"QM=`^TZ+T30>*"7UHP79FT+`]XZG'6L\ -MW'3<*[8QRQUU_BL#$<3CBL[E(@]M")K3M['@V'_!"[?M(#I"3_,U%[)[(5,X -MCQ?4X)`0K&W+WU'$MNY\^BETX(,E$T_>>O@\3+N\@XWN,(<$U),8FGFQC`#" -M6NG&##NJ^WE7A0/#\O6!IL)._$8.82-4A"L"1;]/,"R[+2/#U(QK"G"=]*V3 -M+'P)*%W=K8WU>5J4I(JT<!T[CQ>B:I40XX5-:"'7!TZ5%P<B(I%RUQ((F$@^ -M(YU,GN7N0"6XBU%*\<DS'LV&%EWB<I'%[E`T9Z*&-]%%I^\-M/K+RR&;Q&I% -MR$Q3"V^EO%=?Z<H<<G@?SX4XF_D&VY=6AH0BD&PN?6NI!3OG^@'JXRUXL[-S -MZ1SY:([%]%5KRXPZ]9.<D/`N,K<?"+S87];PIG\S@4=J8&>'Y'X8X,@T@T'2 -M"'4/5X^"])."F)G:7BO0Y!RO(T,3D"_BLU6XC-446ED8O_'4JJDY,=@\.=9H -MS[66=;_YS;>3VS]4RY;)U_HEN/.MA"I.)2&B&ZN*1DCH3$IA40*?Q1\E]JE, -ME7E]&QLK^35FW)MZV"%2"V<EC<?I5MT2'6B+MHV9"!T75!$#4QZ==;QVUX=H -M;)<LGS@Y[B-7>"#L9HT$VN31HD##Z'@M*+^4$.]6FFXISR>DPJ60E#%Z7Y#1 -MR!@X9^E*",\B@BK+#8C;L[!/D%"\CC7*4[XF>:C*V^9:!ON`QG^_]Z7KPM^1 -M=3IZ#@CSN$1X%L?VK9`=0EV8<"9U]?D0TVT0BBDR+D(/KU!7J;EO0=LKCX-Y -M7EP<J:WRLJ]#"-6@%G`G#34@D-)&$`(:XQ4R\;"*+]V`V(Y2F.#LU3_OW,!5 -M,,Q88^VIZ*M^D3H,N05P"@RTF**#IF6NR*.#F,CK&G4``(0-`TY1*HQ`(!Q5 -MCD'8NGH0)7MF=:VKU7=>KY5KZ-G;OK\7+,\;E+,#(N^4PW)=[<@PK*V`9/KS -MH>O/]IUG[/6EM";_'MWGIIM_-`Z5@\6'<K/&,KG:#%S].05=DQJ<)4GIG)'2 -MW0B:@O<M=N0A94G:LX;J5=\HUN&9L1&=.N2:(JJOV#1%QMPX%:C7$P9\DUNH -MY;$[IU,.CRW$<UHY_GTUJB+JVW=X`K8U"_":*^,$S`6LYBOA^Z+7?DZ*KC`8 -M4')'#K;;0&#$S5%?Y>1]AS7CB;]9O^?.:&+]YL@0D@P"PI,15J`8F'H=]R:' -M5N,D%$=RQW630!I(OD7#7'!I7XI'U:*9L&1:JS>Q2OYV%\N@:.9JJ@87&<Z: -MPT_J>35+32$-HV>Y10AV&6M<:JTT5ZK-T?X_6.W$V1^#6.T'WAFZ<YLH>1=< -M3BUBW<2!8J:NNPA<.:C!=%M2,C=E34G^+]]T>X'WG/K!0)?:8+ZP.#%`?/>= -M)4#0DN-67PM2DW"D^,*N)O$KNC6W=.[U6LCGT-](</Q&IN&M'#1H*\#E9MO^ -MDXLF'KYA_6KN9[G&T9_:HKR6+_1-A@R,,?&%GG[9B;.HRQ@!L;5PH!-I4O%B -M(Z^0SXZ-QBGB!2#7QTMU.CL.'/V@_(WI.`K0M&.L],/:P!\RC@S14O0UVU&0 -MKJN6U7SREZ*!5I13:[4+ZHT,VZD:@!TGS7;[PVQBL!_-)C+<W5?MXV%H:C77 -M6MB5US5O&,`.2$TPWP8BM_(PVSX<^+&SKYU;0&[QAN8;ZZB*K0,_W#0ZJ%BS -MI%"O0,9I<&'1D_(8_^2_,KI,@S+(HDHAI4R$O>V<I@+)N,P%9W&[:ZRT&V-$ -M1(YJ9#P?L>WL1:D*]9J47F<**0LS3[F'1KXV!P8Z+^^1J09A,M7U4N)D9DCY -M>IM"V*NW-:VB(L#'?D0$?@J!EZ/MK+)KSK,!RS^<G8Z,$\9^ODA)QX)$PQ\X -M7O\5FAR=)(_Y/6H($"3K&K$7+B#VSM]N2<WXAC1"U[?YF6UW%T)A*EUB1R>G -M"IJK'?.[1XXR+*W\"W>EQ$-E=!PF/Y"6Q_SQ*8%BAM2F!P)JL)0"NG-DQ+/N -M)MZR1=$N3;"N>ISLJ,!Q&+%-#EBWV2EKF5U%SY$7B0OW?FGB)[.H4!=\T/HG -MGXP<N!>M8I2;:+@C]MFTN4NZ[;S"^NDMY!N@]%*GE7@QX,W0VO`$DGLVZ.'* -MOV_/*,[Q#$9A,2PXSEMIPW$.NM60I'`G'BS!.85#UPN+W[41[7-G+Z.%HV>, -M[.=#4XU*8F_0;USPCP?15-W`'#3IPE:$5J^%,EP<2I]^RW:4G!88VP'$D`PW -M&W;/M87]\`,'US%NWFA6N`%:_Z58J'MU.\HBJY^$6^C/188>S2ONO=BFIU'B -M1\76?<PZ1&K6JM4<4@"YEA?^#KI'2GEG9_/+"(,45B/XJ-(]J]PDR9U]:L?F -M/OFDR2J8,A?CX;L9;)D$4FNL\L/>@RF6)\,EF^R8ED982Y.=_XX>`B'&@"+@ -M[;*`/M\7\5`*-,M&1VP]?,F93;L+!?P]2.SV59&3OM&H`+FL14H'F6Q&T\<7 -M@).B)SSBDHYQY.BZ!<:I6+.9^*\+J/OV@UD[\0YU!5Z/B5;&(26([R6F6[N8 -M7P3LY;<%"5-"'*Y;PYTS-8V`SMIK!V2%`(85).TBG:C+&7V7B,TV80[Z@"J[ -M3PMCQNV!\D18A3\`L&3V%\1!_[59RGVY7YK4&8,;53YA_DQIG39*%!_EZK5[ -MZH4A/O'I"I7+;@XKH-3VE'%IPJN4X%I=]#9>_X4P_+WR5<O\(\D,ZC"WUGE# -M!]=T*B9)!KK#SX3G)XHQ[VE[G@];M"1<X/%\/Z&F&!`:O-^/9>/AT2DJ6.BU -MAN2TV0Z#A&1F8P$HU+QG]B8+8])+C;I9RB"[*O+<6EQVUMO>E%?W61MKGE"H -MTHH@[WJ%F3/+7O^_A2U,^*@Q[?4U0DLY:PNKK>K[H$E.%*2HFBC8+S'V#I=; -M>I%9CQ:9B]FX,FCQV'MPS4B?,7(15R-)&R)]VPWJ@>K]$QDWB2#X9#1])KW] -M>,4(DFJ(X2V<^>U$\[U5YQC@+GZ34WD&>;2FDL3JT;!PO.%62^T;?K=XT4<D -MA.].J,LJGN^KM2QV*LFR_GRYYTZ7!^OC)-;-`E&19\@'K)3<(]6K0G_\U=E1 -M(\7GD[XSN0H6P079`.SH1EK,IH]H*UI9'E1PUYCD(NW,!^^AE_JT]*XJQ4-> -M(3A69J?02H31=[["IH'UJ<GE]Z(*C@.Z_`XL`=-D.PO`+J4PXN[(S;>8C7!] -MXG$\CO?727&8[FEJO6BY5^K;-]3NI60;/6>&KY\HD;TS03MJFY/ZJ,HP'['G -M@JCY=,!_7&YU#"<C87!N29/J4A.3QM7/(,XP.AS)\LV7;'^?8-!.8+9M_E1H -MR?7.!9)JXX&X`%4\/]<_I;92Q>!-TGKE_[S*1&=CO0BK/0W`#]GY,H+F-:S; -M'=G-'`_'^Q>G+7PA@Z;XM:E_8[G<9NKCMY.3J\OE=L[584Q#GCT5O-_<55?8 -M&>IUCBH-$L;C$7RECH;UZY6$\="<QR$DYI=@Q'9TWBIQV"@TMWGC[`W>9O4. -M;ES%=S>&FL:V[">3Z9U0S%L6_Q4W7W$,*1G$TWX\YR%"=NU88*17MM-`EJ2= -M!S@C0&@Y]-SG(63"%W&4>A!=4*#B`Q.SG@WJO]IIE*'9BSA,EE*VO!+B?U&$ -MP10(30E5Y7>ZXX*M&5P_:.\#D"0R5#M95]>X\/LR-GHA0UF*+-^`+JF:&@HQ -M_'L6KUX1#$-F!:8NI]SE"3IZ_&H+<GV\V:9!RCI$"S]8\/OX!7K$ENA2.X41 -M")(NP6E=[Q6*4B(ZQUZ=BVEC#&X!K:Q$*OQI#"&7-`0S4=*X5%_5RX"5S#K` -MRJTOX!JK6M"CC5EIO=,_A=.HMLO2-6RM@ZBFH),SY=!<=8B4M-'4;T$4TW/> -M:GXO#-CNH&F@C5<BVE?;ZDN;T1PG[%\#I_,`+$IRSIL9WAWA1C+;`D?6!C`* -M,)'JZ(RY"=9L?Z%/'BWKLFS&AGFQ-=H'`8!-D_W2>$:?3Z+PO<9-LTJ/M57* -ME.^Z#?H-B9E9)3KP:.@):/ZRIC5T=L!KN/!X*)<%A*G^%';?FI4MP'3GRGIW -M+Y6/+S+!??P(6!39!?,Z.)J#II9GY'*J^G'XJ@!/J(O!C$%'?3,A@GX#$KW? -M,D=/2"W9&[3\%^C,EL'R[`AT(E3(OM@7E"]"5";J9,R>JECFI]8CIU<EW_T" -M,JZ7RW6YR0,V+BV&HS87-F>`H_`O3'31&W$S4W9-/U,J^!33Z4WU,:_Y3QUK -MT)%A%"DQ)H[<],??J9O-_I*?8MJUV&*)PY;?5="%AMJUU>.+Z@!>)ZM3N59H -MNH7-;7X(!D4(+K\:/2^`V).DZ]NV?#[:*5)[/G2Z;40\#?%^^Q%]?(&4U,J% -M4/KW1):YRSQO2H!HRU1YOW+CDCYPD/8R:]B2@)SSUY__OGGN4;D#GX6*UM.N -MAC*MXK3AW?@_60D;43.&.AC`*/`F5U`8"0"T=9B@5VL1SH-S5CY[LNRT5]L; -M'+K;:-#'FHQJOK30^>#<&K'DH!]WM\9;DF!E]C2JW)-8(17@2(7KAFQ8D<V7 -MHFP[@`C?]]9V^^N8!QP;ZL126N"^Y.K5"B6@Y9R=9+FSU6@/P,W]FP`%><%> -M%52.#9?JQ\9*A`OU(=:SVRI'S.S)40RPS;UDEO\4F6QX[B>T7X!RC[?Y[:%Z -MU3Q+"XM.9N@3`_$A9^/FPO+$3P,;XW<H"MG3RTLIX8M?`E_PJ4=!TZ:/ZU62 -MUAQ^Y,"R[J7CG'^;NCP!8E)@^I:S-/FZLY[27G<KSO7KWF;_K+4Y)WL/Y2C1 -M,#%4ISJC!3#&;.U+V=]N[JQ-(H,[R^VZ0*S.*L]FNF=`M[T3(BP'O$`D:ID, -MCPX7Y9RACGGO`PYQE\XNKG?4!IT_&7(14\,#.%FZ!F#>$8?_(-RX08.4;AP> -M(KXY+I%AQO_0<>A;-)WET]E7&8,:?2Z+J2[$0+SD\IO_F<=5&K%5D./A&-5S -M#U5*:KNK?G`ZD\>(+=ZJWPT>&QHOZ`X>2A^7)ZE4?XLMF!EW`WZDM.T2OZ8D -M[B($2EPY<)WP[X@BWYU`^C(JH/,%:A*_,<T(A#L\ACB7\U&OW:S%TUV!^X]O -MP6J7_Q/CKL>L1*-C(=KDL9'2X]?APTQ7A1:&,'O6*4"#2;WK;YMX+41O,X+# -M&%TW<T_.F:SC8UV>DN]GL4N%6ZXCNA$H79Q(86LF]QZC4[!\/>TI@VKAGPT4 -MGTNXN!SM%]69(@-$I>**I%&^+O3F<5C9O7=I:QV5<P>>X.V0#CCQ9RJSVZ@% -M2H>ZJR3?`CHEA"A?=/]#-1[19+C]MT*?^&X*EP#3;LPA.3OIL'=:_JB.MFI@ -M;XKC]W>AC^$=C\(SL!]TG$NKX3K6JU$;@:]L25^=O,HMAPO$DMC@\6V)/X^F -M=[R?YV`NZV!:/^$:ZYBGEL!]C7S+>.(S+')K35+;?<2V)SJN"96*;I]IZ8A) -M8M/4U3'HSY#]SH>#Q31<WUQ-O_N<!UX"O/%5HG='!W(G[7]>4-@(LZI.,T/; -M,].V32O]A5_MO./]Z5'Q`AZ<==K[,P[)N?JD^-R<A]_4@><DPI`$X>)G.D\D -M'K;CI9"*Y!`S2.Q$?N)95;`\+/?HNXBI2C8H1P@7]N>='&KH)2Y'BH_F>E$: -M7U5Y4`%6W-\.:<O'AZZ^!7UN!3DOT+KZB_0W-"L"+$4ASO@76FCR`U.!LW<G -MX+T='K^ZG*+[IK$MQ50:/KCJT+_`\H[>SE$3[]0JM)$GF=',!EHS<+/N!PP^ -MG=[&S3]_DT`B`OO$,=Z3_@'W>0<.;),\<2<IK/T:KT<R]AF'Y?E^WF>D^14% -MT<@]DZ\0JT;%="#86$S.9%GNRZ&<E`]PVA=M=GN/:)_B7>,#`J8#?,*,:X&I -MO"[])6*#S-S9T;?)(9V:32Z!!`*UX#B1P&MGIYD!1MX*H92BKUVY&.>OY=O& -MK:@BA36$H,!Q>RJ$/<2-'_,%*/-L\:<R\WA^\=;;Z%U'T7UZ))_;T$AG[H#F -M*.:D[CTY4OXP<4;8UO(3L*W&Q">&4]EU,VI;6Y<"U%)_V3YXU[F*QBU.'P1& -M..HF/*L`QX8L@6(V,S-5D<W'P>KZDN1KW[:D7L,MUE*[1:QOD-=;\-[9^%R5 -M$LOQ?#/8R969?BH)@"*8<QC+".%5R6B1;=#P6'1G=2;[RR_]4!L^N<;FEWH4 -M@<JZRF]W[.<@U+(A+>*^9C-C]].B=BALQR8[XF(9GX,H&I\_0[++0!W*53(# -M]S6QE[T)HBFE?2)!!QC)#V\I<4.&+CS[<Y4R)4N*-#QV--\?\!2446`%JHH) -MSE:KG":P:+FK3*TRWW+)?5*PM](QL7Q7QE^_U]<,UTL3DN*Y.*Q/GV![6#*V -M\8"R5:-B,]8(R#F=OV$TLP`',D+7%W]V]^P'6?^XR1IPOHA\A$KLM&HI>:BQ -M#7"S/R[1MWESGQE`&*!\6>,K"2AU;)?.M=,;>G_VXT1FE#:#>3/;C[<`!*"] -MO!\$N'53DY6<L`\)YZ3<4*72H@''J*OA+--*\2C@<.+\9HF0`M03*'YY"E>] -MX%%`<.<E38#Z/$<JO5PZMM/O@=V!O<;S=(0`S[6"F5-PW4SD'?Q+'UD-/O/= -M_]E?CI2*APP[@5&(1GHB^8%XI+SY:*D-A@8ZT'DMPJ@ZJ+N\VK1F7<6Y7+,V -M=DBLW\U=<E6)0G?`GSR<&?:]@W7-]73-328::,YMU`=,<?;XJ]\S<Y2\-M)Q -MG>J\"A?'M4KVZED\$!&Z:8,C(_*"^]*:39%)Q77E2I2V$E>PR!$7^%A]=&]S -M*=[Y>O>4@.-B\&P]W45?1*L4?KM$8"=@^.1PT:WM<$!"3,&]K=XE:?F;"5U) -M)V<+?:LR^'RU7FPD%0OQ:O(;`WM5>?"7;FRM>%9"I=I!H"X?>PVBXV4[TCJO -M$'^PG+6C-I,)+7NZ6J%.D8]BI&NI@J2GMM2L5='^_I-H"VF/G%2(67^[C9]` -M$`.#%5",:\X+`H*?#?O$%VZV=\_%B&(J!H6^;E41_K6VFM32]C&7I-Z%FV?: -M\^!O.\L.]`$9\?.%XZRY);F[Q>D9#;5F1.:^:&D']K4>@\V&WN^L$%S?&#^C -M[(%#7HJ.5NOTU6@18AW"U-Q*.M$\$<N!,#NK`39)F!OSJHZSK[Y/]BZ@1T$Y -MVOH`H%^6>))X2-*9S9/.&I`ZC_L![O70LMKV*WX`N^*GJ]G9<S\L0@S5H1F7 -M1?97.?$#E1!Z)6PA5NZZ[*U_=33TM$0]I)L8FQE]68@6'T=[+$KC6:NRI\>E -MD3B?M:L>T+XAD0B5R3A<\SXMI7HGG6N;+>[O,28F7]&;2@&T"E7DUV^@:*K[ -M&T2/]C>)F"O;RR=HQ>OZ/(4*\]+&.A#.9^4$)56_&C@.-YC7%NJ#U.FW#<,9 -MU49+NB(Q,E%C`"T0D)^[+4/6F3C`&H7X0"8N$=Z!,%?)<81$58!Z@G\I&62F -MW;2@NDNR7^ZZ\LPE1WR3*,7#E&BD1.^`/+M5<@$B*,&N'S*'R1'-A#,[A=%M -M5:>F__E60*WP)PR>9M,>W7GH2O`G@K2.U8'3!=DUUFYLH8^0C9QKI0TXZ^F_ -M1U-S^K_70\A04UZVF$4,PF0*DAI=9SJA.'ZS/ZAY&N0W4/ND#@&D<TO(;7.@ -M#::%(@]`,@N_\P,$D76LZP,))D2QT)3,Q17IO]$43<B-A>V??YV0E>ZV%'8W -M-6[/G(0W9L`&;29!]?]#&,H4GGE\/TC@D1_/2`=/P(Y!7!M#B$X\.!XD$9`= -MB:3\>?E*;-4)AWH+R):O]%`2(A/4M@M_9XF#%^D;B-%=J3G".GV4R:A8GU76 -MSW2I^AY]<)"9I6'KF0K>M]0^!G;+FZS1[SQSA\>JRWG.8TUG]X\-P/^-[02] -MBVCC&WE-&?ICHDO%D]T@^U)@,/(H^K$P,M._8A$0=0J*'H9UT3.Z'D+`A6%W -M,KSZ,V;=,#F#CY*EY=]R_Q,EF\S\N4NS6<6'DNBPM^DTZPF+L1S^=X`.' -MFHYR%Z[A^6].J2;PC,^?S721*XCU60+?WOX-\E#7>WK%Z\MS$ABG5=SFFF7K -M/?[WYDLAR8*XMH<D8_ND-S:>;LX7SN]'TD68]B)O7>;/-#KR<"=HA)YV/:<O -M-L'+;D\#YWR;R!U!Y5-;M;&8^3ED?R)X(C)>V\:SJ+8S_A#8:V,?\9^W_G/` -M5*Y-=52U2"*@4TR5TP`;#%2Z3M"F$B-M`H+V_1!WV%O9#41-C'>A+OQ)3;:N -M*4:;&"(!B.V1-(X<*0H6F<M..MS6O@]^UGY.K/'0^[$?$QMLL`-)?[MNZ(A1 -M."Y/DR!LF$%+(AW2D$[>_6BI.RF(E&*%ZE:([WC';/$OS.&$HME@9$!F@?@H -M`1MIA[GE+P.O<;KL,)#:J'JE9V1P^DU!_:MU9]QQPHOV//5\2T!K([5P@P(D -MJT@9N9HO\/-R-,@JU!K=<2H+<\DK8TI]O),FM\BX6Y3$E[6[<OSDKR4QZB\` -M%!6SDYVR5E#&U;=&;)'._NC.(J>;!)$A=PG/TH963H=\3O_JX[.B;XQ;QLUG -M&5,(JE2Q0/O,CP32()\L&IQTBPG.9:6W`*PL1Y@"_Z_E@[D74@#U"S\M]X\L -M'_PY4J2^=!8(RT;N'#\?RTJ%Q::4-0-*`^>`NR>Z562:+I"D7[+/6CQ>E.?2 -M#IW?:`"''D>%[G5;94G9,#77U=4(BTIG:*.U?Q`DC,=?QG1B/<7E6MQ/2,C; -M[08G?DJE%1RE$+S%>0R&*C]<\W7K^\IF[=M*X>!(S,3CW&]GXCIU?P,F+.*9 -M(*[F.C_@=\<Z>RZNPY?*2W?\[+X1RW_J$(8'?R_M[:+V=X>@3Q2LYP5(:/&[ -MV1*:E3M6N:.;V%I=#(78.1BCA/[P^.3_W=R:.1J$F"=2LT2]_U#6"YXM=D#L -M.U8')FGFK8::;)K$^_B8?YJ6+NVZU$NA'G4Z0QS\I:P(C0TR]<NU7-&O5SU! -M'3D(-RP-H8:@L?D4ENU>WRQ7TY8<;!T_5M\5!\"6NO(KB5LO&ES9VB-N`\,\ -M,(KUM\:0]@O03_&UWYO>>:6*VHI'S>TG,_GUMAB\:<&&!:)1X[):N[@9E)3^ -M%/Z1/T3K^6S0JT4'3PG1N>SE%>D)];*&!Q%S6(OEATA9&2&I_Z-G^`#\]%R? -M@,)SFL(`/QPH:<8K`R\6=6+/=B]7#NTJ,DP9_,IME!.KFR>U'AQVX-.BJ?P; -M.8Q([0<`/0@EU8CN(UM6?W[(:FH8,KU:/P2OPTJ&F2RJ!ZBYM@C@0]NIFA,` -M:6EH6W;1-Q1>@\F774_[O8W8Z$Z.*@1%CQ`/`_K'M^?7,W:!.F]D8"O)CGXS -MCM2WG!LL!)00MD=TF*/8CD;G?E@=3Y6/SHACY44=KF8+8TDKL3NWLMB$&Y,F -M>Z;O5`PMDC3V8</)*Y$S4/!16H@TPFP1T1D1K&Y5?5)O002CKQ#+`54NH.FV -M:=5<#6K2KG/5S['KEZ+(@7RGL7X(O[]^Y%>,X0)7PR`36'H6P7`;;&:,A%#J -M#+Z@2W27*?_YA<8S8E6JN,ZX1QOE&ZQ@"]2&2%F60919.5'AEA;8#Q&0[Q"T -M#-%:]35Z6HI01;C5#NS9J+HGK#>H9856L]^I<*(GRXO6.:P+'+93_-=BY'.; -ME`_VSUTEWF+UH:DTKPP#>^1MSAZ!F.K_B$].DOM<K%DUC*V`Z/Z#&"YHZ\9' -M#7H8A-=:\.?K(U"\HZM+8"`H+>8;BO0#L^&*9>]VM(@T0O*7H@K;VNG'ZK%? -MD?W9.76U$F1`F`^M'S>1;#C\N;E\;"B8W;%2<!1#=F`R!XGXBZ#,,,:IG)!] -MFV(9PC!MTQ7N/0-)^CMP'*FB%]]LZ_K$,P^CCHP)NR[$48]T-4PUT:_RC00> -MI+I42BZ`*([527G8/!C0FUF%NAIRZ+7.`L@%8&_>!3%0>/@@ICB<3(*[OHF& -MHO4A://B9J,@Z//U)"G?Q&-8[AM7PXL,JIK-,D;ER$(B89SR4?"LX_>M+1-I -M_($%_%*F<(FX1@!-H+.Q7"T<,=EE#")H?`2\:]R_9G@K(=5>Y%$JH.\)VJ;; -M.?<Q=&>Y>H$[=NP3PA!^G59[$8L%4L^G?6N1X8L*F<U;>`8#:Y;M43*TEHZ, -M4YWU2+K;Y9CC^[_I$]L0H35PR/8TJV#J:WECGQ>OHXIK_>",;A`S>OJT2X"7 -MKN)/32%73;ZVX>YC]XCX0A:7-GVO94V)0G#[61%3DB?`H1X=9CDS<6O:1,:& -M_X1R:V33`$69*&&GN#D8K5K`O%HA+!0%*)F:*/ZMD-F\$'C0GG?Y3JK;%+`6 -M>X0>)911F4YQ(^;Y32W)"%H01CF@^F)Y5876/QQT(N%U110PFB#\\'-)3E,Z -M?J(G*2:XS""995"]%W?HYN14\=,E/2@C\A/^9K'23P]"OQE*OO]ZY,_#_*0G -M"023)FAL=;D#!R0*MV#CNE3^+!A*K*X.>T_4ZW=(MVD22^#'@%Z31=:<3%/W -M:!,8C6WALTC0>\,`H@*X\'96,79A.0A1&CO;C&1.A@[YA%:QEO?3D^K1O\(< -MCGOZD.F^>%C\TI[@3F_?SQ[!>BA,"\7,IG/.LJ2_98!H:AQR,1AYH]>/9W^) -MW,M"S7O(3Z)B/\;W,NH]X!L/-:6/FDQRDRZT"(EI39XK^O0"H:(Z8ZKIA!TX -MY0BT:L(F*.?J=5(FWM.5+7.6F(>2$N.76+<>,/BZ<1ZD=?T^6N,V*2-_<C2J -M)H:#BM55F+HLU2&S7MM+9N3/MDGCOH:V#ES0'2I!_UC0R_3A?";NI[\I%R_8 -MS?7?'T6)=/TM.`N/11J<4*E:7>!0+6YH&ZMY_>^O;Q:;36\+5LG.!`>K]]8T -M8!Q2_(C902?[>G$$M672E5]0WA6[P&X8J1X^=35W?MKZ"2F%AEM."JA1Q,X= -M1/D0PL#G,<OK@4F5Z3S#_9^UHE6<J3P#EP$%'5`278QE&F**!Z^'(C*U"4HQ -M4&!!`'BM$:^7F\/A!:3W74:MA:ME-D\+FA7!]F_&Y/)Y="T;@\FZVS$BS.8: -M2U53L91\Z-S=3-`-F$C-U]5,2RXJ.0;5"Q'DKW%DX;AN35SJF4_Z#V:L.SZ- -M:#-2*'T9$&P@CF%]*M?C71HM9E[<27L01]8A:,HY$Z8AY[(=V<+P^:'&5*BS -MDX#2R$^(JBK7T8@029H:DJ6`,W-];%:G)W37*%P0.ZR66Q_KA#]J`5>X7#BG -M9*I!,"_"<PP[^85:]FZ.H9Q26)(Q]8'_*RV>*8HZ&H$'Y"0:U=[]'+L"<`Y" -M#+:@XO81E,5.Q12'K(6QA1Q-1Y_PCFLI!]<HWML-Z1Y03`^Q&3>%E3_`>E4[ -MLL\H*7_/=L^R22QK/*:T'$;Q";H./?'6*:W8FK2/VCW(=5N]E`%LD2LPC?XW -MR?]WOI@N4VU8P&?J?6=AK4V4UARN??A>89(`S#<$"#EI+].$KG0T`N!9)2I3 -MPWR\\/R72!ES7;]4^R7MW]\U-2V'!RX<P%'&F2,K'*KIQA?_5X]"O4^GE\!? -M[$+EDYE4+5YR@%%"*,3O\9CW.:%8;Y$56@$N,D?3J`2++?VTZ,&I+)2B<9#P -M+#^6\(:#Y,WR]<XB#,O.V6*@0?`Q&=2BA^^I3V;4P(W>;%D1HN*6"\;'M]C4 -MA,Q0%ZGC'88I0+3/M_KT7NR_+]"TX"&XLR/Q6>B'VMWDNS-L(P;3/X5'ORC8 -M(Y.8M=FJ/37<N6>)QW)3AJV-0=:&?C/]Y:RZ>'BX3L%R]]_%"[TG/4<+V-1/ -MLH^Y)$BXGFE`MPEKUSQO7N+*&>'42YAJ0LNY,$#%TD+?YZTIU/UY@A:_)4Q* -MM,3#L!2O:L?/+<`,2$0'1VZ]3T3&5:E@Z4-/X#F5>K]%>;Y(S3;G@=LV%)5P -MX]6F?9=:):W>)%-7>3[<NVV>H9X;T=LR\UH<RQQ'])W9U\6>GL-9K1P\+^C- -MG)C97J`D>M3WL\I^,6AT&RMZ18PYQM?0,2`GY8U^AP0@9?QJ1)?0</UJI7#= -M/K<J!/8U<C"30E`7>/XG-?R[^7\/!UBD?]EP.9(E6DN,=?U-A4`AIO\[/\GC -MB*<A6-(%QK&O:0]KVKZ4AKEW/.6DC(Z,T%"!4YH"GD!*QI]9U.MJ\T2[$.]^ -MN"Y#*E8%3ZK1!6*F,Q%%V;7A'<7,!FZC2VY<WS`$O0\]\5&^;]3XD-4<03G< -MOA4$Z"A%(2V:6/KRK`@C%&K/)')ZJ'\O;W.53L6KK,*B/)=ZVY/",VC(X:R[ -MPX(F$,W'L<_.*/6<S8X"7$1@&(,S5W"=CLC_.I%Z2.,\PW%RFZ+3N*;T&-A. -M2<>QR(C<LTSO!`&Z`\'G\D\SPX@;=C2%]CMOOWIV"$4J"OX*N4X!=U22?ZL( -MF0U`!.TCFG/A@P6!<&(+C\J9K!0;D9LDP;7V%VVP%V"2/7&ZP&8V'%2;/-E& -MZSSM\GVZ.4LA88GP!*;M+9&]*'87'&&"V=#USRT[&%36@.1NJO.OSG&0%=OQ -M?+!2-)-`]1EBI+4M]"NS;V1-_`!_6E`+OV5NPAQE%X!L[9`^7`/VXZ?%E;S@ -M;H2Z>EAF[_,OW._I<<^L9&6.GB:059O]CEA#LW490-D_()!YIIEN5IX-GP,D -M]F/@U416>X*/7DSL2"O<]1#XL9[2VO<!]_=NZXP\F@*13X:6Y(EP[K4PY$-; -MZUU8TS<!?I0BYRG"MM:+[4'E[-@\S$B1-4<$9/`QQMC=RFK(?;1IBATRRV1N -MA]Y*9!5E;A]_K+PNE!?F0-P]&#WDQ[&,65%MMMAVH9R!7!I;'(CS^VNZ[T$; -M9DV##XL:ROD-<1'S[^.O&.I<ZS:9+8W%LX6JR;JS1];3/)*SW)E1R3Y*E.KP -M(,1.)Z9AA"^YM\<CN,]`*\5:M'"2.=FIA!9*>A2/$?Q]*/\27D#%)Q:/R<:[ -M5^A(7]=/>>6K\R)_3D]XH/BC/AGI!F$G("D^LM]PCOHSXIZ#IO+P'!'1;$,] -M@P.'KHR\@<H7C@U).&PM(-90FJ>PVY/88)LW,^.Q^O7,FV,9D"6O),!HL-5$ -M5<;NX*G!0SG%Z<"!T(OT,F`E+M9_(`CBEUT+P$*IPS;3&\;H:7VC.T/D9*W" -M7H<D89M#3F&MUY>,,C896@]'0)^QG`0R5^"`T7W3,L=WHRB$02[2P%_:(F<# -MSVN;!NZKAK$XOD)D\I90$Y0[T;NM">L1V&^Y<X8E[1KTIQL)8FN]]'H/4V`I -M1N*ZWI&(8Q%(SQE_B]P;AUKY>A[*\7T75/OBV1+D%9:X'4;"WD1X+7>9.J:= -M?*J^:K-Q3S"')=XW(1Y<ZK\?"[&7=<@0@&RT[EN_X9*A#)(,S$`*P8>:?^,1 -M5492*U;*_7P?`>/'Y2:/:#)DQ&$R:@<:_Q^+_#2<W^B`K&3.?>59BF(TW(GF -MO`"]&G'$AQ[-9=AI7U+0&+Y_]ZK6IQWM?09N/8S`-*H[W?;6Y`+KD$8EC?_H -MJ>ZNDM`*P/XBD^_&!9R&]IHAC,>8E?L*[52\QMIP-X;U(?QBM]6S\4TOSJKV -M.XM",?]7_.JY3MM,Y.@)OBJ-4OI^E:&0+ZYHB\1I=<`QG/G%B:<^J`;N5-SW -MO_K'JR@27)-IMM<=:@-9XZK^<XGDT<5'\_M;XJ3RU)KZOTH8'5:$$ZAV:U+% -MM-'7CX$Z*$IB=Q.M7["OQYB\L;P/^U_G[W6#9'*UV\RUEEC&TWB"FJB23Q[; -M(NMZWP.(3@]";H:H%;LOE)7>Z^W0@^^I9+V,R;4.IRV=C$':)]:2;!Z-BJJ) -M?BMUJ`SU@:N7CMBELW,F@P<!6<88\%3D'MB"K.[>]7!$OK27Z(;!V\O&^L[: -M#%Q]J4#3^39%_SUB8QV^4[>*-S9@]J.?^CV`N"_8#^_P>_3OL6$"-[-*$BV' -MF\%'#5BC[/\'5>#RQNMG0Y6=28646]QE3MAE5%%9)R.`>>Q7KV>&D"DA$-]8 -MBE$>[PJDGZSA2QC=LV'X_'75]3QP(VYW*3">P$;QSX00(ER2Q=L9HO7:0@S. -M0/?:_Y(M9B,1'39[V,?/<GM8YL!KK'MCP@.MJDIME-?FJ3I14G=1O5SQ<4;E -M`/FC&O>+!Y^#,.3/"0$U.4_ZYE/%B<Z/+RJ^,!DR==NR^G.`KB)9`S^N\NXR -MFNVFA$T_3Y"?JP""PH1US3X9D#8K9TKVMVL??$4:_?2%4ODOTK1;>=76B4U( -M/+ZL4Q!O_W=9;8/J+,)8H#77SG5RYIM]E]!BO<BB5G3=:GC_H?`%"4IHW\4? -M'YXG6DZ?9Y'JP,'8Q;':M9&<^RWDQUO6^>\U2<A"R]@HMNA3P!=4-,GF5`,` -MN]0!D^)#N#Z_T"/O:82K/PN7\$-<=)NC0,XM/IEQ,'Q9L&*>4*]=(+W"?LMH -MJB&S]4G3Q"OJ7NG91"#^48"80WX#4GP&"LZ-F5H_GA#8YSG&NC*U5(DKQQSS -MHWU'&?_8=YZ5[M2Q_%T:]V&J6L<72@U)+?6WRY(.@%3&E$;,TC'@AXU&6=GB -MP_M\R'<)=.G(MC.MFL2\R'W<*T:F@SPTN47L+:\\[GQB5I^S4_,"F-Z.%7]S -MR;[H;S)\3LQ335<KTMEJ7DT.>4*DYD]@;JJ1U&<NB['@]^G;>!=A@9M(>PSY -M+[V?#6S259;<O9^AV:7_&ULGF-G/A>X&%"\<D8!"WV"[)T896N^5YH>3$+%9 -MZ_AG[OJI7%&0X3S0-"+CL=`?P.$$Y4@V&-S5/9L!%=1<4RU0H@82P=%\;C?$ -ML&?0X?SZ3H-ZEE*=2BC89\(7E'?,#JNG//S]FF28+_+D',>+^-%C,0W1U%B@ -MJO:YY`HX,CVO?[3$`HBIKLEM$N(0]5WJ[1!5-8>AI4]BEAX.N?7@E2-ZCW9W -M:Q6"(5V#$_.P1E71HC/%N/W$/FQ&L/TV^I29C#):16>(U*Z-2=S!X5V#S#)M -M=]AUV.JD/A/1<Y&;61UAUKU/S,'J,X:5+?7XF>NG;IDJ^Y9F8^D\U"0W+3\8 -M=U@[(L-6A=_,T-B3/#ZR^U.Z//=-++B.,%0"*-UZVCIX6H6M+HF7P-'3!AUV -M>MVVU\]!B08.4`EI0B;KV$J)_?S2!!84FU,\EK]3A3&A-E';93K/+,\?2<HR -MA,X\)[/0`;[[@!*\Y-S-`+_)EI49K2Q(?KW(#3YU%@.2IPR6*7*[^$ZCMKS8 -M^.V5Q`5Z>E9V/S\Y:YFW=Q+6X#83`MDIQD47L9B6E&,.UPC`5B77/`#!VCU% -M#Z[%CT>:X!\]0(0--I9N=-1O@SATO&MU<;SOH7<EK3Q2##)J+-<6U@HN&.O< -MT;?U\:WM2W&7@U(/(QZ*E5RZRG?UARY77S'G=\0H92RV%.>CRTVVK_=^WXP! -MRP3,W97]]HK;NQ41=WW69MNOZ_-0Q<)IGZ70X<;9`G=MW394R>F<CB9:'@.) -M!6'K77&:\)0]]-\ED4"_,DI/>SM";NW?>MY]J8H3X!VFXT55S82OX+LV-%HE -M.Z$?3/`U<,40/H/,)D<_%%_C"$<KQRI1-(%P>.<Y2ZCY(S\>_P\1&*6^YSM# -ME2J7P#&P32@8JPFIZ2*J?H=>9Z.8*0R5`"P0*J%F/J'[ZYKYF2\;7+RZQ4!Z -MQQ"M+:G9(C!&/28NBJ[N'^YX0*Y(`D/&E(8A0"9R1>PN?GF_FQKVDVC:YM36 -ML%D7%L>E_4E>IH"UX19-[>],397X[M3:O@;M/\E*93&D"NDO&5<<25;U]H7- -MZ\.Z&*D\]5I;`$\@;_#58#7&*88$E^KA4Q6GC2BXD]-J_"%3'VX29$<:Y-4] -M[NO-O)G>CN?\QSMZ*VQ.E3GXI@Q(.ZOR\&]11H1-KWJ6*I;5(@YMCA(=+Q'_ -MF$)`>DV=DCT]K$A2T=[F>B[G`_+KOJ,/Z#[P&>E#F4"5X>[:_XBWF'V]/4O* -M2@^)_,D:PUD.&4_+NOQM;N!_PINQM.^2+H`$=.[R#._#5O4C!C&;(Z^:>TAR -M[X"(*\([;$)0TX(G^O$HHZ@,D)5[VOI)`&_2L.&^")B=IQEL&,VD.&)_7 -M1'^OPJX-KU#ZGS):`[-.-76*M()-E/+QGEE4T[M54*N_[KH;F4:#P6O1B>:? -M[XPH>QQQ*G:BJ7W=IN2^.GC]LB*%UR(M$6M#/6]*H?[0G+F6#:ZQ6HYSKTP` -M_+/INF4H2=@K3U3:D3=U)U*=CEZ!C&K_Z*OFEJHB0T:`Z=413)X]C^G%H$RV -MF1]8/EE1A%:&C#K(W:N\("UOUW<E45EF=.)#65$VP@?CB"?GE-JH\#E:[?YS -MF+KD+R@??-Z.?I_.TQUX!7>C+E(6->9P=*!EV!\$EX[U1B:)A'ETN-QCXT8Z -M<::?N!1:B$E>IA,(NZOO7!E1<,P6W$Q5V_I-_-W2V^!Q3*EW2GM/)G5"3TGE -M'B.=,<=TCX[:O"ZLVJ%[H*P1%=\L0\_\/%8V)PW.\'B6S#=YX/=SB:/^&PWC -M3SALM\!0>T#'AE);\@L%+FC0X]7/O.+T>8ZO(U&\CP+51B]7\^\"^!*\5CYV -MU[=>G[Q5\F`&DM;U$ZHU4I")J,+\"M@+?IR:C0Q1H%3+P9`<=147H!>RH4-V -M,9Z9>R'BA$2>4755]WH:!0CTPJYUKB.^^6B%FI8'M>V.W*R$9_A/)?:1P^#D -MJZP*H'7NP(,NR6!)L?#1X&F5%I9<*)8M@#SD*`/-RT5CFT9(]#G")L7\:"?' -M,Z-+:B*3]>(;8_25[?*?X-/XC,`MSLTCC_^^I]P=U$A#2:P$*T'?SVR*984E -MX+AJ3%'P.J&`VA/,0UG2O@D%A:3YTBV&6R89.H[.R=<#T;HX%;X]X05X2Z>2 -M-Z2PX0!G40ZW&,V,/'YI+$XDVYF^[2LDZ"IT*SR&:]TF91#`0T3/=E3Q.;KU -MX`4(+3+=$=4SCJ-=MZ[Q<LL=D8U^3+NM7\6Z[TR4I^UTRK;_<_FFWO&-1;)/ -M!V3X-41U"7URG[6HR&DH+@P3:XN!"4'>28,0$LEZ:`.VX,Y4PRZIL(9](P?. -M;,:*OB1V+0-8%QG;59'*_)/BUD\-984@VSK@*.=],^VDB51GI8K1XV$,'",E -MN!(%S7\$E@P_"**.6H^WH$C%6<^LO`U>4O?'06(0-BF_XBA-]3=M?Q;'>XI= -MT8ZW'Q<E(;/<YU.:]\[CJ2N-_"2_3<94>QT219:&QP?3C`!IZ>X$607%?[X3 -MDO?J&:`_;!7V$&*W3A_.2"7?(Z/)P_E)!E7&>>'!L):D%:%.D4IS.9TWS[T< -M==877;;=#`&LKU*-C^02UL\>B]W#MDLU^JCG&E79';9F4*_R5KE,O6X/DRD- -M61U2J;<EWRM\?*VPVU)^?VSYAO3;9UFQM5U>LAZ-/7JVW<)9B0&DF_TF'8=J -M-Q,03T\NJ7F;FRP-SW_>4KXXBI)UY?KJ)W$]O^./<=%"+M$*I,[!+U$`,TLH -M6O!_-68GK9SI3E^NS&5[5V44A.IFD%[-DFI^;Y$%^NL,T_"6,B>>1^48P<[M -M67]5N&&/<EC[//-VMXE=4R`]G#]7*U+-=`DV"$TQ0]?;\?1V7O,B+T8#RM;I -MUG_"#]#4"_*-T:'-MA0U>H=!&6UNXSQ3LJ0PKI6H0QQV6RGGA1,K3F4^Z;R+ -M`R3^GQL,2(I=#&5&C2<$G>XUF;7[*-'IN^*"20@,><_IA0)GB0>%_)V2?O(+ -MEE*GI*C<*=]B:]"@\KZPM#PW=T-I0!GV6'[KOE,(?^:KEL#K%>-0TZ?(8\S^ -M4\;T$=$C@)S347:@U'=V,=9_?O<+L2MQA^QR]5O0M@K)%*!E4IMCTSC8I_`\ -MT8+1#A7D2NF8]@#-4`@$_STA++KVA&0J$5Q0JWOR'N"%0H7?((U%57NUP.K2 -MSO:[V?^D&1KURD7`D/%,=)8<GGDLH(8C<^KR6"+@X\<T`5'Z437"%JS.8G$E -M]WFUC*#-\.TRG@,/N5JS"TT?E-?ZYFO;*\^KZPI_1WBN7$#,L"$8\'M=CLH1 -M&TB]<(,TZ53]RU-&K,*3/B]\93"8'7PIZP3IY*#HK!JU(6G'E=>,&>WN3_5) -M#AD7[GLC1,K^D!/M`XEE(A=3L@"W1%73&-&ES479KP>VW(C1^M!++O'IX)'6 -M&OY#7S<4)6C8D=_[/`\W,PYLTOQ3[KA.\O(</-9C^7R>G4H-IRC4,0T0:6E7 -MI\LB!%?,4$A`J?B_)NC(6))1<W+_L[]JZX!82VSJ,LHD/UQNUJ-K;XG^-'2+ -MQ^QI>%T0?CSJ>=[2UO[W^8H&S?AWXW>`!>9`AT9'%^]%5IM9D-Z=@?`@6<Z) -M^SEX<)1"0UU86*[U0?49I2_T%58LB(&2>#ZXNL\-6(2>==F[K-./?J^%2_;Y -M=H*1^H9G+4%I_O@4)WLB4=+1/&7J,1B6A/G^T*3]X4FL39G9:RX*.VZNR#_= -M#,:^Y375/*P&V'*EG.[']0L9.0[O-3:U\3HXMGR//42GUWF(+8A5V&W![=B$ -M8QR&C:0^2VC?C;.3RSZUFI7\#QQ-W[6CQ,K2A@61L)1DIS30NX(_-PM%J/+^ -MFCX'29+@#A%:"#.F:.8L+`EQ9P#4@60!@?0TU*R/`S=*UM7W3#FT4H4:2:'F -M\+>^SE,CN.+*SUQ;.]W\UW:T<X.KRI9W[I=PU#=^N<@/##&\L871C\H&A7]4 -MGI^47IRV_88!LJ]3>R#+M;;V&O.2E_1YU*%CP:J`,2RF-<QS$>'=.?"85EV. -M;PU0)O,]UC_QW);MF7<XAITV+.K[,%9[8K&;Q&\CXJ.\-JQ^#2D/FZ%);>DT -MI3]2$3LZ69\+6\XMZ$453&LR++,D!C_-K9_$Q'/IMRW#`W&^K+X'/<Y!V^%> -M%$[1I!(5M/\J$@P';[3*6(KA?LLTS:##K;I22!<D%S7_O_VT__I/8Q,R>0(< -M4-@'@R@IH'OGP6*RX6:V=U28I,_*K6E7D5[/<>=_(WCU3(L<4$9`IR"^Y-R2 -M-JW;.L3B$Q/-"?HOA-_O5G>2`&I<T%MF_B1I'\=]32L'!NS2>;;\(AXSXRH. -M)9W4;08ZN'%:>5SKLG&L2U7Z47-18%+JN!-\::C.]\#?<X+R"\DU.*YGU8AW -MA1U!.WP;J15_F()H@H-0BZL29R(^#IHK[PA@F%-.'QLEB']G,?1O4Y'0K5OU -MNIDE\3D2'JU_XI.$B-?AJHVCXJ,G.:JFP;<X7S.W(DB9B#O&<@!+M)YJ%KA` -M8TS"!`6R#L!+G;&;$P;'UK"T2I%JQ]W[#_K&-$1;C'9;%,3Q>,_MH35$CY.% -M*&*(//=200`CMV3NEMH&!><?E+QH#>)A;X=K=V!9@P[;&`4FCZ2,>XKLW#EA -M*NQLY5>!5`5N@>@810+8/JFU^VI4BZ<L&P.TRY)"S:,,J;VK,]Y\WX7]<,5? -M";5.+[B:DYA3;8_5"KS)3+`&ROM0_A31J4[12!\K@XJS=YM6*^B,1NVWT$Q[ -M2[260TC6.\I7[_+G9*W-]Q5<A/'A++N0;$$?+YS72J:7'.5$'*05$:'##G4V -M(,B@T?9V(MH:)>9H+N_Z!)TDO%M7NKC*]*Q;>A2.WQ1I-"R(K*I0OQD/(-3% -MWU#L9*O*\D<?Z<]KIB*]#[";0XOB:B.N<_\\-U(Q\.-H]^C]U#M0JRR5D:>] -M-T7_"(A\^`^6#@E403_=U\?E$X6M^((0&'TD0-+C2%-%:>%0SN[FS5TENQI6 -MXV?-#6.5%J*F#TYRUF$Q<F2RZ.3US?6`*^JSD:=32A9K4(>;C@&+`AJX['2V -M_RO7#9'C6H6%_Z[D$&,!*PI5]$KB$UGWPB=-%-3FC^SX1L)IO$+4FB.'1K*; -M]27D%Y^C`LEJ@C^UKB7FD/33GJZBE9INV6V0DDHL:WCW%"Y&5$S\+T@HKS^+ -MCN9_UWKPR:#8N:"A]9D]*48]>!BH^N6.A$,L>OWVM3G_Y$O7E=E0L0V=K!H/ -MVXD)=:?*,+[+GSFCA/P*N_.H6`1`]MFP_W*KAR2[A2O(SPNIV.]L_^(MD`TP -MLPHS=*3^)%YR6SS,K"M#5RF4:K^(;NPW1B*8;5G#L!@G91X&"]1(%ZDFYF\I -M<;Q6#.Z61GT/M)1O(A8^@K^Q1#ODZC.FZSF-)=1MFR82S3*@%M!-I**63;YN -M;A7LBZWQYFGD&_OU]ISU<$QYHP5IIPG;!.,23)-C0BX-,U7AU?;>F`W87AHH -M-''[GNK<^O@IHE7:A:I<YO#CRJ`G#(+5!X62_6<=T[0]PQTW'<BTYOCGQN;( -MF7;TW[??G5X[M_QYR`ZD$G72AKZASKUG4\'1!SKL']GD+;9P9$A2+3+!\PH3 -MY]TARBW#!L#O\N\5!W2YZ2\:^=?)')\>H`I+OT'I=!?=]Y1+EDXX@[*1MM<9 -MEJJ/W(!"?WG1ZM'ZX1A?M)EZ(8C$7K?5[8#5L"@CY'01'UWU9%4G0AEJNMDA -M`2C:C(PO,W-I`+6B8;6ESV)R_DQ.@\AA.1Z$]]CHE;RR%UBZ"A$H-E98]H,< -MI0L79NFGV<Z2#\+PR^NHT#G7?/RTDT%[236?#`^HDB3>!\2T)&0T\49H,M7R -M9);_V<'P;B7[4AR7#2'-Y-[KXKLHA933U!C5S..PCDI)T^2NHQ=(\72`4L?C -M3-YG;N<S7+/61Z?!HQJA_M^;VW1O0\D#<N^_<:(HLEAS&I;WH<=_O2#)U0TE -M:*F3F[R^BSV%72LBMD"C5C.`H>J-1.>]X);:+B^Q<91>,3PDIBPKE0T:$#5T -M"]!R),7/L?><<!=$IG#8U!YV@S>&Y]<1\%?Q8CZ$,[OZMD_">%F'&M"]].6O -M*8XME=?8H[+/B-^RF#CW'7D;`;RCO+4C?L4:R@]<?O$14+Z#TCCN\<`(!M6J -M[A/YY]3B&UOKBD]CMRG(A2G"0+[IU`GR<%EW-H,8YQG>/H`*:+?MC$*!QSO4 -MJ]4;:KCJ^3TBP_B=3^NJ@0O8M^=ZTU72'.6HWO.N<=#6M'5[*W1RM*1+/_MX -MLM<3D2;/W&ER#\T#3IINR*+U6RN32-V)Y8*N+=&?0)P#I6A]*^B7^.&B!$<# -MC'!`2:\@T)I@EAM2<I?430)^HH9&U7]X!'87UY3,JY7PX"$%1N9Z^SUV#C?J -M]B9E(>!=QWRFSZ1Y/MQXSA-0]69?T6BH$Z>=J@#.XE('O(IO5KEF`@-+4U1% -M9$Z\'1[V>K=4CZVH`-J^_<'/,>R%=R$%P7!X?N%]=[2C?Y)<9TVRPLO"$^C/ -M<W8D_QAPHF1Q@GJYE%B02:>@<VZGVGD#6"P<RL8CL!CN4M(LXZUW!'%W,W)' -M>W>T^660O8_WSRQ*-_S`.+1`V2TRX"'40L$SC8Q+)K1V0DJB02Q(E_X?#=/* -M#4JRL*[FQ'I.T$;Z"T[&%#2R5*R3<X4UT%+C1/2,X1>T+)NEL26E:('EQL-' -MUK7GT38>SG7-4N$J?Q.JDYLG%7=/6(&GWN#=:EV6V/793#L53=O9;8B&+3!U -M,@?6`KZ<)!=R8^M!V?L7ZYNH&I;&6F=^AE8:E?+G:)04;**Q2T9($Q_1=M]O -MA2.GRO:HZ]V&Y#HX2FFS.M&R).QM@:AY15VU[7+T).Q5USU:5WCXL=]+T>[0 -M`1]Y^I_WB5.+L'G>HV\7:EQ*0L/7A^"+;$$L:&I\#9+H4#9NFH6#4S?$E;D' -MWBJQ)WOTD;[C7SFEB:PPN9K7J0:L@XM,A3=8)4HJ71069[^>=RO2%1U7B*,1 -M:5"W))L5?MZ7'=K9:CO&#FT6:]5$#(286L,BB:>8H\&`+&[!B;^HFVD$$C8[ -M<CYY?*1*GB;R8#MX$^E;4K/"^W17]2UCKD\_ES&79B1GPIJ)=50X[,@4AW6\ -MHMRU_BM]D=GJ/A8H`,S&"S&\>GQUHD3=5>?ZL>VLXSHS0/G^B\['4D4>7G(J -M%H:P65E?%++?3[>GLY+1BU?*N9K4%9-'B5(WFPPA>5$D5OXA#8*9V&0>I7*S -M>D/%*<+&56+FQR_%*KF+&.A5'[]#GS;IC]3>7&\-*0UXI;8A7F%C)@7V4@1Y -M>TO>;;S]XLK<5(]ER"GE?_]O@_[\`P';T]9IAV<)6":V?_R7K2WS9^5GS"L1 -MF8'<=ND@&&L"N"AV5GH]$!LHI0,,A'/"X<8Q(.X4VBE.\ZND(AS5`=2,.<X? -M3-,Q'V<F^"X)7]%QSU>C=QS@\>/;ZE6!5C@J-.Y#:IO]CRYE?/8BG%7RA<4E -M?$=5S/TOL>I8WU97_Q:<80;N=TE&!_1('AEDYZ]]8.>0ME',W_-]%\]0)E[5 -M;R7=IN[X'S=U?/CFYT^$.K:BP5\^]ZFD.2GK4?621PAQEU<#W$X8K`-S$(=K -MMPLM$6YQQPM#'MI@=DQA8%W(EKH[Q33Y/<'_D##4E]NM(+5+G743?KV8+0M: -M#*0*Q<&4-36V/;]C\N!)L<:E2&8U<!,]-M;1"->&[95VK8[#VF&T?.NCA;R1 -M6'E*&5QY@2Y`(N=1=2>$A_V^/XB1^96J$I$_#U5?Z#7'*]$=L34S=@Z+IVNO -M=!)=35;RQ,!\UI9;2"=F0_CYD9,M=(Q2$0\42:*(];-3=\BCPD0SQT*X7?M& -MNERJ[B!-V.)DH<@;&;UV1J-S/U@I.[$+IUTI,:9_S7`.%FN3.NN.K]&[Y=SH -MUAI+W5'FU\]B;NX\^C$,W0"I">O)(C+&,!T>69\0/7>4V(Q_">.`\LRKP;8@ -M#'W+I%/I#D,_+9EN5^M1;YQ+O(F!!<H6/OK=/5[WZMA],3C@[#316X:$2RX9 -MKXAD=:!%`%A&T>>7@IB('Q,/-?"FZ9!+>!LS\15Y>1NHJE0P;ZTK]%7/J`Q$ -M1GC2-:+XH=%3F`'G5ID1N`<IS8$GA5]VKP\*.TE9!(SLX=GRF`!OU\JH<'>" -MJ4RRT!H6LAT_B8(K/,,S5?9F@\TXSIYQ81]6MF3ECT+7;Q0O*GT)$#\A@6KP -M:Y.T>QSKN0#%+CM1!XY\U&J<A27`BI`]4'R$6-VDR2M(J0S8,5-(89--Y.K_ -MM,4\)J"XO=5([-=@8@H$275^_WA<6:ZXW>[.]EL&V?SG0UCV8M-DPV$3/6V] -M[*=//@&:K&#_DF#0P--LS@9UR0F91KW+4F/U"1R05!+^:2/F0E+\^XOVLQE" -MD+OWI5$WVM?]Q,]V1YB-%3Y-@[WE"<WGF:[@!'RQ<$..RRK[X+$GVOL#A3\2 -M=6,7!(M6N\]PT$*EN2:@W`-L3GS&(:KA?K7@@_:4&1K/JG\$]`=1D^#VM^(^ -MAM3Z%\7XH%=AMVZV0*\3/P*-S#X\>D6GV8/$E*::[/:T1HV8V*_8A`M0ZJ+J -M*+I"MR4E-XFND"[A3L6I,T&S-',WO]\3>LR#UX9MQ^;FI7J0&>-*235%GI15 -M($,;68<1\G$/?$<#)LC:KU&BI-Y[`5+MLK\#Z0GNRWZZ42M6^N@0P-/WC2"9 -M_D08,[P.7\(`[4**=C&:%(>";1PFR2>E[$90'@Q9<ISW:#2\0=W8O^S/A:W; -MW#AM*QV>T-4K/;DVQ2=J+4MC_/6G]F0L^\8>1P9ZRQK%]V&)F,->_0_QT:9R -MGVLYZ/)!(4J%ZU5N)"]+2Y`&*HCNHV;`Q[CN?8$K^F6-/4'><(Z7\O,**C>E -MR(^D(Q[-X-ON(KQ0A&A3R8];,?]`J9(SXG%`&:^)/\:\J3D9:A+.V4E0':Z\ -ML\N].)8SRSN43%/!R0+4GD1M9F4U"K<6XZ$RK\G+I''Z*;"XY3Z]W+<PW6!K -M-.M6'5SCU-93L?+(=9U?]5&II2!!/%3Z@^%%GT9,8FY\DR0V3L`<3:[_:AOP -M038/<$,&">Y/GY*$9"^=.1B'5PZDJ;]O,L!;"<AH<\KU_$HT%ZR\LQ((866M -MGH:*.]J8P^X9!5'?-[$TJTOI;&C$`DVJAK@G'+N<,42^M$B;SC1WQPNW#\]? -M"-CTIWH/V""G;S2.0>-*^TY=4=BS?7"-HE'6(#8W1)Z:M?`I8#=R\"X4J:62 -M$%$A._[#8?`/.PZ"%D^=H;<HL-VDU%:I`\0)BE_P^D:;@[;QMA?]LNKR_RN` -M\HSN3PU/C86<K*FT;U5,)`\?+)(YJIHIRO2#YN6KENY0`C#S9?5.7T47.%PH -MSCB'+V>(?8WP=EC-Q<PDRV-(]\R&KF)346=7_"-6O_4F0F/X]MTT@G=S^;[0 -MYI/7;<"B[4JF7%SFFC15$Z3'806:J7EU'ONM.!T\>[-8LSNQ":WO)]J?)0J( -MMQ*;=0EA`4.,;(BGA05B1!N<;[7?H0=H1ZUAJ=9][8?[FZ)L)`G..4D@!O+S -M9#+NC&JWAZAEI>^)ZP/[+^,S6PC&T)7:AKRLB_HWC<VO'QEZ[C<QC;D`DXY+ -M"N@$OF-"'Q9Y*%JR3S':<B1OJ%W$P(<6\A6Q>&LL"L`'9*8]^.9ER4]0*:G9 -M">Q[@UK"<-20^!PM8#35I6TTK/L4P2U]W#XCE'4(=4I!!KV"N\;$YUMOF*$/ -M#!_F9AM&U(<J!@0+WT+61#C9WIQ&#YQM`I+<Y$$H:@M>6GU^@[_8JTHH7:\D -MS5CK*30D;)!K-/P"Y7S+1-15EUCH(]7M^_&?/\R:&[NY1Z)_8?UDCK8P6B&6 -MXNEVC@@%DD@V<[BL#S"_]E5+31CM6DBF)*XH%D7K:76^*AK.;\FO]$1T-%75 -M2#A&KPR98IME.M37+16!EYM(B'N5A]ML)3!?:9G@G"-J]R^,O89#VS"XQT1V -MGRPC,PEO7K&5TCK24,QA^'XIB3BR!$;&Z.':.'JN0/@]^1@99Y#K'LR9`F9\ -M=L:^U/SQY=FZE\"3P;1Q8<BL"-LS+13\"H*-U*/,F*3?AP92/]I4'W?EV:UG -MOK/9>W2=4YF+T"*/]RCP^R`;WHB@YGK&8-C."W?L"42CJZPG?^,`N"EVE#JQ -M<*E+]Q@D=6Q&$*-55OK<$EN'XFEHPLVB+;%_H(M4HTCR\2@)<[2^S58,9\CZ -M/Q%PC]=-3P?/.A5L'VZB:TGL+*/K)F3%<ZECNF\E3'MN8D^85-6;=N*7.K\3 -MJ*&P*.L88A7"#X\-LKX5>-?Q4*')6,5X:5UJ4G>52,\5^-,)ZFD2JA!\#/"U -MA>%EIW:.N@9<'V)[<HOE2/,QQ(Y8?CNWOX$@!?\-0J>LF3Z7PJ.^ON*PB4$! -M+N=DD=+[F\AV7E=@,Z_#/CJ40SW>.[?/Z8R41&=2),4=/+[\%M6NVUF!F(@O -MLUXI>'@QO\.S])+XD9#R*K5V2CE?8N</<+7O:Y<_\IR!%$>S!'_B+QP5XVBV -MG10N"G:,_%S^'KE<;"!V%0:=UK@I3TZBM6FZC&N>3U\=GYUWTW(1NK>7?(<I -M-HES?L?;.BI^][,?C??];+:8S5$U2R0:,Z(LN/RX!=DWDSI4X72H/)B$INI' -M8FL:LK2?%K^5%>/.W_(:_GT*3J$DB2\O!KZ26]+8=_'I,:-0Q;O!C-$'I)#? -MT/?EU_RZK+NDX!G.Z40,\9:>R3X^4"!Y?ZRQO8#!ZSX)1;+(9AQ&8GFF9YNA -M!6$1KKDM6&?)O\P8[P(`L1&A_(4ZEV`?'U40U79.4_1GS\Y0V,O+Y*P5'BST -MR@Z/NT]6@1L+4B4NR_:>L`&EWR+U7IO=532Y$ZD(@GEED@UWW]I^A9X.;8E4 -MV>6OS>):>]?:R]DY\;7=4\\V\^J8-H@_J;FEBMV?`8<E27)&.U<9]351.>"0 -M@Z@R3WC-3^\0+_+B[<+C-`RO>/T#8>)CZ,$*!812^J?F.4N9L$9Q#7?<DXJ_ -M8#1"'A1++/.0I4&4&J7+<DB`+Y.5M"$^0<)\M*UW0VP#V('</[&$'M2W*@;K -M($4,G)<K)M2Y<1O&>VA)W>L\O.P#OUR_L=R*FL<.DF"`^ZA;>Z((%B@%.Y1) -M?MCIOY1,)K^"X3N6O1Q6$:T0\:.\:23D?-+AODEYM(SMZ@R5E^[@52N?$XH7 -M>E@!HH$6X@/@H%`X#Z"=%&NB3*Z)M+C^N>;'@,O&FHH5,9EA$X]"J975$6RS -M/Z(:IVX/C7@R7V9QTOX*%7@=^$*V_#V/\F/8\9GD]IM+[A5OR&P6&P+=/?&^ -MXCYPU(V*?N']7"OZN!*R9G'XX0P(+UT-?!7**!Z8[;X*L!BCB&[LZTC[V0F+ -MA9[42Q3$KY2P/2\X0^82TT<,;*W4`U)FG^$MS+L8#=>6N*VSQ9@GC&,0-H.R -MW^8SU3X<*^9"\7J'@"FF&\)5/8EN]-TF@SR(AV%V?8-=,:+VVEEPM(\[-N3* -MF[LT+X8%-0^"PIO%89O>:/Q8KC7$/2A48*N.UD#!>W7_DF*&6U#R]ZF-]#:- -ML:R<<!1C5X]V3>#>!`572E^LF)*\V/(Y*'9=\"4)J4^N?#P7MRE[AE8$87_K -M-.')B=$O8R=5A6SBO1,PIA"/EG/5^M(DV3H"L=:/R/G-L^:]Y7OFW4@?/E33 -M;+)#ZAPNAP"0./>KC9&UBY(@FNZMTXNVS+N&CO!&:RVE3865#IH3];33!_4F -MYF;?+H`)MZ<,3^R-IY%122A`@`"$EF.U9NH)M3Y[AG:#E8Q8_,F>^%!3A/C< -MUFN=?@N&$H^ASPHJMR(!P-,:*@[.46ZJ]&^.1[K4<DU:JQ0EEO__<.MP-'^> -M@1^LPK:4F]ZO)NXK6A'^'T3,5/??_+KY/1VE(SOJLE7HWD5)K`BV%CS>7/F- -MG:*<,YR2ZO!#8"F<32YVA@^_]S*`D:/=,%3$^DK_2VL/`36T;L+,\E+ZWQ5+ -M/T5?>`<[+('6Q92VX='C*%V0$(\KQV'Q]9^9C#1DY!O<E19<E/NN&TWWK8A; -M#A;L.(]`HR(8@[OZ)FM`I00.9@J]D_W%J"#E(C8*2IC+_2SB,M8VX@W,^UB_ -M9_DZ]Y(]]=MF0K/F/\>6XI\#_TY>20GG3#9&&L03[[/-`\_@R7?V/EMN0EID -MQ$],*S(.`Q@N'R#S=!$\WI+TN$!K%U*W*%8J57]*%55L%](&*#_2Q>4\ZZ.5 -M-C=$EZ)(7`-IP8.;`0E,XE[LQ:+,.0@IP&7,8S#&P/:\F+A@=7U`BAV6SB1- -M]UCU.:>1LEKYT5E7S-8;M`=LVYX>0!_8D8O#]F=46`=Z;TUD?M'?=.MA9(Z8 -M;_E*-YE:XZ@T[8-P&Y!^`@/XS65PM*-`T(_Q:.8*9T(TETP4LX?0JSIXOAG+ -M:+M9FW/PT16G#-#=.9DHR'DV\A:A&Z8_5;N;2VZ=.C2DSY4B#\RU'N4*A_<! -MHV[+Q.?S:K+8KOP]:+)H.EL/PF$RMQ/H.'RV"I'8.1%Y8M%GT\;FL/:FVP0^ -MWX5A.-127/&ST2Q1'JGHTM0RGMAT7\8^SVH7),.W61ERU2H&>M\9QT1>IFXL -M:($I20P'0NIW@W@-K.L$]/7+>94-@%?'J*<_[1KEN[=#9L@OT$:LTV2$)WJ3 -MG5GZ/>#PCF&@Q6.-+T#]Q&M+#M`3D'F+`2U>6>6K_R^D(H/6R0HH>?1_#6+N -MD8A\+Y/HO7AB/G+`DCGQJ6Q#_:9?MFI:EN5KED.8Z/?M$`AVM;1U[?R]3R4T -M9L#?67RZ5"RP=N``E0QI,W-#YS@$-S>"TM)P6.Z'%+6^T4-0TBID2KM,&[O" -M=JEK?&H>]MI]3IMDXB!PX"6F=S6',3V`8VM$:S8J!,A/A<O"8(KO_SCU&7N0 -MZP$A:[!^B>-(F8J;I2^]WSC[$??TWX^6<'E$VD_[EA<U7Z`82*WWDHLM0%*O -M$/0DT1.=#ZD!J?AZ<1170=767E4[G$-J\440@>A4A%6Q)E;U_GIE*K8.!;I0 -MG_IZ2VA&KQJ08)/AW"]#H*=_TPI^,#7@/!K%[,%`]ROGQS416WJ(2<X`^#?/ -M#_4_%/Q`'CHVW&?IB5]XW(EF]T-GU_RXBFLN-5HS_E?*IWO[[#.-E'_3K5M[ -MN<L`2,H=L"0IT,JCF/-D$F._1]*)1AQ>Q^Y"\@TN_3BAI=#=&`?J[Q=`%3:Z -M?G5*_XT?BIXR(6A]"=0XL>197%@@7>`!A1D6IA@M8GFO]IZ8W(JWCG"QK=>Q -M@FCSOZGZF/Z+8!9^!AFQ9I1W@8`SLI,"_@9G*9I>(P'9);FL?1HG9H[T25$@ -M*D[9@U*1E:,);(IY2QQ9,'_&3=HQEX'IK<@2)(1^5XX`D,5G)0&8"8F.C3T> -M2FY*WIH%'1?!F&+I'Z@&'OR&;XNYBN8EA]WO822ZB)R_)VX\PZ]_&.+K_UMS -M-_+/@A7N8K.Q0&C,&VND_E.)1E^D<GL65*IA=Q%`Q6V=O17U=V#L*T?6!=H! -M!?:>N?U3GJC0D$6U/M1LO1QC0U#FZ@^(]>XM'4W-SC\XO\3)65YXM7PF4%CA -MH,C`';DJCAUFR*G:RS[2JHBVK'S<+C1QM+_$2GG>LCOW(0)A6GOBX._G]88: -MY#Q@F_MB:S=MF`S,(B66CU&@AKC^[UT8.J@KG2YXYN)5FF+PWQ*9QZ0F;R`& -MQT0#)]O4X@O>9W8-PKM88M"/NET:88E9*1KNO<D.2G#G(T#;XC`5]A['=VT@ -MU7[J[3X.I^3+@/]3C105#.2D]/.T<?Y]/W4'=K0]/LM!;*80GO%\^R'#LQQY -M"/MHM)JQJX,_31<GVJ1+'IE>$WLNY7;NO[&NTQY:5SV0>LP8P`3L!?EP?)YP -MJ5DGNXEO25E7`R:E\]&DSF/`R&O2OM_/Q9&>A`JAT%8,_!5@LP(3;+\7_V@) -M;ZM;T^<U,V9(X%8T.5N8#.03C;Z9"^??(,?1%?4?KK?M2&8!E'YGO%'#X],% -M+"E*]'XMIMGE$=G/]T.:ON9CM5T65$+$_,['9R]4&+FI2;9?JW+9_R1OMV/( -MQRRYD<M'XM(5U%3)'(H6/I-2I@DRX(M%&P2A`M#$4[.%=DGLHI$DK,EG&E1Q -MZFT#-#R&/SY.6X*G(3SM/US$CDW$I$F4)+^H.THNK<Q.Q;*M'H[_:U1<O37W -M8TMSK^Q1=[C;A+BL'X1^/RABII2'-*8S2)WXP/1D*7FFK?OM2<]&VN=$GTML -M4H-:>-V"HMI'JCP>TH`DC$\%BH-+E89H#IX@):3%!-'-6T5F%DX8/S_!`3?_ -MO*F(.D\'P!;76>20^$1>6`S->K^1GAZ86K;WT%==OM^;^_QKDAJ($LS#OWKB -M*%SK+A!.0#FEJ/TE:&RA-D@BA'>XZ<(7F)&>;=<84!6S4X#F&<T?QDDF#.S2 -M/TK[!DQ+1SZD=N/<"5().[.<0A9?4$]ZA<?G^A[[XP`?."R!7K.@U@:S_.<C -MIB#B9JH+-%4A(+/?P?SUB0IP&%R'&'1::-14]L7I6<3"K]39*N\47RT7:]*B -MVQPU'/',\7,1[PG[;SAYK+W2Q#;C!MZ.&TQI'H_9]#X5>((WSWI<=Q#:W*NV -M+R2`]-O?A^5A?`EQK9GTZ.ULZX@#:E!Z1[1\J;6.+'72_([6>3Q*9"`J+^76 -M6!=_#GY<9)+E1[WR;)I7R-QG;IZO+F?%]PI5Q-;Q-H]2UYGYD$(6C.$H('.E -M=DS/=_#*FI(Y<YZX"5'K2Q-+[7_62%Z2I'O=1E<Z53FD:5=9?Z,9AE.)I]W3 -M,MG5@$DP6&$R%SZ_<M>U5A@5`O:#A?9YCHWC9!1*WFF[?,["PL"UI8_/U"F4 -M0WK/B;$(<3689#F&:>'E.8#]S"M&,@RC"/RYV[G^U9':SB$[\CF^N+V/!;HJ -M*EQX+\71BU4GR&\1GRAU7B0-8]5&?M&5_/==/D_GB,!AMP*4R,%[`T6_X\I1 -M@SI/["ES:S<$*DJ2_`UZ!^@><-I+_:!+<)O*9JTHS*0A=Z`3S`5CF_JE\6V6 -M\KPUGM"_$'7+2''FG:[+9D@,N3Q'-U^&5*^T:0-J4=&<5D/*T*Q?2G9"XG_F -M^2X)F+9F@P3XI>##N5,;O$/&>=<TGD!V`0&`5K?@KK0_!Q;UK)%_#/IF`D*F -M0`*+CM'SJ<#V_&+"VTVA#NUY6M4".I;AS#G2,"`!&M@/1D1HD4\3WN+JI/?9 -M;HS>_Z13&_4,IA"'OLI+?0HG)N'[U>9S`5\M?0@`=_E0S#,\H$IBJ:7J,5WG -M$\/;P&\[->G/%2/?+VN!$76;T%+"TP38:.!Q/(/)2M?)5,"/HE$L'AI4<$<H -M'D^<]+$5X+UQ%#V"I/F2"_)%JLCJ-/RXML]&;I:0S5P8!F,W1MCNAAQ)4^(< -M+T7ST6Q=RB1?*Q'/?34G9E\HB>5$7'3TGQB]@\5.="J-47WSO*`8W\FR(-TK -MN1#44(^V9R2DKNDRXH&E+N[YJH<"@W5`5"K;-;*&HC8:'"1$?84]D:\RO\EF -M9M'=!$H:GT[W->G'\;/JWKD,IJURPTUV@KX)Q62[`Y8[1QHX%Y4GI.VXJR%O -M#'"\C(WUA!:0IR(R,S0^!"FSLSEVD6T^4(8B-U%,NGOZ/DHTOD*,:'Q#[C0' -M#A53.FF_<]S%96#8SLW%+5@50ZG3^S.<=,2<3@0&?1+AT@37'LR`I,^V]=F: -M]Q;B@18UR\]EATL0/U7<LYANKE:@;>,+3Q.J2)0;_F\86$V_X(=`N6U$88+< -M/5TYN*0R@X$$RQUM&8WV\,K;IW+"YYW+OC[,6(""\2Y`+P>SC:$P.YF$NWD\ -M4LY76R:NINW%%_RCZ@HT<GTR;?I)I>M0'-!L]0@Z-G>0HTD;3VPSCO?>9?)M -MQRD9;9%5J:[N76I4+AC*_5MO2IM)1#40"I9!<N!Z1&/R$^Q^IN(N\P8F>&#M -ML3A:&<("5>#K85DM7=C"]C20H)$+?'V/(6"N?ZT4%CL7W7K[>);E:J8:R+W6 -MA\^*".N9L.;!S:)Z#GY&#.OS$B#CO,'I<[29K3"#2SXJAI$8#,)>0&."HR[( -MJ3?B+8_PRFH<`&):V22,L7H-I1TQ=,%DM_BMNP'3ZJF9_%*>2%>`O80A$6M^ -MOZ-Q*<?[5J74X8/V=4M8MW3BDGG$V3[IX#D9)8_E;`<8:(26TS7,>FEV#T;' -M/[#[S")5SEVOT<J^E<ZQ@]/CK_@^,SI8FW?&M*>:JKNRC!0.[KQ\Z&Z[_,0O -M$O)@Y%`+YOQG2^0(F7U>HP2>26)>)ND`<PX+:1CF`LU%,%<YLW71(LO7C0L^ -M+IP<L-D?92<DGRS5FSO"<)'&+$\7MND`_6V,E=\,2.N=6M&S4'TV#@X:O[Z/ -M[5A2-CQ>3:7B$;I9PZA%-I<B[0U=K25+`T<)AZ*E'9/'G<N2=-N*\7W^[)YL -MKCY8AD=6^5J++;-VZ![+<=Z\"!H(CI/-_`R,8S/UV)`N7P`N5I6F][[<+F++ -MZ)'Z2(?ZJVU.N<Y5-_\B<0(97:`?CT8@YC:+5>NYJ-FG6+9`$U(MAKH+CL0( -ML3E[@4%XM>WTM0:.)3;Z:7+-CZ6^]\.\JCQ+-?WTP@'CUWW]Y!HFBC+76!ZK -M6>!TW_:U,$MNX)JV:IX[:4$P?G#&P**KMO4_ZO.7,]=F["^%!DX#?*>H++O2 -M>&5">P_]4-"/*"#-%8J`\_U;OR$;7ZX`FM6UA'FG-7CT:6`/E%>)(87P4Q.F -M>/VL(E_8OO8:E_$\VSGP<3"KE6)LG[RTI&"SRF_Q\>@TSXHN52^Q"=8JNK5# -MRT/'#:F'PKN'FA\UM<7R)6D0$\=F'JIKV3!I[68\^NB_6I[@*T3$=W=\/_06 -M/9DH[9/KSX7H_\X3W_*O6-/8VEI$Y]J!PZN8<5.6^:F;K2;QL;7`T(Z8]0?Q -MPRR7*\E!@"(WV[]A"?LC@D$[<*HD>,W]?!C\&(XV':O\N'_RPJ*F=`![E1#2 -MI7]D^>"EU@=><1*55VBV1<3[S?NBL=.(VR]/:J,=+M77O2<<+/8[9,QZK%#U -MV)L.`4+)G'V#@<*`.@?EP&^L$Q(N/]MZXR!KU^M;0B%17>J;60["AR!RB<U6 -MH`]6T<L;"[U?8X%XNU1EG:L/ON#WIENG]2P0+K=6;0./`Q;`X7+#2\^JV-C3 -MN>PZ!.2YWPS`(O+*CJ2:G%ZD,Q<9Q%35>->:6[IW0:G/*HR$[Q:RA&0NP.C( -MX&.,OA!Y:C5X\4*43=+E*N5O0&$4T_9M"7A\O*BV@.-HEALG-[(/PA#<+MIX -MO.8"1\E=_#&&L`:HRI\L>==->YMDU,+^TWF'$8XT]:&.1YS0;W5DFD32&FYW -MZ=FK>^%IL`ADNQ20RL-ZQ*\1"\V_3=WK!BU&T@_-FV@+.%=(<<HPF95\PB*, -MD$_+TR@K`3X!]9A@6O3`91-:'_]=&3+GH?$QGQ:C)^?!*B80;LF]>;L11JH& -M8B0GNZ?,@@'N=7'^@V59PI;>H#A1>D?\U[/VYCD\(*"/^;G[!@G"2,F>[^X7 -MH]^I?3?O;+PU[N>V3('5V:V!P\[M^&+-,F(4?K,OZ72R$V1/FOWS!<BV3'E! -M#A17#&FI$3Z@/`*.Q".Y3\39#0O[D-/).>@37`.U[(-!E=:>R>;&LK-ZO`8B -MGP1DCXM0<"6&(KZD3^CJ3KST]H?YT@JR]3Q_%[GJ17OOI?%$54JJIS[\4V9? -MSM_"H\[0:[<0%0_6S20WLGVR44<%??5'Q9NT=B[<*_1=Z665O.%:.N0$QV%( -M2O,Z/-5".^+^V38!E3:/)=O+>C4//+;?70RM]N+?ZE05TA\$@&HD$@J<=*FN -M5\JO$7DCL\O-[N7"EX1G4/DZN:$'B2\-9)>$1A'?]^,UI*$/%WQP20C[9=X: -MYTQEM"CJ(B80D-+-"<J2,,22!/1`B?_1".6?A*/*M-7R)#?)#E*:C%=2+1X` -M'6\S<BQ]V',VQ6TT\YV<_PYP#_Y95WZAP-LS.4%<<N-A;!8[6V*V-Z5KA["U -MP1)9_YK2N,HCKD%'L`H(W^!V$%]LJ;/._"$`?K-P:>^,)V!D&NCXRHW!>@__ -MS(9;H=]>@@G>T).AP;83HN0RX-'?;E1.RQWN5D+"ZS9RC:2__B?MW$BRF/^' -M>3KB50,\"[J]<T+ZK*7G^ZG%6.RG?B*=5<5@K[4RM$5CV>_UB+1FO7>F=WN` -M#T=XUCDBG2WW8F#1#=XU%Q<0\$CD,APLD%R!KE6`69V]$3`8@QF;=>O?VV#; -M:CS$"^%'>8K8G[.&OWR'17S;*[^G:[(L]#OR.(J,/8H<6DA]T'T[<RDX2&`N -M'0^5P2C,E8PQGN)]U6BD1[\?[`WQ/;SJ.'&8,2YD=<:P;-JOC/ZM/KL/)],5 -M)`8^7"])!K#J7%B7%$[Q$&:R^C7'$WZU0Y<SS0UTLY3=\VCK\=SJXWL9WKIL -M>*'##2QF=/7.<\/8X+JO2PC]7ITH\74L%DQ#)K!YK]2,ZB17<[Q4**N5LK(M -M6L\F_7(96#YJ2Y2N?(W:F]"=<'`"+4D)C8[*%,:0-H\7`Q%(W&-$IT)9_C]/ -M2AN*;I][)#0R0"DG@=)?JO>'N<=F8<AE<2*!<0Y=(LEC^%T*@7KOFQ&T[%LE -MT^CPQS7@2[(Q>YK+%J,*C7;$RX[Q!'MS7#I_MI49XLB3F1?6'Z]K]QC.-$0: -M9HQ2@4[WH*0&(XI$\21VQ@#I163\!VAZUR%<ER,G3.T^2S>ZNRA0NJ\&<WR= -MJQBSFA_$;TW1]M#TT$O:1N`PY?6]7GYKR6$YJ!5)SP&LF<L"#8(.-I.(J/*Z -MTN!X19R\+J0P/^+<JWU5:[NK7+!)-9]!DH"^MAU9M<:`RUE6*IQ78;BW=,N1 -MUYDYVY$HW4@0'5]_I,O;-(_LZ>+MU!#M60_,FTY.;VC`*Q(>"TP-UP6#8PQL -ME>,5U%&/,S#64&N&:!PK85RWW;@8*"<_=OR)?%^GU1@>\L:D-%.+ZX.+--2W -MHU`_"[,6?$T.941^_;9"Y`U&8L3,=3X5NQ;\02.*K$Q=+0`CF[2NIN6E&<5Q -M9%6NM9,`#O&/O7J9QT3Y$3"L5>,JY+BJ`%'B!Z^GKD5S0.M^E.1/U[)RJ/0$ -M<_%12N.>Z[1<`1.`1:1J]B1Z1+<@6,PRPVW?P\<JR-0(#XB(N!<RPC!M7%$W -M)'(9I0I<QTXP!;&:NQ;@S1T`TO>@TX"=XC)1H&L4\9]W^_)CQ5(*^EZ,YI+D -MI.O2("R?)!#1;F!-`EY@?3JI(\<NXO%I;(-N*X7-^@#B_DL\28#;R\TFL"`+ -ML\?$4XW)+_)(.K7-:E/S*TKI\)H@_=FU*I05>O[B*^#E&U-!>X&LZMOUOCAD -M>Z"(R3%`"GUKH5\>][="7C]^$@\^,-'?($YHQ.4-Q\#.%,CA`4)#TU4-BW\: -M9\G4<NTFNN78%%+S.:<S'EW5I!/L[<%=S+"!V%7BTKGKG)/I":($GS7"@UVQ -M,:L>;3X/6\-\$;'^29_<SY6N50;Q#4@#F/!E_QSSP91Y$4"W`:?PSEHRB0+? -MG/+V=2"F[^'[Q9*UMZ[O]OS^LGZOGMJ1V.;QLU:>"^@O5*0N5IC@D'M/F_15 -M'(TOV$*/DGN*[0=V[`V8CK)5]H`$J`;F)^EU[^K>&<K]GG,QA5;I0F%AR0]L -M4LD/5A'GM8#-?.I>M_<H%3D#OX-;7TY%5IS5C@:OU#T@_8DD[AE43RH3BYJ( -M60F`<I$-H*DSB!H1#V$RRKP`FFNZE`+L?FI@CF8!!#20=,D8O8;8NLR7<:'. -M@TR8@KP)B;<C=W81JL>&JQ6"(^F3YP0Q_AQ6S^8-E5S'K@WH^7J^546#`_17 -MU=_G4ME>@"=5+"X3PU4K@:#!!6S>%@56FX<J?HUL!;DE=<]"<DVQ?,8C=!*\ -M)\<!HR\,.\;YNPJ4BQP_+?QZ1TA"-.^W9*\G5D=Z>/M6C.33N-LIN&^Y<-T" -M(W>Q-\J%U&2*.W8F#,1L.:`U5T7G)+&+Z>8J"0EJ@C4RCC"\M'SJYF=.RWTX -M7[HPO0!E<A0=4<G32_KU:[4'9^(*C_:S75:RS@`2]SKEB[=OHX?(>&)UK31. -M5E1$;(WA^^A!JT<[:F]&B5EI2;!6TE"G'/J^G^&24"C.^4\04GK@RE.)=FP8 -M20*7L7*RMS#U!JPY1_Y%8WB(Y)/C-]R;M\F`#XQ\@W]0L7J\32=B?X;@D!RK -M(#XE[YISQ'\,R(1>,N]=4/EBH1LH==-N@1@L.%V>+7MLI:/`E.?IY+_]6D(+ -MZ1GCDDK5K-TIF=6A(PV8)="XWX,`>P5I<$%Y(\)DK244[`\D1K=ACR/":I\. -M-=/GP<:C:P1U>3A=1-2T=7SAB<XYVV34Q&1F(D0N%`N)I"VPN$^LF(N7-O'O -M@[BJEP,<>J)2PI&R*0A__,97HSL&IR+G_X!YX'3UGEKC&K>P78FVN6V+D"_C -M3"A'3:!ME2M%T_KOYPGZH%0RL"PT#`+`B>#[)GA+,MT3/5],F8I,$*:8(3A2 -MD<K]YRW/YRI/`<J5F<E5U*?!)U(0-3Y%<RPRQ'*'MM&Q&_(BL/@^RIIJ+ZEP -M>JP!E^G!,5,2$%6R2MV3"[<<BUO_.EW-$?;[G#V%@GLE0"^8,'JT:XSI*I@0 -MRM/]`9<NF?C.JAWGR]EG><C<B,#$/7#?J#0%@$16F:J0S!4Q0M*/_I.O.;*$ -M)Q8:JH)_<L`G-_]`R=(P-D+9/$;J*"$*X%@=+I]9DUI#GL=8W.8O2R<@+VM# -MW:6!`H%HV59!V=$=A>;2^1_OD5)O?@.TUNB0ZQE)(+],T.50<NMA!K!$(X&@ -MA!S$B+FNJ>-TH&D9*932%47KC7JUMTC>15I[4BM^<("J$/#KFK?Z*2RK%#]Y -M/<@\+F4*SHF(NQP!0JW:T:[U-C4-B%7VMPM[QLK#W?T`8HBX*/.:BMVP85+= -M`B$WWP]`,6646CK?5I?I#7A2O#?\+'2W[]@*P\TN4\.8V]AZ5(",C:%?;[ED -MUMB;.JG[.JM\F.P8HD9:Q*C,3!<J>[H!7G"E@,P;RO%NBYV`L[9A*;U+8'8> -M$=",<W*3Y5%HV,<,NS+H_KUEMGH<"<LM_$%&2*9N!<!N$P-D6%V%O-]:U.,. -M]LJ)?&)]UCTD6GQM2;17D78>A6="3BPM.=)FKCE,GF]/Z0$>CF<;2W#MMP-? -MXE:RCNI"3PUM?BJF@S'/-Q&DQ4LO2`&]OV7`NG89G6'*<Z=L4W%XM%5F`ZWN -M'(Z^5LN#H-^&50LFXB%<<(%.1P_T_AO`\N@=&"K@0.<G%"_W$^*L^6(CI;`0 -M2&@U%#^8E>!%@QPX_8M&TTOU0O#6X%@X*9YN*\`0\]C3;VEO5?R7+-;,"/?1 -M3!V9+$OIHRF)%@4JVKI>MF2IK!&%#/BYK6,4"OR<*((*V!@;SKRS[5'4'UM1 -M^*X[*I@8*4H5!.LXW7ZRW0ZUL`NFT/]_"43_,+A[SY"-U+?_215PLV7W%B') -M/?X20SA_(J$)'/OA]RM?66IW$O0&E/HV^RZQRX[3D4X&YDV<8?<M:0NLK#)Y -M@VRPT&60"U[CT.]9F15>[8U(=?.R1)O'GS+(?[?[/>79V(2I4%M>[\B;XI(; -M>UOH<$=:F?^K^G!)`%.^$T*R(*Q98&#;\)8LY%&KJLM;`&)9%$MF+_;\@&/_ -MZS"<^?+284P..5,KF3XW]',>>_'/=)FHMA:DUQKPWQ3ACM]%ZN12O`.,F1"B -M%L]IZ:--PI;]C1N]DE8!W.2/.5`N49MZ/X0#LC?IY6>1Y)="8+/6U-W^E.:Q -MP<N@='[Z,$(AYG+BC&?L`/41TH*2W--IT,&-7J1R8^V_%<GJ"GE]G.E_JS?, -MQB9E[2XF1U`NF7T/;N!FQ[X#)9U%>$?4;L2J9E[CL<0O/?&\=W^"UZ*>\8YJ -M1=R[JS)41[%2[N7-\(<VNZ58L%4RY+]Z/43`C4O,STW2]S[=\H4.H-Q6!5[4 -M#_QR!O=L4J?A-#=?Z^_>NBCTK41F)C=!\OC#=TG)_4%?"UZX(6Z9!^=%[!#M -M_Y&,9\HU?"+M:LI;M/KQIRY93)J@(@5[-OY`6@DN/@5T/=QN!9X`,IM03BLZ -M;B<@4\2VF""-2O`,:8L)Y#'QQ(:E&X]IRWB2D=`'P24^))O+/U8W9T>]Q;V? -M0WQ2[.!DF4*'0_=()*+3W0*$I,#/])_O.X`1<9#)$!NY<SFJ[Z<,B%Z75XG; -M/=ED0LC8J;?VO(Y&);BG7<^KS7NC)PL9B?'KX0-,,9F&9V!7.0VC&,J?B^0O -MB`"XT8>DL<@GA=VH9(V2XG)^^U8)IL!3V64)K!^69&C8P'0<+X_9G%*`(5R9 -MP,@$.:VGB8G+S4/THHW@50$5E[[8M2J9?#N@:[YC=('E#AK/-;ZE'!/105)[ -MA3;W`^S_FXUF+G"C&RK'MO&Z@OLZ?MQ3-6\TX.O)9I:K^\)Z>"%-&>"W0^:G -M_6K<Q&,NFR<G53QE>2ZO3S]<T[#&`LB$@1UABS&9AM0.W2_]7YJ`X)P.70_' -MIRN8]).A+7-B/2IM!Z8^\;7!?MP`S-"BF;3/+K`5SQEH+\/AAA]X3"=BJ"SW -M2\N[6&QMKWFB]>#B<VT>CY!YHHNT^D;"5WV?^*3H?Y;&ZT^?+#W5DFXZV>H% -MK3N(QA?NV`2/IH,G"!T?#J0OF.),$R]C1%\644RQGU<F@C5!CZ1VBT=/RQJ% -M&^O5Z[F0J99<2@9Y=6(^G,W+4V4UU9;P.2'V!?$/4*(S@AGG<F:\8,"J-V&' -MS;O_`N:""3=IN$%9MO`W`(DZ$VT/V(UQC9HK].=_GOJ;IAT"IS"WS:+4@M4S -MX174X`I57\`.?,7^$@0@_<_(VA`:T7STT54>V-)N!98\1@@BHXZX_R,"/PF6 -MWZRCBJ6S%<!50F,8`6>TU97]$PP\I_EPVNCF`=@H#N&.S:MVS&*Z^KO\5Q>C -MAN5V0:;7-329D;ARPF6V2!Z6]N>(_WF%WKD;>%U8\&_7`9;YXP-M+#"^_Q3B -M?8WLDM6;DI`HQLLAR!I>#IJ18V9C.EE6Y,C7_DT\;;E8,J"^^<L>8QTA0"*: -M#24CSL<WOT:67S43;8RB%TJ^UCP6696T\Q@-T)18_27N#LH0M%"2\'DX9[(H -M44G8!H/>EW*30NOW;(Q!`.^Z/8XUXN5$)4.P@%J]-GY<7YX7X;-8)G[E,P:& -M"X;?TIASUU.0!W\J&R>IAV29$;:JR(=??Q`AR/^F@OP:#5075."V$:9:STB5 -MJ33+S@5HU^@M,[3A=E(B*W@VY6_*B',K+YB>1EF_AYM'+#O-+LR2@F)M7*YS -MFZF7P*3S@9NW;U)X/:U_G7-E):['85.!$>?T,M[5B</2Q$Y?7`]`]P-*+.U* -MOSE.GZ^*H=;4]D(#$G_1U:M+CE"%TE]%[N,7Q+)#WNC-UM+\D;GN%M->TT\D -M*^S%><=W71IC;>;T;WB8K<R?F4:3N[!\*C[$3AJ4[Z+B;K7G=5EX"!8]?;1M -MZGWTHH"W,IM+WWR3UI^8M)?%RW28R,_6W1W$K&YE0-8ZC7O:L:DM`JNE+16T -MNI;F=L3+P0C9;;VV@K5H7"A-6=S/H%V*X\7P1B%VZ<&OD(O8:1@\8E]`1:C) -MVW9"->OG`/_,%`&_".+,6[Q-;QV1PFHM4IZU[/V8"QCQ)/_P=@K\ANJ]Z!IJ -M36&\\O7?&Y5VU,IL7LF3IZ/JW]))@FWLTQF6FNR@[8Z,#/P/&M_ICGK?+7`& -MO71'@>&>'LL>$Z[HSQT'NE1E,/!&BMBHY,`#&6QHKX),+&I@E]68-!'4.KD^ -MB2+Q$-),>#T[@M+\:Q(T8'[,I9^CPW14-[^@VKN?L*CO%]K@0&2^G;_5".S[ -M9/F]F$!F*]3XH$$A.02;F=,?*1<#!S@]461'WP`_GFH!X`5AC*A-9>1?_')3 -MWMA\R<>?HYDHC@7[^S\0)8PD0$9PI<'7YRH<9F?@=PVC$B@-;";QZEMH_K-% -M?=2SM@<<C1WS7>YO?\"3(6AE[`$35<&_NDS)>?NN"4#$F78>>.^E\T9K,G%6 -M((S3T\'RL+<ILE"69J2($U*$=UU(7N8+[9:\29%EOV]0Z=EH&(+-?CENEIXV -MT\O=&O@UD+V[I;*LN`&-`^RR?>($WNUT4$5^H=?DC$3(9$B.WOMJ**U'08Y) -M[(R3]!&,+6"/D&FI?(49Q8RT#%06P4N@V-:+K&8;!#0>B-J'_),>#*OF4ZE/ -MHO$+6_GSKPRCEK@<_]CW;.NNPP7+3@UT0YPG?=TX]$TR6CN"N\N:29NOR@+3 -MF@GH%S-*/;BVJ--4'E-GV%)0"[Q9NL*(``!AFHP7="J+:\CI^H7ZU#B<;-0< -MI43VI06;UBE_K\SR$3J5E@HU/:$,Q;:06("EUL?II)K`$F408L1\_>IC7CJI -M//EYLFU`YFW/>R$G8C.P/TVC<YDU-`ZR3$<1'FJG&[DZPEK1:'DYX?/V%8]? -MGW1R[['AR3E](9PG4LPR17)RQ)<[YF0FA0('Q#/\JU?Y57]">.!R<RPS@*(. -M7G8K6K+C)D4MQQ=54QD:<8-NN[C?(=D&YOK65UN%+=(3CC9P(X\[@,#C]2LC -MID[&HT^JN)MS##OWA!4V0U+`%/%6O"@F9IG[!H=GPICE7\W3]4PA=DZC&R(O -MTML#8]V#]`UYJ,(_IW;E%0.%X'*4D6:\9[K<ST&;"=V9>>[^K<]9S.4@MK9; -MAM`YFRC^7`_O0;&9V<0D)]W)XJ@G3MXK[M@/Y4\@1DAG7@C<6RUAYP82!S9[ -MMMD<(RBB:?^JBXN&9XE3ZG@8C`W$HM\!L3K/?G\5A.M;U[K7K\<&9;*S<CK6 -M#(T!^1&-)??RE]ZA7!^JWX+.60YHRQKN<I3!WO[@EG?3@-L*?D@I\7XXI*8( -M&G^#"#[B%IPG$#G:4G%S*B<I.4:#?E2^5@GAP+4]5@V(C%NJ>,T^]JL)A8@C -M,O3W<.[D*=^VERR'"#!9NK=W2T@CFE,*>2>J#OO+)C3\.#(0Y(<+;EP6C7(B -M1">.+SON/',;>Z7;MZ&]U._.YJU(&)ZX)5PB9=2JU:7GE.4/G3VL.,)6Y\;+ -M2'B^%!^LE=?/6'[."2FK?$'<2#4`S7>/NK,9GM`Q]K:6G9*ZM'PT`:USGTL: -MCG'::2>[.(0`ORUR^U6\.Z479\P*31-O)"B4=:O@0-OE99U]R)KP'2?.11H' -MDR%).*6*.8SQM\9[;SP\*++8Y$W"SL,_M^?*&D>7*W/6D0"0I:#R?$7'Q1:O -M5/^N@U)V%/?Z<LR?AKI%X(FP<J^/<E%_4&!.(#*A-HEUDMYYG(+/!I@@P:O4 -M4Y9KC!MD"^]Y?6P:V5L,81Y/X!P8.-%Z\-2]9B\K-153H[#7O%E2-`:$=Q>\ -MJE<$8^ZQU.6*;5_!Y*8;D(!3A#>%P3^S?/LF%7KSJ<>6Q^#]:6.R77X*KV[. -M063OZ^SOP(C@X<E!7U=^^"Q8,B:B);*',*(2CE*(=-Y_]3GO6M6=F7I%!SI\ -M_"GI>GX))<3;)*D<97G?/B=APU.1MN4D'\Y.P_;DI(FD@78=P(W*-$E?2S?3 -M=?1LL6Z)MJ]EW/1$K%H55C%WA[2"*(']T[]&QJ;$='7"-\Y!8N`'RNX-6+AC -MQ(4NEHR!&E$HWFN7,W1Z$*;A40:L>29)3K;?U[5#$?8[Z7]D4JYLTJ('S/32 -M>O[5I_/9!=$8V$0>CJV3DCMD<V:BK:H_Q>S^<&\!Z\CM%5_S^\O$%C76\_5Y -M+ZN+$2=2#))A0`,FTOZG-3)\VNT'*(ITTK;GV2Y6,CJ2WH]H.;5NX0MPMB*E -M0D4;YGCBREB@&0K2-]'D2/<E3.RJ+(=`=HO7Z@!RVTM3@D/`N"*K4+U!V468 -M`WIAG:?Q+YIX7?O0#G67217VA[93)T]C7?Y#ZH-9"8:5&_L;<"##T[II`H$E -M+GQ.S[%I1IB>5/C&*@47!N,6T4?OU/>?2[FV,VT;YR_E`OP[E$Z65XB<W:3Z -MD_FE'D88*Q;]&M>EE#"6L5(NK')ILDG9\S.%JYNS(:)0PV<"6\*]%?9MZ1_+ -M)8UVQPGE@[20;R8DJZR[!S7-,PJ(&=&X?P6M.R/"GX^UU_[+C:Y:JZK?1[,G -M5LU'3,U4N3\7)3Z)'/PP4-1N(+X5QJ(O/.?O[#DED'JI<H9:O9!-Y1B_=Z=1 -M1J$HT^R-@Y*N.0*=;#^N9,?(\\0J2I*H\IM'IJPEC2=?!#V,51GK5KWOC^(< -M;+R1J]AY%E.G+.<I(;,HG4/\CPQEQE;C-FEWWO[M%LOC&(>K](5,+$B/NPVT -MSB5.R\.+]\%?T#_>&O_:>)6P)A;.*0:*\$)/[`2XP1X>"$D>B94<$DT+'$*/ -MHK6B[INHZP5-U/N!E.,(M-4*D"^<G0\[Y&^UIJA9R07()'.52#X-DNDMX?@; -M(?JWW7PO[!-\]^$\MS)V#>KTSZEN/4LF\(F!$A6Y"A!5?IH'^1-Q5$.3;`9P -ML=E>JZB0G!/0W01R@NP<L4T`X>&)JBBVTP6&^%I+;=FP#UH&LRNNLATUY#U% -M7S[>$OG%FQT$).%%>H(SPG+QO-<,W39(BE3@Q[MP*CK04.A*3'].KEJ0"P0H -MZ$Z%UJF!AC@'=^N,KB.`"PAN$3IR>SRN6='Z61#OVA,\RCC4P!:BXL20V]FV -M069A;?]+K???AT5J0AB1/78:![V2VIXAKW*[(0'S/2Y1].W]?%6LZ$:.F)@D -M.@2HN6?$3>VO\2NG?!&;JY26;;@[4)`%N=(3QM,O@YC]4Y)!_?Q\EI=&"A#& -M3?B4M$6;:[?0[ZYBFJ>!-574*[$(8_8^,[R@8??F^%2+[AC]2`2R:%1CLAC> -MVB9-2:3USYY68?L0O#JCS$B'SKHB5BP^CB#Q)S2=O9`H/)<8UOU"O&1.VBJ/ -MB6,W+5`KY"6$]LIJL!M*R!MZ>LN\SRE)H0,@/)<@K9#-`?:01;4!`JY/F*^0 -M%TL),OV[5#TU2E#ZBOSP[*2X0AT8;$WP]UL_BB'<JOD+RAX/Z2Y#E;H]5C#8 -M913:N<A3!HGVE-'\01N>\K\,$L?>:-)7?_\]Z-$%+T*#B7L%K16W*3Q@KX\R -M[=&RL[$HP*&0<'@XVX8N88`*T&9"<TW\LS/9=.VAM"#VC01STV`*8)ERPCE[ -M\=ICVJCNU/\'("G=%U=<8STI&EX0[AO`*B>DT=N"!$]9)9[+7+_QL:NB0IH, -M7D.1X&C/6YW\`;<%HWJ(4`?0G!LXOVE,$I6P85#GIBD%M5B7\S56WQ24'VGP -M,I%%4#P+LOL9UEM8OD;%">45A$0]"?<)=N>C<2Z3E@@>.:XJ^8SG7K)Z3?$3 -M!1$D%))Q`AI^5RD"'32JG#OR?L%EK?M2>11*LJ]O7T89/@7]%`(OAD@K#DS$ -M0Y\8MHJW4H@<K&)$3[C`W:6$4#VI1:[R'CI1,_S1PH^-F_$C\+,\F_$M)K5' -M->]"KU,5WRP#^?J=XRJOFDGO(GWS(F`JHVMN%2,[]O0)0L\*EMM#6T9`X9+G -MPLL@/T#LS+(EYHTO$8PI1%#O;PT07^XT,=QE0$J>^DBRK]7+^>F>@`S*W'*; -MA)L)4D0%\[%DGPC5(`<%,_*X3J/JT1UK5*AX);JWV:WE!8CY>!H427/RG0[A -M)A;J7^;_1/CZ:XA+.*#&BKSAGDGR`FY:4/()"GMTVR"3(LX-"A[/#:O$BM'N -M;P$!!8,TAEHM/0*;ZX)2(@P^H)GNCS_C47C'+2<.##^Q-7+1LA9@IWB9$RQ7 -MH?EM%*=/='DIU\/!ELP]ZZ!._Y;R5U:+M+.P\*ET1Q.D(+D0@:-\R,9QN/CB -M_#V:Y))'*25-#,2"=\H,;VR\K/EVCSG#$CT!^^=0*B,@\;$_.B+(6#A8C-QJ -M13",G3^N+'"0EX"H8.K!;?)H[?+B+FY^?AT)BAS;'9J.V&.O&-NZ'&&<7NU6 -M0!R"=(YJ,%[%OC5;Q(;YS`V3@_K/%>[&%=VZ$=2^"-`I/Q[D@*SQZ<M!C3?+ -MYF<(P&4-@UOI!/J0'#K\D2H)A:N@Y$QKJIK'^(LN-:_A6LA_A1<+/G_J4^0T -M*KT"X1O5>`S'L-E;5QJE,WF]?2*?J<XAH80A!`_[9'U2_*738T`,!-F(UZ!P -MK</'M\0LO&*I@=HA+I\W7!.XT-Z3H,""A)M88XXZN_)SNG]KF6B2=<BP%A1P -M"Z^BT(]_AU@./2O]Q::>ZAHQ.;P4@I)*"X6A<.?5'2L/F/.U>60U1^95<O-= -M_Y4O1-Y@&?08P^P\^\D%0Z5',AU6G66,Q`O@X\)",^S9ADLCYEGLF34<UDKW -M3(E$18C-5ECSI0VYM6[V0>SK*##L%1&,C$=0*"&W53<8F5OW*+=8NW&2<.81 -M_B#WP!A4;V1N=V$7!KKC1,EY^1U#A?HX!J1HC=3!1Q1I(1-]\57L+[>S3G0U -MP[.9>/SN\2LW%EF/3+-;I=P.Z0;53RN&L4U$-A<*_XO6FV'2M%4H$WOU.("% -MR>^/(5./$N$EF(;G,D1.I+5-6DG";Q1*G.L(>R+U@[-OVJP?/G;T:ZGI!-T) -M^9L<Q=QYU?.'&`-C5)V'+8Y9:ED^K'.L5(3/EJC.S,:N)S^BRX65G?):\<_$ -MTCZ(1)473^V728!D$KCV?C%JN&TV//;]$E'QN`=]\TD(Z]>+)PRXVC]'.-+N -M":Z&YO.^YG_@MD>^$`K"E)L5Y(O"OE--1I[QQY,,;)L7P4UIJ]QZW-VIN\O_ -MOH9AJ,DE4E";LLQN'Y>P<8'1OVXA./]1*R0W0Q>?82E-VKJI7A![FL*_MAW\ -M.^&X%2"B40I[Z5C+>5X>,@J:!?=#U98#MW*ZG&=&HW(^<X>G5$L]#7-Q"0K6 -M:Y,TKSFNN$*<ZGD@Q#FA\*^\`O+AM^2+3]%/27*K81K+UKBF#2K9$60Q@ZF2 -M^FP`%UC&]Q'76K(VAC*6)(K+8DSOZ,5?\ROZ7"R9Z'AVQN[46^G&O9Z>-K'= -M^\905W+ZSK<,L".@#=RZT(4J-%D()85747/N\I*_SA5<H3N,32F;N\2;$[EM -M^D*5\Y@.R0\DR8-#*)N^FG"\!WJ?IO?W9C#=NF1=+(!T15=:PUDRSZY3UP)7 -M#<&/]U[,,Y_<P9XQ<("AIR*2/-%V3$\]Y#CW2GZI'7_1"`[<XB:RZLPF2^2M -M]WI^VK[?/.+43@1)QJ3]M$D<M$T1-0\(T']&Y!D/71^I&TRJEFMJ0NGY+Q,F -M&DPO-[%P/E)H%OS\4EYQ^UHL6_L#%K!"8S_\=L['C4ARI9`QK-1'K8L@RZH; -M94P[9:&HB'NVW*(<JZ00)0PF&U,>TG=51T.0Z%@[`=0'=5*CZCG@>\B'24(B -MO+AT'&_:&CAU7C9QS<EU(_1P+AA7)@=;U,(:V`'#`1"QO.X[L)V+2R'\0^JW -ME?K+#VX!_(W@1;K1-G`;*(:@NZ-,S7).68(?3)5R5*1\&Y1\6/TMD</VY3A* -MRR'"TUP&:W$C:?)FW69.N)@P+MK=U1#8X311,X;JT(-&,+Z$&SX?@STP&3>> -MCAUFI!&'YH"&LLK`#O[)1>/R>(CB3Z(Y!=]IMDZ6)D0><P%CN]@\A0<?CZ:O -M"B;C^F"ADWFKE$I-ZMMJ/J9M&AR>)X*A\!B"#D,[TVJ1<M+6>W*P<Q*$U3'J -M"^>)XRE*Z#"]-NW(O(/;\96:^L9WG7'U`>S4EY5)BNF46"3?#_]U$'E)M^SY -M'Z6#DZY+.!O!VBSP,U7')F2*('I"G.E[#M#M^;M%@%;ULD['(FAKUL1%V!2< -MPEP?6(1U<*%SZ+,B+2;("V&T!*BL7B=,!F;XZ&A,D7B7IS'N-Y*>WC8_OQ[: -M[_I@-J*>%LIZ7>"!TV4O2GR-E@;%F;&I;).9'Q23DGYT)E*D^_J=:TWN?`ED -M=ZV%*Y8DHVN0.#L'K52/.<':N>>%NO"#RB`VZI=PY]-7DH'0:4,/P,CHS;;] -M1^.?:RZ_SI%]]=8MJV@$R-2"[>=B+X\`\X:B@_&S7S:]$5S*@]E:R%*$3E^U -MW/&.FMP;EVN.P;[1>Q81YMI-!Y(_.&B,.G*J8V6D<NG07PZO1GL<H.^36FG) -M2-<NTF"ST9`9@.?_1(E.M_G/))TF@AKD`EI5;W?89B\M&-U<#_N_XC\"Z4/& -MEU,'MT*[@@U'#'OY">/V@I#>W^T0&#+#=1NE/V4%-135B=\J=>_;UP.Y^CMV -MIY\J#;&84,1:R0#A!"3(AQJ^=ZYZ0(Y`?!&QJ)0$AO/Y7AA$(Q[>J9^TQ94= -M0I.$&<>`@`)#<!GNP;4S`(@R_'L^I[13@H$MI=XF&V@O<,^_(?R'L!X.RW\H -M3[@6K:!O!&2-\3J>T3)7K!M4&<ODGI(0/<$YWIS_=3>G#"_)?F42M&.JEA9; -M/@E@%B;LU;.T-1DV31Z*]<E:]Y#NS/]ZVF?P1;+!LR%"H4CZCE#]B)H&]ABP -MCF^][.&E1F'>/'(;T`YML=8^E7E:&(AA/8[`JVWB:FKN1ZRQ#]T]%,2M+)OS -M2&"?(Y<R:$Q/.*]JGFI"<Y,T!XU,85A,.CN"&W/<OS'>GP[@7I0T+LQK9E/T -M0'R<6*$"S[CCF*@!ADWK*?2<=I&O*$[BBCWM(5H=Y$,F<J>J&LP?C0`1LA,H -M)I\L]H?@VIS<KWB'3!;](IBJV&,4U6-*/9$#`012#V*91S`X9BP7&4%6'MDE -M:E8Q^GCPS=!>-7RNZN6R/UUS@:]#4YP8AV2P4HH]-'E$'J_*1S`(,GU))CVD -M`^M,L>.\L1#A)Z?LH'3-+O@1P*TQPG%P1Q.)D6*=7515G.?OFO5G:'F=TR]( -M%B6OVS1YJ^\F+:FSB!(RU>.PK+4^V*6(;O^;<;&.^DZRJM279=B[/?KN0[0] -M23?J`2\@;Z:Y!Z9&ITH*IBH);VUI&-J$8HUW.]DD[_BQ=)MZ.KE0B%W=%VY$ -M06X0\:L84:KY?NFR#:R%B8\KT7JC+P:GDET1+[I;F<'OERXTKD^>2!FY+P.0 -MS&O2&`^A)F9C5Q'A_FG+BC:VWML$WNA>H"]NTQ2J2T*C2%RXU_6K@ISB8E+` -M@3V1(&%!W@@D2V]MRM.RL2E./J!(2R3;2".Y$MFVOB&A(WVN#.1I3MP3&ZHK -M:/,<H4V/FYDDE5-):,?,_;^9BY^C.2(EY]RR(I&X<(!8"[?DT[7)')5KH)(; -M^-?WNL_#AP[^7%N>D`6;^UZ22UV>XI9)C]7B&-A?2@\K5O6PGY1%61B]7U0? -M9KX;>)#`@%1NY'E#@D?RGL&G@F81?!3HZDI*/./8"WQ=?"HT-`+CM`?^LD<U -MIO0ZJU?'YI/IF4PLU*492$C(KY($/+/J=U(]"*^ELG,WIF.M!M?"96FIO0R/ -MZA[3<Q,YQR5WC\YD_=A/8+\H`P"#F.#W#>M)I@:+#6Z+-G=OF>,[PJ8*=8). -M2V3HL/S3"T*(E>%EDH)[B.:&3]SO:*7.^.;3`(I.["/07%NQ[X`9=//R=H+Y -M,$SDW3'4#//OEC\Q8)W8TIG^A![8H-<CV\=MDS]O1S]F<_:RL?S2;H4=+_>. -MAP*T4[U+/?&68+@(X/_0HP1AC5_X+WB+N@0PC$-DJ%_NO*0^@CQ.MJ[-BDSX -M_>K]WX'7QF&A8'&FX0K_94/Z@#*MZJ[M,Z(%O#'GXJ-2BC5QDOS)*C3!#=7! -MUD)]N0K4/=J2[`WA,,>*_9Z<?J.R$_/8=-FAG*9T7\/@Q+;[G.?_N<,:X.=V -M;"665>G#[SO(B2-7C-!;=P3?0O<P/VB3*:C:2:`'^;Z<X'W='UBGT\50@+%@ -MSD@^G3Q_RD\&NI>4/H\1)O.2R;DX?"T)NJ.,D9D"F'-'#JOTE3&HK>QL+@FY -MAZC:08VT=@E]-A4<]<&+1/[T7!/W&T9C\`)9S/^O>K(DSQLI9OLM7E73:EJJ -M`A^U7+J^:I;B5&MQSAXWA[[A3R&TR6Y-%TRPSM&C$,8C(;R&`-3P,ARFNR>% -M['<!KOO[XIZ2"U<<W.Q@Y?0QS!X?N-^F=-I($$B.9;(D)<CD=&KBX@Y5`2NO -M6>C>@%=1A&WA;(]6;;NXD>DTJ7FT]?Y-,9':4]M[`!_\YQ#E4NKBSO5I--*$ -MSJ`'UQ'8_??-^)XF<'1,K=<E"Q3B:\)VZNJ'P*'L4M?U?@<-^'K64*^^#RYM -M&M<'9LUM(`3A-,33)#;O9>1AE,`KS/90:H)&5T.5A"*>?'`@AWB<6UC5KS%F -M"R`)BS>1CU!\L6U1CE*+>_'JK^!+'R>IDS\CC-Q\G&>9V3)"BT0KU]3\2']E -MT`O!A"1PL7Z8=NR67TE_J.^8=B:R7G!IQIZLT7Q/OM81H!>6%!A!.XG<0VJK -M[<7^%4M6$>;#;_K/YD@?YK0<RBR8HBB)7K5F*65$6U;NR:U#)7FK++7/KAO/ -MU+^B`JTG9.:SIK+_;>\:0E,/9\R_C>.E05R#Y2^%PON@C2BUPWK5$7-1_8<Z -MZX0<0Z5O>A#&Z%32OQE7*:6Z9_7PP[]/`ZY4)[K;BQ+S*D09)<O_"6`[T9G? -MC3@Q"P.HS,)_RK:8\:`XGRH$4<%H>QR)IZ`.,4+#OY'Z!(7ZB_JQA\7O9@$A -MGWOX)PM4$K_E!B'=<,X6&8<#J_R4.\8?SH<,H"P''&$M$/1:@*?/$D:!,M=Q -M*J>59-/VB+Q>9RM)-0(W.L$ALXT_D+$K46'T%>:@#D8+TNGQT9]G0R]@7'H7 -MW[AHE.G\H=UAQZJG]J/\S3Y,7M+T%@99X""$-FP"<HG[8\?(I8L0S=V#*4<, -M8X.Z[I1?>6_2@L+DIU^!:#)Q-/7TD*ME!VI*(VG`$"E_F)S:AO$P8AU,KE0M -M*V)&!B\8A`3,;($;)@Y*]?12`;H%TLJD\)81!ZB.'%C_X4R;B[4PL%\B3)"< -MA/VEC)5*4U6D^]$'>(2QM6>;F[$@(S=X7]-B*S5^0>)O3=*)RQ.JBXXW/>W8 -M(_G'7U'@?Y>XL3W1*7@5\"GL%?^&IDE"W8NKI@-F$?FMOI@&1%+P4#19YIF& -M&MCKFM<W3B@4X>SYF)V97V0D<Y0-NZ`\PDZJ<+4X3D`FHQQVA&FT>E[>F'ZT -MO-ZNO3JPA7/-$'80?R>*)^LBB]I_E9EPY2'/'=Y4H\.SHK.6T23<AQ\8:^(D -M:LHL^][PA)F,4O4=I69&4[M+3OO:7W(TR;P)HO<!T%+$5#M:@>SD-A)?L[T% -M)B(A_JOL*G]*J>I8,^ZI2PT\.4=8<N4BYQKO\LJZ@VLG$3?E$.!Q(]F-[I<C -MSGLA+JM%&7%6L6/M%([/(@DTSXH:=`<\GR*-JM.>GO*?K-P]%P^+H&\.$V+K -MA<2)%Z"ZFC%NT7G25G)P9X*%U7$CK&RE<]**F\:FDR7HB?^[7A(PV6NL']N\ -M\\TCV3L-#IM&YFP^RFX7LL[ON!AB%6ZX6K<]#A0K_!TT(EJ]!R-F&`R'+X)N -M%M&V&)1WX4&8OJ<4NBU?62R63<M8$/K*V`PT.4`K8<W==!WRO3V;\7)W-6Z, -MJF_&!>YGHP*8.0L<6!#?[')QR<$YZM()?;#&9'KC:<\<^YT&[\BY7WT@5)@5 -M&KXE%O@,4U_C`UEROV2PF@/:%MXWB7>0M:(^_IAY?CE"BR8>M_>>0*;M-_LR -M%@.!&I/]O$1FV\"^+XJC!5,A'QC4NCD!DKLZ0,DV48A::&C8FR*+8*9$;JPJ -MPTOQMAM'=X"=CF="QE&032%W0&NAJZ68VJTM9G:2A_Q>=PYD3R^U9H+>#EH' -MW$_3XK5%MO/U5CF(\PA!8V2^;P,0:VT(#59+\7HZ3!73OG3^@EY][M>_*K3. -M9J\,9.I,3U;SPR;O/)U;)$=N#XLSEX2%.+HWLD\L<9G5LWH6)<C3+,B#;AY> -M#6/$)=%?%S<G%$68XR2>%9E=$FXN,MZ)#MER1T)$:.I/^$](E5BI!4->*J`< -MZYE!E:)-Y1<24JP^(7H7X24=W,BH?(A@PM$\%6.3C9/I!_<C@^"1-B7=.PG0 -M4*V^SS0O0#+7A*ETP@E3GV$3QV:6&U"92?U.NAXR9,";Y-*[;I,W#$Y1R@&' -M;)BF@!VI/-#^T`^$&0G2M;F[7!/[K)HW9$`KEGQ#E;@J2D_KY!\]SM<`:97) -M_-:7^]D5@S3G?1])US8"4B>D0EDR<X]0`D84K--ZR*AJ_8QBL*#=A5QYHV+= -M<;G/@^:"BOH4;88-(FS"$E(NPRKPO[R_/CI$1*W,O/AV096X]>Y_8L/Q<,3^ -MT,3\WK-@[I:BRL%SC&O12WO0%ZWD`C9KY_%>(1DIN[`:**V3K>%+MIRA]9CP -M\2?%2.#ZU-E/$.*#)D5(BA(O0.+QV%Q9_L9[\8IY3_\-51XB$S98P[W12,N. -MC'(=!NQX==7T%N,28.!W#3:;][J+%Z(?^>W)G8/&EBX$=#D%LU4*?4EXWYR= -MI-J^?K;*I;)9^.;=/3BV3"LFM>-/A5'_(GV-[P#:;3YQ0?O<]>?A7#A+8!VV -M?K":T;%M\=@$.K/HUO`0;7I=%",7&A\$??>;-@TJQS4K\4[]>6(XQ3@W(M)0 -MV1QO^781R7\*7F_Y7#C.9<\Z31G,.=XN2GV5A"?-.'\[LJ(>#+(VE=GZ0O,= -M*'5(ZQ)6M(Z]_X\_-]2^7W57K2V*O!M6CEO5,2/.D`TXJ=)M>:C!BNU9BV," -M7@D(@JHJP/W;R'8YI>NZ)8`D^V6>4-TAF_?W53**LWW-_AO/6@#H&/]%7Z)K -M(M]>M.^4-`BI\='%ZI"/<;K4-9LW@#ZQ?\=7S@TJA9#OR1C.>;!NB)X^S$2) -M)N7Y@VDSGZ'=S2#\JYEZAVU.%=D$`N)E8>\*!TY>LO9TT:^I>O?,,62.%-U> -M2Q_=<",1;9GG7Z$`,4R^\ZUZY$`%G%\<V2Z0[B*-LDDCHVUP#UVP16*S&:FX -MLZLN#P<\<W=/<OC?;61X;T7<K5>76!/P1_<>VKUR48+X\>`F:.:0#14"27Y@ -M"%LYX^'%3[:'XQC70IC"Y,OOTK+B/NLJ2ALOJ((6:7GR:;<A)R*N7GP["0B? -M81V^YJR6#400=LAK'XD`9GND=P1(7@X59WUWKZ1&ZN(FT<[9Q([FDJ'["LBJ -MK:GK]1IBI`0M&;Y#O(*VMN;?4(O0IT;`+\?[;/"GXZS]QQ5;")_=91,[-E9? -ME[;S7ZQ(I.?WE.BF]IT,P@RPVO1/.\#YIRQALW$B3QE6R,0,'^?Q*Y_%54IX -MJ(15>2I)PGL"IE_0<IL=A6.M7Z"64H-OUSZ5M6>=#89V#;U.$WBT'+5!3A\W -MFV:80&1R3M`&Y@3G(,`H!-]Q:3,[BN9JZ$6Z5)M.%V!L.?4YSH_OZ$(\2'[! -MUF[WA:26DCKLP'7Z,L&!"TB4=U7Y2EYS:#C/40BS("*G0*BA@W;SMH5Y!W>/ -M&.2^%[B:#!*^VYU'6'@C7%X7&5]@1@86,_TO])ZW>^A_ZAEO4(-^2##A3R1$ -MR9PE1@7E2&K9/$Y,C?'P6N3(^,XPF"E'++\R0.53UJ<B^^Z-&%[XH$D`V#_9 -M3L.,3_]HUV&2QH7`Z^C`"OF:1>#C)#;I8&5<71SXTA$2*U0J6@4#.%@LM:.? -M6C<:W'-Q$A_6`6?"*Q#F<N?1/L]C[:IG`M3.<"_QWTF/U2@&6U2J!RD)-('S -M*!J9U!-F]$AXK3?'J9DX,NUL7I/'QL*&*K%4!JWWIT<`L_9+PK>O.C%<:`0[ -MR!C;F_SX/.>L:,&G?\?'-Y[M5]G$*TQ9Y4*ZTG8\^=-9%[QHD1E(MT@<8>T. -M'G+'G5:@PC78'2DJ?8@Q1EZA;G"+ZTIUK'^HV.8DFOD7J&AP4>TG/V=`@KEV -M^_:.7A#SQS*".LIK=[)LEK;J+'KU'>4^9IXV3V&1UA,QI&;GV,"\%RS'5F%% -M:TXP/V%U+1-V(<*^/'R?-EA/D%;KR<)X!=MTK5L/-5=-[Y:IG"KG.]\8&P-W -M$/?^NHM'-*O#36ZVG^E&X]&)JY:;8V>@22]"OK@G:VL;6-RCR_?6[5HD7Q+F -MRD(.6ND*NK"KMF:"V<@)>^W<`:NW2%<!,KUWLCB9*>B'V+T2^XO4Y$9HH*`F -M%$CDT)_H!R26<Q1'PHUX)JS_R*3C-F\K<:"L54G@'Y.P1F_N]M&C8@?J*?\] -MH;\K5O;D\"'REFOW6=%K5EF`^G.YHG%^E!U%@MODA:@AQW<SB"2K[6]RE%41 -MG^]=M72S?$4HBK;\YNJ^KN'ID`&=H#IX>VRZ83(_&(MW"E9$HRI>[RH>C@D& -MN&@O'&0XAH5G^16_"Z5=Z\8-?G[(JJTSNP4[3/+:B.FC=VHB.TN5T`JU85;% -M5W\(0!C->T+*.$A,;@/Y7GPH@``B*O8\N3)UEC_K"HJA.1*A4F(A84+,X'GU -MW)M#C$;*M\V&;+[HE-[P:9D%%O>&%Y`5H,UQ=C#GMYR=YT.++;2S8O1@/Z/L -MC%2D(QEH$7R#:>"(45>R8T3'OLI7=9&Z=KJS!->T*7T>'<CU!DG%JG:=A?NV -M=_N<8NBB_9?D@JE[=_&XM/(C;B%E_``)X@%"I/GQ>XQD3#95QUD,ED"W'*KE -ML2Q5'6(8G<@_(PEW[JS$HQ-E*4P=D9(G5#V_B+/512LGFDQY0S_3.9&A4=Y, -MM\)YDA2*]$7MA-96'\?<?N7.:>%K:KK?`=#[;FW8>P\.Y>?DD1O/J%6$<PE. -M`"I+8DR7^*;YCW(B.E3_J@J[SVYD/'\Q7R03^%I$);W'#/7CGJ-1F.0I<F%G -ME!QLN5Q+8+K8H6FO5(#<BE@\LD;X]4,D,FYH+(7/\ZO3@;2F>;%%4$YG*[:N -MB]LY+ZD[=CN%GFI>3U_.Y\M8]MBV95Q(^F!3./B([&VSRXV4K</PO&X<$K,' -M6,+&!F*%+S0Y^5#7E++[;YFJG38N^]3'=1(U5OK]F5(JX"<;>O]%-VF6RH3X -M;P.`AAS<_X1YMRY!ZCL!.^U'-!A";Z[+=$&7!A-/'@:N\/%8G'/@CP,2(OG: -MTQHCMI7&%!7I!Q]JKYO[G:9G64Y_->0OT?/M?<]P,,F^F1=!EQXD>/FL2Y:/ -MX:I6)Z8(/N.D1K<RD;=9T'A9084ZMIL$:+\H?/@11J]2P-*RX\AX@;,33/5) -M*,'+F7"PZT]`1^=5%:?A%C@";^+T<5-Z4^5'JB20I4Q?76$L1987R>G%\3L. -M-^(]GW4\.LI07S))_E\";.TQ_K%]#[WQM?^/<L;;*^'TR+VQDL^V-2I`4GLK -M.XBD7#=*Z0PUQLL^#NZYIP9KSVZ0\LWZ:J9^5/>)Y`2<-#?,])SX0_@&A?'P -MOA.:`X+ZMQ2T,HIR+YRT&_@.7^8T\*$[VI8[PYG1PW3JB:J,H_\>2F1+)^:9 -MJ+I2QJ/+>H'U1UN'H:&0$I$31L_XPUN-_;X]B$/>*.U3*`$FF'85I,DK3'Z4 -M(#=V(O->FQY.3A@K$U!T8[)M^/;&<3LA[_$6\O?$;??1+&<2_HH]4J3H<+A/ -MI@#MA\3]9`8TR"5NY#HNF\WVP.\,5K1)2(Y:RA+P]I!H0&9O9GCL_<<MP71C -M<]>$Y5#MQ56!+!US!;SY_7L,'Q+/@&N:5X[(A@HOL^-GWV0@%Y/KHLI_TS&D -MOQDKVU%3-69(2X;FH!/+J+MRY02L]#6/-%9,EPWYJ3MR'#'4JOHCM`,/;DKE -MA4T@M-`1SD/MIR0'024^7`1O\,VK9]M61(X,.N\@;K'56L)_4=D[9L`-=^=< -M.40.1C&.D#>P[??J$]2RP)?-GYSEX$G#XRQ=\_5I<U%F>+UO'%I_P(BS.R<S -M*D*3;W:]9_G!UKX[Y&QG%ZI)-A$Y0%[>(WDB@A@!+.@#D[P[EH+\$N+S!B1Y -M_#87K?-,SSDYC6HU<'`-;W\%@+]T-<5^>@&T-Y$N3*CEOBP&_7+`,7A!&'7@ -MNBI`3S/#@"LSD@JC.41(M&V6!DJ5Y93WS'>C9DS7%]%AQ2+C.!Y)JF/%\IPS -MS*;ZNV"E>GCQ>6%6HJH/WV,;!T%I_ZD$:;4\([%E4EAGX$:3*:EX``R'K+,. -M75,`4\GCJ8$+P&969,>R_%&8S0^O,NS6V\\[_E@=W"*6K]8UO#3%<AW=+37' -MA5G3.Y:?T=J4T3S!;6_:02Y`GTK8U>WWX"V9`D\"]^)9".509G:&TYHDUQ#+ -M68I2LLUA5T=U,'7/JA^$4T<O`;S2#CLR'M)(/\E4L6Q5'V%N+J8]*EO!_\W[ -M#W8*OI7O+V:`G`Q].2TF9TGM[<]>7:N>@JC0MJ2(8B!TMGWTC]%*R3'1JV>7 -ME#^7DS%XVN1OY:ZIK;6)?GBCQY`3MM[GO.ZLIU$)Y:"1Q4$F]0N+H$A!ZZD% -M0J[&CK>">H-&$6UJUU(;:YMN<N9L08LFO;).:V@R%@_MO]X6A;B_`IQ].>-X -M;N8<+T<GOTS^],Y8@&L]9^H%T[+9DRC!^0#X+G+JOZG\?2?AK8XH->U$QS%A -M&[Q@TY<CW<T#G6%IJ$P4RH0<%(0=H?M,W67#M5>3"NM@MYO!.(\T,R.;&J+# -M^UO6D&KG0&VW8AG346=;/O`OP3I*:7Y44VTY2ZHPN#YJ_3A.B6B8SNQ_T,RZ -M0)[^/*&1A\?#3;1_9`&2#D_?!K0C=J?@W;YI%)3Q2U5F[*H%NA9F=0)^7,CA -M5]6VOO:%S#N0;T:0FK;'#H/!<1CKQ[7T7PK":A.I4A.L0X<#!+Z`RR?=:^X( -MXA@3>>K]976/^]+<2;5$5>/B5/8N(`H2PW2]51UZ_MMM\72HLH'3IF9B>W9J -MJXE8+J&Q66<7:QAFA"^23YL@22,;-42U31+QFM_%8[E09?(POX'3Q9>EW-;D -MRD:C^N@W8.2N2Q,1W+<(8RPS>&;_Z5;U+6N((AQL!]K@G>GX=^E2$6_OY.S; -M.YK79C$&A#62,+JK7+>+?SB$(6].6N.):#Y):Z?ZV=#/7K$;WOB49+<T^^H( -M6P3DHG.CK[&OX%T#B,TS7LSXO/@/I%/Q>A">>-JJMX;7&(HD`:ZMBM(FC8F> -M3(G$?DC/G:D<IN0B6R;J%J]@KJTQ4\E$Q@[-*MW5!9*:H8\S>P%/N_K5N>T7 -M0'TC2P[[+$J3>!<YG'S#HT1#&:B.US]#\\`)WO%!H-?JJ/(.X9F<5C%%9%\% -M>./)/031#N%8T0UM-WD=#(38H';\*&)K(;(:/4A\*H:WU;[[[/>0P6!^$SZ= -MBU4B\3COU,+.OQYH#RZ/O^$2@VERP><4=R!A,+ORVNN*_67WBYTI`^8O.3-^ -M4+Z?W?4(;:_+4`,BVE&*U]FYTLR:;2<6$L,VPZ)G]?@>\0QZ@HS,OQINQZ5: -M3637F5E&]#?)[>[?X5,)L-+^[L=D&3N0>#'C>KIC`<</JIFWU=-W,F2M\-74 -M%<*51Y+[%OK@-:V9W(W:)O<TP;3@J(K^(9-[)0%)]K70T3(V`T&3^IZP=)RL -M\%&$N*'7WQ3+/`$8\F,+4A4/'#SJPM8M+GH-OKVFQT@I3R]D?#M_)6]\,UN^ -M-I1I3IQ0QE[N413SQ07()8B-/#\<O'@.W/2,)Y,$<K2W:)/%H^E2/]'C&:PR -MLUS9-&,;K>A4`BU#MD*X7E?Z^1,+UX5H\,UH@O[D?81E#P!PQ7#J#Y+3C@.2 -M<C+)7Q(,2SU:T^Z'_ZK9X+:MH2725LXFAU/S(?*ZJ_5`I=GE@M=M?(8V(4A@ -MD_@`1(W91*MCK&H=1=%H.('D-B<B<C"I*3,IFIVC`Z)G+Q!NRU,<_A?YB_H0 -M=87-D6C_?811W#$[I#1O3EL>*JNZ17!U&]HJZ45+^@J+K_)IH!HA^O&"W"=/ -MC-?VMUWI5TW8`:A!HU-&DE>G>T9;2AI<Z)#-9,VO]9F/59&%S2O`1EF!F"NG -M&&X%DO0S!Y2VW`WLF:J8$`Z8(S;.HF7/_"38F]5%EQ<^(I4\O9\18<3YO;1` -MUZP9F;PKB?93I0I`6-)N6YC"IHP,Z<%50S:MZ^S@T#CM4,L=\T>DL7EJ?=8N -M9?0STL$XD7C"*I+!@_[&G1?9DP(`$)]+)8,7L5N\PMUZ(1DZE]&9L6`G]Y&, -MAGS)'*SRC.Q<,HE&@5AI)U7@4]]>OXE>R7.")N+J%Q[)<Z]?3>E9(J.40-%Q -MYX[3(&5.*<7R#:1=U1;,/4-7'Z%3*-6WM2*RJ:RWMU['VZ/"5\&=I4@MA22( -MS1IC,A-*9./T&=!2TUCI#Q4,MS4;Z"T5(5+UF.7-OO.9$,/%\B66^2.OR"(E -M7,Q\$?O4Y)>>G67-K"DNE^DGJ+H#,_V09^'MAA7=-H/\!>*\2_VMFXE3BZ"+ -MQ?53B$F@:<.`-D(<D"YN\QGDGRQ'V(H:=W?[;X(?TC)S>.T;4NRCX-`P2UF6 -ME&KP!Y/Q!=0Z*/'KOB$0'2Y2U0/Y]`B:W7:C+-JY\Y1<;CP,NU5`1OD%\IM. -MC@IB8E*9M:BAW0JY&YNISC<<K8/]2'PKS?5!Q*Z;=*X<>^MJHAXF9LHC<Z## -M<*0-U`)^$-0J`TEG]2J7WK[XH$1C$9'\+CSNIR;`*VU>J\0`Y%]X.<CX_4QJ -M_VFTL,:C?/F]<*:NW]MY!6+B9NKBW`_J6AW%QG0[MOTV?)+U@AHIOVS*G5AQ -M/F^D]YV*\2D21)@KCD/=>GOR]YI!D/?SG]YQ:D6R_%ISD(#((<YT`9RYLV4! -MKIBP^\5,C8]F.[NXACO5+SP_!+.MFZ5U'IY*:3>QW/!EII;\LB>:(8W'I'#R -MA[J-\A+M9C!`DNL0SEW!,@^X2`PER<EEVQO5FO=-<U([>CA=HDA,3O0+0@6V -M*JJF.86W+<`?&^[7R<W=49E)<=BO[.EVJYWJA$"(2/"Z&J?JWGH-A,&]#$?R -M[TZ9W(IIRE,]Z"Z76=NRX6&+=0,-0UVX!]>:W>KH[E5^IZ'[^Z-GXK'FR"@] -M83;T.>**0M/.?"VM2^V[9:\I[D[<^^K-L"".F9K5`V6,M1ICG([*SK0@^P+4 -M\+25$)KACNIG#>K%SV13B@>JY"FX990[XQ<U[XH,G$_\,P]*JG;S3U'\S[SG -M?"#EI+4T^7%10XA2&`1$/J2SPMZ2$;PM)]A5`K[W:U")8].IO#]2+HHTDP&9 -MW3)&M@2F-"*_?1)>\0?\CS/*HED;ZQU0EEG1V+!H2[`2V]R_TF8GZHWUH<4- -M_@$F2B<9L&[$<,*G5CA"'S%,])-/,)"P"OOP7JIK&2BH;%V)`@*<1P]"#_MO -MZ^+=?WIZ@3R;9C6Q'%T0;.[P0*%V?0YB*_VV2OW'7NU]8U[F=]IUU,DVQIL8 -M5V-;^L_QVH9X=P^ZKB7_HVYN@=Z,!A&E*YG0$YA^LR>$FU5U6@5TEQCP>4>: -M+1KYNR%F,<IVG,C8Y@5A"P/;K,%)U$A8\.2#RG-/3';R*^I44PA>(\=%.*9$ -ML-'.GD3Q[WY0RQPGH[>>6^+34A#JSZD;OG>&0'W37.@:%_.2`^HZW+PR:E6I -MC8P`BK%EQ)$(\DS8]72O*,WV:9P%ES^,T\>RE22Q3,SZP9.O219)E7R%9\3W -M-JGOG%$WL+S#^0D^T^<:7/<SYKN%$BD=#2K^&*#I(_F"ZZ&G%INJ&3>A7N%, -M$3/OK8A)7OR(/.&E@9#>C$9<J<>C':DGF#!5UAUXPQ1;:&K6V=)/1[5R8V3% -M<JRH'$+F:TG&RQ_D1`T6VHD@=AB=?'H?DFV]>V-'VS>6\@NQEB#;+6Z1]2<+ -MTCN$408(\F2I069U-5;\QO!&^"P60JY;')WZMF9K')J$)>H5D)05.(,5A`Y# -M@K^STGC#E09>"D3@3WS=@4@V%3]^:K=*099J]@5_ML)(2)5R%C9LR?C1."C& -M;5>654=R$E]&>5ETG'^GNN`F_F'U,"C6;#T:?2ID7^+\ZILJ^#+]%(9G>Q.8 -M)\4@.%M&+8;Y>0"['R4ZBXPJWR[4K'[/=YL]/MFH$[RJRM16Z_VU.SXE]JQ< -M\F[>0Y$LT0K7[<=]QY9ES#VOPS'G0[P&B%AE%91SK*XQ+5VXO`@S57^2=PP8 -M_,JJCC0VMJAYMV`2@<Q^'(!C:[YDFBJ@B+(S=V<!_2(PG@+P'=..F,=>3>%> -M/&75%:U6=J<*8+='XB77(T%`02AU`&X3(X)V0JLH\:#+!EGN+V%7PCO223K& -M\\<FR)^[X/E_CG[?,VHDFP+@P'-Y8'SUZO_`AJCT/]*1[9[ED.&5*J`]3D'B -MRA.\;EG!O8"L'U(29M+*Y8O$7?]TXM[A9AIB!<BN?U*LOI)0\Q4Q4DT'#K(P -M)PGYU7#++Q.F6MN=QS8]W#9G)V%XR-BSBG4CM]ME;)GCRZ[*L[RJ<+@-MV,^ -M_E?#H]>JHI*C*Y?QMM7J^;457+JJ"_X6GPS(8W%?K&L`_#/,/\`!NJ1)9W.U -M@J0IZ2JBJ[IQ5J(T2)IK.<&*Q$,3\9P502-.5#@PQE'@0P&K3G(GR!O*:"<? -MI'AG%OX8/G=6PK=Y>*&$_M'V=4>5]:>WL0B%=IBA0VW,DOQV(!`XO$&56+A% -M-'@(UJ[#X\-%?W:Y/:-KQT?H4RU+TD$*Z0R)>L`YVI-<^_;[G@YK3UM->H"5 -M+X*'P-6R3N%1M:7SP?X);#M\3)^#[+7!(-HR(/,F*!%&*P$/)XD'`GCZ$Q)? -MCT]9M`+^B9&M2W-3'AZ]A?BMHCUK+2^\]2,(^9&-IV3F"OS>\V7"V'E^W-J2 -M$MG4(XVPQ5/)"T[$A7E)A'GXV$#]C-YU##`-\,QX)DXMX1!ULG95@'3WLY9Y -MPI``B$/PX6^%V)49,>^3VN9:\=23IZP?D)P&QKVC(EQT-'MTV8<HU[489=>" -MP!J#C9R\8U5'<:R>4;('\*!2;5O'[KWXI*B6B+%2Y+@T&Q';/:?SD6VRV&E@ -M8L_OJK](9*2)%N9D-K7M`+@3$1)9)%#()WSKC.6KBMWVA+P>_\;'#@6W9-01 -M$]A]N?*&`M&EJ5WMW41!_'7?C<H85YZ-8/7EG!-[EAK1$1?,S4@^K"ZI3P6@ -MU_QNC'%Q^,<=YJHOW6Q6KR)*]`V:3]CRE(.>B6$L>IPH0TDEIC&W8$:"4/AD -M;WE'2=7ZK8:L/]2U(31ZY!8/T;-JG#"3;(ZY'T-4:831*027\ALKKI[J.WZ` -M<T!;:Y7\DA]SCFG;["D%MNOZ/L=U926E6),*K2>?3S$YQ?`J4LUF>^5.2-&2 -MY''&AU>5C_'7(EY66S$/UI:S'X#%UJMZBM7<L*QR/$Y$II\*^Z]]DMO&G/GT -M;6!%H@AD^\[>X^4@)%SB'V7=04UN0'>NS4Y#Y+V;Y!08J<93JOD^MNLO_>?1 -M9I&]?(@6C9!46X9EGW`OF*()ZYQ)@.S[J=KA(_YN)GAFLU&'J+WJ-/TTWOXC -M0UC(JA.'[U;(X]`CQ`TU]_OX9/ZGR/U,[6!V(!B4QV?HJO/-=O<*#+:7&!"6 -M"4?O6(0VY'6K/K2VXP>?SR(Y@-QUV>8BUHT^;!,3&WY(7^)_1[G^*D"-0"I0 -MC5N)?03KW9\T2K3O%5#$0[N9>0$PRQ'BC3%R)K$:JZ#S$M%$6Z*R._]:D6_6 -MD@U*%7IN\MIQK"#BY>Y;X7['"QU;ISL&-"3!#KV2]N>5J]4CBLA6S$WMO<'U -MA]8LS7MUH?+V'`2_6L`?@Z>ALILBA(;:9=-0O7=EZ3<7^OU)1B_G,UY:_WVE -M!J.3BW>JQW!A0!%<ZJQ<8[L<<_4-Y-A`;J+X:)4H@SJ-A4`%=(PEELL(9TH! -MV&S-F.$5RTS=M.+K1B[+$T'F:WB7Z;H[57H'UT.UMZ@AB^T97Z.W-DGCUKD- -MUT@76$HQ;9U_YD#*?G6LK2X!B-SK#-GN>SE''<<4.A-U-?URW/&(9R/U,(8S -MW9H2U/*T[0VP:XAQX,&1EZA>B6E_/J$+#TBE\T$D'9H:07+X8"']M?]>]0AM -M_$TK38WWNO%!\C?M,7,ZE;XY@:8D_C>^`L,),#@U4>5'JVL+$<,'&3F.:TN. -M?^/3L&1S5\M6/N,QQ']`?"@4SYEP#ZN`T5RT27W"F.C+.Q]^.K;*`.]^K%)3 -M26MBJ%O[!:\@^,JTR@^OI*Q,IH$6J2*GUD-XBCNE=U4N?Z$YP!EPBM0BZ,_9 -MRC.6-=RW%<[=D111A2<A;#X*%367!!1;5"O-=WZ"GU;UQ*/FV=-)5(5FI0;L -MN1B.!\`G[;#>UYA"V(UQ.0:)<)P_7W:O"JYV-O/W%,XY.+6;,KF1/4TM:@PR -M&.LB[J]]5A^`XKYX='X;PV:K2^[T]!^1I1/+&*LTTY1",]X\/)[5`$'46"$0 -M-GQ!U7\SC&<<E8HXY=AH.IC:1']C2VKXZ;V_I*_&8S+?%A6DXN[DMS$U1!1& -M*ELYNAF:L=E!TG-^&1TP'!]QO:3UTVZ<.`F_9SOO#EX#0B+S7:9S:3,CV63M -M.I=+[+NM4A%AI1=5:7`O(*;Z<(&T12X1;("IB#-EIK8J+EK[JT[=SL[JSP)M -M[4+OU:,'*NZ24(+&J\6^^(IW:LEYK>&F3C]VQ'FFA(OPHCYL55*NZ!JKG9NS -M8OM#GLVQN[FL'@`E@#_7!<8*HNK*2MXU/,D!WFXD^3C<2\.GP8KSA6JW[)JY -M>+=5(],!U76-/X">Q./TOXW"KY7A]*GI>F%[V9.@WO@UEQK].'/0#9#3QI84 -M?`?KZY.B;+[+=-53DZ58:<->"_'0MU2\%0`#K<<-=--!K/[$99-LK-F-)_PX -MP]-N9V6=2BNL,$>H^PTPKMI'2"-T)M"WET_+AU,IK^O!O_(_AY)GP#PAW)6- -M\KU"A)=OFIK%,-FW2G+US6^1T]7GEZA:'R#>\&VM,=V/M:Q&<S(E.$H)RQJ< -M()A%VUA?2B&-4H%(ZLI5=`#[;+H7+Z'UW,J]:$5`]%C.$.,_W_;KIRJ*/JX^ -MG8BH_+)7/ZF*O0@G%0GSGWBWQZ?BZR$E+GIS[FE8K9T_G;#;`/X=H_1W==A7 -MPF7OXP4IAT512Q"2=+<&UX(6A]M6(I2]&'N:"G%I;M._'(>K8X")@Q+!B.(+ -M;5%AJ+/?_V.N?G83%V%7+?RR4@7*H/C>3O%3+>"#E-G[IQ1X;6^/N9.Q0YO\ -M_IAO1FP+^0>ZBSB"Z@F/?V/UQ+)3^&)^_#)LK-K-,-`PZ:$+AXV[BFO(S!;H -M%V=B.1`YARLHR?+"C8(ZR.S0?3?\+T-6[<Z?'ROB@;3"3DH:$KDLW2OT/5%N -M,46(PVFSG-3.;A)VKUS3G<QQ3^\"].,["3=&8,&_P`TK"C<C9?Z/X'\]KKUH -MKHT5P>S#`644/VYJT7C+`76T93U)=^<JE4VL`/=:99$QKK)%V$2ZK/9V4N@) -M.Z/(!G>8&WRE*BMT@O77X"!Y+$N3"I9Q0D\K%''4Z%6'&,^FPB/^J6)<0Q'U -MQ<)?.W/>+P*9Q1RNX2WEZ&WXU>Y)S7Z=@9FR60-WCM4<F=3<YX@!BF$(S)X, -MJS/#>><>V/G9QH7Q)&&I>ZV'<]E'7-_69GIQ)6_>/AMN_-+L1"P2+RM?3HDO -M6A/+VO.27J92PJ1$P%^KE6OJ">3AE/Q.`J#5[<%=J<//]*IW6[XG#@$C+&CT -M#?_0UE3$HY$GZCG_24']C1W2=9'%^78L[U:/;?4-PZ4K,VT.+:C#!R'`$^1W -M*<"Q\(\92>\V*K[SU^0,^LL!5'NH]W39N.84')"E2H83:;\OG::'@@5%FB6" -MRD5DZ4:::]9ZW-A7\=7&K"*K^'^D,T(Z.,=?"OUNE:_KS_C+PTI;):FV6ACY -M@UQ68A`';(UHWY4?KQEGHA&!0LU!DGATXJ\,2??I0T<=>8N[DROXK^BKY/@Z -MA2#_+1MZ+]7YB\^)PY)Y%M7`3"89N:&'1KO;#H]:T,%.1]X%%"7?U`S@PFA' -MA(+@]K$;GKAD6RR3)A^-2IL-5XJIN+C837'3F:NJ0,MI/&^AF2GB71"CJ;?8 -M*15-0>DSVM3WJ]+>BMU9)KF$=D9R=GJV8&$\'ETDAR-EMC85S=8\F90%L[22 -M[H\\[7.UXIP&56XC_$*\N4N1!Q2/>\_VZ88XOL2GIN:Y4NRL0#2Q>J$-!W1- -M#=*\#&H\0^=<U`A56[(=7*&ORY%[-IL^1XV\_,+##,^%A:<3_CI\#!84SN/O -M"IK/`?L2.LI*II!UZ-/J)0?^`-$+DZ"K-M/'1ULAE_9GH:^QJER'C?^0`IT* -MS04H:FKP!<$*()P:R+^L5*CX>8`I%?T)E@]@W^X(MHIDC$83E;'<TD.;-$)* -MOWB[,R=!E!5>T6(0MYA(RZ8(C$ID)7H\B-2AQ[8&>(N(X5`ZOVN<F7%'#8V' -MJUVZ_WU[\7")<4X8%A"TQSE?^]>[^)X/KGGC6R)9XL`K`S:O:T:V3^J`UFZZ -M"@CS#@1OOM_1?61Q8EZ9]IHV]13+7!`<FF,B9-$G/2J1N0<SGO)96T0W'Q4= -MX`1#B@R_1PSLYR45JS,0(FLD(QI.$L_5=QN)(5'6=8):T)DMK\I/(GFX+`^- -M$)Q8OFU\.-9:[FUHN(.<D]*$@NG)^+:MZFDMN5M3%@!-.XQ5<Q)AZ<\4LJH' -M\L?'L8A31D&CX0KV*R$TA'R#(R2U";H0:)8R9K4H@F<$H2_D,Y<?D^#6X^B^ -M'(U^7%=QKRM2Z(;FL6R.WLP;@@)+[V/KHZ/\UZ6'QK]^>J@37X%5C''&VD@W -MY3CX?45HY0<MQ,2!5]2?QG>B!#^9.%"DH@46U/N-_2JRE39#+(&AIQI9U_0\ -M9Z?V("HS"R``]Q(B_YR[R,.GU9#4U=1CN6`U4=;7@;A20HVT^R68B$\.=,"5 -M0\UT,;#'$P5X3Q'3#!(A1?`=-OY37%%>`-*[V2841UCY)7L&X;2;!E`?>>== -MFF?1[CYI<"L:"9*ZYS9?[TXPY'3^O@CE0O&D!)'3KM)?=Q.71RQ1!L&&AI,< -MSC[[VH\!+[(R;PV^#"),)]"`VLC9@NFVZ7>C<IA\G'R:V9.R_UHA+V\VU(SF -MBC9V#3C[0+13262VVTL`D9A+`529%Y`O[-7R(+6O)6`$D[2QKQNZ\0[Y@M=S -MZ"XGS[[\K"?X;7X&T@O6-G!4]5?#R=UC&$RSQY*;X>G19=4D\OT)/@U8P5MR -M=[/>[:_]I66'W+VGHV`GH)I$_',U(`\RLD(EK4N!A;:NO#VWC_4N,Z<OU+NI -MJ0%7.@Z+>S]*83LGAZ7(BLTYPH8$6*&9P%<^DS;D6V7T:.<;/40^7RH+CREB -M2:$W[+R#:I>!2G=&N<P?^Z5Z(X#,-P!O>@#6N.<)NC0"(9))::H[R0C03L%I -M)A<L"%B+@FKU>@R0C#*:-ST*-0@3'ZH6R*N4L4^\5\OED]?9TJ'Y'L<6;'O< -MLG^%'`=$W#<^R",A=$LJ7=1"3KH]UZ"^>Z?V`-5UDL_M;R^R^"UCKH3#<F>[ -MV0HGI5CV"OWF'66V5YK)/?0#U^0]MA@7ET5S(`S='DOM'0UB_2;1A'4XJD@B -MT*N\X*$T\C!*VA!IC4WBL>M"UEP-_;S?%0J6?8(YK%IW_36Y`4J_D9G<PFD- -M-1#T/N0M5I^U];NI((MMH'A)L(QH%W2K1;P8+MWLW,84HHB!LJ;&N<13AQB< -MY-K#(Q?\2G>$E@Y*(F<(R?Y^H#GLEPZA_F,/#9#<U#4#N@?@/W:#RQ4<H6:S -MA:32R1>89QOR+C<DTQ>A_GH@[:7"1#(9:`3J91G!??HL/+CP2+U:W9PE80DY -M</6#?GKN)6GB0AE,__4L4K..$M/#(Y<=M++7ML-K%&+7`&U]GBODNT@,G6%7 -M!>O=$5$^ZIK<,MB5=5]L=)7D4"*DC&]<50;V%UKI4$U*K.Z77T!+>W<2$HG/ -M)M[?^Q@>\7-\$-B>!.#4^U^_>Z"C]N:.?,POL]ULLC>9--4;M<Y'XWV38KIU -M`WY7J0Q.`J6L*V,N8.'[8HYZ;9A/K`*:C`!.UG05@"N;)6V0:Y4-8Y._L7;: -M-05ZIOE^]<0,HHOYU]J/'.9`>VG?#`>*44E*EQ>Y\6.MMM8<K'U$$)F30*L+ -M@-,F&'>>^#S>35D](AA%V`M,QF,DBRTE\@ILWS46)?_(!L:!EB=BHIG@)+XJ -M`H%;T[K[QD'ZY2FI^QIG3,8#:%)HUU[Q*R:SM[<1$CS>1<:-WM=HH#I.W'/F -MSY[M,>F(-K2/SOB&M?`,:\GJS]A99R4$!IKX<J:[ILJ3T)QV4FD-F6<Y6W(I -MN^RBYE(P5MO7%.6-M6LF&&-WH'N&*LNW!,2I0W_^L]^ML&O/S"4=%67)KMF= -MK<61<^T?J1X[?FOM"V+,S_SC3NI0OW<W#7\#6=4$17-]7",2?:O**U/>0I$^ -M>Y5MO%>`XR&@HP9LIUB'!`W=JRR;;]+FP2+[*`4V2)X*2%/A8V,6F[,#-,-X -MUNI&EY&,_:?M1'FM!Y`?I(6A,)8):X`^5:Q9[\/)'$7#EB*4&:A-:XD#'F[F -M>:GT/XAH8=!_$8VZ::B5T(^J+%C3>];#?=%QZ>O(A8&TKGI?2->]<V@>O[2% -MBCU?4\X."`R7/3K7`N2+NJA=+\6AV]IH@0A=I4G,"2HLEQ5*+4PC0"E\2:-% -MQ:@X2?N@&'&CQ_OII(D'=`'7&"O+0T&=(?H)&64Y%7KQL@^^ZS8).TB;8:HN -M$K@2;R[EP%5[<8M::3#7#_2E)@XNPS"@N7;"PU7EC12^9?/-:+Z@F3))JO8' -MW^PGAX/)M&PX$%?#^M\V,%4$J)2EXE5,F61TOJ&Z"MIA3I+21MI8+/,WVT:, -M4U,-\6]14&&4,]='>T+;8UZ+:?B]#X8Q_YYQ2Q6L<_?ON%'<LD%7%`1G?P+, -MYQ%/<=4J=M>_3EU6Z4COD!7FHGZI*H7%FSC&)*QO.6AV6GYD=UE2B</HTY[4 -M9V]U2>A=J:JFFIOAM)O&RG68OA>4X2'4J21'28,VA="`J&G`L`''_RS0_;&- -MGQAXYB?'^_=*=BQY!IR\,AQ+?SM)3R'$^=DHWE23XGJ$7!*Y9(N>Y8RSY\2- -MW&4<P(/F;8394KC<+$@9[>'%#4,M^)-6,-$T,O9P*(8MF=%!??\T<G,J1Z<W -M:>(M_SL9^?OZ824<['@5H-5DBKM8]N4I*'NVW$3*(54R*""+5/6<3WGNN=6[ -M>7#7FX7TH,%54GCC1.)HJYG`Y-+\*#AJ(Y&A:&&C.F&D%VE1QJ4T^,]@F&1G -M;`<9'&#Y=MC%!O,NGNE`<_?EO577STG:Z9[^@7#5='Z4$3ZYB\SQA**X2ASN -M68;AUZYHBK&"PW4\3UP?CU$M)]=MW4>AD"/^UG[07EGT:F>'2D8`KS74"M[? -MZ\DL%4!5GLA0_#Y([??Q3LYWM\YX*6!&='$,O\"&*N1EQJ\F_D0VE)&"#"J+ -MZNITF6'WH1\E_\%B6=,&7ZT:&1U=V#)DHG1.H[NA[Q2)@B7<J'W7_\O.O:?X -MEZ"./DP-\\:#ARA@%0CR(NBM3V^?)T3Y>XJ\7A9#R4SN6R$1(#H:8MZD(M;S -MH3YI5$]L&4R!)Y=N12-[(Y*1"BW#FLERNJ-1NZ$PPEKT\&NXIE+B3](.7EO+ -M'R*^Z"R#/5L\AZ)GV#+;&1EXLXK=1ITQ'S9PXSL8Q!J8DH32W&_\5%+U0U\L -M#"-*U"!=W9J?OBODB41O;<MX*'MX`Q)M^VY:FR/U6?,=%:FJ+NOM+_3?25V0 -MU\&U09AN(+:12_R4@$NQ,9)5!#X80S%0I)-ZE^W?D*'\C6Z+WZO9E@.L^.W' -MW0=.<UEV+=(2V,V#GHA.!.Y.HS%Q#IZS!MTVBT"?Z(6RS69Q73#T^=V,O!DH -M`PAOFO!1'.TJI[=0I<$Y;GGS3.=_<0@N[_7/3FP-_<HCR":KL,8.GYU'H.7? -M#\Q6"@:YF"S]!W0R[:0;#L1#!8-VT([`<5R;P=EZ?L`C4F*\/<.."%QZ5N=A -M(^1'*/QC=U8\1Q>+8<4S`./IB\W:N'0!WK5%'7-Q?!G*:I\('SG`,:!D_AK; -M;]3$6>&R4DUBOSEC-H"%8B:T;$)@D#M$P3#ZB?9<TEG?*'*[3P?!W]8GX94F -MAH/$^7C]N0$\.!]]:\'N)PQQAJ3$Z*/=C!$P31&VL(CI@,6M9Y_[$B5%WET9 -MIV;)$7[:65(MP8,#A*W03<3&&T9I1IYA?*^SE+V4#?$LH.VU9$U>OH/A@PAO -M!?J75;``+*RG6X2*;JL@=WI>(_WL$DYNRN+X)D3.7*B<[TD+/[77?O*EZ7QC -M-!4A]R@&Z3Q_`YX64GZ;#,33,.?6:*:65>`(KSP4'<(*'^:Q/2;+;D;+>_5Y -MI3\\BP7F'8J[X_Y6>8(*V>O$BJ(U@ZO+<N7]L=K2TM2%:VX1T.;NFNH3^.;% -MI21W7?I^&@9V^_@-R/7OESYG(6.IQH4L4T%7K2*\4;J/,.[BEZ?>&67C4$S> -M0A8@JO8)&Y4B64[1P@P8D?]I:C18^F/O\:D^>>TH=I`'.6`\K#$Q_",O.ISB -M+"BKT2?+`XSJ'4!\_+"O?8ZB_^?7<%24:,)K&\6C7>)<76_.SELA#ZV$Z\$D -M+:`8MGW_W/'U<EC2/$C-?3OO7`@L1W?:9YN7C\BKPV`H'EW*IW'>][&R!Y9) -M<G50XN&]36#$2+,`<^Q8$"E'>$6>U_RA']J\+`Y30+%:-G9RPW?&1#4S<[3+ -MQW,_CY+))LE3^%IJ]SE$<!*O'-9&[?U-6;UR;?N&,*`F(.^/H,\UI-11_.KE -M,F_-RBSR\&<<9C%EQJ@*ABUA'GX36DZ0L^<;`7E6K85A2N[T&$?-"4UQ$$ET -M.X/,YHH2I`?PEG2V5X61!1Q`9EILBG`)1NEY&9=TXOTV<J.U>E2H[&82N`%* -M?K?]!"QH&HV#'"SW8Y=-%[>CG6O'V%"M.XLFLJ]PLSV05)ALT]3@Q.9RPE$? -M[WP3\H=<:5PU2Z]AOLII4-<%4UQP3K0IAQ98SX4>ZN!U8C62++0:?X2`5'>8 -M6RY))A49;3?9;X;:=D\$.F/64Z_>X`7R*FKB_)$ZU$ID/P&L_UV1\_/VDR46 -MA:5,GD=[QR9^.@F._A*RS_H5NN9P\:#T(BY;348QA(2\<^6G%G36(-3^YH?G -ML-.U3NOA-6;/N&+'3NV?H(H4H$"B8"6^-/OIVO.H<%A/I;IW!%Z[>&BR-6-J -MF]+TGD5J;E,^,KR%G+CZ^S._3<3J1E9F>AK&)9Y=#DB;@\6M&;3P?A2X3:@S -MP.4!N*A9>WY.W:9G;<LEVABF1L)N:_M.B6EHS2JG'W@\WMS"G/D1!Q4[+6=( -M>';1&V"VYF]-\_M1V$L>A'#A5_1W_JH%\\%"@L"VUJ]\!HU*I3J#<74UT*OX -MVLH^EDR#<4T\OS"%$D!Q?&EF!E:_5#=2?-YUY:_'$NZ#G:<<GP/N[*%R#=^S -ML*PSZG6MF^NIRFJ&!UJF+PIF^F*;5#_\"EOC'AHBJ=#+$F$L0W:[109F@[(M -M:DV\,7]1E9,Y\Y1@XL/00&>96Z?GN`.\50?ZP4Q1O=?0`S3S_'&]<3]A'QR1 -M!NV*:-9LS(D4\_5SG]G\_K<9YU;=F.T,0JZPE.4`SFADM!7H7'P(*JZ0YFO@ -MN='@-5EJ\_019!"I:8PLGDJ@\2\#.X7==PY:%8LS'R9L-<LRT1#1*1>F9M6\ -M"G\?T.X:DZM=1EI<.5-E_>!['J%>*%S-_K])EI<42[L!?N,'8(@$G'9(.Q:F -MX;(*#8B'89B2O:P/?2KB&\M->S;L?"5['+0_#A8PJ33<.95C%3C:E4<=2>1` -MXVO-T[/\L6D8H'U*PK@N4,T>"6:".W\LR82ZAJEX@7R/ANA-VRNX@Q[0M95D -M8-!]J"27<3U>]C"5.*)$]-VV7M^>6E_&Q<-<.6HHEC:%?EK[B0SV1,WQ&7<* -M3VH)=YN0NW=,OJ<1`6\B']=+7V(E4DZSAA;9.I2E98Z!:19/3;*/J]&:A0<" -MKQ7-:L-B^T5D96E9@Q*RY6V<S7.)3&*P*2E?CAC?!X(EFUN9RF7$TP/B/3MN -M&Q@LL"-NL$0;,Q]-=*R%*9'_VGB3D@6Y6QM!W$^2J#KR!6OI!"%.>'V!3@O` -M?VB=>_TY_'4%:![G@].98=':@)U[EC>66`,90-YP^FX9IHR,D,7U8+A'?I?2 -MU6[SG"!HZ53-#SEO..E];]P&1?*PX\^YC,U0$NXR1K=2$:(`Q+S=FC\1!-1H -MYAK_XTQC7OU,9V@ZSB*P5KY4E1&52=M8-'N%[8%JY^:H4:+4SHU.,PU]]LIO -MA5DR[/+NHE*<SU-(:H%O+LT-*X5.X?"&4(4S?',+N>,&A0'&TQ]*<8",*IXK -MS&49@WZB4J3*_W$__CB+EOQ]"!6]$2-T=-[*Q4SVFE1AST26FEW[!YT0+AQ9 -M3\S%I659Y-QXG[?BZ'C)_NY/C7(RJ;.`(4JQF%YY!;H8V7;.>'@FARV$ZU\N -M!E4/M6<>A2**A[.\E.VBFF/^B>6DZZ-`PY_VU[08[2("U7`#4CX8QV1RVZ"S -M\VT:?W2;6$HC<&8E[XWTG']5CN1PQ]5,46@]Y]!W4IAG2=9K=A4L52$->6!M -MCLS,+%*(*S^6Y2UOUOA/G4-&VY;:$IHPS%8$:G'VNI07C]/I$S@H>C/,@-5L -M?F:$\C.)Q9^DSI16U6DUP1(*12MRU<@9H0?':C2:#:H&\R-!SY)/OV//L:R\ -MAJY2%E>[&HC*DVZVS@UNF<Y?6;?*HNV+^;PMF/\D.8O"(B6B`@7XNA>-V`&] -MN!-)(,(KP:X[X_3\*>^D!J;:IZV'GU)8U#T&IQ-*\$CVQM>BCXCO[[_IXY*7 -M7V$^V"&Y>_HJ>*3=\T`<[,NL9LIK@#8=8?@(+VK5".IE.XDIKYAH1/3!QT6Q -MF8STG?>/NR3OZHVN3I^HC<H4?%[F^S,0"S84?,!;&R*79RF#F+W\M5>DRZ]@ -M-`RV\,1-4`<'"1GA]!<^6E0FG.[^B%:"TE1R1C"./&:XX[X9Y8\',)?!2-65 -MHTPNYJH1\)$DJG[RQ!,?$84>_JFWHS5G25[/.#05I@.PN:^F'=G/QZY&]GB) -M"^=2\(WD_2M3:0RC'-?'P(I)`=O+)3::!OM)I+`%GYFC0OT-X7GH)?/03Z*9 -MO-.%3:DN0UVI$TG,\./DC&4R3S\#&8(N8XIXB"P-7I1IFS9FB^4B+*_)`[WT -M7:./XKDDS`$54D"Q![])F_:;AC;:B7S>RI.]H0R3`_0.\\.1C';DOI(BETU[ -MTMEH04]Y9<SJLNQI!D8\)'^T0PMU8.]$U1XTR9TFZ!PN\*:D8'X-Z5=8^6I[ -MC7]Y&J!/ZEJGPG<[+.\(W?JGR3O-"]##UN0I7+!PF!',&VG1.[*G(+HX.G5W -M1IT_%I3DIDS"6T&YS#!TZ\]/RZ:=_<6!IB!D,,3TU*R)D,Z"1([Z%ABNAF(! -M#^UO\\4YBRK;.S(YFS]6G*#?:S?-"Q6/5^%^CN;4)';G$*WAQ1J!2Q#$\(Y3 -M2!N*8N^HQ%EVO,=*9)KG0<>?ZLC7;>%IZVRP9&NC:-J29#8Z8'"N2R;*N;]+ -M:!G:=M/5M`3<'">)CO1:W%'V>&=L:"J91O\TQS5,:XCLBT_XW..AD_=3V>NQ -M`?E'JMYE<\HR![N>IIT/?W-=R_G&I#S0#PT)D%B`EXO,>&D:4BX9'7!J,PEA -M8;X-S^L.6?8"YN3^7W:C"(L,>X\V(^HM!7SG08F#5#(=A[N';F&S85;,^WCU -M>`/#2:_`"^7&-46<>;$N:/>IZ[%Q\[\E)9#B=VR+7']<9XLHJ;(QY$@5"O"$ -M_$<F;'F^%[+'4#ZG(^[G,D.F3D9K0"5O3273S+O'#!(W;@I9TI991WO2<<9J -MA`1C%]AVC*2L>Q\@*-9^[19_JCA52?4I#<V_!1STJ>H:PU]LM#7O,D_".^9[ -M5/_?P]EVUEU6!5OUOHR2+VIS:1PF!D^;'9$@G*ET2\U3<3MR11!&'RME'EL" -M6F?Y(*S2XO3VO3@J9F;N(-(.[7`]6_Z))((K#96D0=V8ZCDA3&N!L^EV<Z@# -MH8,CIC1?-[I<VQ2L1I6G'+/%%;W=CFI$I@K&'?[V(,HZQH3X`MLQ:1ZSG#2C -M0I]4`J<[;&450P1V,7'%&M*ZNZ[@12DR*.5*O!Y;_6CC(>.#4>.FJ'VEE*?2 -MREJ61.3PEB>#8APQJ==!F].=$(7%@:&^$IUUX>8EF)/`5+]UJ]T%GQH5YI&P -MAF#Q3'T;^/$\3A+,SG8GNA[:LL(L#=#N;&U$Z,A_4LV]*K-,]\X`8TK5;)H% -M$[MLS'KTT]KZ(-B22FQ!2P^QH29G-L9=\!TUSD#FI!&TR+6B/E9L8AFO1.CB -M42%3:5H6G,0A,@T*9-[OYK:2M.-J'WR(`C#`@8CE635>7&$G7-B])KNZHD\A -M,9R,-N#:+#?PO19:"$?3I*)-*(74\2T/0!%T#\4X3JH/C=>VJNS@W\<R5>M2 -MTF+G<Z>L8:O;-5!>6K-6NF*_L?#S=*+YC;=Q:ME9GD_7,]##K'^Z']2APP\C -M@04C?<PEY-`6D?OS#/E%4MPBI?X80`M,HCG5&]@V[@^!"$/W]\!/3Q�JB -MZTZ1,;NF[9?"9@<,/6?=QXT;^.=SD3`VSFW9LR"C.WQ75N\HYB-DR^!;,@P7 -MN3QPO/2L"6%\"RQ:L?WT-<+,ITNH#U.>O*[0_*Y#5Z+.A&S&RO`P.2.\Q2`5 -M=%VL<]41RRLKDK5(\`V/Y7ZK1__O@+WET-:!L6^Y?.+UP:5,U977F';)35B" -M91><<`H:5^:VF:RS-8QN[N@LK)(8/KV2;%G/^-?'X3C+'7<)F3ZIQ1#^)F'A -MIW'+P`9UVOTIB/SGJ6TO22U;ZQY2;HZNE2`2YI4"G@H$M$E9P21-*J@ZQH2N -MPW8;,>9^[?ONGH#%.N3"(#)LQ+8ZK-?IAVC5O5]N(RUW?YU?$T;*;!EA,-4" -MW='@</<R>(6:MR:BK/!00W\,FK'668^*/11SI[=Q8VVU]I/AS6R*K]UU[RS( -MV0C)2J[OZVC*RHOD0+YY:NH6(07?1J6KU^-N)!K<I_"LB'\499[[L^%0HKU[ -M^P'\MYHEQ]?%+&([?DQ%766!U+9_W'[CX:(@D/^_CQ?7-)LJ?VB],H"RM^SE -M7731B?!+MT)!')_5#^>ZC$%^!2[2I0_<6&=XG).7?54GN.7M#6?=+U#(*E,O -MWPJSF39;698N2<Q^B4F*>7?[ML>=ES`:2^,Q=O"W14E?`_"+]=B==`I%>#SP -MH-^Q'XBJ=QA\V9.6FC,.C_T'4H)E3!`DXPZP?8DC:O_,]B5CK'_#&,T&8,'; -M%>"HV!!@]Y;.Y=/Z/"U9M$U'&_K&[IT9TJ6ES6O@FF?*`<U*(.@+$-S=*]R? -M9\=^]A[+PM>+\'CRX)<+\?N^8&BAVCQ9,:E.@BPX7IZXE%U.`M'[1C$5_<5Z -M:**MMA*,8N`)C\B(6"[A.>IFBZAJ3\()D\\VI5E^9Y97@"W^R2R>VN2!R-!] -M34Q6W`*IUV3QH[$X-'R@;S,5^(C.T<;'HWFZP]&<]K),;D#K1;$I,.)8!7&# -M/."5X.HD$G;_)A\GVNL<'NB>/HVPZ;3X.1ZDW8]!!Q?*E`'S@XW*(PK]D73) -M07@)M8*L@#!@1K=T'!%*[2LM\01/?@]UW:R022=.\+,)%'8>@+:SZ']B:4;5 -MR@M92A:7_.B^E7Y*/KML%B;.Q_3/.V00_"30BCU;`@:M,3&Q1RG5A3D_2>=! -MANJ<K&IO`J08%;81G"QI"^&3ZK!XFKRW[B,\],28HR6B&>F=L&H"?3Z0*7Q< -M*R?$:ZU?+DY4O%Y$<K/Z.+*@Z.A2I98GV3QQD(K[P&I]"#F2:%ZGPYMR61,[ -M),?9U`ZJ]V0&72V(?R,[72^MB$:"O@R-7]OE"MRDP?7\UV^U:_9F-F>V7PLA -M%:31!!VX<1B\V?E&R*F?[BL\B?;9S*9N3AM!.AKP%0NO,K/,%E&VJB.7WEN< -M1ODQ^%J(NQ+LZ37Y=T:0K6TH>\VK(X1661[XS5D8-3[U5#@8.,6GGS&3GSF& -M]87!J<&D.\'8\O4TN:(&:@N07LEPQV6<<6+A[SX@FW)E",[[W%D2Y+>Z>QK6 -MS+@G?29H\*L]=M4<$R'RW>8)IPS?[%!/R='ARV6]:B;L%T@_(\[2%*ZHV=9> -MYX5#;C)[KQ";Q=[?/E=B).ROQ-OW2$A"V34MJK0$2)=/B$M@$2(N271<'&8/ -M/!OVI:/\BLO$;(+UF]CO0]>_9;U:"Z^I\A9==]K)Q]A<%$K1P2B\75;,_<6H -M`'1G]/#!8\C"F3DJ=C^(*Q"ZTITB!1N-ZVH\M#>$Q)MD,WYJO>24<W1*(XS* -MF'9,K@29V%]]GQ1^2U<+W#-FZZ`N%VB]F1;\=O(83$;E#78BYC6KLXR4DH!Y -MS,=2OU+#MN]NJ4D5<-61`8.O@RZK(Q:LL'%"')MQ?/7\ODM2ML+,E<E\;_DT -M1Z+`VPC\Y>#^&.@HGB.RP#'(UU[0K[WWPX?24_OJ]3$C:*R+4`^'/#)*&_DC -MWKY<Y!Q29?<<8C#5L&/7!;\13EL7PM3"3^#_2X;P4*:)!=&!J#6>K"T#[V.T -MY,QSPD6C1*K).`)/P;/&#M@.(@^K;4FA22YK981X5@<U1OL\0'@F!#D"%8&, -M$#"&VK=QC4+OQ^^7PU3Q//VR/FQ:2SBOYXJ"*[$8:UW4:`=P3JXG.99!$$HT -MHXE,6Z0P#]E_Q!^KHO)N3D.D9]2;EC.O&1!^J?EW@5IZ5Q]CZ&\!-%-9NY9P -M7'&F@,3U.79Z5$=D<G.7&Z3G]E6Y@4T_?'8<@8"W#2S(]6O]>$=>VAX@N[NZ -M2NQ!TR$,P^Y8ZADZ>N@Z<2:WD'J)-C99IZCVBL1I/RD)(A3+N,*54KS"RTD< -M]A6X-:1@P$4(1%E2B6!-J=M`KL,V3<#WI:8N6!_":RU@')NMZ=BR\AN=RYS` -MDC@=_\!2W">Q.@TA!']#]^7LEA*"T.RYFT\!P'M8=(#8[Z"VGV6.H-W)Q$V+ -M<HG@<@J"ZZ`WA\8D(?BF,]@LM3*`VJS"T:`%?9-C(VTA&R_50]?=SKF,[F`O -M"*"R?$M!`'+"\^[T0$+KV^QF]$$0CF8]_E<M9Z6!H"*'H3O2W*95DN?='(@O -MN,KXF:.I38KU3S1$&@8@X@/XEO-F^<:318<&E=)3C+U\B*30E!*2H>HBMB:X -M\Z288+ZIV]V>LU/5+9$//2_@SVM=Y>UST(M#]O.\M0@.!SKRKG-?K,8&_0N? -M;$:S%0WR'@!&T#L0>C0!VB\^KH39\N5RO@!;_V(P=N^?XCO&_98\6O\^0Y82 -M5EM*H9]%2,/-'GM7%>2D7#"I!@+T"#0&[UYDL3`A]RI:=)1\ORT<.S+?"%=? -M1X;J-KR?TK]B\!=_FO(M8YS<YQ2%<V)C6*\]`CXI>[5E/)_8_7\-:F'""SYP -M)U![V!F^PPKD.>&*4[O:,5"OT')=UZ?C62FI*4U?#&`2_@U>KK:"GT/!.INN -MJLY.3'Z%<L%D4J1O)YD*_LG6'X="LW<>0RYW'XS!2@JC!,#\<J,\'7J#QL]X -M<L2(@F%VM_:8#2#][IOY@L/>)W2-Z_XA1SL53[ZFZ8UMDS+APY`;XLMI7Q8F -M2PD8@_9TX4_(`DZ-X:9`=82#K1K!8^FCKQHC2$.!AE'D4L(1-ZEEM+_`?IG@ -M`;YN-]=M+WSYY35Y$+X#1!9H[$;@QD)\;E5.;5-4+C>:7S,##-%OYOWLA)"_ -M<BX>23U0JNZ,"\ZPL]6<VD'86@N;(,B<S94"DRV6[@K*GG9X^V&$LQKVJ+K' -M#'4T#!<7I]'_Y:XH?'!G=H3Q'MILB^<E5,4TI"&^-Y=*&&-]V!(<29EW(H8X -MN[M&1&"D<\J[XD=[\4,GJ@=,EHPQP-`[0''?\#Q"E_!'S(Q^!HI:;X5_E2FL -M4F"+F8J(LS:JRV6-SYYD@LE]V)NQY.$F1;HTB3B!O`>HQDPV;7I=/-XASP2: -M4<T=Z["4^BB\V7Q*94/&P30;E)2+CO,VGZ'CBP4QV)Q#IE6'_E5*^+?/T%NR -M&^8$.U1*&0[G`FD:R%D29;,&/\HZ"`*5OWSNB)>FS`\P&:[A23S+]/O@V]Y7 -M+0\W(E?=&T3IC_QK^#-/X[+^;:(XR0O)DN8TN+X0CG>_:H%>^C5(>S7C?0JZ -M]LU,[.>Y;+"2:GYJ227P0.5"'D:[-VZNRRE*3,))37(RMA8G#1R-T7T=!`D7 -M)Y<R*ARY2V/K_FF^<=!\YQ(P)[+AC`>IHU7FD5<YN>J(A*1;LFCE$[FUA?C; -M9EF5KZMESOV>;B$7<?791I>HYEBX+OO`2$#[`I$B2.[=;>W'AAI4:%],K7Y2 -M<^!.VX`_-")B'&OM>_':]5VR.03>-##N9ON0WF@1[Q93+(JF0Y%^0O^U^S6S -M*V]=QB7]7@8L1>C)59[Y#^"B+)[8_@5O1;N!/G:51L3#J)CFDI#ZBTDXHB5] -MK3"67RM/F$&9,HB&![K\3<(0WY:O\GY`SOP+TBB6OG?]A]JQY_*4?1\*W->K -MRMP:#($,#:C1"!D=Y]X-\KSQ%89U_7]LON<];'I!L0E/!K>@IUY0S"KQQ5)L -M4_V:@RX)Z,6GSW?BP6Z^!(S,W_W<[S<FNV)#7E'1G1;=N0>:[A&N61T_P)5Y -MJE1/;/M&NFH^=>[]LA-Z%Y[1Z3CXM9(_00&\%SMR@?E<LI%+'!'(J)^<\9O` -MW4#0R7ORA*,1[9R1V6ME=Q#4!DHMTA6+>--]B5DHZ'"94HPPVQ[BIR92E*^< -M43X5=A2T*B<KH:,2^-7:X1>[[6]^6B:Y6+=Y\>#12%-15FYW*5Y+^#"01O?" -M6A6>#389L0<Y=IOTM2")3K+K<3]Y79`/)HXB<4J<M\VXS>!YA<&FNJY!L_`" -MG9TQ<N\I6&/5N41>]JU"QZ9$0D\[I1G1X9+%Q.OW4=\/ODXJ4>88V%W#U>,@ -MY41ECU^PNA!'2GEV1H`X/_BAKG-_?0H_:E8?HOTF5>$ZOYC@$CA=2^:G_U5D -MFN1_D4R,@@;2B1E1(*RMUV'D(PC#(RY/Y2%,M]%C<DVE%G="=[ES3NF_T59$ -M_TD3@!VCF[@%6LWM53DQ>GRU^W_-`4V;F6B9WI=N%Z)_#P$I/D1:W?ZJ%FI< -M\O9!G&%F-*OE>-KBT\('_L#["P'N]E*647.-OI*55%/VH]GRMF('%/C"]J6: -M(3W@TK<U=(TU$)#WHRG.3IUDLYDGXR?D6!GW+:!BU*O7L/Z26D.INU+'+X// -MQU9?XI]3[#2\)&Q,]+[`MY"/5#H<#R>V-$O$QS1\Z]O_<A*7*BA4_J&&BWH[ -MU"?!I@<&HY4(*Y<F8AJLI?]YMIDZ\\V5(\W"G&H!O*+.::=`_==02O5$V3BW -M$.@09ISL9^@W*@",'P*\OSVUO]=56CZ!1?(6.`/#SG2)1(=I8Y8QV?4A_].2 -M1Z//3&OS17P/VKO.S]V=K-CW[1B<%3FOIC&8FX>#!N:G%;QVNTP>=B;+9+R( -MT--A2DDH`=HDFL+S>S'9:U=:QJ><?^0T:>5)IW7>.>1^":NP)88%YP=IY`F2 -MB4GX6X?M!D^>AI6D0T2S`CY7M#J[TDZ62><+'-:D+N@'HT.;FC9UR"@27^JC -M]W(>?MIB(;>-^4.)-B6GKM+R[),#AYXKHM)PHB*CJ!%R)%W*"`8,;'&-O^^# -MDWW363H).%R_AYDSI*%8*K%*B1&7Q<'H:$CDAP+G>*[FA>D8_E#2?TV=9ZVR -MADI-IQ4P'Q+W(78W:<;.*A+CZ82:HH\T%"5_8K,$S:5^.S[+=G(SM7@]:1;# -M)[=54<[8+EOOT]NE6U/$4_5U]9#NJ.9*H*3[R&=FRIVK/QY:R,,R:5Z*G5WN -M6I_(Y!<6696SO1;,1:R^S:M)729`BA+SZ+)1'Y@VSQGIL02=68!\;/%SG#^+ -MX;OI?L7@5K8;24ME)!;IFVSZ79U6E_;)IO2,4JXQ#H;)+UCSE>-4"V+,89.U -M?%0F>?[CEEC[NQX'J.KSX$-`VP=%QRY-3%C"+@D\;F$J=WQX%:YEQ<P7AZ,M -MLZ8'_$ORJN,\S$*<X''=,`O8A/YRJG'YWQ?@88GX\R!:YM'X42T'B2=)'K-. -M*+_;%:)N1FFY5"MC06[^[];%`RT'.FGJF<W][BG;MV-_?4^C8")*IR#`>U&G -MP3_1K+,HV?LU']#I)HHF)GO92YJ:RL>XCVKUMBT_H3QNJ9^332,LG>*T[-_" -MJWC?[0,QV$C<@E3C-SJN$:O)+_O?=]A?#W+K$O)"Q(?.0<[:OHE0PT+73>)@ -M"WGL(R:.,%>IJ>PVPPZPN81CK`ZM_`HTM^'L7%8W3\VTIFRZPP"EXOMYW#C9 -M!#BQ246Q:"Z^N&S=/Q)R(V]T_0W]\>4`/^`TYOYKT<U'5@J_)D+-BZ^.>OA[ -MC#H:_SMTUP>_`B7GROE9(MOXTS?7>^D;Z/D3$+>JI('UYJ#Z*RXJJ5%5!0HG -MR-2C>*(:`P.0DA!.9MB"3U]^LQ]\]1493]PQ4,2SG$ZI@NI2!I)5F52R"370 -M_L13(H'>.0P"]3[4NHH"NUQ(+]9S2VW(0%@KN_O-RE!<?NBJW?18\XILVU/L -MUO$&\A-.$2O))]);&6]G*/]M2=X>=]*[+FI]HX5QJ03L[7.7/#S-,=HQNI'\ -MRE7Z$.8!"R7I6K?H9.!90R5T9;24H^38X9='V!"^+!5@3%,-Y9>)T+;AQ1[^ -M",FK\!+KGKF4+BZ.T&+E1Y4%WYRNFR,"QN#-LW*6)U>NY9V!R<8A=NU)Y[67 -M;]?-&YAF;=9(!R@_>;DND7@\B1CFF<<FEQ8YSXZ`Y"!!.[^]E?.N6T.NGBTL -M@C9`'8]-M,'0O\`1\_?%#M%\R*CD/>B=JG^*Y6*_*O9DNV"<I:WG81U)+74N -MQ7XG^Z-9MDF1FVFSDZ3<*ZZT)L*ATBX[8WQ_3""@V$H6<D)6#6A9<$G^30Z" -M0DJ035_W7?:[9G=6'IRY<=T=W*LKWR%!LK40U('>A2/-H%\2C*C!R]K`=5]> -MN"J$?C330VMB2G:?795%%L!;R_$%O;I'Q5<QB()RL@094:S3,0V95+X7,$4# -M%7-S-H5L$)XO]B#C,G>4?Z+6S-=6>1@(:-B"GX+78B-ISZ&IP4(51E+@$5I. -MWT*#0ZRT8$SJ@)PQ.)6@HNE;ODQ4C@9`B)_FZ9Q_Y\,[,J.B!?IL!_DN<7*D -MJOUP4!M[F4"EK@)D(KN@_T!<)\(+/KGC`<(56V.:[V^<,N3Q-^$R/"`L?9?U -MXFB8<1>-Q7`9(0*=_0<IX"96ETH72SPZ_RS&.89YEC1U(^[*U<:OPIQS<8F* -M#-M?[,!7+6X_BRL^RDOUFO"MPH**";Y.-3:D'6E3PUR["0U)]H@=H-?':>6= -MTGM_(SA+!:$I(8[?]L6Q63@"UF#5(XWM$%C,IP4Y;]@S5@Y3S=-'$0GB'.B? -MWQ+CS7OCL'W/MV7E6']C=([_SQC0ZW-MS5DV,\ST0SZ9=&/IQ].8YS6:0T0U -M*3T=@;J"I^U5&F2&*^Y7'`K'*1,A5:PMEG92O^*XLU8$<]T!2(HUY%06)I)& -M7,TN0P+*D:8($O;SI-"6+EY:@.#Q'W3CP_U`ETK[D3%R(=.HZW9C2HJ9)=Z@ -M^B_;!B<?2)U=U'GAY\7$XW]\/YCEE(9K#>Y/O5F#)1,%I2'1-1O$+-B11)>J -M+R@'LDK@Y_ZE"A*,H2S_V5Y(IEOEAQ@M;ROXS7G')"221L,BI*`6QVCWC`T. -MXA0^HI^=YA[^;'.6!T\VC87$I`^-15^]=84#,CKOV[%9.)/0Q`K5\!(E(GT2 -M9%XX,W&GSU0:B$J`FBPB/<$<!&5K"LRB[1?P]A)R</2$,=*;\M08CU)F$HF* -MJ3ANH@9[VH0=`75J84[RKB6\B.HJY(FO%>&G`;J$EY;Z];W(Y)_'U=8!?5FD -M6=0'0^L..T6VDI%H[S7<(NE9(/DV0XV3VNA/5\/%`ZO:&)(K>,88(W[_K*O> -M<JD#7=`CYV&%?3V@BAHQ4+\'+2XG,D-0ODKV)8!NBW6X4B\(__&)9"Q22-[W -M,UAD5+1>A2/'UYLK[X_[^*<U`.0'^_XR&>GV=W@P^U!DF%,M#0C9':>S`?PO -MZJK/4$':&B148:UOF9*6?;-5E_-58Y^&1RXW*-)#%<GS^>6F90W/"&^WXD]. -M:2OL]F+7S_"?TOG;D\#X?K70QJ\\83R*FOCUJ6^FCL]N1'TV/J]KUU)5X&DM -M^Y4P_'R#C+:;O$<1CA>4Y8Q.'Z=W.?L)%[HYJ"!$R2`6T<A^]E]7:0QKL.^" -M8^<G^TOO1P=7_BHR@B<6:/LR&7SZ,%N[Q*424F!?5P.9R2=)F#:TDOO<+K#U -M:=FZ0$;ESE%9*DP)HW*3C`?[:EGYG^(_%@QIR."<,7%;'YU-"J\6>-"W,:C7 -MWN*1M0.P3D(8UFG=I\6HG)$8+V+C7F'XVH)^XEI^)D"AI!]'A6B(U*\Y1Z(L -MP7@U+(3I:9ZMOTAG8A?(9H):193*M6^TL?'W'>USC(VO*J/89=<\\@Z9R58Z -MN16K_.A.`R+RMG[)<VX4HXF43,#!3U#$-<CK[VG;XH2DJZ*.$;&(4AB.\H?B -MWXCOIZCK$35GS]I+YF:D]IB#//N?##%>E(M8\@"@?J.\F\G?%S.MRD*0#=Y/ -M,([Q;:WF`E7D7`+E"0*26-;7U%@AMY(J>5T*!QJYW_XJX""4V^(`]AZ$DAK@ -MF`.E'(-<(.16>XE$V1H<&/YE>$W,/]E8Q)9A!,JYLI2"?/S^/A[@YD%;5@K8 -M[]O60`(#)^%C).[@OJL_%,W@+*S>-\D0.Q<D2%T.7DU)HYS80Y6W:=^0T.%: -MT+(0*E!=)_LBF6;;?ZS%<[6U0GM=`U'*4W<7Q:]MXK;R^[=41IM9C(S*M&#P -M5(\?0Y7.9OZ-^"`1E!B`T&&<<,<3\-HW?Q<+;ZY/6#^E>5=1XKX)[$SFL]I] -MB^DO*D&APRZWF"TV:KU6'6_.5E`E]T!=H98`%!]'[G#*T&S%%/U[][MBGZNK -MY/:(]L3OU"#/!E./M:N'J9'+''CB:1G,$AQOM;.[9DL")S^*$$:.9X1$XAW, -MJ"GEIFW2";NJ?`U($DJ?`L#5E1_-3NO;GXD9"N.=J;1[W/N@Y`#&3Z`1]DAL -MFP'VE;E^D,(AZ:\Y`N60NB;FFH@`6W&@BN9:+?[NJQ9RG#O(W14A,0-3Q*GA -MX1%O]I#-_<'[0?XS!EGB<FLMWDP#`:=M$*XS<<7.GJ$M]70,^Y\N&J`U`[QQ -M%KCF!>%5-F_?7Z!^V^=5'\QDTR(5[\B"]N0B['5E%XT\+RL,:]=MCE_>W^'` -M[2/4EQ<#CRRSJ?VX\1H!_&9BH;7AIQ_RI&ZM'8BMM;BOHAE!U&HX;145<"[9 -MUH]:[4+\^S-U"^,(0+U@-.<*L52")%!X\?U\VZ*#\(2T@K/_?$C#-_*($(AC -MBR@UY+1V<U.H_!DI692?ICZ98]FC;&,_AO3!8S204$68K+0#OF?SA*\ZRP6. -MV!8$#`HXWE2_#O@+N=><!97QV>5]F9$#T+3E"WS.G7ZGK_%0L[)VADS%:%V@ -M,I._<&:YFOR4#P*"*WT[N:RBT`VA6'FU&8@T6UU<B5#`JNG-6B,F;L./+0A" -M/!A`&FK."U)^)F0?<LOO<MOXWNF5,O8FC248UX]"<07O;RPFZ8U%!0H9='LO -M$8-.-`4.QB>*E-^&W_.1?"(#9AT0,]^=V^_J+4W!U6%\[,*4MU'-12.`,=`= -MO&YYV8%84/DO8$6L!-CY@&BY07TX==D>ZF#>?#R"=0ZC[)P1&T6+(B2@7ZGX -M]AIMQQ!95_+O71G>,(#!0!J:&YE8V!Z-B280@W-W_B0D@IRH(%1B<^"W!>U_ -MVA_OQM3?#_W29"=RF.W@FVXE5_V(9\`L=0L$NCQ&D-=,[PE$85E+ESO+@<.J -M!:^#+,<W23$#,R2.$F;96W+X\BP:<];5%8!I,:X!#A26-^6BH)"X<:%.'EGI -M;:+.<;?<UFX?X>LBQU;=&=`!'UN/NJ*A%@<C`]II^^LF.#:\7D1LAG7`,K^S -M/=TZ`WR8U#C'_4VAFH[H,U%`[5)]/$ZRV%9C._DH\9UB*A>9+`7]KV9,-$_- -MQ./-P#Z&W@,7W;N)G'FS5'-_D0[AP,[,9KA-BE&H.ZBKKB9AE6)IX^S!8@\= -MQVAA]AD^)+C/5?6;=?<0*X=I`KJV:3B]FP.KVI&!GF:[Q[@)8#H-A4>]5M)+ -M47Z7[@4=?A+H%9OWORZL5!6S0XN2-^X4)VSHY,].\M:J)9%]#_(@1D%7B<UX -M--'P\4)'/);DY28/O1I623:UIH.*`YXN4XKA;="!7FU>S%!@7K/B"6E7?7P* -M6CY['4,N\M39@WN]DZF6,1^*]`FZ>F`F739L9CCSJ_MFMF,A;]A4!A8-/?^J -MB1!WU$:-1[UW>3%38_VY7R$IP(27LVTHXC?^&O;UCCYD":`XP"35T$K0+=&( -M3%:C$?]"DXQG-=T<K7$3_P2F/=7?S`H>%]L&"NOV/#M>FGD_@23B6EP;:6[T -M>%.]6,`'!(^M>:A<7\)GMG[!8450Q0D9ND/E\[U@IQ-H7-9X%\W,.E;*5&G^ -MR:3B(XYD#6UU!\](S5NH<WC"-&3/-@CQ_TY;Q!OS$9+_\,"H,UMZ-V[.B29< -MD/$$S1KA0'[6]#,P=X1?F`N]\<U"ZK^$1;+/MFOW7B$U:-@XHGK4HQ$=$=,@ -M_,*//_<H2CS[6"^!O30M^-B`S:>M2]W0SU?$PYP!S#,NV7";"&#<JK8^P,F- -M=)_%$[7GW2SM@?DAUTG33"!BI2;P.@FHC%CEXDLY6JW'])]XPH17Z"2OUIMI -M!T-P`=.@FEI"YLVDWO_BZ)C]>0"&_!(QL^.(H3^MYSG'?JF2O0<,VIA#-.]@ -M63-R+#".9$OY'OCF48'7WAL.YUY`YB0ML.3(@SD#D>HTU_WWG4!S_=O-+M(& -MVY_[TK'?80K6MH[&"QY3<*A@<-03`QZ#V0Z!H,'E]WMN-VHK/'L\XL<H6^$O -M6F35(?#1(`R<_X-@D+``F6!?`Z7BN$W+&APH]SPQ^)5:68^FI#`X?4?L3W4. -M6%E7&8VH!3(?U3FO.GB=X^(9E[Z"$T^XECV8PB+7>]S3!RKW&M2C^ZVA>[D] -MM.7J\(9;(2D=\8<.-2GOC33GYT-7,&G%;/X\L@G5-'E=^/HQ+U['B5EHH=)P -M]G/#8P1+S,&FBZ<3"MA)HJ.CU9@R@I7?/7XYRG$OV'&5+86"G0(`*';VT]QW -MN9**GP1RP2"[LC'-+OMN'/O2OS3VPF?=)[%5P9G$AT&)8N=+J8J'DF($M&T, -M"_7\(O#WG._NOG[6A`/@AB!'/>>"G-#+T(:-UD27V*FV(^_/#<.+1H(PM!J_ -M/B^6D6>C-_2><,)>U':E$JA27TSZR\P.W_Y\S=4%_U&?R\&\I(>(%3\<\9'; -M_KL4!ZO7'?<\U=!1PB]F_C!'U\?F&(*IK'[#)SC#I5QR[+#(.&K8\TD$]Y)2 -M/*$O5,(#Y$$9&^OV-0P]Z*4K8P=N:EPKM>A1K?3_R._+`8O:#'8E`KUV'>.L -M<;"Z9$\V:*YS1>CXZ2M)[P'7ZUIA+?&MT/M2*8JD3YC53]\W2=;[:4"^.\R9 -M77[NFA*H39'UV[2_3>/.H*UEPF.9XV8_9KJ7`MG*QV4>'Y8E\ZW569RSD[O; -M`(02H.!^`?I!J8B`3&ODME/V$KSRSFXY)RB/:JA-$&3M+%+JI/C`"OYQ#"ZR -M?'DR./?\T""$3H'JB;1IV=2F(U_8B"+UQN4G>=VK,8&EK*1854LNB9E',:WM -M]ZEH.^&B7KP,PDA)#>T7<8+!KUG0Q3!V?5%L04=/\TEZ79RT=U!C8(O>+2XJ -M$F@%!)M*NZ%XB8E]3RL=9U!?W9>T^<7RL:S[QM,B\=.3$-M-,`GF?G\ITAF< -M:)(%47T&CD>D/=:NYORN]`=VJ=/"CY.9]9Y#Q`\<G2`/W9(;S#A[J==&*>"[ -MD*O<%G`;X9VQR0=!67L>>M7.B-.%EE%>ERBKD"<>^T'E=L#/(-F`-^?E]X3U -M3SE]4NZ<QP/8=BEFAZ&*3_Q'N"7]R_;#7CL/.EI3V6[)VM:M?(8-AO"<5+@Y -MJN4W[$=!.*67"Y0=W_R\90L=TS&[R:<J,4JJ.W!(8=E7>Y&$O54-/B[.2M*$ -M-$>-!UBY;ID<E(9=+DGCN['[L5^1FS9O#*'D3:Y+MTH%%E5&U#@?<2"]\]`! -MEW,3/*72=;<&DA;DPXV7.!#4#4FHLP:=DI>!8JM5JDTAK(7L<-BJWF!VG=YD -M*9.&,_)0$D5\1<&(,JYJ[[5_O$_?[*Q@:]4(:8D<$65QANG.B/!3M)NYZ<G) -M?DVYFRD+@FJA#807R-:E]^FRV!PST99-0O0O\42&EJPL#Y\]JPQG*0OLG*^\ -MV'\57Z`T)8`S#M:ER25<5?`A8;PF9M:3&I,M[/G3(BB8_[YUICE^USZ#]!-I -M_%W3#VW_F.46FP.231/2<;D?4*:-Z_N%^..3<>57=TA598NS^#_60J!]]??X -M5P@3SLFX_)//[A'QY0=&_?G(*(FY^C$G"$[Z/:,_39?Y7"'.+',Z[+L/-^;$ -M=O;L5\82BEW[0&<[,I##(Z;8"K%;'&60X&O>53E\7BTG!A+@TV?.V/4T3X+) -M;BKSZS:<`;O$CMM3C,N@*\(VE-B>]`0)7\,9=X8^W1PT\QF9P&J42YGQ[J.# -MKOV[Z]C`U(DJILZ\-)CTTJ#)"7\6K8G5'5$5SZXDW4DXY)JV>W2!<<;T@$:? -M,'A=/\9-GU];Q&(XYOG?SJE]HQ353V@QG-WP#=!3*C=>+67U"B2:8\F_N7ID -M8;RFN;`^=M'T[_(C?IM.I:>R:(MR!<I+14Z/1GX90=XS;\ITT>;@+Z+QG5@` -M2"G"=//C^%C"D\^W1E:75S+H*3,GCU>^W>P0;&7BWEM0%Y>SM*)'K_6>[XE4 -MG^`9J5I!&DI>29K9.;D0EC'+D"@C%TI4#RTMB%H,NVN$Q4J;%:>>I"GC#:>< -M:^JK$O-(U)`W?:,L?5@0\J'UY$(?&I>2#11:"`'Q&?[!4+`%]?,=%/^2'KJ4 -M(FAUR57H2!/PH7I$HV@9)FK)<RSKBIQ3@FBP\E@[5+=Y:OJUN!DL#,E5)&!4 -M_RIMZ+8?![)R00@7$.BJTG24Q_;R,]*4'F3.H.UA#")`:QJ@*]7PE8^N!2)E -M2_.S=NBZ6%=*"L<%?N^%,A%F[%$L%T3<OH'",5<9*QQS*2$:0,*QYMYJ7MO= -MXNL>1_`2C`@-/`@=L`FHM0"R\B;J9_PA>U4%`>DN3#WR,M3V[2E+XZ,DN^[0 -MV2;%M=^.1A%0;Z.7%>.JB0W1]?5V!2G@*UCV9XHJH$8.XNW<(O`9RA\AU/F+ -M]R6$IVKB&-CN3=DL9<C70*!D1#:=V](WU!.IY9U3><YX>A6(5022%(STL2=H -ME<N:AY4/OR!T579`BMAX>7O8I>>]-3]IYD+G09K,JZTEQ7MH-0\LPN<78SE= -M8`J9)L3+N9G*--`:7,P.I:B-8PWMCDKZ4#)WE(FK5X<M,5L)6E'B>HG)HXH3 -MC+P<N4I.\UTMOD?0O3E&;^FL@NZ7+?B!;PD1#9?YWK1(!9AIK>-;OY=%M/?* -M,4"ER?VIUVQ4)K)15@YW@FZUED\'7A6E/^#KOD7=>5O\R,1`],$4/[5=E82M -M.C`^HB^S.@>;O>9R.(Z^SP#^,3]E,HJP_Z'<UTQ+#*W5^U]),BM"FB<GS:07 -M*"+7\KOOO!(3N#M+[CP(,>-S1%[!]Y=-SA]X+XB",?-3=*11<]:__<Z6&21M -MTE9MC4[[HB>$UOUW9B(X*%^EO=4,[!3!&Y?(<GE?)K$DFU8TL3K<<'A?8GPJ -M-0R_PV\N8\@,L#@ECZE?F;%M2HPJL_89P::)ZI?-&UAP!.BUCU1)UGAR_KPA -MT%I/^'ZA>F>!5*KPY-LS"Q5MT[)FSCZ$XE0(WO6[FGR*MR0YW&C;T,H2K?X9 -M'J\!C[>,!,[S*:@B&VR;-7%`V7_8XQKJ93FG)]Z;ZG"RK&T,Q)_N==IB=>JI -MRFWG'6_3-:N?O@`_D@*;\%1@5VAF\*,ZQWM*PQAZMCMB,?;,N@4UI#1'B$CW -MT$ZPX8%"[=UE(:GL-$IRAA?&;A$3B_!E_R;)7U[K2TJ2#OAY).W4OP%HXHK[ -M1#!I*V:GL-2"DZSD-)P";+UW\P(@0B:DJ[#&D$@I,TNE`HL&Y"+*[O$OUY_K -M=/8A6QIIZX^HKUBH7>]+".=`S'$(\7<P`[AN2[*[6V?:#';!8<&9])6-IO(W -M4UL$U>2%34HGD[B4;5K3O(;!GO0E\8I-SNQ^F&FO@J0MT,FZ/;362U^"/0%Z -MU>T#*:B5X]4<(A=%AT08%'H.P1[\T_QL@TUS9@^1EF;)Q)UFMMN?=B33S:-M -MNU<DTU[^H;W("K3M.,.N2)C#UG+#6W(]0$-TS!OQ,.U/4"TPE5V;2@?.U4Q1 -M^Y@K.D[DT56<OZ\6+:HW4):A+M9(I)S^C_$#O>,AK>E;YJLSKV`)[S4TEZ36 -MR4A1U^\^:TP$*MW$)L<$\W>Q`?E),O3MZ76YY`T"K7V25@^"A>UQ"H\P[]VQ -M'>UZ98I;]%/@F``WO`J`TMAC-]\5:ERE+LTLHM@U4S:;AKX,`;8O_OL52<1B -M<8W`E;S65N'^BR\.O=\H8_/?!I)=>`_+$`TRCZ::#BD(<6B2>6F4IY&C[BJ: -MZL$3J<3X'B]T#\[@'HH7H<@Q-ONM`1YVHRZ^HO0W_C.+LAD'E0C$XR>Q#I-< -M9750\QN.,W3G-D12.[:8Z^A+>L1@B92.OD9VC_?0P:",[O2[^IO0&/G2ANRD -MBG'M!2U<X/<8I)%!V><K[-LOL8;BY2&Y1+=?S;+'K576;P9GG@%.3E$GO4TS -MT>F\<?P*MY>WH4.?PE*E4`+$S("Y4!M(O?JQLWE-N4==254UTWZWC_MS`*O% -MD[)%P@`;B&=%4#6,R@%J\V5L_#@F3PNS"X%]D\$AJ%[5X\-6-G4>0EV80CXQ -MK]=:+!IVEYC.YS#?Q>S2RO'*[?+/`MCPPU./FC8)X)JC>AT/FQBR7;,A='\V -MVJ()]!"+:CV4!/$Y-K:P4*1"04[/SR(/RH>"F^YU2Z^:T1[9%]D%Z5<&8!P) -MBI&<\=JF'YW\0)J"3I+UR.M%B-<EZN73QZK#N3AD&K`RN?)PF*1=0:U=F!,! -M@<(&B7G+?RFX7?%/@6B0'](\X.26-YY:9^W.O)\=.ZNB,C5(M=<1[\M?36)T -M]"<F=85,'@7^+E3F!@?:/R*/37T9"D%P:;H:=1?8>AK:\]^;-S#^!(WCOQ[\ -MQ/JWY`=_#F:U/8EO(LRBC],WZ*]GFPBS.,*NSAW2@L/2T(CWGZSQ84H5#=6Y -MPCO'VHN+?=<(AU!@+J3L42<FY9Q!!KP[BY$GI3<CS5T0[O)5_L?Y(AGF''TM -MF]H<4,F9KC6"KLVA:C:$RJ&3`#>(3<;D:'FI7PZ-($CZXK?(M2J;N=+(V]0B -ME5=AB2VK/;B\.C/T]]"T#DPVR9[=Z[7/5#EYD#J<]<%O.:9OED.N&L?H9XP4 -METB`N4@CJRU2ZH]X[TG>R\SY<]M[L.R]O^UB.GZ&M\3TZ'E2(4-C1O:A*`,1 -M44C+G/?G8N8W!0F$$9_J,PW-R8UPM\N+STX1N`70A%6[%M:/N<QUQV%^`%2! -M<M'ENZ*+:T]AFP+.>W#]/3.(I/`3WQFH`7B3=$R\T,9F1"M)8C5NJL-GOTTZ -MHM+M5K][2B(-K]\IKF$VH/)&2*?I(H(E!/R6B(L_,J;2WV(D3;DTZX8R]\M" -M2/M:8MJ"[-@;BR"HH;SDS&WJ7=A-VIS9=0/H1,]'QZWT!G/IPP?V>-B,2$KL -M3)MR@K3;BET&'G)G9KC/595+GYN'Z;3JMPX-ZW<OCC6-LQUR[WX9LSW7^QSA -MB!L)G7:^T9VEO6[WC]2ES*>FZG!`U_.^-25C8DE/\J7TR.+^VA,$:B"E][6S -M,-KN);=USVNKCX^T\HWSYKR?6I3YC'^&,?#CL]M(+BQ%)>CR)E)X.IGS(K## -M,,WC#8>,O*P-6Q3?UU^(22&>1MM.U$E10B22_1CW_4TT%YY#J0WI9^CBN_/" -M:B*$R'OV:O@SC8E>@>`.8O9-ZF(_3:DK#@[ZPE2BC$A,PT@TX!_M(RN=!"I^ -MSN*U3_"L":<6G^XVX_J\&N.R"`%&JKF7]K$O)[3S6R_*,-I=MJ3[J:T\ZH-( -M_MS.E-[A%I44U,%2T/1/L]T$HSAK>,`8M\&.,DS=%,LX$\1:G[I\+@^>62J! -M'VTXDXE\R6^"SW7"312`J.6+_V[/SK#"6"-A<^\<RB]E(V6&?L`("HL1Y@:6 -M<:AO2JQZF15RV2OZ4IRDK<<-.W[%5`>)7@","G9(^!Z`A'QJF0]?YZ&J9<K5 -M6<[U(=Y=JQ=#7-K<^O-F/]BF\D[6?@"9&U!DV6:@X/1X4Q^N,&\PFO[K4JOZ -M2QS)-;MB=.WZ85C_G"'<Q1D<KMEAH8OH.WPZARSV675A#@GB)(,#/1RKFT[" -MGHEL$KB\&VKWDSXDO2$B%+B&\J'8KI'7C\]&58D9534/$AS-8@/GA]N%KP>3 -MDP&#ML,HX/`#[_+W>_TF<V)J3VOY"@*Q`B-)!BW5/]E6:5_4P>$Z9P5G]$`/ -M+4#^#JM).YMA<:.*J'D$J?ONKF<DA"C!=^NCTIY@.+>??*<7L2QT!..'D1B& -MI*LZ-@G_N;/@"RQ%198\^25R4\B-TE_Q5\U7`)YW%;_6]U-U)??57HP4WX&D -M!O6=`!]?]FMKB3J5;9)TVG-).]TTR:<\7]?,<<EGP>3&S-/VWH^^;"`AZ`>? -M4M\7!$?7GRKZO<[R0NB8:EX69@M&WXA8?E>)C!&M5.^!H<_L1V,JD91>"L7$ -M,I]5``LA0<!MA(>/T40IO9EAWII&4"ZM^>S\"V!>\EBL4\\DZK.?V7,9A$+5 -M6-?\0W@/UM!7`163NV[Y,QKS*/KO!('R9W)H[`A&)SHG_TM[&`A)6R\:Y>]= -MB!25:/9M5.I2F[3738,_N3D_!Z#Y9?`J-#ZRO!0'43J*#3*FFA>OZPD!WL[? -M\#?3G[`*/0`^52;!J":^],.$_..5MBEA.P/(`]$=F[-N-D10<B.$I;79JG%2 -M.RQP6)07FM=KCJ#3#T1O,YVP'.?.2F!*K:=:=:)!C?`7X)@%JK$:^<4AGPQ2 -MG.TL4F,9&7R7^<J>I@"_?""V2M0?E6K$)'V`[8"L4_^[;0O=S?*R"%5;-+%% -MQ5E^UWM`N:-(B\*L\=#Q_T@4_[Y1&Q.7I^FY*!(4M*6A%U57$8DPM$>'Y)=^ -M.?<^1"\$&!&JM^4&KC7""5+1J&V5O*\<F#&,&DXH*[N<]$1O!1F$80[7G8@M -M"'(UE]G6O^A$1@4<<@FL[[GC=6(</(V>TQRH/_%J.9?@)?S+)1*W9INP5R>V -MX\:/!V811YV@ZI&T<";%U;#;/4%G!5%F4;$M^'^.-J8!SR;!/9WM%NT[A"18 -M6`/\X((+B2`6S.*E![/4&MPN:#9I[DG#(Q^D0%Z;YLQA1`6C-VY[-YS:I>/K -M+0\*QIPWBVQG"K?D^J]^-\"([N5.$OS?I6I$WFL0_P^#J^<K4M@'CEQ/(BFR -MK=X;X%8/QV_-J_>.'=E!3H:UBB-K;GP3&B8MFR]5<J8M5.,BBH;WIM]R#63\ -M`,WZ46PN-%FB+3%4QI1]WSDR"FCH<X,_+E<_>N*Q_H!6ZU(7@8:>Y!D`VWQ_ -M<&7@@)3DKDBAAM3]1)FJ\Y!Y;@#3]Q5N]+#WUDS7K9YCR)R"(X7<?QTVYXC8 -M(R-2JDD!%9-,`XK%68:!H-A8L8=%%AP\%AAOK?4ZH#KIIRY$B&L47#YC(K.+ -MZKP<'0#%+]JUQ]7&ZGEYA)S5SZ--SB&HW_E+"`_[72?0H<=,X=S\<*KGH3,J -MK0I$U^]PDY8,`[G)C@,P?L%2]6+6%!+Q)7?CIW?K2.\#.4V+KF9\F0YVL4B# -M.:(1-1CF0,X:=UP52N[.>@8!M6G$\K<>L+R]-IIUC&/J5=4F#YJG@<E[5)+] -MG+@#\<+2L0B5!F%5WBH;M$-7'TBV@**)A69I*^!UEF+$+/:S_I>EF\=,;;X` -M-3;%-8;PD:*T83G;2;DAP2S.8X9815&@R_`B<&=Y/G)T[-%RKL.JY&\/6V_N -MN&D7%B2S,7!,3W?-5DIT"`$<$V.4!;+3MDIK!NXF:':@:XHK]@!-0PLGUA=5 -M/6UV1@6F@>!>^O[^,,.,-(Z][-XSU,M_JMJ($FL!8WJ3@DQ=YR.->CTJP7`2 -M\^G`32I:8AO(?S7IO&8='X!IH/_7;!V%YPA452Q_LJ=AC?,?-,\?%\<`'&[Z -M5<X>?'E8=O&Q?B1TM0CYR.\+24$SX=%%-X!.C9)/N?W54ELW&UO96.EX-Y0[ -MI\46:ZQWV9B%PCM^OG:KL0J@RN57,.[+^LD<>T/NME.H[:$_>A<L1P#?;*X_ -MW3&YY3Q[R4;*$F$-Z6+P+4Q"=`>=?S?:!",QYV+.KG?U].40BE\\Y4RE;3*/ -M#N/ZFU,'):10@3C\65\?IQU12CI\@N2+;DV@R"*3<'--7B\R3[EX9IT31Q3R -M:70RL/N<O7*R?`U9V\RNVP=D(27H8+FF1K0O=3MR!.M@X#0>DK"&RNMAXV(" -MZ<-Z@F<0(E%@[IE/6#*X>C[P-IE#QONK>M*"&7Z3J'E9O=K^BW<M</M]1R=. -M-L1>C!>K7#,.D#5D5Y"95I`D-I:1(NC6L2*E(5DN_R_MS/0U9&QPWFG)G`#P -M:T]5IS5RB.+\IAYS0XX&P5B3*452W)K21`10(J(]3P.MON5-1B#JZ"*#>L?` -M'WH9E%*I7#H,[4*W9,,'W6T*[+NEBN`!!&T`Q.&3^H:1]HL[F%.DGH>[T>'/ -M:QV->LMHX>;U.CDN1U8I&>HYZAIV<>3\*'@0D:G96FP$4GJ8O'*]AA-N'##W -M+8Q`9E<NP'#]#ZX&TO;KPW*C<0[>DFL/O<6L=Y(UOY0PZSI]A@104,Y]#;?" -M$NMLAOF33.K6M'H=;=3QL"!+?NS9G/G5O/F7?$B$.++O&O)LEU/>QS[*ZD': -ME*$KFX-=2].4J*J)FU3@$'W>%]4PQ_:B_2#:%[%KP=&G43BYX_NZ;'<K,&L9 -MAW-@PZETR51,WAMRU6J05)N&(58]_)K(`UG8R1I-K#T%XVE+R0W-N90YX.Q` -MX[^-`8@=9$,;O*,"QM<1E;)(?[IEW7*[(`<-=1FT2&&5W61#NASD$13.X+%3 -MNH!>E1BG/`6KW;_9E/CT>;8%N/5HYS]I(+B63C/Z$.#\#6\>:]F$VS1.;;B. -M!6B5Z,Z*?0MO3[-?''WY.Q7J645#D!O>_^\/B$E3+TFF%;V"=)!>-;Y<^:J+ -MUB&^B5.K2P!*Y&WQRG$,3T5T(_O:[MKOV&_K@W(?.GPE?E3S;8;5J/$+_)6\ -M8^P=L>YEO@Q@GJ],EPR.._N?;7?T+Y<OQ_I'5[>R35#K2U/OJ.@G!2O%N#E[ -M-CQN8[?I"C(O[B<W+5S4[5IFO,XG>R0W,;F-#8"V`1C!0I<G+TH6D\43I+;I -MSD%M#=+P]JKB(3>**#YQG?!S96K?8&;VW79E&UVSDO3UJC8H[,K_J:0G<9BN -MGR+SN<+>D-O-/V(,-2Q&>5;0OIA("W!W6/*3*#CV')^?A\-!-*#B+^QC!T<H -MXK]MJ5?=OH_.(@`/=*FJ'$A'+QAMX.?5@%E,12ZUTP:T4S33EBU4%B9>32^% -M-^^AQH'D@6>QWM6Z)\!7%?FFG<O)F#^\1D[0A$DUC9=,OEZ^<:G2K%<&D+?M -MMEWZ\MK4;RREHIQRY>&BVQH8'>QBGIW0TI`#$*(!KE?R%G^S#Z'G6,T/8`U6 -M^ZGLWG4<]5DIUM6D4^3R.OI<L=LCQ7TNFP-+_$X_,[8$)+A:E-W\]<-17R.Y -M61'8;]7(T`T"T3O%>OOT,)(=(C?IE_B5`-4CP4(S0.#ZU,$EW`QIG%%N[16@ -MC2D^W'5ZO7ZLGST37!A#A29!]1/_WS^M2%AN.T*^)8Z)VY&%$A1MVF)@OS;5 -M?$8A%94<?4,W/PT%HBA\!!==US=9>QP07U%8?E^`BK6!LT&61R]NF5A2,4Q< -MJD4>H&3PH*J[;<&WA(6EWA!,Q#`8CY["\D9F`XUQ&,D$M,1/M$]K:'G:4S@7 -MI7QT.'I.D;F(&&],_')#C7-.`5FU4P9&]"V!BMG_&,7DT5:MKDJ9L#]Y]L#. -MNPQ]7Y5#FS+6#[+K:3M)_B#A#?7+VY`VEXWWR%&>8=Y-(?MXK1-KU6*&U?J^ -M?!>L&E<.4)9=VZ/Q_6NY4S;70979!+[@C(H9QYH""ZO,#GP+LD-PP'3*>K;% -MD&4_G+-IV78$1TW&Y#1R1ROEM,-4#ZQ8T>\-,H;=P1(#V>*5;@&9D[)(4?V/ -ME02\2T"0@[B]*?\PTK'M)"#OE4L.1\];@5YZ?'G44N4,7;]E.(H`)8\C\&Q_ -MKOUCT!Y?-K-&>`RGU4Y&%>`I%DC"<4#05E-+^^Q>/5HMH4LAX])^M8\P"#[R -MN!.5"*&K48=DNF,2@34*FN1(IEP#W:(8S3#*E%.WTAUP/H!A`'X`]BUHA,!% -M<+O"05/'@DY.!Q6#[UTF2OE`$0*^A%(0!]\AU<3]->?(_]M&XUY<T9T_5,I; -MC,F7'IDADYIN)??P`AK4+9_-0*Y`@.DC>3/-D3N%'3@$B-B$[888)CO%PC=? -M<+D5_U57<_[,$H/<KGVEBQ`6160!G2(BH.D^BGDQAT<<G9AU?_J7.%3L>(ZP -MM`.V(J6*C*!Z(B`;("FF$EF)1TE;\@](SP)[0JW>8KG;X5<N^[G5>1X0];$G -METI8+;!U0J(PV.,6'L>W-V%SWNQS/\M:X3/PO?A>L\CZ'K'N_*\M>==RG_GU -MH.:;R:2[O-'+AAG@KD[&PX2!^&R,KJ:_!=+,NDK*-]-;>?!>7=OE$1DX+K59 -M\1O`M2JHR]@9(&%LO@R"A[$OSI0JP5V`40HW(]B*K]&,>7J2>4U7GP=[]PN< -M]'P^T9O^;FP"$8Z6E8?J;O7&3N3!BESQ1X$8=BM\I",#7):`?=-G+4DMKJ!K -M@F9QEOS5+DU71EX9%2D@TW#0T7>LL9B)=B+D7/%3@IL]+!]?JK$T567P=,.$ -M\03QC'2).RUWEUU6.G@X;CNL]ZKNL-NN@;-50Y,MBMWO7T65R3B=H-U+5`CU -ML[EY0!Z<N'(<<,LOH?[#`8R4#"J2D^N$`NO5,(V-J.[47,OHX-"PNH^\UXJ1 -MKI=K.L4YW4[8O4'>7-'4H72'ZUVO03[@L;]5'D7G>/U1VQ5:W%6,12_JL-5) -M_AC'PUF7=6Z+<QKZMK;3=(A_6L^(YP6[6^>X/@E]UM9'@U.\E`^C'U0(@2K- -M_N%M7H[D7Y1342`9>=%'HY"#&1NFVE6/+V'O\3<4R5E\I5KP)Z_B?"-SWV\Z -M-`K0*4#`<)"F"WG9^7GH1G7P:NDU/FJW+-7EOU0@F!6"8\`!Y>62JE%:SB#3 -M4=L.@4XN+;BFZG\)^[Y?P7U%/!7J*]L:@Y-<S9H9U[\PN['G6(R9=U&V\]LH -MPDA`^%Z+S8:7J"YS;HU$J?G!>IHF]J(JQU*A""7L53#3`>(Y*D6T*JB1G[XL -M'@%FDI:6`(DTU,LXI1^Y`56F%/%O';682K;=6K,=_)#];ASLVDV>6%/KD[1A -MBS('5AOW8!TP`<YG'#RO$42R,)4#(WMSA*E97?/[UAZK!O=T_.J@=U+'=>^L -MB<-&$6_CQC;BVPV5+/XYJ2I#5M["5[ZS*SH3`7)?OWAZG25?C)N,/DFDS'(X -M3.Y=\^JV4IUM%>J*8#ADHM^*<C`/L2B8FQ5L-(O5ZG?'C!(_)`9>5TZ(PV!J -M&VIDPY2Z[)*3NOX._P_/@1-AGT'[1>W(X+-"D*1B+`XRQ(82P7B6CE_33T?P -M*0@:(THTL3_;5>$@UDA)<V^GTA7#L-WNHXYYW_'L=!E,S#];S>G#4^KE\"/E -MXJAY>V#1WIY"#^1-+0D.+4(T$)(XYP0?&\7N#F[!KK7X`>PU5Y:YG>)\[XG4 -M"N#K;PEB@FH>CW`J+3VV'_.YPJUM=(;PR?T6W7Z;`C<GG(2E@4'8G._Z#T`_ -M)J7YK%O>+>7#^T="5,T"42%@'GQ3R[&D)15$`5DBO4X;P-1UM"\E%]=4@^9% -M(GPRGW"+/X%UC"SK+[>T]Y2DPB*1;W!\9O;B32T.^.>!N-TDN*N!1MI\:N7P -M<K*9<Y]=L6O?X62PI/GA]J+4R\L173,6QP1Z8ETOK-B@A>&6X#W7\M,MU]2) -MLC=RZ"G)=DF<:JBFF2U]!_DB^QT@8)`KS>2P7"4Z,$G$<G+D)"_QC*U^W*,I -MW@23@2QJ*RR@>+'F<F/,K,A&+?]68EG>YQ[U-!)P/C0`A0NG[2D^T6+H3X74 -MIX-"J\'653J/#)`I]$5C^M00M[9^L/=5+/O>T6,];.!%+#+T9%HDP%)^#TFC -M_Y>UZK#-]_.`X8+1#>8J'JULUE0"R7D>SH-F.`6L+FLFGFDIZJO_:_92HI:` -M[V3?"=0.5+>J;J&;8C:]/WAVOHC\XBS7YS%)E@I22V2:NKPF;WA]M,=95EZ) -M&S-2KP8/B[0CZO9<:C$N]*AC;B95.0[TA,)`_ROE)$3-/P-!_\3'G,M=(++K -M'=U#:.A.#1OBAO%BRK3DO+V<U"PF#9P6#'3,2HD[@PFI&</?BEJ%H#9D/%ES -M$Y`[+6M1EJ[784NG7M\*'LN5;(4Y>LC!I\N\XC;NQ"H_-,^HJ6]1'8#_N_L. -MK&N"J2J[NRTXH[XU66V1^)=BT_ODUD38[-C#'DZ*&B9>&+WM?4(*&A'#13C? -MN4N^3N)0AN52(:%FJL4#+NH%!=[%XCU6.-\+^:I_!E-!KCV7,Y#CKM<*3$KY -M-^YNH,K74'^<K\:><=J<+-.K"DMWQAY3`BG65W0TI?FB4C"B#;N=H5K!^.B@ -MI\!NM^R7.0(3@_VW<J0CG%H'!0AQ'L#60;<5N0K@[4*BTF+TKN(W\B'BQGG4 -M'DBF<19`$75U2^=W^CE..V_11$E86-^6V:H.ZVJ-Z)/:8X7@9TFYRN]?E!N$ -M:"8^8]]+#]*(6E<7T)$V.#/G:"/QYZK$@$4\`#94G/9?WD@LV:@BW<LMTYM' -M08>WX78[+?RL_;NN@,E?]"``;+/-=ZA.WM4+J#X7$?3FR.+):$WZHS\_VF70 -MI_4@_P43CV%V'9Z.L%E_RG_:I"ZB(NYAEJ"Q!$5\H`_RDVT3BD,9VN>RL4>9 -M@T-:"?]!D0`SWVE?L!E1WUP(/SON(IILHQ7-C3$?@<!:"ZH#=Q(YMPMZ;,_R -M[5L%&U(1T0ZU7:YS8[D:Z`Q3`C=AK"'?;/YSMZJNNZLG%*[\F!<+E4U433VF -MWCQ3IV(""1&_)5&IFX'K85&-B9G=W2D$/\H^5[DSAQ>H-6&J45-6D6Y[3,[G -M-LM-I!T36W]"!PSC`J(4&---W*RMM>+VC7U>W?=XUQ=7GF0;OPP&>>V+8UIX -M6>=/&J84@KT#,NBIGR,?;0P_W(PJ_.LI6"Y\S^@/RMA]NEN"0B+^E[Z679=G -ME$D]\<,ITD[WQ(I%3XQ]/%'+0U*4'-O>*5BXE:DX0<]E=+DO&,[]%VM`D;%C -M:9B`ZTTFNKSME,K*#+$%8%W:A*F]P6@*^A3Z>Y<D2&^QR9]%T1_M)&<!VBWB -MTYA'Q'A/D]L=LE%_0X0[-Q@0?]_(@PF;^-`VIVI/@#>,::YU?:->H%L+LR>( -M3743+1"?98<,Z71Y=87$3KHF.,-\,815Y*:F2`+YR^R7YW9/#4+`R%?7Q+]@ -M&%R6(%S<\PL:2XN2#V_=G<"%D"BJCK3H.42M=M=+R$L^I7_UT*ZNYN1(8"&T -MM@V[7F[65C0`RPGV!03$W>-II057<]_6IH>T#XEA/"[LP)RM@O1#/"O$2N-! -M4\VS.&'M_7X:+L)6,;RC9U8%'>0QC_Y<QCH1A-D*0E*&##C?+Y2#HQ%PV.N? -M)<ZYRC[K&K)$3B74(<MT)B2V2+",U\YO#X-H(M$7.4H)QUPI9L+^8.QNQ34< -M9DWUR2^)8102C]F.+`RQRPH9/%2"&QDXE&49KN5-$9_"MYG*C_D=$Z+#0O9: -M@V5\V.3X:R\[@2VC4K"J@E'[S.?ZC4UF!=ZKJ>N$1B'#3$6P`$&MRM:]%B0P -MI4)9X1MC=_L1#T>XA/L6MWD^<U),3'<-5NNX2JY&*[A5$>.]DJ*OBU<!RYP* -M7IA\&64FXV\^.($V;MZU*.;:7XS7"0@7(*I%>3]]OH/1CLRFO(TVCGUE`K17 -MA;[=OKO!I9?"=3>&D!`:@:!])5C?"%/VEYEC'A9VR*F`K2<GLS+"1)[[=X_D -M;+>&3O;=J%%'?FU*6>:NJEW)4U*`(G7UI5/(B;:G*3.S(;__>_M'EVW=)P\6 -MN<..Z?.^\]WEIM1G="0WP3B$JY8R>3!L1M;<II@2BW5,G5]U$W@Z\%MOHP(9 -M;?I'#4Y5AFV3)#KJXA,8WN0B\%^/"$#%.^[I<&?>U\[""R!B35K!GX:'V)LY -MNX"?O0KL"6^2^8!:E9O4;0#'0<.,B5RJ,$LP)T<C$*.=5&IWN?]EX$/Q[U?_ -MMG6N*Z7W(VW:R\UTT6@@%P;?*C1#2'^6RR%H12:R&Q"G>'>&B\MKYS)<73/V -M7`-&,;(=$JIUD-A"[UBOY0'T\8JQH8;I*/%/9\_#NF=YB;CY(XZ>?++EU"B' -M/NMO0NWU@D=QGF6XHU8C%VD&5H%6VBP?Y[D1'&BHS1R$B?/R@.LH4!%#:]:4 -M.B+RUT,8`S7XXDV:_1F/DP?\Q)LX#/]$*JHR(>F]0WE3*'/\?Q96S\9T!(Y1 -MG!5KNIJDM0_2O\E%`OF7_D&Z',`P$Z)\7Y+\\F/I/.ZUG)I0![2^1+H9[\7P -M5+W5-$(.C-B%SQ6:Y$X9']3]Y?X?L(3G;<^@?*;FT,UX-ELEKH-*=Y)!:RXS -MM@'6HOD@_>\URN?MV@*L0=4W!`I5@-1/0E?21%U`SI#>U!\;:0C,VC\R8Y^V -M&*/Q7'7%S2+RO@$3X/=)*P7Y`L`]Y3D513V#:^#7\O^'$NA[_>0-I//L)3], -M?6&?A*=\/,Q*)N(&=Q4`*MP8H66'`)(X*D"S/<4@BX$;_<GD#<C_M,I\H%Q> -M$QR?5PF'TTLN>3>`P(Q)J%+TPW'N$2M.Z1E.*N5Z@B1V6]'YQ$]Q79F-"G9O -M\K77QQ+$\)]T]9.7B/V:P=>(_CG`)9!=SAR"N5A2^[>1Y(512+%?K*]`^Y03 -M(G5+/@Z(?G-Z6D.EC;4]C]+`:."U8TVK8V-=?>+Y+\2I4-ZU9[`&KW+.#VRA -MW'9`U/+`WY'X&[ZIVZ3;@1'D@*MQ=AVZM245'SFG(GLT(`T6\Z(%FT^/LA1( -ML%JC%9(MC6.XC)NK;3[)_:78+YD)-<2T<JQJ%D%XZM>8Y+#8K8+L[YKLW.;L -M#\_>0>47TBG>1W#U>6*=9-+>*XJ(@1:N3[[!AD45M,TCVF:/%)!,&S'T^C/I -MZ<KFLW-;:CQ\:-'1+"[>3]=0?6.%D5<,77M[+K&W;"46[#;=W%*A-`EH9AS! -MT-1XQ.)R5).>FW>0*V'>.;5*H`BPS?1@QIC-^1'*^6N`<PA%>=G.T6[9-?PR -M*10DFZ5Z`XC!6L9K'6810=RISK9NG1,VTT@.L!\&"JJ8NB]NK5^^I_3KI8M^ -M]D%7=[_X+F%.<9][Y%/^[-I50_L3-QEE`P9HMQ5'3-I2&64.Y!,;;VZ9&UI8 -M*K((WW)QK:7]M7G!R\6'X7>\8AMG)%<!=,OZTWA=69!M_KRMW=4A?Y&%.\6D -M>D078"1C6(%C\N)_B^)L(Q&BD+X-BM9Q9V9?<:+$M"(>,,\P]$!#11>G6ZJ0 -MG7%>I_"1`N_J3+Y8Y][(>$GMY=4M>)P:0A]!W/ZBKJ?P&?@<-">3S,]R_^4L -MTQ+A4H8BHQ[+,HP!Y#<HB+FM&]LDR-B+Q:>#)%=NN&Q^6_)S^5^0$<L,70P^ -MN'`60BTMI[1!3&M]^#A?,/W_H!E70)#[I`$%ON8@+X$P_*"ON!4.TIK'KKXQ -M&RX6U<FC\A3*`2;?:.GS4\@REI&:CY?FTLU,`WY'M>X1>+T*SO$]Y$09--E6 -MWY9\Q=31^A`J6;Q\!['XR01MPTF(LKDM!D49'NQ).D=ZS/TVJ84$Q(K6=?DR -MAF.L0)GR*82(9NC.N!&;7S5QD>%,6ZA+R,)P8<\7XGV?[>UN98#QOQM&'40_ -MJ!&Z5ETFM:LEUWR&@S^;B>/L.7-DE(Z>\B[S2D.BJ/'QIJ4G,#5P0%G>:97L -M9."_:KH87F_!"<Z*T8<NZCG`L*\=10`Q&C`!E8]L@T!$AVZ$0;>?MC3:RW,N -M?O'1TE\8.W6.DAV4?'\.OGI@7];/H\3:@`-\OX_M474]*?U.T+>#H$6DV4V? -MXO]",$7+'`V%H0:V;DA#<M$"'7/DL.*-^1?1':#R\:WWOU^$]HK?CZ$&;28% -MF/`0!G]87L;L",\,9\C$YC#OK=,]T/GL_#]TBUAZ/:29SY"WN3V9HI5)*Y3V -MQ3'^H@*>V;E9I$T"CA&F$1YFV>234);XZOG>*<`.6QX[QR6O2Y4Y"=G_W7:1 -M0]-H$/[C&KK8%T2?=V55]_=!,S+17/0)L%!?[.TFG/1([EMU=;T=:MP[SO`9 -M.D0^]Y?S0>QF!>B@X^@GK,>0!Q$H50"G"H6O*FIP>%=E$U,#JPWK78AX-238 -MBW]V)3)B9[(Z3-^U55Y$,Y??5MBKL/5O]U2@QW>X""RA?843K:!Q(!&-XB[R -MSZ*Y.8VBDP-%VZN]4,$O+@^1J'S1P'%TY5K^#\RQ%+`9,WVQFB/^O<B4D[]` -M5>Q>&9<_\YID51L)8R@TQ@-2G]<PZ3P]'1>94[Y#QR``(B(``B(B(`B48CWT -M#:9+`.KIL\&<+Z#?_KT56D^=!)6:W\?`5>"9Q3M^O=Y+B0\A4NW&$US#F@OX -M?BW*3PIQ0WSUGU[HB?$6=J:C1ROH'FL7M6JB[6?N:;O1]63P]FC]DM<7'K,L -MW#*%*PF/T"W.K9`0_E($^CS$`^1E2Z,U$81U8'#01]E^KBBL?ZKV8<7.X[]/ -M]!8<5Y0-+@'L\$$KC"J<;V_9@\9",AW,%M5O+3(<Z2A-@$Z6#;!-3%%SB:Q, -M_.58QW]72#"I0P_^4?UN,ZBMVW(U[G$79U>TS68>R14:$9!F),WC"VA`G;+I -MA:_NS*TQ:@V'(.X.*Z053HI;K\8GH%/QD?BEH*F^Y*BV.#/3]Q2MH<8HTZI* -M%J5WL\3YB+=!E2H5"=XXLVT=%-"N%7CNQ]6NK9[[/%^E1JVTUJBDI;U^[I2S -MZMH?HE\7+,=L6F-@,QZ[Z"%;N_0`=4N+KW0TR^;2S`DX8G$`,=#-"W26`CBJ -M]*WBXFL[^;OH0GR*@<W$T=-KE9H.1KR$8N^BJ^5KZ=JF?H!K([7I&8=L]9ST -M8F24X"G=3Z,=+]-%4(9U>.SOEZ_^&I[?LB4-G(ZW;LS<U/;@C[8.5^O$X!?_ -M%49<LS0;JQ3&X\(H'X*[0?A6`\@GPW%&'#Y4/A1X;`7UM7LJ-[KEAC1,)T7X -MWSUIX[(I\2]C+QVR:D\=59H/=XWNE/L/&:&0O<Z@_Q;4WI0+R*AS2(Z,=AJP -M@^JJE'V$KX2(TR`11_L38][YVP]-J+C"M-<3>Q`(<*862`6E<LI-X.BZ-\)= -M%B_;U+3E-IOA2J/"B&+C;"4]3WTZ%ZO?0Q!9W9/9@ZNPC`S<S3'6`1@OT<QW -MFA#C>Z*#NUL`R3716+IKE>45,`!DV96SH0J=W#6>5)U@M>AX*0!U2[.N0NW$ -M*0#=@LPHJO2V#G%2VBGIVK?`"7<E&&@0IS33&`8:/19CE]9$;4!^>>%,:$)F -M*1\>BPP!:5_*4[FBZ(<O[J$F/ZS><W0HJ4TCU.IUC,_!4D%)M_0ZJR(1IKZ' -ME2JJ=/L@."]HE&_1)^!G0C34=(0['`>,#2H(O-&;)]_(U+"!-)<Z-U`%`,O* -ML-.]4_PO[R]F\#;I1*2"7_IK'XW:C!R5$IG?CB&+A]2%^T5C$:4#3I<O^0V8 -M&=$!JLOFP&`29]U,J#IMN]N]2F\C)HA--%Q[()];IBD*.$'DEK?][W3QDY^J -MX[Y&G^V=;<'A3&+L=+0$X,SATI`XP0KS]<RBX6*X(Q?^P56HQ\G;QVM<B$3G -MP+TB%]T=<\N03SIDZ&`LVS!&$P:=/9Z=7+J&00+:&5>%1R%V1)FA0H2G7R[* -MM^&^WZQP&PUY+00*`A>39^'))`5<0>2:PAE+R[,A'#O5-SY0@:;8#]$6LP)* -MR7[>Z\=<FY2(/$3_M0+FP!13$-T/!_%'7#E/*)@RNQ8=A=[/;XDV;AV26A(8 -MB#%,Z#6'"^T^0MBA)'R9.^'?V#KTA19($>%LR7KZ-*T@:WVFM&KQJ-P:>$NS -M$EO%'."?.,_ENBAV:%ZYJRM`OWU[)">!(AB3<E+>&>/JX[1+@&^3410##33; -M-Y`FMV?X?REOA6H*0I+@,K54R&@R,[NHCB7<-3OH&QB_F,;KU7_T%7SC@S>^ -MN07.>B@B%[*\T!$B.?*FR%K/TH9@S2^&!'+OBEB5^[17P1<YL=[;4:?VA8CS -MAQ=#D`NRBL@\]K>@P-NK&RB0PUIT)OE%H0!=.Z-?ZJ2/,J>FK7Z#HK5J&5R' -M?&M%W;=S%8XM273[BT>&;X)U%A5YI=R+OS5IY=9+2O8"A,F]70:F$EP[B@I` -M=_U`,<W$2RZ8&!U:%>"L=?:-`J+6DC)O3PH%%[Y^:KAE'5^^*DT-Y9/-K,:N -M`V=:B[PY;/'WIX%<PPE5D=$U*:,R&#S=Q,I9GF'1L&D8W)*:^Q<47!XW>=\D -MTZ1/Y3G?A:_\NN6/OR7V1Q9Q3KF.>""FU"P'8<;6S9,NWD)C59=J_XW;A9KF -M99IP,F!<Q!A7\KI#U`M^]#(<05M%AM:'J<09`-<:$K;$"DU=SF/H[$PE/'-W -MZ';2K*\97,$(M&/<J76*POK7[75!Z!S,/?QDGXEFU2-1T7CV?(*#0P3TPX_Q -M]8PH0FA_8D67T&0U`4K#<2=LUPA7I>2>5;T*]W$"8ENZ^A$8+2[Y[L"@4F0_ -M"<^,C]]&\!7V5)=^>&S5__X<=S/H`"`$:*'+(K).WAZKWC4%*W]O+G"3%ESI -M).7VFUQDMR*2GRH#VN:M3XL]]H3#BG,7ZUNI9(8JA&NY4\>(>F;]KSJ\529X -MY_0K;8)/(GT0!,B%&7=',L#%$T;]G363YNE31U#7S\'V7^A%E*@%Z-.+9XTO -MO`IK'Z`@.*_R7R7DY?7\5GA4ZY8&//YA7(B*XSL=T]MD]B@@E'>;S?@N3M3> -M*A]XGP\+-%`92I(BUR>?"6\)6U[4?)1DNH\R+#BG4;L?-D2)P4W'/'W)2$C7 -M5>H9EOG13%VST,<QSE1,#8\Y,>,K/G`UPP)B6LYU\M+6\.PL<;+_Y"88^'FX -M5R?XZ,X@@*7Q3B(.IGC=MV7YG+1>=<O-IC+X.,-P\?YL)SQ_0@4UG>1)I'$. -MZA]R@5KY=YFN&Q(N7["&>C-=+DOI:9/,_L#NU240B+_HI4&$N2\!J"M4?JJ( -MNHI:67WD&7K*=UY]R;%(BEN!8NU/X'6IA,*Y7'GC;<'--G;K5#%_TEV7;]:) -M7''T7D1<11W@YOVYRE!8LV`0\?R9VAXKF!<22L@EP)H*K_@!X>M9S]V??C%U -M(X<F#+:V/^'PB,YLE_[XSX'RE"])I[28P]L\YLM>"JY5[4#-54QK^T1@L)%B -MQ+L$S%1\&"-#DE2N!.K#;9"?-4GSA'7`0THA/YM;/`PZ9&+@(-&9K#7Q12!F -MZOQ-M<QM,`U<DNEH8_PD[$J6IUP4RDGIM/)E?+9D?+'?8LR4-ZZ5SNT%+@6, -MV_`^;NF(3Z#"K-0!*>=_0G'IG=#35>+J2XE0$#2Z^.P?_*!%_<&U8$_V/RZM -M\F`!ZACID'V(;F%1PZ'3+VF,R))&D+[+EFWI#>H>W<#)E/*V)+@J&;_:*!34 -M[@=G&[?#@_H6(N4R'5)4]3>A'Q^&772N3.=ZOVP(=)AC`'&=[R#(_&3D8?_) -MO<FK3+4ZB<$!XU=]KM5R!"9-MOZOF&7&.;Z0L33.T-0_E%H>Z9R+$T>O(VC> -M>N!JA@!88!.+,J_<_!;J$X<0Z8])HC3?9MK63CEZ=+OK5-T-&+*:#=#4.ZU1 -MUQ/4*RNCZABL^@?32Q$^W$M[.OKD4FG@S>Y]Z_D[$>74B*!X]4COZ%R5E6U< -M*+A8^M+K@3-!^E`QA-B\=ZKD")GI\LI)>C^#]R>@?$X8F^.IIL%K8Z@"$E=! -MA-1#L'H[N`N"Q4JD2E_C2\6Z.;S,!.Y?.8<>VH63RN1,%'S8VIS7R3-"WS72 -M(M^"`@"+-X<PC'JISJ3`KCQ0OZT9"^<UL*>MWJ,H/#DF?V*BVNLA@2(NG6_W -MLX^4@^-QY*>%N6/IW`&%J<E/XO#.<MUVH6/EDS;$9,LPE/N-*.^K4E(L/*\S -M%"0[-H1<[L;&$-SO4R2@E]!$E&W@E0<,]PJ/U\L&+OFL;2"&7T-JF1F$$3\P -M-INM#<G"95%YA\^85H<L-&1'L=TA6`3)PV%4]=KAMCC9X"6^QN4\AH"W(I:1 -M17"WFS6ZXN902EUKB+0`DA^?`R'5Z%G80KKPP>^YS"7K^];J4)P7XPW'HNGP --^K_XNY(IPH2"!#,T>``` -` -end diff --git a/sbin/gbde/template.txt b/sbin/gbde/template.txt deleted file mode 100644 index 6de6c6b0b485..000000000000 --- a/sbin/gbde/template.txt +++ /dev/null @@ -1,31 +0,0 @@ -# -# Sector size is the smallest unit of data which can be read or written. -# Making it too small decreases performance and decreases available space. -# Making it too large may prevent filesystems from working. 512 is the -# minimum and always safe. For UFS, use the fragment size -# -sector_size = 512 - -# -# Start and end of the encrypted section of the partition. Specify in -# sector numbers. If none specified, "all" will be assumed, to the -# extent the value of this can be established. -# -#first_sector = 0 -#last_sector = 2879 -#total_sectors = 2880 - -# -# An encrypted partition can have more than one key. It may be a good idea -# to make at least two keys, and save one of them for "just in case" use. -# The minimum is obviously one and the maximum is 4. -# -number_of_keys = 4 - -# -# Flushing the partition with random bytes prevents a brute-force attack -# from skipping sectors which obviously contains un-encrypted data. -# NB: This variable is boolean, if it is present it means "yes" even if -# you set it to the value "no" -# -#random_flush = diff --git a/sbin/gbde/test.sh b/sbin/gbde/test.sh deleted file mode 100644 index 502bfd8dbd68..000000000000 --- a/sbin/gbde/test.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/bin/sh - -set -e - -MD=99 -mdconfig -d -u $MD > /dev/null 2>&1 || true - -mdconfig -a -t malloc -s 1m -u $MD - -D=/dev/md$MD - -./gbde init $D -P foo -L /tmp/_l1 -./gbde setkey $D -p foo -l /tmp/_l1 -P bar -L /tmp/_l1 -./gbde setkey $D -p bar -l /tmp/_l1 -P foo -L /tmp/_l1 - -./gbde setkey $D -p foo -l /tmp/_l1 -n 2 -P foo2 -L /tmp/_l2 -./gbde setkey $D -p foo2 -l /tmp/_l2 -n 3 -P foo3 -L /tmp/_l3 -./gbde setkey $D -p foo3 -l /tmp/_l3 -n 4 -P foo4 -L /tmp/_l4 -./gbde setkey $D -p foo4 -l /tmp/_l4 -n 1 -P foo1 -L /tmp/_l1 - -./gbde nuke $D -p foo1 -l /tmp/_l1 -n 4 -if ./gbde nuke $D -p foo4 -l /tmp/_l4 -n 3 ; then false ; fi -./gbde destroy $D -p foo2 -l /tmp/_l2 -if ./gbde destroy $D -p foo2 -l /tmp/_l2 ; then false ; fi - -./gbde nuke $D -p foo1 -l /tmp/_l1 -n -1 -if ./gbde nuke $D -p foo1 -l /tmp/_l1 -n -1 ; then false ; fi -if ./gbde nuke $D -p foo2 -l /tmp/_l2 -n -1 ; then false ; fi -if ./gbde nuke $D -p foo3 -l /tmp/_l3 -n -1 ; then false ; fi -if ./gbde nuke $D -p foo4 -l /tmp/_l4 -n -1 ; then false ; fi - -rm -f /tmp/_l1 /tmp/_l2 /tmp/_l3 /tmp/_l4 - -./gbde init $D -P foo -./gbde setkey $D -p foo -P bar -./gbde setkey $D -p bar -P foo - -./gbde setkey $D -p foo -n 2 -P foo2 -./gbde setkey $D -p foo2 -n 3 -P foo3 -./gbde setkey $D -p foo3 -n 4 -P foo4 -./gbde setkey $D -p foo4 -n 1 -P foo1 - -mdconfig -d -u $MD - -mdconfig -a -t malloc -s 1m -u $MD -if [ -f image.uu ] ; then - uudecode -p image.uu | bzcat > $D -else - uudecode -p ${1}/image.uu | bzcat > $D -fi - -if [ `md5 < $D` != "a4066a739338d451b919e63f9ee4a12c" ] ; then - echo "Failed to set up md(4) device correctly" - exit 2 -fi - -./gbde attach $D -p foo -fsck_ffs ${D}.bde -./gbde detach $D -mdconfig -d -u $MD - - -echo "***********" -echo "Test passed" -echo "***********" -exit 0 diff --git a/sbin/geom/Makefile b/sbin/geom/Makefile index b311a9e8e8fb..078503d3ae67 100644 --- a/sbin/geom/Makefile +++ b/sbin/geom/Makefile @@ -1,4 +1,3 @@ - .include <src.opts.mk> .PATH: ${.CURDIR}/core ${.CURDIR}/misc diff --git a/sbin/geom/Makefile.depend b/sbin/geom/Makefile.depend index 082d59a0b0f6..08cfc713d335 100644 --- a/sbin/geom/Makefile.depend +++ b/sbin/geom/Makefile.depend @@ -6,9 +6,7 @@ DIRDEPS = \ lib/${CSU_DIR} \ lib/libc \ lib/libcompiler_rt \ - lib/libexpat \ lib/libgeom \ - lib/libsbuf \ lib/libutil \ diff --git a/sbin/geom/core/geom.8 b/sbin/geom/core/geom.8 index 124ea0f2be11..b864b3b238f5 100644 --- a/sbin/geom/core/geom.8 +++ b/sbin/geom/core/geom.8 @@ -1,3 +1,6 @@ +.\"- +.\" SPDX-License-Identifier: BSD-2-Clause +.\" .\" Copyright (c) 2004-2005 Pawel Jakub Dawidek <pjd@FreeBSD.org> .\" All rights reserved. .\" @@ -27,7 +30,7 @@ .Os .Sh NAME .Nm geom -.Nd "universal control utility for GEOM classes" +.Nd universal control utility for GEOM classes .Sh SYNOPSIS .Nm .Ar class @@ -66,7 +69,7 @@ which can be used for existing .Nm unaware classes. Here is the list of standard commands: -.Bl -tag -width ".Cm status" +.Bl -tag -width indent .It Cm help List all available commands for the given class. .It Cm list @@ -74,7 +77,7 @@ Print detailed information (within the given class) about all geoms (if no additional arguments were specified) or the given geoms. This command is only available if the given class exists in the kernel. Additional options include: -.Bl -tag -width ".Fl a" +.Bl -tag -width "-a" .It Fl a Print information for geoms without providers. .El @@ -84,9 +87,11 @@ Print general information (within the given class) about all geoms This command is only available if the given class exists in the kernel. .Pp Additional options include: -.Bl -tag -width ".Fl s" +.Bl -tag -width "-s" .It Fl a -When used with -g, print status for geoms without providers. +When used with +.Fl g , +print status for geoms without providers. .It Fl g Report statuses for geoms instead of providers. .It Fl s @@ -107,7 +112,7 @@ kernel module. .El .Pp Additional options include: -.Bl -tag -width ".Cm status" +.Bl -tag -width indent .It Fl p Ar provider-name Print detailed information about the geom which provides .Ar provider-name . @@ -163,14 +168,12 @@ STRIPE .It UNION .It -VINUM (deprecated) -.It VIRSTOR .El .Sh ENVIRONMENT The following environment variables affect the execution of .Nm : -.Bl -tag -width ".Ev GEOM_LIBRARY_PATH" +.Bl -tag -width "GEOM_LIBRARY_PATH" .It Ev GEOM_LIBRARY_PATH Specifies the path where shared libraries are stored instead of .Pa /lib/geom/ . @@ -213,11 +216,9 @@ geom md unload .Xr gnop 8 , .Xr gpart 8 , .Xr graid3 8 , -.Xr gsched 8 , .Xr gshsec 8 , .Xr gstripe 8 , .Xr gunion 8 , -.Xr gvinum 8 , .Xr gvirstor 8 .Sh HISTORY The diff --git a/sbin/geom/core/geom.c b/sbin/geom/core/geom.c index ccaf7adca305..b78021194ddd 100644 --- a/sbin/geom/core/geom.c +++ b/sbin/geom/core/geom.c @@ -26,7 +26,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/linker.h> #include <sys/module.h> @@ -250,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/geom/misc/subr.c b/sbin/geom/misc/subr.c index 2d3db86c943f..18bac7108ed0 100644 --- a/sbin/geom/misc/subr.c +++ b/sbin/geom/misc/subr.c @@ -26,7 +26,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/disk.h> #include <sys/endian.h> diff --git a/sbin/ggate/Makefile b/sbin/ggate/Makefile index 9aa1d5115505..753e0bd0ddbb 100644 --- a/sbin/ggate/Makefile +++ b/sbin/ggate/Makefile @@ -1,4 +1,3 @@ - .include <src.opts.mk> SUBDIR= ggatec \ diff --git a/sbin/ggate/Makefile.inc b/sbin/ggate/Makefile.inc index d3b5cbd3a79b..01b5f23410c8 100644 --- a/sbin/ggate/Makefile.inc +++ b/sbin/ggate/Makefile.inc @@ -1,2 +1 @@ - .include "../Makefile.inc" diff --git a/sbin/ggate/ggatec/Makefile b/sbin/ggate/ggatec/Makefile index 5591b2a3a8ba..9ea38ceb87d4 100644 --- a/sbin/ggate/ggatec/Makefile +++ b/sbin/ggate/ggatec/Makefile @@ -1,4 +1,3 @@ - .PATH: ${.CURDIR:H}/shared PROG= ggatec diff --git a/sbin/ggate/ggatec/ggatec.8 b/sbin/ggate/ggatec/ggatec.8 index 1b780632f154..99f9bcab5964 100644 --- a/sbin/ggate/ggatec/ggatec.8 +++ b/sbin/ggate/ggatec/ggatec.8 @@ -34,6 +34,7 @@ .Op Fl n .Op Fl v .Op Fl o Cm ro | wo | rw +.Op Fl o Cm direct .Op Fl p Ar port .Op Fl q Ar queue_size .Op Fl R Ar rcvbuf @@ -48,6 +49,7 @@ .Op Fl n .Op Fl v .Op Fl o Cm ro | wo | rw +.Op Fl o Cm direct .Op Fl p Ar port .Op Fl R Ar rcvbuf .Op Fl S Ar sndbuf @@ -108,13 +110,21 @@ provider (cancels all pending requests). Do not use .Dv TCP_NODELAY option on TCP sockets. -.It Fl o Cm ro | wo | rw -Specify permissions to use when opening the file or device: read-only -.Pq Cm ro , +.It Fl o Ar option +Specify permissions and options to use when opening the file or device. +.Bl -tag -width indent +.It Cm ro +read-only +.It Cm wo write-only -.Pq Cm wo , -or read-write -.Pq Cm rw . +.It Cm rw +read-write +.It Cm direct +open with +.Dv O_DIRECT +option on the file +.El +.Pp Default is .Cm rw . .It Fl p Ar port @@ -160,11 +170,14 @@ Use a CD-ROM device on a remote host. .Bd -literal -offset indent server# cat /etc/gg.exports client RO /dev/cd0 +client RW /tmp/image server# ggated client# ggatec create -o ro server /dev/cd0 ggate0 client# mount_cd9660 /dev/ggate0 /cdrom +client# ggatec create -o rw -o direct server /tmp/image +ggate1 .Ed .Sh SEE ALSO .Xr geom 4 , diff --git a/sbin/ggate/ggatec/ggatec.c b/sbin/ggate/ggatec/ggatec.c index 5ce605596fb9..b6f248c05290 100644 --- a/sbin/ggate/ggatec/ggatec.c +++ b/sbin/ggate/ggatec/ggatec.c @@ -52,7 +52,6 @@ #include <netinet/tcp.h> #include <arpa/inet.h> -#include <geom/gate/g_gate.h> #include "ggate.h" @@ -62,6 +61,7 @@ static const char *path = NULL; static const char *host = NULL; static int unit = G_GATE_UNIT_AUTO; static unsigned flags = 0; +static int direct_flag = 0; static int force = 0; static unsigned queue_size = G_GATE_QUEUE_SIZE; static unsigned port = G_GATE_PORT; @@ -78,10 +78,12 @@ static void usage(void) { - fprintf(stderr, "usage: %s create [-nv] [-o <ro|wo|rw>] [-p port] " + fprintf(stderr, "usage: %s create [-nv] [-o <ro|wo|rw>] " + "[-o <direct>] [-p port] " "[-q queue_size] [-R rcvbuf] [-S sndbuf] [-s sectorsize] " "[-t timeout] [-u unit] <host> <path>\n", getprogname()); - fprintf(stderr, " %s rescue [-nv] [-o <ro|wo|rw>] [-p port] " + fprintf(stderr, " %s rescue [-nv] [-o <ro|wo|rw>] " + "[-o <direct>] [-p port] " "[-R rcvbuf] [-S sndbuf] <-u unit> <host> <path>\n", getprogname()); fprintf(stderr, " %s destroy [-f] <-u unit>\n", getprogname()); fprintf(stderr, " %s list [-v] [-u unit]\n", getprogname()); @@ -361,7 +363,7 @@ handshake(int dir) close(sfd); return (-1); } - cinit.gc_flags = flags | dir; + cinit.gc_flags = flags | direct_flag | dir; cinit.gc_token = token; cinit.gc_nconn = 2; g_gate_swap2n_cinit(&cinit); @@ -585,6 +587,8 @@ main(int argc, char *argv[]) flags = G_GATE_FLAG_WRITEONLY; else if (strcasecmp("rw", optarg) == 0) flags = 0; + else if (strcasecmp("direct", optarg) == 0) + direct_flag = GGATE_FLAG_DIRECT; else { errx(EXIT_FAILURE, "Invalid argument for '-o' option."); diff --git a/sbin/ggate/ggated/Makefile b/sbin/ggate/ggated/Makefile index 2939e4de49eb..629d6aded308 100644 --- a/sbin/ggate/ggated/Makefile +++ b/sbin/ggate/ggated/Makefile @@ -1,4 +1,3 @@ - .PATH: ${.CURDIR:H}/shared PROG= ggated diff --git a/sbin/ggate/ggated/ggated.8 b/sbin/ggate/ggated/ggated.8 index 7016dd58f87a..8e3ad68d1db0 100644 --- a/sbin/ggate/ggated/ggated.8 +++ b/sbin/ggate/ggated/ggated.8 @@ -85,10 +85,51 @@ An alternate location for the exports file. .Pp The format of an exports file is as follows: .Bd -literal -offset indent -1.2.3.4 RO /dev/cd0 -1.2.3.0/24 RW /tmp/test.img -hostname WO /tmp/image +1.2.3.4 RO /dev/cd0 +1.2.3.0/24 RW /tmp/test.img +hostname WO /tmp/image +hostname RW,DIRECT /tmp/direct-image +hostname RW,NODIRECT /tmp/nodirect-image .Ed +.Pp +The first colunm specifies the ip, network with netmask, or the hostname +that the export applies to. +.Pp +The next column is the access flags that apply to the export +.Bl -tag -width ".Cm NODIRECT" +.It Cm RO +Read-Only the path specified will be exported to the client read only. +.It Cm WO +Write-Only the path specified will be exported to the client write only. +.It Cm RW +Read-Write the path specified will be exported to the client read-write. +.It Cm DIRECT +The path specified will always be opened with O_DIRECT for clients. +.It Cm NODIRECT +The path specified will never be opened with O_DIRECT for clients. +.El +.Pp +The final column specifies the path to export. +.Pp +Files are opened with the least common flags between the client and the +server. A client may request read or write only to a read-write export +and the server will honor the client request and open the file in the +requested mode. A client requesting greater access than permissions listed +in the file will be rejected. +.Pp +DIRECT and NODIRECT are used to coerce the use of the O_DIRECT flag to +.Xr open 2 when the specified path is opened. If DIRECT is specified the +path is always opened with O_DIRECT. If NODIRECT is specified the path is +never opened with O_DIRECT. DIRECT access limits the cache effects of +IO operaions on the file. This has the effect of having clients accessing +exports to not impact the cache of the local machine, however it +will cause greater IO utilization to the devices on which the files reside. +.Pp +If neither is specified the server will use +the preference specified by the client, with the default to not use O_DIRECT. +If the client specifies a preference against the server's configuration the +client preference will be silently ignored. +.Pp .Sh FILES .Bl -tag -width ".Pa /var/run/ggated.pid" -compact .It Pa /var/run/ggated.pid @@ -104,13 +145,18 @@ should be called with the .Fl v option. .Sh EXAMPLES -Export CD-ROM device and a file: +Export CD-ROM device, a file, and a file with +.Dv O_DIRECT +option: .Bd -literal -offset indent # echo "1.2.3.0/24 RO /dev/cd0" > /etc/gg.exports # echo "client RW /image" >> /etc/gg.exports +# echo "client RW,DIRECT /image2" >> /etc/gg.exports +# echo "client RW,NODIRECT /image3" >> /etc/gg.exports # ggated .Ed .Sh SEE ALSO +.Xr open 2 , .Xr geom 4 , .Xr ggatec 8 , .Xr ggatel 8 diff --git a/sbin/ggate/ggated/ggated.c b/sbin/ggate/ggated/ggated.c index c579da1c1267..ef7b61cb13c4 100644 --- a/sbin/ggate/ggated/ggated.c +++ b/sbin/ggate/ggated/ggated.c @@ -64,7 +64,7 @@ struct ggd_connection { off_t c_mediasize; unsigned c_sectorsize; - unsigned c_flags; /* flags (RO/RW) */ + int c_flags; /* flags (RO/RW) */ int c_diskfd; int c_sendfd; int c_recvfd; @@ -85,11 +85,18 @@ struct ggd_request { #define r_length r_hdr.gh_length #define r_error r_hdr.gh_error +#define EFLAGS_RDONLY 0x0000 +#define EFLAGS_WRONLY 0x0001 +#define EFLAGS_RDWR 0x0002 +#define EFLAGS_ACCMODE 0x0003 +#define EFLAGS_DIRECT 0x0004 +#define EFLAGS_NODIRECT 0x0008 + struct ggd_export { char *e_path; /* path to device/file */ in_addr_t e_ip; /* remote IP address */ in_addr_t e_mask; /* IP mask */ - unsigned e_flags; /* flags (RO/RW) */ + int e_flags; /* flags (WO/RO/RW/DIRECT/NODIRECT) */ SLIST_ENTRY(ggd_export) e_next; }; @@ -146,20 +153,61 @@ countmask(unsigned m) return (mask); } +static int +parse_flags(const char *flagsstr, int lineno) +{ + char *flagscpy; + char *word, *brkf; + int access_flags = -1; + int direct_flags = 0; + + flagscpy = strdup(flagsstr); + if (flagscpy == NULL) { + g_gate_xlog("Not enough memory."); + } + + for (word = strtok_r(flagscpy, ",", &brkf); + word != NULL; + word = strtok_r(NULL, ",", &brkf)) { + if (strcasecmp("ro", word) == 0 || + strcasecmp("rd", word) == 0) { + access_flags = EFLAGS_RDONLY; + } else if (strcasecmp("wo", word) == 0) { + access_flags = EFLAGS_WRONLY; + } else if (strcasecmp("rw", word) == 0) { + access_flags = EFLAGS_RDWR; + } else if (strcasecmp("direct", word) == 0) { + direct_flags = EFLAGS_DIRECT; + } else if (strcasecmp("nodirect", word) == 0) { + direct_flags = EFLAGS_NODIRECT; + } else { + g_gate_xlog("Invalid value (%s) in flags field at " + "line %u.", word, lineno); + } + } + free(flagscpy); + if (access_flags == -1) { + g_gate_xlog("Invalid value (%s) in flags field at " + "line %u.", flagsstr, lineno); + } + return (direct_flags | access_flags); +} + static void line_parse(char *line, unsigned lineno) { struct ggd_export *ex; - char *word, *path, *sflags; - unsigned flags, i, vmask; + char *word, *path, *sflags, *brkl; + unsigned i, vmask; + int flags; in_addr_t ip, mask; ip = mask = flags = vmask = 0; path = NULL; sflags = NULL; - for (i = 0, word = strtok(line, " \t"); word != NULL; - i++, word = strtok(NULL, " \t")) { + for (i = 0, word = strtok_r(line, " \t", &brkl); word != NULL; + i++, word = strtok_r(NULL, " \t", &brkl)) { switch (i) { case 0: /* IP address or host name */ ip = g_gate_str2ip(strsep(&word, "/")); @@ -185,17 +233,7 @@ line_parse(char *line, unsigned lineno) mask = countmask(vmask); break; case 1: /* flags */ - if (strcasecmp("rd", word) == 0 || - strcasecmp("ro", word) == 0) { - flags = O_RDONLY; - } else if (strcasecmp("wo", word) == 0) { - flags = O_WRONLY; - } else if (strcasecmp("rw", word) == 0) { - flags = O_RDWR; - } else { - g_gate_xlog("Invalid value in flags field at " - "line %u.", lineno); - } + flags = parse_flags(word, lineno); sflags = word; break; case 2: /* path */ @@ -306,13 +344,16 @@ exports_check(struct ggd_export *ex, struct g_gate_cinit *cinit, struct ggd_connection *conn) { char ipmask[32]; /* 32 == strlen("xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx")+1 */ - int error = 0, flags; + int error = 0, flags, access_flags, direct_flags = 0; strlcpy(ipmask, ip2str(ex->e_ip), sizeof(ipmask)); strlcat(ipmask, "/", sizeof(ipmask)); strlcat(ipmask, ip2str(ex->e_mask), sizeof(ipmask)); + + access_flags = ex->e_flags & EFLAGS_ACCMODE; + if ((cinit->gc_flags & GGATE_FLAG_RDONLY) != 0) { - if (ex->e_flags == O_WRONLY) { + if (access_flags == EFLAGS_WRONLY) { g_gate_log(LOG_WARNING, "Read-only access requested, " "but %s (%s) is exported write-only.", ex->e_path, ipmask); @@ -321,7 +362,7 @@ exports_check(struct ggd_export *ex, struct g_gate_cinit *cinit, conn->c_flags |= GGATE_FLAG_RDONLY; } } else if ((cinit->gc_flags & GGATE_FLAG_WRONLY) != 0) { - if (ex->e_flags == O_RDONLY) { + if (access_flags == EFLAGS_RDONLY) { g_gate_log(LOG_WARNING, "Write-only access requested, " "but %s (%s) is exported read-only.", ex->e_path, ipmask); @@ -330,24 +371,41 @@ exports_check(struct ggd_export *ex, struct g_gate_cinit *cinit, conn->c_flags |= GGATE_FLAG_WRONLY; } } else { - if (ex->e_flags == O_RDONLY) { + if (access_flags == EFLAGS_RDONLY) { g_gate_log(LOG_WARNING, "Read-write access requested, " "but %s (%s) is exported read-only.", ex->e_path, ipmask); return (EPERM); - } else if (ex->e_flags == O_WRONLY) { + } else if (access_flags == EFLAGS_WRONLY) { g_gate_log(LOG_WARNING, "Read-write access requested, " "but %s (%s) is exported write-only.", ex->e_path, ipmask); return (EPERM); } } + + if ((cinit->gc_flags & GGATE_FLAG_DIRECT) != 0) { + if (ex->e_flags & EFLAGS_NODIRECT) { + g_gate_log(LOG_WARNING, "Direct IO requested, " + "but %s (%s) is exported NODIRECT.", ex->e_path, + ipmask); + } else { + conn->c_flags |= GGATE_FLAG_DIRECT; + direct_flags = O_DIRECT; + } + } + + if (ex->e_flags & EFLAGS_DIRECT) { + direct_flags = O_DIRECT; + } + if ((conn->c_flags & GGATE_FLAG_RDONLY) != 0) flags = O_RDONLY; else if ((conn->c_flags & GGATE_FLAG_WRONLY) != 0) flags = O_WRONLY; else flags = O_RDWR; + flags |= direct_flags; if (conn->c_diskfd != -1) { if (strcmp(conn->c_path, ex->e_path) != 0) { g_gate_log(LOG_ERR, "old %s and new %s: " diff --git a/sbin/ggate/ggatel/Makefile b/sbin/ggate/ggatel/Makefile index ec1302bf9cda..eb9ff9a675ce 100644 --- a/sbin/ggate/ggatel/Makefile +++ b/sbin/ggate/ggatel/Makefile @@ -1,4 +1,3 @@ - .PATH: ${.CURDIR:H}/shared PROG= ggatel diff --git a/sbin/ggate/ggatel/ggatel.8 b/sbin/ggate/ggatel/ggatel.8 index 1ac41ed9eaa7..512e4ab9d187 100644 --- a/sbin/ggate/ggatel/ggatel.8 +++ b/sbin/ggate/ggatel/ggatel.8 @@ -32,7 +32,7 @@ .Nm .Cm create .Op Fl v -.Op Fl o Cm ro | wo | rw +.Oo Fl o option Oc ... .Op Fl s Ar sectorsize .Op Fl t Ar timeout .Op Fl u Ar unit @@ -48,7 +48,7 @@ .Nm .Cm rescue .Op Fl v -.Op Fl o Cm ro | wo | rw +.Oo Fl o option Oc ... .Fl u Ar unit .Ar path .Sh DESCRIPTION @@ -92,13 +92,21 @@ Available options: Forcibly destroy .Nm ggate provider (cancels all pending requests). -.It Fl o Cm ro | wo | rw -Specify permissions to use when opening the file or device: read-only -.Pq Cm ro , +.It Fl o Ar option +Specify permissions and options to use when opening the file or device. +.Bl -tag -width indent +.It Cm ro +read-only +.It Cm wo write-only -.Pq Cm wo , -or read-write -.Pq Cm rw . +.It Cm rw +read-write +.It Cm direct +open with +.Dv O_DIRECT +option on the file +.El +.Pp Default is .Cm rw . .It Fl s Ar sectorsize @@ -125,27 +133,8 @@ To get details about the failure, should be called with the .Fl v option. -.Sh EXAMPLES -.Dq GEOMify -the -.Dq Li fd0 -device and use -.Xr gbde 8 -to encrypt data on a floppy disk. -.Bd -literal -offset indent -ggatel create -u 5 /dev/fd0 -gbde init /dev/ggate5 -gbde attach ggate5 -newfs /dev/ggate5.bde -mount /dev/ggate5.bde /secret -cp /private/foo /secret/ -umount /secret -gbde detach ggate5 -ggatel destroy -u 5 -.Ed .Sh SEE ALSO .Xr geom 4 , -.Xr gbde 8 , .Xr ggatec 8 , .Xr ggated 8 , .Xr mount 8 , diff --git a/sbin/ggate/ggatel/ggatel.c b/sbin/ggate/ggatel/ggatel.c index 246a419ecaaf..1cbb6af28b7e 100644 --- a/sbin/ggate/ggatel/ggatel.c +++ b/sbin/ggate/ggatel/ggatel.c @@ -43,7 +43,6 @@ #include <sys/stat.h> #include <sys/syslog.h> -#include <geom/gate/g_gate.h> #include "ggate.h" @@ -52,6 +51,7 @@ static enum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET; static const char *path = NULL; static int unit = G_GATE_UNIT_AUTO; static unsigned flags = 0; +static int direct_flag = 0; static int force = 0; static unsigned sectorsize = 0; static unsigned timeout = G_GATE_TIMEOUT; @@ -60,24 +60,30 @@ static void usage(void) { - fprintf(stderr, "usage: %s create [-v] [-o <ro|wo|rw>] " + fprintf(stderr, "usage: %s create [-v] [-o option] ... " "[-s sectorsize] [-t timeout] [-u unit] <path>\n", getprogname()); - fprintf(stderr, " %s rescue [-v] [-o <ro|wo|rw>] <-u unit> " + fprintf(stderr, " %s rescue [-v] [-o option] ... <-u unit> " "<path>\n", getprogname()); fprintf(stderr, " %s destroy [-f] <-u unit>\n", getprogname()); fprintf(stderr, " %s list [-v] [-u unit]\n", getprogname()); + fprintf(stderr, " option = {ro, wo, rw, direct}\n"); exit(EXIT_FAILURE); } static int g_gate_openflags(unsigned ggflags) { + int openflags = O_RDWR; if ((ggflags & G_GATE_FLAG_READONLY) != 0) - return (O_RDONLY); + openflags = O_RDONLY; else if ((ggflags & G_GATE_FLAG_WRITEONLY) != 0) - return (O_WRONLY); - return (O_RDWR); + openflags = O_WRONLY; + + if (direct_flag) + openflags |= O_DIRECT; + + return (openflags); } static void @@ -248,6 +254,8 @@ main(int argc, char *argv[]) flags = G_GATE_FLAG_WRITEONLY; else if (strcasecmp("rw", optarg) == 0) flags = 0; + else if (strcasecmp("direct", optarg) == 0) + direct_flag = 1; else { errx(EXIT_FAILURE, "Invalid argument for '-o' option."); diff --git a/sbin/ggate/shared/ggate.h b/sbin/ggate/shared/ggate.h index 0170df2dd1b6..78488a24c3fa 100644 --- a/sbin/ggate/shared/ggate.h +++ b/sbin/ggate/shared/ggate.h @@ -29,6 +29,7 @@ #ifndef _GGATE_H_ #define _GGATE_H_ +#include <geom/gate/g_gate.h> #include <sys/endian.h> #include <stdarg.h> @@ -42,8 +43,8 @@ #define GGATE_MAGIC "GEOM_GATE " #define GGATE_VERSION 0 -#define GGATE_FLAG_RDONLY 0x0001 -#define GGATE_FLAG_WRONLY 0x0002 +#define GGATE_FLAG_RDONLY G_GATE_FLAG_READONLY +#define GGATE_FLAG_WRONLY G_GATE_FLAG_WRITEONLY /* * If neither the GGATE_FLAG_SEND nor the GGATE_FLAG_RECV flag is * set - this is initial connection. @@ -53,6 +54,8 @@ #define GGATE_FLAG_SEND 0x0004 #define GGATE_FLAG_RECV 0x0008 +#define GGATE_FLAG_DIRECT 0x0010 + #define GGATE_CMD_READ 0 #define GGATE_CMD_WRITE 1 #define GGATE_CMD_FLUSH 3 diff --git a/sbin/growfs/Makefile b/sbin/growfs/Makefile index 3f72e6ac625f..b5b3d99f287d 100644 --- a/sbin/growfs/Makefile +++ b/sbin/growfs/Makefile @@ -1,17 +1,12 @@ -# @(#)Makefile 8.8 (Berkeley) 6/21/2000 -# # $TSHeader: src/sbin/growfs/Makefile,v 1.4 2000/12/05 19:45:24 tomsoft Exp $ # .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/debug.c b/sbin/growfs/debug.c index e0dfc997fcf6..3fe7e26e3e50 100644 --- a/sbin/growfs/debug.c +++ b/sbin/growfs/debug.c @@ -41,11 +41,6 @@ * */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <limits.h> @@ -62,7 +57,7 @@ static FILE *dbg_log = NULL; static unsigned int indent = 0; /* - * prototypes not done here, as they come with debug.h + * prototypes are not done here, as they come with debug.h */ /* @@ -310,8 +305,6 @@ dbg_dump_fs(struct fs *sb, const char *comment) sb->fs_avgfilesize); fprintf(dbg_log, "avgfpdir int32_t 0x%08x\n", sb->fs_avgfpdir); - fprintf(dbg_log, "save_cgsize int32_t 0x%08x\n", - sb->fs_save_cgsize); fprintf(dbg_log, "flags int32_t 0x%08x\n", sb->fs_flags); fprintf(dbg_log, "contigsumsize int32_t 0x%08x\n", diff --git a/sbin/growfs/growfs.8 b/sbin/growfs/growfs.8 index f83c3c00cf1f..f23817b0afbe 100644 --- a/sbin/growfs/growfs.8 +++ b/sbin/growfs/growfs.8 @@ -36,7 +36,7 @@ .\" .\" $TSHeader: src/sbin/growfs/growfs.8,v 1.3 2000/12/12 19:31:00 tomsoft Exp $ .\" -.Dd December 13, 2017 +.Dd January 23, 2025 .Dt GROWFS 8 .Os .Sh NAME @@ -55,22 +55,15 @@ Before running .Nm the partition or slice containing the file system must be extended using .Xr gpart 8 . -If you are using volumes you must enlarge them by using -.Xr gvinum 8 . The .Nm utility extends the size of the file system on the specified special file. The following options are available: -.Bl -tag -width indent +.Bl -tag -width "-s size" .It Fl N .Dq Test mode . Causes the new file system parameters to be printed out without actually enlarging the file system. -.It Fl y -Causes -.Nm -to assume yes -as the answer to all operator questions. .It Fl s Ar size Determines the .Ar size @@ -87,7 +80,16 @@ This value defaults to the size of the raw partition specified in (in other words, .Nm will enlarge the file system to the size of the entire partition). +.It Fl y +Causes +.Nm +to assume yes +as the answer to all operator questions. .El +.Sh EXIT STATUS +Exit status is 0 on success, and >= 1 on errors. +Errors recoverable by user action are indicated by 2. +OS errors, which are usually not recoverable, are indicated by 3 or greater. .Sh EXAMPLES Expand root file system to fill up available space: .Dl growfs / diff --git a/sbin/growfs/growfs.c b/sbin/growfs/growfs.c index fc314d19a4e3..9a48287107e7 100644 --- a/sbin/growfs/growfs.c +++ b/sbin/growfs/growfs.c @@ -45,14 +45,6 @@ * */ -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz\n\ -Copyright (c) 1980, 1989, 1993 The Regents of the University of California.\n\ -All rights reserved.\n"; -#endif /* not lint */ - -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioctl.h> #include <sys/stat.h> @@ -147,7 +139,7 @@ growfs(int fsi, int fso, unsigned int Nflag) */ fscs = (struct csum *)calloc((size_t)1, (size_t)sblock.fs_cssize); if (fscs == NULL) - errx(1, "calloc failed"); + errx(3, "calloc failed"); memcpy(fscs, osblock.fs_csp, osblock.fs_cssize); free(osblock.fs_csp); osblock.fs_csp = NULL; @@ -240,7 +232,7 @@ growfs(int fsi, int fso, unsigned int Nflag) * * We probably should rather change the summary for the cylinder group * statistics here to the value of what would be in there, if the file - * system were created initially with the new size. Therefor we still + * system were created initially with the new size. Therefore we still * need to find an easy way of calculating that. * Possibly we can try to read the first superblock copy and apply the * "diffed" stats between the old and new superblock by still copying @@ -259,7 +251,7 @@ growfs(int fsi, int fso, unsigned int Nflag) * and all the alternates back to disk. */ if (!Nflag && sbput(fso, &sblock, sblock.fs_ncg) != 0) - errc(2, EIO, "could not write updated superblock"); + errc(3, EIO, "could not write updated superblock"); DBG_PRINT0("fscs written\n"); #ifdef FS_DEBUG @@ -687,7 +679,7 @@ updjcg(int cylno, time_t modtime, int fsi, int fso, unsigned int Nflag) sblock.fs_frag); } else { /* - * Lets rejoin a possible partially growed + * Lets rejoin a possible partially grown * fragment. */ k = 0; @@ -713,7 +705,7 @@ updjcg(int cylno, time_t modtime, int fsi, int fso, unsigned int Nflag) j++; } /* - * Lets rejoin a possible partially growed fragment. + * Lets rejoin a possible partially grown fragment. */ k = 0; while (isset(cg_blksfree(&acg), i) && @@ -741,7 +733,7 @@ updjcg(int cylno, time_t modtime, int fsi, int fso, unsigned int Nflag) } /* - * Handle the last new block if there are stll some new fragments left. + * Handle the last new block if there are still some new fragments left. * Here we don't have to bother about the cluster summary or the even * the rotational layout table. */ @@ -1339,7 +1331,7 @@ main(int argc, char **argv) size <<= 30; size <<= 10; } else - errx(1, "unknown suffix on -s argument"); + errx(2, "unknown suffix on -s argument"); break; case 'v': /* for compatibility to newfs */ break; @@ -1364,23 +1356,23 @@ main(int argc, char **argv) statfsp = getmntpoint(*argv); device = getdev(*argv, statfsp); if (device == NULL) - errx(1, "cannot find special device for %s", *argv); + errx(2, "cannot find special device for %s", *argv); fsi = open(device, O_RDONLY); if (fsi < 0) - err(1, "%s", device); + err(3, "%s", device); /* * Try to guess the slice size if not specified. */ if (ioctl(fsi, DIOCGMEDIASIZE, &mediasize) == -1) - err(1,"DIOCGMEDIASIZE"); + err(3,"DIOCGMEDIASIZE"); /* * Check if that partition is suitable for growing a file system. */ if (mediasize < 1) - errx(1, "partition is unavailable"); + errx(2, "partition is unavailable"); /* * Read the current superblock, and take a backup. @@ -1388,16 +1380,16 @@ main(int argc, char **argv) if ((ret = sbget(fsi, &fs, UFS_STDSB, 0)) != 0) { switch (ret) { case ENOENT: - errx(1, "superblock not recognized"); + errx(2, "superblock not recognized"); default: - errc(1, ret, "unable to read superblock"); + errc(3, ret, "unable to read superblock"); } } /* * Check for filesystem that was unclean at mount time. */ if ((fs->fs_flags & (FS_UNCLEAN | FS_NEEDSFSCK)) != 0) - errx(1, "%s is not clean - run fsck.\n", *argv); + errx(2, "%s is not clean - run fsck.\n", *argv); memcpy(&osblock, fs, fs->fs_sbsize); free(fs); memcpy((void *)&fsun1, (void *)&fsun2, osblock.fs_sbsize); @@ -1418,7 +1410,7 @@ main(int argc, char **argv) mediasize, "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); - errx(1, "requested size %s is larger " + errx(2, "requested size %s is larger " "than the available %s", oldsizebuf, newsizebuf); } } @@ -1439,7 +1431,7 @@ main(int argc, char **argv) if (size == (uint64_t)(osblock.fs_size * osblock.fs_fsize)) errx(0, "requested size %s is equal to the current " "filesystem size %s", newsizebuf, oldsizebuf); - errx(1, "requested size %s is smaller than the current " + errx(2, "requested size %s is smaller than the current " "filesystem size %s", newsizebuf, oldsizebuf); } @@ -1451,7 +1443,7 @@ main(int argc, char **argv) * Are we really growing? */ if (osblock.fs_size >= sblock.fs_size) { - errx(1, "we are not growing (%jd->%jd)", + errx(3, "we are not growing (%jd->%jd)", (intmax_t)osblock.fs_size, (intmax_t)sblock.fs_size); } @@ -1461,7 +1453,7 @@ main(int argc, char **argv) if (yflag == 0) { for (j = 0; j < FSMAXSNAP; j++) { if (sblock.fs_snapinum[j]) { - errx(1, "active snapshot found in file system; " + errx(2, "active snapshot found in file system; " "please remove all snapshots before " "using growfs"); } @@ -1506,14 +1498,14 @@ main(int argc, char **argv) if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) == 0) { fso = open(_PATH_UFSSUSPEND, O_RDWR); if (fso == -1) - err(1, "unable to open %s", _PATH_UFSSUSPEND); + err(3, "unable to open %s", _PATH_UFSSUSPEND); error = ioctl(fso, UFSSUSPEND, &statfsp->f_fsid); if (error != 0) - err(1, "UFSSUSPEND"); + err(3, "UFSSUSPEND"); } else { fso = open(device, O_WRONLY); if (fso < 0) - err(1, "%s", device); + err(3, "%s", device); } } @@ -1522,7 +1514,7 @@ main(int argc, char **argv) */ testbuf = malloc(sblock.fs_fsize); if (testbuf == NULL) - err(1, "malloc"); + err(3, "malloc"); rdfs((ufs2_daddr_t)((size - sblock.fs_fsize) / DEV_BSIZE), sblock.fs_fsize, testbuf, fsi); wtfs((ufs2_daddr_t)((size - sblock.fs_fsize) / DEV_BSIZE), @@ -1577,7 +1569,7 @@ main(int argc, char **argv) fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum)); if (osblock.fs_size >= sblock.fs_size) - errx(1, "not enough new space"); + errx(3, "not enough new space"); DBG_PRINT0("sblock calculated\n"); @@ -1591,11 +1583,11 @@ main(int argc, char **argv) if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) == 0) { error = ioctl(fso, UFSRESUME); if (error != 0) - err(1, "UFSRESUME"); + err(3, "UFSRESUME"); } error = close(fso); if (error != 0) - err(1, "close"); + err(3, "close"); if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) != 0 && chkdoreload(statfsp, warn) != 0) exit(9); diff --git a/sbin/growfs/tests/Makefile b/sbin/growfs/tests/Makefile index 270eb46ce958..a23b430f58fa 100644 --- a/sbin/growfs/tests/Makefile +++ b/sbin/growfs/tests/Makefile @@ -1,4 +1,3 @@ - TAP_TESTS_PERL= legacy_test .include <bsd.test.mk> 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/gvinum/Makefile b/sbin/gvinum/Makefile deleted file mode 100644 index 8ffabc694c31..000000000000 --- a/sbin/gvinum/Makefile +++ /dev/null @@ -1,14 +0,0 @@ - -PACKAGE=geom -PROG= gvinum -SRCS= gvinum.c gvinum.h geom_vinum_share.c -MAN= gvinum.8 - -WARNS?= 2 -CFLAGS+= -I${SRCTOP}/sys -I${SYSROOT:U${DESTDIR}}/${INCLUDEDIR}/edit - -LIBADD= edit geom - -.PATH: ${SRCTOP}/sys/geom/vinum - -.include <bsd.prog.mk> diff --git a/sbin/gvinum/gvinum.8 b/sbin/gvinum/gvinum.8 deleted file mode 100644 index 8bf2fcc92b8e..000000000000 --- a/sbin/gvinum/gvinum.8 +++ /dev/null @@ -1,460 +0,0 @@ -.\" Copyright (c) 2005 Chris Jones -.\" All rights reserved. -.\" -.\" This software was developed for the FreeBSD Project by Chris Jones -.\" thanks to the support of Google's Summer of Code program and -.\" mentoring by Lukas Ertl. -.\" -.\" 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 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 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. -.\" -.Dd March 28, 2021 -.Dt GVINUM 8 -.Os -.Sh NAME -.Nm gvinum -.Nd Logical Volume Manager control program -.Sh DEPRECATION NOTICE -.Nm -and associated -.Xr geom 4 -kernel support is deprecated, and may not be available in -.Fx 14.0 -and later. -Users are advised to migrate to -.Xr gconcat 8 , -.Xr gmirror 8 , -.Xr gstripe 8 , -.Xr graid 8 , -or -.Xr zfs 8 . -More information is available at -.Pa https://wiki.freebsd.org/DeprecationPlan/gvinum . -.Sh SYNOPSIS -.Nm -.Op Ar command -.Op Fl options -.Sh COMMANDS -.Bl -tag -width indent -.It Ic attach Ar plex volume Op Cm rename -.It Ic attach Ar subdisk plex Oo Ar offset Oc Op Cm rename -Attach a plex to a volume, or a subdisk to a plex. -If offset is specified, the subdisk will be attached to the given offset within -the plex. -If rename is specified, the subdisk or plex will change name according to the -object it attaches to. -.It Ic checkparity Oo Fl f Oc Ar plex -Check the parity blocks of a RAID-5 plex. -The parity check will start at the -beginning of the plex if the -.Fl f -flag is specified, or otherwise at the location of the parity check pointer, -the first location at which plex's parity is incorrect. -All subdisks in the -plex must be up for a parity check. -.It Ic concat Oo Fl fv Oc Oo Fl n Ar name Oc Ar drives -Create a concatenated volume from the specified drives. -If no name is specified, a unique name will be set by -.Ic gvinum . -.It Ic create Oo Fl f Oc Op Ar description-file -Create a volume as described in -.Ar description-file . -If no -.Ar description-file -provided, opens an editor and provides the current -.Nm -configuration for editing. -The -.Fl f -flag will make gvinum ignore any errors regarding creating objects that already -exists. -However, in contrast to vinum, objects that are not properly named in the -.Ar description-file -will not be created when the -.Fl f -flag is given. -.It Ic detach Oo Fl f Oc Op Ar plex | subdisk -Detach a plex or subdisk from the volume or plex to which it is -attached. -.It Ic grow Ar plex device -Grow a plex by creating a gvinum drive and subdisk on device and attach it to -the plex. -If required by the plex organization, it will be put into the growable state. -.It Ic help -Provides a synopsis of -.Nm -commands and arguments. -.It Ic l | list Oo Fl rvV Oc Op Ar volume | plex | subdisk -.It Ic ld Oo Fl rvV Oc Op Ar drive ... -.It Ic ls Oo Fl rvV Oc Op Ar subdisk ... -.It Ic lp Oo Fl rvV Oc Op Ar plex ... -.It Ic lv Oo Fl rvV Oc Op Ar volume ... -List information about the relevant object(s). -The -.Fl r -flag provides recursive display, showing each object's subordinate objects in -proper relation. -The -.Fl v -and -.Fl V -flags provide progressively more detailed output. -.It Ic mirror Oo Fl fsv Oc Oo Fl n Ar name Oc Ar drives -Create a mirrored volume from the specified drives. -It requires at least a multiple of 2 drives. -If no name is specified, a unique name will be set by gvinum. -If the -.Fl s -flag is specified, a striped mirror will be created, and thus requires a -multiple of 4 drives. -.It Ic move | mv Fl f Ar drive subdisk Op Ar ... -Move the subdisk(s) to the specified drive. -The -.Fl f -flag is required, as all data on the indicated subdisk(s) will be destroyed as -part of the move. -This can currently only be done when the subdisk is -not being accessed. -.Pp -If a single subdisk is moved, and it forms a part of a RAID-5 plex, the moved -subdisks will need to be set to the -.Dq stale -state, and the plex will require a -.Ic start -command. -If multiple subdisk(s) is moved, and form part of a RAID-5 plex, the -moved disk(s) will need to be set to the -.Dq up -state and the plex will require a -.Ic rebuildparity -command. -If the subdisk(s) form part of a plex that is mirrored with other -plexes, the plex will require restarting and will sync once restarted. -Moving -more than one subdisk in a RAID-5 plex or subdisks from both sides of a -mirrored plex volume will destroy data. -Note that parity rebuilds and syncing -must be started manually after a move. -.It Ic printconfig -Write a copy of the current configuration to standard output. -.It Ic quit -Exit -.Nm -when running in interactive mode. -Normally this would be done by entering the -EOF character. -.It Ic raid5 Oo Fl fv Oc Oo Fl s Ar stripesize Oc Oo Fl n Ar name Oc Ar drives -Create a RAID-5 volume from the specified drives. -If no name is specified, a unique name will be set by -.Ic gvinum . -This organization requires at least three drives. -.It Ic rename Oo Fl r Oc Ar drive | subdisk | plex | volume newname -Change the name of the specified object. -The -.Fl r -flag will recursively rename subordinate objects. -.Pp -Note that device nodes will not be renamed until -.Nm -is restarted. -.It Ic rebuildparity Oo Fl f Oc Ar plex -Rebuild the parity blocks of a RAID-5 plex. -The parity rebuild will start at -the beginning of the plex if the -.Fl f -flag is specified, or otherwise at the location of the parity check pointer. -All subdisks in the plex must be up for a parity check. -.It Ic resetconfig Oo Fl f Oc -Reset the complete -.Nm -configuration. -.It Ic rm Oo Fl r Oc Ar volume | plex | subdisk -Remove an object and, if -.Fl r -is specified, its subordinate objects. -.It Ic saveconfig -Save -.Nm -configuration to disk after configuration failures. -.It Ic setstate Oo Fl f Oc Ar state volume | plex | subdisk | drive -Set state without influencing other objects, for diagnostic purposes -only. -The -.Fl f -flag forces state changes regardless of whether they are legal. -.It Ic start -Read configuration from all vinum drives. -.It Ic start Oo Fl S Ar size Oc Ar volume | plex | subdisk -Allow the system to access the objects. -If necessary, plexes will be synced and rebuilt. -If a subdisk was added to a running RAID-5 or striped plex, gvinum will -expand into this subdisk and grow the whole RAID-5 array. -This can be done without unmounting your filesystem. -The -.Fl S -flag is currently ignored. -.It Ic stop Oo Fl f Oc Op Ar volume | plex | subdisk -Terminate access to the objects, or stop -.Nm -if no parameters are specified. -.It Ic stripe Oo Fl fv Oc Oo Fl n Ar name Oc Ar drives -Create a striped volume from the specified drives. -If no name is specified, a unique name will be set by -.Ic gvinum . -This organization requires at least two drives. -.El -.Sh DESCRIPTION -The -.Nm -utility communicates with the kernel component of the GVinum logical volume -manager. -It is designed either for interactive use, when started without -command line arguments, or to execute a single command if the command is -supplied on the command line. -In interactive mode, -.Nm -maintains a command line history. -.Sh OPTIONS -The -.Nm -commands may be followed by an option. -.Bl -tag -width indent -.It Fl f -The -.Fl f -.Pq Dq force -option overrides safety checks. -It should be used with extreme caution. -This -option is required in order to use the -.Ic move -command. -.It Fl r -The -.Fl r -.Pq Dq recursive -option applies the command recursively to subordinate objects. -For example, in -conjunction with the -.Ic lv -command, the -.Fl r -option will also show information about the plexes and subdisks belonging to -the volume. -It is also used by the -.Ic rename -command to indicate that subordinate objects such as subdisks should be renamed -to match the object(s) specified and by the -.Ic rm -command to delete plexes belonging to a volume and so on. -.It Fl v -The -.Fl v -.Pq Dq verbose -option provides more detailed output. -.It Fl V -The -.Fl V -.Pq Dq "very verbose" -option provides even more detailed output than -.Fl v . -.El -.Sh ENVIRONMENT -.Bl -tag -width ".Ev EDITOR" -.It Ev EDITOR -The name of the editor to use for editing configuration files, by -default -.Xr vi 1 -is invoked. -.El -.Sh FILES -.Bl -tag -width ".Pa /dev/gvinum/plex" -.It Pa /dev/gvinum -directory with device nodes for -.Nm -objects -.El -.Sh EXAMPLES -To create a mirror on disks /dev/ada1 and /dev/ada2, create a filesystem, -mount, unmount and then stop -.Ic gvinum : -.Pp -.Dl "gvinum mirror /dev/ada1 /dev/ada2" -.Dl "newfs /dev/gvinum/gvinumvolume0" -.Dl "mount /dev/gvinum/gvinumvolume0 /mnt" -.Dl "..." -.Dl "unmount /mnt" -.Dl "gvinum stop" -.Pp -To create a striped mirror on disks /dev/ada1 /dev/ada2 /dev/ada3 and -/dev/ada4 named "data" and create a filesystem: -.Pp -.Dl "gvinum mirror -s -n data /dev/ada1 /dev/ada2 /dev/ada3 /dev/ada4" -.Dl "newfs /dev/gvinum/data" -.Pp -To create a raid5 array on disks /dev/ada1 /dev/ada2 and /dev/ada3, -with stripesize 493k you can use the raid5 command: -.Pp -.Dl "gvinum raid5 -s 493k /dev/ada1 /dev/ada2 /dev/ada3" -.Pp -Then the volume will be created automatically. -Afterwards, you have to initialize the volume: -.Pp -.Dl "gvinum start myraid5vol" -.Pp -The initialization will start, and the states will be updated when it's -finished. -The list command will give you information about its progress. -.Pp -Imagine that one of the drives fails, and the output of 'printconfig' looks -something like this: -.Pp -.Dl "drive gvinumdrive1 device /dev/ada2" -.Dl "drive gvinumdrive2 device /dev/???" -.Dl "drive gvinumdrive0 device /dev/ada1" -.Dl "volume myraid5vol" -.Dl "plex name myraid5vol.p0 org raid5 986s vol myraid5vol" -.Dl "sd name myraid5vol.p0.s2 drive gvinumdrive2 len 32538s driveoffset 265s" -.Dl "plex myraid5vol.p0 plexoffset 1972s" -.Dl "sd name myraid5vol.p0.s1 drive gvinumdrive1 len 32538s driveoffset 265s" -.Dl "plex myraid5vol.p0 plexoffset 986s" -.Dl "sd name myraid5vol.p0.s0 drive gvinumdrive0 len 32538s driveoffset 265s" -.Dl "plex myraid5vol.p0 plexoffset 0s" -.Pp -Create a new drive with this configuration: -.Pp -.Dl "drive gdrive4 device /dev/ada4" -.Pp -Then move the stale subdisk to the new drive: -.Pp -.Dl "gvinum move gdrive4 myraid5vol.p0.s2" -.Pp -Then, initiate the rebuild: -.Pp -.Dl "gvinum start myraid5vol.p0" -.Pp -The plex will go up form degraded mode after the rebuild is finished. -The plex can still be used while the rebuild is in progress, although requests -might be delayed. -.Pp -Given the configuration as in the previous example, growing a RAID-5 or STRIPED -array is accomplished by using the grow command: -.Pp -.Dl "gvinum grow myraid5vol.p0 /dev/ada4" -.Pp -If everything went ok, the plex state should now be set to growable. -You can then start the growing with the -.Ic start -command: -.Pp -.Dl "gvinum start myraid5vol.p0" -.Pp -As with rebuilding, you can watch the progress using the -.Ic list -command. -.Pp -For a more advanced usage and detailed explanation of gvinum, the -handbook is recommended. -.Sh SEE ALSO -.Xr geom 4 , -.Xr geom 8 -.Sh HISTORY -The -.Nm -utility first appeared in -.Fx 5.3 . -The -.Nm vinum -utility, on which -.Nm -is based, was written by -.An "Greg Lehey" . -.Pp -The -.Nm -utility -was written by -.An "Lukas Ertl" . -The -.Ic move -and -.Ic rename -commands and -documentation were added by -.An "Chris Jones" -through the 2005 Google Summer -of Code program. -A partial rewrite of gvinum was done by -.An "Lukas Ertl" -and -.An "Ulf Lilleengen" -through the 2007 Google Summer of Code program. -The documentation have been updated to reflect the new functionality. -.Sh AUTHORS -.An Lukas Ertl Aq Mt le@FreeBSD.org -.An Chris Jones Aq Mt soc-cjones@FreeBSD.org -.An Ulf Lilleengen Aq Mt lulf@FreeBSD.org -.Sh BUGS -Currently, -.Nm -does not rename devices in -.Pa /dev/gvinum -until reloaded. -.Pp -The -.Fl S -initsize flag to -.Ic start -is ignored. -.Pp -Moving subdisks that are not part of a mirrored or RAID-5 volume will -destroy data. -It is perhaps a bug to permit this. -.Pp -Plexes in which subdisks have been moved do not automatically sync or -rebuild parity. -This may leave data unprotected and is perhaps unwise. -.Pp -Currently, -.Nm -does not yet fully implement all of the functions found in -.Nm vinum . -Specifically, the following commands from -.Nm vinum -are not supported: -.Bl -tag -width indent -.It Ic debug -Cause the volume manager to enter the kernel debugger. -.It Ic debug Ar flags -Set debugging flags. -.It Ic dumpconfig Op Ar drive ... -List the configuration information stored on the specified drives, or all -drives in the system if no drive names are specified. -.It Ic info Op Fl vV -List information about volume manager state. -.It Ic label Ar volume -Create a volume label. -.It Ic resetstats Oo Fl r Oc Op Ar volume | plex | subdisk -Reset statistics counters for the specified objects, or for all objects if none -are specified. -.It Ic setdaemon Op Ar value -Set daemon configuration. -.El diff --git a/sbin/gvinum/gvinum.c b/sbin/gvinum/gvinum.c deleted file mode 100644 index c391f5d61c65..000000000000 --- a/sbin/gvinum/gvinum.c +++ /dev/null @@ -1,1450 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2004 Lukas Ertl - * Copyright (c) 2005 Chris Jones - * Copyright (c) 2007 Ulf Lilleengen - * All rights reserved. - * - * Portions of this software were developed for the FreeBSD Project - * by Chris Jones thanks to the support of Google's Summer of Code - * program and mentoring by Lukas Ertl. - * - * 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 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 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/param.h> -#include <sys/linker.h> -#include <sys/lock.h> -#include <sys/module.h> -#include <sys/mutex.h> -#include <sys/queue.h> -#include <sys/utsname.h> - -#include <geom/vinum/geom_vinum_var.h> -#include <geom/vinum/geom_vinum_share.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <libgeom.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <paths.h> -#include <readline/readline.h> -#include <readline/history.h> -#include <unistd.h> - -#include "gvinum.h" - -static void gvinum_attach(int, char * const *); -static void gvinum_concat(int, char * const *); -static void gvinum_create(int, char * const *); -static void gvinum_detach(int, char * const *); -static void gvinum_grow(int, char * const *); -static void gvinum_help(void); -static void gvinum_list(int, char * const *); -static void gvinum_move(int, char * const *); -static void gvinum_mirror(int, char * const *); -static void gvinum_parityop(int, char * const * , int); -static void gvinum_printconfig(int, char * const *); -static void gvinum_raid5(int, char * const *); -static void gvinum_rename(int, char * const *); -static void gvinum_resetconfig(int, char * const *); -static void gvinum_rm(int, char * const *); -static void gvinum_saveconfig(void); -static void gvinum_setstate(int, char * const *); -static void gvinum_start(int, char * const *); -static void gvinum_stop(int, char * const *); -static void gvinum_stripe(int, char * const *); -static void parseline(int, char * const *); -static void printconfig(FILE *, const char *); - -static char *create_drive(const char *); -static void create_volume(int, char * const * , const char *); -static char *find_name(const char *, int, int); -static const char *find_pattern(char *, const char *); -static void copy_device(struct gv_drive *, const char *); -#define find_drive() \ - find_name("gvinumdrive", GV_TYPE_DRIVE, GV_MAXDRIVENAME) - -int -main(int argc, char **argv) -{ - int tokens; - char buffer[BUFSIZ], *inputline, *token[GV_MAXARGS]; - - /* Load the module if necessary. */ - if (modfind(GVINUMMOD) < 0) { - if (kldload(GVINUMKLD) < 0 && modfind(GVINUMMOD) < 0) - err(1, GVINUMKLD ": Kernel module not available"); - } - - /* Arguments given on the command line. */ - if (argc > 1) { - argc--; - argv++; - parseline(argc, argv); - - /* Interactive mode. */ - } else { - for (;;) { - inputline = readline("gvinum -> "); - if (inputline == NULL) { - if (ferror(stdin)) { - err(1, "can't read input"); - } else { - printf("\n"); - exit(0); - } - } else if (*inputline) { - add_history(inputline); - strcpy(buffer, inputline); - free(inputline); - tokens = gv_tokenize(buffer, token, GV_MAXARGS); - if (tokens) - parseline(tokens, token); - } - } - } - exit(0); -} - -/* Attach a plex to a volume or a subdisk to a plex. */ -static void -gvinum_attach(int argc, char * const *argv) -{ - struct gctl_req *req; - const char *errstr; - int rename; - off_t offset; - - rename = 0; - offset = -1; - if (argc < 3) { - warnx("usage:\tattach <subdisk> <plex> [rename] " - "[<plexoffset>]\n" - "\tattach <plex> <volume> [rename]"); - return; - } - if (argc > 3) { - if (!strcmp(argv[3], "rename")) { - rename = 1; - if (argc == 5) - offset = strtol(argv[4], NULL, 0); - } else - offset = strtol(argv[3], NULL, 0); - } - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "attach"); - gctl_ro_param(req, "child", -1, argv[1]); - gctl_ro_param(req, "parent", -1, argv[2]); - gctl_ro_param(req, "offset", sizeof(off_t), &offset); - gctl_ro_param(req, "rename", sizeof(int), &rename); - errstr = gctl_issue(req); - if (errstr != NULL) - warnx("attach failed: %s", errstr); - gctl_free(req); -} - -static void -gvinum_create(int argc, char * const *argv) -{ - struct gctl_req *req; - struct gv_drive *d; - struct gv_plex *p; - struct gv_sd *s; - struct gv_volume *v; - FILE *tmp; - int drives, errors, fd, flags, i, line, plexes, plex_in_volume; - int sd_in_plex, status, subdisks, tokens, undeffd, volumes; - const char *errstr; - char buf[BUFSIZ], buf1[BUFSIZ], commandline[BUFSIZ], *sdname; - const char *ed; - char original[BUFSIZ], tmpfile[20], *token[GV_MAXARGS]; - char plex[GV_MAXPLEXNAME], volume[GV_MAXVOLNAME]; - - tmp = NULL; - flags = 0; - for (i = 1; i < argc; i++) { - /* Force flag used to ignore already created drives. */ - if (!strcmp(argv[i], "-f")) { - flags |= GV_FLAG_F; - /* Else it must be a file. */ - } else { - if ((tmp = fopen(argv[i], "r")) == NULL) { - warn("can't open '%s' for reading", argv[i]); - return; - } - } - } - - /* We didn't get a file. */ - if (tmp == NULL) { - snprintf(tmpfile, sizeof(tmpfile), "/tmp/gvinum.XXXXXX"); - - if ((fd = mkstemp(tmpfile)) == -1) { - warn("temporary file not accessible"); - return; - } - if ((tmp = fdopen(fd, "w")) == NULL) { - warn("can't open '%s' for writing", tmpfile); - return; - } - printconfig(tmp, "# "); - fclose(tmp); - - ed = getenv("EDITOR"); - if (ed == NULL) - ed = _PATH_VI; - - snprintf(commandline, sizeof(commandline), "%s %s", ed, - tmpfile); - status = system(commandline); - if (status != 0) { - warn("couldn't exec %s; status: %d", ed, status); - return; - } - - if ((tmp = fopen(tmpfile, "r")) == NULL) { - warn("can't open '%s' for reading", tmpfile); - return; - } - } - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "create"); - gctl_ro_param(req, "flags", sizeof(int), &flags); - - drives = volumes = plexes = subdisks = 0; - plex_in_volume = sd_in_plex = undeffd = 0; - plex[0] = '\0'; - errors = 0; - line = 1; - while ((fgets(buf, BUFSIZ, tmp)) != NULL) { - - /* Skip empty lines and comments. */ - if (*buf == '\0' || *buf == '#') { - line++; - continue; - } - - /* Kill off the newline. */ - buf[strlen(buf) - 1] = '\0'; - - /* - * Copy the original input line in case we need it for error - * output. - */ - strlcpy(original, buf, sizeof(original)); - - tokens = gv_tokenize(buf, token, GV_MAXARGS); - if (tokens <= 0) { - line++; - continue; - } - - /* Volume definition. */ - if (!strcmp(token[0], "volume")) { - v = gv_new_volume(tokens, token); - if (v == NULL) { - warnx("line %d: invalid volume definition", - line); - warnx("line %d: '%s'", line, original); - errors++; - line++; - continue; - } - - /* Reset plex count for this volume. */ - plex_in_volume = 0; - - /* - * Set default volume name for following plex - * definitions. - */ - strlcpy(volume, v->name, sizeof(volume)); - - snprintf(buf1, sizeof(buf1), "volume%d", volumes); - gctl_ro_param(req, buf1, sizeof(*v), v); - volumes++; - - /* Plex definition. */ - } else if (!strcmp(token[0], "plex")) { - p = gv_new_plex(tokens, token); - if (p == NULL) { - warnx("line %d: invalid plex definition", line); - warnx("line %d: '%s'", line, original); - errors++; - line++; - continue; - } - - /* Reset subdisk count for this plex. */ - sd_in_plex = 0; - - /* Default name. */ - if (strlen(p->name) == 0) { - snprintf(p->name, sizeof(p->name), "%s.p%d", - volume, plex_in_volume++); - } - - /* Default volume. */ - if (strlen(p->volume) == 0) { - snprintf(p->volume, sizeof(p->volume), "%s", - volume); - } - - /* - * Set default plex name for following subdisk - * definitions. - */ - strlcpy(plex, p->name, sizeof(plex)); - - snprintf(buf1, sizeof(buf1), "plex%d", plexes); - gctl_ro_param(req, buf1, sizeof(*p), p); - plexes++; - - /* Subdisk definition. */ - } else if (!strcmp(token[0], "sd")) { - s = gv_new_sd(tokens, token); - if (s == NULL) { - warnx("line %d: invalid subdisk " - "definition:", line); - warnx("line %d: '%s'", line, original); - errors++; - line++; - continue; - } - - /* Default name. */ - if (strlen(s->name) == 0) { - if (strlen(plex) == 0) { - sdname = find_name("gvinumsubdisk.p", - GV_TYPE_SD, GV_MAXSDNAME); - snprintf(s->name, sizeof(s->name), - "%s.s%d", sdname, undeffd++); - free(sdname); - } else { - snprintf(s->name, sizeof(s->name), - "%s.s%d",plex, sd_in_plex++); - } - } - - /* Default plex. */ - if (strlen(s->plex) == 0) - snprintf(s->plex, sizeof(s->plex), "%s", plex); - - snprintf(buf1, sizeof(buf1), "sd%d", subdisks); - gctl_ro_param(req, buf1, sizeof(*s), s); - subdisks++; - - /* Subdisk definition. */ - } else if (!strcmp(token[0], "drive")) { - d = gv_new_drive(tokens, token); - if (d == NULL) { - warnx("line %d: invalid drive definition:", - line); - warnx("line %d: '%s'", line, original); - errors++; - line++; - continue; - } - - snprintf(buf1, sizeof(buf1), "drive%d", drives); - gctl_ro_param(req, buf1, sizeof(*d), d); - drives++; - - /* Everything else is bogus. */ - } else { - warnx("line %d: invalid definition:", line); - warnx("line %d: '%s'", line, original); - errors++; - } - line++; - } - - fclose(tmp); - unlink(tmpfile); - - if (!errors && (volumes || plexes || subdisks || drives)) { - gctl_ro_param(req, "volumes", sizeof(int), &volumes); - gctl_ro_param(req, "plexes", sizeof(int), &plexes); - gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); - gctl_ro_param(req, "drives", sizeof(int), &drives); - errstr = gctl_issue(req); - if (errstr != NULL) - warnx("create failed: %s", errstr); - } - gctl_free(req); -} - -/* Create a concatenated volume. */ -static void -gvinum_concat(int argc, char * const *argv) -{ - - if (argc < 2) { - warnx("usage:\tconcat [-fv] [-n name] drives\n"); - return; - } - create_volume(argc, argv, "concat"); -} - -/* Create a drive quick and dirty. */ -static char * -create_drive(const char *device) -{ - struct gv_drive *d; - struct gctl_req *req; - const char *errstr; - char *drivename, *dname; - int drives, i, flags, volumes, subdisks, plexes; - int found = 0; - - flags = plexes = subdisks = volumes = 0; - drives = 1; - dname = NULL; - - drivename = find_drive(); - if (drivename == NULL) - return (NULL); - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "create"); - d = gv_alloc_drive(); - if (d == NULL) - err(1, "unable to allocate for gv_drive object"); - - strlcpy(d->name, drivename, sizeof(d->name)); - copy_device(d, device); - gctl_ro_param(req, "drive0", sizeof(*d), d); - gctl_ro_param(req, "flags", sizeof(int), &flags); - gctl_ro_param(req, "drives", sizeof(int), &drives); - gctl_ro_param(req, "volumes", sizeof(int), &volumes); - gctl_ro_param(req, "plexes", sizeof(int), &plexes); - gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); - errstr = gctl_issue(req); - if (errstr != NULL) { - warnx("error creating drive: %s", errstr); - drivename = NULL; - } else { - /* XXX: This is needed because we have to make sure the drives - * are created before we return. */ - /* Loop until it's in the config. */ - for (i = 0; i < 100000; i++) { - dname = find_name("gvinumdrive", GV_TYPE_DRIVE, - GV_MAXDRIVENAME); - /* If we got a different name, quit. */ - if (dname == NULL) - continue; - if (strcmp(dname, drivename)) - found = 1; - free(dname); - dname = NULL; - if (found) - break; - usleep(100000); /* Sleep for 0.1s */ - } - if (found == 0) { - warnx("error creating drive"); - drivename = NULL; - } - } - gctl_free(req); - return (drivename); -} - -/* - * General routine for creating a volume. Mainly for use by concat, mirror, - * raid5 and stripe commands. - */ -static void -create_volume(int argc, char * const *argv, const char *verb) -{ - struct gctl_req *req; - const char *errstr; - char buf[BUFSIZ], *drivename, *volname; - int drives, flags, i; - off_t stripesize; - - flags = 0; - drives = 0; - volname = NULL; - stripesize = 262144; - - /* XXX: Should we check for argument length? */ - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - - for (i = 1; i < argc; i++) { - if (!strcmp(argv[i], "-f")) { - flags |= GV_FLAG_F; - } else if (!strcmp(argv[i], "-n")) { - volname = argv[++i]; - } else if (!strcmp(argv[i], "-v")) { - flags |= GV_FLAG_V; - } else if (!strcmp(argv[i], "-s")) { - flags |= GV_FLAG_S; - if (!strcmp(verb, "raid5")) - stripesize = gv_sizespec(argv[++i]); - } else { - /* Assume it's a drive. */ - snprintf(buf, sizeof(buf), "drive%d", drives++); - - /* First we create the drive. */ - drivename = create_drive(argv[i]); - if (drivename == NULL) - goto bad; - /* Then we add it to the request. */ - gctl_ro_param(req, buf, -1, drivename); - } - } - - gctl_ro_param(req, "stripesize", sizeof(off_t), &stripesize); - - /* Find a free volume name. */ - if (volname == NULL) - volname = find_name("gvinumvolume", GV_TYPE_VOL, GV_MAXVOLNAME); - - /* Then we send a request to actually create the volumes. */ - gctl_ro_param(req, "verb", -1, verb); - gctl_ro_param(req, "flags", sizeof(int), &flags); - gctl_ro_param(req, "drives", sizeof(int), &drives); - gctl_ro_param(req, "name", -1, volname); - errstr = gctl_issue(req); - if (errstr != NULL) - warnx("creating %s volume failed: %s", verb, errstr); -bad: - gctl_free(req); -} - -/* Parse a line of the config, return the word after <pattern>. */ -static const char * -find_pattern(char *line, const char *pattern) -{ - char *ptr; - - ptr = strsep(&line, " "); - while (ptr != NULL) { - if (!strcmp(ptr, pattern)) { - /* Return the next. */ - ptr = strsep(&line, " "); - return (ptr); - } - ptr = strsep(&line, " "); - } - return (NULL); -} - -/* Find a free name for an object given a prefix. */ -static char * -find_name(const char *prefix, int type, int namelen) -{ - struct gctl_req *req; - char comment[1], buf[GV_CFG_LEN - 1], *sname, *ptr; - const char *errstr, *name; - int i, n, begin, len, conflict; - char line[1024]; - - comment[0] = '\0'; - buf[0] = '\0'; - - /* Find a name. Fetch out configuration first. */ - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "getconfig"); - gctl_ro_param(req, "comment", -1, comment); - gctl_add_param(req, "config", sizeof(buf), buf, - GCTL_PARAM_WR | GCTL_PARAM_ASCII); - errstr = gctl_issue(req); - if (errstr != NULL) { - warnx("can't get configuration: %s", errstr); - return (NULL); - } - gctl_free(req); - - begin = 0; - len = strlen(buf); - i = 0; - sname = malloc(namelen + 1); - - /* XXX: Max object setting? */ - for (n = 0; n < 10000; n++) { - snprintf(sname, namelen, "%s%d", prefix, n); - conflict = 0; - begin = 0; - /* Loop through the configuration line by line. */ - for (i = 0; i < len; i++) { - if (buf[i] == '\n' || buf[i] == '\0') { - ptr = buf + begin; - strlcpy(line, ptr, (i - begin) + 1); - begin = i + 1; - switch (type) { - case GV_TYPE_DRIVE: - name = find_pattern(line, "drive"); - break; - case GV_TYPE_VOL: - name = find_pattern(line, "volume"); - break; - case GV_TYPE_PLEX: - case GV_TYPE_SD: - name = find_pattern(line, "name"); - break; - default: - printf("Invalid type given\n"); - continue; - } - if (name == NULL) - continue; - if (!strcmp(sname, name)) { - conflict = 1; - /* XXX: Could quit the loop earlier. */ - } - } - } - if (!conflict) - return (sname); - } - free(sname); - return (NULL); -} - -static void -copy_device(struct gv_drive *d, const char *device) -{ - - if (strncmp(device, "/dev/", 5) == 0) - strlcpy(d->device, (device + 5), sizeof(d->device)); - else - strlcpy(d->device, device, sizeof(d->device)); -} - -/* Detach a plex or subdisk from its parent. */ -static void -gvinum_detach(int argc, char * const *argv) -{ - const char *errstr; - struct gctl_req *req; - int flags, i; - - flags = 0; - optreset = 1; - optind = 1; - while ((i = getopt(argc, argv, "f")) != -1) { - switch (i) { - case 'f': - flags |= GV_FLAG_F; - break; - default: - warn("invalid flag: %c", i); - return; - } - } - argc -= optind; - argv += optind; - if (argc != 1) { - warnx("usage: detach [-f] <subdisk> | <plex>"); - return; - } - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "detach"); - gctl_ro_param(req, "object", -1, argv[0]); - gctl_ro_param(req, "flags", sizeof(int), &flags); - - errstr = gctl_issue(req); - if (errstr != NULL) - warnx("detach failed: %s", errstr); - gctl_free(req); -} - -static void -gvinum_help(void) -{ - - printf("COMMANDS\n" - "checkparity [-f] plex\n" - " Check the parity blocks of a RAID-5 plex.\n" - "create [-f] description-file\n" - " Create as per description-file or open editor.\n" - "attach plex volume [rename]\n" - "attach subdisk plex [offset] [rename]\n" - " Attach a plex to a volume, or a subdisk to a plex\n" - "concat [-fv] [-n name] drives\n" - " Create a concatenated volume from the specified drives.\n" - "detach [-f] [plex | subdisk]\n" - " Detach a plex or a subdisk from the volume or plex to\n" - " which it is attached.\n" - "grow plex drive\n" - " Grow plex by creating a properly sized subdisk on drive\n" - "l | list [-r] [-v] [-V] [volume | plex | subdisk]\n" - " List information about specified objects.\n" - "ld [-r] [-v] [-V] [volume]\n" - " List information about drives.\n" - "ls [-r] [-v] [-V] [subdisk]\n" - " List information about subdisks.\n" - "lp [-r] [-v] [-V] [plex]\n" - " List information about plexes.\n" - "lv [-r] [-v] [-V] [volume]\n" - " List information about volumes.\n" - "mirror [-fsv] [-n name] drives\n" - " Create a mirrored volume from the specified drives.\n" - "move | mv -f drive object ...\n" - " Move the object(s) to the specified drive.\n" - "quit Exit the vinum program when running in interactive mode." - " Nor-\n" - " mally this would be done by entering the EOF character.\n" - "raid5 [-fv] [-s stripesize] [-n name] drives\n" - " Create a RAID-5 volume from the specified drives.\n" - "rename [-r] [drive | subdisk | plex | volume] newname\n" - " Change the name of the specified object.\n" - "rebuildparity plex [-f]\n" - " Rebuild the parity blocks of a RAID-5 plex.\n" - "resetconfig [-f]\n" - " Reset the complete gvinum configuration\n" - "rm [-r] [-f] volume | plex | subdisk | drive\n" - " Remove an object.\n" - "saveconfig\n" - " Save vinum configuration to disk after configuration" - " failures.\n" - "setstate [-f] state [volume | plex | subdisk | drive]\n" - " Set state without influencing other objects, for" - " diagnostic pur-\n" - " poses only.\n" - "start [-S size] volume | plex | subdisk\n" - " Allow the system to access the objects.\n" - "stripe [-fv] [-n name] drives\n" - " Create a striped volume from the specified drives.\n" - ); -} - -static void -gvinum_setstate(int argc, char * const *argv) -{ - struct gctl_req *req; - int flags, i; - const char *errstr; - - flags = 0; - - optreset = 1; - optind = 1; - - while ((i = getopt(argc, argv, "f")) != -1) { - switch (i) { - case 'f': - flags |= GV_FLAG_F; - break; - case '?': - default: - warn("invalid flag: %c", i); - return; - } - } - - argc -= optind; - argv += optind; - - if (argc != 2) { - warnx("usage: setstate [-f] <state> <obj>"); - return; - } - - /* - * XXX: This hack is needed to avoid tripping over (now) invalid - * 'classic' vinum states and will go away later. - */ - if (strcmp(argv[0], "up") && strcmp(argv[0], "down") && - strcmp(argv[0], "stale")) { - warnx("invalid state '%s'", argv[0]); - return; - } - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "setstate"); - gctl_ro_param(req, "state", -1, argv[0]); - gctl_ro_param(req, "object", -1, argv[1]); - gctl_ro_param(req, "flags", sizeof(int), &flags); - - errstr = gctl_issue(req); - if (errstr != NULL) - warnx("%s", errstr); - gctl_free(req); -} - -static void -gvinum_list(int argc, char * const *argv) -{ - struct gctl_req *req; - int flags, i, j; - const char *errstr; - char buf[20], config[GV_CFG_LEN + 1]; - const char *cmd; - - flags = 0; - cmd = "list"; - - if (argc) { - optreset = 1; - optind = 1; - cmd = argv[0]; - while ((j = getopt(argc, argv, "rsvV")) != -1) { - switch (j) { - case 'r': - flags |= GV_FLAG_R; - break; - case 's': - flags |= GV_FLAG_S; - break; - case 'v': - flags |= GV_FLAG_V; - break; - case 'V': - flags |= GV_FLAG_V; - flags |= GV_FLAG_VV; - break; - case '?': - default: - return; - } - } - argc -= optind; - argv += optind; - - } - - config[0] = '\0'; - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "list"); - gctl_ro_param(req, "cmd", -1, cmd); - gctl_ro_param(req, "argc", sizeof(int), &argc); - gctl_ro_param(req, "flags", sizeof(int), &flags); - gctl_add_param(req, "config", sizeof(config), config, - GCTL_PARAM_WR | GCTL_PARAM_ASCII); - if (argc) { - for (i = 0; i < argc; i++) { - snprintf(buf, sizeof(buf), "argv%d", i); - gctl_ro_param(req, buf, -1, argv[i]); - } - } - errstr = gctl_issue(req); - if (errstr != NULL) { - warnx("can't get configuration: %s", errstr); - gctl_free(req); - return; - } - - printf("%s", config); - gctl_free(req); -} - -/* Create a mirrored volume. */ -static void -gvinum_mirror(int argc, char * const *argv) -{ - - if (argc < 2) { - warnx("usage\tmirror [-fsv] [-n name] drives\n"); - return; - } - create_volume(argc, argv, "mirror"); -} - -/* Note that move is currently of form '[-r] target object [...]' */ -static void -gvinum_move(int argc, char * const *argv) -{ - struct gctl_req *req; - const char *errstr; - char buf[20]; - int flags, i, j; - - flags = 0; - if (argc) { - optreset = 1; - optind = 1; - while ((j = getopt(argc, argv, "f")) != -1) { - switch (j) { - case 'f': - flags |= GV_FLAG_F; - break; - case '?': - default: - return; - } - } - argc -= optind; - argv += optind; - } - - switch (argc) { - case 0: - warnx("no destination or object(s) to move specified"); - return; - case 1: - warnx("no object(s) to move specified"); - return; - default: - break; - } - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "move"); - gctl_ro_param(req, "argc", sizeof(int), &argc); - gctl_ro_param(req, "flags", sizeof(int), &flags); - gctl_ro_param(req, "destination", -1, argv[0]); - for (i = 1; i < argc; i++) { - snprintf(buf, sizeof(buf), "argv%d", i); - gctl_ro_param(req, buf, -1, argv[i]); - } - errstr = gctl_issue(req); - if (errstr != NULL) - warnx("can't move object(s): %s", errstr); - gctl_free(req); -} - -static void -gvinum_printconfig(int argc __unused, char * const *argv __unused) -{ - - printconfig(stdout, ""); -} - -static void -gvinum_parityop(int argc, char * const *argv, int rebuild) -{ - struct gctl_req *req; - int flags, i; - const char *errstr; - const char *op; - - if (rebuild) { - op = "rebuildparity"; - } else { - op = "checkparity"; - } - - optreset = 1; - optind = 1; - flags = 0; - while ((i = getopt(argc, argv, "fv")) != -1) { - switch (i) { - case 'f': - flags |= GV_FLAG_F; - break; - case 'v': - flags |= GV_FLAG_V; - break; - default: - warnx("invalid flag '%c'", i); - return; - } - } - argc -= optind; - argv += optind; - - if (argc != 1) { - warn("usage: %s [-f] [-v] <plex>", op); - return; - } - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, op); - gctl_ro_param(req, "rebuild", sizeof(int), &rebuild); - gctl_ro_param(req, "flags", sizeof(int), &flags); - gctl_ro_param(req, "plex", -1, argv[0]); - - errstr = gctl_issue(req); - if (errstr) - warnx("%s\n", errstr); - gctl_free(req); -} - -/* Create a RAID-5 volume. */ -static void -gvinum_raid5(int argc, char * const *argv) -{ - - if (argc < 2) { - warnx("usage:\traid5 [-fv] [-s stripesize] [-n name] drives\n"); - return; - } - create_volume(argc, argv, "raid5"); -} - -static void -gvinum_rename(int argc, char * const *argv) -{ - struct gctl_req *req; - const char *errstr; - int flags, j; - - flags = 0; - - if (argc) { - optreset = 1; - optind = 1; - while ((j = getopt(argc, argv, "r")) != -1) { - switch (j) { - case 'r': - flags |= GV_FLAG_R; - break; - default: - return; - } - } - argc -= optind; - argv += optind; - } - - switch (argc) { - case 0: - warnx("no object to rename specified"); - return; - case 1: - warnx("no new name specified"); - return; - case 2: - break; - default: - warnx("more than one new name specified"); - return; - } - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "rename"); - gctl_ro_param(req, "flags", sizeof(int), &flags); - gctl_ro_param(req, "object", -1, argv[0]); - gctl_ro_param(req, "newname", -1, argv[1]); - errstr = gctl_issue(req); - if (errstr != NULL) - warnx("can't rename object: %s", errstr); - gctl_free(req); -} - -static void -gvinum_rm(int argc, char * const *argv) -{ - struct gctl_req *req; - int flags, i, j; - const char *errstr; - char buf[20]; - - flags = 0; - optreset = 1; - optind = 1; - while ((j = getopt(argc, argv, "rf")) != -1) { - switch (j) { - case 'f': - flags |= GV_FLAG_F; - break; - case 'r': - flags |= GV_FLAG_R; - break; - default: - return; - } - } - argc -= optind; - argv += optind; - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "remove"); - gctl_ro_param(req, "argc", sizeof(int), &argc); - gctl_ro_param(req, "flags", sizeof(int), &flags); - if (argc) { - for (i = 0; i < argc; i++) { - snprintf(buf, sizeof(buf), "argv%d", i); - gctl_ro_param(req, buf, -1, argv[i]); - } - } - errstr = gctl_issue(req); - if (errstr != NULL) { - warnx("can't remove: %s", errstr); - gctl_free(req); - return; - } - gctl_free(req); -} - -static void -gvinum_resetconfig(int argc, char * const *argv) -{ - struct gctl_req *req; - const char *errstr; - char reply[32]; - int flags, i; - - flags = 0; - while ((i = getopt(argc, argv, "f")) != -1) { - switch (i) { - case 'f': - flags |= GV_FLAG_F; - break; - default: - warn("invalid flag: %c", i); - return; - } - } - if ((flags & GV_FLAG_F) == 0) { - if (!isatty(STDIN_FILENO)) { - warn("Please enter this command from a tty device\n"); - return; - } - printf(" WARNING! This command will completely wipe out" - " your gvinum configuration.\n" - " All data will be lost. If you really want to do this," - " enter the text\n\n" - " NO FUTURE\n" - " Enter text -> "); - fgets(reply, sizeof(reply), stdin); - if (strcmp(reply, "NO FUTURE\n")) { - printf("\n No change\n"); - return; - } - } - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "resetconfig"); - errstr = gctl_issue(req); - if (errstr != NULL) { - warnx("can't reset config: %s", errstr); - gctl_free(req); - return; - } - gctl_free(req); - printf("gvinum configuration obliterated\n"); -} - -static void -gvinum_saveconfig(void) -{ - struct gctl_req *req; - const char *errstr; - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "saveconfig"); - errstr = gctl_issue(req); - if (errstr != NULL) - warnx("can't save configuration: %s", errstr); - gctl_free(req); -} - -static void -gvinum_start(int argc, char * const *argv) -{ - struct gctl_req *req; - int i, initsize, j; - const char *errstr; - char buf[20]; - - /* 'start' with no arguments is a no-op. */ - if (argc == 1) - return; - - initsize = 0; - - optreset = 1; - optind = 1; - while ((j = getopt(argc, argv, "S")) != -1) { - switch (j) { - case 'S': - initsize = atoi(optarg); - break; - default: - return; - } - } - argc -= optind; - argv += optind; - - if (!initsize) - initsize = 512; - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "start"); - gctl_ro_param(req, "argc", sizeof(int), &argc); - gctl_ro_param(req, "initsize", sizeof(int), &initsize); - if (argc) { - for (i = 0; i < argc; i++) { - snprintf(buf, sizeof(buf), "argv%d", i); - gctl_ro_param(req, buf, -1, argv[i]); - } - } - errstr = gctl_issue(req); - if (errstr != NULL) { - warnx("can't start: %s", errstr); - gctl_free(req); - return; - } - - gctl_free(req); -} - -static void -gvinum_stop(int argc __unused, char * const *argv __unused) -{ - int err, fileid; - - fileid = kldfind(GVINUMKLD); - if (fileid == -1) { - if (modfind(GVINUMMOD) < 0) - warn("cannot find " GVINUMKLD); - return; - } - - /* - * This little hack prevents that we end up in an infinite loop in - * g_unload_class(). gv_unload() will return EAGAIN so that the GEOM - * event thread will be free for the g_wither_geom() call from - * gv_unload(). It's silly, but it works. - */ - printf("unloading " GVINUMKLD " kernel module... "); - fflush(stdout); - if ((err = kldunload(fileid)) != 0 && (errno == EAGAIN)) { - sleep(1); - err = kldunload(fileid); - } - if (err != 0) { - printf(" failed!\n"); - warn("cannot unload " GVINUMKLD); - return; - } - - printf("done\n"); - exit(0); -} - -/* Create a striped volume. */ -static void -gvinum_stripe(int argc, char * const *argv) -{ - - if (argc < 2) { - warnx("usage:\tstripe [-fv] [-n name] drives\n"); - return; - } - create_volume(argc, argv, "stripe"); -} - -/* Grow a subdisk by adding disk backed by provider. */ -static void -gvinum_grow(int argc, char * const *argv) -{ - struct gctl_req *req; - char *drive, *sdname; - char sdprefix[GV_MAXSDNAME]; - struct gv_drive *d; - struct gv_sd *s; - const char *errstr; - int drives, volumes, plexes, subdisks, flags; - - flags = 0; - drives = volumes = plexes = subdisks = 0; - if (argc < 3) { - warnx("usage:\tgrow plex drive\n"); - return; - } - - s = gv_alloc_sd(); - if (s == NULL) { - warn("unable to create subdisk"); - return; - } - d = gv_alloc_drive(); - if (d == NULL) { - warn("unable to create drive"); - free(s); - return; - } - /* Lookup device and set an appropriate drive name. */ - drive = find_drive(); - if (drive == NULL) { - warn("unable to find an appropriate drive name"); - free(s); - free(d); - return; - } - strlcpy(d->name, drive, sizeof(d->name)); - copy_device(d, argv[2]); - - drives = 1; - - /* We try to use the plex name as basis for the subdisk name. */ - snprintf(sdprefix, sizeof(sdprefix), "%s.s", argv[1]); - sdname = find_name(sdprefix, GV_TYPE_SD, GV_MAXSDNAME); - if (sdname == NULL) { - warn("unable to find an appropriate subdisk name"); - free(s); - free(d); - free(drive); - return; - } - strlcpy(s->name, sdname, sizeof(s->name)); - free(sdname); - strlcpy(s->plex, argv[1], sizeof(s->plex)); - strlcpy(s->drive, d->name, sizeof(s->drive)); - subdisks = 1; - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "create"); - gctl_ro_param(req, "flags", sizeof(int), &flags); - gctl_ro_param(req, "volumes", sizeof(int), &volumes); - gctl_ro_param(req, "plexes", sizeof(int), &plexes); - gctl_ro_param(req, "subdisks", sizeof(int), &subdisks); - gctl_ro_param(req, "drives", sizeof(int), &drives); - gctl_ro_param(req, "drive0", sizeof(*d), d); - gctl_ro_param(req, "sd0", sizeof(*s), s); - errstr = gctl_issue(req); - free(drive); - if (errstr != NULL) { - warnx("unable to grow plex: %s", errstr); - free(s); - free(d); - return; - } - gctl_free(req); -} - -static void -parseline(int argc, char * const *argv) -{ - - if (argc <= 0) - return; - - if (!strcmp(argv[0], "create")) - gvinum_create(argc, argv); - else if (!strcmp(argv[0], "exit") || !strcmp(argv[0], "quit")) - exit(0); - else if (!strcmp(argv[0], "attach")) - gvinum_attach(argc, argv); - else if (!strcmp(argv[0], "detach")) - gvinum_detach(argc, argv); - else if (!strcmp(argv[0], "concat")) - gvinum_concat(argc, argv); - else if (!strcmp(argv[0], "grow")) - gvinum_grow(argc, argv); - else if (!strcmp(argv[0], "help")) - gvinum_help(); - else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "l")) - gvinum_list(argc, argv); - else if (!strcmp(argv[0], "ld")) - gvinum_list(argc, argv); - else if (!strcmp(argv[0], "lp")) - gvinum_list(argc, argv); - else if (!strcmp(argv[0], "ls")) - gvinum_list(argc, argv); - else if (!strcmp(argv[0], "lv")) - gvinum_list(argc, argv); - else if (!strcmp(argv[0], "mirror")) - gvinum_mirror(argc, argv); - else if (!strcmp(argv[0], "move")) - gvinum_move(argc, argv); - else if (!strcmp(argv[0], "mv")) - gvinum_move(argc, argv); - else if (!strcmp(argv[0], "printconfig")) - gvinum_printconfig(argc, argv); - else if (!strcmp(argv[0], "raid5")) - gvinum_raid5(argc, argv); - else if (!strcmp(argv[0], "rename")) - gvinum_rename(argc, argv); - else if (!strcmp(argv[0], "resetconfig")) - gvinum_resetconfig(argc, argv); - else if (!strcmp(argv[0], "rm")) - gvinum_rm(argc, argv); - else if (!strcmp(argv[0], "saveconfig")) - gvinum_saveconfig(); - else if (!strcmp(argv[0], "setstate")) - gvinum_setstate(argc, argv); - else if (!strcmp(argv[0], "start")) - gvinum_start(argc, argv); - else if (!strcmp(argv[0], "stop")) - gvinum_stop(argc, argv); - else if (!strcmp(argv[0], "stripe")) - gvinum_stripe(argc, argv); - else if (!strcmp(argv[0], "checkparity")) - gvinum_parityop(argc, argv, 0); - else if (!strcmp(argv[0], "rebuildparity")) - gvinum_parityop(argc, argv, 1); - else - printf("unknown command '%s'\n", argv[0]); -} - -/* - * The guts of printconfig. This is called from gvinum_printconfig and from - * gvinum_create when called without an argument, in order to give the user - * something to edit. - */ -static void -printconfig(FILE *of, const char *comment) -{ - struct gctl_req *req; - struct utsname uname_s; - const char *errstr; - time_t now; - char buf[GV_CFG_LEN + 1]; - - uname(&uname_s); - time(&now); - buf[0] = '\0'; - - req = gctl_get_handle(); - gctl_ro_param(req, "class", -1, "VINUM"); - gctl_ro_param(req, "verb", -1, "getconfig"); - gctl_ro_param(req, "comment", -1, comment); - gctl_add_param(req, "config", sizeof(buf), buf, - GCTL_PARAM_WR | GCTL_PARAM_ASCII); - errstr = gctl_issue(req); - if (errstr != NULL) { - warnx("can't get configuration: %s", errstr); - return; - } - gctl_free(req); - - fprintf(of, "# Vinum configuration of %s, saved at %s", - uname_s.nodename, - ctime(&now)); - - if (*comment != '\0') - fprintf(of, "# Current configuration:\n"); - - fprintf(of, "%s", buf); -} diff --git a/sbin/gvinum/gvinum.h b/sbin/gvinum/gvinum.h deleted file mode 100644 index f29dc4c986a4..000000000000 --- a/sbin/gvinum/gvinum.h +++ /dev/null @@ -1,41 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-4-Clause - * - * Copyright (c) 1997, 1998 - * Nan Yang Computer Services Limited. All rights reserved. - * - * This software is distributed under the so-called ``Berkeley - * License'': - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Nan Yang Computer - * Services Limited. - * 4. Neither the name of the Company 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 ``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 company 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. - */ - - -#define GVINUMMOD "g_vinum" -#define GVINUMKLD "geom_vinum" diff --git a/sbin/hastctl/Makefile b/sbin/hastctl/Makefile index 3f42f6ecdda7..36152dc835a6 100644 --- a/sbin/hastctl/Makefile +++ b/sbin/hastctl/Makefile @@ -1,4 +1,3 @@ - .include <src.opts.mk> PACKAGE=hast diff --git a/sbin/hastctl/hastctl.8 b/sbin/hastctl/hastctl.8 index a0a1e1f22634..f696858a8071 100644 --- a/sbin/hastctl/hastctl.8 +++ b/sbin/hastctl/hastctl.8 @@ -1,5 +1,4 @@ .\" Copyright (c) 2010 The FreeBSD Foundation -.\" All rights reserved. .\" .\" This software was developed by Pawel Jakub Dawidek under sponsorship from .\" the FreeBSD Foundation. diff --git a/sbin/hastctl/hastctl.c b/sbin/hastctl/hastctl.c index ab2d618c698c..fe37dcaa1b58 100644 --- a/sbin/hastctl/hastctl.c +++ b/sbin/hastctl/hastctl.c @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. @@ -29,7 +28,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <err.h> diff --git a/sbin/hastd/Makefile b/sbin/hastd/Makefile index 64f35483c5d7..8a79320fb665 100644 --- a/sbin/hastd/Makefile +++ b/sbin/hastd/Makefile @@ -1,4 +1,3 @@ - .include <src.opts.mk> PACKAGE=hast diff --git a/sbin/hastd/activemap.c b/sbin/hastd/activemap.c index f5e3c60fd0a0..d693d08d524e 100644 --- a/sbin/hastd/activemap.c +++ b/sbin/hastd/activemap.c @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. @@ -29,7 +28,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> /* powerof2() */ #include <sys/queue.h> diff --git a/sbin/hastd/activemap.h b/sbin/hastd/activemap.h index a1dbf70e51af..ff598f841909 100644 --- a/sbin/hastd/activemap.h +++ b/sbin/hastd/activemap.h @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. diff --git a/sbin/hastd/control.c b/sbin/hastd/control.c index f6f937d3b180..0a9befcc4358 100644 --- a/sbin/hastd/control.c +++ b/sbin/hastd/control.c @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. @@ -29,7 +28,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/wait.h> diff --git a/sbin/hastd/control.h b/sbin/hastd/control.h index 4e16334dc824..ad7fb7978597 100644 --- a/sbin/hastd/control.h +++ b/sbin/hastd/control.h @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. diff --git a/sbin/hastd/ebuf.c b/sbin/hastd/ebuf.c index 522e4e821b11..1ec8dd672c35 100644 --- a/sbin/hastd/ebuf.c +++ b/sbin/hastd/ebuf.c @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. @@ -29,7 +28,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <errno.h> diff --git a/sbin/hastd/ebuf.h b/sbin/hastd/ebuf.h index a79821735539..a4a2b30f4cb4 100644 --- a/sbin/hastd/ebuf.h +++ b/sbin/hastd/ebuf.h @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. diff --git a/sbin/hastd/hast_proto.h b/sbin/hastd/hast_proto.h index ab0a46494b45..0158aa4ccee6 100644 --- a/sbin/hastd/hast_proto.h +++ b/sbin/hastd/hast_proto.h @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. diff --git a/sbin/hastd/hastd.8 b/sbin/hastd/hastd.8 index fefd03f7dbe0..05a2d9b5e4e9 100644 --- a/sbin/hastd/hastd.8 +++ b/sbin/hastd/hastd.8 @@ -1,5 +1,4 @@ .\" Copyright (c) 2010 The FreeBSD Foundation -.\" All rights reserved. .\" .\" This software was developed by Pawel Jakub Dawidek under sponsorship from .\" the FreeBSD Foundation. diff --git a/sbin/hastd/hastd.c b/sbin/hastd/hastd.c index f303f277e0ad..c62b85fd1925 100644 --- a/sbin/hastd/hastd.c +++ b/sbin/hastd/hastd.c @@ -30,7 +30,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/linker.h> #include <sys/module.h> diff --git a/sbin/hastd/hastd.h b/sbin/hastd/hastd.h index 15ad243adf7c..a54ef316f37a 100644 --- a/sbin/hastd/hastd.h +++ b/sbin/hastd/hastd.h @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. diff --git a/sbin/hastd/hooks.c b/sbin/hastd/hooks.c index 7a5ccb4e22e6..43ba6f06851a 100644 --- a/sbin/hastd/hooks.c +++ b/sbin/hastd/hooks.c @@ -30,7 +30,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/sysctl.h> #include <sys/wait.h> diff --git a/sbin/hastd/lzf.c b/sbin/hastd/lzf.c index 2b0ee88b2cd3..371dee3a037b 100644 --- a/sbin/hastd/lzf.c +++ b/sbin/hastd/lzf.c @@ -118,7 +118,7 @@ lzf_compress (const void *const in_data, unsigned int in_len, /* off requires a type wide enough to hold a general pointer difference. * ISO C doesn't have that (size_t might not be enough and ptrdiff_t only * works for differences within a single object). We also assume that no - * no bit pattern traps. Since the only platform that is both non-POSIX + * bit pattern traps. Since the only platform that is both non-POSIX * and fails to support both assumptions is windows 64 bit, we make a * special workaround for it. */ diff --git a/sbin/hastd/metadata.c b/sbin/hastd/metadata.c index 2f9cbc5e0971..5318d9f6aee5 100644 --- a/sbin/hastd/metadata.c +++ b/sbin/hastd/metadata.c @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. diff --git a/sbin/hastd/metadata.h b/sbin/hastd/metadata.h index b5215c1a85c9..6d8c593f0a46 100644 --- a/sbin/hastd/metadata.h +++ b/sbin/hastd/metadata.h @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. diff --git a/sbin/hastd/nv.c b/sbin/hastd/nv.c index 5f50ae349023..16ab95cf0dc6 100644 --- a/sbin/hastd/nv.c +++ b/sbin/hastd/nv.c @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. @@ -29,7 +28,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/endian.h> @@ -99,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 : \ @@ -249,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/nv.h b/sbin/hastd/nv.h index d1214f2b29ec..657a66fe38eb 100644 --- a/sbin/hastd/nv.h +++ b/sbin/hastd/nv.h @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. diff --git a/sbin/hastd/pjdlog.c b/sbin/hastd/pjdlog.c index 6726cab47697..3dbbe163812e 100644 --- a/sbin/hastd/pjdlog.c +++ b/sbin/hastd/pjdlog.c @@ -30,7 +30,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> diff --git a/sbin/hastd/primary.c b/sbin/hastd/primary.c index 76c454d14a74..5d166a088b9b 100644 --- a/sbin/hastd/primary.c +++ b/sbin/hastd/primary.c @@ -30,7 +30,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/time.h> #include <sys/bio.h> diff --git a/sbin/hastd/proto.c b/sbin/hastd/proto.c index 56ff4933c9b8..70166271a747 100644 --- a/sbin/hastd/proto.c +++ b/sbin/hastd/proto.c @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. @@ -29,7 +28,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/queue.h> #include <sys/socket.h> diff --git a/sbin/hastd/proto.h b/sbin/hastd/proto.h index a7cecbe67479..729daa518d9d 100644 --- a/sbin/hastd/proto.h +++ b/sbin/hastd/proto.h @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. diff --git a/sbin/hastd/proto_common.c b/sbin/hastd/proto_common.c index 143b7666b7ca..39ef696f5b69 100644 --- a/sbin/hastd/proto_common.c +++ b/sbin/hastd/proto_common.c @@ -30,7 +30,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/socket.h> diff --git a/sbin/hastd/proto_impl.h b/sbin/hastd/proto_impl.h index 4f84f90c6f23..0a0074545f38 100644 --- a/sbin/hastd/proto_impl.h +++ b/sbin/hastd/proto_impl.h @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. diff --git a/sbin/hastd/proto_socketpair.c b/sbin/hastd/proto_socketpair.c index ab30a6a27263..59ac9553ff6e 100644 --- a/sbin/hastd/proto_socketpair.c +++ b/sbin/hastd/proto_socketpair.c @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. @@ -29,7 +28,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/socket.h> diff --git a/sbin/hastd/proto_tcp.c b/sbin/hastd/proto_tcp.c index dc5a23da7b23..aeec887433f8 100644 --- a/sbin/hastd/proto_tcp.c +++ b/sbin/hastd/proto_tcp.c @@ -30,7 +30,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> /* MAXHOSTNAMELEN */ #include <sys/socket.h> diff --git a/sbin/hastd/proto_uds.c b/sbin/hastd/proto_uds.c index 5bf4c64fe5f6..acb61a360180 100644 --- a/sbin/hastd/proto_uds.c +++ b/sbin/hastd/proto_uds.c @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. diff --git a/sbin/hastd/rangelock.c b/sbin/hastd/rangelock.c index 33aee39651a4..26025d1169ac 100644 --- a/sbin/hastd/rangelock.c +++ b/sbin/hastd/rangelock.c @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. diff --git a/sbin/hastd/rangelock.h b/sbin/hastd/rangelock.h index d01dbd755b40..4847887b6b7a 100644 --- a/sbin/hastd/rangelock.h +++ b/sbin/hastd/rangelock.h @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. diff --git a/sbin/hastd/secondary.c b/sbin/hastd/secondary.c index bf118d7e1237..6bf889bba657 100644 --- a/sbin/hastd/secondary.c +++ b/sbin/hastd/secondary.c @@ -30,7 +30,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/time.h> #include <sys/bio.h> diff --git a/sbin/hastd/subr.c b/sbin/hastd/subr.c index 27641a6f8acb..084ea50dae7c 100644 --- a/sbin/hastd/subr.c +++ b/sbin/hastd/subr.c @@ -30,7 +30,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/disk.h> #include <sys/ioctl.h> @@ -157,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; /* @@ -208,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) { @@ -287,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/hastd/synch.h b/sbin/hastd/synch.h index 9e03fcb8ca74..32463f836bd0 100644 --- a/sbin/hastd/synch.h +++ b/sbin/hastd/synch.h @@ -2,7 +2,6 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009-2010 The FreeBSD Foundation - * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile index b95d7a6ea178..2553e61b5e9e 100644 --- a/sbin/ifconfig/Makefile +++ b/sbin/ifconfig/Makefile @@ -1,5 +1,3 @@ -# From: @(#)Makefile 8.1 (Berkeley) 6/5/93 - .include <src.opts.mk> PACKAGE=runtime diff --git a/sbin/ifconfig/af_inet.c b/sbin/ifconfig/af_inet.c index 83b605e8c4cb..4fd74d481b3a 100644 --- a/sbin/ifconfig/af_inet.c +++ b/sbin/ifconfig/af_inet.c @@ -29,11 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/ioctl.h> #include <sys/socket.h> @@ -360,7 +355,7 @@ in_delete_first_nl(if_ctx *ctx) ifahdr->ifa_family = AF_INET; ifahdr->ifa_index = ifindex; - if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return (EINVAL); nlmsg_seq = hdr->nlmsg_seq; @@ -391,7 +386,7 @@ in_delete_first_nl(if_ctx *ctx) ifahdr->ifa_index = ifindex; snl_add_msg_attr_ip4(&nw, IFA_LOCAL, &addr); - if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return (EINVAL); memset(&e, 0, sizeof(e)); snl_read_reply_code(ss, hdr->nlmsg_seq, &e); @@ -431,7 +426,7 @@ in_exec_nl(if_ctx *ctx, unsigned long action, void *data) snl_add_msg_attr_u32(&nw, IFAF_VHID, pdata->vhid); snl_end_attr_nested(&nw, off); - if (!snl_finalize_msg(&nw) || !snl_send_message(ctx->io_ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ctx->io_ss, hdr)) return (0); struct snl_errmsg_data e = {}; @@ -441,36 +436,13 @@ in_exec_nl(if_ctx *ctx, unsigned long action, void *data) return (e.error); } - -static void -in_setdefaultmask_nl(void) -{ - struct in_px *px = sintab_nl[ADDR]; - - in_addr_t i = ntohl(px->addr.s_addr); - - /* - * If netmask isn't supplied, use historical default. - * This is deprecated for interfaces other than loopback - * or point-to-point; warn in other cases. In the future - * we should return an error rather than warning. - */ - if (IN_CLASSA(i)) - px->plen = IN_CLASSA_NSHIFT; - else if (IN_CLASSB(i)) - px->plen = IN_CLASSB_NSHIFT; - else - px->plen = IN_CLASSC_NSHIFT; - px->maskset = true; -} #endif static void -warn_nomask(int ifflags) +err_nomask(int ifflags) { if ((ifflags & (IFF_POINTOPOINT | IFF_LOOPBACK)) == 0) { - warnx("WARNING: setting interface address without mask " - "is deprecated,\ndefault mask may not be correct."); + errx(1, "ERROR: setting interface address without mask is no longer supported."); } } @@ -479,12 +451,11 @@ in_postproc(if_ctx *ctx __unused, int newaddr, int ifflags) { #ifdef WITHOUT_NETLINK if (sintab[ADDR]->sin_len != 0 && sintab[MASK]->sin_len == 0 && newaddr) { - warn_nomask(ifflags); + err_nomask(ifflags); } #else if (sintab_nl[ADDR]->addrset && !sintab_nl[ADDR]->maskset && newaddr) { - warn_nomask(ifflags); - in_setdefaultmask_nl(); + err_nomask(ifflags); } #endif } diff --git a/sbin/ifconfig/af_inet6.c b/sbin/ifconfig/af_inet6.c index a0138e9b3de6..9386f5eaf513 100644 --- a/sbin/ifconfig/af_inet6.c +++ b/sbin/ifconfig/af_inet6.c @@ -29,11 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/ioctl.h> #include <sys/socket.h> @@ -433,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) { @@ -493,7 +493,7 @@ in6_exec_nl(if_ctx *ctx, unsigned long action, void *data) snl_add_msg_attr_u32(&nw, IFAF_VHID, pdata->vhid); snl_end_attr_nested(&nw, off); - if (!snl_finalize_msg(&nw) || !snl_send_message(ctx->io_ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ctx->io_ss, hdr)) return (0); struct snl_errmsg_data e = {}; @@ -726,6 +726,8 @@ static struct cmd inet6_cmds[] = { DEF_CMD_ARG("pltime", setip6pltime), DEF_CMD_ARG("vltime", setip6vltime), DEF_CMD("eui64", 0, setip6eui64), + DEF_CMD("stableaddr", ND6_IFF_STABLEADDR, setnd6flags), + DEF_CMD("-stableaddr", -ND6_IFF_STABLEADDR, setnd6flags), #ifdef EXPERIMENTAL DEF_CMD("ipv6_only", ND6_IFF_IPV6_ONLY_MANUAL,setnd6flags), DEF_CMD("-ipv6_only", -ND6_IFF_IPV6_ONLY_MANUAL,setnd6flags), @@ -753,13 +755,13 @@ static struct afswtch af_inet6 = { #ifdef WITHOUT_NETLINK .af_difaddr = SIOCDIFADDR_IN6, .af_aifaddr = SIOCAIFADDR_IN6, - .af_ridreq = &in6_addreq, + .af_ridreq = &in6_ridreq, .af_addreq = &in6_addreq, .af_exec = af_exec_ioctl, #else .af_difaddr = NL_RTM_DELADDR, .af_aifaddr = NL_RTM_NEWADDR, - .af_ridreq = &in6_add, + .af_ridreq = &in6_del, .af_addreq = &in6_add, .af_exec = in6_exec_nl, #endif diff --git a/sbin/ifconfig/af_link.c b/sbin/ifconfig/af_link.c index 17de87539d9a..55b75d847c16 100644 --- a/sbin/ifconfig/af_link.c +++ b/sbin/ifconfig/af_link.c @@ -29,11 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/types.h> #include <sys/ioctl.h> #include <sys/socket.h> @@ -165,17 +160,6 @@ pcp: } #else -static uint8_t -convert_iftype(uint8_t iftype) -{ - switch (iftype) { - case IFT_IEEE8023ADLAG: - return (IFT_ETHER); - case IFT_INFINIBANDLAG: - return (IFT_INFINIBAND); - } - return (iftype); -} static void link_status_nl(if_ctx *ctx, if_link_t *link, if_addr_t *ifa __unused) @@ -226,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 7eeb86585197..fb7e72028e2e 100644 --- a/sbin/ifconfig/af_nd6.c +++ b/sbin/ifconfig/af_nd6.c @@ -25,11 +25,6 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/ioctl.h> #include <sys/socket.h> @@ -57,17 +52,23 @@ static const char rcsid[] = #include "ifconfig.h" #define MAX_SYSCTL_TRY 5 +static const char *ND6BITS[] = { + [0] = "PERFORMNUD", + [1] = "ACCEPT_RTADV", + [2] = "PREFER_SOURCE", + [3] = "IFDISABLED", + [4] = "DONT_SET_IFROUTE", + [5] = "AUTO_LINKLOCAL", + [6] = "NO_RADR", + [7] = "NO_PREFER_IFACE", + [8] = "NO_DAD", #ifdef DRAFT_IETF_6MAN_IPV6ONLY_FLAG -#define ND6BITS "\020\001PERFORMNUD\002ACCEPT_RTADV\003PREFER_SOURCE" \ - "\004IFDISABLED\005DONT_SET_IFROUTE\006AUTO_LINKLOCAL" \ - "\007NO_RADR\010NO_PREFER_IFACE\011NO_DAD" \ - "\012IPV6_ONLY\013IPV6_ONLY_MANUAL" \ - "\020DEFAULTIF" -#else -#define ND6BITS "\020\001PERFORMNUD\002ACCEPT_RTADV\003PREFER_SOURCE" \ - "\004IFDISABLED\005DONT_SET_IFROUTE\006AUTO_LINKLOCAL" \ - "\007NO_RADR\010NO_PREFER_IFACE\011NO_DAD\020DEFAULTIF" + [9] = "IPV6_ONLY", + [10] = "IPV6_ONLY_MANUAL", #endif + [11] = "STABLEADDR", + [15] = "DEFAULTIF", +}; static int isnd6defif(if_ctx *ctx, int s); void setnd6flags(if_ctx *, const char *, int); @@ -146,6 +147,7 @@ nd6_status(if_ctx *ctx) int s6; int error; int isdefif; + uint32_t bits; strlcpy(nd.ifname, ctx->ifname, sizeof(nd.ifname)); if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { @@ -164,7 +166,8 @@ nd6_status(if_ctx *ctx) close(s6); if (nd.ndi.flags == 0 && !isdefif) return; - printb("\tnd6 options", - (unsigned int)(nd.ndi.flags | (isdefif << 15)), ND6BITS); + bits = (nd.ndi.flags | (isdefif << 15)); + printf("\tnd6 options=%x", bits); + print_bits("options", &bits, 1, ND6BITS, nitems(ND6BITS)); putchar('\n'); } diff --git a/sbin/ifconfig/carp.c b/sbin/ifconfig/carp.c index dd09838b5d8b..7c7398f92d48 100644 --- a/sbin/ifconfig/carp.c +++ b/sbin/ifconfig/carp.c @@ -68,6 +68,9 @@ static int carpr_state = -1; static struct in_addr carp_addr; static struct in6_addr carp_addr6; static unsigned char const *carpr_key; +static carp_version_t carpr_version; +static uint8_t carpr_vrrp_prio; +static uint16_t carpr_vrrp_adv_inter; static void carp_status(if_ctx *ctx) @@ -79,19 +82,28 @@ carp_status(if_ctx *ctx) return; for (size_t i = 0; i < carpr[0].carpr_count; i++) { - printf("\tcarp: %s vhid %d advbase %d advskew %d", - carp_states[carpr[i].carpr_state], carpr[i].carpr_vhid, - carpr[i].carpr_advbase, carpr[i].carpr_advskew); - if (ctx->args->printkeys && carpr[i].carpr_key[0] != '\0') - printf(" key \"%s\"\n", carpr[i].carpr_key); - else - printf("\n"); - - inet_ntop(AF_INET6, &carpr[i].carpr_addr6, addr_buf, - sizeof(addr_buf)); - - printf("\t peer %s peer6 %s\n", - inet_ntoa(carpr[i].carpr_addr), addr_buf); + switch (carpr[i].carpr_version) { + case CARP_VERSION_CARP: + printf("\tcarp: %s vhid %d advbase %d advskew %d", + carp_states[carpr[i].carpr_state], carpr[i].carpr_vhid, + carpr[i].carpr_advbase, carpr[i].carpr_advskew); + if (ctx->args->printkeys && carpr[i].carpr_key[0] != '\0') + printf(" key \"%s\"\n", carpr[i].carpr_key); + else + printf("\n"); + + inet_ntop(AF_INET6, &carpr[i].carpr_addr6, addr_buf, + sizeof(addr_buf)); + + printf("\t peer %s peer6 %s\n", + inet_ntoa(carpr[i].carpr_addr), addr_buf); + break; + case CARP_VERSION_VRRPv3: + printf("\tvrrp: %s vrid %d prio %d interval %d\n", + carp_states[carpr[i].carpr_state], carpr[i].carpr_vhid, + carpr[i].carpr_vrrp_prio, carpr[i].carpr_vrrp_adv_inter); + break; + } } } @@ -137,6 +149,12 @@ setcarp_callback(if_ctx *ctx, void *arg __unused) if (! IN6_IS_ADDR_UNSPECIFIED(&carp_addr6)) memcpy(&carpr.carpr_addr6, &carp_addr6, sizeof(carp_addr6)); + if (carpr_version != 0) + carpr.carpr_version = carpr_version; + if (carpr_vrrp_prio != 0) + carpr.carpr_vrrp_prio = carpr_vrrp_prio; + if (carpr_vrrp_adv_inter != 0) + carpr.carpr_vrrp_adv_inter = carpr_vrrp_adv_inter; if (ifconfig_carp_set_info(lifh, ctx->ifname, &carpr)) err(1, "SIOCSVH"); @@ -226,6 +244,31 @@ setcarp_mcast6(if_ctx *ctx __unused, const char *val __unused, int dummy __unuse carp_addr6.s6_addr[15] = 0x12; } +static void +setcarp_version(if_ctx *ctx __unused, const char *val, int dummy __unused) +{ + carpr_version = atoi(val); + + if (carpr_version != CARP_VERSION_CARP && carpr_version != CARP_VERSION_VRRPv3) + errx(1, "version must be %d or %d", CARP_VERSION_CARP, + CARP_VERSION_VRRPv3); +} + +static void +setvrrp_prio(if_ctx *ctx __unused, const char *val, int dummy __unused) +{ + carpr_vrrp_prio = atoi(val); +} + +static void +setvrrp_interval(if_ctx *ctx __unused, const char *val, int dummy __unused) +{ + carpr_vrrp_adv_inter = atoi(val); + + if (carpr_vrrp_adv_inter == 0 || carpr_vrrp_adv_inter > VRRP_MAX_INTERVAL) + errx(1, "vrrpinterval must be greater than 0 and less than %d", VRRP_MAX_INTERVAL); +} + static struct cmd carp_cmds[] = { DEF_CMD_ARG("advbase", setcarp_advbase), DEF_CMD_ARG("advskew", setcarp_advskew), @@ -236,6 +279,9 @@ static struct cmd carp_cmds[] = { DEF_CMD("mcast", 0, setcarp_mcast), DEF_CMD_ARG("peer6", setcarp_peer6), DEF_CMD("mcast6", 0, setcarp_mcast6), + DEF_CMD_ARG("carpver", setcarp_version), + DEF_CMD_ARG("vrrpprio", setvrrp_prio), + DEF_CMD_ARG("vrrpinterval", setvrrp_interval), }; static struct afswtch af_carp = { .af_name = "af_carp", @@ -246,10 +292,6 @@ static struct afswtch af_carp = { static __constructor void carp_ctor(void) { - /* Default to multicast. */ - setcarp_mcast(NULL, NULL, 0); - setcarp_mcast6(NULL, NULL, 0); - for (size_t i = 0; i < nitems(carp_cmds); i++) cmd_register(&carp_cmds[i]); af_register(&af_carp); diff --git a/sbin/ifconfig/ifbridge.c b/sbin/ifconfig/ifbridge.c index 3a97a5af3931..eff443447c13 100644 --- a/sbin/ifconfig/ifbridge.c +++ b/sbin/ifconfig/ifbridge.c @@ -35,11 +35,6 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/ioctl.h> #include <sys/socket.h> @@ -65,6 +60,10 @@ static const char rcsid[] = #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 }; @@ -85,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 = {}; @@ -152,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; @@ -184,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) { @@ -192,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); @@ -216,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 @@ -392,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 = 1; /* 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, ""); } @@ -582,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; @@ -617,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), @@ -651,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), @@ -664,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/ifclone.c b/sbin/ifconfig/ifclone.c index 8b378cbe341f..f44d052c97ad 100644 --- a/sbin/ifconfig/ifclone.c +++ b/sbin/ifconfig/ifclone.c @@ -29,11 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/ioctl.h> #include <sys/queue.h> diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 index 1dc4c2e1b56d..fafb77e1ca6c 100644 --- a/sbin/ifconfig/ifconfig.8 +++ b/sbin/ifconfig/ifconfig.8 @@ -1,3 +1,6 @@ +.\"- +.\" SPDX-License-Identifier: BSD-3-Clause +.\" .\" Copyright (c) 1983, 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" @@ -25,9 +28,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" From: @(#)ifconfig.8 8.3 (Berkeley) 1/5/94 -.\" -.Dd June 20, 2023 +.Dd September 12, 2025 .Dt IFCONFIG 8 .Os .Sh NAME @@ -35,8 +36,8 @@ .Nd configure network interface parameters .Sh SYNOPSIS .Nm -.Op Fl j Ar jail -.Op Fl kLmn +.Op Fl j Ar jid +.Op Fl DkLmn .Op Fl f Ar type Ns Cm \&: Ns Ar format .Ar interface .Op Cm create @@ -49,13 +50,13 @@ .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 dkLmuv +.Op Fl dDkLmuv .Op Fl f Ar type Ns Cm \&: Ns Ar format .Op Fl G Ar groupname .Op Fl g Ar groupname @@ -63,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 @@ -104,6 +105,12 @@ with no additional information. Use of this flag is mutually exclusive with all other flags and commands. .It Fl d Display only the interfaces that are down. +.It Fl D +Include the driver name and unit number of the interface in the output. +This is normally the original name of the interface, +even if it has been renamed; it may differ from the original name +in some cases, such as +.Xr epair 4 . .It Fl f Xo .Ar type Ns Cm \&: Ns Ar format Ns .Op Cm \&, Ns Ar type Ns Cm \&: Ns Ar format Ar ... @@ -131,7 +138,7 @@ and their associated .Ar format strings are: .Pp -.Bl -tag -width ether +.Bl -tag -width default .It Cm addr Adjust the display of inet and inet6 addresses: .Pp @@ -196,15 +203,24 @@ Integer format, for example: .Ql prefixlen 64 .El .El +.Pp +In addition, the following shortcuts are accepted: +.Bl -tag -width default +.It Cm default +Resets all formats to their default values. +.It Cm cidr +Shortcut notation for +.Cm inet:cidr,inet6:cidr . +.El +.Pp .It Fl G Ar groupname Exclude members of the specified .Ar groupname from the output. -.Ar groupname . .Pp -Only one option +Only one .Fl G -should be specified as later override previous ones +option should be specified as later ones override earlier ones. .Ar groupname may contain shell patterns in which case it should be quoted. .Pp @@ -230,9 +246,9 @@ lists names of interfaces belonging to .Ar groupname . Any other flags and arguments are ignored in this case. .Pp -Only one option +Only one .Fl g -should be specified as later override previous ones +option should be specified as later ones override earlier ones. .Ar groupname may contain shell patterns in which case it should be quoted. .Pp @@ -241,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 , @@ -432,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 @@ -458,13 +473,17 @@ sending out requests and listening for replies. .It Cm stickyarp Enable the so-called sticky ARP mode for the interface. If this option is enabled on the given interface, any resolved address is -marked as a static one and never expires. This may be used to increase +marked as a static one and never expires. +This may be used to increase security of the network by preventing ARP spoofing or to reduce latency for high-performance Ethernet networks where the time needed for ARP resolution is -too high. Please note that a similar feature is also provided for bridges. See +too high. +Please note that a similar feature is also provided for bridges. +See the sticky option in the .Sx Bridge Interface Parameters -section. Enabling this +section. +Enabling this option may impact techniques which rely on ARP expiration/overwriting feature such as load-balancers or high-availabity solutions such as .Xr carp 4 . @@ -482,6 +501,10 @@ Enable driver dependent debugging code; usually, this turns on extra console error logging. .It Fl debug Disable driver dependent debugging code. +.It Cm allmulti +Enable promiscuous mode for multicast packets. +.It Fl allmulti +Disable promiscuous mode for multicast packets. .It Cm promisc Put interface into permanently promiscuous mode. .It Fl promisc @@ -545,9 +568,10 @@ A FIB .Ar fib_number is assigned to all packets encapsulated by tunnel interface, e.g., .Xr gif 4 , -.Xr gre 4 +.Xr gre 4 , +.Xr vxlan 4 , and -.Xr vxlan 4 . +.Xr wg 4 . .It Cm maclabel Ar label If Mandatory Access Control support is enabled in the kernel, set the MAC label to @@ -707,7 +731,7 @@ buffers, enable them on the interface. .It Fl mextpg If the driver supports extended multi-page .Xr mbuf 9 -biffers, disable them on the interface. +buffers, disable them on the interface. .It Cm wol , wol_ucast , wol_mcast , wol_magic Enable Wake On Lan (WOL) support, if available. WOL is a facility whereby a machine in a low power state may be woken @@ -980,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. @@ -1064,7 +1118,7 @@ Legacy .Cm wds devices have a fixed peer relationship and do not, for example, roam if their peer stops communicating. -For completeness a Dynamic WDS (DWDS) interface may marked as +For completeness a Dynamic WDS (DWDS) interface may be marked as .Fl wdslegacy . .It Cm bssid Request a unique local mac address for the cloned device. @@ -1221,8 +1275,8 @@ Set the interval at which beacon frames are sent when operating in ad-hoc or ap mode. The .Ar interval -parameter is specified in TU's (1024 usecs). -By default beacon frames are transmitted every 100 TU's. +parameter is specified in TUs (1024 usecs). +By default beacon frames are transmitted every 100 TUs. .It Cm bmissthreshold Ar count Set the number of consecutive missed beacons at which the station will attempt to roam (i.e., search for a new access point). @@ -1879,8 +1933,8 @@ Use .Fl powersave to disable powersave operation when operating as a client. .It Cm powersavesleep Ar sleep -Set the desired max powersave sleep time in TU's (1024 usecs). -By default the max powersave sleep time is 100 TU's. +Set the desired max powersave sleep time in TUs (1024 usecs). +By default the max powersave sleep time is 100 TUs. .It Cm protmode Ar technique For interfaces operating in 802.11g, use the specified .Ar technique @@ -2470,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 @@ -2496,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 @@ -2671,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: @@ -2803,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, @@ -2870,12 +3036,24 @@ to send and receive pfsync state synchronisation messages. .It Fl syncdev Stop sending pfsync state synchronisation messages over the network. .It Cm syncpeer Ar peer_address -Make the pfsync link point-to-point rather than using -multicast to broadcast the state synchronisation messages. -The peer_address is the IP address of the other host taking part in -the pfsync cluster. +Set the destination address for the state synchronization messages sent. +The +.Ar peer_address +is normally the IPv4 or IPv6 address of the other host taking +part in the pfsync cluster. +.Pp +When the +.Ar peer_address +is set to a unicast IP address, the pfsync link will behave +as point-to-point rather than using multicast to broadcast the messages. +.Pp +When the +.Ar peer_address +is set to ff12::f0, the state synchronization +messages will be broadcast using multicast over IPv6. .It Fl syncpeer -Broadcast the packets using multicast. +Unset the syncpeer. +Packets will then be broadcast using multicast over IPv4. .It Cm maxupd Ar n Set the maximum number of updates for a single state which can be collapsed into one. @@ -2886,6 +3064,11 @@ acknowledged that the associated state has been inserted. .It Fl defer Do not defer the first packet in a state. This is the default. +.It Fl version Ar n +Configure message format for compatibility with older versions of FreeBSD. +Refer to +.Xr pfsync 4 +for details. .El .Ss VLAN Parameters The following parameters are specific to @@ -3129,6 +3312,20 @@ announcements to. Restore the default destination address for (IPv4) .Xr carp 4 announcements, which is ff02::12. +.It Cm carpver +Set the protocol version. +Valid choices are 2 (for +.Xr carp 4) +and 3 (for VRRPv3). +This can only be set when +.Xr carp 4 +is initiated. +.It Cm vrrpprio +Set the VRRPv3 priority. +Valid values are 1-255. +.It Cm vrrpinterval +Set the VRRPv3 Master Advertisement Interval. +Values are in centiseconds. .El .Sh ENVIRONMENT The following environment variables affect the execution of @@ -3270,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 b26fbaf82776..9aeb4a09ef49 100644 --- a/sbin/ifconfig/ifconfig.c +++ b/sbin/ifconfig/ifconfig.c @@ -29,17 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1983, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#if 0 -static char sccsid[] = "@(#)ifconfig.c 8.2 (Berkeley) 2/16/94"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/ioctl.h> #ifdef JAIL @@ -324,14 +313,10 @@ cmpifaddrs(struct ifaddrs *a, struct ifaddrs *b, struct ifa_queue *q) static void freeformat(void) { - if (f_inet != NULL) - free(f_inet); - if (f_inet6 != NULL) - free(f_inet6); - if (f_ether != NULL) - free(f_ether); - if (f_addr != NULL) - free(f_addr); + free(f_inet); + free(f_inet6); + free(f_ether); + free(f_addr); } static void setformat(char *input) @@ -341,9 +326,18 @@ static void setformat(char *input) formatstr = strdup(input); while ((category = strsep(&formatstr, ",")) != NULL) { modifier = strchr(category, ':'); - if (modifier == NULL || modifier[1] == '\0') { - warnx("Skipping invalid format specification: %s\n", - category); + if (modifier == NULL) { + if (strcmp(category, "default") == 0) { + freeformat(); + } else if (strcmp(category, "cidr") == 0) { + free(f_inet); + f_inet = strdup(category); + free(f_inet6); + f_inet6 = strdup(category); + } else { + warnx("Skipping invalid format: %s\n", + category); + } continue; } @@ -351,14 +345,19 @@ static void setformat(char *input) modifier[0] = '\0'; modifier++; - if (strcmp(category, "addr") == 0) + if (strcmp(category, "addr") == 0) { + free(f_addr); f_addr = strdup(modifier); - else if (strcmp(category, "ether") == 0) + } else if (strcmp(category, "ether") == 0) { + free(f_ether); f_ether = strdup(modifier); - else if (strcmp(category, "inet") == 0) + } else if (strcmp(category, "inet") == 0) { + free(f_inet); f_inet = strdup(modifier); - else if (strcmp(category, "inet6") == 0) + } else if (strcmp(category, "inet6") == 0) { + free(f_inet6); f_inet6 = strdup(modifier); + } } free(formatstr); } @@ -464,10 +463,13 @@ 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 */ - strlcpy(options, "G:adf:j:klmnuv", sizeof(options)); + strlcpy(options, "G:adDf:j:klmnuv", sizeof(options)); for (p = opts; p != NULL; p = p->next) strlcat(options, p->opt, sizeof(options)); while ((c = getopt(argc, argv, options)) != -1) { @@ -478,6 +480,9 @@ args_parse(struct ifconfig_args *args, int argc, char *argv[]) case 'd': /* restrict scan to "down" interfaces */ args->downonly = true; break; + case 'D': /* Print driver name */ + args->drivername = true; + break; case 'f': if (optarg == NULL) usage(); @@ -492,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 @@ -609,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; @@ -620,8 +626,6 @@ main(int ac, char *av[]) .io_s = -1, }; - f_inet = f_inet6 = f_ether = f_addr = NULL; - lifh = ifconfig_open(); if (lifh == NULL) err(EXIT_FAILURE, "ifconfig_open"); @@ -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); @@ -768,7 +762,7 @@ match_afp(const struct afswtch *afp, int sa_family, const struct sockaddr_dl *sd return (true); /* special case for "ether" address family */ if (!strcmp(afp->af_name, "ether")) { - if (sdl == NULL && !match_ether(sdl)) + if (sdl == NULL || !match_ether(sdl)) return (false); return (true); } @@ -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++; @@ -1617,17 +1618,61 @@ unsetifdescr(if_ctx *ctx, const char *val __unused, int value __unused) #ifdef WITHOUT_NETLINK -#define IFFBITS \ -"\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\7RUNNING" \ -"\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2" \ -"\20MULTICAST\22PPROMISC\23MONITOR\24STATICARP\25STICKYARP" +static const char *IFFBITS[] = { + [0] = "UP", + [1] = "BROADCAST", + [2] = "DEBUG", + [3] = "LOOPBACK", + [4] = "POINTOPOINT", + [6] = "RUNNING", + [7] = "NOARP", + [8] = "PROMISC", + [9] = "ALLMULTI", + [10] = "OACTIVE", + [11] = "SIMPLEX", + [12] = "LINK0", + [13] = "LINK1", + [14] = "LINK2", + [15] = "MULTICAST", + [17] = "PPROMISC", + [18] = "MONITOR", + [19] = "STATICARP", + [20] = "STICKYARP", +}; -#define IFCAPBITS \ -"\020\1RXCSUM\2TXCSUM\3NETCONS\4VLAN_MTU\5VLAN_HWTAGGING\6JUMBO_MTU\7POLLING" \ -"\10VLAN_HWCSUM\11TSO4\12TSO6\13LRO\14WOL_UCAST\15WOL_MCAST\16WOL_MAGIC" \ -"\17TOE4\20TOE6\21VLAN_HWFILTER\23VLAN_HWTSO\24LINKSTATE\25NETMAP" \ -"\26RXCSUM_IPV6\27TXCSUM_IPV6\31TXRTLMT\32HWRXTSTMP\33NOMAP\34TXTLS4\35TXTLS6" \ -"\36VXLAN_HWCSUM\37VXLAN_HWTSO\40TXTLS_RTLMT" +static const char *IFCAPBITS[] = { + [0] = "RXCSUM", + [1] = "TXCSUM", + [2] = "NETCONS", + [3] = "VLAN_MTU", + [4] = "VLAN_HWTAGGING", + [5] = "JUMBO_MTU", + [6] = "POLLING", + [7] = "VLAN_HWCSUM", + [8] = "TSO4", + [9] = "TSO6", + [10] = "LRO", + [11] = "WOL_UCAST", + [12] = "WOL_MCAST", + [13] = "WOL_MAGIC", + [14] = "TOE4", + [15] = "TOE6", + [16] = "VLAN_HWFILTER", + [18] = "VLAN_HWTSO", + [19] = "LINKSTATE", + [20] = "NETMAP", + [21] = "RXCSUM_IPV6", + [22] = "TXCSUM_IPV6", + [23] = "HWSTATS", + [24] = "TXRTLMT", + [25] = "HWRXTSTMP", + [26] = "MEXTPG", + [27] = "TXTLS4", + [28] = "TXTLS6", + [29] = "VXLAN_HWCSUM", + [30] = "VXLAN_HWTSO", + [31] = "TXTLS_RTLMT", +}; static void print_ifcap_nv(if_ctx *ctx) @@ -1699,10 +1744,12 @@ print_ifcap(if_ctx *ctx) if ((ifr.ifr_curcap & IFCAP_NV) != 0) print_ifcap_nv(ctx); else { - printb("\toptions", ifr.ifr_curcap, IFCAPBITS); + printf("\toptions=%x", ifr.ifr_curcap); + print_bits("options", &ifr.ifr_curcap, 1, IFCAPBITS, nitems(IFCAPBITS)); putchar('\n'); if (ctx->args->supmedia && ifr.ifr_reqcap != 0) { - printb("\tcapabilities", ifr.ifr_reqcap, IFCAPBITS); + printf("\tcapabilities=%x", ifr.ifr_reqcap); + print_bits("capabilities", &ifr.ifr_reqcap, 1, IFCAPBITS, nitems(IFCAPBITS)); putchar('\n'); } } @@ -1790,8 +1837,8 @@ status(if_ctx *ctx, const struct sockaddr_dl *sdl __unused, struct ifaddrs *ifa) old_s = ctx->io_s; ctx->io_s = s; - printf("%s: ", ctx->ifname); - printb("flags", ifa->ifa_flags, IFFBITS); + printf("%s: flags=%x", ctx->ifname, ifa->ifa_flags); + print_bits("flags", &ifa->ifa_flags, 1, IFFBITS, nitems(IFFBITS)); print_metric(ctx); print_mtu(ctx); putchar('\n'); @@ -1877,6 +1924,29 @@ Perror(const char *cmd) Perrorc(cmd, errno); } +void +print_bits(const char *btype, uint32_t *v, const int v_count, + const char **names, const int n_count) +{ + int num = 0; + + for (int i = 0; i < v_count * 32; i++) { + bool is_set = v[i / 32] & (1U << (i % 32)); + if (is_set) { + if (num++ == 0) + printf("<"); + if (num != 1) + printf(","); + if (i < n_count) + printf("%s", names[i]); + else + printf("%s_%d", btype, i); + } + } + if (num > 0) + printf(">"); +} + /* * Print a value a la the %b format of the kernel's printf */ @@ -2010,6 +2080,8 @@ static struct cmd basic_cmds[] = { DEF_CMD_ARG("descr", setifdescr), DEF_CMD("-description", 0, unsetifdescr), DEF_CMD("-descr", 0, unsetifdescr), + DEF_CMD("allmulti", IFF_PALLMULTI, setifflags), + DEF_CMD("-allmulti", IFF_PALLMULTI, clearifflags), DEF_CMD("promisc", IFF_PPROMISC, setifflags), DEF_CMD("-promisc", IFF_PPROMISC, clearifflags), DEF_CMD("add", IFF_UP, notealias), @@ -2017,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), @@ -2076,6 +2143,8 @@ static struct cmd basic_cmds[] = { setifcapnv), DEF_CMD_SARG("-rxtls", "-"IFCAP2_RXTLS4_NAME ",-" IFCAP2_RXTLS6_NAME, setifcapnv), + DEF_CMD_SARG("ipsec", IFCAP2_IPSEC_OFFLOAD_NAME, setifcapnv), + DEF_CMD_SARG("-ipsec", "-"IFCAP2_IPSEC_OFFLOAD_NAME, setifcapnv), DEF_CMD("wol", IFCAP_WOL, setifcap), DEF_CMD("-wol", IFCAP_WOL, clearifcap), DEF_CMD("wol_ucast", IFCAP_WOL_UCAST, setifcap), diff --git a/sbin/ifconfig/ifconfig.h b/sbin/ifconfig/ifconfig.h index 80e6b9bc2289..672020443b8c 100644 --- a/sbin/ifconfig/ifconfig.h +++ b/sbin/ifconfig/ifconfig.h @@ -38,6 +38,7 @@ #include <libifconfig.h> #include <stdbool.h> +#include <net/if_types.h> #define __constructor __attribute__((constructor)) @@ -66,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; @@ -74,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; @@ -120,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, \ @@ -230,6 +241,7 @@ struct ifconfig_args { bool supmedia; /* Supported media */ bool printkeys; /* Print security keys */ bool allfamilies; /* Print all families */ + bool drivername; /* Print driver name */ int verbose; /* verbosity level */ int argc; char **argv; @@ -237,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 { @@ -258,6 +269,8 @@ void setifcap(if_ctx *ctx, const char *, int value); void setifcapnv(if_ctx *ctx, const char *vname, const char *arg); void Perror(const char *cmd); +void print_bits(const char *btype, uint32_t *v, const int v_count, + const char **names, const int n_count); void printb(const char *s, unsigned value, const char *bits); void ifmaybeload(struct ifconfig_args *args, const char *name); @@ -279,6 +292,7 @@ struct afswtch *af_getbyfamily(int af); void af_other_status(if_ctx *ctx); void print_ifstatus(if_ctx *ctx); void print_metric(if_ctx *ctx); +ifType convert_iftype(ifType iftype); /* Netlink-related functions */ void list_interfaces_nl(struct ifconfig_args *args); diff --git a/sbin/ifconfig/ifconfig_netlink.c b/sbin/ifconfig/ifconfig_netlink.c index 76dd99307f31..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> @@ -77,34 +79,11 @@ static const char *IFFBITS[] = { "STICKYARP", /* 20:0x100000 IFF_STICKYARP*/ "DYING", /* 21:0x200000 IFF_DYING*/ "RENAMING", /* 22:0x400000 IFF_RENAMING*/ - "NOGROUP", /* 23:0x800000 IFF_NOGROUP*/ + "PALLMULTI", /* 23:0x800000 IFF_PALLMULTI*/ "LOWER_UP", /* 24:0x1000000 IFF_NETLINK_1*/ }; static void -print_bits(const char *btype, uint32_t *v, const int v_count, - const char **names, const int n_count) -{ - int num = 0; - - for (int i = 0; i < v_count * 32; i++) { - bool is_set = v[i / 32] & (1U << (i % 32)); - if (is_set) { - if (num++ == 0) - printf("<"); - if (num != 1) - printf(","); - if (i < n_count) - printf("%s", names[i]); - else - printf("%s_%d", btype, i); - } - } - if (num > 0) - printf(">"); -} - -static void nl_init_socket(struct snl_state *ss) { if (snl_init(ss, NETLINK_ROUTE)) @@ -162,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 = {}; @@ -170,8 +149,10 @@ 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 (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return (NULL); uint32_t nlmsg_seq = hdr->nlmsg_seq; @@ -212,7 +193,7 @@ if_nametoindex_nl(struct snl_state *ss, const char *ifname) snl_reserve_msg_object(&nw, struct ifinfomsg); snl_add_msg_attr_string(&nw, IFLA_IFNAME, ifname); - if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return (0); hdr = snl_read_reply(ss, hdr->nlmsg_seq); @@ -224,6 +205,19 @@ if_nametoindex_nl(struct snl_state *ss, const char *ifname) return (link.ifi_index); } +ifType +convert_iftype(ifType iftype) +{ + switch (iftype) { + case IFT_IEEE8023ADLAG: + return (IFT_ETHER); + case IFT_INFINIBANDLAG: + return (IFT_INFINIBAND); + default: + return (iftype); + } +} + static void prepare_ifaddrs(struct snl_state *ss, struct ifmap *ifmap) { @@ -234,7 +228,7 @@ prepare_ifaddrs(struct snl_state *ss, struct ifmap *ifmap) hdr->nlmsg_flags |= NLM_F_DUMP; snl_reserve_msg_object(&nw, struct ifaddrmsg); - if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return; uint32_t nlmsg_seq = hdr->nlmsg_seq; @@ -282,11 +276,17 @@ match_iface(struct ifconfig_args *args, struct iface *iface) struct sockaddr_dl sdl = { .sdl_len = sizeof(struct sockaddr_dl), .sdl_family = AF_LINK, - .sdl_type = link->ifi_type, + .sdl_type = convert_iftype(link->ifi_type), .sdl_alen = NLA_DATA_LEN(link->ifla_address), }; return (match_ether(&sdl)); - } + } else if (args->afp->af_af == AF_LINK) + /* + * The rtnetlink(4) RTM_GETADDR does not list link level + * addresses, so latter cycle won't match anything. Short + * circuit on RTM_GETLINK has provided us an address. + */ + return (link->ifla_address != NULL); for (struct ifa *ifa = iface->ifa; ifa != NULL; ifa = ifa->next) { if (args->afp->af_af == ifa->addr.ifa_family) @@ -369,6 +369,7 @@ status_nl(if_ctx *ctx, struct iface *iface) { if_link_t *link = &iface->link; struct ifconfig_args *args = ctx->args; + char *drivername = NULL; printf("%s: ", link->ifla_ifname); @@ -413,6 +414,22 @@ status_nl(if_ctx *ctx, struct iface *iface) args->afp->af_other_status(ctx); print_ifstatus(ctx); + if (args->drivername || args->verbose) { + if (ifconfig_get_orig_name(lifh, link->ifla_ifname, + &drivername) != 0) { + if (ifconfig_err_errtype(lifh) == OTHER) + fprintf(stderr, "get original name: %s\n", + strerror(ifconfig_err_errno(lifh))); + else + fprintf(stderr, + "get original name: error type %d\n", + ifconfig_err_errtype(lifh)); + exit_code = 1; + } + if (drivername != NULL) + printf("\tdrivername: %s\n", drivername); + free(drivername); + } if (args->verbose > 0) sfp_status(ctx); } @@ -440,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 3a41ef63d1d3..9b8be210a59e 100644 --- a/sbin/ifconfig/ifgif.c +++ b/sbin/ifconfig/ifgif.c @@ -25,11 +25,6 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif - #include <sys/param.h> #include <sys/ioctl.h> #include <sys/socket.h> @@ -53,7 +48,10 @@ static const char rcsid[] = #include "ifconfig.h" -#define GIFBITS "\020\2IGNORE_SOURCE" +static const char *GIFBITS[] = { + [0] = "NOCLAMP", + [1] = "IGNORE_SOURCE", +}; static void gif_status(if_ctx *ctx) @@ -65,7 +63,8 @@ gif_status(if_ctx *ctx) return; if (opts == 0) return; - printb("\toptions", opts, GIFBITS); + printf("\toptions=%x", opts); + print_bits("options", &opts, 1, GIFBITS, nitems(GIFBITS)); putchar('\n'); } @@ -92,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/ifgre.c b/sbin/ifconfig/ifgre.c index dda04e363bfc..43c86a546167 100644 --- a/sbin/ifconfig/ifgre.c +++ b/sbin/ifconfig/ifgre.c @@ -25,7 +25,6 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioctl.h> #include <sys/socket.h> @@ -42,7 +41,11 @@ #include "ifconfig.h" -#define GREBITS "\020\01ENABLE_CSUM\02ENABLE_SEQ\03UDPENCAP" +static const char *GREBITS[] = { + [0] = "ENABLE_CSUM", + [1] = "ENABLE_SEQ", + [2] = "UDPENCAP", +}; static void gre_status(if_ctx *ctx) @@ -61,7 +64,8 @@ gre_status(if_ctx *ctx) ifr.ifr_data = (caddr_t)&port; if (ioctl_ctx_ifr(ctx, GREGPORT, &ifr) == 0 && port != 0) printf("\tudpport: %u\n", port); - printb("\toptions", opts, GREBITS); + printf("\toptions=%x", opts); + print_bits("options", &opts, 1, GREBITS, nitems(GREBITS)); putchar('\n'); } diff --git a/sbin/ifconfig/ifgroup.c b/sbin/ifconfig/ifgroup.c index 702e4732db7c..49cce678bb5e 100644 --- a/sbin/ifconfig/ifgroup.c +++ b/sbin/ifconfig/ifgroup.c @@ -25,11 +25,6 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/ioctl.h> #include <sys/socket.h> diff --git a/sbin/ifconfig/ifieee80211.c b/sbin/ifconfig/ifieee80211.c index 42ef6d731662..e73dcc2c4f24 100644 --- a/sbin/ifconfig/ifieee80211.c +++ b/sbin/ifconfig/ifieee80211.c @@ -70,6 +70,7 @@ #include <net/if_media.h> #include <net/route.h> +#define WANT_NET80211 1 #include <net80211/ieee80211_ioctl.h> #include <net80211/ieee80211_freebsd.h> #include <net80211/ieee80211_superg.h> @@ -132,8 +133,10 @@ #define IEEE80211_FVHT_VHT 0x000000001 /* CONF: VHT supported */ #define IEEE80211_FVHT_USEVHT40 0x000000002 /* CONF: Use VHT40 */ #define IEEE80211_FVHT_USEVHT80 0x000000004 /* CONF: Use VHT80 */ -#define IEEE80211_FVHT_USEVHT160 0x000000008 /* CONF: Use VHT160 */ -#define IEEE80211_FVHT_USEVHT80P80 0x000000010 /* CONF: Use VHT 80+80 */ +#define IEEE80211_FVHT_USEVHT80P80 0x000000008 /* CONF: Use VHT 80+80 */ +#define IEEE80211_FVHT_USEVHT160 0x000000010 /* CONF: Use VHT160 */ +#define IEEE80211_FVHT_STBC_TX 0x00000020 /* CONF: STBC tx enabled */ +#define IEEE80211_FVHT_STBC_RX 0x00000040 /* CONF: STBC rx enabled */ #endif /* Helper macros unified. */ @@ -195,8 +198,10 @@ static int gottxparams = 0; static struct ieee80211_channel curchan; static int gotcurchan = 0; static struct ifmediareq *global_ifmr; + +/* HT */ static int htconf = 0; -static int gothtconf = 0; +static int gothtconf = 0; static void gethtconf(if_ctx *ctx) @@ -210,7 +215,7 @@ gethtconf(if_ctx *ctx) /* VHT */ static int vhtconf = 0; -static int gotvhtconf = 0; +static int gotvhtconf = 0; static void getvhtconf(if_ctx *ctx) @@ -1975,13 +1980,11 @@ set80211vhtconf(if_ctx *ctx, const char *val __unused, int d) { if (get80211val(ctx, IEEE80211_IOC_VHTCONF, &vhtconf) < 0) errx(-1, "cannot set VHT setting"); - printf("%s: vhtconf=0x%08x, d=%d\n", __func__, vhtconf, d); if (d < 0) { d = -d; vhtconf &= ~d; } else vhtconf |= d; - printf("%s: vhtconf is now 0x%08x\n", __func__, vhtconf); set80211(ctx, IEEE80211_IOC_VHTCONF, vhtconf, 0, NULL); } @@ -2295,7 +2298,7 @@ regdomain_addchans(if_ctx *ctx, struct ieee80211req_chaninfo *ci, memset(c, 0, sizeof(*c)); c->ic_freq = freq; c->ic_flags = flags; - if (c->ic_flags & IEEE80211_CHAN_DFS) + if (c->ic_flags & IEEE80211_CHAN_DFS) c->ic_maxregpower = nb->maxPowerDFS; else c->ic_maxregpower = nb->maxPower; @@ -2776,14 +2779,193 @@ 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); if (ctx->args->verbose) { - const struct ieee80211_ie_vhtcap *vhtcap = - (const struct ieee80211_ie_vhtcap *) ie; - uint32_t vhtcap_info = LE_READ_4(&vhtcap->vht_cap_info); + const struct ieee80211_vht_cap *vhtcap; + uint32_t vhtcap_info; + + /* Check that the right size. */ + if (ie[1] != sizeof(*vhtcap)) { + printf("<err: vht_cap inval. length>"); + return; + } + /* Skip Element ID and Length. */ + vhtcap = (const struct ieee80211_vht_cap *)(ie + 2); + vhtcap_info = LE_READ_4(&vhtcap->vht_cap_info); printf("<cap 0x%08x", vhtcap_info); printf(" rx_mcs_map 0x%x", LE_READ_2(&vhtcap->supp_mcs.rx_mcs_map)); @@ -2803,13 +2985,20 @@ printvhtinfo(if_ctx *ctx, const char *tag, const u_int8_t *ie) { printf("%s", tag); if (ctx->args->verbose) { - const struct ieee80211_ie_vht_operation *vhtinfo = - (const struct ieee80211_ie_vht_operation *) ie; + const struct ieee80211_vht_operation *vhtinfo; - printf("<chw %d freq1_idx %d freq2_idx %d basic_mcs_set 0x%04x>", + /* Check that the right size. */ + if (ie[1] != sizeof(*vhtinfo)) { + printf("<err: vht_operation inval. length>"); + return; + } + /* Skip Element ID and Length. */ + vhtinfo = (const struct ieee80211_vht_operation *)(ie + 2); + + printf("<chw %d freq0_idx %d freq1_idx %d basic_mcs_set 0x%04x>", vhtinfo->chan_width, - vhtinfo->center_freq_seg1_idx, - vhtinfo->center_freq_seg2_idx, + vhtinfo->center_freq_seq0_idx, + vhtinfo->center_freq_seq1_idx, LE_READ_2(&vhtinfo->basic_mcs_set)); } } @@ -3118,6 +3307,12 @@ rsn_cipher(const u_int8_t *sel) return "AES-CCMP"; case RSN_SEL(RSN_CSE_WRAP): 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 @@ -3134,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"; } @@ -3184,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] ) | \ @@ -3561,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) { @@ -3583,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: @@ -3592,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; @@ -3649,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); @@ -3675,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]; @@ -4729,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; @@ -4743,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]; @@ -4750,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) { @@ -4864,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]; @@ -4914,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) { @@ -5064,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) { @@ -5073,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: @@ -5400,26 +5717,27 @@ end: if (IEEE80211_IS_CHAN_VHT(c) || verbose) { getvhtconf(ctx); - if (vhtconf & IEEE80211_FVHT_VHT) + if (vhtconf & IEEE80211_FVHT_VHT) { LINE_CHECK("vht"); - else + + if (vhtconf & IEEE80211_FVHT_USEVHT40) + LINE_CHECK("vht40"); + else + LINE_CHECK("-vht40"); + if (vhtconf & IEEE80211_FVHT_USEVHT80) + LINE_CHECK("vht80"); + else + LINE_CHECK("-vht80"); + if (vhtconf & IEEE80211_FVHT_USEVHT160) + LINE_CHECK("vht160"); + else + LINE_CHECK("-vht160"); + if (vhtconf & IEEE80211_FVHT_USEVHT80P80) + LINE_CHECK("vht80p80"); + else + LINE_CHECK("-vht80p80"); + } else if (verbose) LINE_CHECK("-vht"); - if (vhtconf & IEEE80211_FVHT_USEVHT40) - LINE_CHECK("vht40"); - else - LINE_CHECK("-vht40"); - if (vhtconf & IEEE80211_FVHT_USEVHT80) - LINE_CHECK("vht80"); - else - LINE_CHECK("-vht80"); - if (vhtconf & IEEE80211_FVHT_USEVHT160) - LINE_CHECK("vht160"); - else - LINE_CHECK("-vht160"); - if (vhtconf & IEEE80211_FVHT_USEVHT80P80) - LINE_CHECK("vht80p80"); - else - LINE_CHECK("-vht80p80"); } if (get80211val(ctx, IEEE80211_IOC_WME, &wme) != -1) { @@ -6013,7 +6331,7 @@ static struct cmd ieee80211_cmds[] = { DEF_CMD("ht", 3, set80211htconf), /* NB: 20+40 */ DEF_CMD("-ht", 0, set80211htconf), DEF_CMD("vht", IEEE80211_FVHT_VHT, set80211vhtconf), - DEF_CMD("-vht", 0, set80211vhtconf), + DEF_CMD("-vht", -IEEE80211_FVHT_VHT, set80211vhtconf), DEF_CMD("vht40", IEEE80211_FVHT_USEVHT40, set80211vhtconf), DEF_CMD("-vht40", -IEEE80211_FVHT_USEVHT40, set80211vhtconf), DEF_CMD("vht80", IEEE80211_FVHT_USEVHT80, set80211vhtconf), @@ -6022,6 +6340,12 @@ static struct cmd ieee80211_cmds[] = { DEF_CMD("-vht160", -IEEE80211_FVHT_USEVHT160, set80211vhtconf), DEF_CMD("vht80p80", IEEE80211_FVHT_USEVHT80P80, set80211vhtconf), DEF_CMD("-vht80p80", -IEEE80211_FVHT_USEVHT80P80, set80211vhtconf), + DEF_CMD("vhtstbctx", IEEE80211_FVHT_STBC_TX, set80211vhtconf), + DEF_CMD("-vhtstbctx", -IEEE80211_FVHT_STBC_TX, set80211vhtconf), + DEF_CMD("vhtstbcrx", IEEE80211_FVHT_STBC_RX, set80211vhtconf), + DEF_CMD("-vhtstbcrx", -IEEE80211_FVHT_STBC_RX, set80211vhtconf), + DEF_CMD("vhtstbc", (IEEE80211_FVHT_STBC_TX|IEEE80211_FVHT_STBC_RX), set80211vhtconf), + DEF_CMD("-vhtstbc", -(IEEE80211_FVHT_STBC_TX|IEEE80211_FVHT_STBC_RX), set80211vhtconf), DEF_CMD("rifs", 1, set80211rifs), DEF_CMD("-rifs", 0, set80211rifs), DEF_CMD("smps", IEEE80211_HTCAP_SMPS_ENA, set80211smps), diff --git a/sbin/ifconfig/ifipsec.c b/sbin/ifconfig/ifipsec.c index 6fd1f9f630f4..b8ab38d6165a 100644 --- a/sbin/ifconfig/ifipsec.c +++ b/sbin/ifconfig/ifipsec.c @@ -25,7 +25,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioctl.h> #include <sys/socket.h> diff --git a/sbin/ifconfig/iflagg.c b/sbin/ifconfig/iflagg.c index 9d148c314119..4de437d25bd9 100644 --- a/sbin/ifconfig/iflagg.c +++ b/sbin/ifconfig/iflagg.c @@ -1,11 +1,6 @@ /*- */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/ioctl.h> #include <sys/socket.h> diff --git a/sbin/ifconfig/ifpfsync.c b/sbin/ifconfig/ifpfsync.c index 33e4e5ac8cd3..264b1ecd5b56 100644 --- a/sbin/ifconfig/ifpfsync.c +++ b/sbin/ifconfig/ifpfsync.c @@ -374,8 +374,7 @@ pfsync_status(if_ctx *ctx) nvlist_destroy(nvl); - if (syncdev[0] != '\0' || syncpeer.ss_family != AF_UNSPEC) - printf("\t"); + printf("\t"); if (syncdev[0] != '\0') printf("syncdev: %s ", syncdev); diff --git a/sbin/ifconfig/ifvlan.c b/sbin/ifconfig/ifvlan.c index 7fb63b8142f4..0a2603f1736f 100644 --- a/sbin/ifconfig/ifvlan.c +++ b/sbin/ifconfig/ifvlan.c @@ -4,7 +4,7 @@ * Copyright (c) 1999 Bill Paul <wpaul@ctr.columbia.edu> * Copyright (c) 2012 ADARA Networks, Inc. * All rights reserved. - * + * * Portions of this software were developed by Robert N. M. Watson under * contract to ADARA Networks, Inc. * @@ -59,12 +59,8 @@ #include "ifconfig.h" -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif - #define NOTAG ((u_short) -1) +#define NOPROTO ((u_short) -1) static const char proto_8021Q[] = "802.1q"; static const char proto_8021ad[] = "802.1ad"; @@ -72,7 +68,7 @@ static const char proto_qinq[] = "qinq"; static struct vlanreq params = { .vlr_tag = NOTAG, - .vlr_proto = ETHERTYPE_VLAN, + .vlr_proto = NOPROTO, }; static void @@ -162,6 +158,8 @@ vlan_create(if_ctx *ctx, struct ifreq *ifr) errx(1, "must specify a tag for vlan create"); if (params.vlr_parent[0] == '\0') errx(1, "must specify a parent device for vlan create"); + if (params.vlr_proto == NOPROTO) + params.vlr_proto = ETHERTYPE_VLAN; ifr->ifr_data = (caddr_t) ¶ms; } ifcreate_ioctl(ctx, ifr); @@ -178,6 +176,8 @@ static void vlan_set(int s, struct ifreq *ifr) { if (params.vlr_tag != NOTAG && params.vlr_parent[0] != '\0') { + if (params.vlr_proto == NOPROTO) + params.vlr_proto = ETHERTYPE_VLAN; ifr->ifr_data = (caddr_t) ¶ms; if (ioctl(s, SIOCSETVLAN, (caddr_t)ifr) == -1) err(1, "SIOCSETVLAN"); @@ -201,8 +201,16 @@ setvlantag(if_ctx *ctx, const char *val, int dummy __unused) errx(1, "value for vlan out of range"); if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) != -1) { - vreq.vlr_tag = params.vlr_tag; - memcpy(¶ms, &vreq, sizeof(params)); + /* + * Retrieve the current settings if the interface has already + * been configured. + */ + if (vreq.vlr_parent[0] != '\0') { + if (params.vlr_parent[0] == '\0') + strlcpy(params.vlr_parent, vreq.vlr_parent, IFNAMSIZ); + if (params.vlr_proto == NOPROTO) + params.vlr_proto = vreq.vlr_proto; + } vlan_set(ctx->io_s, &ifr); } } @@ -235,8 +243,16 @@ setvlanproto(if_ctx *ctx, const char *val, int dummy __unused) errx(1, "invalid value for vlanproto"); if (ioctl_ctx_ifr(ctx, SIOCGETVLAN, &ifr) != -1) { - vreq.vlr_proto = params.vlr_proto; - memcpy(¶ms, &vreq, sizeof(params)); + /* + * Retrieve the current settings if the interface has already + * been configured. + */ + if (vreq.vlr_parent[0] != '\0') { + if (params.vlr_parent[0] == '\0') + strlcpy(params.vlr_parent, vreq.vlr_parent, IFNAMSIZ); + if (params.vlr_tag == NOTAG) + params.vlr_tag = vreq.vlr_tag; + } vlan_set(ctx->io_s, &ifr); } } diff --git a/sbin/ifconfig/ifvxlan.c b/sbin/ifconfig/ifvxlan.c index e4871da645fd..cbea685b6ba0 100644 --- a/sbin/ifconfig/ifvxlan.c +++ b/sbin/ifconfig/ifvxlan.c @@ -24,7 +24,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioctl.h> #include <sys/socket.h> diff --git a/sbin/ifconfig/sfp.c b/sbin/ifconfig/sfp.c index a357a2bbefdd..0dc1def751b1 100644 --- a/sbin/ifconfig/sfp.c +++ b/sbin/ifconfig/sfp.c @@ -23,11 +23,6 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/types.h> #include <sys/param.h> #include <sys/ioctl.h> diff --git a/sbin/ifconfig/tests/Makefile b/sbin/ifconfig/tests/Makefile index 9b29983afc7c..e902f262552a 100644 --- a/sbin/ifconfig/tests/Makefile +++ b/sbin/ifconfig/tests/Makefile @@ -1,6 +1,8 @@ +NETBSD_ATF_TESTS_SH= nonexistent_test +ATF_TESTS_SH+= inet6 -NETBSD_ATF_TESTS_SH= nonexistent_test +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 79ccaf149ace..1fc9b633f664 100644 --- a/sbin/init/Makefile +++ b/sbin/init/Makefile @@ -1,9 +1,6 @@ -# @(#)Makefile 8.1 (Berkeley) 7/19/93 - CONFGROUPS= CONFTTYS PACKAGE=runtime PROG= init -SRCS= init.c getmntopts.c MAN= init.8 PRECIOUSPROG= INSTALLFLAGS=-b -B.bak @@ -13,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.8 b/sbin/init/init.8 index 88d663a1afe8..8f6e78b79d7b 100644 --- a/sbin/init/init.8 +++ b/sbin/init/init.8 @@ -28,8 +28,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)init.8 8.3 (Berkeley) 4/18/94 -.\" .Dd July 22, 2021 .Dt INIT 8 .Os @@ -398,6 +396,7 @@ back to the default, so that the .Pa /etc/rc script is executed with the standard shell .Pa /bin/sh . +.El .Sh FILES .Bl -tag -width /var/log/init.log -compact .It Pa /dev/console diff --git a/sbin/init/init.c b/sbin/init/init.c index dd8800d21910..d28501053c7f 100644 --- a/sbin/init/init.c +++ b/sbin/init/init.c @@ -32,20 +32,6 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1991, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 7/15/93"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/boottrace.h> #include <sys/ioctl.h> @@ -63,6 +49,7 @@ static const char rcsid[] = #include <fcntl.h> #include <kenv.h> #include <libutil.h> +#include <mntopts.h> #include <paths.h> #include <signal.h> #include <stdarg.h> @@ -83,7 +70,6 @@ static const char rcsid[] = #include <login_cap.h> #endif -#include "mntopts.h" #include "pathnames.h" /* @@ -865,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; @@ -899,6 +885,7 @@ single_user(void) */ open_console(); + pp = getpwnam("root"); #ifdef SECURE /* * Check the root password. @@ -906,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); @@ -923,7 +909,6 @@ single_user(void) } } endttyent(); - endpwent(); #endif /* SECURE */ #ifdef DEBUGSHELL @@ -944,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/init/pathnames.h b/sbin/init/pathnames.h index e385aed648b4..c04155a901ae 100644 --- a/sbin/init/pathnames.h +++ b/sbin/init/pathnames.h @@ -30,8 +30,6 @@ * 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. - * - * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 */ #include <paths.h> diff --git a/sbin/init/ttys b/sbin/init/ttys index f5cfe4118da6..d3ac4546d9be 100644 --- a/sbin/init/ttys +++ b/sbin/init/ttys @@ -1,6 +1,3 @@ -# -# @(#)ttys 5.1 (Berkeley) 4/17/89 -# # This file specifies various information about terminals on the system. # It is used by several different programs. Common entries for the # various columns include: diff --git a/sbin/ipf/Makefile b/sbin/ipf/Makefile index 32cead444f77..1b0a18d3d9c3 100644 --- a/sbin/ipf/Makefile +++ b/sbin/ipf/Makefile @@ -1,4 +1,3 @@ - SUBDIR= libipf .WAIT SUBDIR+= ipf ipfs ipfstat ipmon ipnat ippool # XXX Temporarily disconnected. diff --git a/sbin/ipf/Makefile.inc b/sbin/ipf/Makefile.inc index 39bdc3674b2b..cb3a3df50e0c 100644 --- a/sbin/ipf/Makefile.inc +++ b/sbin/ipf/Makefile.inc @@ -1,4 +1,3 @@ - .include <src.opts.mk> WARNS?= 2 diff --git a/sbin/ipf/common/ipf.h b/sbin/ipf/common/ipf.h index c672c53cb56d..3e6ee594b8b6 100644 --- a/sbin/ipf/common/ipf.h +++ b/sbin/ipf/common/ipf.h @@ -3,8 +3,6 @@ * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. - * - * @(#)ipf.h 1.12 6/5/96 * $Id$ */ @@ -180,7 +178,7 @@ typedef int (* copyfunc_t)(void *, void *, size_t); extern char thishost[MAXHOSTNAMELEN]; extern char flagset[]; -extern u_char flags[]; +extern uint16_t flags[]; extern struct ipopt_names ionames[]; extern struct ipopt_names secclass[]; extern char *icmpcodes[MAX_ICMPCODE + 1]; @@ -332,7 +330,7 @@ extern int remove_hash(struct iphtable_s *, ioctlfunc_t); extern int remove_hashnode(int, char *, struct iphtent_s *, ioctlfunc_t); extern int remove_pool(ip_pool_t *, ioctlfunc_t); extern int remove_poolnode(int, char *, ip_pool_node_t *, ioctlfunc_t); -extern u_char tcpflags(char *); +extern uint16_t tcpflags(char *); extern void printc(struct frentry *); extern void printC(int); extern void emit(int, int, void *, struct frentry *); diff --git a/sbin/ipf/common/ipmon.h b/sbin/ipf/common/ipmon.h index 8caf20fdf725..4d377027eb3d 100644 --- a/sbin/ipf/common/ipmon.h +++ b/sbin/ipf/common/ipmon.h @@ -3,8 +3,6 @@ * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. - * - * @(#)ip_fil.h 1.35 6/5/96 * $Id$ */ diff --git a/sbin/ipf/common/lexer.c b/sbin/ipf/common/lexer.c index 5a898c72032d..56ac3586af6e 100644 --- a/sbin/ipf/common/lexer.c +++ b/sbin/ipf/common/lexer.c @@ -10,6 +10,7 @@ # include "netinet/ip_scan.h" #endif #include <sys/ioctl.h> +#include <sys/param.h> #include <syslog.h> #ifdef TEST_LEXER # define NO_YACC @@ -448,7 +449,7 @@ buildipv6: oc = c; if (prior == YY_NUMBER && c == ':') { - snprintf(s, sizeof(s), "%d", priornum); + snprintf(s, sizeof(ipv6buf), "%d", priornum); s += strlen(s); } @@ -675,7 +676,7 @@ yysetfixeddict(wordtab_t *newdict) if (yydebug) printf("yysetfixeddict(%lx)\n", (u_long)newdict); - if (yysavedepth == sizeof(yysavewords)/sizeof(yysavewords[0])) { + if (yysavedepth == nitems(yysavewords)) { fprintf(stderr, "%d: at maximum dictionary depth\n", yylineNum); return; @@ -694,7 +695,7 @@ yysetdict(wordtab_t *newdict) if (yydebug) printf("yysetdict(%lx)\n", (u_long)newdict); - if (yysavedepth == sizeof(yysavewords)/sizeof(yysavewords[0])) { + if (yysavedepth == nitems(yysavewords)) { fprintf(stderr, "%d: at maximum dictionary depth\n", yylineNum); return; diff --git a/sbin/ipf/ipf/Makefile b/sbin/ipf/ipf/Makefile index 981be4acd99e..00cabf50cd89 100644 --- a/sbin/ipf/ipf/Makefile +++ b/sbin/ipf/ipf/Makefile @@ -1,4 +1,3 @@ - PACKAGE= ipf PROG= ipf SRCS= ${GENHDRS} ipf.c ipfcomp.c ipf_y.c ipf_l.c bpf_filter.c diff --git a/sbin/ipf/ipf/bpf-ipf.h b/sbin/ipf/ipf/bpf-ipf.h index 1c3032150377..e41e9d71bbb9 100644 --- a/sbin/ipf/ipf/bpf-ipf.h +++ b/sbin/ipf/ipf/bpf-ipf.h @@ -35,10 +35,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * @(#)bpf.h 7.1 (Berkeley) 5/7/91 - * - * @(#) $Header: /devel/CVS/IP-Filter/bpf-ipf.h,v 2.1 2002/10/26 12:14:26 darrenr Exp $ (LBL) */ #ifndef BPF_MAJOR_VERSION @@ -90,7 +86,7 @@ struct bpf_stat { * bpf understands a program iff kernel_major == filter_major && * kernel_minor >= filter_minor, that is, if the value returned by the * running kernel has the same major number and a minor number equal - * equal to or less than the filter being downloaded. Otherwise, the + * to or less than the filter being downloaded. Otherwise, the * results are undefined, meaning an error may be returned or packets * may be accepted haphazardly. * It has nothing to do with the source code version. diff --git a/sbin/ipf/ipf/bpf_filter.c b/sbin/ipf/ipf/bpf_filter.c index ff7c93576d1e..bd7012d2f1ab 100644 --- a/sbin/ipf/ipf/bpf_filter.c +++ b/sbin/ipf/ipf/bpf_filter.c @@ -35,15 +35,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * @(#)bpf.c 7.5 (Berkeley) 7/15/91 */ -#if !(defined(lint) || defined(KERNEL) || defined(_KERNEL)) -static const char rcsid[] = - "@(#) $Header: /devel/CVS/IP-Filter/bpf_filter.c,v 2.2.2.3 2006/10/03 11:25:56 darrenr Exp $ (LBL)"; -#endif - #include <sys/param.h> #include <sys/types.h> #include <sys/time.h> diff --git a/sbin/ipf/ipf/ipf.4 b/sbin/ipf/ipf/ipf.4 index c474d5d895de..c5b3bac34947 100644 --- a/sbin/ipf/ipf/ipf.4 +++ b/sbin/ipf/ipf/ipf.4 @@ -6,7 +6,6 @@ ipf \- packet filtering kernel interface .br #include <netinet/ip_fil.h> .SH IOCTLS -.PP To add and delete rules to the filter list, three 'basic' ioctls are provided for use. The ioctl's are called as: .LP diff --git a/sbin/ipf/ipf/ipf.5 b/sbin/ipf/ipf/ipf.5 index 79e6b2c45b05..4ff33290814a 100644 --- a/sbin/ipf/ipf/ipf.5 +++ b/sbin/ipf/ipf/ipf.5 @@ -2,7 +2,6 @@ .SH NAME ipf, ipf.conf \- IPFilter firewall rules file format .SH DESCRIPTION -.PP The ipf.conf file is used to specify rules for the firewall, packet authentication and packet accounting components of IPFilter. To load rules specified in the ipf.conf file, the ipf(8) program is used. @@ -29,9 +28,8 @@ the direction of the packet (in or out) address patterns or "all" to match any address information .RE .SS Long lines -.PP For rules lines that are particularly long, it is possible to split -them over multiple lines implicity like this: +them over multiple lines implicitly like this: .PP .nf pass in on bgeo proto tcp from 1.1.1.1 port > 1000 @@ -45,7 +43,6 @@ pass in on bgeo proto tcp from 1.1.1.1 port > 1000 \\ to 2.2.2.2 port < 5000 flags S keep state .fi .SS Comments -.PP Comments in the ipf.conf file are indicated by the use of the '#' character. This can either be at the start of the line, like this: .PP @@ -60,7 +57,6 @@ Or at the end of a like, like this: pass in proto icmp from any to any # Allow all ICMP packets in .fi .SH Firewall rules -.PP This section goes into detail on how to construct firewall rules that are placed in the ipf.conf file. .PP @@ -69,7 +65,6 @@ firewall rule set or which packets should be blocked or allowed in. Some suggestions will be provided but further reading is expected to fully understand what is safe and unsafe to allow in/out. .SS Filter rule keywords -.PP The first word found in any filter rule describes what the eventual outcome of a packet that matches it will be. Descriptions of the many and various sections that can be used to match on the contents of packet headers will @@ -131,7 +126,6 @@ rule to match a packet is a pass, if there is a later matching rule that is a block and no further rules match the packet, then it will be blocked. .SS Matching Network Interfaces -.PP On systems with more than one network interface, it is necessary to be able to specify different filter rules for each of them. In the first instance, this is because different networks will send us @@ -158,7 +152,6 @@ block in on bge0 all pass out on bge0 all .fi .SS Address matching (basic) -.PP The first and most basic part of matching for filtering rules is to specify IP addresses and TCP/UDP port numbers. The source address information is matched by the "from" information in a filter rule @@ -197,7 +190,6 @@ is processing that part of the configuration file, leading to long delays, if not errors, in loading the filter rules. .RE .SS Protocol Matching -.PP To match packets based on TCP/UDP port information, it is first necessary to indicate which protocol the packet must be. This is done using the "proto" keyword, followed by either the protocol number or a name which @@ -209,7 +201,6 @@ block out proto udp from any to 10.1.1.1 pass in proto icmp from any to 192.168.0.0/16 .fi .SS Sending back error packets -.PP When a packet is just discarded using a block rule, there is no feedback given to the host that sent the packet. This is both good and bad. If this is the desired behaviour and it is not desirable to send any feedback about packets @@ -223,7 +214,7 @@ To address this problem, a block rule can be qualified in two ways. The first of these is specific to TCP and instructs IPFilter to send back a reset (RST) packet. This packet indicates to the remote system that the packet it sent has been rejected and that it shouldn't make any further -attempts( to send packets to that port. Telling IPFilter to return a TCP); +attempts to send packets to that port. Telling IPFilter to return a TCP RST packet in response to something that has been received is achieved with the return-rst keyword like this: .PP @@ -239,18 +230,18 @@ For all of the other protocols handled by the IP protocol suite, to send back an error indicating that the received packet was dropped requires sending back an ICMP error packet. Whilst these can also be used for TCP, the sending host may not treat the received ICMP error as a hard error -in( the same way as it does the TCP RST packet. To return an ICMP error); +in the same way as it does the TCP RST packet. To return an ICMP error it is necessary to place return-icmp after the block keyword like this: .PP .nf block return-icmp in proto udp from any to 192.168.0.1/24 .fi .PP -When( electing to return an ICMP error packet, it is also possible to); +When electing to return an ICMP error packet, it is also possible to select what type of ICMP error is returned. Whilst the full compliment of ICMP unreachable codes can be used by specifying a number instead of the string below, only the following should be used in conjunction with -return-icmp.( Which return code to use is a choice to be made when); +return-icmp. Which return code to use is a choice to be made when weighing up the pro's and con's. Using some of the codes may make it more obvious that a firewall is being used rather than just the host not responding. @@ -295,7 +286,7 @@ proto-unr (protocol unreachable) the IP protocol specified in the packet is not available to receive packets. -.DE +.RE .PP An example that shows how to send back a port unreachable packet for UDP packets to 192.168.1.0/24 is as follows: @@ -317,7 +308,6 @@ block return-icmp-as-dest(port-unr) in proto udp \\ from any to 192.168.1.0/24 .fi .SS TCP/UDP Port Matching -.PP Having specified which protocol is being matched, it is then possible to indicate which port numbers a packet must have in order to match the rule. Due to port numbers being used differently to addresses, it is therefore @@ -361,7 +351,6 @@ If there is no desire to mention any specific source or destintion information in a filter rule then the word "all" can be used to indicate that all addresses are considered to match the rule. .SS IPv4 or IPv6 -.PP If a filter rule is constructed without any addresses then IPFilter will attempt to match both IPv4 and IPv6 packets with it. In the next list of rules, each one can be applied to either network protocol @@ -399,13 +388,11 @@ protocol family qualifier: pass in family inet6 proto udp from any to any port = 53 .fi .SS First match vs last match -.PP To change the default behaviour from being the last matched rule decides the outcome to being the first matched rule, the word "quick" is inserted to the rule. .SH Extended Packet Matching .SS Beyond using plain addresses -.PP On firewalls that are working with large numbers of hosts and networks or simply trying to filter discretely against various hosts, it can be an easier administration task to define a pool of addresses and have @@ -475,7 +462,6 @@ with. pass in proto icmp from any to (bge0)/32 .fi .SS Using address pools -.PP Rather than list out multiple rules that either allow or deny specific addresses, it is possible to create a single object, call an address pool, that contains all of those addresses and reference that in the @@ -505,7 +491,6 @@ There are different operational characteristics with each, so there may be some situations where a pool works better than hash and vice versa. .SS Matching TCP flags -.PP The TCP header contains a field of flags that is used to decide if the packet is a connection request, connection termination, data, etc. By matching on the flags in conjunction with port numbers, it is @@ -536,7 +521,7 @@ URG - this bit is set to indicate that the packet contains urgent data .HP R RST - this bit is set only in packets that are a reply to another -that has been received but is not targetted at any open port +that has been received but is not targeted at any open port .HP C CWN @@ -548,10 +533,10 @@ When matching TCP flags, it is normal to just list the flag that you wish to be set. By default the set of flags it is compared against is "FSRPAU". Rules that say "flags S" will be displayed by ipfstat(8) as having "flags S/FSRPAU". This is normal. -The last two flags, "C" and "E", are optional - they +The last three flags, "E", "W" and "e", are optional - they may or may not be used by an end host and have no bearing on either the acceptance of data nor control of the connection. Masking them -out with "flags S/FSRPAUCE" may cause problems for remote hosts +out with "flags S/FSRPAUEWe" may cause problems for remote hosts making a successful connection. .PP .nf @@ -562,7 +547,6 @@ pass out quick proto tcp from any port = 22 to any flags SA By itself, filtering based on the TCP flags becomes more work but when combined with stateful filtering (see below), the situation changes. .SS Matching on ICMP header information -.PP The TCP and UDP are not the only protocols for which filtering beyond just the IP header is possible, extended matching on ICMP packets is also available. The list of valid ICMP types is different for IPv4 @@ -627,7 +611,6 @@ unreach (unreachable, whoreq (WRU request), whorep (WRU reply). .SH Stateful Packet Filtering -.PP Stateful packet filtering is where IPFilter remembers some information from one or more packets that it has seen and is able to apply it to future packets that it receives from the network. @@ -686,7 +669,7 @@ Once a TCP connection has reached the established state, the default timeout allows for it to be idle for 5 days before it is removed from the state table. The timeouts for the other TCP connection states vary from 240 seconds to 30 seconds. -Both UDP and ICMP state entries have asymetric timeouts where the timeout +Both UDP and ICMP state entries have asymmetric timeouts where the timeout set upon seeing packets in the forward direction is much larger than for the reverse direction. For UDP the default timeouts are 120 and 12 seconds, for ICMP 60 and 6 seconds. This is a reflection of the @@ -694,7 +677,6 @@ use of these protocols being more for query-response than for ongoing connections. For all other protocols the timeout is 60 seconds in both directions. .SS Stateful filtering options -.PP The following options can be used with stateful filtering: .HP limit @@ -812,7 +794,6 @@ If there is no IP protocol implied by addresses or other features of the rule, IPFilter will assume that no netmask is an all ones netmask for both IPv4 and IPv6. .SS Tieing down a connection -.PP For any connection that transits a firewall, each packet will be seen twice: once going in and once going out. Thus a connection has 4 flows of packets: @@ -851,7 +832,6 @@ pass in on bge0,bge1 out-via bge1,bge0 proto tcp \\ from any to any port = 22 flags S keep state .fi .SS Working with packet fragments -.PP Fragmented packets result in 1 packet containing all of the layer 3 and 4 header information whilst the data is split across a number of other packets. .PP @@ -883,15 +863,14 @@ An example of how this is done is as follows: pass in proto udp from any port = 2049 to any with frags keep frags .fi .SH Building a tree of rules -.PP Writing your filter rules as one long list of rules can be both inefficient in terms of processing the rules and difficult to understand. To make the construction of filter rules easier, it is possible to place them in groups. A rule can be both a member of a group and the head of a new group. .PP Using filter groups requires at least two rules: one to be in the group -one one to send matchign packets to the group. If a packet matches a -filtre rule that is a group head but does not match any of the rules +one one to send matching packets to the group. If a packet matches a +filter rule that is a group head but does not match any of the rules in that group, then the packet is considered to have matched the head rule. .PP @@ -947,7 +926,6 @@ to deliver spam, I could load the following rule to complement the above: block in quick from 10.1.1.1 to any group spammers .fi .SS Decapsulation -.PP Rule groups also form a different but vital role for decapsulation rules. With the following simple rule, if IPFilter receives an IP packet that has an AH header as its layer 4 payload, IPFilter would adjust its view of the @@ -982,7 +960,6 @@ It is possible to construct a decapsulate rule without the group head at the end that ipf(8) will accept but such rules will not result in anything happening. .SS Policy Based Routing -.PP With firewalls being in the position they often are, at the boundary of different networks connecting together and multiple connections that have different properties, it is often desirable to have packets flow @@ -1034,10 +1011,9 @@ pass in on bge0 to bge1:1.1.1.1 reply-to hme1:2.1.1.2 \\ to any port = 80 flags S keep state .fi .SS Matching IPv4 options -.PP The design for IPv4 allows for the header to be upto 64 bytes long, however most traffic only uses the basic header which is 20 bytes long. -The other 44 bytes can be uesd to store IP options. These options are +The other 44 bytes can be used to store IP options. These options are generally not necessary for proper interaction and function on the Internet today. For most people it is sufficient to block and drop all packets that have any options set. This can be achieved with this @@ -1090,7 +1066,7 @@ some of the nodes the packet must go through, with the ssrr option, every next hop router must be specified. .PP The complete list of IPv4 options that can be matched on is: -addext (Address Extention), +addext (Address Extension), cipso (Classical IP Security Option), dps (Dynamic Packet State), e-sec (Extended Security), @@ -1115,7 +1091,6 @@ ump (Upstream Multicast Packet), visa (Experimental Access Control) and zsu (Experimental Measurement). .SS Security with CIPSO and IPSO -.PP IPFilter supports filtering on IPv4 packets using security attributes embedded in the IP options part of the packet. These options are usually only used on networks and systems that are using lablled security. Unless you know that @@ -1139,7 +1114,6 @@ block in quick all with opt sec-class unclass pass in all with opt sec-class secret .fi .SS Matching IPv6 extension headers -.PP Just as it is possible to filter on the various IPv4 header options, so too it is possible to filter on the IPv6 extension headers that are placed between the IPv6 header and the transport protocol header. @@ -1153,7 +1127,6 @@ mobility (IP mobility), none, routing. .SS Logging -.PP There are two ways in which packets can be logged with IPFilter. The first is with a rule that specifically says log these types of packets and the second is a qualifier to one of the other keywords. Thus it is @@ -1211,7 +1184,6 @@ pass in log level local1.info proto tcp \\ ipfstat(8) reports how many packets have been successfully logged and how many failed attempts to log a packet there were. .SS Filter rule comments -.PP If there is a desire to associate a text string, be it an administrative comment or otherwise, with an IPFilter rule, this can be achieved by giving the filter rule a comment. The comment is loaded with the rule into the @@ -1224,7 +1196,6 @@ pass out quick proto tcp from any port = 80 \\ to any comment "all web server traffic is ok" .fi .SS Tags -.PP To enable filtering and NAT to correctly match up packets with rules, tags can be added at with NAT (for inbound packets) and filtering (for outbound packets.) This allows a filter to be correctly mated with its @@ -1249,7 +1220,6 @@ such as grep, extracting log records of interest is simplified. block in quick log ... set-tag(log=33) .fi .SH Filter Rule Expiration -.PP IPFilter allows rules to be added into the kernel that it will remove after a specific period of time by specifying rule-ttl at the end of a rule. When listing rules in the kernel using ipfstat(8), rules that are going @@ -1264,7 +1234,6 @@ pass in on fxp0 proto tcp from any \\ to port = 22 flags S keep state rule-ttl 30 .fi .SH Internal packet attributes -.PP In addition to being able to filter on very specific network and transport header fields, it is possible to filter on other attributes that IPFilter attaches to a packet. These attributes are placed in a rule after the @@ -1332,7 +1301,6 @@ block in all pass in all with not bad .fi .SH Tuning IPFilter -.PP The ipf.conf file can also be used to tune the behaviour of IPFilter, allowing, for example, timeouts for the NAT/state table(s) to be set along with their sizes. The presence and names of tunables may change @@ -1357,7 +1325,7 @@ A list of the currently available variables inside IPFilter that may be tuned from ipf.conf are as follows: .HP active -set through -s command line switch of ipf(8). See ipf(8) for detals. +set through -s command line switch of ipf(8). See ipf(8) for details. .HP chksrc when set, enables reverse path verification on source addresses and @@ -1430,7 +1398,7 @@ sets the size of the in-kernel log buffer in bytes. log_suppress when set, IPFilter will check to see if the packet it is logging is similar to the one it previously logged and if so, increases -the occurance count for that packet. The previously logged packet +the occurrence count for that packet. The previously logged packet must not have yet been read by ipmon(8). .HP min_ttl @@ -1467,8 +1435,8 @@ when the fill percentage of the NAT table exceeds this mark, more aggressive flushing is enabled. .HP nat_table_wm_low -this sets the percentage at which the NAT table's agressive flushing -will turn itself off at. +this sets the percentage at which the NAT table's aggressive flushing +will turn itself off. .HP rdr_rules_size size of the hash table to store rdr rules. @@ -1492,7 +1460,7 @@ state_size size of the hash table used for stateful filtering .HP state_wm_freq -this controls how often the agressive flushing should be run once the +this controls how often the aggressive flushing should be run once the state table exceeds state_wm_high in percentage full. .HP state_wm_high @@ -1500,7 +1468,7 @@ when the fill percentage of the state table exceeds this mark, more aggressive flushing is enabled. .HP state_wm_low -this sets the percentage at which the state table's agressive flushing +this sets the percentage at which the state table's aggressive flushing will turn itself off at. .HP tcp_close_wait @@ -1543,7 +1511,6 @@ update_ipid when set, turns on changing the IP id field in NAT'd packets to a random number. .SS Table of visible variables -.PP A list of all of the tunables, their minimum, maximum and current values is as follows. .PP @@ -1602,7 +1569,6 @@ udp_timeout 1 MAXINT 240 update_ipid 0 1 0 .fi .SH Calling out to internal functions -.PP IPFilter provides a pair of functions that can be called from a rule that allow for a single rule to jump out to a group rather than walk through a list of rules to find the group. If you've got multiple @@ -1637,7 +1603,6 @@ group-map in role=ipf number=1010 { 1.1.1.1 group = 1020, 3.3.0.0/16 group = 1030; }; .fi .SS IPFilter matching expressions -.PP An experimental feature that has been added to filter rules is to use the same expression matching that is available with various commands to flush and list state/NAT table entries. The use of such an expression @@ -1647,7 +1612,6 @@ precludes the filter rule from using the normal IP header matching. pass in exp { "tcp.sport 23 or tcp.sport 50" } keep state .fi .SS Filter rules with BPF -.PP On platforms that have the BPF built into the kernel, IPFilter can be built to allow BPF expressions in filter rules. This allows for packet matching to be on arbitrary data in the packt. The use of a BPF expression @@ -1665,7 +1629,6 @@ accurately reconstruct the original text filter. The end result is that while ipf.conf() can be easy to read, understanding the output from ipfstat might not be. .SH VARIABLES -.PP This configuration file, like all others used with IPFilter, supports the use of variable substitution throughout the text. .PP diff --git a/sbin/ipf/ipf/ipf.8 b/sbin/ipf/ipf/ipf.8 index b999f04e5d03..fba145b0c785 100644 --- a/sbin/ipf/ipf/ipf.8 +++ b/sbin/ipf/ipf/ipf.8 @@ -22,7 +22,6 @@ ipf \- alters packet filtering lists for IP packet input and output <\fIfilename\fP> [...]] .SH DESCRIPTION -.PP \fBipf\fP opens the filenames listed (treating "\-" as stdin) and parses the file for a set of rules which are to be added or removed from the packet filter rule set. @@ -48,7 +47,7 @@ supports \fBlanguage\fI. At present, the only target language supported is \fBC\fB (-cc) for which two files - \fBip_rules.c\fP and \fBip_rules.h\fP are generated in the \fBCURRENT DIRECTORY\fP when \fBipf\fP is being run. These files can be used with the -\fBIPFILTER_COMPILED\fP kernel option to build filter rules staticlly into +\fBIPFILTER_COMPILED\fP kernel option to build filter rules statically into the kernel. .TP .B \-d @@ -158,7 +157,8 @@ Zero global statistics held in the kernel for filtering only (this doesn't affect fragment or state statistics). .DT .SH ENVIRONMENT -.NM utilizes the following environment variable. +.B ipf +utilizes the following environment variable. .TP .B IPF_PREDEFINED ipfilter variables, see VARIABLES in ipf(5), can be specified in this @@ -175,9 +175,7 @@ IPF_PREDEFINED='my_server="10.1.1.1"; my_client="10.1.1.2";' .SH SEE ALSO ipftest(1), mkfilters(1), ipf(4), ipl(4), ipf(5), ipfstat(8), ipmon(8), ipnat(8) .SH DIAGNOSTICS -.PP Needs to be run as root for the packet filtering lists to actually be affected inside the kernel. .SH BUGS -.PP If you find any, please send email to me at darrenr@pobox.com diff --git a/sbin/ipf/ipf/ipf.c b/sbin/ipf/ipf/ipf.c index fb40371fefd3..cf14c06ee829 100644 --- a/sbin/ipf/ipf/ipf.c +++ b/sbin/ipf/ipf/ipf.c @@ -10,10 +10,6 @@ #include <sys/ioctl.h> #include "netinet/ipl.h" -#if !defined(lint) -static const char sccsid[] = "@(#)ipf.c 1.23 6/5/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif #if !defined(__SVR4) && defined(__GNUC__) extern char *index(const char *, int); diff --git a/sbin/ipf/ipf/ipfcomp.c b/sbin/ipf/ipf/ipfcomp.c index 47094e01ab2f..9d0b3642e732 100644 --- a/sbin/ipf/ipf/ipfcomp.c +++ b/sbin/ipf/ipf/ipfcomp.c @@ -4,10 +4,6 @@ * * See the IPFILTER.LICENCE file for details on licencing. */ -#if !defined(lint) -static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif #include "ipf.h" @@ -689,7 +685,7 @@ emitGroup(int num, int dir, void *v, frentry_t *fr, char *group, if (n) { /* * Calculate the indentation interval upto the last common - * common comparison being made. + * comparison being made. */ for (i = 0, in = 1; i < FRC_MAX; i++) { if (n[i].c != m[i].c) diff --git a/sbin/ipf/ipf/ipfilter.4 b/sbin/ipf/ipf/ipfilter.4 index f262f711b8ff..39676e3c1dae 100644 --- a/sbin/ipf/ipf/ipfilter.4 +++ b/sbin/ipf/ipf/ipfilter.4 @@ -123,7 +123,7 @@ file syslog .PP ipsend(1) -generates arbitary IP packets for ethernet connected machines. +generates arbitrary IP packets for ethernet connected machines. .PP ipresend(1) reads in a data file of saved IP packets (ie diff --git a/sbin/ipf/ipf/ipfilter.5 b/sbin/ipf/ipf/ipfilter.5 index 0bba0f4bad02..0a1da67d70cd 100644 --- a/sbin/ipf/ipf/ipfilter.5 +++ b/sbin/ipf/ipf/ipfilter.5 @@ -2,7 +2,6 @@ .SH NAME IP Filter .SH DESCRIPTION -.PP IP Filter is a package providing packet filtering capabilities for a variety of operating systems. On a properly setup system, it can be used to build a firewall. diff --git a/sbin/ipf/ipfs/Makefile b/sbin/ipf/ipfs/Makefile index 56eb92edec09..09bf881deca3 100644 --- a/sbin/ipf/ipfs/Makefile +++ b/sbin/ipf/ipfs/Makefile @@ -1,4 +1,3 @@ - PACKAGE=ipf PROG= ipfs MAN= ipfs.8 diff --git a/sbin/ipf/ipfs/ipfs.8 b/sbin/ipf/ipfs/ipfs.8 index a58d02db078a..cf668cc09400 100644 --- a/sbin/ipf/ipfs/ipfs.8 +++ b/sbin/ipf/ipfs/ipfs.8 @@ -40,7 +40,6 @@ ipfs \- saves and restores information for NAT and state tables. .B \-i <if1>,<if2> .SH DESCRIPTION -.PP \fBipfs\fP allows state information created for NAT entries and rules using \fIkeep state\fP to be locked (modification prevented) and then saved to disk, allowing for the system to experience a reboot, followed by the restoration @@ -117,10 +116,8 @@ operation and unlocked once complete. .SH SEE ALSO ipf(8), ipl(4), ipmon(8), ipnat(8) .SH DIAGNOSTICS -.PP Perhaps the -W and -R operations should set the locking but rather than undo it, restore it to what it was previously. Fragment table information is currently not saved. .SH BUGS -.PP If you find any, please send email to me at darrenr@pobox.com diff --git a/sbin/ipf/ipfs/ipfs.c b/sbin/ipf/ipfs/ipfs.c index bb342cbb9319..6225c6e1154d 100644 --- a/sbin/ipf/ipfs/ipfs.c +++ b/sbin/ipf/ipfs/ipfs.c @@ -30,9 +30,6 @@ #include "ipf.h" #include "netinet/ipl.h" -#if !defined(lint) -static const char rcsid[] = "@(#)$Id$"; -#endif #ifndef IPF_SAVEDIR # define IPF_SAVEDIR "/var/db/ipf" diff --git a/sbin/ipf/ipfstat/Makefile b/sbin/ipf/ipfstat/Makefile index 7d03f282f672..795e89e0a14f 100644 --- a/sbin/ipf/ipfstat/Makefile +++ b/sbin/ipf/ipfstat/Makefile @@ -1,4 +1,3 @@ - NOGCCERROR= # defined PACKAGE= ipf diff --git a/sbin/ipf/ipfstat/ipfstat.c b/sbin/ipf/ipfstat/ipfstat.c index f9696bfce73d..fd0ac83097a0 100644 --- a/sbin/ipf/ipfstat/ipfstat.c +++ b/sbin/ipf/ipfstat/ipfstat.c @@ -37,10 +37,6 @@ # include <paths.h> #endif -#if !defined(lint) -static const char sccsid[] = "@(#)fils.c 1.21 4/20/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif extern char *optarg; @@ -327,7 +323,7 @@ int main(int argc, char *argv[]) case 'm' : filter = parseipfexpr(optarg, NULL); if (filter == NULL) { - fprintf(stderr, "Error parseing '%s'\n", + fprintf(stderr, "Error parsing '%s'\n", optarg); exit(1); } diff --git a/sbin/ipf/ipfsync/ipfsyncd.c b/sbin/ipf/ipfsync/ipfsyncd.c index ead92b70371c..e22aa7c1423c 100644 --- a/sbin/ipf/ipfsync/ipfsyncd.c +++ b/sbin/ipf/ipfsync/ipfsyncd.c @@ -3,10 +3,6 @@ * * See the IPFILTER.LICENCE file for details on licencing. */ -#if !defined(lint) -static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id: ipfsyncd.c,v 1.1.2.2 2012/07/22 08:04:24 darren_r Exp $"; -#endif #include <sys/types.h> #include <sys/time.h> #include <sys/socket.h> diff --git a/sbin/ipf/ipfsync/ipsyncm.c b/sbin/ipf/ipfsync/ipsyncm.c index a9484a339c82..74dada9f56c5 100644 --- a/sbin/ipf/ipfsync/ipsyncm.c +++ b/sbin/ipf/ipfsync/ipsyncm.c @@ -4,10 +4,6 @@ * * See the IPFILTER.LICENCE file for details on licencing. */ -#if !defined(lint) -static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif #include <sys/types.h> #include <sys/time.h> #include <sys/socket.h> diff --git a/sbin/ipf/ipfsync/ipsyncs.c b/sbin/ipf/ipfsync/ipsyncs.c index ee9ecfd4f886..4aec6925f079 100644 --- a/sbin/ipf/ipfsync/ipsyncs.c +++ b/sbin/ipf/ipfsync/ipsyncs.c @@ -4,10 +4,6 @@ * * See the IPFILTER.LICENCE file for details on licencing. */ -#if !defined(lint) -static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif #include <sys/types.h> #include <sys/time.h> #include <sys/socket.h> diff --git a/sbin/ipf/ipftest/Makefile b/sbin/ipf/ipftest/Makefile index 3ff410e8f225..d446ab9d22be 100644 --- a/sbin/ipf/ipftest/Makefile +++ b/sbin/ipf/ipftest/Makefile @@ -1,4 +1,3 @@ - PACKAGE= ipf PROG= ipftest SRCS= ${GENHDRS} ipftest.c fil.c ip_frag.c ip_state.c ip_nat.c \ diff --git a/sbin/ipf/ipftest/ip_fil.c b/sbin/ipf/ipftest/ip_fil.c index 74d4ec778d8e..6df3bed8224e 100644 --- a/sbin/ipf/ipftest/ip_fil.c +++ b/sbin/ipf/ipftest/ip_fil.c @@ -6,10 +6,6 @@ * * $Id$ */ -#if !defined(lint) -static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif #include "ipf.h" #include "md5.h" diff --git a/sbin/ipf/ipftest/ipftest.1 b/sbin/ipf/ipftest/ipftest.1 index 11b64e288600..456304c9d0b2 100644 --- a/sbin/ipf/ipftest/ipftest.1 +++ b/sbin/ipf/ipftest/ipftest.1 @@ -34,7 +34,6 @@ interface <optionlist> ] .SH DESCRIPTION -.PP \fBipftest\fP is provided for the purpose of being able to test a set of filter rules without having to put them in place, in operation and proceed to test their effectiveness. The hope is that this minimises disruptions @@ -126,7 +125,7 @@ This is the default if no \fB\-F\fP argument is specified. The format used is as follows: .nf "in"|"out" "on" if ["tcp"|"udp"|"icmp"] - srchost[,srcport] dsthost[,destport] [FSRPAU] + srchost[,srcport] dsthost[,destport] [FSRPAUEWe] .fi .PP This allows for a packet going "in" or "out" of an interface (if) to be diff --git a/sbin/ipf/ipftest/ipftest.c b/sbin/ipf/ipftest/ipftest.c index d83d47884089..2dfbe20592b3 100644 --- a/sbin/ipf/ipftest/ipftest.c +++ b/sbin/ipf/ipftest/ipftest.c @@ -9,10 +9,6 @@ #include <sys/ioctl.h> #include <sys/file.h> -#if !defined(lint) -static const char sccsid[] = "@(#)ipt.c 1.19 6/3/96 (C) 1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif extern char *optarg; extern struct ipread pcap, iptext, iphex; diff --git a/sbin/ipf/iplang/iplang_y.y b/sbin/ipf/iplang/iplang_y.y index c5c7c729e0a9..9e8ebf4e4312 100644 --- a/sbin/ipf/iplang/iplang_y.y +++ b/sbin/ipf/iplang/iplang_y.y @@ -1045,9 +1045,9 @@ void set_tcpsum(char **arg) void set_tcpflags(char **arg) { - static char flags[] = "ASURPF"; + static char flags[] = "ASURPFEWe"; static int flagv[] = { TH_ACK, TH_SYN, TH_URG, TH_RST, TH_PUSH, - TH_FIN } ; + TH_FIN, TH_ECE, TH_CWR, TH_AE } ; char *s, *t; for (s = *arg; *s; s++) @@ -1056,10 +1056,11 @@ void set_tcpflags(char **arg) fprintf(stderr, "unknown TCP flag %c\n", *s); break; } - tcp->th_flags = strtol(*arg, NULL, 0); + __tcp_set_flags(tcp, strtol(*arg, NULL, 0)); break; } else - tcp->th_flags |= flagv[t - flags]; + __tcp_set_flags(tcp, __tcp_get_flags(tcp) | + flagv[t - flags]); free(*arg); *arg = NULL; } diff --git a/sbin/ipf/ipmon/Makefile b/sbin/ipf/ipmon/Makefile index 11506d8a85e8..257e3b999777 100644 --- a/sbin/ipf/ipmon/Makefile +++ b/sbin/ipf/ipmon/Makefile @@ -1,4 +1,3 @@ - PACKAGE= ipf PROG= ipmon SRCS= ${GENHDRS} ipmon.c ipmon_y.c ipmon_l.c diff --git a/sbin/ipf/ipmon/ipmon.5 b/sbin/ipf/ipmon/ipmon.5 index ccca214b26a6..c6a4b6c12a42 100644 --- a/sbin/ipf/ipmon/ipmon.5 +++ b/sbin/ipf/ipmon/ipmon.5 @@ -52,7 +52,6 @@ The lines above would save all ipf log entries to /var/log/ipf-log, send all of the entries for NAT (ipnat related) to syslog and generate an email to root for each log entry from the state tables. .SH SYNTAX - MATCHING -.PP In the above example, the matching segment was confined to matching on the type of log entry generated. The full list of fields that can be used here is: @@ -189,7 +188,6 @@ it can then be used in any .I do statement. .SH EXAMPLES -.PP Some further examples are: .nf @@ -208,7 +206,6 @@ match { dstip 127.0.0.1; } do { local("local options"); }; # .fi .SH MATCHING -.PP All entries of the rules present in the file are compared for matches - there is no first or last rule match. .SH FILES diff --git a/sbin/ipf/ipmon/ipmon.8 b/sbin/ipf/ipmon/ipmon.8 index cb6567e316b0..901d1a2a804e 100644 --- a/sbin/ipf/ipmon/ipmon.8 +++ b/sbin/ipf/ipmon/ipmon.8 @@ -27,7 +27,6 @@ ipmon \- monitors /dev/ipl for logged packets .B <filename> ] .SH DESCRIPTION -.LP \fBipmon\fP opens \fB/dev/ipl\fP for reading and awaits data to be saved from the packet filter. The binary data read from the device is reprinted in human readable form, however, IP#'s are not mapped back to hostnames, nor are @@ -191,5 +190,4 @@ recorded data. .SH SEE ALSO ipl(4), ipmon(5), ipf(8), ipfstat(8), ipnat(8) .SH BUGS -.PP If you find any, please send email to me at darrenr@pobox.com diff --git a/sbin/ipf/ipmon/ipmon.c b/sbin/ipf/ipmon/ipmon.c index 1f3a81fa20f8..a07401ff5e88 100644 --- a/sbin/ipf/ipmon/ipmon.c +++ b/sbin/ipf/ipmon/ipmon.c @@ -13,10 +13,6 @@ #include <fcntl.h> #include <signal.h> -#if !defined(lint) -static const char sccsid[] = "@(#)ipmon.c 1.21 6/5/96 (C)1993-2000 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif #define STRERROR(x) strerror(x) @@ -81,7 +77,8 @@ struct flags tcpfl[] = { { TH_URG, 'U' }, { TH_PUSH,'P' }, { TH_ECN, 'E' }, - { TH_CWR, 'C' }, + { TH_CWR, 'W' }, + { TH_AE, 'e' }, { 0, '\0' } }; @@ -582,7 +579,7 @@ dumphex(FILE *log, int dopts, char *buf, int len) } if ((j + 1) & 0xf) - *t++ = ' ';; + *t++ = ' '; } if (j & 0xf) { @@ -1200,7 +1197,7 @@ print_ipflog(config_t *conf, char *buf, int blen) *t++ = ' '; *t++ = '-'; for (i = 0; tcpfl[i].value; i++) - if (tp->th_flags & tcpfl[i].value) + if (__tcp_get_flags(tp) & tcpfl[i].value) *t++ = tcpfl[i].flag; if (ipmonopts & IPMON_VERBOSE) { sprintf(t, " %lu %lu %hu", diff --git a/sbin/ipf/ipnat/Makefile b/sbin/ipf/ipnat/Makefile index e4015e34c4e3..869a399bd44f 100644 --- a/sbin/ipf/ipnat/Makefile +++ b/sbin/ipf/ipnat/Makefile @@ -1,4 +1,3 @@ - PACKAGE= ipf PROG= ipnat SRCS= ${GENHDRS} ipnat.c ipnat_y.c ipnat_l.c diff --git a/sbin/ipf/ipnat/ipnat.1 b/sbin/ipf/ipnat/ipnat.1 index f24141546171..0e41ccc42b2a 100644 --- a/sbin/ipf/ipnat/ipnat.1 +++ b/sbin/ipf/ipnat/ipnat.1 @@ -8,7 +8,6 @@ ipnat \- user interface to the NAT ] .B \-f <\fIfilename\fP> .SH DESCRIPTION -.PP \fBipnat\fP opens the filename given (treating "\-" as stdin) and parses the file for a set of rules which are to be added or removed from the IP NAT. .PP diff --git a/sbin/ipf/ipnat/ipnat.4 b/sbin/ipf/ipnat/ipnat.4 index 2a866d4a8f19..d848378d8e98 100644 --- a/sbin/ipf/ipnat/ipnat.4 +++ b/sbin/ipf/ipnat/ipnat.4 @@ -10,7 +10,6 @@ ipnat \- Network Address Translation kernel interface .br #include <netinet/ip_nat.h> .SH IOCTLS -.PP To add and delete rules to the NAT list, two 'basic' ioctls are provided for use. The ioctl's are called as: .LP diff --git a/sbin/ipf/ipnat/ipnat.5 b/sbin/ipf/ipnat/ipnat.5 index 01931c6be4f4..c6c8c2e54fc2 100644 --- a/sbin/ipf/ipnat/ipnat.5 +++ b/sbin/ipf/ipnat/ipnat.5 @@ -3,7 +3,6 @@ .SH NAME ipnat, ipnat.conf \- IPFilter NAT file format .SH DESCRIPTION -.PP The .B ipnat.conf file is used to specify rules for the Network Address Translation (NAT) @@ -30,7 +29,6 @@ to text that appears before the "->" and the "right hand side" (RHS) for text that appears after it. In essence, the LHS is the packet matching and the RHS is the new data to be used. .SH VARIABLES -.PP This configuration file, like all others used with IPFilter, supports the use of variable substitution throughout the text. .nf @@ -149,7 +147,7 @@ For TCP connections exiting a connection such as PPPoE where the MTU is slightly smaller than normal ethernet, it can be useful to reduce the Maximum Segment Size (MSS) offered by the internal machines to match, reducing the liklihood that the either end will attempt to send packets -that are too big and result in fragmentation. This is acheived using the +that are too big and result in fragmentation. This is achieved using the .B mssclamp option with TCP .B map @@ -220,7 +218,7 @@ that requires the destination port number to be 21 if this rule is to be activated. The word "ftp" is the proxy identifier that the kernel will try and resolve internally, "tcp" the protocol that packets must match. .PP -See below for a list of proxies and their relative staus. +See below for a list of proxies and their relative status. .PP To associate NAT rules with filtering rules, it is possible to set and match tags during either inbound or outbound processing. At present the @@ -280,7 +278,6 @@ of (say) 172.192.0.2 wanted 260 simultaneous connections going out, they would be limited to 252 with \fBmap-block\fP but would just \fImove on\fP to the next IP address with the \fBmap\fP command. .SS Extended matching -.PP If it is desirable to match on both the source and destination of a packet before applying an address translation to it, this can be achieved by using the same from-to syntax as is used in \fBipf.conf\fP(5). What follows @@ -322,7 +319,6 @@ the defined pool only has /24's or /32's. Pools may also be used .I wherever the from-to syntax in \fBipnat.conf\fR(5) is allowed. .SH INBOUND DESTINATION TRANSLATION (redirection) -.PP Redirection of packets is used to change the destination fields in a packet and is supported for packets that are moving \fIin\fP on a network interface. While the same general syntax for @@ -336,7 +332,7 @@ a network or range of network addresses, so a rule written like this: rdr le0 0/0 -> 192.168.1.0 .fi .PP -Will not spread packets across all 256 IP addresses in that class C network. +Will not spread packets across all 256 IP addresses in that /24 subnet. If you were to try a rule like this: .nf @@ -355,7 +351,7 @@ rdr le0 from 1.1.0.0/16 to any -> 192.168.1.3 rdr le0 ! from 1.1.0.0/16 to any -> 192.168.1.4 .fi .PP -If there is a consective set of addresses you wish to spread the packets +If there is a consecutive set of addresses you wish to spread the packets over, then this can be done in one of two ways, the word "range" optional to preserve: .nf @@ -382,9 +378,9 @@ rdr le0 0/0 -> 192.168.1.5,192.168.1.7 round-robin rdr le0 0/0 -> 192.168.1.9 round-robin .fi .PP -If there are a large number of redirect rules and hosts being targetted +If there are a large number of redirect rules and hosts being targeted then it may be desirable to have all those from a single source address -be targetted at the same destination address. To achieve this, the +be targeted at the same destination address. To achieve this, the word .B sticky is appended to the rule like this: @@ -399,9 +395,9 @@ The .B sticky feature can only be combined with .B round-robin -and the use of comma. +and the use of a comma. .PP -For TCP and UDP packets, it is possible to both match on the destiantion +For TCP and UDP packets, it is possible to both match on the destination port number and to modify it. For example, to change the destination port from 80 to 3128, we would use a rule like this: .nf @@ -465,7 +461,6 @@ rdr le0,ppp0 9.8.7.6/32 port 80 -> 1.1.1.1,1.1.1.2 port 80 tcp round-robin frag age 40/40 sticky mssclamp 1000 tag tagged .fi .SH REWRITING SOURCE AND DESTINATION -.PP Whilst the above two commands provide a lot of flexibility in changing addressing fields in packets, often it can be of benefit to translate \fIboth\fP source \fBand\fR destination at the same time or to change @@ -549,7 +544,6 @@ rewrite from any to any port = 80 -> src 1.1.2.3 - 1.1.2.6 dst 2.2.3.4 - 2.2.3.6; .fi .SH DIVERTING PACKETS -.PP If you'd like to send packets to a UDP socket rather than just another computer to be decapsulated, this can be achieved using a .B divert @@ -572,7 +566,7 @@ On the LHS is a normal set of matching capabilities but on the RHS it is a requirement to specify both the source and destination addresses and ports. .PP -As this feature is intended to be used with targetting packets at sockets +As this feature is intended to be used with targeting packets at sockets and not IPFilter running on other systems, there is no rule provided to \fIundivert\fR packets. .TP @@ -598,7 +592,6 @@ are flushed out, it is expected that the operator will similarly flush the NAT table and thus NAT sessions are not removed when the NAT rules are flushed out. .SH RULE ORDERING -.PP .B NOTE: Rules in .B ipnat.conf @@ -655,7 +648,6 @@ rdr le0 from 1.1.1.0/24 to 192.2.2.1 port 80 -> 127.0.0.1 3128 tcp .PP Then no packets will match the 2nd rule, they'll all match the first. .SH IPv6 -.PP In all of the examples above, where an IPv4 address is present, an IPv6 address can also be used. All rules must use either IPv4 addresses with both halves of the NAT rule or IPv6 addresses for both halves. Mixing @@ -667,7 +659,6 @@ For shorthand notations such as "0/32", the equivalent for IPv6 is implicit direction that the address should be IPv6, not IPv4. To be unambiguous with 0/0, for IPv6 use ::0/0. .SH KERNEL PROXIES -.PP IP Filter comes with a few, simple, proxies built into the code that is loaded into the kernel to allow secondary channels to be opened without forcing the packets through a user program. The current state of the proxies is listed diff --git a/sbin/ipf/ipnat/ipnat.8 b/sbin/ipf/ipnat/ipnat.8 index 2ef14a971831..b3893f117709 100644 --- a/sbin/ipf/ipnat/ipnat.8 +++ b/sbin/ipf/ipnat/ipnat.8 @@ -15,7 +15,6 @@ ipnat \- user interface to the NAT subsystem ] .B \-f <\fIfilename\fP> .SH DESCRIPTION -.PP \fBipnat\fP opens the filename given (treating "\-" as stdin) and parses the file for a set of rules which are to be added or removed from the IP NAT. .PP diff --git a/sbin/ipf/ipnat/ipnat.c b/sbin/ipf/ipnat/ipnat.c index 07d11fc535ee..8eb2e240f6cf 100644 --- a/sbin/ipf/ipnat/ipnat.c +++ b/sbin/ipf/ipnat/ipnat.c @@ -49,10 +49,6 @@ # define STRERROR(x) strerror(x) -#if !defined(lint) -static const char sccsid[] ="@(#)ipnat.c 1.9 6/5/96 (C) 1993 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif #if SOLARIS diff --git a/sbin/ipf/ippool/Makefile b/sbin/ipf/ippool/Makefile index d9b267a58580..8ed864ee9267 100644 --- a/sbin/ipf/ippool/Makefile +++ b/sbin/ipf/ippool/Makefile @@ -1,4 +1,3 @@ - PACKAGE= ipf PROG= ippool SRCS= ${GENHDRS} ippool_y.c ippool_l.c ippool.c diff --git a/sbin/ipf/ippool/ippool.5 b/sbin/ipf/ippool/ippool.5 index 6ead9f7fbf3f..b45675bea069 100644 --- a/sbin/ipf/ippool/ippool.5 +++ b/sbin/ipf/ippool/ippool.5 @@ -38,7 +38,6 @@ heirarchical matching, so it is possible to define a subnet as matching but then exclude specific addresses from it. .SS Evolving Configuration -.PP Over time the configuration syntax used by ippool.conf(5) has evolved. Originally the syntax used was more verbose about what a particular value was being used for, for example: @@ -65,7 +64,6 @@ configuration syntax and all output using "ippool -l" will also be in the new configuration syntax. .SS IPFilter devices and pools -.PP To cater to different administration styles, ipool.conf(5) allows you to tie a pool to a specific role in IPFilter. The recognised role names are: .HP @@ -89,7 +87,6 @@ all pools that are defined for the "all" role are available to all types of rules, be they NAT rules in ipnat.conf(5) or firewall rules in ipf.conf(5). .SH Address Pools -.PP An address pool can be used in ipf.conf(5) and ipnat.conf(5) for matching the source or destination address of packets. They can be referred to either by name or number and can hold an arbitrary number of address patterns to @@ -108,7 +105,7 @@ only ever match an entry in a pool that is of the same address family. The address pool searches the list of addresses configured for the best match. The "best match" is considered to be the match that has the highest number of bits set in the mask. Thus if both 2.2.0.0/16 and 2.2.2.0/24 are -present in an address pool, the addres 2.2.2.1 will match 2.2.2.0/24 and +present in an address pool, the address 2.2.2.1 will match 2.2.2.0/24 and 2.2.1.1 will match 2.2.0.0/16. The reason for this is to allow exceptions to be added through the use of negative matching. In the following example, the pool contains "2.2.0.0/16" and "!2.2.2.0/24", meaning that all packets @@ -124,7 +121,7 @@ addresses from. To do this simply use a "file://" URL where you would specify an actual IP address. .PP .nf -pool ipf/tree (name rfc1918;) { file:///etc/ipf/rfc1918; }; +pool ipf/tree (name rfc1918;) { "file:///etc/ipf/rfc1918;" }; .fi .PP The contents of the file might look something like this: @@ -163,7 +160,6 @@ block in from pool/microsoft to any Note that there are limitations on the output returned by whois servers so be aware that their output may not be 100% perfect for your goal. .SH Destination Lists -.PP Destination lists are provided for use primarily with NAT redirect rules (rdr). Their purpose is to allow more sophisticated methods of selecting which host to send traffic to next than the simple round-robin technique @@ -242,7 +238,6 @@ pool all/dstlist (name servers; policy weighted connection;) { bge0:1.1.1.2; bge0:1.1.1.4; bge1:1.1.1.5; bge1:1.1.1.9; }; .fi .SH Group maps -.PP Group maps are provided to allow more efficient processing of packets where there are a larger number of subnets and groups of rules for those subnets. Group maps are used with "call" rules in ipf.conf(5) that @@ -282,7 +277,6 @@ The limitation with group maps is that only the source address or the destination address can be used to map the packet to the starting group, not both, in your ipf.conf(5) file. .SH Hash Tables -.PP The hash table is operationally similar to the address pool. It is used as a store for a collection of address to match on, saving the need to write a lengthy list of rules. As with address pools, searching diff --git a/sbin/ipf/ippool/ippool.8 b/sbin/ipf/ippool/ippool.8 index 1ff9911a87d8..c879c97b01dd 100644 --- a/sbin/ipf/ippool/ippool.8 +++ b/sbin/ipf/ippool/ippool.8 @@ -28,7 +28,6 @@ ippool \- user interface to the IPFilter pools .B ippool -s [-dtv] .SH DESCRIPTION -.PP .B Ippool is used to manage information stored in the IP pools subsystem of IPFilter. Configuration file information may be parsed and loaded into the kernel, diff --git a/sbin/ipf/ipresend/Makefile b/sbin/ipf/ipresend/Makefile index fc655c5423d7..f866891c4c97 100644 --- a/sbin/ipf/ipresend/Makefile +++ b/sbin/ipf/ipresend/Makefile @@ -1,4 +1,3 @@ - PACKAGE= ipf PROG= ipresend SRCS= ipresend.c ip.c resend.c sbpf.c sock.c 44arp.c diff --git a/sbin/ipf/ipscan/Makefile b/sbin/ipf/ipscan/Makefile index 1169e7dcade5..1f62d796076e 100644 --- a/sbin/ipf/ipscan/Makefile +++ b/sbin/ipf/ipscan/Makefile @@ -1,4 +1,3 @@ - PACKAGE= ipf PROG= ipscan SRCS= ${GENHDRS} ipscan_y.c diff --git a/sbin/ipf/ipscan/ipscan.5 b/sbin/ipf/ipscan/ipscan.5 index 345561f9bb91..76738b607080 100644 --- a/sbin/ipf/ipscan/ipscan.5 +++ b/sbin/ipf/ipscan/ipscan.5 @@ -3,7 +3,6 @@ .SH NAME ipscan, ipscan.conf \- ipscan file format .SH DESCRIPTION -.PP WARNING: This feature is to be considered experimental and may change significantly until a final implementation is drawn up. .PP @@ -20,7 +19,7 @@ match-char ::= "*" | "?" | "." .fi .PP In this example an ip-address is a dotted-quad IPv4 address and a port-number -is a number betwee 1 and 65535, inclusive. The match string is must be of +is a number between 1 and 65535, inclusive. The match string is must be of same length as the literal string that it is matching (literal). The length of either string is limited to 16 bytes. .PP diff --git a/sbin/ipf/ipscan/ipscan.8 b/sbin/ipf/ipscan/ipscan.8 index 292d5764519a..da4068a1e8f2 100644 --- a/sbin/ipf/ipscan/ipscan.8 +++ b/sbin/ipf/ipscan/ipscan.8 @@ -10,7 +10,6 @@ ipscan \- user interface to the IPFilter content scanning ] .B \-f <\fIfilename\fP> .SH DESCRIPTION -.PP \fBipscan\fP opens the filename given (treating "\-" as stdin) and parses the file to build up a content scanning configuration to load into the kernel. Currently only the first 16 bytes of a connection can be compared. diff --git a/sbin/ipf/ipsend/arp.c b/sbin/ipf/ipsend/arp.c index 1aed79aa500f..a9409093213f 100644 --- a/sbin/ipf/ipsend/arp.c +++ b/sbin/ipf/ipsend/arp.c @@ -4,10 +4,6 @@ * * See the IPFILTER.LICENCE file for details on licencing. */ -#if !defined(lint) -static const char sccsid[] = "@(#)arp.c 1.4 1/11/96 (C)1995 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif #include <sys/types.h> #include <sys/socket.h> # include <sys/sockio.h> diff --git a/sbin/ipf/ipsend/ip.c b/sbin/ipf/ipsend/ip.c index 14197c382260..8cdfca893d15 100644 --- a/sbin/ipf/ipsend/ip.c +++ b/sbin/ipf/ipsend/ip.c @@ -4,10 +4,6 @@ * * See the IPFILTER.LICENCE file for details on licencing. */ -#if !defined(lint) -static const char sccsid[] = "%W% %G% (C)1995"; -static const char rcsid[] = "@(#)$Id$"; -#endif #include <sys/param.h> #include <sys/types.h> #include <netinet/in_systm.h> @@ -265,7 +261,7 @@ send_tcp(int nfd, int mtu, ip_t *ip, struct in_addr gwip) i = sizeof(struct tcpiphdr) / sizeof(long); - if ((t2->th_flags == TH_SYN) && !ntohs(ip->ip_off) && + if ((__tcp_get_flags(t2) == TH_SYN) && !ntohs(ip->ip_off) && (lbuf[i] != htonl(0x020405b4))) { lbuf[i] = htonl(0x020405b4); bcopy((char *)ip + hlen + thlen, (char *)ip + hlen + thlen + 4, diff --git a/sbin/ipf/ipsend/ipresend.1 b/sbin/ipf/ipsend/ipresend.1 index 529c1649b756..e7714349e6af 100644 --- a/sbin/ipf/ipsend/ipresend.1 +++ b/sbin/ipf/ipsend/ipresend.1 @@ -20,7 +20,6 @@ ipresend \- resend IP packets out to network <\fIfilename\fP> ] .SH DESCRIPTION -.PP \fBipresend\fP was designed to allow packets to be resent, once captured, back out onto the network for use in testing. \fIipresend\fP supports a number of different file formats as input, including saved snoop/tcpdump @@ -97,10 +96,8 @@ The input file is composed of text descriptions of IP packets. .SH SEE ALSO snoop(1m), tcpdump(8), etherfind(8c), ipftest(1), ipresend(1), iptest(1), bpf(4), dlpi(7p) .SH DIAGNOSTICS -.PP Needs to be run as root. .SH BUGS -.PP Not all of the input formats are sufficiently capable of introducing a wide enough variety of packets for them to be all useful in testing. If you find any, please send email to me at darrenr@pobox.com diff --git a/sbin/ipf/ipsend/ipresend.c b/sbin/ipf/ipsend/ipresend.c index be7b5bc1943a..c00367a4a586 100644 --- a/sbin/ipf/ipsend/ipresend.c +++ b/sbin/ipf/ipsend/ipresend.c @@ -5,10 +5,6 @@ * See the IPFILTER.LICENCE file for details on licencing. * */ -#if !defined(lint) -static const char sccsid[] = "%W% %G% (C)1995 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif #include <sys/param.h> #include <sys/types.h> #include <sys/time.h> diff --git a/sbin/ipf/ipsend/ipsend.1 b/sbin/ipf/ipsend/ipsend.1 index d0770c41775e..3cbb991694b1 100644 --- a/sbin/ipf/ipsend/ipsend.1 +++ b/sbin/ipf/ipsend/ipsend.1 @@ -35,12 +35,11 @@ ipsend \- sends IP packets <\fIwindow\fP> ] <destination> [TCP-flags] .SH DESCRIPTION -.PP \fBipsend\fP can be compiled in two ways. The first is used to send one-off packets to a destination host, using command line options to specify various attributes present in the headers. The \fIdestination\fP must be given as the last command line option, except for when TCP flags are specified as -a combination of A, S, F, U, P and R, last. +a combination of A, S, F, U, P, R, E, W and e, last. .PP The other way it may be compiled, with DOSOCKET defined, is to allow an attempt at making a TCP connection using a with ipsend resending the SYN @@ -103,8 +102,6 @@ enable verbose mode. .SH SEE ALSO ipsend(1), ipresend(1), iptest(1), protocols(4), bpf(4), dlpi(7p) .SH DIAGNOSTICS -.PP Needs to be run as root. .SH BUGS -.PP If you find any, please send email to me at darrenr@pobox.com diff --git a/sbin/ipf/ipsend/ipsend.5 b/sbin/ipf/ipsend/ipsend.5 index 998957a030c9..67c456e54d34 100644 --- a/sbin/ipf/ipsend/ipsend.5 +++ b/sbin/ipf/ipsend/ipsend.5 @@ -7,7 +7,6 @@ text file which fits the grammar described below. The purpose of this grammar is to allow IP packets to be described in an arbitary way which also allows encapsulation to be so done to an arbitary level. .SH GRAMMAR -.LP .nf line ::= iface | arp | send | defrouter | ipv4line . @@ -80,7 +79,6 @@ databodyopts ::= "len" number | "value" string | "file" filename . icmpechoopts ::= "icmpseq" number | "icmpid" number . .fi .SH COMMANDS -.PP Before sending any packets or defining any packets, it is necessary to describe the interface(s) which will be used to send packets out. .TP @@ -252,7 +250,8 @@ unset, it defaults to 0 and is automatically calculated. .TP .B flags <tcp-flags> sets the TCP flags field to match the flags specified. Valid flags are -"S" (SYN), "A" (ACK), "R" (RST), "F" (FIN), "U" (URG), "P" (PUSH). +"S" (SYN), "A" (ACK), "R" (RST), "F" (FIN), "U" (URG), "P" (PUSH), +"E" (ECE), "W" (CWR), "e" (AE). .TP .B opt indicates that TCP header options follow. As TCP options are added to the diff --git a/sbin/ipf/ipsend/ipsend.c b/sbin/ipf/ipsend/ipsend.c index 22218b07c40e..78a8ccaa3f30 100644 --- a/sbin/ipf/ipsend/ipsend.c +++ b/sbin/ipf/ipsend/ipsend.c @@ -3,10 +3,6 @@ * * See the IPFILTER.LICENCE file for details on licencing. */ -#if !defined(lint) -static const char sccsid[] = "@(#)ipsend.c 1.5 12/10/95 (C)1995 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif #include <sys/param.h> #include <sys/types.h> #include <sys/time.h> @@ -369,22 +365,31 @@ main(int argc, char **argv) switch(c) { case 'S' : case 's' : - tcp->th_flags |= TH_SYN; + __tcp_set_flags(tcp, __tcp_get_flags(tcp) | TH_SYN); break; case 'A' : case 'a' : - tcp->th_flags |= TH_ACK; + __tcp_set_flags(tcp, __tcp_get_flags(tcp) | TH_ACK); break; case 'F' : case 'f' : - tcp->th_flags |= TH_FIN; + __tcp_set_flags(tcp, __tcp_get_flags(tcp) | TH_FIN); break; case 'R' : case 'r' : - tcp->th_flags |= TH_RST; + __tcp_set_flags(tcp, __tcp_get_flags(tcp) | TH_RST); break; case 'P' : case 'p' : - tcp->th_flags |= TH_PUSH; + __tcp_set_flags(tcp, __tcp_get_flags(tcp) | TH_PUSH); break; case 'U' : case 'u' : - tcp->th_flags |= TH_URG; + __tcp_set_flags(tcp, __tcp_get_flags(tcp) | TH_URG); + break; + case 'E' : + __tcp_set_flags(tcp, __tcp_get_flags(tcp) | TH_ECE); + break; + case 'W' : + __tcp_set_flags(tcp, __tcp_get_flags(tcp) | TH_CWR); + break; + case 'e' : + __tcp_set_flags(tcp, __tcp_get_flags(tcp) | TH_AE); break; } @@ -394,8 +399,8 @@ main(int argc, char **argv) printf("Source: %s\n", inet_ntoa(ip->ip_src)); printf("Dest: %s\n", inet_ntoa(ip->ip_dst)); printf("Gateway: %s\n", inet_ntoa(gwip)); - if (ip->ip_p == IPPROTO_TCP && tcp->th_flags) - printf("Flags: %#x\n", tcp->th_flags); + if (ip->ip_p == IPPROTO_TCP && __tcp_get_flags(tcp) != 0) + printf("Flags: %#x\n", __tcp_get_flags(tcp)); printf("mtu: %d\n", mtu); if (ip->ip_p == IPPROTO_UDP) { diff --git a/sbin/ipf/ipsend/ipsopt.c b/sbin/ipf/ipsend/ipsopt.c index 6efbea16bc26..ffad9c008461 100644 --- a/sbin/ipf/ipsend/ipsopt.c +++ b/sbin/ipf/ipsend/ipsopt.c @@ -5,10 +5,6 @@ * See the IPFILTER.LICENCE file for details on licencing. * */ -#if !defined(lint) -static const char sccsid[] = "@(#)ipsopt.c 1.2 1/11/96 (C)1995 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif #include <sys/param.h> #include <sys/types.h> #include <sys/time.h> diff --git a/sbin/ipf/ipsend/iptest.1 b/sbin/ipf/ipsend/iptest.1 index afc907042a83..5ccebc681cbc 100644 --- a/sbin/ipf/ipsend/iptest.1 +++ b/sbin/ipf/ipsend/iptest.1 @@ -23,7 +23,6 @@ iptest \- automatically generate a packets to test IP functionality <\fIsource\fP> ] <destination> .SH DESCRIPTION -.PP \fBiptest\fP ... .SH OPTIONS .TP @@ -98,5 +97,4 @@ Only one of the numeric test options may be given when \fIiptest\fP is run. .PP Needs to be run as root. .SH BUGS -.PP If you find any, please send email to me at darrenr@pobox.com diff --git a/sbin/ipf/ipsend/iptest.c b/sbin/ipf/ipsend/iptest.c index 16b97c221f41..db31168cd380 100644 --- a/sbin/ipf/ipsend/iptest.c +++ b/sbin/ipf/ipsend/iptest.c @@ -5,10 +5,6 @@ * See the IPFILTER.LICENCE file for details on licencing. * */ -#if !defined(lint) -static const char sccsid[] = "%W% %G% (C)1995 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif #include <sys/param.h> #include <sys/types.h> #include <sys/time.h> diff --git a/sbin/ipf/ipsend/iptests.c b/sbin/ipf/ipsend/iptests.c index 3fccf1389f6c..6a72a0adfffd 100644 --- a/sbin/ipf/ipsend/iptests.c +++ b/sbin/ipf/ipsend/iptests.c @@ -5,10 +5,6 @@ * See the IPFILTER.LICENCE file for details on licencing. * */ -#if !defined(lint) -static const char sccsid[] = "%W% %G% (C)1995 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif #include <sys/param.h> #include <sys/types.h> #if defined(__NetBSD__) && defined(__vax__) @@ -78,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 @@ -907,7 +902,6 @@ ip_test5(char *dev, int mtu, ip_t *ip, struct in_addr gwip, int ptest) int nfd, i; t = (tcphdr_t *)((char *)ip + (IP_HL(ip) << 2)); - t->th_x2 = 0; TCP_OFF_A(t, 0); t->th_sport = htons(1); t->th_dport = htons(1); @@ -924,13 +918,12 @@ ip_test5(char *dev, int mtu, ip_t *ip, struct in_addr gwip, int ptest) if (!ptest || (ptest == 1)) { /* - * Test 1: flags variations, 0 - 3f + * Test 1: flags variations, 0 - 1ff */ TCP_OFF_A(t, sizeof(*t) >> 2); printf("5.1 Test TCP flag combinations\n"); - for (i = 0; i <= (TH_URG|TH_ACK|TH_PUSH|TH_RST|TH_SYN|TH_FIN); - i++) { - t->th_flags = i; + for (i = 0; i <= TH_FLAGS; i++) { + __tcp_set_flags(t, i); (void) send_tcp(nfd, mtu, ip, gwip); printf("%d\r", i); fflush(stdout); @@ -940,7 +933,7 @@ ip_test5(char *dev, int mtu, ip_t *ip, struct in_addr gwip, int ptest) } if (!ptest || (ptest == 2)) { - t->th_flags = TH_SYN; + __tcp_set_flags(t, TH_SYN); /* * Test 2: seq = 0, seq = 1, seq = 0x7fffffff, seq=0x80000000, * seq = 0xa000000, seq = 0xffffffff @@ -983,7 +976,7 @@ ip_test5(char *dev, int mtu, ip_t *ip, struct in_addr gwip, int ptest) } if (!ptest || (ptest == 3)) { - t->th_flags = TH_ACK; + __tcp_set_flags(t, TH_ACK); /* * Test 3: ack = 0, ack = 1, ack = 0x7fffffff, ack = 0x8000000 * ack = 0xa000000, ack = 0xffffffff @@ -1026,7 +1019,7 @@ ip_test5(char *dev, int mtu, ip_t *ip, struct in_addr gwip, int ptest) } if (!ptest || (ptest == 4)) { - t->th_flags = TH_SYN; + __tcp_set_flags(t, TH_SYN); /* * Test 4: win = 0, win = 32768, win = 65535 */ @@ -1096,7 +1089,7 @@ ip_test5(char *dev, int mtu, ip_t *ip, struct in_addr gwip, int ptest) /* * Test 5: urp */ - t->th_flags = TH_ACK|TH_URG; + __tcp_set_flags(t, TH_ACK|TH_URG); printf("5.5.1 TCP Urgent pointer, sport %hu dport %hu\n", ntohs(t->th_sport), ntohs(t->th_dport)); t->th_urp = htons(1); @@ -1115,7 +1108,7 @@ ip_test5(char *dev, int mtu, ip_t *ip, struct in_addr gwip, int ptest) (void) send_tcp(nfd, mtu, ip, gwip); PAUSE(); t->th_urp = 0; - t->th_flags &= ~TH_URG; + __tcp_set_flags(t, __tcp_get_flags(t) & ~TH_URG); ip->ip_len = sizeof(ip_t) + sizeof(tcphdr_t); } @@ -1123,7 +1116,7 @@ ip_test5(char *dev, int mtu, ip_t *ip, struct in_addr gwip, int ptest) /* * Test 6: data offset, off = 0, off is inside, off is outside */ - t->th_flags = TH_ACK; + __tcp_set_flags(t, TH_ACK); printf("5.6.1 TCP off = 1-15, len = 40\n"); for (i = 1; i < 16; i++) { TCP_OFF_A(t, ntohs(i)); @@ -1145,7 +1138,7 @@ skip_five_and_six: TCP_OFF_A(t, 0); if (!ptest || (ptest == 7)) { - t->th_flags = TH_SYN; + __tcp_set_flags(t, TH_SYN); /* * Test 7: sport = 0, sport = 1, sport = 32767 * sport = 32768, sport = 65535 @@ -1183,7 +1176,7 @@ skip_five_and_six: if (!ptest || (ptest == 8)) { t->th_sport = htons(1); - t->th_flags = TH_SYN; + __tcp_set_flags(t, TH_SYN); /* * Test 8: dport = 0, dport = 1, dport = 32767 * dport = 32768, dport = 65535 @@ -1225,7 +1218,7 @@ skip_five_and_six: /* chose SMTP port 25 */ t->th_sport = htons(25); t->th_dport = htons(25); - t->th_flags = TH_SYN; + __tcp_set_flags(t, TH_SYN); ip->ip_src = ip->ip_dst; (void) send_tcp(nfd, mtu, ip, gwip); fflush(stdout); diff --git a/sbin/ipf/ipsend/resend.c b/sbin/ipf/ipsend/resend.c index 484709e89c53..a306edddff19 100644 --- a/sbin/ipf/ipsend/resend.c +++ b/sbin/ipf/ipsend/resend.c @@ -5,10 +5,6 @@ * See the IPFILTER.LICENCE file for details on licencing. * */ -#if !defined(lint) -static const char sccsid[] = "@(#)resend.c 1.3 1/11/96 (C)1995 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif #include <sys/param.h> #include <sys/types.h> #include <sys/time.h> @@ -54,9 +50,9 @@ dumppacket(ip_t *ip) if (ip->ip_p == IPPROTO_TCP) { printf(" seq %lu:%lu flags ", (u_long)t->th_seq, (u_long)t->th_ack); - for (j = 0, i = 1; i < 256; i *= 2, j++) - if (t->th_flags & i) - printf("%c", "FSRPAU--"[j]); + for (j = 0, i = 1; i < TH_FLAGS; i <<= 1, j++) + if (__tcp_get_flags(t) & i) + printf("%c", "FSRPAUEWe"[j]); } putchar('\n'); } diff --git a/sbin/ipf/ipsend/sbpf.c b/sbin/ipf/ipsend/sbpf.c index d2efc4a19f6b..6d5d60292ce9 100644 --- a/sbin/ipf/ipsend/sbpf.c +++ b/sbin/ipf/ipsend/sbpf.c @@ -39,10 +39,6 @@ #include "ipsend.h" -#if !defined(lint) -static const char sccsid[] = "@(#)sbpf.c 1.3 8/25/95 (C)1995 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif /* * the code herein is dervied from libpcap. diff --git a/sbin/ipf/ipsend/sdlpi.c b/sbin/ipf/ipsend/sdlpi.c index a8ccd2d41df4..d4195c456622 100644 --- a/sbin/ipf/ipsend/sdlpi.c +++ b/sbin/ipf/ipsend/sdlpi.c @@ -38,10 +38,6 @@ #include "ipsend.h" -#if !defined(lint) -static const char sccsid[] = "@(#)sdlpi.c 1.3 10/30/95 (C)1995 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif #define CHUNKSIZE 8192 #define BUFSPACE (4*CHUNKSIZE) diff --git a/sbin/ipf/ipsend/snit.c b/sbin/ipf/ipsend/snit.c index e7fb51b72546..6dc9df06714f 100644 --- a/sbin/ipf/ipsend/snit.c +++ b/sbin/ipf/ipsend/snit.c @@ -38,10 +38,6 @@ #include "ipsend.h" -#if !defined(lint) -static const char sccsid[] = "@(#)snit.c 1.5 1/11/96 (C)1995 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif #define CHUNKSIZE 8192 #define BUFSPACE (4*CHUNKSIZE) diff --git a/sbin/ipf/ipsend/sock.c b/sbin/ipf/ipsend/sock.c index 6159864364bb..7ffacc950d22 100644 --- a/sbin/ipf/ipsend/sock.c +++ b/sbin/ipf/ipsend/sock.c @@ -4,10 +4,6 @@ * See the IPFILTER.LICENCE file for details on licencing. * */ -#if !defined(lint) -static const char sccsid[] = "@(#)sock.c 1.2 1/11/96 (C)1995 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif #include <sys/param.h> #include <sys/types.h> #include <sys/time.h> @@ -62,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/ipsend/sockraw.c b/sbin/ipf/ipsend/sockraw.c index 56fcb7174532..ab65f63753c7 100644 --- a/sbin/ipf/ipsend/sockraw.c +++ b/sbin/ipf/ipsend/sockraw.c @@ -28,9 +28,6 @@ #include <errno.h> #include "ipsend.h" -#if !defined(lint) && defined(LIBC_SCCS) -static char sirix[] = "@(#)sirix.c 1.0 10/9/97 (C)1997 Marc Boucher"; -#endif int diff --git a/sbin/ipf/libipf/Makefile b/sbin/ipf/libipf/Makefile index 8971e014faef..bdd56876dadd 100644 --- a/sbin/ipf/libipf/Makefile +++ b/sbin/ipf/libipf/Makefile @@ -1,4 +1,3 @@ - PACKAGE= ipf LIB= ipf INTERNALLIB= diff --git a/sbin/ipf/libipf/facpri.c b/sbin/ipf/libipf/facpri.c index eac41abcfea5..9325a03c8dc3 100644 --- a/sbin/ipf/libipf/facpri.c +++ b/sbin/ipf/libipf/facpri.c @@ -10,7 +10,7 @@ #include <stdio.h> #include <string.h> #include <limits.h> -#include <sys/types.h> +#include <sys/param.h> #if !defined(__SVR4) && !defined(__svr4__) #include <strings.h> #endif @@ -20,9 +20,6 @@ #include <syslog.h> #include "facpri.h" -#if !defined(lint) -static const char rcsid[] = "@(#)$Id$"; -#endif typedef struct table { @@ -79,7 +76,7 @@ fac_toname(int facpri) fac = facpri & LOG_FACMASK; j = fac >> 3; - if (j < (sizeof(facs)/sizeof(facs[0]))) { + if (j < nitems(facs)) { if (facs[j].value == fac) return (facs[j].name); } diff --git a/sbin/ipf/libipf/flags.c b/sbin/ipf/libipf/flags.c index f16624b97d4f..b476936e0dba 100644 --- a/sbin/ipf/libipf/flags.c +++ b/sbin/ipf/libipf/flags.c @@ -18,7 +18,10 @@ #ifndef TH_CWR # define TH_CWR 0x80 #endif +#ifndef TH_AE +# define TH_AE 0x100 +#endif -char flagset[] = "FSRPAUEC"; -u_char flags[] = { TH_FIN, TH_SYN, TH_RST, TH_PUSH, TH_ACK, TH_URG, - TH_ECN, TH_CWR }; +char flagset[] = "FSRPAUEWe"; +uint16_t flags[] = { TH_FIN, TH_SYN, TH_RST, TH_PUSH, TH_ACK, TH_URG, + TH_ECN, TH_CWR, TH_AE }; diff --git a/sbin/ipf/libipf/getnattype.c b/sbin/ipf/libipf/getnattype.c index 60ab7793a325..30bae7ce0216 100644 --- a/sbin/ipf/libipf/getnattype.c +++ b/sbin/ipf/libipf/getnattype.c @@ -9,9 +9,6 @@ #include "ipf.h" #include "kmem.h" -#if !defined(lint) -static const char rcsid[] = "@(#)$Id$"; -#endif /* diff --git a/sbin/ipf/libipf/inet_addr.c b/sbin/ipf/libipf/inet_addr.c index 11e468eb09f7..d64b3da41361 100644 --- a/sbin/ipf/libipf/inet_addr.c +++ b/sbin/ipf/libipf/inet_addr.c @@ -54,10 +54,6 @@ * --Copyright-- */ -#if !defined(lint) -static const char sccsid[] = "@(#)inet_addr.c 8.1 (Berkeley) 6/17/93"; -static const char rcsid[] = "@(#)$Id: inet_addr.c,v 1.8.2.3 2004/12/09 19:41:20 darrenr Exp $"; -#endif /* LIBC_SCCS and not lint */ #include <sys/param.h> #include <netinet/in.h> diff --git a/sbin/ipf/libipf/interror.c b/sbin/ipf/libipf/interror.c index f59f4f633070..981823ca6bb9 100644 --- a/sbin/ipf/libipf/interror.c +++ b/sbin/ipf/libipf/interror.c @@ -144,7 +144,7 @@ static ipf_error_entry_t ipf_errors[IPF_NUM_ERRORS] = { { 116, "error copying in match array" }, { 117, "match array type is not IPFOBJ_IPFEXPR" }, { 118, "bad size for match array" }, - { 119, "cannot allocate memory for match aray" }, + { 119, "cannot allocate memory for match array" }, { 120, "error copying in match array" }, { 121, "error verifying contents of match array" }, { 122, "need write permissions to set ipf lock status" }, @@ -527,7 +527,7 @@ find_error(int errnum) ipf_error_entry_t *ie; int l = -1, r = IPF_NUM_ERRORS + 1, step; - step = (r - l) / 2;; + step = (r - l) / 2; while (step != 0) { ie = ipf_errors + l + step; @@ -538,7 +538,7 @@ find_error(int errnum) r = step; else l = step; - step = (r - l) / 2;; + step = (r - l) / 2; } return (NULL); diff --git a/sbin/ipf/libipf/ipf_dotuning.c b/sbin/ipf/libipf/ipf_dotuning.c index e435ce035194..74d5dd154aae 100644 --- a/sbin/ipf/libipf/ipf_dotuning.c +++ b/sbin/ipf/libipf/ipf_dotuning.c @@ -19,7 +19,7 @@ void ipf_dotuning(int fd, char *tuneargs, ioctlfunc_t iocfn) bzero((char *)&tu, sizeof(tu)); obj.ipfo_rev = IPFILTER_VERSION; - obj.ipfo_size = sizeof(tu);; + obj.ipfo_size = sizeof(tu); obj.ipfo_ptr = (void *)&tu; obj.ipfo_type = IPFOBJ_TUNEABLE; diff --git a/sbin/ipf/libipf/ipft_hx.c b/sbin/ipf/libipf/ipft_hx.c index d6ae80ec4efd..98c0d1eb0114 100644 --- a/sbin/ipf/libipf/ipft_hx.c +++ b/sbin/ipf/libipf/ipft_hx.c @@ -4,10 +4,6 @@ * * See the IPFILTER.LICENCE file for details on licencing. */ -#if !defined(lint) -static const char sccsid[] = "@(#)ipft_hx.c 1.1 3/9/96 (C) 1996 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif #include <ctype.h> diff --git a/sbin/ipf/libipf/ipft_pc.c b/sbin/ipf/libipf/ipft_pc.c index 0f4a9f6a5983..ff4acd5d1753 100644 --- a/sbin/ipf/libipf/ipft_pc.c +++ b/sbin/ipf/libipf/ipft_pc.c @@ -9,9 +9,6 @@ #include "ipf.h" #include "ipt.h" -#if !defined(lint) -static const char rcsid[] = "@(#)$Id$"; -#endif struct llc { int lc_type; diff --git a/sbin/ipf/libipf/ipft_tx.c b/sbin/ipf/libipf/ipft_tx.c index bab33016b9cc..1e23f06be3fd 100644 --- a/sbin/ipf/libipf/ipft_tx.c +++ b/sbin/ipf/libipf/ipft_tx.c @@ -6,10 +6,6 @@ * * $Id$ */ -#if !defined(lint) -static const char sccsid[] = "@(#)ipft_tx.c 1.7 6/5/96 (C) 1993 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif #include <ctype.h> @@ -24,9 +20,9 @@ static int text_open(char *), text_close(void); static int text_readip(mb_t *, char **, int *); static int parseline(char *, ip_t *, char **, int *); -static char myflagset[] = "FSRPAUEC"; -static u_char myflags[] = { TH_FIN, TH_SYN, TH_RST, TH_PUSH, - TH_ACK, TH_URG, TH_ECN, TH_CWR }; +static char myflagset[] = "FSRPAUEWe"; +static uint16_t myflags[] = { TH_FIN, TH_SYN, TH_RST, TH_PUSH, + TH_ACK, TH_URG, TH_ECN, TH_CWR, TH_AE }; struct ipread iptext = { text_open, text_close, text_readip, R_DO_CKSUM }; static FILE *tfp = NULL; @@ -269,15 +265,16 @@ parseline(char *line, ip_t *ip, char **ifn, int *out) if (*cpp != NULL) { char *s, *t; - tcp->th_flags = 0; + __tcp_set_flags(tcp, 0); for (s = *cpp; *s; s++) - if ((t = strchr(myflagset, *s))) - tcp->th_flags |= myflags[t-myflagset]; - if (tcp->th_flags) + if ((t = strchr(myflagset, *s))) + __tcp_set_flags(tcp, __tcp_get_flags(tcp) | + myflags[t-myflagset]); + if (__tcp_get_flags(tcp)) cpp++; } - if (tcp->th_flags & TH_URG) + if (__tcp_get_flags(tcp) & TH_URG) tcp->th_urp = htons(1); if (*cpp && !strncasecmp(*cpp, "seq=", 4)) { @@ -440,15 +437,16 @@ parseipv6(char **cpp, ip6_t *ip6, char **ifn, int *out) if (*cpp != NULL) { char *s, *t; - tcp->th_flags = 0; + __tcp_set_flags(tcp, 0); for (s = *cpp; *s; s++) - if ((t = strchr(myflagset, *s))) - tcp->th_flags |= myflags[t-myflagset]; - if (tcp->th_flags) + if ((t = strchr(myflagset, *s))) + __tcp_set_flags(tcp, __tcp_get_flags(tcp) | + myflags[t-myflagset]); + if (__tcp_get_flags(tcp)) cpp++; } - if (tcp->th_flags & TH_URG) + if (__tcp_get_flags(tcp) & TH_URG) tcp->th_urp = htons(1); if (*cpp && !strncasecmp(*cpp, "seq=", 4)) { diff --git a/sbin/ipf/libipf/kmem.c b/sbin/ipf/libipf/kmem.c index 0f15159b0348..6ebd0dee6b72 100644 --- a/sbin/ipf/libipf/kmem.c +++ b/sbin/ipf/libipf/kmem.c @@ -29,10 +29,6 @@ #include "kmem.h" -#if !defined(lint) -static const char sccsid[] = "@(#)kmem.c 1.4 1/12/96 (C) 1992 Darren Reed"; -static const char rcsid[] = "@(#)$Id$"; -#endif diff --git a/sbin/ipf/libipf/load_hashnode.c b/sbin/ipf/libipf/load_hashnode.c index 11525dc96e35..12b11687710d 100644 --- a/sbin/ipf/libipf/load_hashnode.c +++ b/sbin/ipf/libipf/load_hashnode.c @@ -53,9 +53,28 @@ load_hashnode(int unit, char *name, iphtent_t *node, int ttl, if (err != 0) if (!(opts & OPT_DONOTHING)) { - char msg[80]; + char msg[255]; + char ipaddr[80], mask_msg[10], mask[8]; - snprintf(msg, sizeof(msg), "%s node from lookup hash table", what); + inet_ntop(ipe.ipe_family, + ipe.ipe_addr.vptr, ipaddr, + sizeof(ipaddr)); +#ifdef USE_INET6 + if (ipe.ipe_family == AF_INET) { +#endif + inet_ntop(ipe.ipe_family, + ipe.ipe_mask.vptr, mask, + sizeof(mask)); + mask_msg[0]='/'; + mask_msg[1]='\0'; + strlcat(mask_msg, mask, sizeof(mask_msg)); +#ifdef USE_INET6 + } else { + mask_msg[0]='\0'; + } +#endif + + snprintf(msg, sizeof(msg), "%s node from lookup hash table(%s) node(%s%s)", what, name, ipaddr, mask_msg); return (ipf_perror_fd(pool_fd(), iocfunc, msg)); } return (0); diff --git a/sbin/ipf/libipf/load_poolnode.c b/sbin/ipf/libipf/load_poolnode.c index 5c9fd838bfa0..880a6cd1c681 100644 --- a/sbin/ipf/libipf/load_poolnode.c +++ b/sbin/ipf/libipf/load_poolnode.c @@ -52,11 +52,30 @@ load_poolnode(int role, char *name, ip_pool_node_t *node, int ttl, if (err != 0) { if ((opts & OPT_DONOTHING) == 0) { - char msg[80]; + char msg[255]; + char ipaddr[80], mask_msg[10], mask[8]; - snprintf(msg, sizeof(msg), "%s pool node(%s/", what, - inet_ntoa(pn.ipn_addr.adf_addr.in4)); - strcat(msg, inet_ntoa(pn.ipn_mask.adf_addr.in4)); + inet_ntop(pn.ipn_addr.adf_family, + pn.ipn_addr.adf_addr.vptr, ipaddr, + sizeof(ipaddr)); + +#ifdef USE_INET6 + if (pn.ipn_mask.adf_family == AF_INET) { +#endif + inet_ntop(pn.ipn_mask.adf_family, + pn.ipn_mask.adf_addr.vptr, mask, + sizeof(mask)); + mask_msg[0]='/'; + mask_msg[1]='\0'; + strlcat(mask_msg, mask, sizeof(mask_msg)); +#ifdef USE_INET6 + } else { + mask_msg[0]='\0'; + } +#endif + + snprintf(msg, sizeof(msg), "%s pool(%s) node(%s%s)", + what, name, ipaddr, mask_msg); return (ipf_perror_fd(pool_fd(), iocfunc, msg)); } } diff --git a/sbin/ipf/libipf/nat_setgroupmap.c b/sbin/ipf/libipf/nat_setgroupmap.c index fd3a2c120e1e..97a5211688c3 100644 --- a/sbin/ipf/libipf/nat_setgroupmap.c +++ b/sbin/ipf/libipf/nat_setgroupmap.c @@ -4,9 +4,6 @@ * * See the IPFILTER.LICENCE file for details on licencing. */ -#if !defined(lint) -static const char rcsid[] = "@(#)$Id$"; -#endif #include "ipf.h" diff --git a/sbin/ipf/libipf/parseipfexpr.c b/sbin/ipf/libipf/parseipfexpr.c index 215e43b2978d..be86456e143b 100644 --- a/sbin/ipf/libipf/parseipfexpr.c +++ b/sbin/ipf/libipf/parseipfexpr.c @@ -75,7 +75,7 @@ parseipfexpr(char *line, char **errorptr) for (ops = strtok(temp, ";"); ops != NULL; ops = strtok(NULL, ";")) { arg = strchr(ops, '='); if ((arg < ops + 2) || (arg == NULL)) { - error = "bad 'arg' vlaue"; + error = "bad 'arg' value"; goto parseerror; } @@ -93,11 +93,7 @@ parseipfexpr(char *line, char **errorptr) break; } if (e->ipoe_word == NULL) { - error = malloc(32); - if (error != NULL) { - snprintf(error, sizeof(error), "keyword (%.10s) not found", - ops); - } + asprintf(&error, "keyword (%.10s) not found", ops); goto parseerror; } diff --git a/sbin/ipf/libipf/printactiveaddr.c b/sbin/ipf/libipf/printactiveaddr.c index 66b5b00aaf98..70deafa345e0 100644 --- a/sbin/ipf/libipf/printactiveaddr.c +++ b/sbin/ipf/libipf/printactiveaddr.c @@ -9,9 +9,6 @@ #include "ipf.h" -#if !defined(lint) -static const char rcsid[] = "@(#)$Id: printactiveaddr.c,v 1.3.2.2 2012/07/22 08:04:24 darren_r Exp $"; -#endif void diff --git a/sbin/ipf/libipf/printactivenat.c b/sbin/ipf/libipf/printactivenat.c index 0a4c254c9529..ba792d3b2be0 100644 --- a/sbin/ipf/libipf/printactivenat.c +++ b/sbin/ipf/libipf/printactivenat.c @@ -10,9 +10,6 @@ #include "ipf.h" -#if !defined(lint) -static const char rcsid[] = "@(#)$Id$"; -#endif static int proto_opened = 0; diff --git a/sbin/ipf/libipf/printaps.c b/sbin/ipf/libipf/printaps.c index ee0c516ae2e3..df27c48b72c8 100644 --- a/sbin/ipf/libipf/printaps.c +++ b/sbin/ipf/libipf/printaps.c @@ -11,9 +11,6 @@ #include "kmem.h" -#if !defined(lint) -static const char rcsid[] = "@(#)$Id$"; -#endif void 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/printnat.c b/sbin/ipf/libipf/printnat.c index e4c096429b59..0b032955c8d8 100644 --- a/sbin/ipf/libipf/printnat.c +++ b/sbin/ipf/libipf/printnat.c @@ -11,9 +11,6 @@ #include "kmem.h" -#if !defined(lint) -static const char rcsid[] = "@(#)$Id$"; -#endif /* diff --git a/sbin/ipf/libipf/printnataddr.c b/sbin/ipf/libipf/printnataddr.c index ee00b5b14d6c..e6003f0b076b 100644 --- a/sbin/ipf/libipf/printnataddr.c +++ b/sbin/ipf/libipf/printnataddr.c @@ -10,9 +10,6 @@ #include "kmem.h" -#if !defined(lint) -static const char rcsid[] = "@(#)$Id: printnataddr.c,v 1.4.2.2 2012/07/22 08:04:24 darren_r Exp $"; -#endif void diff --git a/sbin/ipf/libipf/printpacket.c b/sbin/ipf/libipf/printpacket.c index a48725a220e0..f8407c3a3102 100644 --- a/sbin/ipf/libipf/printpacket.c +++ b/sbin/ipf/libipf/printpacket.c @@ -13,12 +13,12 @@ # define IP_OFFMASK 0x3fff #endif - void printpacket(int dir, mb_t *m) { u_short len, off; tcphdr_t *tcp; + uint16_t tcpflags; ip_t *ip; ip = MTOD(m, ip_t *); @@ -82,24 +82,27 @@ printpacket(int dir, mb_t *m) if (!(off & IP_OFFMASK)) { if (ip->ip_p == IPPROTO_TCP || ip->ip_p == IPPROTO_UDP) PRINTF(",%d", ntohs(tcp->th_dport)); - if ((ip->ip_p == IPPROTO_TCP) && (tcp->th_flags != 0)) { + if ((ip->ip_p == IPPROTO_TCP) && + ((tcpflags = __tcp_get_flags(tcp)) != 0)) { putchar(' '); - if (tcp->th_flags & TH_FIN) + if (tcpflags & TH_FIN) putchar('F'); - if (tcp->th_flags & TH_SYN) + if (tcpflags & TH_SYN) putchar('S'); - if (tcp->th_flags & TH_RST) + if (tcpflags & TH_RST) putchar('R'); - if (tcp->th_flags & TH_PUSH) + if (tcpflags & TH_PUSH) putchar('P'); - if (tcp->th_flags & TH_ACK) + if (tcpflags & TH_ACK) putchar('A'); - if (tcp->th_flags & TH_URG) + if (tcpflags & TH_URG) putchar('U'); - if (tcp->th_flags & TH_ECN) + if (tcpflags & TH_ECN) putchar('E'); - if (tcp->th_flags & TH_CWR) - putchar('C'); + if (tcpflags & TH_CWR) + putchar('W'); + if (tcpflags & TH_AE) + putchar('e'); } } 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/ipf/libipf/printproto.c b/sbin/ipf/libipf/printproto.c index 7bc4952b3b7c..3d4c881ff6d0 100644 --- a/sbin/ipf/libipf/printproto.c +++ b/sbin/ipf/libipf/printproto.c @@ -7,9 +7,6 @@ #include "ipf.h" -#if !defined(lint) -static const char rcsid[] = "@(#)$Id$"; -#endif void diff --git a/sbin/ipf/libipf/printtcpflags.c b/sbin/ipf/libipf/printtcpflags.c index d134fb4a3120..349e18be127e 100644 --- a/sbin/ipf/libipf/printtcpflags.c +++ b/sbin/ipf/libipf/printtcpflags.c @@ -4,10 +4,10 @@ void printtcpflags(u_32_t tcpf, u_32_t tcpfm) { - u_char *t; + uint16_t *t; char *s; - if (tcpf & ~TCPF_ALL) { + if (tcpf & ~TH_FLAGS) { PRINTF("0x%x", tcpf); } else { for (s = flagset, t = flags; *s; s++, t++) { @@ -18,7 +18,7 @@ printtcpflags(u_32_t tcpf, u_32_t tcpfm) if (tcpfm) { (void)putchar('/'); - if (tcpfm & ~TCPF_ALL) { + if (tcpfm & ~TH_FLAGS) { PRINTF("0x%x", tcpfm); } else { for (s = flagset, t = flags; *s; s++, t++) diff --git a/sbin/ipf/libipf/tcp_flags.c b/sbin/ipf/libipf/tcp_flags.c index 0156d8cd7f31..9247933ee85b 100644 --- a/sbin/ipf/libipf/tcp_flags.c +++ b/sbin/ipf/libipf/tcp_flags.c @@ -9,13 +9,13 @@ #include "ipf.h" -extern char flagset[]; -extern u_char flags[]; +extern char flagset[]; +extern uint16_t flags[]; -u_char tcp_flags(char *flgs, u_char *mask, int linenum) +uint16_t tcp_flags(char *flgs, uint16_t *mask, int linenum) { - u_char tcpf = 0, tcpfm = 0; + uint16_t tcpf = 0, tcpfm = 0; char *s; s = strchr(flgs, '/'); @@ -37,9 +37,9 @@ u_char tcp_flags(char *flgs, u_char *mask, int linenum) if (!tcpfm) { if (tcpf == TH_SYN) - tcpfm = 0xff & ~(TH_ECN|TH_CWR); + tcpfm = TH_FLAGS & ~(TH_ECN|TH_CWR); else - tcpfm = 0xff & ~(TH_ECN); + tcpfm = TH_FLAGS & ~(TH_ECN); } *mask = tcpfm; return (tcpf); diff --git a/sbin/ipf/libipf/tcpflags.c b/sbin/ipf/libipf/tcpflags.c index 92437d1be798..46b64e96deda 100644 --- a/sbin/ipf/libipf/tcpflags.c +++ b/sbin/ipf/libipf/tcpflags.c @@ -19,14 +19,17 @@ #ifndef TH_CWR # define TH_CWR 0x80 #endif +#ifndef TH_AE +# define TH_AE 0x100 +#endif -extern char flagset[]; -extern u_char flags[]; +extern char flagset[]; +extern uint16_t flags[]; -u_char tcpflags(char *flgs) +uint16_t tcpflags(char *flgs) { - u_char tcpf = 0; + uint16_t tcpf = 0; char *s, *t; for (s = flgs; *s; s++) { diff --git a/sbin/ipfw/Makefile b/sbin/ipfw/Makefile index a800ec86b0cf..418c0f613741 100644 --- a/sbin/ipfw/Makefile +++ b/sbin/ipfw/Makefile @@ -1,4 +1,3 @@ - .include <src.opts.mk> PACKAGE=ipfw @@ -18,6 +17,9 @@ CFLAGS+=-DPF LIBADD= jail util MAN= ipfw.8 +HAS_TESTS= +SUBDIR.${MK_TESTS}= tests + .include <bsd.prog.mk> CWARNFLAGS+= -Wno-cast-align diff --git a/sbin/ipfw/dummynet.c b/sbin/ipfw/dummynet.c index 26d535428ec3..6714b2af3124 100644 --- a/sbin/ipfw/dummynet.c +++ b/sbin/ipfw/dummynet.c @@ -22,7 +22,7 @@ #define NEW_AQM #include <sys/limits.h> -#include <sys/types.h> +#include <sys/param.h> #include <sys/socket.h> /* XXX there are several sysctl leftover here */ #include <sys/sysctl.h> @@ -471,7 +471,7 @@ print_flowset_parms(struct dn_fs *fs, char *prefix) { int l; char qs[30]; - char plr[30]; + char plr[40]; char red[200]; /* Display RED parameters */ l = fs->qsize; @@ -482,9 +482,17 @@ print_flowset_parms(struct dn_fs *fs, char *prefix) sprintf(qs, "%d B", l); } else sprintf(qs, "%3d sl.", l); - if (fs->plr) - sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff)); - else + if (fs->plr[0] || fs->plr[1]) { + if (fs->plr[1] == 0) + sprintf(plr, "plr %f", + 1.0 * fs->plr[0] / (double)(0x7fffffff)); + else + sprintf(plr, "plr %f,%f,%f,%f", + 1.0 * fs->plr[0] / (double)(0x7fffffff), + 1.0 * fs->plr[1] / (double)(0x7fffffff), + 1.0 * fs->plr[2] / (double)(0x7fffffff), + 1.0 * fs->plr[3] / (double)(0x7fffffff)); + } else plr[0] = '\0'; if (fs->flags & DN_IS_RED) { /* RED parameters */ @@ -1393,7 +1401,7 @@ ipfw_config_pipe(int ac, char **av) */ if (p) p->bandwidth = -1; - for (j = 0; j < sizeof(fs->par)/sizeof(fs->par[0]); j++) + for (j = 0; j < nitems(fs->par); j++) fs->par[j] = -1; while (ac > 0) { double d; @@ -1408,13 +1416,27 @@ ipfw_config_pipe(int ac, char **av) case TOK_PLR: NEED(fs, "plr is only for pipes"); - NEED1("plr needs argument 0..1\n"); - d = strtod(av[0], NULL); - if (d > 1) - d = 1; - else if (d < 0) - d = 0; - fs->plr = (int)(d*0x7fffffff); + NEED1("plr needs one or four arguments 0..1\n"); + if ((end = strsep(&av[0], ","))) { + d = strtod(end, NULL); + d = (d < 0) ? 0 : (d <= 1) ? d : 1; + fs->plr[0] = (int)(d*0x7fffffff); + } + if ((end = strsep(&av[0], ","))) { + d = strtod(end, NULL); + d = (d < 0) ? 0 : (d <= 1) ? d : 1; + fs->plr[1] = (int)(d*0x7fffffff); + } + if ((end = strsep(&av[0], ","))) { + d = strtod(end, NULL); + d = (d < 0) ? 0 : (d <= 1) ? d : 1; + fs->plr[2] = (int)(d*0x7fffffff); + } + if ((end = strsep(&av[0], ","))) { + d = strtod(end, NULL); + d = (d < 0) ? 0 : (d <= 1) ? d : 1; + fs->plr[3] = (int)(d*0x7fffffff); + } ac--; av++; break; diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 index 1a042ae2bbbf..ddfdc35ce651 100644 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -1,11 +1,11 @@ .\" -.Dd April 25, 2023 +.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 @@ -40,10 +40,10 @@ in-kernel NAT. .Ss SYSCTL SHORTCUTS .Nm .Cm enable -.Brq Cm firewall | altq | one_pass | debug | verbose | dyn_keepalive +.Brq Cm firewall | altq | one_pass | debug | verbose | dyn_keepalive | skipto_cache .Nm .Cm disable -.Brq Cm firewall | altq | one_pass | debug | verbose | dyn_keepalive +.Brq Cm firewall | altq | one_pass | debug | verbose | dyn_keepalive | skipto_cache .Ss LOOKUP TABLES .Nm .Oo Cm set Ar N Oc Cm table Ar name Cm create Ar create-options @@ -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 @@ -1395,11 +1440,21 @@ Matches any IPv4 or IPv6 address for which an entry exists in the lookup table If an optional 32-bit unsigned .Ar value is also specified, an entry will match only if it has this value. +If +.Ar value +is specified in form +.Ar valtype=value , +then specified value type field will be checked. +It can be +.Ar skipto, pipe, fib, nat, dscp, tag, divert, netgraph, limit, nh4 +and +.Ar mark. + 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 @@ -1415,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) @@ -1432,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) @@ -1459,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 @@ -1865,7 +1920,7 @@ One or more of source and destination addresses and ports can be specified. .It Cm lookup Bro Cm dst-ip | dst-port | dst-mac | src-ip | src-port | src-mac | uid | -.Cm jail | dscp | mark Brc Ar name +.Cm jail | dscp | mark | rulenum Brc Ar name Search an entry in lookup table .Ar name that matches the field specified as argument. @@ -3039,12 +3094,47 @@ needed for some experimental setups where you want to simulate loss or congestion at a remote router. .Pp .It Cm plr Ar packet-loss-rate +.It Cm plr Ar K,p,H,r Packet loss rate. Argument .Ar packet-loss-rate is a floating-point number between 0 and 1, with 0 meaning no loss, 1 meaning 100% loss. -The loss rate is internally represented on 31 bits. +.Pp +When invoked with four arguments, the simple Gilbert-Elliott +channel model with two states (Good and Bad) is used. +.Bd -literal -offset indent + r + .----------------. + v | + .------------. .------------. + | G | | B | + | drop (K) | | drop (H) | + '------------' '------------' + | ^ + '----------------' + p + +.Ed +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: +.Pp +K = 1 - k ; H = 1 - h +.Pp +This is to retain consistency within the interface and allow the +quick re-use of loss probability when giving only a single argument. +In addition the state change probabilities +.Po Ar p +and +.Ar r Pc +are given. +All of the above probabilities are internally represented on 31 bits. .Pp .It Cm queue Brq Ar slots | size Ns Cm Kbytes Queue size, in @@ -3159,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. @@ -3179,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. @@ -3368,6 +3458,26 @@ Skip instance in case of global state lookup (see below). .It Cm port_range Ar lower-upper Set the aliasing ports between the ranges given. Upper port has to be greater than lower. +.It Cm udp_eim +When enabled, UDP packets use endpoint-independent mapping (EIM) from RFC 4787 +("full cone" NAT of RFC 3489). +All packets from the same internal address:port are mapped to the same NAT +address:port, regardless of their destination address:port. +If filtering rules allow, and if +.Em deny_in +is unset, any other external address:port can +also send to the internal address:port through its mapped NAT address:port. +This is more compatible with applications, and can reduce the need for port +forwarding, but less scalable as each NAT address:port can only be +concurrently used by at most one internal address:port. +.Pp +When disabled, UDP packets use endpoint-dependent mapping (EDM) ("symmetric" +NAT). +Each connection from a particular internal address:port to different +external addresses:ports is mapped to a random and unpredictable NAT +address:port. +Two appplications behind EDM NATs can only connect to each other +by port forwarding on the NAT, or tunnelling through an in-between server. .El .Pp Some special values can be supplied instead of @@ -3519,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 @@ -3761,6 +3872,16 @@ or .Xr kenv 1 before ipfw module gets loaded. .Bl -tag -width indent +.It Va net.inet.ip.fw.enable : No 1 +Enables the firewall. +Setting this variable to 0 lets you run your machine without +firewall even if compiled in. +.It Va net.inet6.ip6.fw.enable : No 1 +provides the same functionality as above for the IPv6 case. +.It Va net.link.ether.ipfw : No 0 +Controls whether layer2 packets are passed to +.Nm . +Default is no. .It Va net.inet.ip.fw.default_to_accept : No 0 Defines ipfw last rule behavior. This value overrides @@ -4154,12 +4275,6 @@ Keep dynamic states on rule/set deletion. States are relinked to default rule (65535). This can be handly for ruleset reload. Turned off by default. -.It Va net.inet.ip.fw.enable : No 1 -Enables the firewall. -Setting this variable to 0 lets you run your machine without -firewall even if compiled in. -.It Va net.inet6.ip6.fw.enable : No 1 -provides the same functionality as above for the IPv6 case. .It Va net.inet.ip.fw.one_pass : No 1 When set, the packet exiting from the .Nm dummynet @@ -4176,10 +4291,6 @@ Enables verbose messages. Limits the number of messages produced by a verbose firewall. .It Va net.inet6.ip6.fw.deny_unknown_exthdrs : No 1 If enabled packets with unknown IPv6 Extension Headers will be denied. -.It Va net.link.ether.ipfw : No 0 -Controls whether layer2 packets are passed to -.Nm . -Default is no. .It Va net.link.bridge.ipfw : No 0 Controls whether bridged packets are passed to .Nm . @@ -4223,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 @@ -4395,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 @@ -4884,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 7a8601aad46a..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> @@ -290,6 +292,12 @@ static struct _s_x rule_actions[] = { { NULL, 0 } /* terminator */ }; +static struct _s_x return_types[] = { + { "next-rulenum", RETURN_NEXT_RULENUM }, + { "next-rule", RETURN_NEXT_RULE }, + { NULL, 0 } /* terminator */ +}; + static struct _s_x rule_action_params[] = { { "altq", TOK_ALTQ }, { "log", TOK_LOG }, @@ -300,7 +308,7 @@ static struct _s_x rule_action_params[] = { /* * The 'lookup' instruction accepts one of the following arguments. - * Arguments are passed as v[1] in O_DST_LOOKUP options. + * Arguments are passed as o.arg1 and o->value in O_DST_LOOKUP option. */ static struct _s_x lookup_keys[] = { { "dst-ip", LOOKUP_DST_IP }, @@ -313,9 +321,29 @@ static struct _s_x lookup_keys[] = { { "jail", LOOKUP_JAIL }, { "dscp", LOOKUP_DSCP }, { "mark", LOOKUP_MARK }, + { "rulenum", LOOKUP_RULENUM }, { NULL, 0 }, }; +/* + * table(name,valuename=value) instruction accepts one of the + * following arguments as valuename. + */ +static struct _s_x tvalue_names[] = { + { "tag", TVALUE_TAG }, + { "pipe", TVALUE_PIPE }, + { "divert", TVALUE_DIVERT }, + { "skipto", TVALUE_SKIPTO }, + { "netgraph", TVALUE_NETGRAPH }, + { "fib", TVALUE_FIB }, + { "nat", TVALUE_NAT }, + { "nh4", TVALUE_NH4 }, + { "dscp", TVALUE_DSCP }, + { "limit", TVALUE_LIMIT }, + { "mark", TVALUE_MARK }, + { NULL, 0 } +}; + static struct _s_x rule_options[] = { { "tagged", TOK_TAGGED }, { "uid", TOK_UID }, @@ -415,12 +443,12 @@ static int ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo, static void ipfw_list_tifaces(void); struct tidx; -static uint16_t pack_object(struct tidx *tstate, const char *name, int otype); -static uint16_t pack_table(struct tidx *tstate, const char *name); +static uint32_t pack_object(struct tidx *tstate, const char *name, int otype); +static uint32_t pack_table(struct tidx *tstate, const char *name); -static char *table_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx); +static char *table_search_ctlv(ipfw_obj_ctlv *ctlv, uint32_t idx); static void object_sort_ctlv(ipfw_obj_ctlv *ctlv); -static char *object_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx, +static char *object_search_ctlv(ipfw_obj_ctlv *ctlv, uint32_t idx, uint16_t type); int @@ -650,6 +678,7 @@ do_set3(int optname, ip_fw3_opheader *op3, size_t optlen) { op3->opcode = optname; + op3->version = IP_FW3_OPVER; /* use last version */ if (g_co.debug_only) { struct debug_header dbg = { @@ -690,6 +719,7 @@ do_get3(int optname, ip_fw3_opheader *op3, size_t *optlen) socklen_t len; op3->opcode = optname; + op3->version = IP_FW3_OPVER; /* use last version */ if (g_co.debug_only) { struct debug_header dbg = { @@ -1284,6 +1314,15 @@ print_flags(struct buf_pr *bp, char const *name, const ipfw_insn *cmd, } } +static void +print_tvalue(struct buf_pr *bp, const ipfw_insn_table *cmd) +{ + const char *name; + + name = match_value(tvalue_names, IPFW_TVALUE_TYPE(&cmd->o)); + bprintf(bp, ",%s=%u", name != NULL ? name: "<invalid>", cmd->value); +} + /* * Print the ip address contained in a command. @@ -1295,33 +1334,46 @@ print_ip(struct buf_pr *bp, const struct format_opts *fo, struct hostent *he = NULL; const struct in_addr *ia; const uint32_t *a = ((const ipfw_insn_u32 *)cmd)->d; - uint32_t len = F_LEN((const ipfw_insn *)cmd); + uint32_t len = F_LEN(&cmd->o); char *t; bprintf(bp, " "); - if (cmd->o.opcode == O_IP_DST_LOOKUP && len > F_INSN_SIZE(ipfw_insn_u32)) { - const char *arg; - - arg = match_value(lookup_keys, a[1]); - t = table_search_ctlv(fo->tstate, - ((const ipfw_insn *)cmd)->arg1); - bprintf(bp, "lookup %s %s", arg, t); - return; - } - if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) { + switch (cmd->o.opcode) { + case O_IP_SRC_ME: + case O_IP_DST_ME: bprintf(bp, "me"); return; - } - if (cmd->o.opcode == O_IP_SRC_LOOKUP || - cmd->o.opcode == O_IP_DST_LOOKUP) { + + case O_IP_DST_LOOKUP: + if ((len == F_INSN_SIZE(ipfw_insn_kidx) || + len == F_INSN_SIZE(ipfw_insn_table)) && + IPFW_LOOKUP_TYPE(&cmd->o) != LOOKUP_NONE) { + const char *key; + + key = match_value(lookup_keys, + IPFW_LOOKUP_TYPE(&cmd->o)); + t = table_search_ctlv(fo->tstate, + insntoc(&cmd->o, kidx)->kidx); + if (len == F_INSN_SIZE(ipfw_insn_table)) { + bprintf(bp, "lookup %s:%#x %s", + (key != NULL ? key : "<invalid>"), + insntoc(&cmd->o, table)->value, t); + } else + bprintf(bp, "lookup %s %s", key != NULL ? key: + "<invalid>", t); + return; + } + /* FALLTHROUGH */ + case O_IP_SRC_LOOKUP: t = table_search_ctlv(fo->tstate, - ((const ipfw_insn *)cmd)->arg1); + insntoc(&cmd->o, kidx)->kidx); bprintf(bp, "table(%s", t); - if (len == F_INSN_SIZE(ipfw_insn_u32)) - bprintf(bp, ",%u", *a); + if (len == F_INSN_SIZE(ipfw_insn_table)) + print_tvalue(bp, insntoc(&cmd->o, table)); bprintf(bp, ")"); return; } + if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) { const uint32_t *map = (const uint32_t *)&cmd->mask; struct in_addr addr; @@ -1427,10 +1479,10 @@ print_mac_lookup(struct buf_pr *bp, const struct format_opts *fo, bprintf(bp, " "); - t = table_search_ctlv(fo->tstate, cmd->arg1); + t = table_search_ctlv(fo->tstate, insntoc(cmd, kidx)->kidx); bprintf(bp, "table(%s", t); - if (len == F_INSN_SIZE(ipfw_insn_u32)) - bprintf(bp, ",%u", ((const ipfw_insn_u32 *)cmd)->d[0]); + if (len == F_INSN_SIZE(ipfw_insn_table)) + print_tvalue(bp, insntoc(cmd, table)); bprintf(bp, ")"); } @@ -1497,10 +1549,9 @@ print_dscp(struct buf_pr *bp, const ipfw_insn_u32 *cmd) } } -#define insntod(cmd, type) ((const ipfw_insn_ ## type *)(cmd)) struct show_state { struct ip_fw_rule *rule; - const ipfw_insn *eaction; + const ipfw_insn_kidx *eaction; uint8_t *printed; int flags; #define HAVE_PROTO 0x0001 @@ -1583,7 +1634,7 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo, switch (cmd->opcode) { case O_PROB: - d = 1.0 * insntod(cmd, u32)->d[0] / 0x7fffffff; + d = 1.0 * insntoc(cmd, u32)->d[0] / 0x7fffffff; bprintf(bp, "prob %f ", d); break; case O_PROBE_STATE: /* no need to print anything here */ @@ -1596,30 +1647,36 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo, case O_IP_SRC_SET: if (state->flags & HAVE_SRCIP) bprintf(bp, " src-ip"); - print_ip(bp, fo, insntod(cmd, ip)); + print_ip(bp, fo, insntoc(cmd, ip)); break; case O_IP_DST: - case O_IP_DST_LOOKUP: case O_IP_DST_MASK: case O_IP_DST_ME: case O_IP_DST_SET: - if (state->flags & HAVE_DSTIP) + case O_IP_DST_LOOKUP: + /* + * Special handling for O_IP_DST_LOOKUP when + * lookup type is not LOOKUP_NONE. + */ + if ((state->flags & HAVE_DSTIP) != 0 && ( + cmd->opcode != O_IP_DST_LOOKUP || + IPFW_LOOKUP_TYPE(cmd) == LOOKUP_NONE)) bprintf(bp, " dst-ip"); - print_ip(bp, fo, insntod(cmd, ip)); + print_ip(bp, fo, insntoc(cmd, ip)); break; case O_IP6_SRC: case O_IP6_SRC_MASK: case O_IP6_SRC_ME: if (state->flags & HAVE_SRCIP) bprintf(bp, " src-ip6"); - print_ip6(bp, insntod(cmd, ip6)); + print_ip6(bp, insntoc(cmd, ip6)); break; case O_IP6_DST: case O_IP6_DST_MASK: case O_IP6_DST_ME: if (state->flags & HAVE_DSTIP) bprintf(bp, " dst-ip6"); - print_ip6(bp, insntod(cmd, ip6)); + print_ip6(bp, insntoc(cmd, ip6)); break; case O_MAC_SRC_LOOKUP: bprintf(bp, " src-mac"); @@ -1630,11 +1687,11 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo, print_mac_lookup(bp, fo, cmd); break; case O_FLOW6ID: - print_flow6id(bp, insntod(cmd, u32)); + print_flow6id(bp, insntoc(cmd, u32)); break; case O_IP_DSTPORT: case O_IP_SRCPORT: - print_newports(bp, insntod(cmd, u16), state->proto, + print_newports(bp, insntoc(cmd, u16), state->proto, (state->flags & (HAVE_SRCIP | HAVE_DSTIP)) == (HAVE_SRCIP | HAVE_DSTIP) ? cmd->opcode: 0); break; @@ -1649,10 +1706,10 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo, state->proto = cmd->arg1; break; case O_MACADDR2: - print_mac(bp, insntod(cmd, mac)); + print_mac(bp, insntoc(cmd, mac)); break; case O_MAC_TYPE: - print_newports(bp, insntod(cmd, u16), + print_newports(bp, insntoc(cmd, u16), IPPROTO_ETHERTYPE, cmd->opcode); break; case O_FRAG: @@ -1695,26 +1752,27 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo, s = "recv"; else /* if (cmd->opcode == O_VIA) */ s = "via"; - switch (insntod(cmd, if)->name[0]) { + switch (insntoc(cmd, if)->name[0]) { case '\0': bprintf(bp, " %s %s", s, - inet_ntoa(insntod(cmd, if)->p.ip)); + inet_ntoa(insntoc(cmd, if)->p.ip)); break; case '\1': bprintf(bp, " %s table(%s)", s, table_search_ctlv(fo->tstate, - insntod(cmd, if)->p.kidx)); + insntoc(cmd, if)->p.kidx)); break; default: bprintf(bp, " %s %s", s, - insntod(cmd, if)->name); + insntoc(cmd, if)->name); } break; case O_IP_FLOW_LOOKUP: - s = table_search_ctlv(fo->tstate, cmd->arg1); + s = table_search_ctlv(fo->tstate, + insntoc(cmd, kidx)->kidx); bprintf(bp, " flow table(%s", s); - if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) - bprintf(bp, ",%u", insntod(cmd, u32)->d[0]); + if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_table)) + print_tvalue(bp, insntoc(cmd, table)); bprintf(bp, ")"); break; case O_IPID: @@ -1749,7 +1807,7 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo, } bprintf(bp, " %s %u", s, cmd->arg1); } else - print_newports(bp, insntod(cmd, u16), 0, + print_newports(bp, insntoc(cmd, u16), 0, cmd->opcode); break; case O_IPVER: @@ -1759,7 +1817,7 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo, bprintf(bp, " ipprecedence %u", cmd->arg1 >> 5); break; case O_DSCP: - print_dscp(bp, insntod(cmd, u32)); + print_dscp(bp, insntoc(cmd, u32)); break; case O_IPOPT: print_flags(bp, "ipoptions", cmd, f_ipopts); @@ -1768,7 +1826,7 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo, print_flags(bp, "iptos", cmd, f_iptos); break; case O_ICMPTYPE: - print_icmptypes(bp, insntod(cmd, u32)); + print_icmptypes(bp, insntoc(cmd, u32)); break; case O_ESTAB: bprintf(bp, " established"); @@ -1781,30 +1839,30 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo, break; case O_TCPACK: bprintf(bp, " tcpack %d", - ntohl(insntod(cmd, u32)->d[0])); + ntohl(insntoc(cmd, u32)->d[0])); break; case O_TCPSEQ: bprintf(bp, " tcpseq %d", - ntohl(insntod(cmd, u32)->d[0])); + ntohl(insntoc(cmd, u32)->d[0])); break; case O_UID: - pwd = getpwuid(insntod(cmd, u32)->d[0]); + pwd = getpwuid(insntoc(cmd, u32)->d[0]); if (pwd != NULL) bprintf(bp, " uid %s", pwd->pw_name); else bprintf(bp, " uid %u", - insntod(cmd, u32)->d[0]); + insntoc(cmd, u32)->d[0]); break; case O_GID: - grp = getgrgid(insntod(cmd, u32)->d[0]); + grp = getgrgid(insntoc(cmd, u32)->d[0]); if (grp != NULL) bprintf(bp, " gid %s", grp->gr_name); else bprintf(bp, " gid %u", - insntod(cmd, u32)->d[0]); + insntoc(cmd, u32)->d[0]); break; case O_JAIL: - bprintf(bp, " jail %d", insntod(cmd, u32)->d[0]); + bprintf(bp, " jail %d", insntoc(cmd, u32)->d[0]); break; case O_VERREVPATH: bprintf(bp, " verrevpath"); @@ -1827,7 +1885,8 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo, else bprintf(bp, " record-state"); bprintf(bp, " :%s", - object_search_ctlv(fo->tstate, cmd->arg1, + object_search_ctlv(fo->tstate, + insntoc(cmd, kidx)->kidx, IPFW_TLV_STATE_NAME)); break; case O_LIMIT: @@ -1835,9 +1894,10 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo, bprintf(bp, " limit"); else bprintf(bp, " set-limit"); - print_limit_mask(bp, insntod(cmd, limit)); + print_limit_mask(bp, insntoc(cmd, limit)); bprintf(bp, " :%s", - object_search_ctlv(fo->tstate, cmd->arg1, + object_search_ctlv(fo->tstate, + insntoc(cmd, kidx)->kidx, IPFW_TLV_STATE_NAME)); break; case O_IP6: @@ -1851,7 +1911,7 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo, bprintf(bp, " ip4"); break; case O_ICMP6TYPE: - print_icmp6types(bp, insntod(cmd, u32)); + print_icmp6types(bp, insntoc(cmd, u32)); break; case O_EXT_HDR: print_ext6hdr(bp, cmd); @@ -1860,7 +1920,7 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo, if (F_LEN(cmd) == 1) bprint_uint_arg(bp, " tagged ", cmd->arg1); else - print_newports(bp, insntod(cmd, u16), + print_newports(bp, insntoc(cmd, u16), 0, O_TAGGED); break; case O_SKIP_ACTION: @@ -1871,12 +1931,10 @@ print_instruction(struct buf_pr *bp, const struct format_opts *fo, if (cmd->arg1 == IP_FW_TARG) bprintf(bp, " tablearg"); else - bprintf(bp, " %#x", - ((const ipfw_insn_u32 *)cmd)->d[0]); + bprintf(bp, " %#x", insntoc(cmd, u32)->d[0]); - if (((const ipfw_insn_u32 *)cmd)->d[1] != 0xFFFFFFFF) - bprintf(bp, ":%#x", - ((const ipfw_insn_u32 *)cmd)->d[1]); + if (insntoc(cmd, u32)->d[1] != 0xFFFFFFFF) + bprintf(bp, ":%#x", insntoc(cmd, u32)->d[1]); break; default: @@ -1929,14 +1987,14 @@ print_fwd(struct buf_pr *bp, const ipfw_insn *cmd) uint16_t port; if (cmd->opcode == O_FORWARD_IP) { - sa = insntod(cmd, sa); + sa = insntoc(cmd, sa); port = sa->sa.sin_port; if (sa->sa.sin_addr.s_addr == INADDR_ANY) bprintf(bp, "fwd tablearg"); else bprintf(bp, "fwd %s", inet_ntoa(sa->sa.sin_addr)); } else { - sa6 = insntod(cmd, sa6); + sa6 = insntoc(cmd, sa6); port = sa6->sa.sin6_port; bprintf(bp, "fwd "); if (getnameinfo((const struct sockaddr *)&sa6->sa, @@ -1948,6 +2006,26 @@ print_fwd(struct buf_pr *bp, const ipfw_insn *cmd) bprintf(bp, ",%u", port); } +static void +print_logdst(struct buf_pr *bp, uint16_t arg1) +{ + char const *comma = ""; + + bprintf(bp, " logdst "); + if (arg1 & IPFW_LOG_SYSLOG) { + bprintf(bp, "%ssyslog", comma); + comma = ","; + } + if (arg1 & IPFW_LOG_IPFW0) { + bprintf(bp, "%sipfw0", comma); + comma = ","; + } + if (arg1 & IPFW_LOG_RTSOCK) { + bprintf(bp, "%srtsock", comma); + comma = ","; + } +} + static int print_action_instruction(struct buf_pr *bp, const struct format_opts *fo, struct show_state *state, const ipfw_insn *cmd) @@ -1959,8 +2037,9 @@ print_action_instruction(struct buf_pr *bp, const struct format_opts *fo, switch (cmd->opcode) { case O_CHECK_STATE: bprintf(bp, "check-state"); - if (cmd->arg1 != 0) - s = object_search_ctlv(fo->tstate, cmd->arg1, + if (insntoc(cmd, kidx)->kidx != 0) + s = object_search_ctlv(fo->tstate, + insntoc(cmd, kidx)->kidx, IPFW_TLV_STATE_NAME); else s = NULL; @@ -1985,7 +2064,7 @@ print_action_instruction(struct buf_pr *bp, const struct format_opts *fo, else if (cmd->arg1 == ICMP_UNREACH_NEEDFRAG && cmd->len == F_INSN_SIZE(ipfw_insn_u16)) bprintf(bp, "needfrag %u", - ((const ipfw_insn_u16 *)cmd)->ports[0]); + insntoc(cmd, u16)->ports[0]); else print_reject_code(bp, cmd->arg1); break; @@ -1998,7 +2077,7 @@ print_action_instruction(struct buf_pr *bp, const struct format_opts *fo, print_unreach6_code(bp, cmd->arg1); break; case O_SKIPTO: - bprint_uint_arg(bp, "skipto ", cmd->arg1); + bprint_uint_arg(bp, "skipto ", insntoc(cmd, u32)->d[0]); break; case O_PIPE: bprint_uint_arg(bp, "pipe ", cmd->arg1); @@ -2023,15 +2102,16 @@ print_action_instruction(struct buf_pr *bp, const struct format_opts *fo, print_fwd(bp, cmd); break; case O_LOG: - if (insntod(cmd, log)->max_log > 0) - bprintf(bp, " log logamount %d", - insntod(cmd, log)->max_log); - else - bprintf(bp, " log"); + bprintf(bp, " log"); + if (insntoc(cmd, log)->max_log > 0) + bprintf(bp, " logamount %d", + insntoc(cmd, log)->max_log); + if (cmd->arg1 != IPFW_LOG_DEFAULT) + print_logdst(bp, cmd->arg1); break; case O_ALTQ: #ifndef NO_ALTQ - print_altq_cmd(bp, insntod(cmd, altq)); + print_altq_cmd(bp, insntoc(cmd, altq)); #endif break; case O_TAG: @@ -2060,8 +2140,9 @@ print_action_instruction(struct buf_pr *bp, const struct format_opts *fo, * NOTE: in case when external action has no named * instances support, the second opcode isn't needed. */ - state->eaction = cmd; - s = object_search_ctlv(fo->tstate, cmd->arg1, + state->eaction = insntoc(cmd, kidx); + s = object_search_ctlv(fo->tstate, + state->eaction->kidx, IPFW_TLV_EACTION); if (match_token(rule_eactions, s) != -1) bprintf(bp, "%s", s); @@ -2081,11 +2162,12 @@ print_action_instruction(struct buf_pr *bp, const struct format_opts *fo, * we calculate TLV type using IPFW_TLV_EACTION_NAME() * macro. */ - s = object_search_ctlv(fo->tstate, cmd->arg1, 0); + s = object_search_ctlv(fo->tstate, + insntoc(cmd, kidx)->kidx, 0); if (s == NULL) s = object_search_ctlv(fo->tstate, - cmd->arg1, IPFW_TLV_EACTION_NAME( - state->eaction->arg1)); + insntoc(cmd, kidx)->kidx, IPFW_TLV_EACTION_NAME( + state->eaction->kidx)); bprintf(bp, " %s", s); break; case O_EXTERNAL_DATA: @@ -2100,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) { @@ -2117,17 +2199,18 @@ print_action_instruction(struct buf_pr *bp, const struct format_opts *fo, bprintf(bp, "reass"); break; case O_CALLRETURN: - if (cmd->len & F_NOT) - bprintf(bp, "return"); - else - bprint_uint_arg(bp, "call ", cmd->arg1); + if (cmd->len & F_NOT) { + s = match_value(return_types, cmd->arg1); + bprintf(bp, "return %s", s ? s: "<invalid>"); + } else + bprint_uint_arg(bp, "call ", insntoc(cmd, u32)->d[0]); break; case O_SETMARK: if (cmd->arg1 == IP_FW_TARG) { bprintf(bp, "setmark tablearg"); break; } - bprintf(bp, "setmark %#x", ((const ipfw_insn_u32 *)cmd)->d[0]); + bprintf(bp, "setmark %#x", insntoc(cmd, u32)->d[0]); break; default: bprintf(bp, "** unrecognized action %d len %d ", @@ -2233,9 +2316,16 @@ print_address(struct buf_pr *bp, struct format_opts *fo, count = portcnt = 0; for (l = state->rule->act_ofs, cmd = state->rule->cmd; l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { - if (match_opcode(cmd->opcode, opcodes, nops)) + if (match_opcode(cmd->opcode, opcodes, nops)) { + /* + * Special handling for O_IP_DST_LOOKUP when + * lookup type is not LOOKUP_NONE. + */ + if (cmd->opcode == O_IP_DST_LOOKUP && + IPFW_LOOKUP_TYPE(cmd) != LOOKUP_NONE) + continue; count++; - else if (cmd->opcode == portop) + } else if (cmd->opcode == portop) portcnt++; } if (count == 0) @@ -2292,6 +2382,12 @@ static const int dst_opcodes[] = { O_IP_DST_SET, O_IP6_DST, O_IP6_DST_MASK, O_IP6_DST_ME }; +#if IPFW_DEFAULT_RULE > 65535 +#define RULENUM_FORMAT "%06d" +#else +#define RULENUM_FORMAT "%05d" +#endif + static void show_static_rule(struct cmdline_opts *co, struct format_opts *fo, struct buf_pr *bp, struct ip_fw_rule *rule, struct ip_fw_bcounter *cntr) @@ -2313,7 +2409,8 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, warn("init_show_state() failed"); return; } - bprintf(bp, "%05u ", rule->rulenum); + + bprintf(bp, RULENUM_FORMAT " ", rule->rulenum); /* only if counters are available */ if (cntr != NULL) { @@ -2380,6 +2477,13 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, if (rule->flags & IPFW_RULE_JUSTOPTS) { state.flags |= HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP; + /* + * Print `proto ip` if all opcodes has been already printed. + */ + if (memchr(state.printed, 0, rule->act_ofs) == NULL) { + bprintf(bp, " proto ip"); + goto end; + } goto justopts; } @@ -2416,23 +2520,21 @@ show_dyn_state(struct cmdline_opts *co, struct format_opts *fo, { struct protoent *pe; struct in_addr a; - uint16_t rulenum; char buf[INET6_ADDRSTRLEN]; - if (d->expire == 0 && d->dyn_type != O_LIMIT_PARENT) + if (!d->expire && !(d->type == O_LIMIT_PARENT)) return; - bcopy(&d->rule, &rulenum, sizeof(rulenum)); - bprintf(bp, "%05d", rulenum); + bprintf(bp, RULENUM_FORMAT, d->rulenum); if (fo->pcwidth > 0 || fo->bcwidth > 0) { bprintf(bp, " "); pr_u64(bp, &d->pcnt, fo->pcwidth); pr_u64(bp, &d->bcnt, fo->bcwidth); bprintf(bp, "(%ds)", d->expire); } - switch (d->dyn_type) { + switch (d->type) { case O_LIMIT_PARENT: - bprintf(bp, " PARENT %d", d->count); + bprintf(bp, " PARENT %u", d->count); break; case O_LIMIT: bprintf(bp, " LIMIT"); @@ -2524,9 +2626,8 @@ ipfw_sets_handler(char *av[]) ipfw_range_tlv rt; const char *msg; size_t size; - uint32_t masks[2]; + uint32_t masks[2], rulenum; int i; - uint16_t rulenum; uint8_t cmd; av++; @@ -2577,7 +2678,7 @@ ipfw_sets_handler(char *av[]) if (av[0] == NULL || av[1] == NULL || av[2] == NULL || av[3] != NULL || _substrcmp(av[1], "to") != 0) errx(EX_USAGE, "syntax: set move [rule] X to Y\n"); - rulenum = atoi(av[0]); + rulenum = (uint32_t)strtoul(av[0], NULL, 10); rt.new_set = atoi(av[2]); if (cmd == IP_FW_XMOVE) { rt.start_rule = rulenum; @@ -2630,6 +2731,18 @@ ipfw_sets_handler(char *av[]) errx(EX_USAGE, "invalid set command %s\n", *av); } +static void +manage_skipto_cache(int op) +{ + ipfw_cmd_header req; + + memset(&req, 0, sizeof(req)); + req.size = sizeof(req); + req.cmd = op ? SKIPTO_CACHE_ENABLE : SKIPTO_CACHE_DISABLE; + + do_set3(IP_FW_SKIPTO_CACHE, &req.opheader, sizeof(req)); +} + void ipfw_sysctl_handler(char *av[], int which) { @@ -2654,6 +2767,8 @@ ipfw_sysctl_handler(char *av[], int which) } else if (_substrcmp(*av, "dyn_keepalive") == 0) { sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0, &which, sizeof(which)); + } else if (_substrcmp(*av, "skipto_cache") == 0) { + manage_skipto_cache(which); #ifndef NO_ALTQ } else if (_substrcmp(*av, "altq") == 0) { altq_set_enabled(which); @@ -2672,7 +2787,6 @@ prepare_format_dyn(struct cmdline_opts *co, struct format_opts *fo, { ipfw_dyn_rule *d; int width; - uint8_t set; d = (ipfw_dyn_rule *)_state; /* Count _ALL_ states */ @@ -2681,13 +2795,9 @@ prepare_format_dyn(struct cmdline_opts *co, struct format_opts *fo, if (fo->show_counters == 0) return; - if (co->use_set) { - /* skip states from another set */ - bcopy((char *)&d->rule + sizeof(uint16_t), &set, - sizeof(uint8_t)); - if (set != co->use_set - 1) - return; - } + /* skip states from another set */ + if (co->use_set != 0 && d->set != co->use_set - 1) + return; width = pr_u64(NULL, &d->pcnt, 0); if (width > fo->pcwidth) @@ -2815,24 +2925,17 @@ static void list_dyn_state(struct cmdline_opts *co, struct format_opts *fo, void *_arg, void *_state) { - uint16_t rulenum; - uint8_t set; ipfw_dyn_rule *d; struct buf_pr *bp; d = (ipfw_dyn_rule *)_state; bp = (struct buf_pr *)_arg; - bcopy(&d->rule, &rulenum, sizeof(rulenum)); - if (rulenum > fo->last) + if (d->rulenum > fo->last) return; - if (co->use_set) { - bcopy((char *)&d->rule + sizeof(uint16_t), - &set, sizeof(uint8_t)); - if (set != co->use_set - 1) - return; - } - if (rulenum >= fo->first) { + if (co->use_set != 0 && d->set != co->use_set - 1) + return; + if (d->rulenum >= fo->first) { show_dyn_state(co, fo, bp, d); printf("%s\n", bp->buf); bp_flush(bp); @@ -3158,7 +3261,7 @@ eaction_check_name(const char *name) return (0); } -static uint16_t +static uint32_t pack_object(struct tidx *tstate, const char *name, int otype) { ipfw_obj_ntlv *ntlv; @@ -3195,7 +3298,7 @@ pack_object(struct tidx *tstate, const char *name, int otype) return (ntlv->idx); } -static uint16_t +static uint32_t pack_table(struct tidx *tstate, const char *name) { @@ -3205,12 +3308,32 @@ pack_table(struct tidx *tstate, const char *name) return (pack_object(tstate, name, IPFW_TLV_TBL_NAME)); } +static void +fill_table_value(ipfw_insn *cmd, char *s) +{ + char *p; + int i; + + p = strchr(s, '='); + if (p != NULL) { + *p++ = '\0'; + i = match_token(tvalue_names, s); + if (i == -1) + errx(EX_USAGE, + "format: unknown table value name %s", s); + } else { + i = TVALUE_TAG; + p = s; + } + + IPFW_SET_TVALUE_TYPE(cmd, i); + insntod(cmd, table)->value = strtoul(p, NULL, 0); +} + void -fill_table(struct _ipfw_insn *cmd, char *av, uint8_t opcode, - struct tidx *tstate) +fill_table(ipfw_insn *cmd, char *av, uint8_t opcode, struct tidx *tstate) { - uint32_t *d = ((ipfw_insn_u32 *)cmd)->d; - uint16_t uidx; + ipfw_insn_kidx *c = insntod(cmd, kidx); char *p; if ((p = strchr(av + 6, ')')) == NULL) @@ -3220,19 +3343,19 @@ fill_table(struct _ipfw_insn *cmd, char *av, uint8_t opcode, if (p) *p++ = '\0'; - if ((uidx = pack_table(tstate, av + 6)) == 0) + if ((c->kidx = pack_table(tstate, av + 6)) == 0) errx(EX_DATAERR, "Invalid table name: %s", av + 6); cmd->opcode = opcode; - cmd->arg1 = uidx; if (p) { - cmd->len |= F_INSN_SIZE(ipfw_insn_u32); - d[0] = strtoul(p, NULL, 0); - } else - cmd->len |= F_INSN_SIZE(ipfw_insn); + cmd->len |= F_INSN_SIZE(ipfw_insn_table); + fill_table_value(cmd, p); + } else { + IPFW_SET_LOOKUP_TYPE(cmd, LOOKUP_NONE); + cmd->len |= F_INSN_SIZE(ipfw_insn_kidx); + } } - /* * fills the addr and mask fields in the instruction as appropriate from av. * Update length as appropriate. @@ -3463,7 +3586,7 @@ ipfw_delete(char *av[]) { ipfw_range_tlv rt; char *sep; - int i, j; + uint32_t i, j; int exitval = EX_OK; int do_set = 0; @@ -3496,8 +3619,8 @@ ipfw_delete(char *av[]) rt.set = i & 31; rt.flags = IPFW_RCFLAG_SET; } else { - rt.start_rule = i & 0xffff; - rt.end_rule = j & 0xffff; + rt.start_rule = i; + rt.end_rule = j; if (rt.start_rule == 0 && rt.end_rule == 0) rt.flags |= IPFW_RCFLAG_ALL; else @@ -3522,7 +3645,7 @@ ipfw_delete(char *av[]) if (g_co.do_quiet) continue; if (rt.start_rule != rt.end_rule) - warnx("no rules rules in %u-%u range", + warnx("no rules in %u-%u range", rt.start_rule, rt.end_rule); else warnx("rule %u not found", @@ -3534,7 +3657,6 @@ ipfw_delete(char *av[]) exit(exitval); } - /* * fill the interface structure. We do not check the name as we can * create interfaces dynamically, so checking them at insert time @@ -3546,7 +3668,7 @@ static void fill_iface(ipfw_insn_if *cmd, char *arg, int cblen, struct tidx *tstate) { char *p; - uint16_t uidx; + uint32_t uidx; cmd->name[0] = '\0'; cmd->o.len |= F_INSN_SIZE(ipfw_insn_if); @@ -3854,6 +3976,8 @@ static struct _s_x f_reserved_keywords[] = { { "to", TOK_OR }, { "via", TOK_OR }, { "{", TOK_OR }, + { "lookup", TOK_OR }, + { "tagged", TOK_OR }, { NULL, 0 } /* terminator */ }; @@ -3934,8 +4058,35 @@ add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen, struct tidx *tstate) return ret; } -static inline int -arg_or_targ_relaxed(const char *arg, const char *action) +static uint16_t +parse_logdst(char *logdst_iter) +{ + char *token; + uint16_t ret; + + ret = IPFW_LOG_DEFAULT; + while ((token = strsep(&logdst_iter, ",")) != NULL) { + if (_substrcmp(token, "syslog") == 0) { + ret |= IPFW_LOG_SYSLOG; + continue; + } + if (_substrcmp(token, "ipfw0") == 0) { + /* XXX add multiple ipfw* */ + ret |= IPFW_LOG_IPFW0; + continue; + } + if (_substrcmp(token, "rtsock") == 0) { + ret |= IPFW_LOG_RTSOCK; + continue; + } + errx(EX_DATAERR, + "unsupported logdst token"); + } + return (ret); +} + +static inline uint32_t +arg_or_targ_relaxed(const char *arg, const char *action, uint32_t maxarg) { uint32_t arg1 = (uint32_t)(-1); @@ -3943,7 +4094,7 @@ arg_or_targ_relaxed(const char *arg, const char *action) errx(EX_USAGE, "missing argument for %s", action); if (isdigit(arg[0])) { arg1 = strtoul(arg, NULL, 10); - if (arg1 <= 0 || arg1 >= IP_FW_TABLEARG) + if (arg1 < IPFW_ARG_MIN || arg1 > maxarg) errx(EX_DATAERR, "illegal argument %s(%u) for %s", arg, arg1, action); } else if (_substrcmp(arg, "tablearg") == 0) @@ -3951,10 +4102,10 @@ arg_or_targ_relaxed(const char *arg, const char *action) return (arg1); } -static inline uint16_t +static inline uint32_t arg_or_targ(const char *arg, const char *action) { - uint32_t arg1 = arg_or_targ_relaxed(arg, action); + uint32_t arg1 = arg_or_targ_relaxed(arg, action, IPFW_ARG_MAX); if (arg1 == (uint32_t)(-1)) errx(EX_DATAERR, "illegal argument %s(%u) for %s", @@ -3965,7 +4116,7 @@ arg_or_targ(const char *arg, const char *action) static uint16_t get_divert_port(const char *arg, const char *action) { - uint32_t arg1 = arg_or_targ_relaxed(arg, action); + uint32_t arg1 = arg_or_targ_relaxed(arg, action, IPFW_ARG_MAX); if (arg1 != (uint32_t)(-1)) return (arg1); @@ -4035,8 +4186,8 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) rblen = *rbufsize / sizeof(uint32_t); rblen -= sizeof(struct ip_fw_rule) / sizeof(uint32_t); - ablen = sizeof(actbuf) / sizeof(actbuf[0]); - cblen = sizeof(cmdbuf) / sizeof(cmdbuf[0]); + ablen = nitems(actbuf); + cblen = nitems(cmdbuf); cblen -= F_INSN_SIZE(ipfw_insn_u32) + 1; #define CHECK_RBUFLEN(len) { CHECK_LENGTH(rblen, len); rblen -= len; } @@ -4079,18 +4230,20 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) case TOK_CHECKSTATE: have_state = action; action->opcode = O_CHECK_STATE; + action->len = F_INSN_SIZE(ipfw_insn_kidx); + CHECK_ACTLEN; if (*av == NULL || match_token(rule_options, *av) == TOK_COMMENT) { - action->arg1 = pack_object(tstate, + insntod(have_state, kidx)->kidx = pack_object(tstate, default_state_name, IPFW_TLV_STATE_NAME); break; } if (*av[0] == ':') { if (strcmp(*av + 1, "any") == 0) - action->arg1 = 0; + insntod(have_state, kidx)->kidx = 0; else if (state_check_name(*av + 1) == 0) - action->arg1 = pack_object(tstate, *av + 1, - IPFW_TLV_STATE_NAME); + insntod(have_state, kidx)->kidx = pack_object( + tstate, *av + 1, IPFW_TLV_STATE_NAME); else errx(EX_DATAERR, "Invalid state name %s", *av); @@ -4147,7 +4300,7 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) errx(EX_DATAERR, "illegal argument for %s", *(av - 1)); action->len = F_INSN_SIZE(ipfw_insn_u16); - ((ipfw_insn_u16 *)action)->ports[0] = mtu; + insntod(action, u16)->ports[0] = mtu; av++; } break; @@ -4185,7 +4338,10 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) break; case TOK_SKIPTO: action->opcode = O_SKIPTO; - action->arg1 = arg_or_targ(av[0], *(av - 1)); + action->len = F_INSN_SIZE(ipfw_insn_u32); + CHECK_ACTLEN; + insntod(action, u32)->d[0] = + arg_or_targ_relaxed(av[0], *(av - 1), IPFW_DEFAULT_RULE); av++; break; case TOK_NETGRAPH: @@ -4210,7 +4366,10 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) break; case TOK_CALL: action->opcode = O_CALLRETURN; - action->arg1 = arg_or_targ(av[0], *(av - 1)); + action->len = F_INSN_SIZE(ipfw_insn_u32); + CHECK_ACTLEN; + insntod(action, u32)->d[0] = + arg_or_targ_relaxed(av[0], *(av - 1), IPFW_DEFAULT_RULE); av++; break; @@ -4378,7 +4537,21 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) break; case TOK_RETURN: - fill_cmd(action, O_CALLRETURN, F_NOT, 0); + action->opcode = O_CALLRETURN; + action->len = F_INSN_SIZE(ipfw_insn_u32) | F_NOT; + CHECK_ACTLEN; + if (*av != NULL) { + /* + * Return type is optional. + * By default we use RETURN_NEXT_RULENUM. + */ + i = match_token(return_types, *av); + if (i >= 0) { + action->arg1 = i; + av++; + } else + action->arg1 = RETURN_NEXT_RULENUM; + } break; case TOK_SETMARK: { @@ -4388,8 +4561,7 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) if (strcmp(*av, "tablearg") == 0) { action->arg1 = IP_FW_TARG; } else { - ((ipfw_insn_u32 *)action)->d[0] = - strtoul(*av, NULL, 0); + insntod(action, u32)->d[0] = strtoul(*av, NULL, 0); /* This is not a tablearg */ action->arg1 |= 0x8000; } @@ -4400,12 +4572,16 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) case TOK_TCPSETMSS: { u_long mss; - uint16_t idx; + uint32_t idx; idx = pack_object(tstate, "tcp-setmss", IPFW_TLV_EACTION); if (idx == 0) errx(EX_DATAERR, "pack_object failed"); - fill_cmd(action, O_EXTERNAL_ACTION, 0, idx); + action->opcode = O_EXTERNAL_ACTION; + action->len = F_INSN_SIZE(ipfw_insn_kidx); + CHECK_ACTLEN; + insntod(action, kidx)->kidx = idx; + NEED1("Missing MSS value"); action = next_cmd(action, &ablen); action->len = 1; @@ -4431,7 +4607,7 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) * syntax. */ case TOK_EACTION: { - uint16_t idx; + uint32_t idx; NEED1("Missing eaction name"); if (eaction_check_name(*av) != 0) @@ -4439,12 +4615,13 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) idx = pack_object(tstate, *av, IPFW_TLV_EACTION); if (idx == 0) errx(EX_DATAERR, "pack_object failed"); - fill_cmd(action, O_EXTERNAL_ACTION, 0, idx); + action->opcode = O_EXTERNAL_ACTION; + action->len = F_INSN_SIZE(ipfw_insn_kidx); + CHECK_ACTLEN; + insntod(action, kidx)->kidx = idx; + av++; NEED1("Missing eaction instance name"); - action = next_cmd(action, &ablen); - action->len = 1; - CHECK_ACTLEN; if (eaction_check_name(*av) != 0) errx(EX_DATAERR, "Invalid eaction instance name %s", *av); @@ -4456,7 +4633,11 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) idx = pack_object(tstate, *av, 0); if (idx == 0) errx(EX_DATAERR, "pack_object failed"); - fill_cmd(action, O_EXTERNAL_INSTANCE, 0, idx); + action = next_cmd(action, &ablen); + action->opcode = O_EXTERNAL_INSTANCE; + action->len = F_INSN_SIZE(ipfw_insn_kidx); + CHECK_ACTLEN; + insntod(action, kidx)->kidx = idx; av++; } } @@ -4484,6 +4665,15 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) cmd->len = F_INSN_SIZE(ipfw_insn_log); CHECK_CMDLEN; cmd->opcode = O_LOG; + cmd->arg1 = IPFW_LOG_DEFAULT; + /* logdst before logamount */ + if (av[0] && _substrcmp(*av, "logdst") == 0) { + av++; + NEED1("logdst requires argument"); + cmd->arg1 = parse_logdst(*av); + av++; + } + if (av[0] && _substrcmp(*av, "logamount") == 0) { av++; NEED1("logamount requires argument"); @@ -4505,6 +4695,14 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) "net.inet.ip.fw.verbose_limit"); } } + + /* logdst after logamount */ + if (av[0] && _substrcmp(*av, "logdst") == 0) { + av++; + NEED1("logdst requires argument"); + cmd->arg1 = parse_logdst(*av); + av++; + } } break; @@ -4817,7 +5015,7 @@ read_options: case TOK_VIA: NEED1("recv, xmit, via require interface name" " or address"); - fill_iface((ipfw_insn_if *)cmd, av[0], cblen, tstate); + fill_iface(insntod(cmd, if), av[0], cblen, tstate); av++; if (F_LEN(cmd) == 0) /* not a valid address */ break; @@ -5027,7 +5225,7 @@ read_options: case TOK_KEEPSTATE: case TOK_RECORDSTATE: { - uint16_t uidx; + uint32_t uidx; if (open_par) errx(EX_USAGE, "keep-state or record-state cannot be part " @@ -5047,13 +5245,16 @@ read_options: IPFW_TLV_STATE_NAME); have_state = cmd; have_rstate = i == TOK_RECORDSTATE; - fill_cmd(cmd, O_KEEP_STATE, 0, uidx); + cmd->opcode = O_KEEP_STATE; + cmd->len = F_INSN_SIZE(ipfw_insn_kidx); + CHECK_CMDLEN; + insntod(have_state, kidx)->kidx = uidx; break; } case TOK_LIMIT: case TOK_SETLIMIT: { - ipfw_insn_limit *c = (ipfw_insn_limit *)cmd; + ipfw_insn_limit *c = insntod(cmd, limit); int val; if (open_par) @@ -5065,10 +5266,10 @@ read_options: have_state = cmd; have_rstate = i == TOK_SETLIMIT; + cmd->opcode = O_LIMIT; cmd->len = F_INSN_SIZE(ipfw_insn_limit); CHECK_CMDLEN; - cmd->opcode = O_LIMIT; - c->limit_mask = c->conn_limit = 0; + c->limit_mask = c->conn_limit = c->kidx = 0; while ( av[0] != NULL ) { if ((val = match_token(limit_masks, *av)) <= 0) @@ -5088,11 +5289,11 @@ read_options: if (state_check_name(*av + 1) != 0) errx(EX_DATAERR, "Invalid state name %s", *av); - cmd->arg1 = pack_object(tstate, *av + 1, + c->kidx = pack_object(tstate, *av + 1, IPFW_TLV_STATE_NAME); av++; } else - cmd->arg1 = pack_object(tstate, + c->kidx = pack_object(tstate, default_state_name, IPFW_TLV_STATE_NAME); break; } @@ -5249,24 +5450,55 @@ read_options: break; case TOK_LOOKUP: { - ipfw_insn_u32 *c = (ipfw_insn_u32 *)cmd; + /* optional mask for some LOOKUP types */ + ipfw_insn_table *c = insntod(cmd, table); + char *lkey; if (!av[0] || !av[1]) - errx(EX_USAGE, "format: lookup argument tablenum"); + errx(EX_USAGE, + "format: lookup argument tablenum"); cmd->opcode = O_IP_DST_LOOKUP; - cmd->len |= F_INSN_SIZE(ipfw_insn) + 2; - i = match_token(lookup_keys, *av); - if (i == -1) - errx(EX_USAGE, "format: cannot lookup on %s", *av); - __PAST_END(c->d, 1) = i; - av++; - if ((i = pack_table(tstate, *av)) == 0) - errx(EX_DATAERR, "Invalid table name: %s", *av); + lkey = strsep(av, ":"); + i = match_token(lookup_keys, lkey); + if (i == -1) + errx(EX_USAGE, + "format: cannot lookup on %s", lkey); + /* masked lookup key */ + if (*av != NULL) { + switch (i) { + case LOOKUP_DST_PORT: + case LOOKUP_SRC_PORT: + case LOOKUP_UID: + case LOOKUP_JAIL: + case LOOKUP_DSCP: + case LOOKUP_MARK: + case LOOKUP_RULENUM: + break; + default: + errx(EX_USAGE, + "masked lookup is not supported " + "for %s", lkey); + } + cmd->len |= F_INSN_SIZE(ipfw_insn_table); + c->value = strtoul(*av, NULL, 0); + if (c->value == 0) + errx(EX_USAGE, + "all-zeroes bitmask for lookup " + "is meaningless"); + } else { + cmd->len |= F_INSN_SIZE(ipfw_insn_kidx); + } + CHECK_CMDLEN; - cmd->arg1 = i; + IPFW_SET_LOOKUP_TYPE(cmd, i); av++; - } + c->kidx = pack_table(tstate, *av); + if (c->kidx == 0) + errx(EX_DATAERR, + "Invalid table name: %s", *av); + av++; + } break; case TOK_FLOW: NEED1("missing table name"); @@ -5329,7 +5561,9 @@ done: * generate O_PROBE_STATE if necessary */ if (have_state && have_state->opcode != O_CHECK_STATE && !have_rstate) { - fill_cmd(dst, O_PROBE_STATE, 0, have_state->arg1); + dst->opcode = O_PROBE_STATE; + dst->len = F_INSN_SIZE(ipfw_insn_kidx); + insntod(dst, kidx)->kidx = insntod(have_state, kidx)->kidx; dst = next_cmd(dst, &rblen); } @@ -5451,7 +5685,7 @@ object_sort_ctlv(ipfw_obj_ctlv *ctlv) } struct object_kt { - uint16_t uidx; + uint32_t uidx; uint16_t type; }; static int @@ -5485,7 +5719,7 @@ compare_object_kntlv(const void *k, const void *v) * Returns table name or NULL. */ static char * -object_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx, uint16_t type) +object_search_ctlv(ipfw_obj_ctlv *ctlv, uint32_t idx, uint16_t type) { ipfw_obj_ntlv *ntlv; struct object_kt key; @@ -5503,7 +5737,7 @@ object_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx, uint16_t type) } static char * -table_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx) +table_search_ctlv(ipfw_obj_ctlv *ctlv, uint32_t idx) { return (object_search_ctlv(ctlv, idx, IPFW_TLV_TBL_NAME)); @@ -5724,6 +5958,7 @@ static struct _s_x intcmds[] = { { "iflist", TOK_IFLIST }, { "olist", TOK_OLIST }, { "vlist", TOK_VLIST }, + { "monitor", TOK_MONITOR }, { NULL, 0 } }; @@ -5798,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[]) { @@ -5822,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 2137719296f9..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, @@ -305,6 +306,8 @@ enum tokens { TOK_LOGOFF, TOK_PRIVATE, TOK_PRIVATEOFF, + TOK_SWAPCONF, + TOK_SWAPCONFOFF, /* NAT64 CLAT tokens */ TOK_NAT64CLAT, @@ -324,6 +327,7 @@ enum tokens { TOK_SETMARK, TOK_SKIPACTION, + TOK_UDP_EIM, }; /* @@ -344,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 */ @@ -462,5 +466,5 @@ int table_check_name(const char *tablename); void ipfw_list_ta(int ac, char *av[]); void ipfw_list_values(int ac, char *av[]); void table_fill_ntlv(struct _ipfw_obj_ntlv *ntlv, const char *name, - uint8_t set, uint16_t uidx); + uint8_t set, uint32_t uidx); diff --git a/sbin/ipfw/main.c b/sbin/ipfw/main.c index 1e5f4fbafc1d..92c593f4f09e 100644 --- a/sbin/ipfw/main.c +++ b/sbin/ipfw/main.c @@ -43,7 +43,7 @@ help(void) "add [num] [set N] [prob x] RULE-BODY\n" "{pipe|queue} N config PIPE-BODY\n" "[pipe|queue] {zero|delete|show} [N{,N}]\n" -"nat N config {ip IPADDR|if IFNAME|log|deny_in|same_ports|unreg_only|unreg_cgn|\n" +"nat N config {ip IPADDR|if IFNAME|log|deny_in|same_ports|unreg_only|unreg_cgn|udp_eim|\n" " reset|reverse|proxy_only|redirect_addr linkspec|\n" " redirect_port linkspec|redirect_proto linkspec|\n" " port_range lower-upper}\n" diff --git a/sbin/ipfw/nat.c b/sbin/ipfw/nat.c index a96da30c9f8b..db74abaab233 100644 --- a/sbin/ipfw/nat.c +++ b/sbin/ipfw/nat.c @@ -67,6 +67,7 @@ static struct _s_x nat_params[] = { { "redirect_addr", TOK_REDIR_ADDR }, { "redirect_port", TOK_REDIR_PORT }, { "redirect_proto", TOK_REDIR_PROTO }, + { "udp_eim", TOK_UDP_EIM }, { NULL, 0 } /* terminator */ }; @@ -676,6 +677,9 @@ nat_show_cfg(struct nat44_cfg_nat *n, void *arg __unused) } else if (n->mode & PKT_ALIAS_PROXY_ONLY) { printf(" proxy_only"); n->mode &= ~PKT_ALIAS_PROXY_ONLY; + } else if (n->mode & PKT_ALIAS_UDP_EIM) { + printf(" udp_eim"); + n->mode &= ~PKT_ALIAS_UDP_EIM; } } /* Print all the redirect's data configuration. */ @@ -821,6 +825,7 @@ ipfw_config_nat(int ac, char **av) case TOK_RESET_ADDR: case TOK_ALIAS_REV: case TOK_PROXY_ONLY: + case TOK_UDP_EIM: break; case TOK_REDIR_ADDR: if (ac1 < 2) @@ -927,6 +932,9 @@ ipfw_config_nat(int ac, char **av) case TOK_PROXY_ONLY: n->mode |= PKT_ALIAS_PROXY_ONLY; break; + case TOK_UDP_EIM: + n->mode |= PKT_ALIAS_UDP_EIM; + break; /* * All the setup_redir_* functions work directly in * the final buffer, see above for details. diff --git a/sbin/ipfw/nat64clat.c b/sbin/ipfw/nat64clat.c index e8b767a0c801..63fa7dae14e2 100644 --- a/sbin/ipfw/nat64clat.c +++ b/sbin/ipfw/nat64clat.c @@ -27,7 +27,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/socket.h> diff --git a/sbin/ipfw/nat64lsn.c b/sbin/ipfw/nat64lsn.c index f50ea3661236..fe3ff8e0fdb8 100644 --- a/sbin/ipfw/nat64lsn.c +++ b/sbin/ipfw/nat64lsn.c @@ -27,7 +27,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/socket.h> @@ -381,6 +380,8 @@ static struct _s_x nat64newcmds[] = { { "-log", TOK_LOGOFF }, { "allow_private", TOK_PRIVATE }, { "-allow_private", TOK_PRIVATEOFF }, + { "swap_conf", TOK_SWAPCONF }, + { "-swap_conf", TOK_SWAPCONFOFF }, /* for compatibility with old configurations */ { "max_ports", TOK_MAX_PORTS }, /* unused */ { NULL, 0 } @@ -515,6 +516,12 @@ nat64lsn_create(const char *name, uint8_t set, int ac, char **av) case TOK_PRIVATEOFF: cfg->flags &= ~NAT64_ALLOW_PRIVATE; break; + case TOK_SWAPCONF: + cfg->flags |= NAT64LSN_ALLOW_SWAPCONF; + break; + case TOK_SWAPCONFOFF: + cfg->flags &= ~NAT64LSN_ALLOW_SWAPCONF; + break; } } @@ -632,6 +639,12 @@ nat64lsn_config(const char *name, uint8_t set, int ac, char **av) case TOK_PRIVATEOFF: cfg->flags &= ~NAT64_ALLOW_PRIVATE; break; + case TOK_SWAPCONF: + cfg->flags |= NAT64LSN_ALLOW_SWAPCONF; + break; + case TOK_SWAPCONFOFF: + cfg->flags &= ~NAT64LSN_ALLOW_SWAPCONF; + break; default: errx(EX_USAGE, "Can't change %s option", opt); } @@ -797,6 +810,8 @@ nat64lsn_show_cb(ipfw_nat64lsn_cfg *cfg, const char *name, uint8_t set) printf(" icmp_age %u", cfg->st_icmp_ttl); if (g_co.verbose || cfg->jmaxlen != NAT64LSN_JMAXLEN) printf(" jmaxlen %u", cfg->jmaxlen); + if (cfg->flags & NAT64LSN_ALLOW_SWAPCONF) + printf(" swap_conf"); if (cfg->flags & NAT64_LOG) printf(" log"); if (cfg->flags & NAT64_ALLOW_PRIVATE) diff --git a/sbin/ipfw/nat64stl.c b/sbin/ipfw/nat64stl.c index d8dd239e7daa..b6c970001f5c 100644 --- a/sbin/ipfw/nat64stl.c +++ b/sbin/ipfw/nat64stl.c @@ -26,7 +26,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/socket.h> diff --git a/sbin/ipfw/nptv6.c b/sbin/ipfw/nptv6.c index fc9ce638b8af..eee6109a3d9e 100644 --- a/sbin/ipfw/nptv6.c +++ b/sbin/ipfw/nptv6.c @@ -25,7 +25,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/socket.h> @@ -154,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); @@ -168,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> @@ -190,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); @@ -206,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: @@ -217,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: @@ -237,24 +234,29 @@ nptv6_create(const char *name, uint8_t set, int ac, char *av[]) break; case TOK_PREFIXLEN: NEED1("IPv6 prefix length required"); - plen = strtol(*av, &p, 10); -check_prefix: - if (*p != '\0' || plen < 8 || plen > 64) + pplen = strtol(*av, &p, 10); + if (*p != '\0' || pplen < 8 || pplen > 64) errx(EX_USAGE, "wrong prefix length: %s", *av); - /* RFC 6296 Sec. 3.1 */ - if (cfg->plen > 0 && cfg->plen != plen) { - warnx("Prefix length mismatch (%d vs %d). " - "It was extended up to %d", - cfg->plen, plen, MAX(plen, cfg->plen)); - plen = MAX(plen, cfg->plen); - } - cfg->plen = plen; - flags |= NPTV6_HAS_PREFIXLEN; ac--; av++; break; } } + /* RFC 6296 Sec. 3.1 */ + if (pplen != 0) { + if ((eplen != 0 && eplen != pplen) || + (iplen != 0 && iplen != pplen)) + errx(EX_USAGE, "prefix length mismatch"); + cfg->plen = pplen; + flags |= NPTV6_HAS_PREFIXLEN; + } else if (eplen != 0 || iplen != 0) { + if (eplen != 0 && iplen != 0 && eplen != iplen) + errx(EX_USAGE, "prefix length mismatch"); + warnx("use prefixlen instead"); + cfg->plen = eplen ? eplen : iplen; + flags |= NPTV6_HAS_PREFIXLEN; + } + /* Check validness */ if ((flags & NPTV6_HAS_INTPREFIX) != NPTV6_HAS_INTPREFIX) errx(EX_USAGE, "int_prefix required"); diff --git a/sbin/ipfw/tables.c b/sbin/ipfw/tables.c index 7901950acadd..7c3b1bb35a01 100644 --- a/sbin/ipfw/tables.c +++ b/sbin/ipfw/tables.c @@ -312,7 +312,7 @@ ipfw_table_handler(int ac, char *av[]) void table_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set, - uint16_t uidx) + uint32_t uidx) { ntlv->head.type = IPFW_TLV_TBL_NAME; @@ -1037,6 +1037,9 @@ table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add, } } + /* Get real OS error */ + error = errno; + /* Report results back */ ptent = tent_buf; for (i = 0; i < count; ptent++, i++) { @@ -1089,8 +1092,6 @@ table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add, if (error == 0) return; - /* Get real OS error */ - error = errno; /* Try to provide more human-readable error */ switch (error) { 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/kldconfig/kldconfig.c b/sbin/kldconfig/kldconfig.c index 27956a961ccf..570ca5cbf713 100644 --- a/sbin/kldconfig/kldconfig.c +++ b/sbin/kldconfig/kldconfig.c @@ -26,7 +26,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/types.h> #include <sys/queue.h> diff --git a/sbin/kldload/kldload.8 b/sbin/kldload/kldload.8 index 4140f845b5e2..0dea7838d75c 100644 --- a/sbin/kldload/kldload.8 +++ b/sbin/kldload/kldload.8 @@ -23,7 +23,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd February 11, 2021 +.Dd December 20, 2023 .Dt KLDLOAD 8 .Os .Sh NAME @@ -114,6 +114,7 @@ Only those modules necessary for booting the system, including those required for mounting the root filesystem, should be handled by .Xr loader.conf 5 . .Sh SEE ALSO +.Xr kenv 1 , .Xr kldload 2 , .Xr loader.conf 5 , .Xr rc.conf 5 , diff --git a/sbin/kldload/kldload.c b/sbin/kldload/kldload.c index c573a253c7c3..82469a2d40f3 100644 --- a/sbin/kldload/kldload.c +++ b/sbin/kldload/kldload.c @@ -26,7 +26,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/param.h> #include <sys/linker.h> diff --git a/sbin/kldstat/kldstat.8 b/sbin/kldstat/kldstat.8 index f7e7552625e3..09094da3741d 100644 --- a/sbin/kldstat/kldstat.8 +++ b/sbin/kldstat/kldstat.8 @@ -1,4 +1,6 @@ .\" +.\" SPDX-License-Identifier: BSD-2-Clause +.\" .\" Copyright (c) 1997 Doug Rabson .\" All rights reserved. .\" @@ -23,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd January 19, 2016 +.Dd January 4, 2025 .Dt KLDSTAT 8 .Os .Sh NAME @@ -48,22 +50,22 @@ utility displays the status of any files dynamically linked into the kernel. .Pp The following options are available: -.Bl -tag -width indentXX +.Bl -tag -width "-n filename" +.It Fl d +Show the module specific data (as int, unsigned int and unsigned long) .It Fl h Display the size field in a human-readable form, using unit suffixes instead of hex values. -.It Fl v -Be more verbose. -.It Fl d -Show the module specific data (as int, unsigned int and unsigned long) .It Fl i Ar id Display the status of only the file with this ID. +.It Fl m Ar modname +Display the status of only the module with this modname. .It Fl n Ar filename Display the status of only the file with this filename. .It Fl q -Only check if file is loaded or compiled into the kernel. -.It Fl m Ar modname -Display the status of only the module with this modname. +Quietly check if file is loaded or compiled into the kernel. +.It Fl v +Be more verbose. .El .Sh EXIT STATUS .Ex -std diff --git a/sbin/kldstat/kldstat.c b/sbin/kldstat/kldstat.c index 60d12e0327ab..3a90f1c97eb4 100644 --- a/sbin/kldstat/kldstat.c +++ b/sbin/kldstat/kldstat.c @@ -26,7 +26,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/param.h> #include <sys/module.h> @@ -36,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)) @@ -52,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/kldunload/kldunload.c b/sbin/kldunload/kldunload.c index ebdc17ce414f..69739d0f4afe 100644 --- a/sbin/kldunload/kldunload.c +++ b/sbin/kldunload/kldunload.c @@ -26,7 +26,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/linker.h> diff --git a/sbin/ldconfig/Makefile b/sbin/ldconfig/Makefile index 2ed58431fc03..621be3937ec0 100644 --- a/sbin/ldconfig/Makefile +++ b/sbin/ldconfig/Makefile @@ -1,4 +1,3 @@ - PACKAGE=runtime PROG= ldconfig SRCS= elfhints.c ldconfig.c diff --git a/sbin/ldconfig/elfhints.c b/sbin/ldconfig/elfhints.c index 72b9273ab93a..97499e398c9d 100644 --- a/sbin/ldconfig/elfhints.c +++ b/sbin/ldconfig/elfhints.c @@ -27,6 +27,7 @@ */ #include <sys/param.h> +#include <sys/endian.h> #include <sys/mman.h> #include <sys/stat.h> @@ -48,11 +49,12 @@ static void add_dir(const char *, const char *, bool); static void read_dirs_from_file(const char *, const char *); -static void read_elf_hints(const char *, bool); +static void read_elf_hints(const char *, bool, bool); static void write_elf_hints(const char *); static const char *dirs[MAXDIRS]; static int ndirs; +static bool is_be; bool insecure; static void @@ -95,7 +97,7 @@ list_elf_hints(const char *hintsfile) int i; int nlibs; - read_elf_hints(hintsfile, 1); + read_elf_hints(hintsfile, true, false); printf("%s:\n", hintsfile); printf("\tsearch directories:"); for (i = 0; i < ndirs; i++) @@ -183,8 +185,11 @@ read_dirs_from_file(const char *hintsfile, const char *listfile) fclose(fp); } +/* Convert between native byte order and forced little resp. big endian. */ +#define COND_SWAP(n) (is_be ? be32toh(n) : le32toh(n)) + static void -read_elf_hints(const char *hintsfile, bool must_exist) +read_elf_hints(const char *hintsfile, bool must_exist, bool force_be) { int fd; struct stat s; @@ -193,6 +198,7 @@ read_elf_hints(const char *hintsfile, bool must_exist) char *strtab; char *dirlist; char *p; + int hdr_version; if ((fd = open(hintsfile, O_RDONLY)) == -1) { if (errno == ENOENT && !must_exist) @@ -214,14 +220,18 @@ read_elf_hints(const char *hintsfile, bool must_exist) close(fd); hdr = (struct elfhints_hdr *)mapbase; - if (hdr->magic != ELFHINTS_MAGIC) + is_be = hdr->magic == htobe32(ELFHINTS_MAGIC); + if (COND_SWAP(hdr->magic) != ELFHINTS_MAGIC) errx(1, "\"%s\": invalid file format", hintsfile); - if (hdr->version != 1) + if (force_be && !is_be) + errx(1, "\"%s\": incompatible endianness requested", hintsfile); + hdr_version = COND_SWAP(hdr->version); + if (hdr_version != 1) errx(1, "\"%s\": unrecognized file version (%d)", hintsfile, - hdr->version); + hdr_version); - strtab = (char *)mapbase + hdr->strtab; - dirlist = strtab + hdr->dirlist; + strtab = (char *)mapbase + COND_SWAP(hdr->strtab); + dirlist = strtab + COND_SWAP(hdr->dirlist); if (*dirlist != '\0') while ((p = strsep(&dirlist, ":")) != NULL) @@ -229,13 +239,19 @@ read_elf_hints(const char *hintsfile, bool must_exist) } void -update_elf_hints(const char *hintsfile, int argc, char **argv, bool merge) +update_elf_hints(const char *hintsfile, int argc, char **argv, bool merge, + bool force_be) { struct stat s; int i; + /* + * Create little-endian hints files on all architectures unless + * ldconfig has been invoked with the -B option. + */ + is_be = force_be; if (merge) - read_elf_hints(hintsfile, false); + read_elf_hints(hintsfile, false, force_be); for (i = 0; i < argc; i++) { if (stat(argv[i], &s) == -1) warn("warning: %s", argv[i]); @@ -265,9 +281,9 @@ write_elf_hints(const char *hintsfile) if ((fp = fdopen(fd, "wb")) == NULL) err(1, "fdopen(%s)", tempname); - hdr.magic = ELFHINTS_MAGIC; - hdr.version = 1; - hdr.strtab = sizeof hdr; + hdr.magic = COND_SWAP(ELFHINTS_MAGIC); + hdr.version = COND_SWAP(1); + hdr.strtab = COND_SWAP(sizeof hdr); hdr.strsize = 0; hdr.dirlist = 0; memset(hdr.spare, 0, sizeof hdr.spare); @@ -278,8 +294,10 @@ write_elf_hints(const char *hintsfile) for (i = 1; i < ndirs; i++) hdr.strsize += 1 + strlen(dirs[i]); } - hdr.dirlistlen = hdr.strsize; + hdr.dirlistlen = COND_SWAP(hdr.strsize); hdr.strsize++; /* For the null terminator */ + /* convert in-place from native to target endianness */ + hdr.strsize = COND_SWAP(hdr.strsize); /* Write the header. */ if (fwrite(&hdr, 1, sizeof hdr, fp) != sizeof hdr) diff --git a/sbin/ldconfig/ldconfig.8 b/sbin/ldconfig/ldconfig.8 index 47e0dfa99b50..cf7037b86bd6 100644 --- a/sbin/ldconfig/ldconfig.8 +++ b/sbin/ldconfig/ldconfig.8 @@ -1,7 +1,7 @@ .\" .\" Copyright (c) 1993 Paul Kranenburg .\" All rights reserved. -.\" Copyright (c) 2021 The FreeBSD Foundation, Inc. +.\" Copyright (c) 2021 The FreeBSD Foundation .\" .\" Portions of this documentation were written by .\" Konstantin Belousov <kib@FreeBSD.org> under sponsorship @@ -32,7 +32,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 May 15, 2021 +.Dd April 20, 2024 .Dt LDCONFIG 8 .Os .Sh NAME @@ -41,7 +41,7 @@ .Sh SYNOPSIS .Nm .Op Fl 32 -.Op Fl Rimrv +.Op Fl BRimr .Op Fl f Ar hints_file .Op Ar directory | Ar .Sh DESCRIPTION @@ -105,6 +105,12 @@ Generate the hints for 32-bit ABI shared libraries on 64-bit systems that support running 32-bit binaries. .It Fl elf Ignored for backwards compatibility. +.It Fl B +Force writing big-endian binary data to the hints file. +The default is to create hints files in little-endian byte-order on all +architectures. +Reading of and merging into hints files preserves the endianness of the +existing hints file. .It Fl R Appends pathnames on the command line to the directory list from the hints file. @@ -126,9 +132,14 @@ on the standard output. The hints file is not modified. .Pp Scan and print all libraries found on the directories list. -.It Fl v -Switch on verbose mode. .El +.Pp +The historic options +.Fl elf , +.Fl s , +and +.Fl v +are accepted but ignored. .Sh SECURITY Special care must be taken when loading shared libraries into the address space of @@ -145,21 +156,23 @@ Thus, serves to specify the trusted collection of directories from which shared objects can be safely loaded. .Sh FILES -.Bl -tag -width /var/run/ld-elf.so.hintsxxx -compact +.Bl -tag -width /usr/local/libdata/ldconfig/* -compact .It Pa /var/run/ld-elf.so.hints Standard hints file for the ELF dynamic linker. -.It Pa /etc/ld-elf.so.conf -Conventional configuration file containing directory names for -invocations with -.Fl elf . .It Pa /var/run/ld-elf32.so.hints -Conventional configuration files containing directory names for -invocations with +Hints file for 32 bit libraries on 64 bit architectures, processed by +ldconfig when invoked with .Fl 32 . +.It Pa /etc/ld-elf.so.conf +Optional file with names of directories to be included in the standard +hints file when booting to multi-user mode. +.It Pa /usr/local/libdata/ldconfig/* +Additional files with names of directories provided by optional ports +or packages. .El .Sh SEE ALSO .Xr ld 1 , -.Xr ld-elf.so.1 , +.Xr ld-elf.so.1 1 , .Xr link 5 .Sh HISTORY A diff --git a/sbin/ldconfig/ldconfig.c b/sbin/ldconfig/ldconfig.c index 2f5cdbd6505e..7700fd1f4574 100644 --- a/sbin/ldconfig/ldconfig.c +++ b/sbin/ldconfig/ldconfig.c @@ -57,9 +57,9 @@ main(int argc, char **argv) { const char *hints_file; int c; - bool is_32, justread, merge, rescan, verbose; + bool is_32, justread, merge, rescan, force_be; - is_32 = justread = merge = rescan = verbose = false; + force_be = is_32 = justread = merge = rescan = false; while (argc > 1) { if (strcmp(argv[1], "-aout") == 0) { @@ -80,8 +80,11 @@ main(int argc, char **argv) hints_file = __PATH_ELF_HINTS("32"); else hints_file = _PATH_ELF_HINTS; - while((c = getopt(argc, argv, "Rf:imrsv")) != -1) { + while((c = getopt(argc, argv, "BRf:imrsv")) != -1) { switch (c) { + case 'B': + force_be = true; + break; case 'R': rescan = true; break; @@ -101,7 +104,7 @@ main(int argc, char **argv) /* was nostd */ break; case 'v': - verbose = true; + /* was verbose */ break; default: usage(); @@ -115,7 +118,7 @@ main(int argc, char **argv) if (argc == optind) rescan = true; update_elf_hints(hints_file, argc - optind, - argv + optind, merge || rescan); + argv + optind, merge || rescan, force_be); } exit(0); } @@ -124,7 +127,7 @@ static void usage(void) { fprintf(stderr, - "usage: ldconfig [-32] [-elf] [-Rimrv] [-f hints_file] " + "usage: ldconfig [-32] [-BRimr] [-f hints_file]" "[directory | file ...]\n"); exit(1); } diff --git a/sbin/ldconfig/ldconfig.h b/sbin/ldconfig/ldconfig.h index e03ba928be7d..aa7ad810bacd 100644 --- a/sbin/ldconfig/ldconfig.h +++ b/sbin/ldconfig/ldconfig.h @@ -36,7 +36,7 @@ extern bool insecure; /* -i flag, needed here for elfhints.c */ __BEGIN_DECLS void list_elf_hints(const char *); -void update_elf_hints(const char *, int, char **, bool); +void update_elf_hints(const char *, int, char **, bool, bool); __END_DECLS #endif diff --git a/sbin/md5/Makefile b/sbin/md5/Makefile index 359c4b96f9fd..ed87ff84876b 100644 --- a/sbin/md5/Makefile +++ b/sbin/md5/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.1 (Berkeley) 6/9/93 - PACKAGE=runtime PROG= md5 @@ -59,16 +57,19 @@ MLINKS= md5.1 md5sum.1 \ LIBADD= md -.ifndef(BOOTSTRAPPING) +.include <src.opts.mk> + +.if ${MK_CASPER} != "no" && !defined(RESCUE) && !defined(BOOTSTRAPPING) # Avoid depending on capsicum during bootstrap. caph_limit_stdout() is not # available when building for Linux/MacOS or older FreeBSD hosts. # We need to bootstrap md5 when building on Linux since the md5sum command there # produces different output. CFLAGS+=-DHAVE_CAPSICUM +CFLAGS+=-DWITH_CASPER +LIBADD+= casper +LIBADD+= cap_fileargs .endif -.include <src.opts.mk> - HAS_TESTS= SUBDIR.${MK_TESTS}+= tests diff --git a/sbin/md5/Makefile.depend b/sbin/md5/Makefile.depend index e442dca35596..61733f6b057b 100644 --- a/sbin/md5/Makefile.depend +++ b/sbin/md5/Makefile.depend @@ -7,6 +7,7 @@ DIRDEPS = \ lib/libc \ lib/libcapsicum \ lib/libcasper/libcasper \ + lib/libcasper/services/cap_fileargs \ lib/libcompiler_rt \ lib/libmd \ diff --git a/sbin/md5/md5.1 b/sbin/md5/md5.1 index 0cdfff928211..b530292c8269 100644 --- a/sbin/md5/md5.1 +++ b/sbin/md5/md5.1 @@ -1,4 +1,4 @@ -.Dd May 10, 2023 +.Dd March 12, 2024 .Dt MD5 1 .Os .Sh NAME @@ -79,6 +79,11 @@ utility does the same, but with command-line options and an output format that match those of the similarly named utility that ships with Perl. .Pp +In all cases, each file listed on the command line is processed separately. +If no files are listed on the command line, or a file name is given as +.Pa - , +input is taken from stdin instead. +.Pp It is conjectured that it is computationally infeasible to produce two messages having the same message digest, or to produce any message having a given prespecified target message digest. @@ -124,6 +129,7 @@ option, the calculated digest is printed in addition to the exit status being se .Pq Note that this option is not yet useful if multiple files are specified. .It Fl p , -passthrough Echo stdin to stdout and append the checksum to stdout. +In this mode, any files specified on the command line are silently ignored. .It Fl q , -quiet Quiet mode \(em only the checksum is printed out. Overrides the @@ -141,6 +147,7 @@ options. .It Fl s Ar string , Fl -string= Ns Ar string Print a checksum of the given .Ar string . +In this mode, any files specified on the command line are silently ignored. .It Fl t , Fl -time-trial Run a built-in time trial. For the @@ -360,7 +367,7 @@ for .Pa /boot/loader.conf in the example above. .Bd -literal -offset indent -$ md5 -c digest /boot/loader.conf +$ md5sum -c digest /boot/loader.conf: OK .Ed .Pp diff --git a/sbin/md5/md5.c b/sbin/md5/md5.c index 15fd7ebec5d4..10ffae53c775 100644 --- a/sbin/md5/md5.c +++ b/sbin/md5/md5.c @@ -17,7 +17,6 @@ * documentation and/or software. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/resource.h> #include <sys/stat.h> @@ -47,6 +46,8 @@ #ifdef HAVE_CAPSICUM #include <sys/capsicum.h> #include <capsicum_helpers.h> +#include <libcasper.h> +#include <casper/cap_fileargs.h> #endif /* @@ -262,7 +263,8 @@ static const char *gnu_shortopts = "bctwz"; static const struct option perl_longopts[] = { { "algorithm", required_argument, 0, opt_algorithm }, - { "check", required_argument, 0, opt_check }, + { "binary", no_argument, 0, opt_binary }, + { "check", no_argument, 0, opt_check }, { "help", no_argument, 0, opt_help }, { "ignore-missing", no_argument, 0, opt_ignore_missing }, { "quiet", no_argument, 0, opt_quiet }, @@ -285,9 +287,10 @@ MD5_Update(MD5_CTX *c, const unsigned char *data, size_t len) } struct chksumrec { - char *filename; - char *chksum; - struct chksumrec *next; + char *filename; + enum input_mode input_mode; + char *chksum; + struct chksumrec *next; }; static struct chksumrec *head = NULL; @@ -300,17 +303,18 @@ static unsigned int numrecs; static void gnu_check(const char *checksumsfile) { - FILE *inp; - char *linebuf = NULL; - size_t linecap; - ssize_t linelen; - int lineno; - char *filename; - char *hashstr; - struct chksumrec *rec; - const char *digestname; - size_t digestnamelen; - size_t hashstrlen; + FILE *inp; + char *linebuf = NULL; + size_t linecap; + ssize_t linelen; + int lineno; + char *filename; + char *hashstr; + struct chksumrec *rec; + const char *digestname; + size_t digestnamelen; + size_t hashstrlen; + struct stat st; if (strcmp(checksumsfile, "-") == 0) inp = stdin; @@ -358,8 +362,19 @@ gnu_check(const char *checksumsfile) rec = malloc(sizeof(*rec)); if (rec == NULL) errx(1, "malloc failed"); + + if ((*filename == '*' || *filename == ' ' || + *filename == 'U' || *filename == '^') && + lstat(filename, &st) != 0 && + lstat(filename + 1, &st) == 0) { + rec->filename = strdup(filename + 1); + rec->input_mode = (enum input_mode)*filename; + } else { + rec->filename = strdup(filename); + rec->input_mode = input_mode; + } + rec->chksum = strdup(hashstr); - rec->filename = strdup(filename); if (rec->chksum == NULL || rec->filename == NULL) errx(1, "malloc failed"); rec->next = NULL; @@ -384,16 +399,17 @@ int main(int argc, char *argv[]) { #ifdef HAVE_CAPSICUM - cap_rights_t rights; + cap_rights_t rights; + fileargs_t *fa = NULL; #endif const struct option *longopts; const char *shortopts; - FILE *f; - int i, opt; - char *p, *string = NULL; - char buf[HEX_DIGEST_LENGTH]; - size_t len; - struct chksumrec *rec; + FILE *f; + int i, opt; + char *p, *string = NULL; + char buf[HEX_DIGEST_LENGTH]; + size_t len; + struct chksumrec *rec; if ((progname = strrchr(argv[0], '/')) == NULL) progname = argv[0]; @@ -562,7 +578,7 @@ main(int argc, char *argv[]) argv += optind; #ifdef HAVE_CAPSICUM - if (caph_limit_stdout() < 0 || caph_limit_stderr() < 0) + if (caph_limit_stdio() < 0) err(1, "unable to limit rights for stdio"); #endif @@ -576,7 +592,7 @@ main(int argc, char *argv[]) while (argc--) gnu_check(*argv++); argc = 0; - argv = calloc(sizeof(char *), numrecs + 1); + argv = calloc(numrecs + 1, sizeof(char *)); for (rec = head; rec != NULL; rec = rec->next) { argv[argc] = rec->filename; argc++; @@ -585,59 +601,49 @@ main(int argc, char *argv[]) rec = head; } - if (*argv) { +#ifdef HAVE_CAPSICUM + fa = fileargs_init(argc, argv, O_RDONLY, 0, + cap_rights_init(&rights, CAP_READ, CAP_FSTAT, CAP_FCNTL), FA_OPEN | FA_LSTAT); + if (fa == NULL) + err(1, "Unable to initialize casper"); + if (caph_enter_casper() < 0) + err(1, "Unable to enter capability mode"); +#endif + + if (*argv && !pflag && string == NULL) { do { - struct stat st; const char *filename = *argv; const char *filemode = "rb"; - if (*filename == '*' || - *filename == ' ' || - *filename == 'U' || - *filename == '^') { - if (lstat(filename, &st) != 0) { - input_mode = (int)*filename; - filename++; - } + if (cflag && mode != mode_bsd) { + input_mode = rec->input_mode; + checkAgainst = rec->chksum; + rec = rec->next; } if (input_mode == input_text) filemode = "r"; - if ((f = fopen(filename, filemode)) == NULL) { + if (strcmp(filename, "-") == 0) { + f = stdin; + } else { +#ifdef HAVE_CAPSICUM + f = fileargs_fopen(fa, filename, filemode); +#else + f = fopen(filename, filemode); +#endif + } + if (f == NULL) { if (errno != ENOENT || !(cflag && ignoreMissing)) { warn("%s", filename); failed = true; } - if (cflag && mode != mode_bsd) - rec = rec->next; continue; } - /* - * XXX Enter capability mode on the last argv file. - * When a casper file service or other approach is - * available, switch to that and enter capability mode - * earlier. - */ - if (*(argv + 1) == NULL) { -#ifdef HAVE_CAPSICUM - cap_rights_init(&rights, CAP_READ, CAP_FSTAT); - if (caph_rights_limit(fileno(f), &rights) < 0 || - caph_enter() < 0) - err(1, "capsicum"); -#endif - } - if (cflag && mode != mode_bsd) { - checkAgainst = rec->chksum; - rec = rec->next; - } p = MDInput(&Algorithm[digest], f, buf, false); - (void)fclose(f); + if (f != stdin) + (void)fclose(f); MDOutput(&Algorithm[digest], p, filename); } while (*++argv); } else if (!cflag && string == NULL && !skip) { -#ifdef HAVE_CAPSICUM - if (caph_limit_stdin() < 0 || caph_enter() < 0) - err(1, "capsicum"); -#endif if (mode == mode_bsd) output_mode = output_bare; p = MDInput(&Algorithm[digest], stdin, buf, pflag); @@ -659,6 +665,9 @@ main(int argc, char *argv[]) if (checksFailed != 0 || (strict && malformed > 0)) return (1); } +#ifdef HAVE_CAPSICUM + fileargs_free(fa); +#endif if (failed) return (1); if (checksFailed > 0) diff --git a/sbin/md5/tests/Makefile b/sbin/md5/tests/Makefile index 236e8433fd43..03c4da684eba 100644 --- a/sbin/md5/tests/Makefile +++ b/sbin/md5/tests/Makefile @@ -1,4 +1,3 @@ - .include <bsd.own.mk> PACKAGE= tests diff --git a/sbin/md5/tests/Makefile.depend b/sbin/md5/tests/Makefile.depend new file mode 100644 index 000000000000..11aba52f82cf --- /dev/null +++ b/sbin/md5/tests/Makefile.depend @@ -0,0 +1,10 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/sbin/md5/tests/md5_test.sh b/sbin/md5/tests/md5_test.sh index c6bc1dfd7be0..34bdf43f13ea 100644 --- a/sbin/md5/tests/md5_test.sh +++ b/sbin/md5/tests/md5_test.sh @@ -197,11 +197,15 @@ bsd_${alg}_vec${i}_body() { printf '%s' \"\$inp_${i}\" >in atf_check -o inline:\"\$out_${i}_${alg}\n\" ${alg} <in atf_check -o inline:\"\$name_bsd_${alg} (in) = \$out_${i}_${alg}\n\" ${alg} in + atf_check -o inline:\"\$name_bsd_${alg} (-) = \$out_${i}_${alg}\n\" ${alg} - <in atf_check -o inline:\"\$out_${i}_${alg} in\n\" ${alg} -r in + atf_check -o inline:\"\$out_${i}_${alg} -\n\" ${alg} -r - <in # -q overrides -r regardless of order for opt in -q -qr -rq ; do atf_check -o inline:\"\$out_${i}_${alg}\n\" ${alg} \${opt} in done + atf_check -o inline:\"\$inp_${i}\$out_${i}_${alg}\n\" ${alg} -p <in + atf_check -o inline:\"\$out_${i}_${alg}\n\" ${alg} -s \"\$inp_${i}\" } " eval " @@ -215,9 +219,13 @@ gnu_${alg}_vec${i}_body() { atf_check -o inline:\"\$out_${i}_${alg} -\n\" ${alg}sum <in atf_check -o inline:\"\$out_${i}_${alg} *-\n\" ${alg}sum -b <in atf_check -o inline:\"\$out_${i}_${alg} in\n\" ${alg}sum in + atf_check -o inline:\"\$out_${i}_${alg} -\n\" ${alg}sum - <in atf_check -o inline:\"\$out_${i}_${alg} *in\n\" ${alg}sum -b in + atf_check -o inline:\"\$out_${i}_${alg} *-\n\" ${alg}sum -b - <in atf_check -o inline:\"\$name_bsd_${alg} (in) = \$out_${i}_${alg}\n\" ${alg}sum --tag in + atf_check -o inline:\"\$name_bsd_${alg} (-) = \$out_${i}_${alg}\n\" ${alg}sum --tag - <in atf_check -o inline:\"\$out_${i}_${alg} in\0\" ${alg}sum -z in + atf_check -o inline:\"\$out_${i}_${alg} -\0\" ${alg}sum -z - <in } " eval " @@ -233,9 +241,13 @@ perl_${alg}_vec${i}_body() { atf_check -o inline:\"\$out_${i}_${alg} *-\n\" shasum \$alg_perl_${alg} -b <in atf_check -o inline:\"\$out_${i}_${alg} U-\n\" shasum \$alg_perl_${alg} -U <in atf_check -o inline:\"\$out_${i}_${alg} in\n\" shasum \$alg_perl_${alg} in + atf_check -o inline:\"\$out_${i}_${alg} -\n\" shasum \$alg_perl_${alg} - <in atf_check -o inline:\"\$out_${i}_${alg} *in\n\" shasum \$alg_perl_${alg} -b in + atf_check -o inline:\"\$out_${i}_${alg} *-\n\" shasum \$alg_perl_${alg} -b - <in atf_check -o inline:\"\$out_${i}_${alg} Uin\n\" shasum \$alg_perl_${alg} -U in + atf_check -o inline:\"\$out_${i}_${alg} U-\n\" shasum \$alg_perl_${alg} -U - <in atf_check -o inline:\"\$name_perl_${alg} (in) = \$out_${i}_${alg}\n\" shasum \$alg_perl_${alg} --tag in + atf_check -o inline:\"\$name_perl_${alg} (-) = \$out_${i}_${alg}\n\" shasum \$alg_perl_${alg} --tag - <in } " done @@ -357,6 +369,28 @@ gnu_cflag_body() } +atf_test_case gnu_cflag_mode +gnu_cflag_mode_head() +{ + atf_set descr "Verify handling of input modes in GNU check mode" + atf_set require.progs "sha1sum" +} +gnu_cflag_mode_body() +{ + printf "The Magic Words are 01010011 01001111\r\n" >input + # The first line is malformed per GNU coreutils but matches + # what we produce when mode == mode_bsd && output_mode == + # output_reverse (i.e. `sha1 -r`) so we want to support it. + cat >digests <<EOF +53d88300dfb2be42f0ef25e3d9de798e31bb7e69 input +53d88300dfb2be42f0ef25e3d9de798e31bb7e69 *input +53d88300dfb2be42f0ef25e3d9de798e31bb7e69 input +2290cf6ba4ac5387e520088de760b71a523871b0 ^input +c1065e0d2bbc1c67dcecee0187d61316fb9c5582 Uinput +EOF + atf_check sha1sum --quiet --check digests +} + atf_init_test_cases() { for alg in $algorithms ; do @@ -371,4 +405,5 @@ atf_init_test_cases() done atf_add_test_case gnu_bflag atf_add_test_case gnu_cflag + atf_add_test_case gnu_cflag_mode } diff --git a/sbin/mdconfig/Makefile b/sbin/mdconfig/Makefile index 5e102d635e93..bd835df0daec 100644 --- a/sbin/mdconfig/Makefile +++ b/sbin/mdconfig/Makefile @@ -1,4 +1,3 @@ - .include <src.opts.mk> PACKAGE=runtime diff --git a/sbin/mdconfig/mdconfig.8 b/sbin/mdconfig/mdconfig.8 index 757e38cc4841..9a86a9c686fd 100644 --- a/sbin/mdconfig/mdconfig.8 +++ b/sbin/mdconfig/mdconfig.8 @@ -31,11 +31,9 @@ .\" 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. -.\" -.\" @(#)vnconfig.8 8.1 (Berkeley) 6/5/93 .\" from: src/usr.sbin/vnconfig/vnconfig.8,v 1.19 2000/12/27 15:30:29 .\" -.Dd August 27, 2021 +.Dd June 1, 2024 .Dt MDCONFIG 8 .Os .Sh NAME @@ -232,8 +230,6 @@ option tends to waste memory by giving unwanted double caching, but it saves time if there is memory to spare. .It Oo Cm no Oc Ns Cm reserve Allocate and reserve all needed storage from the start, rather than as needed. -.It Oo Cm no Oc Ns Cm cluster -Enable clustering on this disk. .It Oo Cm no Oc Ns Cm compress Enable/disable compression features to reduce memory usage. .It Oo Cm no Oc Ns Cm force @@ -335,7 +331,7 @@ mdconfig -rs 2g -u md3 .Ed .Pp Create a 1 gigabyte swap backed disk, initialize an -.Xr ffs 7 +.Xr ffs 4 file system on it, and mount it on .Pa /tmp : .Bd -literal -offset indent @@ -368,8 +364,8 @@ mount /dev/md1.nop /mnt .Xr fpathconf 2 , .Xr fspacectl 2 , .Xr open 2 , +.Xr ffs 4 , .Xr md 4 , -.Xr ffs 7 , .Xr gpart 8 , .Xr mdmfs 8 , .Xr malloc 9 , diff --git a/sbin/mdconfig/mdconfig.c b/sbin/mdconfig/mdconfig.c index 740f800e3e79..4b03dd81e591 100644 --- a/sbin/mdconfig/mdconfig.c +++ b/sbin/mdconfig/mdconfig.c @@ -56,7 +56,7 @@ static struct md_ioctl mdio; static enum {UNSET, ATTACH, DETACH, RESIZE, LIST} action = UNSET; -static int nflag; +static int md_fd, nflag; static void usage(void) __dead2; static void md_set_file(const char *); @@ -65,6 +65,7 @@ static int md_query(const char *, const int, const char *); static int md_list(const char *, int, const char *); static char *geom_config_get(struct gconf *g, const char *name); static void md_prthumanval(char *length); +static void print_options(const char *s, const char *); #define OPT_VERBOSE 0x01 #define OPT_UNIT 0x02 @@ -86,11 +87,11 @@ usage(void) " mdconfig -l [-v] [-n] [-f file] [-u unit]\n" " mdconfig file\n"); fprintf(stderr, "\t\ttype = {malloc, vnode, swap}\n"); - fprintf(stderr, "\t\toption = {cache, cluster, compress, force,\n"); - fprintf(stderr, "\t\t mustdealloc, readonly, reserve, ro,\n"); - fprintf(stderr, "\t\t verify}\n"); + fprintf(stderr, "\t\toption = {async, cache, compress,\n"); + fprintf(stderr, "\t\t force, mustdealloc, readonly, ro,\n"); + fprintf(stderr, "\t\t reserve, verify}\n"); fprintf(stderr, "\t\tsize = %%d (512 byte blocks), %%db (B),\n"); - fprintf(stderr, "\t\t %%dk (kB), %%dm (MB), %%dg (GB), \n"); + fprintf(stderr, "\t\t %%dk (kB), %%dm (MB), %%dg (GB),\n"); fprintf(stderr, "\t\t %%dt (TB), or %%dp (PB)\n"); exit(1); } @@ -98,7 +99,7 @@ usage(void) int main(int argc, char **argv) { - int ch, fd, i, vflag; + int ch, i, vflag; char *p; char *fflag = NULL, *sflag = NULL, *tflag = NULL, *uflag = NULL; @@ -155,13 +156,13 @@ main(int argc, char **argv) mdio.md_options |= MD_AUTOUNIT | MD_COMPRESS; } else if (!strcmp(optarg, "vnode")) { mdio.md_type = MD_VNODE; - mdio.md_options |= MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS; + mdio.md_options |= MD_AUTOUNIT | MD_COMPRESS; } else if (!strcmp(optarg, "swap")) { mdio.md_type = MD_SWAP; - mdio.md_options |= MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS; + mdio.md_options |= MD_AUTOUNIT | MD_COMPRESS; } else if (!strcmp(optarg, "null")) { mdio.md_type = MD_NULL; - mdio.md_options |= MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS; + mdio.md_options |= MD_AUTOUNIT | MD_COMPRESS; } else errx(1, "unknown type: %s", optarg); break; @@ -181,10 +182,15 @@ main(int argc, char **argv) mdio.md_options |= MD_CACHE; else if (!strcmp(optarg, "nocache")) mdio.md_options &= ~MD_CACHE; - else if (!strcmp(optarg, "cluster")) - mdio.md_options |= MD_CLUSTER; - else if (!strcmp(optarg, "nocluster")) - mdio.md_options &= ~MD_CLUSTER; + /* + * For backwards-compatibility, continue to recognize + * "cluster" + */ + else if (!strcmp(optarg, "cluster") || + !strcmp(optarg, "nocluster")) + { + warnx("Option cluster is ignored"); + } else if (!strcmp(optarg, "compress")) mdio.md_options |= MD_COMPRESS; else if (!strcmp(optarg, "nocompress")) @@ -281,13 +287,11 @@ main(int argc, char **argv) if (fflag != NULL || argc > 0) { /* Imply ``-t vnode'' */ mdio.md_type = MD_VNODE; - mdio.md_options |= MD_CLUSTER | MD_AUTOUNIT | - MD_COMPRESS; + mdio.md_options |= MD_AUTOUNIT | MD_COMPRESS; } else if (sflag != NULL) { /* Imply ``-t swap'' */ mdio.md_type = MD_SWAP; - mdio.md_options |= MD_CLUSTER | MD_AUTOUNIT | - MD_COMPRESS; + mdio.md_options |= MD_AUTOUNIT | MD_COMPRESS; } else errx(1, "unable to determine type"); } @@ -366,12 +370,12 @@ main(int argc, char **argv) if (!kld_isloaded("g_md") && kld_load("geom_md") == -1) err(1, "failed to load geom_md module"); - fd = open(_PATH_DEV MDCTL_NAME, O_RDWR, 0); - if (fd < 0) + md_fd = open(_PATH_DEV MDCTL_NAME, O_RDWR, 0); + if (md_fd < 0) err(1, "open(%s%s)", _PATH_DEV, MDCTL_NAME); if (action == ATTACH) { - i = ioctl(fd, MDIOCATTACH, &mdio); + i = ioctl(md_fd, MDIOCATTACH, &mdio); if (i < 0) err(1, "ioctl(%s%s)", _PATH_DEV, MDCTL_NAME); if (mdio.md_options & MD_AUTOUNIT) @@ -379,13 +383,13 @@ main(int argc, char **argv) } else if (action == DETACH) { if (mdio.md_options & MD_AUTOUNIT) errx(1, "-d requires -u"); - i = ioctl(fd, MDIOCDETACH, &mdio); + i = ioctl(md_fd, MDIOCDETACH, &mdio); if (i < 0) err(1, "ioctl(%s%s)", _PATH_DEV, MDCTL_NAME); } else if (action == RESIZE) { if (mdio.md_options & MD_AUTOUNIT) errx(1, "-r requires -u"); - i = ioctl(fd, MDIOCRESIZE, &mdio); + i = ioctl(md_fd, MDIOCRESIZE, &mdio); if (i < 0) err(1, "ioctl(%s%s)", _PATH_DEV, MDCTL_NAME); } else if (action == LIST) { @@ -399,11 +403,67 @@ main(int argc, char **argv) return (md_query(uflag, vflag, fflag)); } else usage(); - close(fd); + close(md_fd); return (0); } static void +print_options(const char *dev, const char *file) +{ + struct md_ioctl mdiox; + int unit; + const char *sep = ""; + + if (sscanf(dev, "md%d", &unit) != 1) + err(1, "invalid device: %s", dev); + + memset(&mdiox, 0, sizeof(mdiox)); + mdiox.md_version = MDIOVERSION; + mdiox.md_unit = unit; + mdiox.md_file = file[0] == '-' ? NULL : strdup(file); + + if (ioctl(md_fd, MDIOCQUERY, &mdiox) < 0) + err(1, "ioctl(%s%s)", _PATH_DEV, MDCTL_NAME); + + if (mdiox.md_file != NULL) + free(mdiox.md_file); + + printf("\t"); + if (mdiox.md_options & MD_ASYNC) { + printf("%sasync", sep); + sep = ","; + } + if (mdiox.md_options & MD_CACHE) { + printf("%scache", sep); + sep = ","; + } + if (mdiox.md_options & MD_COMPRESS) { + printf("%scompress", sep); + sep = ","; + } + if (mdiox.md_options & MD_FORCE) { + printf("%sforce", sep); + sep = ","; + } + if (mdiox.md_options & MD_READONLY) { + printf("%sreadonly", sep); + sep = ","; + } + if (mdiox.md_options & MD_RESERVE) { + printf("%sreserve", sep); + sep = ","; + } + if (mdiox.md_options & MD_VERIFY) { + printf("%sverify", sep); + sep = ","; + } + if (mdiox.md_options & MD_MUSTDEALLOC) { + printf("%smustdealloc", sep); + sep = ","; + } +} + +static void md_set_file(const char *fn) { struct stat sb; @@ -500,11 +560,12 @@ md_list(const char *units, int opt, const char *fflag) if (file == NULL) file = "-"; printf("\t%s", file); - file = NULL; label = geom_config_get(gc, "label"); if (label == NULL) - label = ""; + label = "-"; printf("\t%s", label); + print_options(pp->lg_name, file); + file = label = NULL; } opt |= OPT_DONE; if ((opt & OPT_LIST) && !(opt & OPT_VERBOSE)) diff --git a/sbin/mdconfig/tests/Makefile b/sbin/mdconfig/tests/Makefile index 674536a33d74..a3841f8f0dd0 100644 --- a/sbin/mdconfig/tests/Makefile +++ b/sbin/mdconfig/tests/Makefile @@ -1,4 +1,3 @@ - ATF_TESTS_SH= mdconfig_test TEST_METADATA.mdconfig_test+= required_user="root" diff --git a/sbin/mdconfig/tests/mdconfig_test.sh b/sbin/mdconfig/tests/mdconfig_test.sh index 9aaf724206b1..ea87ff5d542d 100755 --- a/sbin/mdconfig/tests/mdconfig_test.sh +++ b/sbin/mdconfig/tests/mdconfig_test.sh @@ -291,7 +291,26 @@ attach_size_rounddown_body() -x "mdconfig -r -u ${md#md} -s ${ms2}b" check_diskinfo "$md" 16384 2 $ss } -attach_size_rounddown() +attach_size_rounddown_cleanup() +{ + cleanup_common +} + +atf_test_case query_verbose cleanup +query_verbose() +{ + atf_set "descr" "mdconfig -lv should print device details" +} +query_verbose_body() +{ + atf_check -s exit:0 -o save:mdconfig.out \ + -x 'mdconfig -a -t swap -s 1m -o reserve -o force' + md=$(cat mdconfig.out) + atf_check -s exit:0 \ + -o match:"$md[[:space:]]+swap[[:space:]]+1024K[[:space:]]+[-][[:space:]]+[-][[:space:]]+force,reserve" \ + -x "mdconfig -lv -u $md" +} +query_verbose_cleanup() { cleanup_common } @@ -307,4 +326,5 @@ atf_init_test_cases() atf_add_test_case attach_swap atf_add_test_case attach_with_specific_unit_number atf_add_test_case attach_size_rounddown + atf_add_test_case query_verbose } diff --git a/sbin/mdmfs/Makefile b/sbin/mdmfs/Makefile index 592ceaa152c2..0665dfcc20a3 100644 --- a/sbin/mdmfs/Makefile +++ b/sbin/mdmfs/Makefile @@ -1,4 +1,3 @@ - PACKAGE=runtime PROG= mdmfs LINKS= ${BINDIR}/${PROG} ${BINDIR}/mount_mfs diff --git a/sbin/mdmfs/mdmfs.8 b/sbin/mdmfs/mdmfs.8 index 6824e5ccac03..ff5b78068299 100644 --- a/sbin/mdmfs/mdmfs.8 +++ b/sbin/mdmfs/mdmfs.8 @@ -32,7 +32,7 @@ .Nd configure and mount an in-memory file system using the .Xr md 4 driver or the -.Xr tmpfs 5 +.Xr tmpfs 4 filesystem .Sh SYNOPSIS .Nm @@ -61,7 +61,7 @@ filesystem The .Nm utility is designed to be a work-alike and look-alike of the deprecated -.Xr mount_mfs 8 . +.Nm mount_mfs . The end result is essentially the same, but is accomplished in a completely different way. Based on @@ -69,7 +69,7 @@ Based on the .Nm utility either creates a -.Xr tmpfs 5 +.Xr tmpfs 4 filesystem, or it configures an .Xr md 4 disk using @@ -91,7 +91,7 @@ When is `auto', .Nm uses -.Xr tmpfs 5 +.Xr tmpfs 4 if it is present in the kernel or can be loaded as a module, otherwise it falls back to using .Xr md 4 @@ -102,14 +102,14 @@ When is `tmpfs', .Nm mounts a -.Xr tmpfs 5 +.Xr tmpfs 4 filesystem, translating the .Fl s size option, if present, into a `-o size=' mount option. Any .Fl o options on the command line are passed through to the -.Xr tmpfs 5 +.Xr tmpfs 4 mount. Options specific to .Xr mdconfig 8 @@ -120,7 +120,7 @@ are ignored. When .Ar md-device does not result in -.Xr tmpfs 5 +.Xr tmpfs 4 being used, then an .Xr md 4 device is configured instead. @@ -149,7 +149,7 @@ it uses the default arguments to all the helper programs. The following options are available. Where possible, the option letter matches the one used by -.Xr mount_mfs 8 +.Nm mount_mfs for the same thing. .Bl -tag -width indent .It Fl a Ar maxcontig @@ -404,10 +404,10 @@ Mount a vnode-backed cd9660 file system using automatic device numbering: The .Nm utility, while designed to be compatible with -.Xr mount_mfs 8 , +.Nm mount_mfs , can be useful by itself. Since -.Xr mount_mfs 8 +.Nm mount_mfs had some silly defaults, a .Dq compatibility mode is provided for the case where bug-to-bug compatibility is desired. @@ -421,7 +421,7 @@ or (as returned by .Xr getprogname 3 ) . In this mode, the following behavior, as done by -.Xr mount_mfs 8 , +.Nm mount_mfs , is duplicated: .Bl -bullet -offset indent .It @@ -435,8 +435,8 @@ was given on the command line. .El .Sh SEE ALSO .Xr md 4 , +.Xr tmpfs 4 , .Xr fstab 5 , -.Xr tmpfs 5 , .Xr mdconfig 8 , .Xr mount 8 , .Xr newfs 8 diff --git a/sbin/mdmfs/mdmfs.c b/sbin/mdmfs/mdmfs.c index dbd206f953d2..69149b3fb465 100644 --- a/sbin/mdmfs/mdmfs.c +++ b/sbin/mdmfs/mdmfs.c @@ -32,7 +32,6 @@ * the deprecated mount_mfs(8). */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/linker.h> #include <sys/mdioctl.h> @@ -105,7 +104,7 @@ main(int argc, char **argv) bool detach, softdep, autounit, newfs; const char *mtpoint, *size_arg, *skel, *unitstr; char *p; - int ch, idx; + int ch, idx, rv; void *set; unsigned long ul; @@ -358,6 +357,13 @@ main(int argc, char **argv) do_mdconfig_attach(mdconfig_arg, mdtype); if (newfs) do_newfs(newfs_arg); + if (!softdep) { + rv = run(NULL, "%s %s /dev/%s%d", _PATH_TUNEFS, + "-n disable", mdname, unit); + if (rv) + errx(1, "tunefs exited %s %d", run_exitstr(rv), + run_exitnumber(rv)); + } do_mount_md(mount_arg, mtpoint); } diff --git a/sbin/mknod/Makefile b/sbin/mknod/Makefile index 3a3be17f9cf7..2182f31ca247 100644 --- a/sbin/mknod/Makefile +++ b/sbin/mknod/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.1 (Berkeley) 6/5/93 - PACKAGE=runtime PROG= mknod MAN= mknod.8 diff --git a/sbin/mknod/mknod.8 b/sbin/mknod/mknod.8 index 7bac489f4a2e..fc562e76f0db 100644 --- a/sbin/mknod/mknod.8 +++ b/sbin/mknod/mknod.8 @@ -25,8 +25,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)mknod.8 8.2 (Berkeley) 12/11/93 -.\" .Dd October 3, 2016 .Dt MKNOD 8 .Os @@ -109,7 +107,7 @@ will cause the number to be interpreted as octal. The .Nm utility can be used to recreate deleted device nodes under a -.Xr devfs 5 +.Xr devfs 4 mount point by invoking it with only a filename as an argument. Example: .Pp @@ -131,7 +129,7 @@ devices. As of .Fx 5.0 , device nodes are managed by the device file system -.Xr devfs 5 , +.Xr devfs 4 , making the .Nm utility superfluous. @@ -142,7 +140,7 @@ nodes cannot be used to access devices. .Sh SEE ALSO .Xr mkfifo 1 , .Xr mknod 2 , -.Xr devfs 5 , +.Xr devfs 4 , .Xr chown 8 .Sh HISTORY A diff --git a/sbin/mknod/mknod.c b/sbin/mknod/mknod.c index be7c97cf06fb..a9eb65e7a76c 100644 --- a/sbin/mknod/mknod.c +++ b/sbin/mknod/mknod.c @@ -32,20 +32,6 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1989, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)mknod.c 8.1 (Berkeley) 6/5/93"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/types.h> #include <sys/stat.h> diff --git a/sbin/mksnap_ffs/Makefile b/sbin/mksnap_ffs/Makefile index b11bf0a25fa5..cf5079fdfa8e 100644 --- a/sbin/mksnap_ffs/Makefile +++ b/sbin/mksnap_ffs/Makefile @@ -1,13 +1,11 @@ - .PATH: ${.CURDIR:H}/mount 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 98ac87c2d741..e2a3098813c4 100644 --- a/sbin/mount/Makefile +++ b/sbin/mount/Makefile @@ -1,17 +1,7 @@ -# @(#)Makefile 8.6 (Berkeley) 5/8/95 - 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 31987f7b0b78..000000000000 --- a/sbin/mount/getmntopts.c +++ /dev/null @@ -1,307 +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. - */ - -#if 0 -#ifndef lint -static char sccsid[] = "@(#)getmntopts.c 8.3 (Berkeley) 3/29/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> -#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 782acabef1a0..000000000000 --- a/sbin/mount/mntopts.3 +++ /dev/null @@ -1,381 +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. -.\" -.\" @(#)getmntopts.3 8.3 (Berkeley) 3/30/95 -.\" -.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 14.0 . diff --git a/sbin/mount/mntopts.h b/sbin/mount/mntopts.h deleted file mode 100644 index 21d4965ea39b..000000000000 --- a/sbin/mount/mntopts.h +++ /dev/null @@ -1,111 +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. - * - * @(#)mntopts.h 8.7 (Berkeley) 3/29/95 - */ - -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 5632ad678ffb..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,9 +28,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)mount.8 8.8 (Berkeley) 6/16/94 -.\" -.Dd March 17, 2022 +.Dd July 16, 2025 .Dt MOUNT 8 .Os .Sh NAME @@ -39,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 @@ -48,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 @@ -79,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 @@ -157,6 +158,9 @@ For this reason, the .Cm async flag should be used sparingly, and only when some data recovery mechanism is present. +.It Cm atime +Update the file access time when reading from a file. +This is the default. .It Cm automounted This flag indicates that the file system was mounted by .Xr automountd 8 . @@ -269,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. @@ -431,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. @@ -546,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 @@ -561,15 +571,18 @@ support for a particular file system might be provided either on a static .Xr setfacl 1 , .Xr nmount 2 , .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 , +.Xr ffs 4 , .Xr mac 4 , -.Xr cd9660 5 , -.Xr devfs 5 , -.Xr ext2fs 5 , +.Xr procfs 4 , +.Xr tarfs 4 , +.Xr tmpfs 4 , .Xr fstab 5 , -.Xr procfs 5 , -.Xr tmpfs 5 , .Xr automount 8 , .Xr fstyp 8 , .Xr kldload 8 , @@ -580,6 +593,7 @@ support for a particular file system might be provided either on a static .Xr mount_smbfs 8 , .Xr mount_udf 8 , .Xr mount_unionfs 8 , +.Xr quotacheck 8 , .Xr umount 8 , .Xr zfs 8 , .Xr zpool 8 @@ -612,3 +626,17 @@ only when the file system is mounted via .Nm . .Sh BUGS It is possible for a corrupted file system to cause a crash. +.Pp +The +.Fl p +option will not list +.Cm userquota +or +.Cm groupquota +items from +.Xr fstab 5 +because they are not true mount options and are not information returned by +.Xr getmntinfo 3 . +At boot +.Xr quotacheck 8 , +processes these items. diff --git a/sbin/mount/mount.c b/sbin/mount/mount.c index 2fcc94e40818..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 @@ -29,16 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1980, 1989, 1993, 1994\n\ - The Regents of the University of California. All rights reserved.\n"; -#if 0 -static char sccsid[] = "@(#)mount.c 8.25 (Berkeley) 5/8/95"; -#endif -#endif /* not lint */ - -#include <sys/cdefs.h> #include <sys/param.h> #define _WANT_MNTOPTNAMES #include <sys/mount.h> @@ -46,9 +36,10 @@ static char sccsid[] = "@(#)mount.c 8.25 (Berkeley) 5/8/95"; #include <sys/wait.h> #include <ctype.h> -#include <err.h> + #include <errno.h> #include <fstab.h> +#include <mntopts.h> #include <paths.h> #include <pwd.h> #include <signal.h> @@ -61,12 +52,12 @@ static char sccsid[] = "@(#)mount.c 8.25 (Berkeley) 5/8/95"; #include <libxo/xo.h> #include "extern.h" -#include "mntopts.h" #include "pathnames.h" #define EXIT(a) { \ xo_close_container("mount"); \ - xo_finish(); \ + if (xo_finish() < 0) \ + xo_err(EXIT_FAILURE, "stdout"); \ exit(a); \ } @@ -152,7 +143,7 @@ exec_mountprog(const char *name, const char *execname, char *const argv[]) switch (pid = fork()) { case -1: /* Error. */ xo_warn("fork"); - EXIT(1); + EXIT(EXIT_FAILURE); case 0: /* Child. */ /* Go find an executable. */ execvP(execname, _PATH_SYSPATH, argv); @@ -162,7 +153,7 @@ exec_mountprog(const char *name, const char *execname, char *const argv[]) xo_warnx("in path: %s", _PATH_SYSPATH); } } - EXIT(1); + EXIT(EXIT_FAILURE); default: /* Parent. */ if (waitpid(pid, &status, 0) < 0) { xo_warn("waitpid"); @@ -227,7 +218,7 @@ main(int argc, char *argv[]) argc = xo_parse_args(argc, argv); if (argc < 0) - exit(1); + exit(EXIT_FAILURE); xo_open_container("mount"); while ((ch = getopt(argc, argv, "adF:fLlno:prt:uvw")) != -1) @@ -299,7 +290,7 @@ main(int argc, char *argv[]) if ((init_flags & MNT_UPDATE) && (ro == 0)) options = catopt(options, "noro"); - rval = 0; + rval = EXIT_SUCCESS; switch (argc) { case 0: if ((mntsize = getmntinfo(&mntbuf, @@ -330,7 +321,7 @@ main(int argc, char *argv[]) if (mountfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file, init_flags, options, fs->fs_mntops) && !failok) - rval = 1; + rval = EXIT_FAILURE; } } else if (fstab_style) { xo_open_list("fstab"); @@ -451,7 +442,7 @@ main(int argc, char *argv[]) * If the mount was successfully, and done by root, tell mountd the * good news. */ - if (rval == 0 && getuid() == 0) + if (rval == EXIT_SUCCESS && getuid() == 0) restart_mountd(); EXIT(rval); @@ -891,19 +882,21 @@ 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"); - EXIT(1); +" mount [-dfpruvw] [-o options] [-t [no]type[,type ...]] special node"); + EXIT(EXIT_FAILURE); } void putfsent(struct statfs *ent) { struct fstab *fst; + const char *mntfromname; char *opts, *rw; int l; + mntfromname = ent->f_mntfromname; opts = NULL; /* flags2opts() doesn't return the "rw" option. */ if ((ent->f_flags & MNT_RDONLY) != 0) @@ -914,16 +907,14 @@ putfsent(struct statfs *ent) opts = flags2opts(ent->f_flags); opts = catopt(rw, opts); - if (strncmp(ent->f_mntfromname, "<below>", 7) == 0 || - strncmp(ent->f_mntfromname, "<above>", 7) == 0) { - strlcpy(ent->f_mntfromname, - (strnstr(ent->f_mntfromname, ":", 8) +1), - sizeof(ent->f_mntfromname)); + if (strncmp(mntfromname, "<below>:", 8) == 0 || + strncmp(mntfromname, "<above>:", 8) == 0) { + mntfromname += 8; } - l = strlen(ent->f_mntfromname); + l = strlen(mntfromname); xo_emit("{:device}{P:/%s}{P:/%s}{P:/%s}", - ent->f_mntfromname, + mntfromname, l < 8 ? "\t" : "", l < 16 ? "\t" : "", l < 24 ? "\t" : " "); @@ -939,7 +930,7 @@ putfsent(struct statfs *ent) l < 8 ? "\t" : " "); free(opts); - if ((fst = getfsspec(ent->f_mntfromname))) + if ((fst = getfsspec(mntfromname))) xo_emit("{P:\t}{n:dump/%u}{P: }{n:pass/%u}\n", fst->fs_freq, fst->fs_passno); else if ((fst = getfsfile(ent->f_mntonname))) diff --git a/sbin/mount/mount_fs.c b/sbin/mount/mount_fs.c index 5674e94594bf..30c34ae32f31 100644 --- a/sbin/mount/mount_fs.c +++ b/sbin/mount/mount_fs.c @@ -32,33 +32,19 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1992, 1993, 1994\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)mount_fs.c 8.6 (Berkeley) 4/26/95"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/mount.h> #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/pathnames.h b/sbin/mount/pathnames.h index f584d7ce9f12..169d5384cc7b 100644 --- a/sbin/mount/pathnames.h +++ b/sbin/mount/pathnames.h @@ -27,8 +27,6 @@ * 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. - * - * @(#)pathnames.h 8.2 (Berkeley) 3/27/94 */ #define _PATH_MOUNTDPID "/var/run/mountd.pid" diff --git a/sbin/mount/vfslist.c b/sbin/mount/vfslist.c index 3785451e65b2..ea6086651483 100644 --- a/sbin/mount/vfslist.c +++ b/sbin/mount/vfslist.c @@ -29,12 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -#if 0 -static char sccsid[] = "@(#)vfslist.c 8.1 (Berkeley) 5/8/95"; -#endif -#endif /* not lint */ -#include <sys/cdefs.h> #include <err.h> #include <stdlib.h> #include <string.h> diff --git a/sbin/mount_cd9660/Makefile b/sbin/mount_cd9660/Makefile index 30c2b39afd96..9fa8e7436169 100644 --- a/sbin/mount_cd9660/Makefile +++ b/sbin/mount_cd9660/Makefile @@ -1,18 +1,10 @@ -# @(#)Makefile 8.3 (Berkeley) 3/27/94 - 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.8 b/sbin/mount_cd9660/mount_cd9660.8 index a31e6d833245..2c6af7500277 100644 --- a/sbin/mount_cd9660/mount_cd9660.8 +++ b/sbin/mount_cd9660/mount_cd9660.8 @@ -29,9 +29,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)mount_cd9660.8 8.3 (Berkeley) 3/27/94 -.\" -.Dd August 11, 2018 +.Dd December 6, 2024 .Dt MOUNT_CD9660 8 .Os .Sh NAME @@ -41,8 +39,12 @@ .Nm .Op Fl begjrv .Op Fl C Ar charset +.Op Fl G Ar gid +.Op Fl m Ar mask +.Op Fl M Ar mask .Op Fl o Ar options .Op Fl s Ar startsector +.Op Fl U Ar uid .Ar special node .Sh DESCRIPTION The @@ -68,6 +70,38 @@ Do not strip version numbers on files. only the last one will be listed.) In either case, files may be opened without explicitly stating a version number. +.It Fl G Ar group +Set the group of the files in the file system to +.Ar group . +The default gid on volumes without extended attributes or Rockridge extensions +is zero. +.It Fl U Ar user +Set the owner of the files in the file system to +.Ar user . +The default uid on volumes without extended attributes or Rockridge extensions +is zero. +.It Fl m Ar mask +Specify the maximum file permissions for files +in the file system. +For example, a +.Ar mask +of +.Li 544 +limits the owner to +read and execute permissions for files and +others to only read permission. +See +.Xr chmod 1 +for more information about octal file modes. +The default +.Ar mask +is 7777. +The default permissions on volumes without extended attributes or Rockridge +extensions is 555. +.It Fl M Ar mask +Specify the maximum file permissions for directories +in the file system. +See the previous option's description for details. .It Fl j Do not use any Joliet extensions included in the file system. .It Fl o @@ -137,7 +171,7 @@ The following command can be used to mount a Kodak Photo-CD: .Xr cdcontrol 1 , .Xr mount 2 , .Xr unmount 2 , -.Xr cd9660 5 , +.Xr cd9660 4 , .Xr fstab 5 , .Xr mdconfig 8 , .Xr mount 8 diff --git a/sbin/mount_cd9660/mount_cd9660.c b/sbin/mount_cd9660/mount_cd9660.c index f322ac73a439..3eddbefb9a03 100644 --- a/sbin/mount_cd9660/mount_cd9660.c +++ b/sbin/mount_cd9660/mount_cd9660.c @@ -32,24 +32,8 @@ * 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. - * - * @(#)mount_cd9660.c 8.7 (Berkeley) 5/1/95 */ -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1992, 1993, 1994\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -/* -static char sccsid[] = "@(#)mount_cd9660.c 8.7 (Berkeley) 5/1/95"; -*/ -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/cdio.h> #include <sys/file.h> #include <sys/param.h> @@ -60,22 +44,27 @@ static const char rcsid[] = #include <arpa/inet.h> +#include <ctype.h> #include <err.h> #include <errno.h> +#include <grp.h> +#include <mntopts.h> +#include <pwd.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, MOPT_END }; +static gid_t a_gid(const char *); +static uid_t a_uid(const char *); +static mode_t a_mask(const char *); static int get_ssector(const char *dev); static int set_charset(struct iovec **, int *iovlen, const char *); void usage(void); @@ -96,7 +85,7 @@ main(int argc, char **argv) mntflags = verbose = 0; ssector = -1; - while ((ch = getopt(argc, argv, "begjo:rs:vC:")) != -1) + while ((ch = getopt(argc, argv, "begG:jm:M:o:rs:U:vC:")) != -1) switch (ch) { case 'b': build_iovec(&iov, &iovlen, "brokenjoliet", NULL, (size_t)-1); @@ -107,6 +96,15 @@ main(int argc, char **argv) case 'g': build_iovec(&iov, &iovlen, "gens", NULL, (size_t)-1); break; + case 'G': + build_iovec_argf(&iov, &iovlen, "gid", "%d", a_gid(optarg)); + break; + case 'm': + build_iovec_argf(&iov, &iovlen, "mask", "%u", a_mask(optarg)); + break; + case 'M': + build_iovec_argf(&iov, &iovlen, "dirmask", "%u", a_mask(optarg)); + break; case 'j': build_iovec(&iov, &iovlen, "nojoliet", NULL, (size_t)-1); break; @@ -126,6 +124,9 @@ main(int argc, char **argv) case 's': ssector = atoi(optarg); break; + case 'U': + build_iovec_argf(&iov, &iovlen, "uid", "%d", a_uid(optarg)); + break; case 'v': verbose++; break; @@ -189,8 +190,8 @@ void usage(void) { (void)fprintf(stderr, -"usage: mount_cd9660 [-begjrv] [-C charset] [-o options] [-s startsector]\n" -" special node\n"); +"usage: mount_cd9660 [-begjrv] [-C charset] [-G gid] [-m mask] [-M mask]\n" +" [-o options] [-U uid] [-s startsector] special node\n"); exit(EX_USAGE); } @@ -270,3 +271,58 @@ set_charset(struct iovec **iov, int *iovlen, const char *localcs) return (0); } + +static gid_t +a_gid(const char *s) +{ + struct group *gr; + const char *gname; + gid_t gid; + + if ((gr = getgrnam(s)) != NULL) + gid = gr->gr_gid; + else { + for (gname = s; *s && isdigit(*s); ++s); + if (!*s) + gid = atoi(gname); + else + errx(EX_NOUSER, "unknown group id: %s", gname); + } + return (gid); +} + +static uid_t +a_uid(const char *s) +{ + struct passwd *pw; + const char *uname; + uid_t uid; + + if ((pw = getpwnam(s)) != NULL) + uid = pw->pw_uid; + else { + for (uname = s; *s && isdigit(*s); ++s); + if (!*s) + uid = atoi(uname); + else + errx(EX_NOUSER, "unknown user id: %s", uname); + } + return (uid); +} + +static mode_t +a_mask(const char *s) +{ + int done, rv; + char *ep; + + done = 0; + rv = -1; + if (*s >= '0' && *s <= '7') { + done = 1; + rv = strtol(optarg, &ep, 8); + } + if (!done || rv < 0 || *ep) + errx(EX_USAGE, "invalid file mode: %s", s); + return (rv); +} diff --git a/sbin/mount_fusefs/Makefile b/sbin/mount_fusefs/Makefile index 741cbed1dd5e..a237ba99eb6b 100644 --- a/sbin/mount_fusefs/Makefile +++ b/sbin/mount_fusefs/Makefile @@ -1,4 +1,3 @@ - .if defined(DEBUG) DEBUG_FLAGS+= -D_DEBUG -g .endif @@ -21,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 59667c39db10..2d65494421ef 100644 --- a/sbin/mount_fusefs/mount_fusefs.c +++ b/sbin/mount_fusefs/mount_fusefs.c @@ -33,7 +33,6 @@ * */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/mount.h> #include <sys/uio.h> @@ -50,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 94532cafb543..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 @@ -179,8 +179,8 @@ To mount a Japanese MS-DOS file system located in .Sh SEE ALSO .Xr mount 2 , .Xr unmount 2 , +.Xr msdosfs 4 , .Xr fstab 5 , -.Xr msdosfs 5 , .Xr mount 8 .Pp List of Localized MS Operating Systems: diff --git a/sbin/mount_msdosfs/mount_msdosfs.c b/sbin/mount_msdosfs/mount_msdosfs.c index 36be6a161ecf..36e1c524922d 100644 --- a/sbin/mount_msdosfs/mount_msdosfs.c +++ b/sbin/mount_msdosfs/mount_msdosfs.c @@ -32,11 +32,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/mount.h> #include <sys/stat.h> @@ -49,6 +44,7 @@ static const char rcsid[] = #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 */ @@ -58,8 +54,6 @@ static const char rcsid[] = #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 567dff2fc150..fb425106dd81 100644 --- a/sbin/mount_nfs/Makefile +++ b/sbin/mount_nfs/Makefile @@ -1,15 +1,12 @@ -# @(#)Makefile 8.2 (Berkeley) 3/27/94 -# - 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.8 b/sbin/mount_nfs/mount_nfs.8 index 7afbc6f691a0..4ae4b6dbd46b 100644 --- a/sbin/mount_nfs/mount_nfs.8 +++ b/sbin/mount_nfs/mount_nfs.8 @@ -25,9 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)mount_nfs.8 8.3 (Berkeley) 3/29/95 -.\" -.Dd June 14, 2023 +.Dd November 29, 2024 .Dt MOUNT_NFS 8 .Os .Sh NAME @@ -198,6 +196,16 @@ This should be used for mounts on old servers that cannot handle a group list size of 16, as specified in RFC 1057. Try 8, if users in a lot of groups cannot get response from the mount point. +.It Cm mountport Ns = Ns Aq Ar value +Specify the port number to be used to communicate with +.Xr mountd 8 +on the NFS server. +This option allows an NFSv2 or NFSv3 mount to be done without +the need to run the +.Xr rpcbind 8 +service. +This option is meaningless for an NFSv4 mount, since NFSv4 +does not use the Mount protocol. .It Cm mntudp Force the mount protocol to use UDP transport, even for TCP NFS mounts. (Necessary for some old @@ -635,6 +643,7 @@ Same as .It Fl t Same as .Fl o Cm retransmit Ns = Ns Aq Ar value +(deprecated) .It Fl w Same as .Fl o Cm wsize Ns = Ns Aq Ar value @@ -649,7 +658,7 @@ named options are equivalent to other .Fl o named options and are supported for compatibility with other operating systems (e.g., Linux, Solaris, and OSX) to ease usage of -.Xr autofs 5 +.Xr autofs 4 support. .Bl -tag -width indent .It Fl o Cm vers Ns = Ns 2 @@ -673,6 +682,7 @@ Same as .Xr mount 8 , .Xr nfsd 8 , .Xr nfsiod 8 , +.Xr rpcbind 8 , .Xr rpc.tlsclntd 8 , .Xr showmount 8 .Sh HISTORY diff --git a/sbin/mount_nfs/mount_nfs.c b/sbin/mount_nfs/mount_nfs.c index f2f22e0a0ccd..55adb01fc1da 100644 --- a/sbin/mount_nfs/mount_nfs.c +++ b/sbin/mount_nfs/mount_nfs.c @@ -32,18 +32,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1992, 1993, 1994\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)mount_nfs.c 8.11 (Berkeley) 5/4/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/linker.h> #include <sys/module.h> @@ -70,6 +58,7 @@ static char sccsid[] = "@(#)mount_nfs.c 8.11 (Berkeley) 5/4/95"; #include <err.h> #include <errno.h> #include <fcntl.h> +#include <mntopts.h> #include <netdb.h> #include <stdbool.h> #include <stdio.h> @@ -79,7 +68,6 @@ static char sccsid[] = "@(#)mount_nfs.c 8.11 (Berkeley) 5/4/95"; #include <sysexits.h> #include <unistd.h> -#include "mntopts.h" #include "mounttab.h" /* Table for af,sotype -> netid conversions. */ @@ -120,6 +108,7 @@ static u_char *fh = NULL; static int fhsize = 0; static int secflavor = -1; static int got_principal = 0; +static in_port_t mntproto_port = 0; static enum mountmode { ANY, @@ -372,6 +361,13 @@ main(int argc, char *argv[]) softintr = true; } else if (strcmp(opt, "intr") == 0) { softintr = true; + } else if (strcmp(opt, "mountport") == 0) { + num = strtol(val, &p, 10); + if (*p || num <= 0 || num > IPPORT_MAX) + errx(1, "illegal port num -- " + "%s", val); + mntproto_port = num; + pass_flag_to_nmount=0; } if (pass_flag_to_nmount) { build_iovec(&iov, &iovlen, opt, @@ -591,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 && @@ -647,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", @@ -682,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'. @@ -709,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. @@ -912,11 +917,47 @@ tryagain: return (TRYRET_SUCCESS); } + /* + * malloc() and copy the address, so that it can be used for + * nfsargs below. + */ + addrlen = nfs_nb.len; + addr = malloc(addrlen); + if (addr == NULL) + err(1, "malloc"); + bcopy(nfs_nb.buf, addr, addrlen); + /* Send the MOUNTPROC_MNT RPC to get the root filehandle. */ try.tv_sec = 10; try.tv_usec = 0; - clp = clnt_tp_create(hostp, MOUNTPROG, mntvers, nconf_mnt); + if (mntproto_port != 0) { + struct sockaddr *sad; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + sad = (struct sockaddr *)nfs_nb.buf; + switch (sad->sa_family) { + case AF_INET: + sin = (struct sockaddr_in *)nfs_nb.buf; + sin->sin_port = htons(mntproto_port); + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *)nfs_nb.buf; + sin6->sin6_port = htons(mntproto_port); + break; + default: + snprintf(errbuf, sizeof(errbuf), + "Mnt port bad addr family %d\n", sad->sa_family); + return (TRYRET_LOCALERR); + } + clp = clnt_tli_create(RPC_ANYFD, nconf_mnt, &nfs_nb, MOUNTPROG, + mntvers, 0, 0); + } else { + /* Get the Mount protocol port# via rpcbind. */ + clp = clnt_tp_create(hostp, MOUNTPROG, mntvers, nconf_mnt); + } if (clp == NULL) { + free(addr); snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create")); return (returncode(rpc_createerr.cf_stat, @@ -926,10 +967,10 @@ tryagain: nfhret.auth = secflavor; nfhret.vers = mntvers; clntstat = clnt_call(clp, MOUNTPROC_MNT, (xdrproc_t)xdr_dir, spec, - (xdrproc_t)xdr_fh, &nfhret, - try); + (xdrproc_t)xdr_fh, &nfhret, try); auth_destroy(clp->cl_auth); if (clntstat != RPC_SUCCESS) { + free(addr); if (clntstat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { clnt_destroy(clp); trymntmode = V2; @@ -944,6 +985,7 @@ tryagain: clnt_destroy(clp); if (nfhret.stat != 0) { + free(addr); snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt, hostp, spec, strerror(nfhret.stat)); return (TRYRET_REMOTEERR); @@ -953,13 +995,10 @@ tryagain: * Store the filehandle and server address in nfsargsp, making * sure to copy any locally allocated structures. */ - addrlen = nfs_nb.len; - addr = malloc(addrlen); fhsize = nfhret.fhsize; fh = malloc(fhsize); - if (addr == NULL || fh == NULL) + if (fh == NULL) err(1, "malloc"); - bcopy(nfs_nb.buf, addr, addrlen); bcopy(nfhret.nfh, fh, fhsize); build_iovec(iov, iovlen, "addr", addr, addrlen); diff --git a/sbin/mount_nullfs/Makefile b/sbin/mount_nullfs/Makefile index 3063cb6030fe..1844aa14b24f 100644 --- a/sbin/mount_nullfs/Makefile +++ b/sbin/mount_nullfs/Makefile @@ -1,13 +1,6 @@ -# @(#)Makefile 8.3 (Berkeley) 3/27/94 - 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 e3648a2fb2f2..17b1f45f5e42 100644 --- a/sbin/mount_nullfs/mount_nullfs.8 +++ b/sbin/mount_nullfs/mount_nullfs.8 @@ -30,9 +30,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)mount_null.8 8.6 (Berkeley) 5/1/95 -.\" -.Dd June 11, 2023 +.Dd March 24, 2024 .Dt MOUNT_NULLFS 8 .Os .Sh NAME @@ -47,7 +45,7 @@ The .Nm utility creates a -.Xr nullfs 5 +.Xr nullfs 4 layer, duplicating a sub-tree of the file system name space under another part of the global file system namespace. This allows existing files and directories to be accessed @@ -92,15 +90,21 @@ See the .Xr mount 8 man page for possible options and their meanings. Additionally the following option is supported: -.Bl -tag -width indent +.Bl -tag -width nocache .It Cm nocache Disable metadata caching in the null layer. Some lower-layer file systems may force this option. Depending on the access pattern, this may result in increased lock contention. +.It Cm cache +Force enable metadata caching. .El .El .Pp +The +.Dv vfs.nullfs.cache_vnodes +sysctl specifies global default for mount-specific cache/nocache option. +.Pp The null layer has two purposes. First, it serves as a demonstration of layering by providing a layer which does nothing. @@ -253,7 +257,7 @@ is that vnode arguments must be manually mapped. .\" .\" .Sh SEE ALSO -.Xr nullfs 5 , +.Xr nullfs 4 , .Xr mount 8 .Pp UCLA Technical Report CSD-910056, diff --git a/sbin/mount_nullfs/mount_nullfs.c b/sbin/mount_nullfs/mount_nullfs.c index 55d7ac982f70..fc04961e6247 100644 --- a/sbin/mount_nullfs/mount_nullfs.c +++ b/sbin/mount_nullfs/mount_nullfs.c @@ -32,34 +32,19 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1992, 1993, 1994\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)mount_null.c 8.6 (Berkeley) 4/26/95"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/mount.h> #include <sys/stat.h> #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 bf165eb28ffe..6506f6ba3985 100644 --- a/sbin/mount_udf/Makefile +++ b/sbin/mount_udf/Makefile @@ -1,13 +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 6cd7eeace1e0..35e7e8fa75b7 100644 --- a/sbin/mount_unionfs/Makefile +++ b/sbin/mount_unionfs/Makefile @@ -1,13 +1,6 @@ -# @(#)Makefile 8.3 (Berkeley) 3/27/94 - 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.8 b/sbin/mount_unionfs/mount_unionfs.8 index 879198f51a89..5469fef4ad35 100644 --- a/sbin/mount_unionfs/mount_unionfs.8 +++ b/sbin/mount_unionfs/mount_unionfs.8 @@ -28,8 +28,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)mount_union.8 8.6 (Berkeley) 3/27/94 -.\" .Dd October 3, 2016 .Dt MOUNT_UNIONFS 8 .Os diff --git a/sbin/mount_unionfs/mount_unionfs.c b/sbin/mount_unionfs/mount_unionfs.c index 9aafaf13d81f..d207e5581745 100644 --- a/sbin/mount_unionfs/mount_unionfs.c +++ b/sbin/mount_unionfs/mount_unionfs.c @@ -35,27 +35,13 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1992, 1993, 1994\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)mount_union.c 8.5 (Berkeley) 3/27/94"; -#else -static const char rcsid[] = - "$FreeBSD$"; -#endif -#endif /* not lint */ - #include <sys/param.h> #include <sys/mount.h> #include <sys/uio.h> #include <sys/errno.h> #include <err.h> +#include <mntopts.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -64,8 +50,6 @@ static const char rcsid[] = #include <grp.h> #include <pwd.h> -#include "mntopts.h" - static int subdir(const char *p, const char *dir) { diff --git a/sbin/natd/Makefile b/sbin/natd/Makefile index 39e3fd708bc7..b8d54c780bfa 100644 --- a/sbin/natd/Makefile +++ b/sbin/natd/Makefile @@ -1,4 +1,3 @@ - PACKAGE= natd PROG = natd SRCS = natd.c icmp.c diff --git a/sbin/natd/natd.8 b/sbin/natd/natd.8 index b2edece6cce1..8beff2e66b6a 100644 --- a/sbin/natd/natd.8 +++ b/sbin/natd/natd.8 @@ -1,4 +1,4 @@ -.Dd October 5, 2016 +.Dd December 6, 2024 .Dt NATD 8 .Os .Sh NAME @@ -14,6 +14,7 @@ .Op Fl deny_incoming | d .Op Fl use_sockets | s .Op Fl same_ports | m +.Op Fl udp_eim .Op Fl verbose | v .Op Fl dynamic .Op Fl in_port | i Ar port @@ -114,6 +115,26 @@ With this option, protocols such as RPC will have a better chance of working. If it is not possible to maintain the port number, it will be silently changed as per normal. +.It Fl udp_eim +When enabled, UDP packets use endpoint-independent mapping (EIM) from RFC 4787 +("full cone" NAT of RFC 3489). +All packets from the same internal address:port are mapped to the same NAT +address:port, regardless of their destination address:port. +If filtering rules allow, and if +.Em deny_incoming +is disabled, any other external address:port can +also send to the internal address:port through its mapped NAT address:port. +This is more compatible with applications, and can reduce the need for port +forwarding, but less scalable as each NAT address:port can only be +concurrently used by at most one internal address:port. +.Pp +When disabled, UDP packets use endpoint-dependent mapping (EDM) ("symmetric" +NAT). +Each connection from a particular internal address:port to different +external addresses:ports is mapped to a random and unpredictable NAT +address:port. +Two appplications behind EDM NATs can only connect to each other +by port forwarding on the NAT, or tunnelling through an in-between server. .It Fl verbose | v Do not call .Xr daemon 3 diff --git a/sbin/natd/natd.c b/sbin/natd/natd.c index 29c68987adf4..6a62495dd064 100644 --- a/sbin/natd/natd.c +++ b/sbin/natd/natd.c @@ -1138,6 +1138,14 @@ static struct OptionInfo optionTable[] = { "same_ports", "m" }, + { LibAliasOption, + PKT_ALIAS_UDP_EIM, + YesNo, + "[yes|no]", + "UDP traffic uses endpoint-independent mapping (\"full cone\" NAT)", + "udp_eim", + NULL }, + { Verbose, 0, YesNo, diff --git a/sbin/newfs/Makefile b/sbin/newfs/Makefile index 50834666fba4..1138e819055f 100644 --- a/sbin/newfs/Makefile +++ b/sbin/newfs/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.2 (Berkeley) 3/27/94 - .PATH: ${SRCTOP}/sys/geom PACKAGE= ufs diff --git a/sbin/newfs/mkfs.c b/sbin/newfs/mkfs.c index b0d178de984e..3715ef58ad0f 100644 --- a/sbin/newfs/mkfs.c +++ b/sbin/newfs/mkfs.c @@ -38,12 +38,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static char sccsid[] = "@(#)mkfs.c 8.11 (Berkeley) 5/3/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #define _WANT_P_OSREL #include <sys/param.h> #include <sys/disklabel.h> @@ -95,10 +89,6 @@ static struct csum *fscs; #define sblock disk.d_fs #define acg disk.d_cg -union dinode { - struct ufs1_dinode dp1; - struct ufs2_dinode dp2; -}; #define DIP(dp, field) \ ((sblock.fs_magic == FS_UFS1_MAGIC) ? \ (dp)->dp1.field : (dp)->dp2.field) diff --git a/sbin/newfs/newfs.8 b/sbin/newfs/newfs.8 index e1496af814ca..16bca26f7cd8 100644 --- a/sbin/newfs/newfs.8 +++ b/sbin/newfs/newfs.8 @@ -25,9 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)newfs.8 8.6 (Berkeley) 5/3/95 -.\" -.Dd October 21, 2022 +.Dd January 23, 2025 .Dt NEWFS 8 .Os .Sh NAME @@ -100,6 +98,10 @@ The default format is UFS2. For backward compatibility. .It Fl U Enable soft updates on the new file system. +Soft updates are enabled by default for UFS2 format file systems. +Use +.Xr tunefs 8 +to disable soft updates if they are not wanted. .It Fl a Ar maxcontig Specify the maximum number of contiguous blocks that will be laid out before forcing a rotational delay. @@ -348,18 +350,18 @@ than the historical defaults This large fragment size may lead to much wasted space on file systems that contain many small files. .Sh SEE ALSO -.Xr fdformat 8 , +.Xr ffs 4 , .Xr geom 4 , .Xr disktab 5 , .Xr fs 5 , .Xr camcontrol 8 , .Xr dump 8 , .Xr dumpfs 8 , +.Xr fdformat 8 , .Xr fsck 8 , .Xr gjournal 8 , .Xr gpart 8 , .Xr growfs 8 , -.Xr gvinum 8 , .Xr makefs 8 , .Xr mount 8 , .Xr newfs_msdos 8 , diff --git a/sbin/newfs/newfs.c b/sbin/newfs/newfs.c index afb71f9f25b4..418319d1cd3a 100644 --- a/sbin/newfs/newfs.c +++ b/sbin/newfs/newfs.c @@ -38,18 +38,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1983, 1989, 1993, 1994\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)newfs.c 8.13 (Berkeley) 5/1/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> /* * newfs: friendly front end to mkfs */ @@ -117,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); @@ -340,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; @@ -356,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]; @@ -395,6 +385,9 @@ main(int argc, char *argv[]) fprintf(stderr, "because minfree is less than %d%%\n", MINFREE); opt = FS_OPTSPACE; } + /* Use soft updates by default for UFS2 and above */ + if (Oflag > 1) + Uflag = 1; realsectorsize = sectorsize; if (sectorsize != DEV_BSIZE) { /* XXX */ int secperblk = sectorsize / DEV_BSIZE; @@ -442,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/Makefile b/sbin/newfs_msdos/Makefile index f4efc6490675..bd5b3486b6b6 100644 --- a/sbin/newfs_msdos/Makefile +++ b/sbin/newfs_msdos/Makefile @@ -1,4 +1,3 @@ - .include <src.opts.mk> PACKAGE=runtime @@ -10,7 +9,6 @@ SRCS= newfs_msdos.c mkfs_msdos.c .if ${MACHINE_CPUARCH} == "arm" WARNS?= 3 .endif -CSTD= c11 HAS_TESTS= SUBDIR.${MK_TESTS}+= tests diff --git a/sbin/newfs_msdos/mkfs_msdos.c b/sbin/newfs_msdos/mkfs_msdos.c index 13a804c82625..dcc2bb982efc 100644 --- a/sbin/newfs_msdos/mkfs_msdos.c +++ b/sbin/newfs_msdos/mkfs_msdos.c @@ -25,11 +25,6 @@ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #ifdef MAKEFS /* In the makefs case we only want struct disklabel */ @@ -254,7 +249,7 @@ mkfs_msdos(const char *fname, const char *dtype, const struct msdos_options *op) ssize_t n; time_t now; u_int fat, bss, rds, cls, dir, lsn, x, x1, x2; - u_int extra_res, alignment, saved_x, attempts=0; + u_int extra_res, alignment, alignto, saved_x, attempts=0; bool set_res, set_spf, set_spc; int fd, fd1, rv; struct msdos_options o = *op; @@ -417,8 +412,12 @@ mkfs_msdos(const char *fname, const char *dtype, const struct msdos_options *op) } bpb.bpbFATs = o.num_FAT; } - if (o.directory_entries) - bpb.bpbRootDirEnts = o.directory_entries; + if (o.directory_entries) { + bpb.bpbRootDirEnts = roundup(o.directory_entries, + bpb.bpbBytesPerSec / sizeof(struct de)); + if (bpb.bpbBytesPerSec == 0 || o.directory_entries >= MAXU16) + bpb.bpbRootDirEnts = MAXU16; + } if (o.media_descriptor_set) { if (o.media_descriptor < 0xf0) { warnx("illegal media descriptor (%#x)", o.media_descriptor); @@ -569,14 +568,17 @@ mkfs_msdos(const char *fname, const char *dtype, const struct msdos_options *op) x1 += (bpb.bpbBigFATsecs - 1) * bpb.bpbFATs; } if (set_res) { - /* attempt to align root directory */ - alignment = (bpb.bpbResSectors + bpb.bpbBigFATsecs * bpb.bpbFATs) % - bpb.bpbSecPerClust; - if (o.align) - extra_res += bpb.bpbSecPerClust - alignment; + alignto = bpb.bpbSecPerClust; + if (alignto > 1) { + /* align data clusters */ + alignment = (bpb.bpbResSectors + bpb.bpbBigFATsecs * bpb.bpbFATs + rds) % + alignto; + if (alignment != 0) + extra_res += alignto - alignment; + } } attempts++; - } while (o.align && alignment != 0 && attempts < 2); + } while (alignment != 0 && attempts < 2); if (o.align && alignment != 0) warnx("warning: Alignment failed."); diff --git a/sbin/newfs_msdos/newfs_msdos.8 b/sbin/newfs_msdos/newfs_msdos.8 index 278612548dc4..03dfbfced51f 100644 --- a/sbin/newfs_msdos/newfs_msdos.8 +++ b/sbin/newfs_msdos/newfs_msdos.8 @@ -23,7 +23,7 @@ .\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN .\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd June 14, 2018 +.Dd June 6, 2024 .Dt NEWFS_MSDOS 8 .Os .Sh NAME @@ -91,7 +91,11 @@ A suffix s, k, m, g (lower or upper case) appended to the offset specifies that the number is in sectors, kilobytes, megabytes or gigabytes, respectively. .It Fl A -Attempt to cluster align root directory, useful for SD card. +Attempt to cluster align the data area, useful for flash based storage. +This option is enabled by default, unless a number of reserved sectors +is specified using the +.Fl r +option. .It Fl B Ar boot Get bootstrap from file. .It Fl C Ar create-size @@ -134,7 +138,7 @@ File system block size (bytes per cluster). This should resolve to an acceptable number of sectors per cluster (see below). .It Fl c Ar cluster-size -Sectors per cluster. +Sectors per cluster, also called allocation size. Acceptable values are powers of 2 in the range 1 through 128. If the block or cluster size are not specified, the code @@ -167,6 +171,10 @@ is 2. Number of hidden sectors. .It Fl r Ar reserved Number of reserved sectors. +If the +.Fl r +option is not used, the number of reserved sectors is set to a value that +aligns the start of the data area to a multiple of the cluster size. .It Fl s Ar total File system size. .It Fl u Ar track-size @@ -224,6 +232,12 @@ Create a file system, using default parameters, on newfs_msdos /dev/ada0s1 .Ed .Pp +Create a FAT32 filesystem with a 32K allocation size on +.Pa /dev/mmcsd0s1 : +.Bd -literal -offset indent +newfs_msdos -F 32 -A -c 64 /dev/mmcsd0s1 +.Ed +.Pp Create a standard 1.44M file system, with volume label .Ar foo , on @@ -238,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/newfs_msdos/newfs_msdos.c b/sbin/newfs_msdos/newfs_msdos.c index 1ba399fe447e..312a862d9113 100644 --- a/sbin/newfs_msdos/newfs_msdos.c +++ b/sbin/newfs_msdos/newfs_msdos.c @@ -27,11 +27,6 @@ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/stat.h> #include <err.h> diff --git a/sbin/nfsiod/Makefile b/sbin/nfsiod/Makefile index 78e3e77da608..974c7de3362b 100644 --- a/sbin/nfsiod/Makefile +++ b/sbin/nfsiod/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.1 (Berkeley) 6/5/93 - PROG= nfsiod MAN= nfsiod.8 diff --git a/sbin/nfsiod/nfsiod.8 b/sbin/nfsiod/nfsiod.8 index cb9ab471dddb..7abe5a832bbf 100644 --- a/sbin/nfsiod/nfsiod.8 +++ b/sbin/nfsiod/nfsiod.8 @@ -25,8 +25,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" From: @(#)nfsiod.8 8.2 (Berkeley) 2/22/94 -.\" .Dd December 26, 2009 .Dt NFSIOD 8 .Os diff --git a/sbin/nfsiod/nfsiod.c b/sbin/nfsiod/nfsiod.c index 45a0ec0a9741..c34671a8c551 100644 --- a/sbin/nfsiod/nfsiod.c +++ b/sbin/nfsiod/nfsiod.c @@ -32,18 +32,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1989, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif - -#ifndef lint -static char sccsid[] = "@(#)nfsiod.c 8.4 (Berkeley) 5/3/95"; -#endif -#endif -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/syslog.h> #include <sys/wait.h> diff --git a/sbin/nos-tun/Makefile b/sbin/nos-tun/Makefile index 8934e138b49d..bef6878b7f25 100644 --- a/sbin/nos-tun/Makefile +++ b/sbin/nos-tun/Makefile @@ -1,4 +1,3 @@ - PROG= nos-tun MAN= nos-tun.8 WARNS?= 3 diff --git a/sbin/nos-tun/nos-tun.c b/sbin/nos-tun/nos-tun.c index 509f928a2bb8..ca0e33b2509a 100644 --- a/sbin/nos-tun/nos-tun.c +++ b/sbin/nos-tun/nos-tun.c @@ -57,11 +57,6 @@ * */ -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/types.h> #include <sys/ioctl.h> #include <sys/signal.h> @@ -146,7 +141,7 @@ tun_open(char *dev_name, struct sockaddr *ouraddr, char *theiraddr) * when tunN have no addresses, so - log and ignore it. * */ - if (ioctl(s, SIOCDIFADDR, &ifra) < 0) { + if (ioctl(s, SIOCDIFADDR, &ifrq) < 0) { syslog(LOG_ERR,"SIOCDIFADDR - %m"); } @@ -225,10 +220,8 @@ Finish(int signum) /* * Delete addresses for interface */ - bzero(&ifra.ifra_addr, sizeof(ifra.ifra_addr)); - bzero(&ifra.ifra_broadaddr, sizeof(ifra.ifra_addr)); - bzero(&ifra.ifra_mask, sizeof(ifra.ifra_addr)); - if (ioctl(s, SIOCDIFADDR, &ifra) < 0) { + bzero(&ifrq.ifr_addr, sizeof(ifrq.ifr_addr)); + if (ioctl(s, SIOCDIFADDR, &ifrq) < 0) { syslog(LOG_ERR,"can't delete interface's addresses - %m"); } closing_fds: diff --git a/sbin/nvmecontrol/Makefile b/sbin/nvmecontrol/Makefile index facb5f09835d..cf9c23548e36 100644 --- a/sbin/nvmecontrol/Makefile +++ b/sbin/nvmecontrol/Makefile @@ -1,17 +1,36 @@ - .include <src.opts.mk> PACKAGE=nvme-tools PROG= nvmecontrol -SRCS= comnd.c nvmecontrol.c -SRCS+= devlist.c firmware.c format.c identify.c logpage.c ns.c nsid.c -SRCS+= perftest.c power.c reset.c resv.c sanitize.c +SRCS+= comnd.c +SRCS+= connect.c +SRCS+= devlist.c +SRCS+= disconnect.c +SRCS+= discover.c +SRCS+= fabrics.c +SRCS+= firmware.c +SRCS+= format.c +SRCS+= identify.c +SRCS+= identify_ext.c +SRCS+= logpage.c +SRCS+= nc_util.c +SRCS+= ns.c +SRCS+= nsid.c +SRCS+= nvme_util.c +SRCS+= nvmecontrol.c SRCS+= passthru.c -SRCS+= identify_ext.c nvme_util.c nc_util.c +SRCS+= perftest.c +SRCS+= power.c +SRCS+= reconnect.c +SRCS+= reset.c +SRCS+= resv.c +SRCS+= sanitize.c SRCS+= selftest.c +SRCS+= telemetry.c +CFLAGS+= -I${SRCTOP}/lib/libnvmf MAN= nvmecontrol.8 LDFLAGS+= -rdynamic -LIBADD+= util +LIBADD+= nvmf sbuf util SUBDIR= modules HAS_TESTS= SUBDIR.${MK_TESTS}+= tests diff --git a/sbin/nvmecontrol/Makefile.depend b/sbin/nvmecontrol/Makefile.depend index 678747db6f2c..4eaed14a78cc 100644 --- a/sbin/nvmecontrol/Makefile.depend +++ b/sbin/nvmecontrol/Makefile.depend @@ -6,6 +6,7 @@ DIRDEPS = \ lib/${CSU_DIR} \ lib/libc \ lib/libcompiler_rt \ + lib/libnvmf \ lib/libutil \ diff --git a/sbin/nvmecontrol/comnd.c b/sbin/nvmecontrol/comnd.c index de979af71d90..112857f58943 100644 --- a/sbin/nvmecontrol/comnd.c +++ b/sbin/nvmecontrol/comnd.c @@ -25,7 +25,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioccom.h> diff --git a/sbin/nvmecontrol/connect.c b/sbin/nvmecontrol/connect.c new file mode 100644 index 000000000000..3d6d12bf2c48 --- /dev/null +++ b/sbin/nvmecontrol/connect.c @@ -0,0 +1,313 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023-2024 Chelsio Communications, Inc. + * Written by: John Baldwin <jhb@FreeBSD.org> + */ + +#include <sys/socket.h> +#include <err.h> +#include <libnvmf.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +#include "comnd.h" +#include "fabrics.h" + +/* + * Settings that are currently hardcoded but could be exposed to the + * user via additional command line options: + * + * - ADMIN queue entries + * - MaxR2T + */ + +static struct options { + const char *transport; + const char *address; + const char *cntlid; + 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; + bool flow_control; + bool header_digests; +} opt = { + .transport = "tcp", + .address = NULL, + .cntlid = "dynamic", + .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, + .flow_control = false, + .header_digests = false, +}; + +static void +tcp_association_params(struct nvmf_association_params *params) +{ + params->tcp.pda = 0; + params->tcp.header_digests = opt.header_digests; + params->tcp.data_digests = opt.data_digests; + /* XXX */ + params->tcp.maxr2t = 1; +} + +static int +connect_nvm_controller(enum nvmf_trtype trtype, int adrfam, const char *address, + const char *port, uint16_t cntlid, const char *subnqn, + const struct nvme_discovery_log_entry *dle) +{ + struct nvme_controller_data cdata; + struct nvme_discovery_log_entry dle_thunk; + struct nvmf_association_params aparams; + struct nvmf_qpair *admin, **io; + const char *hostnqn; + int error; + + memset(&aparams, 0, sizeof(aparams)); + aparams.sq_flow_control = opt.flow_control; + switch (trtype) { + case NVMF_TRTYPE_TCP: + tcp_association_params(&aparams); + break; + default: + warnx("Unsupported transport %s", nvmf_transport_type(trtype)); + return (EX_UNAVAILABLE); + } + + hostnqn = opt.hostnqn; + if (hostnqn == NULL) + hostnqn = nvmf_default_hostnqn(); + io = calloc(opt.num_io_queues, sizeof(*io)); + error = connect_nvm_queues(&aparams, trtype, adrfam, address, port, + cntlid, subnqn, hostnqn, opt.kato * 1000, &admin, io, + opt.num_io_queues, opt.queue_size, &cdata); + if (error != 0) { + free(io); + return (error); + } + + if (dle == NULL) { + error = nvmf_init_dle_from_admin_qp(admin, &cdata, &dle_thunk); + if (error != 0) { + warnc(error, "Failed to generate handoff parameters"); + disconnect_nvm_queues(admin, io, opt.num_io_queues); + free(io); + return (EX_IOERR); + } + dle = &dle_thunk; + } + + error = nvmf_handoff_host(dle, hostnqn, admin, opt.num_io_queues, io, + &cdata, opt.reconnect_delay, opt.controller_loss_timeout); + if (error != 0) { + warnc(error, "Failed to handoff queues to kernel"); + free(io); + return (EX_IOERR); + } + free(io); + return (0); +} + +static void +connect_discovery_entry(struct nvme_discovery_log_entry *entry) +{ + int adrfam; + + switch (entry->trtype) { + case NVMF_TRTYPE_TCP: + switch (entry->adrfam) { + case NVMF_ADRFAM_IPV4: + adrfam = AF_INET; + break; + case NVMF_ADRFAM_IPV6: + adrfam = AF_INET6; + break; + default: + warnx("Skipping unsupported address family for %s", + entry->subnqn); + return; + } + switch (entry->tsas.tcp.sectype) { + case NVME_TCP_SECURITY_NONE: + break; + default: + warnx("Skipping unsupported TCP security type for %s", + entry->subnqn); + return; + } + break; + default: + warnx("Skipping unsupported transport %s for %s", + nvmf_transport_type(entry->trtype), entry->subnqn); + return; + } + + /* + * XXX: Track portids and avoid duplicate connections for a + * given (subnqn,portid)? + */ + + /* XXX: Should this make use of entry->aqsz in some way? */ + connect_nvm_controller(entry->trtype, adrfam, entry->traddr, + entry->trsvcid, entry->cntlid, entry->subnqn, entry); +} + +static void +connect_discovery_log_page(struct nvmf_qpair *qp) +{ + struct nvme_discovery_log *log; + int error; + + error = nvmf_host_fetch_discovery_log_page(qp, &log); + if (error != 0) + errc(EX_IOERR, error, "Failed to fetch discovery log page"); + + for (u_int i = 0; i < log->numrec; i++) + connect_discovery_entry(&log->entries[i]); + free(log); +} + +static void +discover_controllers(enum nvmf_trtype trtype, const char *address, + const char *port) +{ + struct nvmf_qpair *qp; + + qp = connect_discovery_adminq(trtype, address, port, opt.hostnqn); + + connect_discovery_log_page(qp); + + nvmf_free_qpair(qp); +} + +static void +connect_fn(const struct cmd *f, int argc, char *argv[]) +{ + enum nvmf_trtype trtype; + const char *address, *port; + char *tofree; + u_long cntlid; + int error; + + if (arg_parse(argc, argv, f)) + return; + + if (opt.num_io_queues <= 0) + errx(EX_USAGE, "Invalid number of I/O queues"); + + if (strcasecmp(opt.transport, "tcp") == 0) { + trtype = NVMF_TRTYPE_TCP; + } else + errx(EX_USAGE, "Unsupported or invalid transport"); + + nvmf_parse_address(opt.address, &address, &port, &tofree); + if (port == NULL) + errx(EX_USAGE, "Explicit port required"); + + cntlid = nvmf_parse_cntlid(opt.cntlid); + + error = connect_nvm_controller(trtype, AF_UNSPEC, address, port, cntlid, + opt.subnqn, NULL); + if (error != 0) + exit(error); + + free(tofree); +} + +static void +connect_all_fn(const struct cmd *f, int argc, char *argv[]) +{ + enum nvmf_trtype trtype; + const char *address, *port; + char *tofree; + + if (arg_parse(argc, argv, f)) + return; + + if (opt.num_io_queues <= 0) + errx(EX_USAGE, "Invalid number of I/O queues"); + + if (strcasecmp(opt.transport, "tcp") == 0) { + trtype = NVMF_TRTYPE_TCP; + } else + errx(EX_USAGE, "Unsupported or invalid transport"); + + nvmf_parse_address(opt.address, &address, &port, &tofree); + discover_controllers(trtype, address, port); + + free(tofree); +} + +static const struct opts connect_opts[] = { +#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } + OPT("transport", 't', arg_string, opt, transport, + "Transport type"), + OPT("cntlid", 'c', arg_string, opt, cntlid, + "Controller ID"), + OPT("nr-io-queues", 'i', arg_uint16, opt, num_io_queues, + "Number of I/O queues"), + OPT("queue-size", 'Q', arg_uint16, opt, queue_size, + "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, + "Request SQ flow control"), + OPT("hdr_digests", 'g', arg_none, opt, header_digests, + "Enable TCP PDU header digests"), + OPT("data_digests", 'G', arg_none, opt, data_digests, + "Enable TCP PDU data digests"), + { NULL, 0, arg_none, NULL, NULL } +}; +#undef OPT + +static const struct args connect_args[] = { + { arg_string, &opt.address, "address" }, + { arg_string, &opt.subnqn, "SubNQN" }, + { arg_none, NULL, NULL }, +}; + +static const struct args connect_all_args[] = { + { arg_string, &opt.address, "address" }, + { arg_none, NULL, NULL }, +}; + +static struct cmd connect_cmd = { + .name = "connect", + .fn = connect_fn, + .descr = "Connect to a fabrics controller", + .ctx_size = sizeof(opt), + .opts = connect_opts, + .args = connect_args, +}; + +static struct cmd connect_all_cmd = { + .name = "connect-all", + .fn = connect_all_fn, + .descr = "Discover and connect to fabrics controllers", + .ctx_size = sizeof(opt), + .opts = connect_opts, + .args = connect_all_args, +}; + +CMD_COMMAND(connect_cmd); +CMD_COMMAND(connect_all_cmd); diff --git a/sbin/nvmecontrol/devlist.c b/sbin/nvmecontrol/devlist.c index 7e3dfe60fff4..7bf6bc6f097c 100644 --- a/sbin/nvmecontrol/devlist.c +++ b/sbin/nvmecontrol/devlist.c @@ -26,13 +26,15 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> +#include <sys/nv.h> +#include <sys/time.h> #include <err.h> #include <errno.h> #include <fcntl.h> #include <stdbool.h> +#include <libnvmf.h> #include <libutil.h> #include <paths.h> #include <stddef.h> @@ -40,6 +42,7 @@ #include <stdlib.h> #include <string.h> #include <sysexits.h> +#include <time.h> #include <unistd.h> #include "nvmecontrol.h" @@ -83,25 +86,151 @@ ns_get_sector_size(struct nvme_namespace_data *nsdata) { uint8_t flbas_fmt, lbads; - flbas_fmt = (nsdata->flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) & - NVME_NS_DATA_FLBAS_FORMAT_MASK; - lbads = (nsdata->lbaf[flbas_fmt] >> NVME_NS_DATA_LBAF_LBADS_SHIFT) & - NVME_NS_DATA_LBAF_LBADS_MASK; + flbas_fmt = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, nsdata->flbas); + lbads = NVMEV(NVME_NS_DATA_LBAF_LBADS, nsdata->lbaf[flbas_fmt]); return (1 << lbads); } static void -devlist(const struct cmd *f, int argc, char *argv[]) +scan_namespace(int fd, int ctrlr, uint32_t nsid) { - struct nvme_controller_data cdata; - struct nvme_namespace_data nsdata; + struct nvme_namespace_data nsdata; char name[64]; - uint8_t mn[64]; uint8_t buf[7]; - uint32_t i; uint64_t size; - int ctrlr, fd, found, ret; + + if (read_namespace_data(fd, nsid, &nsdata) != 0) + return; + if (nsdata.nsze == 0) + return; + snprintf(name, sizeof(name), "%s%d%s%d", NVME_CTRLR_PREFIX, ctrlr, + NVME_NS_PREFIX, nsid); + size = nsdata.nsze * (uint64_t)ns_get_sector_size(&nsdata); + if (opt.human) { + humanize_number(buf, sizeof(buf), size, "B", + HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); + printf(" %10s (%s)\n", name, buf); + } else { + printf(" %10s (%juMB)\n", name, (uintmax_t)size / 1024 / 1024); + } +} + +static bool +print_controller_info(const char *name, int fd) +{ + static struct timespec now; + struct nvme_controller_data cdata; + struct timespec last_disconnect, delta; + uint8_t mn[64]; + nvlist_t *nvl; + const nvlist_t *nvl_ts; + bool connected; + + /* + * If the controller doesn't support connection status, assume + * it is connected. + */ + if (nvmf_connection_status(fd, &nvl) != 0) { + connected = true; + nvl = NULL; + } else { + connected = nvlist_get_bool(nvl, "connected"); + } + + if (connected) { + if (read_controller_data(fd, &cdata) != 0) { + nvlist_destroy(nvl); + return (false); + } + } else { + if (ioctl(fd, NVME_GET_CONTROLLER_DATA, &cdata) == -1) { + nvlist_destroy(nvl); + return (false); + } + } + + nvme_strvis(mn, cdata.mn, sizeof(mn), NVME_MODEL_NUMBER_LENGTH); + printf("%6s: %s", name, mn); + if (connected) { + const struct nvme_discovery_log_entry *dle; + size_t len; + + nvlist_destroy(nvl); + if (nvmf_reconnect_params(fd, &nvl) == 0) { + dle = nvlist_get_binary(nvl, "dle", &len); + if (len == sizeof(*dle)) { + printf(" (connected via %s %.*s:%.*s)", + nvmf_transport_type(dle->trtype), + (int)sizeof(dle->traddr), dle->traddr, + (int)sizeof(dle->trsvcid), dle->trsvcid); + } + } else { + nvl = NULL; + } + } else { + if (now.tv_sec == 0) + clock_gettime(CLOCK_REALTIME, &now); + + nvl_ts = nvlist_get_nvlist(nvl, "last_disconnect"); + last_disconnect.tv_sec = nvlist_get_number(nvl_ts, "tv_sec"); + last_disconnect.tv_nsec = nvlist_get_number(nvl_ts, "tv_nsec"); + timespecsub(&now, &last_disconnect, &delta); + printf(" (disconnected for %ju seconds)", + (uintmax_t)delta.tv_sec); + } + printf("\n"); + nvlist_destroy(nvl); + return (connected); +} + +static bool +scan_controller(int ctrlr) +{ + struct nvme_ns_list nslist; + char name[64]; + uint32_t nsid; + int fd, ret; + + snprintf(name, sizeof(name), "%s%d", NVME_CTRLR_PREFIX, ctrlr); + + ret = open_dev(name, &fd, 0, 0); + + if (ret == EACCES) { + warnx("could not open "_PATH_DEV"%s\n", name); + return (false); + } else if (ret != 0) + return (false); + + if (!print_controller_info(name, fd)) { + close(fd); + return (true); + } + + nsid = 0; + for (;;) { + if (read_active_namespaces(fd, nsid, &nslist) != 0) + break; + for (u_int i = 0; i < nitems(nslist.ns); i++) { + nsid = nslist.ns[i]; + if (nsid == 0) { + break; + } + + scan_namespace(fd, ctrlr, nsid); + } + if (nsid == 0 || nsid >= NVME_GLOBAL_NAMESPACE_TAG - 1) + break; + } + + close(fd); + return (true); +} + +static void +devlist(const struct cmd *f, int argc, char *argv[]) +{ + int ctrlr, found; if (arg_parse(argc, argv, f)) return; @@ -111,41 +240,8 @@ devlist(const struct cmd *f, int argc, char *argv[]) while (ctrlr < NVME_MAX_UNIT) { ctrlr++; - sprintf(name, "%s%d", NVME_CTRLR_PREFIX, ctrlr); - - ret = open_dev(name, &fd, 0, 0); - - if (ret == EACCES) { - warnx("could not open "_PATH_DEV"%s\n", name); - continue; - } else if (ret != 0) - continue; - - found++; - if (read_controller_data(fd, &cdata)) - continue; - nvme_strvis(mn, cdata.mn, sizeof(mn), NVME_MODEL_NUMBER_LENGTH); - printf("%6s: %s\n", name, mn); - - for (i = 0; i < cdata.nn; i++) { - if (read_namespace_data(fd, i + 1, &nsdata)) - continue; - if (nsdata.nsze == 0) - continue; - sprintf(name, "%s%d%s%d", NVME_CTRLR_PREFIX, ctrlr, - NVME_NS_PREFIX, i + 1); - size = nsdata.nsze * (uint64_t)ns_get_sector_size(&nsdata); - if (opt.human) { - humanize_number(buf, sizeof(buf), size, "B", - HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); - printf(" %10s (%s)\n", name, buf); - - } else { - printf(" %10s (%juMB)\n", name, (uintmax_t)size / 1024 / 1024); - } - } - - close(fd); + if (scan_controller(ctrlr)) + found++; } if (found == 0) { diff --git a/sbin/nvmecontrol/disconnect.c b/sbin/nvmecontrol/disconnect.c new file mode 100644 index 000000000000..b1b6af6271e8 --- /dev/null +++ b/sbin/nvmecontrol/disconnect.c @@ -0,0 +1,82 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023-2024 Chelsio Communications, Inc. + * Written by: John Baldwin <jhb@FreeBSD.org> + */ + +#include <err.h> +#include <libnvmf.h> +#include <stdlib.h> +#include <sysexits.h> +#include <unistd.h> + +#include "nvmecontrol.h" + +static struct options { + const char *dev; +} opt = { + .dev = NULL +}; + +static const struct args args[] = { + { arg_string, &opt.dev, "controller-id|namespace-id|SubNQN" }, + { arg_none, NULL, NULL }, +}; + +static void +disconnect(const struct cmd *f, int argc, char *argv[]) +{ + int error, fd; + char *path; + + if (arg_parse(argc, argv, f)) + return; + if (nvmf_nqn_valid(opt.dev)) { + error = nvmf_disconnect_host(opt.dev); + if (error != 0) + errc(EX_IOERR, error, "failed to disconnect from %s", + opt.dev); + } else { + open_dev(opt.dev, &fd, 1, 1); + get_nsid(fd, &path, NULL); + close(fd); + + error = nvmf_disconnect_host(path); + if (error != 0) + errc(EX_IOERR, error, "failed to disconnect from %s", + path); + } + + exit(0); +} + +static void +disconnect_all(const struct cmd *f __unused, int argc __unused, + char *argv[] __unused) +{ + int error; + + error = nvmf_disconnect_all(); + if (error != 0) + errc(EX_IOERR, error, + "failed to disconnect from remote controllers"); + + exit(0); +} + +static struct cmd disconnect_cmd = { + .name = "disconnect", + .fn = disconnect, + .descr = "Disconnect from a fabrics controller", + .args = args, +}; + +static struct cmd disconnect_all_cmd = { + .name = "disconnect-all", + .fn = disconnect_all, + .descr = "Disconnect from all fabrics controllers", +}; + +CMD_COMMAND(disconnect_cmd); +CMD_COMMAND(disconnect_all_cmd); diff --git a/sbin/nvmecontrol/discover.c b/sbin/nvmecontrol/discover.c new file mode 100644 index 000000000000..c782ebeb7452 --- /dev/null +++ b/sbin/nvmecontrol/discover.c @@ -0,0 +1,300 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023-2024 Chelsio Communications, Inc. + * Written by: John Baldwin <jhb@FreeBSD.org> + */ + +#include <err.h> +#include <libnvmf.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> + +#include "comnd.h" +#include "fabrics.h" +#include "nvmecontrol_ext.h" + +static struct options { + const char *transport; + const char *address; + const char *hostnqn; + bool verbose; +} opt = { + .transport = "tcp", + .address = NULL, + .hostnqn = NULL, + .verbose = false, +}; + +static void +identify_controller(struct nvmf_qpair *qp) +{ + struct nvme_controller_data cdata; + int error; + + error = nvmf_host_identify_controller(qp, &cdata); + if (error != 0) + errc(EX_IOERR, error, "Failed to fetch controller data"); + nvme_print_controller(&cdata); +} + +static const char * +nvmf_address_family(uint8_t adrfam) +{ + static char buf[8]; + + switch (adrfam) { + case NVMF_ADRFAM_IPV4: + return ("AF_INET"); + case NVMF_ADRFAM_IPV6: + return ("AF_INET6"); + case NVMF_ADRFAM_IB: + return ("InfiniBand"); + case NVMF_ADRFAM_FC: + return ("Fibre Channel"); + case NVMF_ADRFAM_INTRA_HOST: + return ("Intra-host"); + default: + snprintf(buf, sizeof(buf), "0x%02x\n", adrfam); + return (buf); + } +} + +static const char * +nvmf_subsystem_type(uint8_t subtype) +{ + static char buf[8]; + + switch (subtype) { + case NVMF_SUBTYPE_DISCOVERY: + return ("Discovery"); + case NVMF_SUBTYPE_NVME: + return ("NVMe"); + default: + snprintf(buf, sizeof(buf), "0x%02x\n", subtype); + return (buf); + } +} + +static const char * +nvmf_secure_channel(uint8_t treq) +{ + switch (treq & 0x03) { + case NVMF_TREQ_SECURE_CHANNEL_NOT_SPECIFIED: + return ("Not specified"); + case NVMF_TREQ_SECURE_CHANNEL_REQUIRED: + return ("Required"); + case NVMF_TREQ_SECURE_CHANNEL_NOT_REQUIRED: + return ("Not required"); + default: + return ("0x03"); + } +} + +static const char * +nvmf_controller_id(uint16_t cntlid) +{ + static char buf[8]; + + switch (cntlid) { + case NVMF_CNTLID_DYNAMIC: + return ("Dynamic"); + case NVMF_CNTLID_STATIC_ANY: + return ("Static"); + default: + snprintf(buf, sizeof(buf), "%u", cntlid); + return (buf); + } +} + +static const char * +nvmf_rdma_service_type(uint8_t qptype) +{ + static char buf[8]; + + switch (qptype) { + case NVMF_RDMA_QPTYPE_RELIABLE_CONNECTED: + return ("Reliable connected"); + case NVMF_RDMA_QPTYPE_RELIABLE_DATAGRAM: + return ("Reliable datagram"); + default: + snprintf(buf, sizeof(buf), "0x%02x\n", qptype); + return (buf); + } +} + +static const char * +nvmf_rdma_provider_type(uint8_t prtype) +{ + static char buf[8]; + + switch (prtype) { + case NVMF_RDMA_PRTYPE_NONE: + return ("None"); + case NVMF_RDMA_PRTYPE_IB: + return ("InfiniBand"); + case NVMF_RDMA_PRTYPE_ROCE: + return ("RoCE (v1)"); + case NVMF_RDMA_PRTYPE_ROCE2: + return ("RoCE (v2)"); + case NVMF_RDMA_PRTYPE_IWARP: + return ("iWARP"); + default: + snprintf(buf, sizeof(buf), "0x%02x\n", prtype); + return (buf); + } +} + +static const char * +nvmf_rdma_cms(uint8_t cms) +{ + static char buf[8]; + + switch (cms) { + case NVMF_RDMA_CMS_RDMA_CM: + return ("RDMA_IP_CM"); + default: + snprintf(buf, sizeof(buf), "0x%02x\n", cms); + return (buf); + } +} + +static const char * +nvmf_tcp_security_type(uint8_t sectype) +{ + static char buf[8]; + + switch (sectype) { + case NVME_TCP_SECURITY_NONE: + return ("None"); + case NVME_TCP_SECURITY_TLS_1_2: + return ("TLS 1.2"); + case NVME_TCP_SECURITY_TLS_1_3: + return ("TLS 1.3"); + default: + snprintf(buf, sizeof(buf), "0x%02x\n", sectype); + return (buf); + } +} + +static void +print_discovery_entry(u_int i, struct nvme_discovery_log_entry *entry) +{ + printf("Entry %02d\n", i + 1); + printf("========\n"); + printf(" Transport type: %s\n", + nvmf_transport_type(entry->trtype)); + printf(" Address family: %s\n", + nvmf_address_family(entry->adrfam)); + printf(" Subsystem type: %s\n", + nvmf_subsystem_type(entry->subtype)); + printf(" SQ flow control: %s\n", + (entry->treq & (1 << 2)) == 0 ? "required" : "optional"); + printf(" Secure Channel: %s\n", nvmf_secure_channel(entry->treq)); + printf(" Port ID: %u\n", entry->portid); + printf(" Controller ID: %s\n", + nvmf_controller_id(entry->cntlid)); + printf(" Max Admin SQ Size: %u\n", entry->aqsz); + printf(" Sub NQN: %s\n", entry->subnqn); + printf(" Transport address: %s\n", entry->traddr); + printf(" Service identifier: %s\n", entry->trsvcid); + switch (entry->trtype) { + case NVMF_TRTYPE_RDMA: + printf(" RDMA Service Type: %s\n", + nvmf_rdma_service_type(entry->tsas.rdma.rdma_qptype)); + printf(" RDMA Provider Type: %s\n", + nvmf_rdma_provider_type(entry->tsas.rdma.rdma_prtype)); + printf(" RDMA CMS: %s\n", + nvmf_rdma_cms(entry->tsas.rdma.rdma_cms)); + printf(" Partition key: %u\n", + entry->tsas.rdma.rdma_pkey); + break; + case NVMF_TRTYPE_TCP: + printf(" Security Type: %s\n", + nvmf_tcp_security_type(entry->tsas.tcp.sectype)); + break; + } +} + +static void +dump_discovery_log_page(struct nvmf_qpair *qp) +{ + struct nvme_discovery_log *log; + int error; + + error = nvmf_host_fetch_discovery_log_page(qp, &log); + if (error != 0) + errc(EX_IOERR, error, "Failed to fetch discovery log page"); + + printf("Discovery\n"); + printf("=========\n"); + if (log->numrec == 0) { + printf("No entries found\n"); + } else { + for (u_int i = 0; i < log->numrec; i++) + print_discovery_entry(i, &log->entries[i]); + } + free(log); +} + +static void +discover(const struct cmd *f, int argc, char *argv[]) +{ + enum nvmf_trtype trtype; + struct nvmf_qpair *qp; + const char *address, *port; + char *tofree; + + if (arg_parse(argc, argv, f)) + return; + + if (strcasecmp(opt.transport, "tcp") == 0) { + trtype = NVMF_TRTYPE_TCP; + } else + errx(EX_USAGE, "Unsupported or invalid transport"); + + nvmf_parse_address(opt.address, &address, &port, &tofree); + qp = connect_discovery_adminq(trtype, address, port, opt.hostnqn); + free(tofree); + + /* Use Identify to fetch controller data */ + if (opt.verbose) { + identify_controller(qp); + printf("\n"); + } + + /* Fetch Log pages */ + dump_discovery_log_page(qp); + + nvmf_free_qpair(qp); +} + +static const struct opts discover_opts[] = { +#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } + OPT("transport", 't', arg_string, opt, transport, + "Transport type"), + OPT("hostnqn", 'q', arg_string, opt, hostnqn, + "Host NQN"), + OPT("verbose", 'v', arg_none, opt, verbose, + "Display the discovery controller's controller data"), + { NULL, 0, arg_none, NULL, NULL } +}; +#undef OPT + +static const struct args discover_args[] = { + { arg_string, &opt.address, "address" }, + { arg_none, NULL, NULL }, +}; + +static struct cmd discover_cmd = { + .name = "discover", + .fn = discover, + .descr = "List discovery log pages from a fabrics controller", + .ctx_size = sizeof(opt), + .opts = discover_opts, + .args = discover_args, +}; + +CMD_COMMAND(discover_cmd); diff --git a/sbin/nvmecontrol/fabrics.c b/sbin/nvmecontrol/fabrics.c new file mode 100644 index 000000000000..5fdbf68127d4 --- /dev/null +++ b/sbin/nvmecontrol/fabrics.c @@ -0,0 +1,559 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023-2024 Chelsio Communications, Inc. + * Written by: John Baldwin <jhb@FreeBSD.org> + */ + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <err.h> +#include <libnvmf.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +#include "fabrics.h" + +/* + * Subroutines shared by several Fabrics commands. + */ +static char nqn[NVMF_NQN_MAX_LEN]; +static uint8_t hostid[16]; +static bool hostid_initted = false; + +static bool +init_hostid(void) +{ + int error; + + if (hostid_initted) + return (true); + + error = nvmf_hostid_from_hostuuid(hostid); + if (error != 0) { + warnc(error, "Failed to generate hostid"); + return (false); + } + error = nvmf_nqn_from_hostuuid(nqn); + if (error != 0) { + warnc(error, "Failed to generate host NQN"); + return (false); + } + + hostid_initted = true; + return (true); +} + +const char * +nvmf_default_hostnqn(void) +{ + if (!init_hostid()) + exit(EX_IOERR); + return (nqn); +} + +void +nvmf_parse_address(const char *in_address, const char **address, + const char **port, char **tofree) +{ + char *cp; + + /* + * Accepts the following address formats: + * + * [IPv6 address]:port + * IPv4 address:port + * hostname:port + * [IPv6 address] + * IPv6 address + * IPv4 address + * hostname + */ + if (in_address[0] == '[') { + /* IPv6 address in square brackets. */ + cp = strchr(in_address + 1, ']'); + if (cp == NULL || cp == in_address + 1) + errx(EX_USAGE, "Invalid address %s", in_address); + *tofree = strndup(in_address + 1, cp - (in_address + 1)); + *address = *tofree; + + /* Skip over ']' */ + cp++; + switch (*cp) { + case '\0': + *port = NULL; + return; + case ':': + if (cp[1] != '\0') { + *port = cp + 1; + return; + } + /* FALLTHROUGH */ + default: + errx(EX_USAGE, "Invalid address %s", in_address); + } + } + + /* Look for the first colon. */ + cp = strchr(in_address, ':'); + if (cp == NULL) { + *address = in_address; + *port = NULL; + *tofree = NULL; + return; + } + + /* If there is another colon, assume this is an IPv6 address. */ + if (strchr(cp + 1, ':') != NULL) { + *address = in_address; + *port = NULL; + *tofree = NULL; + return; + } + + /* Both strings on either side of the colon must be non-empty. */ + if (cp == in_address || cp[1] == '\0') + errx(EX_USAGE, "Invalid address %s", in_address); + + *tofree = strndup(in_address, cp - in_address); + *address = *tofree; + + /* Skip over ':' */ + *port = cp + 1; +} + +uint16_t +nvmf_parse_cntlid(const char *cntlid) +{ + u_long value; + + if (strcasecmp(cntlid, "dynamic") == 0) + return (NVMF_CNTLID_DYNAMIC); + else if (strcasecmp(cntlid, "static") == 0) + return (NVMF_CNTLID_STATIC_ANY); + else { + value = strtoul(cntlid, NULL, 0); + + if (value > NVMF_CNTLID_STATIC_MAX) + errx(EX_USAGE, "Invalid controller ID"); + + return (value); + } +} + +static bool +tcp_qpair_params(struct nvmf_qpair_params *params, int adrfam, + const char *address, const char *port, struct addrinfo **aip, + struct addrinfo **listp) +{ + struct addrinfo hints, *ai, *list; + int error, s; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = adrfam; + hints.ai_protocol = IPPROTO_TCP; + error = getaddrinfo(address, port, &hints, &list); + if (error != 0) { + warnx("%s", gai_strerror(error)); + return (false); + } + + for (ai = list; ai != NULL; ai = ai->ai_next) { + s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (s == -1) + continue; + + if (connect(s, ai->ai_addr, ai->ai_addrlen) != 0) { + close(s); + continue; + } + + params->tcp.fd = s; + if (listp != NULL) { + *aip = ai; + *listp = list; + } else + freeaddrinfo(list); + return (true); + } + warn("Failed to connect to controller at %s:%s", address, port); + freeaddrinfo(list); + return (false); +} + +static bool +tcp_qpair_params_ai(struct nvmf_qpair_params *params, struct addrinfo *ai) +{ + int s; + + s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (s == -1) + return (false); + + if (connect(s, ai->ai_addr, ai->ai_addrlen) != 0) { + close(s); + return (false); + } + + params->tcp.fd = s; + return (true); +} + +static void +tcp_discovery_association_params(struct nvmf_association_params *params) +{ + params->tcp.pda = 0; + params->tcp.header_digests = false; + params->tcp.data_digests = false; + params->tcp.maxr2t = 1; +} + +struct nvmf_qpair * +connect_discovery_adminq(enum nvmf_trtype trtype, const char *address, + const char *port, const char *hostnqn) +{ + struct nvmf_association_params aparams; + struct nvmf_qpair_params qparams; + struct nvmf_association *na; + struct nvmf_qpair *qp; + uint64_t cap, cc, csts; + int error, timo; + + memset(&aparams, 0, sizeof(aparams)); + aparams.sq_flow_control = false; + switch (trtype) { + case NVMF_TRTYPE_TCP: + /* 7.4.9.3 Default port for discovery */ + if (port == NULL) + port = "8009"; + tcp_discovery_association_params(&aparams); + break; + default: + errx(EX_UNAVAILABLE, "Unsupported transport %s", + nvmf_transport_type(trtype)); + } + + if (!init_hostid()) + exit(EX_IOERR); + if (hostnqn != NULL) { + if (!nvmf_nqn_valid(hostnqn)) + errx(EX_USAGE, "Invalid HostNQN %s", hostnqn); + } else + hostnqn = nqn; + + na = nvmf_allocate_association(trtype, false, &aparams); + if (na == NULL) + err(EX_IOERR, "Failed to create discovery association"); + memset(&qparams, 0, sizeof(qparams)); + qparams.admin = true; + if (!tcp_qpair_params(&qparams, AF_UNSPEC, address, port, NULL, NULL)) + exit(EX_NOHOST); + qp = nvmf_connect(na, &qparams, 0, NVME_MIN_ADMIN_ENTRIES, hostid, + NVMF_CNTLID_DYNAMIC, NVMF_DISCOVERY_NQN, hostnqn, 0); + if (qp == NULL) + errx(EX_IOERR, "Failed to connect to discovery controller: %s", + nvmf_association_error(na)); + nvmf_free_association(na); + + /* Fetch Controller Capabilities Property */ + error = nvmf_read_property(qp, NVMF_PROP_CAP, 8, &cap); + if (error != 0) + errc(EX_IOERR, error, "Failed to fetch CAP"); + + /* Set Controller Configuration Property (CC.EN=1) */ + error = nvmf_read_property(qp, NVMF_PROP_CC, 4, &cc); + if (error != 0) + errc(EX_IOERR, error, "Failed to fetch CC"); + + /* Clear known fields preserving any reserved fields. */ + cc &= ~(NVMEM(NVME_CC_REG_SHN) | NVMEM(NVME_CC_REG_AMS) | + NVMEM(NVME_CC_REG_MPS) | NVMEM(NVME_CC_REG_CSS)); + + /* Leave AMS, MPS, and CSS as 0. */ + + cc |= NVMEF(NVME_CC_REG_EN, 1); + + error = nvmf_write_property(qp, NVMF_PROP_CC, 4, cc); + if (error != 0) + errc(EX_IOERR, error, "Failed to set CC"); + + /* Wait for CSTS.RDY in Controller Status */ + timo = NVME_CAP_LO_TO(cap); + for (;;) { + error = nvmf_read_property(qp, NVMF_PROP_CSTS, 4, &csts); + if (error != 0) + errc(EX_IOERR, error, "Failed to fetch CSTS"); + + if (NVMEV(NVME_CSTS_REG_RDY, csts) != 0) + break; + + if (timo == 0) + errx(EX_IOERR, "Controller failed to become ready"); + timo--; + usleep(500 * 1000); + } + + return (qp); +} + +/* + * XXX: Should this accept the admin queue size as a parameter rather + * than always using NVMF_MIN_ADMIN_MAX_SQ_SIZE? + */ +static int +connect_nvm_adminq(struct nvmf_association *na, + const struct nvmf_qpair_params *params, struct nvmf_qpair **qpp, + uint16_t cntlid, const char *subnqn, const char *hostnqn, uint32_t kato, + uint16_t *mqes) +{ + struct nvmf_qpair *qp; + uint64_t cap, cc, csts; + u_int mps, mpsmin, mpsmax; + int error, timo; + + qp = nvmf_connect(na, params, 0, NVMF_MIN_ADMIN_MAX_SQ_SIZE, hostid, + cntlid, subnqn, hostnqn, kato); + if (qp == NULL) { + warnx("Failed to connect to NVM controller %s: %s", subnqn, + nvmf_association_error(na)); + return (EX_IOERR); + } + + /* Fetch Controller Capabilities Property */ + error = nvmf_read_property(qp, NVMF_PROP_CAP, 8, &cap); + if (error != 0) { + warnc(error, "Failed to fetch CAP"); + nvmf_free_qpair(qp); + return (EX_IOERR); + } + + /* Require the NVM command set. */ + if (NVME_CAP_HI_CSS_NVM(cap >> 32) == 0) { + warnx("Controller %s does not support the NVM command set", + subnqn); + nvmf_free_qpair(qp); + return (EX_UNAVAILABLE); + } + + *mqes = NVME_CAP_LO_MQES(cap); + + /* Prefer native host page size if it fits. */ + mpsmin = NVMEV(NVME_CAP_HI_REG_MPSMIN, cap >> 32); + mpsmax = NVMEV(NVME_CAP_HI_REG_MPSMAX, cap >> 32); + mps = ffs(getpagesize()) - 1; + if (mps < mpsmin + NVME_MPS_SHIFT) + mps = mpsmin; + else if (mps > mpsmax + NVME_MPS_SHIFT) + mps = mpsmax; + else + mps -= NVME_MPS_SHIFT; + + /* Configure controller. */ + error = nvmf_read_property(qp, NVMF_PROP_CC, 4, &cc); + if (error != 0) { + warnc(error, "Failed to fetch CC"); + nvmf_free_qpair(qp); + return (EX_IOERR); + } + + /* Clear known fields preserving any reserved fields. */ + cc &= ~(NVMEM(NVME_CC_REG_IOCQES) | NVMEM(NVME_CC_REG_IOSQES) | + NVMEM(NVME_CC_REG_SHN) | NVMEM(NVME_CC_REG_AMS) | + NVMEM(NVME_CC_REG_MPS) | NVMEM(NVME_CC_REG_CSS)); + + cc |= NVMEF(NVME_CC_REG_IOCQES, 4); /* CQE entry size == 16 */ + cc |= NVMEF(NVME_CC_REG_IOSQES, 6); /* SEQ entry size == 64 */ + cc |= NVMEF(NVME_CC_REG_AMS, 0); /* AMS 0 (Round-robin) */ + cc |= NVMEF(NVME_CC_REG_MPS, mps); + cc |= NVMEF(NVME_CC_REG_CSS, 0); /* NVM command set */ + cc |= NVMEF(NVME_CC_REG_EN, 1); /* EN = 1 */ + + error = nvmf_write_property(qp, NVMF_PROP_CC, 4, cc); + if (error != 0) { + warnc(error, "Failed to set CC"); + nvmf_free_qpair(qp); + return (EX_IOERR); + } + + /* Wait for CSTS.RDY in Controller Status */ + timo = NVME_CAP_LO_TO(cap); + for (;;) { + error = nvmf_read_property(qp, NVMF_PROP_CSTS, 4, &csts); + if (error != 0) { + warnc(error, "Failed to fetch CSTS"); + nvmf_free_qpair(qp); + return (EX_IOERR); + } + + if (NVMEV(NVME_CSTS_REG_RDY, csts) != 0) + break; + + if (timo == 0) { + warnx("Controller failed to become ready"); + nvmf_free_qpair(qp); + return (EX_IOERR); + } + timo--; + usleep(500 * 1000); + } + + *qpp = qp; + return (0); +} + +static void +shutdown_controller(struct nvmf_qpair *qp) +{ + uint64_t cc; + int error; + + error = nvmf_read_property(qp, NVMF_PROP_CC, 4, &cc); + if (error != 0) { + warnc(error, "Failed to fetch CC"); + goto out; + } + + cc |= NVMEF(NVME_CC_REG_SHN, NVME_SHN_NORMAL); + + error = nvmf_write_property(qp, NVMF_PROP_CC, 4, cc); + if (error != 0) { + warnc(error, "Failed to set CC to trigger shutdown"); + goto out; + } + +out: + nvmf_free_qpair(qp); +} + +/* Returns a value from <sysexits.h> */ +int +connect_nvm_queues(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, struct nvmf_qpair **admin, struct nvmf_qpair **io, + u_int num_io_queues, u_int queue_size, struct nvme_controller_data *cdata) +{ + struct nvmf_qpair_params qparams; + struct nvmf_association *na; + struct addrinfo *ai, *list; + u_int queues; + int error; + uint16_t mqes; + + switch (trtype) { + case NVMF_TRTYPE_TCP: + break; + default: + warnx("Unsupported transport %s", nvmf_transport_type(trtype)); + return (EX_UNAVAILABLE); + } + + if (!init_hostid()) + return (EX_IOERR); + if (hostnqn == NULL || !nvmf_nqn_valid(hostnqn)) { + warnx("Invalid HostNQN %s", hostnqn); + return (EX_USAGE); + } + + /* Association. */ + na = nvmf_allocate_association(trtype, false, aparams); + if (na == NULL) { + warn("Failed to create association for %s", subnqn); + return (EX_IOERR); + } + + /* Admin queue. */ + memset(&qparams, 0, sizeof(qparams)); + qparams.admin = true; + if (!tcp_qpair_params(&qparams, adrfam, address, port, &ai, &list)) { + nvmf_free_association(na); + return (EX_NOHOST); + } + error = connect_nvm_adminq(na, &qparams, admin, cntlid, subnqn, hostnqn, + kato, &mqes); + if (error != 0) { + nvmf_free_association(na); + freeaddrinfo(list); + return (error); + } + + /* Validate I/O queue size. */ + memset(io, 0, sizeof(*io) * num_io_queues); + if (queue_size == 0) + queue_size = (u_int)mqes + 1; + else if (queue_size > (u_int)mqes + 1) { + warnx("I/O queue size exceeds controller maximum (%u)", + mqes + 1); + error = EX_USAGE; + goto out; + } + + /* Fetch controller data. */ + error = nvmf_host_identify_controller(*admin, cdata); + if (error != 0) { + warnc(error, "Failed to fetch controller data for %s", subnqn); + error = EX_IOERR; + goto out; + } + + nvmf_update_assocation(na, cdata); + + error = nvmf_host_request_queues(*admin, num_io_queues, &queues); + if (error != 0) { + warnc(error, "Failed to request I/O queues"); + error = EX_IOERR; + goto out; + } + if (queues < num_io_queues) { + warnx("Controller enabled fewer I/O queues (%u) than requested (%u)", + queues, num_io_queues); + error = EX_PROTOCOL; + goto out; + } + + /* I/O queues. */ + for (u_int i = 0; i < num_io_queues; i++) { + memset(&qparams, 0, sizeof(qparams)); + qparams.admin = false; + if (!tcp_qpair_params_ai(&qparams, ai)) { + warn("Failed to connect to controller at %s:%s", + address, port); + error = EX_NOHOST; + goto out; + } + io[i] = nvmf_connect(na, &qparams, i + 1, queue_size, hostid, + nvmf_cntlid(*admin), subnqn, hostnqn, 0); + if (io[i] == NULL) { + warnx("Failed to create I/O queue: %s", + nvmf_association_error(na)); + error = EX_IOERR; + goto out; + } + } + nvmf_free_association(na); + freeaddrinfo(list); + return (0); + +out: + disconnect_nvm_queues(*admin, io, num_io_queues); + nvmf_free_association(na); + freeaddrinfo(list); + return (error); +} + +void +disconnect_nvm_queues(struct nvmf_qpair *admin, struct nvmf_qpair **io, + u_int num_io_queues) +{ + for (u_int i = 0; i < num_io_queues; i++) { + if (io[i] == NULL) + break; + nvmf_free_qpair(io[i]); + } + shutdown_controller(admin); +} diff --git a/sbin/nvmecontrol/fabrics.h b/sbin/nvmecontrol/fabrics.h new file mode 100644 index 000000000000..1f43fc53bb8f --- /dev/null +++ b/sbin/nvmecontrol/fabrics.h @@ -0,0 +1,50 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023-2024 Chelsio Communications, Inc. + * Written by: John Baldwin <jhb@FreeBSD.org> + */ + +#ifndef __FABRICS_H__ +#define __FABRICS_H__ + +/* + * Splits 'in_address' into separate 'address' and 'port' strings. If + * a separate buffer for the address was allocated, 'tofree' is set to + * the allocated buffer, otherwise 'tofree' is set to NULL. + */ +void nvmf_parse_address(const char *in_address, const char **address, + const char **port, char **tofree); + +uint16_t nvmf_parse_cntlid(const char *cntlid); + +const char *nvmf_default_hostnqn(void); + +int nvmf_init_dle_from_address(enum nvmf_trtype trtype, const char *address, + const char *port, uint16_t cntlid, const char *subnqn, + struct nvme_discovery_log_entry *dle); + +/* Connect to a discovery controller and return the Admin qpair. */ +struct nvmf_qpair *connect_discovery_adminq(enum nvmf_trtype trtype, + const char *address, const char *port, const char *hostnqn); + +/* + * Connect to an NVM controller establishing an Admin qpair and one or + * more I/O qpairs. The controller's controller data is returned in + * *cdata on success. Returns a non-zero value from <sysexits.h> on + * failure. + */ +int connect_nvm_queues(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, struct nvmf_qpair **admin, struct nvmf_qpair **io, + u_int num_io_queues, u_int queue_size, struct nvme_controller_data *cdata); + +/* + * Disconnect from an NVM controller disconnecting all queues and + * shutting down the controller. + */ +void disconnect_nvm_queues(struct nvmf_qpair *admin, struct nvmf_qpair **io, + u_int num_io_queues); + +#endif /* !__FABRICS_H__ */ diff --git a/sbin/nvmecontrol/firmware.c b/sbin/nvmecontrol/firmware.c index 256847ef8ac2..c1c26c310627 100644 --- a/sbin/nvmecontrol/firmware.c +++ b/sbin/nvmecontrol/firmware.c @@ -29,7 +29,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioccom.h> #include <sys/stat.h> @@ -103,9 +102,10 @@ slot_has_valid_firmware(int fd, int slot) int has_fw = false; read_logpage(fd, NVME_LOG_FIRMWARE_SLOT, - NVME_GLOBAL_NAMESPACE_TAG, 0, 0, 0, &fw, sizeof(fw)); + NVME_GLOBAL_NAMESPACE_TAG, 0, 0, 0, 0, 0, 0, 0, + &fw, sizeof(fw)); - if (fw.revision[slot-1] != 0LLU) + if (fw.revision[slot-1][0] != '\0') has_fw = true; return (has_fw); @@ -278,21 +278,18 @@ firmware(const struct cmd *f, int argc, char *argv[]) if (read_controller_data(fd, &cdata)) errx(EX_IOERR, "Identify request failed"); - oacs_fw = (cdata.oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) & - NVME_CTRLR_DATA_OACS_FIRMWARE_MASK; + oacs_fw = NVMEV(NVME_CTRLR_DATA_OACS_FIRMWARE, cdata.oacs); if (oacs_fw == 0) errx(EX_UNAVAILABLE, "controller does not support firmware activate/download"); - fw_slot1_ro = (cdata.frmw >> NVME_CTRLR_DATA_FRMW_SLOT1_RO_SHIFT) & - NVME_CTRLR_DATA_FRMW_SLOT1_RO_MASK; + fw_slot1_ro = NVMEV(NVME_CTRLR_DATA_FRMW_SLOT1_RO, cdata.frmw); if (opt.fw_img && opt.slot == 1 && fw_slot1_ro) errx(EX_UNAVAILABLE, "slot %d is marked as read only", opt.slot); - fw_num_slots = (cdata.frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) & - NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK; + fw_num_slots = NVMEV(NVME_CTRLR_DATA_FRMW_NUM_SLOTS, cdata.frmw); if (opt.slot > fw_num_slots) errx(EX_UNAVAILABLE, diff --git a/sbin/nvmecontrol/format.c b/sbin/nvmecontrol/format.c index 541b207b373f..16699605ea8c 100644 --- a/sbin/nvmecontrol/format.c +++ b/sbin/nvmecontrol/format.c @@ -25,7 +25,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioccom.h> @@ -74,7 +73,7 @@ static struct options { static const struct opts format_opts[] = { #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } OPT("crypto", 'C', arg_none, opt, Cflag, - "Crptographic erase"), + "Cryptographic erase"), OPT("erase", 'E', arg_none, opt, Eflag, "User data erase"), OPT("lbaf", 'f', arg_uint32, opt, lbaf, @@ -160,38 +159,33 @@ format(const struct cmd *f, int argc, char *argv[]) /* Check that controller can execute this command. */ if (read_controller_data(fd, &cd)) errx(EX_IOERR, "Identify request failed"); - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_FORMAT_SHIFT) & - NVME_CTRLR_DATA_OACS_FORMAT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_FORMAT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support format"); - if (((cd.fna >> NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_SHIFT) & - NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_MASK) == 0 && ses == SES_CRYPTO) + if (NVMEV(NVME_CTRLR_DATA_FNA_CRYPTO_ERASE, cd.fna) == 0 && + ses == SES_CRYPTO) errx(EX_UNAVAILABLE, "controller does not support cryptographic erase"); if (nsid != NVME_GLOBAL_NAMESPACE_TAG) { - if (((cd.fna >> NVME_CTRLR_DATA_FNA_FORMAT_ALL_SHIFT) & - NVME_CTRLR_DATA_FNA_FORMAT_ALL_MASK) && ses == SES_NONE) + if (NVMEV(NVME_CTRLR_DATA_FNA_FORMAT_ALL, cd.fna) && + ses == SES_NONE) errx(EX_UNAVAILABLE, "controller does not support per-NS format"); - if (((cd.fna >> NVME_CTRLR_DATA_FNA_ERASE_ALL_SHIFT) & - NVME_CTRLR_DATA_FNA_ERASE_ALL_MASK) && ses != SES_NONE) + if (NVMEV(NVME_CTRLR_DATA_FNA_ERASE_ALL, cd.fna) && + ses != SES_NONE) errx(EX_UNAVAILABLE, "controller does not support per-NS erase"); /* Try to keep previous namespace parameters. */ if (read_namespace_data(fd, nsid, &nsd)) errx(EX_IOERR, "Identify request failed"); if (lbaf < 0) - lbaf = (nsd.flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) - & NVME_NS_DATA_FLBAS_FORMAT_MASK; + lbaf = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, nsd.flbas); if (lbaf > nsd.nlbaf) errx(EX_USAGE, "LBA format is out of range"); if (ms < 0) - ms = (nsd.flbas >> NVME_NS_DATA_FLBAS_EXTENDED_SHIFT) - & NVME_NS_DATA_FLBAS_EXTENDED_MASK; + ms = NVMEV(NVME_NS_DATA_FLBAS_EXTENDED, nsd.flbas); if (pi < 0) - pi = (nsd.dps >> NVME_NS_DATA_DPS_MD_START_SHIFT) - & NVME_NS_DATA_DPS_MD_START_MASK; + pi = NVMEV(NVME_NS_DATA_DPS_MD_START, nsd.dps); if (pil < 0) - pil = (nsd.dps >> NVME_NS_DATA_DPS_PIT_SHIFT) - & NVME_NS_DATA_DPS_PIT_MASK; + pil = NVMEV(NVME_NS_DATA_DPS_PIT, nsd.dps); } else { /* We have no previous parameters, so default to zeroes. */ diff --git a/sbin/nvmecontrol/identify.c b/sbin/nvmecontrol/identify.c index 50698e8bfc70..98a3141bf9ad 100644 --- a/sbin/nvmecontrol/identify.c +++ b/sbin/nvmecontrol/identify.c @@ -27,7 +27,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <ctype.h> @@ -67,11 +66,9 @@ print_namespace(struct nvme_namespace_data *nsdata) uint8_t thin_prov, ptype; uint8_t flbas_fmt, t; - thin_prov = (nsdata->nsfeat >> NVME_NS_DATA_NSFEAT_THIN_PROV_SHIFT) & - NVME_NS_DATA_NSFEAT_THIN_PROV_MASK; + thin_prov = NVMEV(NVME_NS_DATA_NSFEAT_THIN_PROV, nsdata->nsfeat); - flbas_fmt = (nsdata->flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) & - NVME_NS_DATA_FLBAS_FORMAT_MASK; + flbas_fmt = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, nsdata->flbas); printf("Size: %lld blocks\n", (long long)nsdata->nsze); @@ -83,83 +80,77 @@ print_namespace(struct nvme_namespace_data *nsdata) thin_prov ? "Supported" : "Not Supported"); printf("Number of LBA Formats: %d\n", nsdata->nlbaf+1); printf("Current LBA Format: LBA Format #%02d", flbas_fmt); - if (nsdata->lbaf[flbas_fmt] >> NVME_NS_DATA_LBAF_MS_SHIFT & NVME_NS_DATA_LBAF_MS_MASK) - printf(" %s metadata\n", nsdata->flbas >> NVME_NS_DATA_FLBAS_EXTENDED_SHIFT & - NVME_NS_DATA_FLBAS_EXTENDED_MASK ? "Extended" : "Separate"); + if (NVMEV(NVME_NS_DATA_LBAF_MS, nsdata->lbaf[flbas_fmt]) != 0) + printf(" %s metadata\n", + NVMEV(NVME_NS_DATA_FLBAS_EXTENDED, nsdata->flbas) != 0 ? + "Extended" : "Separate"); else printf("\n"); printf("Metadata Capabilities\n"); printf(" Extended: %s\n", - nsdata->mc >> NVME_NS_DATA_MC_EXTENDED_SHIFT & NVME_NS_DATA_MC_EXTENDED_MASK ? "Supported" : "Not Supported"); + NVMEV(NVME_NS_DATA_MC_EXTENDED, nsdata->mc) != 0 ? "Supported" : + "Not Supported"); printf(" Separate: %s\n", - nsdata->mc >> NVME_NS_DATA_MC_POINTER_SHIFT & NVME_NS_DATA_MC_POINTER_MASK ? "Supported" : "Not Supported"); + NVMEV(NVME_NS_DATA_MC_POINTER, nsdata->mc) != 0 ? "Supported" : + "Not Supported"); printf("Data Protection Caps: %s%s%s%s%s%s\n", (nsdata->dpc == 0) ? "Not Supported" : "", - ((nsdata->dpc >> NVME_NS_DATA_DPC_MD_END_SHIFT) & - NVME_NS_DATA_DPC_MD_END_MASK) ? "Last Bytes, " : "", - ((nsdata->dpc >> NVME_NS_DATA_DPC_MD_START_SHIFT) & - NVME_NS_DATA_DPC_MD_START_MASK) ? "First Bytes, " : "", - ((nsdata->dpc >> NVME_NS_DATA_DPC_PIT3_SHIFT) & - NVME_NS_DATA_DPC_PIT3_MASK) ? "Type 3, " : "", - ((nsdata->dpc >> NVME_NS_DATA_DPC_PIT2_SHIFT) & - NVME_NS_DATA_DPC_PIT2_MASK) ? "Type 2, " : "", - ((nsdata->dpc >> NVME_NS_DATA_DPC_PIT1_SHIFT) & - NVME_NS_DATA_DPC_PIT1_MASK) ? "Type 1" : ""); + NVMEV(NVME_NS_DATA_DPC_MD_END, nsdata->dpc) != 0 ? "Last Bytes, " : + "", + NVMEV(NVME_NS_DATA_DPC_MD_START, nsdata->dpc) != 0 ? + "First Bytes, " : "", + NVMEV(NVME_NS_DATA_DPC_PIT3, nsdata->dpc) != 0 ? "Type 3, " : "", + NVMEV(NVME_NS_DATA_DPC_PIT2, nsdata->dpc) != 0 ? "Type 2, " : "", + NVMEV(NVME_NS_DATA_DPC_PIT1, nsdata->dpc) != 0 ? "Type 1" : ""); printf("Data Protection Settings: "); - ptype = (nsdata->dps >> NVME_NS_DATA_DPS_PIT_SHIFT) & - NVME_NS_DATA_DPS_PIT_MASK; - if (ptype) { + ptype = NVMEV(NVME_NS_DATA_DPS_PIT, nsdata->dps); + if (ptype != 0) { printf("Type %d, %s Bytes\n", ptype, - ((nsdata->dps >> NVME_NS_DATA_DPS_MD_START_SHIFT) & - NVME_NS_DATA_DPS_MD_START_MASK) ? "First" : "Last"); + NVMEV(NVME_NS_DATA_DPS_MD_START, nsdata->dps) != 0 ? + "First" : "Last"); } else { printf("Not Enabled\n"); } printf("Multi-Path I/O Capabilities: %s%s\n", (nsdata->nmic == 0) ? "Not Supported" : "", - ((nsdata->nmic >> NVME_NS_DATA_NMIC_MAY_BE_SHARED_SHIFT) & - NVME_NS_DATA_NMIC_MAY_BE_SHARED_MASK) ? "May be shared" : ""); + NVMEV(NVME_NS_DATA_NMIC_MAY_BE_SHARED, nsdata->nmic) != 0 ? + "May be shared" : ""); printf("Reservation Capabilities: %s%s%s%s%s%s%s%s%s\n", (nsdata->rescap == 0) ? "Not Supported" : "", - ((nsdata->rescap >> NVME_NS_DATA_RESCAP_IEKEY13_SHIFT) & - NVME_NS_DATA_RESCAP_IEKEY13_MASK) ? "IEKEY13, " : "", - ((nsdata->rescap >> NVME_NS_DATA_RESCAP_EX_AC_AR_SHIFT) & - NVME_NS_DATA_RESCAP_EX_AC_AR_MASK) ? "EX_AC_AR, " : "", - ((nsdata->rescap >> NVME_NS_DATA_RESCAP_WR_EX_AR_SHIFT) & - NVME_NS_DATA_RESCAP_WR_EX_AR_MASK) ? "WR_EX_AR, " : "", - ((nsdata->rescap >> NVME_NS_DATA_RESCAP_EX_AC_RO_SHIFT) & - NVME_NS_DATA_RESCAP_EX_AC_RO_MASK) ? "EX_AC_RO, " : "", - ((nsdata->rescap >> NVME_NS_DATA_RESCAP_WR_EX_RO_SHIFT) & - NVME_NS_DATA_RESCAP_WR_EX_RO_MASK) ? "WR_EX_RO, " : "", - ((nsdata->rescap >> NVME_NS_DATA_RESCAP_EX_AC_SHIFT) & - NVME_NS_DATA_RESCAP_EX_AC_MASK) ? "EX_AC, " : "", - ((nsdata->rescap >> NVME_NS_DATA_RESCAP_WR_EX_SHIFT) & - NVME_NS_DATA_RESCAP_WR_EX_MASK) ? "WR_EX, " : "", - ((nsdata->rescap >> NVME_NS_DATA_RESCAP_PTPL_SHIFT) & - NVME_NS_DATA_RESCAP_PTPL_MASK) ? "PTPL" : ""); + NVMEV(NVME_NS_DATA_RESCAP_IEKEY13, nsdata->rescap) != 0 ? + "IEKEY13, " : "", + NVMEV(NVME_NS_DATA_RESCAP_EX_AC_AR, nsdata->rescap) != 0 ? + "EX_AC_AR, " : "", + NVMEV(NVME_NS_DATA_RESCAP_WR_EX_AR, nsdata->rescap) != 0 ? + "WR_EX_AR, " : "", + NVMEV(NVME_NS_DATA_RESCAP_EX_AC_RO, nsdata->rescap) != 0 ? + "EX_AC_RO, " : "", + NVMEV(NVME_NS_DATA_RESCAP_WR_EX_RO, nsdata->rescap) != 0 ? + "WR_EX_RO, " : "", + NVMEV(NVME_NS_DATA_RESCAP_EX_AC, nsdata->rescap) != 0 ? + "EX_AC, " : "", + NVMEV(NVME_NS_DATA_RESCAP_WR_EX, nsdata->rescap) != 0 ? + "WR_EX, " : "", + NVMEV(NVME_NS_DATA_RESCAP_PTPL, nsdata->rescap) != 0 ? "PTPL" : ""); printf("Format Progress Indicator: "); - if ((nsdata->fpi >> NVME_NS_DATA_FPI_SUPP_SHIFT) & - NVME_NS_DATA_FPI_SUPP_MASK) { + if (NVMEV(NVME_NS_DATA_FPI_SUPP, nsdata->fpi) != 0) { printf("%u%% remains\n", - (nsdata->fpi >> NVME_NS_DATA_FPI_PERC_SHIFT) & - NVME_NS_DATA_FPI_PERC_MASK); + NVMEV(NVME_NS_DATA_FPI_PERC, nsdata->fpi)); } else printf("Not Supported\n"); - t = (nsdata->dlfeat >> NVME_NS_DATA_DLFEAT_READ_SHIFT) & - NVME_NS_DATA_DLFEAT_READ_MASK; + t = NVMEV(NVME_NS_DATA_DLFEAT_READ, nsdata->dlfeat); printf("Deallocate Logical Block: Read %s%s%s\n", (t == NVME_NS_DATA_DLFEAT_READ_NR) ? "Not Reported" : (t == NVME_NS_DATA_DLFEAT_READ_00) ? "00h" : (t == NVME_NS_DATA_DLFEAT_READ_FF) ? "FFh" : "Unknown", - (nsdata->dlfeat >> NVME_NS_DATA_DLFEAT_DWZ_SHIFT) & - NVME_NS_DATA_DLFEAT_DWZ_MASK ? ", Write Zero" : "", - (nsdata->dlfeat >> NVME_NS_DATA_DLFEAT_GCRC_SHIFT) & - NVME_NS_DATA_DLFEAT_GCRC_MASK ? ", Guard CRC" : ""); + NVMEV(NVME_NS_DATA_DLFEAT_DWZ, nsdata->dlfeat) != 0 ? + ", Write Zero" : "", + NVMEV(NVME_NS_DATA_DLFEAT_GCRC, nsdata->dlfeat) != 0 ? + ", Guard CRC" : ""); printf("Optimal I/O Boundary: %u blocks\n", nsdata->noiob); printf("NVM Capacity: %s bytes\n", uint128_to_str(to128(nsdata->nvmcap), cbuf, sizeof(cbuf))); - if ((nsdata->nsfeat >> NVME_NS_DATA_NSFEAT_NPVALID_SHIFT) & - NVME_NS_DATA_NSFEAT_NPVALID_MASK) { + if (NVMEV(NVME_NS_DATA_NSFEAT_NPVALID, nsdata->nsfeat) != 0) { printf("Preferred Write Granularity: %u blocks\n", nsdata->npwg + 1); printf("Preferred Write Alignment: %u blocks\n", @@ -181,14 +172,11 @@ print_namespace(struct nvme_namespace_data *nsdata) printf("\n"); for (i = 0; i <= nsdata->nlbaf; i++) { lbaf = nsdata->lbaf[i]; - lbads = (lbaf >> NVME_NS_DATA_LBAF_LBADS_SHIFT) & - NVME_NS_DATA_LBAF_LBADS_MASK; + lbads = NVMEV(NVME_NS_DATA_LBAF_LBADS, lbaf); if (lbads == 0) continue; - ms = (lbaf >> NVME_NS_DATA_LBAF_MS_SHIFT) & - NVME_NS_DATA_LBAF_MS_MASK; - rp = (lbaf >> NVME_NS_DATA_LBAF_RP_SHIFT) & - NVME_NS_DATA_LBAF_RP_MASK; + ms = NVMEV(NVME_NS_DATA_LBAF_MS, lbaf); + rp = NVMEV(NVME_NS_DATA_LBAF_RP, lbaf); printf("LBA Format #%02d: Data Size: %5d Metadata Size: %5d" " Performance: %s\n", i, 1 << lbads, ms, (rp == 0) ? "Best" : @@ -279,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/identify_ext.c b/sbin/nvmecontrol/identify_ext.c index 72b1ff5ccd7b..95ca4b5187d4 100644 --- a/sbin/nvmecontrol/identify_ext.c +++ b/sbin/nvmecontrol/identify_ext.c @@ -27,7 +27,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <ctype.h> @@ -57,41 +56,27 @@ nvme_print_controller(struct nvme_controller_data *cdata) uint8_t fwug; oncs = cdata->oncs; - compare = (oncs >> NVME_CTRLR_DATA_ONCS_COMPARE_SHIFT) & - NVME_CTRLR_DATA_ONCS_COMPARE_MASK; - write_unc = (oncs >> NVME_CTRLR_DATA_ONCS_WRITE_UNC_SHIFT) & - NVME_CTRLR_DATA_ONCS_WRITE_UNC_MASK; - dsm = (oncs >> NVME_CTRLR_DATA_ONCS_DSM_SHIFT) & - NVME_CTRLR_DATA_ONCS_DSM_MASK; + compare = NVMEV(NVME_CTRLR_DATA_ONCS_COMPARE, oncs); + write_unc = NVMEV(NVME_CTRLR_DATA_ONCS_WRITE_UNC, oncs); + dsm = NVMEV(NVME_CTRLR_DATA_ONCS_DSM, oncs); oacs = cdata->oacs; - security = (oacs >> NVME_CTRLR_DATA_OACS_SECURITY_SHIFT) & - NVME_CTRLR_DATA_OACS_SECURITY_MASK; - fmt = (oacs >> NVME_CTRLR_DATA_OACS_FORMAT_SHIFT) & - NVME_CTRLR_DATA_OACS_FORMAT_MASK; - fw = (oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) & - NVME_CTRLR_DATA_OACS_FIRMWARE_MASK; - nsmgmt = (oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK; + security = NVMEV(NVME_CTRLR_DATA_OACS_SECURITY, oacs); + fmt = NVMEV(NVME_CTRLR_DATA_OACS_FORMAT, oacs); + fw = NVMEV(NVME_CTRLR_DATA_OACS_FIRMWARE, oacs); + nsmgmt = NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, oacs); - fw_num_slots = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) & - NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK; - fw_slot1_ro = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_SLOT1_RO_SHIFT) & - NVME_CTRLR_DATA_FRMW_SLOT1_RO_MASK; + fw_num_slots = NVMEV(NVME_CTRLR_DATA_FRMW_NUM_SLOTS, cdata->frmw); + fw_slot1_ro = NVMEV(NVME_CTRLR_DATA_FRMW_SLOT1_RO, cdata->frmw); fwug = cdata->fwug; - ns_smart = (cdata->lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) & - NVME_CTRLR_DATA_LPA_NS_SMART_MASK; + ns_smart = NVMEV(NVME_CTRLR_DATA_LPA_NS_SMART, cdata->lpa); - sqes_min = (cdata->sqes >> NVME_CTRLR_DATA_SQES_MIN_SHIFT) & - NVME_CTRLR_DATA_SQES_MIN_MASK; - sqes_max = (cdata->sqes >> NVME_CTRLR_DATA_SQES_MAX_SHIFT) & - NVME_CTRLR_DATA_SQES_MAX_MASK; + sqes_min = NVMEV(NVME_CTRLR_DATA_SQES_MIN, cdata->sqes); + sqes_max = NVMEV(NVME_CTRLR_DATA_SQES_MAX, cdata->sqes); - cqes_min = (cdata->cqes >> NVME_CTRLR_DATA_CQES_MIN_SHIFT) & - NVME_CTRLR_DATA_CQES_MIN_MASK; - cqes_max = (cdata->cqes >> NVME_CTRLR_DATA_CQES_MAX_SHIFT) & - NVME_CTRLR_DATA_CQES_MAX_MASK; + cqes_min = NVMEV(NVME_CTRLR_DATA_CQES_MIN, cdata->cqes); + cqes_max = NVMEV(NVME_CTRLR_DATA_CQES_MAX, cdata->cqes); printf("Controller Capabilities/Features\n"); printf("================================\n"); @@ -108,14 +93,14 @@ nvme_print_controller(struct nvme_controller_data *cdata) cdata->ieee[2], cdata->ieee[1], cdata->ieee[0]); printf("Multi-Path I/O Capabilities: %s%s%s%s%s\n", (cdata->mic == 0) ? "Not Supported" : "", - ((cdata->mic >> NVME_CTRLR_DATA_MIC_ANAR_SHIFT) & - NVME_CTRLR_DATA_MIC_SRIOVVF_MASK) ? "Asymmetric, " : "", - ((cdata->mic >> NVME_CTRLR_DATA_MIC_SRIOVVF_SHIFT) & - NVME_CTRLR_DATA_MIC_SRIOVVF_MASK) ? "SR-IOV VF, " : "", - ((cdata->mic >> NVME_CTRLR_DATA_MIC_MCTRLRS_SHIFT) & - NVME_CTRLR_DATA_MIC_MCTRLRS_MASK) ? "Multiple controllers, " : "", - ((cdata->mic >> NVME_CTRLR_DATA_MIC_MPORTS_SHIFT) & - NVME_CTRLR_DATA_MIC_MPORTS_MASK) ? "Multiple ports" : ""); + NVMEV(NVME_CTRLR_DATA_MIC_ANAR, cdata->mic) != 0 ? + "Asymmetric, " : "", + NVMEV(NVME_CTRLR_DATA_MIC_SRIOVVF, cdata->mic) != 0 ? + "SR-IOV VF, " : "", + NVMEV(NVME_CTRLR_DATA_MIC_MCTRLRS, cdata->mic) != 0 ? + "Multiple controllers, " : "", + NVMEV(NVME_CTRLR_DATA_MIC_MPORTS, cdata->mic) != 0 ? + "Multiple ports" : ""); /* TODO: Use CAP.MPSMIN to determine true memory page size. */ printf("Max Data Transfer Size: "); if (cdata->mdts == 0) @@ -123,24 +108,19 @@ nvme_print_controller(struct nvme_controller_data *cdata) else printf("%ld bytes\n", PAGE_SIZE * (1L << cdata->mdts)); printf("Sanitize Crypto Erase: %s\n", - ((cdata->sanicap >> NVME_CTRLR_DATA_SANICAP_CES_SHIFT) & - NVME_CTRLR_DATA_SANICAP_CES_MASK) ? - "Supported" : "Not Supported"); + NVMEV(NVME_CTRLR_DATA_SANICAP_CES, cdata->sanicap) != 0 ? + "Supported" : "Not Supported"); printf("Sanitize Block Erase: %s\n", - ((cdata->sanicap >> NVME_CTRLR_DATA_SANICAP_BES_SHIFT) & - NVME_CTRLR_DATA_SANICAP_BES_MASK) ? - "Supported" : "Not Supported"); + NVMEV(NVME_CTRLR_DATA_SANICAP_BES, cdata->sanicap) != 0 ? + "Supported" : "Not Supported"); printf("Sanitize Overwrite: %s\n", - ((cdata->sanicap >> NVME_CTRLR_DATA_SANICAP_OWS_SHIFT) & - NVME_CTRLR_DATA_SANICAP_OWS_MASK) ? - "Supported" : "Not Supported"); + NVMEV(NVME_CTRLR_DATA_SANICAP_OWS, cdata->sanicap) != 0 ? + "Supported" : "Not Supported"); printf("Sanitize NDI: %s\n", - ((cdata->sanicap >> NVME_CTRLR_DATA_SANICAP_NDI_SHIFT) & - NVME_CTRLR_DATA_SANICAP_NDI_MASK) ? - "Supported" : "Not Supported"); + NVMEV(NVME_CTRLR_DATA_SANICAP_NDI, cdata->sanicap) != 0 ? + "Supported" : "Not Supported"); printf("Sanitize NODMMAS: "); - switch (((cdata->sanicap >> NVME_CTRLR_DATA_SANICAP_NODMMAS_SHIFT) & - NVME_CTRLR_DATA_SANICAP_NODMMAS_MASK)) { + switch (NVMEV(NVME_CTRLR_DATA_SANICAP_NODMMAS, cdata->sanicap)) { case NVME_CTRLR_DATA_SANICAP_NODMMAS_UNDEF: printf("Undefined\n"); break; @@ -158,6 +138,36 @@ nvme_print_controller(struct nvme_controller_data *cdata) printf("Version: %d.%d.%d\n", (cdata->ver >> 16) & 0xffff, (cdata->ver >> 8) & 0xff, cdata->ver & 0xff); + printf("Traffic Based Keep Alive: %sSupported\n", + NVMEV(NVME_CTRLR_DATA_CTRATT_TBKAS, cdata->ctratt) ? "" : "Not "); + printf("Controller Type: "); + switch (cdata->cntrltype) { + case 0: + printf("Not Reported\n"); + break; + case 1: + printf("I/O Controller\n"); + break; + case 2: + printf("Discovery Controller\n"); + break; + case 3: + printf("Administrative Controller\n"); + break; + default: + printf("%d (Reserved)\n", cdata->cntrltype); + break; + } + printf("Keep Alive Timer "); + if (cdata->kas == 0) + printf("Not Supported\n"); + else + printf("%u ms granularity\n", cdata->kas * 100); + printf("Maximum Outstanding Commands "); + if (cdata->maxcmd == 0) + printf("Not Specified\n"); + else + printf("%u\n", cdata->maxcmd); printf("\n"); printf("Admin Command Set Attributes\n"); @@ -171,32 +181,26 @@ nvme_print_controller(struct nvme_controller_data *cdata) printf("Namespace Management: %s\n", nsmgmt ? "Supported" : "Not Supported"); printf("Device Self-test: %sSupported\n", - ((oacs >> NVME_CTRLR_DATA_OACS_SELFTEST_SHIFT) & - NVME_CTRLR_DATA_OACS_SELFTEST_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_OACS_SELFTEST, oacs) != 0 ? "" : "Not "); printf("Directives: %sSupported\n", - ((oacs >> NVME_CTRLR_DATA_OACS_DIRECTIVES_SHIFT) & - NVME_CTRLR_DATA_OACS_DIRECTIVES_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_OACS_DIRECTIVES, oacs) != 0 ? "" : "Not "); printf("NVMe-MI Send/Receive: %sSupported\n", - ((oacs >> NVME_CTRLR_DATA_OACS_NVMEMI_SHIFT) & - NVME_CTRLR_DATA_OACS_NVMEMI_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_OACS_NVMEMI, oacs) != 0 ? "" : "Not "); printf("Virtualization Management: %sSupported\n", - ((oacs >> NVME_CTRLR_DATA_OACS_VM_SHIFT) & - NVME_CTRLR_DATA_OACS_VM_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_OACS_VM, oacs) != 0 ? "" : "Not "); printf("Doorbell Buffer Config: %sSupported\n", - ((oacs >> NVME_CTRLR_DATA_OACS_DBBUFFER_SHIFT) & - NVME_CTRLR_DATA_OACS_DBBUFFER_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_OACS_DBBUFFER, oacs) != 0 ? "" : "Not "); printf("Get LBA Status: %sSupported\n", - ((oacs >> NVME_CTRLR_DATA_OACS_GETLBA_SHIFT) & - NVME_CTRLR_DATA_OACS_GETLBA_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_OACS_GETLBA, oacs) != 0 ? "" : "Not "); printf("Sanitize: "); if (cdata->sanicap != 0) { printf("%s%s%s\n", - ((cdata->sanicap >> NVME_CTRLR_DATA_SANICAP_CES_SHIFT) & - NVME_CTRLR_DATA_SANICAP_CES_MASK) ? "crypto, " : "", - ((cdata->sanicap >> NVME_CTRLR_DATA_SANICAP_BES_SHIFT) & - NVME_CTRLR_DATA_SANICAP_BES_MASK) ? "block, " : "", - ((cdata->sanicap >> NVME_CTRLR_DATA_SANICAP_OWS_SHIFT) & - NVME_CTRLR_DATA_SANICAP_OWS_MASK) ? "overwrite" : ""); + NVMEV(NVME_CTRLR_DATA_SANICAP_CES, cdata->sanicap) != 0 ? + "crypto, " : "", + NVMEV(NVME_CTRLR_DATA_SANICAP_BES, cdata->sanicap) != 0 ? + "block, " : "", + NVMEV(NVME_CTRLR_DATA_SANICAP_OWS, cdata->sanicap) != 0 ? + "overwrite" : ""); } else { printf("Not Supported\n"); } @@ -245,39 +249,54 @@ nvme_print_controller(struct nvme_controller_data *cdata) printf("Dataset Management Command: %s\n", dsm ? "Supported" : "Not Supported"); printf("Write Zeroes Command: %sSupported\n", - ((oncs >> NVME_CTRLR_DATA_ONCS_WRZERO_SHIFT) & - NVME_CTRLR_DATA_ONCS_WRZERO_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_ONCS_WRZERO, oncs) != 0 ? "" : "Not "); printf("Save Features: %sSupported\n", - ((oncs >> NVME_CTRLR_DATA_ONCS_SAVEFEAT_SHIFT) & - NVME_CTRLR_DATA_ONCS_SAVEFEAT_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_ONCS_SAVEFEAT, oncs) != 0 ? "" : "Not "); printf("Reservations: %sSupported\n", - ((oncs >> NVME_CTRLR_DATA_ONCS_RESERV_SHIFT) & - NVME_CTRLR_DATA_ONCS_RESERV_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_ONCS_RESERV, oncs) != 0 ? "" : "Not "); printf("Timestamp feature: %sSupported\n", - ((oncs >> NVME_CTRLR_DATA_ONCS_TIMESTAMP_SHIFT) & - NVME_CTRLR_DATA_ONCS_TIMESTAMP_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_ONCS_TIMESTAMP, oncs) != 0 ? "" : "Not "); printf("Verify feature: %sSupported\n", - ((oncs >> NVME_CTRLR_DATA_ONCS_VERIFY_SHIFT) & - NVME_CTRLR_DATA_ONCS_VERIFY_MASK) ? "" : "Not "); + NVMEV(NVME_CTRLR_DATA_ONCS_VERIFY, oncs) != 0 ? "" : "Not "); printf("Fused Operation Support: %s%s\n", (cdata->fuses == 0) ? "Not Supported" : "", - ((cdata->fuses >> NVME_CTRLR_DATA_FUSES_CNW_SHIFT) & - NVME_CTRLR_DATA_FUSES_CNW_MASK) ? "Compare and Write" : ""); + NVMEV(NVME_CTRLR_DATA_FUSES_CNW, cdata->fuses) != 0 ? + "Compare and Write" : ""); printf("Format NVM Attributes: %s%s Erase, %s Format\n", - ((cdata->fna >> NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_SHIFT) & - NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_MASK) ? "Crypto Erase, " : "", - ((cdata->fna >> NVME_CTRLR_DATA_FNA_ERASE_ALL_SHIFT) & - NVME_CTRLR_DATA_FNA_ERASE_ALL_MASK) ? "All-NVM" : "Per-NS", - ((cdata->fna >> NVME_CTRLR_DATA_FNA_FORMAT_ALL_SHIFT) & - NVME_CTRLR_DATA_FNA_FORMAT_ALL_MASK) ? "All-NVM" : "Per-NS"); - t = (cdata->vwc >> NVME_CTRLR_DATA_VWC_ALL_SHIFT) & - NVME_CTRLR_DATA_VWC_ALL_MASK; + NVMEV(NVME_CTRLR_DATA_FNA_CRYPTO_ERASE, cdata->fna) != 0 ? + "Crypto Erase, " : "", + NVMEV(NVME_CTRLR_DATA_FNA_ERASE_ALL, cdata->fna) != 0 ? + "All-NVM" : "Per-NS", + NVMEV(NVME_CTRLR_DATA_FNA_FORMAT_ALL, cdata->fna) != 0 ? + "All-NVM" : "Per-NS"); + t = NVMEV(NVME_CTRLR_DATA_VWC_ALL, cdata->vwc); printf("Volatile Write Cache: %s%s\n", - ((cdata->vwc >> NVME_CTRLR_DATA_VWC_PRESENT_SHIFT) & - NVME_CTRLR_DATA_VWC_PRESENT_MASK) ? "Present" : "Not Present", + NVMEV(NVME_CTRLR_DATA_VWC_PRESENT, cdata->vwc) != 0 ? + "Present" : "Not Present", (t == NVME_CTRLR_DATA_VWC_ALL_NO) ? ", no flush all" : (t == NVME_CTRLR_DATA_VWC_ALL_YES) ? ", flush all" : ""); if (cdata->ver >= 0x010201) printf("\nNVM Subsystem Name: %.256s\n", cdata->subnqn); + + if (cdata->ioccsz != 0) { + printf("\n"); + printf("Fabrics Attributes\n"); + printf("==================\n"); + printf("I/O Command Capsule Size: %d bytes\n", + cdata->ioccsz * 16); + printf("I/O Response Capsule Size: %d bytes\n", + cdata->iorcsz * 16); + printf("In Capsule Data Offset: %d bytes\n", + cdata->icdoff * 16); + printf("Controller Model: %s\n", + (cdata->fcatt & 1) == 0 ? "Dynamic" : "Static"); + printf("Max SGL Descriptors: "); + if (cdata->msdbd == 0) + printf("Unlimited\n"); + else + printf("%d\n", cdata->msdbd); + printf("Disconnect of I/O Queues: %sSupported\n", + (cdata->ofcs & 1) == 1 ? "" : "Not "); + } } diff --git a/sbin/nvmecontrol/logpage.c b/sbin/nvmecontrol/logpage.c index 725815edc475..023adf8f2d6a 100644 --- a/sbin/nvmecontrol/logpage.c +++ b/sbin/nvmecontrol/logpage.c @@ -6,6 +6,7 @@ * * Copyright (C) 2012-2013 Intel Corporation * All rights reserved. + * Copyright (C) 2016-2023 Warner Losh <imp@FreeBSD.org> * Copyright (C) 2018-2019 Alexander Motin <mav@FreeBSD.org> * * Redistribution and use in source and binary forms, with or without @@ -30,7 +31,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioccom.h> @@ -190,11 +190,11 @@ get_log_buffer(uint32_t size) void read_logpage(int fd, uint8_t log_page, uint32_t nsid, uint8_t lsp, - uint16_t lsi, uint8_t rae, void *payload, uint32_t payload_size) + uint16_t lsi, uint8_t rae, uint64_t lpo, uint8_t csi, uint8_t ot, + uint16_t uuid_index, void *payload, uint32_t payload_size) { struct nvme_pt_command pt; - struct nvme_error_information_entry *err_entry; - u_int i, err_pages, numd; + u_int numd; numd = payload_size / sizeof(uint32_t) - 1; memset(&pt, 0, sizeof(pt)); @@ -208,9 +208,12 @@ read_logpage(int fd, uint8_t log_page, uint32_t nsid, uint8_t lsp, pt.cmd.cdw11 = htole32( ((uint32_t)lsi << 16) | /* LSI */ (numd >> 16)); /* NUMDU */ - pt.cmd.cdw12 = 0; /* LPOL */ - pt.cmd.cdw13 = 0; /* LPOU */ - pt.cmd.cdw14 = 0; /* UUID Index */ + pt.cmd.cdw12 = htole32(lpo & 0xffffffff); /* LPOL */ + pt.cmd.cdw13 = htole32(lpo >> 32); /* LPOU */ + pt.cmd.cdw14 = htole32( + (csi << 24) | /* CSI */ + (ot << 23) | /* OT */ + uuid_index); /* UUID Index */ pt.buf = payload; pt.len = payload_size; pt.is_read = 1; @@ -218,49 +221,6 @@ read_logpage(int fd, uint8_t log_page, uint32_t nsid, uint8_t lsp, if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) err(EX_IOERR, "get log page request failed"); - /* Convert data to host endian */ - switch (log_page) { - case NVME_LOG_ERROR: - err_entry = (struct nvme_error_information_entry *)payload; - err_pages = payload_size / sizeof(struct nvme_error_information_entry); - for (i = 0; i < err_pages; i++) - nvme_error_information_entry_swapbytes(err_entry++); - break; - case NVME_LOG_HEALTH_INFORMATION: - nvme_health_information_page_swapbytes( - (struct nvme_health_information_page *)payload); - break; - case NVME_LOG_FIRMWARE_SLOT: - nvme_firmware_page_swapbytes( - (struct nvme_firmware_page *)payload); - break; - case NVME_LOG_CHANGED_NAMESPACE: - nvme_ns_list_swapbytes((struct nvme_ns_list *)payload); - break; - case NVME_LOG_DEVICE_SELF_TEST: - nvme_device_self_test_swapbytes( - (struct nvme_device_self_test_page *)payload); - break; - case NVME_LOG_COMMAND_EFFECT: - nvme_command_effects_page_swapbytes( - (struct nvme_command_effects_page *)payload); - break; - case NVME_LOG_RES_NOTIFICATION: - nvme_res_notification_page_swapbytes( - (struct nvme_res_notification_page *)payload); - break; - case NVME_LOG_SANITIZE_STATUS: - nvme_sanitize_status_page_swapbytes( - (struct nvme_sanitize_status_page *)payload); - break; - case INTEL_LOG_TEMP_STATS: - intel_log_temp_stats_swapbytes( - (struct intel_log_temp_stats *)payload); - break; - default: - break; - } - if (nvme_completion_is_error(&pt.cpl)) errx(EX_IOERR, "get log page request returned error"); } @@ -276,17 +236,17 @@ print_log_error(const struct nvme_controller_data *cdata __unused, void *buf, ui printf("Error Information Log\n"); printf("=====================\n"); - if (entry->error_count == 0) { + if (letoh(entry->error_count) == 0) { printf("No error entries found\n"); return; } - nentries = size/sizeof(struct nvme_error_information_entry); + nentries = size / sizeof(struct nvme_error_information_entry); for (i = 0; i < nentries; i++, entry++) { - if (entry->error_count == 0) + if (letoh(entry->error_count) == 0) break; - status = entry->status; + status = letoh(entry->status); p = NVME_STATUS_GET_P(status); sc = NVME_STATUS_GET_SC(status); @@ -296,9 +256,9 @@ print_log_error(const struct nvme_controller_data *cdata __unused, void *buf, ui printf("Entry %02d\n", i + 1); printf("=========\n"); - printf(" Error count: %ju\n", entry->error_count); - printf(" Submission queue ID: %u\n", entry->sqid); - printf(" Command ID: %u\n", entry->cid); + printf(" Error count: %ju\n", letoh(entry->error_count)); + printf(" Submission queue ID: %u\n", letoh(entry->sqid)); + printf(" Command ID: %u\n", letoh(entry->cid)); /* TODO: Export nvme_status_string structures from kernel? */ printf(" Status:\n"); printf(" Phase tag: %d\n", p); @@ -306,13 +266,13 @@ print_log_error(const struct nvme_controller_data *cdata __unused, void *buf, ui printf(" Status code type: %d\n", sct); printf(" More: %d\n", m); printf(" DNR: %d\n", dnr); - printf(" Error location: %u\n", entry->error_location); - printf(" LBA: %ju\n", entry->lba); - printf(" Namespace ID: %u\n", entry->nsid); - printf(" Vendor specific info: %u\n", entry->vendor_specific); - printf(" Transport type: %u\n", entry->trtype); - printf(" Command specific info:%ju\n", entry->csi); - printf(" Transport specific: %u\n", entry->ttsi); + printf(" Error location: %u\n", letoh(entry->error_location)); + printf(" LBA: %ju\n", letoh(entry->lba)); + printf(" Namespace ID: %u\n", letoh(entry->nsid)); + printf(" Vendor specific info: %u\n", letoh(entry->vendor_specific)); + printf(" Transport type: %u\n", letoh(entry->trtype)); + printf(" Command specific info:%ju\n", letoh(entry->csi)); + printf(" Transport specific: %u\n", letoh(entry->ttsi)); } } @@ -336,7 +296,7 @@ print_log_health(const struct nvme_controller_data *cdata __unused, void *buf, u uint8_t warning; int i; - warning = health->critical_warning; + warning = letoh(health->critical_warning); printf("SMART/Health Information Log\n"); printf("============================\n"); @@ -353,13 +313,13 @@ print_log_health(const struct nvme_controller_data *cdata __unused, void *buf, u printf(" Volatile memory backup: %d\n", !!(warning & NVME_CRIT_WARN_ST_VOLATILE_MEMORY_BACKUP)); printf("Temperature: "); - print_temp_K(health->temperature); + print_temp_K(letoh(health->temperature)); printf("Available spare: %u\n", - health->available_spare); + letoh(health->available_spare)); printf("Available spare threshold: %u\n", - health->available_spare_threshold); + letoh(health->available_spare_threshold)); printf("Percentage used: %u\n", - health->percentage_used); + letoh(health->percentage_used)); printf("Data units (512,000 byte) read: %s\n", uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf))); @@ -382,18 +342,18 @@ print_log_health(const struct nvme_controller_data *cdata __unused, void *buf, u printf("No. error info log entries: %s\n", uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf))); - printf("Warning Temp Composite Time: %d\n", health->warning_temp_time); - printf("Error Temp Composite Time: %d\n", health->error_temp_time); + printf("Warning Temp Composite Time: %d\n", letoh(health->warning_temp_time)); + printf("Error Temp Composite Time: %d\n", letoh(health->error_temp_time)); for (i = 0; i < 8; i++) { - if (health->temp_sensor[i] == 0) + if (letoh(health->temp_sensor[i]) == 0) continue; printf("Temperature Sensor %d: ", i + 1); - print_temp_K(health->temp_sensor[i]); + print_temp_K(letoh(health->temp_sensor[i])); } - printf("Temperature 1 Transition Count: %d\n", health->tmt1tc); - printf("Temperature 2 Transition Count: %d\n", health->tmt2tc); - printf("Total Time For Temperature 1: %d\n", health->ttftmt1); - printf("Total Time For Temperature 2: %d\n", health->ttftmt2); + printf("Temperature 1 Transition Count: %d\n", letoh(health->tmt1tc)); + printf("Temperature 2 Transition Count: %d\n", letoh(health->tmt2tc)); + printf("Total Time For Temperature 1: %d\n", letoh(health->ttftmt1)); + printf("Total Time For Temperature 2: %d\n", letoh(health->ttftmt2)); } static void @@ -406,13 +366,10 @@ print_log_firmware(const struct nvme_controller_data *cdata, void *buf, uint32_t uint16_t oacs_fw; uint8_t fw_num_slots; - afi_slot = fw->afi >> NVME_FIRMWARE_PAGE_AFI_SLOT_SHIFT; - afi_slot &= NVME_FIRMWARE_PAGE_AFI_SLOT_MASK; + afi_slot = NVMEV(NVME_FIRMWARE_PAGE_AFI_SLOT, fw->afi); - oacs_fw = (cdata->oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) & - NVME_CTRLR_DATA_OACS_FIRMWARE_MASK; - fw_num_slots = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) & - NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK; + oacs_fw = NVMEV(NVME_CTRLR_DATA_OACS_FIRMWARE, cdata->oacs); + fw_num_slots = NVMEV(NVME_CTRLR_DATA_FRMW_NUM_SLOTS, cdata->frmw); printf("Firmware Slot Log\n"); printf("=================\n"); @@ -429,15 +386,10 @@ print_log_firmware(const struct nvme_controller_data *cdata, void *buf, uint32_t else status = "Inactive"; - if (fw->revision[i] == 0LLU) + if (fw->revision[i][0] == '\0') printf("Empty\n"); else - if (isprint(*(char *)&fw->revision[i])) - printf("[%s] %.8s\n", status, - (char *)&fw->revision[i]); - else - printf("[%s] %016jx\n", status, - fw->revision[i]); + printf("[%s] %.8s\n", status, fw->revision[i]); } } @@ -452,8 +404,8 @@ print_log_ns(const struct nvme_controller_data *cdata __unused, void *buf, printf("Changed Namespace List\n"); printf("======================\n"); - for (i = 0; i < nitems(nsl->ns) && nsl->ns[i] != 0; i++) { - printf("%08x\n", nsl->ns[i]); + for (i = 0; i < nitems(nsl->ns) && letoh(nsl->ns[i]) != 0; i++) { + printf("%08x\n", letoh(nsl->ns[i])); } } @@ -471,42 +423,28 @@ print_log_command_effects(const struct nvme_controller_data *cdata __unused, printf(" Command\tLBCC\tNCC\tNIC\tCCC\tCSE\tUUID\n"); for (i = 0; i < 255; i++) { - s = ce->acs[i]; - if (((s >> NVME_CE_PAGE_CSUP_SHIFT) & - NVME_CE_PAGE_CSUP_MASK) == 0) + s = letoh(ce->acs[i]); + if (NVMEV(NVME_CE_PAGE_CSUP, s) == 0) continue; printf("Admin\t%02x\t%s\t%s\t%s\t%s\t%u\t%s\n", i, - ((s >> NVME_CE_PAGE_LBCC_SHIFT) & - NVME_CE_PAGE_LBCC_MASK) ? "Yes" : "No", - ((s >> NVME_CE_PAGE_NCC_SHIFT) & - NVME_CE_PAGE_NCC_MASK) ? "Yes" : "No", - ((s >> NVME_CE_PAGE_NIC_SHIFT) & - NVME_CE_PAGE_NIC_MASK) ? "Yes" : "No", - ((s >> NVME_CE_PAGE_CCC_SHIFT) & - NVME_CE_PAGE_CCC_MASK) ? "Yes" : "No", - ((s >> NVME_CE_PAGE_CSE_SHIFT) & - NVME_CE_PAGE_CSE_MASK), - ((s >> NVME_CE_PAGE_UUID_SHIFT) & - NVME_CE_PAGE_UUID_MASK) ? "Yes" : "No"); + NVMEV(NVME_CE_PAGE_LBCC, s) != 0 ? "Yes" : "No", + NVMEV(NVME_CE_PAGE_NCC, s) != 0 ? "Yes" : "No", + NVMEV(NVME_CE_PAGE_NIC, s) != 0 ? "Yes" : "No", + NVMEV(NVME_CE_PAGE_CCC, s) != 0 ? "Yes" : "No", + NVMEV(NVME_CE_PAGE_CSE, s), + NVMEV(NVME_CE_PAGE_UUID, s) != 0 ? "Yes" : "No"); } for (i = 0; i < 255; i++) { - s = ce->iocs[i]; - if (((s >> NVME_CE_PAGE_CSUP_SHIFT) & - NVME_CE_PAGE_CSUP_MASK) == 0) + s = letoh(ce->iocs[i]); + if (NVMEV(NVME_CE_PAGE_CSUP, s) == 0) continue; printf("I/O\t%02x\t%s\t%s\t%s\t%s\t%u\t%s\n", i, - ((s >> NVME_CE_PAGE_LBCC_SHIFT) & - NVME_CE_PAGE_LBCC_MASK) ? "Yes" : "No", - ((s >> NVME_CE_PAGE_NCC_SHIFT) & - NVME_CE_PAGE_NCC_MASK) ? "Yes" : "No", - ((s >> NVME_CE_PAGE_NIC_SHIFT) & - NVME_CE_PAGE_NIC_MASK) ? "Yes" : "No", - ((s >> NVME_CE_PAGE_CCC_SHIFT) & - NVME_CE_PAGE_CCC_MASK) ? "Yes" : "No", - ((s >> NVME_CE_PAGE_CSE_SHIFT) & - NVME_CE_PAGE_CSE_MASK), - ((s >> NVME_CE_PAGE_UUID_SHIFT) & - NVME_CE_PAGE_UUID_MASK) ? "Yes" : "No"); + NVMEV(NVME_CE_PAGE_LBCC, s) != 0 ? "Yes" : "No", + NVMEV(NVME_CE_PAGE_NCC, s) != 0 ? "Yes" : "No", + NVMEV(NVME_CE_PAGE_NIC, s) != 0 ? "Yes" : "No", + NVMEV(NVME_CE_PAGE_CCC, s) != 0 ? "Yes" : "No", + NVMEV(NVME_CE_PAGE_CSE, s), + NVMEV(NVME_CE_PAGE_UUID, s) != 0 ? "Yes" : "No"); } } @@ -520,9 +458,10 @@ print_log_res_notification(const struct nvme_controller_data *cdata __unused, printf("Reservation Notification\n"); printf("========================\n"); - printf("Log Page Count: %ju\n", rn->log_page_count); + printf("Log Page Count: %ju\n", + (uintmax_t)letoh(rn->log_page_count)); printf("Log Page Type: "); - switch (rn->log_page_type) { + switch (letoh(rn->log_page_type)) { case 0: printf("Empty Log Page\n"); break; @@ -536,11 +475,11 @@ print_log_res_notification(const struct nvme_controller_data *cdata __unused, printf("Reservation Preempted\n"); break; default: - printf("Unknown %x\n", rn->log_page_type); + printf("Unknown %x\n", letoh(rn->log_page_type)); break; }; - printf("Number of Available Log Pages: %d\n", rn->available_log_pages); - printf("Namespace ID: 0x%x\n", rn->nsid); + printf("Number of Available Log Pages: %d\n", letoh(rn->available_log_pages)); + printf("Namespace ID: 0x%x\n", letoh(rn->nsid)); } static void @@ -549,16 +488,18 @@ print_log_sanitize_status(const struct nvme_controller_data *cdata __unused, { struct nvme_sanitize_status_page *ss; u_int p; + uint16_t sprog, sstat; ss = (struct nvme_sanitize_status_page *)buf; printf("Sanitize Status\n"); printf("===============\n"); + sprog = letoh(ss->sprog); printf("Sanitize Progress: %u%% (%u/65535)\n", - (ss->sprog * 100 + 32768) / 65536, ss->sprog); + (sprog * 100 + 32768) / 65536, sprog); printf("Sanitize Status: "); - switch ((ss->sstat >> NVME_SS_PAGE_SSTAT_STATUS_SHIFT) & - NVME_SS_PAGE_SSTAT_STATUS_MASK) { + sstat = letoh(ss->sstat); + switch (NVMEV(NVME_SS_PAGE_SSTAT_STATUS, sstat)) { case NVME_SS_PAGE_SSTAT_STATUS_NEVER: printf("Never sanitized"); break; @@ -575,24 +516,22 @@ print_log_sanitize_status(const struct nvme_controller_data *cdata __unused, printf("Completed with deallocation"); break; default: - printf("Unknown"); + printf("Unknown 0x%x", sstat); break; } - p = (ss->sstat >> NVME_SS_PAGE_SSTAT_PASSES_SHIFT) & - NVME_SS_PAGE_SSTAT_PASSES_MASK; + p = NVMEV(NVME_SS_PAGE_SSTAT_PASSES, sstat); if (p > 0) printf(", %d passes", p); - if ((ss->sstat >> NVME_SS_PAGE_SSTAT_GDE_SHIFT) & - NVME_SS_PAGE_SSTAT_GDE_MASK) + if (NVMEV(NVME_SS_PAGE_SSTAT_GDE, sstat) != 0) printf(", Global Data Erased"); printf("\n"); - printf("Sanitize Command Dword 10: 0x%x\n", ss->scdw10); - printf("Time For Overwrite: %u sec\n", ss->etfo); - printf("Time For Block Erase: %u sec\n", ss->etfbe); - printf("Time For Crypto Erase: %u sec\n", ss->etfce); - printf("Time For Overwrite No-Deallocate: %u sec\n", ss->etfownd); - printf("Time For Block Erase No-Deallocate: %u sec\n", ss->etfbewnd); - printf("Time For Crypto Erase No-Deallocate: %u sec\n", ss->etfcewnd); + printf("Sanitize Command Dword 10: 0x%x\n", letoh(ss->scdw10)); + printf("Time For Overwrite: %u sec\n", letoh(ss->etfo)); + printf("Time For Block Erase: %u sec\n", letoh(ss->etfbe)); + printf("Time For Crypto Erase: %u sec\n", letoh(ss->etfce)); + printf("Time For Overwrite No-Deallocate: %u sec\n", letoh(ss->etfownd)); + printf("Time For Block Erase No-Deallocate: %u sec\n", letoh(ss->etfbewnd)); + printf("Time For Crypto Erase No-Deallocate: %u sec\n", letoh(ss->etfcewnd)); } static const char * @@ -616,13 +555,14 @@ print_log_self_test_status(const struct nvme_controller_data *cdata __unused, { struct nvme_device_self_test_page *dst; uint32_t r; + uint16_t vs; dst = buf; printf("Device Self-test Status\n"); printf("=======================\n"); printf("Current Operation: "); - switch (dst->curr_operation) { + switch (letoh(dst->curr_operation)) { case 0x0: printf("No device self-test operation in progress\n"); break; @@ -636,19 +576,20 @@ print_log_self_test_status(const struct nvme_controller_data *cdata __unused, printf("Vendor specific\n"); break; default: - printf("Reserved (0x%x)\n", dst->curr_operation); + printf("Reserved (0x%x)\n", letoh(dst->curr_operation)); } - if (dst->curr_operation != 0) - printf("Current Completion: %u%%\n", dst->curr_compl & 0x7f); + if (letoh(dst->curr_operation) != 0) + printf("Current Completion: %u%%\n", letoh(dst->curr_compl) & 0x7f); printf("Results\n"); for (r = 0; r < 20; r++) { uint64_t failing_lba; - uint8_t code, res; + uint8_t code, res, status; - code = (dst->result[r].status >> 4) & 0xf; - res = dst->result[r].status & 0xf; + status = letoh(dst->result[r].status); + code = (status >> 4) & 0xf; + res = status & 0xf; if (res == 0xf) continue; @@ -673,21 +614,24 @@ print_log_self_test_status(const struct nvme_controller_data *cdata __unused, printf(" Reserved status 0x%x", res); if (res == 7) - printf(" starting in segment %u", dst->result[r].segment_num); + printf(" starting in segment %u", + letoh(dst->result[r].segment_num)); #define BIT(b) (1 << (b)) - if (dst->result[r].valid_diag_info & BIT(0)) - printf(" NSID=0x%x", dst->result[r].nsid); - if (dst->result[r].valid_diag_info & BIT(1)) { + if (letoh(dst->result[r].valid_diag_info) & BIT(0)) + printf(" NSID=0x%x", letoh(dst->result[r].nsid)); + if (letoh(dst->result[r].valid_diag_info) & BIT(1)) { memcpy(&failing_lba, dst->result[r].failing_lba, sizeof(failing_lba)); - printf(" FLBA=0x%jx", failing_lba); + printf(" FLBA=0x%jx", (uintmax_t)letoh(failing_lba)); } - if (dst->result[r].valid_diag_info & BIT(2)) - printf(" SCT=0x%x", dst->result[r].status_code_type); - if (dst->result[r].valid_diag_info & BIT(3)) - printf(" SC=0x%x", dst->result[r].status_code); + if (letoh(dst->result[r].valid_diag_info) & BIT(2)) + printf(" SCT=0x%x", letoh(dst->result[r].status_code_type)); + if (letoh(dst->result[r].valid_diag_info) & BIT(3)) + printf(" SC=0x%x", letoh(dst->result[r].status_code)); #undef BIT + memcpy(&vs, dst->result[r].vendor_specific, sizeof(vs)); + printf(" VENDOR_SPECIFIC=0x%x", letoh(vs)); printf("\n"); } } @@ -805,8 +749,7 @@ logpage(const struct cmd *f, int argc, char *argv[]) if (read_controller_data(fd, &cdata)) errx(EX_IOERR, "Identify request failed"); - ns_smart = (cdata.lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) & - NVME_CTRLR_DATA_LPA_NS_SMART_MASK; + ns_smart = NVMEV(NVME_CTRLR_DATA_LPA_NS_SMART, cdata.lpa); /* * The log page attributes indicate whether or not the controller @@ -855,7 +798,8 @@ logpage(const struct cmd *f, int argc, char *argv[]) /* Read the log page */ buf = get_log_buffer(size); - read_logpage(fd, opt.page, nsid, opt.lsp, opt.lsi, opt.rae, buf, size); + read_logpage(fd, opt.page, nsid, opt.lsp, opt.lsi, opt.rae, + 0, 0, 0, 0, buf, size); print_fn(&cdata, buf, size); close(fd); diff --git a/sbin/nvmecontrol/modules/Makefile b/sbin/nvmecontrol/modules/Makefile index f89b8dc84ab6..f3c3572acb34 100644 --- a/sbin/nvmecontrol/modules/Makefile +++ b/sbin/nvmecontrol/modules/Makefile @@ -1,4 +1,6 @@ - -SUBDIR= intel wdc samsung +SUBDIR= intel +SUBDIR+=micron +SUBDIR+=samsung +SUBDIR+=wdc .include <bsd.subdir.mk> diff --git a/sbin/nvmecontrol/modules/Makefile.inc b/sbin/nvmecontrol/modules/Makefile.inc index f949af979454..030b488e44e0 100644 --- a/sbin/nvmecontrol/modules/Makefile.inc +++ b/sbin/nvmecontrol/modules/Makefile.inc @@ -1,9 +1,7 @@ - PACKAGE=nvme-tools NVMECONTROLDIR= ${SRCTOP}/sbin/nvmecontrol MK_INSTALLLIB= no -MK_PROFILE= no CFLAGS+= -I${NVMECONTROLDIR} diff --git a/sbin/nvmecontrol/modules/intel/Makefile b/sbin/nvmecontrol/modules/intel/Makefile index 3191980c5623..9e4eafa0feec 100644 --- a/sbin/nvmecontrol/modules/intel/Makefile +++ b/sbin/nvmecontrol/modules/intel/Makefile @@ -1,4 +1,3 @@ - LIB= intel SRCS= intel.c diff --git a/sbin/nvmecontrol/modules/intel/intel.c b/sbin/nvmecontrol/modules/intel/intel.c index cc5c9c49beb5..6ffe2c4c1563 100644 --- a/sbin/nvmecontrol/modules/intel/intel.c +++ b/sbin/nvmecontrol/modules/intel/intel.c @@ -29,7 +29,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioccom.h> @@ -62,18 +61,21 @@ print_intel_temp_stats(const struct nvme_controller_data *cdata __unused, void * printf("=====================\n"); printf("Current: "); - print_temp_C(temp->current); - printf("Overtemp Last Flags %#jx\n", (uintmax_t)temp->overtemp_flag_last); - printf("Overtemp Lifetime Flags %#jx\n", (uintmax_t)temp->overtemp_flag_life); + print_temp_C(letoh(temp->current)); + printf("Overtemp Last Flags %#jx\n", + (uintmax_t)letoh(temp->overtemp_flag_last)); + printf("Overtemp Lifetime Flags %#jx\n", + (uintmax_t)letoh(temp->overtemp_flag_life)); printf("Max Temperature "); - print_temp_C(temp->max_temp); + print_temp_C(letoh(temp->max_temp)); printf("Min Temperature "); - print_temp_C(temp->min_temp); + print_temp_C(letoh(temp->min_temp)); printf("Max Operating Temperature "); - print_temp_C(temp->max_oper_temp); + print_temp_C(letoh(temp->max_oper_temp)); printf("Min Operating Temperature "); - print_temp_C(temp->min_oper_temp); - printf("Estimated Temperature Offset: %ju C/K\n", (uintmax_t)temp->est_offset); + print_temp_C(letoh(temp->min_oper_temp)); + printf("Estimated Temperature Offset: %ju C/K\n", + (uintmax_t)letoh(temp->est_offset)); } /* @@ -116,6 +118,8 @@ print_intel_write_lat_log(const struct nvme_controller_data *cdata __unused, voi /* * Table 19. 5.4 SMART Attributes. Others also implement this and some extra data not documented. + * Note: different models implement the same key values to mean different things. To fix that, + * we'd need to index this to a vendor/device values. */ void print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) @@ -136,11 +140,15 @@ print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *b { 0xe2, "Timed: Media Wear" }, { 0xe3, "Timed: Host Read %" }, { 0xe4, "Timed: Elapsed Time" }, + { 0xe7, "Lifetime Temperature" }, + { 0xe8, "Power" }, { 0xea, "Thermal Throttle Status" }, { 0xf0, "Retry Buffer Overflows" }, { 0xf3, "PLL Lock Loss Count" }, { 0xf4, "NAND Bytes Written" }, { 0xf5, "Host Bytes Written" }, + { 0xf9, "NAND GiB Written" }, + { 0xfa, "NAND GiB Read" }, }; printf("Additional SMART Data Log\n"); @@ -168,17 +176,37 @@ print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *b case 0xe2: printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0); break; + case 0xe7: + printf("%-32s: %3d %#jx max: %dK min: %dK cur: %dK\n", name, normalized, + (uintmax_t)raw, le16dec(walker+5), le16dec(walker+7), le16dec(walker+9)); + break; + case 0xe8: + printf("%-32s: %3d %#jx max: %dW min: %dW cur: %dW\n", name, normalized, + (uintmax_t)raw, le16dec(walker+5), le16dec(walker+7), le16dec(walker+9)); + break; case 0xea: printf("%-32s: %3d %d%% %d times\n", name, normalized, walker[5], le32dec(walker+6)); break; default: - printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw); + printf("%-32s: %3d %ju %#jx\n", name, normalized, (uintmax_t)raw, (uintmax_t)raw); break; } walker += 12; } } +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)); @@ -188,6 +216,9 @@ NVME_LOGPAGE(intel_rlat, NVME_LOGPAGE(intel_wlat, INTEL_LOG_WRITE_LAT_LOG, "intel", "Write Latencies", print_intel_write_lat_log, DEFAULT_SIZE); -NVME_LOGPAGE(intel_smart, +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/modules/samsung/Makefile b/sbin/nvmecontrol/modules/samsung/Makefile index 353a2d141508..750dd07cbeb4 100644 --- a/sbin/nvmecontrol/modules/samsung/Makefile +++ b/sbin/nvmecontrol/modules/samsung/Makefile @@ -1,4 +1,3 @@ - LIB= samsung SRCS= samsung.c diff --git a/sbin/nvmecontrol/modules/samsung/samsung.c b/sbin/nvmecontrol/modules/samsung/samsung.c index cc4528a0a78c..82e7af8ecd33 100644 --- a/sbin/nvmecontrol/modules/samsung/samsung.c +++ b/sbin/nvmecontrol/modules/samsung/samsung.c @@ -25,7 +25,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioccom.h> diff --git a/sbin/nvmecontrol/modules/wdc/Makefile b/sbin/nvmecontrol/modules/wdc/Makefile index 9a71b2b2a802..8f8185d5f7ad 100644 --- a/sbin/nvmecontrol/modules/wdc/Makefile +++ b/sbin/nvmecontrol/modules/wdc/Makefile @@ -1,4 +1,3 @@ - LIB= wdc SRCS= wdc.c diff --git a/sbin/nvmecontrol/modules/wdc/wdc.c b/sbin/nvmecontrol/modules/wdc/wdc.c index 2600464eeb0a..55758dc7e037 100644 --- a/sbin/nvmecontrol/modules/wdc/wdc.c +++ b/sbin/nvmecontrol/modules/wdc/wdc.c @@ -23,7 +23,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioccom.h> #include <sys/endian.h> diff --git a/sbin/nvmecontrol/ns.c b/sbin/nvmecontrol/ns.c index dd678568d5ca..c538338eb6c3 100644 --- a/sbin/nvmecontrol/ns.c +++ b/sbin/nvmecontrol/ns.c @@ -26,7 +26,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioccom.h> @@ -334,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, @@ -417,8 +416,7 @@ nsactive(const struct cmd *f, int argc, char *argv[]) errx(EX_IOERR, "Identify request failed"); /* Check that controller can execute this command. */ - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); @@ -463,8 +461,7 @@ nsallocated(const struct cmd *f, int argc, char *argv[]) errx(EX_IOERR, "Identify request failed"); /* Check that controller can execute this command. */ - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); @@ -509,8 +506,7 @@ nscontrollers(const struct cmd *f, int argc, char *argv[]) errx(EX_IOERR, "Identify request failed"); /* Check that controller can execute this command. */ - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); @@ -535,7 +531,7 @@ nscontrollers(const struct cmd *f, int argc, char *argv[]) /* * NS MGMT Command specific status values: * 0xa = Invalid Format - * 0x15 = Namespace Insuffience capacity + * 0x15 = Namespace Insufficient capacity * 0x16 = Namespace ID unavailable (number namespaces exceeded) * 0xb = Thin Provisioning Not supported */ @@ -571,8 +567,7 @@ nscreate(const struct cmd *f, int argc, char *argv[]) errx(EX_IOERR, "Identify request failed"); /* Check that controller can execute this command. */ - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support namespace management"); memset(&nsdata, 0, sizeof(nsdata)); @@ -584,14 +579,12 @@ nscreate(const struct cmd *f, int argc, char *argv[]) /* Default to the first format, whatever it is. */ nsdata.flbas = 0; if (create_opt.lbaf != NONE) { - nsdata.flbas |= (create_opt.lbaf & - NVME_NS_DATA_FLBAS_FORMAT_MASK) - << NVME_NS_DATA_FLBAS_FORMAT_SHIFT; + nsdata.flbas |= NVMEF(NVME_NS_DATA_FLBAS_FORMAT, + create_opt.lbaf); } if (create_opt.mset != NONE) { - nsdata.flbas |= (create_opt.mset & - NVME_NS_DATA_FLBAS_EXTENDED_MASK) - << NVME_NS_DATA_FLBAS_EXTENDED_SHIFT; + nsdata.flbas |= NVMEF(NVME_NS_DATA_FLBAS_EXTENDED, + create_opt.mset); } } if (create_opt.dps != NONE) { @@ -600,22 +593,19 @@ nscreate(const struct cmd *f, int argc, char *argv[]) /* Default to protection disabled. */ nsdata.dps = 0; if (create_opt.pi != NONE) { - nsdata.dps |= (create_opt.pi & - NVME_NS_DATA_DPS_MD_START_MASK) - << NVME_NS_DATA_DPS_MD_START_SHIFT; + nsdata.dps |= NVMEF(NVME_NS_DATA_DPS_MD_START, + create_opt.pi); } if (create_opt.pil != NONE) { - nsdata.dps |= (create_opt.pil & - NVME_NS_DATA_DPS_PIT_MASK) - << NVME_NS_DATA_DPS_PIT_SHIFT; + nsdata.dps |= NVMEF(NVME_NS_DATA_DPS_PIT, + create_opt.pil); } } if (create_opt.nmic != NONE) { nsdata.nmic = create_opt.nmic; } else { /* Allow namespaces sharing if Multi-Path I/O is supported. */ - nsdata.nmic = cd.mic ? (NVME_NS_DATA_NMIC_MAY_BE_SHARED_MASK << - NVME_NS_DATA_NMIC_MAY_BE_SHARED_SHIFT) : 0; + nsdata.nmic = NVMEF(NVME_NS_DATA_NMIC_MAY_BE_SHARED, !!cd.mic); } nvme_namespace_data_swapbytes(&nsdata); @@ -630,8 +620,7 @@ nscreate(const struct cmd *f, int argc, char *argv[]) if (nvme_completion_is_error(&pt.cpl)) { errx(EX_IOERR, "namespace creation failed: %s", - get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & - NVME_STATUS_SC_MASK)); + get_res_str(NVMEV(NVME_STATUS_SC, pt.cpl.status))); } printf("namespace %d created\n", pt.cpl.cdw0); exit(0); @@ -657,7 +646,7 @@ nsdelete(const struct cmd *f, int argc, char *argv[]) open_dev(path, &fd, 1, 1); } else if (delete_opt.nsid == NONE - 1) { close(fd); - fprintf(stderr, "No NSID specified"); + fprintf(stderr, "No NSID specified\n"); arg_help(argc, argv, f); } if (delete_opt.nsid != NONE - 1) @@ -667,8 +656,7 @@ nsdelete(const struct cmd *f, int argc, char *argv[]) errx(EX_IOERR, "Identify request failed"); /* Check that controller can execute this command. */ - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); @@ -684,8 +672,7 @@ nsdelete(const struct cmd *f, int argc, char *argv[]) if (nvme_completion_is_error(&pt.cpl)) { errx(EX_IOERR, "namespace deletion failed: %s", - get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & - NVME_STATUS_SC_MASK)); + get_res_str(NVMEV(NVME_STATUS_SC, pt.cpl.status))); } printf("namespace %d deleted\n", nsid); exit(0); @@ -725,7 +712,7 @@ nsattach(const struct cmd *f, int argc, char *argv[]) open_dev(path, &fd, 1, 1); } else if (attach_opt.nsid == NONE) { close(fd); - fprintf(stderr, "No NSID specified"); + fprintf(stderr, "No NSID specified\n"); arg_help(argc, argv, f); } if (attach_opt.nsid != NONE) @@ -734,8 +721,7 @@ nsattach(const struct cmd *f, int argc, char *argv[]) errx(EX_IOERR, "Identify request failed"); /* Check that controller can execute this command. */ - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support namespace management"); if (attach_opt.ctrlrid == NONE) { @@ -771,8 +757,7 @@ nsattach(const struct cmd *f, int argc, char *argv[]) if (nvme_completion_is_error(&pt.cpl)) { errx(EX_IOERR, "namespace attach failed: %s", - get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & - NVME_STATUS_SC_MASK)); + get_res_str(NVMEV(NVME_STATUS_SC, pt.cpl.status))); } printf("namespace %d attached\n", nsid); exit(0); @@ -797,7 +782,7 @@ nsdetach(const struct cmd *f, int argc, char *argv[]) open_dev(path, &fd, 1, 1); } else if (detach_opt.nsid == NONE) { close(fd); - fprintf(stderr, "No NSID specified"); + fprintf(stderr, "No NSID specified\n"); arg_help(argc, argv, f); } if (detach_opt.nsid != NONE) @@ -806,8 +791,7 @@ nsdetach(const struct cmd *f, int argc, char *argv[]) errx(EX_IOERR, "Identify request failed"); /* Check that controller can execute this command. */ - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support namespace management"); if (detach_opt.ctrlrid == NONE) { @@ -850,8 +834,7 @@ nsdetach(const struct cmd *f, int argc, char *argv[]) if (nvme_completion_is_error(&pt.cpl)) { errx(EX_IOERR, "namespace detach failed: %s", - get_res_str((pt.cpl.status >> NVME_STATUS_SC_SHIFT) & - NVME_STATUS_SC_MASK)); + get_res_str(NVMEV(NVME_STATUS_SC, pt.cpl.status))); } printf("namespace %d detached\n", nsid); exit(0); @@ -876,7 +859,7 @@ nsattached(const struct cmd *f, int argc, char *argv[]) open_dev(path, &fd, 1, 1); } else if (attached_opt.nsid == NONE) { close(fd); - fprintf(stderr, "No NSID specified"); + fprintf(stderr, "No NSID specified\n"); arg_help(argc, argv, f); } if (attached_opt.nsid != NONE) @@ -885,8 +868,7 @@ nsattached(const struct cmd *f, int argc, char *argv[]) errx(EX_IOERR, "Identify request failed"); /* Check that controller can execute this command. */ - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); @@ -930,7 +912,7 @@ nsidentify(const struct cmd *f, int argc, char *argv[]) open_dev(path, &fd, 1, 1); } else if (identify_opt.nsid == NONE) { close(fd); - fprintf(stderr, "No NSID specified"); + fprintf(stderr, "No NSID specified\n"); arg_help(argc, argv, f); } if (identify_opt.nsid != NONE) @@ -939,8 +921,7 @@ nsidentify(const struct cmd *f, int argc, char *argv[]) errx(EX_IOERR, "Identify request failed"); /* Check that controller can execute this command. */ - if (((cd.oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) & - NVME_CTRLR_DATA_OACS_NSMGMT_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_NSMGMT, cd.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support namespace management"); memset(&pt, 0, sizeof(pt)); diff --git a/sbin/nvmecontrol/nsid.c b/sbin/nvmecontrol/nsid.c index 3d71186f396a..c49cec246737 100644 --- a/sbin/nvmecontrol/nsid.c +++ b/sbin/nvmecontrol/nsid.c @@ -25,7 +25,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <stdio.h> diff --git a/sbin/nvmecontrol/nvmecontrol.8 b/sbin/nvmecontrol/nvmecontrol.8 index c4ab4429f5cd..dc757bcf90c3 100644 --- a/sbin/nvmecontrol/nvmecontrol.8 +++ b/sbin/nvmecontrol/nvmecontrol.8 @@ -33,7 +33,7 @@ .\" .\" Author: Jim Harris <jimharris@FreeBSD.org> .\" -.Dd December 14, 2022 +.Dd July 9, 2025 .Dt NVMECONTROL 8 .Os .Sh NAME @@ -155,7 +155,7 @@ .Ic format .Op Fl f Ar fmt .Op Fl m Ar mset -.Op Fl o Ar pi +.Op Fl p Ar pi .Op Fl l Ar pil .Op Fl E .Op Fl C @@ -205,9 +205,62 @@ .Ic io-passthru .Op args .Aq Ar namespace-id +.Nm +.Ic discover +.Op Fl v +.Op Fl t Ar transport +.Op Fl q Ar HostNQN +.Nm +.Ic connect +.Op Fl FGg +.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 +.Aq Ar address +.Aq Ar SubNQN +.Nm +.Ic connect-all +.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 +.Aq Ar address +.Nm +.Ic disconnect +.Aq Ar device-id | Ar namespace-id | Ar SubNQN +.Nm +.Ic reconnect +.Aq Ar device-id +.Nm +.Ic reconnect +.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 +.Aq Ar device-id +.Aq Ar address +.Nm +.Ic telemetry-log +.Fl O Ar output-file +.Op Fl d Ar data-area +.Aq Ar device-id .Sh DESCRIPTION -NVM Express (NVMe) is a storage protocol standard, for SSDs and other -high-speed storage devices over PCI Express. +NVM Express (NVMe) is a storage protocol standard for SSDs and other +high-speed storage devices over PCI Express as well as remote storage +devices accessed via a network fabric. .Ss devlist List all NVMe controllers and namespaces along with their device nodes. With the @@ -250,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" @@ -275,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 @@ -302,6 +358,47 @@ will set Retain Asynchronous Event. Various namespace management commands. If namespace management is supported by device, allow list, create and delete namespaces, list, attach and detach controllers to namespaces. +Each NVM device consists of one or more NVM subsystems. +Each NVM subsystem has one or more NVM ports. +Each NVM port is attached to one or more NVM controllers (though typically 1). +Each NVM controller is attached to one or more namespaces. +.Pp +After a namespace is created, it is considered +.Dq allocated . +All namespaces that have not been created are unallocated. +An allocated namespace may be active or inactive. +An active namespace is attached to the controller and may be interacted with. +A namespace can move from active to inactive when detached. +An allocated namespace may be deleted to become unallocated. +For more details on the nuances of NVM namespaces, please see section 2 +.Em Theory of Operation +and section 3 +.Em NVM Express Architecture +of the latest NVM standard. +.Ss ns active +Provide a list of active namespace identifiers for the givne NVM controller. +.Ss ns allocated +Provide a list of allocated namespace identifiers for the givne NVM controller. +.Ss ns attach +Attach an nsid to a controller. +The primary controller is used if one is not specified. +.Ss ns attached +Provide a list of controllers attached to a nsid. +If only a nvme controller argument is provided, a nsid must also be specified. +.Ss ns controllers +Provide a list of all controllers in the NVM subsystem. +.Ss ns create +Creates a new namespace. +.Ss ns delete +Delete a namespace. +It must be currently inactive. +.Ss ns detach +Detach a namespace from a controller. +The namespace will become inaccessible, but its contents will remain if it is +.Em activated +again. +.Ss ns identify +Print detailed information about the namespace. .Ss nsid Reports the namespace id and controller device associated with the .Aq Ar namespace-id @@ -401,24 +498,62 @@ Use Extended Data Structure. .Ss format Format either specified namespace, or all namespaces of specified controller, using specified parameters: +.Bl -tag -width 8n +.It Fl f Ar fmt +The index .Ar fmt -LBA Format, +of the parameters to use. +LBA Format #, as specified in the identification of the namespace using +.Dq nvmecontrol identify +command with a namespace specified maps this index into these parameters. +.It Fl m Ar mset +Metadata Setting. .Ar mset -Metadata Settings, -.Ar pi -Protection Information, -.Ar pil +.Bl -tag -compact -width 6n +.It Dv 0 +do not transfer metadata with LBA information +.It Dv 1 +Transfer the metadata as part of the extended LBA information. +.El +.It Fl p Ar pi +Protection Information. +.Bl -tag -compact -width 6n +.It Dv 0 +Protection Information not enabled. +.It Dv 1 +Type 1 information protection enabled. +.It Dv 2 +Type 2 information protection enabled. +.It Dv 3 +Type 3 information protection enabled. +.El +.It Fl l Ar pil Protection Information Location. +.Bl -tag -compact -width 6n +.It Dv 0 +Transfer the protection metadata as the last N bytes of the transfer. +.It Dv 1 +Transfer the protection metadata as the first N bytes of the transfer. +.El +.It Fl E +Enables User Data Erase during format. +All users data is erased and subsequent reads are indeterminate. +The drive may implement this as a cryptographic erase or it may +physically erase the underlying media. +.It Fl C +Enables Cryptographic Erase during format. +All user data is erased cryptographically by deleting the encryption key, +rendering it unintelligible. +.El +.Pp When formatting specific namespace, existing values are used as defaults. When formatting all namespaces, all parameters should be specified. Some controllers may not support formatting or erasing specific or all namespaces. -Option -.Fl E -enables User Data Erase during format. -Option -.Fl C -enables Cryptographic Erase during format. +The +.Xr nvme 4 +driver does not currently support metadata and protection information +transfers. .Ss sanitize Sanitize NVM subsystem of specified controller, using specified parameters: @@ -449,6 +584,16 @@ A failed sanitize operation can only be exited if it was run in the unrestricted completion mode, as provided by the .Fl U argument. +.It 1, 2, 3, 4 +nvme-cli compatible +.Fl a +values for +.Dq exitfailure , +.Dq block , +.Dq overwrite , +and +.Dq crypto +respectively. .El .It Fl c Ar passes The number of passes when performing an @@ -597,6 +742,140 @@ Commands either read data or write it, but not both. Commands needing metadata are not supported by the .Xr nvme 4 drive. +.Ss discover +List the remote controllers advertised by a remote Discovery Controller: +.Bl -tag -width 6n +.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. +.It Fl v +Display the +.Dv IDENTIFY_CONTROLLER +data for the Discovery Controller. +.El +.Ss connect +Establish an association with the I/O controller named +.Ar SubNQN +at +.Ar address . +The address must include a port. +.Pp +An admin queue pair and one or more I/O queue pairs are created and handed +off to the kernel to create a new controller device. +.Bl -tag -width 6n +.It Fl c Ar cntl-id +Remote controller ID to request: +.Bl -tag +.It dynamic +Request a dynamic controller ID for controllers using the dynamic +controller model. +This is the default. +.It static +Request a dynamic controller ID for controllers using the static +controller model. +.It Ar number +Request a specific controller ID for controllers using the static +controller model. +.El +.It Fl F +Request submission queue flow control. +By default submission queue flow control is disabled unless the remote +controller requires it. +.It Fl g +Enable TCP PDU header digests. +.It Fl G +Enable TCP PDU data digests. +.It Fl i Ar queues +Number of I/O queue pairs to create. +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 +.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. +.It Fl Q Ar entries +Number of entries in each I/O queue. +By default the maximum queue size reported by the MQES field +of the remote host's CAP property is used. +.El +.Ss connect-all +Query the Discovery Controller at +.Ar address +and establish an association for each advertised I/O controller. +The +.Fl t +flag determines the transport used for the initial association with +the Discovery Controller and defaults to +.Ar tcp . +All other flags are used to control properties of each I/O assocation as +described above for the +.Cm connect +command. +.Ss disconnect +Delete the controller device associated with a remote I/O controller +including any active association and open queues. +.Ss reconnect +Reestablish an association for the remote I/O controller associated with +.Ar device-id . +If an +.Ar address +is not provided, +the resolved address and settings from the previous association are used +to establish a new association. +If an +.Ar address +is provided, +the supplied address and command line flags are used to establish a new +association. +In this case, +the address must include a port and +the flags have the same meaning for the new association as described above +for the +.Cm connect +command. +.Ss telemetry-log +Extract the telemetry log associated with +.Ar device-id , +using the specified parameters: +.Bl -tag -width 6n +.It Fl O Ar output-file +Output file for the data. +This parameter is mandatory. +.It Fl d Ar data-area +The data area is either 1, 2 or 3. +.El .Sh DEVICE NAMES Where .Aq Ar namespace-id @@ -608,7 +887,7 @@ or .Pa nvdZ . The leading .Pa /dev/ -is omitted. +may be omitted. Where .Aq Ar device-id is required, you can use either the @@ -626,6 +905,37 @@ A of .Dq 0 means query the drive itself. +.Sh FABRICS TRANSPORTS +The following NVM Express over Fabrics transports are supported for +accessing remote controllers: +.Bl -tag +.It tcp +TCP transport +.El +.Sh NETWORK ADDRESSES +Network addresses for remote controllers can use one of the following formats: +.Bl -bullet +.It +.Bq Ar IPv6 address +.Ns : Ns Ar port +.It +.Ar IPv4 address +.Ns : Ns Ar port +.It +.Ar hostname Ns : Ns Ar port +.It +.Bq Ar IPv6 address +.It +.Ar IPv6 address +.It +.Ar IPv4 address +.It +.Ar hostname +.El +.Pp +If a +.Ar port +is not provided, a default value is used if possible. .Sh EXAMPLES .Dl nvmecontrol devlist .Pp @@ -722,6 +1032,27 @@ device. The corresponding .Pa nvmeXnsY device is used automatically. +.Pp +.Dl nvmecontrol format -f 2 -m 0 -p 0 -l 0 -C nvme2 +.Pp +Format all the name spaces on nvme2 using parameters from +.Dq LBA Format #2 +with no metadata or protection data using cryptographic erase. +If the +.Dq nvmecontrol identify -n 1 nvme2 +command ended with +.Pp +.Bd -literal +LBA Format #00: Data Size: 512 Metadata Size: 0 Performance: Good +LBA Format #01: Data Size: 512 Metadata Size: 8 Performance: Good +LBA Format #02: Data Size: 4096 Metadata Size: 0 Performance: Good +LBA Format #03: Data Size: 4096 Metadata Size: 8 Performance: Good +LBA Format #04: Data Size: 4096 Metadata Size: 64 Performance: Good +.Ed +.Pp +then this would give a 4k data format for at least namespace 1, with no +metadata. +.Pp .Sh DYNAMIC LOADING The directories .Pa /lib/nvmecontrol diff --git a/sbin/nvmecontrol/nvmecontrol.c b/sbin/nvmecontrol/nvmecontrol.c index 92fbf9e25378..b38fb8038fc3 100644 --- a/sbin/nvmecontrol/nvmecontrol.c +++ b/sbin/nvmecontrol/nvmecontrol.c @@ -26,7 +26,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioccom.h> #include <sys/stat.h> @@ -144,11 +143,38 @@ read_namespace_data(int fd, uint32_t nsid, struct nvme_namespace_data *nsdata) } int +read_active_namespaces(int fd, uint32_t nsid, struct nvme_ns_list *nslist) +{ + struct nvme_pt_command pt; + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc = NVME_OPC_IDENTIFY; + pt.cmd.nsid = htole32(nsid); + pt.cmd.cdw10 = htole32(2); + pt.buf = nslist; + pt.len = sizeof(*nslist); + pt.is_read = 1; + + if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) + return (errno); + + /* Convert data to host endian */ + nvme_ns_list_swapbytes(nslist); + + if (nvme_completion_is_error(&pt.cpl)) + return (EIO); + return (0); +} + +int open_dev(const char *str, int *fd, int write, int exit_on_error) { - char full_path[64]; + char full_path[MAXPATHLEN]; - snprintf(full_path, sizeof(full_path), _PATH_DEV"%s", str); + if (str[0] == '/') /* Full path */ + strlcpy(full_path, str, sizeof(full_path)); + else /* Add /dev/ */ + snprintf(full_path, sizeof(full_path), _PATH_DEV"%s", str); *fd = open(full_path, write ? O_RDWR : O_RDONLY); if (*fd < 0) { if (exit_on_error) { diff --git a/sbin/nvmecontrol/nvmecontrol.h b/sbin/nvmecontrol/nvmecontrol.h index ce717e5732c3..968e9c20160e 100644 --- a/sbin/nvmecontrol/nvmecontrol.h +++ b/sbin/nvmecontrol/nvmecontrol.h @@ -60,6 +60,17 @@ struct kv_name { const char *name; }; +/* + * Generically convert little endian to host endian, based on the type of the thing + * being converted. Use the proposed name for future changes to endian.h. + */ +#define letoh(x) \ + _Generic(x, \ + uint8_t: (x), \ + uint16_t: le16toh(x), \ + uint32_t: le32toh(x), \ + uint64_t: le64toh(x)) + const char *kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key); void logpage_register(struct logpage_function *p); @@ -70,32 +81,45 @@ int open_dev(const char *str, int *fd, int write, int exit_on_error); void get_nsid(int fd, char **ctrlr_str, uint32_t *nsid); int read_controller_data(int fd, struct nvme_controller_data *cdata); int read_namespace_data(int fd, uint32_t nsid, struct nvme_namespace_data *nsdata); +int read_active_namespaces(int fd, uint32_t nsid, struct nvme_ns_list *nslist); void print_hex(void *data, uint32_t length); void print_namespace(struct nvme_namespace_data *nsdata); void read_logpage(int fd, uint8_t log_page, uint32_t nsid, uint8_t lsp, - uint16_t lsi, uint8_t rae, void *payload, uint32_t payload_size); + uint16_t lsi, uint8_t rae, uint64_t lpo, uint8_t csi, uint8_t ot, + uint16_t uuid_index, void *payload, uint32_t payload_size); void print_temp_C(uint16_t t); void print_temp_K(uint16_t t); void print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused); /* Utility Routines */ /* - * 128-bit integer augments to standard values. On i386 this - * doesn't exist, so we use 64-bit values. So, on 32-bit i386, - * you'll get truncated values until someone implement 128bit - * ints in software. + * C23 supports 128-bit integers via _BitInt(128). clang 16 and gcc 13 support + * this. Older compilers will support 128-bit ints on 64-bit + * platforms. Otherwise we truncate this to 64-bit on 32-bit systems with older + * compilers. We also check for > C18 instead of >= C23 because clang 17 was + * released before the __STDC_VERSION__ was defined. */ #define UINT128_DIG 39 -#ifdef __i386__ -typedef uint64_t uint128_t; -#else +#if __STDC_VERSION__ >= 202311L +typedef unsigned _BitInt(128) uint128_t; +#elif defined(__SIZEOF_INT128__) typedef __uint128_t uint128_t; +#else +typedef uint64_t uint128_t; #endif static __inline uint128_t to128(void *p) { - return *(uint128_t *)p; +#if __STDC_VERSION__ >= 202311L || defined(__SIZEOF_INT128__) + uint64_t lo, hi; + + lo = le64dec(p); + hi = le64dec((const char *)p + 8); + return ((uint128_t)hi << 64 | lo); +#else + return (le64dec(p)); +#endif } uint64_t le48dec(const void *pp); diff --git a/sbin/nvmecontrol/passthru.c b/sbin/nvmecontrol/passthru.c index 015c52305ceb..cf38e185a582 100644 --- a/sbin/nvmecontrol/passthru.c +++ b/sbin/nvmecontrol/passthru.c @@ -25,7 +25,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioccom.h> diff --git a/sbin/nvmecontrol/perftest.c b/sbin/nvmecontrol/perftest.c index 0afab9951e50..59439d3ed2a9 100644 --- a/sbin/nvmecontrol/perftest.c +++ b/sbin/nvmecontrol/perftest.c @@ -26,7 +26,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioccom.h> diff --git a/sbin/nvmecontrol/power.c b/sbin/nvmecontrol/power.c index 95ee1763a28c..b26570da0c52 100644 --- a/sbin/nvmecontrol/power.c +++ b/sbin/nvmecontrol/power.c @@ -23,7 +23,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioccom.h> @@ -63,14 +62,10 @@ power_list_one(int i, struct nvme_power_state *nps) int mpower, apower, ipower; uint8_t mps, nops, aps, apw; - mps = (nps->mps_nops >> NVME_PWR_ST_MPS_SHIFT) & - NVME_PWR_ST_MPS_MASK; - nops = (nps->mps_nops >> NVME_PWR_ST_NOPS_SHIFT) & - NVME_PWR_ST_NOPS_MASK; - apw = (nps->apw_aps >> NVME_PWR_ST_APW_SHIFT) & - NVME_PWR_ST_APW_MASK; - aps = (nps->apw_aps >> NVME_PWR_ST_APS_SHIFT) & - NVME_PWR_ST_APS_MASK; + mps = NVMEV(NVME_PWR_ST_MPS, nps->mps_nops); + nops = NVMEV(NVME_PWR_ST_NOPS, nps->mps_nops); + apw = NVMEV(NVME_PWR_ST_APW, nps->apw_aps); + aps = NVMEV(NVME_PWR_ST_APS, nps->apw_aps); mpower = nps->mp; if (mps == 0) @@ -95,7 +90,7 @@ power_list(struct nvme_controller_data *cdata) int i; printf("\nPower States Supported: %d\n\n", cdata->npss + 1); - printf(" # Max pwr Enter Lat Exit Lat RT RL WT WL Idle Pwr Act Pwr Workloadd\n"); + printf(" # Max pwr Enter Lat Exit Lat RT RL WT WL Idle Pwr Act Pwr Workload\n"); printf("-- -------- --------- --------- -- -- -- -- -------- -------- --\n"); for (i = 0; i <= cdata->npss; i++) power_list_one(i, &cdata->power_state[i]); diff --git a/sbin/nvmecontrol/reconnect.c b/sbin/nvmecontrol/reconnect.c new file mode 100644 index 000000000000..06af40624177 --- /dev/null +++ b/sbin/nvmecontrol/reconnect.c @@ -0,0 +1,333 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023-2024 Chelsio Communications, Inc. + * Written by: John Baldwin <jhb@FreeBSD.org> + */ + +#include <sys/dnv.h> +#include <sys/nv.h> +#include <sys/socket.h> +#include <err.h> +#include <libnvmf.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +#include "nvmecontrol.h" +#include "fabrics.h" + +/* + * See comment about other possible settings in connect.c. + */ + +static struct options { + const char *dev; + 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; + bool flow_control; + bool header_digests; +} opt = { + .dev = NULL, + .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, + .flow_control = false, + .header_digests = false, +}; + +static void +tcp_association_params(struct nvmf_association_params *params, + bool header_digests, bool data_digests) +{ + params->tcp.pda = 0; + params->tcp.header_digests = header_digests; + params->tcp.data_digests = data_digests; + /* XXX */ + params->tcp.maxr2t = 1; +} + +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) +{ + struct nvme_controller_data cdata; + struct nvme_discovery_log_entry dle_thunk; + struct nvmf_qpair *admin, **io; + int error; + + io = calloc(num_io_queues, sizeof(*io)); + error = connect_nvm_queues(aparams, trtype, adrfam, address, port, + cntlid, subnqn, hostnqn, kato, &admin, io, num_io_queues, + queue_size, &cdata); + if (error != 0) { + free(io); + return (error); + } + + if (dle == NULL) { + error = nvmf_init_dle_from_admin_qp(admin, &cdata, &dle_thunk); + if (error != 0) { + warnc(error, "Failed to generate handoff parameters"); + disconnect_nvm_queues(admin, io, num_io_queues); + free(io); + return (EX_IOERR); + } + dle = &dle_thunk; + } + + error = nvmf_reconnect_host(fd, dle, hostnqn, admin, num_io_queues, io, + &cdata, reconnect_delay, controller_loss_timeout); + if (error != 0) { + warnc(error, "Failed to handoff queues to kernel"); + free(io); + return (EX_IOERR); + } + free(io); + return (0); +} + +static int +reconnect_by_address(int fd, const nvlist_t *rparams, const char *addr) +{ + const struct nvme_discovery_log_entry *dle; + struct nvmf_association_params aparams; + enum nvmf_trtype trtype; + const char *address, *hostnqn, *port; + char *subnqn, *tofree; + int error; + + memset(&aparams, 0, sizeof(aparams)); + aparams.sq_flow_control = opt.flow_control; + if (strcasecmp(opt.transport, "tcp") == 0) { + trtype = NVMF_TRTYPE_TCP; + tcp_association_params(&aparams, opt.header_digests, + opt.data_digests); + } else { + warnx("Unsupported or invalid transport"); + return (EX_USAGE); + } + + nvmf_parse_address(addr, &address, &port, &tofree); + if (port == NULL) { + free(tofree); + warnx("Explicit port required"); + return (EX_USAGE); + } + + dle = nvlist_get_binary(rparams, "dle", NULL); + + hostnqn = opt.hostnqn; + if (hostnqn == NULL) + hostnqn = nvmf_default_hostnqn(); + + /* Ensure subnqn is a terminated C string. */ + subnqn = strndup(dle->subnqn, sizeof(dle->subnqn)); + + error = reconnect_nvm_controller(fd, &aparams, trtype, AF_UNSPEC, + address, port, le16toh(dle->cntlid), subnqn, hostnqn, + opt.kato * 1000, opt.reconnect_delay, opt.controller_loss_timeout, + opt.num_io_queues, opt.queue_size, NULL); + free(subnqn); + free(tofree); + return (error); +} + +static int +reconnect_by_params(int fd, const nvlist_t *rparams) +{ + struct nvmf_association_params aparams; + const struct nvme_discovery_log_entry *dle; + char *address, *port, *subnqn; + int adrfam, error; + + dle = nvlist_get_binary(rparams, "dle", NULL); + + memset(&aparams, 0, sizeof(aparams)); + aparams.sq_flow_control = nvlist_get_bool(rparams, "sq_flow_control"); + switch (dle->trtype) { + case NVMF_TRTYPE_TCP: + switch (dle->adrfam) { + case NVMF_ADRFAM_IPV4: + adrfam = AF_INET; + break; + case NVMF_ADRFAM_IPV6: + adrfam = AF_INET6; + break; + default: + warnx("Unsupported address family"); + return (EX_UNAVAILABLE); + } + switch (dle->tsas.tcp.sectype) { + case NVME_TCP_SECURITY_NONE: + break; + default: + warnx("Unsupported TCP security type"); + return (EX_UNAVAILABLE); + } + break; + + tcp_association_params(&aparams, + nvlist_get_bool(rparams, "header_digests"), + nvlist_get_bool(rparams, "data_digests")); + break; + default: + warnx("Unsupported transport %s", + nvmf_transport_type(dle->trtype)); + return (EX_UNAVAILABLE); + } + + /* Ensure address, port, and subnqn is a terminated C string. */ + address = strndup(dle->traddr, sizeof(dle->traddr)); + port = strndup(dle->trsvcid, sizeof(dle->trsvcid)); + subnqn = strndup(dle->subnqn, sizeof(dle->subnqn)); + + error = reconnect_nvm_controller(fd, &aparams, dle->trtype, adrfam, + 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); + free(port); + free(address); + return (error); +} + +static int +fetch_and_validate_rparams(int fd, nvlist_t **rparamsp) +{ + const struct nvme_discovery_log_entry *dle; + nvlist_t *rparams; + size_t len; + int error; + + error = nvmf_reconnect_params(fd, &rparams); + if (error != 0) { + warnc(error, "Failed to fetch reconnect parameters"); + return (EX_IOERR); + } + + if (!nvlist_exists_binary(rparams, "dle") || + !nvlist_exists_string(rparams, "hostnqn") || + !nvlist_exists_number(rparams, "num_io_queues") || + !nvlist_exists_number(rparams, "io_qsize") || + !nvlist_exists_bool(rparams, "sq_flow_control")) { + nvlist_destroy(rparams); + warnx("Missing required reconnect parameters"); + return (EX_IOERR); + } + + dle = nvlist_get_binary(rparams, "dle", &len); + if (len != sizeof(*dle)) { + nvlist_destroy(rparams); + warnx("Discovery Log entry reconnect parameter is wrong size"); + return (EX_IOERR); + } + + switch (dle->trtype) { + case NVMF_TRTYPE_TCP: + if (!nvlist_exists_bool(rparams, "header_digests") || + !nvlist_exists_bool(rparams, "data_digests")) { + nvlist_destroy(rparams); + warnx("Missing required reconnect parameters"); + return (EX_IOERR); + } + break; + default: + nvlist_destroy(rparams); + warnx("Unsupported transport %s", + nvmf_transport_type(dle->trtype)); + return (EX_UNAVAILABLE); + } + + *rparamsp = rparams; + return (0); +} + +static void +reconnect_fn(const struct cmd *f, int argc, char *argv[]) +{ + nvlist_t *rparams; + int error, fd; + + if (arg_parse(argc, argv, f)) + return; + + open_dev(opt.dev, &fd, 1, 1); + error = fetch_and_validate_rparams(fd, &rparams); + if (error != 0) + exit(error); + + /* Check for optional address. */ + if (optind < argc) + error = reconnect_by_address(fd, rparams, argv[optind]); + else + error = reconnect_by_params(fd, rparams); + if (error != 0) + exit(error); + + nvlist_destroy(rparams); + close(fd); +} + +static const struct opts reconnect_opts[] = { +#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } + OPT("transport", 't', arg_string, opt, transport, + "Transport type"), + OPT("nr-io-queues", 'i', arg_uint16, opt, num_io_queues, + "Number of I/O queues"), + OPT("queue-size", 'Q', arg_uint16, opt, queue_size, + "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, + "Request SQ flow control"), + OPT("hdr_digests", 'g', arg_none, opt, header_digests, + "Enable TCP PDU header digests"), + OPT("data_digests", 'G', arg_none, opt, data_digests, + "Enable TCP PDU data digests"), + { NULL, 0, arg_none, NULL, NULL } +}; +#undef OPT + +static const struct args reconnect_args[] = { + { arg_string, &opt.dev, "controller-id" }, + { arg_none, NULL, NULL }, +}; + +static struct cmd reconnect_cmd = { + .name = "reconnect", + .fn = reconnect_fn, + .descr = "Reconnect to a fabrics controller", + .ctx_size = sizeof(opt), + .opts = reconnect_opts, + .args = reconnect_args, +}; + +CMD_COMMAND(reconnect_cmd); diff --git a/sbin/nvmecontrol/reset.c b/sbin/nvmecontrol/reset.c index 4cd4987d88b5..267413c975f8 100644 --- a/sbin/nvmecontrol/reset.c +++ b/sbin/nvmecontrol/reset.c @@ -26,7 +26,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioccom.h> diff --git a/sbin/nvmecontrol/resv.c b/sbin/nvmecontrol/resv.c index 835ad47a924c..d607b0b015e6 100644 --- a/sbin/nvmecontrol/resv.c +++ b/sbin/nvmecontrol/resv.c @@ -25,7 +25,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioccom.h> diff --git a/sbin/nvmecontrol/sanitize.c b/sbin/nvmecontrol/sanitize.c index d56a74685651..e720c6d43497 100644 --- a/sbin/nvmecontrol/sanitize.c +++ b/sbin/nvmecontrol/sanitize.c @@ -25,7 +25,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioccom.h> @@ -131,8 +130,11 @@ sanitize(const struct cmd *f, int argc, char *argv[]) sanact = 3; else if (strcmp(opt.sanact, "crypto") == 0) sanact = 4; + else if ((sanact = (int)strtol(opt.sanact, NULL, 10) != 0) + && (sanact >= 1 && sanact <= 4)) + ; /* compat with nvme sanitize -a number */ else { - fprintf(stderr, "Incorrect Sanitize Action value\n"); + fprintf(stderr, "Incorrect Sanitize Action value: %s\n", opt.sanact); arg_help(argc, argv, f); } } @@ -155,14 +157,11 @@ sanitize(const struct cmd *f, int argc, char *argv[]) /* Check that controller can execute this command. */ if (read_controller_data(fd, &cd)) errx(EX_IOERR, "Identify request failed"); - if (((cd.sanicap >> NVME_CTRLR_DATA_SANICAP_BES_SHIFT) & - NVME_CTRLR_DATA_SANICAP_BES_MASK) == 0 && sanact == 2) + if (NVMEV(NVME_CTRLR_DATA_SANICAP_BES, cd.sanicap) == 0 && sanact == 2) errx(EX_UNAVAILABLE, "controller does not support Block Erase"); - if (((cd.sanicap >> NVME_CTRLR_DATA_SANICAP_OWS_SHIFT) & - NVME_CTRLR_DATA_SANICAP_OWS_MASK) == 0 && sanact == 3) + if (NVMEV(NVME_CTRLR_DATA_SANICAP_OWS, cd.sanicap) == 0 && sanact == 3) errx(EX_UNAVAILABLE, "controller does not support Overwrite"); - if (((cd.sanicap >> NVME_CTRLR_DATA_SANICAP_CES_SHIFT) & - NVME_CTRLR_DATA_SANICAP_CES_MASK) == 0 && sanact == 4) + if (NVMEV(NVME_CTRLR_DATA_SANICAP_CES, cd.sanicap) == 0 && sanact == 4) errx(EX_UNAVAILABLE, "controller does not support Crypto Erase"); /* @@ -186,9 +185,9 @@ sanitize(const struct cmd *f, int argc, char *argv[]) wait: read_logpage(fd, NVME_LOG_SANITIZE_STATUS, - NVME_GLOBAL_NAMESPACE_TAG, 0, 0, 0, &ss, sizeof(ss)); - switch ((ss.sstat >> NVME_SS_PAGE_SSTAT_STATUS_SHIFT) & - NVME_SS_PAGE_SSTAT_STATUS_MASK) { + NVME_GLOBAL_NAMESPACE_TAG, 0, 0, 0, + 0, 0, 0, 0, &ss, sizeof(ss)); + switch (NVMEV(NVME_SS_PAGE_SSTAT_STATUS, ss.sstat)) { case NVME_SS_PAGE_SSTAT_STATUS_NEVER: printf("Never sanitized"); break; diff --git a/sbin/nvmecontrol/selftest.c b/sbin/nvmecontrol/selftest.c index 6139a7902234..6a4da902999e 100644 --- a/sbin/nvmecontrol/selftest.c +++ b/sbin/nvmecontrol/selftest.c @@ -25,7 +25,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioccom.h> @@ -100,8 +99,7 @@ selftest(const struct cmd *f, int argc, char *argv[]) if (read_controller_data(fd, &cdata)) errx(EX_IOERR, "Identify request failed"); - if (((cdata.oacs >> NVME_CTRLR_DATA_OACS_SELFTEST_SHIFT) & - NVME_CTRLR_DATA_OACS_SELFTEST_MASK) == 0) + if (NVMEV(NVME_CTRLR_DATA_OACS_SELFTEST, cdata.oacs) == 0) errx(EX_UNAVAILABLE, "controller does not support self-test"); selftest_op(fd, nsid, opt.stc); diff --git a/sbin/nvmecontrol/telemetry.c b/sbin/nvmecontrol/telemetry.c new file mode 100644 index 000000000000..72c304229a80 --- /dev/null +++ b/sbin/nvmecontrol/telemetry.c @@ -0,0 +1,182 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (C) 2024 Netflix, Inc + * + * 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/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 <sysexits.h> +#include <unistd.h> +#include <sys/endian.h> + +#include "nvmecontrol.h" + +/* Tables for command line parsing */ + +static cmd_fn_t telemetry_log; + +#define NONE 0xffffffffu +static struct options { + const char *outfn; + const char *dev; + uint8_t da; +} opt = { + .outfn = NULL, + .dev = NULL, + .da = 3, +}; + +static const struct opts telemetry_log_opts[] = { +#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } + OPT("output-file", 'O', arg_string, opt, outfn, + "output file for telemetry data"), + OPT("data-area", 'd', arg_uint8, opt, da, + "output file for telemetry data"), + { NULL, 0, arg_none, NULL, NULL } +}; +#undef OPT + +static const struct args telemetry_log_args[] = { + { arg_string, &opt.dev, "<controller id|namespace id>" }, + { arg_none, NULL, NULL }, +}; + +static struct cmd telemetry_log_cmd = { + .name = "telemetry-log", + .fn = telemetry_log, + .descr = "Retrieves telemetry log pages from drive", + .ctx_size = sizeof(opt), + .opts = telemetry_log_opts, + .args = telemetry_log_args, +}; + +CMD_COMMAND(telemetry_log_cmd); + +/* End of tables for command line parsing */ + +/* + * Note: Even though this is a logpage, it's variable size and tricky + * to get with some weird options, so it's its own command. + */ + +static void +telemetry_log(const struct cmd *f, int argc, char *argv[]) +{ + int fd, fdout; + char *path; + uint32_t nsid; + ssize_t size; + uint64_t off; + ssize_t chunk; + struct nvme_controller_data cdata; + bool can_telemetry; + struct nvme_telemetry_log_page tlp, buf; + + if (arg_parse(argc, argv, f)) + return; + if (opt.da < 1 || opt.da > 3) + errx(EX_USAGE, "Data area %d is not in the range 1-3\n", opt.da); + if (opt.outfn == NULL) + errx(EX_USAGE, "No output file specified"); + + open_dev(opt.dev, &fd, 0, 1); + get_nsid(fd, &path, &nsid); + if (nsid == 0) { + nsid = NVME_GLOBAL_NAMESPACE_TAG; + } else { + close(fd); + open_dev(path, &fd, 0, 1); + } + free(path); + + if (read_controller_data(fd, &cdata)) + errx(EX_IOERR, "Identify request failed"); + + can_telemetry = NVMEV(NVME_CTRLR_DATA_LPA_TELEMETRY, cdata.lpa); + if (!can_telemetry) + errx(EX_UNAVAILABLE, "Drive does not support telemetry"); + if (nsid != NVME_GLOBAL_NAMESPACE_TAG) + errx(EX_UNAVAILABLE, "Cannot operate on namespace"); + + fdout = open(opt.outfn, O_WRONLY | O_CREAT, 0664); + if (fdout == -1) + err(EX_IOERR, "Can't create %s", opt.outfn); + + /* Read the log page */ + size = sizeof(tlp); + off = 0; + read_logpage(fd, NVME_LOG_TELEMETRY_HOST_INITIATED, nsid, 0, 0, 0, + off, 0, 0, 0, &tlp, size); + switch(opt.da) { + case 1: + size = letoh(tlp.da1_last); + break; + case 2: + size = letoh(tlp.da2_last); + break; + case 3: + size = letoh(tlp.da3_last); + break; + default: + errx(EX_USAGE, "Impossible data area %d", opt.da); + } + size = (size + 1) * 512; /* The count of additional pages */ + chunk = 4096; + + printf("Extracting %llu bytes\n", (unsigned long long)size); + do { + if (chunk > size) + chunk = size; + read_logpage(fd, NVME_LOG_TELEMETRY_HOST_INITIATED, nsid, 0, 0, 0, + off, 0, 0, 0, &buf, chunk); + if (off == 0) { + /* + * Sanity check to make sure that the generation number + * didn't change between the two reads. + */ + if (tlp.hi_gen != buf.hi_gen) + warnx( + "Generation number changed from %d to %d", + tlp.hi_gen, buf.hi_gen); + } + if (write(fdout, &buf, chunk) != chunk) + err(EX_IOERR, "Error writing %s", opt.outfn); + off += chunk; + size -= chunk; + } while (size > 0); + + close(fdout); + close(fd); + exit(0); +} diff --git a/sbin/nvmecontrol/tests/Makefile b/sbin/nvmecontrol/tests/Makefile index eebe8819339e..5754dbd743b8 100644 --- a/sbin/nvmecontrol/tests/Makefile +++ b/sbin/nvmecontrol/tests/Makefile @@ -1,4 +1,3 @@ - PACKAGE= tests ATF_TESTS_SH+= basic diff --git a/sbin/nvmecontrol/tests/Makefile.depend b/sbin/nvmecontrol/tests/Makefile.depend new file mode 100644 index 000000000000..11aba52f82cf --- /dev/null +++ b/sbin/nvmecontrol/tests/Makefile.depend @@ -0,0 +1,10 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/sbin/nvmecontrol/tests/basic.sh b/sbin/nvmecontrol/tests/basic.sh index f4a6b9504a3e..83b74721c66b 100755 --- a/sbin/nvmecontrol/tests/basic.sh +++ b/sbin/nvmecontrol/tests/basic.sh @@ -94,11 +94,11 @@ admin_passthru_head() admin_passthru_body() { if [ -c "${TEST_DEV_PATH}" ] ; then - atf_check -o not-empty -e empty nvmecontrol admin-passthru --opcode=06 --data-len=4096 --cdw10=1 -r 0 ${TEST_DEV} + atf_check -o not-empty -e empty nvmecontrol admin-passthru --opcode=06 --data-len=4096 --cdw10=1 -r ${TEST_DEV} else - atf_check -s not-exit:0 -o empty -e not-empty nvmecontrol admin-passthru --opcode=06 --data-len=4096 --cdw10=1 -r 0 ${TEST_DEV} + atf_check -s not-exit:0 -o empty -e not-empty nvmecontrol admin-passthru --opcode=06 --data-len=4096 --cdw10=1 -r ${TEST_DEV} fi - atf_check -s not-exit:0 -o ignore -e match:"${INV_OPT_MSG}" nvmecontrol admin-passthru ${INV_OPT} --opcode=06 --data-len=4096 --cdw10=1 -r 0 ${TEST_DEV} + atf_check -s not-exit:0 -o ignore -e match:"${INV_OPT_MSG}" nvmecontrol admin-passthru ${INV_OPT} --opcode=06 --data-len=4096 --cdw10=1 -r ${TEST_DEV} } atf_test_case devlist @@ -142,11 +142,11 @@ io_passthru_head() io_passthru_body() { if [ -c "${TEST_DEV_PATH}" ] ; then - atf_check -o not-empty -e empty nvmecontrol io-passthru --opcode=02 --data-len=4096 --cdw10=0 --cdw11=0 --cdw12=0x70000 -r 0 nvme0 ${TEST_DEV} + atf_check -o not-empty -e empty nvmecontrol io-passthru --opcode=02 --data-len=4096 --cdw10=0 --cdw11=0 --cdw12=0x70000 -r nvme0 ${TEST_DEV} else - atf_check -s not-exit:0 -o empty -e not-empty nvmecontrol io-passthru --opcode=02 --data-len=4096 --cdw10=0 --cdw11=0 --cdw12=0x70000 -r 0 nvme0 ${TEST_DEV} + atf_check -s not-exit:0 -o empty -e not-empty nvmecontrol io-passthru --opcode=02 --data-len=4096 --cdw10=0 --cdw11=0 --cdw12=0x70000 -r nvme0 ${TEST_DEV} fi - atf_check -s not-exit:0 -o ignore -e match:"${INV_OPT_MSG}" nvmecontrol io-passthru ${INV_OPT} --opcode=02 --data-len=4096 --cdw10=0 --cdw11=0 --cdw12=0x70000 -r 0 nvme0 ${TEST_DEV} + atf_check -s not-exit:0 -o ignore -e match:"${INV_OPT_MSG}" nvmecontrol io-passthru ${INV_OPT} --opcode=02 --data-len=4096 --cdw10=0 --cdw11=0 --cdw12=0x70000 -r nvme0 ${TEST_DEV} } atf_test_case logpage diff --git a/sbin/pfctl/Makefile b/sbin/pfctl/Makefile index c9108f702f29..08ca9a7af81b 100644 --- a/sbin/pfctl/Makefile +++ b/sbin/pfctl/Makefile @@ -1,4 +1,3 @@ - .include <src.opts.mk> PACKAGE=pf diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index 041dcb0587b3..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; @@ -172,7 +177,8 @@ enum { PF_STATE_OPT_MAX, PF_STATE_OPT_NOSYNC, PF_STATE_OPT_SRCTRACK, PF_STATE_OPT_MAX_SRC_STATES, PF_STATE_OPT_MAX_SRC_CONN, PF_STATE_OPT_MAX_SRC_CONN_RATE, PF_STATE_OPT_MAX_SRC_NODES, PF_STATE_OPT_OVERLOAD, PF_STATE_OPT_STATELOCK, - PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY, }; + PF_STATE_OPT_TIMEOUT, PF_STATE_OPT_SLOPPY, + PF_STATE_OPT_PFLOW, PF_STATE_OPT_ALLOW_RELATED }; enum { PF_SRCTRACK_NONE, PF_SRCTRACK, PF_SRCTRACK_GLOBAL, PF_SRCTRACK_RULE }; @@ -221,6 +227,33 @@ struct node_qassign { char *pqname; }; +struct range { + int a; + int b; + int t; +}; + +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; + struct pf_poolhashkey *key; + struct pf_mape_portset mape; +} pool_opts; + +struct redirspec { + struct node_host *host; + struct range rport; + struct pool_opts pool_opts; + sa_family_t af; + bool binat; +}; + static struct filter_opts { int marker; #define FOM_FLAGS 0x0001 @@ -230,16 +263,17 @@ static struct filter_opts { #define FOM_SRCTRACK 0x0010 #define FOM_MINTTL 0x0020 #define FOM_MAXMSS 0x0040 -#define FOM_AFTO 0x0080 /* not yet implemmented */ +#define FOM_AFTO 0x0080 #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 */ struct node_uid *uid; struct node_gid *gid; + struct node_if *rcv; struct { u_int8_t b1; u_int8_t b2; @@ -272,12 +306,19 @@ static struct filter_opts { struct node_host *addr; u_int16_t port; } divert; + 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 { @@ -321,32 +362,21 @@ static struct table_opts { struct node_tinithead init_nodes; } table_opts; -static struct pool_opts { - int marker; -#define POM_TYPE 0x01 -#define POM_STICKYADDRESS 0x02 - u_int8_t opts; - int type; - int staticport; - struct pf_poolhashkey *key; - struct pf_mape_portset mape; - -} pool_opts; - static struct codel_opts codel_opts; static struct node_hfsc_opts hfsc_opts; 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, @@ -361,11 +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 *, - struct node_host *, struct node_proto *, struct node_os *, - struct node_host *, struct node_port *, struct node_host *, - struct node_port *, struct node_uid *, struct node_gid *, - struct node_icmp *, const char *); +int 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_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 *); int expand_altq(struct pf_altq *, struct node_if *, struct node_queue *, struct node_queue_bw bwspec, struct node_queue_opt *); @@ -382,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); @@ -414,11 +456,7 @@ typedef struct { u_int16_t w; u_int16_t w2; } b; - struct range { - int a; - int b; - int t; - } range; + struct range range; struct node_if *interface; struct node_proto *proto; struct node_etherproto *etherproto; @@ -444,16 +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 { - struct node_host *host; - struct range rport; - } *redirection; + struct redirspec *redirspec; struct { int action; struct node_state_opt *options; @@ -504,17 +536,18 @@ int parseport(char *, struct range *r, int); %token REASSEMBLE ANCHOR NATANCHOR RDRANCHOR BINATANCHOR %token SET OPTIMIZATION TIMEOUT LIMIT LOGINTERFACE BLOCKPOLICY FAILPOLICY %token RANDOMID REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID -%token ANTISPOOF FOR INCLUDE KEEPCOUNTERS SYNCOOKIES L3 +%token ANTISPOOF FOR INCLUDE KEEPCOUNTERS SYNCOOKIES L3 MATCHES %token ETHER %token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY MAPEPORTSET %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 STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE -%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY +%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 +%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 @@ -538,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 @@ -688,10 +722,7 @@ option : SET REASSEMBLE yesno optnodf { yyerror("hostid must be non-zero"); YYERROR; } - if (pfctl_set_hostid(pf, $3) != 0) { - yyerror("error setting hostid %08x", $3); - YYERROR; - } + pfctl_set_hostid(pf, $3); } | SET BLOCKPOLICY DROP { if (pf->opts & PF_OPT_VERBOSE) @@ -762,7 +793,7 @@ option : SET REASSEMBLE yesno optnodf { free($3); YYERROR; } - if (pfctl_set_debug(pf, $3) != 0) { + if (pfctl_do_set_debug(pf, $3) != 0) { yyerror("error setting debuglevel %s", $3); free($3); YYERROR; @@ -883,6 +914,8 @@ varset : STRING '=' varstring { if (isspace((unsigned char)*s)) { yyerror("macro name cannot contain " "whitespace"); + free($1); + free($3); YYERROR; } } @@ -893,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 : '{' @@ -909,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++; @@ -921,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 '}' @@ -945,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; @@ -1038,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 " @@ -1046,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, $7, $8.src_os, - $8.src.host, $8.src.port, $8.dst.host, $8.dst.port, - $9.uid, $9.gid, $9.icmpspec, - pf->astack[pf->asd + 1] ? pf->alast->name : $2); + 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); free($2); pf->astack[pf->asd + 1] = NULL; } @@ -1084,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; @@ -1092,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, $5, $6.src_os, - $6.src.host, $6.src.port, $6.dst.host, $6.dst.port, - 0, 0, 0, $2); + 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); free($2); } | RDRANCHOR string interface af proto fromto rtable { @@ -1105,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; @@ -1134,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, $5, $6.src_os, - $6.src.host, $6.src.port, $6.dst.host, $6.dst.port, - 0, 0, 0, $2); + 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); free($2); } | BINATANCHOR string interface af proto fromto rtable { @@ -1147,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; @@ -1171,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)); @@ -1194,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) @@ -1229,7 +1204,7 @@ etherrule : ETHER action dir quick interface bridge etherproto etherfromto l3fro r.direction = $3; r.quick = $4.quick; if ($10.tag != NULL) - memcpy(&r.tagname, $10.tag, sizeof(r.tagname)); + strlcpy(r.tagname, $10.tag, sizeof(r.tagname)); if ($10.match_tag) if (strlcpy(r.match_tagname, $10.match_tag, PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) { @@ -1239,7 +1214,7 @@ etherrule : ETHER action dir quick interface bridge etherproto etherfromto l3fro } r.match_tag_not = $10.match_tag_not; if ($10.queues.qname != NULL) - memcpy(&r.qname, $10.queues.qname, sizeof(r.qname)); + strlcpy(r.qname, $10.queues.qname, sizeof(r.qname)); r.dnpipe = $10.dnpipe; r.dnflags = $10.free_flags; if (eth_rule_label(&r, $10.label)) @@ -1265,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++; @@ -1412,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; @@ -1457,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, $6, $7.src_os, - $7.src.host, $7.src.port, $7.dst.host, $7.dst.port, - 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); } ; @@ -1574,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; @@ -1622,9 +1599,9 @@ antispoof : ANTISPOOF logquick antispoof_ifspc af antispoof_opts { } if (h != NULL) - expand_rule(&r, j, NULL, NULL, NULL, h, - 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)); @@ -1644,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, h, 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); } @@ -1725,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; } @@ -2358,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) { @@ -2381,63 +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; - } - 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; @@ -2453,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. */ @@ -2615,6 +2531,22 @@ pfrule : action dir logquick interface route af proto fromto } r.rule_flag |= PFRULE_STATESLOPPY; break; + case PF_STATE_OPT_PFLOW: + if (r.rule_flag & PFRULE_PFLOW) { + yyerror("state pflow option: " + "multiple definitions"); + YYERROR; + } + r.rule_flag |= PFRULE_PFLOW; + break; + case PF_STATE_OPT_ALLOW_RELATED: + if (r.rule_flag & PFRULE_ALLOW_RELATED) { + yyerror("state allow-related option: " + "multiple definitions"); + YYERROR; + } + r.rule_flag |= PFRULE_ALLOW_RELATED; + break; case PF_STATE_OPT_TIMEOUT: if (o->data.timeout.number == PFTM_ADAPTIVE_START || @@ -2682,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); @@ -2696,62 +2624,18 @@ pfrule : action dir logquick interface route af proto fromto YYERROR; } r.rt = $5.rt; - r.rpool.opts = $5.pool_opts; - if ($5.key != NULL) - memcpy(&r.rpool.key, $5.key, - sizeof(struct pf_poolhashkey)); - } - if (r.rt) { - decide_address_family($5.host, &r.af); - remove_invalid_hosts(&$5.host, &r.af); - if ($5.host == NULL) { - yyerror("no routing address with " - "matching address family found."); - YYERROR; - } - if ((r.rpool.opts & PF_POOL_TYPEMASK) == - PF_POOL_NONE && ($5.host->next != NULL || - $5.host->addr.type == PF_ADDR_TABLE || - DYNIF_MULTIADDR($5.host->addr))) - r.rpool.opts |= PF_POOL_ROUNDROBIN; - if ((r.rpool.opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN && - disallow_table($5.host, "tables are only " - "supported in round-robin routing pools")) - YYERROR; - if ((r.rpool.opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN && - disallow_alias($5.host, "interface (%s) " - "is only supported in round-robin " - "routing pools")) - YYERROR; - if ($5.host->next != NULL) { - if ((r.rpool.opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN) { - yyerror("r.rpool.opts must " - "be PF_POOL_ROUNDROBIN"); + + 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 @@ -2790,9 +2674,40 @@ pfrule : action dir logquick interface route af proto fromto r.free_flags |= PFRULE_DN_IS_QUEUE; } - expand_rule(&r, $4, $5.host, $7, $8.src_os, - $8.src.host, $8.src.port, $8.dst.host, $8.dst.port, - $9.uid, $9.gid, $9.icmpspec, ""); + if ($9.marker & FOM_AFTO) { + 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, 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); } ; @@ -2927,6 +2842,14 @@ filter_opt : USER uids { filter_opts.match_tag = $3; filter_opts.match_tag_not = $1; } + | not RECEIVEDON if_item { + if (filter_opts.rcv) { + yyerror("cannot respecify received-on"); + YYERROR; + } + filter_opts.rcv = $3; + filter_opts.rcv->not = $1; + } | PROBABILITY probability { double p; @@ -2957,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); @@ -2999,6 +2922,94 @@ filter_opt : USER uids { filter_opts.marker |= FOM_SCRUB_TCP; filter_opts.marker |= $3.marker; } + | 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; + } + if ($2 == 0) { + yyerror("no address family specified"); + YYERROR; + } + + 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.marker |= FOM_AFTO; + } + | AFTO af FROM port_redirspec TO port_redirspec { + if (filter_opts.nat) { + yyerror("cannot respecify af-to"); + YYERROR; + } + if ($2 == 0) { + yyerror("no address family specified"); + YYERROR; + } + 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.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 ; @@ -3204,8 +3215,8 @@ logopts : logopt { $$ = $1; } ; logopt : ALL { $$.log = PF_LOG_ALL; $$.logif = 0; } - | USER { $$.log = PF_LOG_SOCKET_LOOKUP; $$.logif = 0; } - | GROUP { $$.log = PF_LOG_SOCKET_LOOKUP; $$.logif = 0; } + | MATCHES { $$.log = PF_LOG_MATCHES; $$.logif = 0; } + | USER { $$.log = PF_LOG_USER; $$.logif = 0; } | TO string { const char *errstr; u_int i; @@ -3265,6 +3276,15 @@ if_item : STRING { $$->next = NULL; $$->tail = $$; } + | ANY { + $$ = calloc(1, sizeof(struct node_if)); + if ($$ == NULL) + err(1, "if_item: calloc"); + strlcpy($$->ifname, "any", sizeof($$->ifname)); + $$->not = 0; + $$->next = NULL; + $$->tail = $$; + } ; af : /* empty */ { $$ = 0; } @@ -3554,11 +3574,13 @@ toipspec : TO ipspec { $$ = $2; } host_list : ipspec optnl { $$ = $1; } | host_list comma ipspec optnl { - if ($3 == NULL) + if ($1 == NULL) { + freehostlist($3); $$ = $1; - else if ($1 == NULL) + } else if ($3 == NULL) { + freehostlist($1); $$ = $3; - else { + } else { $1->tail->next = $3; $1->tail = $3->tail; $$ = $1; @@ -3594,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"); @@ -3606,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"); @@ -3615,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) { @@ -3642,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"); @@ -3660,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"); @@ -3786,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 = $$; @@ -3805,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 = $$; } @@ -3820,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 = $$; } @@ -3866,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; @@ -3881,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; @@ -3899,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); } @@ -3944,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; @@ -3959,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; @@ -3977,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); } @@ -4193,7 +4229,7 @@ tos : STRING { | NUMBER { $$ = $1; if ($$ < 0 || $$ > 255) { - yyerror("illegal tos value %s", $1); + yyerror("illegal tos value %lu", $1); YYERROR; } } @@ -4368,6 +4404,22 @@ state_opt_item : MAXIMUM NUMBER { $$->next = NULL; $$->tail = $$; } + | PFLOW { + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + $$->type = PF_STATE_OPT_PFLOW; + $$->next = NULL; + $$->tail = $$; + } + | ALLOW_RELATED { + $$ = calloc(1, sizeof(struct node_state_opt)); + if ($$ == NULL) + err(1, "state_opt_item: calloc"); + $$->type = PF_STATE_OPT_ALLOW_RELATED; + $$->next = NULL; + $$->tail = $$; + } | STRING NUMBER { int i; @@ -4440,7 +4492,7 @@ portstar : numberstring { } ; -redirspec : host { $$ = $1; } +redir_host : host { $$ = $1; } | '{' optnl redir_host_list '}' { $$ = $3; } ; @@ -4452,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; } ; @@ -4576,6 +4650,22 @@ pool_opt : BITMASK { pool_opts.marker |= POM_STICKYADDRESS; pool_opts.opts |= PF_POOL_STICKYADDR; } + | ENDPI { + if (pool_opts.marker & POM_ENDPI) { + yyerror("endpoint-independent cannot be redefined"); + YYERROR; + } + 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"); @@ -4608,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; } @@ -4660,14 +4750,15 @@ 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; if (check_rulestate(PFCTL_STATE_NAT)) YYERROR; - memset(&r, 0, sizeof(r)); + pfctl_init_rule(&r); r.action = $1.b1; r.natpass = $1.b2; @@ -4714,138 +4805,52 @@ 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; remove_invalid_hosts(&$9->host, &r.af); if (invalid_redirect($9->host, r.af)) YYERROR; + if ($9->host->addr.type == PF_ADDR_DYNIFTL) { + if (($9->host = gen_dynnode($9->host, r.af)) == NULL) + err(1, "calloc"); + } if (check_netmask($9->host, r.af)) YYERROR; + } - r.rpool.proxy_port[0] = ntohs($9->rport.a); - - switch (r.action) { - case PF_RDR: - if (!$9->rport.b && $9->rport.t && - $5.dst.port != NULL) { - r.rpool.proxy_port[1] = - ntohs($9->rport.a) + - (ntohs( - $5.dst.port->port[1]) - - ntohs( - $5.dst.port->port[0])); - } else - r.rpool.proxy_port[1] = - ntohs($9->rport.b); - break; - case PF_NAT: - r.rpool.proxy_port[1] = - ntohs($9->rport.b); - if (!r.rpool.proxy_port[0] && - !r.rpool.proxy_port[1]) { - r.rpool.proxy_port[0] = - PF_NAT_PROXY_PORT_LOW; - r.rpool.proxy_port[1] = - PF_NAT_PROXY_PORT_HIGH; - } else if (!r.rpool.proxy_port[1]) - r.rpool.proxy_port[1] = - r.rpool.proxy_port[0]; - break; - default: - break; - } - - r.rpool.opts = $10.type; - if ((r.rpool.opts & PF_POOL_TYPEMASK) == - PF_POOL_NONE && ($9->host->next != NULL || - $9->host->addr.type == PF_ADDR_TABLE || - DYNIF_MULTIADDR($9->host->addr))) - r.rpool.opts = PF_POOL_ROUNDROBIN; - if ((r.rpool.opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN && - disallow_table($9->host, "tables are only " - "supported in round-robin redirection " - "pools")) - YYERROR; - if ((r.rpool.opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN && - disallow_alias($9->host, "interface (%s) " - "is only supported in round-robin " - "redirection pools")) - YYERROR; - if ($9->host->next != NULL) { - if ((r.rpool.opts & PF_POOL_TYPEMASK) != - PF_POOL_ROUNDROBIN) { - yyerror("only round-robin " - "valid for multiple " - "redirection addresses"); + o = keep_state_defaults; + while (o) { + switch (o->type) { + case PF_STATE_OPT_PFLOW: + if (r.rule_flag & PFRULE_PFLOW) { + yyerror("state pflow option: " + "multiple definitions"); YYERROR; } + r.rule_flag |= PFRULE_PFLOW; + break; } + o = o->next; } - if ($10.key != NULL) - memcpy(&r.rpool.key, $10.key, - sizeof(struct pf_poolhashkey)); - - if ($10.opts) - r.rpool.opts |= $10.opts; - - if ($10.staticport) { - if (r.action != PF_NAT) { - yyerror("the 'static-port' option is " - "only valid with nat rules"); - YYERROR; - } - if (r.rpool.proxy_port[0] != - PF_NAT_PROXY_PORT_LOW && - r.rpool.proxy_port[1] != - PF_NAT_PROXY_PORT_HIGH) { - yyerror("the 'static-port' option can't" - " be used when specifying a port" - " range"); - YYERROR; - } - r.rpool.proxy_port[0] = 0; - r.rpool.proxy_port[1] = 0; - } - - 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.rpool.proxy_port[0] != - PF_NAT_PROXY_PORT_LOW && - r.rpool.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.rpool.mape = $10.mape; - } - - expand_rule(&r, $2, $9 == NULL ? NULL : $9->host, $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, ""); - 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; @@ -4853,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\""); @@ -4936,6 +4941,10 @@ binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag yyerror("binat ip versions must match"); YYERROR; } + if ($8->addr.type == PF_ADDR_DYNIFTL) { + if (($8 = gen_dynnode($8, binat.af)) == NULL) + err(1, "calloc"); + } if (check_netmask($8, binat.af)) YYERROR; memcpy(&binat.src.addr, &$8->addr, @@ -4951,6 +4960,10 @@ binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag yyerror("binat ip versions must match"); YYERROR; } + if ($9->addr.type == PF_ADDR_DYNIFTL) { + if (($9 = gen_dynnode($9, binat.af)) == NULL) + err(1, "calloc"); + } if (check_netmask($9, binat.af)) YYERROR; memcpy(&binat.dst.addr, &$9->addr, @@ -4980,6 +4993,10 @@ binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag "a single address"); YYERROR; } + if ($13->host->addr.type == PF_ADDR_DYNIFTL) { + if (($13->host = gen_dynnode($13->host, binat.af)) == NULL) + err(1, "calloc"); + } if (check_netmask($13->host, binat.af)) YYERROR; @@ -4992,19 +5009,19 @@ binatrule : no BINAT natpasslog interface af proto FROM ipspec toipspec tag YYERROR; } - TAILQ_INIT(&binat.rpool.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; - TAILQ_INSERT_TAIL(&binat.rpool.list, + 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); } ; @@ -5055,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; @@ -5073,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; } ; @@ -5116,7 +5113,7 @@ timeout_spec : STRING NUMBER yyerror("only positive values permitted"); YYERROR; } - if (pfctl_set_timeout(pf, $1, $2, 0) != 0) { + if (pfctl_apply_timeout(pf, $1, $2, 0) != 0) { yyerror("unknown timeout %s", $1); free($1); YYERROR; @@ -5130,7 +5127,7 @@ timeout_spec : STRING NUMBER yyerror("only positive values permitted"); YYERROR; } - if (pfctl_set_timeout(pf, "interval", $2, 0) != 0) + if (pfctl_apply_timeout(pf, "interval", $2, 0) != 0) YYERROR; } ; @@ -5149,7 +5146,7 @@ limit_spec : STRING NUMBER yyerror("only positive values permitted"); YYERROR; } - if (pfctl_set_limit(pf, $1, $2) != 0) { + if (pfctl_apply_limit(pf, $1, $2) != 0) { yyerror("unable to set limit %s %u", $1, $2); free($1); YYERROR; @@ -5181,10 +5178,10 @@ yesno : NO { $$ = 0; } ; unaryop : '=' { $$ = PF_OP_EQ; } - | '!' '=' { $$ = PF_OP_NE; } - | '<' '=' { $$ = PF_OP_LE; } + | NE { $$ = PF_OP_NE; } + | LE { $$ = PF_OP_LE; } | '<' { $$ = PF_OP_LT; } - | '>' '=' { $$ = PF_OP_GE; } + | GE { $$ = PF_OP_GE; } | '>' { $$ = PF_OP_GT; } ; @@ -5205,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) @@ -5238,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; @@ -5248,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: @@ -5267,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; @@ -5286,6 +5296,10 @@ filter_consistent(struct pfctl_rule *r, int anchor_call) yyerror("must indicate address family with icmp-type/code"); problems++; } + if (r->rule_flag & PFRULE_AFTO && r->af == r->naf) { + yyerror("must indicate different address family with af-to"); + problems++; + } if (r->overload_tblname[0] && r->max_src_conn == 0 && r->max_src_conn_rate.seconds == 0) { yyerror("'overload' requires 'max-src-conn' " @@ -5299,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 || @@ -5327,8 +5342,20 @@ filter_consistent(struct pfctl_rule *r, int anchor_call) "synproxy state or modulate state"); problems++; } - /* match rules rules */ - if (r->action == PF_MATCH) { + 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 " + "must not be used on af-to rules"); + problems++; + } + } + /* Basic rule sanity check. */ + switch (r->action) { + case PF_MATCH: if (r->divert.port) { yyerror("divert is not supported on match rules"); problems++; @@ -5338,6 +5365,32 @@ filter_consistent(struct pfctl_rule *r, int anchor_call) "must not be used on match rules"); problems++; } + if (r->rule_flag & PFRULE_AFTO) { + 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 (!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'"); } return (-problems); } @@ -5363,8 +5416,8 @@ rdr_consistent(struct pfctl_rule *r) yyerror("dst port only applies to tcp/udp/sctp"); problems++; } - if (r->rpool.proxy_port[0]) { - yyerror("rpool port only applies to tcp/udp/sctp"); + if (r->rdr.proxy_port[0]) { + yyerror("rdr port only applies to tcp/udp/sctp"); problems++; } } @@ -5377,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); @@ -5388,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)); @@ -5407,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) @@ -5421,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); @@ -5475,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); } @@ -5534,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), @@ -5652,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"); @@ -5686,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; @@ -5710,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; @@ -5806,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++; @@ -5827,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; @@ -5908,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, @@ -5945,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)); @@ -5966,14 +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, - struct node_if *interfaces, struct node_host *rpool_hosts, - struct node_proto *protos, struct node_os *src_oses, - struct node_host *src_hosts, struct node_port *src_ports, - struct node_host *dst_hosts, struct node_port *dst_ports, - struct node_uid *uids, struct node_gid *gids, struct node_icmp *icmp_types, - const char *anchor_call) +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_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) { sa_family_t af = r->af; int added = 0, error = 0; @@ -5981,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; + 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; @@ -6000,14 +6343,20 @@ expand_rule(struct pfctl_rule *r, LOOP_THROUGH(struct node_proto, proto, protos, LOOP_THROUGH(struct node_icmp, icmp_type, icmp_types, LOOP_THROUGH(struct node_host, src_host, src_hosts, - LOOP_THROUGH(struct node_port, src_port, src_ports, - LOOP_THROUGH(struct node_os, src_os, src_oses, LOOP_THROUGH(struct node_host, dst_host, dst_hosts, + LOOP_THROUGH(struct node_port, src_port, src_ports, LOOP_THROUGH(struct node_port, dst_port, dst_ports, + LOOP_THROUGH(struct node_os, src_os, src_oses, LOOP_THROUGH(struct node_uid, uid, uids, LOOP_THROUGH(struct node_gid, gid, gids, r->af = af; + + if (r->rule_flag & PFRULE_AFTO) { + assert(nat != NULL); + r->naf = nat->af; + } + /* for link-local IPv6 address, interface must match up */ if ((r->af && src_host->af && r->af != src_host->af) || (r->af && dst_host->af && r->af != dst_host->af) || @@ -6016,9 +6365,9 @@ expand_rule(struct pfctl_rule *r, (src_host->ifindex && dst_host->ifindex && src_host->ifindex != dst_host->ifindex) || (src_host->ifindex && *interface->ifname && - src_host->ifindex != if_nametoindex(interface->ifname)) || + src_host->ifindex != ifa_nametoindex(interface->ifname)) || (dst_host->ifindex && *interface->ifname && - dst_host->ifindex != if_nametoindex(interface->ifname))) + dst_host->ifindex != ifa_nametoindex(interface->ifname))) continue; if (!r->af && src_host->af) r->af = src_host->af; @@ -6028,9 +6377,9 @@ expand_rule(struct pfctl_rule *r, if (*interface->ifname) strlcpy(r->ifname, interface->ifname, sizeof(r->ifname)); - else if (if_indextoname(src_host->ifindex, ifname)) + else if (ifa_indextoname(src_host->ifindex, ifname)) strlcpy(r->ifname, ifname, sizeof(r->ifname)); - else if (if_indextoname(dst_host->ifindex, ifname)) + else if (ifa_indextoname(dst_host->ifindex, ifname)) strlcpy(r->ifname, ifname, sizeof(r->ifname)); else memset(r->ifname, '\0', sizeof(r->ifname)); @@ -6038,10 +6387,22 @@ 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, "%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, "%s: calloc", __func__); + } error += check_netmask(src_host, r->af); error += check_netmask(dst_host, r->af); @@ -6064,6 +6425,11 @@ expand_rule(struct pfctl_rule *r, r->gid.op = gid->op; r->gid.gid[0] = gid->gid[0]; r->gid.gid[1] = gid->gid[1]; + if (rcv) { + strlcpy(r->rcv_ifname, rcv->ifname, + sizeof(r->rcv_ifname)); + r->rcvifnot = rcv->not; + } r->type = icmp_type->type; r->code = icmp_type->code; @@ -6097,43 +6463,85 @@ expand_rule(struct pfctl_rule *r, r->os_fingerprint = PF_OSFP_ANY; } - TAILQ_INIT(&r->rpool.list); - for (h = rpool_hosts; h != NULL; h = h->next) { - pa = calloc(1, sizeof(struct pf_pooladdr)); - if (pa == NULL) - err(1, "expand_rule: calloc"); - pa->addr = h->addr; - if (h->ifname != NULL) { - if (strlcpy(pa->ifname, h->ifname, - sizeof(pa->ifname)) >= - sizeof(pa->ifname)) - errx(1, "expand_rule: strlcpy"); - } else - pa->ifname[0] = 0; - TAILQ_INSERT_TAIL(&r->rpool.list, pa, entries); + if (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); + + 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 (nat && nat->binat) + error += check_binat_redirspec(src_host, r, af); } - if (rule_consistent(r, anchor_call[0]) < 0 || error) + if (rule_consistent(r) < 0 || error) yyerror("skipping rule due to errors"); else { r->nr = pf->astack[pf->asd]->match++; - pfctl_append_rule(pf, r, anchor_call); + pfctl_append_rule(pf, r); added++; } + /* 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; + } + if (odsth && dst_host->addr.type == PF_ADDR_DYNIFTL) { + free(dst_host); + dst_host = odsth; + } + )))))))))); - FREE_LIST(struct node_if, interfaces); - FREE_LIST(struct node_proto, protos); - FREE_LIST(struct node_host, src_hosts); - FREE_LIST(struct node_port, src_ports); - FREE_LIST(struct node_os, src_oses); - FREE_LIST(struct node_host, dst_hosts); - FREE_LIST(struct node_port, dst_ports); - FREE_LIST(struct node_uid, uids); - FREE_LIST(struct node_gid, gids); - FREE_LIST(struct node_icmp, icmp_types); - FREE_LIST(struct node_host, rpool_hosts); + if (!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"); @@ -6175,6 +6583,12 @@ expand_skip_interface(struct node_if *interfaces) return (0); } +void +freehostlist(struct node_host *h) +{ + FREE_LIST(struct node_host, h); +} + #undef FREE_LIST #undef LOOP_THROUGH @@ -6201,8 +6615,10 @@ lookup(char *s) { /* this has to be sorted always */ static const struct keywords keywords[] = { + { "af-to", AFTO}, { "all", ALL}, { "allow-opts", ALLOWOPTS}, + { "allow-related", ALLOW_RELATED}, { "altq", ALTQ}, { "anchor", ANCHOR}, { "antispoof", ANTISPOOF}, @@ -6210,6 +6626,7 @@ lookup(char *s) { "bandwidth", BANDWIDTH}, { "binat", BINAT}, { "binat-anchor", BINATANCHOR}, + { "binat-to", BINATTO}, { "bitmask", BITMASK}, { "block", BLOCK}, { "block-policy", BLOCKPOLICY}, @@ -6225,6 +6642,7 @@ lookup(char *s) { "dnqueue", DNQUEUE}, { "drop", DROP}, { "dup-to", DUPTO}, + { "endpoint-independent", ENDPI}, { "ether", ETHER}, { "fail-policy", FAILPOLICY}, { "fairq", FAIRQ}, @@ -6261,8 +6679,11 @@ lookup(char *s) { "loginterface", LOGINTERFACE}, { "map-e-portset", MAPEPORTSET}, { "match", MATCH}, + { "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}, @@ -6271,17 +6692,21 @@ 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}, { "overload", OVERLOAD}, { "pass", PASS}, + { "pflow", PFLOW}, { "port", PORT}, + { "prefer-ipv6-nexthop", IPV6NH}, { "prio", PRIO}, { "priority", PRIORITY}, { "priq", PRIQ}, @@ -6294,8 +6719,10 @@ lookup(char *s) { "random-id", RANDOMID}, { "rdr", RDR}, { "rdr-anchor", RDRANCHOR}, + { "rdr-to", RDRTO}, { "realtime", REALTIME}, { "reassemble", REASSEMBLE}, + { "received-on", RECEIVEDON}, { "reply-to", REPLYTO}, { "require-order", REQUIREORDER}, { "return", RETURN}, @@ -6352,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; @@ -6398,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 @@ -6427,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; @@ -6462,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); @@ -6484,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; } @@ -6502,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++; @@ -6513,6 +6954,9 @@ top: } else if (c == quotec) { *p = '\0'; break; + } else if (c == '\0') { + yyerror("syntax error"); + return (findeol()); } if (p + 1 >= buf + sizeof(buf) - 1) { yyerror("string too long"); @@ -6522,14 +6966,21 @@ 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); + if (next == '=') + return (NE); + lungetc(next); + break; case '<': next = lgetc(0); if (next == '>') { yylval.v.i = PF_OP_XRG; return (PORTBINARY); - } + } else if (next == '=') + return (LE); lungetc(next); break; case '>': @@ -6537,7 +6988,8 @@ top: if (next == '<') { yylval.v.i = PF_OP_IRG; return (PORTBINARY); - } + } else if (next == '=') + return (GE); lungetc(next); break; case '-': @@ -6554,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()); } @@ -6593,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()); } @@ -6602,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') { @@ -6627,8 +7079,8 @@ check_file_secrecy(int fd, const char *fname) warnx("%s: owner not root or current user", fname); return (-1); } - if (st.st_mode & (S_IRWXG | S_IRWXO)) { - warnx("%s: group/world readable/writeable", fname); + if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) { + warnx("%s: group writable or world read/writable", fname); return (-1); } return (0); @@ -6641,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); @@ -6664,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); } @@ -6674,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 @@ -6706,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; @@ -6730,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) @@ -6773,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); @@ -6789,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); } @@ -6804,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); } } @@ -6834,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) @@ -7042,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; @@ -7051,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); } @@ -7137,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], @@ -7195,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 20594e050c9e..417ff70de975 100644 --- a/sbin/pfctl/pf_print_state.c +++ b/sbin/pfctl/pf_print_state.c @@ -32,7 +32,6 @@ * */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/endian.h> @@ -86,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"); @@ -127,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); } @@ -242,8 +229,10 @@ 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; + const char *sn_type_names[] = PF_SN_TYPE_NAMES; #ifndef __NO_STRICT_ALIGNMENT struct pfctl_state_key aligned_key[2]; @@ -253,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) { @@ -277,22 +265,26 @@ print_state(struct pfctl_state *s, int opts) else printf("%u ", proto); - print_host(&nk->addr[1], nk->port[1], af, opts); - if (PF_ANEQ(&nk->addr[1], &sk->addr[1], af) || + print_host(&nk->addr[1], nk->port[1], nk->af, opts); + if (nk->af != sk->af || PF_ANEQ(&nk->addr[1], &sk->addr[1], nk->af) || nk->port[1] != sk->port[1]) { + idx = afto ? 0 : 1; printf(" ("); - print_host(&sk->addr[1], sk->port[1], af, opts); + print_host(&sk->addr[idx], sk->port[idx], sk->af, + opts); printf(")"); } - if (s->direction == PF_OUT) + if (s->direction == PF_OUT || (afto && s->direction == PF_IN)) printf(" -> "); else printf(" <- "); - print_host(&nk->addr[0], nk->port[0], af, opts); - if (PF_ANEQ(&nk->addr[0], &sk->addr[0], af) || + print_host(&nk->addr[0], nk->port[0], nk->af, opts); + if (nk->af != sk->af || PF_ANEQ(&nk->addr[0], &sk->addr[0], nk->af) || nk->port[0] != sk->port[0]) { + idx = afto ? 1 : 0; printf(" ("); - print_host(&sk->addr[0], sk->port[0], af, opts); + print_host(&sk->addr[idx], sk->port[idx], sk->af, + opts); printf(")"); } @@ -377,6 +369,8 @@ print_state(struct pfctl_state *s, int opts) printf(", sloppy"); if (s->state_flags & PFSTATE_NOSYNC) printf(", no-sync"); + if (s->state_flags & PFSTATE_PFLOW) + printf(", pflow"); if (s->state_flags & PFSTATE_ACK) printf(", psync-ack"); if (s->state_flags & PFSTATE_NODF) @@ -398,10 +392,14 @@ print_state(struct pfctl_state *s, int opts) printf(", dummynet queue (%d %d)", s->dnpipe, s->dnrpipe); } - if (s->sync_flags & PFSYNC_FLAG_SRCNODE) - printf(", source-track"); - if (s->sync_flags & PFSYNC_FLAG_NATSRCNODE) - printf(", sticky-address"); + if (s->src_node_flags & PFSTATE_SRC_NODE_LIMIT) + printf(", %s", sn_type_names[PF_SN_LIMIT]); + if (s->src_node_flags & PFSTATE_SRC_NODE_LIMIT_GLOBAL) + printf(" global"); + if (s->src_node_flags & PFSTATE_SRC_NODE_NAT) + printf(", %s", sn_type_names[PF_SN_NAT]); + if (s->src_node_flags & PFSTATE_SRC_NODE_ROUTE) + printf(", %s", sn_type_names[PF_SN_ROUTE]); if (s->log) printf(", log"); if (s->log & PF_LOG_ALL) @@ -431,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); } @@ -445,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/pf_ruleset.c b/sbin/pfctl/pf_ruleset.c index 09ee0fdb916c..2b7ec09f28aa 100644 --- a/sbin/pfctl/pf_ruleset.c +++ b/sbin/pfctl/pf_ruleset.c @@ -36,7 +36,6 @@ * $OpenBSD: pf_ruleset.c,v 1.2 2008/12/18 15:31:37 dhill Exp $ */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/socket.h> #include <sys/mbuf.h> diff --git a/sbin/pfctl/pfctl.8 b/sbin/pfctl/pfctl.8 index 41eb2bea9f94..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 22, 2021 +.Dd August 28, 2025 .Dt PFCTL 8 .Os .Sh NAME @@ -43,7 +43,7 @@ .Op Fl K Ar host | network .Xo .Oo Fl k -.Ar host | network | label | id | gateway +.Ar host | network | label | id | gateway | nat .Oc Xc .Op Fl o Ar level .Op Fl p Ar device @@ -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 @@ -104,9 +104,7 @@ to 1. Set them permanently in .Xr sysctl.conf 5 . .Pp -The -.Nm -utility provides several commands. +At least one option must be specified. The options are as follows: .Bl -tag -width Ds .It Fl A @@ -116,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, @@ -187,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 @@ -205,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 . @@ -256,15 +281,17 @@ 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 +.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 gateway. +.Ar nat. .Pp For example, to kill all of the state entries originating from .Dq host : @@ -294,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 @@ -305,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 @@ -332,6 +370,10 @@ To kill all states using a gateway in 192.168.0.0/24: .Pp .Dl # pfctl -k gateway -k 192.168.0.0/24 .Pp +States can also be killed based on their pre-NAT address: +.Pp +.Dl # pfctl -k nat -k 192.168.0.1 +.Pp .It Fl M Kill matching states in the opposite direction (on other interfaces) when killing states. @@ -360,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 @@ -385,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 , @@ -404,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 @@ -423,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 @@ -434,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. @@ -467,43 +524,93 @@ 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 -.It Fl T Ar command Op Ar address ... +.Pp +Counters shown with +.Fl s Cm info +are: +.Pp +.Bl -tag -width xxxxxxxxxxxxxx -compact +.It match +explicit rule match +.It bad-offset +currently unused +.It fragment +invalid fragments dropped +.It short +short packets dropped +.It normalize +dropped by normalizer: illegal packets +.It memory +memory could not be allocated +.It bad-timestamp +bad TCP timestamp; RFC 1323 +.It congestion +network interface queue congested +.It ip-option +bad IP/IPv6 options +.It proto-cksum +invalid protocol checksum +.It state-mismatch +packet was associated with a state entry, but sequence numbers did not match +.It state-insert +state insertion failure +.It state-limit +configured state limit was reached +.It src-limit +source node/connection limit +.It synproxy +dropped by synproxy +.It map-failed +address mapping failed +.It translate +no free ports in translation port range +.El +.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 -Clear all the statistics of a table. -.It Fl T Cm load +.It Cm zero Op Ar address ... +Clear all the statistics of a table, or only for specified addresses. +.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 Cm load Load only the table definitions from .Xr pf.conf 5 . This is used in conjunction with the @@ -526,6 +633,8 @@ line and/or in an unformatted text file, using the flag. Comments starting with a .Sq # +or +.Sq \; are allowed in the text file. With these commands, the .Fl v @@ -666,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 bfa76b299a02..21562fa03e0d 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(int, 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 *); + 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 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; @@ -132,7 +152,7 @@ static const char *showopt; static const char *debugopt; static char *anchoropt; static const char *optiopt = NULL; -static const char *pf_device = "/dev/pf"; +static const char *pf_device = PF_DEVICE; static char *ifaceopt; static char *tableopt; static const char *tblcmdopt; @@ -144,8 +164,10 @@ int loadopt; int altqsupport; 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) { \ @@ -164,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; @@ -227,18 +253,18 @@ 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[] = { "ether", "nat", "queue", "rules", "Anchors", "Sources", "states", "info", "Interfaces", "labels", "timeouts", "memory", "Tables", - "osfp", "Running", "all", NULL + "osfp", "Running", "all", "creatorids", NULL }; static const char * const tblcmdopt_list[] = { "kill", "flush", "add", "delete", "load", "replace", "show", - "test", "zero", "expire", NULL + "test", "zero", "expire", "reset", NULL }; static const char * const debugopt_list[] = { @@ -255,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", @@ -264,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. * @@ -310,13 +370,15 @@ pfctl_proto2name(int proto) int pfctl_enable(int dev, int opts) { - if (ioctl(dev, DIOCSTART)) { - if (errno == EEXIST) + int ret; + + if ((ret = pfctl_startstop(pfh, 1)) != 0) { + if (ret == EEXIST) errx(1, "pf already enabled"); - else if (errno == ESRCH) + else if (ret == ESRCH) errx(1, "pfil registeration failed"); else - err(1, "DIOCSTART"); + errc(1, ret, "DIOCSTART"); } if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "pf enabled\n"); @@ -331,11 +393,13 @@ pfctl_enable(int dev, int opts) int pfctl_disable(int dev, int opts) { - if (ioctl(dev, DIOCSTOP)) { - if (errno == ENOENT) + int ret; + + if ((ret = pfctl_startstop(pfh, 0)) != 0) { + if (ret == ENOENT) errx(1, "pf not enabled"); else - err(1, "DIOCSTOP"); + errc(1, ret, "DIOCSTOP"); } if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "pf disabled\n"); @@ -347,17 +411,17 @@ pfctl_disable(int dev, int opts) return (0); } -int -pfctl_clear_stats(int dev, int opts) +void +pfctl_clear_stats(struct pfctl_handle *h, int opts) { - if (ioctl(dev, DIOCCLRSTATUS)) - err(1, "DIOCCLRSTATUS"); + int ret; + if ((ret = pfctl_clear_status(h)) != 0) + 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)); @@ -370,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; @@ -388,8 +451,6 @@ pfctl_check_skip_ifaces(char *ifname) continue; for (n = h; n != NULL; n = n->next) { - if (p->pfik_ifp == NULL) - continue; if (strncmp(p->pfik_name, ifname, IFNAMSIZ)) continue; @@ -397,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; @@ -416,9 +476,6 @@ pfctl_adjust_skip_ifaces(struct pfctl *pf) for (n = h; n != NULL; n = n->next) PFRB_FOREACH(pp, &skip_b) { - if (pp->pfik_ifp == NULL) - continue; - if (strncmp(pp->pfik_name, n->ifname, IFNAMSIZ)) continue; @@ -431,16 +488,14 @@ pfctl_adjust_skip_ifaces(struct pfctl *pf) } PFRB_FOREACH(p, &skip_b) { - if (p->pfik_ifp == NULL || ! (p->pfik_flags & PFI_IFLAG_SKIP)) + if (! (p->pfik_flags & PFI_IFLAG_SKIP)) continue; 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; @@ -454,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; @@ -468,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 @@ -478,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; @@ -495,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 @@ -516,66 +569,66 @@ 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; unsigned int killed; + int ret; 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 (pfctl_clear_states(dev, &kill, &killed)) - err(1, "DIOCCLRSTATES"); + if ((ret = pfctl_clear_states_h(pfh, &kill, &killed)) != 0) + 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; @@ -594,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; @@ -614,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; @@ -631,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) @@ -668,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; @@ -697,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; @@ -708,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; + int ret; killed = sources = dests = 0; @@ -719,17 +745,20 @@ 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; + state_kill[0] = state_kill[1]; + 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; @@ -741,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) @@ -778,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 (pfctl_kill_states(dev, &kill, &newkilled)) - err(1, "DIOCKILLSTATES"); + if ((ret = pfctl_kill_states_h(pfh, &kill, &newkilled)) != 0) + pfctl_errx(opts, 1, "DIOCKILLSTATES"); killed += newkilled; } freeaddrinfo(res[1]); } else { - if (pfctl_kill_states(dev, &kill, &newkilled)) - err(1, "DIOCKILLSTATES"); + if ((ret = pfctl_kill_states_h(pfh, &kill, &newkilled)) != 0) + pfctl_errx(opts, 1, "DIOCKILLSTATES"); killed += newkilled; } } @@ -807,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; @@ -818,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"); @@ -831,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; @@ -852,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); - - if (pfctl_kill_states(dev, &kill, &newkilled)) - err(1, "DIOCKILLSTATES"); + copy_satopfaddr(&kill.rt_addr.addr.v.a.addr, + resp->ai_addr); + if (pfctl_kill_states_h(pfh, &kill, &newkilled)) + pfctl_errx(opts, 1, "DIOCKILLSTATES"); killed += newkilled; } @@ -871,14 +865,14 @@ 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; unsigned int killed; + int ret; if (state_killers != 2 || (strlen(state_kill[1]) == 0)) { warnx("no label specified"); @@ -887,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; @@ -896,20 +890,19 @@ pfctl_label_kill_states(int dev, const char *iface, int opts) sizeof(kill.label)) errx(1, "label too long: %s", state_kill[1]); - if (pfctl_kill_states(dev, &kill, &killed)) - err(1, "DIOCKILLSTATES"); + 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); - - return (0); } -int +void pfctl_id_kill_states(int dev, const char *iface, int opts) { struct pfctl_kill kill; unsigned int killed; + int ret; if (state_killers != 2 || (strlen(state_kill[1]) == 0)) { warnx("no id specified"); @@ -935,44 +928,144 @@ pfctl_id_kill_states(int dev, const char *iface, int opts) usage(); } - if (pfctl_kill_states(dev, &kill, &killed)) - err(1, "DIOCKILLSTATES"); + 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); +} + +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) + 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; memset(&pp, 0, sizeof(pp)); - memcpy(pp.anchor, anchorname, sizeof(pp.anchor)); - pp.r_action = r_action; - pp.r_num = nr; - pp.ticket = ticket; - if (ioctl(dev, DIOCGETADDRS, &pp)) { - warn("DIOCGETADDRS"); + if ((ret = pfctl_get_addrs(pfh, ticket, nr, r_action, anchorname, &mpnr, which)) != 0) { + warnc(ret, "DIOCGETADDRS"); return (-1); } - mpnr = pp.nr; + TAILQ_INIT(&pool->list); for (pnr = 0; pnr < mpnr; ++pnr) { - pp.nr = pnr; - if (ioctl(dev, DIOCGETADDR, &pp)) { - warn("DIOCGETADDR"); + if ((ret = pfctl_get_addr(pfh, ticket, nr, r_action, anchorname, pnr, &pp, which)) != 0) { + 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); } @@ -982,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); @@ -993,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); @@ -1032,7 +1125,7 @@ pfctl_print_rule_counters(struct pfctl_rule *rule, int opts) { if (opts & PF_OPT_DEBUG) { const char *t[PF_SKIP_COUNT] = { "i", "d", "f", - "p", "sa", "sp", "da", "dp" }; + "p", "sa", "da", "sp", "dp" }; int i; printf(" [ Skip steps: "); @@ -1049,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 " @@ -1058,6 +1154,15 @@ pfctl_print_rule_counters(struct pfctl_rule *rule, int opts) rule->packets[1]), (unsigned long long)(rule->bytes[0] + rule->bytes[1]), (uintmax_t)rule->states_cur); + printf(" [ Source Nodes: %-6ju " + "Limit: %-6ju " + "NAT/RDR: %-6ju " + "Route: %-6ju " + "]\n", + (uintmax_t)rule->src_nodes, + (uintmax_t)rule->src_nodes_type[PF_SN_LIMIT], + (uintmax_t)rule->src_nodes_type[PF_SN_NAT], + (uintmax_t)rule->src_nodes_type[PF_SN_ROUTE]); if (!(opts & PF_OPT_DEBUG)) printf(" [ Inserted: uid %u pid %u " "State Creations: %-6ju]\n", @@ -1096,6 +1201,7 @@ pfctl_show_eth_rules(int dev, char *path, int opts, enum pfctl_show format, int brace; int dotitle = opts & PF_OPT_SHOWALL; int len = strlen(path); + int ret; char *npath, *p; /* @@ -1110,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]) @@ -1128,12 +1234,12 @@ pfctl_show_eth_rules(int dev, char *path, int opts, enum pfctl_show format, struct pfctl_eth_rulesets_info ri; u_int32_t mnr, nr; - if (pfctl_get_eth_rulesets_info(dev, &ri, npath)) { - if (errno == EINVAL) { + if ((ret = pfctl_get_eth_rulesets_info(dev, &ri, npath)) != 0) { + if (ret == EINVAL) { fprintf(stderr, "Anchor '%s' " "not found.\n", anchorname); } else { - warn("DIOCGETETHRULESETS"); + warnc(ret, "DIOCGETETHRULESETS"); return (-1); } } @@ -1143,8 +1249,8 @@ pfctl_show_eth_rules(int dev, char *path, int opts, enum pfctl_show format, for (nr = 0; nr < mnr; ++nr) { struct pfctl_eth_ruleset_info rs; - if (pfctl_get_eth_ruleset(dev, npath, nr, &rs)) - err(1, "DIOCGETETHRULESET"); + if ((ret = pfctl_get_eth_ruleset(dev, npath, nr, &rs)) != 0) + errc(1, ret, "DIOCGETETHRULESET"); INDENT(depth, !(opts & PF_OPT_VERBOSE)); printf("anchor \"%s\" all {\n", rs.name); pfctl_show_eth_rules(dev, npath, opts, @@ -1156,16 +1262,16 @@ pfctl_show_eth_rules(int dev, char *path, int opts, enum pfctl_show format, return (0); } - if (pfctl_get_eth_rules_info(dev, &info, path)) { - warn("DIOCGETETHRULES"); + if ((ret = pfctl_get_eth_rules_info(dev, &info, path)) != 0) { + warnc(ret, "DIOCGETETHRULES"); return (-1); } for (int nr = 0; nr < info.nr; nr++) { brace = 0; INDENT(depth, !(opts & PF_OPT_VERBOSE)); - if (pfctl_get_eth_rule(dev, nr, info.ticket, path, &rule, - opts & PF_OPT_CLRRULECTRS, anchor_call) != 0) { - warn("DIOCGETETHRULE"); + if ((ret = pfctl_get_eth_rule(dev, nr, info.ticket, path, &rule, + opts & PF_OPT_CLRRULECTRS, anchor_call)) != 0) { + warnc(ret, "DIOCGETETHRULE"); return (-1); } if (anchor_call[0] && @@ -1209,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; @@ -1226,8 +1331,8 @@ 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"); - snprintf(npath, MAXPATHLEN, "%s", anchorname); + errx(1, "calloc"); + strlcpy(npath, anchorname, MAXPATHLEN); } else { if (path[0]) snprintf(&path[len], MAXPATHLEN - len, "/%s", anchorname); @@ -1245,21 +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)); - memcpy(prs.path, npath, sizeof(prs.path)); - if (ioctl(dev, DIOCGETRULESETS, &prs)) { - if (errno == EINVAL) - fprintf(stderr, "Anchor '%s' " - "not found.\n", anchorname); - else - err(1, "DIOCGETRULESETS"); - } - mnr = prs.nr; + 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) { - prs.nr = nr; - if (ioctl(dev, DIOCGETRULESET, &prs)) - err(1, "DIOCGETRULESET"); + if ((ret = pfctl_get_ruleset(pfh, npath, nr, &prs)) != 0) + 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, @@ -1272,16 +1368,16 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format, } if (opts & PF_OPT_SHOWALL) { - ret = pfctl_get_rules_info(dev, &ri, PF_PASS, path); + ret = pfctl_get_rules_info_h(pfh, &ri, PF_PASS, path); if (ret != 0) { - warn("DIOCGETRULES"); + warnx("%s", pf_strerror(ret)); goto error; } header++; } - ret = pfctl_get_rules_info(dev, &ri, PF_SCRUB, path); + ret = pfctl_get_rules_info_h(pfh, &ri, PF_SCRUB, path); if (ret != 0) { - warn("DIOCGETRULES"); + warnx("%s", pf_strerror(ret)); goto error; } if (opts & PF_OPT_SHOWALL) { @@ -1292,14 +1388,22 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format, } for (nr = 0; nr < ri.nr; ++nr) { - if (pfctl_get_clear_rule(dev, nr, ri.ticket, path, PF_SCRUB, - &rule, anchor_call, opts & PF_OPT_CLRRULECTRS)) { - warn("DIOCGETRULENV"); + if ((ret = pfctl_get_clear_rule_h(pfh, nr, ri.ticket, path, PF_SCRUB, + &rule, anchor_call, opts & PF_OPT_CLRRULECTRS)) != 0) { + warnc(ret, "DIOCGETRULENV"); goto error; } - if (pfctl_get_pool(dev, &rule.rpool, - nr, ri.ticket, PF_SCRUB, path) != 0) + if (pfctl_get_pool(dev, &rule.rdr, + nr, ri.ticket, PF_SCRUB, path, PF_RDR) != 0) + goto error; + + if (pfctl_get_pool(dev, &rule.nat, + nr, ri.ticket, PF_SCRUB, path, PF_NAT) != 0) + goto error; + + if (pfctl_get_pool(dev, &rule.route, + nr, ri.ticket, PF_SCRUB, path, PF_RT) != 0) goto error; switch (format) { @@ -1308,29 +1412,45 @@ 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: break; } - pfctl_clear_pool(&rule.rpool); + pfctl_clear_pool(&rule.rdr); + pfctl_clear_pool(&rule.nat); + pfctl_clear_pool(&rule.route); } - ret = pfctl_get_rules_info(dev, &ri, PF_PASS, path); + ret = pfctl_get_rules_info_h(pfh, &ri, PF_PASS, path); if (ret != 0) { - warn("DIOCGETRULES"); + warnc(ret, "DIOCGETRULES"); goto error; } for (nr = 0; nr < ri.nr; ++nr) { - if (pfctl_get_clear_rule(dev, nr, ri.ticket, path, PF_PASS, - &rule, anchor_call, opts & PF_OPT_CLRRULECTRS)) { - warn("DIOCGETRULE"); + if ((ret = pfctl_get_clear_rule_h(pfh, nr, ri.ticket, path, PF_PASS, + &rule, anchor_call, opts & PF_OPT_CLRRULECTRS)) != 0) { + warnc(ret, "DIOCGETRULE"); goto error; } - if (pfctl_get_pool(dev, &rule.rpool, - nr, ri.ticket, PF_PASS, path) != 0) + if (pfctl_get_pool(dev, &rule.rdr, + nr, ri.ticket, PF_PASS, path, PF_RDR) != 0) + goto error; + + if (pfctl_get_pool(dev, &rule.nat, + nr, ri.ticket, PF_PASS, path, PF_NAT) != 0) + goto error; + + if (pfctl_get_pool(dev, &rule.route, + nr, ri.ticket, PF_PASS, path, PF_RT) != 0) goto error; switch (format) { @@ -1357,13 +1477,21 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format, (unsigned long long)rule.bytes[1], (uintmax_t)rule.states_tot); } + + if (anchor_call[0] && + (((p = strrchr(anchor_call, '/')) ? + p[1] == '_' : anchor_call[0] == '_') || + opts & PF_OPT_RECURSE)) { + pfctl_show_rules(dev, npath, opts, format, + anchor_call, depth, rule.anchor_wildcard); + } break; } case PFCTL_SHOW_RULES: 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 @@ -1389,7 +1517,8 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format, case PFCTL_SHOW_NOTHING: break; } - pfctl_clear_pool(&rule.rpool); + pfctl_clear_pool(&rule.rdr); + pfctl_clear_pool(&rule.nat); } error: @@ -1398,7 +1527,8 @@ 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; struct pfctl_rule rule; @@ -1406,46 +1536,86 @@ pfctl_show_nat(int dev, char *path, int opts, char *anchorname, int depth) u_int32_t nr; static int nattype[3] = { PF_NAT, PF_RDR, PF_BINAT }; int i, dotitle = opts & PF_OPT_SHOWALL; - int brace, ret; + int ret; int len = strlen(path); - char *p; + char *npath, *p; - if (path[0]) - snprintf(&path[len], MAXPATHLEN - len, "/%s", anchorname); - else - snprintf(&path[len], MAXPATHLEN - len, "%s", anchorname); + /* + * Truncate a trailing / and * on an anchorname before searching for + * the ruleset, this is syntactic sugar that doesn't actually make it + * to the kernel. + */ + if ((p = strrchr(anchorname, '/')) != NULL && + p[1] == '*' && p[2] == '\0') { + p[0] = '\0'; + } + + if ((npath = calloc(1, MAXPATHLEN)) == NULL) + errx(1, "calloc"); + + if (anchorname[0] == '/') { + snprintf(npath, MAXPATHLEN, "%s", anchorname); + } else { + snprintf(npath, MAXPATHLEN, "%s", path); + if (npath[0]) + snprintf(&npath[len], MAXPATHLEN - len, "/%s", anchorname); + else + snprintf(&npath[len], MAXPATHLEN - len, "%s", anchorname); + } + + /* + * If this anchor was called with a wildcard path, go through + * the rulesets in the anchor rather than the rules. + */ + if (wildcard && (opts & PF_OPT_RECURSE)) { + struct pfioc_ruleset prs; + u_int32_t mnr, nr; + memset(&prs, 0, sizeof(prs)); + if ((ret = pfctl_get_rulesets(pfh, npath, &mnr)) != 0) { + if (ret == EINVAL) + fprintf(stderr, "NAT anchor '%s' " + "not found.\n", anchorname); + else + errx(1, "%s", pf_strerror(ret)); + } + + for (nr = 0; nr < mnr; ++nr) { + if ((ret = pfctl_get_ruleset(pfh, npath, nr, &prs)) != 0) + 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, + prs.name, depth + 1, 0); + INDENT(depth, !(opts & PF_OPT_VERBOSE)); + printf("}\n"); + } + npath[len] = '\0'; + return (0); + } for (i = 0; i < 3; i++) { - ret = pfctl_get_rules_info(dev, &ri, nattype[i], path); + ret = pfctl_get_rules_info_h(pfh, &ri, nattype[i], npath); if (ret != 0) { - warn("DIOCGETRULES"); + warnc(ret, "DIOCGETRULES"); return (-1); } for (nr = 0; nr < ri.nr; ++nr) { - brace = 0; INDENT(depth, !(opts & PF_OPT_VERBOSE)); - if (pfctl_get_rule(dev, nr, ri.ticket, path, - nattype[i], &rule, anchor_call)) { - warn("DIOCGETRULE"); + 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.rpool, nr, - ri.ticket, nattype[i], path) != 0) + if (pfctl_get_pool(dev, &rule.rdr, nr, + ri.ticket, nattype[i], npath, PF_RDR) != 0) + return (-1); + if (pfctl_get_pool(dev, &rule.nat, nr, + ri.ticket, nattype[i], npath, PF_NAT) != 0) + return (-1); + if (pfctl_get_pool(dev, &rule.route, nr, + ri.ticket, nattype[i], npath, PF_RT) != 0) return (-1); - - if (anchor_call[0] && - ((((p = strrchr(anchor_call, '_')) != NULL) && - (p == anchor_call || - *(--p) == '/')) || (opts & PF_OPT_RECURSE))) { - brace++; - if ((p = strrchr(anchor_call, '/')) != - NULL) - p++; - else - p = &anchor_call[0]; - } else - p = &anchor_call[0]; if (dotitle) { pfctl_print_title("TRANSLATION RULES:"); @@ -1453,90 +1623,85 @@ pfctl_show_nat(int dev, char *path, int opts, char *anchorname, int depth) } print_rule(&rule, anchor_call, opts & PF_OPT_VERBOSE2, opts & PF_OPT_NUMERIC); - if (brace) + if (anchor_call[0] && + (((p = strrchr(anchor_call, '/')) ? + p[1] == '_' : anchor_call[0] == '_') || + opts & PF_OPT_RECURSE)) { printf(" {\n"); - else - printf("\n"); - pfctl_print_rule_counters(&rule, opts); - pfctl_clear_pool(&rule.rpool); - if (brace) { - pfctl_show_nat(dev, path, opts, p, depth + 1); + pfctl_print_rule_counters(&rule, opts); + pfctl_show_nat(dev, npath, opts, anchor_call, + depth + 1, rule.anchor_wildcard); INDENT(depth, !(opts & PF_OPT_VERBOSE)); printf("}\n"); + } else { + printf("\n"); + pfctl_print_rule_counters(&rule, opts); } } } return (0); } -int -pfctl_show_src_nodes(int dev, int opts) +static int +pfctl_print_src_node(struct pfctl_src_node *sn, void *arg) { - struct pfioc_src_nodes psn; - struct pf_src_node *p; - char *inbuf = NULL, *newinbuf = NULL; - unsigned int len = 0; - int i; + int *opts = (int *)arg; - memset(&psn, 0, sizeof(psn)); - for (;;) { - psn.psn_len = len; - if (len) { - newinbuf = realloc(inbuf, len); - if (newinbuf == NULL) - err(1, "realloc"); - psn.psn_buf = inbuf = newinbuf; - } - if (ioctl(dev, DIOCGETSRCNODES, &psn) < 0) { - warn("DIOCGETSRCNODES"); - free(inbuf); - return (-1); - } - if (psn.psn_len + sizeof(struct pfioc_src_nodes) < len) - break; - if (len == 0 && psn.psn_len == 0) - goto done; - if (len == 0 && psn.psn_len != 0) - len = psn.psn_len; - if (psn.psn_len == 0) - goto done; /* no src_nodes */ - len *= 2; - } - p = psn.psn_src_nodes; - if (psn.psn_len > 0 && (opts & PF_OPT_SHOWALL)) + if (*opts & PF_OPT_SHOWALL) { pfctl_print_title("SOURCE TRACKING NODES:"); - for (i = 0; i < psn.psn_len; i += sizeof(*p)) { - print_src_node(p, opts); - p++; + *opts &= ~PF_OPT_SHOWALL; } -done: - free(inbuf); + + print_src_node(sn, *opts); + return (0); } int -pfctl_show_states(int dev, const char *iface, int opts) +pfctl_show_src_nodes(int dev, int opts) { - struct pfctl_states states; - struct pfctl_state *s; - int dotitle = (opts & PF_OPT_SHOWALL); + int error; - memset(&states, 0, sizeof(states)); + error = pfctl_get_srcnodes(pfh, pfctl_print_src_node, &opts); - if (pfctl_get_states(dev, &states)) - return (-1); + return (error); +} - TAILQ_FOREACH(s, &states.states, entry) { - if (iface != NULL && strcmp(s->ifname, iface)) - continue; - if (dotitle) { - pfctl_print_title("STATES:"); - dotitle = 0; - } - print_state(s, opts); +struct pfctl_show_state_arg { + int opts; + int dotitle; + const char *iface; +}; + +static int +pfctl_show_state(struct pfctl_state *s, void *arg) +{ + struct pfctl_show_state_arg *a = (struct pfctl_show_state_arg *)arg; + + if (a->dotitle) { + pfctl_print_title("STATES:"); + a->dotitle = 0; } + print_state(s, a->opts); + + return (0); +} - pfctl_free_states(&states); +int +pfctl_show_states(int dev, const char *iface, int opts) +{ + struct pfctl_show_state_arg arg; + struct pfctl_state_filter filter = {}; + + if (iface != NULL) + strlcpy(filter.ifname, iface, IFNAMSIZ); + + arg.opts = opts; + arg.dotitle = opts & PF_OPT_SHOWALL; + arg.iface = iface; + + if (pfctl_get_filtered_states_iter(&filter, pfctl_show_state, &arg)) + return (-1); return (0); } @@ -1546,14 +1711,15 @@ pfctl_show_status(int dev, int opts) { struct pfctl_status *status; struct pfctl_syncookies cookies; + int ret; - if ((status = pfctl_get_status(dev)) == NULL) { + if ((status = pfctl_get_status_h(pfh)) == NULL) { warn("DIOCGETSTATUS"); return (-1); } - if (pfctl_get_syncookies(dev, &cookies)) { + if ((ret = pfctl_get_syncookies(dev, &cookies)) != 0) { pfctl_free_status(status); - warn("DIOCGETSYNCOOKIES"); + warnc(ret, "DIOCGETSYNCOOKIES"); return (-1); } if (opts & PF_OPT_SHOWALL) @@ -1569,7 +1735,7 @@ pfctl_show_running(int dev) struct pfctl_status *status; int running; - if ((status = pfctl_get_status(dev)) == NULL) { + if ((status = pfctl_get_status_h(pfh)) == NULL) { warn("DIOCGETSTATUS"); return (-1); } @@ -1584,17 +1750,16 @@ pfctl_show_running(int dev) int pfctl_show_timeouts(int dev, int opts) { - struct pfioc_tm pt; + uint32_t seconds; int i; + int ret; if (opts & PF_OPT_SHOWALL) pfctl_print_title("TIMEOUTS:"); - memset(&pt, 0, sizeof(pt)); for (i = 0; pf_timeouts[i].name; i++) { - pt.timeout = pf_timeouts[i].timeout; - if (ioctl(dev, DIOCGETTIMEOUT, &pt)) - err(1, "DIOCGETTIMEOUT"); - printf("%-20s %10d", pf_timeouts[i].name, pt.seconds); + if ((ret = pfctl_get_timeout(pfh, pf_timeouts[i].timeout, &seconds)) != 0) + errc(1, ret, "DIOCGETTIMEOUT"); + printf("%-20s %10d", pf_timeouts[i].name, seconds); if (pf_timeouts[i].timeout >= PFTM_ADAPTIVE_START && pf_timeouts[i].timeout <= PFTM_ADAPTIVE_END) printf(" states"); @@ -1609,55 +1774,98 @@ pfctl_show_timeouts(int dev, int opts) int pfctl_show_limits(int dev, int opts) { - struct pfioc_limit pl; + unsigned int limit; int i; + int ret; if (opts & PF_OPT_SHOWALL) pfctl_print_title("LIMITS:"); - memset(&pl, 0, sizeof(pl)); for (i = 0; pf_limits[i].name; i++) { - pl.index = pf_limits[i].index; - if (ioctl(dev, DIOCGETLIMIT, &pl)) - err(1, "DIOCGETLIMIT"); + if ((ret = pfctl_get_limit(pfh, pf_limits[i].index, &limit)) != 0) + errc(1, ret, "DIOCGETLIMIT"); printf("%-13s ", pf_limits[i].name); - if (pl.limit == UINT_MAX) + if (limit == UINT_MAX) printf("unlimited\n"); else - printf("hard limit %8u\n", pl.limit); + printf("hard limit %8u\n", limit); } return (0); } -/* callbacks for rule/nat/rdr/addr */ -int -pfctl_add_pool(struct pfctl *pf, struct pfctl_pool *p, sa_family_t af) +void +pfctl_read_limits(struct pfctl_handle *h) { - struct pf_pooladdr *pa; + int i; - if ((pf->opts & PF_OPT_NOACTION) == 0) { - if (ioctl(pf->dev, DIOCBEGINADDRS, &pf->paddr)) - err(1, "DIOCBEGINADDRS"); + 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; + uint32_t creators[16]; + size_t count = nitems(creators); + + ret = pfctl_get_creatorids(pfh, creators, &count); + if (ret != 0) + errx(ret, "Failed to retrieve creators"); + + printf("Creator IDs:\n"); + for (size_t i = 0; i < count; i++) + printf("%08x\n", creators[i]); +} + +/* callbacks for rule/nat/rdr/addr */ +int +pfctl_add_pool(struct pfctl *pf, struct pfctl_pool *p, int which) +{ + 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 (ioctl(pf->dev, DIOCADDADDR, &pf->paddr)) - err(1, "DIOCADDADDR"); + if ((ret = pfctl_add_addr(pf->h, &pf->paddr, which)) != 0) + errc(1, ret, "DIOCADDADDR"); } } 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) @@ -1665,37 +1873,17 @@ 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)); - TAILQ_INIT(&rule->rpool.list); - pfctl_move_pool(&r->rpool, &rule->rpool); + TAILQ_INIT(&rule->rdr.list); + pfctl_move_pool(&r->rdr, &rule->rdr); + TAILQ_INIT(&rule->nat.list); + pfctl_move_pool(&r->nat, &rule->nat); + TAILQ_INIT(&rule->route.list); + pfctl_move_pool(&r->route, &rule->route); TAILQ_INSERT_TAIL(rs->rules[rs_num].active.ptr, rule, entries); - return (0); } int @@ -1853,6 +2041,7 @@ pfctl_load_eth_rule(struct pfctl *pf, char *path, struct pfctl_eth_rule *r, char *name; char anchor[PF_ANCHOR_NAME_SIZE]; int len = strlen(path); + int ret; if (strlcpy(anchor, path, sizeof(anchor)) >= sizeof(anchor)) errx(1, "pfctl_load_eth_rule: strlcpy"); @@ -1872,9 +2061,9 @@ pfctl_load_eth_rule(struct pfctl *pf, char *path, struct pfctl_eth_rule *r, name = ""; if ((pf->opts & PF_OPT_NOACTION) == 0) - if (pfctl_add_eth_rule(pf->dev, r, anchor, name, - pf->eth_ticket)) - err(1, "DIOCADDETHRULENV"); + if ((ret = pfctl_add_eth_rule(pf->dev, r, anchor, name, + pf->eth_ticket)) != 0) + errc(1, ret, "DIOCADDETHRULENV"); if (pf->opts & PF_OPT_VERBOSE) { INDENT(depth, !(pf->opts & PF_OPT_VERBOSE2)); @@ -1887,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) @@ -1910,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) @@ -1936,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); @@ -1958,15 +2183,17 @@ 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; 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) - ticket = pfctl_get_ticket(pf->trans, rs_num, path); + if ((pf->opts & PF_OPT_NOACTION) == 0) { + if (pf->trans == NULL) + errx(1, "pfctl_load_rule: no transaction"); + pf->anchor->ruleset.tticket = pfctl_get_ticket(pf->trans, rs_num, path); + } if (strlcpy(anchor, path, sizeof(anchor)) >= sizeof(anchor)) errx(1, "pfctl_load_rule: strlcpy"); @@ -1986,9 +2213,19 @@ pfctl_load_rule(struct pfctl *pf, char *path, struct pfctl_rule *r, int depth) was_present = false; if ((pf->opts & PF_OPT_NOACTION) == 0) { - if (pfctl_add_pool(pf, &r->rpool, r->af)) + if ((pf->opts & PF_OPT_NOACTION) == 0) { + if ((error = pfctl_begin_addrs(pf->h, + &pf->paddr.ticket)) != 0) + errc(1, error, "DIOCBEGINADDRS"); + } + + if (pfctl_add_pool(pf, &r->rdr, PF_RDR)) return (1); - error = pfctl_add_rule(pf->dev, r, anchor, name, ticket, + if (pfctl_add_pool(pf, &r->nat, PF_NAT)) + return (1); + if (pfctl_add_pool(pf, &r->route, PF_RT)) + return (1); + error = pfctl_add_rule_h(pf->h, r, anchor, name, pf->anchor->ruleset.tticket, pf->paddr.ticket); switch (error) { case 0: @@ -1999,7 +2236,7 @@ pfctl_load_rule(struct pfctl *pf, char *path, struct pfctl_rule *r, int depth) was_present = true; break; default: - err(1, "DIOCADDRULENV"); + errc(1, error, "DIOCADDRULE"); } } @@ -2012,7 +2249,8 @@ pfctl_load_rule(struct pfctl *pf, char *path, struct pfctl_rule *r, int depth) printf(" -- rule was already present"); } path[len] = '\0'; - pfctl_clear_pool(&r->rpool); + pfctl_clear_pool(&r->rdr); + pfctl_clear_pool(&r->nat); return (0); } @@ -2042,8 +2280,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; @@ -2051,12 +2289,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)); @@ -2066,6 +2306,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 { @@ -2078,27 +2319,28 @@ 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; pf.optimize = optimize; pf.loadopt = loadopt; /* non-brace anchor, create without resolving the path */ if ((pf.anchor = calloc(1, sizeof(*pf.anchor))) == NULL) - ERRX("pfctl_rules: calloc"); + 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; @@ -2111,14 +2353,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); @@ -2132,7 +2374,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)) @@ -2173,17 +2415,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); @@ -2234,6 +2476,11 @@ pfctl_init_options(struct pfctl *pf) pf->timeout[PFTM_TCP_CLOSING] = PFTM_TCP_CLOSING_VAL; pf->timeout[PFTM_TCP_FIN_WAIT] = PFTM_TCP_FIN_WAIT_VAL; pf->timeout[PFTM_TCP_CLOSED] = PFTM_TCP_CLOSED_VAL; + pf->timeout[PFTM_SCTP_FIRST_PACKET] = PFTM_TCP_FIRST_PACKET_VAL; + pf->timeout[PFTM_SCTP_OPENING] = PFTM_TCP_OPENING_VAL; + pf->timeout[PFTM_SCTP_ESTABLISHED] = PFTM_TCP_ESTABLISHED_VAL; + pf->timeout[PFTM_SCTP_CLOSING] = PFTM_TCP_CLOSING_VAL; + pf->timeout[PFTM_SCTP_CLOSED] = PFTM_TCP_CLOSED_VAL; pf->timeout[PFTM_UDP_FIRST_PACKET] = PFTM_UDP_FIRST_PACKET_VAL; pf->timeout[PFTM_UDP_SINGLE] = PFTM_UDP_SINGLE_VAL; pf->timeout[PFTM_UDP_MULTIPLE] = PFTM_UDP_MULTIPLE_VAL; @@ -2251,8 +2498,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; @@ -2279,7 +2532,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] && @@ -2333,7 +2586,7 @@ pfctl_load_options(struct pfctl *pf) } int -pfctl_set_limit(struct pfctl *pf, const char *opt, unsigned int limit) +pfctl_apply_limit(struct pfctl *pf, const char *opt, unsigned int limit) { int i; @@ -2353,29 +2606,29 @@ pfctl_set_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); } int pfctl_load_limit(struct pfctl *pf, unsigned int index, unsigned int limit) { - struct pfioc_limit pl; - - memset(&pl, 0, sizeof(pl)); - pl.index = index; - pl.limit = limit; - if (ioctl(pf->dev, DIOCSETLIMIT, &pl)) { + if (pfctl_set_limit(pf->h, index, limit)) { if (errno == EBUSY) - warnx("Current pool size exceeds requested hard limit"); + warnx("Current pool size exceeds requested %s limit %u", + pf_limits[index].name, limit); else - warnx("DIOCSETLIMIT"); + warnx("Cannot set %s limit to %u", + pf_limits[index].name, limit); return (1); } return (0); } int -pfctl_set_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet) +pfctl_apply_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet) { int i; @@ -2405,12 +2658,7 @@ pfctl_set_timeout(struct pfctl *pf, const char *opt, int seconds, int quiet) int pfctl_load_timeout(struct pfctl *pf, unsigned int timeout, unsigned int seconds) { - struct pfioc_tm pt; - - memset(&pt, 0, sizeof(pt)); - pt.timeout = timeout; - pt.seconds = seconds; - if (ioctl(pf->dev, DIOCSETTIMEOUT, &pt)) { + if (pfctl_set_timeout(pf->h, timeout, seconds)) { warnx("DIOCSETTIMEOUT"); return (1); } @@ -2459,7 +2707,7 @@ pfctl_set_optimization(struct pfctl *pf, const char *opt) } for (i = 0; hint[i].name; i++) - if ((r = pfctl_set_timeout(pf, hint[i].name, + if ((r = pfctl_apply_timeout(pf, hint[i].name, hint[i].timeout, 1))) return (r); @@ -2495,26 +2743,18 @@ pfctl_set_logif(struct pfctl *pf, char *ifname) int pfctl_load_logif(struct pfctl *pf, char *ifname) { - struct pfioc_if pi; - - memset(&pi, 0, sizeof(pi)); - if (ifname && strlcpy(pi.ifname, ifname, - sizeof(pi.ifname)) >= sizeof(pi.ifname)) { + if (ifname != NULL && strlen(ifname) >= IFNAMSIZ) { warnx("pfctl_load_logif: strlcpy"); return (1); } - if (ioctl(pf->dev, DIOCSETSTATUSIF, &pi)) { - warnx("DIOCSETSTATUSIF"); - return (1); - } - return (0); + return (pfctl_set_statusif(pfh, ifname ? ifname : "")); } -int +void pfctl_set_hostid(struct pfctl *pf, u_int32_t hostid) { if ((loadopt & PFCTL_FLAG_OPTION) == 0) - return (0); + return; HTONL(hostid); @@ -2523,8 +2763,6 @@ pfctl_set_hostid(struct pfctl *pf, u_int32_t hostid) if (pf->opts & PF_OPT_VERBOSE) printf("set hostid 0x%08x\n", ntohl(hostid)); - - return (0); } int @@ -2609,9 +2847,10 @@ pfctl_cfg_syncookies(struct pfctl *pf, uint8_t val, struct pfctl_watermarks *w) } int -pfctl_set_debug(struct pfctl *pf, char *d) +pfctl_do_set_debug(struct pfctl *pf, char *d) { u_int32_t level; + int ret; if ((loadopt & PFCTL_FLAG_OPTION) == 0) return (0); @@ -2633,8 +2872,8 @@ pfctl_set_debug(struct pfctl *pf, char *d) level = pf->debug; if ((pf->opts & PF_OPT_NOACTION) == 0) - if (ioctl(dev, DIOCSETDEBUG, &level)) - err(1, "DIOCSETDEBUG"); + if ((ret = pfctl_set_debug(pfh, level)) != 0) + errc(1, ret, "DIOCSETDEBUG"); if (pf->opts & PF_OPT_VERBOSE) printf("set debug %s\n", d); @@ -2645,7 +2884,7 @@ pfctl_set_debug(struct pfctl *pf, char *d) int pfctl_load_debug(struct pfctl *pf, unsigned int level) { - if (ioctl(pf->dev, DIOCSETDEBUG, &level)) { + if (pfctl_set_debug(pf->h, level)) { warnx("DIOCSETDEBUG"); return (1); } @@ -2678,7 +2917,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"); @@ -2691,8 +2930,10 @@ pfctl_set_interface_flags(struct pfctl *pf, char *ifname, int flags, int how) void pfctl_debug(int dev, u_int32_t level, int opts) { - if (ioctl(dev, DIOCSETDEBUG, &level)) - err(1, "DIOCSETDEBUG"); + int ret; + + if ((ret = pfctl_set_debug(pfh, level)) != 0) + errc(1, ret, "DIOCSETDEBUG"); if ((opts & PF_OPT_QUIET) == 0) { fprintf(stderr, "debug level set to '"); switch (level) { @@ -2735,45 +2976,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)); - memcpy(pr.path, anchorname, sizeof(pr.path)); - if (ioctl(dev, DIOCGETRULESETS, &pr)) { - if (errno == EINVAL) - fprintf(stderr, "Anchor '%s' not found.\n", - anchorname); - else - err(1, "DIOCGETRULESETS"); - return (-1); - } - mnr = pr.nr; + if ((ret = pfctl_get_rulesets(pfh, anchor, &mnr)) != 0) + errx(1, "%s", pf_strerror(ret)); for (nr = 0; nr < mnr; ++nr) { char sub[MAXPATHLEN]; - pr.nr = nr; - if (ioctl(dev, DIOCGETRULESET, &pr)) - err(1, "DIOCGETRULESET"); + 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; @@ -2785,15 +3168,15 @@ pfctl_show_eth_anchors(int dev, int opts, char *anchorname) fprintf(stderr, "Anchor '%s' not found.\n", anchorname); else - err(1, "DIOCGETETHRULESETS"); + errc(1, ret, "DIOCGETETHRULESETS"); return (-1); } for (int nr = 0; nr < ri.nr; nr++) { char sub[MAXPATHLEN]; - if (pfctl_get_eth_ruleset(dev, anchorname, nr, &rs) != 0) - err(1, "DIOCGETETHRULESET"); + if ((ret = pfctl_get_eth_ruleset(dev, anchorname, nr, &rs)) != 0) + errc(1, ret, "DIOCGETETHRULESET"); if (!strcmp(rs.name, PF_RESERVED_ANCHOR)) continue; @@ -2821,10 +3204,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; @@ -2836,7 +3258,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; @@ -2938,6 +3360,9 @@ main(int argc, char *argv[]) usage(); } break; + case 'S': + opts |= PF_OPT_NODNS; + break; case 't': tableopt = optarg; break; @@ -2973,6 +3398,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; @@ -2981,7 +3412,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(); @@ -2990,12 +3421,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'; @@ -3028,10 +3468,21 @@ main(int argc, char *argv[]) altqsupport = 1; #endif } + pfh = pfctl_open(pf_device); + 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) { @@ -3053,7 +3504,7 @@ main(int argc, char *argv[]) break; case 'n': pfctl_load_fingerprints(dev, opts); - pfctl_show_nat(dev, path, opts, anchorname, 0); + pfctl_show_nat(dev, path, opts, anchorname, 0, 0); break; case 'q': pfctl_show_altq(dev, ifaceopt, opts, @@ -3069,7 +3520,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); @@ -3088,20 +3539,27 @@ main(int argc, char *argv[]) pfctl_show_eth_rules(dev, path, opts, 0, anchorname, 0, 0); - pfctl_show_nat(dev, path, opts, anchorname, 0); - pfctl_show_rules(dev, path, opts, 0, anchorname, 0, 0); + pfctl_show_nat(dev, path, opts, 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); @@ -3110,6 +3568,9 @@ main(int argc, char *argv[]) case 'I': pfctl_show_ifaces(ifaceopt, opts); break; + case 'c': + pfctl_show_creators(opts); + break; } } @@ -3121,16 +3582,16 @@ 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"); - 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); @@ -3145,27 +3606,45 @@ main(int argc, char *argv[]) pfctl_clear_src_nodes(dev, opts); break; case 'i': - pfctl_clear_stats(dev, opts); + 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_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(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_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; } } @@ -3176,15 +3655,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; } @@ -3205,29 +3686,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|PF_OPT_NOACTION)) && + 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) { @@ -3246,5 +3720,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 b9da5e96a90e..136f51ea08f9 100644 --- a/sbin/pfctl/pfctl.h +++ b/sbin/pfctl/pfctl.h @@ -36,6 +36,14 @@ #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; enum pfctl_show { PFCTL_SHOW_RULES, PFCTL_SHOW_LABELS, PFCTL_SHOW_NOTHING }; @@ -53,13 +61,52 @@ struct pfr_buffer { (var) != NULL; \ (var) = pfr_buf_next((buf), (var))) -int pfr_get_fd(void); -int pfr_clr_tables(struct pfr_table *, int *, int); -int pfr_add_tables(struct pfr_table *, int, int *, int); -int pfr_del_tables(struct pfr_table *, int, int *, int); +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); int pfr_del_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int); @@ -75,19 +122,19 @@ 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_clear_tables(const char *, int); -int pfctl_show_tables(const char *, int); -int pfctl_command_tables(int, char *[], char *, const char *, char *, +int pfctl_do_clear_tables(const char *, int); +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 *); #ifdef __FreeBSD__ @@ -119,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 *); @@ -149,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 a377f9eb04dc..2d16bbd22b39 100644 --- a/sbin/pfctl/pfctl_optimize.c +++ b/sbin/pfctl/pfctl_optimize.c @@ -16,7 +16,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/ioctl.h> #include <sys/socket.h> @@ -136,7 +135,9 @@ static struct pf_rule_field { PF_RULE_FIELD(return_ttl, BREAK), PF_RULE_FIELD(overload_tblname, BREAK), PF_RULE_FIELD(flush, BREAK), - PF_RULE_FIELD(rpool, BREAK), + PF_RULE_FIELD(rdr, BREAK), + PF_RULE_FIELD(nat, BREAK), + PF_RULE_FIELD(route, BREAK), PF_RULE_FIELD(logif, BREAK), /* @@ -173,6 +174,7 @@ static struct pf_rule_field { PF_RULE_FIELD(dst.port_op, NOMERGE), PF_RULE_FIELD(src.neg, NOMERGE), PF_RULE_FIELD(dst.neg, NOMERGE), + PF_RULE_FIELD(af, NOMERGE), /* These fields can be merged */ PF_RULE_FIELD(src.addr, COMBINED), @@ -239,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 *, @@ -250,8 +254,8 @@ static const char *skip_comparitors_names[PF_SKIP_COUNT]; { "af", PF_SKIP_AF, skip_cmp_af }, \ { "proto", PF_SKIP_PROTO, skip_cmp_proto }, \ { "saddr", PF_SKIP_SRC_ADDR, skip_cmp_src_addr }, \ - { "sport", PF_SKIP_SRC_PORT, skip_cmp_src_port }, \ { "daddr", PF_SKIP_DST_ADDR, skip_cmp_dst_addr }, \ + { "sport", PF_SKIP_SRC_PORT, skip_cmp_src_port }, \ { "dport", PF_SKIP_DST_PORT, skip_cmp_dst_port } \ } @@ -269,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); @@ -290,13 +297,24 @@ pfctl_optimize_ruleset(struct pfctl *pf, struct pfctl_ruleset *rs) if ((por = calloc(1, sizeof(*por))) == NULL) err(1, "calloc"); memcpy(&por->por_rule, r, sizeof(*r)); - if (TAILQ_FIRST(&r->rpool.list) != NULL) { - TAILQ_INIT(&por->por_rule.rpool.list); - pfctl_move_pool(&r->rpool, &por->por_rule.rpool); + if (TAILQ_FIRST(&r->rdr.list) != NULL) { + TAILQ_INIT(&por->por_rule.rdr.list); + pfctl_move_pool(&r->rdr, &por->por_rule.rdr); } else - bzero(&por->por_rule.rpool, - sizeof(por->por_rule.rpool)); - + bzero(&por->por_rule.rdr, + sizeof(por->por_rule.rdr)); + if (TAILQ_FIRST(&r->nat.list) != NULL) { + TAILQ_INIT(&por->por_rule.nat.list); + pfctl_move_pool(&r->nat, &por->por_rule.nat); + } else + bzero(&por->por_rule.nat, + sizeof(por->por_rule.nat)); + if (TAILQ_FIRST(&r->route.list) != NULL) { + TAILQ_INIT(&por->por_rule.route.list); + pfctl_move_pool(&r->route, &por->por_rule.route); + } else + bzero(&por->por_rule.route, + sizeof(por->por_rule.route)); TAILQ_INSERT_TAIL(&opt_queue, por, por_entry); } @@ -325,14 +343,18 @@ pfctl_optimize_ruleset(struct pfctl *pf, struct pfctl_ruleset *rs) if ((r = calloc(1, sizeof(*r))) == NULL) err(1, "calloc"); memcpy(r, &por->por_rule, sizeof(*r)); - TAILQ_INIT(&r->rpool.list); - pfctl_move_pool(&por->por_rule.rpool, &r->rpool); + TAILQ_INIT(&r->rdr.list); + pfctl_move_pool(&por->por_rule.rdr, &r->rdr); + TAILQ_INIT(&r->nat.list); + pfctl_move_pool(&por->por_rule.nat, &r->nat); 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); @@ -340,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))) { @@ -522,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 && @@ -544,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); } } } @@ -705,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; @@ -730,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 @@ -845,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); @@ -878,24 +885,23 @@ block_feedback(struct pfctl *pf, struct superblock *block) int load_feedback_profile(struct pfctl *pf, struct superblocks *superblocks) { + char anchor_call[MAXPATHLEN] = ""; struct superblock *block, *blockcur; struct superblocks prof_superblocks; struct pf_opt_rule *por; struct pf_opt_queue queue; - struct pfioc_rule pr; + 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); - memset(&pr, 0, sizeof(pr)); - pr.rule.action = PF_PASS; - if (ioctl(pf->dev, DIOCGETRULES, &pr)) { - warn("DIOCGETRULES"); + if ((ret = pfctl_get_rules_info_h(pf->h, &rules, PF_PASS, "")) != 0) { + warnx("%s", pf_strerror(ret)); return (1); } - mnr = pr.nr; + mnr = rules.nr; DEBUG("Loading %d active rules for a feedback profile", mnr); for (nr = 0; nr < mnr; ++nr) { @@ -904,24 +910,27 @@ load_feedback_profile(struct pfctl *pf, struct superblocks *superblocks) warn("calloc"); return (1); } - pr.nr = nr; - if (pfctl_get_rule(pf->dev, nr, pr.ticket, "", PF_PASS, - &rule, pr.anchor_call)) { - warn("DIOCGETRULENV"); + if (pfctl_get_rule_h(pf->h, nr, rules.ticket, "", PF_PASS, + &rule, anchor_call)) { + warnx("%s", pf_strerror(ret)); + free(por); return (1); } memcpy(&por->por_rule, &rule, sizeof(por->por_rule)); - rs = pf_find_or_create_ruleset(pr.anchor_call); + rs = pf_find_or_create_ruleset(anchor_call); por->por_rule.anchor = rs->anchor; - if (TAILQ_EMPTY(&por->por_rule.rpool.list)) - memset(&por->por_rule.rpool, 0, - sizeof(por->por_rule.rpool)); + if (TAILQ_EMPTY(&por->por_rule.rdr.list)) + memset(&por->por_rule.rdr, 0, + sizeof(por->por_rule.rdr)); + if (TAILQ_EMPTY(&por->por_rule.nat.list)) + memset(&por->por_rule.nat, 0, + sizeof(por->por_rule.nat)); TAILQ_INSERT_TAIL(&queue, por, por_entry); - /* XXX pfctl_get_pool(pf->dev, &rule.rpool, nr, pr.ticket, + /* XXX pfctl_get_pool(pf->dev, &rule.rdr, nr, pr.ticket, * PF_PASS, pf->anchor) ??? - * ... pfctl_clear_pool(&rule.rpool) + * ... pfctl_clear_pool(&rule.rdr) */ } @@ -1233,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); } @@ -1249,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)) { @@ -1305,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 */ @@ -1321,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); @@ -1584,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]) && @@ -1612,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]) && @@ -1641,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) @@ -1662,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 7fcdcf96228d..5770c8343a46 100644 --- a/sbin/pfctl/pfctl_osfp.c +++ b/sbin/pfctl/pfctl_osfp.c @@ -16,7 +16,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/ioctl.h> #include <sys/socket.h> @@ -113,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)); @@ -251,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); @@ -275,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 936c5ec53759..b8531067d3f6 100644 --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -33,18 +33,19 @@ * */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <sys/param.h> #include <sys/proc.h> +#include <net/if_dl.h> #include <net/if.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <netinet/icmp6.h> +#include <netinet/tcp.h> #include <net/pfvar.h> #include <arpa/inet.h> @@ -59,6 +60,7 @@ #include <errno.h> #include <err.h> #include <ifaddrs.h> +#include <inttypes.h> #include <unistd.h> #include "pfctl_parser.h" @@ -66,18 +68,17 @@ void print_op (u_int8_t, const char *, const char *); void print_port (u_int8_t, u_int16_t, u_int16_t, const char *, int); -void print_ugid (u_int8_t, unsigned, unsigned, const char *, unsigned); -void print_flags (u_int8_t); +void print_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 = "FSRPAUEW"; +const char * const tcpflags = "FSRPAUEWe"; static const struct icmptypeent icmp_type[] = { { "echoreq", ICMP_ECHO }, @@ -133,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[] = { @@ -192,6 +194,11 @@ const struct pf_timeout pf_timeouts[] = { { "tcp.finwait", PFTM_TCP_FIN_WAIT }, { "tcp.closed", PFTM_TCP_CLOSED }, { "tcp.tsdiff", PFTM_TS_DIFF }, + { "sctp.first", PFTM_SCTP_FIRST_PACKET }, + { "sctp.opening", PFTM_SCTP_OPENING }, + { "sctp.established", PFTM_SCTP_ESTABLISHED }, + { "sctp.closing", PFTM_SCTP_CLOSING }, + { "sctp.closed", PFTM_SCTP_CLOSED }, { "udp.first", PFTM_UDP_FIRST_PACKET }, { "udp.single", PFTM_UDP_SINGLE }, { "udp.multiple", PFTM_UDP_MULTIPLE }, @@ -221,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++) { @@ -243,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++) { @@ -262,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++) { @@ -283,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++) { @@ -346,21 +364,21 @@ 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); } void -print_flags(u_int8_t f) +print_flags(uint16_t f) { int i; @@ -371,9 +389,11 @@ print_flags(u_int8_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) && @@ -411,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) @@ -424,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; @@ -484,18 +503,17 @@ print_pool(struct pfctl_pool *pool, u_int16_t p1, u_int16_t p2, } if (pool->opts & PF_POOL_STICKYADDR) printf(" sticky-address"); + if (pool->opts & PF_POOL_ENDPI) + printf(" endpoint-independent"); if (id == PF_NAT && p1 == 0 && p2 == 0) printf(" static-port"); 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"); } -const char * const pf_reasons[PFRES_MAX+1] = PFRES_NAMES; -const char * const pf_lcounters[LCNT_MAX+1] = LCNT_NAMES; -const char * const pf_fcounters[FCNT_MAX+1] = FCNT_NAMES; -const char * const pf_scounters[FCNT_MAX+1] = FCNT_NAMES; - void print_status(struct pfctl_status *s, struct pfctl_syncookies *cookies, int opts) { @@ -510,7 +528,8 @@ print_status(struct pfctl_status *s, struct pfctl_syncookies *cookies, int opts) running = s->running ? "Enabled" : "Disabled"; if (s->since) { - unsigned int sec, min, hrs, day = runtime; + unsigned int sec, min, hrs; + time_t day = runtime; sec = day % 60; day /= 60; @@ -519,8 +538,8 @@ print_status(struct pfctl_status *s, struct pfctl_syncookies *cookies, int opts) hrs = day % 24; day /= 24; snprintf(statline, sizeof(statline), - "Status: %s for %u days %.2u:%.2u:%.2u", - running, day, hrs, min, sec); + "Status: %s for %lld days %.2u:%.2u:%.2u", + running, (long long)day, hrs, min, sec); } else snprintf(statline, sizeof(statline), "Status: %s", running); printf("%-44s", statline); @@ -597,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); @@ -623,6 +656,11 @@ print_status(struct pfctl_status *s, struct pfctl_syncookies *cookies, int opts) PFCTL_SYNCOOKIES_MODE_NAMES[cookies->mode]); printf(" %-25s %s\n", "active", s->syncookies_active ? "active" : "inactive"); + if (opts & PF_OPT_VERBOSE2) { + printf(" %-25s %d %%\n", "highwater", cookies->highwater); + printf(" %-25s %d %%\n", "lowwater", cookies->lowwater); + printf(" %-25s %d\n", "halfopen states", cookies->halfopen_states); + } printf("Reassemble %24s %s\n", s->reass & PF_REASS_ENABLED ? "yes" : "no", s->reass & PF_REASS_NODF ? "no-df" : "" @@ -637,10 +675,11 @@ print_running(struct pfctl_status *status) } void -print_src_node(struct pf_src_node *sn, int opts) +print_src_node(struct pfctl_src_node *sn, int opts) { struct pf_addr_wrap aw; - int min, sec; + uint64_t min, sec; + const char *sn_type_names[] = PF_SN_TYPE_NAMES; memset(&aw, 0, sizeof(aw)); if (sn->af == AF_INET) @@ -652,7 +691,7 @@ print_src_node(struct pf_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->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); @@ -661,38 +700,35 @@ print_src_node(struct pf_src_node *sn, int opts) sn->creation /= 60; min = sn->creation % 60; sn->creation /= 60; - printf(" age %.2u:%.2u:%.2u", sn->creation, min, sec); + printf(" age %.2" PRIu64 ":%.2" PRIu64 ":%.2" PRIu64, + sn->creation, min, sec); if (sn->states == 0) { sec = sn->expire % 60; sn->expire /= 60; min = sn->expire % 60; sn->expire /= 60; - printf(", expires in %.2u:%.2u:%.2u", + printf(", expires in %.2" PRIu64 ":%.2" PRIu64 ":%.2" PRIu64, sn->expire, min, sec); } - printf(", %llu pkts, %llu bytes", -#ifdef __FreeBSD__ - (unsigned long long)(sn->packets[0] + sn->packets[1]), - (unsigned long long)(sn->bytes[0] + sn->bytes[1])); -#else + printf(", %" PRIu64 " pkts, %" PRIu64 " bytes", sn->packets[0] + sn->packets[1], sn->bytes[0] + sn->bytes[1]); -#endif switch (sn->ruletype) { case PF_NAT: - if (sn->rule.nr != -1) - printf(", nat rule %u", sn->rule.nr); + if (sn->rule != -1) + printf(", nat rule %u", sn->rule); break; case PF_RDR: - if (sn->rule.nr != -1) - printf(", rdr rule %u", sn->rule.nr); + if (sn->rule != -1) + printf(", rdr rule %u", sn->rule); break; case PF_PASS: case PF_MATCH: - if (sn->rule.nr != -1) - printf(", filter rule %u", sn->rule.nr); + if (sn->rule != -1) + printf(", filter rule %u", sn->rule); break; } + printf(", %s", sn_type_names[sn->type]); printf("\n"); } } @@ -820,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, opts; + 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) @@ -907,7 +948,9 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer printf(" ("); if (r->log & PF_LOG_ALL) printf("%sall", count++ ? ", " : ""); - if (r->log & PF_LOG_SOCKET_LOOKUP) + if (r->log & PF_LOG_MATCHES) + printf("%smatches", count++ ? ", " : ""); + if (r->log & PF_LOG_USER) printf("%suser", count++ ? ", " : ""); if (r->logif) printf("%sto pflog%u", count++ ? ", " : "", @@ -931,7 +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->rpool, 0, 0, r->af, PF_PASS); + print_pool(&r->route, 0, 0, PF_PASS); } if (r->af) { if (r->af == AF_INET) @@ -948,13 +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); @@ -991,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 ("); @@ -1031,70 +1080,72 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer } printf(" probability %s%%", buf); } - opts = 0; + ropts = 0; if (r->max_states || r->max_src_nodes || r->max_src_states) - opts = 1; + ropts = 1; if (r->rule_flag & PFRULE_NOSYNC) - opts = 1; + ropts = 1; if (r->rule_flag & PFRULE_SRCTRACK) - opts = 1; + ropts = 1; if (r->rule_flag & PFRULE_IFBOUND) - opts = 1; + ropts = 1; if (r->rule_flag & PFRULE_STATESLOPPY) - opts = 1; - for (i = 0; !opts && i < PFTM_MAX; ++i) + ropts = 1; + if (r->rule_flag & PFRULE_PFLOW) + ropts = 1; + for (i = 0; !ropts && i < PFTM_MAX; ++i) if (r->timeout[i]) - opts = 1; - if (opts) { + ropts = 1; + if (ropts) { printf(" ("); if (r->max_states) { printf("max %u", r->max_states); - opts = 0; + ropts = 0; } if (r->rule_flag & PFRULE_NOSYNC) { - if (!opts) + if (!ropts) printf(", "); printf("no-sync"); - opts = 0; + ropts = 0; } if (r->rule_flag & PFRULE_SRCTRACK) { - if (!opts) + if (!ropts) printf(", "); printf("source-track"); if (r->rule_flag & PFRULE_RULESRCTRACK) printf(" rule"); else printf(" global"); - opts = 0; + ropts = 0; } if (r->max_src_states) { - if (!opts) + if (!ropts) printf(", "); printf("max-src-states %u", r->max_src_states); - opts = 0; + ropts = 0; } if (r->max_src_conn) { - if (!opts) + if (!ropts) printf(", "); printf("max-src-conn %u", r->max_src_conn); - opts = 0; + ropts = 0; } if (r->max_src_conn_rate.limit) { - if (!opts) + if (!ropts) printf(", "); printf("max-src-conn-rate %u/%u", r->max_src_conn_rate.limit, r->max_src_conn_rate.seconds); - opts = 0; + ropts = 0; } if (r->max_src_nodes) { - if (!opts) + if (!ropts) printf(", "); printf("max-src-nodes %u", r->max_src_nodes); - opts = 0; + ropts = 0; } if (r->overload_tblname[0]) { - if (!opts) + if (!ropts) printf(", "); printf("overload <%s>", r->overload_tblname); if (r->flush) @@ -1103,24 +1154,30 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer printf(" global"); } if (r->rule_flag & PFRULE_IFBOUND) { - if (!opts) + if (!ropts) printf(", "); printf("if-bound"); - opts = 0; + ropts = 0; } if (r->rule_flag & PFRULE_STATESLOPPY) { - if (!opts) + if (!ropts) printf(", "); printf("sloppy"); - opts = 0; + ropts = 0; + } + if (r->rule_flag & PFRULE_PFLOW) { + if (!ropts) + printf(", "); + printf("pflow"); + ropts = 0; } for (i = 0; i < PFTM_MAX; ++i) if (r->timeout[i]) { int j; - if (!opts) + if (!ropts) printf(", "); - opts = 0; + ropts = 0; for (j = 0; pf_timeouts[j].name != NULL; ++j) if (pf_timeouts[j].timeout == i) @@ -1183,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]) @@ -1203,24 +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] && (r->action == PF_NAT || - r->action == PF_BINAT || r->action == PF_RDR)) { + if (anchor_call[0]) + return; + if (r->action == PF_NAT || r->action == PF_BINAT || r->action == PF_RDR) { printf(" -> "); - print_pool(&r->rpool, r->rpool.proxy_port[0], - r->rpool.proxy_port[1], r->af, r->action); + 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)) { + 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], PF_RDR); + } + } + + if (r->rule_flag & PFRULE_EXPIRED) { + printf(" # expired"); + + if (r->exptime != 0) + printf(" %s", ctime(&r->exptime)); } } @@ -1266,7 +1345,7 @@ int parse_flags(char *s) { char *p, *q; - u_int8_t f = 0; + uint16_t f = 0; for (p = s; *p; p++) { if ((q = strchr(tcpflags, *p)) == NULL) @@ -1274,18 +1353,24 @@ parse_flags(char *s) else f |= 1 << (q - tcpflags); } - return (f ? f : PF_TH_ALL); + return (f ? f : TH_FLAGS); } 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; @@ -1306,27 +1391,45 @@ int check_netmask(struct node_host *h, sa_family_t af) { struct node_host *n = NULL; - struct pf_addr *m; + struct pf_addr *m; for (n = h; n != NULL; n = n->next) { if (h->addr.type == PF_ADDR_TABLE) continue; m = &h->addr.v.a.mask; - /* fix up netmask for dynaddr */ - if (af == AF_INET && h->addr.type == PF_ADDR_DYNIFTL && - unmask(m, AF_INET6) > 32) - set_ipmask(n, 32); /* netmasks > 32 bit are invalid on v4 */ if (af == AF_INET && (m->addr32[1] || m->addr32[2] || m->addr32[3])) { fprintf(stderr, "netmask %u invalid for IPv4 address\n", - unmask(m, AF_INET6)); + unmask(m)); return (1); } } return (0); } +struct node_host * +gen_dynnode(struct node_host *h, sa_family_t af) +{ + struct node_host *n; + + if (h->addr.type != PF_ADDR_DYNIFTL) + return (NULL); + + if ((n = calloc(1, sizeof(*n))) == NULL) + return (NULL); + bcopy(h, n, sizeof(*n)); + n->ifname = NULL; + n->next = NULL; + n->tail = NULL; + + /* fix up netmask */ + if (af == AF_INET && unmask(&n->addr.v.a.mask) > 32) + set_ipmask(n, 32); + + return (n); +} + /* interface lookup routines */ static struct node_host *iftab; @@ -1367,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; @@ -1406,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__ @@ -1431,43 +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) { + 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) @@ -1523,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); @@ -1531,6 +1620,34 @@ is_a_group(char *name) return (*(int *)ret_item->data); } +unsigned int +ifa_nametoindex(const char *ifa_name) +{ + struct node_host *p; + + for (p = iftab; p; p = p->next) { + if (p->af == AF_LINK && strcmp(p->ifname, ifa_name) == 0) + return (p->ifindex); + } + errno = ENXIO; + return (0); +} + +char * +ifa_indextoname(unsigned int ifindex, char *ifa_name) +{ + struct node_host *p; + + for (p = iftab; p; p = p->next) { + if (p->af == AF_LINK && ifindex == p->ifindex) { + strlcpy(ifa_name, p->ifname, IFNAMSIZ); + return (ifa_name); + } + } + errno = ENXIO; + return (NULL); +} + struct node_host * ifa_exists(char *ifa_name) { @@ -1619,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; @@ -1640,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, @@ -1652,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); @@ -1703,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; @@ -1770,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) @@ -1876,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 && @@ -1902,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) @@ -1928,6 +1987,7 @@ host_dns(const char *s, int v4mask, int v6mask) } } freeaddrinfo(res0); +error: free(ps); return (h); @@ -1939,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; @@ -1947,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); } @@ -1976,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 83a4880106a8..44ddfb45fbe1 100644 --- a/sbin/pfctl/pfctl_parser.h +++ b/sbin/pfctl/pfctl_parser.h @@ -36,26 +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_TH_ALL 0xFF +#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 @@ -75,6 +78,7 @@ struct pfr_buffer; /* forward definition */ struct pfctl { int dev; + struct pfctl_handle *h; int opts; int optimize; int loadopt; @@ -88,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]; @@ -136,6 +141,8 @@ struct node_host { struct node_host *tail; }; +void freehostlist(struct node_host *); + struct node_mac { u_int8_t mac[ETHER_ADDR_LEN]; u_int8_t mask[ETHER_ADDR_LEN]; @@ -256,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 { @@ -273,33 +280,38 @@ 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 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 *); -int pfctl_set_timeout(struct pfctl *, const char *, int, int); +int pfctl_apply_timeout(struct pfctl *, const char *, int, int); int pfctl_set_reassembly(struct pfctl *, int, int); int pfctl_set_optimization(struct pfctl *, const char *); -int pfctl_set_limit(struct pfctl *, const char *, unsigned int); +int pfctl_apply_limit(struct pfctl *, const char *, unsigned int); int pfctl_set_logif(struct pfctl *, char *); -int pfctl_set_hostid(struct pfctl *, u_int32_t); -int pfctl_set_debug(struct pfctl *, char *); +void pfctl_set_hostid(struct pfctl *, u_int32_t); +int pfctl_do_set_debug(struct pfctl *, char *); int pfctl_set_interface_flags(struct pfctl *, char *, int, int); 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_src_node(struct pf_src_node *, 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); void print_tabledef(const char *, int, int, struct node_tinithead *); @@ -317,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 *); @@ -357,18 +369,24 @@ 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 *); +char *ifa_indextoname(unsigned int, char *); 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 d33f091d8b69..98f907738d95 100644 --- a/sbin/pfctl/pfctl_radix.c +++ b/sbin/pfctl/pfctl_radix.c @@ -32,7 +32,6 @@ * */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/ioctl.h> #include <sys/socket.h> @@ -49,6 +48,7 @@ #include <err.h> #include "pfctl.h" +#include "pfctl_parser.h" #define BUF_SIZE 256 @@ -56,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) @@ -75,65 +88,15 @@ pfr_report_error(struct pfr_table *tbl, struct pfioc_table *io, } int -pfr_clr_tables(struct pfr_table *filter, int *ndel, int flags) +pfr_add_table(struct pfr_table *tbl, int *nadd, int flags) { - struct pfioc_table io; - - bzero(&io, sizeof io); - io.pfrio_flags = flags; - if (filter != NULL) - io.pfrio_table = *filter; - if (ioctl(dev, DIOCRCLRTABLES, &io)) - return (-1); - if (ndel != NULL) - *ndel = io.pfrio_ndel; - return (0); + return (pfctl_add_table(pfh, tbl, nadd, flags)); } int -pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags) +pfr_del_table(struct pfr_table *tbl, int *ndel, int flags) { - struct pfioc_table io; - - if (size < 0 || (size && tbl == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_buffer = tbl; - io.pfrio_esize = sizeof(*tbl); - io.pfrio_size = size; - if (ioctl(dev, DIOCRADDTABLES, &io)) { - pfr_report_error(tbl, &io, "add table"); - return (-1); - } - if (nadd != NULL) - *nadd = io.pfrio_nadd; - return (0); -} - -int -pfr_del_tables(struct pfr_table *tbl, int size, int *ndel, int flags) -{ - struct pfioc_table io; - - if (size < 0 || (size && tbl == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_buffer = tbl; - io.pfrio_esize = sizeof(*tbl); - io.pfrio_size = size; - if (ioctl(dev, DIOCRDELTABLES, &io)) { - pfr_report_error(tbl, &io, "delete table"); - return (-1); - } - if (ndel != NULL) - *ndel = io.pfrio_ndel; - return (0); + return (pfctl_del_table(pfh, tbl, ndel, flags)); } int @@ -162,47 +125,9 @@ pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size, } int -pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size, - int flags) -{ - struct pfioc_table io; - - if (size == NULL || *size < 0 || (*size && tbl == NULL)) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - if (filter != NULL) - io.pfrio_table = *filter; - io.pfrio_buffer = tbl; - io.pfrio_esize = sizeof(*tbl); - io.pfrio_size = *size; - if (ioctl(dev, DIOCRGETTSTATS, &io)) { - pfr_report_error(filter, &io, "get tstats for"); - return (-1); - } - *size = io.pfrio_size; - return (0); -} - -int pfr_clr_addrs(struct pfr_table *tbl, int *ndel, int flags) { - struct pfioc_table io; - - if (tbl == NULL) { - errno = EINVAL; - return (-1); - } - bzero(&io, sizeof io); - io.pfrio_flags = flags; - io.pfrio_table = *tbl; - if (ioctl(dev, DIOCRCLRADDRS, &io)) - return (-1); - if (ndel != NULL) - *ndel = io.pfrio_ndel; - return (0); + return (pfctl_clear_addrs(pfh, tbl, ndel, flags)); } int @@ -211,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); @@ -225,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); @@ -288,23 +216,23 @@ pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size, } int -pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags) +pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size, + int *nzero, int flags) { struct pfioc_table io; - if (size < 0 || (size && !tbl)) { + if (size < 0 || !tbl || (size && !addr)) { errno = EINVAL; return (-1); } bzero(&io, sizeof io); io.pfrio_flags = flags; - io.pfrio_buffer = tbl; - io.pfrio_esize = sizeof(*tbl); + io.pfrio_table = *tbl; + io.pfrio_buffer = addr; + io.pfrio_esize = sizeof(*addr); io.pfrio_size = size; - if (ioctl(dev, DIOCRCLRTSTATS, &io)) { - pfr_report_error(tbl, &io, "clear tstats from"); + if (ioctl(dev, DIOCRCLRASTATS, &io) == -1) return (-1); - } if (nzero) *nzero = io.pfrio_nzero; return (0); @@ -342,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); } @@ -462,25 +391,15 @@ pfr_buf_grow(struct pfr_buffer *b, int minsize) if (!b->pfrb_msize) { if (minsize < 64) minsize = 64; - b->pfrb_caddr = calloc(bs, minsize); - if (b->pfrb_caddr == NULL) - return (-1); - b->pfrb_msize = minsize; - } else { - if (minsize == 0) - minsize = b->pfrb_msize * 2; - if (minsize < 0 || minsize >= SIZE_T_MAX / bs) { - /* msize overflow */ - errno = ENOMEM; - return (-1); - } - p = realloc(b->pfrb_caddr, minsize * bs); - if (p == NULL) - return (-1); - bzero(p + b->pfrb_msize * bs, (minsize - b->pfrb_msize) * bs); - b->pfrb_caddr = p; - b->pfrb_msize = minsize; } + if (minsize == 0) + minsize = b->pfrb_msize * 2; + p = reallocarray(b->pfrb_caddr, minsize, bs); + if (p == NULL) + return (-1); + bzero(p + b->pfrb_msize * bs, (minsize - b->pfrb_msize) * bs); + b->pfrb_caddr = p; + b->pfrb_msize = minsize; return (0); } @@ -492,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]; @@ -516,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; } @@ -535,8 +453,8 @@ pfr_next_token(char buf[BUF_SIZE], FILE *fp) /* skip spaces */ while (isspace(next_ch) && !feof(fp)) next_ch = fgetc(fp); - /* remove from '#' until end of line */ - if (next_ch == '#') + /* remove from '#' or ';' until end of line */ + if (next_ch == '#' || next_ch == ';') while (!feof(fp)) { next_ch = fgetc(fp); if (next_ch == '\n') @@ -561,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 fe934a8d2ea2..4955e1791fd7 100644 --- a/sbin/pfctl/pfctl_table.c +++ b/sbin/pfctl/pfctl_table.c @@ -32,14 +32,14 @@ * */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/ioctl.h> #include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> #include <net/if.h> #include <net/pfvar.h> -#include <arpa/inet.h> #include <ctype.h> #include <err.h> @@ -55,14 +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(struct pfr_table *, int, int); -static void print_tstats(struct pfr_tstats *, int); -static int load_addr(struct pfr_buffer *, int, char *[], char *, int); +static void print_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, 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); @@ -76,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_tables(&table, 1, &nadd, flags)) && \ + (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); \ @@ -104,24 +104,23 @@ static const char *istats_text[2][2][2] = { } while(0) int -pfctl_clear_tables(const char *anchor, int opts) +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 @@ -157,39 +156,42 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command, if (!strcmp(command, "-F")) { if (argc || file != NULL) usage(); - RVTEST(pfr_clr_tables(&table, &ndel, flags)); + RVTEST(pfctl_clear_tables(pfh, &table, &ndel, flags)); xprintf(opts, "%d tables deleted", ndel); } else if (!strcmp(command, "-s")) { b.pfrb_type = (opts & PF_OPT_VERBOSE2) ? PFRB_TSTATS : PFRB_TABLES; if (argc || file != NULL) usage(); - for (;;) { - pfr_buf_grow(&b, b.pfrb_size); - b.pfrb_size = b.pfrb_msize; - if (opts & PF_OPT_VERBOSE2) - RVTEST(pfr_get_tstats(&table, - b.pfrb_caddr, &b.pfrb_size, flags)); - else - RVTEST(pfr_get_tables(&table, - b.pfrb_caddr, &b.pfrb_size, flags)); - if (b.pfrb_size <= b.pfrb_msize) - break; - } if ((opts & PF_OPT_SHOWALL) && b.pfrb_size > 0) pfctl_print_title("TABLES:"); - PFRB_FOREACH(p, &b) - if (opts & PF_OPT_VERBOSE2) - print_tstats(p, opts & PF_OPT_DEBUG); - else + if (opts & PF_OPT_VERBOSE2) { + uintptr_t arg = opts & PF_OPT_DEBUG; + pfctl_get_tstats(pfh, &table, + (pfctl_get_tstats_fn)print_tstats, (void *)arg); + } else { + for (;;) { + pfr_buf_grow(&b, b.pfrb_size); + b.pfrb_size = b.pfrb_msize; + RVTEST(pfr_get_tables(&table, + b.pfrb_caddr, &b.pfrb_size, flags)); + if (b.pfrb_size <= b.pfrb_msize) + break; + } + + if ((opts & PF_OPT_SHOWALL) && b.pfrb_size > 0) + pfctl_print_title("TABLES:"); + + PFRB_FOREACH(p, &b) print_table(p, opts & PF_OPT_VERBOSE, opts & PF_OPT_DEBUG); + } } else if (!strcmp(command, "kill")) { if (argc || file != NULL) usage(); - RVTEST(pfr_del_tables(&table, 1, &ndel, flags)); + RVTEST(pfr_del_table(&table, &ndel, flags)); xprintf(opts, "%d table deleted", ndel); } else if (!strcmp(command, "flush")) { if (argc || file != NULL) @@ -198,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) @@ -208,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; @@ -222,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) @@ -253,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")) { @@ -276,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, @@ -291,6 +296,37 @@ 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 != PFR_FB_NONE) + print_addrx(a, NULL, + opts & PF_OPT_USEDNS); + } else if (!strcmp(command, "reset")) { + struct pfr_astats *as; + + b.pfrb_type = PFRB_ASTATS; + b2.pfrb_type = PFRB_ADDRS; + if (argc || file != NULL) + usage(); + do { + pfr_buf_grow(&b, b.pfrb_size); + b.pfrb_size = b.pfrb_msize; + RVTEST(pfr_get_astats(&table, b.pfrb_caddr, + &b.pfrb_size, flags)); + } while (b.pfrb_size > b.pfrb_msize); + PFRB_FOREACH(as, &b) { + as->pfras_a.pfra_fback = 0; + if (nonzero_astats(as)) + if (pfr_buf_add(&b2, &as->pfras_a)) + err(1, "duplicate buffer"); + } + + if (opts & PF_OPT_VERBOSE) + flags |= PFR_FLAG_FEEDBACK; + RVTEST(pfr_clr_astats(&table, b2.pfrb_caddr, b2.pfrb_size, + &nzero, flags)); + xprintf(opts, "%d/%d stats cleared", nzero, b.pfrb_size); + if (opts & PF_OPT_VERBOSE) + PFRB_FOREACH(a, &b2) if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback) print_addrx(a, NULL, opts & PF_OPT_USEDNS); @@ -320,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; @@ -345,11 +381,24 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command, } if (nmatch < b.pfrb_size) rv = 2; + } else if (!strcmp(command, "zero") && (argc || file != NULL)) { + b.pfrb_type = PFRB_ADDRS; + if (load_addr(&b, argc, argv, file, 0, opts)) + goto _error; + if (opts & PF_OPT_VERBOSE) + flags |= PFR_FLAG_FEEDBACK; + RVTEST(pfr_clr_astats(&table, b.pfrb_caddr, b.pfrb_size, + &nzero, flags)); + xprintf(opts, "%d/%d addresses cleared", nzero, b.pfrb_size); + if (opts & PF_OPT_VERBOSE) + PFRB_FOREACH(a, &b) + if (opts & PF_OPT_VERBOSE2 || + a->pfra_fback != PFR_FB_NONE) + print_addrx(a, NULL, + opts & PF_OPT_USEDNS); } else if (!strcmp(command, "zero")) { - if (argc || file != NULL) - usage(); 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); @@ -364,38 +413,43 @@ _cleanup: } void -print_table(struct pfr_table *ta, int verbose, int debug) +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"); } -void -print_tstats(struct pfr_tstats *ts, int debug) +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; + 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]); @@ -408,19 +462,21 @@ print_tstats(struct pfr_tstats *ts, int debug) stats_text[dir][op], (unsigned long long)ts->pfrts_packets[dir][op], (unsigned long long)ts->pfrts_bytes[dir][op]); + + return (0); } 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); } @@ -453,34 +509,56 @@ print_addrx(struct pfr_addr *ad, struct pfr_addr *rad, int dns) printf("\t nomatch"); if (dns && ad->pfra_net == hostnet) { char host[NI_MAXHOST]; - union sockaddr_union sa; + struct sockaddr_storage ss; strlcpy(host, "?", sizeof(host)); - bzero(&sa, sizeof(sa)); - sa.sa.sa_family = ad->pfra_af; - if (sa.sa.sa_family == AF_INET) { - sa.sa.sa_len = sizeof(sa.sin); - sa.sin.sin_addr = ad->pfra_ip4addr; + bzero(&ss, sizeof(ss)); + ss.ss_family = ad->pfra_af; + if (ss.ss_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)&ss; + + sin->sin_len = sizeof(*sin); + sin->sin_addr = ad->pfra_ip4addr; } else { - sa.sa.sa_len = sizeof(sa.sin6); - sa.sin6.sin6_addr = ad->pfra_ip6addr; + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss; + + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_addr = ad->pfra_ip6addr; } - if (getnameinfo(&sa.sa, sa.sa.sa_len, host, sizeof(host), - NULL, 0, NI_NAMEREQD) == 0) + if (getnameinfo((struct sockaddr *)&ss, ss.ss_len, host, + sizeof(host), NULL, 0, NI_NAMEREQD) == 0) printf("\t(%s)", host); } printf("\n"); } +int +nonzero_astats(struct pfr_astats *as) +{ + uint64_t s = 0; + + for (int dir = 0; dir < PFR_DIR_MAX; dir++) + for (int op = 0; op < PFR_OP_ADDR_MAX; op++) + s |= as->pfras_packets[dir][op] | + as->pfras_bytes[dir][op]; + + return (!!s); +} + 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++) @@ -490,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; - - 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; + 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; + } - return pfr_ina_define(&tbl, ab->pfrb_caddr, ab->pfrb_size, NULL, - NULL, ticket, addrs ? PFR_FLAG_ADDRSTOO : 0); + 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)); } 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; @@ -536,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); } @@ -578,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; @@ -589,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; } @@ -600,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) { @@ -618,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/Makefile b/sbin/pfctl/tests/Makefile index db41a445903f..281cd97dee78 100644 --- a/sbin/pfctl/tests/Makefile +++ b/sbin/pfctl/tests/Makefile @@ -1,4 +1,3 @@ - PACKAGE= tests ATF_TESTS_C= pfctl_test @@ -8,4 +7,6 @@ LIBADD+= sbuf SUBDIR+= files WARNS=6 +pfctl_test.o: pfctl_test_list.inc + .include <bsd.test.mk> diff --git a/sbin/pfctl/tests/Makefile.depend b/sbin/pfctl/tests/Makefile.depend index 11aba52f82cf..a59dba5bf1aa 100644 --- a/sbin/pfctl/tests/Makefile.depend +++ b/sbin/pfctl/tests/Makefile.depend @@ -1,6 +1,11 @@ # Autogenerated - do NOT edit! DIRDEPS = \ + lib/${CSU_DIR} \ + lib/atf/libatf-c \ + lib/libc \ + lib/libcompiler_rt \ + lib/libsbuf \ .include <dirdeps.mk> diff --git a/sbin/pfctl/tests/files/Makefile b/sbin/pfctl/tests/files/Makefile index c676a42f62b8..fc52b1db3c30 100644 --- a/sbin/pfctl/tests/files/Makefile +++ b/sbin/pfctl/tests/files/Makefile @@ -1,10 +1,9 @@ - PACKAGE= tests 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/pf0004.ok b/sbin/pfctl/tests/files/pf0004.ok index 5fca4a50f7b1..87b71cdeff3d 100644 --- a/sbin/pfctl/tests/files/pf0004.ok +++ b/sbin/pfctl/tests/files/pf0004.ok @@ -15,48 +15,48 @@ block drop in proto tcp from any port >= 80 to any port 1024:2048 block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 192.168.0.0/16 port = ircd block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 192.168.0.0/16 port = 6668 block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 192.168.0.0/16 port 6669:65535 -block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = ircd -block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = 6668 -block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 12.34.56.78 port 6669:65535 block drop in inet proto tcp from 10.0.0.0/8 port = ftp to 192.168.0.0/16 port = ircd block drop in inet proto tcp from 10.0.0.0/8 port = ftp to 192.168.0.0/16 port = 6668 block drop in inet proto tcp from 10.0.0.0/8 port = ftp to 192.168.0.0/16 port 6669:65535 +block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = ircd +block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = 6668 +block drop in inet proto tcp from 10.0.0.0/8 port = ssh to 12.34.56.78 port 6669:65535 block drop in inet proto tcp from 10.0.0.0/8 port = ftp to 12.34.56.78 port = ircd block drop in inet proto tcp from 10.0.0.0/8 port = ftp to 12.34.56.78 port = 6668 block drop in inet proto tcp from 10.0.0.0/8 port = ftp to 12.34.56.78 port 6669:65535 block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 192.168.0.0/16 port = ircd block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 192.168.0.0/16 port = 6668 block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 192.168.0.0/16 port 6669:65535 -block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = ircd -block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = 6668 -block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 12.34.56.78 port 6669:65535 block drop in inet proto tcp from 172.16.0.0/12 port = ftp to 192.168.0.0/16 port = ircd block drop in inet proto tcp from 172.16.0.0/12 port = ftp to 192.168.0.0/16 port = 6668 block drop in inet proto tcp from 172.16.0.0/12 port = ftp to 192.168.0.0/16 port 6669:65535 +block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = ircd +block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = 6668 +block drop in inet proto tcp from 172.16.0.0/12 port = ssh to 12.34.56.78 port 6669:65535 block drop in inet proto tcp from 172.16.0.0/12 port = ftp to 12.34.56.78 port = ircd block drop in inet proto tcp from 172.16.0.0/12 port = ftp to 12.34.56.78 port = 6668 block drop in inet proto tcp from 172.16.0.0/12 port = ftp to 12.34.56.78 port 6669:65535 block drop in inet proto udp from 10.0.0.0/8 port = ssh to 192.168.0.0/16 port = 6667 block drop in inet proto udp from 10.0.0.0/8 port = ssh to 192.168.0.0/16 port = 6668 block drop in inet proto udp from 10.0.0.0/8 port = ssh to 192.168.0.0/16 port 6669:65535 -block drop in inet proto udp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = 6667 -block drop in inet proto udp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = 6668 -block drop in inet proto udp from 10.0.0.0/8 port = ssh to 12.34.56.78 port 6669:65535 block drop in inet proto udp from 10.0.0.0/8 port = ftp to 192.168.0.0/16 port = 6667 block drop in inet proto udp from 10.0.0.0/8 port = ftp to 192.168.0.0/16 port = 6668 block drop in inet proto udp from 10.0.0.0/8 port = ftp to 192.168.0.0/16 port 6669:65535 +block drop in inet proto udp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = 6667 +block drop in inet proto udp from 10.0.0.0/8 port = ssh to 12.34.56.78 port = 6668 +block drop in inet proto udp from 10.0.0.0/8 port = ssh to 12.34.56.78 port 6669:65535 block drop in inet proto udp from 10.0.0.0/8 port = ftp to 12.34.56.78 port = 6667 block drop in inet proto udp from 10.0.0.0/8 port = ftp to 12.34.56.78 port = 6668 block drop in inet proto udp from 10.0.0.0/8 port = ftp to 12.34.56.78 port 6669:65535 block drop in inet proto udp from 172.16.0.0/12 port = ssh to 192.168.0.0/16 port = 6667 block drop in inet proto udp from 172.16.0.0/12 port = ssh to 192.168.0.0/16 port = 6668 block drop in inet proto udp from 172.16.0.0/12 port = ssh to 192.168.0.0/16 port 6669:65535 -block drop in inet proto udp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = 6667 -block drop in inet proto udp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = 6668 -block drop in inet proto udp from 172.16.0.0/12 port = ssh to 12.34.56.78 port 6669:65535 block drop in inet proto udp from 172.16.0.0/12 port = ftp to 192.168.0.0/16 port = 6667 block drop in inet proto udp from 172.16.0.0/12 port = ftp to 192.168.0.0/16 port = 6668 block drop in inet proto udp from 172.16.0.0/12 port = ftp to 192.168.0.0/16 port 6669:65535 +block drop in inet proto udp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = 6667 +block drop in inet proto udp from 172.16.0.0/12 port = ssh to 12.34.56.78 port = 6668 +block drop in inet proto udp from 172.16.0.0/12 port = ssh to 12.34.56.78 port 6669:65535 block drop in inet proto udp from 172.16.0.0/12 port = ftp to 12.34.56.78 port = 6667 block drop in inet proto udp from 172.16.0.0/12 port = ftp to 12.34.56.78 port = 6668 block drop in inet proto udp from 172.16.0.0/12 port = ftp to 12.34.56.78 port 6669:65535 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/pf0102.ok b/sbin/pfctl/tests/files/pf0102.ok index 3233ca5a2643..1c76ec2725ba 100644 --- a/sbin/pfctl/tests/files/pf0102.ok +++ b/sbin/pfctl/tests/files/pf0102.ok @@ -1,8 +1,8 @@ pass inet from 1.1.1.1 to (self) flags S/SA keep state -pass inet6 from 2002:: to (self)/32 flags S/SA keep state +pass inet6 from 2002:: to (self) flags S/SA keep state pass inet6 from 2002:: to (self) flags S/SA keep state pass inet from 1.1.1.1 to (self) flags S/SA keep state pass inet from 1.1.1.1 to (self) flags S/SA keep state -pass inet6 from 2002:: to (self)/32 flags S/SA keep state +pass inet6 from 2002:: to (self)/40 flags S/SA keep state pass inet6 from 2002:: to (self)/40 flags S/SA keep state pass inet from 1.1.1.1 to (self) flags S/SA keep state diff --git a/sbin/pfctl/tests/files/pf1002.in b/sbin/pfctl/tests/files/pf1002.in index 5180e8395f9c..3fdde81be7de 100644 --- a/sbin/pfctl/tests/files/pf1002.in +++ b/sbin/pfctl/tests/files/pf1002.in @@ -1 +1,6 @@ set timeout interval 10 +set timeout sctp.first 11 +set timeout sctp.opening 12 +set timeout sctp.established 13 +set timeout sctp.closing 14 +set timeout sctp.closed 15 diff --git a/sbin/pfctl/tests/files/pf1002.ok b/sbin/pfctl/tests/files/pf1002.ok index 5180e8395f9c..3fdde81be7de 100644 --- a/sbin/pfctl/tests/files/pf1002.ok +++ b/sbin/pfctl/tests/files/pf1002.ok @@ -1 +1,6 @@ set timeout interval 10 +set timeout sctp.first 11 +set timeout sctp.opening 12 +set timeout sctp.established 13 +set timeout sctp.closing 14 +set timeout sctp.closed 15 diff --git a/sbin/pfctl/tests/files/pf1018.in b/sbin/pfctl/tests/files/pf1018.in new file mode 100644 index 000000000000..90f0a3a0bab7 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1018.in @@ -0,0 +1 @@ +pass from { 192.0.2.1 2001:db8::1 } to (pppoe0) diff --git a/sbin/pfctl/tests/files/pf1018.ok b/sbin/pfctl/tests/files/pf1018.ok new file mode 100644 index 000000000000..04950f0035b8 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1018.ok @@ -0,0 +1,2 @@ +pass inet from 192.0.2.1 to (pppoe0) flags S/SA keep state +pass inet6 from 2001:db8::1 to (pppoe0) flags S/SA keep state diff --git a/sbin/pfctl/tests/files/pf1019.in b/sbin/pfctl/tests/files/pf1019.in new file mode 100644 index 000000000000..04a770768714 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1019.in @@ -0,0 +1 @@ +pass in keep state (pflow) diff --git a/sbin/pfctl/tests/files/pf1019.ok b/sbin/pfctl/tests/files/pf1019.ok new file mode 100644 index 000000000000..e865d57da16c --- /dev/null +++ b/sbin/pfctl/tests/files/pf1019.ok @@ -0,0 +1 @@ +pass in all flags S/SA keep state (pflow) diff --git a/sbin/pfctl/tests/files/pf1020.in b/sbin/pfctl/tests/files/pf1020.in new file mode 100644 index 000000000000..7f98df69bd04 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1020.in @@ -0,0 +1,3 @@ +table <tabl1> file "./pf1020.include" + +block from <tabl1> diff --git a/sbin/pfctl/tests/files/pf1020.include b/sbin/pfctl/tests/files/pf1020.include new file mode 100644 index 000000000000..3fca07f64bfa --- /dev/null +++ b/sbin/pfctl/tests/files/pf1020.include @@ -0,0 +1,4 @@ +; comment1 +# comment2 +1.0.0.1/32 ; comment1 +2.0.0.2/32 # comment2 diff --git a/sbin/pfctl/tests/files/pf1020.ok b/sbin/pfctl/tests/files/pf1020.ok new file mode 100644 index 000000000000..16073b3d6987 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1020.ok @@ -0,0 +1,2 @@ +table <tabl1> file "./pf1020.include" +block drop from <tabl1> to any diff --git a/sbin/pfctl/tests/files/pf1021.in b/sbin/pfctl/tests/files/pf1021.in new file mode 100644 index 000000000000..841b024157c6 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1021.in @@ -0,0 +1 @@ +nat on vtnet1 inet from ! (vtnet1) to any -> (vtnet1) endpoint-independent diff --git a/sbin/pfctl/tests/files/pf1021.ok b/sbin/pfctl/tests/files/pf1021.ok new file mode 100644 index 000000000000..3b5b84e2e11b --- /dev/null +++ b/sbin/pfctl/tests/files/pf1021.ok @@ -0,0 +1 @@ +nat on vtnet1 inet from ! (vtnet1) to any -> (vtnet1) round-robin endpoint-independent diff --git a/sbin/pfctl/tests/files/pf1022.in b/sbin/pfctl/tests/files/pf1022.in new file mode 100644 index 000000000000..640eb1334100 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1022.in @@ -0,0 +1 @@ +pass out on em0 from 192.0.2.1 to 198.51.100.1 received-on fxp0 diff --git a/sbin/pfctl/tests/files/pf1022.ok b/sbin/pfctl/tests/files/pf1022.ok new file mode 100644 index 000000000000..2f7b4a5bd616 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1022.ok @@ -0,0 +1 @@ +pass out on em0 inet from 192.0.2.1 to 198.51.100.1 received-on fxp0 flags S/SA keep state diff --git a/sbin/pfctl/tests/files/pf1023.in b/sbin/pfctl/tests/files/pf1023.in new file mode 100644 index 000000000000..4855ae0f339e --- /dev/null +++ b/sbin/pfctl/tests/files/pf1023.in @@ -0,0 +1,3 @@ +match log(matches) inet proto tcp +match log(matches) inet from 192.0.2.0/24 +pass diff --git a/sbin/pfctl/tests/files/pf1023.ok b/sbin/pfctl/tests/files/pf1023.ok new file mode 100644 index 000000000000..63fa40113ecf --- /dev/null +++ b/sbin/pfctl/tests/files/pf1023.ok @@ -0,0 +1,3 @@ +match log (matches) inet proto tcp all +match log (matches) inet from 192.0.2.0/24 to any +pass all flags S/SA keep state diff --git a/sbin/pfctl/tests/files/pf1024.in b/sbin/pfctl/tests/files/pf1024.in new file mode 100644 index 000000000000..be518bb3bd53 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1024.in @@ -0,0 +1 @@ +pass in inet af-to inet6 from 2001:db8::1 diff --git a/sbin/pfctl/tests/files/pf1024.ok b/sbin/pfctl/tests/files/pf1024.ok new file mode 100644 index 000000000000..2d4ddb9d0ce7 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1024.ok @@ -0,0 +1 @@ +pass in inet all flags S/SA keep state af-to inet6 from 2001:db8::1 diff --git a/sbin/pfctl/tests/files/pf1025.in b/sbin/pfctl/tests/files/pf1025.in new file mode 100644 index 000000000000..d4ad821a6899 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1025.in @@ -0,0 +1 @@ +pass in from 10.0.0.0/8 af-to inet6 from 2001:db8::1 diff --git a/sbin/pfctl/tests/files/pf1025.ok b/sbin/pfctl/tests/files/pf1025.ok new file mode 100644 index 000000000000..8f48c987c6a0 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1025.ok @@ -0,0 +1 @@ +pass in inet from 10.0.0.0/8 to any flags S/SA keep state af-to inet6 from 2001:db8::1 diff --git a/sbin/pfctl/tests/files/pf1026.in b/sbin/pfctl/tests/files/pf1026.in new file mode 100644 index 000000000000..3691d0947b39 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1026.in @@ -0,0 +1 @@ +pass in on epair2b route-to (epair0a 192.0.2.2) inet6 from any to 64:ff9b::/96 af-to inet from (epair0a) diff --git a/sbin/pfctl/tests/files/pf1026.ok b/sbin/pfctl/tests/files/pf1026.ok new file mode 100644 index 000000000000..323036f2b800 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1026.ok @@ -0,0 +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) round-robin diff --git a/sbin/pfctl/tests/files/pf1027.in b/sbin/pfctl/tests/files/pf1027.in new file mode 100644 index 000000000000..3c5c24025e0a --- /dev/null +++ b/sbin/pfctl/tests/files/pf1027.in @@ -0,0 +1 @@ +pass in on epair2b reply-to (epair0a 2001:db8::1) inet6 from any to 64:ff9b::/96 af-to inet from (epair0a) diff --git a/sbin/pfctl/tests/files/pf1027.ok b/sbin/pfctl/tests/files/pf1027.ok new file mode 100644 index 000000000000..b50f1e216837 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1027.ok @@ -0,0 +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) 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 5a98ad9f0d5f..5f0aa7826bb4 100644 --- a/sbin/pfctl/tests/pfctl_test.c +++ b/sbin/pfctl/tests/pfctl_test.c @@ -30,7 +30,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. */ -#include <sys/cdefs.h> + #include <sys/types.h> #include <sys/param.h> #include <err.h> @@ -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 98ea3257d492..9dd4a590ad8f 100644 --- a/sbin/pfctl/tests/pfctl_test_list.inc +++ b/sbin/pfctl/tests/pfctl_test_list.inc @@ -126,3 +126,61 @@ PFCTL_TEST(1014, "Ethernet rule with one label") PFCTL_TEST(1015, "Ethernet rule with several labels") PFCTL_TEST(1016, "Ethernet rule with ridentifier and one label") PFCTL_TEST(1017, "Ethernet rule with ridentifier and several labels") +PFCTL_TEST(1018, "Test dynamic address mask") +PFCTL_TEST(1019, "Test pflow option") +PFCTL_TEST(1020, "Test hashmark and semicolon comment") +PFCTL_TEST(1021, "Endpoint-independent") +PFCTL_TEST(1022, "Test received-on") +PFCTL_TEST(1023, "Test match log(matches)") +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/pfilctl/Makefile b/sbin/pfilctl/Makefile index fb67b5ea6490..d5efad642bfd 100644 --- a/sbin/pfilctl/Makefile +++ b/sbin/pfilctl/Makefile @@ -1,4 +1,3 @@ - PROG= pfilctl SRCS= pfilctl.c diff --git a/sbin/pfilctl/pfilctl.c b/sbin/pfilctl/pfilctl.c index c2fe04ecc777..1af2190072b6 100644 --- a/sbin/pfilctl/pfilctl.c +++ b/sbin/pfilctl/pfilctl.c @@ -25,7 +25,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/ioctl.h> #include <net/if.h> @@ -151,8 +150,8 @@ listhooks(int argc __unused, char *argv[] __unused) u_int nhooks, i; plh.pio_nhooks = 0; - if (ioctl(dev, PFILIOC_LISTHEADS, &plh) != 0) - err(1, "ioctl(PFILIOC_LISTHEADS)"); + if (ioctl(dev, PFILIOC_LISTHOOKS, &plh) != 0) + err(1, "ioctl(PFILIOC_LISTHOOKS)"); retry: plh.pio_hooks = calloc(plh.pio_nhooks, sizeof(struct pfilioc_hook)); if (plh.pio_hooks == NULL) diff --git a/sbin/pflogd/Makefile b/sbin/pflogd/Makefile index 17077a4f825f..7b4a636843e0 100644 --- a/sbin/pflogd/Makefile +++ b/sbin/pflogd/Makefile @@ -1,4 +1,3 @@ - .PATH: ${SRCTOP}/contrib/pf/pflogd PACKAGE=pf diff --git a/sbin/pflowctl/Makefile b/sbin/pflowctl/Makefile new file mode 100644 index 000000000000..dd20f7b7a37a --- /dev/null +++ b/sbin/pflowctl/Makefile @@ -0,0 +1,9 @@ +.include <src.opts.mk> + +PACKAGE=pf +PROG= pflowctl +MAN= pflowctl.8 + +SRCS = pflowctl.c + +.include <bsd.prog.mk> diff --git a/sbin/gbde/Makefile.depend b/sbin/pflowctl/Makefile.depend index 2edf986e595a..6ef78fac5cbf 100644 --- a/sbin/gbde/Makefile.depend +++ b/sbin/pflowctl/Makefile.depend @@ -6,9 +6,6 @@ DIRDEPS = \ lib/${CSU_DIR} \ lib/libc \ lib/libcompiler_rt \ - lib/libgeom \ - lib/libmd \ - lib/libutil \ .include <dirdeps.mk> diff --git a/sbin/pflowctl/pflowctl.8 b/sbin/pflowctl/pflowctl.8 new file mode 100644 index 000000000000..407947613f6f --- /dev/null +++ b/sbin/pflowctl/pflowctl.8 @@ -0,0 +1,97 @@ +.\" $OpenBSD: pflow.4,v 1.19 2014/03/29 11:26:03 florian Exp $ +.\" +.\" Copyright (c) 2008 Henning Brauer <henning@openbsd.org> +.\" Copyright (c) 2008 Joerg Goltermann <jg@osn.de> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: January 25 2024 $ +.Dt PFLOWCTL 8 +.Os +.Sh NAME +.Nm pflowctl +.Nd control pflow data export +.Sh SYNOPSIS +.Nm pflowctl +.Bk -words +.Op Fl v +.Op Fl lc +.Op Fl d Ar id +.Op Fl s Ar id ... +.Ek +.Sh DESCRIPTION +The +.Nm +utility creates, configures and deletes netflow accounting data export using the +.Xr pflow 4 +subsystem. +.Pp +The +.Nm +utility provides several commands. +The options are as follows: +.Bl -tag -width Ds +.It Fl v +Produce verbose output. +.It Fl c +Create a new +.Xr pflow 4 +exporter. +.It Fl d Ar id +Remove an existing +.Xr pflow 4 +exporter. +The +.Ar id +may be either numeric or the full pflowX name. +.It Fl l +List all existing +.Xr pflow 4 +exporters. +.It Fl s Ar id ... +Configure an existing +.Xr pflow 4 +exporter. +This takes the following keywords: +.Pp +.Bl -tag -width xxxxxxxxxxxx -compact +.It Cm src +set the source IP address (and optionally port). +.It Cm dst +set the destination IP address (and optionally port). +.It Cm proto +set the protocol version. +Valid values are 5 and 10. +.It Cm domain +set the observation domain. +.El +.Pp +Multiple keywords may be passed in the same command invocation. +.Pp +For example, the following command sets 10.0.0.1 as the source +and 10.0.0.2:1234 as destination: +.Bd -literal -offset indent +# pflowctl -s pflow0 src 10.0.0.1 dst 10.0.0.2:1234 +.Ed +.El +.Sh SEE ALSO +.Xr netintro 4 , +.Xr pf 4 , +.Xr pflow 4 , +.Xr udp 4 , +.Xr pf.conf 5 +.Sh HISTORY +The +.Nm +command first appeared in +.Fx 15.0 . diff --git a/sbin/pflowctl/pflowctl.c b/sbin/pflowctl/pflowctl.c new file mode 100644 index 000000000000..b6561f7787c0 --- /dev/null +++ b/sbin/pflowctl/pflowctl.c @@ -0,0 +1,602 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2023 Rubicon Communications, LLC (Netgate) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <sys/cdefs.h> + +#include <err.h> +#include <errno.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <net/pflow.h> + +#include <netlink/netlink.h> +#include <netlink/netlink_generic.h> +#include <netlink/netlink_snl.h> +#include <netlink/netlink_snl_generic.h> +#include <netlink/netlink_snl_route.h> + +static int get(int id); + +static bool verbose = false; + +extern char *__progname; + +static void +usage(void) +{ + fprintf(stderr, +"usage: %s [-lc] [-d id] [-s id ...] [-v]\n", + __progname); + + exit(1); +} + +static int +pflow_to_id(const char *name) +{ + int ret, id; + + ret = sscanf(name, "pflow%d", &id); + if (ret == 1) + return (id); + + ret = sscanf(name, "%d", &id); + if (ret == 1) + return (id); + + return (-1); +} + +struct pflowctl_list { + int id; +}; +#define _IN(_field) offsetof(struct genlmsghdr, _field) +#define _OUT(_field) offsetof(struct pflowctl_list, _field) +static struct snl_attr_parser ap_list[] = { + { .type = PFLOWNL_L_ID, .off = _OUT(id), .cb = snl_attr_get_int32 }, +}; +static struct snl_field_parser fp_list[] = {}; +#undef _IN +#undef _OUT +SNL_DECLARE_PARSER(list_parser, struct genlmsghdr, fp_list, ap_list); + +static int +list(void) +{ + struct snl_state ss = {}; + struct snl_errmsg_data e = {}; + struct pflowctl_list l = {}; + struct snl_writer nw; + struct nlmsghdr *hdr; + uint32_t seq_id; + int family_id; + + snl_init(&ss, NETLINK_GENERIC); + family_id = snl_get_genl_family(&ss, PFLOWNL_FAMILY_NAME); + if (family_id == 0) + errx(1, "pflow.ko is not loaded."); + + snl_init_writer(&ss, &nw); + hdr = snl_create_genl_msg_request(&nw, family_id, PFLOWNL_CMD_LIST); + + hdr = snl_finalize_msg(&nw); + if (hdr == NULL) + return (ENOMEM); + seq_id = hdr->nlmsg_seq; + + snl_send_message(&ss, hdr); + + while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) { + if (! snl_parse_nlmsg(&ss, hdr, &list_parser, &l)) + continue; + + get(l.id); + } + + if (e.error) + errc(1, e.error, "failed to list"); + + return (0); +} + +struct pflowctl_create { + int id; +}; +#define _IN(_field) offsetof(struct genlmsghsdr, _field) +#define _OUT(_field) offsetof(struct pflowctl_create, _field) +static struct snl_attr_parser ap_create[] = { + { .type = PFLOWNL_CREATE_ID, .off = _OUT(id), .cb = snl_attr_get_int32 }, +}; +static struct snl_field_parser pf_create[] = {}; +#undef _IN +#undef _OUT +SNL_DECLARE_PARSER(create_parser, struct genlmsghdr, pf_create, ap_create); + +static int +create(void) +{ + struct snl_state ss = {}; + struct snl_errmsg_data e = {}; + struct pflowctl_create c = {}; + struct snl_writer nw; + struct nlmsghdr *hdr; + uint32_t seq_id; + int family_id; + + snl_init(&ss, NETLINK_GENERIC); + family_id = snl_get_genl_family(&ss, PFLOWNL_FAMILY_NAME); + if (family_id == 0) + errx(1, "pflow.ko is not loaded."); + + snl_init_writer(&ss, &nw); + hdr = snl_create_genl_msg_request(&nw, family_id, PFLOWNL_CMD_CREATE); + + hdr = snl_finalize_msg(&nw); + if (hdr == NULL) + return (ENOMEM); + seq_id = hdr->nlmsg_seq; + + snl_send_message(&ss, hdr); + + while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) { + if (! snl_parse_nlmsg(&ss, hdr, &create_parser, &c)) + continue; + + printf("pflow%d\n", c.id); + } + + if (e.error) + errc(1, e.error, "failed to create"); + + return (0); +} + +static int +del(char *idstr) +{ + struct snl_state ss = {}; + struct snl_errmsg_data e = {}; + struct snl_writer nw; + struct nlmsghdr *hdr; + int family_id; + int id; + + id = pflow_to_id(idstr); + if (id < 0) + return (EINVAL); + + snl_init(&ss, NETLINK_GENERIC); + family_id = snl_get_genl_family(&ss, PFLOWNL_FAMILY_NAME); + if (family_id == 0) + errx(1, "pflow.ko is not loaded."); + + snl_init_writer(&ss, &nw); + hdr = snl_create_genl_msg_request(&nw, family_id, PFLOWNL_CMD_DEL); + + snl_add_msg_attr_s32(&nw, PFLOWNL_DEL_ID, id); + + hdr = snl_finalize_msg(&nw); + if (hdr == NULL) + return (ENOMEM); + + snl_send_message(&ss, hdr); + snl_read_reply_code(&ss, hdr->nlmsg_seq, &e); + + if (e.error) + errc(1, e.error, "failed to delete"); + + return (0); +} + +struct pflowctl_sockaddr { + union { + struct sockaddr_in in; + struct sockaddr_in6 in6; + struct sockaddr_storage storage; + }; +}; +static bool +pflowctl_post_sockaddr(struct snl_state* ss __unused, void *target) +{ + struct pflowctl_sockaddr *s = (struct pflowctl_sockaddr *)target; + + if (s->storage.ss_family == AF_INET) + s->storage.ss_len = sizeof(struct sockaddr_in); + else if (s->storage.ss_family == AF_INET6) + s->storage.ss_len = sizeof(struct sockaddr_in6); + else + return (false); + + return (true); +} +#define _OUT(_field) offsetof(struct pflowctl_sockaddr, _field) +static struct snl_attr_parser nla_p_sockaddr[] = { + { .type = PFLOWNL_ADDR_FAMILY, .off = _OUT(in.sin_family), .cb = snl_attr_get_uint8 }, + { .type = PFLOWNL_ADDR_PORT, .off = _OUT(in.sin_port), .cb = snl_attr_get_uint16 }, + { .type = PFLOWNL_ADDR_IP, .off = _OUT(in.sin_addr), .cb = snl_attr_get_in_addr }, + { .type = PFLOWNL_ADDR_IP6, .off = _OUT(in6.sin6_addr), .cb = snl_attr_get_in6_addr }, +}; +SNL_DECLARE_ATTR_PARSER_EXT(sockaddr_parser, 0, nla_p_sockaddr, pflowctl_post_sockaddr); +#undef _OUT + +struct pflowctl_get { + int id; + int version; + struct pflowctl_sockaddr src; + struct pflowctl_sockaddr dst; + uint32_t obs_dom; + uint8_t so_status; +}; +#define _IN(_field) offsetof(struct genlmsghdr, _field) +#define _OUT(_field) offsetof(struct pflowctl_get, _field) +static struct snl_attr_parser ap_get[] = { + { .type = PFLOWNL_GET_ID, .off = _OUT(id), .cb = snl_attr_get_int32 }, + { .type = PFLOWNL_GET_VERSION, .off = _OUT(version), .cb = snl_attr_get_int16 }, + { .type = PFLOWNL_GET_SRC, .off = _OUT(src), .arg = &sockaddr_parser, .cb = snl_attr_get_nested }, + { .type = PFLOWNL_GET_DST, .off = _OUT(dst), .arg = &sockaddr_parser, .cb = snl_attr_get_nested }, + { .type = PFLOWNL_GET_OBSERVATION_DOMAIN, .off = _OUT(obs_dom), .cb = snl_attr_get_uint32 }, + { .type = PFLOWNL_GET_SOCKET_STATUS, .off = _OUT(so_status), .cb = snl_attr_get_uint8 }, +}; +static struct snl_field_parser fp_get[] = {}; +#undef _IN +#undef _OUT +SNL_DECLARE_PARSER(get_parser, struct genlmsghdr, fp_get, ap_get); + +static void +print_sockaddr(const char *prefix, const struct sockaddr_storage *s) +{ + char buf[INET6_ADDRSTRLEN]; + int error; + + if (s->ss_family != AF_INET && s->ss_family != AF_INET6) + return; + + if (s->ss_family == AF_INET || + s->ss_family == AF_INET6) { + error = getnameinfo((const struct sockaddr *)s, + s->ss_len, buf, sizeof(buf), NULL, 0, + NI_NUMERICHOST); + if (error) + err(1, "sender: %s", gai_strerror(error)); + } + + printf("%s", prefix); + switch (s->ss_family) { + case AF_INET: { + const struct sockaddr_in *sin = (const struct sockaddr_in *)s; + if (sin->sin_addr.s_addr != INADDR_ANY) { + printf("%s", buf); + if (sin->sin_port != 0) + printf(":%u", ntohs(sin->sin_port)); + } + break; + } + case AF_INET6: { + const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)s; + if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + printf("[%s]", buf); + if (sin6->sin6_port != 0) + printf(":%u", ntohs(sin6->sin6_port)); + } + break; + } + } +} + +static int +get(int id) +{ + struct snl_state ss = {}; + struct snl_errmsg_data e = {}; + struct pflowctl_get g = {}; + struct snl_writer nw; + struct nlmsghdr *hdr; + uint32_t seq_id; + int family_id; + + snl_init(&ss, NETLINK_GENERIC); + family_id = snl_get_genl_family(&ss, PFLOWNL_FAMILY_NAME); + if (family_id == 0) + errx(1, "pflow.ko is not loaded."); + + snl_init_writer(&ss, &nw); + hdr = snl_create_genl_msg_request(&nw, family_id, PFLOWNL_CMD_GET); + snl_add_msg_attr_s32(&nw, PFLOWNL_GET_ID, id); + + hdr = snl_finalize_msg(&nw); + if (hdr == NULL) + return (ENOMEM); + seq_id = hdr->nlmsg_seq; + + snl_send_message(&ss, hdr); + + while ((hdr = snl_read_reply_multi(&ss, seq_id, &e)) != NULL) { + if (! snl_parse_nlmsg(&ss, hdr, &get_parser, &g)) + continue; + + printf("pflow%d: version %d domain %u", g.id, g.version, g.obs_dom); + print_sockaddr(" src ", &g.src.storage); + print_sockaddr(" dst ", &g.dst.storage); + printf("\n"); + if (verbose) { + printf("\tsocket: %s\n", + g.so_status ? "connected" : "disconnected"); + } + } + + if (e.error) + errc(1, e.error, "failed to get"); + + return (0); +} + +struct pflowctl_set { + int id; + uint16_t version; + struct sockaddr_storage src; + struct sockaddr_storage dst; + uint32_t obs_dom; +}; +static inline bool +snl_add_msg_attr_sockaddr(struct snl_writer *nw, int attrtype, struct sockaddr_storage *s) +{ + int off = snl_add_msg_attr_nested(nw, attrtype); + + snl_add_msg_attr_u8(nw, PFLOWNL_ADDR_FAMILY, s->ss_family); + + switch (s->ss_family) { + case AF_INET: { + const struct sockaddr_in *in = (const struct sockaddr_in *)s; + snl_add_msg_attr_u16(nw, PFLOWNL_ADDR_PORT, in->sin_port); + snl_add_msg_attr_ip4(nw, PFLOWNL_ADDR_IP, &in->sin_addr); + break; + } + case AF_INET6: { + const struct sockaddr_in6 *in6 = (const struct sockaddr_in6 *)s; + snl_add_msg_attr_u16(nw, PFLOWNL_ADDR_PORT, in6->sin6_port); + snl_add_msg_attr_ip6(nw, PFLOWNL_ADDR_IP6, &in6->sin6_addr); + break; + } + default: + return (false); + } + snl_end_attr_nested(nw, off); + + return (true); +} + +static int +do_set(struct pflowctl_set *s) +{ + struct snl_state ss = {}; + struct snl_errmsg_data e = {}; + struct snl_writer nw; + struct nlmsghdr *hdr; + int family_id; + + snl_init(&ss, NETLINK_GENERIC); + family_id = snl_get_genl_family(&ss, PFLOWNL_FAMILY_NAME); + if (family_id == 0) + errx(1, "pflow.ko is not loaded."); + + snl_init_writer(&ss, &nw); + snl_create_genl_msg_request(&nw, family_id, PFLOWNL_CMD_SET); + + snl_add_msg_attr_s32(&nw, PFLOWNL_SET_ID, s->id); + if (s->version != 0) + snl_add_msg_attr_u16(&nw, PFLOWNL_SET_VERSION, s->version); + if (s->src.ss_len != 0) + snl_add_msg_attr_sockaddr(&nw, PFLOWNL_SET_SRC, &s->src); + if (s->dst.ss_len != 0) + snl_add_msg_attr_sockaddr(&nw, PFLOWNL_SET_DST, &s->dst); + if (s->obs_dom != 0) + snl_add_msg_attr_u32(&nw, PFLOWNL_SET_OBSERVATION_DOMAIN, s->obs_dom); + + hdr = snl_finalize_msg(&nw); + if (hdr == NULL) + return (1); + + snl_send_message(&ss, hdr); + snl_read_reply_code(&ss, hdr->nlmsg_seq, &e); + + if (e.error) + errc(1, e.error, "failed to set"); + + return (0); +} + +static void +pflowctl_addr(const char *val, struct sockaddr_storage *ss) +{ + struct addrinfo *res0; + int error; + bool flag; + char *ip, *port; + char buf[sysconf(_SC_HOST_NAME_MAX) + 1 + sizeof(":65535")]; + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_DGRAM, /*dummy*/ + .ai_flags = AI_NUMERICHOST, + }; + + if (strlcpy(buf, val, sizeof(buf)) >= sizeof(buf)) + errx(1, "%s bad value", val); + + port = NULL; + flag = *buf == '['; + + for (char *cp = buf; *cp; ++cp) { + if (*cp == ']' && *(cp + 1) == ':' && flag) { + *cp = '\0'; + *(cp + 1) = '\0'; + port = cp + 2; + break; + } + if (*cp == ']' && *(cp + 1) == '\0' && flag) { + *cp = '\0'; + port = NULL; + break; + } + if (*cp == ':' && !flag) { + *cp = '\0'; + port = cp + 1; + break; + } + } + + ip = buf; + if (flag) + ip++; + + if ((error = getaddrinfo(ip, port, &hints, &res0)) != 0) + errx(1, "error in parsing address string: %s", + gai_strerror(error)); + + memcpy(ss, res0->ai_addr, res0->ai_addr->sa_len); + freeaddrinfo(res0); +} + +static int +set(char *idstr, int argc, char *argv[]) +{ + struct pflowctl_set s = {}; + + s.id = pflow_to_id(idstr); + if (s.id < 0) + return (EINVAL); + + while (argc > 0) { + if (strcmp(argv[0], "src") == 0) { + if (argc < 2) + usage(); + + pflowctl_addr(argv[1], &s.src); + + argc -= 2; + argv += 2; + } else if (strcmp(argv[0], "dst") == 0) { + if (argc < 2) + usage(); + + pflowctl_addr(argv[1], &s.dst); + + argc -= 2; + argv += 2; + } else if (strcmp(argv[0], "proto") == 0) { + if (argc < 2) + usage(); + + s.version = strtol(argv[1], NULL, 10); + + argc -= 2; + argv += 2; + } else if (strcmp(argv[0], "domain") == 0) { + if (argc < 2) + usage(); + + s.obs_dom = strtol(argv[1], NULL, 10); + + argc -= 2; + argv += 2; + } else { + usage(); + } + } + + return (do_set(&s)); +} + +static const struct snl_hdr_parser *all_parsers[] = { + &list_parser, + &get_parser, +}; + +enum pflowctl_op_t { + OP_HELP, + OP_LIST, + OP_CREATE, + OP_DELETE, + OP_SET, +}; +int +main(int argc, char *argv[]) +{ + int ch; + enum pflowctl_op_t op = OP_HELP; + char **set_args = NULL; + size_t set_arg_count = 0; + + SNL_VERIFY_PARSERS(all_parsers); + + if (argc < 2) + usage(); + + while ((ch = getopt(argc, argv, + "lcd:s:v")) != -1) { + switch (ch) { + case 'l': + op = OP_LIST; + break; + case 'c': + op = OP_CREATE; + break; + case 'd': + op = OP_DELETE; + break; + case 's': + op = OP_SET; + set_arg_count = argc - optind; + set_args = argv + optind; + break; + case 'v': + verbose = true; + break; + } + } + + switch (op) { + case OP_LIST: + return (list()); + case OP_CREATE: + return (create()); + case OP_DELETE: + return (del(optarg)); + case OP_SET: + return (set(optarg, set_arg_count, set_args)); + case OP_HELP: + usage(); + break; + } + + return (0); +} diff --git a/sbin/ping/Makefile b/sbin/ping/Makefile index 4713b01cac90..30c68cbaba52 100644 --- a/sbin/ping/Makefile +++ b/sbin/ping/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.1 (Berkeley) 6/5/93 - .include <src.opts.mk> PACKAGE=runtime @@ -20,6 +18,7 @@ CFLAGS+= -DINET6 -DKAME_SCOPEID SRCS+= ping6.c LIBADD+= md LINKS= ${BINDIR}/ping ${BINDIR}/ping6 +MLINKS= ping.8 ping6.8 .endif .if ${MK_DYNAMICROOT} == "no" @@ -33,8 +32,6 @@ CFLAGS+=-DWITH_CASPER CFLAGS+=-DIPSEC LIBADD+= ipsec -CFLAGS+= -Wno-error=unused-but-set-variable - HAS_TESTS= SUBDIR.${MK_TESTS}+= tests diff --git a/sbin/ping/main.c b/sbin/ping/main.c index 47d20f569008..8a572cb3c914 100644 --- a/sbin/ping/main.c +++ b/sbin/ping/main.c @@ -26,7 +26,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <sys/socket.h> @@ -35,10 +34,13 @@ #include <netinet/in.h> #include <err.h> +#include <math.h> +#include <signal.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sysexits.h> #include <unistd.h> #include "main.h" @@ -59,115 +61,186 @@ #error At least one of INET and INET6 is required #endif +/* various options */ +u_int options; + +char *hostname; + +/* counters */ +long nreceived; /* # of packets we got back */ +long nrepeats; /* number of duplicates */ +long ntransmitted; /* sequence # for outbound packets = #sent */ +long nrcvtimeout = 0; /* # of packets we got back after waittime */ + +/* nonzero if we've been told to finish up */ +volatile sig_atomic_t seenint; +volatile sig_atomic_t seeninfo; + +/* timing */ +int timing; /* flag to do timing */ +double tmin = 999999999.0; /* minimum round trip time */ +double tmax = 0.0; /* maximum round trip time */ +double tsum = 0.0; /* sum of all times, for doing average */ +double tsumsq = 0.0; /* sum of all times squared, for std. dev. */ + int main(int argc, char *argv[]) { -#if defined(INET) && defined(INET6) +#if defined(INET) struct in_addr a; - struct in6_addr a6; #endif -#if defined(INET) || defined(INET6) - struct addrinfo hints; +#if defined(INET6) + struct in6_addr a6; #endif - int ch; -#ifdef INET - bool ipv4 = false; +#if defined(INET) && defined(INET6) + struct addrinfo hints, *res, *ai; + const char *target; + int error; #endif -#ifdef INET6 - bool ipv6 = false; + int opt; +#ifdef INET6 if (strcmp(getprogname(), "ping6") == 0) - ipv6 = true; + return ping6(argc, argv); #endif - while ((ch = getopt(argc, argv, ":" OPTSTR)) != -1) { - switch(ch) { + while ((opt = getopt(argc, argv, ":" OPTSTR)) != -1) { + switch (opt) { #ifdef INET case '4': - ipv4 = true; - break; + goto ping4; #endif #ifdef INET6 case '6': - ipv6 = true; - break; + goto ping6; #endif -#if defined(INET) && defined(INET6) case 'S': /* * If -S is given with a numeric parameter, * force use of the corresponding version. */ +#ifdef INET if (inet_pton(AF_INET, optarg, &a) == 1) - ipv4 = true; - else if (inet_pton(AF_INET6, optarg, &a6) == 1) - ipv6 = true; - break; + goto ping4; #endif +#ifdef INET6 + if (inet_pton(AF_INET6, optarg, &a6) == 1) + goto ping6; +#endif + break; default: break; } } + /* + * For IPv4, only one positional argument, the target, is allowed. + * For IPv6, multiple positional argument are allowed; the last + * one is the target, and preceding ones are intermediate hops. + * This nuance is lost here, but the only case where it matters is + * an error. + */ if (optind >= argc) usage(); - optreset = 1; - optind = 1; -#if defined(INET) && defined(INET6) - if (ipv4 && ipv6) - errx(1, "-4 and -6 cannot be used simultaneously"); -#endif - #if defined(INET) && defined(INET6) - if (inet_pton(AF_INET, argv[argc - 1], &a) == 1) { - if (ipv6) - errx(1, "IPv6 requested but IPv4 target address " - "provided"); + target = argv[argc - 1]; + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_RAW; + if (feature_present("inet") && !feature_present("inet6")) hints.ai_family = AF_INET; - } - else if (inet_pton(AF_INET6, argv[argc - 1], &a6) == 1) { - if (ipv4) - errx(1, "IPv4 requested but IPv6 target address " - "provided"); + else if (feature_present("inet6") && !feature_present("inet")) hints.ai_family = AF_INET6; - } else if (ipv6) - hints.ai_family = AF_INET6; - else if (ipv4) - hints.ai_family = AF_INET; - else { - if (!feature_present("inet6")) - hints.ai_family = AF_INET; - else if (!feature_present("inet")) - hints.ai_family = AF_INET6; - else { - struct addrinfo *res; - - memset(&hints, 0, sizeof(hints)); - hints.ai_socktype = SOCK_RAW; - hints.ai_family = AF_UNSPEC; - getaddrinfo(argv[argc - 1], NULL, &hints, &res); - if (res != NULL) { - hints.ai_family = res[0].ai_family; - freeaddrinfo(res); - } + else + hints.ai_family = AF_UNSPEC; + error = getaddrinfo(target, NULL, &hints, &res); + if (res == NULL) + errx(EX_NOHOST, "cannot resolve %s: %s", + target, gai_strerror(error)); + for (ai = res; ai != NULL; ai = ai->ai_next) { + if (ai->ai_family == AF_INET) { + freeaddrinfo(res); + goto ping4; + } + if (ai->ai_family == AF_INET6) { + freeaddrinfo(res); + goto ping6; } } -#elif defined(INET) - hints.ai_family = AF_INET; -#elif defined(INET6) - hints.ai_family = AF_INET6; + freeaddrinfo(res); + errx(EX_NOHOST, "cannot resolve %s", target); #endif - #ifdef INET - if (hints.ai_family == AF_INET) - return ping(argc, argv); -#endif /* INET */ +ping4: + optreset = 1; + optind = 1; + return ping(argc, argv); +#endif #ifdef INET6 - if (hints.ai_family == AF_INET6) - return ping6(argc, argv); -#endif /* INET6 */ - errx(1, "Unknown host"); +ping6: + optreset = 1; + optind = 1; + return ping6(argc, argv); +#endif +} + +/* + * onsignal -- + * Set the global bit that causes the main loop to quit. + */ +void +onsignal(int sig) +{ + switch (sig) { + case SIGALRM: + case SIGINT: + /* + * When doing reverse DNS lookups, the seenint flag might not + * be noticed for a while. Just exit if we get a second SIGINT. + */ + if (!(options & F_HOSTNAME) && seenint != 0) + _exit(nreceived ? 0 : 2); + seenint++; + break; + case SIGINFO: + seeninfo++; + break; + } +} + +/* + * pr_summary -- + * Print out summary statistics to the given output stream. + */ +void +pr_summary(FILE * restrict stream) +{ + fprintf(stream, "\n--- %s ping statistics ---\n", hostname); + fprintf(stream, "%ld packets transmitted, ", ntransmitted); + fprintf(stream, "%ld packets received, ", nreceived); + if (nrepeats) + fprintf(stream, "+%ld duplicates, ", nrepeats); + if (ntransmitted) { + if (nreceived > ntransmitted) + fprintf(stream, "-- somebody's duplicating packets!"); + else + fprintf(stream, "%.1f%% packet loss", + ((((double)ntransmitted - nreceived) * 100.0) / + ntransmitted)); + } + if (nrcvtimeout) + fprintf(stream, ", %ld packets out of wait time", nrcvtimeout); + fputc('\n', stream); + if (nreceived && timing) { + /* Only display average to microseconds */ + double num = nreceived + nrepeats; + double avg = tsum / num; + double stddev = sqrt(fmax(0, tsumsq / num - avg * avg)); + fprintf(stream, + "round-trip min/avg/max/stddev = %.3f/%.3f/%.3f/%.3f ms\n", + tmin, avg, tmax, stddev); + } + fflush(stream); } void @@ -211,7 +284,7 @@ usage(void) "Z" #endif "] " - "[-b bufsiz] [-c count] [-e gateway]\n" + "[-b bufsiz] [-C pcp] [-c count] [-e gateway]\n" "\t [-I interface] [-i wait] [-k addrtype] [-l preload] " "[-m hoplimit]\n" "\t [-p pattern]" diff --git a/sbin/ping/main.h b/sbin/ping/main.h index 7084585e6bb0..9a883b61a350 100644 --- a/sbin/ping/main.h +++ b/sbin/ping/main.h @@ -49,6 +49,31 @@ #endif #define PING6OPTS ".::6Aab:C:c:Dde:fHI:i:k:l:m:nNoOp:qS:s:t:uvyYW:z:" PING6ADDOPTS +/* various options */ +extern u_int options; +#define F_HOSTNAME 0x0004 + +extern char *hostname; + +/* counters */ +extern long nreceived; /* # of packets we got back */ +extern long nrepeats; /* number of duplicates */ +extern long ntransmitted; /* sequence # for outbound packets = #sent */ +extern long nrcvtimeout; /* # of packets we got back after waittime */ + +/* nonzero if we've been told to finish up */ +extern volatile sig_atomic_t seenint; +extern volatile sig_atomic_t seeninfo; + +/* timing */ +extern int timing; /* flag to do timing */ +extern double tmin; /* minimum round trip time */ +extern double tmax; /* maximum round trip time */ +extern double tsum; /* sum of all times, for doing average */ +extern double tsumsq; /* sum of all times squared, for std. dev. */ + +void onsignal(int); +void pr_summary(FILE * __restrict); void usage(void) __dead2; #endif diff --git a/sbin/ping/ping.8 b/sbin/ping/ping.8 index 7419aef51dfa..43dca1148e16 100644 --- a/sbin/ping/ping.8 +++ b/sbin/ping/ping.8 @@ -25,9 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)ping.8 8.2 (Berkeley) 12/11/93 -.\" -.Dd November 20, 2022 +.Dd September 15, 2023 .Dt PING 8 .Os .Sh NAME @@ -81,6 +79,7 @@ packets to network hosts .Op Fl 6AaDdEfHNnOoquvYyZ .Op Fl .\& Ns Ar chars .Op Fl b Ar bufsiz +.Op Fl C Ar pcp .Op Fl c Ar count .Op Fl e Ar gateway .Op Fl I Ar interface @@ -231,7 +230,7 @@ option. .It Fl I Ar iface For an IPv4 target, .Ar iface -is an IP address indentifying an interface from which the packets will be sent. +is an IP address identifying an interface from which the packets will be sent. This flag applies only if the ping target is a multicast address. .Pp For an IPv6 target, diff --git a/sbin/ping/ping.c b/sbin/ping/ping.c index 072d4607f745..d10e3c85c9bd 100644 --- a/sbin/ping/ping.c +++ b/sbin/ping/ping.c @@ -32,18 +32,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1989, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)ping.c 8.1 (Berkeley) 6/5/93"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> /* * P I N G . C * @@ -87,7 +75,6 @@ static char sccsid[] = "@(#)ping.c 8.1 (Berkeley) 6/5/93"; #include <ctype.h> #include <err.h> #include <errno.h> -#include <math.h> #include <netdb.h> #include <stddef.h> #include <signal.h> @@ -127,10 +114,8 @@ struct tv32 { }; /* various options */ -static int options; #define F_FLOOD 0x0001 #define F_INTERVAL 0x0002 -#define F_NUMERIC 0x0004 #define F_PINGFILLED 0x0008 #define F_QUIET 0x0010 #define F_RROUTE 0x0020 @@ -178,7 +163,6 @@ static char BSPACE = '\b'; /* characters written for flood */ static const char *DOT = "."; static size_t DOTlen = 1; static size_t DOTidx = 0; -static char *hostname; static char *shostname; static int ident; /* process id to identify our packets */ static int uid; /* cached uid for micro-optimization */ @@ -190,43 +174,24 @@ static int send_len; /* counters */ static long nmissedmax; /* max value of ntransmitted - nreceived - 1 */ static long npackets; /* max packets to transmit */ -static long nreceived; /* # of packets we got back */ -static long nrepeats; /* number of duplicates */ -static long ntransmitted; /* sequence # for outbound packets = #sent */ -static long snpackets; /* max packets to transmit in one sweep */ +static long snpackets; /* max packets to transmit in one sweep */ static long sntransmitted; /* # of packets we sent in this sweep */ static int sweepmax; /* max value of payload in sweep */ static int sweepmin = 0; /* start value of payload in sweep */ static int sweepincr = 1; /* payload increment in sweep */ static int interval = 1000; /* interval between packets, ms */ static int waittime = MAXWAIT; /* timeout for each packet */ -static long nrcvtimeout = 0; /* # of packets we got back after waittime */ - -/* timing */ -static int timing; /* flag to do timing */ -static double tmin = 999999999.0; /* minimum round trip time */ -static double tmax = 0.0; /* maximum round trip time */ -static double tsum = 0.0; /* sum of all times, for doing average */ -static double tsumsq = 0.0; /* sum of all times squared, for std. dev. */ - -/* nonzero if we've been told to finish up */ -static volatile sig_atomic_t finish_up; -static volatile sig_atomic_t siginfo_p; static cap_channel_t *capdns; static void fill(char *, char *); static cap_channel_t *capdns_setup(void); -static void check_status(void); -static void finish(void) __dead2; static void pinger(void); static char *pr_addr(struct in_addr); static char *pr_ntime(n_time); static void pr_icmph(struct icmp *, struct ip *, const u_char *const); static void pr_iph(struct ip *, const u_char *); static void pr_pack(char *, ssize_t, struct sockaddr_in *, struct timespec *); -static void status(int); -static void stopit(int); int ping(int argc, char *const *argv) @@ -264,8 +229,6 @@ ping(int argc, char *const *argv) #endif cap_rights_t rights; - options |= F_NUMERIC; - /* * Do the stuff that we need root priv's for *first*, and * then drop our setuid bit. Save error reporting for @@ -350,47 +313,47 @@ ping(int argc, char *const *argv) options |= F_DOT; setbuf(stdout, (char *)NULL); break; - case 'G': /* Maximum packet size for ping sweep */ + case 'G': /* Maximum payload size for ping sweep */ ltmp = strtonum(optarg, 1, INT_MAX, &errstr); if (errstr != NULL) { - errx(EX_USAGE, "invalid packet size: `%s'", + errx(EX_USAGE, "invalid payload size: `%s'", optarg); } sweepmax = (int)ltmp; if (uid != 0 && sweepmax > DEFDATALEN) { errc(EX_NOPERM, EPERM, - "packet size too large: %d > %u", + "payload size too large: %d > %u", sweepmax, DEFDATALEN); } options |= F_SWEEP; break; - case 'g': /* Minimum packet size for ping sweep */ - ltmp = strtonum(optarg, 1, INT_MAX, &errstr); + case 'g': /* Minimum payload size for ping sweep */ + ltmp = strtonum(optarg, 0, INT_MAX, &errstr); if (errstr != NULL) { - errx(EX_USAGE, "invalid packet size: `%s'", + errx(EX_USAGE, "invalid payload size: `%s'", optarg); } sweepmin = (int)ltmp; if (uid != 0 && sweepmin > DEFDATALEN) { errc(EX_NOPERM, EPERM, - "packet size too large: %d > %u", + "payload size too large: %d > %u", sweepmin, DEFDATALEN); } options |= F_SWEEP; break; case 'H': - options &= ~F_NUMERIC; + options |= F_HOSTNAME; break; - case 'h': /* Packet size increment for ping sweep */ + case 'h': /* Payload size increment for ping sweep */ ltmp = strtonum(optarg, 1, INT_MAX, &errstr); if (errstr != NULL) { - errx(EX_USAGE, "invalid packet size: `%s'", + errx(EX_USAGE, "invalid payload size: `%s'", optarg); } sweepincr = (int)ltmp; if (uid != 0 && sweepincr > DEFDATALEN) { errc(EX_NOPERM, EPERM, - "packet size too large: %d > %u", + "payload size too large: %d > %u", sweepincr, DEFDATALEN); } options |= F_SWEEP; @@ -452,7 +415,7 @@ ping(int argc, char *const *argv) options |= F_TTL; break; case 'n': - options |= F_NUMERIC; + options &= ~F_HOSTNAME; break; case 'o': options |= F_ONCE; @@ -882,22 +845,17 @@ ping(int argc, char *const *argv) sigemptyset(&si_sa.sa_mask); si_sa.sa_flags = 0; - - si_sa.sa_handler = stopit; - if (sigaction(SIGINT, &si_sa, 0) == -1) { + si_sa.sa_handler = onsignal; + if (sigaction(SIGINT, &si_sa, 0) == -1) err(EX_OSERR, "sigaction SIGINT"); - } - - si_sa.sa_handler = status; - if (sigaction(SIGINFO, &si_sa, 0) == -1) { - err(EX_OSERR, "sigaction"); - } - - if (alarmtimeout > 0) { - si_sa.sa_handler = stopit; + seenint = 0; + if (sigaction(SIGINFO, &si_sa, 0) == -1) + err(EX_OSERR, "sigaction SIGINFO"); + seeninfo = 0; + if (alarmtimeout > 0) { if (sigaction(SIGALRM, &si_sa, 0) == -1) err(EX_OSERR, "sigaction SIGALRM"); - } + } bzero(&msg, sizeof(msg)); msg.msg_name = (caddr_t)&from; @@ -929,13 +887,18 @@ ping(int argc, char *const *argv) } almost_done = 0; - while (!finish_up) { + while (seenint == 0) { struct timespec now, timeout; fd_set rfds; int n; ssize_t cc; - check_status(); + /* signal handling */ + if (seeninfo) { + pr_summary(stderr); + seeninfo = 0; + continue; + } if ((unsigned)srecv >= FD_SETSIZE) errx(EX_OSERR, "descriptor too large"); FD_ZERO(&rfds); @@ -945,9 +908,10 @@ ping(int argc, char *const *argv) timespecsub(&timeout, &now, &timeout); if (timeout.tv_sec < 0) timespecclear(&timeout); + n = pselect(srecv + 1, &rfds, NULL, NULL, &timeout, NULL); if (n < 0) - continue; /* Must be EINTR. */ + continue; /* EINTR */ if (n == 1) { struct timespec *tv = NULL; #ifdef SO_TIMESTAMP @@ -982,7 +946,7 @@ ping(int argc, char *const *argv) (npackets && nreceived >= npackets)) break; } - if (n == 0 || options & F_FLOOD) { + if (n == 0 || (options & F_FLOOD)) { if (sweepmax && sntransmitted == snpackets) { if (datalen + sweepincr > sweepmax) break; @@ -998,14 +962,21 @@ ping(int argc, char *const *argv) if (almost_done) break; almost_done = 1; + /* + * If we're not transmitting any more packets, + * change the timer to wait two round-trip times + * if we've received any packets or (waittime) + * milliseconds if we haven't. + */ intvl.tv_nsec = 0; if (nreceived) { intvl.tv_sec = 2 * tmax / 1000; - if (!intvl.tv_sec) + if (intvl.tv_sec == 0) intvl.tv_sec = 1; } else { intvl.tv_sec = waittime / 1000; - intvl.tv_nsec = waittime % 1000 * 1000000; + intvl.tv_nsec = + waittime % 1000 * 1000000; } } (void)clock_gettime(CLOCK_MONOTONIC, &last); @@ -1016,28 +987,9 @@ ping(int argc, char *const *argv) } } } - finish(); - /* NOTREACHED */ - exit(0); /* Make the compiler happy */ -} + pr_summary(stdout); -/* - * stopit -- - * Set the global bit that causes the main loop to quit. - * Do NOT call finish() from here, since finish() does far too much - * to be called from a signal handler. - */ -void -stopit(int sig __unused) -{ - - /* - * When doing reverse DNS lookups, the finish_up flag might not - * be noticed for a while. Just exit if we get a second SIGINT. - */ - if (!(options & F_NUMERIC) && finish_up) - _exit(nreceived ? 0 : 2); - finish_up = 1; + exit(nreceived ? 0 : 2); } /* @@ -1297,14 +1249,14 @@ pr_pack(char *buf, ssize_t cc, struct sockaddr_in *from, struct timespec *tv) for (i = 0; i < datalen; ++i, ++cp) { if ((i % 16) == 8) (void)printf("\n\t"); - (void)printf("%2x ", *cp); + (void)printf(" %2x", *cp); } (void)printf("\ndp:"); cp = &outpack[ICMP_MINLEN]; for (i = 0; i < datalen; ++i, ++cp) { if ((i % 16) == 8) (void)printf("\n\t"); - (void)printf("%2x ", *cp); + (void)printf(" %2x", *cp); } break; } @@ -1406,7 +1358,7 @@ pr_pack(char *buf, ssize_t cc, struct sockaddr_in *from, struct timespec *tv) (void)putchar('\n'); } } else - (void)printf("\t(truncated route)\n"); + (void)printf("\t(truncated route)"); break; case IPOPT_RR: j = cp[IPOPT_OLEN]; /* get length */ @@ -1464,77 +1416,6 @@ pr_pack(char *buf, ssize_t cc, struct sockaddr_in *from, struct timespec *tv) } /* - * status -- - * Print out statistics when SIGINFO is received. - */ - -static void -status(int sig __unused) -{ - - siginfo_p = 1; -} - -static void -check_status(void) -{ - - if (siginfo_p) { - siginfo_p = 0; - (void)fprintf(stderr, "\r%ld/%ld packets received (%.1f%%)", - nreceived, ntransmitted, - ntransmitted ? nreceived * 100.0 / ntransmitted : 0.0); - if (nreceived && timing) - (void)fprintf(stderr, " %.3f min / %.3f avg / %.3f max", - tmin, tsum / (nreceived + nrepeats), tmax); - (void)fprintf(stderr, "\n"); - } -} - -/* - * finish -- - * Print out statistics, and give up. - */ -static void -finish(void) -{ - - (void)signal(SIGINT, SIG_IGN); - (void)signal(SIGALRM, SIG_IGN); - (void)putchar('\n'); - (void)fflush(stdout); - (void)printf("--- %s ping statistics ---\n", hostname); - (void)printf("%ld packets transmitted, ", ntransmitted); - (void)printf("%ld packets received, ", nreceived); - if (nrepeats) - (void)printf("+%ld duplicates, ", nrepeats); - if (ntransmitted) { - if (nreceived > ntransmitted) - (void)printf("-- somebody's printing up packets!"); - else - (void)printf("%.1f%% packet loss", - ((ntransmitted - nreceived) * 100.0) / - ntransmitted); - } - if (nrcvtimeout) - (void)printf(", %ld packets out of wait time", nrcvtimeout); - (void)putchar('\n'); - if (nreceived && timing) { - double n = nreceived + nrepeats; - double avg = tsum / n; - double vari = tsumsq / n - avg * avg; - (void)printf( - "round-trip min/avg/max/stddev = %.3f/%.3f/%.3f/%.3f ms\n", - tmin, avg, tmax, sqrt(vari)); - } - - if (nreceived) - exit(0); - else - exit(2); -} - -/* * pr_icmph -- * Print a descriptive string about an ICMP header. */ @@ -1668,13 +1549,21 @@ pr_icmph(struct icmp *icp, struct ip *oip, const u_char *const oicmp_raw) static void pr_iph(struct ip *ip, const u_char *cp) { - struct in_addr ina; + struct in_addr dst_ina, src_ina; int hlen; hlen = ip->ip_hl << 2; cp = cp + sizeof(struct ip); /* point to options */ - (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst\n"); + memcpy(&src_ina, &ip->ip_src.s_addr, sizeof(src_ina)); + memcpy(&dst_ina, &ip->ip_dst.s_addr, sizeof(dst_ina)); + + (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks %*s %*s", + (int)strlen(inet_ntoa(src_ina)), "Src", + (int)strlen(inet_ntoa(dst_ina)), "Dst"); + if (hlen > (int)sizeof(struct ip)) + (void)printf(" Opts"); + (void)putchar('\n'); (void)printf(" %1x %1x %02x %04x %04x", ip->ip_v, ip->ip_hl, ip->ip_tos, ntohs(ip->ip_len), ntohs(ip->ip_id)); @@ -1683,13 +1572,14 @@ pr_iph(struct ip *ip, const u_char *cp) ntohs(ip->ip_off) & 0x1fff); (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ntohs(ip->ip_sum)); - memcpy(&ina, &ip->ip_src.s_addr, sizeof ina); - (void)printf(" %s ", inet_ntoa(ina)); - memcpy(&ina, &ip->ip_dst.s_addr, sizeof ina); - (void)printf(" %s ", inet_ntoa(ina)); + (void)printf(" %s", inet_ntoa(src_ina)); + (void)printf(" %s", inet_ntoa(dst_ina)); /* dump any option bytes */ - while (hlen-- > (int)sizeof(struct ip)) { - (void)printf("%02x", *cp++); + if (hlen > (int)sizeof(struct ip)) { + (void)printf(" "); + while (hlen-- > (int)sizeof(struct ip)) { + (void)printf("%02x", *cp++); + } } (void)putchar('\n'); } @@ -1705,7 +1595,7 @@ pr_addr(struct in_addr ina) struct hostent *hp; static char buf[16 + 3 + MAXHOSTNAMELEN]; - if (options & F_NUMERIC) + if (!(options & F_HOSTNAME)) return inet_ntoa(ina); hp = cap_gethostbyaddr(capdns, (char *)&ina, sizeof(ina), AF_INET); diff --git a/sbin/ping/ping6.c b/sbin/ping/ping6.c index bd1658f9500a..356f0f72a6f8 100644 --- a/sbin/ping/ping6.c +++ b/sbin/ping/ping6.c @@ -65,19 +65,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1989, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)ping.c 8.1 (Berkeley) 6/5/93"; -#endif /* not lint */ -#endif - -#include <sys/cdefs.h> /* * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, * measure round-trip-delays and packet loss across network paths. @@ -124,7 +111,6 @@ static char sccsid[] = "@(#)ping.c 8.1 (Berkeley) 6/5/93"; #include <err.h> #include <errno.h> #include <fcntl.h> -#include <math.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> @@ -188,7 +174,6 @@ struct tv32 { #define F_FQDN 0x1000 #define F_INTERFACE 0x2000 #define F_SRCADDR 0x4000 -#define F_HOSTNAME 0x10000 #define F_FQDNOLD 0x20000 #define F_NIGROUP 0x40000 #define F_SUPTYPES 0x80000 @@ -200,7 +185,6 @@ struct tv32 { #define F_NOUSERDATA (F_NODEADDR | F_FQDN | F_FQDNOLD | F_SUPTYPES) #define F_WAITTIME 0x2000000 #define F_DOT 0x4000000 -static u_int options; #define IN6LEN sizeof(struct in6_addr) #define SA6LEN sizeof(struct sockaddr_in6) @@ -229,7 +213,6 @@ static char BBELL = '\a'; /* characters written for AUDIBLE */ static const char *DOT = "."; static size_t DOTlen = 1; static size_t DOTidx = 0; -static char *hostname; static int ident; /* process id to identify our packets */ static u_int8_t nonce[8]; /* nonce field for node information */ static int hoplimit = -1; /* hoplimit */ @@ -241,20 +224,9 @@ static cap_channel_t *capdns; /* counters */ static long nmissedmax; /* max value of ntransmitted - nreceived - 1 */ static long npackets; /* max packets to transmit */ -static long nreceived; /* # of packets we got back */ -static long nrepeats; /* number of duplicates */ -static long ntransmitted; /* sequence # for outbound packets = #sent */ static long ntransmitfailures; /* number of transmit failures */ static int interval = 1000; /* interval between packets in ms */ static int waittime = MAXWAIT; /* timeout for each packet */ -static long nrcvtimeout = 0; /* # of packets we got back after waittime */ - -/* timing */ -static int timing; /* flag to do timing */ -static double tmin = 999999999.0; /* minimum round trip time */ -static double tmax = 0.0; /* maximum round trip time */ -static double tsum = 0.0; /* sum of all times, for doing average */ -static double tsumsq = 0.0; /* sum of all times squared, for std. dev. */ /* for node addresses */ static u_short naflags; @@ -264,18 +236,11 @@ static struct msghdr smsghdr; static struct iovec smsgiov; static char *scmsg = 0; -static volatile sig_atomic_t seenint; -#ifdef SIGINFO -static volatile sig_atomic_t seeninfo; -#endif - static cap_channel_t *capdns_setup(void); static void fill(char *, char *); static int get_hoplim(struct msghdr *); static int get_pathmtu(struct msghdr *); static struct in6_pktinfo *get_rcvpktinfo(struct msghdr *); -static void onsignal(int); -static void onint(int); static size_t pingerlen(void); static int pinger(void); static const char *pr_addr(struct sockaddr *, int); @@ -293,7 +258,6 @@ static void pr_ip6opt(void *, size_t); static void pr_rthdr(void *, size_t); static int pr_bitrange(u_int32_t, int, int); static void pr_retip(struct ip6_hdr *, u_char *); -static void summary(void); #ifdef IPSEC #ifdef IPSEC_POLICY_IPSEC static int setpolicy(int, char *); @@ -677,14 +641,15 @@ ping6(int argc, char *argv[]) error = cap_getaddrinfo(capdns, target, NULL, &hints, &res); if (error) - errx(1, "%s", gai_strerror(error)); + errx(EX_NOHOST, "cannot resolve %s: %s", + target, gai_strerror(error)); if (res->ai_canonname) hostname = strdup(res->ai_canonname); else hostname = target; if (!res->ai_addr) - errx(1, "cap_getaddrinfo failed"); + errx(EX_NOHOST, "cannot resolve %s", target); (void)memcpy(&dst, res->ai_addr, res->ai_addrlen); @@ -735,7 +700,7 @@ ping6(int argc, char *argv[]) } /* - * let the kerel pass extension headers of incoming packets, + * let the kernel pass extension headers of incoming packets, * for privileged socket options */ if ((options & F_VERBOSE) != 0) { @@ -895,7 +860,7 @@ ping6(int argc, char *argv[]) } #endif /*ICMP6_FILTER*/ - /* let the kerel pass extension headers of incoming packets */ + /* let the kernel pass extension headers of incoming packets */ if ((options & F_VERBOSE) != 0) { int opton = 1; @@ -1142,7 +1107,7 @@ ping6(int argc, char *argv[]) if (caph_rights_limit(ssend, &rights_ssend) < 0) err(1, "caph_rights_limit ssend setsockopt"); - printf("PING6(%lu=40+8+%lu bytes) ", (unsigned long)(40 + pingerlen()), + printf("PING(%lu=40+8+%lu bytes) ", (unsigned long)(40 + pingerlen()), (unsigned long)(pingerlen() - 8)); printf("%s --> ", pr_addr((struct sockaddr *)&src, sizeof(src))); printf("%s\n", pr_addr((struct sockaddr *)&dst, sizeof(dst))); @@ -1163,11 +1128,9 @@ ping6(int argc, char *argv[]) if (sigaction(SIGINT, &si_sa, 0) == -1) err(EX_OSERR, "sigaction SIGINT"); seenint = 0; -#ifdef SIGINFO if (sigaction(SIGINFO, &si_sa, 0) == -1) err(EX_OSERR, "sigaction SIGINFO"); seeninfo = 0; -#endif if (alarmtimeout > 0) { if (sigaction(SIGALRM, &si_sa, 0) == -1) err(EX_OSERR, "sigaction SIGALRM"); @@ -1186,15 +1149,11 @@ ping6(int argc, char *argv[]) int n; /* signal handling */ - if (seenint) - onint(SIGINT); -#ifdef SIGINFO if (seeninfo) { - summary(); + pr_summary(stderr); seeninfo = 0; continue; } -#endif FD_ZERO(&rfds); FD_SET(srecv, &rfds); clock_gettime(CLOCK_MONOTONIC, &now); @@ -1258,12 +1217,12 @@ ping6(int argc, char *argv[]) if (almost_done) break; almost_done = 1; - /* - * If we're not transmitting any more packets, - * change the timer to wait two round-trip times - * if we've received any packets or (waittime) - * milliseconds if we haven't. - */ + /* + * If we're not transmitting any more packets, + * change the timer to wait two round-trip times + * if we've received any packets or (waittime) + * milliseconds if we haven't. + */ intvl.tv_nsec = 0; if (nreceived) { intvl.tv_sec = 2 * tmax / 1000; @@ -1272,7 +1231,7 @@ ping6(int argc, char *argv[]) } else { intvl.tv_sec = waittime / 1000; intvl.tv_nsec = - waittime % 1000 * 1000000; + waittime % 1000 * 1000000; } } clock_gettime(CLOCK_MONOTONIC, &last); @@ -1288,7 +1247,7 @@ ping6(int argc, char *argv[]) si_sa.sa_handler = SIG_IGN; sigaction(SIGINT, &si_sa, 0); sigaction(SIGALRM, &si_sa, 0); - summary(); + pr_summary(stdout); if(packet != NULL) free(packet); @@ -1301,23 +1260,6 @@ ping6(int argc, char *argv[]) exit(EX_OSERR); } -static void -onsignal(int sig) -{ - - switch (sig) { - case SIGINT: - case SIGALRM: - seenint++; - break; -#ifdef SIGINFO - case SIGINFO: - seeninfo++; - break; -#endif - } -} - /* * pinger -- * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet @@ -1469,7 +1411,7 @@ pinger(void) ntransmitfailures++; warn("sendmsg"); } - (void)printf("ping6: wrote %s %d chars, ret=%d\n", + (void)printf("ping: wrote %s %d chars, ret=%d\n", hostname, cc, i); } if (!(options & F_QUIET) && options & F_DOT) @@ -2165,7 +2107,7 @@ pr_nodeaddr(struct icmp6_nodeinfo *ni, int nilen) /* * In icmp-name-lookups 05 and later, TTL of each returned address - * is contained in the resposne. We try to detect the version + * is contained in the response. We try to detect the version * by the length of the data, but note that the detection algorithm * is incomplete. We assume the latest draft by default. */ @@ -2305,59 +2247,6 @@ get_pathmtu(struct msghdr *mhdr) return(0); } -/* - * onint -- - * SIGINT handler. - */ -/* ARGSUSED */ -static void -onint(int notused __unused) -{ - /* - * When doing reverse DNS lookups, the seenint flag might not - * be noticed for a while. Just exit if we get a second SIGINT. - */ - if ((options & F_HOSTNAME) && seenint != 0) - _exit(nreceived ? 0 : 2); -} - -/* - * summary -- - * Print out statistics. - */ -static void -summary(void) -{ - - (void)printf("\n--- %s ping6 statistics ---\n", hostname); - (void)printf("%ld packets transmitted, ", ntransmitted); - (void)printf("%ld packets received, ", nreceived); - if (nrepeats) - (void)printf("+%ld duplicates, ", nrepeats); - if (ntransmitted) { - if (nreceived > ntransmitted) - (void)printf("-- somebody's duplicating packets!"); - else - (void)printf("%.1f%% packet loss", - ((((double)ntransmitted - nreceived) * 100.0) / - ntransmitted)); - } - if (nrcvtimeout) - printf(", %ld packets out of wait time", nrcvtimeout); - (void)putchar('\n'); - if (nreceived && timing) { - /* Only display average to microseconds */ - double num = nreceived + nrepeats; - double avg = tsum / num; - double dev = sqrt(tsumsq / num - avg * avg); - (void)printf( - "round-trip min/avg/max/std-dev = %.3f/%.3f/%.3f/%.3f ms\n", - tmin, avg, tmax, dev); - (void)fflush(stdout); - } - (void)fflush(stdout); -} - /*subject type*/ static const char *niqcode[] = { "IPv6 address", @@ -2640,7 +2529,7 @@ pr_addr(struct sockaddr *addr, int addrlen) static char buf[NI_MAXHOST]; int flag = 0; - if ((options & F_HOSTNAME) == 0) + if (!(options & F_HOSTNAME)) flag |= NI_NUMERICHOST; if (cap_getnameinfo(capdns, addr, addrlen, buf, sizeof(buf), NULL, 0, diff --git a/sbin/ping/tests/Makefile b/sbin/ping/tests/Makefile index 3bdf53c15a7c..0520b1d634cf 100644 --- a/sbin/ping/tests/Makefile +++ b/sbin/ping/tests/Makefile @@ -1,4 +1,3 @@ - ATF_TESTS_C+= in_cksum_test SRCS.in_cksum_test= in_cksum_test.c ../utils.c diff --git a/sbin/gvinum/Makefile.depend b/sbin/ping/tests/Makefile.depend index 8d1c78126db9..b372e6d58e9f 100644 --- a/sbin/gvinum/Makefile.depend +++ b/sbin/ping/tests/Makefile.depend @@ -1,14 +1,11 @@ # Autogenerated - do NOT edit! DIRDEPS = \ - include \ - include/xlocale \ lib/${CSU_DIR} \ + lib/atf/libatf-c \ lib/libc \ lib/libcompiler_rt \ - lib/libedit \ - lib/libedit/readline \ - lib/libgeom \ + sbin/ping \ .include <dirdeps.mk> diff --git a/sbin/ping/tests/in_cksum_test.c b/sbin/ping/tests/in_cksum_test.c index fd51a83b1f4a..ffdf8b4c63c5 100644 --- a/sbin/ping/tests/in_cksum_test.c +++ b/sbin/ping/tests/in_cksum_test.c @@ -26,7 +26,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <atf-c.h> diff --git a/sbin/ping/tests/ping_6_c1_s8_t1.out b/sbin/ping/tests/ping_6_c1_s8_t1.out index 81c56e6cf586..0d207e74bc3e 100644 --- a/sbin/ping/tests/ping_6_c1_s8_t1.out +++ b/sbin/ping/tests/ping_6_c1_s8_t1.out @@ -1,6 +1,6 @@ -PING6(56=40+8+8 bytes) ::1 --> ::1 +PING(56=40+8+8 bytes) ::1 --> ::1 16 bytes from ::1, icmp_seq=0 hlim= time= ms ---- localhost ping6 statistics --- +--- localhost ping statistics --- 1 packets transmitted, 1 packets received, 0.0% packet loss -round-trip min/avg/max/std-dev = /// ms +round-trip min/avg/max/stddev = /// ms diff --git a/sbin/ping/tests/ping_c1_s8_t1_S1.out b/sbin/ping/tests/ping_c1_s8_t1_S1.out index 81c56e6cf586..0d207e74bc3e 100644 --- a/sbin/ping/tests/ping_c1_s8_t1_S1.out +++ b/sbin/ping/tests/ping_c1_s8_t1_S1.out @@ -1,6 +1,6 @@ -PING6(56=40+8+8 bytes) ::1 --> ::1 +PING(56=40+8+8 bytes) ::1 --> ::1 16 bytes from ::1, icmp_seq=0 hlim= time= ms ---- localhost ping6 statistics --- +--- localhost ping statistics --- 1 packets transmitted, 1 packets received, 0.0% packet loss -round-trip min/avg/max/std-dev = /// ms +round-trip min/avg/max/stddev = /// ms diff --git a/sbin/ping/tests/ping_test.sh b/sbin/ping/tests/ping_test.sh index 4a2dda0ebcce..5a12ace104d7 100644 --- a/sbin/ping/tests/ping_test.sh +++ b/sbin/ping/tests/ping_test.sh @@ -106,6 +106,7 @@ ping6_c1_s8_t1_body() check_ping_statistics std.out $(atf_get_srcdir)/ping_6_c1_s8_t1.out } +atf_test_case ping_c1t6 ping_c1t6_head() { atf_set "descr" "-t6 is not interpreted as -t -6 by ping" @@ -116,6 +117,7 @@ ping_c1t6_body() atf_check -s exit:0 -o ignore -e empty ping -c1 -t6 127.0.0.1 } +atf_test_case ping_c1t4 ping6_c1t4_head() { atf_set "descr" "-t4 is not interpreted as -t -4 by ping6" @@ -126,6 +128,7 @@ ping6_c1t4_body() atf_check -s exit:0 -o ignore -e empty ping6 -c1 -t4 ::1 } +atf_test_case ping_46 ping_46_head() { atf_set "descr" "-4 and -6 cannot be used simultaneously" @@ -135,21 +138,69 @@ ping_46_body() require_ipv4 require_ipv6 atf_check -s exit:1 \ - -e match:"-4 and -6 cannot be used simultaneously" \ + -e match:"illegal option -- 6" \ ping -4 -6 localhost } -ping6_46_head() +atf_test_case ping_64 +ping_64_head() { atf_set "descr" "-4 and -6 cannot be used simultaneously" } -ping6_46_body() +ping_64_body() { require_ipv4 require_ipv6 atf_check -s exit:1 \ - -e match:"-4 and -6 cannot be used simultaneously" \ - ping6 -4 -6 localhost + -e match:"illegal option -- 4" \ + ping -6 -4 localhost +} + +atf_test_case ping6_4 +ping6_4_head() +{ + atf_set "descr" "ping6 does not accept -4" +} +ping6_4_body() +{ + require_ipv4 + require_ipv6 + atf_check -s exit:1 \ + -e match:"illegal option -- 4" \ + ping6 -4 localhost +} + +atf_test_case ping_nohost +ping_nohost_head() +{ + atf_set "descr" "ping a nonexistent host" +} +ping_nohost_body() +{ + atf_check -s exit:68 -e match:"cannot resolve" \ + ping nonexistent.in-addr.arpa. +} + +atf_test_case ping4_nohost +ping4_nohost_head() +{ + atf_set "descr" "ping -4 a nonexistent host" +} +ping4_nohost_body() +{ + atf_check -s exit:68 -e match:"cannot resolve" \ + ping -4 nonexistent.in-addr.arpa. +} + +atf_test_case ping6_nohost +ping6_nohost_head() +{ + atf_set "descr" "ping -6 a nonexistent host" +} +ping6_nohost_body() +{ + atf_check -s exit:68 -e match:"cannot resolve" \ + ping -6 nonexistent.in-addr.arpa. } atf_test_case "inject_opts" "cleanup" @@ -212,7 +263,11 @@ atf_init_test_cases() atf_add_test_case ping_c1t6 atf_add_test_case ping6_c1t4 atf_add_test_case ping_46 - atf_add_test_case ping6_46 + atf_add_test_case ping_64 + atf_add_test_case ping6_4 + atf_add_test_case ping_nohost + atf_add_test_case ping4_nohost + atf_add_test_case ping6_nohost atf_add_test_case inject_opts atf_add_test_case inject_pip atf_add_test_case inject_reply diff --git a/sbin/ping/tests/test_ping.py b/sbin/ping/tests/test_ping.py index 26a69b62f8fb..3f9a3aecf924 100644 --- a/sbin/ping/tests/test_ping.py +++ b/sbin/ping/tests/test_ping.py @@ -51,7 +51,7 @@ def build_response_packet(echo, ip, icmp, oip_ihl, special): # Build a package with a timestamp of INT_MAX # (time-warped package) payload_no_timestamp = sc.bytes_hex(load)[16:] - load = (b"\xff" * 8) + sc.hex_bytes(payload_no_timestamp) + load = b"\x7f" + (b"\xff" * 7) + sc.hex_bytes(payload_no_timestamp) if special == "wrong": # Build a package with a wrong last byte payload_no_last_byte = sc.bytes_hex(load)[:-2] @@ -63,14 +63,14 @@ def build_response_packet(echo, ip, icmp, oip_ihl, special): if icmp.type in icmp_id_seq_types: pkt = ip / icmp / load else: - ip.options = "" + del ip.options pkt = ip / icmp / oip / oicmp / load return pkt def generate_ip_options(opts): if not opts: - return "" + return [] routers = [ "192.0.2.10", @@ -85,11 +85,11 @@ def generate_ip_options(opts): ] routers_zero = [0, 0, 0, 0, 0, 0, 0, 0, 0] if opts == "EOL": - options = sc.IPOption(b"\x00") + options = sc.IPOption_EOL() elif opts == "NOP": - options = sc.IPOption(b"\x01") + options = sc.IPOption_NOP() elif opts == "NOP-40": - options = sc.IPOption(b"\x01" * 40) + options = sc.IPOption_NOP() * 40 elif opts == "RR": ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) options = sc.IPOption_RR(pointer=40, routers=routers) @@ -113,12 +113,12 @@ def generate_ip_options(opts): options = sc.IPOption_SSRR(length=3, routers=routers_zero) elif opts == "unk": ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) - options = sc.IPOption(b"\x9f") + options = b"\x9f" elif opts == "unk-40": ToolsHelper.set_sysctl("net.inet.ip.process_options", 0) - options = sc.IPOption(b"\x9f" * 40) + options = b"\x9f" * 40 else: - options = "" + options = [] return options @@ -134,7 +134,7 @@ def pinger( icmp_code: sc.scapy.fields.MultiEnumField, # IP arguments ihl: Optional[sc.scapy.fields.BitField] = None, - flags: Optional[sc.scapy.fields.FlagsField] = None, + flags: Optional[sc.scapy.fields.FlagsField] = 0, opts: Optional[str] = None, oip_ihl: Optional[sc.scapy.fields.BitField] = None, special: Optional[str] = None, @@ -169,7 +169,7 @@ def pinger( :keyword ihl: Internet Header Length, defaults to None :type ihl: class:`scapy.fields.BitField`, optional - :keyword flags: IP flags - one of `DF`, `MF` or `evil`, defaults to None + :keyword flags: IP flags - one of `DF`, `MF` or `evil`, defaults to 0 :type flags: class:`scapy.fields.FlagsField`, optional :keyword opts: Include IP options - one of `EOL`, `NOP`, `NOP-40`, `unk`, `unk-40`, `RR`, `RR-same`, `RR-trunc`, `LSRR`, `LSRR-trunc`, `SSRR` or @@ -270,13 +270,15 @@ def pinger( def redact(output): """Redact some elements of ping's output""" pattern_replacements = [ - ("localhost \([0-9]{1,3}(\.[0-9]{1,3}){3}\)", "localhost"), - ("from [0-9]{1,3}(\.[0-9]{1,3}){3}", "from"), + (r"localhost \([0-9]{1,3}(\.[0-9]{1,3}){3}\)", "localhost"), + (r"from [0-9]{1,3}(\.[0-9]{1,3}){3}", "from"), ("hlim=[0-9]*", "hlim="), ("ttl=[0-9]*", "ttl="), ("time=[0-9.-]*", "time="), - ("\(-[0-9\.]+[0-9]+ ms\)", "(- ms)"), - ("[0-9\.]+/[0-9.]+", "/"), + ("cp: .*", "cp: xx xx xx xx xx xx xx xx"), + ("dp: .*", "dp: xx xx xx xx xx xx xx xx"), + (r"\(-[0-9\.]+[0-9]+ ms\)", "(- ms)"), + (r"[0-9\.]+/[0-9.]+", "/"), ] for pattern, repl in pattern_replacements: output = re.sub(pattern, repl, output) @@ -311,12 +313,12 @@ round-trip min/avg/max/stddev = /// ms "args": "ping -6 -c1 -s8 -t1 localhost", "returncode": 0, "stdout": """\ -PING6(56=40+8+8 bytes) ::1 --> ::1 +PING(56=40+8+8 bytes) ::1 --> ::1 16 bytes from ::1, icmp_seq=0 hlim= time= ms ---- localhost ping6 statistics --- +--- localhost ping statistics --- 1 packets transmitted, 1 packets received, 0.0% packet loss -round-trip min/avg/max/std-dev = /// ms +round-trip min/avg/max/stddev = /// ms """, "stderr": "", }, @@ -357,12 +359,12 @@ PING 192.0.2.2 (192.0.2.2): 56 data bytes "args": "ping -A -c1 2001:db8::1", "returncode": 0, "stdout": """\ -PING6(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 +PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 16 bytes from 2001:db8::1, icmp_seq=0 hlim= time= ms ---- 2001:db8::1 ping6 statistics --- +--- 2001:db8::1 ping statistics --- 1 packets transmitted, 1 packets received, 0.0% packet loss -round-trip min/avg/max/std-dev = /// ms +round-trip min/avg/max/stddev = /// ms """, "stderr": "", }, @@ -373,9 +375,9 @@ round-trip min/avg/max/std-dev = /// ms "args": "ping -A -c1 2001:db8::2", "returncode": 2, "stdout": """\ -PING6(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 +PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 ---- 2001:db8::2 ping6 statistics --- +--- 2001:db8::2 ping statistics --- 1 packets transmitted, 0 packets received, 100.0% packet loss """, "stderr": "", @@ -419,14 +421,14 @@ round-trip min/avg/max/stddev = /// ms "args": "ping -A -c3 2001:db8::1", "returncode": 0, "stdout": """\ -PING6(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 +PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 16 bytes from 2001:db8::1, icmp_seq=0 hlim= time= ms 16 bytes from 2001:db8::1, icmp_seq=1 hlim= time= ms 16 bytes from 2001:db8::1, icmp_seq=2 hlim= time= ms ---- 2001:db8::1 ping6 statistics --- +--- 2001:db8::1 ping statistics --- 3 packets transmitted, 3 packets received, 0.0% packet loss -round-trip min/avg/max/std-dev = /// ms +round-trip min/avg/max/stddev = /// ms """, "stderr": "", }, @@ -437,9 +439,9 @@ round-trip min/avg/max/std-dev = /// ms "args": "ping -A -c3 2001:db8::2", "returncode": 2, "stdout": """\ -\x07\x07PING6(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 +\x07\x07PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 ---- 2001:db8::2 ping6 statistics --- +--- 2001:db8::2 ping statistics --- 3 packets transmitted, 0 packets received, 100.0% packet loss """, "stderr": "", @@ -481,12 +483,12 @@ PING 192.0.2.2 (192.0.2.2): 56 data bytes "args": "ping -c1 2001:db8::1", "returncode": 0, "stdout": """\ -PING6(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 +PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 16 bytes from 2001:db8::1, icmp_seq=0 hlim= time= ms ---- 2001:db8::1 ping6 statistics --- +--- 2001:db8::1 ping statistics --- 1 packets transmitted, 1 packets received, 0.0% packet loss -round-trip min/avg/max/std-dev = /// ms +round-trip min/avg/max/stddev = /// ms """, "stderr": "", }, @@ -497,9 +499,9 @@ round-trip min/avg/max/std-dev = /// ms "args": "ping -c1 2001:db8::2", "returncode": 2, "stdout": """\ -PING6(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 +PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 ---- 2001:db8::2 ping6 statistics --- +--- 2001:db8::2 ping statistics --- 1 packets transmitted, 0 packets received, 100.0% packet loss """, "stderr": "", @@ -527,12 +529,12 @@ round-trip min/avg/max/stddev = /// ms "args": "ping -c1 -S::1 -s8 -t1 localhost", "returncode": 0, "stdout": """\ -PING6(56=40+8+8 bytes) ::1 --> ::1 +PING(56=40+8+8 bytes) ::1 --> ::1 16 bytes from ::1, icmp_seq=0 hlim= time= ms ---- localhost ping6 statistics --- +--- localhost ping statistics --- 1 packets transmitted, 1 packets received, 0.0% packet loss -round-trip min/avg/max/std-dev = /// ms +round-trip min/avg/max/stddev = /// ms """, "stderr": "", }, @@ -575,14 +577,14 @@ PING 192.0.2.2 (192.0.2.2): 56 data bytes "args": "ping -c3 2001:db8::1", "returncode": 0, "stdout": """\ -PING6(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 +PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 16 bytes from 2001:db8::1, icmp_seq=0 hlim= time= ms 16 bytes from 2001:db8::1, icmp_seq=1 hlim= time= ms 16 bytes from 2001:db8::1, icmp_seq=2 hlim= time= ms ---- 2001:db8::1 ping6 statistics --- +--- 2001:db8::1 ping statistics --- 3 packets transmitted, 3 packets received, 0.0% packet loss -round-trip min/avg/max/std-dev = /// ms +round-trip min/avg/max/stddev = /// ms """, "stderr": "", }, @@ -593,9 +595,9 @@ round-trip min/avg/max/std-dev = /// ms "args": "ping -c3 2001:db8::2", "returncode": 2, "stdout": """\ -PING6(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 +PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 ---- 2001:db8::2 ping6 statistics --- +--- 2001:db8::2 ping statistics --- 3 packets transmitted, 0 packets received, 100.0% packet loss """, "stderr": "", @@ -636,11 +638,11 @@ PING 192.0.2.2 (192.0.2.2): 56 data bytes "args": "ping -q -c1 2001:db8::1", "returncode": 0, "stdout": """\ -PING6(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 +PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 ---- 2001:db8::1 ping6 statistics --- +--- 2001:db8::1 ping statistics --- 1 packets transmitted, 1 packets received, 0.0% packet loss -round-trip min/avg/max/std-dev = /// ms +round-trip min/avg/max/stddev = /// ms """, "stderr": "", }, @@ -651,9 +653,9 @@ round-trip min/avg/max/std-dev = /// ms "args": "ping -q -c1 2001:db8::2", "returncode": 2, "stdout": """\ -PING6(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 +PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 ---- 2001:db8::2 ping6 statistics --- +--- 2001:db8::2 ping statistics --- 1 packets transmitted, 0 packets received, 100.0% packet loss """, "stderr": "", @@ -694,11 +696,11 @@ PING 192.0.2.2 (192.0.2.2): 56 data bytes "args": "ping -q -c3 2001:db8::1", "returncode": 0, "stdout": """\ -PING6(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 +PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::1 ---- 2001:db8::1 ping6 statistics --- +--- 2001:db8::1 ping statistics --- 3 packets transmitted, 3 packets received, 0.0% packet loss -round-trip min/avg/max/std-dev = /// ms +round-trip min/avg/max/stddev = /// ms """, "stderr": "", }, @@ -709,9 +711,9 @@ round-trip min/avg/max/std-dev = /// ms "args": "ping -q -c3 2001:db8::2", "returncode": 2, "stdout": """\ -PING6(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 +PING(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 ---- 2001:db8::2 ping6 statistics --- +--- 2001:db8::2 ping statistics --- 3 packets transmitted, 0 packets received, 100.0% packet loss """, "stderr": "", @@ -721,6 +723,8 @@ PING6(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 ] @pytest.mark.parametrize("expected", testdata) + @pytest.mark.require_user("root") + @pytest.mark.require_user("unprivileged") def test_ping(self, expected): """Test ping""" ping = subprocess.run( @@ -749,6 +753,8 @@ PING6(56=40+8+8 bytes) 2001:db8::1 --> 2001:db8::2 ] @pytest.mark.parametrize("expected", ping46_testdata) + @pytest.mark.require_user("root") + @pytest.mark.require_user("unprivileged") def test_ping_46(self, expected): """Test ping -4/ping -6""" for version in [4, 6]: @@ -859,7 +865,6 @@ PING 192.0.2.2 (192.0.2.2): 56 data bytes 64 bytes from: icmp_seq=0 ttl= time= ms LSRR: (truncated route) - --- 192.0.2.2 ping statistics --- 1 packets transmitted, 1 packets received, 0.0% packet loss round-trip min/avg/max/stddev = /// ms @@ -916,7 +921,6 @@ PING 192.0.2.2 (192.0.2.2): 56 data bytes 64 bytes from: icmp_seq=0 ttl= time= ms SSRR: (truncated route) - --- 192.0.2.2 ping statistics --- 1 packets transmitted, 1 packets received, 0.0% packet loss round-trip min/avg/max/stddev = /// ms @@ -1034,6 +1038,118 @@ round-trip min/avg/max/stddev = /// ms { "src": "192.0.2.1", "dst": "192.0.2.2", + "icmp_type": 3, + "icmp_code": 1, + "ihl": 0x4, + }, + { + "returncode": 2, + "stdout": """\ +PING 192.0.2.2 (192.0.2.2): 56 data bytes + +--- 192.0.2.2 ping statistics --- +1 packets transmitted, 0 packets received, 100.0% packet loss +""", + "stderr": "", # "IHL too short" message not shown + "redacted": False, + }, + id="_IHL_too_short", + ), + pytest.param( + { + "src": "192.0.2.1", + "dst": "192.0.2.2", + "icmp_type": 3, + "icmp_code": 1, + "special": "no-payload", + }, + { + "returncode": 2, + "stdout": """\ +PATTERN: 0x01 +PING 192.0.2.2 (192.0.2.2): 56 data bytes + +--- 192.0.2.2 ping statistics --- +1 packets transmitted, 0 packets received, 100.0% packet loss +""", + "stderr": """\ +ping: quoted data too short (28 bytes) from 192.0.2.2 +""", + "redacted": False, + }, + id="_quoted_data_too_short", + ), + pytest.param( + { + "src": "192.0.2.1", + "dst": "192.0.2.2", + "icmp_type": 3, + "icmp_code": 1, + "oip_ihl": 0x4, + }, + { + "returncode": 2, + "stdout": """\ +PING 192.0.2.2 (192.0.2.2): 56 data bytes + +--- 192.0.2.2 ping statistics --- +1 packets transmitted, 0 packets received, 100.0% packet loss +""", + "stderr": "", # "inner IHL too short" message not shown + "redacted": False, + }, + id="_inner_IHL_too_short", + ), + pytest.param( + { + "src": "192.0.2.1", + "dst": "192.0.2.2", + "icmp_type": 3, + "icmp_code": 1, + "oip_ihl": 0xF, + }, + { + "returncode": 2, + "stdout": """\ +PING 192.0.2.2 (192.0.2.2): 56 data bytes + +--- 192.0.2.2 ping statistics --- +1 packets transmitted, 0 packets received, 100.0% packet loss +""", + "stderr": """\ +ping: inner packet too short (84 bytes) from 192.0.2.2 +""", + "redacted": False, + }, + id="_inner_packet_too_short", + ), + pytest.param( + { + "src": "192.0.2.1", + "dst": "192.0.2.2", + "icmp_type": 3, + "icmp_code": 1, + "oip_ihl": 0xF, + "special": "no-payload", + }, + { + "returncode": 2, + "stdout": """\ +PATTERN: 0x01 +PING 192.0.2.2 (192.0.2.2): 56 data bytes + +--- 192.0.2.2 ping statistics --- +1 packets transmitted, 0 packets received, 100.0% packet loss +""", + "stderr": "", + "redacted": False, + }, + id="_max_inner_packet_ihl_without_payload", + ), + pytest.param( + { + "src": "192.0.2.1", + "dst": "192.0.2.2", "icmp_type": 0, "icmp_code": 0, "opts": "NOP-40", @@ -1132,8 +1248,8 @@ round-trip min/avg/max/stddev = /// ms "stdout": """\ PING 192.0.2.2 (192.0.2.2): 56 data bytes 132 bytes from 192.0.2.2: Destination Host Unreachable -Vr HL TOS Len ID Flg off TTL Pro cks Src Dst - 4 f 00 007c 0001 0 0000 40 01 d868 192.0.2.1 192.0.2.2 01010101010101010101010101010101010101010101010101010101010101010101010101010101 +Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Opts + 4 f 00 007c 0001 0 0000 40 01 d868 192.0.2.1 192.0.2.2 01010101010101010101010101010101010101010101010101010101010101010101010101010101 --- 192.0.2.2 ping statistics --- @@ -1157,8 +1273,8 @@ Vr HL TOS Len ID Flg off TTL Pro cks Src Dst "stdout": """\ PING 192.0.2.2 (192.0.2.2): 56 data bytes 92 bytes from 192.0.2.2: Destination Host Unreachable -Vr HL TOS Len ID Flg off TTL Pro cks Src Dst - 4 5 00 0054 0001 2 0000 40 01 b6a4 192.0.2.1 192.0.2.2 +Vr HL TOS Len ID Flg off TTL Pro cks Src Dst + 4 5 00 0054 0001 2 0000 40 01 b6a4 192.0.2.1 192.0.2.2 --- 192.0.2.2 ping statistics --- @@ -1230,8 +1346,8 @@ ping: quoted data too short (28 bytes) from 192.0.2.2 "stdout": """\ PING 192.0.2.2 (192.0.2.2): 56 data bytes 92 bytes from 192.0.2.2: Destination Host Unreachable -Vr HL TOS Len ID Flg off TTL Pro cks Src Dst - 4 5 00 0054 0001 0 0000 40 01 f6a4 192.0.2.1 192.0.2.2 +Vr HL TOS Len ID Flg off TTL Pro cks Src Dst + 4 5 00 0054 0001 0 0000 40 01 f6a4 192.0.2.1 192.0.2.2 --- 192.0.2.2 ping statistics --- @@ -1291,6 +1407,39 @@ ping: time of day goes back (- ms), clamping time to 0 }, id="_0_0_special_warp", ), + pytest.param( + { + "src": "192.0.2.1", + "dst": "192.0.2.2", + "icmp_type": 0, + "icmp_code": 0, + "special": "wrong", + }, + { + "returncode": 0, + "stdout": """\ +PATTERN: 0x01 +PING 192.0.2.2 (192.0.2.2): 56 data bytes +64 bytes from: icmp_seq=0 ttl= time= ms +wrong data byte #55 should be 0x1 but was 0x0 +cp: xx xx xx xx xx xx xx xx + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +dp: xx xx xx xx xx xx xx xx + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + +--- 192.0.2.2 ping statistics --- +1 packets transmitted, 1 packets received, 0.0% packet loss +round-trip min/avg/max/stddev = /// ms +""", + "stderr": "", + "redacted": True, + }, + id="_0_0_special_wrong", + ), ] @pytest.mark.parametrize("pinger_kargs, expected", pinger_testdata) diff --git a/sbin/ping/utils.c b/sbin/ping/utils.c index 920102a68961..e723f690be2b 100644 --- a/sbin/ping/utils.c +++ b/sbin/ping/utils.c @@ -32,18 +32,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1989, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)ping.c 8.1 (Berkeley) 6/5/93"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #include <string.h> #include "utils.h" diff --git a/sbin/quotacheck/Makefile b/sbin/quotacheck/Makefile index 644e46e3a772..edaac8679459 100644 --- a/sbin/quotacheck/Makefile +++ b/sbin/quotacheck/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.1 (Berkeley) 6/5/93 - PACKAGE=quotacheck PROG= quotacheck SRCS= quotacheck.c preen.c fsutil.c utilities.c diff --git a/sbin/quotacheck/preen.c b/sbin/quotacheck/preen.c index 6013eb9d5ffc..b7bd79a462d7 100644 --- a/sbin/quotacheck/preen.c +++ b/sbin/quotacheck/preen.c @@ -31,14 +31,7 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)preen.c 8.5 (Berkeley) 4/28/95"; -#else -__RCSID("$NetBSD: preen.c,v 1.18 1998/07/26 20:02:36 mycroft Exp $"); -#endif -#endif /* not lint */ +/* $NetBSD: preen.c,v 1.18 1998/07/26 20:02:36 mycroft Exp $ */ #include <sys/param.h> #include <sys/stat.h> diff --git a/sbin/quotacheck/quotacheck.8 b/sbin/quotacheck/quotacheck.8 index 2675245aa345..cd1b60e51060 100644 --- a/sbin/quotacheck/quotacheck.8 +++ b/sbin/quotacheck/quotacheck.8 @@ -28,8 +28,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)quotacheck.8 8.1 (Berkeley) 6/5/93 -.\" .Dd January 25, 2007 .Dt QUOTACHECK 8 .Os diff --git a/sbin/quotacheck/quotacheck.c b/sbin/quotacheck/quotacheck.c index 3f608c103b1d..1871d2efc25a 100644 --- a/sbin/quotacheck/quotacheck.c +++ b/sbin/quotacheck/quotacheck.c @@ -32,18 +32,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1980, 1990, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)quotacheck.c 8.3 (Berkeley) 1/29/94"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> /* * Fix up / report on disk quotas & usage */ @@ -89,10 +77,6 @@ union { long dev_bsize = 1; ino_t maxino; -union dinode { - struct ufs1_dinode dp1; - struct ufs2_dinode dp2; -}; #define DIP(dp, field) \ ((sblock.fs_magic == FS_UFS1_MAGIC) ? \ (dp)->dp1.field : (dp)->dp2.field) diff --git a/sbin/rcorder/hash.c b/sbin/rcorder/hash.c index e2231461f1a9..a40e52529d06 100644 --- a/sbin/rcorder/hash.c +++ b/sbin/rcorder/hash.c @@ -40,19 +40,6 @@ * SUCH DAMAGE. */ -#ifdef MAKE_BOOTSTRAP -static char rcsid[] = "$NetBSD: hash.c,v 1.1.1.1 1999/11/19 04:30:56 mrg Exp $"; -#else -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)hash.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: hash.c,v 1.1.1.1 1999/11/19 04:30:56 mrg Exp $"); -#endif -#endif /* not lint */ -#endif - #include <sys/types.h> #include <stdlib.h> diff --git a/sbin/rcorder/hash.h b/sbin/rcorder/hash.h index 6ccf76d002a8..d1f096a3c017 100644 --- a/sbin/rcorder/hash.h +++ b/sbin/rcorder/hash.h @@ -38,8 +38,6 @@ * 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. - * - * from: @(#)hash.h 8.1 (Berkeley) 6/6/93 */ /* hash.h -- diff --git a/sbin/rcorder/rcorder.8 b/sbin/rcorder/rcorder.8 index 5481f821e853..cc94ca6992d5 100644 --- a/sbin/rcorder/rcorder.8 +++ b/sbin/rcorder/rcorder.8 @@ -224,7 +224,7 @@ If a file has an item in or in .Ql BEFORE that could not be provided, -this missing provider and the requirement will be drawn bold red as well. +this missing provider and the requirement will be drawn in bold red as well. .Sh SEE ALSO .Xr acpiconf 8 , .Xr rc 8 , diff --git a/sbin/rcorder/sprite.h b/sbin/rcorder/sprite.h index 29b25b0ba263..8ca07f7aa6d8 100644 --- a/sbin/rcorder/sprite.h +++ b/sbin/rcorder/sprite.h @@ -38,8 +38,6 @@ * 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. - * - * from: @(#)sprite.h 8.1 (Berkeley) 6/6/93 */ /* diff --git a/sbin/reboot/Makefile b/sbin/reboot/Makefile index 7f7fe4a77410..27805d307794 100644 --- a/sbin/reboot/Makefile +++ b/sbin/reboot/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.1 (Berkeley) 6/5/93 - PACKAGE=runtime PROG= reboot MAN= reboot.8 nextboot.8 @@ -14,9 +12,9 @@ MAN+= boot_i386.8 MLINKS+= boot_i386.8 boot.8 .endif -LINKS= ${BINDIR}/reboot ${BINDIR}/halt ${BINDIR}/reboot ${BINDIR}/fastboot \ - ${BINDIR}/reboot ${BINDIR}/fasthalt - -SCRIPTS= nextboot.sh +LINKS= ${BINDIR}/reboot ${BINDIR}/halt \ + ${BINDIR}/reboot ${BINDIR}/fastboot \ + ${BINDIR}/reboot ${BINDIR}/fasthalt \ + ${BINDIR}/reboot ${BINDIR}/nextboot .include <bsd.prog.mk> diff --git a/sbin/reboot/boot_i386.8 b/sbin/reboot/boot_i386.8 index da8fc2e430ac..26dd588fb437 100644 --- a/sbin/reboot/boot_i386.8 +++ b/sbin/reboot/boot_i386.8 @@ -32,9 +32,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)boot_i386.8 8.2 (Berkeley) 4/19/94 -.\" -.Dd July 11, 2020 +.Dd April 7, 2025 .Dt BOOT 8 i386 .Os .Sh NAME @@ -47,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/nextboot.sh b/sbin/reboot/nextboot.sh deleted file mode 100644 index 0c9cca51eb8d..000000000000 --- a/sbin/reboot/nextboot.sh +++ /dev/null @@ -1,132 +0,0 @@ -#! /bin/sh -# -# SPDX-License-Identifier: BSD-2-Clause -# -# Copyright (c) 2002 Gordon Tetlow. All rights reserved. -# Copyright (c) 2012 Sandvine Incorporated. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# - -append="NO" -delete="NO" -kenv= -force="NO" -nextboot_file="/boot/nextboot.conf" -zfs= - -add_kenv() -{ - local var value - - var=$1 - # strip literal quotes if passed in - value=${2%\"*} - value=${value#*\"} - - if [ -n "${kenv}" ]; then - kenv="${kenv} -" - fi - kenv="${kenv}${var}=\"${value}\"" -} - -display_usage() { - cat <<-EOF - Usage: nextboot [-af] [-e variable=value] [-k kernel] [-o options] - nextboot -D - EOF -} - -while getopts "aDe:fk:o:" argument ; do - case "${argument}" in - a) - append="YES" - ;; - D) - delete="YES" - ;; - e) - var=${OPTARG%%=*} - value=${OPTARG#*=} - if [ -z "$var" -o -z "$value" ]; then - display_usage - exit 1 - fi - add_kenv "$var" "$value" - ;; - f) - force="YES" - ;; - k) - kernel="${OPTARG}" - add_kenv kernel "$kernel" - ;; - o) - add_kenv kernel_options "${OPTARG}" - ;; - *) - display_usage - exit 1 - ;; - esac -done - -if [ ${delete} = "YES" ]; then - rm -f ${nextboot_file} - exit 0 -fi - -if [ -z "${kenv}" ]; then - display_usage - exit 1 -fi - -if [ -n "${kernel}" -a ${force} = "NO" -a ! -d /boot/${kernel} ]; then - echo "Error: /boot/${kernel} doesn't exist. Use -f to override." - exit 1 -fi - -zfs=$(df -Tn "/boot/" 2>/dev/null | while read _fs _type _other ; do - [ "zfs" = "${_type}" ] || continue - echo "${_fs%%/*}" -done) - -set -e - -nextboot_tmp=$(mktemp $(dirname ${nextboot_file})/nextboot.XXXXXX) - -if [ -n "${zfs}" ]; then - zfsbootcfg -z ${zfs} -n freebsd:nvstore -k nextboot_enable -v YES - cat >> ${nextboot_tmp} << EOF -$kenv -EOF -else -cat >> ${nextboot_tmp} << EOF -nextboot_enable="YES" -$kenv -EOF -fi - -fsync ${nextboot_tmp} - -mv ${nextboot_tmp} ${nextboot_file} diff --git a/sbin/reboot/reboot.8 b/sbin/reboot/reboot.8 index 13119ecf64f7..1bbc39d52be4 100644 --- a/sbin/reboot/reboot.8 +++ b/sbin/reboot/reboot.8 @@ -25,9 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)reboot.8 8.1 (Berkeley) 6/9/93 -.\" -.Dd December 20, 2017 +.Dd April 12, 2025 .Dt REBOOT 8 .Os .Sh NAME @@ -38,17 +36,25 @@ .Nd stopping and restarting the system .Sh SYNOPSIS .Nm halt -.Op Fl lNnpq +.Op Fl DflNnpq +.Op Fl e Ar variable=value .Op Fl k Ar kernel +.Op Fl o Ar options .Nm -.Op Fl cdlNnpqr +.Op Fl cDdflNnpqr +.Op Fl e Ar variable=value .Op Fl k Ar kernel +.Op Fl o Ar options .Nm fasthalt -.Op Fl lNnpq +.Op Fl DflNnpq +.Op Fl e Ar variable=value .Op Fl k Ar kernel +.Op Fl o Ar options .Nm fastboot -.Op Fl dlNnpq +.Op Fl dDflNnpq +.Op Fl e Ar variable=value .Op Fl k Ar kernel +.Op Fl o Ar options .Sh DESCRIPTION The .Nm halt @@ -79,23 +85,56 @@ driver implements the power cycle functionality and only on hardware with a BMC that supports power cycling. Unlike power off, the amount of hardware that supports power cycling is small. +.It Fl D +Delete existing +.Nm nextboot +configuration and exit. .It Fl d The system is requested to create a crash dump. This option is supported only when rebooting, and it has no effect unless a dump device has previously been specified with .Xr dumpon 8 . -.It Fl k Ar kernel -Boot the specified -.Ar kernel +.It Fl e Ar variable=value +Sets +.Va variable +to +.Va value +in the loader's and kernel's environment. +If +.Va value +is not already enclosed in double quotes, they will be added before writing to the +.Nm nextboot +configuration. +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 on the next system boot. -If the kernel boots successfully, the +This is a one-shot option, the .Em default -kernel will be booted on successive boots, this is a one-shot option. -If the boot fails, the system will continue attempting to boot -.Ar kernel -until the boot process is interrupted and a valid kernel booted. -This may change in the future. +kernel will be booted on successive boots. +No +.Nm reboot +or +.Nm halt +will be performed if +.Em /boot/kname/kernel +does not exist unless the +.Fl f +flag is specified. .It Fl l The halt or reboot is .Em not @@ -120,6 +159,9 @@ This can happen when devices have been disconnected, such as with .It Fl n The file system cache is not flushed. This option should probably not be used. +.It Fl o Ar options +This option +allows the passing of kernel flags for the next boot. .It Fl p The system will turn off the power if it can. If the power down action fails, the system @@ -142,7 +184,7 @@ After changing vfs.root.mountfrom with .Nm Fl r can be used to change the root filesystem while preserving kernel state. This requires the -.Xr tmpfs 5 +.Xr tmpfs 4 kernel module to be loaded because .Xr init 8 needs a place to store itself after the old root is unmounted, but @@ -180,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 6f656fd5f88c..f6065e80fb66 100644 --- a/sbin/reboot/reboot.c +++ b/sbin/reboot/reboot.c @@ -29,83 +29,258 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1980, 1986, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)reboot.c 8.1 (Berkeley) 6/5/93"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> -#include <sys/types.h> #include <sys/boottrace.h> +#include <sys/mount.h> #include <sys/reboot.h> +#include <sys/stat.h> #include <sys/sysctl.h> #include <sys/time.h> +#include <sys/wait.h> -#include <signal.h> #include <err.h> #include <errno.h> #include <fcntl.h> +#include <paths.h> #include <pwd.h> -#include <syslog.h> +#include <signal.h> +#include <spawn.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <syslog.h> #include <unistd.h> #include <utmpx.h> +extern char **environ; + +#define PATH_NEXTBOOT "/boot/nextboot.conf" + static void usage(void) __dead2; -static u_int get_pageins(void); +static uint64_t get_pageins(void); + +static bool dohalt; +static bool donextboot; + +#define E(...) do { \ + if (force) { \ + warn( __VA_ARGS__ ); \ + return; \ + } \ + err(1, __VA_ARGS__); \ + } while (0) \ + +static void +zfsbootcfg(const char *pool, bool force) +{ + const char * const av[] = { + "zfsbootcfg", + "-z", + pool, + "-n", + "freebsd:nvstore", + "-k", + "nextboot_enable", + "-v", + "YES", + NULL + }; + int rv, status; + pid_t p; + + rv = posix_spawnp(&p, av[0], NULL, NULL, __DECONST(char **, av), + environ); + if (rv == -1) + E("system zfsbootcfg"); + if (waitpid(p, &status, WEXITED) < 0) { + if (errno == EINTR) + return; + E("waitpid zfsbootcfg"); + } + if (WIFEXITED(status)) { + int e = WEXITSTATUS(status); + + if (e == 0) + return; + if (e == 127) + E("zfsbootcfg not found in path"); + E("zfsbootcfg returned %d", e); + } + if (WIFSIGNALED(status)) + E("zfsbootcfg died with signal %d", WTERMSIG(status)); + E("zfsbootcfg unexpected status %d", status); +} + +static void +write_nextboot(const char *fn, const char *env, bool force) +{ + char tmp[PATH_MAX]; + FILE *fp; + struct statfs sfs; + int tmpfd; + bool supported = false; + bool zfs = false; + + if (statfs("/boot", &sfs) != 0) + err(1, "statfs /boot"); + if (strcmp(sfs.f_fstypename, "ufs") == 0) { + /* + * Only UFS supports the full nextboot protocol. + */ + supported = true; + } else if (strcmp(sfs.f_fstypename, "zfs") == 0) { + zfs = true; + } + + if (zfs) { + char *slash; + + slash = strchr(sfs.f_mntfromname, '/'); + if (slash != NULL) + *slash = '\0'; + zfsbootcfg(sfs.f_mntfromname, force); + } + + if (strlcpy(tmp, fn, sizeof(tmp)) >= sizeof(tmp)) + E("Path too long %s", fn); + if (strlcat(tmp, ".XXXXXX", sizeof(tmp)) >= sizeof(tmp)) + E("Path too long %s", fn); + tmpfd = mkstemp(tmp); + if (tmpfd == -1) + E("mkstemp %s", tmp); + + fp = fdopen(tmpfd, "w"); + if (fp == NULL) + E("fdopen %s", tmp); + + if (fprintf(fp, "%s%s", + supported ? "nextboot_enable=\"YES\"\n" : "", + env != NULL ? env : "") < 0) { + int e; + + e = errno; + if (unlink(tmp)) + warn("unlink %s", tmp); + errno = e; + E("Can't write %s", tmp); + } + if (fsync(fileno(fp)) != 0) + E("Can't fsync %s", fn); + if (rename(tmp, fn) != 0) { + int e; -static int dohalt; + e = errno; + if (unlink(tmp)) + warn("unlink %s", tmp); + errno = e; + E("Can't rename %s to %s", tmp, fn); + } + fclose(fp); +} + +static char * +split_kv(char *raw) +{ + char *eq; + int len; + + eq = strchr(raw, '='); + if (eq == NULL) + errx(1, "No = in environment string %s", raw); + *eq++ = '\0'; + len = strlen(eq); + if (len == 0) + errx(1, "Invalid null value %s=", raw); + if (eq[0] == '"') { + if (len < 2 || eq[len - 1] != '"') + errx(1, "Invalid string '%s'", eq); + eq[len - 1] = '\0'; + return (eq + 1); + } + return (eq); +} + +static void +add_env(char **env, const char *key, const char *value) +{ + char *oldenv; + + oldenv = *env; + asprintf(env, "%s%s=\"%s\"\n", oldenv != NULL ? oldenv : "", key, value); + if (env == NULL) + errx(1, "No memory to build env array"); + free(oldenv); +} + +/* + * Different options are valid for different programs. + */ +#define GETOPT_REBOOT "cDde:fk:lNno:pqr" +#define GETOPT_NEXTBOOT "De:fk:o:" int main(int argc, char *argv[]) { struct utmpx utx; const struct passwd *pw; - int ch, howto, i, fd, lflag, nflag, qflag, sverrno, Nflag; - u_int pageins; - const char *user, *kernel = NULL; + struct stat st; + int ch, howto = 0, i, sverrno; + bool Dflag, fflag, lflag, Nflag, nflag, qflag; + uint64_t pageins; + const char *user, *kernel = NULL, *getopts = GETOPT_REBOOT; + char *env = NULL, *v; if (strstr(getprogname(), "halt") != NULL) { - dohalt = 1; + dohalt = true; howto = RB_HALT; - } else + } else if (strcmp(getprogname(), "nextboot") == 0) { + donextboot = true; + getopts = GETOPT_NEXTBOOT; /* Note: reboot's extra opts return '?' */ + } else { + /* reboot */ howto = 0; - lflag = nflag = qflag = Nflag = 0; - while ((ch = getopt(argc, argv, "cdk:lNnpqr")) != -1) + } + Dflag = fflag = lflag = Nflag = nflag = qflag = false; + while ((ch = getopt(argc, argv, getopts)) != -1) { switch(ch) { case 'c': howto |= RB_POWERCYCLE; break; + case 'D': + Dflag = true; + break; case 'd': howto |= RB_DUMP; break; + case 'e': + v = split_kv(optarg); + add_env(&env, optarg, v); + break; + case 'f': + fflag = true; + break; case 'k': kernel = optarg; break; case 'l': - lflag = 1; + lflag = true; break; case 'n': - nflag = 1; + nflag = true; howto |= RB_NOSYNC; break; case 'N': - nflag = 1; - Nflag = 1; + nflag = true; + Nflag = true; + break; + case 'o': + add_env(&env, "kernel_options", optarg); break; case 'p': howto |= RB_POWEROFF; break; case 'q': - qflag = 1; + qflag = true; break; case 'r': howto |= RB_REROOT; @@ -114,11 +289,20 @@ main(int argc, char *argv[]) default: usage(); } + } + argc -= optind; argv += optind; 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)) errx(1, "cannot dump (-d) when halting; must reboot instead"); if (Nflag && (howto & RB_NOSYNC) != 0) @@ -127,7 +311,31 @@ main(int argc, char *argv[]) errx(1, "-c and -p cannot be used together"); if ((howto & RB_REROOT) != 0 && howto != RB_REROOT) errx(1, "-r cannot be used with -c, -d, -n, or -p"); - if (geteuid()) { + if ((howto & RB_REROOT) != 0 && kernel != NULL) + errx(1, "-r and -k cannot be used together, there is no next kernel"); + + if (Dflag) { + struct stat sb; + + /* + * Break the rule about stat then doing + * something. When we're booting, there's no + * race. When we're a read-only root, though, the + * read-only error takes priority over the file not + * there error in unlink. So stat it first and exit + * with success if it isn't there. Otherwise, let + * unlink sort error reporting. POSIX-1.2024 suggests + * ENOENT should be preferred to EROFS for unlink, + * but FreeBSD historically has preferred EROFS. + */ + if (stat(PATH_NEXTBOOT, &sb) != 0 && errno == ENOENT) + exit(0); + if (unlink(PATH_NEXTBOOT) != 0) + warn("unlink " PATH_NEXTBOOT); + exit(0); + } + + if (!donextboot && geteuid() != 0) { errno = EPERM; err(1, NULL); } @@ -138,17 +346,27 @@ main(int argc, char *argv[]) } if (kernel != NULL) { - fd = open("/boot/nextboot.conf", O_WRONLY | O_CREAT | O_TRUNC, - 0444); - if (fd > -1) { - (void)write(fd, "nextboot_enable=\"YES\"\n", 22); - (void)write(fd, "kernel=\"", 8L); - (void)write(fd, kernel, strlen(kernel)); - (void)write(fd, "\"\n", 2); - close(fd); + if (!fflag) { + char *k; + struct stat sb; + + asprintf(&k, "/boot/%s/kernel", kernel); + if (k == NULL) + errx(1, "No memory to check %s", kernel); + if (stat(k, &sb) != 0) + err(1, "stat %s", k); + if (!S_ISREG(sb.st_mode)) + errx(1, "%s is not a file", k); + free(k); } + add_env(&env, "kernel", kernel); } + if (env != NULL) + write_nextboot(PATH_NEXTBOOT, env, fflag); + if (donextboot) + exit (0); + /* Log the reboot. */ if (!lflag) { if ((user = getlogin()) == NULL) @@ -272,17 +490,17 @@ usage(void) exit(1); } -static u_int +static uint64_t get_pageins(void) { - u_int pageins; + uint64_t pageins; size_t len; len = sizeof(pageins); if (sysctlbyname("vm.stats.vm.v_swappgsin", &pageins, &len, NULL, 0) != 0) { - warnx("v_swappgsin"); + warn("v_swappgsin"); return (0); } - return pageins; + return (pageins); } diff --git a/sbin/recoverdisk/Makefile b/sbin/recoverdisk/Makefile index e0a8d118656f..41bc736ecc6e 100644 --- a/sbin/recoverdisk/Makefile +++ b/sbin/recoverdisk/Makefile @@ -1,4 +1,3 @@ - PACKAGE=runtime PROG= recoverdisk 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 43b62fc142f3..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(sizeof *pp, 1L); + 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/resolvconf/Makefile b/sbin/resolvconf/Makefile index e381ee4118a8..328153a2b0ba 100644 --- a/sbin/resolvconf/Makefile +++ b/sbin/resolvconf/Makefile @@ -1,4 +1,3 @@ - PACKAGE=resolvconf DIST= ${SRCTOP}/contrib/openresolv .PATH: ${DIST} diff --git a/sbin/restore/Makefile b/sbin/restore/Makefile index f23258a00cd5..99fc7e190bfd 100644 --- a/sbin/restore/Makefile +++ b/sbin/restore/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.1 (Berkeley) 6/5/93 - .PATH: ${.CURDIR:H}/dump PACKAGE=ufs diff --git a/sbin/restore/dirs.c b/sbin/restore/dirs.c index 4a25b728e8a0..16fdecba6c18 100644 --- a/sbin/restore/dirs.c +++ b/sbin/restore/dirs.c @@ -34,14 +34,6 @@ * SUCH DAMAGE. */ -#ifndef lint -#if 0 -static char sccsid[] = "@(#)dirs.c 8.7 (Berkeley) 5/1/95"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/file.h> #include <sys/stat.h> diff --git a/sbin/restore/extern.h b/sbin/restore/extern.h index fac58891a355..0b2f85d878c3 100644 --- a/sbin/restore/extern.h +++ b/sbin/restore/extern.h @@ -27,8 +27,6 @@ * 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. - * - * @(#)extern.h 8.2 (Berkeley) 1/7/94 */ struct entry *addentry(char *, ino_t, int); diff --git a/sbin/restore/interactive.c b/sbin/restore/interactive.c index 336ab61221dc..ea7fc0f18b64 100644 --- a/sbin/restore/interactive.c +++ b/sbin/restore/interactive.c @@ -29,13 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -#if 0 -static char sccsid[] = "@(#)interactive.c 8.5 (Berkeley) 5/1/95"; -#endif -#endif /* not lint */ - -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/stat.h> diff --git a/sbin/restore/main.c b/sbin/restore/main.c index 6eba6954b6e4..f94b37503d18 100644 --- a/sbin/restore/main.c +++ b/sbin/restore/main.c @@ -29,19 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1983, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/4/95"; -#endif -#endif /* not lint */ - -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/stat.h> diff --git a/sbin/restore/restore.8 b/sbin/restore/restore.8 index 1499b8d737de..b7b96a1a0d2a 100644 --- a/sbin/restore/restore.8 +++ b/sbin/restore/restore.8 @@ -25,8 +25,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)restore.8 8.4 (Berkeley) 5/1/95 -.\" .Dd October 12, 2006 .Dt RESTORE 8 .Os diff --git a/sbin/restore/restore.c b/sbin/restore/restore.c index 33e97c18c41c..1a5e16174b2c 100644 --- a/sbin/restore/restore.c +++ b/sbin/restore/restore.c @@ -29,13 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -#if 0 -static char sccsid[] = "@(#)restore.c 8.3 (Berkeley) 9/13/94"; -#endif -#endif /* not lint */ - -#include <sys/cdefs.h> #include <sys/types.h> #include <limits.h> diff --git a/sbin/restore/restore.h b/sbin/restore/restore.h index e4db29340b8e..407816993196 100644 --- a/sbin/restore/restore.h +++ b/sbin/restore/restore.h @@ -32,8 +32,6 @@ * 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. - * - * @(#)restore.h 8.3 (Berkeley) 9/13/94 */ /* @@ -59,7 +57,7 @@ extern long volno; /* current volume being read */ extern long ntrec; /* number of TP_BSIZE records per tape block */ extern time_t dumptime; /* time that this dump begins */ extern time_t dumpdate; /* time that this dump was made */ -extern char command; /* opration being performed */ +extern char command; /* operation being performed */ extern FILE *terminal; /* file descriptor for the terminal input */ extern int Bcvt; /* need byte swapping on inodes and dirs */ extern int oldinofmt; /* reading tape with FreeBSD 1 format inodes */ @@ -73,7 +71,7 @@ struct entry { char e_type; /* type of this entry, see below */ short e_flags; /* status flags, see below */ ino_t e_ino; /* inode number in previous file sys */ - long e_index; /* unique index (for dumpped table) */ + long e_index; /* unique index (for dumped table) */ struct entry *e_parent; /* pointer to parent directory (..) */ struct entry *e_sibling; /* next element in this directory (.) */ struct entry *e_links; /* hard links to this inode */ diff --git a/sbin/restore/symtab.c b/sbin/restore/symtab.c index 49ed39dd2022..3ae6bf20c7be 100644 --- a/sbin/restore/symtab.c +++ b/sbin/restore/symtab.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -#if 0 -static char sccsid[] = "@(#)symtab.c 8.3 (Berkeley) 4/28/95"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - /* * These routines maintain the symbol table which tracks the state * of the file system being restored. They provide lookup by either diff --git a/sbin/restore/tape.c b/sbin/restore/tape.c index c3fe0d7d8b05..5b7cde10c670 100644 --- a/sbin/restore/tape.c +++ b/sbin/restore/tape.c @@ -34,16 +34,10 @@ * SUCH DAMAGE. */ -#ifndef lint -#if 0 -static char sccsid[] = "@(#)tape.c 8.9 (Berkeley) 5/1/95"; -#endif -#endif /* not lint */ - -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/file.h> #include <sys/mtio.h> +#include <sys/queue.h> #include <sys/stat.h> #include <sys/time.h> #include <sys/extattr.h> diff --git a/sbin/restore/utilities.c b/sbin/restore/utilities.c index a53d42f9e600..c4d4fb1ea42f 100644 --- a/sbin/restore/utilities.c +++ b/sbin/restore/utilities.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -#if 0 -static char sccsid[] = "@(#)utilities.c 8.5 (Berkeley) 4/28/95"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/stat.h> diff --git a/sbin/route/Makefile b/sbin/route/Makefile index 7685a2fecd64..43b9c0d8d821 100644 --- a/sbin/route/Makefile +++ b/sbin/route/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.1 (Berkeley) 6/5/93 - .include <src.opts.mk> PACKAGE=runtime diff --git a/sbin/route/Makefile.depend b/sbin/route/Makefile.depend index 344a5d0e9310..672ec75190c6 100644 --- a/sbin/route/Makefile.depend +++ b/sbin/route/Makefile.depend @@ -7,6 +7,7 @@ DIRDEPS = \ lib/${CSU_DIR} \ lib/libc \ lib/libcompiler_rt \ + lib/libjail \ .include <dirdeps.mk> diff --git a/sbin/route/keywords b/sbin/route/keywords index 496fb8b95e2e..cbd8799ffb6b 100644 --- a/sbin/route/keywords +++ b/sbin/route/keywords @@ -1,4 +1,3 @@ -# @(#)keywords 8.2 (Berkeley) 3/19/94 4 6 diff --git a/sbin/route/route.8 b/sbin/route/route.8 index 4eccd511f0cc..572781ef59c8 100644 --- a/sbin/route/route.8 +++ b/sbin/route/route.8 @@ -25,9 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)route.8 8.3 (Berkeley) 3/19/94 -.\" -.Dd June 16, 2023 +.Dd November 12, 2024 .Dt ROUTE 8 .Os .Sh NAME @@ -554,17 +552,3 @@ The .Nm utility appeared in .Bx 4.2 . -.Sh BUGS -The first paragraph may have slightly exaggerated -.Xr routed 8 Ns 's -abilities. -.Pp -Currently, routes with the -.Dv RTF_BLACKHOLE -flag set need to have the gateway set to an instance of the -.Xr lo 4 -driver, using the -.Fl iface -option, for the flag to have any effect; unless IP fast forwarding -is enabled, in which case the meaning of the flag will always -be honored. diff --git a/sbin/route/route.c b/sbin/route/route.c index 3913bdc9e6af..f37d23d25c2c 100644 --- a/sbin/route/route.c +++ b/sbin/route/route.c @@ -29,19 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1983, 1989, 1991, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)route.c 8.6 (Berkeley) 4/28/95"; -#endif -#endif /* not lint */ - -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/file.h> #include <sys/socket.h> @@ -107,8 +94,8 @@ static u_long rtm_inits; static uid_t uid; static int defaultfib; static int numfibs; -static char domain[MAXHOSTNAMELEN + 1]; -static bool domain_initialized; +static char domain_storage[MAXHOSTNAMELEN + 1]; +static const char *domain; static char rt_line[NI_MAXHOST]; static char net_line[MAXHOSTNAMELEN + 1]; @@ -594,14 +581,16 @@ routename(struct sockaddr *sa) const char *cp; int n; - if (!domain_initialized) { - domain_initialized = true; - if (gethostname(domain, MAXHOSTNAMELEN) == 0 && - (cp = strchr(domain, '.'))) { - domain[MAXHOSTNAMELEN] = '\0'; - (void)strcpy(domain, cp + 1); - } else - domain[0] = '\0'; + if (domain == NULL) { + if (gethostname(domain_storage, + sizeof(domain_storage) - 1) == 0 && + (cp = strchr(domain_storage, '.')) != NULL) { + domain_storage[sizeof(domain_storage) - 1] = '\0'; + domain = cp + 1; + } else { + domain_storage[0] = '\0'; + domain = domain_storage; + } } /* If the address is zero-filled, use "default". */ @@ -1342,6 +1331,9 @@ getaddr(int idx, char *str, int nrflags) q = strchr(str,'/'); if (q != NULL && idx == RTAX_DST) { /* A.B.C.D/NUM */ + struct sockaddr_in *mask; + uint32_t mask_bits; + *q = '\0'; if (inet_aton(str, &sin->sin_addr) == 0) errx(EX_NOHOST, "bad address: %s", str); @@ -1351,6 +1343,20 @@ getaddr(int idx, char *str, int nrflags) errx(EX_NOHOST, "bad mask length: %s", q + 1); inet_makemask((struct sockaddr_in *)&so[RTAX_NETMASK],masklen); + + /* + * Check for bogus destination such as "10/8"; heuristic is + * that there are bits set in the host part, and no dot + * is present. + */ + mask = ((struct sockaddr_in *) &so[RTAX_NETMASK]); + mask_bits = ntohl(mask->sin_addr.s_addr); + if ((ntohl(sin->sin_addr.s_addr) & ~mask_bits) != 0 && + strchr(str, '.') == NULL) + errx(EX_NOHOST, + "malformed address, bits set after mask;" + " %s means %s", + str, inet_ntoa(sin->sin_addr)); return (0); } if (inet_aton(str, &sin->sin_addr) != 0) @@ -1657,6 +1663,7 @@ static const char *const msgtypes[] = { "RTM_DELMADDR: multicast group membership removed from iface", "RTM_IFANNOUNCE: interface arrival/departure", "RTM_IEEE80211: IEEE 802.11 wireless event", + "RTM_IPFWLOG: IPFW log", }; static const char metricnames[] = diff --git a/sbin/route/route_netlink.c b/sbin/route/route_netlink.c index 0dbd90151e10..ba22a2ec1e22 100644 --- a/sbin/route/route_netlink.c +++ b/sbin/route/route_netlink.c @@ -266,27 +266,32 @@ rtmsg_nl_int(struct nl_helper *h, int cmd, int rtm_flags, int fib, int rtm_addrs if (rt_metrics->rmx_weight > 0) snl_add_msg_attr_u32(&nw, NL_RTA_WEIGHT, rt_metrics->rmx_weight); - if (snl_finalize_msg(&nw) && snl_send_message(ss, hdr)) { + if ((hdr = snl_finalize_msg(&nw)) && snl_send_message(ss, hdr)) { struct snl_errmsg_data e = {}; hdr = snl_read_reply(ss, hdr->nlmsg_seq); if (nl_type == NL_RTM_GETROUTE) { - if (hdr->nlmsg_type == NL_RTM_NEWROUTE) + if (hdr->nlmsg_type == NL_RTM_NEWROUTE) { print_getmsg(h, hdr, dst); - else { - snl_parse_errmsg(ss, hdr, &e); - if (e.error == ESRCH) - warn("route has not been found"); - else - warn("message indicates error %d", e.error); + return (0); } - - return (0); } - if (snl_parse_errmsg(ss, hdr, &e)) + if (snl_parse_errmsg(ss, hdr, &e)) { + switch (e.error) { + case (ESRCH): + warnx("route has not been found"); + break; + default: + if (e.error == 0) + break; + warnc(e.error, "message indicates error"); + } + return (e.error); + } } + return (EINVAL); } @@ -314,7 +319,7 @@ get_ifdata(struct nl_helper *h, uint32_t ifindex, struct snl_parsed_link_simple struct ifinfomsg *ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg); if (ifmsg != NULL) ifmsg->ifi_index = ifindex; - if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return; hdr = snl_read_reply(ss, hdr->nlmsg_seq); @@ -584,7 +589,7 @@ print_nlmsg_link(struct nl_helper *h, struct nlmsghdr *hdr, printf("iface#%u %s ", l.ifi_index, l.ifla_ifname); printf("admin %s ", (l.ifi_flags & IFF_UP) ? "UP" : "DOWN"); - if (l.ifla_operstate < NL_ARRAY_LEN(operstate)) + if (l.ifla_operstate < nitems(operstate)) printf("oper %s ", operstate[l.ifla_operstate]); if (l.ifla_mtu > 0) printf("mtu %u ", l.ifla_mtu); @@ -663,7 +668,7 @@ print_nlmsg_neigh(struct nl_helper *h, struct nlmsghdr *hdr, struct snl_parsed_link_simple link = {}; get_ifdata(h, attrs.nda_ifindex, &link); - for (unsigned int i = 0; i < NL_ARRAY_LEN(nudstate); i++) { + for (unsigned int i = 0; i < nitems(nudstate); i++) { if ((1 << i) & attrs.ndm_state) { printf("state %s ", nudstate[i]); break; @@ -733,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); } @@ -763,7 +769,7 @@ monitor_nl(int fib) socklen_t optlen = sizeof(optval); setsockopt(ss_event.fd, SOL_NETLINK, NETLINK_MSG_INFO, &optval, optlen); - for (unsigned int i = 0; i < NL_ARRAY_LEN(groups); i++) { + for (unsigned int i = 0; i < nitems(groups); i++) { int error; int optval = groups[i]; socklen_t optlen = sizeof(optval); @@ -817,7 +823,7 @@ flushroute_one(struct nl_helper *h, struct snl_parsed_route *r) snl_add_msg_attr_u32(&nw, RTA_TABLE, r->rta_table); snl_add_msg_attr_ip(&nw, RTA_DST, r->rta_dst); - if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) return (ENOMEM); if (!snl_read_reply_code(ss, hdr->nlmsg_seq, &e)) { @@ -864,7 +870,7 @@ flushroutes_fib_nl(int fib, int af) rtm->rtm_family = af; snl_add_msg_attr_u32(&nw, RTA_TABLE, fib); - if (!snl_finalize_msg(&nw) || !snl_send_message(&ss, hdr)) { + if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) { snl_free(&ss); return (EINVAL); } diff --git a/sbin/route/tests/Makefile b/sbin/route/tests/Makefile index 1ae964693ff4..9d634d7c48b8 100644 --- a/sbin/route/tests/Makefile +++ b/sbin/route/tests/Makefile @@ -1,10 +1,9 @@ - PACKAGE= tests -ATF_TESTS_SH+= \ - basic +ATF_TESTS_SH+= basic +# Tests reuse jail names and so cannot run in parallel. +TEST_METADATA.basic+= is_exclusive=true -${PACKAGE}FILES+= \ - utils.subr +${PACKAGE}FILES+= utils.subr .include <bsd.test.mk> diff --git a/sbin/route/tests/Makefile.depend b/sbin/route/tests/Makefile.depend new file mode 100644 index 000000000000..11aba52f82cf --- /dev/null +++ b/sbin/route/tests/Makefile.depend @@ -0,0 +1,10 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/sbin/route/tests/basic.sh b/sbin/route/tests/basic.sh index bb432daa0793..2e2ab9dba97f 100644 --- a/sbin/route/tests/basic.sh +++ b/sbin/route/tests/basic.sh @@ -117,8 +117,66 @@ basic_v6_cleanup() vnet_cleanup } +atf_test_case "interface_route_v4" "cleanup" +interface_route_v4_head() +{ + atf_set descr 'add interface route for v4' + atf_set require.user root + atf_set require.progs jail jq +} + +interface_route_v4_body() +{ + epair=$(vnet_mkepair) + ifconfig ${epair}a up + vnet_mkjail alcatraz ${epair}b + + # add interface route + jexec alcatraz route add "192.0.2.1" -iface ${epair}b + gateway=$(check_route "alcatraz" "192.0.2.1") + + if [ -z "${gateway}" ]; then + atf_fail "Failed to add interface route." + fi +} + +interface_route_v4_cleanup() +{ + vnet_cleanup +} + +atf_test_case "interface_route_v6" "cleanup" +interface_route_v6_head() +{ + atf_set descr 'add interface route for v6' + atf_set require.user root + atf_set require.progs jail jq +} + +interface_route_v6_body() +{ + epair=$(vnet_mkepair) + ifconfig ${epair}a up + vnet_mkjail alcatraz ${epair}b + + # add interface route + jexec alcatraz route add -6 "2001:db8:cc4b::1" -iface ${epair}b + gateway=$(check_route "alcatraz" "2001:db8:cc4b::1") + + if [ -z "${gateway}" ]; then + atf_fail "Failed to add interface route." + fi +} + +interface_route_v6_cleanup() +{ + vnet_cleanup +} + atf_init_test_cases() { atf_add_test_case "basic_v4" atf_add_test_case "basic_v6" + atf_add_test_case "interface_route_v4" + atf_add_test_case "interface_route_v6" } 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 d3b5cbd3a79b..e09337e43724 100644 --- a/sbin/routed/Makefile.inc +++ b/sbin/routed/Makefile.inc @@ -1,2 +1,3 @@ +PACKAGE= rip .include "../Makefile.inc" diff --git a/sbin/routed/defs.h b/sbin/routed/defs.h index 903d716e3ab0..81f3131f2676 100644 --- a/sbin/routed/defs.h +++ b/sbin/routed/defs.h @@ -27,8 +27,6 @@ * 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. - * - * @(#)defs.h 8.1 (Berkeley) 6/5/93 */ @@ -86,9 +84,6 @@ #define RIPVERSION RIPv2 #include <protocols/routed.h> -#ifndef __RCSID -#define __RCSID(_s) static const char rcsid[] UNUSED = _s -#endif #ifndef __COPYRIGHT #define __COPYRIGHT(_s) static const char copyright[] UNUSED = _s #endif diff --git a/sbin/routed/main.c b/sbin/routed/main.c index 491a40026345..49be52ccbdf4 100644 --- a/sbin/routed/main.c +++ b/sbin/routed/main.c @@ -35,9 +35,6 @@ #include <fcntl.h> #include <sys/file.h> -__COPYRIGHT("@(#) Copyright (c) 1983, 1988, 1993 " - "The Regents of the University of California." - " All rights reserved."); pid_t mypid; naddr myaddr; /* system address */ diff --git a/sbin/routed/pathnames.h b/sbin/routed/pathnames.h index 75dc17364302..f5b8bd31b4d5 100644 --- a/sbin/routed/pathnames.h +++ b/sbin/routed/pathnames.h @@ -27,8 +27,6 @@ * 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. - * - * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 */ #include <paths.h> diff --git a/sbin/routed/radix.c b/sbin/routed/radix.c index c4a0e5de2c2c..ad5b1df1f572 100644 --- a/sbin/routed/radix.c +++ b/sbin/routed/radix.c @@ -27,8 +27,6 @@ * 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. - * - * @(#)radix.c 8.4 (Berkeley) 11/2/94 */ /* diff --git a/sbin/routed/radix.h b/sbin/routed/radix.h index dfe502f280bb..35f7dcf7e08c 100644 --- a/sbin/routed/radix.h +++ b/sbin/routed/radix.h @@ -27,8 +27,6 @@ * 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. - * - * @(#)radix.h 8.2 (Berkeley) 10/31/94 */ #ifndef __RADIX_H_ diff --git a/sbin/routed/routed.8 b/sbin/routed/routed.8 index 08d39041cdf9..334c828b943e 100644 --- a/sbin/routed/routed.8 +++ b/sbin/routed/routed.8 @@ -27,15 +27,20 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)routed.8 8.2 (Berkeley) 12/11/93 -.\" -.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/routed/rtquery/rtquery.c b/sbin/routed/rtquery/rtquery.c index 24c01a8ac308..67a635545624 100644 --- a/sbin/routed/rtquery/rtquery.c +++ b/sbin/routed/rtquery/rtquery.c @@ -29,7 +29,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/protosw.h> #include <sys/socket.h> @@ -50,23 +49,6 @@ #endif #define UNUSED __attribute__((unused)) -#ifndef __RCSID -#define __RCSID(_s) static const char rcsid[] UNUSED = _s -#endif -#ifndef __COPYRIGHT -#define __COPYRIGHT(_s) static const char copyright[] UNUSED = _s -#endif -__COPYRIGHT("@(#) Copyright (c) 1983, 1988, 1993\n" - "The Regents of the University of California." - " All rights reserved.\n"); -#ifdef __NetBSD__ -__RCSID("$NetBSD$"); -#elif defined(__FreeBSD__) -#else -__RCSID("$Revision: 2.26 $"); -#ident "$Revision: 2.26 $" -#endif - #ifndef sgi #define _HAVE_SIN_LEN #endif diff --git a/sbin/savecore/Makefile b/sbin/savecore/Makefile index fe10df459309..7e194a62deb1 100644 --- a/sbin/savecore/Makefile +++ b/sbin/savecore/Makefile @@ -1,4 +1,3 @@ - PACKAGE=runtime CONFS= minfree VAR_CRASH= /var/crash @@ -19,4 +18,7 @@ CFLAGS+= -DWITH_CASPER LIBADD+= casper cap_fileargs cap_syslog .endif +HAS_TESTS= +SUBDIR.${MK_TESTS}+= tests + .include <bsd.prog.mk> diff --git a/sbin/savecore/savecore.8 b/sbin/savecore/savecore.8 index 8a3b80d73469..1fb79c51f98d 100644 --- a/sbin/savecore/savecore.8 +++ b/sbin/savecore/savecore.8 @@ -25,9 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" From: @(#)savecore.8 8.1 (Berkeley) 6/5/93 -.\" -.Dd April 4, 2022 +.Dd July 16, 2025 .Dt SAVECORE 8 .Os .Sh NAME @@ -71,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, @@ -195,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/savecore.c b/sbin/savecore/savecore.c index a571144589bb..2f3ad3adc7ab 100644 --- a/sbin/savecore/savecore.c +++ b/sbin/savecore/savecore.c @@ -62,7 +62,6 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/disk.h> #include <sys/kerneldump.h> @@ -102,6 +101,9 @@ #define STATUS_GOOD 1 #define STATUS_UNKNOWN 2 +#define LOG_OPTIONS LOG_PERROR +#define LOG_FACILITY LOG_DAEMON + static cap_channel_t *capsyslog; static fileargs_t *capfa; static bool checkfor, compress, uncompress, clear, force, keep; /* flags */ @@ -336,7 +338,8 @@ write_header_info(xo_handle_t *xostdout, const struct kerneldumpheader *kdh, printheader(xoinfo, kdh, device, bounds, status); xo_close_container_h(xoinfo, "crashdump"); xo_flush_h(xoinfo); - xo_finish_h(xoinfo); + if (xo_finish_h(xoinfo) < 0) + xo_err(EXIT_FAILURE, "stdout"); fclose(info); return (0); @@ -434,7 +437,7 @@ check_space(const char *savedir, int savedirfd, off_t dumpsize, int bounds) if (fstatfs(savedirfd, &fsbuf) < 0) { logmsg(LOG_ERR, "%s: %m", savedir); - exit(1); + exit(EXIT_FAILURE); } spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024; totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024; @@ -1124,7 +1127,7 @@ DoFile(const char *savedir, int savedirfd, const char *device) if (checkfor) { printf("A dump exists on %s\n", device); close(fddev); - exit(0); + exit(EXIT_SUCCESS); } if (kdhl.panicstring[0] != '\0') @@ -1276,7 +1279,8 @@ nuke: "error while clearing the dump header: %m"); } xo_close_container_h(xostdout, "crashdump"); - xo_finish_h(xostdout); + if (xo_finish_h(xostdout) < 0) + xo_err(EXIT_FAILURE, "stdout"); free(dumpkey); free(temp); close(fddev); @@ -1301,7 +1305,7 @@ devify(int argc, char **argv) devs = malloc(argc * sizeof(*argv)); if (devs == NULL) { logmsg(LOG_ERR, "malloc(): %m"); - exit(1); + exit(EXIT_FAILURE); } for (i = 0; i < argc; i++) { if (strncmp(argv[i], _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) @@ -1312,16 +1316,16 @@ devify(int argc, char **argv) fullpath = malloc(PATH_MAX); if (fullpath == NULL) { logmsg(LOG_ERR, "malloc(): %m"); - exit(1); + exit(EXIT_FAILURE); } l = snprintf(fullpath, PATH_MAX, "%s%s", _PATH_DEV, argv[i]); if (l < 0) { logmsg(LOG_ERR, "snprintf(): %m"); - exit(1); + exit(EXIT_FAILURE); } else if (l >= PATH_MAX) { logmsg(LOG_ERR, "device name too long"); - exit(1); + exit(EXIT_FAILURE); } devs[i] = fullpath; } @@ -1346,7 +1350,7 @@ enum_dumpdevs(int *argcp) argv = malloc(n * sizeof(*argv)); if (argv == NULL) { logmsg(LOG_ERR, "malloc(): %m"); - exit(1); + exit(EXIT_FAILURE); } for (;;) { fsp = getfsent(); @@ -1360,13 +1364,13 @@ enum_dumpdevs(int *argcp) argv = realloc(argv, n * sizeof(*argv)); if (argv == NULL) { logmsg(LOG_ERR, "realloc(): %m"); - exit(1); + exit(EXIT_FAILURE); } } argv[argc] = strdup(fsp->fs_spec); if (argv[argc] == NULL) { logmsg(LOG_ERR, "strdup(): %m"); - exit(1); + exit(EXIT_FAILURE); } argc++; } @@ -1383,7 +1387,7 @@ init_caps(int argc, char **argv) capcas = cap_init(); if (capcas == NULL) { logmsg(LOG_ERR, "cap_init(): %m"); - exit(1); + exit(EXIT_FAILURE); } /* * The fileargs capability does not currently provide a way to limit @@ -1394,20 +1398,21 @@ init_caps(int argc, char **argv) 0, &rights, FA_OPEN); if (capfa == NULL) { logmsg(LOG_ERR, "fileargs_init(): %m"); - exit(1); + exit(EXIT_FAILURE); } caph_cache_catpages(); caph_cache_tzdata(); if (caph_enter_casper() != 0) { logmsg(LOG_ERR, "caph_enter_casper(): %m"); - exit(1); + exit(EXIT_FAILURE); } capsyslog = cap_service_open(capcas, "system.syslog"); if (capsyslog == NULL) { logmsg(LOG_ERR, "cap_service_open(system.syslog): %m"); - exit(1); + exit(EXIT_FAILURE); } cap_close(capcas); + cap_openlog(capsyslog, "savecore", LOG_OPTIONS, LOG_FACILITY); } static void @@ -1418,7 +1423,7 @@ usage(void) " savecore -C [-v] [device ...]", " savecore -L [-fvZz] [-m maxdumps] [directory]", " savecore [-fkuvz] [-m maxdumps] [directory [device ...]]"); - exit(1); + exit(EXIT_FAILURE); } int @@ -1435,12 +1440,12 @@ main(int argc, char **argv) savedir = "."; comp_desired = KERNELDUMP_COMP_NONE; - openlog("savecore", LOG_PERROR, LOG_DAEMON); + openlog("savecore", LOG_OPTIONS, LOG_FACILITY); signal(SIGINFO, infohandler); argc = xo_parse_args(argc, argv); if (argc < 0) - exit(1); + exit(EXIT_FAILURE); while ((ch = getopt(argc, argv, "CcfkLm:uvZz")) != -1) switch(ch) { @@ -1463,7 +1468,7 @@ main(int argc, char **argv) maxdumps = atoi(optarg); if (maxdumps <= 0) { logmsg(LOG_ERR, "Invalid maxdump value"); - exit(1); + exit(EXIT_FAILURE); } break; case 'u': @@ -1503,7 +1508,7 @@ main(int argc, char **argv) error = chdir(argv[0]); if (error) { logmsg(LOG_ERR, "chdir(%s): %m", argv[0]); - exit(1); + exit(EXIT_FAILURE); } savedir = argv[0]; argc--; @@ -1525,7 +1530,7 @@ main(int argc, char **argv) savedirfd = open(savedir, O_RDONLY | O_DIRECTORY); if (savedirfd < 0) { logmsg(LOG_ERR, "open(%s): %m", savedir); - exit(1); + exit(EXIT_FAILURE); } (void)cap_rights_init(&rights, CAP_CREATE, CAP_FCNTL, CAP_FSTATAT, CAP_FSTATFS, CAP_PREAD, CAP_SYMLINKAT, CAP_FTRUNCATE, CAP_UNLINKAT, @@ -1535,7 +1540,7 @@ main(int argc, char **argv) CAP_RENAMEAT_TARGET); if (caph_rights_limit(savedirfd, &rights) < 0) { logmsg(LOG_ERR, "cap_rights_limit(): %m"); - exit(1); + exit(EXIT_FAILURE); } /* Enter capability mode. */ @@ -1548,7 +1553,7 @@ main(int argc, char **argv) if (checkfor) { if (verbose) printf("No dump exists\n"); - exit(1); + exit(EXIT_FAILURE); } if (verbose) logmsg(LOG_WARNING, "no dumps found"); @@ -1557,14 +1562,14 @@ main(int argc, char **argv) if (verbose) logmsg(LOG_WARNING, "unsaved dumps found but not saved"); - exit(1); + exit(EXIT_FAILURE); } else if (verbose) logmsg(LOG_WARNING, "no unsaved dumps found"); } else if (verbose) { logmsg(LOG_NOTICE, "%d cores saved in %s\n", nsaved, savedir); } - return (0); + exit(EXIT_SUCCESS); } static void diff --git a/sbin/savecore/tests/Makefile b/sbin/savecore/tests/Makefile new file mode 100644 index 000000000000..7ec3c9aeedcc --- /dev/null +++ b/sbin/savecore/tests/Makefile @@ -0,0 +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/savecore/tests/livedump_test.sh b/sbin/savecore/tests/livedump_test.sh new file mode 100644 index 000000000000..382b090235ee --- /dev/null +++ b/sbin/savecore/tests/livedump_test.sh @@ -0,0 +1,54 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2024 Mark Johnston <markj@FreeBSD.org> +# + +atf_test_case livedump_kldstat +livedump_kldstat_head() +{ + atf_set "descr" "Test livedump integrity" + atf_set "require.progs" kgdb + atf_set "require.user" root +} +livedump_kldstat_body() +{ + atf_check -e match:"savecore .*- livedump" savecore -L . + + kernel=$(sysctl -n kern.bootfile) + + if ! [ -f /usr/lib/debug/${kernel}.debug ]; then + atf_skip "No debug symbols for the running kernel" + fi + + # Implement kldstat using gdb script. + cat >./kldstat.gdb <<'__EOF__' +printf "Id Refs Address Size Name\n" +set $_lf = linker_files.tqh_first +while ($_lf) + printf "%2d %4d %p %8x %s\n", $_lf->id, $_lf->refs, $_lf->address, $_lf->size, $_lf->filename + set $_lf = $_lf->link.tqe_next +end +__EOF__ + + # Ignore stderr since kgdb prints some warnings about inaccessible + # source files. + # + # Use a script to source the main gdb script, otherwise kgdb prints + # a bunch of line noise that is annoying to filter out. + echo "source ./kldstat.gdb" > ./script.gdb + atf_check -o save:out -e ignore \ + kgdb -q ${kernel} ./livecore.0 < ./script.gdb + + # Get rid of gunk printed by kgdb. + sed -i '' -n -e 's/^(kgdb) //' -e '/^Id Refs /,$p' out + + # The output of kgdb should match the output of kldstat. + atf_check -o save:kldstat kldstat + atf_check diff kldstat out +} + +atf_init_test_cases() +{ + atf_add_test_case livedump_kldstat +} diff --git a/sbin/savecore/tests/log_test.sh b/sbin/savecore/tests/log_test.sh new file mode 100644 index 000000000000..9b006a2f2644 --- /dev/null +++ b/sbin/savecore/tests/log_test.sh @@ -0,0 +1,25 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2025 Stéphane Rochoy <stephane.rochoy@stormshield.eu> +# + +atf_test_case log_perror +log_perror_head() +{ + atf_set "descr" "Test LOG_PERROR behavior" +} +log_perror_body() +{ + atf_check -s exit:1 \ + -o ignore \ + -e save:savecore.err \ + savecore -vC /dev/missing + grep -qE 'savecore [0-9]+ - - /dev/missing: No such file or directory' savecore.err \ + || atf_fail "missing/invalid error output" +} + +atf_init_test_cases() +{ + atf_add_test_case log_perror +} diff --git a/sbin/setkey/parse.y b/sbin/setkey/parse.y index 448a8ee5278c..a7bcd2d8dafc 100644 --- a/sbin/setkey/parse.y +++ b/sbin/setkey/parse.y @@ -46,6 +46,7 @@ #include <string.h> #include <unistd.h> +#include <stdbool.h> #include <stdio.h> #include <stdint.h> #include <netdb.h> @@ -68,6 +69,8 @@ u_int p_natt_type; struct addrinfo *p_natt_oai, *p_natt_oar; int p_natt_sport, p_natt_dport; int p_natt_fraglen; +bool esn; +vchar_t p_hwif; static int p_aiflags = 0, p_aifamily = PF_UNSPEC; @@ -115,6 +118,7 @@ extern void yyerror(const char *); %token SPDADD SPDDELETE SPDDUMP SPDFLUSH %token F_POLICY PL_REQUESTS %token F_AIFLAGS F_NATT F_NATT_MTU +%token F_ESN F_HWIF %token TAGGED %type <num> prefix protocol_spec upper_spec @@ -539,12 +543,21 @@ extension { p_natt_fraglen = $2; } + | F_ESN + { + esn = true; + p_ext |= SADB_X_SAFLAGS_ESN; + } + | F_HWIF STRING + { + p_hwif = $2; + } ; /* definition about command for SPD management */ /* spdadd */ spdadd_command - : SPDADD ipaddropts STRING prefix portstr STRING prefix portstr upper_spec upper_misc_spec policy_spec EOT + : SPDADD ipaddropts STRING prefix portstr STRING prefix portstr upper_spec upper_misc_spec policy_spec spd_hwif EOT { int status; struct addrinfo *src, *dst; @@ -648,6 +661,14 @@ ipaddropts | ipaddropts ipaddropt ; +spd_hwif + : + | F_HWIF STRING + { + p_hwif = $2; + } + ; + ipaddropt : F_AIFLAGS { @@ -831,6 +852,7 @@ setkeymsg_spdaddr(unsigned type, unsigned upper, vchar_t *policy, char buf[BUFSIZ]; int l, l0; struct sadb_address m_addr; + struct sadb_x_if_hw_offl m_if_hw; struct addrinfo *s, *d; int n; int plen; @@ -849,6 +871,20 @@ setkeymsg_spdaddr(unsigned type, unsigned upper, vchar_t *policy, memcpy(buf + l, policy->buf, policy->len); l += policy->len; + if (p_hwif.len != 0) { + l0 = sizeof(struct sadb_x_if_hw_offl); + m_if_hw.sadb_x_if_hw_offl_len = PFKEY_UNIT64(l0); + m_if_hw.sadb_x_if_hw_offl_exttype = SADB_X_EXT_IF_HW_OFFL; + m_if_hw.sadb_x_if_hw_offl_flags = 0; + memset(&m_if_hw.sadb_x_if_hw_offl_if[0], 0, + sizeof(m_if_hw.sadb_x_if_hw_offl_if)); + strlcpy(&m_if_hw.sadb_x_if_hw_offl_if[0], p_hwif.buf, + sizeof(m_if_hw.sadb_x_if_hw_offl_if)); + + memcpy(buf + l, &m_if_hw, l0); + l += l0; + } + l0 = l; n = 0; @@ -1040,6 +1076,7 @@ setkeymsg_add(unsigned type, unsigned satype, struct addrinfo *srcs, struct sadb_x_nat_t_type m_natt_type; struct sadb_x_nat_t_port m_natt_port; struct sadb_x_nat_t_frag m_natt_frag; + struct sadb_x_if_hw_offl m_if_hw; int n; int plen; struct sockaddr *sa; @@ -1256,6 +1293,20 @@ setkeymsg_add(unsigned type, unsigned satype, struct addrinfo *srcs, } } + if (p_hwif.len != 0) { + len = sizeof(struct sadb_x_if_hw_offl); + m_if_hw.sadb_x_if_hw_offl_len = PFKEY_UNIT64(len); + m_if_hw.sadb_x_if_hw_offl_exttype = SADB_X_EXT_IF_HW_OFFL; + m_if_hw.sadb_x_if_hw_offl_flags = 0; + memset(&m_if_hw.sadb_x_if_hw_offl_if[0], 0, + sizeof(m_if_hw.sadb_x_if_hw_offl_if)); + strlcpy(&m_if_hw.sadb_x_if_hw_offl_if[0], p_hwif.buf, + sizeof(m_if_hw.sadb_x_if_hw_offl_if)); + + memcpy(buf + l, &m_if_hw, len); + l += len; + } + if (n == 0) return -1; else @@ -1355,6 +1406,10 @@ parse_init(void) p_natt_oai = p_natt_oar = NULL; p_natt_sport = p_natt_dport = 0; p_natt_fraglen = -1; + + esn = false; + p_hwif.len = 0; + p_hwif.buf = NULL; } void diff --git a/sbin/setkey/setkey.8 b/sbin/setkey/setkey.8 index 7dab0f622efd..e3bcf10b9a9a 100644 --- a/sbin/setkey/setkey.8 +++ b/sbin/setkey/setkey.8 @@ -27,7 +27,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 27, 2023 +.Dd August 25, 2024 .Dt SETKEY 8 .Os .\" @@ -230,7 +230,7 @@ IPv4/v6 address. The .Nm utility -can resolve a FQDN into numeric addresses. +can resolve an FQDN into numeric addresses. If the FQDN resolves into multiple addresses, .Nm will install multiple SAD/SPD entries into the kernel @@ -341,6 +341,13 @@ symbols are part of the syntax for the ports specification, not indication of the optional components. .It Fl natt_mtu Ar fragsize Configure NAT-T fragment size. +.It Fl esn +Enable Extended Sequence Number extension for this SA. +.It Fl hwif Ar ifname +Request hardware offload to the specified interface +.Ar ifname +(only). +By default offload occurs to all capable interfaces. .El .\" .Pp @@ -472,27 +479,43 @@ is expressed in one of the following three formats: .Xc .El .Pp -The direction of a policy must be specified as -one of: +.Bl -tag -compact -width "policy level" +.It Ar direction +The +.Ar direction +of a policy must be specified as one of: .Li out or .Li in . +.It Ar policy level The direction is followed by one of the following policy levels: .Li discard , .Li none , or .Li ipsec . +.Bl -compact -bullet +.It The .Li discard -policylevel means that packets matching the supplied indices will -be discarded while +policy level means that packets matching the supplied indices will +be discarded. +.It +The .Li none -means that IPsec operations will not take place on the packet and +policy level means that IPsec operations will not take place on +the packet. +.It +The .Li ipsec -means that IPsec operation will take place onto the packet. +policy level means that IPsec operation will take place onto +the packet. +.El +.It Ar protocol/mode/src-dst/level The .Ar protocol/mode/src-dst/level statement gives the rule for how to process the packet. +.Bl -compact -bullet +.It The .Ar protocol is specified as @@ -500,12 +523,15 @@ is specified as .Li esp or .Li ipcomp . +.It The .Ar mode is either .Li transport or .Li tunnel . +.El +.Pp If .Ar mode is @@ -517,6 +543,7 @@ and with a dash, .Sq - , between the addresses. +.Pp If .Ar mode is @@ -526,6 +553,7 @@ both and .Ar dst can be omitted. +.Pp The .Ar level is one of the following: @@ -534,25 +562,32 @@ or .Li unique . If the SA is not available in every level, the kernel will request the SA from the key exchange daemon. +.Pp +.Bl -compact -bullet +.It A value of .Li default tells the kernel to use the system wide default protocol e.g.,\& the one from the .Li esp_trans_deflev sysctl variable, when the kernel processes the packet. +.It A value of .Li use means that the kernel will use an SA if it is available, otherwise the kernel will pass the packet as it would normally. +.It A value of .Li require means that an SA is required whenever the kernel sends a packet matched that matches the policy. +.It The .Li unique level is the same as .Li require but, in addition, it allows the policy to bind with the unique out-bound SA. +.Pp For example, if you specify the policy level .Li unique , .Xr racoon 8 Pq Pa ports/security/ipsec-tools @@ -570,6 +605,8 @@ must be between 1 and 32767, which corresponds to .Ar extensions Fl u of manual SA configuration. +.El +.El .Pp When you want to use an SA bundle, you can define multiple rules. For diff --git a/sbin/setkey/setkey.c b/sbin/setkey/setkey.c index d9b905ad845e..f7268d8f1e10 100644 --- a/sbin/setkey/setkey.c +++ b/sbin/setkey/setkey.c @@ -502,7 +502,7 @@ static const char *ipproto[] = { }; #define STR_OR_ID(x, tab) \ - (((x) < sizeof(tab)/sizeof(tab[0]) && tab[(x)]) ? tab[(x)] : numstr(x)) + (((x) < nitems(tab) && tab[(x)]) ? tab[(x)] : numstr(x)) const char * numstr(int x) diff --git a/sbin/setkey/token.l b/sbin/setkey/token.l index 054a57ef1015..65756f0fd12c 100644 --- a/sbin/setkey/token.l +++ b/sbin/setkey/token.l @@ -187,6 +187,8 @@ nocyclic-seq { return(NOCYCLICSEQ); } {hyphen}ls { return(F_LIFETIME_SOFT); } {hyphen}natt { return(F_NATT); } {hyphen}natt_mtu { return(F_NATT_MTU); } +{hyphen}esn { return(F_ESN); } +{hyphen}hwif { return(F_HWIF); } /* ... */ any { return(ANY); } diff --git a/sbin/shutdown/Makefile b/sbin/shutdown/Makefile index 32c9d6979934..6c5d6e4487e2 100644 --- a/sbin/shutdown/Makefile +++ b/sbin/shutdown/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.1 (Berkeley) 6/5/93 - PACKAGE=runtime PROG= shutdown MAN= shutdown.8 diff --git a/sbin/shutdown/shutdown.8 b/sbin/shutdown/shutdown.8 index 7f60f22cc0e3..ed44ac36aef6 100644 --- a/sbin/shutdown/shutdown.8 +++ b/sbin/shutdown/shutdown.8 @@ -25,9 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)shutdown.8 8.2 (Berkeley) 4/27/95 -.\" -.Dd November 7, 2022 +.Dd August 4, 2024 .Dt SHUTDOWN 8 .Os .Sh NAME @@ -38,13 +36,14 @@ .Nm .Op Fl .Oo -.Fl c | Fl h | Fl p | +.Fl c | Fl f | Fl h | Fl p | .Fl r | Fl k .Oc .Oo .Fl o .Op Fl n .Oc +.Op Fl q .Ar time .Op Ar warning-message ... .Nm poweroff @@ -72,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 . @@ -115,6 +120,12 @@ to or .Xr reboot 8 . This option should probably not be used. +.It Fl q +Shut down quietly. +Suppress the warning message to all logged in users about system shutdown. +It is an error to supply a +.Ar warning-message +when warnings are suppressed. .It Ar time .Ar Time is the time at which @@ -201,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 @@ -214,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 3864e44025eb..762b23ab6bd9 100644 --- a/sbin/shutdown/shutdown.c +++ b/sbin/shutdown/shutdown.c @@ -29,21 +29,10 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1988, 1990, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)shutdown.c 8.4 (Berkeley) 4/28/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/boottrace.h> #include <sys/resource.h> +#include <sys/stat.h> #include <sys/syslog.h> #include <sys/time.h> @@ -55,6 +44,7 @@ static char sccsid[] = "@(#)shutdown.c 8.4 (Berkeley) 4/28/95"; #include <pwd.h> #include <setjmp.h> #include <signal.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -90,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; @@ -98,7 +89,7 @@ static void badtime(void); static void die_you_gravy_sucking_pig_dog(void); static void finish(int); static void getoffset(char *); -static void loop(void); +static void loop(bool); static void nolog(void); static void timeout(int); static void timewarn(int); @@ -111,13 +102,16 @@ main(int argc, char **argv) { char *p, *endp; struct passwd *pw; + struct stat st; int arglen, ch, len, readstdin; + bool dowarn; #ifndef DEBUG if (geteuid()) errx(1, "NOT super-user"); #endif + dowarn = true; nosync = NULL; readstdin = 0; @@ -142,7 +136,7 @@ main(int argc, char **argv) goto poweroff; } - while ((ch = getopt(argc, argv, "-chknopr")) != -1) + while ((ch = getopt(argc, argv, "-cfhknopqr")) != -1) switch (ch) { case '-': readstdin = 1; @@ -150,6 +144,9 @@ main(int argc, char **argv) case 'c': docycle = 1; break; + case 'f': + ign_noshutdown = 1; + break; case 'h': dohalt = 1; break; @@ -165,6 +162,9 @@ main(int argc, char **argv) case 'p': dopower = 1; break; + case 'q': + dowarn = false; + break; case 'r': doreboot = 1; break; @@ -190,6 +190,8 @@ main(int argc, char **argv) getoffset(*argv++); poweroff: + if (!dowarn && *argv != NULL) + usage("warning-message supplied but suppressed with -q"); if (*argv) { for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { arglen = strlen(*argv); @@ -220,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)); @@ -247,12 +255,12 @@ poweroff: setsid(); #endif openlog("shutdown", LOG_CONS, LOG_AUTH); - loop(); + loop(dowarn); return(0); } static void -loop(void) +loop(bool dowarn) { struct interval *tp; u_int sltime; @@ -275,13 +283,14 @@ loop(void) * the next wait time. */ if ((sltime = offset - tp->timeleft)) { - if (sltime > (u_int)(tp->timetowait / 5)) + if (dowarn && sltime > (u_int)(tp->timetowait / 5)) timewarn(offset); (void)sleep(sltime); } } for (;; ++tp) { - timewarn(tp->timeleft); + if (dowarn) + timewarn(tp->timeleft); if (!logged && tp->timeleft <= NOLOG_TIME) { logged = 1; nolog(); @@ -596,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]] 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 4cca18367c1e..0c034338a533 100644 --- a/sbin/swapon/Makefile +++ b/sbin/swapon/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.1 (Berkeley) 6/5/93 - PACKAGE=runtime PROG= swapon MAN= swapon.8 @@ -10,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.8 b/sbin/swapon/swapon.8 index 64ad9edca776..f5534c06eb9f 100644 --- a/sbin/swapon/swapon.8 +++ b/sbin/swapon/swapon.8 @@ -25,8 +25,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)swapon.8 8.1 (Berkeley) 6/5/93 -.\" .Dd November 29, 2021 .Dt SWAPON 8 .Os diff --git a/sbin/swapon/swapon.c b/sbin/swapon/swapon.c index b9b865c0fc09..3dff4df5e63f 100644 --- a/sbin/swapon/swapon.c +++ b/sbin/swapon/swapon.c @@ -29,18 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1980, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)swapon.c 8.1 (Berkeley) 6/5/93"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/disk.h> #include <sys/disklabel.h> @@ -66,9 +54,10 @@ static char sccsid[] = "@(#)swapon.c 8.1 (Berkeley) 6/5/93"; #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_gbde(const char *, int); static const char *swap_on_off_geli(const char *, char *, int); static const char *swap_on_off_md(const char *, char *, int); static const char *swap_on_off_sfile(const char *, int); @@ -243,21 +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_BDE. */ - if (fnmatch("*.bde", base, 0) == 0) { - free(basebuf); - return (swap_on_off_gbde(name, doingall)); - } - /* 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)); } @@ -281,59 +267,6 @@ swap_basename(const char *name) return (dname); } -static const char * -swap_on_off_gbde(const char *name, int doingall) -{ - const char *ret; - char pass[64 * 2 + 1]; - unsigned char bpass[64]; - char *dname; - int i, error; - - dname = swap_basename(name); - if (dname == NULL) - return (NULL); - - if (which_prog == SWAPON) { - arc4random_buf(bpass, sizeof(bpass)); - for (i = 0; i < (int)sizeof(bpass); i++) - sprintf(&pass[2 * i], "%02x", bpass[i]); - pass[sizeof(pass) - 1] = '\0'; - - error = run_cmd(NULL, "%s init %s -P %s", _PATH_GBDE, - dname, pass); - if (error) { - /* bde device found. Ignore it. */ - free(dname); - if (qflag == 0) - warnx("%s: Device already in use", name); - return (NULL); - } - error = run_cmd(NULL, "%s attach %s -p %s", _PATH_GBDE, - dname, pass); - free(dname); - if (error) { - warnx("gbde (attach) error: %s", name); - return (NULL); - } - } - - ret = swap_on_off_sfile(name, doingall); - - if (which_prog == SWAPOFF) { - error = run_cmd(NULL, "%s detach %s", _PATH_GBDE, dname); - free(dname); - if (error) { - /* bde device not found. Ignore it. */ - if (qflag == 0) - warnx("%s: Device not found", name); - return (NULL); - } - } - - return (ret); -} - /* Build geli(8) arguments from mntops */ static char * swap_on_geli_args(const char *mntops) @@ -399,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) { @@ -488,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) { @@ -645,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/Makefile b/sbin/sysctl/Makefile index e5455568223c..99074e47964d 100644 --- a/sbin/sysctl/Makefile +++ b/sbin/sysctl/Makefile @@ -1,5 +1,3 @@ -# @(#)Makefile 8.1 (Berkeley) 6/6/93 - .include <src.opts.mk> PACKAGE=runtime @@ -8,6 +6,11 @@ PROG= sysctl WARNS?= 3 MAN= sysctl.8 +.if ${MK_JAIL} != "no" && !defined(RESCUE) +CFLAGS+= -DJAIL +LIBADD+= jail +.endif + HAS_TESTS= SUBDIR.${MK_TESTS}+= tests diff --git a/sbin/sysctl/sysctl.8 b/sbin/sysctl/sysctl.8 index 3e995e40131b..e0e35f075a78 100644 --- a/sbin/sysctl/sysctl.8 +++ b/sbin/sysctl/sysctl.8 @@ -1,3 +1,6 @@ +.\"- +.\" SPDX-License-Identifier: BSD-3-Clause +.\" .\" Copyright (c) 1993 .\" The Regents of the University of California. All rights reserved. .\" @@ -25,9 +28,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" From: @(#)sysctl.8 8.1 (Berkeley) 6/6/93 -.\" -.Dd December 24, 2022 +.Dd January 31, 2025 .Dt SYSCTL 8 .Os .Sh NAME @@ -35,13 +36,15 @@ .Nd get or set kernel state .Sh SYNOPSIS .Nm -.Op Fl bdeFhilNnoTtqWx +.Op Fl j Ar jail +.Op Fl bdeFhiJlNnoqTtVWx .Op Fl B Ar bufsize .Op Fl f Ar filename .Ar name Ns Op = Ns Ar value Ns Op , Ns Ar value .Ar ... .Nm -.Op Fl bdeFhlNnoTtqWx +.Op Fl j Ar jail +.Op Fl bdeFhJlNnoqTtVWx .Op Fl B Ar bufsize .Fl a .Sh DESCRIPTION @@ -52,11 +55,10 @@ privilege to set kernel state. The state to be retrieved or set is described using a .Dq Management Information Base .Pq Dq MIB -style name, described as a dotted set of -components. +style name, described as a dotted set of components. .Pp The following options are available: -.Bl -tag -width indent +.Bl -tag -width "-f filename" .It Fl A Equivalent to .Fl o a @@ -68,10 +70,6 @@ opaque or excluded from listing via the flag. This option is ignored if one or more variable names are specified on the command line. -.It Fl b -Force the value of the variable(s) to be output in raw, binary format. -No names are printed and no terminating newlines are output. -This is mostly useful with a single variable. .It Fl B Ar bufsize Set the buffer size to read from the .Nm @@ -81,6 +79,10 @@ This is necessary for a .Nm that has variable length, and the probe value of 0 is a valid length, such as .Va kern.arandom . +.It Fl b +Force the value of the variable(s) to be output in raw, binary format. +No names are printed and no terminating newlines are output. +This is mostly useful with a single variable. .It Fl d Print the description of the variable instead of its value. .It Fl e @@ -94,15 +96,19 @@ This option is ignored if either or .Fl n is specified, or a variable is being set. +.It Fl F +Print the format of the variable. +This is additional information to describe the type of the variable and +most useful with struct types such as clockinfo, timeval, and loadavg. .It Fl f Ar filename Specify a file which contains a pair of name and value in each line. .Nm reads and processes the specified file first and then processes the name and value pairs in the command line argument. -.It Fl F -Print the format of the variable. -This is additional information to describe the type of the variable and -most useful with struct types such as clockinfo, timeval, and loadavg. +Note that when the +.Fl j Ar jail +option is specified, the file will be opened before attaching to the jail and +then be processed inside the jail. .It Fl h Format output for human, rather than machine, readability. .It Fl i @@ -111,6 +117,12 @@ The purpose is to make use of .Nm for collecting data from a variety of machines (not all of which are necessarily running exactly the same software) easier. +.It Fl J +Display only jail prision sysctl variables (CTLFLAG_PRISON). +.It Fl j Ar jail +Perform the actions inside the +.Ar jail +(by jail id or jail name). .It Fl l Show the length of variables along with their values. This option cannot be combined with the @@ -153,6 +165,8 @@ to standard error. Display only variables that are settable via loader (CTLFLAG_TUN). .It Fl t Print the type of the variable. +.It Fl V +Display only VNET sysctl variables (CTLFLAG_VNET). .It Fl W Display only writable variables that are not statistical. Useful for determining the set of runtime tunable sysctls. @@ -194,7 +208,9 @@ for more information on which tunables are available and how to set them. .Pp The string and integer information is summarized below. For a detailed description of these variables see -.Xr sysctl 3 . +.Xr sysctl 3 +and +.Xr security 7 . .Pp The changeable column indicates whether a process with appropriate privilege can change the value. @@ -202,75 +218,77 @@ String and integer values can be set using .Nm . .Bl -column security.bsd.unprivileged_read_msgbuf integerxxx .It Sy "Name Type Changeable" -.It "kern.ostype string no" -.It "kern.osrelease string no" -.It "kern.osrevision integer no" -.It "kern.version string no" -.It "kern.maxvnodes integer yes" -.It "kern.maxproc integer no" -.It "kern.maxprocperuid integer yes" -.It "kern.maxfiles integer yes" -.It "kern.maxfilesperproc integer yes" -.It "kern.argmax integer no" -.It "kern.securelevel integer raise only" -.It "kern.hostname string yes" -.It "kern.hostid integer yes" -.It "kern.clockrate struct no" -.It "kern.posix1version integer no" -.It "kern.ngroups integer no" -.It "kern.job_control integer no" -.It "kern.saved_ids integer no" -.It "kern.boottime struct no" -.It "kern.domainname string yes" -.It "kern.filedelay integer yes" -.It "kern.dirdelay integer yes" -.It "kern.metadelay integer yes" -.It "kern.osreldate integer no" -.It "kern.bootfile string yes" -.It "kern.corefile string yes" -.It "kern.logsigexit integer yes" -.It "security.bsd.suser_enabled integer yes" -.It "security.bsd.see_other_uids integer yes" -.It "security.bsd.unprivileged_proc_debug integer yes" -.It "security.bsd.unprivileged_read_msgbuf integer yes" -.It "vm.loadavg struct no" -.It "hw.machine string no" -.It "hw.model string no" -.It "hw.ncpu integer no" -.It "hw.byteorder integer no" -.It "hw.physmem integer no" -.It "hw.usermem integer no" -.It "hw.pagesize integer no" -.It "hw.floatingpoint integer no" -.It "hw.machine_arch string no" -.It "hw.realmem integer no" -.It "machdep.adjkerntz integer yes" -.It "machdep.disable_rtc_set integer yes" -.It "machdep.guessed_bootdev string no" -.It "user.cs_path string no" -.It "user.bc_base_max integer no" -.It "user.bc_dim_max integer no" -.It "user.bc_scale_max integer no" -.It "user.bc_string_max integer no" -.It "user.coll_weights_max integer no" -.It "user.expr_nest_max integer no" -.It "user.line_max integer no" -.It "user.re_dup_max integer no" -.It "user.posix2_version integer no" -.It "user.posix2_c_bind integer no" -.It "user.posix2_c_dev integer no" -.It "user.posix2_char_term integer no" -.It "user.posix2_fort_dev integer no" -.It "user.posix2_fort_run integer no" -.It "user.posix2_localedef integer no" -.It "user.posix2_sw_dev integer no" -.It "user.posix2_upe integer no" -.It "user.stream_max integer no" -.It "user.tzname_max integer no" -.It "user.localbase string no" +.It Va "kern.ostype string no" +.It Va "kern.osrelease string no" +.It Va "kern.osrevision integer no" +.It Va "kern.version string no" +.It Va "kern.maxvnodes integer yes" +.It Va "kern.maxproc integer no" +.It Va "kern.maxprocperuid integer yes" +.It Va "kern.maxfiles integer yes" +.It Va "kern.maxfilesperproc integer yes" +.It Va "kern.argmax integer no" +.It Va "kern.securelevel integer raise only" +.It Va "kern.hostname string yes" +.It Va "kern.hostid integer yes" +.It Va "kern.clockrate struct no" +.It Va "kern.posix1version integer no" +.It Va "kern.ngroups integer no" +.It Va "kern.job_control integer no" +.It Va "kern.saved_ids integer no" +.It Va "kern.boottime struct no" +.It Va "kern.domainname string yes" +.It Va "kern.filedelay integer yes" +.It Va "kern.dirdelay integer yes" +.It Va "kern.metadelay integer yes" +.It Va "kern.osreldate integer no" +.It Va "kern.bootfile string yes" +.It Va "kern.corefile string yes" +.It Va "kern.logsigexit integer yes" +.It Va "security.bsd.suser_enabled integer yes" +.It Va "security.bsd.see_other_uids integer yes" +.It Va "security.bsd.see_other_gids integer yes" +.It Va "security.bsd.see_jail_proc integer yes" +.It Va "security.bsd.unprivileged_proc_debug integer yes" +.It Va "security.bsd.unprivileged_read_msgbuf integer yes" +.It Va "vm.loadavg struct no" +.It Va "hw.machine string no" +.It Va "hw.model string no" +.It Va "hw.ncpu integer no" +.It Va "hw.byteorder integer no" +.It Va "hw.physmem integer no" +.It Va "hw.usermem integer no" +.It Va "hw.pagesize integer no" +.It Va "hw.floatingpoint integer no" +.It Va "hw.machine_arch string no" +.It Va "hw.realmem integer no" +.It Va "machdep.adjkerntz integer yes" +.It Va "machdep.disable_rtc_set integer yes" +.It Va "machdep.guessed_bootdev string no" +.It Va "user.cs_path string no" +.It Va "user.bc_base_max integer no" +.It Va "user.bc_dim_max integer no" +.It Va "user.bc_scale_max integer no" +.It Va "user.bc_string_max integer no" +.It Va "user.coll_weights_max integer no" +.It Va "user.expr_nest_max integer no" +.It Va "user.line_max integer no" +.It Va "user.re_dup_max integer no" +.It Va "user.posix2_version integer no" +.It Va "user.posix2_c_bind integer no" +.It Va "user.posix2_c_dev integer no" +.It Va "user.posix2_char_term integer no" +.It Va "user.posix2_fort_dev integer no" +.It Va "user.posix2_fort_run integer no" +.It Va "user.posix2_localedef integer no" +.It Va "user.posix2_sw_dev integer no" +.It Va "user.posix2_upe integer no" +.It Va "user.stream_max integer no" +.It Va "user.tzname_max integer no" +.It Va "user.localbase string no" .El .Sh FILES -.Bl -tag -width ".In netinet/icmp_var.h" -compact +.Bl -tag -width "<netinet/icmp_var.h>" -compact .It In sys/sysctl.h definitions for top level identifiers, second level kernel and hardware identifiers, and user level identifiers @@ -294,20 +312,20 @@ definitions for fourth level UDP identifiers For example, to retrieve the maximum number of processes allowed in the system, one would use the following request: .Pp -.Dl "sysctl kern.maxproc" +.Dl Va "sysctl kern.maxproc" .Pp To set the maximum number of processes allowed per uid to 1000, one would use the following request: .Pp -.Dl "sysctl kern.maxprocperuid=1000" +.Dl Va "sysctl kern.maxprocperuid=1000" .Pp Information about the system clock rate may be obtained with: .Pp -.Dl "sysctl kern.clockrate" +.Dl Va "sysctl kern.clockrate" .Pp Information about the load average history may be obtained with: .Pp -.Dl "sysctl vm.loadavg" +.Dl Va "sysctl vm.loadavg" .Pp More variables than these exist, and the best and likely only place to search for their deeper meaning is undoubtedly the source where @@ -320,7 +338,9 @@ option has been deprecated and is silently ignored. .Xr sysctl 3 , .Xr loader.conf 5 , .Xr sysctl.conf 5 , -.Xr loader 8 +.Xr security 7 , +.Xr loader 8 , +.Xr jail 8 .Sh HISTORY A .Nm diff --git a/sbin/sysctl/sysctl.c b/sbin/sysctl/sysctl.c index fe1a53a9d645..302dc6865123 100644 --- a/sbin/sysctl/sysctl.c +++ b/sbin/sysctl/sysctl.c @@ -29,11 +29,13 @@ * SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/param.h> #include <sys/time.h> #include <sys/resource.h> #include <sys/stat.h> +#ifdef JAIL +#include <sys/jail.h> +#endif #include <sys/sysctl.h> #include <sys/vmmeter.h> #include <dev/evdev/input.h> @@ -52,6 +54,9 @@ #include <err.h> #include <errno.h> #include <inttypes.h> +#ifdef JAIL +#include <jail.h> +#endif #include <locale.h> #include <stdbool.h> #include <stdio.h> @@ -60,14 +65,18 @@ #include <sysexits.h> #include <unistd.h> +#ifdef JAIL +static const char *jailname; +#endif static const char *conffile; static int aflag, bflag, Bflag, dflag, eflag, hflag, iflag; static int Nflag, nflag, oflag, qflag, tflag, Tflag, Wflag, xflag; -static bool Fflag, lflag; +static bool Fflag, Jflag, lflag, Vflag; +static void attach_jail(void); static int oidfmt(int *, int, char *, u_int *); -static int parsefile(const char *); +static int parsefile(FILE *); static int parse(const char *, int); static int show_var(int *, int, bool); static int sysctl_all(int *, int); @@ -122,8 +131,8 @@ usage(void) { (void)fprintf(stderr, "%s\n%s\n", - "usage: sysctl [-bdeFhilNnoqTtWx] [ -B <bufsize> ] [-f filename] name[=value] ...", - " sysctl [-bdeFhlNnoqTtWx] [ -B <bufsize> ] -a"); + "usage: sysctl [-j jail] [-bdeFhiJlNnoqTtVWx] [ -B <bufsize> ] [-f filename] name[=value] ...", + " sysctl [-j jail] [-bdeFhJlNnoqTtVWx] [ -B <bufsize> ] -a"); exit(1); } @@ -132,12 +141,13 @@ main(int argc, char **argv) { int ch; int warncount = 0; + FILE *file = NULL; setlocale(LC_NUMERIC, ""); setbuf(stdout,0); setbuf(stderr,0); - while ((ch = getopt(argc, argv, "AabB:def:FhilNnoqtTwWxX")) != -1) { + while ((ch = getopt(argc, argv, "AaB:bdeFf:hiJj:lNnoqTtVWwXx")) != -1) { switch (ch) { case 'A': /* compatibility */ @@ -146,30 +156,41 @@ main(int argc, char **argv) case 'a': aflag = 1; break; - case 'b': - bflag = 1; - break; case 'B': Bflag = strtol(optarg, NULL, 0); break; + case 'b': + bflag = 1; + break; case 'd': dflag = 1; break; case 'e': eflag = 1; break; - case 'f': - conffile = optarg; - break; case 'F': Fflag = true; break; + case 'f': + conffile = optarg; + break; case 'h': hflag = 1; break; case 'i': iflag = 1; break; + case 'J': + Jflag = true; + break; + case 'j': +#ifdef JAIL + if ((jailname = optarg) == NULL) + usage(); +#else + errx(1, "not built with jail support"); +#endif + break; case 'l': lflag = true; break; @@ -185,19 +206,22 @@ main(int argc, char **argv) case 'q': qflag = 1; break; + case 'T': + Tflag = 1; + break; case 't': tflag = 1; break; - case 'T': - Tflag = 1; + case 'V': + Vflag = true; + break; + case 'W': + Wflag = 1; break; case 'w': /* compatibility */ /* ignored */ break; - case 'W': - Wflag = 1; - break; case 'X': /* compatibility */ aflag = xflag = 1; @@ -212,17 +236,27 @@ main(int argc, char **argv) argc -= optind; argv += optind; - /* Nflag is name only and doesn't make sense to combind with these */ + /* Nflag is name only and doesn't make sense to combine with these */ /* TODO: few other combinations do not make sense but come back later */ if (Nflag && (lflag || nflag)) usage(); - if (aflag && argc == 0) + if (aflag && argc == 0) { + attach_jail(); exit(sysctl_all(NULL, 0)); + } if (argc == 0 && conffile == NULL) usage(); - if (conffile != NULL) - warncount += parsefile(conffile); + if (conffile != NULL) { + file = fopen(conffile, "r"); + if (file == NULL) + err(EX_NOINPUT, "%s", conffile); + } + attach_jail(); + if (file != NULL) { + warncount += parsefile(file); + fclose(file); + } while (argc-- > 0) warncount += parse(*argv++, 0); @@ -230,6 +264,23 @@ main(int argc, char **argv) return (warncount); } +static void +attach_jail(void) +{ +#ifdef JAIL + int jid; + + if (jailname == NULL) + return; + + jid = jail_getid(jailname); + if (jid == -1) + errx(1, "jail not found"); + if (jail_attach(jid) != 0) + errx(1, "cannot attach to jail"); +#endif +} + /* * Parse a single numeric value, append it to 'newbuf', and update * 'newsize'. Returns true if the value was parsed and false if the @@ -563,15 +614,11 @@ parse(const char *string, int lineno) } static int -parsefile(const char *filename) +parsefile(FILE *file) { - FILE *file; char line[BUFSIZ], *p, *pq, *pdq; int warncount = 0, lineno = 0; - file = fopen(filename, "r"); - if (file == NULL) - err(EX_NOINPUT, "%s", filename); while (fgets(line, sizeof(line), file) != NULL) { lineno++; p = line; @@ -607,7 +654,6 @@ parsefile(const char *filename) else warncount += parse(p, lineno); } - fclose(file); return (warncount); } @@ -998,8 +1044,10 @@ show_info(char *name, const char *sep, int ctltype, char *fmt, int *qoid, int nl bzero(buf, BUFSIZ); j = sizeof(buf); i = sysctl(qoid, nlen + 2, buf, &j, 0, 0); - if (i < 0) + if (i < 0) { + putchar('\n'); return (1); + } fputs(buf, stdout); return (error); } @@ -1047,10 +1095,18 @@ show_var(int *oid, int nlen, bool honor_skip) if (Wflag && ((kind & CTLFLAG_WR) == 0 || (kind & CTLFLAG_STATS) != 0)) return (1); + /* if Jflag then only list sysctls that are prison variables. */ + if (Jflag && (kind & CTLFLAG_PRISON) == 0) + return (1); + /* if Tflag then only list sysctls that are tuneables. */ if (Tflag && (kind & CTLFLAG_TUN) == 0) return (1); + /* if Vflag then only list sysctls that are vnet variables. */ + if (Vflag && (kind & CTLFLAG_VNET) == 0) + return (1); + if (Nflag) { printf("%s", name); return (0); diff --git a/sbin/sysctl/tests/Makefile b/sbin/sysctl/tests/Makefile index c3f117dcff1f..b209b2448241 100644 --- a/sbin/sysctl/tests/Makefile +++ b/sbin/sysctl/tests/Makefile @@ -1,4 +1,3 @@ - ATF_TESTS_SH= sysctl_test .include <bsd.test.mk> diff --git a/sbin/sysctl/tests/sysctl_test.sh b/sbin/sysctl/tests/sysctl_test.sh index e932626a9f14..b4cc7180a0f9 100644 --- a/sbin/sysctl/tests/sysctl_test.sh +++ b/sbin/sysctl/tests/sysctl_test.sh @@ -1,3 +1,6 @@ +# +# SPDX-License-Identifier: BSD-2-Clause +# # Copyright (c) 2022 Yoshihiro Ota <ota@j.email.ne.jp> # # Redistribution and use in source and binary forms, with or without @@ -20,12 +23,65 @@ # 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. +# sysctl_name="kern.ostype" sysctl_value="FreeBSD" sysctl_type="string" sysctl_description="Operating system type" +atf_test_case sysctl_aflag +sysctl_aflag_head() +{ + atf_set "descr" "Exercise all sysctl handlers" +} +sysctl_aflag_body() +{ + # Avoid using atf_check here since sysctl -ao generates tons of + # 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 + atf_fail "sysctl -ao printed to stderr" + fi +} + + +atf_test_case sysctl_aflag_jail +sysctl_aflag_jail_head() +{ + atf_set "descr" "Exercise all sysctl handlers in a jail" + atf_set "require.user" "root" +} +sysctl_aflag_jail_body() +{ + local jail + + jail=sysctl_test_aflag_jail + + # Avoid using atf_check here since sysctl -ao generates tons of + # output and it would all otherwise be saved. + jail -c name=$jail command=sysctl -ao >/dev/null 2>stderr + if [ $? -ne 0 ]; then + atf_fail "sysctl -ao failed" + elif [ -s stderr ]; then + cat stderr + atf_fail "sysctl -ao printed to stderr" + fi + + jail -c name=$jail vnet command=sysctl -ao >/dev/null 2>stderr + if [ $? -ne 0 ]; then + atf_fail "sysctl -ao failed" + elif [ -s stderr ]; then + cat stderr + atf_fail "sysctl -ao printed to stderr" + fi +} + + atf_test_case sysctl_by_name sysctl_by_name_head() { @@ -106,6 +162,8 @@ sysctl_nflag_tflag_dflag_body() atf_init_test_cases() { + atf_add_test_case sysctl_aflag + atf_add_test_case sysctl_aflag_jail atf_add_test_case sysctl_by_name atf_add_test_case sysctl_nflag atf_add_test_case sysctl_eflag diff --git a/sbin/tests/Makefile b/sbin/tests/Makefile index b8996030aafc..29b1b564beca 100644 --- a/sbin/tests/Makefile +++ b/sbin/tests/Makefile @@ -1,4 +1,3 @@ - .PATH: ${SRCTOP}/tests KYUAFILE= yes diff --git a/sbin/tunefs/Makefile b/sbin/tunefs/Makefile index e78c3fe671c2..4ea2219c8e03 100644 --- a/sbin/tunefs/Makefile +++ b/sbin/tunefs/Makefile @@ -1,15 +1,8 @@ -# @(#)Makefile 8.1 (Berkeley) 6/5/93 - 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/tunefs/tunefs.8 b/sbin/tunefs/tunefs.8 index bda39462a272..0fb11041d97d 100644 --- a/sbin/tunefs/tunefs.8 +++ b/sbin/tunefs/tunefs.8 @@ -25,9 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)tunefs.8 8.2 (Berkeley) 12/11/93 -.\" -.Dd August 16, 2022 +.Dd November 17, 2023 .Dt TUNEFS 8 .Os .Sh NAME @@ -217,7 +215,9 @@ read this to determine the device file for a specified mount point. .El .Sh SEE ALSO +.Xr ffs 4 , .Xr fs 5 , +.Xr tuning 7 , .Xr dumpfs 8 , .Xr gjournal 8 , .Xr growfs 8 , diff --git a/sbin/tunefs/tunefs.c b/sbin/tunefs/tunefs.c index 9b38e23effd0..6f6df8446cd9 100644 --- a/sbin/tunefs/tunefs.c +++ b/sbin/tunefs/tunefs.c @@ -29,18 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1983, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)tunefs.c 8.2 (Berkeley) 4/19/94"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> /* * tunefs: change layout parameters to an existing file system. */ diff --git a/sbin/umbctl/Makefile b/sbin/umbctl/Makefile new file mode 100644 index 000000000000..35afb1bcfd4b --- /dev/null +++ b/sbin/umbctl/Makefile @@ -0,0 +1,8 @@ +CFLAGS+= -I${SRCTOP}/sys/dev/usb/net + +PROG= umbctl +MAN= umbctl.8 + +BINDIR= /sbin + +.include <bsd.prog.mk> diff --git a/sbin/umbctl/umbctl.8 b/sbin/umbctl/umbctl.8 new file mode 100644 index 000000000000..55f8e315fabc --- /dev/null +++ b/sbin/umbctl/umbctl.8 @@ -0,0 +1,161 @@ +.\"- +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.\" Copyright (c) 2018 by Pierre Pronchery <khorben@defora.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(S) ``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(S) 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. +.\" +.\" From: pppoectl.8,v 1.30 2016/09/12 05:35:20 sevan Exp $ +.\" +.\" $NetBSD: umbctl.8,v 1.3 2020/03/22 07:45:02 khorben Exp $ +.\" +.\" last edit-date: [Fri Dec 20 18:20:00 2024] +.\" +.Dd December 20, 2024 +.Dt UMBCTL 8 +.Os +.Sh NAME +.Nm umbctl +.Nd display or set MBIM cellular modem interface parameters (4G/LTE) +.Sh SYNOPSIS +.Nm +.Op Fl v +.Ar ifname +.Op Ar parameter Op Ar value +.Ar ... +.Nm +.Op Fl v +.Fl f Ar config-file +.Ar ifname +.Sh DESCRIPTION +.Nm +supports the following options: +.Bl -tag -width "-f config_file" +.It Fl f Ar config-file +Parse +.Ar config-file +for +.Ar parameter Ns Op \&= Ns Ar value +pairs, one per line, as if they had been specified on the command line. +This allows the password or PIN codes to be not passed as command line +arguments. +Comments starting with # to the end of the current line are ignored. +.It Fl v +Enables verbose mode. +.El +.Pp +The +.Xr umb 4 +driver may require a number of additional arguments or optional +parameters besides the settings that can be adjusted with +.Xr ifconfig 8 . +These may be credentials or other tunable connectivity variables. +The +.Nm +utility can be used to display the current settings, or to adjust these +parameters as required. +.Pp +For whatever intent +.Nm +is being called, at least the parameter +.Ar ifname +needs to be specified, naming the interface for which the settings +are to be performed or displayed. +Use +.Xr ifconfig 8 +or +.Xr netstat 1 +to see which interfaces are available. +.Pp +If no other parameter is given, +.Nm +will just list the current status for +.Ar ifname +and exit. +.Pp +If any additional parameter is supplied, superuser privileges are +required, and the command works in +.Ql set +mode. +This is normally done quietly, unless the option +.Fl v +is also enabled, which will cause a final printout of the status as +described above once all other actions have been taken. +.Pp +The parameters currently supported include: +.Bl -tag -width "username=username" +.It Ar apn Ns \&= Ns Em access-point +Set the APN to +.Em access-point . +.It Ar username Ns \&= Ns Em username +Set the username to +.Em username . +.It Ar password Ns \&= Ns Em password +Set the password to +.Em password . +.It Ar pin Ns \&= Ns Em pin-code +Enter the PIN +.Em pin-code . +.It Ar puk Ns \&= Ns Em puk-code +Enter the PUK +.Em puk-code . +.It Ar roaming +Allow data connections when roaming. +.It Ar -roaming +Deny data connections when roaming. +.El +.Sh EXAMPLES +Display the settings for umb0: +.Bd -literal +# umbctl umb0 +umb0: state up, mode automatic, registration home network + provider "BSD-Net", dataclass LTE, signal good + phone number "+15554242", roaming "" (denied) + APN "", TX 50000000, RX 100000000 + firmware "MBIM_FW_V1.0", hardware "MBIM_HW_V1.0" +.Ed +.Pp +Configure the connection parameters for umb0 from the command line: +.Bd -literal +# umbctl umb0 apn operator.internet username mobile password mobile +.Ed +.Pp +Configure the connection parameters for umb0 from a file: +.Bd -literal +# umbctl -f /dev/stdin umb0 << EOF +pin=1234 +EOF +.Ed +.Sh SEE ALSO +.Xr netstat 1 , +.Xr umb 4 , +.Xr ifconfig 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Nx 9.0 , +and +.Fx 15.0 . +.Sh AUTHORS +The program was written by +.An Pierre Pronchery . diff --git a/sbin/umbctl/umbctl.c b/sbin/umbctl/umbctl.c new file mode 100644 index 000000000000..3d57b486ad80 --- /dev/null +++ b/sbin/umbctl/umbctl.c @@ -0,0 +1,557 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Original copyright (c) 2018 Pierre Pronchery <khorben@defora.org> (for the + * NetBSD Project) + * + * 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 DEVELOPERS ``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 DEVELOPERS 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. + * + * Copyright (c) 2022 ADISTA SAS (FreeBSD updates) + * + * Updates for FreeBSD by Pierre Pronchery <pierre@defora.net> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the copyright holder 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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. + * + * $NetBSD: umbctl.c,v 1.4 2020/05/13 21:44:30 khorben Exp $ + */ + +#include <sys/endian.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sockio.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <errno.h> +#include <inttypes.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> + +#include "mbim.h" +#include "if_umbreg.h" + +/* constants */ +static const struct umb_valdescr _umb_actstate[] = + MBIM_ACTIVATION_STATE_DESCRIPTIONS; + +static const struct umb_valdescr _umb_simstate[] = + MBIM_SIMSTATE_DESCRIPTIONS; + +static const struct umb_valdescr _umb_regstate[] = + MBIM_REGSTATE_DESCRIPTIONS; + +static const struct umb_valdescr _umb_pktstate[] = + MBIM_PKTSRV_STATE_DESCRIPTIONS; + +static const struct umb_valdescr _umb_dataclass[] = + MBIM_DATACLASS_DESCRIPTIONS; + +static const struct umb_valdescr _umb_state[] = + UMB_INTERNAL_STATE_DESCRIPTIONS; + +static const struct umb_valdescr _umb_pin_state[] = +{ + { UMB_PIN_REQUIRED, "PIN required"}, + { UMB_PIN_UNLOCKED, "PIN unlocked"}, + { UMB_PUK_REQUIRED, "PUK required"}, + { 0, NULL } +}; + +static const struct umb_valdescr _umb_regmode[] = +{ + { MBIM_REGMODE_UNKNOWN, "unknown" }, + { MBIM_REGMODE_AUTOMATIC, "automatic" }, + { MBIM_REGMODE_MANUAL, "manual" }, + { 0, NULL } +}; + +static const struct umb_valdescr _umb_ber[] = +{ + { UMB_BER_EXCELLENT, "excellent" }, + { UMB_BER_VERYGOOD, "very good" }, + { UMB_BER_GOOD, "good" }, + { UMB_BER_OK, "ok" }, + { UMB_BER_MEDIUM, "medium" }, + { UMB_BER_BAD, "bad" }, + { UMB_BER_VERYBAD, "very bad" }, + { UMB_BER_EXTREMELYBAD, "extremely bad" }, + { 0, NULL } +}; + + +/* prototypes */ +static int _char_to_utf16(const char * in, uint16_t * out, size_t outlen); +static int _error(int ret, char const * format, ...); +static int _umbctl(char const * ifname, int verbose, int argc, char * argv[]); +static int _umbctl_file(char const * ifname, char const * filename, + int verbose); +static void _umbctl_info(char const * ifname, struct umb_info * umbi); +static int _umbctl_ioctl(char const * ifname, int fd, unsigned long request, + struct ifreq * ifr); +static int _umbctl_set(char const * ifname, struct umb_parameter * umbp, + int argc, char * argv[]); +static int _umbctl_socket(void); +static int _usage(void); +static void _utf16_to_char(uint16_t * in, int inlen, char * out, size_t outlen); + + +/* functions */ +/* char_to_utf16 */ +/* this function is from OpenBSD's ifconfig(8) */ +static int _char_to_utf16(const char * in, uint16_t * out, size_t outlen) +{ + int n = 0; + uint16_t c; + + for (;;) { + c = *in++; + + if (c == '\0') { + /* + * NUL termination is not required, but zero out the + * residual buffer + */ + memset(out, 0, outlen); + return n; + } + if (outlen < sizeof(*out)) + return -1; + + *out++ = htole16(c); + n += sizeof(*out); + outlen -= sizeof(*out); + } +} + + +/* error */ +static int _error(int ret, char const * format, ...) +{ + va_list ap; + + fputs("umbctl: ", stderr); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fputs("\n", stderr); + return ret; +} + + +/* umbctl */ +static int _umbctl(char const * ifname, int verbose, int argc, char * argv[]) +{ + int fd; + struct ifreq ifr; + struct umb_info umbi; + struct umb_parameter umbp; + + if((fd = _umbctl_socket()) < 0) + return 2; + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + if(argc != 0) + { + memset(&umbp, 0, sizeof(umbp)); + ifr.ifr_data = (caddr_t)&umbp; + if(_umbctl_ioctl(ifname, fd, SIOCGUMBPARAM, &ifr) != 0 + || _umbctl_set(ifname, &umbp, argc, argv) != 0 + || _umbctl_ioctl(ifname, fd, SIOCSUMBPARAM, + &ifr) != 0) + { + close(fd); + return 2; + } + } + if(argc == 0 || verbose > 0) + { + ifr.ifr_data = (caddr_t)&umbi; + if(_umbctl_ioctl(ifname, fd, SIOCGUMBINFO, &ifr) != 0) + { + close(fd); + return 3; + } + _umbctl_info(ifname, &umbi); + } + if(close(fd) != 0) + return _error(2, "%s: %s", ifname, strerror(errno)); + return 0; +} + + +/* umbctl_file */ +static int _file_parse(char const * ifname, struct umb_parameter * umbp, + char const * filename); + +static int _umbctl_file(char const * ifname, char const * filename, int verbose) +{ + int fd; + struct ifreq ifr; + struct umb_parameter umbp; + struct umb_info umbi; + + if((fd = _umbctl_socket()) < 0) + return 2; + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + ifr.ifr_data = (caddr_t)&umbp; + memset(&umbp, 0, sizeof(umbp)); + if(_umbctl_ioctl(ifname, fd, SIOCGUMBPARAM, &ifr) != 0 + || _file_parse(ifname, &umbp, filename) != 0 + || _umbctl_ioctl(ifname, fd, SIOCSUMBPARAM, &ifr) != 0) + { + close(fd); + return 2; + } + if(verbose > 0) + { + ifr.ifr_data = (caddr_t)&umbi; + if(_umbctl_ioctl(ifname, fd, SIOCGUMBINFO, &ifr) != 0) + { + close(fd); + return 3; + } + _umbctl_info(ifname, &umbi); + } + if(close(fd) != 0) + return _error(2, "%s: %s", ifname, strerror(errno)); + return 0; +} + +static int _file_parse(char const * ifname, struct umb_parameter * umbp, + char const * filename) +{ + int ret = 0; + FILE * fp; + char buf[512]; + size_t len; + int i; + int eof; + char * tokens[3] = { buf, NULL, NULL }; + char * p; + + if((fp = fopen(filename, "r")) == NULL) + return _error(2, "%s: %s", filename, strerror(errno)); + while(fgets(buf, sizeof(buf), fp) != NULL) + { + if(buf[0] == '#') + continue; + buf[sizeof(buf) - 1] = '\0'; + if((len = strlen(buf)) > 0) + { + if(buf[len - 1] != '\n') + { + ret = _error(2, "%s: %s", filename, + "Line too long"); + while((i = fgetc(fp)) != EOF && i != '\n'); + continue; + } + buf[len - 1] = '\0'; + } + if((p = strchr(buf, '=')) != NULL) + { + tokens[1] = p + 1; + *p = '\0'; + } else + tokens[1] = NULL; + ret |= _umbctl_set(ifname, umbp, (p != NULL) ? 2 : 1, tokens) + ? 2 : 0; + } + eof = feof(fp); + if(fclose(fp) != 0 || !eof) + return _error(2, "%s: %s", filename, strerror(errno)); + return ret; +} + + +/* umbctl_info */ +static void _umbctl_info(char const * ifname, struct umb_info * umbi) +{ + char provider[UMB_PROVIDERNAME_MAXLEN + 1]; + char pn[UMB_PHONENR_MAXLEN + 1]; + char roaming[UMB_ROAMINGTEXT_MAXLEN + 1]; + char apn[UMB_APN_MAXLEN + 1]; + char fwinfo[UMB_FWINFO_MAXLEN + 1]; + char hwinfo[UMB_HWINFO_MAXLEN + 1]; + + _utf16_to_char(umbi->provider, UMB_PROVIDERNAME_MAXLEN, + provider, sizeof(provider)); + _utf16_to_char(umbi->pn, UMB_PHONENR_MAXLEN, pn, sizeof(pn)); + _utf16_to_char(umbi->roamingtxt, UMB_ROAMINGTEXT_MAXLEN, + roaming, sizeof(roaming)); + _utf16_to_char(umbi->apn, UMB_APN_MAXLEN, apn, sizeof(apn)); + _utf16_to_char(umbi->fwinfo, UMB_FWINFO_MAXLEN, fwinfo, sizeof(fwinfo)); + _utf16_to_char(umbi->hwinfo, UMB_HWINFO_MAXLEN, hwinfo, sizeof(hwinfo)); + printf("%s: state %s, mode %s, registration %s\n" + "\tprovider \"%s\", dataclass %s, signal %s\n" + "\tphone number \"%s\", roaming \"%s\" (%s)\n" + "\tAPN \"%s\", TX %" PRIu64 ", RX %" PRIu64 "\n" + "\tfirmware \"%s\", hardware \"%s\"\n", + ifname, umb_val2descr(_umb_state, umbi->state), + umb_val2descr(_umb_regmode, umbi->regmode), + umb_val2descr(_umb_regstate, umbi->regstate), provider, + umb_val2descr(_umb_dataclass, umbi->cellclass), + umb_val2descr(_umb_ber, umbi->ber), pn, roaming, + umbi->enable_roaming ? "allowed" : "denied", + apn, umbi->uplink_speed, umbi->downlink_speed, + fwinfo, hwinfo); +} + + +/* umbctl_ioctl */ +static int _umbctl_ioctl(char const * ifname, int fd, unsigned long request, + struct ifreq * ifr) +{ + if(ioctl(fd, request, ifr) != 0) + return _error(-1, "%s: %s", ifname, strerror(errno)); + return 0; +} + + +/* umbctl_set */ +/* callbacks */ +static int _set_apn(char const *, struct umb_parameter *, char const *); +static int _set_username(char const *, struct umb_parameter *, char const *); +static int _set_password(char const *, struct umb_parameter *, char const *); +static int _set_pin(char const *, struct umb_parameter *, char const *); +static int _set_puk(char const *, struct umb_parameter *, char const *); +static int _set_roaming_allow(char const *, struct umb_parameter *, + char const *); +static int _set_roaming_deny(char const *, struct umb_parameter *, + char const *); + +static int _umbctl_set(char const * ifname, struct umb_parameter * umbp, + int argc, char * argv[]) +{ + struct + { + char const * name; + int (*callback)(char const *, + struct umb_parameter *, char const *); + int parameter; + } callbacks[] = + { + { "apn", _set_apn, 1 }, + { "username", _set_username, 1 }, + { "password", _set_password, 1 }, + { "pin", _set_pin, 1 }, + { "puk", _set_puk, 1 }, + { "roaming", _set_roaming_allow, 0 }, + { "-roaming", _set_roaming_deny, 0 }, + }; + int i; + size_t j; + + for(i = 0; i < argc; i++) + { + for(j = 0; j < sizeof(callbacks) / sizeof(*callbacks); j++) + if(strcmp(argv[i], callbacks[j].name) == 0) + { + if(callbacks[j].parameter && i + 1 == argc) + return _error(-1, "%s: Incomplete" + " parameter", argv[i]); + if(callbacks[j].callback(ifname, umbp, + callbacks[j].parameter + ? argv[i + 1] : NULL)) + return -1; + if(callbacks[j].parameter) + i++; + break; + } + if(j == sizeof(callbacks) / sizeof(*callbacks)) + return _error(-1, "%s: Unknown parameter", argv[i]); + } + return 0; +} + +static int _set_apn(char const * ifname, struct umb_parameter * umbp, + char const * apn) +{ + umbp->apnlen = _char_to_utf16(apn, umbp->apn, sizeof(umbp->apn)); + if(umbp->apnlen < 0 || (size_t)umbp->apnlen > sizeof(umbp->apn)) + return _error(-1, "%s: %s", ifname, "APN too long"); + return 0; +} + +static int _set_username(char const * ifname, struct umb_parameter * umbp, + char const * username) +{ + umbp->usernamelen = _char_to_utf16(username, umbp->username, + sizeof(umbp->username)); + if(umbp->usernamelen < 0 + || (size_t)umbp->usernamelen > sizeof(umbp->username)) + return _error(-1, "%s: %s", ifname, "Username too long"); + return 0; +} + +static int _set_password(char const * ifname, struct umb_parameter * umbp, + char const * password) +{ + umbp->passwordlen = _char_to_utf16(password, umbp->password, + sizeof(umbp->password)); + if(umbp->passwordlen < 0 + || (size_t)umbp->passwordlen > sizeof(umbp->password)) + return _error(-1, "%s: %s", ifname, "Password too long"); + return 0; +} + +static int _set_pin(char const * ifname, struct umb_parameter * umbp, + char const * pin) +{ + umbp->is_puk = 0; + umbp->op = MBIM_PIN_OP_ENTER; + umbp->pinlen = _char_to_utf16(pin, umbp->pin, sizeof(umbp->pin)); + if(umbp->pinlen < 0 || (size_t)umbp->pinlen + > sizeof(umbp->pin)) + return _error(-1, "%s: %s", ifname, "PIN code too long"); + return 0; +} + +static int _set_puk(char const * ifname, struct umb_parameter * umbp, + char const * puk) +{ + umbp->is_puk = 1; + umbp->op = MBIM_PIN_OP_ENTER; + umbp->pinlen = _char_to_utf16(puk, umbp->pin, sizeof(umbp->pin)); + if(umbp->pinlen < 0 || (size_t)umbp->pinlen > sizeof(umbp->pin)) + return _error(-1, "%s: %s", ifname, "PUK code too long"); + return 0; +} + +static int _set_roaming_allow(char const * ifname, struct umb_parameter * umbp, + char const * unused) +{ + (void) ifname; + (void) unused; + + umbp->roaming = 1; + return 0; +} + +static int _set_roaming_deny(char const * ifname, struct umb_parameter * umbp, + char const * unused) +{ + (void) ifname; + (void) unused; + + umbp->roaming = 0; + return 0; +} + + +/* umbctl_socket */ +static int _umbctl_socket(void) +{ + int fd; + + if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + return _error(-1, "socket: %s", strerror(errno)); + return fd; +} + + +/* usage */ +static int _usage(void) +{ + fputs("Usage: umbctl [-v] ifname [parameter [value]] [...]\n" +" umbctl -f config-file ifname\n", + stderr); + return 1; +} + + +/* utf16_to_char */ +static void _utf16_to_char(uint16_t * in, int inlen, char * out, size_t outlen) +{ + uint16_t c; + + while (outlen > 0) { + c = inlen > 0 ? htole16(*in) : 0; + if (c == 0 || --outlen == 0) { + /* always NUL terminate result */ + *out = '\0'; + break; + } + *out++ = isascii(c) ? (char)c : '?'; + in++; + inlen--; + } +} + + +/* main */ +int main(int argc, char * argv[]) +{ + int o; + char const * filename = NULL; + int verbose = 0; + + while((o = getopt(argc, argv, "f:gv")) != -1) + switch(o) + { + case 'f': + filename = optarg; + break; + case 'v': + verbose++; + break; + default: + return _usage(); + } + if(optind == argc) + return _usage(); + if(filename != NULL) + { + if(optind + 1 != argc) + return _usage(); + return _umbctl_file(argv[optind], filename, verbose); + } + return _umbctl(argv[optind], verbose, argc - optind - 1, + &argv[optind + 1]); +} diff --git a/sbin/umount/Makefile b/sbin/umount/Makefile index 5d8cb0670cc3..d97283d70c21 100644 --- a/sbin/umount/Makefile +++ b/sbin/umount/Makefile @@ -1,6 +1,3 @@ -# @(#)Makefile 8.4 (Berkeley) 6/22/95 -# - PACKAGE=runtime PROG= umount SRCS= umount.c vfslist.c mounttab.c diff --git a/sbin/umount/umount.8 b/sbin/umount/umount.8 index b5a7d7230269..2c26ca868dae 100644 --- a/sbin/umount/umount.8 +++ b/sbin/umount/umount.8 @@ -25,9 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)umount.8 8.2 (Berkeley) 5/8/95 -.\" -.Dd June 19, 2020 +.Dd January 16, 2024 .Dt UMOUNT 8 .Os .Sh NAME @@ -35,7 +33,7 @@ .Nd unmount file systems .Sh SYNOPSIS .Nm -.Op Fl fNnv +.Op Fl dfNnv .Ar special ... | node ... | fsid ... .Nm .Fl a | A @@ -73,6 +71,11 @@ except for those mounted at .Pa / or .Pa /dev . +.It Fl d +If the filesystem is mounted on an +.Xr md 4 +device (a memory disk), detach it after +.Xr unmount 2 . .It Fl F Ar fstab Specify the .Pa fstab @@ -184,6 +187,7 @@ file system table .Xr unmount 2 , .Xr fstab 5 , .Xr autounmountd 8 , +.Xr mdconfig 8 , .Xr mount 8 .Sh HISTORY A diff --git a/sbin/umount/umount.c b/sbin/umount/umount.c index 9119c5be0da1..c15fd13d065a 100644 --- a/sbin/umount/umount.c +++ b/sbin/umount/umount.c @@ -29,20 +29,6 @@ * SUCH DAMAGE. */ -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1980, 1989, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)umount.c 8.8 (Berkeley) 5/8/95"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - #include <sys/param.h> #include <sys/mount.h> #include <sys/socket.h> @@ -64,10 +50,20 @@ static const char rcsid[] = #include "mounttab.h" +/* used by md_detach() */ +#include <sys/ioctl.h> +#include <sys/mdioctl.h> +#include <fcntl.h> +#include <paths.h> + +#define DEV_MD _PATH_DEV MD_NAME +#define DEV_MDCTL _PATH_DEV MDCTL_NAME + typedef enum { FIND, REMOVE, CHECKUNIQUE } dowhat; static struct addrinfo *nfshost_ai = NULL; -static int fflag, vflag; +static int dflag, fflag, vflag; +static int all; static char *nfshost; struct statfs *checkmntlist(char *); @@ -84,17 +80,18 @@ int checkname (char *, char **); int umountfs(struct statfs *sfs); void usage (void); int xdr_dir (XDR *, char *); +int md_detach(const char *); int main(int argc, char *argv[]) { - int all, errs, ch, mntsize, error, nfsforce, ret; + int errs, ch, mntsize, error, nfsforce, ret; char **typelist = NULL; struct statfs *mntbuf, *sfs; struct addrinfo hints; nfsforce = all = errs = 0; - while ((ch = getopt(argc, argv, "AaF:fh:Nnt:v")) != -1) + while ((ch = getopt(argc, argv, "AadF:fh:Nnt:v")) != -1) switch (ch) { case 'A': all = 2; @@ -102,6 +99,9 @@ main(int argc, char *argv[]) case 'a': all = 1; break; + case 'd': + dflag = 1; + break; case 'F': setfstab(optarg); break; @@ -469,6 +469,16 @@ umountfs(struct statfs *sfs) clnt_destroy(clp); } free(orignfsdirname); + + if (dflag) { + if (md_detach(sfs->f_mntfromname) == 0) { + if (vflag) + (void)printf("%s: detached\n", + sfs->f_mntfromname); + } else if (!all) + return (-1); + } + return (0); } @@ -651,7 +661,45 @@ usage(void) { (void)fprintf(stderr, "%s\n%s\n", - "usage: umount [-fNnv] special ... | node ... | fsid ...", - " umount -a | -A [-F fstab] [-fnv] [-h host] [-t type]"); + "usage: umount [-dfNnv] special ... | node ... | fsid ...", + " umount -a | -A [-F fstab] [-dfnv] [-h host] [-t type]"); exit(1); } + +int +md_detach(const char *device) +{ + struct md_ioctl mdio; + char *eptr; + int fd; + + if (strncmp(device, DEV_MD, sizeof(DEV_MD) - 1)) { + if (!all) + warnx("invalid md device: %s", device); + return (-1); + } + + memset(&mdio, 0, sizeof(mdio)); + mdio.md_version = MDIOVERSION; + mdio.md_options = fflag ? MD_FORCE : 0; + mdio.md_unit = strtoul(device + sizeof(DEV_MD) - 1, &eptr, 0); + if (mdio.md_unit == (unsigned)ULONG_MAX || eptr - device == sizeof(DEV_MD) - 1) { + warnx("invalid md device: %s", device); + return (-1); + } + + fd = open(DEV_MDCTL, O_RDWR, 0); + if (fd < 0) { + warn("%s", DEV_MDCTL); + return (-1); + } + + if (ioctl(fd, MDIOCDETACH, &mdio) < 0) { + warn("%s", DEV_MD); + close(fd); + return (-1); + } + + close(fd); + return (0); +} diff --git a/sbin/veriexec/Makefile b/sbin/veriexec/Makefile index 78f7059109fb..6b6007c4bb3d 100644 --- a/sbin/veriexec/Makefile +++ b/sbin/veriexec/Makefile @@ -1,4 +1,3 @@ - PROG= veriexec MAN= veriexec.8 SRCS= \ diff --git a/sbin/veriexec/Makefile.depend b/sbin/veriexec/Makefile.depend index 9307e12bc8dd..cbe57ebe4cf8 100644 --- a/sbin/veriexec/Makefile.depend +++ b/sbin/veriexec/Makefile.depend @@ -1,7 +1,6 @@ # Autogenerated - do NOT edit! DIRDEPS = \ - gnu/lib/csu \ include \ include/xlocale \ lib/${CSU_DIR} \ @@ -10,6 +9,7 @@ DIRDEPS = \ lib/libcompiler_rt \ lib/libsecureboot \ lib/libveriexec \ + usr.bin/yacc.host \ .include <dirdeps.mk> diff --git a/sbin/veriexec/veriexec.8 b/sbin/veriexec/veriexec.8 index c325f267689d..8352dd8e5e49 100644 --- a/sbin/veriexec/veriexec.8 +++ b/sbin/veriexec/veriexec.8 @@ -1,5 +1,7 @@ .\"- -.\" Copyright (c) 2018, Juniper Networks, Inc. +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.\" Copyright (c) 2018-2024, Juniper Networks, Inc. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -22,7 +24,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE .\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd July 8, 2022 +.Dd April 19, 2025 .Dt VERIEXEC 8 .Os .Sh NAME @@ -39,6 +41,9 @@ .Nm .Fl i Ar state .Nm +.Fl l +.Ar file ... +.Nm .Fl x .Ar file ... .Sh DESCRIPTION @@ -67,6 +72,14 @@ and with to query the current .Ar state . .Pp +With +.Fl l +.Nm +will report any labels associated with the remaining arguments +assumed to be files. +If only a single file argument is given, the bare label (if any) +will be reported, otherwise the pathname followed by label. +.Pp The final form with .Fl x is used to test whether @@ -84,7 +97,7 @@ The possible states are: .Bl -tag -width enforce .It Ar loaded -set automatically when first +set automatically when the first .Pa manifest has been loaded. .It Ar active @@ -96,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 @@ -124,10 +137,11 @@ The manifest contains a mapping of relative pathnames to fingerprints with optional flags. For example: .Bd -literal -offset indent -sbin/veriexec sha256=f22136...c0ff71 no_ptrace +sbin/veriexec sha256=f22136...c0ff71 no_ptrace trusted usr/bin/python sha256=5944d9...876525 indirect sbin/somedaemon sha256=77fc2f...63f5687 label=mod1/val1,mod2/val2 .Ed +.Pp The supported flags are: .Bl -tag -width indirect .It Ql indirect @@ -136,16 +150,31 @@ but can be used as an interpreter for example via: .Bd -literal -offset indent #!/usr/bin/python .Ed +.It Ql no_fips +If the system has a notion of running in FIPS mode, +a file marked with this flag will not be allowed to +exec. .It Ql no_ptrace do not allow running executable under a debugger. Useful for any application critical to the security state of system. +.It Ql trusted +this flag is required for a process to use +.Xr veriexec 4 +to interact with +.Xr mac_veriexec 4 . +Generally only +.Nm +should need this flag. +Implies +.Ql no_ptrace . + .El .Pp The .Ql label argument allows associating a .Xr maclabel 7 -with the executable. +with a file. Neither .Nm nor @@ -154,10 +183,60 @@ nor pay any attention to the content of the label they are provided for the use of other .Xr mac 4 -modules. +modules or indeed other applications. +.Sh EXAMPLES +Load the manifest for a +.Xr tarfs 5 +package mounted on +.Pa /mnt +and be strict about enforcing certificate validity: +.Bd -literal -offset indent +# veriexec -S -C /mnt /mnt/manifest + +.Ed +.Nm +will look for a detached signature that it recognizes, such as +.Pa manifest.asc +(OpenPGP) or +.Pa manifest.*sig +(X.509). +In the case of an X.509 signature we also need a matching certificate chain +.Pa manifest.*certs . +In either case there needs to be a suitable trust anchor in the trust store. +.Pp +We can now activate: +.Bd -literal -offset indent +# veriexec -z active + +.Ed +Any user can check if +.Xr mac_veriexec 4 +is +.Ql active : +.Bd -literal -offset indent +$ veriexec -i active + +.Ed +Any user can check that +.Pa /mnt/bin/app +is verified: +.Bd -literal -offset indent +$ veriexec -x /mnt/bin/app + +.Ed +If it is not, we will get an Authentiaction error, +but unless +.Xr mac_veriexec 4 +is enforcing we would still be able to run it. +.Sh NOTES +It is only safe to set +.Xr mac_veriexec 4 +to +.Ql enforce +state, if sufficient manifests have been loaded +to cover all the applications that might need to be run. .Sh HISTORY The Verified Exec system first appeared in .Nx . -This utility derrives from the one found in Junos. -The key difference is the requirement that manifest files -be digitally signed. +This utility derives from the one found in Junos, +which requires that manifest files be digitally signed. diff --git a/sbin/veriexec/veriexec.c b/sbin/veriexec/veriexec.c index 3899a781625a..0619b261665e 100644 --- a/sbin/veriexec/veriexec.c +++ b/sbin/veriexec/veriexec.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2018, Juniper Networks, Inc. + * Copyright (c) 2018-2023, Juniper Networks, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -53,7 +53,7 @@ static int veriexec_usage(void) { printf("%s", - "Usage:\tveriexec [-h] [-i state] [-C] [-xv state|verbosity] [path]\n"); + "Usage:\tveriexec [-C path] [-hlxv] [-[iz] state] [path]\n"); return (0); } @@ -135,6 +135,45 @@ veriexec_state_modify(const char *arg_text) return (state); } +#ifdef HAVE_VERIEXEC_GET_PATH_LABEL +static void +veriexec_check_labels(int argc, char *argv[]) +{ + char buf[BUFSIZ]; + char *cp; + int n; + + n = (argc - optind); + for (; optind < argc; optind++) { + cp = veriexec_get_path_label(argv[optind], buf, sizeof(buf)); + if (cp) { + if (n > 1) + printf("%s: %s\n", argv[optind], cp); + else + printf("%s\n", cp); + if (cp != buf) + free(cp); + } + } + exit(EX_OK); +} +#endif + +static void +veriexec_check_paths(int argc, char *argv[]) +{ + int x; + + x = EX_OK; + for (; optind < argc; optind++) { + if (veriexec_check_path(argv[optind])) { + warn("%s", argv[optind]); + x = 2; + } + } + exit(x); +} + int main(int argc, char *argv[]) { @@ -147,7 +186,7 @@ main(int argc, char *argv[]) dev_fd = open(_PATH_DEV_VERIEXEC, O_WRONLY, 0); - while ((c = getopt(argc, argv, "hC:i:Sxvz:")) != -1) { + while ((c = getopt(argc, argv, "C:hi:lSxvz:")) != -1) { switch (c) { case 'h': /* Print usage info */ @@ -173,6 +212,11 @@ main(int argc, char *argv[]) exit((x & state) == 0); break; +#ifdef HAVE_VERIEXEC_GET_PATH_LABEL + case 'l': + veriexec_check_labels(argc, argv); + break; +#endif case 'S': /* Strictly enforce certificate validity */ ve_enforce_validity_set(1); @@ -188,13 +232,7 @@ main(int argc, char *argv[]) /* * -x says all other args are paths to check. */ - for (x = EX_OK; optind < argc; optind++) { - if (veriexec_check_path(argv[optind])) { - warn("%s", argv[optind]); - x = 2; - } - } - exit(x); + veriexec_check_paths(argc, argv); break; case 'z': /* Modify the state */ diff --git a/sbin/zfsbootcfg/Makefile b/sbin/zfsbootcfg/Makefile index e5e20fc52130..318de8a030fa 100644 --- a/sbin/zfsbootcfg/Makefile +++ b/sbin/zfsbootcfg/Makefile @@ -1,20 +1,7 @@ -# @(#)Makefile 8.4 (Berkeley) 6/22/95 - +PACKAGE=zfs PROG= zfsbootcfg MAN= zfsbootcfg.8 LIBADD+=zfsbootenv -CFLAGS+= -DIN_BASE -CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include -CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include/os/freebsd -CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include -CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd -CFLAGS+= -I${SRCTOP}/sys -CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include -CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/module/icp/include -CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h -CFLAGS+= -DHAVE_ISSETUGID -CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h - .include <bsd.prog.mk> 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 diff --git a/sbin/zfsbootcfg/zfsbootcfg.c b/sbin/zfsbootcfg/zfsbootcfg.c index 0c1e9e0f7ea1..03b028a9ab71 100644 --- a/sbin/zfsbootcfg/zfsbootcfg.c +++ b/sbin/zfsbootcfg/zfsbootcfg.c @@ -23,7 +23,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <sys/cdefs.h> #include <sys/types.h> #include <errno.h> #include <limits.h> |